diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/bcm2708.dtsi linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+#include "bcm2835.dtsi"
+#include "bcm270x.dtsi"
+
+/ {
+	__overrides__ {
+		arm_freq;
+	};
+};
+
+&vc4 {
+	status = "disabled";
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/bcm2708-rpi-b.dts linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708-rpi-b.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/bcm2708-rpi-b.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708-rpi-b.dts	2021-07-25 16:45:34.748814264 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/dts-v1/;
+
+#include "bcm2708.dtsi"
+#include "bcm2708-rpi.dtsi"
+#include "bcm283x-rpi-smsc9512.dtsi"
+#include "bcm283x-rpi-csi1-2lane.dtsi"
+#include "bcm283x-rpi-i2c0mux_0_28.dtsi"
+#include "bcm283x-rpi-cam1-regulator.dtsi"
+
+/ {
+	compatible = "raspberrypi,model-b", "brcm,bcm2835";
+	model = "Raspberry Pi Model B";
+};
+
+&gpio {
+	spi0_pins: spi0_pins {
+		brcm,pins = <9 10 11>;
+		brcm,function = <4>; /* alt0 */
+	};
+
+	spi0_cs_pins: spi0_cs_pins {
+		brcm,pins = <8 7>;
+		brcm,function = <1>; /* output */
+	};
+
+	i2c0_pins: i2c0 {
+		brcm,pins = <0 1>;
+		brcm,function = <4>;
+	};
+
+	i2c1_pins: i2c1 {
+		brcm,pins = <2 3>;
+		brcm,function = <4>;
+	};
+
+	i2s_pins: i2s {
+		brcm,pins = <28 29 30 31>;
+		brcm,function = <6>; /* alt2 */
+	};
+
+	audio_pins: audio_pins {
+		brcm,pins = <40 45>;
+		brcm,function = <4>;
+	};
+};
+
+&uart0 {
+	status = "okay";
+};
+
+&spi0 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&spi0_pins &spi0_cs_pins>;
+	cs-gpios = <&gpio 8 1>, <&gpio 7 1>;
+
+	spidev0: spidev@0{
+		compatible = "spidev";
+		reg = <0>;	/* CE0 */
+		#address-cells = <1>;
+		#size-cells = <0>;
+		spi-max-frequency = <125000000>;
+	};
+
+	spidev1: spidev@1{
+		compatible = "spidev";
+		reg = <1>;	/* CE1 */
+		#address-cells = <1>;
+		#size-cells = <0>;
+		spi-max-frequency = <125000000>;
+	};
+};
+
+&i2c0if {
+	clock-frequency = <100000>;
+};
+
+&i2c1 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&i2c1_pins>;
+	clock-frequency = <100000>;
+};
+
+&i2c2 {
+	clock-frequency = <100000>;
+};
+
+&i2s {
+	pinctrl-names = "default";
+	pinctrl-0 = <&i2s_pins>;
+};
+
+&leds {
+	act_led: act {
+		label = "led0";
+		linux,default-trigger = "mmc0";
+		gpios = <&gpio 16 1>;
+	};
+};
+
+&hdmi {
+	hpd-gpios = <&gpio 46 GPIO_ACTIVE_HIGH>;
+};
+
+&audio {
+	pinctrl-names = "default";
+	pinctrl-0 = <&audio_pins>;
+};
+
+&cam1_reg {
+	gpio = <&gpio 21 GPIO_ACTIVE_HIGH>;
+};
+
+/ {
+	__overrides__ {
+		act_led_gpio = <&act_led>,"gpios:4";
+		act_led_activelow = <&act_led>,"gpios:8";
+		act_led_trigger = <&act_led>,"linux,default-trigger";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/bcm2708-rpi-b-plus.dts linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708-rpi-b-plus.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/bcm2708-rpi-b-plus.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708-rpi-b-plus.dts	2021-07-25 16:45:34.748814264 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/dts-v1/;
+
+#include "bcm2708.dtsi"
+#include "bcm2708-rpi.dtsi"
+#include "bcm283x-rpi-smsc9514.dtsi"
+#include "bcm283x-rpi-csi1-2lane.dtsi"
+#include "bcm283x-rpi-i2c0mux_0_28.dtsi"
+#include "bcm283x-rpi-cam1-regulator.dtsi"
+
+/ {
+	compatible = "raspberrypi,model-b-plus", "brcm,bcm2835";
+	model = "Raspberry Pi Model B+";
+};
+
+&gpio {
+	spi0_pins: spi0_pins {
+		brcm,pins = <9 10 11>;
+		brcm,function = <4>; /* alt0 */
+	};
+
+	spi0_cs_pins: spi0_cs_pins {
+		brcm,pins = <8 7>;
+		brcm,function = <1>; /* output */
+	};
+
+	i2c0_pins: i2c0 {
+		brcm,pins = <0 1>;
+		brcm,function = <4>;
+	};
+
+	i2c1_pins: i2c1 {
+		brcm,pins = <2 3>;
+		brcm,function = <4>;
+	};
+
+	i2s_pins: i2s {
+		brcm,pins = <18 19 20 21>;
+		brcm,function = <4>; /* alt0 */
+	};
+
+	audio_pins: audio_pins {
+		brcm,pins = <40 45>;
+		brcm,function = <4>;
+	};
+};
+
+&uart0 {
+	status = "okay";
+};
+
+&spi0 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&spi0_pins &spi0_cs_pins>;
+	cs-gpios = <&gpio 8 1>, <&gpio 7 1>;
+
+	spidev0: spidev@0{
+		compatible = "spidev";
+		reg = <0>;	/* CE0 */
+		#address-cells = <1>;
+		#size-cells = <0>;
+		spi-max-frequency = <125000000>;
+	};
+
+	spidev1: spidev@1{
+		compatible = "spidev";
+		reg = <1>;	/* CE1 */
+		#address-cells = <1>;
+		#size-cells = <0>;
+		spi-max-frequency = <125000000>;
+	};
+};
+
+&i2c0if {
+	clock-frequency = <100000>;
+};
+
+&i2c1 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&i2c1_pins>;
+	clock-frequency = <100000>;
+};
+
+&i2c2 {
+	clock-frequency = <100000>;
+};
+
+&i2s {
+	pinctrl-names = "default";
+	pinctrl-0 = <&i2s_pins>;
+};
+
+&leds {
+	act_led: act {
+		label = "led0";
+		linux,default-trigger = "mmc0";
+		gpios = <&gpio 47 0>;
+	};
+
+	pwr_led: pwr {
+		label = "led1";
+		linux,default-trigger = "input";
+		gpios = <&gpio 35 0>;
+	};
+};
+
+&hdmi {
+	hpd-gpios = <&gpio 46 GPIO_ACTIVE_LOW>;
+};
+
+&audio {
+	pinctrl-names = "default";
+	pinctrl-0 = <&audio_pins>;
+};
+
+&cam1_reg {
+	gpio = <&gpio 41 GPIO_ACTIVE_HIGH>;
+};
+
+/ {
+	__overrides__ {
+		act_led_gpio = <&act_led>,"gpios:4";
+		act_led_activelow = <&act_led>,"gpios:8";
+		act_led_trigger = <&act_led>,"linux,default-trigger";
+
+		pwr_led_gpio = <&pwr_led>,"gpios:4";
+		pwr_led_activelow = <&pwr_led>,"gpios:8";
+		pwr_led_trigger = <&pwr_led>,"linux,default-trigger";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/bcm2708-rpi-b-rev1.dts linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708-rpi-b-rev1.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/bcm2708-rpi-b-rev1.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708-rpi-b-rev1.dts	2021-07-25 16:45:34.748814264 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/dts-v1/;
+
+#include "bcm2708.dtsi"
+#include "bcm2708-rpi.dtsi"
+#include "bcm283x-rpi-smsc9512.dtsi"
+#include "bcm283x-rpi-csi1-2lane.dtsi"
+#include "bcm283x-rpi-cam1-regulator.dtsi"
+
+/ {
+	compatible = "raspberrypi,model-b", "brcm,bcm2835";
+	model = "Raspberry Pi Model B";
+};
+
+&gpio {
+	spi0_pins: spi0_pins {
+		brcm,pins = <9 10 11>;
+		brcm,function = <4>; /* alt0 */
+	};
+
+	spi0_cs_pins: spi0_cs_pins {
+		brcm,pins = <8 7>;
+		brcm,function = <1>; /* output */
+	};
+
+	i2c0_pins: i2c0 {
+		brcm,pins = <0 1>;
+		brcm,function = <4>;
+	};
+
+	i2c1_pins: i2c1 {
+		brcm,pins = <2 3>;
+		brcm,function = <4>;
+	};
+
+	i2s_pins: i2s {
+		brcm,pins = <28 29 30 31>;
+		brcm,function = <6>; /* alt2 */
+	};
+
+	audio_pins: audio_pins {
+		brcm,pins = <40 45>;
+		brcm,function = <4>;
+	};
+};
+
+&uart0 {
+	status = "okay";
+};
+
+&spi0 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&spi0_pins &spi0_cs_pins>;
+	cs-gpios = <&gpio 8 1>, <&gpio 7 1>;
+
+	spidev0: spidev@0{
+		compatible = "spidev";
+		reg = <0>;	/* CE0 */
+		#address-cells = <1>;
+		#size-cells = <0>;
+		spi-max-frequency = <125000000>;
+	};
+
+	spidev1: spidev@1{
+		compatible = "spidev";
+		reg = <1>;	/* CE1 */
+		#address-cells = <1>;
+		#size-cells = <0>;
+		spi-max-frequency = <125000000>;
+	};
+};
+
+/delete-node/ &i2c0mux;
+
+i2c0: &i2c0if {
+	pinctrl-names = "default";
+	pinctrl-0 = <&i2c0_pins>;
+	clock-frequency = <100000>;
+};
+
+i2c_csi_dsi: &i2c1 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&i2c1_pins>;
+	clock-frequency = <100000>;
+};
+
+/ {
+	aliases {
+		i2c0 = &i2c0;
+	};
+
+	__overrides__ {
+		i2c0 = <&i2c0>, "status";
+	};
+};
+
+&i2c2 {
+	clock-frequency = <100000>;
+};
+
+&i2s {
+	pinctrl-names = "default";
+	pinctrl-0 = <&i2s_pins>;
+};
+
+&leds {
+	act_led: act {
+		label = "led0";
+		linux,default-trigger = "mmc0";
+		gpios = <&gpio 16 1>;
+	};
+};
+
+&hdmi {
+	hpd-gpios = <&gpio 46 GPIO_ACTIVE_HIGH>;
+};
+
+&audio {
+	pinctrl-names = "default";
+	pinctrl-0 = <&audio_pins>;
+};
+
+&cam1_reg {
+	gpio = <&gpio 27 GPIO_ACTIVE_HIGH>;
+};
+
+/ {
+	__overrides__ {
+		act_led_gpio = <&act_led>,"gpios:4";
+		act_led_activelow = <&act_led>,"gpios:8";
+		act_led_trigger = <&act_led>,"linux,default-trigger";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/bcm2708-rpi-bt.dtsi linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708-rpi-bt.dtsi
--- linux-5.10.52-orig/arch/arm/boot/dts/bcm2708-rpi-bt.dtsi	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708-rpi-bt.dtsi	2021-07-25 16:45:34.748814264 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// SPDX-License-Identifier: GPL-2.0
+
+&uart0 {
+	bt: bluetooth {
+		compatible = "brcm,bcm43438-bt";
+		max-speed = <3000000>;
+		shutdown-gpios = <&gpio 45 GPIO_ACTIVE_HIGH>;
+		status = "disabled";
+	};
+};
+
+&uart1 {
+	minibt: bluetooth {
+		compatible = "brcm,bcm43438-bt";
+		max-speed = <460800>;
+		shutdown-gpios = <&gpio 45 GPIO_ACTIVE_HIGH>;
+		status = "disabled";
+	};
+};
+
+/ {
+	__overrides__ {
+		krnbt = <&bt>,"status";
+		krnbt_baudrate = <&bt>,"max-speed:0";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/bcm2708-rpi-cm.dts linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708-rpi-cm.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/bcm2708-rpi-cm.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708-rpi-cm.dts	2021-07-25 16:45:34.748814264 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/dts-v1/;
+
+#include "bcm2708-rpi-cm.dtsi"
+#include "bcm283x-rpi-csi0-2lane.dtsi"
+#include "bcm283x-rpi-csi1-4lane.dtsi"
+#include "bcm283x-rpi-i2c0mux_0_28.dtsi"
+
+/ {
+	compatible = "raspberrypi,compute-module", "brcm,bcm2835";
+	model = "Raspberry Pi Compute Module";
+
+	cam1_reg: cam1_reg {
+		compatible = "regulator-fixed";
+		regulator-name = "cam1-regulator";
+		gpio = <&gpio 2 GPIO_ACTIVE_HIGH>;
+		enable-active-high;
+		status = "disabled";
+	};
+	cam0_reg: cam0_reg {
+		compatible = "regulator-fixed";
+		regulator-name = "cam0-regulator";
+		gpio = <&gpio 30 GPIO_ACTIVE_HIGH>;
+		enable-active-high;
+		status = "disabled";
+	};
+};
+
+&uart0 {
+	status = "okay";
+};
+
+&gpio {
+	spi0_pins: spi0_pins {
+		brcm,pins = <9 10 11>;
+		brcm,function = <4>; /* alt0 */
+	};
+
+	spi0_cs_pins: spi0_cs_pins {
+		brcm,pins = <8 7>;
+		brcm,function = <1>; /* output */
+	};
+
+	i2c0_pins: i2c0 {
+		brcm,pins = <0 1>;
+		brcm,function = <4>;
+	};
+
+	i2c1_pins: i2c1 {
+		brcm,pins = <2 3>;
+		brcm,function = <4>;
+	};
+
+	i2s_pins: i2s {
+		brcm,pins = <18 19 20 21>;
+		brcm,function = <4>; /* alt0 */
+	};
+
+	audio_pins: audio_pins {
+		brcm,pins;
+		brcm,function;
+	};
+};
+
+&spi0 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&spi0_pins &spi0_cs_pins>;
+	cs-gpios = <&gpio 8 1>, <&gpio 7 1>;
+
+	spidev0: spidev@0{
+		compatible = "spidev";
+		reg = <0>;	/* CE0 */
+		#address-cells = <1>;
+		#size-cells = <0>;
+		spi-max-frequency = <125000000>;
+	};
+
+	spidev1: spidev@1{
+		compatible = "spidev";
+		reg = <1>;	/* CE1 */
+		#address-cells = <1>;
+		#size-cells = <0>;
+		spi-max-frequency = <125000000>;
+	};
+};
+
+&i2c0if {
+	clock-frequency = <100000>;
+};
+
+&i2c1 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&i2c1_pins>;
+	clock-frequency = <100000>;
+};
+
+&i2c2 {
+	clock-frequency = <100000>;
+};
+
+&i2s {
+	pinctrl-names = "default";
+	pinctrl-0 = <&i2s_pins>;
+};
+
+&audio {
+	pinctrl-names = "default";
+	pinctrl-0 = <&audio_pins>;
+};
+
+&hdmi {
+	hpd-gpios = <&gpio 46 GPIO_ACTIVE_HIGH>;
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/bcm2708-rpi-cm.dtsi linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708-rpi-cm.dtsi
--- linux-5.10.52-orig/arch/arm/boot/dts/bcm2708-rpi-cm.dtsi	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708-rpi-cm.dtsi	2021-07-25 16:45:34.748814264 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+#include "bcm2708.dtsi"
+#include "bcm2708-rpi.dtsi"
+
+&leds {
+	act_led: act {
+		label = "led0";
+		linux,default-trigger = "mmc0";
+		gpios = <&gpio 47 0>;
+	};
+};
+
+/ {
+	__overrides__ {
+		act_led_gpio = <&act_led>,"gpios:4";
+		act_led_activelow = <&act_led>,"gpios:8";
+		act_led_trigger = <&act_led>,"linux,default-trigger";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/bcm2708-rpi.dtsi linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708-rpi.dtsi
--- linux-5.10.52-orig/arch/arm/boot/dts/bcm2708-rpi.dtsi	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708-rpi.dtsi	2021-07-25 16:45:34.748814264 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/* Downstream modifications common to bcm2835, bcm2836, bcm2837 */
+
+#include "bcm2835-rpi.dtsi"
+#include "bcm270x-rpi.dtsi"
+
+/ {
+	memory@0 {
+		device_type = "memory";
+		reg = <0x0 0x0>;
+	};
+
+	aliases {
+		i2c2 = &i2c2;
+	};
+
+	__overrides__ {
+		i2c2_iknowwhatimdoing = <&i2c2>,"status";
+		i2c2_baudrate = <&i2c2>,"clock-frequency:0";
+		sd_poll_once = <&sdhost>,"non-removable?";
+	};
+};
+
+&sdhost {
+	pinctrl-names = "default";
+	pinctrl-0 = <&sdhost_gpio48>;
+	status = "okay";
+};
+
+&hdmi {
+	power-domains = <&power RPI_POWER_DOMAIN_HDMI>;
+	status = "disabled";
+};
+
+&i2c2 {
+	status = "disabled";
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/bcm2708-rpi-zero.dts linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708-rpi-zero.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/bcm2708-rpi-zero.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708-rpi-zero.dts	2021-07-25 16:45:34.748814264 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/dts-v1/;
+
+#include "bcm2708.dtsi"
+#include "bcm2708-rpi.dtsi"
+#include "bcm283x-rpi-csi1-2lane.dtsi"
+#include "bcm283x-rpi-i2c0mux_0_28.dtsi"
+#include "bcm283x-rpi-cam1-regulator.dtsi"
+
+/ {
+	compatible = "raspberrypi,model-zero", "brcm,bcm2835";
+	model = "Raspberry Pi Zero";
+
+	chosen {
+		bootargs = "coherent_pool=1M snd_bcm2835.enable_compat_alsa=0 snd_bcm2835.enable_hdmi=1";
+	};
+};
+
+&gpio {
+	spi0_pins: spi0_pins {
+		brcm,pins = <9 10 11>;
+		brcm,function = <4>; /* alt0 */
+	};
+
+	spi0_cs_pins: spi0_cs_pins {
+		brcm,pins = <8 7>;
+		brcm,function = <1>; /* output */
+	};
+
+	i2c0_pins: i2c0 {
+		brcm,pins = <0 1>;
+		brcm,function = <4>;
+	};
+
+	i2c1_pins: i2c1 {
+		brcm,pins = <2 3>;
+		brcm,function = <4>;
+	};
+
+	i2s_pins: i2s {
+		brcm,pins = <18 19 20 21>;
+		brcm,function = <4>; /* alt0 */
+	};
+
+	audio_pins: audio_pins {
+		brcm,pins = <>;
+		brcm,function = <>;
+	};
+};
+
+&uart0 {
+	status = "okay";
+};
+
+&spi0 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&spi0_pins &spi0_cs_pins>;
+	cs-gpios = <&gpio 8 1>, <&gpio 7 1>;
+
+	spidev0: spidev@0{
+		compatible = "spidev";
+		reg = <0>;	/* CE0 */
+		#address-cells = <1>;
+		#size-cells = <0>;
+		spi-max-frequency = <125000000>;
+	};
+
+	spidev1: spidev@1{
+		compatible = "spidev";
+		reg = <1>;	/* CE1 */
+		#address-cells = <1>;
+		#size-cells = <0>;
+		spi-max-frequency = <125000000>;
+	};
+};
+
+&i2c0if {
+	clock-frequency = <100000>;
+};
+
+&i2c1 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&i2c1_pins>;
+	clock-frequency = <100000>;
+};
+
+&i2c2 {
+	clock-frequency = <100000>;
+};
+
+&i2s {
+	pinctrl-names = "default";
+	pinctrl-0 = <&i2s_pins>;
+};
+
+&leds {
+	act_led: act {
+		label = "led0";
+		linux,default-trigger = "actpwr";
+		gpios = <&gpio 47 GPIO_ACTIVE_LOW>;
+	};
+};
+
+&hdmi {
+	hpd-gpios = <&gpio 46 GPIO_ACTIVE_LOW>;
+};
+
+&audio {
+	pinctrl-names = "default";
+	pinctrl-0 = <&audio_pins>;
+	brcm,disable-headphones = <1>;
+};
+
+&cam1_reg {
+	gpio = <&gpio 41 GPIO_ACTIVE_HIGH>;
+};
+
+/ {
+	__overrides__ {
+		act_led_gpio = <&act_led>,"gpios:4";
+		act_led_activelow = <&act_led>,"gpios:8";
+		act_led_trigger = <&act_led>,"linux,default-trigger";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/bcm2708-rpi-zero-w.dts linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708-rpi-zero-w.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/bcm2708-rpi-zero-w.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708-rpi-zero-w.dts	2021-07-25 16:45:34.748814264 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/dts-v1/;
+
+#include "bcm2708.dtsi"
+#include "bcm2708-rpi.dtsi"
+#include "bcm283x-rpi-csi1-2lane.dtsi"
+#include "bcm283x-rpi-i2c0mux_0_28.dtsi"
+#include "bcm2708-rpi-bt.dtsi"
+#include "bcm283x-rpi-cam1-regulator.dtsi"
+
+/ {
+	compatible = "raspberrypi,model-zero-w", "brcm,bcm2835";
+	model = "Raspberry Pi Zero W";
+
+	chosen {
+		bootargs = "coherent_pool=1M 8250.nr_uarts=1 snd_bcm2835.enable_compat_alsa=0 snd_bcm2835.enable_hdmi=1";
+	};
+
+	aliases {
+		serial0 = &uart1;
+		serial1 = &uart0;
+		mmc1 = &mmcnr;
+	};
+};
+
+&gpio {
+	spi0_pins: spi0_pins {
+		brcm,pins = <9 10 11>;
+		brcm,function = <4>; /* alt0 */
+	};
+
+	spi0_cs_pins: spi0_cs_pins {
+		brcm,pins = <8 7>;
+		brcm,function = <1>; /* output */
+	};
+
+	i2c0_pins: i2c0 {
+		brcm,pins = <0 1>;
+		brcm,function = <4>;
+	};
+
+	i2c1_pins: i2c1 {
+		brcm,pins = <2 3>;
+		brcm,function = <4>;
+	};
+
+	i2s_pins: i2s {
+		brcm,pins = <18 19 20 21>;
+		brcm,function = <4>; /* alt0 */
+	};
+
+	sdio_pins: sdio_pins {
+		brcm,pins = <34 35 36 37 38 39>;
+		brcm,function = <7>; /* ALT3 = SD1 */
+		brcm,pull = <0 2 2 2 2 2>;
+	};
+
+	bt_pins: bt_pins {
+		brcm,pins = <43>;
+		brcm,function = <4>; /* alt0:GPCLK2 */
+		brcm,pull = <0>; /* none */
+	};
+
+	uart0_pins: uart0_pins {
+		brcm,pins = <30 31 32 33>;
+		brcm,function = <7>; /* alt3=UART0 */
+		brcm,pull = <2 0 0 2>; /* up none none up */
+	};
+
+	uart1_pins: uart1_pins {
+		brcm,pins;
+		brcm,function;
+		brcm,pull;
+	};
+
+	audio_pins: audio_pins {
+		brcm,pins = <>;
+		brcm,function = <>;
+	};
+};
+
+&mmcnr {
+	pinctrl-names = "default";
+	pinctrl-0 = <&sdio_pins>;
+	bus-width = <4>;
+	status = "okay";
+};
+
+&uart0 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&uart0_pins &bt_pins>;
+	status = "okay";
+};
+
+&uart1 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&uart1_pins>;
+	status = "okay";
+};
+
+&spi0 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&spi0_pins &spi0_cs_pins>;
+	cs-gpios = <&gpio 8 1>, <&gpio 7 1>;
+
+	spidev0: spidev@0{
+		compatible = "spidev";
+		reg = <0>;	/* CE0 */
+		#address-cells = <1>;
+		#size-cells = <0>;
+		spi-max-frequency = <125000000>;
+	};
+
+	spidev1: spidev@1{
+		compatible = "spidev";
+		reg = <1>;	/* CE1 */
+		#address-cells = <1>;
+		#size-cells = <0>;
+		spi-max-frequency = <125000000>;
+	};
+};
+
+&i2c0if {
+	clock-frequency = <100000>;
+};
+
+&i2c1 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&i2c1_pins>;
+	clock-frequency = <100000>;
+};
+
+&i2c2 {
+	clock-frequency = <100000>;
+};
+
+&i2s {
+	pinctrl-names = "default";
+	pinctrl-0 = <&i2s_pins>;
+};
+
+&leds {
+	act_led: act {
+		label = "led0";
+		linux,default-trigger = "actpwr";
+		gpios = <&gpio 47 GPIO_ACTIVE_LOW>;
+	};
+};
+
+&hdmi {
+	hpd-gpios = <&gpio 46 GPIO_ACTIVE_LOW>;
+};
+
+&audio {
+	pinctrl-names = "default";
+	pinctrl-0 = <&audio_pins>;
+	brcm,disable-headphones = <1>;
+};
+
+&cam1_reg {
+	gpio = <&gpio 44 GPIO_ACTIVE_HIGH>;
+};
+
+/ {
+	__overrides__ {
+		act_led_gpio = <&act_led>,"gpios:4";
+		act_led_activelow = <&act_led>,"gpios:8";
+		act_led_trigger = <&act_led>,"linux,default-trigger";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/bcm2709.dtsi linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2709.dtsi
--- linux-5.10.52-orig/arch/arm/boot/dts/bcm2709.dtsi	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2709.dtsi	2021-07-25 16:45:34.748814264 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+#include "bcm2836.dtsi"
+#include "bcm270x.dtsi"
+
+/ {
+	soc {
+		ranges = <0x7e000000 0x3f000000 0x01000000>,
+		         <0x40000000 0x40000000 0x00040000>;
+
+		/delete-node/ timer@7e003000;
+	};
+
+	__overrides__ {
+		arm_freq = <&v7_cpu0>, "clock-frequency:0",
+			   <&v7_cpu1>, "clock-frequency:0",
+			   <&v7_cpu2>, "clock-frequency:0",
+			   <&v7_cpu3>, "clock-frequency:0";
+	};
+};
+
+&vc4 {
+	status = "disabled";
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/bcm2709-rpi-2-b.dts linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2709-rpi-2-b.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/bcm2709-rpi-2-b.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2709-rpi-2-b.dts	2021-07-25 16:45:34.748814264 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/dts-v1/;
+
+#include "bcm2709.dtsi"
+#include "bcm2709-rpi.dtsi"
+#include "bcm283x-rpi-smsc9514.dtsi"
+#include "bcm283x-rpi-csi1-2lane.dtsi"
+#include "bcm283x-rpi-i2c0mux_0_28.dtsi"
+#include "bcm283x-rpi-cam1-regulator.dtsi"
+
+/ {
+	compatible = "raspberrypi,2-model-b", "brcm,bcm2836";
+	model = "Raspberry Pi 2 Model B";
+};
+
+&gpio {
+	spi0_pins: spi0_pins {
+		brcm,pins = <9 10 11>;
+		brcm,function = <4>; /* alt0 */
+	};
+
+	spi0_cs_pins: spi0_cs_pins {
+		brcm,pins = <8 7>;
+		brcm,function = <1>; /* output */
+	};
+
+	i2c0_pins: i2c0 {
+		brcm,pins = <0 1>;
+		brcm,function = <4>;
+	};
+
+	i2c1_pins: i2c1 {
+		brcm,pins = <2 3>;
+		brcm,function = <4>;
+	};
+
+	i2s_pins: i2s {
+		brcm,pins = <18 19 20 21>;
+		brcm,function = <4>; /* alt0 */
+	};
+
+	audio_pins: audio_pins {
+		brcm,pins = <40 45>;
+		brcm,function = <4>;
+	};
+};
+
+&uart0 {
+	status = "okay";
+};
+
+&spi0 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&spi0_pins &spi0_cs_pins>;
+	cs-gpios = <&gpio 8 1>, <&gpio 7 1>;
+
+	spidev0: spidev@0{
+		compatible = "spidev";
+		reg = <0>;	/* CE0 */
+		#address-cells = <1>;
+		#size-cells = <0>;
+		spi-max-frequency = <125000000>;
+	};
+
+	spidev1: spidev@1{
+		compatible = "spidev";
+		reg = <1>;	/* CE1 */
+		#address-cells = <1>;
+		#size-cells = <0>;
+		spi-max-frequency = <125000000>;
+	};
+};
+
+&i2c0if {
+	clock-frequency = <100000>;
+};
+
+&i2c1 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&i2c1_pins>;
+	clock-frequency = <100000>;
+};
+
+&i2c2 {
+	clock-frequency = <100000>;
+};
+
+&i2s {
+	pinctrl-names = "default";
+	pinctrl-0 = <&i2s_pins>;
+};
+
+&leds {
+	act_led: act {
+		label = "led0";
+		linux,default-trigger = "mmc0";
+		gpios = <&gpio 47 0>;
+	};
+
+	pwr_led: pwr {
+		label = "led1";
+		linux,default-trigger = "input";
+		gpios = <&gpio 35 0>;
+	};
+};
+
+&hdmi {
+	hpd-gpios = <&gpio 46 GPIO_ACTIVE_LOW>;
+};
+
+&audio {
+	pinctrl-names = "default";
+	pinctrl-0 = <&audio_pins>;
+};
+
+&cam1_reg {
+	gpio = <&gpio 41 GPIO_ACTIVE_HIGH>;
+};
+
+/ {
+	__overrides__ {
+		act_led_gpio = <&act_led>,"gpios:4";
+		act_led_activelow = <&act_led>,"gpios:8";
+		act_led_trigger = <&act_led>,"linux,default-trigger";
+
+		pwr_led_gpio = <&pwr_led>,"gpios:4";
+		pwr_led_activelow = <&pwr_led>,"gpios:8";
+		pwr_led_trigger = <&pwr_led>,"linux,default-trigger";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/bcm2709-rpi.dtsi linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2709-rpi.dtsi
--- linux-5.10.52-orig/arch/arm/boot/dts/bcm2709-rpi.dtsi	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2709-rpi.dtsi	2021-07-25 16:45:34.748814264 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:2 @
+#include "bcm2708-rpi.dtsi"
+
+&vchiq {
+	compatible = "brcm,bcm2836-vchiq", "brcm,bcm2835-vchiq";
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/bcm270x.dtsi linux-5.10.52-v7l+/arch/arm/boot/dts/bcm270x.dtsi
--- linux-5.10.52-orig/arch/arm/boot/dts/bcm270x.dtsi	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm270x.dtsi	2021-07-25 16:45:34.748814264 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/* Downstream bcm283x.dtsi diff */
+#include <dt-bindings/power/raspberrypi-power.h>
+
+/ {
+	chosen {
+		bootargs = "coherent_pool=1M snd_bcm2835.enable_compat_alsa=0 snd_bcm2835.enable_hdmi=1";
+		/delete-property/ stdout-path;
+	};
+
+	soc: soc {
+
+		watchdog: watchdog@7e100000 {
+			/* Add label */
+		};
+
+		random: rng@7e104000 {
+			/* Add label */
+		};
+
+		spi0: spi@7e204000 {
+			/* Add label */
+		};
+
+#ifndef BCM2711
+		pixelvalve0: pixelvalve@7e206000 {
+			/* Add label */
+			status = "disabled";
+		};
+
+		pixelvalve1: pixelvalve@7e207000 {
+			/* Add label */
+			status = "disabled";
+		};
+#endif
+
+		/delete-node/ sdhci@7e300000;
+
+		sdhci: mmc: mmc@7e300000 {
+			compatible = "brcm,bcm2835-mmc", "brcm,bcm2835-sdhci";
+			reg = <0x7e300000 0x100>;
+			interrupts = <2 30>;
+			clocks = <&clocks BCM2835_CLOCK_EMMC>;
+			dmas = <&dma 11>;
+			dma-names = "rx-tx";
+			brcm,overclock-50 = <0>;
+			status = "disabled";
+		};
+
+		/* A clone of mmc but with non-removable set */
+		mmcnr: mmcnr@7e300000 {
+			compatible = "brcm,bcm2835-mmc", "brcm,bcm2835-sdhci";
+			reg = <0x7e300000 0x100>;
+			interrupts = <2 30>;
+			clocks = <&clocks BCM2835_CLOCK_EMMC>;
+			dmas = <&dma 11>;
+			dma-names = "rx-tx";
+			brcm,overclock-50 = <0>;
+			non-removable;
+			status = "disabled";
+		};
+
+		hvs: hvs@7e400000 {
+			/* Add label */
+			status = "disabled";
+		};
+
+		firmwarekms: firmwarekms@7e600000 {
+			compatible = "raspberrypi,rpi-firmware-kms";
+			/* SMI interrupt reg */
+			reg = <0x7e600000 0x100>;
+			interrupts = <2 16>;
+			brcm,firmware = <&firmware>;
+			status = "disabled";
+		};
+
+		smi: smi@7e600000 {
+			compatible = "brcm,bcm2835-smi";
+			reg = <0x7e600000 0x100>;
+			interrupts = <2 16>;
+			clocks = <&clocks BCM2835_CLOCK_SMI>;
+			assigned-clocks = <&clocks BCM2835_CLOCK_SMI>;
+			assigned-clock-rates = <125000000>;
+			dmas = <&dma 4>;
+			dma-names = "rx-tx";
+			status = "disabled";
+		};
+
+		csi0: csi@7e800000 {
+			compatible = "brcm,bcm2835-unicam";
+			reg = <0x7e800000 0x800>,
+			      <0x7e802000 0x4>;
+			interrupts = <2 6>;
+			clocks = <&clocks BCM2835_CLOCK_CAM0>,
+				 <&firmware_clocks 4>;
+			clock-names = "lp", "vpu";
+			power-domains = <&power RPI_POWER_DOMAIN_UNICAM0>;
+			#address-cells = <1>;
+			#size-cells = <0>;
+			#clock-cells = <1>;
+			status = "disabled";
+		};
+
+		csi1: csi@7e801000 {
+			compatible = "brcm,bcm2835-unicam";
+			reg = <0x7e801000 0x800>,
+			      <0x7e802004 0x4>;
+			interrupts = <2 7>;
+			clocks = <&clocks BCM2835_CLOCK_CAM1>,
+				 <&firmware_clocks 4>;
+			clock-names = "lp", "vpu";
+			power-domains = <&power RPI_POWER_DOMAIN_UNICAM1>;
+			#address-cells = <1>;
+			#size-cells = <0>;
+			#clock-cells = <1>;
+			status = "disabled";
+		};
+
+#ifndef BCM2711
+		pixelvalve2: pixelvalve@7e807000 {
+			/* Add label */
+			status = "disabled";
+		};
+#endif
+
+		hdmi@7e902000 { /* hdmi */
+			status = "disabled";
+		};
+
+		usb@7e980000 { /* usb */
+			compatible = "brcm,bcm2708-usb";
+			reg = <0x7e980000 0x10000>,
+			      <0x7e006000 0x1000>;
+			interrupt-names = "usb",
+					  "soft";
+			interrupts = <1 9>,
+				     <2 0>;
+		};
+
+#ifndef BCM2711
+		v3d@7ec00000 { /* vd3 */
+			compatible = "brcm,vc4-v3d";
+			power-domains = <&power RPI_POWER_DOMAIN_V3D>;
+			status = "disabled";
+		};
+#endif
+
+		axiperf: axiperf {
+			compatible = "brcm,bcm2835-axiperf";
+			reg = <0x7e009800 0x100>,
+			      <0x7ee08000 0x100>;
+			firmware = <&firmware>;
+			status = "disabled";
+		};
+	};
+
+	__overrides__ {
+		cam0-pwdn-ctrl;
+		cam0-pwdn;
+		cam0-led-ctrl;
+		cam0-led;
+	};
+};
+
+&gpio {
+	interrupts = <2 17>, <2 18>;
+
+	dpi_18bit_cpadhi_gpio0: dpi_18bit_cpadhi_gpio0 {
+		brcm,pins = <0 1 2 3 4 5 6 7 8 9
+			     12 13 14 15 16 17
+			     20 21 22 23 24 25>;
+		brcm,function = <BCM2835_FSEL_ALT2>;
+		brcm,pull = <0>; /* no pull */
+	};
+	dpi_18bit_cpadhi_gpio2: dpi_18bit_cpadhi_gpio2 {
+		brcm,pins = <2 3 4 5 6 7 8 9
+			     12 13 14 15 16 17
+			     20 21 22 23 24 25>;
+		brcm,function = <BCM2835_FSEL_ALT2>;
+	};
+	dpi_18bit_gpio0: dpi_18bit_gpio0 {
+		brcm,pins = <0 1 2 3 4 5 6 7 8 9 10 11
+			     12 13 14 15 16 17 18 19
+			     20 21>;
+		brcm,function = <BCM2835_FSEL_ALT2>;
+	};
+	dpi_18bit_gpio2: dpi_18bit_gpio2 {
+		brcm,pins = <2 3 4 5 6 7 8 9 10 11
+			     12 13 14 15 16 17 18 19
+			     20 21>;
+		brcm,function = <BCM2835_FSEL_ALT2>;
+	};
+};
+
+&uart0 {
+	/* Enable CTS bug workaround */
+	cts-event-workaround;
+};
+
+&i2s {
+	#sound-dai-cells = <0>;
+	dmas = <&dma 2>, <&dma 3>;
+	dma-names = "tx", "rx";
+};
+
+&sdhost {
+	dmas = <&dma (13|(1<<29))>;
+	dma-names = "rx-tx";
+	bus-width = <4>;
+	brcm,overclock-50 = <0>;
+	brcm,pio-limit = <1>;
+};
+
+&spi0 {
+	dmas = <&dma 6>, <&dma 7>;
+	dma-names = "tx", "rx";
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/bcm270x-rpi.dtsi linux-5.10.52-v7l+/arch/arm/boot/dts/bcm270x-rpi.dtsi
--- linux-5.10.52-orig/arch/arm/boot/dts/bcm270x-rpi.dtsi	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm270x-rpi.dtsi	2021-07-25 16:45:34.748814264 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/* Downstream modifications to bcm2835-rpi.dtsi */
+
+/ {
+	aliases {
+		audio = &audio;
+		aux = &aux;
+		sound = &sound;
+		soc = &soc;
+		dma = &dma;
+		intc = &intc;
+		watchdog = &watchdog;
+		random = &random;
+		mailbox = &mailbox;
+		gpio = &gpio;
+		uart0 = &uart0;
+		uart1 = &uart1;
+		sdhost = &sdhost;
+		mmc = &mmc;
+		mmc1 = &mmc;
+		mmc0 = &sdhost;
+		i2s = &i2s;
+		i2c0 = &i2c0;
+		i2c1 = &i2c1;
+		i2c10 = &i2c_csi_dsi;
+		spi0 = &spi0;
+		spi1 = &spi1;
+		spi2 = &spi2;
+		usb = &usb;
+		leds = &leds;
+		fb = &fb;
+		thermal = &thermal;
+		axiperf = &axiperf;
+	};
+
+	/* Define these notional regulators for use by overlays */
+	vdd_3v3_reg: fixedregulator_3v3 {
+		compatible = "regulator-fixed";
+		regulator-always-on;
+		regulator-max-microvolt = <3300000>;
+		regulator-min-microvolt = <3300000>;
+		regulator-name = "3v3";
+	};
+
+	vdd_5v0_reg: fixedregulator_5v0 {
+		compatible = "regulator-fixed";
+		regulator-always-on;
+		regulator-max-microvolt = <5000000>;
+		regulator-min-microvolt = <5000000>;
+		regulator-name = "5v0";
+	};
+
+	leds: leds {
+		compatible = "gpio-leds";
+	};
+
+	soc {
+		gpiomem {
+			compatible = "brcm,bcm2835-gpiomem";
+			reg = <0x7e200000 0x1000>;
+		};
+
+		fb: fb {
+			compatible = "brcm,bcm2708-fb";
+			firmware = <&firmware>;
+			status = "okay";
+		};
+
+		vcsm: vcsm {
+			compatible = "raspberrypi,bcm2835-vcsm";
+			firmware = <&firmware>;
+			status = "okay";
+		};
+
+		/* External sound card */
+		sound: sound {
+			status = "disabled";
+		};
+	};
+
+	__overrides__ {
+		cache_line_size;
+
+		uart0 = <&uart0>,"status";
+		uart1 = <&uart1>,"status";
+		i2s = <&i2s>,"status";
+		spi = <&spi0>,"status";
+		i2c0 = <&i2c0if>,"status",<&i2c0mux>,"status";
+		i2c1 = <&i2c1>,"status";
+		i2c0_baudrate = <&i2c0if>,"clock-frequency:0";
+		i2c1_baudrate = <&i2c1>,"clock-frequency:0";
+
+		audio = <&audio>,"status";
+		watchdog = <&watchdog>,"status";
+		random = <&random>,"status";
+		sd_overclock = <&sdhost>,"brcm,overclock-50:0";
+		sd_force_pio = <&sdhost>,"brcm,force-pio?";
+		sd_pio_limit = <&sdhost>,"brcm,pio-limit:0";
+		sd_debug     = <&sdhost>,"brcm,debug";
+		sdio_overclock = <&mmc>,"brcm,overclock-50:0",
+				 <&mmcnr>,"brcm,overclock-50:0";
+		axiperf      = <&axiperf>,"status";
+	};
+};
+
+&uart0 {
+	skip-init;
+};
+
+&uart1 {
+	skip-init;
+};
+
+&txp {
+	status = "disabled";
+};
+
+&i2c0if {
+	status = "disabled";
+};
+
+&i2c0mux {
+	status = "disabled";
+};
+
+&i2c1 {
+	status = "disabled";
+};
+
+&clocks {
+	firmware = <&firmware>;
+};
+
+&sdhci {
+	pinctrl-names = "default";
+	pinctrl-0 = <&emmc_gpio48>;
+	bus-width = <4>;
+};
+
+&cpu_thermal {
+	/delete-node/ trips;
+};
+
+&vec {
+	status = "disabled";
+};
+
+&vchiq {
+	/* Onboard audio */
+	audio: bcm2835_audio {
+		compatible = "brcm,bcm2835-audio";
+		brcm,pwm-channels = <8>;
+		status = "disabled";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/bcm2710.dtsi linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2710.dtsi
--- linux-5.10.52-orig/arch/arm/boot/dts/bcm2710.dtsi	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2710.dtsi	2021-07-25 16:45:34.748814264 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+#include "bcm2837.dtsi"
+#include "bcm270x.dtsi"
+
+/ {
+	compatible = "brcm,bcm2837", "brcm,bcm2836";
+
+	arm-pmu {
+		compatible = "arm,cortex-a53-pmu", "arm,cortex-a7-pmu";
+	};
+
+	soc {
+		/delete-node/ timer@7e003000;
+	};
+
+	__overrides__ {
+		arm_freq = <&cpu0>, "clock-frequency:0",
+		       <&cpu1>, "clock-frequency:0",
+		       <&cpu2>, "clock-frequency:0",
+		       <&cpu3>, "clock-frequency:0";
+	};
+};
+
+&vc4 {
+	status = "disabled";
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/bcm2710-rpi-2-b.dts linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2710-rpi-2-b.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/bcm2710-rpi-2-b.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2710-rpi-2-b.dts	2021-07-25 16:45:34.748814264 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/dts-v1/;
+
+#include "bcm2710.dtsi"
+#include "bcm2709-rpi.dtsi"
+#include "bcm283x-rpi-smsc9514.dtsi"
+#include "bcm283x-rpi-csi1-2lane.dtsi"
+#include "bcm283x-rpi-i2c0mux_0_28.dtsi"
+#include "bcm283x-rpi-cam1-regulator.dtsi"
+
+/ {
+	compatible = "raspberrypi,2-model-b-rev2", "brcm,bcm2837";
+	model = "Raspberry Pi 2 Model B rev 1.2";
+};
+
+&gpio {
+	spi0_pins: spi0_pins {
+		brcm,pins = <9 10 11>;
+		brcm,function = <4>; /* alt0 */
+	};
+
+	spi0_cs_pins: spi0_cs_pins {
+		brcm,pins = <8 7>;
+		brcm,function = <1>; /* output */
+	};
+
+	i2c0_pins: i2c0 {
+		brcm,pins = <0 1>;
+		brcm,function = <4>;
+	};
+
+	i2c1_pins: i2c1 {
+		brcm,pins = <2 3>;
+		brcm,function = <4>;
+	};
+
+	i2s_pins: i2s {
+		brcm,pins = <18 19 20 21>;
+		brcm,function = <4>; /* alt0 */
+	};
+
+	audio_pins: audio_pins {
+		brcm,pins = <40 45>;
+		brcm,function = <4>;
+	};
+};
+
+&uart0 {
+	status = "okay";
+};
+
+&spi0 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&spi0_pins &spi0_cs_pins>;
+	cs-gpios = <&gpio 8 1>, <&gpio 7 1>;
+
+	spidev0: spidev@0{
+		compatible = "spidev";
+		reg = <0>;	/* CE0 */
+		#address-cells = <1>;
+		#size-cells = <0>;
+		spi-max-frequency = <125000000>;
+	};
+
+	spidev1: spidev@1{
+		compatible = "spidev";
+		reg = <1>;	/* CE1 */
+		#address-cells = <1>;
+		#size-cells = <0>;
+		spi-max-frequency = <125000000>;
+	};
+};
+
+&i2c0if {
+	clock-frequency = <100000>;
+};
+
+&i2c1 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&i2c1_pins>;
+	clock-frequency = <100000>;
+};
+
+&i2c2 {
+	clock-frequency = <100000>;
+};
+
+&i2s {
+	pinctrl-names = "default";
+	pinctrl-0 = <&i2s_pins>;
+};
+
+&leds {
+	act_led: act {
+		label = "led0";
+		linux,default-trigger = "mmc0";
+		gpios = <&gpio 47 0>;
+	};
+
+	pwr_led: pwr {
+		label = "led1";
+		linux,default-trigger = "input";
+		gpios = <&gpio 35 0>;
+	};
+};
+
+&hdmi {
+	hpd-gpios = <&gpio 46 GPIO_ACTIVE_LOW>;
+};
+
+&audio {
+	pinctrl-names = "default";
+	pinctrl-0 = <&audio_pins>;
+};
+
+&cam1_reg {
+	gpio = <&gpio 41 GPIO_ACTIVE_HIGH>;
+};
+
+/ {
+	__overrides__ {
+		act_led_gpio = <&act_led>,"gpios:4";
+		act_led_activelow = <&act_led>,"gpios:8";
+		act_led_trigger = <&act_led>,"linux,default-trigger";
+
+		pwr_led_gpio = <&pwr_led>,"gpios:4";
+		pwr_led_activelow = <&pwr_led>,"gpios:8";
+		pwr_led_trigger = <&pwr_led>,"linux,default-trigger";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/bcm2710-rpi-3-b.dts linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2710-rpi-3-b.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/bcm2710-rpi-3-b.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2710-rpi-3-b.dts	2021-07-25 16:45:34.748814264 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/dts-v1/;
+
+#include "bcm2710.dtsi"
+#include "bcm2709-rpi.dtsi"
+#include "bcm283x-rpi-smsc9514.dtsi"
+#include "bcm283x-rpi-csi1-2lane.dtsi"
+#include "bcm283x-rpi-i2c0mux_0_44.dtsi"
+#include "bcm271x-rpi-bt.dtsi"
+#include "bcm283x-rpi-cam1-regulator.dtsi"
+
+/ {
+	compatible = "raspberrypi,3-model-b", "brcm,bcm2837";
+	model = "Raspberry Pi 3 Model B";
+
+	chosen {
+		bootargs = "coherent_pool=1M 8250.nr_uarts=1 snd_bcm2835.enable_compat_alsa=0 snd_bcm2835.enable_hdmi=1";
+	};
+
+	aliases {
+		serial0 = &uart1;
+		serial1 = &uart0;
+		mmc1 = &mmcnr;
+	};
+};
+
+&gpio {
+	spi0_pins: spi0_pins {
+		brcm,pins = <9 10 11>;
+		brcm,function = <4>; /* alt0 */
+	};
+
+	spi0_cs_pins: spi0_cs_pins {
+		brcm,pins = <8 7>;
+		brcm,function = <1>; /* output */
+	};
+
+	i2c0_pins: i2c0 {
+		brcm,pins = <0 1>;
+		brcm,function = <4>;
+	};
+
+	i2c1_pins: i2c1 {
+		brcm,pins = <2 3>;
+		brcm,function = <4>;
+	};
+
+	i2s_pins: i2s {
+		brcm,pins = <18 19 20 21>;
+		brcm,function = <4>; /* alt0 */
+	};
+
+	sdio_pins: sdio_pins {
+		brcm,pins =     <34 35 36 37 38 39>;
+		brcm,function = <7>; // alt3 = SD1
+		brcm,pull =     <0 2 2 2 2 2>;
+	};
+
+	bt_pins: bt_pins {
+		brcm,pins = <43>;
+		brcm,function = <4>; /* alt0:GPCLK2 */
+		brcm,pull = <0>;
+	};
+
+	uart0_pins: uart0_pins {
+		brcm,pins = <32 33>;
+		brcm,function = <7>; /* alt3=UART0 */
+		brcm,pull = <0 2>;
+	};
+
+	uart1_pins: uart1_pins {
+		brcm,pins;
+		brcm,function;
+		brcm,pull;
+	};
+
+	audio_pins: audio_pins {
+		brcm,pins = <40 41>;
+		brcm,function = <4>;
+	};
+};
+
+&mmcnr {
+	pinctrl-names = "default";
+	pinctrl-0 = <&sdio_pins>;
+	bus-width = <4>;
+	status = "okay";
+};
+
+&soc {
+	virtgpio: virtgpio {
+		compatible = "brcm,bcm2835-virtgpio";
+		gpio-controller;
+		#gpio-cells = <2>;
+		firmware = <&firmware>;
+		status = "okay";
+	};
+
+};
+
+&firmware {
+	expgpio: expgpio {
+		compatible = "raspberrypi,firmware-gpio";
+		gpio-controller;
+		#gpio-cells = <2>;
+		status = "okay";
+	};
+};
+
+&uart0 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&uart0_pins &bt_pins>;
+	status = "okay";
+};
+
+&uart1 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&uart1_pins>;
+	status = "okay";
+};
+
+&bt {
+	max-speed = <921600>;
+};
+
+&spi0 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&spi0_pins &spi0_cs_pins>;
+	cs-gpios = <&gpio 8 1>, <&gpio 7 1>;
+
+	spidev0: spidev@0{
+		compatible = "spidev";
+		reg = <0>;	/* CE0 */
+		#address-cells = <1>;
+		#size-cells = <0>;
+		spi-max-frequency = <125000000>;
+	};
+
+	spidev1: spidev@1{
+		compatible = "spidev";
+		reg = <1>;	/* CE1 */
+		#address-cells = <1>;
+		#size-cells = <0>;
+		spi-max-frequency = <125000000>;
+	};
+};
+
+&i2c0if {
+	clock-frequency = <100000>;
+};
+
+&i2c1 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&i2c1_pins>;
+	clock-frequency = <100000>;
+};
+
+&i2c2 {
+	clock-frequency = <100000>;
+};
+
+&i2s {
+	pinctrl-names = "default";
+	pinctrl-0 = <&i2s_pins>;
+};
+
+&leds {
+	act_led: act {
+		label = "led0";
+		linux,default-trigger = "mmc0";
+		gpios = <&virtgpio 0 0>;
+	};
+
+	pwr_led: pwr {
+		label = "led1";
+		linux,default-trigger = "input";
+		gpios = <&expgpio 7 0>;
+	};
+};
+
+&hdmi {
+	hpd-gpios = <&expgpio 4 GPIO_ACTIVE_LOW>;
+};
+
+&audio {
+	pinctrl-names = "default";
+	pinctrl-0 = <&audio_pins>;
+};
+
+&cam1_reg {
+	gpio = <&expgpio 5 GPIO_ACTIVE_HIGH>;
+};
+
+/ {
+	__overrides__ {
+		act_led_gpio = <&act_led>,"gpios:4";
+		act_led_activelow = <&act_led>,"gpios:8";
+		act_led_trigger = <&act_led>,"linux,default-trigger";
+
+		pwr_led_gpio = <&pwr_led>,"gpios:4";
+		pwr_led_activelow = <&pwr_led>,"gpios:8";
+		pwr_led_trigger = <&pwr_led>,"linux,default-trigger";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/bcm2710-rpi-3-b-plus.dts linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2710-rpi-3-b-plus.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/bcm2710-rpi-3-b-plus.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2710-rpi-3-b-plus.dts	2021-07-25 16:45:34.748814264 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/dts-v1/;
+
+#include "bcm2710.dtsi"
+#include "bcm2709-rpi.dtsi"
+#include "bcm283x-rpi-lan7515.dtsi"
+#include "bcm283x-rpi-csi1-2lane.dtsi"
+#include "bcm283x-rpi-i2c0mux_0_44.dtsi"
+#include "bcm271x-rpi-bt.dtsi"
+#include "bcm283x-rpi-cam1-regulator.dtsi"
+
+/ {
+	compatible = "raspberrypi,3-model-b-plus", "brcm,bcm2837";
+	model = "Raspberry Pi 3 Model B+";
+
+	chosen {
+		bootargs = "coherent_pool=1M 8250.nr_uarts=1 snd_bcm2835.enable_compat_alsa=0 snd_bcm2835.enable_hdmi=1";
+	};
+
+	aliases {
+		serial0 = &uart1;
+		serial1 = &uart0;
+		mmc1 = &mmcnr;
+	};
+};
+
+&gpio {
+	spi0_pins: spi0_pins {
+		brcm,pins = <9 10 11>;
+		brcm,function = <4>; /* alt0 */
+	};
+
+	spi0_cs_pins: spi0_cs_pins {
+		brcm,pins = <8 7>;
+		brcm,function = <1>; /* output */
+	};
+
+	i2c0_pins: i2c0 {
+		brcm,pins = <0 1>;
+		brcm,function = <4>;
+	};
+
+	i2c1_pins: i2c1 {
+		brcm,pins = <2 3>;
+		brcm,function = <4>;
+	};
+
+	i2s_pins: i2s {
+		brcm,pins = <18 19 20 21>;
+		brcm,function = <4>; /* alt0 */
+	};
+
+	sdio_pins: sdio_pins {
+		brcm,pins =     <34 35 36 37 38 39>;
+		brcm,function = <7>; // alt3 = SD1
+		brcm,pull =     <0 2 2 2 2 2>;
+	};
+
+	bt_pins: bt_pins {
+		brcm,pins = <43>;
+		brcm,function = <4>; /* alt0:GPCLK2 */
+		brcm,pull = <0>;
+	};
+
+	uart0_pins: uart0_pins {
+		brcm,pins = <32 33>;
+		brcm,function = <7>; /* alt3=UART0 */
+		brcm,pull = <0 2>;
+	};
+
+	uart1_pins: uart1_pins {
+		brcm,pins;
+		brcm,function;
+		brcm,pull;
+	};
+
+	audio_pins: audio_pins {
+		brcm,pins = <40 41>;
+		brcm,function = <4>;
+	};
+};
+
+&mmcnr {
+	pinctrl-names = "default";
+	pinctrl-0 = <&sdio_pins>;
+	bus-width = <4>;
+	status = "okay";
+};
+
+&firmware {
+	expgpio: expgpio {
+		compatible = "raspberrypi,firmware-gpio";
+		gpio-controller;
+		#gpio-cells = <2>;
+		status = "okay";
+	};
+};
+
+&uart0 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&uart0_pins &bt_pins>;
+	status = "okay";
+};
+
+&uart1 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&uart1_pins>;
+	status = "okay";
+};
+
+&spi0 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&spi0_pins &spi0_cs_pins>;
+	cs-gpios = <&gpio 8 1>, <&gpio 7 1>;
+
+	spidev0: spidev@0{
+		compatible = "spidev";
+		reg = <0>;	/* CE0 */
+		#address-cells = <1>;
+		#size-cells = <0>;
+		spi-max-frequency = <125000000>;
+	};
+
+	spidev1: spidev@1{
+		compatible = "spidev";
+		reg = <1>;	/* CE1 */
+		#address-cells = <1>;
+		#size-cells = <0>;
+		spi-max-frequency = <125000000>;
+	};
+};
+
+&i2c0if {
+	clock-frequency = <100000>;
+};
+
+&i2c1 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&i2c1_pins>;
+	clock-frequency = <100000>;
+};
+
+&i2c2 {
+	clock-frequency = <100000>;
+};
+
+&i2s {
+	pinctrl-names = "default";
+	pinctrl-0 = <&i2s_pins>;
+};
+
+&leds {
+	act_led: act {
+		label = "led0";
+		linux,default-trigger = "mmc0";
+		gpios = <&gpio 29 0>;
+	};
+
+	pwr_led: pwr {
+		label = "led1";
+		linux,default-trigger = "default-on";
+		gpios = <&expgpio 2 GPIO_ACTIVE_LOW>;
+	};
+};
+
+&hdmi {
+	hpd-gpios = <&gpio 28 GPIO_ACTIVE_LOW>;
+};
+
+&audio {
+	pinctrl-names = "default";
+	pinctrl-0 = <&audio_pins>;
+};
+
+&eth_phy {
+	microchip,eee-enabled;
+	microchip,tx-lpi-timer = <600>; /* non-aggressive*/
+	microchip,downshift-after = <2>;
+};
+
+&cam1_reg {
+	gpio = <&expgpio 5 GPIO_ACTIVE_HIGH>;
+};
+
+/ {
+	__overrides__ {
+		act_led_gpio = <&act_led>,"gpios:4";
+		act_led_activelow = <&act_led>,"gpios:8";
+		act_led_trigger = <&act_led>,"linux,default-trigger";
+
+		pwr_led_gpio = <&pwr_led>,"gpios:4";
+		pwr_led_activelow = <&pwr_led>,"gpios:8";
+		pwr_led_trigger = <&pwr_led>,"linux,default-trigger";
+
+		eee = <&eth_phy>,"microchip,eee-enabled?";
+		tx_lpi_timer = <&eth_phy>,"microchip,tx-lpi-timer:0";
+		eth_led0 = <&eth_phy>,"microchip,led-modes:0";
+		eth_led1 = <&eth_phy>,"microchip,led-modes:4";
+		eth_downshift_after = <&eth_phy>,"microchip,downshift-after:0";
+		eth_max_speed = <&eth_phy>,"max-speed:0";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/bcm2710-rpi-cm3.dts linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2710-rpi-cm3.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/bcm2710-rpi-cm3.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2710-rpi-cm3.dts	2021-07-25 16:45:34.748814264 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/dts-v1/;
+
+#include "bcm2710.dtsi"
+#include "bcm2709-rpi.dtsi"
+#include "bcm283x-rpi-csi0-2lane.dtsi"
+#include "bcm283x-rpi-csi1-4lane.dtsi"
+#include "bcm283x-rpi-i2c0mux_0_28.dtsi"
+/ {
+	compatible = "raspberrypi,3-compute-module", "brcm,bcm2837";
+	model = "Raspberry Pi Compute Module 3";
+
+	cam1_reg: cam1_reg {
+		compatible = "regulator-fixed";
+		regulator-name = "cam1-regulator";
+		gpio = <&gpio 2 GPIO_ACTIVE_HIGH>;
+		enable-active-high;
+		status = "disabled";
+	};
+	cam0_reg: cam0_reg {
+		compatible = "regulator-fixed";
+		regulator-name = "cam0-regulator";
+		gpio = <&gpio 30 GPIO_ACTIVE_HIGH>;
+		enable-active-high;
+		status = "disabled";
+	};
+};
+
+&uart0 {
+	status = "okay";
+};
+
+&gpio {
+	spi0_pins: spi0_pins {
+		brcm,pins = <9 10 11>;
+		brcm,function = <4>; /* alt0 */
+	};
+
+	spi0_cs_pins: spi0_cs_pins {
+		brcm,pins = <8 7>;
+		brcm,function = <1>; /* output */
+	};
+
+	i2c0_pins: i2c0 {
+		brcm,pins = <0 1>;
+		brcm,function = <4>;
+	};
+
+	i2c1_pins: i2c1 {
+		brcm,pins = <2 3>;
+		brcm,function = <4>;
+	};
+
+	i2s_pins: i2s {
+		brcm,pins = <18 19 20 21>;
+		brcm,function = <4>; /* alt0 */
+	};
+
+	audio_pins: audio_pins {
+		brcm,pins;
+		brcm,function;
+	};
+};
+
+&soc {
+	virtgpio: virtgpio {
+		compatible = "brcm,bcm2835-virtgpio";
+		gpio-controller;
+		#gpio-cells = <2>;
+		firmware = <&firmware>;
+		status = "okay";
+	};
+
+};
+
+&firmware {
+	expgpio: expgpio {
+		compatible = "raspberrypi,firmware-gpio";
+		gpio-controller;
+		#gpio-cells = <2>;
+		status = "okay";
+	};
+};
+
+&spi0 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&spi0_pins &spi0_cs_pins>;
+	cs-gpios = <&gpio 8 1>, <&gpio 7 1>;
+
+	spidev0: spidev@0{
+		compatible = "spidev";
+		reg = <0>;	/* CE0 */
+		#address-cells = <1>;
+		#size-cells = <0>;
+		spi-max-frequency = <125000000>;
+	};
+
+	spidev1: spidev@1{
+		compatible = "spidev";
+		reg = <1>;	/* CE1 */
+		#address-cells = <1>;
+		#size-cells = <0>;
+		spi-max-frequency = <125000000>;
+	};
+};
+
+&i2c0if {
+	clock-frequency = <100000>;
+};
+
+&i2c1 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&i2c1_pins>;
+	clock-frequency = <100000>;
+};
+
+&i2c2 {
+	clock-frequency = <100000>;
+};
+
+&i2s {
+	pinctrl-names = "default";
+	pinctrl-0 = <&i2s_pins>;
+};
+
+&leds {
+	act_led: act {
+		label = "led0";
+		linux,default-trigger = "mmc0";
+		gpios = <&virtgpio 0 0>;
+	};
+};
+
+&hdmi {
+	hpd-gpios = <&expgpio 0 GPIO_ACTIVE_LOW>;
+};
+
+&audio {
+	pinctrl-names = "default";
+	pinctrl-0 = <&audio_pins>;
+};
+
+/ {
+	__overrides__ {
+		act_led_gpio = <&act_led>,"gpios:4";
+		act_led_activelow = <&act_led>,"gpios:8";
+		act_led_trigger = <&act_led>,"linux,default-trigger";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/bcm2711.dtsi linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2711.dtsi
--- linux-5.10.52-orig/arch/arm/boot/dts/bcm2711.dtsi	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2711.dtsi	2021-07-25 16:45:34.748814264 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:303 @
 			status = "disabled";
 		};
 
+		vec: vec@7ec13000 {
+			compatible = "brcm,bcm2711-vec";
+			reg = <0x7ec13000 0x1000>;
+			clocks = <&clocks BCM2835_CLOCK_VEC>;
+			interrupts = <GIC_SPI 123 IRQ_TYPE_LEVEL_HIGH>;
+			status = "disabled";
+		};
+
 		dvp: clock@7ef00000 {
 			compatible = "brcm,brcm2711-dvp";
 			reg = <0x7ef00000 0x10>;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:319 @
 			#reset-cells = <1>;
 		};
 
+		aon_intr: interrupt-controller@7ef00100 {
+			compatible = "brcm,bcm2711-l2-intc", "brcm,l2-intc";
+			reg = <0x7ef00100 0x30>;
+			interrupts = <GIC_SPI 96 IRQ_TYPE_LEVEL_HIGH>;
+			interrupt-controller;
+			#interrupt-cells = <1>;
+			status = "disabled";
+		};
+
 		hdmi0: hdmi@7ef00700 {
 			compatible = "brcm,bcm2711-hdmi0";
 			reg = <0x7ef00700 0x300>,
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:338 @
 			      <0x7ef01f00 0x400>,
 			      <0x7ef00200 0x80>,
 			      <0x7ef04300 0x100>,
-			      <0x7ef20000 0x100>;
+			      <0x7ef20000 0x100>,
+			      <0x7ef00100 0x30>;
 			reg-names = "hdmi",
 				    "dvp",
 				    "phy",
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:348 @
 				    "metadata",
 				    "csc",
 				    "cec",
-				    "hd";
+				    "hd",
+				    "intr2";
+			clocks = <&firmware_clocks 13>,
+				 <&firmware_clocks 14>,
+				 <&dvp 0>,
+				 <&clk_27MHz>;
 			clock-names = "hdmi", "bvb", "audio", "cec";
 			resets = <&dvp 0>;
+			interrupt-parent = <&aon_intr>;
+			interrupts = <0>, <1>, <2>,
+				     <3>, <4>, <5>;
+			interrupt-names = "cec-tx", "cec-rx", "cec-low",
+					  "wakeup", "hpd-connected", "hpd-removed";
 			ddc = <&ddc0>;
-			dmas = <&dma 10>;
+			dmas = <&dma (10 | (1 << 27) | (1 << 24)| (15 << 20) | (10 << 16))>;
 			dma-names = "audio-rx";
 			status = "disabled";
 		};
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:385 @
 			      <0x7ef06f00 0x400>,
 			      <0x7ef00280 0x80>,
 			      <0x7ef09300 0x100>,
-			      <0x7ef20000 0x100>;
+			      <0x7ef20000 0x100>,
+			      <0x7ef00100 0x30>;
 			reg-names = "hdmi",
 				    "dvp",
 				    "phy",
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:395 @
 				    "metadata",
 				    "csc",
 				    "cec",
-				    "hd";
+				    "hd",
+				    "intr2";
 			ddc = <&ddc1>;
 			clock-names = "hdmi", "bvb", "audio", "cec";
+			clocks = <&firmware_clocks 13>,
+				 <&firmware_clocks 14>,
+				 <&dvp 0>,
+				 <&clk_27MHz>;
 			resets = <&dvp 1>;
-			dmas = <&dma 17>;
+			interrupt-parent = <&aon_intr>;
+			interrupts = <8>, <7>, <6>,
+				     <9>, <10>, <11>;
+			interrupt-names = "cec-tx", "cec-rx", "cec-low",
+					  "wakeup", "hpd-connected", "hpd-removed";
+			dmas = <&dma (17 | (1 << 27) | (1 << 24)| (15 << 20) | (10 << 16))>;
 			dma-names = "audio-rx";
 			status = "disabled";
 		};
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:510 @
 	scb {
 		compatible = "simple-bus";
 		#address-cells = <2>;
-		#size-cells = <1>;
+		#size-cells = <2>;
 
-		ranges = <0x0 0x7c000000  0x0 0xfc000000  0x03800000>,
-			 <0x6 0x00000000  0x6 0x00000000  0x40000000>;
+		ranges = <0x0 0x7c000000  0x0 0xfc000000  0x0 0x03800000>,
+			 <0x6 0x00000000  0x6 0x00000000  0x0 0x40000000>;
 
 		pcie0: pcie@7d500000 {
 			compatible = "brcm,bcm2711-pcie";
-			reg = <0x0 0x7d500000 0x9310>;
+			reg = <0x0 0x7d500000  0x0 0x9310>;
 			device_type = "pci";
 			#address-cells = <3>;
 			#interrupt-cells = <1>;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:531 @
 			msi-controller;
 			msi-parent = <&pcie0>;
 
-			ranges = <0x02000000 0x0 0xf8000000 0x6 0x00000000
-				  0x0 0x04000000>;
+			ranges = <0x02000000 0x0 0xc0000000 0x6 0x00000000
+				  0x0 0x40000000>;
 			/*
 			 * The wrapper around the PCIe block has a bug
 			 * preventing it from accessing beyond the first 3GB of
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:545 @
 
 		genet: ethernet@7d580000 {
 			compatible = "brcm,bcm2711-genet-v5";
-			reg = <0x0 0x7d580000 0x10000>;
+			reg = <0x0 0x7d580000  0x0 0x10000>;
 			#address-cells = <0x1>;
 			#size-cells = <0x1>;
 			interrupts = <GIC_SPI 157 IRQ_TYPE_LEVEL_HIGH>,
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:582 @
 
 &dsi1 {
 	interrupts = <GIC_SPI 108 IRQ_TYPE_LEVEL_HIGH>;
+	compatible = "brcm,bcm2711-dsi1";
 };
 
 &gpio {
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1053 @
 	alloc-ranges = <0x0 0x00000000 0x40000000>;
 };
 
-&i2c0 {
+&i2c0if {
 	compatible = "brcm,bcm2711-i2c", "brcm,bcm2835-i2c";
 	interrupts = <GIC_SPI 117 IRQ_TYPE_LEVEL_HIGH>;
 };
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1109 @
 &usb {
 	interrupts = <GIC_SPI 73 IRQ_TYPE_LEVEL_HIGH>;
 };
-
-&vec {
-	interrupts = <GIC_SPI 123 IRQ_TYPE_LEVEL_HIGH>;
-};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/bcm2711-rpi-400.dts linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2711-rpi-400.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/bcm2711-rpi-400.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2711-rpi-400.dts	2021-07-25 16:45:34.748814264 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// SPDX-License-Identifier: GPL-2.0
+/dts-v1/;
+#include "bcm2711.dtsi"
+#include "bcm2835-rpi.dtsi"
+
+#include <dt-bindings/reset/raspberrypi,firmware-reset.h>
+
+/ {
+	compatible = "raspberrypi,400", "brcm,bcm2711";
+	model = "Raspberry Pi 400";
+
+	chosen {
+		/* 8250 auxiliary UART instead of pl011 */
+		stdout-path = "serial1:115200n8";
+	};
+
+	/* Will be filled by the bootloader */
+	memory@0 {
+		device_type = "memory";
+		reg = <0 0 0>;
+	};
+
+	aliases {
+		emmc2bus = &emmc2bus;
+		ethernet0 = &genet;
+		pcie0 = &pcie0;
+		blconfig = &blconfig;
+	};
+
+	leds {
+		act {
+			gpios = <&gpio 42 GPIO_ACTIVE_HIGH>;
+		};
+
+		pwr {
+			label = "PWR";
+			gpios = <&expgpio 2 GPIO_ACTIVE_LOW>;
+			default-state = "keep";
+			linux,default-trigger = "default-on";
+		};
+	};
+
+	wifi_pwrseq: wifi-pwrseq {
+		compatible = "mmc-pwrseq-simple";
+		reset-gpios = <&expgpio 1 GPIO_ACTIVE_LOW>;
+	};
+
+	sd_io_1v8_reg: sd_io_1v8_reg {
+		compatible = "regulator-gpio";
+		regulator-name = "vdd-sd-io";
+		regulator-min-microvolt = <1800000>;
+		regulator-max-microvolt = <3300000>;
+		regulator-boot-on;
+		regulator-always-on;
+		regulator-settling-time-us = <5000>;
+		gpios = <&expgpio 4 GPIO_ACTIVE_HIGH>;
+		states = <1800000 0x1
+			  3300000 0x0>;
+		status = "okay";
+	};
+
+	sd_vcc_reg: sd_vcc_reg {
+		compatible = "regulator-fixed";
+		regulator-name = "vcc-sd";
+		regulator-min-microvolt = <3300000>;
+		regulator-max-microvolt = <3300000>;
+		regulator-boot-on;
+		enable-active-high;
+		gpio = <&expgpio 6 GPIO_ACTIVE_HIGH>;
+	};
+};
+
+&ddc0 {
+	status = "okay";
+};
+
+&ddc1 {
+	status = "okay";
+};
+
+&firmware {
+	firmware_clocks: clocks {
+		compatible = "raspberrypi,firmware-clocks";
+		#clock-cells = <1>;
+	};
+
+	expgpio: gpio {
+		compatible = "raspberrypi,firmware-gpio";
+		gpio-controller;
+		#gpio-cells = <2>;
+		gpio-line-names = "BT_ON",
+				  "WL_ON",
+				  "PWR_LED_OFF",
+				  "GLOBAL_RESET",
+				  "VDD_SD_IO_SEL",
+				  "CAM_GPIO",
+				  "SD_PWR_ON",
+				  "SD_OC_N";
+		status = "okay";
+	};
+
+	reset: reset {
+		compatible = "raspberrypi,firmware-reset";
+		#reset-cells = <1>;
+	};
+};
+
+&gpio {
+	/*
+	 * Parts taken from rpi_SCH_4b_4p0_reduced.pdf and
+	 * the official GPU firmware DT blob.
+	 *
+	 * Legend:
+	 * "FOO" = GPIO line named "FOO" on the schematic
+	 * "FOO_N" = GPIO line named "FOO" on schematic, active low
+	 */
+	gpio-line-names = "ID_SDA",
+			  "ID_SCL",
+			  "SDA1",
+			  "SCL1",
+			  "GPIO_GCLK",
+			  "GPIO5",
+			  "GPIO6",
+			  "SPI_CE1_N",
+			  "SPI_CE0_N",
+			  "SPI_MISO",
+			  "SPI_MOSI",
+			  "SPI_SCLK",
+			  "GPIO12",
+			  "GPIO13",
+			  /* Serial port */
+			  "TXD1",
+			  "RXD1",
+			  "GPIO16",
+			  "GPIO17",
+			  "GPIO18",
+			  "GPIO19",
+			  "GPIO20",
+			  "GPIO21",
+			  "GPIO22",
+			  "GPIO23",
+			  "GPIO24",
+			  "GPIO25",
+			  "GPIO26",
+			  "GPIO27",
+			  "RGMII_MDIO",
+			  "RGMIO_MDC",
+			  /* Used by BT module */
+			  "CTS0",
+			  "RTS0",
+			  "TXD0",
+			  "RXD0",
+			  /* Used by Wifi */
+			  "SD1_CLK",
+			  "SD1_CMD",
+			  "SD1_DATA0",
+			  "SD1_DATA1",
+			  "SD1_DATA2",
+			  "SD1_DATA3",
+			  /* Shared with SPI flash */
+			  "PWM0_MISO",
+			  "PWM1_MOSI",
+			  "STATUS_LED_G_CLK",
+			  "SPIFLASH_CE_N",
+			  "SDA0",
+			  "SCL0",
+			  "RGMII_RXCLK",
+			  "RGMII_RXCTL",
+			  "RGMII_RXD0",
+			  "RGMII_RXD1",
+			  "RGMII_RXD2",
+			  "RGMII_RXD3",
+			  "RGMII_TXCLK",
+			  "RGMII_TXCTL",
+			  "RGMII_TXD0",
+			  "RGMII_TXD1",
+			  "RGMII_TXD2",
+			  "RGMII_TXD3";
+};
+
+&hdmi0 {
+	clocks = <&firmware_clocks 13>, <&firmware_clocks 14>, <&dvp 0>, <&clk_27MHz>;
+	clock-names = "hdmi", "bvb", "audio", "cec";
+	status = "okay";
+};
+
+&hdmi1 {
+	clocks = <&firmware_clocks 13>, <&firmware_clocks 14>, <&dvp 1>, <&clk_27MHz>;
+	clock-names = "hdmi", "bvb", "audio", "cec";
+	status = "okay";
+};
+
+&hvs {
+	clocks = <&firmware_clocks 4>;
+};
+
+&pixelvalve0 {
+	status = "okay";
+};
+
+&pixelvalve1 {
+	status = "okay";
+};
+
+&pixelvalve2 {
+	status = "okay";
+};
+
+&pixelvalve4 {
+	status = "okay";
+};
+
+&pwm1 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&pwm1_0_gpio40 &pwm1_1_gpio41>;
+	status = "okay";
+};
+
+&rmem {
+	/*
+	 * RPi4's co-processor will copy the board's bootloader configuration
+	 * into memory for the OS to consume. It'll also update this node with
+	 * its placement information.
+	 */
+	blconfig: nvram@0 {
+		compatible = "raspberrypi,bootloader-config", "nvmem-rmem";
+		#address-cells = <1>;
+		#size-cells = <1>;
+		reg = <0x0 0x0 0x0>;
+		no-map;
+		status = "disabled";
+	};
+};
+
+/* SDHCI is used to control the SDIO for wireless */
+&sdhci {
+	#address-cells = <1>;
+	#size-cells = <0>;
+	pinctrl-names = "default";
+	pinctrl-0 = <&emmc_gpio34>;
+	bus-width = <4>;
+	non-removable;
+	mmc-pwrseq = <&wifi_pwrseq>;
+	status = "okay";
+
+	brcmf: wifi@1 {
+		reg = <1>;
+		compatible = "brcm,bcm4329-fmac";
+	};
+};
+
+/* EMMC2 is used to drive the SD card */
+&emmc2 {
+	vqmmc-supply = <&sd_io_1v8_reg>;
+	vmmc-supply = <&sd_vcc_reg>;
+	broken-cd;
+	status = "okay";
+};
+
+&genet {
+	phy-handle = <&phy1>;
+	phy-mode = "rgmii-rxid";
+	status = "okay";
+};
+
+&genet_mdio {
+	phy1: ethernet-phy@1 {
+		/* No PHY interrupt */
+		reg = <0x1>;
+	};
+};
+
+&pcie0 {
+	pci@1,0 {
+		#address-cells = <3>;
+		#size-cells = <2>;
+		ranges;
+
+		reg = <0 0 0 0 0>;
+
+		usb@1,0 {
+			reg = <0x10000 0 0 0 0>;
+			resets = <&reset RASPBERRYPI_FIRMWARE_RESET_ID_USB>;
+		};
+	};
+};
+
+/* uart0 communicates with the BT module */
+&uart0 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&uart0_ctsrts_gpio30 &uart0_gpio32>;
+	uart-has-rtscts;
+	status = "okay";
+
+	bluetooth {
+		compatible = "brcm,bcm43438-bt";
+		max-speed = <2000000>;
+		shutdown-gpios = <&expgpio 0 GPIO_ACTIVE_HIGH>;
+	};
+};
+
+/* uart1 is mapped to the pin header */
+&uart1 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&uart1_gpio14>;
+	status = "okay";
+};
+
+&vchiq {
+	interrupts = <GIC_SPI 34 IRQ_TYPE_LEVEL_HIGH>;
+};
+
+&vc4 {
+	status = "okay";
+};
+
+&vec {
+	status = "disabled";
+};
+
+// =============================================
+// Downstream rpi- changes
+
+#define BCM2711
+
+#include "bcm270x.dtsi"
+#include "bcm271x-rpi-bt.dtsi"
+
+/ {
+	soc {
+		/delete-node/ pixelvalve@7e807000;
+		/delete-node/ hdmi@7e902000;
+	};
+};
+
+#include "bcm2711-rpi.dtsi"
+#include "bcm283x-rpi-csi1-2lane.dtsi"
+#include "bcm283x-rpi-i2c0mux_0_44.dtsi"
+
+/ {
+	chosen {
+		bootargs = "coherent_pool=1M 8250.nr_uarts=1 snd_bcm2835.enable_compat_alsa=0 snd_bcm2835.enable_hdmi=1";
+	};
+
+	aliases {
+		serial0 = &uart1;
+		serial1 = &uart0;
+		mmc0 = &emmc2;
+		mmc1 = &mmcnr;
+		mmc2 = &sdhost;
+		i2c3 = &i2c3;
+		i2c4 = &i2c4;
+		i2c5 = &i2c5;
+		i2c6 = &i2c6;
+		i2c20 = &ddc0;
+		i2c21 = &ddc1;
+		spi3 = &spi3;
+		spi4 = &spi4;
+		spi5 = &spi5;
+		spi6 = &spi6;
+		/delete-property/ intc;
+	};
+
+	/delete-node/ wifi-pwrseq;
+};
+
+&mmcnr {
+	pinctrl-names = "default";
+	pinctrl-0 = <&sdio_pins>;
+	bus-width = <4>;
+	status = "okay";
+};
+
+&uart0 {
+	pinctrl-0 = <&uart0_pins &bt_pins>;
+	status = "okay";
+};
+
+&uart1 {
+	pinctrl-0 = <&uart1_pins>;
+};
+
+&spi0 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&spi0_pins &spi0_cs_pins>;
+	cs-gpios = <&gpio 8 1>, <&gpio 7 1>;
+
+	spidev0: spidev@0{
+		compatible = "spidev";
+		reg = <0>;	/* CE0 */
+		#address-cells = <1>;
+		#size-cells = <0>;
+		spi-max-frequency = <125000000>;
+	};
+
+	spidev1: spidev@1{
+		compatible = "spidev";
+		reg = <1>;	/* CE1 */
+		#address-cells = <1>;
+		#size-cells = <0>;
+		spi-max-frequency = <125000000>;
+	};
+};
+
+&gpio {
+	spi0_pins: spi0_pins {
+		brcm,pins = <9 10 11>;
+		brcm,function = <BCM2835_FSEL_ALT0>;
+	};
+
+	spi0_cs_pins: spi0_cs_pins {
+		brcm,pins = <8 7>;
+		brcm,function = <BCM2835_FSEL_GPIO_OUT>;
+	};
+
+	spi3_pins: spi3_pins {
+		brcm,pins = <1 2 3>;
+		brcm,function = <BCM2835_FSEL_ALT3>;
+	};
+
+	spi3_cs_pins: spi3_cs_pins {
+		brcm,pins = <0 24>;
+		brcm,function = <BCM2835_FSEL_GPIO_OUT>;
+	};
+
+	spi4_pins: spi4_pins {
+		brcm,pins = <5 6 7>;
+		brcm,function = <BCM2835_FSEL_ALT3>;
+	};
+
+	spi4_cs_pins: spi4_cs_pins {
+		brcm,pins = <4 25>;
+		brcm,function = <BCM2835_FSEL_GPIO_OUT>;
+	};
+
+	spi5_pins: spi5_pins {
+		brcm,pins = <13 14 15>;
+		brcm,function = <BCM2835_FSEL_ALT3>;
+	};
+
+	spi5_cs_pins: spi5_cs_pins {
+		brcm,pins = <12 26>;
+		brcm,function = <BCM2835_FSEL_GPIO_OUT>;
+	};
+
+	spi6_pins: spi6_pins {
+		brcm,pins = <19 20 21>;
+		brcm,function = <BCM2835_FSEL_ALT3>;
+	};
+
+	spi6_cs_pins: spi6_cs_pins {
+		brcm,pins = <18 27>;
+		brcm,function = <BCM2835_FSEL_GPIO_OUT>;
+	};
+
+	i2c0_pins: i2c0 {
+		brcm,pins = <0 1>;
+		brcm,function = <BCM2835_FSEL_ALT0>;
+		brcm,pull = <BCM2835_PUD_UP>;
+	};
+
+	i2c1_pins: i2c1 {
+		brcm,pins = <2 3>;
+		brcm,function = <BCM2835_FSEL_ALT0>;
+		brcm,pull = <BCM2835_PUD_UP>;
+	};
+
+	i2c3_pins: i2c3 {
+		brcm,pins = <4 5>;
+		brcm,function = <BCM2835_FSEL_ALT5>;
+		brcm,pull = <BCM2835_PUD_UP>;
+	};
+
+	i2c4_pins: i2c4 {
+		brcm,pins = <8 9>;
+		brcm,function = <BCM2835_FSEL_ALT5>;
+		brcm,pull = <BCM2835_PUD_UP>;
+	};
+
+	i2c5_pins: i2c5 {
+		brcm,pins = <12 13>;
+		brcm,function = <BCM2835_FSEL_ALT5>;
+		brcm,pull = <BCM2835_PUD_UP>;
+	};
+
+	i2c6_pins: i2c6 {
+		brcm,pins = <22 23>;
+		brcm,function = <BCM2835_FSEL_ALT5>;
+		brcm,pull = <BCM2835_PUD_UP>;
+	};
+
+	i2s_pins: i2s {
+		brcm,pins = <18 19 20 21>;
+		brcm,function = <BCM2835_FSEL_ALT0>;
+	};
+
+	sdio_pins: sdio_pins {
+		brcm,pins =     <34 35 36 37 38 39>;
+		brcm,function = <BCM2835_FSEL_ALT3>; // alt3 = SD1
+		brcm,pull =     <0 2 2 2 2 2>;
+	};
+
+	bt_pins: bt_pins {
+		brcm,pins = "-"; // non-empty to keep btuart happy, //4 = 0
+				 // to fool pinctrl
+		brcm,function = <0>;
+		brcm,pull = <2>;
+	};
+
+	uart0_pins: uart0_pins {
+		brcm,pins = <32 33>;
+		brcm,function = <BCM2835_FSEL_ALT3>;
+		brcm,pull = <0 2>;
+	};
+
+	uart1_pins: uart1_pins {
+		brcm,pins;
+		brcm,function;
+		brcm,pull;
+	};
+
+	uart2_pins: uart2_pins {
+		brcm,pins = <0 1>;
+		brcm,function = <BCM2835_FSEL_ALT4>;
+		brcm,pull = <0 2>;
+	};
+
+	uart3_pins: uart3_pins {
+		brcm,pins = <4 5>;
+		brcm,function = <BCM2835_FSEL_ALT4>;
+		brcm,pull = <0 2>;
+	};
+
+	uart4_pins: uart4_pins {
+		brcm,pins = <8 9>;
+		brcm,function = <BCM2835_FSEL_ALT4>;
+		brcm,pull = <0 2>;
+	};
+
+	uart5_pins: uart5_pins {
+		brcm,pins = <12 13>;
+		brcm,function = <BCM2835_FSEL_ALT4>;
+		brcm,pull = <0 2>;
+	};
+};
+
+&i2c0if {
+	clock-frequency = <100000>;
+};
+
+&i2c1 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&i2c1_pins>;
+	clock-frequency = <100000>;
+};
+
+&i2s {
+	pinctrl-names = "default";
+	pinctrl-0 = <&i2s_pins>;
+};
+
+// =============================================
+// Board specific stuff here
+
+/ {
+	power_ctrl: power_ctrl {
+		compatible = "gpio-poweroff";
+		gpios = <&expgpio 5 0>;
+		force;
+	};
+};
+
+&sdhost {
+	status = "disabled";
+};
+
+&phy1 {
+	led-modes = <0x00 0x08>; /* link/activity link */
+};
+
+&gpio {
+	audio_pins: audio_pins {
+		brcm,pins = <>;
+		brcm,function = <>;
+	};
+};
+
+&leds {
+	act_led: act {
+		label = "led0";
+		linux,default-trigger = "default-on";
+		default-state = "on";
+		gpios = <&gpio 42 GPIO_ACTIVE_HIGH>;
+	};
+
+	pwr_led: pwr {
+		label = "led1";
+		linux,default-trigger = "default-on";
+		gpios = <&expgpio 2 GPIO_ACTIVE_LOW>;
+	};
+};
+
+&pwm1 {
+	status = "disabled";
+};
+
+&audio {
+	pinctrl-names = "default";
+	pinctrl-0 = <&audio_pins>;
+	brcm,disable-headphones = <1>;
+};
+
+&genet_mdio {
+	clock-frequency = <1950000>;
+};
+
+/ {
+	__overrides__ {
+		act_led_gpio = <&act_led>,"gpios:4";
+		act_led_activelow = <&act_led>,"gpios:8";
+		act_led_trigger = <&act_led>,"linux,default-trigger";
+
+		pwr_led_gpio = <&pwr_led>,"gpios:4";
+		pwr_led_activelow = <&pwr_led>,"gpios:8";
+		pwr_led_trigger = <&pwr_led>,"linux,default-trigger";
+
+		eth_led0 = <&phy1>,"led-modes:0";
+		eth_led1 = <&phy1>,"led-modes:4";
+
+		sd_poll_once = <&emmc2>, "non-removable?";
+		spi_dma4 = <&spi0>, "dmas:0=", <&dma40>,
+			   <&spi0>, "dmas:8=", <&dma40>;
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/bcm2711-rpi-4-b.dts linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2711-rpi-4-b.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/bcm2711-rpi-4-b.dts	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2711-rpi-4-b.dts	2021-07-25 16:45:34.748814264 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:5 @
 /dts-v1/;
 #include "bcm2711.dtsi"
 #include "bcm2835-rpi.dtsi"
-#include "bcm283x-rpi-usb-peripheral.dtsi"
 
 #include <dt-bindings/reset/raspberrypi,firmware-reset.h>
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:27 @
 		emmc2bus = &emmc2bus;
 		ethernet0 = &genet;
 		pcie0 = &pcie0;
+		blconfig = &blconfig;
 	};
 
 	leds {
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:98 @
 				  "VDD_SD_IO_SEL",
 				  "CAM_GPIO",
 				  "SD_PWR_ON",
-				  "";
+				  "SD_OC_N";
 		status = "okay";
 	};
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:219 @
 	status = "okay";
 };
 
+&rmem {
+	/*
+	 * RPi4's co-processor will copy the board's bootloader configuration
+	 * into memory for the OS to consume. It'll also update this node with
+	 * its placement information.
+	 */
+	blconfig: nvram@0 {
+		compatible = "raspberrypi,bootloader-config", "nvmem-rmem";
+		#address-cells = <1>;
+		#size-cells = <1>;
+		reg = <0x0 0x0 0x0>;
+		no-map;
+		status = "disabled";
+	};
+};
+
 /* SDHCI is used to control the SDIO for wireless */
 &sdhci {
 	#address-cells = <1>;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:320 @
 &vec {
 	status = "disabled";
 };
+
+// =============================================
+// Downstream rpi- changes
+
+#define BCM2711
+
+#include "bcm270x.dtsi"
+#include "bcm271x-rpi-bt.dtsi"
+
+/ {
+	soc {
+		/delete-node/ pixelvalve@7e807000;
+		/delete-node/ hdmi@7e902000;
+	};
+};
+
+#include "bcm2711-rpi.dtsi"
+#include "bcm283x-rpi-csi1-2lane.dtsi"
+#include "bcm283x-rpi-i2c0mux_0_44.dtsi"
+#include "bcm283x-rpi-cam1-regulator.dtsi"
+
+/ {
+	chosen {
+		bootargs = "coherent_pool=1M 8250.nr_uarts=1 snd_bcm2835.enable_compat_alsa=0 snd_bcm2835.enable_hdmi=1";
+	};
+
+	aliases {
+		serial0 = &uart1;
+		serial1 = &uart0;
+		mmc0 = &emmc2;
+		mmc1 = &mmcnr;
+		mmc2 = &sdhost;
+		i2c3 = &i2c3;
+		i2c4 = &i2c4;
+		i2c5 = &i2c5;
+		i2c6 = &i2c6;
+		i2c20 = &ddc0;
+		i2c21 = &ddc1;
+		spi3 = &spi3;
+		spi4 = &spi4;
+		spi5 = &spi5;
+		spi6 = &spi6;
+		/delete-property/ intc;
+	};
+
+	/delete-node/ wifi-pwrseq;
+};
+
+&mmcnr {
+	pinctrl-names = "default";
+	pinctrl-0 = <&sdio_pins>;
+	bus-width = <4>;
+	status = "okay";
+};
+
+&uart0 {
+	pinctrl-0 = <&uart0_pins &bt_pins>;
+	status = "okay";
+};
+
+&uart1 {
+	pinctrl-0 = <&uart1_pins>;
+};
+
+&spi0 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&spi0_pins &spi0_cs_pins>;
+	cs-gpios = <&gpio 8 1>, <&gpio 7 1>;
+
+	spidev0: spidev@0{
+		compatible = "spidev";
+		reg = <0>;	/* CE0 */
+		#address-cells = <1>;
+		#size-cells = <0>;
+		spi-max-frequency = <125000000>;
+	};
+
+	spidev1: spidev@1{
+		compatible = "spidev";
+		reg = <1>;	/* CE1 */
+		#address-cells = <1>;
+		#size-cells = <0>;
+		spi-max-frequency = <125000000>;
+	};
+};
+
+&gpio {
+	spi0_pins: spi0_pins {
+		brcm,pins = <9 10 11>;
+		brcm,function = <BCM2835_FSEL_ALT0>;
+	};
+
+	spi0_cs_pins: spi0_cs_pins {
+		brcm,pins = <8 7>;
+		brcm,function = <BCM2835_FSEL_GPIO_OUT>;
+	};
+
+	spi3_pins: spi3_pins {
+		brcm,pins = <1 2 3>;
+		brcm,function = <BCM2835_FSEL_ALT3>;
+	};
+
+	spi3_cs_pins: spi3_cs_pins {
+		brcm,pins = <0 24>;
+		brcm,function = <BCM2835_FSEL_GPIO_OUT>;
+	};
+
+	spi4_pins: spi4_pins {
+		brcm,pins = <5 6 7>;
+		brcm,function = <BCM2835_FSEL_ALT3>;
+	};
+
+	spi4_cs_pins: spi4_cs_pins {
+		brcm,pins = <4 25>;
+		brcm,function = <BCM2835_FSEL_GPIO_OUT>;
+	};
+
+	spi5_pins: spi5_pins {
+		brcm,pins = <13 14 15>;
+		brcm,function = <BCM2835_FSEL_ALT3>;
+	};
+
+	spi5_cs_pins: spi5_cs_pins {
+		brcm,pins = <12 26>;
+		brcm,function = <BCM2835_FSEL_GPIO_OUT>;
+	};
+
+	spi6_pins: spi6_pins {
+		brcm,pins = <19 20 21>;
+		brcm,function = <BCM2835_FSEL_ALT3>;
+	};
+
+	spi6_cs_pins: spi6_cs_pins {
+		brcm,pins = <18 27>;
+		brcm,function = <BCM2835_FSEL_GPIO_OUT>;
+	};
+
+	i2c0_pins: i2c0 {
+		brcm,pins = <0 1>;
+		brcm,function = <BCM2835_FSEL_ALT0>;
+		brcm,pull = <BCM2835_PUD_UP>;
+	};
+
+	i2c1_pins: i2c1 {
+		brcm,pins = <2 3>;
+		brcm,function = <BCM2835_FSEL_ALT0>;
+		brcm,pull = <BCM2835_PUD_UP>;
+	};
+
+	i2c3_pins: i2c3 {
+		brcm,pins = <4 5>;
+		brcm,function = <BCM2835_FSEL_ALT5>;
+		brcm,pull = <BCM2835_PUD_UP>;
+	};
+
+	i2c4_pins: i2c4 {
+		brcm,pins = <8 9>;
+		brcm,function = <BCM2835_FSEL_ALT5>;
+		brcm,pull = <BCM2835_PUD_UP>;
+	};
+
+	i2c5_pins: i2c5 {
+		brcm,pins = <12 13>;
+		brcm,function = <BCM2835_FSEL_ALT5>;
+		brcm,pull = <BCM2835_PUD_UP>;
+	};
+
+	i2c6_pins: i2c6 {
+		brcm,pins = <22 23>;
+		brcm,function = <BCM2835_FSEL_ALT5>;
+		brcm,pull = <BCM2835_PUD_UP>;
+	};
+
+	i2s_pins: i2s {
+		brcm,pins = <18 19 20 21>;
+		brcm,function = <BCM2835_FSEL_ALT0>;
+	};
+
+	sdio_pins: sdio_pins {
+		brcm,pins =     <34 35 36 37 38 39>;
+		brcm,function = <BCM2835_FSEL_ALT3>; // alt3 = SD1
+		brcm,pull =     <0 2 2 2 2 2>;
+	};
+
+	bt_pins: bt_pins {
+		brcm,pins = "-"; // non-empty to keep btuart happy, //4 = 0
+				 // to fool pinctrl
+		brcm,function = <0>;
+		brcm,pull = <2>;
+	};
+
+	uart0_pins: uart0_pins {
+		brcm,pins = <32 33>;
+		brcm,function = <BCM2835_FSEL_ALT3>;
+		brcm,pull = <0 2>;
+	};
+
+	uart1_pins: uart1_pins {
+		brcm,pins;
+		brcm,function;
+		brcm,pull;
+	};
+
+	uart2_pins: uart2_pins {
+		brcm,pins = <0 1>;
+		brcm,function = <BCM2835_FSEL_ALT4>;
+		brcm,pull = <0 2>;
+	};
+
+	uart3_pins: uart3_pins {
+		brcm,pins = <4 5>;
+		brcm,function = <BCM2835_FSEL_ALT4>;
+		brcm,pull = <0 2>;
+	};
+
+	uart4_pins: uart4_pins {
+		brcm,pins = <8 9>;
+		brcm,function = <BCM2835_FSEL_ALT4>;
+		brcm,pull = <0 2>;
+	};
+
+	uart5_pins: uart5_pins {
+		brcm,pins = <12 13>;
+		brcm,function = <BCM2835_FSEL_ALT4>;
+		brcm,pull = <0 2>;
+	};
+};
+
+&i2c0if {
+	clock-frequency = <100000>;
+};
+
+&i2c1 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&i2c1_pins>;
+	clock-frequency = <100000>;
+};
+
+&i2s {
+	pinctrl-names = "default";
+	pinctrl-0 = <&i2s_pins>;
+};
+
+// =============================================
+// Board specific stuff here
+
+&sdhost {
+	status = "disabled";
+};
+
+&phy1 {
+	led-modes = <0x00 0x08>; /* link/activity link */
+};
+
+&gpio {
+	audio_pins: audio_pins {
+		brcm,pins = <40 41>;
+		brcm,function = <4>;
+	};
+};
+
+&leds {
+	act_led: act {
+		label = "led0";
+		linux,default-trigger = "mmc0";
+		gpios = <&gpio 42 GPIO_ACTIVE_HIGH>;
+	};
+
+	pwr_led: pwr {
+		label = "led1";
+		linux,default-trigger = "default-on";
+		gpios = <&expgpio 2 GPIO_ACTIVE_LOW>;
+	};
+};
+
+&pwm1 {
+	status = "disabled";
+};
+
+&audio {
+	pinctrl-names = "default";
+	pinctrl-0 = <&audio_pins>;
+};
+
+&cam1_reg {
+	gpio = <&expgpio 5 GPIO_ACTIVE_HIGH>;
+};
+
+/ {
+	__overrides__ {
+		act_led_gpio = <&act_led>,"gpios:4";
+		act_led_activelow = <&act_led>,"gpios:8";
+		act_led_trigger = <&act_led>,"linux,default-trigger";
+
+		pwr_led_gpio = <&pwr_led>,"gpios:4";
+		pwr_led_activelow = <&pwr_led>,"gpios:8";
+		pwr_led_trigger = <&pwr_led>,"linux,default-trigger";
+
+		eth_led0 = <&phy1>,"led-modes:0";
+		eth_led1 = <&phy1>,"led-modes:4";
+
+		sd_poll_once = <&emmc2>, "non-removable?";
+		spi_dma4 = <&spi0>, "dmas:0=", <&dma40>,
+			   <&spi0>, "dmas:8=", <&dma40>;
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/bcm2711-rpi-cm4.dts linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2711-rpi-cm4.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/bcm2711-rpi-cm4.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2711-rpi-cm4.dts	2021-07-25 16:45:34.748814264 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// SPDX-License-Identifier: GPL-2.0
+/dts-v1/;
+#include "bcm2711.dtsi"
+#include "bcm2835-rpi.dtsi"
+
+#include <dt-bindings/reset/raspberrypi,firmware-reset.h>
+
+/ {
+	compatible = "raspberrypi,4-compute-module", "brcm,bcm2711";
+	model = "Raspberry Pi Compute Module 4";
+
+	chosen {
+		/* 8250 auxiliary UART instead of pl011 */
+		stdout-path = "serial1:115200n8";
+	};
+
+	/* Will be filled by the bootloader */
+	memory@0 {
+		device_type = "memory";
+		reg = <0 0 0>;
+	};
+
+	aliases {
+		emmc2bus = &emmc2bus;
+		ethernet0 = &genet;
+		pcie0 = &pcie0;
+		blconfig = &blconfig;
+	};
+
+	leds {
+		act {
+			gpios = <&gpio 42 GPIO_ACTIVE_HIGH>;
+		};
+
+		pwr {
+			label = "PWR";
+			gpios = <&expgpio 2 GPIO_ACTIVE_LOW>;
+			default-state = "keep";
+			linux,default-trigger = "default-on";
+		};
+	};
+
+	wifi_pwrseq: wifi-pwrseq {
+		compatible = "mmc-pwrseq-simple";
+		reset-gpios = <&expgpio 1 GPIO_ACTIVE_LOW>;
+	};
+
+	sd_io_1v8_reg: sd_io_1v8_reg {
+		compatible = "regulator-gpio";
+		regulator-name = "vdd-sd-io";
+		regulator-min-microvolt = <1800000>;
+		regulator-max-microvolt = <3300000>;
+		regulator-boot-on;
+		regulator-always-on;
+		regulator-settling-time-us = <5000>;
+		gpios = <&expgpio 4 GPIO_ACTIVE_HIGH>;
+		states = <1800000 0x1
+			  3300000 0x0>;
+		status = "okay";
+	};
+
+	sd_vcc_reg: sd_vcc_reg {
+		compatible = "regulator-fixed";
+		regulator-name = "vcc-sd";
+		regulator-min-microvolt = <3300000>;
+		regulator-max-microvolt = <3300000>;
+		regulator-boot-on;
+		enable-active-high;
+		gpio = <&expgpio 6 GPIO_ACTIVE_HIGH>;
+	};
+};
+
+&ddc0 {
+	status = "okay";
+};
+
+&ddc1 {
+	status = "okay";
+};
+
+&firmware {
+	firmware_clocks: clocks {
+		compatible = "raspberrypi,firmware-clocks";
+		#clock-cells = <1>;
+	};
+
+	expgpio: gpio {
+		compatible = "raspberrypi,firmware-gpio";
+		gpio-controller;
+		#gpio-cells = <2>;
+		gpio-line-names = "BT_ON",
+				  "WL_ON",
+				  "PWR_LED_OFF",
+				  "ANT1",
+				  "VDD_SD_IO_SEL",
+				  "CAM_GPIO",
+				  "SD_PWR_ON",
+				  "ANT2";
+		status = "okay";
+
+		ant1: ant1 {
+			gpio-hog;
+			gpios = <3 GPIO_ACTIVE_HIGH>;
+			output-high;
+		};
+
+		ant2: ant2 {
+			gpio-hog;
+			gpios = <7 GPIO_ACTIVE_HIGH>;
+			output-low;
+		};
+	};
+
+	reset: reset {
+		compatible = "raspberrypi,firmware-reset";
+		#reset-cells = <1>;
+	};
+};
+
+&gpio {
+	/*
+	 * Parts taken from rpi_SCH_4b_4p0_reduced.pdf and
+	 * the official GPU firmware DT blob.
+	 *
+	 * Legend:
+	 * "FOO" = GPIO line named "FOO" on the schematic
+	 * "FOO_N" = GPIO line named "FOO" on schematic, active low
+	 */
+	gpio-line-names = "ID_SDA",
+			  "ID_SCL",
+			  "SDA1",
+			  "SCL1",
+			  "GPIO_GCLK",
+			  "GPIO5",
+			  "GPIO6",
+			  "SPI_CE1_N",
+			  "SPI_CE0_N",
+			  "SPI_MISO",
+			  "SPI_MOSI",
+			  "SPI_SCLK",
+			  "GPIO12",
+			  "GPIO13",
+			  /* Serial port */
+			  "TXD1",
+			  "RXD1",
+			  "GPIO16",
+			  "GPIO17",
+			  "GPIO18",
+			  "GPIO19",
+			  "GPIO20",
+			  "GPIO21",
+			  "GPIO22",
+			  "GPIO23",
+			  "GPIO24",
+			  "GPIO25",
+			  "GPIO26",
+			  "GPIO27",
+			  "RGMII_MDIO",
+			  "RGMIO_MDC",
+			  /* Used by BT module */
+			  "CTS0",
+			  "RTS0",
+			  "TXD0",
+			  "RXD0",
+			  /* Used by Wifi */
+			  "SD1_CLK",
+			  "SD1_CMD",
+			  "SD1_DATA0",
+			  "SD1_DATA1",
+			  "SD1_DATA2",
+			  "SD1_DATA3",
+			  /* Shared with SPI flash */
+			  "PWM0_MISO",
+			  "PWM1_MOSI",
+			  "STATUS_LED_G_CLK",
+			  "SPIFLASH_CE_N",
+			  "SDA0",
+			  "SCL0",
+			  "RGMII_RXCLK",
+			  "RGMII_RXCTL",
+			  "RGMII_RXD0",
+			  "RGMII_RXD1",
+			  "RGMII_RXD2",
+			  "RGMII_RXD3",
+			  "RGMII_TXCLK",
+			  "RGMII_TXCTL",
+			  "RGMII_TXD0",
+			  "RGMII_TXD1",
+			  "RGMII_TXD2",
+			  "RGMII_TXD3";
+};
+
+&hdmi0 {
+	clocks = <&firmware_clocks 13>, <&firmware_clocks 14>, <&dvp 0>, <&clk_27MHz>;
+	clock-names = "hdmi", "bvb", "audio", "cec";
+	status = "okay";
+};
+
+&hdmi1 {
+	clocks = <&firmware_clocks 13>, <&firmware_clocks 14>, <&dvp 1>, <&clk_27MHz>;
+	clock-names = "hdmi", "bvb", "audio", "cec";
+	status = "okay";
+};
+
+&hvs {
+	clocks = <&firmware_clocks 4>;
+};
+
+&pixelvalve0 {
+	status = "okay";
+};
+
+&pixelvalve1 {
+	status = "okay";
+};
+
+&pixelvalve2 {
+	status = "okay";
+};
+
+&pixelvalve4 {
+	status = "okay";
+};
+
+&pwm1 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&pwm1_0_gpio40 &pwm1_1_gpio41>;
+	status = "okay";
+};
+
+&rmem {
+	/*
+	 * RPi4's co-processor will copy the board's bootloader configuration
+	 * into memory for the OS to consume. It'll also update this node with
+	 * its placement information.
+	 */
+	blconfig: nvram@0 {
+		compatible = "raspberrypi,bootloader-config", "nvmem-rmem";
+		#address-cells = <1>;
+		#size-cells = <1>;
+		reg = <0x0 0x0 0x0>;
+		no-map;
+		status = "disabled";
+	};
+};
+
+/* SDHCI is used to control the SDIO for wireless */
+&sdhci {
+	#address-cells = <1>;
+	#size-cells = <0>;
+	pinctrl-names = "default";
+	pinctrl-0 = <&emmc_gpio34>;
+	bus-width = <4>;
+	non-removable;
+	mmc-pwrseq = <&wifi_pwrseq>;
+	status = "okay";
+
+	brcmf: wifi@1 {
+		reg = <1>;
+		compatible = "brcm,bcm4329-fmac";
+	};
+};
+
+/* EMMC2 is used to drive the EMMC card */
+&emmc2 {
+	bus-width = <8>;
+	vqmmc-supply = <&sd_io_1v8_reg>;
+	vmmc-supply = <&sd_vcc_reg>;
+	broken-cd;
+	status = "okay";
+};
+
+&genet {
+	phy-handle = <&phy1>;
+	phy-mode = "rgmii-rxid";
+	status = "okay";
+};
+
+&genet_mdio {
+	phy1: ethernet-phy@1 {
+		/* No PHY interrupt */
+		reg = <0x1>;
+	};
+};
+
+&pcie0 {
+	pci@1,0 {
+		#address-cells = <3>;
+		#size-cells = <2>;
+		ranges;
+
+		reg = <0 0 0 0 0>;
+
+		usb@1,0 {
+			reg = <0x10000 0 0 0 0>;
+			resets = <&reset RASPBERRYPI_FIRMWARE_RESET_ID_USB>;
+		};
+	};
+};
+
+/* uart0 communicates with the BT module */
+&uart0 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&uart0_ctsrts_gpio30 &uart0_gpio32>;
+	uart-has-rtscts;
+	status = "okay";
+
+	bluetooth {
+		compatible = "brcm,bcm43438-bt";
+		max-speed = <2000000>;
+		shutdown-gpios = <&expgpio 0 GPIO_ACTIVE_HIGH>;
+	};
+};
+
+/* uart1 is mapped to the pin header */
+&uart1 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&uart1_gpio14>;
+	status = "okay";
+};
+
+&vchiq {
+	interrupts = <GIC_SPI 34 IRQ_TYPE_LEVEL_HIGH>;
+};
+
+&vc4 {
+	status = "okay";
+};
+
+&vec {
+	status = "disabled";
+};
+
+// =============================================
+// Downstream rpi- changes
+
+#define BCM2711
+
+#include "bcm270x.dtsi"
+#include "bcm271x-rpi-bt.dtsi"
+
+/ {
+	soc {
+		/delete-node/ pixelvalve@7e807000;
+		/delete-node/ hdmi@7e902000;
+	};
+};
+
+#include "bcm2711-rpi.dtsi"
+#include "bcm283x-rpi-csi0-2lane.dtsi"
+#include "bcm283x-rpi-csi1-4lane.dtsi"
+#include "bcm283x-rpi-i2c0mux_0_44.dtsi"
+#include "bcm283x-rpi-cam1-regulator.dtsi"
+
+/ {
+	chosen {
+		bootargs = "coherent_pool=1M 8250.nr_uarts=1 snd_bcm2835.enable_compat_alsa=0 snd_bcm2835.enable_hdmi=1";
+	};
+
+	aliases {
+		serial0 = &uart1;
+		serial1 = &uart0;
+		mmc0 = &emmc2;
+		mmc1 = &mmcnr;
+		mmc2 = &sdhost;
+		i2c3 = &i2c3;
+		i2c4 = &i2c4;
+		i2c5 = &i2c5;
+		i2c6 = &i2c6;
+		i2c20 = &ddc0;
+		i2c21 = &ddc1;
+		spi3 = &spi3;
+		spi4 = &spi4;
+		spi5 = &spi5;
+		spi6 = &spi6;
+		/delete-property/ intc;
+	};
+
+	/delete-node/ wifi-pwrseq;
+};
+
+&mmcnr {
+	pinctrl-names = "default";
+	pinctrl-0 = <&sdio_pins>;
+	bus-width = <4>;
+	status = "okay";
+};
+
+&uart0 {
+	pinctrl-0 = <&uart0_pins &bt_pins>;
+	status = "okay";
+};
+
+&uart1 {
+	pinctrl-0 = <&uart1_pins>;
+};
+
+&spi0 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&spi0_pins &spi0_cs_pins>;
+	cs-gpios = <&gpio 8 1>, <&gpio 7 1>;
+
+	spidev0: spidev@0{
+		compatible = "spidev";
+		reg = <0>;	/* CE0 */
+		#address-cells = <1>;
+		#size-cells = <0>;
+		spi-max-frequency = <125000000>;
+	};
+
+	spidev1: spidev@1{
+		compatible = "spidev";
+		reg = <1>;	/* CE1 */
+		#address-cells = <1>;
+		#size-cells = <0>;
+		spi-max-frequency = <125000000>;
+	};
+};
+
+&gpio {
+	spi0_pins: spi0_pins {
+		brcm,pins = <9 10 11>;
+		brcm,function = <BCM2835_FSEL_ALT0>;
+	};
+
+	spi0_cs_pins: spi0_cs_pins {
+		brcm,pins = <8 7>;
+		brcm,function = <BCM2835_FSEL_GPIO_OUT>;
+	};
+
+	spi3_pins: spi3_pins {
+		brcm,pins = <1 2 3>;
+		brcm,function = <BCM2835_FSEL_ALT3>;
+	};
+
+	spi3_cs_pins: spi3_cs_pins {
+		brcm,pins = <0 24>;
+		brcm,function = <BCM2835_FSEL_GPIO_OUT>;
+	};
+
+	spi4_pins: spi4_pins {
+		brcm,pins = <5 6 7>;
+		brcm,function = <BCM2835_FSEL_ALT3>;
+	};
+
+	spi4_cs_pins: spi4_cs_pins {
+		brcm,pins = <4 25>;
+		brcm,function = <BCM2835_FSEL_GPIO_OUT>;
+	};
+
+	spi5_pins: spi5_pins {
+		brcm,pins = <13 14 15>;
+		brcm,function = <BCM2835_FSEL_ALT3>;
+	};
+
+	spi5_cs_pins: spi5_cs_pins {
+		brcm,pins = <12 26>;
+		brcm,function = <BCM2835_FSEL_GPIO_OUT>;
+	};
+
+	spi6_pins: spi6_pins {
+		brcm,pins = <19 20 21>;
+		brcm,function = <BCM2835_FSEL_ALT3>;
+	};
+
+	spi6_cs_pins: spi6_cs_pins {
+		brcm,pins = <18 27>;
+		brcm,function = <BCM2835_FSEL_GPIO_OUT>;
+	};
+
+	i2c0_pins: i2c0 {
+		brcm,pins = <0 1>;
+		brcm,function = <BCM2835_FSEL_ALT0>;
+		brcm,pull = <BCM2835_PUD_UP>;
+	};
+
+	i2c1_pins: i2c1 {
+		brcm,pins = <2 3>;
+		brcm,function = <BCM2835_FSEL_ALT0>;
+		brcm,pull = <BCM2835_PUD_UP>;
+	};
+
+	i2c3_pins: i2c3 {
+		brcm,pins = <4 5>;
+		brcm,function = <BCM2835_FSEL_ALT5>;
+		brcm,pull = <BCM2835_PUD_UP>;
+	};
+
+	i2c4_pins: i2c4 {
+		brcm,pins = <8 9>;
+		brcm,function = <BCM2835_FSEL_ALT5>;
+		brcm,pull = <BCM2835_PUD_UP>;
+	};
+
+	i2c5_pins: i2c5 {
+		brcm,pins = <12 13>;
+		brcm,function = <BCM2835_FSEL_ALT5>;
+		brcm,pull = <BCM2835_PUD_UP>;
+	};
+
+	i2c6_pins: i2c6 {
+		brcm,pins = <22 23>;
+		brcm,function = <BCM2835_FSEL_ALT5>;
+		brcm,pull = <BCM2835_PUD_UP>;
+	};
+
+	i2s_pins: i2s {
+		brcm,pins = <18 19 20 21>;
+		brcm,function = <BCM2835_FSEL_ALT0>;
+	};
+
+	sdio_pins: sdio_pins {
+		brcm,pins =     <34 35 36 37 38 39>;
+		brcm,function = <BCM2835_FSEL_ALT3>; // alt3 = SD1
+		brcm,pull =     <0 2 2 2 2 2>;
+	};
+
+	bt_pins: bt_pins {
+		brcm,pins = "-"; // non-empty to keep btuart happy, //4 = 0
+				 // to fool pinctrl
+		brcm,function = <0>;
+		brcm,pull = <2>;
+	};
+
+	uart0_pins: uart0_pins {
+		brcm,pins = <32 33>;
+		brcm,function = <BCM2835_FSEL_ALT3>;
+		brcm,pull = <0 2>;
+	};
+
+	uart1_pins: uart1_pins {
+		brcm,pins;
+		brcm,function;
+		brcm,pull;
+	};
+
+	uart2_pins: uart2_pins {
+		brcm,pins = <0 1>;
+		brcm,function = <BCM2835_FSEL_ALT4>;
+		brcm,pull = <0 2>;
+	};
+
+	uart3_pins: uart3_pins {
+		brcm,pins = <4 5>;
+		brcm,function = <BCM2835_FSEL_ALT4>;
+		brcm,pull = <0 2>;
+	};
+
+	uart4_pins: uart4_pins {
+		brcm,pins = <8 9>;
+		brcm,function = <BCM2835_FSEL_ALT4>;
+		brcm,pull = <0 2>;
+	};
+
+	uart5_pins: uart5_pins {
+		brcm,pins = <12 13>;
+		brcm,function = <BCM2835_FSEL_ALT4>;
+		brcm,pull = <0 2>;
+	};
+};
+
+&i2c0if {
+	clock-frequency = <100000>;
+};
+
+&i2c1 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&i2c1_pins>;
+	clock-frequency = <100000>;
+};
+
+&i2s {
+	pinctrl-names = "default";
+	pinctrl-0 = <&i2s_pins>;
+};
+
+// =============================================
+// Board specific stuff here
+
+&pcie0 {
+       brcm,enable-l1ss;
+};
+
+&sdhost {
+	status = "disabled";
+};
+
+&phy1 {
+	led-modes = <0x00 0x08>; /* link/activity link */
+};
+
+&gpio {
+	audio_pins: audio_pins {
+		brcm,pins = <>;
+		brcm,function = <>;
+	};
+};
+
+&leds {
+	act_led: act {
+		label = "led0";
+		linux,default-trigger = "mmc0";
+		gpios = <&gpio 42 GPIO_ACTIVE_HIGH>;
+	};
+
+	pwr_led: pwr {
+		label = "led1";
+		linux,default-trigger = "default-on";
+		gpios = <&expgpio 2 GPIO_ACTIVE_LOW>;
+	};
+};
+
+&pwm1 {
+	status = "disabled";
+};
+
+&audio {
+	pinctrl-names = "default";
+	pinctrl-0 = <&audio_pins>;
+	brcm,disable-headphones = <1>;
+};
+
+cam0_reg: &cam1_reg {
+	gpio = <&expgpio 5 GPIO_ACTIVE_HIGH>;
+};
+
+/ {
+	__overrides__ {
+		act_led_gpio = <&act_led>,"gpios:4";
+		act_led_activelow = <&act_led>,"gpios:8";
+		act_led_trigger = <&act_led>,"linux,default-trigger";
+
+		pwr_led_gpio = <&pwr_led>,"gpios:4";
+		pwr_led_activelow = <&pwr_led>,"gpios:8";
+		pwr_led_trigger = <&pwr_led>,"linux,default-trigger";
+
+		eth_led0 = <&phy1>,"led-modes:0";
+		eth_led1 = <&phy1>,"led-modes:4";
+
+		ant1 =  <&ant1>,"output-high?=on",
+			<&ant1>, "output-low?=off",
+			<&ant2>, "output-high?=off",
+			<&ant2>, "output-low?=on";
+		ant2 =  <&ant1>,"output-high?=off",
+			<&ant1>, "output-low?=on",
+			<&ant2>, "output-high?=on",
+			<&ant2>, "output-low?=off";
+		noant = <&ant1>,"output-high?=off",
+			<&ant1>, "output-low?=on",
+			<&ant2>, "output-high?=off",
+			<&ant2>, "output-low?=on";
+
+		sd_poll_once = <&emmc2>, "non-removable?";
+		spi_dma4 = <&spi0>, "dmas:0=", <&dma40>,
+			   <&spi0>, "dmas:8=", <&dma40>;
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/bcm2711-rpi.dtsi linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2711-rpi.dtsi
--- linux-5.10.52-orig/arch/arm/boot/dts/bcm2711-rpi.dtsi	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2711-rpi.dtsi	2021-07-25 16:45:34.748814264 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// SPDX-License-Identifier: GPL-2.0
+#include "bcm270x-rpi.dtsi"
+
+/ {
+	__overrides__ {
+		arm_freq;
+	};
+
+	v3dbus: v3dbus {
+		compatible = "simple-bus";
+		#address-cells = <1>;
+		#size-cells = <2>;
+		ranges = <0x7c500000  0x0 0xfc500000  0x0 0x03300000>,
+			 <0x40000000  0x0 0xff800000  0x0 0x00800000>;
+		dma-ranges = <0x00000000  0x0 0x00000000  0x4 0x00000000>;
+
+		v3d: v3d@7ec04000 {
+			compatible = "brcm,2711-v3d";
+			reg =
+			    <0x7ec00000  0x0 0x4000>,
+			    <0x7ec04000  0x0 0x4000>;
+			reg-names = "hub", "core0";
+
+			power-domains = <&pm BCM2835_POWER_DOMAIN_GRAFX_V3D>;
+			resets = <&pm BCM2835_RESET_V3D>;
+			clocks = <&firmware_clocks 5>;
+			clocks-names = "v3d";
+			interrupts = <GIC_SPI 74 IRQ_TYPE_LEVEL_HIGH>;
+			status = "disabled";
+		};
+	};
+
+	scb: scb {
+	     /* Add a label */
+	};
+};
+
+&vc4 {
+	raspberrypi,firmware = <&firmware>;
+};
+
+&cma {
+	/* Limit cma to the lower 768MB to allow room for HIGHMEM on 32-bit */
+	alloc-ranges = <0x0 0x00000000 0x30000000>;
+};
+
+&scb {
+	ranges = <0x0 0x7c000000  0x0 0xfc000000  0x0 0x03800000>,
+		 <0x0 0x40000000  0x0 0xff800000  0x0 0x00800000>,
+		 <0x6 0x00000000  0x6 0x00000000  0x0 0x40000000>,
+		 <0x0 0x00000000  0x0 0x00000000  0x0 0xfc000000>;
+	dma-ranges = <0x0 0x00000000  0x0 0x00000000  0x4 0x00000000>;
+
+	dma40: dma@7e007b00 {
+		compatible = "brcm,bcm2711-dma";
+		reg = <0x0 0x7e007b00  0x0 0x400>;
+		interrupts =
+			<GIC_SPI 89 IRQ_TYPE_LEVEL_HIGH>, /* dma4 11 */
+			<GIC_SPI 90 IRQ_TYPE_LEVEL_HIGH>, /* dma4 12 */
+			<GIC_SPI 91 IRQ_TYPE_LEVEL_HIGH>, /* dma4 13 */
+			<GIC_SPI 92 IRQ_TYPE_LEVEL_HIGH>; /* dma4 14 */
+		interrupt-names = "dma11",
+			"dma12",
+			"dma13",
+			"dma14";
+		#dma-cells = <1>;
+		brcm,dma-channel-mask = <0x7800>;
+	};
+
+	xhci: xhci@7e9c0000 {
+		compatible = "generic-xhci";
+		status = "disabled";
+		reg = <0x0 0x7e9c0000  0x0 0x100000>;
+		interrupts = <GIC_SPI 176 IRQ_TYPE_LEVEL_HIGH>;
+		power-domains = <&power RPI_POWER_DOMAIN_USB>;
+	};
+
+	hevc-decoder@7eb00000 {
+		compatible = "raspberrypi,rpivid-hevc-decoder";
+		reg = <0x0 0x7eb00000  0x0 0x10000>;
+		status = "okay";
+	};
+
+	rpivid-local-intc@7eb10000 {
+		compatible = "raspberrypi,rpivid-local-intc";
+		reg = <0x0 0x7eb10000  0x0 0x1000>;
+		status = "okay";
+		interrupts = <GIC_SPI 98 IRQ_TYPE_LEVEL_HIGH>;
+	};
+
+	h264-decoder@7eb20000 {
+		compatible = "raspberrypi,rpivid-h264-decoder";
+		reg = <0x0 0x7eb20000  0x0 0x10000>;
+		status = "okay";
+	};
+
+	vp9-decoder@7eb30000 {
+		compatible = "raspberrypi,rpivid-vp9-decoder";
+		reg = <0x0 0x7eb30000  0x0 0x10000>;
+		status = "okay";
+	};
+};
+
+&dma40 {
+	/* The VPU firmware uses DMA channel 11 for VCHIQ */
+	brcm,dma-channel-mask = <0x7000>;
+};
+
+&vchiq {
+	compatible = "brcm,bcm2711-vchiq";
+};
+
+&firmwarekms {
+	compatible = "raspberrypi,rpi-firmware-kms-2711";
+	interrupts = <GIC_SPI 112 IRQ_TYPE_LEVEL_HIGH>;
+};
+
+&smi {
+	interrupts = <GIC_SPI 112 IRQ_TYPE_LEVEL_HIGH>;
+};
+
+&mmc {
+	interrupts = <GIC_SPI 126 IRQ_TYPE_LEVEL_HIGH>;
+};
+
+&mmcnr {
+	interrupts = <GIC_SPI 126 IRQ_TYPE_LEVEL_HIGH>;
+};
+
+&csi0 {
+	interrupts = <GIC_SPI 102 IRQ_TYPE_LEVEL_HIGH>;
+};
+
+&csi1 {
+	interrupts = <GIC_SPI 103 IRQ_TYPE_LEVEL_HIGH>;
+};
+
+&random {
+	compatible = "brcm,bcm2711-rng200";
+	status = "okay";
+};
+
+&usb {
+	/* Enable the FIQ support */
+	reg = <0x7e980000 0x10000>,
+	      <0x7e00b200 0x200>;
+	interrupts = <GIC_SPI 73 IRQ_TYPE_LEVEL_HIGH>,
+		     <GIC_SPI 40 IRQ_TYPE_LEVEL_HIGH>;
+	status = "disabled";
+};
+
+&gpio {
+	interrupts = <GIC_SPI 113 IRQ_TYPE_LEVEL_HIGH>,
+		     <GIC_SPI 114 IRQ_TYPE_LEVEL_HIGH>;
+};
+
+&emmc2 {
+	mmc-ddr-3_3v;
+};
+
+&vc4 {
+	status = "disabled";
+};
+
+&pixelvalve0 {
+	status = "disabled";
+};
+
+&pixelvalve1 {
+	status = "disabled";
+};
+
+&pixelvalve2 {
+	status = "disabled";
+};
+
+&pixelvalve3 {
+	status = "disabled";
+};
+
+&pixelvalve4 {
+	status = "disabled";
+};
+
+&hdmi0 {
+	dmas = <&dma (10|(1<<27)|(1<<24)|(10<<16)|(15<<20))>;
+	status = "disabled";
+};
+
+&ddc0 {
+	status = "disabled";
+};
+
+&hdmi1 {
+	dmas = <&dma (17|(1<<27)|(1<<24)|(10<<16)|(15<<20))>;
+	status = "disabled";
+};
+
+&ddc1 {
+	status = "disabled";
+};
+
+&dvp {
+	status = "disabled";
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/bcm271x-rpi-bt.dtsi linux-5.10.52-v7l+/arch/arm/boot/dts/bcm271x-rpi-bt.dtsi
--- linux-5.10.52-orig/arch/arm/boot/dts/bcm271x-rpi-bt.dtsi	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm271x-rpi-bt.dtsi	2021-07-25 16:45:34.748814264 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// SPDX-License-Identifier: GPL-2.0
+
+&uart0 {
+	bt: bluetooth {
+		compatible = "brcm,bcm43438-bt";
+		max-speed = <3000000>;
+		shutdown-gpios = <&expgpio 0 GPIO_ACTIVE_HIGH>;
+		status = "disabled";
+	};
+};
+
+&uart1 {
+	minibt: bluetooth {
+		compatible = "brcm,bcm43438-bt";
+		max-speed = <460800>;
+		shutdown-gpios = <&expgpio 0 GPIO_ACTIVE_HIGH>;
+		status = "disabled";
+	};
+};
+
+/ {
+	__overrides__ {
+		krnbt = <&bt>,"status";
+		krnbt_baudrate = <&bt>,"max-speed:0";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/bcm2835-common.dtsi linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2835-common.dtsi
--- linux-5.10.52-orig/arch/arm/boot/dts/bcm2835-common.dtsi	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2835-common.dtsi	2021-07-25 16:45:34.748814264 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:109 @
 			status = "okay";
 		};
 
+		vec: vec@7e806000 {
+			compatible = "brcm,bcm2835-vec";
+			reg = <0x7e806000 0x1000>;
+			clocks = <&clocks BCM2835_CLOCK_VEC>;
+			interrupts = <2 27>;
+			status = "disabled";
+		};
+
 		pixelvalve@7e807000 {
 			compatible = "brcm,bcm2835-pixelvalve2";
 			reg = <0x7e807000 0x100>;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:127 @
 			compatible = "brcm,bcm2835-hdmi";
 			reg = <0x7e902000 0x600>,
 			      <0x7e808000 0x100>;
+			reg-names = "hdmi",
+				    "hd";
 			interrupts = <2 8>, <2 9>;
 			ddc = <&i2c2>;
 			clocks = <&clocks BCM2835_PLLH_PIX>,
 				 <&clocks BCM2835_CLOCK_HSM>;
 			clock-names = "pixel", "hdmi";
-			dmas = <&dma 17>;
+			dmas = <&dma (17|(1<<27)|(1<<24))>;
 			dma-names = "audio-rx";
 			status = "disabled";
 		};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/bcm2835.dtsi linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2835.dtsi
--- linux-5.10.52-orig/arch/arm/boot/dts/bcm2835.dtsi	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2835.dtsi	2021-07-25 16:45:34.758814097 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:22 @
 
 	soc {
 		ranges = <0x7e000000 0x20000000 0x02000000>;
-		dma-ranges = <0x40000000 0x00000000 0x20000000>;
+		dma-ranges = <0x80000000 0x00000000 0x20000000>;
 	};
 
 	arm-pmu {
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/bcm2835-rpi-a.dts linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2835-rpi-a.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/bcm2835-rpi-a.dts	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2835-rpi-a.dts	2021-07-25 16:45:34.748814264 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:124 @
 	pinctrl-0 = <&uart0_gpio14>;
 	status = "okay";
 };
+
+/* i2c0 on camera/display connector is gpio 0&1. Not exposed on header.
+ * To avoid having to remap everything, map both ports to gpios 0&1
+ */
+&i2c0mux {
+	pinctrl-1 = <&i2c0_gpio0>;
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/bcm2835-rpi-a-plus.dts linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2835-rpi-a-plus.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/bcm2835-rpi-a-plus.dts	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2835-rpi-a-plus.dts	2021-07-25 16:45:34.748814264 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:129 @
 	pinctrl-0 = <&uart0_gpio14>;
 	status = "okay";
 };
+
+/* i2c on camera/display connector is gpio 28&29 */
+&i2c0mux {
+	pinctrl-1 = <&i2c0_gpio28>;
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/bcm2835-rpi-b.dts linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2835-rpi-b.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/bcm2835-rpi-b.dts	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2835-rpi-b.dts	2021-07-25 16:45:34.758814097 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:119 @
 	pinctrl-0 = <&uart0_gpio14>;
 	status = "okay";
 };
+
+/* camera/display connector use BSC1 on GPIOS 2&3.
+ * To avoid having to remap everything, map both ports to gpios 0&1
+ */
+&i2c0mux {
+	pinctrl-1 = <&i2c0_gpio0>;
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/bcm2835-rpi-b-plus.dts linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2835-rpi-b-plus.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/bcm2835-rpi-b-plus.dts	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2835-rpi-b-plus.dts	2021-07-25 16:45:34.748814264 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:131 @
 	pinctrl-0 = <&uart0_gpio14>;
 	status = "okay";
 };
+
+/* i2c on camera/display connector is gpio 28&29 */
+&i2c0mux {
+	pinctrl-1 = <&i2c0_gpio28>;
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/bcm2835-rpi-b-rev2.dts linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2835-rpi-b-rev2.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/bcm2835-rpi-b-rev2.dts	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2835-rpi-b-rev2.dts	2021-07-25 16:45:34.758814097 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:124 @
 	pinctrl-0 = <&uart0_gpio14>;
 	status = "okay";
 };
+
+/* i2c0 on camera/display connector is gpio 0&1. Not exposed on header.
+ * To avoid having to remap everything, map both ports to gpios 0&1
+ */
+&i2c0mux {
+	pinctrl-1 = <&i2c0_gpio0>;
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/bcm2835-rpi-cm1-io1.dts linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2835-rpi-cm1-io1.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/bcm2835-rpi-cm1-io1.dts	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2835-rpi-cm1-io1.dts	2021-07-25 16:45:34.758814097 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:98 @
 	pinctrl-0 = <&uart0_gpio14>;
 	status = "okay";
 };
+
+/* WHAT TO DO HERE? */
+&i2c0mux {
+	pinctrl-1 = <&i2c0_gpio28>;
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/bcm2835-rpi.dtsi linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2835-rpi.dtsi
--- linux-5.10.52-orig/arch/arm/boot/dts/bcm2835-rpi.dtsi	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2835-rpi.dtsi	2021-07-25 16:45:34.758814097 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:22 @
 
 			mboxes = <&mailbox>;
 			dma-ranges;
+
+			firmware_clocks: clocks {
+				compatible = "raspberrypi,firmware-clocks";
+				#clock-cells = <1>;
+			};
 		};
 
 		power: power {
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:57 @
 	};
 };
 
-&i2c0 {
-	pinctrl-names = "default";
-	pinctrl-0 = <&i2c0_gpio0>;
+&i2c0if {
 	status = "okay";
 	clock-frequency = <100000>;
 };
 
+&i2c0mux {
+	pinctrl-0 = <&i2c0_gpio0>;
+	/* pinctrl-1 varies based on platform */
+	status = "okay";
+};
+
 &i2c1 {
 	pinctrl-names = "default";
 	pinctrl-0 = <&i2c1_gpio2>;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:79 @
 	power-domains = <&power RPI_POWER_DOMAIN_USB>;
 };
 
+&vc4 {
+	raspberrypi,firmware = <&firmware>;
+};
+
 &vec {
 	power-domains = <&power RPI_POWER_DOMAIN_VEC>;
 	status = "okay";
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/bcm2835-rpi-zero.dts linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2835-rpi-zero.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/bcm2835-rpi-zero.dts	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2835-rpi-zero.dts	2021-07-25 16:45:34.758814097 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:120 @
 	pinctrl-0 = <&uart0_gpio14>;
 	status = "okay";
 };
+
+/* i2c on camera/display connector is gpio 28&29 */
+&i2c0mux {
+	pinctrl-1 = <&i2c0_gpio28>;
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/bcm2835-rpi-zero-w.dts linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2835-rpi-zero-w.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/bcm2835-rpi-zero-w.dts	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2835-rpi-zero-w.dts	2021-07-25 16:45:34.758814097 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:152 @
 	pinctrl-0 = <&uart1_gpio14>;
 	status = "okay";
 };
+
+/* i2c on camera/display connector is gpio 28&29 */
+&i2c0mux {
+	pinctrl-1 = <&i2c0_gpio28>;
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/bcm2836-rpi-2-b.dts linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2836-rpi-2-b.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/bcm2836-rpi-2-b.dts	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2836-rpi-2-b.dts	2021-07-25 16:45:34.758814097 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:131 @
 	pinctrl-0 = <&uart0_gpio14>;
 	status = "okay";
 };
+
+/* i2c on camera/display connector is gpio 28&29 */
+&i2c0mux {
+	pinctrl-1 = <&i2c0_gpio28>;
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/bcm2837-rpi-3-a-plus.dts linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2837-rpi-3-a-plus.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/bcm2837-rpi-3-a-plus.dts	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2837-rpi-3-a-plus.dts	2021-07-25 16:45:34.758814097 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:181 @
 	pinctrl-0 = <&uart1_gpio14>;
 	status = "okay";
 };
+
+/* i2c on camera/display connector is gpio 44&45 */
+&i2c0mux {
+	pinctrl-1 = <&i2c0_gpio44>;
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/bcm2837-rpi-3-b.dts linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2837-rpi-3-b.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/bcm2837-rpi-3-b.dts	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2837-rpi-3-b.dts	2021-07-25 16:45:34.758814097 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:177 @
 	status = "okay";
 	bus-width = <4>;
 };
+
+/* i2c on camera/display connector is gpio 44&45 */
+&i2c0mux {
+	pinctrl-1 = <&i2c0_gpio44>;
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/bcm2837-rpi-3-b-plus.dts linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2837-rpi-3-b-plus.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/bcm2837-rpi-3-b-plus.dts	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2837-rpi-3-b-plus.dts	2021-07-25 16:45:34.758814097 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:184 @
 	pinctrl-0 = <&uart1_gpio14>;
 	status = "okay";
 };
+
+/* i2c on camera/display connector is gpio 44&45 */
+&i2c0mux {
+	pinctrl-1 = <&i2c0_gpio44>;
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/bcm2837-rpi-cm3-io3.dts linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2837-rpi-cm3-io3.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/bcm2837-rpi-cm3-io3.dts	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2837-rpi-cm3-io3.dts	2021-07-25 16:45:34.758814097 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:97 @
 	pinctrl-0 = <&uart0_gpio14>;
 	status = "okay";
 };
+
+/* WHAT TO DO HERE? */
+&i2c0mux {
+	pinctrl-1 = <&i2c0_gpio28>;
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/bcm283x.dtsi linux-5.10.52-v7l+/arch/arm/boot/dts/bcm283x.dtsi
--- linux-5.10.52-orig/arch/arm/boot/dts/bcm283x.dtsi	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm283x.dtsi	2021-07-25 16:45:34.758814097 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:337 @
 			status = "disabled";
 		};
 
-		i2c0: i2c@7e205000 {
+		i2c0if: i2c@7e205000 {
 			compatible = "brcm,bcm2835-i2c";
 			reg = <0x7e205000 0x200>;
 			interrupts = <2 21>;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:347 @
 			status = "disabled";
 		};
 
+		i2c0mux: i2c0mux {
+			compatible = "i2c-mux-pinctrl";
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			i2c-parent = <&i2c0if>;
+
+			pinctrl-names = "i2c0", "i2c_csi_dsi";
+
+			status = "disabled";
+
+			i2c0: i2c@0 {
+				reg = <0>;
+				#address-cells = <1>;
+				#size-cells = <0>;
+			};
+
+			i2c_csi_dsi: i2c@1 {
+				reg = <1>;
+				#address-cells = <1>;
+				#size-cells = <0>;
+			};
+		};
+
 		dpi: dpi@7e208000 {
 			compatible = "brcm,bcm2835-dpi";
 			reg = <0x7e208000 0x8c>;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:491 @
 			status = "disabled";
 		};
 
-		vec: vec@7e806000 {
-			compatible = "brcm,bcm2835-vec";
-			reg = <0x7e806000 0x1000>;
-			clocks = <&clocks BCM2835_CLOCK_VEC>;
-			interrupts = <2 27>;
-			status = "disabled";
-		};
-
 		usb: usb@7e980000 {
 			compatible = "brcm,bcm2835-usb";
 			reg = <0x7e980000 0x10000>;
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/bcm283x-rpi-cam1-regulator.dtsi linux-5.10.52-v7l+/arch/arm/boot/dts/bcm283x-rpi-cam1-regulator.dtsi
--- linux-5.10.52-orig/arch/arm/boot/dts/bcm283x-rpi-cam1-regulator.dtsi	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm283x-rpi-cam1-regulator.dtsi	2021-07-25 16:45:34.758814097 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// SPDX-License-Identifier: GPL-2.0
+
+/ {
+	cam1_reg: cam1_reg {
+		compatible = "regulator-fixed";
+		regulator-name = "cam1-reg";
+		enable-active-high;
+		status = "disabled";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/bcm283x-rpi-csi0-2lane.dtsi linux-5.10.52-v7l+/arch/arm/boot/dts/bcm283x-rpi-csi0-2lane.dtsi
--- linux-5.10.52-orig/arch/arm/boot/dts/bcm283x-rpi-csi0-2lane.dtsi	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm283x-rpi-csi0-2lane.dtsi	2021-07-25 16:45:34.758814097 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1 @
+// SPDX-License-Identifier: GPL-2.0-only
+&csi0 {
+	brcm,num-data-lanes = <2>;
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/bcm283x-rpi-csi1-2lane.dtsi linux-5.10.52-v7l+/arch/arm/boot/dts/bcm283x-rpi-csi1-2lane.dtsi
--- linux-5.10.52-orig/arch/arm/boot/dts/bcm283x-rpi-csi1-2lane.dtsi	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm283x-rpi-csi1-2lane.dtsi	2021-07-25 16:45:34.758814097 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1 @
+// SPDX-License-Identifier: GPL-2.0-only
+&csi1 {
+	brcm,num-data-lanes = <2>;
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/bcm283x-rpi-csi1-4lane.dtsi linux-5.10.52-v7l+/arch/arm/boot/dts/bcm283x-rpi-csi1-4lane.dtsi
--- linux-5.10.52-orig/arch/arm/boot/dts/bcm283x-rpi-csi1-4lane.dtsi	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm283x-rpi-csi1-4lane.dtsi	2021-07-25 16:45:34.758814097 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1 @
+// SPDX-License-Identifier: GPL-2.0-only
+&csi1 {
+	brcm,num-data-lanes = <4>;
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/bcm283x-rpi-i2c0mux_0_28.dtsi linux-5.10.52-v7l+/arch/arm/boot/dts/bcm283x-rpi-i2c0mux_0_28.dtsi
--- linux-5.10.52-orig/arch/arm/boot/dts/bcm283x-rpi-i2c0mux_0_28.dtsi	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm283x-rpi-i2c0mux_0_28.dtsi	2021-07-25 16:45:34.758814097 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1 @
+&i2c0mux {
+	pinctrl-0 = <&i2c0_gpio0>;
+	pinctrl-1 = <&i2c0_gpio28>;
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/bcm283x-rpi-i2c0mux_0_44.dtsi linux-5.10.52-v7l+/arch/arm/boot/dts/bcm283x-rpi-i2c0mux_0_44.dtsi
--- linux-5.10.52-orig/arch/arm/boot/dts/bcm283x-rpi-i2c0mux_0_44.dtsi	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm283x-rpi-i2c0mux_0_44.dtsi	2021-07-25 16:45:34.758814097 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1 @
+&i2c0mux {
+	pinctrl-0 = <&i2c0_gpio0>;
+	pinctrl-1 = <&i2c0_gpio44>;
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/bcm283x-rpi-usb-peripheral.dtsi linux-5.10.52-v7l+/arch/arm/boot/dts/bcm283x-rpi-usb-peripheral.dtsi
--- linux-5.10.52-orig/arch/arm/boot/dts/bcm283x-rpi-usb-peripheral.dtsi	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm283x-rpi-usb-peripheral.dtsi	1970-01-01 01:00:00.000000000 +0100
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1 @
-// SPDX-License-Identifier: GPL-2.0
-&usb {
-	dr_mode = "peripheral";
-	g-rx-fifo-size = <256>;
-	g-np-tx-fifo-size = <32>;
-	g-tx-fifo-size = <256 256 512 512 512 768 768>;
-};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/Makefile linux-5.10.52-v7l+/arch/arm/boot/dts/Makefile
--- linux-5.10.52-orig/arch/arm/boot/dts/Makefile	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/Makefile	2021-07-25 16:45:34.658815773 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
 # SPDX-License-Identifier: GPL-2.0
+
+dtb-$(CONFIG_ARCH_BCM2835) += \
+	bcm2708-rpi-b.dtb \
+	bcm2708-rpi-b-rev1.dtb \
+	bcm2708-rpi-b-plus.dtb \
+	bcm2708-rpi-cm.dtb \
+	bcm2708-rpi-zero.dtb \
+	bcm2708-rpi-zero-w.dtb \
+	bcm2709-rpi-2-b.dtb \
+	bcm2710-rpi-2-b.dtb \
+	bcm2710-rpi-3-b.dtb \
+	bcm2710-rpi-3-b-plus.dtb \
+	bcm2711-rpi-4-b.dtb \
+	bcm2711-rpi-400.dtb \
+	bcm2710-rpi-cm3.dtb \
+	bcm2711-rpi-cm4.dtb
+
 dtb-$(CONFIG_ARCH_ALPINE) += \
 	alpine-db.dtb
 dtb-$(CONFIG_MACH_ARTPEC6) += \
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:112 @
 	bcm2837-rpi-3-b.dtb \
 	bcm2837-rpi-3-b-plus.dtb \
 	bcm2837-rpi-cm3-io3.dtb \
-	bcm2711-rpi-4-b.dtb \
 	bcm2835-rpi-zero.dtb \
 	bcm2835-rpi-zero-w.dtb
 dtb-$(CONFIG_ARCH_BCM_5301X) += \
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1427 @
 	aspeed-bmc-opp-zaius.dtb \
 	aspeed-bmc-portwell-neptune.dtb \
 	aspeed-bmc-quanta-q71l.dtb
+
+targets += dtbs dtbs_install
+targets += $(dtb-y)
+
+subdir-y	:= overlays
+
+# Enable fixups to support overlays on BCM2835 platforms
+ifeq ($(CONFIG_ARCH_BCM2835),y)
+	DTC_FLAGS ?= -@
+endif
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/act-led-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/act-led-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/act-led-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/act-led-overlay.dts	2021-07-25 16:45:35.138807726 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/dts-v1/;
+/plugin/;
+
+/* Pi3 uses a GPIO expander to drive the LEDs which can only be accessed
+   from the VPU. There is a special driver for this with a separate DT node,
+   which has the unfortunate consequence of breaking the act_led_gpio and
+   act_led_activelow dtparams.
+
+   This overlay changes the GPIO controller back to the standard one and
+   restores the dtparams.
+*/
+
+/{
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&act_led>;
+		frag0: __overlay__ {
+			gpios = <&gpio 0 0>;
+		};
+	};
+
+	__overrides__ {
+		gpio = <&frag0>,"gpios:4";
+		activelow = <&frag0>,"gpios:8";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/adafruit18-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/adafruit18-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/adafruit18-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/adafruit18-overlay.dts	2021-07-25 16:45:35.138807726 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/*
+ * Device Tree overlay for Adafruit 1.8" TFT LCD with ST7735R chip 160x128
+ */
+
+/dts-v1/;
+/plugin/;
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&spidev0>;
+		__overlay__ {
+			status = "disabled";
+		};
+	};
+
+	fragment@1 {
+		target = <&spi0>;
+		__overlay__ {
+			/* needed to avoid dtc warning */
+			#address-cells = <1>;
+			#size-cells = <0>;
+			status = "okay";
+
+			af18: adafruit18@0 {
+				compatible = "fbtft,adafruit18";
+				reg = <0>;
+				pinctrl-names = "default";
+				spi-max-frequency = <40000000>;
+				rotate = <90>;
+				buswidth = <8>;
+				fps = <50>;
+				height = <160>;
+				width = <128>;
+				reset-gpios = <&gpio 25 1>;
+				dc-gpios = <&gpio 24 0>;
+				led-gpios = <&gpio 18 0>;
+				debug = <0>;
+			};
+		};
+	};
+
+	__overrides__ {
+		green = <&af18>, "compatible=fbtft,adafruit18_green";
+		speed     = <&af18>,"spi-max-frequency:0";
+		rotate    = <&af18>,"rotate:0";
+		fps       = <&af18>,"fps:0";
+		bgr       = <&af18>,"bgr?";
+		debug     = <&af18>,"debug:0";
+		dc_pin    = <&af18>,"dc-gpios:4";
+		reset_pin = <&af18>,"reset-gpios:4";
+		led_pin   = <&af18>,"led-gpios:4";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/adau1977-adc-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/adau1977-adc-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/adau1977-adc-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/adau1977-adc-overlay.dts	2021-07-25 16:45:35.138807726 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// Definitions for ADAU1977 ADC
+/dts-v1/;
+/plugin/;
+
+/ {
+	compatible = "brcm,bcm2835";
+    
+	fragment@0 {
+        	target = <&i2c>;
+        	
+		__overlay__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			status = "okay";
+			
+			adau1977: codec@11 {
+                        	compatible = "adi,adau1977";
+                        	reg = <0x11>;
+                        	reset-gpios = <&gpio 5 0>;
+				AVDD-supply = <&vdd_3v3_reg>;
+                	};
+        	};
+	};
+
+	fragment@1 {
+		target = <&i2s>;
+		__overlay__ {
+			status = "okay";
+		};
+	};
+
+	fragment@2 {
+		target = <&sound>;
+		__overlay__ {
+			compatible = "adi,adau1977-adc";
+			i2s-controller = <&i2s>;
+			status = "okay";
+		};
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/adau7002-simple-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/adau7002-simple-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/adau7002-simple-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/adau7002-simple-overlay.dts	2021-07-25 16:45:35.138807726 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/dts-v1/;
+/plugin/;
+
+/ {
+    compatible = "brcm,bcm2835";
+
+    fragment@0 {
+        target = <&i2s>;
+        __overlay__ {
+            status = "okay";
+        };
+    };
+
+    fragment@1 {
+        target-path = "/";
+        __overlay__ {
+                adau7002_codec: adau7002-codec {
+                #sound-dai-cells = <0>;
+                compatible = "adi,adau7002";
+/*                IOVDD-supply = <&supply>;*/
+                status = "okay";
+            };
+        };
+    };
+
+    fragment@2 {
+        target = <&sound>;
+            sound_overlay: __overlay__ {
+            compatible = "simple-audio-card";
+            simple-audio-card,format = "i2s";
+            simple-audio-card,name = "adau7002";
+            simple-audio-card,bitclock-slave = <&dailink0_slave>;
+            simple-audio-card,frame-slave = <&dailink0_slave>;
+            simple-audio-card,widgets =
+                    "Microphone", "Microphone Jack";
+            simple-audio-card,routing =
+                    "PDM_DAT", "Microphone Jack";
+            status = "okay";
+            simple-audio-card,cpu {
+                sound-dai = <&i2s>;
+            };
+            dailink0_slave: simple-audio-card,codec {
+                sound-dai = <&adau7002_codec>;
+            };
+        };
+    };
+
+
+    __overrides__ {
+        card-name = <&sound_overlay>,"simple-audio-card,name";
+    };
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/ads1015-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/ads1015-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/ads1015-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/ads1015-overlay.dts	2021-07-25 16:45:35.138807726 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/*
+ * 2016 - Erik Sejr
+ */
+/dts-v1/;
+/plugin/;
+
+/ {
+    compatible = "brcm,bcm2835";
+    /* ----------- ADS1015 ------------ */
+    fragment@0 {
+        target = <&i2c_arm>;
+        __overlay__ {
+            #address-cells = <1>;
+            #size-cells = <0>;
+            status = "okay";
+            ads1015: ads1015@48 {
+                compatible = "ti,ads1015";
+                status = "okay";
+                #address-cells = <1>;
+                #size-cells = <0>;
+                reg = <0x48>;
+            };
+        };
+    };
+
+    fragment@1 {
+        target = <&ads1015>;
+        __overlay__ {
+            #address-cells = <1>;
+            #size-cells = <0>;
+            channel_a: channel_a {
+                reg = <4>;
+                ti,gain = <2>;
+                ti,datarate = <4>;
+            };
+        };
+    };
+
+    fragment@2 {
+        target = <&ads1015>;
+        __dormant__ {
+            #address-cells = <1>;
+            #size-cells = <0>;
+            channel_b: channel_b {
+                reg = <5>;
+                ti,gain = <2>;
+                ti,datarate = <4>;
+            };
+        };
+    };
+
+    fragment@3 {
+        target = <&ads1015>;
+        __dormant__ {
+            #address-cells = <1>;
+            #size-cells = <0>;
+            channel_c: channel_c {
+                reg = <6>;
+                ti,gain = <2>;
+                ti,datarate = <4>;
+            };
+        };
+    };
+
+    fragment@4 {
+        target = <&ads1015>;
+        __dormant__ {
+            #address-cells = <1>;
+            #size-cells = <0>;
+            channel_d: channel_d {
+                reg = <7>;
+                ti,gain = <2>;
+                ti,datarate = <4>;
+            };
+        };
+    };
+
+    __overrides__ {
+        addr =            <&ads1015>,"reg:0";
+        cha_enable =      <0>,"=1";
+        cha_cfg =         <&channel_a>,"reg:0";
+        cha_gain =        <&channel_a>,"ti,gain:0";
+        cha_datarate =    <&channel_a>,"ti,datarate:0";
+        chb_enable =      <0>,"=2";
+        chb_cfg =         <&channel_b>,"reg:0";
+        chb_gain =        <&channel_b>,"ti,gain:0";
+        chb_datarate =    <&channel_b>,"ti,datarate:0";
+        chc_enable =      <0>,"=3";
+        chc_cfg =         <&channel_c>,"reg:0";
+        chc_gain =        <&channel_c>,"ti,gain:0";
+        chc_datarate =    <&channel_c>,"ti,datarate:0";
+        chd_enable =      <0>,"=4";
+        chd_cfg =         <&channel_d>,"reg:0";
+        chd_gain =        <&channel_d>,"ti,gain:0";
+        chd_datarate =    <&channel_d>,"ti,datarate:0";
+   };
+
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/ads1115-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/ads1115-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/ads1115-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/ads1115-overlay.dts	2021-07-25 16:45:35.138807726 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/*
+ * TI ADS1115 multi-channel ADC overlay
+ */
+
+/dts-v1/;
+/plugin/;
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&i2c_arm>;
+		__overlay__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			status = "okay";
+
+			ads1115: ads1115@48 {
+				compatible = "ti,ads1115";
+				status = "okay";
+				#address-cells = <1>;
+				#size-cells = <0>;
+				reg = <0x48>;
+			};
+		};
+	};
+
+	fragment@1 {
+		target = <&ads1115>;
+		__dormant__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			channel_a: channel_a {
+				reg = <4>;
+				ti,gain = <1>;
+				ti,datarate = <7>;
+			};
+		};
+	};
+
+	fragment@2 {
+		target = <&ads1115>;
+		__dormant__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			channel_b: channel_b {
+				reg = <5>;
+				ti,gain = <1>;
+				ti,datarate = <7>;
+			};
+		};
+	};
+
+	fragment@3 {
+		target = <&ads1115>;
+		__dormant__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			channel_c: channel_c {
+				reg = <6>;
+				ti,gain = <1>;
+				ti,datarate = <7>;
+			};
+		};
+	};
+
+	fragment@4 {
+		target = <&ads1115>;
+		__dormant__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			channel_d: channel_d {
+				reg = <7>;
+				ti,gain = <1>;
+				ti,datarate = <7>;
+			};
+		};
+	};
+
+	__overrides__ {
+		addr =            <&ads1115>,"reg:0";
+		cha_enable =      <0>,"=1";
+		cha_cfg =         <&channel_a>,"reg:0";
+		cha_gain =        <&channel_a>,"ti,gain:0";
+		cha_datarate =    <&channel_a>,"ti,datarate:0";
+		chb_enable =      <0>,"=2";
+		chb_cfg =         <&channel_b>,"reg:0";
+		chb_gain =        <&channel_b>,"ti,gain:0";
+		chb_datarate =    <&channel_b>,"ti,datarate:0";
+		chc_enable =      <0>,"=3";
+		chc_cfg =         <&channel_c>,"reg:0";
+		chc_gain =        <&channel_c>,"ti,gain:0";
+		chc_datarate =    <&channel_c>,"ti,datarate:0";
+		chd_enable =      <0>,"=4";
+		chd_cfg =         <&channel_d>,"reg:0";
+		chd_gain =        <&channel_d>,"ti,gain:0";
+		chd_datarate =    <&channel_d>,"ti,datarate:0";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/ads7846-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/ads7846-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/ads7846-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/ads7846-overlay.dts	2021-07-25 16:45:35.138807726 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/*
+ * Generic Device Tree overlay for the ADS7846 touch controller
+ *
+ */
+
+/dts-v1/;
+/plugin/;
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&spi0>;
+		__overlay__ {
+			status = "okay";
+		};
+	};
+
+	fragment@1 {
+		target = <&spidev0>;
+		__overlay__ {
+			status = "disabled";
+		};
+	};
+
+	fragment@2 {
+		target = <&spidev1>;
+		__overlay__ {
+			status = "disabled";
+		};
+	};
+
+	fragment@3 {
+		target = <&gpio>;
+		__overlay__ {
+			ads7846_pins: ads7846_pins {
+				brcm,pins = <255>; /* illegal default value */
+				brcm,function = <0>; /* in */
+				brcm,pull = <0>; /* none */
+			};
+		};
+	};
+
+	fragment@4 {
+		target = <&spi0>;
+		__overlay__ {
+			/* needed to avoid dtc warning */
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			ads7846: ads7846@1 {
+				compatible = "ti,ads7846";
+				reg = <1>;
+				pinctrl-names = "default";
+				pinctrl-0 = <&ads7846_pins>;
+
+				spi-max-frequency = <2000000>;
+				interrupts = <255 2>; /* high-to-low edge triggered */
+				interrupt-parent = <&gpio>;
+				pendown-gpio = <&gpio 255 0>;
+
+				/* driver defaults */
+				ti,x-min = /bits/ 16 <0>;
+				ti,y-min = /bits/ 16 <0>;
+				ti,x-max = /bits/ 16 <0x0FFF>;
+				ti,y-max = /bits/ 16 <0x0FFF>;
+				ti,pressure-min = /bits/ 16 <0>;
+				ti,pressure-max = /bits/ 16 <0xFFFF>;
+				ti,x-plate-ohms = /bits/ 16 <400>;
+			};
+		};
+	};
+	__overrides__ {
+		cs =     <&ads7846>,"reg:0";
+		speed =  <&ads7846>,"spi-max-frequency:0";
+		penirq = <&ads7846_pins>,"brcm,pins:0", /* REQUIRED */
+			 <&ads7846>,"interrupts:0",
+			 <&ads7846>,"pendown-gpio:4";
+		penirq_pull = <&ads7846_pins>,"brcm,pull:0";
+		swapxy = <&ads7846>,"ti,swap-xy?";
+		xmin =   <&ads7846>,"ti,x-min;0";
+		ymin =   <&ads7846>,"ti,y-min;0";
+		xmax =   <&ads7846>,"ti,x-max;0";
+		ymax =   <&ads7846>,"ti,y-max;0";
+		pmin =   <&ads7846>,"ti,pressure-min;0";
+		pmax =   <&ads7846>,"ti,pressure-max;0";
+		xohms =  <&ads7846>,"ti,x-plate-ohms;0";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/adv7282m-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/adv7282m-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/adv7282m-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/adv7282m-overlay.dts	2021-07-25 16:45:35.138807726 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// SPDX-License-Identifier: GPL-2.0-only
+// Definitions for Analog Devices ADV7282-M video to CSI2 bridge on VC I2C bus
+/dts-v1/;
+/plugin/;
+
+/{
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&i2c_csi_dsi>;
+		__overlay__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			status = "okay";
+
+			adv728x: adv728x@21 {
+				compatible = "adi,adv7282-m";
+				reg = <0x21>;
+				status = "okay";
+				clock-frequency = <24000000>;
+				port {
+					adv728x_0: endpoint {
+						remote-endpoint = <&csi1_ep>;
+						clock-lanes = <0>;
+						data-lanes = <1>;
+						link-frequencies =
+							/bits/ 64 <297000000>;
+
+						mclk-frequency = <12000000>;
+					};
+				};
+			};
+		};
+	};
+	fragment@1 {
+		target = <&csi1>;
+		__overlay__ {
+			status = "okay";
+
+			port {
+				csi1_ep: endpoint {
+					remote-endpoint = <&adv728x_0>;
+					data-lanes = <1>;
+				};
+			};
+		};
+	};
+	fragment@2 {
+		target = <&i2c0if>;
+		__overlay__ {
+			status = "okay";
+		};
+	};
+
+	fragment@3 {
+		target = <&i2c0mux>;
+		__overlay__ {
+			status = "okay";
+		};
+	};
+
+	__overrides__ {
+		addr =			<&adv728x>,"reg:0";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/adv728x-m-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/adv728x-m-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/adv728x-m-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/adv728x-m-overlay.dts	2021-07-25 16:45:35.138807726 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// SPDX-License-Identifier: GPL-2.0-only
+// Definitions for Analog Devices ADV728[0|1|2]-M video to CSI2 bridges on VC
+// I2C bus
+
+#include "adv7282m-overlay.dts"
+
+/{
+	compatible = "brcm,bcm2835";
+
+	// Fragment numbers deliberately high to avoid conflicts with the
+	// included adv7282m overlay file.
+
+	fragment@101 {
+		target = <&adv728x>;
+		__dormant__ {
+			compatible = "adi,adv7280-m";
+		};
+	};
+	fragment@102 {
+		target = <&adv728x>;
+		__dormant__ {
+			compatible = "adi,adv7281-m";
+		};
+	};
+	fragment@103 {
+		target = <&adv728x>;
+		__dormant__ {
+			compatible = "adi,adv7281-ma";
+		};
+	};
+
+	__overrides__ {
+		adv7280m = <0>, "+101";
+		adv7281m = <0>, "+102";
+		adv7281ma = <0>, "+103";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/akkordion-iqdacplus-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/akkordion-iqdacplus-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/akkordion-iqdacplus-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/akkordion-iqdacplus-overlay.dts	2021-07-25 16:45:35.138807726 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// Definitions for Digital Dreamtime Akkordion using IQaudIO DAC+ or DACZero
+/dts-v1/;
+/plugin/;
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&i2s>;
+		__overlay__ {
+			status = "okay";
+		};
+	};
+
+	fragment@1 {
+		target = <&i2c1>;
+		__overlay__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			status = "okay";
+
+			pcm5122@4c {
+				#sound-dai-cells = <0>;
+				compatible = "ti,pcm5122";
+				reg = <0x4c>;
+				AVDD-supply = <&vdd_3v3_reg>;
+				DVDD-supply = <&vdd_3v3_reg>;
+				CPVDD-supply = <&vdd_3v3_reg>;
+				status = "okay";
+			};
+		};
+	};
+
+	fragment@2 {
+		target = <&sound>;
+		frag2: __overlay__ {
+			compatible = "iqaudio,iqaudio-dac";
+			card_name = "Akkordion";
+			dai_name = "IQaudIO DAC";
+			dai_stream_name = "IQaudIO DAC HiFi";
+			i2s-controller = <&i2s>;
+			status = "okay";
+		};
+	};
+
+	__overrides__ {
+		24db_digital_gain = <&frag2>,"iqaudio,24db_digital_gain?";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/allo-boss2-dac-audio-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/allo-boss2-dac-audio-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/allo-boss2-dac-audio-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/allo-boss2-dac-audio-overlay.dts	2021-07-25 16:45:35.138807726 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/* * Definitions for Allo Boss2 DAC boards
+ */
+
+/dts-v1/;
+/plugin/;
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&i2s>;
+		__overlay__ {
+			#sound-dai-cells = <0>;
+			status = "okay";
+			cpu_port: port {
+				cpu_endpoint: endpoint {
+					remote-endpoint = <&codec_endpoint>;
+					bitclock-master = <&codec_endpoint>;
+					frame-master = <&codec_endpoint>;
+					dai-format = "i2s";
+				};
+			};
+		};
+	};
+
+	fragment@1 {
+		target = <&i2c1>;
+		__overlay__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			status = "okay";
+			allo-cs43130@30 {
+				#sound-dai-cells = <0>;
+				compatible = "allo,allo-cs43198";
+				clock44-gpio = <&gpio 5 0>;
+				clock48-gpio = <&gpio 6 0>;
+				reg = <0x30>;
+				port {
+					codec_endpoint: endpoint {
+					remote-endpoint = <&cpu_endpoint>;
+					};
+				};
+			};
+		};
+	};
+
+	fragment@2 {
+		target = <&sound>;
+		boss2_dac: __overlay__ {
+			compatible = "audio-graph-card";
+			label = "Allo Boss2";
+			dais = <&cpu_port>;
+			status = "okay";
+		};
+	};
+};
+
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/allo-boss-dac-pcm512x-audio-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/allo-boss-dac-pcm512x-audio-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/allo-boss-dac-pcm512x-audio-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/allo-boss-dac-pcm512x-audio-overlay.dts	2021-07-25 16:45:35.138807726 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/*
+ * Definitions for Allo Boss DAC board
+ */
+
+/dts-v1/;
+/plugin/;
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target-path = "/";
+		__overlay__ {
+			boss_osc: boss_osc {
+				compatible = "allo,dac-clk";
+				#clock-cells = <0>;
+			};
+		};
+	};
+
+	fragment@1 {
+		target = <&i2s>;
+		__overlay__ {
+			status = "okay";
+		};
+	};
+
+	fragment@2 {
+		target = <&i2c1>;
+		__overlay__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			status = "okay";
+
+			pcm5122@4d {
+				#sound-dai-cells = <0>;
+				compatible = "ti,pcm5122";
+				clocks = <&boss_osc>;
+				reg = <0x4d>;
+				status = "okay";
+			};
+		};
+	};
+
+	fragment@3 {
+		target = <&sound>;
+		boss_dac: __overlay__ {
+			compatible = "allo,boss-dac";
+			i2s-controller = <&i2s>;
+			mute-gpios = <&gpio 6 1>;
+			status = "okay";
+		};
+	};
+
+	__overrides__ {
+		24db_digital_gain = <&boss_dac>,"allo,24db_digital_gain?";
+		slave = <&boss_dac>,"allo,slave?";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/allo-digione-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/allo-digione-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/allo-digione-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/allo-digione-overlay.dts	2021-07-25 16:45:35.138807726 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// Definitions for Allo DigiOne
+/dts-v1/;
+/plugin/;
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&i2s>;
+		__overlay__ {
+			status = "okay";
+		};
+	};
+
+	fragment@1 {
+		target = <&i2c1>;
+		__overlay__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			status = "okay";
+
+			wm8804@3b {
+				#sound-dai-cells = <0>;
+				compatible = "wlf,wm8804";
+				reg = <0x3b>;
+				PVDD-supply = <&vdd_3v3_reg>;
+				DVDD-supply = <&vdd_3v3_reg>;
+				status = "okay";
+				wlf,reset-gpio = <&gpio 17 0>;
+			};
+		};
+	};
+
+	fragment@2 {
+		target = <&sound>;
+		__overlay__ {
+			compatible = "allo,allo-digione";
+			i2s-controller = <&i2s>;
+			status = "okay";
+			clock44-gpio = <&gpio 5 0>;
+			clock48-gpio = <&gpio 6 0>;
+		};
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/allo-katana-dac-audio-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/allo-katana-dac-audio-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/allo-katana-dac-audio-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/allo-katana-dac-audio-overlay.dts	2021-07-25 16:45:35.138807726 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/*
+ * Definitions for Allo Katana DAC boards
+ */
+
+/dts-v1/;
+/plugin/;
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&i2s>;
+		__overlay__ {
+			#sound-dai-cells = <0>;
+			status = "okay";
+			cpu_port: port {
+				cpu_endpoint: endpoint {
+					remote-endpoint = <&codec_endpoint>;
+					bitclock-master = <&codec_endpoint>;
+					frame-master = <&codec_endpoint>;
+					dai-format = "i2s";
+				};
+			};
+		};
+	};
+
+	fragment@1 {
+		target = <&i2c1>;
+		__overlay__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			status = "okay";
+
+			allo-katana-codec@30 {
+				#sound-dai-cells = <0>;
+				compatible = "allo,allo-katana-codec";
+				reg = <0x30>;
+				port {
+					codec_endpoint: endpoint {
+					remote-endpoint = <&cpu_endpoint>;
+					};
+				};
+			};
+		};
+	};
+
+	fragment@2 {
+		target = <&sound>;
+		katana_dac: __overlay__ {
+			compatible = "audio-graph-card";
+			label = "Allo Katana";
+			dais = <&cpu_port>;
+			status = "okay";
+		};
+	};
+};
+
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/allo-piano-dac-pcm512x-audio-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/allo-piano-dac-pcm512x-audio-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/allo-piano-dac-pcm512x-audio-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/allo-piano-dac-pcm512x-audio-overlay.dts	2021-07-25 16:45:35.138807726 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/*
+ * Definitions for Allo Piano DAC (2.0/2.1) boards
+ *
+ * NB. The Piano DAC 2.1 board contains 2x TI PCM5142 DAC's. One DAC is stereo
+ * (left/right) and the other provides a subwoofer output, using DSP on the
+ * chip for digital high/low pass crossover.
+ * The initial support for this hardware, that doesn't require any codec driver
+ * modifications, uses only one DAC chip for stereo (left/right) output, the
+ * chip with 0x4c slave address. The other chip at 0x4d is currently ignored!
+ */
+
+/dts-v1/;
+/plugin/;
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&i2s>;
+		__overlay__ {
+			status = "okay";
+		};
+	};
+
+	fragment@1 {
+		target = <&i2c1>;
+		__overlay__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			status = "okay";
+
+			pcm5142@4c {
+				#sound-dai-cells = <0>;
+				compatible = "ti,pcm5142";
+				reg = <0x4c>;
+				status = "okay";
+			};
+		};
+	};
+
+	fragment@2 {
+		target = <&sound>;
+		piano_dac: __overlay__ {
+			compatible = "allo,piano-dac";
+			i2s-controller = <&i2s>;
+			status = "okay";
+		};
+	};
+
+	__overrides__ {
+		24db_digital_gain =
+			<&piano_dac>,"allo,24db_digital_gain?";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/allo-piano-dac-plus-pcm512x-audio-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/allo-piano-dac-plus-pcm512x-audio-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/allo-piano-dac-plus-pcm512x-audio-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/allo-piano-dac-plus-pcm512x-audio-overlay.dts	2021-07-25 16:45:35.138807726 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// Definitions for Piano DAC
+/dts-v1/;
+/plugin/;
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&i2s>;
+		__overlay__ {
+			status = "okay";
+		};
+	};
+
+	fragment@1 {
+		target = <&i2c1>;
+		__overlay__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			status = "okay";
+
+			allo_pcm5122_4c: pcm5122@4c {
+				#sound-dai-cells = <0>;
+				compatible = "ti,pcm5122";
+				reg = <0x4c>;
+				sound-name-prefix = "Main";
+				status = "okay";
+			};
+			allo_pcm5122_4d: pcm5122@4d {
+				#sound-dai-cells = <0>;
+				compatible = "ti,pcm5122";
+				reg = <0x4d>;
+				sound-name-prefix = "Sub";
+				status = "okay";
+			};
+		};
+	};
+
+	fragment@2 {
+		target = <&sound>;
+		piano_dac: __overlay__ {
+			compatible = "allo,piano-dac-plus";
+			audio-codec = <&allo_pcm5122_4c &allo_pcm5122_4d>;
+			i2s-controller = <&i2s>;
+			mute1-gpios = <&gpio 6 1>;
+			mute2-gpios = <&gpio 25 1>;
+			status = "okay";
+		};
+	};
+
+	__overrides__ {
+		24db_digital_gain =
+			<&piano_dac>,"allo,24db_digital_gain?";
+		glb_mclk =
+			<&piano_dac>,"allo,glb_mclk?";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/anyspi-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/anyspi-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/anyspi-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/anyspi-overlay.dts	2021-07-25 16:45:35.138807726 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/*
+ * Universal device tree overlay for SPI devices
+ */
+
+/dts-v1/;
+/plugin/;
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&spidev0>;
+		__dormant__ {
+			status = "disabled";
+		};
+	};
+
+	fragment@1 {
+		target = <&spidev1>;
+		__dormant__ {
+			status = "disabled";
+		};
+	};
+
+	fragment@2 {
+		target-path = "spi1/spidev@0";
+		__dormant__ {
+			status = "disabled";
+		};
+	};
+
+	fragment@3 {
+		target-path = "spi1/spidev@1";
+		__dormant__ {
+			status = "disabled";
+		};
+	};
+
+	fragment@4 {
+		target-path = "spi1/spidev@2";
+		__dormant__ {
+			status = "disabled";
+		};
+	};
+
+	fragment@5 {
+		target-path = "spi2/spidev@0";
+		__dormant__ {
+			status = "disabled";
+		};
+	};
+
+	fragment@6 {
+		target-path = "spi2/spidev@1";
+		__dormant__ {
+			status = "disabled";
+		};
+	};
+
+	fragment@7 {
+		target-path = "spi2/spidev@2";
+		__dormant__ {
+			status = "disabled";
+		};
+	};
+
+	fragment@8 {
+		target = <&spi0>;
+		__dormant__ {
+			status = "okay";
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			anyspi_00: anyspi@0 {
+				reg = <0>;
+				spi-max-frequency = <500000>;
+			};
+		};
+	};
+
+	fragment@9 {
+		target = <&spi0>;
+		__dormant__ {
+			status = "okay";
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			anyspi_01: anyspi@1 {
+				reg = <1>;
+				spi-max-frequency = <500000>;
+			};
+		};
+	};
+
+	fragment@10 {
+		target = <&spi1>;
+		__dormant__ {
+			status = "okay";
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			anyspi_10: anyspi@0 {
+				reg = <0>;
+				spi-max-frequency = <500000>;
+			};
+		};
+	};
+
+	fragment@11 {
+		target = <&spi1>;
+		__dormant__ {
+			status = "okay";
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			anyspi_11: anyspi@1 {
+				reg = <1>;
+				spi-max-frequency = <500000>;
+			};
+		};
+	};
+
+	fragment@12 {
+		target = <&spi1>;
+		__dormant__ {
+			status = "okay";
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			anyspi_12: anyspi@2 {
+				reg = <2>;
+				spi-max-frequency = <500000>;
+			};
+		};
+	};
+
+	fragment@13 {
+		target = <&spi2>;
+		__dormant__ {
+			status = "okay";
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			anyspi_20: anyspi@0 {
+				reg = <0>;
+				spi-max-frequency = <500000>;
+			};
+		};
+	};
+
+	fragment@14 {
+		target = <&spi2>;
+		__dormant__ {
+			status = "okay";
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			anyspi_21: anyspi@1 {
+				reg = <1>;
+				spi-max-frequency = <500000>;
+			};
+		};
+	};
+
+	fragment@15 {
+		target = <&spi2>;
+		__dormant__ {
+			status = "okay";
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			anyspi_22: anyspi@2 {
+				reg = <2>;
+				spi-max-frequency = <500000>;
+			};
+		};
+	};
+
+	__overrides__ {
+		spi0-0 = <0>, "+0+8";
+		spi0-1 = <0>, "+1+9";
+		spi1-0 = <0>, "+2+10";
+		spi1-1 = <0>, "+3+11";
+		spi1-2 = <0>, "+4+12";
+		spi2-0 = <0>, "+5+13";
+		spi2-1 = <0>, "+6+14";
+		spi2-2 = <0>, "+7+15";
+		dev = <&anyspi_00>,"compatible",
+		      <&anyspi_01>,"compatible",
+		      <&anyspi_10>,"compatible",
+		      <&anyspi_11>,"compatible",
+		      <&anyspi_12>,"compatible",
+		      <&anyspi_20>,"compatible",
+		      <&anyspi_21>,"compatible",
+		      <&anyspi_22>,"compatible";
+		speed = <&anyspi_00>, "spi-max-frequency:0",
+		        <&anyspi_01>, "spi-max-frequency:0",
+		        <&anyspi_10>, "spi-max-frequency:0",
+		        <&anyspi_11>, "spi-max-frequency:0",
+		        <&anyspi_12>, "spi-max-frequency:0",
+		        <&anyspi_20>, "spi-max-frequency:0",
+		        <&anyspi_21>, "spi-max-frequency:0",
+		        <&anyspi_22>, "spi-max-frequency:0";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/apds9960-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/apds9960-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/apds9960-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/apds9960-overlay.dts	2021-07-25 16:45:35.138807726 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// Definitions for APDS-9960 ambient light and gesture sensor
+
+/dts-v1/;
+/plugin/;
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&i2c1>;
+		__overlay__ {
+			status = "okay";
+		};
+	};
+
+	fragment@1 {
+		target = <&gpio>;
+		__overlay__ {
+			apds9960_pins: apds9960_pins@39 {
+				brcm,pins = <4>;
+				brcm,function = <0>;
+			};
+		};
+	};
+
+	fragment@2 {
+		target = <&i2c1>;
+		__overlay__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			apds9960: apds@39 {
+				compatible = "avago,apds9960";
+				reg = <0x39>;
+				status = "okay";
+			};
+		};
+	};
+
+	fragment@3 {
+		target = <&i2c1>;
+		__overlay__ {
+			apds9960_irq: apds@39 {
+				#interrupt-cells=<2>;
+				interrupt-parent = <&gpio>;
+				interrupts = <4 1>;
+			};
+		};
+	};
+
+	__overrides__ {
+		gpiopin = <&apds9960_pins>,"brcm,pins:0",
+				<&apds9960_irq>,"interrupts:0";
+		noints = <0>,"!1!3";
+	};
+};
+
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/applepi-dac-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/applepi-dac-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/applepi-dac-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/applepi-dac-overlay.dts	2021-07-25 16:45:35.138807726 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/dts-v1/;
+/plugin/;
+
+/ {
+    compatible = "brcm,bcm2835";
+
+    fragment@0 {
+        target = <&sound>;
+        __overlay__ {
+            compatible = "simple-audio-card";
+            simple-audio-card,name = "ApplePi-DAC";
+
+            status = "okay";
+
+            playback_link: simple-audio-card,dai-link@1 {
+                format = "i2s";
+
+                p_cpu_dai: cpu {
+                    sound-dai = <&i2s>;
+                    dai-tdm-slot-num = <2>;
+                    dai-tdm-slot-width = <32>;
+                };
+
+                p_codec_dai: codec {
+                    sound-dai = <&codec_out>;
+                };
+            };
+        };
+    };
+
+    fragment@1 {
+        target-path = "/";
+        __overlay__ {
+            codec_out: pcm1794a-codec {
+                #sound-dai-cells = <0>;
+                compatible = "ti,pcm1794a";
+                status = "okay";
+            };
+        };
+    };
+
+    fragment@2 {
+        target = <&i2s>;
+        __overlay__ {
+            #sound-dai-cells = <0>;
+            status = "okay";
+        };
+    };
+};
+
+/*
+   Written by: Leonid Ayzenshtat
+   Company: Orchard Audio (www.orchardaudio.com)
+
+   compile with:
+   dtc -@ -H epapr -O dtb -o ApplePi-DAC.dtbo -W no-unit_address_vs_reg ApplePi-DAC.dts
+*/
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/at86rf233-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/at86rf233-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/at86rf233-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/at86rf233-overlay.dts	2021-07-25 16:45:35.138807726 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/dts-v1/;
+/plugin/;
+
+/* Overlay for Atmel AT86RF233 IEEE 802.15.4 WPAN transceiver on spi0.0 */
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&spi0>;
+		__overlay__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			status = "okay";
+
+			lowpan0: at86rf233@0 {
+				compatible = "atmel,at86rf233";
+				reg = <0>;
+				interrupt-parent = <&gpio>;
+				interrupts = <23 4>; /* active high */
+				reset-gpio = <&gpio 24 1>;
+				sleep-gpio = <&gpio 25 1>;
+				spi-max-frequency = <3000000>;
+				xtal-trim = /bits/ 8 <0xf>;
+			};
+		};
+	};
+
+	fragment@1 {
+		target = <&spidev0>;
+		__overlay__ {
+			status = "disabled";
+		};
+	};
+
+	fragment@2 {
+		target = <&gpio>;
+		__overlay__ {
+			lowpan0_pins: lowpan0_pins {
+				brcm,pins = <23 24 25>;
+				brcm,function = <0 1 1>; /* in out out */
+			};
+		};
+	};
+
+	__overrides__ {
+		interrupt = <&lowpan0>, "interrupts:0",
+			<&lowpan0_pins>, "brcm,pins:0";
+		reset     = <&lowpan0>, "reset-gpio:4",
+			<&lowpan0_pins>, "brcm,pins:4";
+		sleep     = <&lowpan0>, "sleep-gpio:4",
+			<&lowpan0_pins>, "brcm,pins:8";
+		speed     = <&lowpan0>, "spi-max-frequency:0";
+		trim      = <&lowpan0>, "xtal-trim.0";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/audioinjector-addons-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/audioinjector-addons-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/audioinjector-addons-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/audioinjector-addons-overlay.dts	2021-07-25 16:45:35.138807726 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// Definitions for audioinjector.net audio add on soundcard
+/dts-v1/;
+/plugin/;
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&i2s>;
+		__overlay__ {
+			status = "okay";
+		};
+	};
+
+	fragment@1 {
+		target-path = "/";
+		__overlay__ {
+			cs42448_mclk: codec-mclk {
+				compatible = "fixed-clock";
+				#clock-cells = <0>;
+				clock-frequency = <49152000>;
+			};
+		};
+	};
+
+	fragment@2 {
+		target = <&i2c1>;
+		__overlay__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			status = "okay";
+
+			cs42448: cs42448@48 {
+				#sound-dai-cells = <0>;
+				compatible = "cirrus,cs42448";
+				reg = <0x48>;
+				clocks = <&cs42448_mclk>;
+				clock-names = "mclk";
+				status = "okay";
+			};
+		};
+	};
+
+	fragment@3 {
+		target = <&sound>;
+		snd: __overlay__ {
+			compatible = "ai,audioinjector-octo-soundcard";
+			mult-gpios = <&gpio 27 0>, <&gpio 22 0>, <&gpio 23 0>,
+				     <&gpio 24 0>;
+			reset-gpios = <&gpio 5 0>;
+			i2s-controller = <&i2s>;
+			codec = <&cs42448>;
+			status = "okay";
+		};
+	};
+
+	__overrides__ {
+		non-stop-clocks = <&snd>, "non-stop-clocks?";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/audioinjector-isolated-soundcard-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/audioinjector-isolated-soundcard-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/audioinjector-isolated-soundcard-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/audioinjector-isolated-soundcard-overlay.dts	2021-07-25 16:45:35.138807726 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// Definitions for audioinjector.net audio isolated soundcard
+/dts-v1/;
+/plugin/;
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&i2s>;
+		__overlay__ {
+			status = "okay";
+		};
+	};
+
+	fragment@1 {
+		target-path = "/";
+		__overlay__ {
+			cs4272_mclk: codec-mclk {
+				compatible = "fixed-clock";
+				#clock-cells = <0>;
+				clock-frequency = <24576000>;
+			};
+		};
+	};
+
+	fragment@2 {
+		target = <&i2c1>;
+		__overlay__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			status = "okay";
+
+			cs4272: cs4271@10 {
+				#sound-dai-cells = <0>;
+				compatible = "cirrus,cs4271";
+				reg = <0x10>;
+				reset-gpio = <&gpio 5 0>;
+				clocks = <&cs4272_mclk>;
+				clock-names = "mclk";
+				status = "okay";
+			};
+		};
+	};
+
+	fragment@3 {
+		target = <&sound>;
+		snd: __overlay__ {
+			compatible = "ai,audioinjector-isolated-soundcard";
+			mute-gpios = <&gpio 17 0>;
+			i2s-controller = <&i2s>;
+			codec = <&cs4272>;
+			status = "okay";
+		};
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/audioinjector-ultra-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/audioinjector-ultra-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/audioinjector-ultra-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/audioinjector-ultra-overlay.dts	2021-07-25 16:45:35.138807726 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// Definitions for audioinjector.net audio add on soundcard
+/dts-v1/;
+/plugin/;
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&i2s>;
+		__overlay__ {
+			status = "okay";
+		};
+	};
+
+	fragment@1 {
+		target = <&i2c1>;
+		__overlay__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			status = "okay";
+
+			cs4265: cs4265@4e {
+				#sound-dai-cells = <0>;
+				compatible = "cirrus,cs4265";
+				reg = <0x4e>;
+				reset-gpios = <&gpio 5 0>;
+				status = "okay";
+			};
+		};
+	};
+
+	fragment@2 {
+		target = <&sound>;
+		__overlay__ {
+			compatible = "simple-audio-card";
+			i2s-controller = <&i2s>;
+			status = "okay";
+
+			simple-audio-card,name = "audioinjector-ultra";
+
+			simple-audio-card,widgets =
+				"Line", "OUTPUTS",
+				"Line", "INPUTS";
+
+			simple-audio-card,routing =
+				"OUTPUTS","LINEOUTL",
+				"OUTPUTS","LINEOUTR",
+				"OUTPUTS","SPDIFOUT",
+				"LINEINL","INPUTS",
+				"LINEINR","INPUTS",
+				"MICL","INPUTS",
+				"MICR","INPUTS";
+
+			simple-audio-card,format = "i2s";
+
+			simple-audio-card,bitclock-master = <&sound_master>;
+			simple-audio-card,frame-master = <&sound_master>;
+
+			simple-audio-card,cpu {
+				sound-dai = <&i2s>;
+				dai-tdm-slot-num = <2>;
+				dai-tdm-slot-width = <32>;
+			};
+
+			sound_master: simple-audio-card,codec {
+				sound-dai = <&cs4265>;
+				system-clock-frequency = <12288000>;
+			};
+		};
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/audioinjector-wm8731-audio-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/audioinjector-wm8731-audio-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/audioinjector-wm8731-audio-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/audioinjector-wm8731-audio-overlay.dts	2021-07-25 16:45:35.138807726 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// Definitions for audioinjector.net audio add on soundcard
+/dts-v1/;
+/plugin/;
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&i2s>;
+		__overlay__ {
+			status = "okay";
+		};
+	};
+
+	fragment@1 {
+		target = <&i2c1>;
+		__overlay__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			status = "okay";
+
+			wm8731@1a {
+				#sound-dai-cells = <0>;
+				compatible = "wlf,wm8731";
+				reg = <0x1a>;
+				status = "okay";
+			};
+		};
+	};
+
+	fragment@2 {
+		target = <&sound>;
+		__overlay__ {
+			compatible = "ai,audioinjector-pi-soundcard";
+			i2s-controller = <&i2s>;
+			status = "okay";
+		};
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/audiosense-pi-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/audiosense-pi-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/audiosense-pi-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/audiosense-pi-overlay.dts	2021-07-25 16:45:35.138807726 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// Definitions for audiosense add on soundcard
+/dts-v1/;
+/plugin/;
+#include <dt-bindings/pinctrl/bcm2835.h>
+#include <dt-bindings/gpio/gpio.h>
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&i2s>;
+		__overlay__ {
+			status = "okay";
+		};
+	};
+
+	fragment@1 {
+		target-path = "/";
+		__overlay__ {
+			codec_reg_1v8: codec-reg-1v8 {
+				compatible = "regulator-fixed";
+				regulator-name = "tlv320aic3204_1v8";
+				regulator-min-microvolt = <1800000>;
+				regulator-max-microvolt = <1800000>;
+				regulator-always-on;
+			};
+
+			/* audio external oscillator */
+			codec_osc: codec_osc {
+				compatible = "fixed-clock";
+				#clock-cells = <0>;
+				clock-frequency = <12000000>;	/* 12 MHz */
+			};
+		};
+	};
+
+	fragment@2 {
+		target = <&gpio>;
+		__overlay__ {
+			codec_rst: codec-rst {
+				brcm,pins = <26>;
+				brcm,function = <BCM2835_FSEL_GPIO_OUT>;
+			};
+		};
+	};
+
+	fragment@3 {
+		target = <&i2c1>;
+		__overlay__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			status = "okay";
+
+			codec: tlv320aic32x4@18 {
+				#sound-dai-cells = <0>;
+				compatible = "ti,tlv320aic32x4";
+				reg = <0x18>;
+
+				clocks = <&codec_osc>;
+				clock-names = "mclk";
+
+				iov-supply = <&vdd_3v3_reg>;
+				ldoin-supply = <&vdd_3v3_reg>;
+
+				gpio-controller;
+				#gpio-cells = <2>;
+				reset-gpios = <&gpio 26 GPIO_ACTIVE_HIGH>;
+
+				status = "okay";
+			};
+		};
+	};
+
+	fragment@4 {
+		target = <&sound>;
+		__overlay__ {
+			compatible = "as,audiosense-pi";
+			i2s-controller = <&i2s>;
+			status = "okay";
+		};
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/audremap-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/audremap-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/audremap-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/audremap-overlay.dts	2021-07-25 16:45:35.138807726 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/dts-v1/;
+/plugin/;
+
+/ {
+        compatible = "brcm,bcm2835";
+
+        fragment@0 {
+                target = <&audio_pins>;
+                frag0: __overlay__ {
+                };
+        };
+
+	fragment@1 {
+                target = <&audio_pins>;
+                __overlay__ {
+                        brcm,pins = < 12 13 >;
+                        brcm,function = < 4 >; /* alt0 alt0 */
+                };
+        };
+
+	fragment@2 {
+		target = <&audio_pins>;
+		__dormant__ {
+                        brcm,pins = < 18 19 >;
+                        brcm,function = < 2 >; /* alt5 alt5 */
+		};
+	};
+
+	fragment@3 {
+		target = <&audio>;
+		__overlay__  {
+			brcm,disable-headphones = <0>;
+		};
+	};
+
+	__overrides__ {
+		swap_lr = <&frag0>, "swap_lr?";
+		enable_jack = <&frag0>, "enable_jack?";
+		pins_12_13 = <0>,"+1-2";
+		pins_18_19 = <0>,"-1+2";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/balena-fin-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/balena-fin-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/balena-fin-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/balena-fin-overlay.dts	2021-07-25 16:45:35.138807726 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/dts-v1/;
+/plugin/;
+
+#include <dt-bindings/gpio/gpio.h>
+
+/{
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&mmcnr>;
+		__overlay__ {
+			pinctrl-names = "default";
+			pinctrl-0 = <&sdio_pins>;
+			bus-width = <4>;
+			brcm,overclock-50 = <35>;
+			status = "okay";
+		};
+	};
+
+	fragment@1 {
+		target = <&gpio>;
+		__overlay__ {
+			sdio_pins: sdio_pins {
+				brcm,pins = <34 35 36 37 38 39>;
+				brcm,function = <7>; /* ALT3 = SD1 */
+				brcm,pull = <0 2 2 2 2 2>;
+			};
+
+			power_ctrl_pins: power_ctrl_pins {
+				brcm,pins = <40>;
+				brcm,function = <1>; // out
+			};
+		};
+	};
+
+	fragment@2 {
+		target-path = "/";
+		__overlay__ {
+			// We should switch to mmc-pwrseq-sd8787 after making it
+			// compatible with sd8887
+			// Currently that module requires two GPIOs to function since it
+			// targets a slightly different chip
+			power_ctrl: power_ctrl {
+				compatible = "gpio-poweroff";
+				gpios = <&gpio 40 1>;
+				force;
+				pinctrl-names = "default";
+				pinctrl-0 = <&power_ctrl_pins>;
+			};
+
+			i2c_soft: i2c@0 {
+				compatible = "i2c-gpio";
+				gpios = <&gpio 43 (GPIO_ACTIVE_HIGH|GPIO_OPEN_DRAIN) /* sda */
+				         &gpio 42 (GPIO_ACTIVE_HIGH|GPIO_OPEN_DRAIN) /* scl */>;
+				i2c-gpio,delay-us = <5>;
+				i2c-gpio,scl-open-drain;
+				i2c-gpio,sda-open-drain;
+				#address-cells = <1>;
+				#size-cells = <0>;
+			};
+
+			sd8xxx-wlan {
+				drvdbg = <0x6>;
+				drv_mode = <0x1>;
+				cfg80211_wext = <0xf>;
+				sta_name = "wlan";
+				wfd_name = "p2p";
+				cal_data_cfg = "none";
+			};
+		};
+	};
+
+	fragment@3 {
+		target = <&i2c_soft>;
+		__overlay__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			status = "okay";
+
+			gpio_expander: gpio_expander@20 {
+				compatible = "nxp,pca9554";
+				gpio-controller;
+				#gpio-cells = <2>;
+				reg = <0x20>;
+				status = "okay";
+			};
+
+			// rtc clock
+			ds1307: ds1307@68 {
+				compatible = "dallas,ds1307";
+				reg = <0x68>;
+				status = "okay";
+			};
+
+			// RGB LEDs (>= v1.1.0)
+			pca9633: pca9633@62 {
+				compatible = "nxp,pca9633";
+				reg = <0x62>;
+				#address-cells = <1>;
+				#size-cells = <0>;
+
+				red@0 {
+					label = "red";
+					reg = <0>;
+					linux,default-trigger = "none";
+				};
+				green@1 {
+					label = "green";
+					reg = <1>;
+					linux,default-trigger = "none";
+				};
+				blue@2 {
+					label = "blue";
+					reg = <2>;
+					linux,default-trigger = "none";
+				};
+				unused@3 {
+					label = "unused";
+					reg = <3>;
+					linux,default-trigger = "none";
+				};
+			};
+		};
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/cap1106-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/cap1106-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/cap1106-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/cap1106-overlay.dts	2021-07-25 16:45:35.138807726 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// Overlay for cap1106 from  Microchip Semiconductor
+// add CONFIG_KEYBOARD_CAP11XX=y
+
+/dts-v1/;
+/plugin/;
+
+/ {
+        compatible = "brcm,bcm2835";
+        fragment@0 {
+                target = <&i2c1>;
+                __overlay__{
+                        status = "okay";
+                        cap1106: cap1106@28 {
+                                compatible = "microchip,cap1106";
+                                pinctrl-0 = <&cap1106_pins>;
+                                pinctrl-names = "default";
+                                interrupt-parent = <&gpio>;
+                                interrupts = <4 2>;
+                                reg = <0x28>;
+                                autorepeat;
+                                microchip,sensor-gain = <2>;
+
+                                linux,keycodes = <2>,           /* KEY_1 */
+                                                <3>,            /* KEY_2 */
+                                                <4>,            /* KEY_3 */
+                                                <5>,            /* KEY_4 */
+                                                <6>,            /* KEY_5 */
+                                                <7>;            /* KEY_6 */
+
+                                #address-cells = <1>;
+                                #size-cells = <0>;
+                                status = "okay";
+
+                        };
+                };
+        };
+        fragment@1 {
+                target = <&gpio>;
+                __overlay__ {
+                        cap1106_pins: cap1106_pins {
+                                brcm,pins = <4>;
+                                brcm,function = <0>; /* in */
+                                brcm,pull = <0>; /* none */
+                        };
+                };
+        };
+
+        __overrides__ {
+                int_pin = <&cap1106>, "interrupts:0",
+                          <&cap1106_pins>, "brcm,pins:0";
+        };
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/chipdip-i2s-master-dac-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/chipdip-i2s-master-dac-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/chipdip-i2s-master-dac-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/chipdip-i2s-master-dac-overlay.dts	2021-07-25 16:45:35.138807726 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/*
+ * Device Tree overlay for ChipDip I2S master DAC
+ */
+
+/dts-v1/;
+/plugin/;
+
+/ {
+    compatible = "brcm,bcm2835";
+    fragment@0 {
+        target = <&sound>;
+        __overlay__ {
+            compatible = "simple-audio-card";
+            simple-audio-card,name = "ChipDip I2S master DAC";
+            status="okay";
+            playback_link: simple-audio-card,dai-link@0 {
+                format = "i2s";
+		bitclock-master = <&p_codec_dai>;
+                frame-master = <&p_codec_dai>;
+                p_cpu_dai: cpu {
+                        sound-dai = <&i2s>;
+			dai-tdm-slot-num = <2>;
+			dai-tdm-slot-width = <32>;
+				};
+
+                p_codec_dai: codec {
+                        sound-dai = <&codec_out>;
+                };
+            };
+        };
+    }; 
+
+    fragment@1 {
+        target-path = "/";
+        __overlay__ {
+            codec_out: spdif-transmitter {
+                #address-cells = <0>;
+                #size-cells = <0>;
+                #sound-dai-cells = <0>;
+                compatible = "linux,spdif-dit";
+                status = "okay";
+            };
+        };
+    };
+ 
+    fragment@2 {
+        target = <&i2s>;
+        __overlay__ {
+            #sound-dai-cells = <0>;
+            status = "okay";
+        };
+    };
+}; 
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/cma-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/cma-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/cma-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/cma-overlay.dts	2021-07-25 16:45:35.138807726 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/*
+ * cma.dts
+ */
+
+/dts-v1/;
+/plugin/;
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&cma>;
+		frag0: __overlay__ {
+			/*
+			 * The default size when using this overlay is 256 MB
+			 * and should be kept as is for backwards
+			 * compatibility.
+			 */
+			size = <0x10000000>;
+		};
+	};
+
+	__overrides__ {
+		cma-512 = <&frag0>,"size:0=",<0x20000000>;
+		cma-448 = <&frag0>,"size:0=",<0x1c000000>;
+		cma-384 = <&frag0>,"size:0=",<0x18000000>;
+		cma-320 = <&frag0>,"size:0=",<0x14000000>;
+		cma-256 = <&frag0>,"size:0=",<0x10000000>;
+		cma-192 = <&frag0>,"size:0=",<0xC000000>;
+		cma-128 = <&frag0>,"size:0=",<0x8000000>;
+		cma-96  = <&frag0>,"size:0=",<0x6000000>;
+		cma-64  = <&frag0>,"size:0=",<0x4000000>;
+		cma-size = <&frag0>,"size:0"; /* in bytes, 4MB aligned */
+		cma-default = <0>,"-0";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/dht11-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/dht11-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/dht11-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/dht11-overlay.dts	2021-07-25 16:45:35.138807726 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/*
+ * Overlay for the DHT11/21/22 humidity/temperature sensor modules.
+ */
+/dts-v1/;
+/plugin/;
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target-path = "/";
+		__overlay__ {
+
+			dht11: dht11@0 {
+				compatible = "dht11";
+				pinctrl-names = "default";
+				pinctrl-0 = <&dht11_pins>;
+				gpios = <&gpio 4 0>;
+				status = "okay";
+			};
+		};
+	};
+
+	fragment@1 {
+		target = <&gpio>;
+		__overlay__ {
+			dht11_pins: dht11_pins@0 {
+				brcm,pins = <4>;
+				brcm,function = <0>; // in
+				brcm,pull = <0>; // off
+			};
+		};
+	};
+
+	__overrides__ {
+		gpiopin = <&dht11_pins>,"brcm,pins:0",
+			<&dht11_pins>, "reg:0",
+			<&dht11>,"gpios:4",
+			<&dht11>,"reg:0";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/dionaudio-loco-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/dionaudio-loco-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/dionaudio-loco-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/dionaudio-loco-overlay.dts	2021-07-25 16:45:35.138807726 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// Definitions for Dion Audio LOCO DAC-AMP
+
+/*
+ * PCM5242 DAC (in hardware mode) and TPA3118 AMP.
+ */
+
+/dts-v1/;
+/plugin/;
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&i2s>;
+		__overlay__ {
+			status = "okay";
+		};
+	};
+
+	fragment@1 {
+		target-path = "/";
+		__overlay__ {
+			pcm5102a-codec {
+				#sound-dai-cells = <0>;
+				compatible = "ti,pcm5102a";
+				status = "okay";
+			};
+		};
+	};
+
+	fragment@2 {
+		target = <&sound>;
+		__overlay__ {
+			compatible = "dionaudio,loco-pcm5242-tpa3118";
+			i2s-controller = <&i2s>;
+			status = "okay";
+		};
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/dionaudio-loco-v2-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/dionaudio-loco-v2-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/dionaudio-loco-v2-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/dionaudio-loco-v2-overlay.dts	2021-07-25 16:45:35.138807726 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/*
+ * Definitions for Dion Audio LOCO-V2 DAC-AMP
+ *  eg. dtoverlay=dionaudio-loco-v2
+ *
+ * PCM5242 DAC (in software mode) and TPA3255 AMP.
+ */
+
+/dts-v1/;
+/plugin/;
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&sound>;
+		frag0: __overlay__ {
+			compatible = "dionaudio,dionaudio-loco-v2";
+			i2s-controller = <&i2s>;
+			status = "okay";
+		};
+	};
+
+	fragment@1 {
+		target = <&i2s>;
+		__overlay__ {
+			status = "okay";
+		};
+	};
+
+	fragment@2 {
+		target = <&i2c1>;
+		__overlay__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			status = "okay";
+
+			pcm5122@4c {
+				#sound-dai-cells = <0>;
+				compatible = "ti,pcm5122";
+				reg = <0x4d>;
+				status = "okay";
+			};
+		};
+	};
+
+	__overrides__ {
+		24db_digital_gain = <&frag0>,"dionaudio,24db_digital_gain?";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/disable-bt-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/disable-bt-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/disable-bt-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/disable-bt-overlay.dts	2021-07-25 16:45:35.138807726 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/dts-v1/;
+/plugin/;
+
+/* Disable Bluetooth and restore UART0/ttyAMA0 over GPIOs 14 & 15.
+   To disable the systemd service that initialises the modem so it doesn't use
+   the UART:
+
+       sudo systemctl disable hciuart
+*/
+
+#include <dt-bindings/gpio/gpio.h>
+
+/{
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&uart1>;
+		__overlay__ {
+			status = "disabled";
+		};
+	};
+
+	fragment@1 {
+		target = <&uart0>;
+		__overlay__ {
+			pinctrl-names = "default";
+			pinctrl-0 = <&uart0_pins>;
+			status = "okay";
+		};
+	};
+
+	fragment@2 {
+		target = <&bt>;
+		__overlay__ {
+			status = "disabled";
+		};
+	};
+
+	fragment@3 {
+		target = <&uart0_pins>;
+		__overlay__ {
+			brcm,pins;
+			brcm,function;
+			brcm,pull;
+		};
+	};
+
+	fragment@4 {
+		target = <&bt_pins>;
+		__overlay__ {
+			brcm,pins;
+			brcm,function;
+			brcm,pull;
+		};
+	};
+
+	fragment@5 {
+		target-path = "/aliases";
+		__overlay__ {
+			serial0 = "/soc/serial@7e201000";
+			serial1 = "/soc/serial@7e215040";
+		};
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/disable-wifi-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/disable-wifi-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/disable-wifi-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/disable-wifi-overlay.dts	2021-07-25 16:45:35.138807726 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/dts-v1/;
+/plugin/;
+
+/{
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&mmc>;
+		__overlay__ {
+			status = "disabled";
+		};
+	};
+
+	fragment@1 {
+		target = <&mmcnr>;
+		__overlay__ {
+			status = "disabled";
+		};
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/dpi18cpadhi-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/dpi18cpadhi-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/dpi18cpadhi-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/dpi18cpadhi-overlay.dts	2021-07-25 16:45:35.138807726 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/*
+ * dpi18cpadhi-overlay.dts
+ */
+
+/dts-v1/;
+/plugin/;
+
+/{
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&fb>;
+		__overlay__ {
+			pinctrl-names = "default";
+			pinctrl-0 = <&dpi_18bit_cpadhi_gpio0>;
+		};
+	};
+
+	fragment@1 {
+		target = <&vc4>;
+		__overlay__ {
+			pinctrl-names = "default";
+			pinctrl-0 = <&dpi_18bit_cpadhi_gpio0>;
+		};
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/dpi18-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/dpi18-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/dpi18-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/dpi18-overlay.dts	2021-07-25 16:45:35.138807726 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/dts-v1/;
+/plugin/;
+
+/{
+	compatible = "brcm,bcm2835";
+
+	// There is no DPI driver module, but we need a platform device
+	// node (that doesn't already use pinctrl) to hang the pinctrl
+	// reference on - leds will do
+
+	fragment@0 {
+		target = <&fb>;
+		__overlay__ {
+			pinctrl-names = "default";
+			pinctrl-0 = <&dpi18_pins>;
+		};
+	};
+
+	fragment@1 {
+		target = <&vc4>;
+		__overlay__ {
+			pinctrl-names = "default";
+			pinctrl-0 = <&dpi18_pins>;
+		};
+	};
+
+	fragment@2 {
+		target = <&gpio>;
+		__overlay__ {
+			dpi18_pins: dpi18_pins {
+				brcm,pins = <0 1 2 3 4 5 6 7 8 9 10 11
+					     12 13 14 15 16 17 18 19 20
+					     21>;
+				brcm,function = <6>; /* alt2 */
+				brcm,pull = <0>; /* no pull */
+			};
+		};
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/dpi24-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/dpi24-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/dpi24-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/dpi24-overlay.dts	2021-07-25 16:45:35.138807726 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/dts-v1/;
+/plugin/;
+
+/{
+	compatible = "brcm,bcm2835";
+
+	// There is no DPI driver module, but we need a platform device
+	// node (that doesn't already use pinctrl) to hang the pinctrl
+	// reference on - leds will do
+
+	fragment@0 {
+		target = <&fb>;
+		__overlay__ {
+			pinctrl-names = "default";
+			pinctrl-0 = <&dpi24_pins>;
+		};
+	};
+
+	fragment@1 {
+		target = <&vc4>;
+		__overlay__ {
+			pinctrl-names = "default";
+			pinctrl-0 = <&dpi24_pins>;
+		};
+	};
+
+	fragment@2 {
+		target = <&gpio>;
+		__overlay__ {
+			dpi24_pins: dpi24_pins {
+				brcm,pins = <0 1 2 3 4 5 6 7 8 9 10 11
+					     12 13 14 15 16 17 18 19 20
+					     21 22 23 24 25 26 27>;
+				brcm,function = <6>; /* alt2 */
+				brcm,pull = <0>; /* no pull */
+			};
+		};
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/draws-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/draws-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/draws-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/draws-overlay.dts	2021-07-25 16:45:35.138807726 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+#include <dt-bindings/clock/bcm2835.h>
+/*
+ * Device tree overlay for the DRAWS Hardware
+ */
+
+/dts-v1/;
+/plugin/;
+
+/ {
+    compatible = "brcm,bcm2835";
+    fragment@0 {
+        target = <&i2s>;
+        __overlay__ {
+            status = "okay";
+        };
+    };
+
+    fragment@1 {
+        target-path = "/";
+        __overlay__ {
+            regulators {
+                compatible = "simple-bus";
+                #address-cells = <1>;
+                #size-cells = <0>;
+
+                udrc0_ldoin: udrc0_ldoin {
+                    compatible = "regulator-fixed";
+                    regulator-name = "ldoin";
+                    regulator-min-microvolt = <3300000>;
+                    regulator-max-microvolt = <3300000>;
+                    regulator-always-on;
+                };
+
+                sc16is752_clk: sc16is752_draws_clk {
+                    compatible = "fixed-clock";
+                    #clock-cells = <0>;
+                    clock-frequency = <1843200>;
+                };
+            };
+
+            pps: pps {
+                compatible = "pps-gpio";
+                pinctrl-names = "default";
+                pinctrl-0 = <&pps_pins>;
+                gpios = <&gpio 7 0>;
+                status = "okay";
+            };
+
+            iio-hwmon {
+                compatible = "iio-hwmon";
+                status = "okay";
+                io-channels = <&tla2024 4>, <&tla2024 5>, <&tla2024 6>,
+                              <&tla2024 7>;
+            };
+        };
+    };
+
+    fragment@2 {
+        target = <&i2c_arm>;
+        __overlay__ {
+            #address-cells = <1>;
+            #size-cells = <0>;
+            status = "okay";
+
+            tlv320aic32x4: tlv320aic32x4@18 {
+                compatible = "ti,tlv320aic32x4";
+                reg = <0x18>;
+                #sound-dai-cells = <0>;
+                status = "okay";
+
+                clocks = <&clocks BCM2835_CLOCK_GP0>;
+                clock-names = "mclk";
+                assigned-clocks = <&clocks BCM2835_CLOCK_GP0>;
+                assigned-clock-rates = <25000000>;
+
+                pinctrl-names = "default";
+                pinctrl-0 = <&gpclk0_pin &aic3204_reset>;
+
+                reset-gpios = <&gpio 13 0>;
+
+                iov-supply = <&udrc0_ldoin>;
+                ldoin-supply = <&udrc0_ldoin>;
+            };
+
+            sc16is752: sc16is752@50 {
+                compatible = "nxp,sc16is752";
+                reg = <0x50>;
+                clocks = <&sc16is752_clk>;
+                interrupt-parent = <&gpio>;
+                interrupts = <17 2>; /* IRQ_TYPE_EDGE_FALLING */
+
+                pinctrl-names = "default";
+                pinctrl-0 = <&sc16is752_irq>;
+            };
+
+            tla2024: tla2024@48 {
+                compatible = "ti,ads1015";
+                reg = <0x48>;
+                #address-cells = <1>;
+                #size-cells = <0>;
+                #io-channel-cells = <1>;
+
+                adc_ch4: channel@4 {
+                    reg = <4>;
+                    ti,gain = <1>;
+                    ti,datarate = <4>;
+                };
+
+                adc_ch5: channel@5 {
+                    reg = <5>;
+                    ti,gain = <1>;
+                    ti,datarate = <4>;
+                };
+
+                adc_ch6: channel@6 {
+                    reg = <6>;
+                    ti,gain = <2>;
+                    ti,datarate = <4>;
+                };
+
+                adc_ch7: channel@7 {
+                    reg = <7>;
+                    ti,gain = <2>;
+                    ti,datarate = <4>;
+                };
+            };
+        };
+    };
+
+    fragment@3 {
+        target = <&sound>;
+        snd: __overlay__ {
+            compatible = "simple-audio-card";
+            i2s-controller = <&i2s>;
+            status = "okay";
+
+            simple-audio-card,name = "draws";
+            simple-audio-card,format = "i2s";
+
+            simple-audio-card,bitclock-master = <&dailink0_master>;
+            simple-audio-card,frame-master = <&dailink0_master>;
+
+            simple-audio-card,widgets =
+                "Line", "Line In",
+                "Line", "Line Out";
+
+            simple-audio-card,routing =
+                "IN1_R", "Line In",
+                "IN1_L", "Line In",
+                "CM_L", "Line In",
+                "CM_R", "Line In",
+                "Line Out", "LOR",
+                "Line Out", "LOL";
+
+            dailink0_master: simple-audio-card,cpu {
+                sound-dai = <&i2s>;
+            };
+
+            simple-audio-card,codec {
+                sound-dai = <&tlv320aic32x4>;
+            };
+        };
+    };
+
+    fragment@4 {
+        target = <&gpio>;
+        __overlay__ {
+            gpclk0_pin: gpclk0_pin {
+                brcm,pins = <4>;
+                brcm,function = <4>;
+            };
+
+            aic3204_reset: aic3204_reset {
+                brcm,pins = <13>;
+                brcm,function = <1>;
+                brcm,pull = <1>;
+            };
+
+            aic3204_gpio: aic3204_gpio {
+                brcm,pins = <26>;
+            };
+
+            sc16is752_irq: sc16is752_irq {
+                brcm,pins = <17>;
+                brcm,function = <0>;
+                brcm,pull = <2>;
+            };
+
+            pps_pins: pps_pins {
+                brcm,pins = <7>;
+                brcm,function = <0>;
+                brcm,pull = <0>;
+            };
+        };
+    };
+
+    __overrides__ {
+        draws_adc_ch4_gain = <&adc_ch4>,"ti,gain:0";
+        draws_adc_ch4_datarate = <&adc_ch4>,"ti,datarate:0";
+        draws_adc_ch5_gain = <&adc_ch5>,"ti,gain:0";
+        draws_adc_ch5_datarate = <&adc_ch5>,"ti,datarate:0";
+        draws_adc_ch6_gain = <&adc_ch6>,"ti,gain:0";
+        draws_adc_ch6_datarate = <&adc_ch6>,"ti,datarate:0";
+        draws_adc_ch7_gain = <&adc_ch7>,"ti,gain:0";
+        draws_adc_ch7_datarate = <&adc_ch7>,"ti,datarate:0";
+        alsaname = <&snd>, "simple-audio-card,name";
+    };
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/dwc2-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/dwc2-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/dwc2-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/dwc2-overlay.dts	2021-07-25 16:45:35.138807726 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/dts-v1/;
+/plugin/;
+
+/{
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&usb>;
+		#address-cells = <1>;
+		#size-cells = <1>;
+		dwc2_usb: __overlay__ {
+			compatible = "brcm,bcm2835-usb";
+			dr_mode = "otg";
+			g-np-tx-fifo-size = <32>;
+			g-rx-fifo-size = <558>;
+			g-tx-fifo-size = <512 512 512 512 512 256 256>;
+			status = "okay";
+		};
+	};
+
+	__overrides__ {
+		dr_mode = <&dwc2_usb>, "dr_mode";
+		g-np-tx-fifo-size = <&dwc2_usb>,"g-np-tx-fifo-size:0";
+		g-rx-fifo-size = <&dwc2_usb>,"g-rx-fifo-size:0";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/dwc-otg-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/dwc-otg-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/dwc-otg-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/dwc-otg-overlay.dts	2021-07-25 16:45:35.138807726 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/dts-v1/;
+/plugin/;
+
+/{
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&usb>;
+		__overlay__ {
+			compatible = "brcm,bcm2708-usb";
+			status = "okay";
+		};
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/edt-ft5406-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/edt-ft5406-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/edt-ft5406-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/edt-ft5406-overlay.dts	2021-07-25 16:45:35.138807726 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/*
+ * Device Tree overlay for RaspberryPi 7" Touchscreen panel
+ *
+ */
+
+/dts-v1/;
+/plugin/;
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&i2c_csi_dsi>;
+		__overlay__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			status = "okay";
+			ft5406: ts@38 {
+				compatible = "edt,edt-ft5406";
+				reg = <0x38>;
+
+				touchscreen-size-x = < 800 >;
+				touchscreen-size-y = < 480 >;
+			};
+		};
+	};
+
+	fragment@1 {
+		target = <&i2c0if>;
+		__overlay__ {
+			status = "okay";
+		};
+	};
+
+	__overrides__ {
+		sizex = <&ft5406>,"touchscreen-size-x:0";
+		sizey = <&ft5406>,"touchscreen-size-y:0";
+		invx = <&ft5406>,"touchscreen-inverted-x?";
+		invy = <&ft5406>,"touchscreen-inverted-y?";
+		swapxy = <&ft5406>,"touchscreen-swapped-x-y?";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/enc28j60-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/enc28j60-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/enc28j60-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/enc28j60-overlay.dts	2021-07-25 16:45:35.138807726 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// Overlay for the Microchip ENC28J60 Ethernet Controller
+/dts-v1/;
+/plugin/;
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&spi0>;
+		__overlay__ {
+			/* needed to avoid dtc warning */
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			status = "okay";
+
+			eth1: enc28j60@0{
+				compatible = "microchip,enc28j60";
+				reg = <0>; /* CE0 */
+				pinctrl-names = "default";
+				pinctrl-0 = <&eth1_pins>;
+				interrupt-parent = <&gpio>;
+				interrupts = <25 0x2>; /* falling edge */
+				spi-max-frequency = <12000000>;
+				status = "okay";
+			};
+		};
+	};
+
+	fragment@1 {
+		target = <&spidev0>;
+		__overlay__ {
+			status = "disabled";
+		};
+	};
+
+	fragment@2 {
+		target = <&gpio>;
+		__overlay__ {
+			eth1_pins: eth1_pins {
+				brcm,pins = <25>;
+				brcm,function = <0>; /* in */
+				brcm,pull = <0>; /* none */
+			};
+		};
+	};
+
+	__overrides__ {
+		int_pin = <&eth1>, "interrupts:0",
+		          <&eth1_pins>, "brcm,pins:0";
+		speed   = <&eth1>, "spi-max-frequency:0";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/enc28j60-spi2-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/enc28j60-spi2-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/enc28j60-spi2-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/enc28j60-spi2-overlay.dts	2021-07-25 16:45:35.138807726 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// Overlay for the Microchip ENC28J60 Ethernet Controller - SPI2 Compute Module
+// Interrupt pin: 39
+/dts-v1/;
+/plugin/;
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&spi2>;
+		__overlay__ {
+			/* needed to avoid dtc warning */
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			status = "okay";
+
+			eth1: enc28j60@0{
+				compatible = "microchip,enc28j60";
+				reg = <0>; /* CE0 */
+				pinctrl-names = "default";
+				pinctrl-0 = <&eth1_pins>;
+				interrupt-parent = <&gpio>;
+				interrupts = <39 0x2>; /* falling edge */
+				spi-max-frequency = <12000000>;
+				status = "okay";
+			};
+		};
+	};
+
+	fragment@1 {
+		target = <&gpio>;
+		__overlay__ {
+			eth1_pins: eth1_pins {
+				brcm,pins = <39>;
+				brcm,function = <0>; /* in */
+				brcm,pull = <0>; /* none */
+			};
+		};
+	};
+
+	__overrides__ {
+		int_pin = <&eth1>, "interrupts:0",
+		          <&eth1_pins>, "brcm,pins:0";
+		speed   = <&eth1>, "spi-max-frequency:0";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/exc3000-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/exc3000-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/exc3000-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/exc3000-overlay.dts	2021-07-25 16:45:35.138807726 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// Device tree overlay for I2C connected EETI EXC3000 multiple touch controller
+/dts-v1/;
+/plugin/;
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&gpio>;
+		__overlay__ {
+			exc3000_pins: exc3000_pins {
+				brcm,pins = <4>; // interrupt
+				brcm,function = <0>; // in
+				brcm,pull = <2>; // pull-up
+			};
+		};
+	};
+
+	fragment@1 {
+		target = <&i2c1>;
+		__overlay__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			status = "okay";
+
+			exc3000: exc3000@2a {
+				compatible = "eeti,exc3000";
+				reg = <0x2a>;
+				pinctrl-names = "default";
+				pinctrl-0 = <&exc3000_pins>;
+				interrupt-parent = <&gpio>;
+				interrupts = <4 8>; // active low level-sensitive
+				touchscreen-size-x = <4096>;
+				touchscreen-size-y = <4096>;
+			};
+		};
+	};
+
+	__overrides__ {
+		interrupt = <&exc3000_pins>,"brcm,pins:0",
+			<&exc3000>,"interrupts:0";
+		sizex = <&exc3000>,"touchscreen-size-x:0";
+		sizey = <&exc3000>,"touchscreen-size-y:0";
+		invx = <&exc3000>,"touchscreen-inverted-x?";
+		invy = <&exc3000>,"touchscreen-inverted-y?";
+		swapxy = <&exc3000>,"touchscreen-swapped-x-y?";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/fe-pi-audio-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/fe-pi-audio-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/fe-pi-audio-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/fe-pi-audio-overlay.dts	2021-07-25 16:45:35.138807726 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// Definitions for Fe-Pi Audio
+/dts-v1/;
+/plugin/;
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target-path = "/";
+		__overlay__ {
+			sgtl5000_mclk: sgtl5000_mclk {
+				compatible = "fixed-clock";
+				#clock-cells = <0>;
+				clock-frequency = <12288000>;
+				clock-output-names = "sgtl5000-mclk";
+			};
+		};
+	};
+
+	fragment@1 {
+		target = <&soc>;
+		__overlay__ {
+			reg_1v8: reg_1v8@0 {
+				compatible = "regulator-fixed";
+				regulator-name = "1V8";
+				regulator-min-microvolt = <1800000>;
+				regulator-max-microvolt = <1800000>;
+				regulator-always-on;
+			};
+		};
+	};
+
+	fragment@2 {
+		target = <&i2c1>;
+		__overlay__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			status = "okay";
+
+			sgtl5000@0a {
+				#sound-dai-cells = <0>;
+				compatible = "fsl,sgtl5000";
+				reg = <0x0a>;
+				clocks = <&sgtl5000_mclk>;
+				micbias-resistor-k-ohms = <2>;
+				micbias-voltage-m-volts = <3000>;
+				VDDA-supply = <&vdd_3v3_reg>;
+				VDDIO-supply = <&vdd_3v3_reg>;
+				VDDD-supply = <&reg_1v8>;
+				status = "okay";
+			};
+		};
+	};
+
+	fragment@3 {
+		target = <&i2s>;
+		__overlay__ {
+			status = "okay";
+		};
+	};
+
+	fragment@4 {
+		target = <&sound>;
+		__overlay__ {
+			compatible = "fe-pi,fe-pi-audio";
+			i2s-controller = <&i2s>;
+			status = "okay";
+		};
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/fsm-demo-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/fsm-demo-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/fsm-demo-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/fsm-demo-overlay.dts	2021-07-25 16:45:35.138807726 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// Demo overlay for the gpio-fsm driver
+/dts-v1/;
+/plugin/;
+
+#include <dt-bindings/gpio/gpio-fsm.h>
+
+#define BUTTON1 GF_IP(0)
+#define BUTTON2 GF_SW(0)
+#define RED   GF_OP(0) // GPIO7
+#define AMBER GF_OP(1) // GPIO8
+#define GREEN GF_OP(2) // GPIO25
+
+/{
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target-path = "/";
+		__overlay__ {
+			fsm_demo: fsm-demo {
+				compatible = "rpi,gpio-fsm";
+
+				debug = <0>;
+				gpio-controller;
+				#gpio-cells = <2>;
+				num-swgpios = <1>;
+				gpio-line-names = "button2";
+				input-gpios  = <&gpio 6 1>;  // BUTTON1 (active-low)
+				output-gpios = <&gpio 7 0>,  // RED
+					       <&gpio 8 0>,  // AMBER
+					       <&gpio 25 0>; // GREEN
+				shutdown-timeout-ms = <2000>;
+
+				start {
+					start_state;
+					set = <RED 1>, <AMBER 0>, <GREEN 0>;
+					start2 = <GF_DELAY 250>;
+				};
+
+				start2 {
+					set = <RED 0>, <AMBER 1>;
+					go = <GF_DELAY 250>;
+				};
+
+				go {
+					set = <RED 0>, <AMBER 0>, <GREEN 1>;
+					ready_wait = <BUTTON1 0>;
+					shutdown1 = <GF_SHUTDOWN 0>;
+				};
+
+				ready_wait {
+					// Clear the soft GPIO
+					set = <BUTTON2 0>;
+					ready = <GF_DELAY 1000>;
+					shutdown1 = <GF_SHUTDOWN 0>;
+				};
+
+				ready {
+					stopping = <BUTTON1 1>, <BUTTON2 1>;
+					shutdown1 = <GF_SHUTDOWN 0>;
+				};
+
+				stopping {
+					set = <GREEN 0>, <AMBER 1>;
+					stopped = <GF_DELAY 1000>;
+				};
+
+				stopped {
+					set = <AMBER 0>, <RED 1>;
+					get_set = <GF_DELAY 3000>;
+					shutdown1 = <GF_SHUTDOWN 0>;
+				};
+
+				get_set {
+					set = <AMBER 1>;
+					go = <GF_DELAY 1000>;
+				};
+
+				shutdown1 {
+					set = <RED 0>, <AMBER 0>, <GREEN 1>;
+					shutdown2 = <GF_SHUTDOWN 250>;
+				};
+
+				shutdown2 {
+					set = <AMBER 1>, <GREEN 0>;
+					shutdown3 = <GF_SHUTDOWN 250>;
+				};
+
+				shutdown3 {
+					set = <RED 1>, <AMBER 0>;
+					shutdown4 = <GF_SHUTDOWN 250>;
+				};
+
+				shutdown4 {
+					shutdown_state;
+					set = <RED 0>, <AMBER 0>, <GREEN 0>;
+				};
+			};
+	       };
+        };
+
+	__overrides__ {
+		fsm_debug = <&fsm_demo>,"debug:0";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/ghost-amp-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/ghost-amp-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/ghost-amp-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/ghost-amp-overlay.dts	2021-07-25 16:45:35.138807726 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// Overlay for the PCM5122-based Ghost amplifier using gpio-fsm
+/dts-v1/;
+/plugin/;
+
+#include <dt-bindings/gpio/gpio-fsm.h>
+
+#define ENABLE   GF_SW(0)
+#define FAULT    GF_IP(0) // GPIO5
+#define RELAY1   GF_OP(0) // GPIO22
+#define RELAY2   GF_OP(1) // GPIO23
+#define RELAYSSR GF_OP(2) // GPIO24
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&i2s>;
+		__overlay__ {
+			status = "okay";
+		};
+	};
+
+	fragment@1 {
+		target = <&i2c1>;
+		__overlay__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			status = "okay";
+
+			pcm5122@4c {
+				#sound-dai-cells = <0>;
+				compatible = "ti,pcm5122";
+				reg = <0x4c>;
+				AVDD-supply = <&vdd_3v3_reg>;
+				DVDD-supply = <&vdd_3v3_reg>;
+				CPVDD-supply = <&vdd_3v3_reg>;
+				status = "okay";
+			};
+		};
+	};
+
+	fragment@2 {
+		target = <&sound>;
+		iqaudio_dac: __overlay__ {
+			compatible = "iqaudio,iqaudio-dac";
+			i2s-controller = <&i2s>;
+			mute-gpios = <&amp 0 0>;
+			iqaudio-dac,auto-mute-amp;
+			status = "okay";
+		};
+	};
+
+	fragment@3 {
+		target-path = "/";
+		__overlay__ {
+			amp: ghost-amp {
+				compatible = "rpi,gpio-fsm";
+				pinctrl-names = "default";
+				pinctrl-0 = <&ghost_amp_pins>;
+
+				debug = <0>;
+				gpio-controller;
+				#gpio-cells = <2>;
+				num-swgpios = <1>;
+				gpio-line-names = "enable";
+				input-gpios  = <&gpio 5 1>;  // FAULT (active low)
+				output-gpios = <&gpio 22 0>, // RELAY1
+					       <&gpio 23 0>, // RELAY2
+					       <&gpio 24 0>; // RELAYSSR
+				shutdown-timeout-ms = <1000>;
+
+				amp_off {
+					start_state;
+					shutdown_state;
+
+					set = <RELAYSSR 0>,
+					      <RELAY2 0>,
+					      <RELAY1 0>;
+					amp_on_1 = <ENABLE 1>;
+					fault = <FAULT 1>;
+				};
+
+				amp_on_1 {
+					set = <RELAY1 1>;
+					amp_on_2 = <GF_DELAY 1000>;
+					amp_off = <GF_SHUTDOWN 0>;
+					fault = <FAULT 1>;
+				};
+
+				amp_on_2 {
+					set = <RELAY2 1>;
+					amp_on_wait = <ENABLE 0>;
+					amp_on = <GF_DELAY 1>;
+					fault = <FAULT 1>;
+				};
+
+				amp_on {
+					set = <RELAYSSR 1>;
+					amp_on_wait = <ENABLE 0>;
+					fault = <FAULT 1>;
+				};
+
+				amp_on_wait {
+					set = <RELAYSSR 0>;
+					amp_off_1 = <GF_DELAY (30*60*1000)>,
+						    <GF_SHUTDOWN 0>;
+					amp_on = <ENABLE 1>;
+					fault = <FAULT 1>;
+				};
+
+				amp_off_1 {
+					set = <RELAY2 0>;
+					amp_on = <ENABLE 1>;
+					amp_off = <GF_DELAY 100>;
+					fault = <FAULT 1>;
+				};
+
+				// Keep this a distinct state to prevent
+				// changes and for the diagnostic output
+				fault {
+					set = <RELAYSSR 0>,
+					      <RELAY2 0>,
+					      <RELAY1 0>;
+					amp_off = <FAULT 0>;
+					shutdown_state;
+				};
+			};
+		};
+	};
+
+	fragment@4 {
+		target = <&gpio>;
+		__overlay__ {
+			ghost_amp_pins: ghost_amp_pins {
+				brcm,pins = <5 22 23 24>;
+				brcm,function = <0 1 1 1>; /* in out out out */
+				brcm,pull = <2 0 0 0>; /* up none none none */
+			};
+		};
+	};
+
+	__overrides__ {
+		fsm_debug = <&amp>,"debug:0";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/goodix-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/goodix-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/goodix-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/goodix-overlay.dts	2021-07-25 16:45:35.138807726 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// Device tree overlay for I2C connected Goodix gt9271 multiple touch controller
+/dts-v1/;
+/plugin/;
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&gpio>;
+		__overlay__ {
+			goodix_pins: goodix_pins {
+				brcm,pins = <4 17>; // interrupt and reset
+				brcm,function = <0 0>; // in
+				brcm,pull = <2 2>; // pull-up
+			};
+		};
+	};
+
+	fragment@1 {
+		target = <&i2c1>;
+		__overlay__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			status = "okay";
+
+			gt9271: gt9271@14 {
+				compatible = "goodix,gt9271";
+				reg = <0x14>;
+				pinctrl-names = "default";
+				pinctrl-0 = <&goodix_pins>;
+				interrupt-parent = <&gpio>;
+				interrupts = <4 2>; // high-to-low edge triggered
+				irq-gpios = <&gpio 4 0>; // Pin7 on GPIO header
+				reset-gpios = <&gpio 17 1>; // Pin11 on GPIO header
+			};
+		};
+	};
+
+	__overrides__ {
+		interrupt = <&goodix_pins>,"brcm,pins:0",
+			<&gt9271>,"interrupts:0",
+			<&gt9271>,"irq-gpios:4";
+		reset = <&goodix_pins>,"brcm,pins:4",
+			<&gt9271>,"reset-gpios:4";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/googlevoicehat-soundcard-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/googlevoicehat-soundcard-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/googlevoicehat-soundcard-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/googlevoicehat-soundcard-overlay.dts	2021-07-25 16:45:35.138807726 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// Definitions for Google voiceHAT v1 soundcard overlay
+/dts-v1/;
+/plugin/;
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&i2s>;
+		__overlay__ {
+			status = "okay";
+		};
+	};
+
+	fragment@1 {
+		target = <&gpio>;
+		__overlay__ {
+			googlevoicehat_pins: googlevoicehat_pins {
+				brcm,pins = <16>;
+				brcm,function = <1>; /* out */
+				brcm,pull = <0>; /* up */
+			};
+		};
+	};
+
+
+	fragment@2 {
+		target-path = "/";
+		__overlay__ {
+			voicehat-codec {
+				#sound-dai-cells = <0>;
+				compatible = "google,voicehat";
+				pinctrl-names = "default";
+				pinctrl-0 = <&googlevoicehat_pins>;
+				sdmode-gpios= <&gpio 16 0>;
+				status = "okay";
+			};
+		};
+	};
+
+	fragment@3 {
+		target = <&sound>;
+		__overlay__ {
+			compatible = "googlevoicehat,googlevoicehat-soundcard";
+			i2s-controller = <&i2s>;
+			status = "okay";
+		};
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/gpio-fan-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/gpio-fan-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/gpio-fan-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/gpio-fan-overlay.dts	2021-07-25 16:45:35.138807726 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/*
+ * Overlay for the Raspberry Pi GPIO Fan @ BCM GPIO12.
+ * References: 
+ *	- https://www.raspberrypi.org/forums/viewtopic.php?f=107&p=1367135#p1365084
+ *
+ * Optional parameters:
+ *	- "gpiopin"	- BCM number of the pin driving the fan, default 12 (GPIO12);
+ * 	- "temp"	- CPU temperature at which fan is started in millicelsius, default 55000;
+ *
+ * Requires:
+ *	- kernel configurations: CONFIG_SENSORS_GPIO_FAN=m;
+ *	- kernel rebuild;
+ *	- N-MOSFET connected to gpiopin, 2N7002-[https://en.wikipedia.org/wiki/2N7000];
+ *	- DC Fan connected to N-MOSFET Drain terminal, a 12V fan is working fine and quite silently;
+ *	  [https://www.tme.eu/en/details/ee40101s1-999-a/dc12v-fans/sunon/ee40101s1-1000u-999/]
+ *
+ *                   ┌─────────────────────┐
+ *                   │Fan negative terminal│
+ *                   └┬────────────────────┘
+ *                    │D
+ *             G   │──┘
+ * [GPIO12]──────┤ │<─┐  2N7002
+ *                 │──┤
+ *                    │S
+ *                   ─┴─
+ *                   GND
+ *
+ * Build:
+ * 	- `sudo dtc -W no-unit_address_vs_reg -@ -I dts -O dtb -o /boot/overlays/gpio-fan.dtbo gpio-fan-overlay.dts`
+ * Activate:
+ *	- sudo nano /boot/config.txt add "dtoverlay=gpio-fan" or "dtoverlay=gpio-fan,gpiopin=12,temp=45000"
+ *	 or
+ *	- sudo sh -c 'printf "\n# Enable PI GPIO-Fan Default\ndtoverlay=gpio-fan\n" >> /boot/config.txt'
+ *	- sudo sh -c 'printf "\n# Enable PI GPIO-Fan Custom\ndtoverlay=gpio-fan,gpiopin=12,temp=45000\n" >> /boot/config.txt'
+ *
+ */
+/dts-v1/;
+/plugin/;
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target-path = "/";
+		__overlay__ {
+			fan0: gpio-fan@0 {
+				compatible = "gpio-fan";
+				gpios = <&gpio 12 0>;
+				gpio-fan,speed-map = <0    0>,
+									 <5000 1>;
+				#cooling-cells = <2>;
+			};
+		};
+	};
+
+	fragment@1 {
+		target = <&cpu_thermal>;
+		polling-delay = <2000>;	/* milliseconds */
+		__overlay__ {
+			trips {
+				cpu_hot: trip-point@0 {
+					temperature = <55000>;	/* (millicelsius) Fan started at 55°C */
+					hysteresis = <10000>;	/* (millicelsius) Fan stopped at 45°C */
+					type = "active";
+				};
+			};
+			cooling-maps {
+				map0 {
+					trip = <&cpu_hot>;
+					cooling-device = <&fan0 1 1>;
+				};
+			};
+		};
+	};
+	__overrides__ {
+		gpiopin = <&fan0>,"gpios:4", <&fan0>,"brcm,pins:0";
+		temp = <&cpu_hot>,"temperature:0";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/gpio-ir-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/gpio-ir-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/gpio-ir-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/gpio-ir-overlay.dts	2021-07-25 16:45:35.138807726 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// Definitions for ir-gpio module
+/dts-v1/;
+/plugin/;
+
+/ {
+        compatible = "brcm,bcm2835";
+
+        fragment@0 {
+                target-path = "/";
+                __overlay__ {
+                        gpio_ir: ir-receiver@12 {
+                                compatible = "gpio-ir-receiver";
+                                pinctrl-names = "default";
+                                pinctrl-0 = <&gpio_ir_pins>;
+
+                                // pin number, high or low
+                                gpios = <&gpio 18 1>;
+
+                                // parameter for keymap name
+                                linux,rc-map-name = "rc-rc6-mce";
+
+                                status = "okay";
+                        };
+                };
+        };
+
+        fragment@1 {
+                target = <&gpio>;
+                __overlay__ {
+                        gpio_ir_pins: gpio_ir_pins@12 {
+                                brcm,pins = <18>;                       // pin 18
+                                brcm,function = <0>;                    // in
+                                brcm,pull = <2>;                        // up
+                        };
+                };
+        };
+
+        __overrides__ {
+                // parameters
+                gpio_pin =      <&gpio_ir>,"gpios:4",           // pin number
+                                <&gpio_ir>,"reg:0",
+                                <&gpio_ir_pins>,"brcm,pins:0",
+                                <&gpio_ir_pins>,"reg:0";
+                gpio_pull = <&gpio_ir_pins>,"brcm,pull:0";              // pull-up/down state
+                invert = <&gpio_ir>,"gpios:8";                          // 0 = active high input
+
+                rc-map-name = <&gpio_ir>,"linux,rc-map-name";           // default rc map
+        };
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/gpio-ir-tx-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/gpio-ir-tx-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/gpio-ir-tx-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/gpio-ir-tx-overlay.dts	2021-07-25 16:45:35.138807726 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/dts-v1/;
+/plugin/;
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&gpio>;
+		__overlay__ {
+			gpio_ir_tx_pins: gpio_ir_tx_pins@12 {
+				brcm,pins = <18>;
+				brcm,function = <1>;	// out
+			};
+		};
+	};
+
+	fragment@1 {
+		target-path = "/";
+		__overlay__ {
+			gpio_ir_tx: gpio-ir-transmitter@12 {
+				compatible = "gpio-ir-tx";
+				pinctrl-names = "default";
+				pinctrl-0 = <&gpio_ir_tx_pins>;
+				gpios = <&gpio 18 0>;
+			};
+		};
+	};
+
+	__overrides__ {
+		gpio_pin = <&gpio_ir_tx>, "gpios:4",           	// pin number
+			   <&gpio_ir_tx>, "reg:0",
+			   <&gpio_ir_tx_pins>, "brcm,pins:0",
+			   <&gpio_ir_tx_pins>, "reg:0";
+		invert = <&gpio_ir_tx>, "gpios:8";		// 1 = active low
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/gpio-key-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/gpio-key-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/gpio-key-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/gpio-key-overlay.dts	2021-07-25 16:45:35.138807726 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// Definitions for gpio-key module
+/dts-v1/;
+/plugin/;
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		// Configure the gpio pin controller
+		target = <&gpio>;
+		__overlay__ {
+			pin_state: button_pins@0 {
+				brcm,pins = <3>; // gpio number
+				brcm,function = <0>; // 0 = input, 1 = output
+				brcm,pull = <2>; // 0 = none, 1 = pull down, 2 = pull up
+			};
+		};
+	};
+	fragment@1 {
+		target-path = "/";
+		__overlay__ {
+			button: button@0 {
+				compatible = "gpio-keys";
+				pinctrl-names = "default";
+				pinctrl-0 = <&pin_state>;
+				status = "okay";
+
+				key: key {
+					linux,code = <116>;
+					gpios = <&gpio 3 1>;
+					label = "KEY_POWER";
+				};
+			};
+		};
+	};
+
+	__overrides__ {
+		gpio =       <&key>,"gpios:4",
+		             <&button>,"reg:0",
+		             <&pin_state>,"brcm,pins:0",
+		             <&pin_state>,"reg:0";
+		label =      <&key>,"label";
+		keycode =    <&key>,"linux,code:0";
+		gpio_pull =  <&pin_state>,"brcm,pull:0";
+		active_low = <&key>,"gpios:8";
+	};
+
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/gpio-led-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/gpio-led-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/gpio-led-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/gpio-led-overlay.dts	2021-07-25 16:45:35.148807558 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * gpio-led - generic connection of kernel's LED framework to the RPI's GPIO.
+ * Copyright (C) 2021 House Gordon Software Company Ltd. <assafgordon@gmail.com>
+ *
+ * Based on information from:
+ *   https://mjoldfield.com/atelier/2017/03/rpi-devicetree.html
+ *   https://www.raspberrypi.org/documentation/configuration/device-tree.md
+ *   https://www.kernel.org/doc/html/latest/leds/index.html
+ *
+ * compile with:
+ *   dtc -@ -Hepapr -I dts -O dtb -o gpio-led.dtbo gpio-led-overlay.dts
+ *
+ * There will be some warnings (can be ignored):
+ *  Warning (label_is_string): /__overrides__:label: property is not a string
+ *  Warning (unit_address_vs_reg): /fragment@0/__overlay__/led_pins@0:
+ *                                 node has a unit name, but no reg property
+ *  Warning (unit_address_vs_reg): /fragment@1/__overlay__/leds@0:
+ *                                 node has a unit name, but no reg property
+ *  Warning (gpios_property): /__overrides__: Missing property
+ *                 '#gpio-cells' in node /fragment@1/__overlay__/leds@0/led
+ *                  or bad phandle (referred from gpio[0])
+ *
+ * Typical electrical connection is:
+ *    RPI-GPIO.19  ->  LED  -> 300ohm resister  -> RPI-GND
+ *    The GPIO pin number can be changed with the 'gpio=' parameter.
+ *
+ * Test from user-space with:
+ *   # if nothing is shown, the overlay file isn't found in /boot/overlays
+ *   dtoverlay -a | grep gpio-led
+ *
+ *   # Load the overlay
+ *   dtoverlay gpio-led label=moo gpio=19
+ *
+ *   # if nothing is shown, the overlay wasn't loaded successfully
+ *   dtoverlay -l | grep gpio-led
+ *
+ *   echo 1 > /sys/class/leds/moo/brightness
+ *   echo 0 > /sys/class/leds/moo/brightness
+ *   echo cpu > /sys/class/leds/moo/trigger
+ *   echo heartbeat > /sys/class/leds/moo/trigger
+ *
+ *   # unload the overlay
+ *   dtoverlay -r gpio-led
+ *
+ * To load in /boot/config.txt add lines such as:
+ *   dtoverlay=gpio-led,gpio=19,label=heart,trigger=heartbeat
+ *   dtoverlay=gpio-led,gpio=26,label=brain,trigger=cpu
+ */
+
+/dts-v1/;
+/plugin/;
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		// Configure the gpio pin controller
+		target = <&gpio>;
+		__overlay__ {
+			led_pin: led_pins@19 {
+				brcm,pins = <19>; // gpio number
+				brcm,function = <1>; // 0 = input, 1 = output
+				brcm,pull = <0>; // 0 = none, 1 = pull down, 2 = pull up
+			};
+		};
+	};
+	fragment@1 {
+		target-path = "/";
+		__overlay__ {
+			leds: leds@0 {
+				compatible = "gpio-leds";
+				pinctrl-names = "default";
+				pinctrl-0 = <&led_pin>;
+				status = "okay";
+
+				led: led {
+			                label = "myled1";
+					gpios = <&gpio 19 0>;
+			                linux,default-trigger = "none";
+				};
+			};
+		};
+	};
+
+	__overrides__ {
+		gpio =       <&led>,"gpios:4",
+		             <&leds>,"reg:0",
+		             <&led_pin>,"brcm,pins:0",
+		             <&led_pin>,"reg:0";
+		label =      <&led>,"label";
+		active_low = <&led>,"gpios:8";
+		trigger =    <&led>,"linux,default-trigger";
+	};
+
+};
+
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/gpio-no-bank0-irq-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/gpio-no-bank0-irq-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/gpio-no-bank0-irq-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/gpio-no-bank0-irq-overlay.dts	2021-07-25 16:45:35.148807558 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/dts-v1/;
+/plugin/;
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		// Configure the gpio pin controller
+		target = <&gpio>;
+		__overlay__ {
+			    interrupts = <255 255>, <2 18>;
+		};
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/gpio-no-irq-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/gpio-no-irq-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/gpio-no-irq-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/gpio-no-irq-overlay.dts	2021-07-25 16:45:35.148807558 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/dts-v1/;
+/plugin/;
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		// Configure the gpio pin controller
+		target = <&gpio>;
+		__overlay__ {
+			    interrupts;
+		};
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/gpio-poweroff-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/gpio-poweroff-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/gpio-poweroff-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/gpio-poweroff-overlay.dts	2021-07-25 16:45:35.148807558 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// Definitions for gpio-poweroff module
+/dts-v1/;
+/plugin/;
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target-path = "/";
+		__overlay__ {
+			power_ctrl: power_ctrl {
+				compatible = "gpio-poweroff";
+				gpios = <&gpio 26 0>;
+				force;
+			};
+		};
+	};
+
+	fragment@1 {
+		target = <&gpio>;
+		__overlay__ {
+			power_ctrl_pins: power_ctrl_pins {
+				brcm,pins = <26>;
+				brcm,function = <1>; // out
+			};
+		};
+	};
+
+	__overrides__ {
+		gpiopin =       <&power_ctrl>,"gpios:4",
+				<&power_ctrl_pins>,"brcm,pins:0";
+		active_low =    <&power_ctrl>,"gpios:8";
+		input =         <&power_ctrl>,"input?";
+		export =        <&power_ctrl>,"export?";
+		timeout_ms =    <&power_ctrl>,"timeout-ms:0";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/gpio-shutdown-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/gpio-shutdown-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/gpio-shutdown-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/gpio-shutdown-overlay.dts	2021-07-25 16:45:35.148807558 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// Definitions for gpio-poweroff module
+/dts-v1/;
+/plugin/;
+
+// This overlay sets up an input device that generates KEY_POWER events
+// when a given GPIO pin changes. It defaults to using GPIO3, which can
+// also be used to wake up (start) the Rpi again after shutdown.
+// Raspberry Pi 1 Model B rev 1 can be wake up only by GPIO1 pin, so for
+// these boards change default GPIO pin to 1 via gpio_pin parameter. Since
+// wakeup is active-low, this defaults to active-low with a pullup
+// enabled, but all of this can be changed using overlay parameters (but
+// note that GPIO3 has an external pullup on at least some boards).
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		// Configure the gpio pin controller
+		target = <&gpio>;
+		__overlay__ {
+			// Define a pinctrl state, that sets up the gpio
+			// as an input with a pullup enabled. This does
+			// not take effect by itself, only when referenced
+			// by a "pinctrl client", as is done below. See:
+			//   https://www.kernel.org/doc/Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt
+			//   https://www.kernel.org/doc/Documentation/devicetree/bindings/pinctrl/brcm,bcm2835-gpio.txt
+			pin_state: shutdown_button_pins@3 {
+				brcm,pins = <3>; // gpio number
+				brcm,function = <0>; // 0 = input, 1 = output
+				brcm,pull = <2>; // 0 = none, 1 = pull down, 2 = pull up
+			};
+		};
+	};
+	fragment@1 {
+		// Add a new device to the /soc devicetree node
+		target-path = "/soc";
+		__overlay__ {
+			shutdown_button: shutdown_button@3 {
+				// Let the gpio-keys driver handle this device. See:
+				// https://www.kernel.org/doc/Documentation/devicetree/bindings/input/gpio-keys.txt
+				compatible = "gpio-keys";
+
+				// Declare a single pinctrl state (referencing the one declared above) and name it
+				// default, so it is activated automatically.
+				pinctrl-names = "default";
+				pinctrl-0 = <&pin_state>;
+
+				// Enable this device
+				status = "okay";
+
+				// Define a single key, called "shutdown" that monitors the gpio and sends KEY_POWER
+				// (keycode 116, see
+				// https://github.com/torvalds/linux/blob/v4.12/include/uapi/linux/input-event-codes.h#L190)
+				button: shutdown {
+					label = "shutdown";
+					linux,code = <116>; // KEY_POWER
+					gpios = <&gpio 3 1>;
+					debounce-interval = <100>; // ms
+				};
+			};
+		};
+	};
+
+	// This defines parameters that can be specified when loading
+	// the overlay. Each foo = line specifies one parameter, named
+	// foo. The rest of the specification gives properties where the
+	// parameter value is inserted into (changing the values above
+	// or adding new ones).
+	__overrides__ {
+		// Allow overriding the GPIO number.
+		gpio_pin = <&button>,"gpios:4",
+			   <&shutdown_button>,"reg:0",
+			   <&pin_state>,"reg:0",
+		           <&pin_state>,"brcm,pins:0";
+
+		// Allow changing the internal pullup/down state. 0 = none, 1 = pulldown, 2 = pullup
+		// Note that GPIO3 and GPIO2 are the I2c pins and have an external pullup (at least
+		// on some boards). Same applies for GPIO1 on Raspberry Pi 1 Model B rev 1.
+		gpio_pull = <&pin_state>,"brcm,pull:0";
+
+		// Allow setting the active_low flag. 0 = active high, 1 = active low
+		active_low = <&button>,"gpios:8";
+		debounce = <&button>,"debounce-interval:0";
+	};
+
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/hd44780-lcd-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/hd44780-lcd-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/hd44780-lcd-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/hd44780-lcd-overlay.dts	2021-07-25 16:45:35.148807558 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/dts-v1/;
+/plugin/;
+
+/ {
+    compatible = "brcm,bcm2835";
+
+    fragment@0 {
+        target-path = "/";
+        __overlay__ {
+            lcd_screen: auxdisplay {
+                compatible = "hit,hd44780";
+
+                data-gpios = <&gpio 6 0>,
+                             <&gpio 13 0>,
+                             <&gpio 19 0>,
+                             <&gpio 26 0>;
+                enable-gpios = <&gpio 21 0>;
+                rs-gpios = <&gpio 20 0>;
+
+                display-height-chars = <2>;
+                display-width-chars = <16>;
+            };
+
+        };
+    };
+
+    fragment@1 {
+       target = <&lcd_screen>;
+        __dormant__ {
+            backlight-gpios = <&gpio 12 0>;
+        };
+    };
+
+    __overrides__ {
+        pin_d4 = <&lcd_screen>,"data-gpios:4";
+        pin_d5 = <&lcd_screen>,"data-gpios:16";
+        pin_d6 = <&lcd_screen>,"data-gpios:28";
+        pin_d7 = <&lcd_screen>,"data-gpios:40";
+        pin_en = <&lcd_screen>,"enable-gpios:4";
+        pin_rs = <&lcd_screen>,"rs-gpios:4";
+        pin_bl = <0>,"+1", <&lcd_screen>,"backlight-gpios:4";
+        display_height = <&lcd_screen>,"display-height-chars:0";
+        display_width = <&lcd_screen>,"display-width-chars:0";
+    };
+
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/hdmi-backlight-hwhack-gpio-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/hdmi-backlight-hwhack-gpio-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/hdmi-backlight-hwhack-gpio-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/hdmi-backlight-hwhack-gpio-overlay.dts	2021-07-25 16:45:35.148807558 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/*
+ * Devicetree overlay for GPIO based backlight on/off capability.
+ *
+ * Use this if you have one of those HDMI displays whose backlight cannot be
+ * controlled via DPMS over HDMI and plan to do a little soldering to use an
+ * RPi gpio pin for on/off switching.
+ *
+ * See: https://www.waveshare.com/wiki/7inch_HDMI_LCD_(C)#Backlight_Control
+ *
+ */
+/dts-v1/;
+/plugin/;
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@1 {
+		target = <&gpio>;
+		__overlay__ {
+			hdmi_backlight_hwhack_gpio_pins: hdmi_backlight_hwhack_gpio_pins {
+				brcm,pins = <17>;
+				brcm,function = <1>; /* out */
+			};
+		};
+	};
+
+	fragment@2 {
+		target-path = "/";
+		__overlay__ {
+			hdmi_backlight_hwhack_gpio: hdmi_backlight_hwhack_gpio {
+				compatible = "gpio-backlight";
+
+				pinctrl-names = "default";
+				pinctrl-0 = <&hdmi_backlight_hwhack_gpio_pins>;
+
+				gpios = <&gpio 17 0>;
+				default-on;
+			};
+		};
+	};
+
+	__overrides__ {
+		gpio_pin   = <&hdmi_backlight_hwhack_gpio>,"gpios:4",
+		             <&hdmi_backlight_hwhack_gpio_pins>,"brcm,pins:0";
+		active_low = <&hdmi_backlight_hwhack_gpio>,"gpios:8";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/hifiberry-amp100-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/hifiberry-amp100-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/hifiberry-amp100-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/hifiberry-amp100-overlay.dts	2021-07-25 16:45:35.148807558 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// Definitions for HiFiBerry AMP100
+/dts-v1/;
+/plugin/;
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target-path = "/";
+		__overlay__ {
+			dacpro_osc: dacpro_osc {
+				compatible = "hifiberry,dacpro-clk";
+				#clock-cells = <0>;
+			};
+		};
+	};
+
+	fragment@1 {
+		target = <&i2s>;
+		__overlay__ {
+			status = "okay";
+		};
+	};
+
+	fragment@2 {
+		target = <&i2c1>;
+		__overlay__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			status = "okay";
+
+			pcm5122@4d {
+				#sound-dai-cells = <0>;
+				compatible = "ti,pcm5122";
+				reg = <0x4d>;
+				clocks = <&dacpro_osc>;
+				AVDD-supply = <&vdd_3v3_reg>;
+				DVDD-supply = <&vdd_3v3_reg>;
+				CPVDD-supply = <&vdd_3v3_reg>;
+				status = "okay";
+			};
+		};
+	};
+
+	fragment@3 {
+		target = <&sound>;
+		hifiberry_dacplus: __overlay__ {
+			compatible = "hifiberry,hifiberry-dacplus";
+			i2s-controller = <&i2s>;
+			status = "okay";
+			mute-gpio = <&gpio 4 0>;
+			reset-gpio = <&gpio 17 0x11>;
+		};
+	};
+
+	__overrides__ {
+		24db_digital_gain =
+			<&hifiberry_dacplus>,"hifiberry,24db_digital_gain?";
+		slave = <&hifiberry_dacplus>,"hifiberry-dacplus,slave?";
+		leds_off = <&hifiberry_dacplus>,"hifiberry-dacplus,leds_off?";
+		mute_ext_ctl = <&hifiberry_dacplus>,"hifiberry-dacplus,mute_ext_ctl:0";
+		auto_mute = <&hifiberry_dacplus>,"hifiberry-dacplus,auto_mute?";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/hifiberry-amp-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/hifiberry-amp-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/hifiberry-amp-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/hifiberry-amp-overlay.dts	2021-07-25 16:45:35.148807558 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// Definitions for HiFiBerry Amp/Amp+
+/dts-v1/;
+/plugin/;
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&i2s>;
+		__overlay__ {
+			status = "okay";
+		};
+	};
+
+	fragment@1 {
+		target = <&i2c1>;
+		__overlay__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			status = "okay";
+
+			tas5713@1b {
+				#sound-dai-cells = <0>;
+				compatible = "ti,tas5713";
+				reg = <0x1b>;
+				status = "okay";
+			};
+		};
+	};
+
+	fragment@2 {
+		target = <&sound>;
+		__overlay__ {
+			compatible = "hifiberry,hifiberry-amp";
+			i2s-controller = <&i2s>;
+			status = "okay";
+		};
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/hifiberry-dac-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/hifiberry-dac-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/hifiberry-dac-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/hifiberry-dac-overlay.dts	2021-07-25 16:45:35.148807558 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// Definitions for HiFiBerry DAC
+/dts-v1/;
+/plugin/;
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&i2s>;
+		__overlay__ {
+			status = "okay";
+		};
+	};
+
+	fragment@1 {
+		target-path = "/";
+		__overlay__ {
+			pcm5102a-codec {
+				#sound-dai-cells = <0>;
+				compatible = "ti,pcm5102a";
+				status = "okay";
+			};
+		};
+	};
+
+	fragment@2 {
+		target = <&sound>;
+		__overlay__ {
+			compatible = "hifiberry,hifiberry-dac";
+			i2s-controller = <&i2s>;
+			status = "okay";
+		};
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/hifiberry-dacplusadc-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/hifiberry-dacplusadc-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/hifiberry-dacplusadc-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/hifiberry-dacplusadc-overlay.dts	2021-07-25 16:45:35.148807558 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// Definitions for HiFiBerry DAC+ADC
+/dts-v1/;
+/plugin/;
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target-path = "/";
+		__overlay__ {
+			dacpro_osc: dacpro_osc {
+				compatible = "hifiberry,dacpro-clk";
+				#clock-cells = <0>;
+			};
+		};
+	};
+
+	fragment@1 {
+		target = <&i2s>;
+		__overlay__ {
+			status = "okay";
+		};
+	};
+
+	fragment@2 {
+		target = <&i2c1>;
+		__overlay__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			status = "okay";
+
+			pcm_codec: pcm5122@4d {
+				#sound-dai-cells = <0>;
+				compatible = "ti,pcm5122";
+				reg = <0x4d>;
+				clocks = <&dacpro_osc>;
+				AVDD-supply = <&vdd_3v3_reg>;
+				DVDD-supply = <&vdd_3v3_reg>;
+				CPVDD-supply = <&vdd_3v3_reg>;
+				status = "okay";
+			};
+		};
+	};
+
+	fragment@3 {
+		target-path = "/";
+		__overlay__ {
+			dmic {
+				#sound-dai-cells = <0>;
+				compatible = "dmic-codec";
+				num-channels = <2>;
+				status = "okay";
+			};
+		};
+	};
+
+	fragment@4 {
+		target = <&sound>;
+		hifiberry_dacplusadc: __overlay__ {
+			compatible = "hifiberry,hifiberry-dacplusadc";
+			i2s-controller = <&i2s>;
+			status = "okay";
+		};
+	};
+
+	__overrides__ {
+		24db_digital_gain =
+			<&hifiberry_dacplusadc>,"hifiberry,24db_digital_gain?";
+		slave = <&hifiberry_dacplusadc>,"hifiberry-dacplusadc,slave?";
+		leds_off = <&hifiberry_dacplusadc>,"hifiberry-dacplusadc,leds_off?";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/hifiberry-dacplusadcpro-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/hifiberry-dacplusadcpro-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/hifiberry-dacplusadcpro-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/hifiberry-dacplusadcpro-overlay.dts	2021-07-25 16:45:35.148807558 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// Definitions for HiFiBerry DAC+ADC PRO
+/dts-v1/;
+/plugin/;
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target-path = "/";
+		__overlay__ {
+			dacpro_osc: dacpro_osc {
+				compatible = "hifiberry,dacpro-clk";
+				#clock-cells = <0>;
+			};
+		};
+	};
+
+	fragment@1 {
+		target = <&i2s>;
+		__overlay__ {
+			status = "okay";
+		};
+	};
+
+	fragment@2 {
+		target = <&i2c1>;
+		__overlay__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			status = "okay";
+
+			hb_dac: pcm5122@4d {
+				#sound-dai-cells = <0>;
+				compatible = "ti,pcm5122";
+				reg = <0x4d>;
+				clocks = <&dacpro_osc>;
+				status = "okay";
+			};
+			hb_adc: pcm186x@4a {
+				#sound-dai-cells = <0>;
+				compatible = "ti,pcm1863";
+				reg = <0x4a>;
+				clocks = <&dacpro_osc>;
+				status = "okay";
+			};
+		};
+	};
+
+	fragment@3 {
+		target = <&sound>;
+		hifiberry_dacplusadcpro: __overlay__ {
+			compatible = "hifiberry,hifiberry-dacplusadcpro";
+			audio-codec = <&hb_dac &hb_adc>;
+			i2s-controller = <&i2s>;
+			status = "okay";
+		};
+	};
+
+	__overrides__ {
+		24db_digital_gain =
+			<&hifiberry_dacplusadcpro>,"hifiberry-dacplusadcpro,24db_digital_gain?";
+		slave = <&hifiberry_dacplusadcpro>,"hifiberry-dacplusadcpro,slave?";
+		leds_off = <&hifiberry_dacplusadcpro>,"hifiberry-dacplusadcpro,leds_off?";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/hifiberry-dacplusdsp-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/hifiberry-dacplusdsp-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/hifiberry-dacplusdsp-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/hifiberry-dacplusdsp-overlay.dts	2021-07-25 16:45:35.148807558 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// Definitions for hifiberry DAC+DSP soundcard overlay
+/dts-v1/;
+/plugin/;
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&i2s>;
+		__overlay__ {
+			status = "okay";
+		};
+	};
+
+	fragment@1 {
+		target-path = "/";
+		__overlay__ {
+			dacplusdsp-codec {
+				#sound-dai-cells = <0>;
+				compatible = "hifiberry,dacplusdsp";
+				status = "okay";
+			};
+		};
+	};
+
+	fragment@2 {
+		target = <&sound>;
+		__overlay__ {
+			compatible = "hifiberrydacplusdsp,hifiberrydacplusdsp-soundcard";
+			i2s-controller = <&i2s>;
+			status = "okay";
+		};
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/hifiberry-dacplushd-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/hifiberry-dacplushd-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/hifiberry-dacplushd-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/hifiberry-dacplushd-overlay.dts	2021-07-25 16:45:35.148807558 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// Definitions for HiFiBerry DAC+ HD
+/dts-v1/;
+/plugin/;
+
+#include <dt-bindings/gpio/gpio.h>
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target-path = "/";
+		__overlay__ {
+			dachd_osc: pll_dachd_osc {
+				compatible = "hifiberry,dachd-clk";
+				#clock-cells = <0>;
+			};
+		};
+	};
+
+	fragment@1 {
+		target = <&i2s>;
+		__overlay__ {
+			status = "okay";
+		};
+	};
+
+	fragment@2 {
+		target = <&i2c1>;
+		__overlay__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			status = "okay";
+
+			pcm1792a@4c {
+				compatible = "ti,pcm1792a";
+				#sound-dai-cells = <0>;
+				#clock-cells = <0>;
+				clocks = <&dachd_osc>;
+				reg = <0x4c>;
+				status = "okay";
+			};
+			pll: pll@62 {
+				compatible = "hifiberry,dachd-clk";
+				#clock-cells = <0>;
+				reg = <0x62>;
+				clocks = <&dachd_osc>;
+				status = "okay";
+				common_pll_regs = [
+					02 53 03 00 07 20 0F 00
+					10 0D 11 1D 12 0D 13 8C
+					14 8C 15 8C 16 8C 17 8C
+					18 2A 1C 00 1D 0F 1F 00
+					2A 00 2C 00 2F 00 30 00
+					31 00 32 00 34 00 37 00
+					38 00 39 00 3A 00 3B 01
+					3E 00 3F 00 40 00 41 00
+					5A 00 5B 00 95 00 96 00
+					97 00 98 00 99 00 9A 00
+					9B 00 A2 00 A3 00 A4 00
+					B7 92 ];
+				192k_pll_regs = [
+					1A 0C 1B 35 1E F0 20 09
+					21 50 2B 02 2D 10 2E 40
+					33 01 35 22 36 80 3C 22
+					3D 46 ];
+				96k_pll_regs = [
+					1A 0C 1B 35 1E F0 20 09
+					21 50 2B 02 2D 10 2E 40
+					33 01 35 47 36 00 3C 32
+					3D 46 ];
+				48k_pll_regs = [
+					1A 0C 1B 35 1E F0 20 09
+					21 50 2B 02 2D 10 2E 40
+					33 01 35 90 36 00 3C 42
+					3D 46 ];
+				176k4_pll_regs = [
+					1A 3D 1B 09 1E F3 20 13
+					21 75 2B 04 2D 11 2E E0
+					33 02 35 25 36 C0 3C 22
+					3D 7A ];
+				88k2_pll_regs = [
+					1A 3D 1B 09 1E F3 20 13
+					21 75 2B 04 2D 11 2E E0
+					33 01 35 4D 36 80 3C 32
+					3D 7A ];
+				44k1_pll_regs = [
+					1A 3D 1B 09 1E F3 20 13
+					21 75 2B 04 2D 11 2E E0
+					33 01 35 9D 36 00 3C 42
+					3D 7A ];
+			};
+		};
+	};
+
+	fragment@3 {
+		target = <&sound>;
+		__overlay__ {
+			compatible = "hifiberry,hifiberry-dacplushd";
+			i2s-controller = <&i2s>;
+			clocks = <&pll 0>;
+			reset-gpio = <&gpio 16 GPIO_ACTIVE_LOW>;
+			status = "okay";
+		};
+	};
+
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/hifiberry-dacplus-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/hifiberry-dacplus-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/hifiberry-dacplus-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/hifiberry-dacplus-overlay.dts	2021-07-25 16:45:35.148807558 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// Definitions for HiFiBerry DAC+
+/dts-v1/;
+/plugin/;
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target-path = "/";
+		__overlay__ {
+			dacpro_osc: dacpro_osc {
+				compatible = "hifiberry,dacpro-clk";
+				#clock-cells = <0>;
+			};
+		};
+	};
+
+	fragment@1 {
+		target = <&i2s>;
+		__overlay__ {
+			status = "okay";
+		};
+	};
+
+	fragment@2 {
+		target = <&i2c1>;
+		__overlay__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			status = "okay";
+
+			pcm5122@4d {
+				#sound-dai-cells = <0>;
+				compatible = "ti,pcm5122";
+				reg = <0x4d>;
+				clocks = <&dacpro_osc>;
+				AVDD-supply = <&vdd_3v3_reg>;
+				DVDD-supply = <&vdd_3v3_reg>;
+				CPVDD-supply = <&vdd_3v3_reg>;
+				status = "okay";
+			};
+			hpamp: hpamp@60 {
+				compatible = "ti,tpa6130a2";
+				reg = <0x60>;
+				status = "disabled";
+			};
+		};
+	};
+
+	fragment@3 {
+		target = <&sound>;
+		hifiberry_dacplus: __overlay__ {
+			compatible = "hifiberry,hifiberry-dacplus";
+			i2s-controller = <&i2s>;
+			status = "okay";
+		};
+	};
+
+	__overrides__ {
+		24db_digital_gain =
+			<&hifiberry_dacplus>,"hifiberry,24db_digital_gain?";
+		slave = <&hifiberry_dacplus>,"hifiberry-dacplus,slave?";
+		leds_off = <&hifiberry_dacplus>,"hifiberry-dacplus,leds_off?";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/hifiberry-digi-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/hifiberry-digi-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/hifiberry-digi-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/hifiberry-digi-overlay.dts	2021-07-25 16:45:35.148807558 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// Definitions for HiFiBerry Digi
+/dts-v1/;
+/plugin/;
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&i2s>;
+		__overlay__ {
+			status = "okay";
+		};
+	};
+
+	fragment@1 {
+		target = <&i2c1>;
+		__overlay__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			status = "okay";
+
+			wm8804@3b {
+				#sound-dai-cells = <0>;
+				compatible = "wlf,wm8804";
+				reg = <0x3b>;
+				PVDD-supply = <&vdd_3v3_reg>;
+				DVDD-supply = <&vdd_3v3_reg>;
+				status = "okay";
+			};
+		};
+	};
+
+	fragment@2 {
+		target = <&sound>;
+		__overlay__ {
+			compatible = "hifiberry,hifiberry-digi";
+			i2s-controller = <&i2s>;
+			status = "okay";
+		};
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/hifiberry-digi-pro-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/hifiberry-digi-pro-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/hifiberry-digi-pro-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/hifiberry-digi-pro-overlay.dts	2021-07-25 16:45:35.148807558 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// Definitions for HiFiBerry Digi Pro
+/dts-v1/;
+/plugin/;
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&i2s>;
+		__overlay__ {
+			status = "okay";
+		};
+	};
+
+	fragment@1 {
+		target = <&i2c1>;
+		__overlay__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			status = "okay";
+
+			wm8804@3b {
+				#sound-dai-cells = <0>;
+				compatible = "wlf,wm8804";
+				reg = <0x3b>;
+				PVDD-supply = <&vdd_3v3_reg>;
+				DVDD-supply = <&vdd_3v3_reg>;
+				status = "okay";
+			};
+		};
+	};
+
+	fragment@2 {
+		target = <&sound>;
+		__overlay__ {
+			compatible = "hifiberry,hifiberry-digi";
+			i2s-controller = <&i2s>;
+			status = "okay";
+			clock44-gpio = <&gpio 5 0>;
+			clock48-gpio = <&gpio 6 0>;
+		};
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/highperi-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/highperi-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/highperi-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/highperi-overlay.dts	2021-07-25 16:45:35.148807558 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/*
+ * highperi.dts
+ */
+
+/dts-v1/;
+/plugin/;
+
+/ {
+	compatible = "brcm,bcm2711";
+
+	fragment@0 {
+		target = <&soc>;
+		#address-cells = <2>;
+		#size-cells = <1>;
+
+		__overlay__ {
+			#address-cells = <1>;
+			#size-cells = <1>;
+			ranges = <0x7c000000  0x4 0x7c000000  0x04000000>,
+				 <0x40000000  0x4 0xc0000000  0x00800000>;
+		};
+	};
+
+	fragment@1 {
+		target = <&scb>;
+		#address-cells = <2>;
+		#size-cells = <1>;
+
+		__overlay__ {
+			#address-cells = <2>;
+			#size-cells = <2>;
+			ranges = <0x0 0x7c000000  0x4 0x7c000000  0x0 0x04000000>,
+				 <0x0 0x40000000  0x4 0xc0000000  0x0 0x00800000>,
+				 <0x6 0x00000000  0x6 0x00000000  0x0 0x40000000>;
+			dma-ranges = <0x0 0x00000000  0x0 0x00000000  0x2 0x00000000>;
+		};
+	};
+
+	fragment@2 {
+		target = <&v3dbus>;
+		#address-cells = <2>;
+		#size-cells = <1>;
+
+		__overlay__ {
+			#address-cells = <1>;
+			#size-cells = <2>;
+			ranges = <0x7c500000  0x4 0x7c500000  0x0 0x03300000>,
+				 <0x40000000  0x4 0xc0000000  0x0 0x00800000>;
+		};
+	};
+
+	fragment@3 {
+		target = <&emmc2bus>;
+		#address-cells = <2>;
+		#size-cells = <1>;
+
+		__overlay__ {
+			#address-cells = <2>;
+			#size-cells = <1>;
+			ranges = <0x0 0x7e000000  0x4 0x7e000000  0x01800000>;
+		};
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/hy28a-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/hy28a-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/hy28a-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/hy28a-overlay.dts	2021-07-25 16:45:35.148807558 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/*
+ * Device Tree overlay for HY28A display
+ *
+ */
+
+/dts-v1/;
+/plugin/;
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&spi0>;
+		__overlay__ {
+			status = "okay";
+		};
+	};
+
+	fragment@1 {
+		target = <&spidev0>;
+		__overlay__ {
+			status = "disabled";
+		};
+	};
+
+	fragment@2 {
+		target = <&spidev1>;
+		__overlay__ {
+			status = "disabled";
+		};
+	};
+
+	fragment@3 {
+		target = <&gpio>;
+		__overlay__ {
+			hy28a_pins: hy28a_pins {
+				brcm,pins = <17 25 18>;
+				brcm,function = <0 1 1>; /* in out out */
+			};
+		};
+	};
+
+	fragment@4 {
+		target = <&spi0>;
+		__overlay__ {
+			/* needed to avoid dtc warning */
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			hy28a: hy28a@0{
+				compatible = "ilitek,ili9320";
+				reg = <0>;
+				pinctrl-names = "default";
+				pinctrl-0 = <&hy28a_pins>;
+
+				spi-max-frequency = <32000000>;
+				spi-cpol;
+				spi-cpha;
+				rotate = <270>;
+				bgr;
+				fps = <50>;
+				buswidth = <8>;
+				startbyte = <0x70>;
+				reset-gpios = <&gpio 25 1>;
+				led-gpios = <&gpio 18 1>;
+				debug = <0>;
+			};
+
+			hy28a_ts: hy28a-ts@1 {
+				compatible = "ti,ads7846";
+				reg = <1>;
+
+				spi-max-frequency = <2000000>;
+				interrupts = <17 2>; /* high-to-low edge triggered */
+				interrupt-parent = <&gpio>;
+				pendown-gpio = <&gpio 17 0>;
+				ti,x-plate-ohms = /bits/ 16 <100>;
+				ti,pressure-max = /bits/ 16 <255>;
+			};
+		};
+	};
+	__overrides__ {
+		speed =		<&hy28a>,"spi-max-frequency:0";
+		rotate =	<&hy28a>,"rotate:0";
+		fps =		<&hy28a>,"fps:0";
+		debug =		<&hy28a>,"debug:0";
+		xohms =		<&hy28a_ts>,"ti,x-plate-ohms;0";
+		resetgpio =	<&hy28a>,"reset-gpios:4",
+				<&hy28a_pins>, "brcm,pins:4";
+		ledgpio =	<&hy28a>,"led-gpios:4",
+				<&hy28a_pins>, "brcm,pins:8";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/hy28b-2017-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/hy28b-2017-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/hy28b-2017-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/hy28b-2017-overlay.dts	2021-07-25 16:45:35.148807558 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/*
+ * Device Tree overlay for HY28b display shield by Texy.
+ * Modified for 2017 version with ILI9325 D chip
+ */
+
+/dts-v1/;
+/plugin/;
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&spi0>;
+		__overlay__ {
+			status = "okay";
+		};
+	};
+
+	fragment@1 {
+		target = <&spidev0>;
+		__overlay__ {
+			status = "disabled";
+		};
+	};
+
+	fragment@2 {
+		target = <&spidev1>;
+		__overlay__ {
+			status = "disabled";
+		};
+	};
+
+	fragment@3 {
+		target = <&gpio>;
+		__overlay__ {
+			hy28b_pins: hy28b_pins {
+				brcm,pins = <17 25 18>;
+				brcm,function = <0 1 1>; /* in out out */
+			};
+		};
+	};
+
+	fragment@4 {
+		target = <&spi0>;
+		__overlay__ {
+			/* needed to avoid dtc warning */
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			hy28b: hy28b@0{
+				compatible = "ilitek,ili9325";
+				reg = <0>;
+				pinctrl-names = "default";
+				pinctrl-0 = <&hy28b_pins>;
+
+				spi-max-frequency = <48000000>;
+				spi-cpol;
+				spi-cpha;
+				rotate = <270>;
+				bgr;
+				fps = <50>;
+				buswidth = <8>;
+				startbyte = <0x70>;
+				reset-gpios = <&gpio 25 1>;
+				led-gpios = <&gpio 18 1>;
+
+				init = <0x10000e5 0x78F0
+					0x1000001 0x0100
+					0x1000002 0x0700
+				        0x1000003 0x1030
+					0x1000004 0x0000
+					0x1000008 0x0207
+					0x1000009 0x0000
+				        0x100000a 0x0000
+					0x100000c 0x0000
+					0x100000d 0x0000
+					0x100000f 0x0000
+				        0x1000010 0x0000
+					0x1000011 0x0007
+					0x1000012 0x0000
+					0x1000013 0x0000
+					0x1000007 0x0001
+				        0x2000032
+				        0x2000032
+				        0x2000032
+				        0x2000032
+					0x1000010 0x1090
+					0x1000011 0x0227
+				        0x2000032
+					0x1000012 0x001f
+				        0x2000032
+				        0x1000013 0x1500
+					0x1000029 0x0027
+					0x100002b 0x000d
+				        0x2000032
+				        0x1000020 0x0000
+					0x1000021 0x0000
+				        0x2000032
+					0x1000030 0x0000
+					0x1000031 0x0707
+					0x1000032 0x0307
+					0x1000035 0x0200
+					0x1000036 0x0008
+					0x1000037 0x0004
+					0x1000038 0x0000
+					0x1000039 0x0707
+					0x100003c 0x0002
+					0x100003d 0x1d04
+					0x1000050 0x0000
+				        0x1000051 0x00ef
+					0x1000052 0x0000
+					0x1000053 0x013f
+					0x1000060 0xa700
+				        0x1000061 0x0001
+					0x100006a 0x0000
+					0x1000080 0x0000
+					0x1000081 0x0000
+				        0x1000082 0x0000
+					0x1000083 0x0000
+					0x1000084 0x0000
+					0x1000085 0x0000
+				        0x1000090 0x0010
+					0x1000092 0x0600
+					0x1000007 0x0133>;
+				debug = <0>;
+			};
+
+			hy28b_ts: hy28b-ts@1 {
+				compatible = "ti,ads7846";
+				reg = <1>;
+
+				spi-max-frequency = <2000000>;
+				interrupts = <17 2>; /* high-to-low edge triggered */
+				interrupt-parent = <&gpio>;
+				pendown-gpio = <&gpio 17 0>;
+				ti,x-plate-ohms = /bits/ 16 <100>;
+				ti,pressure-max = /bits/ 16 <255>;
+			};
+		};
+	};
+	__overrides__ {
+		speed = 	<&hy28b>,"spi-max-frequency:0";
+		rotate = 	<&hy28b>,"rotate:0";
+		fps = 		<&hy28b>,"fps:0";
+		debug = 	<&hy28b>,"debug:0";
+		xohms =		<&hy28b_ts>,"ti,x-plate-ohms;0";
+		resetgpio =	<&hy28b>,"reset-gpios:4",
+				<&hy28b_pins>, "brcm,pins:4";
+		ledgpio =	<&hy28b>,"led-gpios:4",
+				<&hy28b_pins>, "brcm,pins:8";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/hy28b-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/hy28b-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/hy28b-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/hy28b-overlay.dts	2021-07-25 16:45:35.148807558 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/*
+ * Device Tree overlay for HY28b display shield by Texy
+ *
+ */
+
+/dts-v1/;
+/plugin/;
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&spi0>;
+		__overlay__ {
+			status = "okay";
+		};
+	};
+
+	fragment@1 {
+		target = <&spidev0>;
+		__overlay__ {
+			status = "disabled";
+		};
+	};
+
+	fragment@2 {
+		target = <&spidev1>;
+		__overlay__ {
+			status = "disabled";
+		};
+	};
+
+	fragment@3 {
+		target = <&gpio>;
+		__overlay__ {
+			hy28b_pins: hy28b_pins {
+				brcm,pins = <17 25 18>;
+				brcm,function = <0 1 1>; /* in out out */
+			};
+		};
+	};
+
+	fragment@4 {
+		target = <&spi0>;
+		__overlay__ {
+			/* needed to avoid dtc warning */
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			hy28b: hy28b@0{
+				compatible = "ilitek,ili9325";
+				reg = <0>;
+				pinctrl-names = "default";
+				pinctrl-0 = <&hy28b_pins>;
+
+				spi-max-frequency = <48000000>;
+				spi-cpol;
+				spi-cpha;
+				rotate = <270>;
+				bgr;
+				fps = <50>;
+				buswidth = <8>;
+				startbyte = <0x70>;
+				reset-gpios = <&gpio 25 1>;
+				led-gpios = <&gpio 18 1>;
+
+				gamma = "04 1F 4 7 7 0 7 7 6 0\n0F 00 1 7 4 0 0 0 6 7";
+
+				init = <0x10000e7 0x0010
+					0x1000000 0x0001
+					0x1000001 0x0100
+					0x1000002 0x0700
+				        0x1000003 0x1030
+					0x1000004 0x0000
+					0x1000008 0x0207
+					0x1000009 0x0000
+				        0x100000a 0x0000
+					0x100000c 0x0001
+					0x100000d 0x0000
+					0x100000f 0x0000
+				        0x1000010 0x0000
+					0x1000011 0x0007
+					0x1000012 0x0000
+					0x1000013 0x0000
+				        0x2000032
+					0x1000010 0x1590
+					0x1000011 0x0227
+				        0x2000032
+					0x1000012 0x009c
+				        0x2000032
+				        0x1000013 0x1900
+					0x1000029 0x0023
+					0x100002b 0x000e
+				        0x2000032
+				        0x1000020 0x0000
+					0x1000021 0x0000
+				        0x2000032
+					0x1000050 0x0000
+				        0x1000051 0x00ef
+					0x1000052 0x0000
+					0x1000053 0x013f
+					0x1000060 0xa700
+				        0x1000061 0x0001
+					0x100006a 0x0000
+					0x1000080 0x0000
+					0x1000081 0x0000
+				        0x1000082 0x0000
+					0x1000083 0x0000
+					0x1000084 0x0000
+					0x1000085 0x0000
+				        0x1000090 0x0010
+					0x1000092 0x0000
+					0x1000093 0x0003
+					0x1000095 0x0110
+				        0x1000097 0x0000
+					0x1000098 0x0000
+					0x1000007 0x0133
+					0x1000020 0x0000
+				        0x1000021 0x0000
+				        0x2000064>;
+				debug = <0>;
+			};
+
+			hy28b_ts: hy28b-ts@1 {
+				compatible = "ti,ads7846";
+				reg = <1>;
+
+				spi-max-frequency = <2000000>;
+				interrupts = <17 2>; /* high-to-low edge triggered */
+				interrupt-parent = <&gpio>;
+				pendown-gpio = <&gpio 17 0>;
+				ti,x-plate-ohms = /bits/ 16 <100>;
+				ti,pressure-max = /bits/ 16 <255>;
+			};
+		};
+	};
+	__overrides__ {
+		speed = 	<&hy28b>,"spi-max-frequency:0";
+		rotate = 	<&hy28b>,"rotate:0";
+		fps = 		<&hy28b>,"fps:0";
+		debug = 	<&hy28b>,"debug:0";
+		xohms =		<&hy28b_ts>,"ti,x-plate-ohms;0";
+		resetgpio =	<&hy28b>,"reset-gpios:4",
+				<&hy28b_pins>, "brcm,pins:4";
+		ledgpio =	<&hy28b>,"led-gpios:4",
+				<&hy28b_pins>, "brcm,pins:8";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/i2c0-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/i2c0-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/i2c0-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/i2c0-overlay.dts	2021-07-25 16:45:35.148807558 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/dts-v1/;
+/plugin/;
+
+/{
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&i2c0if>;
+		__overlay__ {
+			status = "okay";
+			pinctrl-names = "default";
+			pinctrl-0 = <&i2c0_pins>;
+		};
+	};
+
+	fragment@1 {
+		target = <&i2c0_pins>;
+		pins1: __overlay__ {
+			brcm,pins = <0 1>;
+			brcm,function = <4>; /* alt0 */
+		};
+	};
+
+	fragment@2 {
+		target = <&i2c0_pins>;
+		pins2: __dormant__ {
+			brcm,pins = <28 29>;
+			brcm,function = <4>; /* alt0 */
+		};
+	};
+
+	fragment@3 {
+		target = <&i2c0_pins>;
+		pins3: __dormant__ {
+			brcm,pins = <44 45>;
+			brcm,function = <5>; /* alt1 */
+		};
+	};
+
+	fragment@4 {
+		target = <&i2c0_pins>;
+		pins4: __dormant__ {
+			brcm,pins = <46 47>;
+			brcm,function = <4>; /* alt0 */
+		};
+	};
+
+	fragment@5 {
+		target = <&i2c0>;
+		__dormant__ {
+			compatible = "brcm,bcm2708-i2c";
+		};
+	};
+
+	fragment@6 {
+		target = <&i2c0mux>;
+		__overlay__ {
+			status = "disabled";
+		};
+	};
+
+	fragment@7 {
+		target-path = "/aliases";
+		__overlay__ {
+			i2c0 = "/soc/i2c@7e205000";
+		};
+	};
+
+	fragment@8 {
+		target-path = "/__symbols__";
+		__overlay__ {
+			i2c0 = "/soc/i2c@7e205000";
+		};
+	};
+
+	__overrides__ {
+		pins_0_1   = <0>,"+1-2-3-4";
+		pins_28_29 = <0>,"-1+2-3-4";
+		pins_44_45 = <0>,"-1-2+3-4";
+		pins_46_47 = <0>,"-1-2-3+4";
+		combine = <0>, "!5";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/i2c1-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/i2c1-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/i2c1-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/i2c1-overlay.dts	2021-07-25 16:45:35.148807558 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/dts-v1/;
+/plugin/;
+
+/{
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&i2c1>;
+		__overlay__ {
+			status = "okay";
+			pinctrl-names = "default";
+			pinctrl-0 = <&i2c1_pins>;
+		};
+	};
+
+	fragment@1 {
+		target = <&i2c1_pins>;
+		pins1: __overlay__ {
+			brcm,pins = <2 3>;
+			brcm,function = <4>; /* alt 0 */
+		};
+	};
+
+	fragment@2 {
+		target = <&i2c1_pins>;
+		pins2: __dormant__ {
+			brcm,pins = <44 45>;
+			brcm,function = <6>; /* alt 2 */
+		};
+	};
+
+	fragment@3 {
+		target = <&i2c1>;
+		__dormant__ {
+			compatible = "brcm,bcm2708-i2c";
+		};
+	};
+
+	__overrides__ {
+		pins_2_3   = <0>,"=1!2";
+		pins_44_45 = <0>,"!1=2";
+		combine = <0>, "!3";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/i2c3-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/i2c3-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/i2c3-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/i2c3-overlay.dts	2021-07-25 16:45:35.148807558 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/dts-v1/;
+/plugin/;
+
+/{
+	compatible = "brcm,bcm2711";
+
+	fragment@0 {
+		target = <&i2c3>;
+		frag0: __overlay__ {
+			status = "okay";
+			pinctrl-names = "default";
+			pinctrl-0 = <&i2c3_pins>;
+			clock-frequency = <100000>;
+		};
+	};
+
+	fragment@1 {
+		target = <&i2c3_pins>;
+		__dormant__ {
+			brcm,pins = <2 3>;
+		};
+	};
+
+	fragment@2 {
+		target = <&i2c3_pins>;
+		__overlay__ {
+			brcm,pins = <4 5>;
+		};
+	};
+
+	__overrides__ {
+		pins_2_3 = <0>,"=1!2";
+		pins_4_5 = <0>,"!1=2";
+		baudrate = <&frag0>, "clock-frequency:0";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/i2c4-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/i2c4-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/i2c4-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/i2c4-overlay.dts	2021-07-25 16:45:35.148807558 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/dts-v1/;
+/plugin/;
+
+/{
+	compatible = "brcm,bcm2711";
+
+	fragment@0 {
+		target = <&i2c4>;
+		frag0: __overlay__ {
+			status = "okay";
+			pinctrl-names = "default";
+			pinctrl-0 = <&i2c4_pins>;
+			clock-frequency = <100000>;
+		};
+	};
+
+	fragment@1 {
+		target = <&i2c4_pins>;
+		__dormant__ {
+			brcm,pins = <6 7>;
+		};
+	};
+
+	fragment@2 {
+		target = <&i2c4_pins>;
+		__overlay__ {
+			brcm,pins = <8 9>;
+		};
+	};
+
+	__overrides__ {
+		pins_6_7 = <0>,"=1!2";
+		pins_8_9 = <0>,"!1=2";
+		baudrate = <&frag0>, "clock-frequency:0";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/i2c5-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/i2c5-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/i2c5-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/i2c5-overlay.dts	2021-07-25 16:45:35.148807558 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/dts-v1/;
+/plugin/;
+
+/{
+	compatible = "brcm,bcm2711";
+
+	fragment@0 {
+		target = <&i2c5>;
+		frag0: __overlay__ {
+			status = "okay";
+			pinctrl-names = "default";
+			pinctrl-0 = <&i2c5_pins>;
+			clock-frequency = <100000>;
+		};
+	};
+
+	fragment@1 {
+		target = <&i2c5_pins>;
+		__dormant__ {
+			brcm,pins = <10 11>;
+		};
+	};
+
+	fragment@2 {
+		target = <&i2c5_pins>;
+		__overlay__ {
+			brcm,pins = <12 13>;
+		};
+	};
+
+	__overrides__ {
+		pins_10_11 = <0>,"=1!2";
+		pins_12_13 = <0>,"!1=2";
+		baudrate = <&frag0>, "clock-frequency:0";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/i2c6-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/i2c6-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/i2c6-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/i2c6-overlay.dts	2021-07-25 16:45:35.148807558 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/dts-v1/;
+/plugin/;
+
+/{
+	compatible = "brcm,bcm2711";
+
+	fragment@0 {
+		target = <&i2c6>;
+		frag0: __overlay__ {
+			status = "okay";
+			pinctrl-names = "default";
+			pinctrl-0 = <&i2c6_pins>;
+			clock-frequency = <100000>;
+		};
+	};
+
+	fragment@1 {
+		target = <&i2c6_pins>;
+		__dormant__ {
+			brcm,pins = <0 1>;
+		};
+	};
+
+	fragment@2 {
+		target = <&i2c6_pins>;
+		__overlay__ {
+			brcm,pins = <22 23>;
+		};
+	};
+
+	__overrides__ {
+		pins_0_1 = <0>,"=1!2";
+		pins_22_23 = <0>,"!1=2";
+		baudrate = <&frag0>, "clock-frequency:0";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/i2c-bcm2708-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/i2c-bcm2708-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/i2c-bcm2708-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/i2c-bcm2708-overlay.dts	2021-07-25 16:45:35.148807558 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/dts-v1/;
+/plugin/;
+
+/{
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&i2c_arm>;
+		__overlay__ {
+			compatible = "brcm,bcm2708-i2c";
+		};
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/i2c-gpio-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/i2c-gpio-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/i2c-gpio-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/i2c-gpio-overlay.dts	2021-07-25 16:45:35.148807558 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// Overlay for i2c_gpio bitbanging host bus.
+/dts-v1/;
+/plugin/;
+
+#include <dt-bindings/gpio/gpio.h>
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target-path = "/";
+
+		__overlay__ {
+			i2c_gpio: i2c@0 {
+				reg = <0xffffffff>;
+				compatible = "i2c-gpio";
+				gpios = <&gpio 23 (GPIO_ACTIVE_HIGH|GPIO_OPEN_DRAIN) /* sda */
+					 &gpio 24 (GPIO_ACTIVE_HIGH|GPIO_OPEN_DRAIN) /* scl */
+					>;
+				i2c-gpio,delay-us = <2>;        /* ~100 kHz */
+				#address-cells = <1>;
+				#size-cells = <0>;
+			};
+		};
+	};
+
+	fragment@1 {
+		target-path = "/aliases";
+		__overlay__ {
+			i2c_gpio = "/i2c@0";
+		};
+	};
+
+	fragment@2 {
+		target-path = "/__symbols__";
+		__overlay__ {
+			i2c_gpio = "/i2c@0";
+		};
+	};
+
+	__overrides__ {
+		i2c_gpio_sda = <&i2c_gpio>,"gpios:4";
+		i2c_gpio_scl = <&i2c_gpio>,"gpios:16";
+		i2c_gpio_delay_us = <&i2c_gpio>,"i2c-gpio,delay-us:0";
+		bus = <&i2c_gpio>, "reg:0";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/i2c-mux-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/i2c-mux-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/i2c-mux-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/i2c-mux-overlay.dts	2021-07-25 16:45:35.148807558 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// Umbrella I2C Mux overlay
+
+/dts-v1/;
+/plugin/;
+
+/{
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&i2c_arm>;
+		__dormant__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			status = "okay";
+
+			pca9542: mux@70 {
+				compatible = "nxp,pca9542";
+				reg = <0x70>;
+				#address-cells = <1>;
+				#size-cells = <0>;
+
+				i2c@0 {
+					#address-cells = <1>;
+					#size-cells = <0>;
+					reg = <0>;
+				};
+				i2c@1 {
+					#address-cells = <1>;
+					#size-cells = <0>;
+					reg = <1>;
+				};
+			};
+		};
+	};
+
+	fragment@1 {
+		target = <&i2c_arm>;
+		__dormant__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			status = "okay";
+
+			pca9545: mux@70 {
+				compatible = "nxp,pca9545";
+				reg = <0x70>;
+				#address-cells = <1>;
+				#size-cells = <0>;
+
+				i2c@0 {
+					#address-cells = <1>;
+					#size-cells = <0>;
+					reg = <0>;
+				};
+				i2c@1 {
+					#address-cells = <1>;
+					#size-cells = <0>;
+					reg = <1>;
+				};
+				i2c@2 {
+					#address-cells = <1>;
+					#size-cells = <0>;
+					reg = <2>;
+				};
+				i2c@3 {
+					#address-cells = <1>;
+					#size-cells = <0>;
+					reg = <3>;
+				};
+			};
+		};
+	};
+
+	fragment@2 {
+		target = <&i2c_arm>;
+		__dormant__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			status = "okay";
+
+			pca9548: mux@70 {
+				compatible = "nxp,pca9548";
+				reg = <0x70>;
+				#address-cells = <1>;
+				#size-cells = <0>;
+
+				i2c@0 {
+					#address-cells = <1>;
+					#size-cells = <0>;
+					reg = <0>;
+				};
+				i2c@1 {
+					#address-cells = <1>;
+					#size-cells = <0>;
+					reg = <1>;
+				};
+				i2c@2 {
+					#address-cells = <1>;
+					#size-cells = <0>;
+					reg = <2>;
+				};
+				i2c@3 {
+					#address-cells = <1>;
+					#size-cells = <0>;
+					reg = <3>;
+				};
+				i2c@4 {
+					#address-cells = <1>;
+					#size-cells = <0>;
+					reg = <4>;
+				};
+				i2c@5 {
+					#address-cells = <1>;
+					#size-cells = <0>;
+					reg = <5>;
+				};
+				i2c@6 {
+					#address-cells = <1>;
+					#size-cells = <0>;
+					reg = <6>;
+				};
+				i2c@7 {
+					#address-cells = <1>;
+					#size-cells = <0>;
+					reg = <7>;
+				};
+			};
+		};
+	};
+
+	__overrides__ {
+		pca9542 = <0>, "+0";
+		pca9545 = <0>, "+1";
+		pca9548 = <0>, "+2";
+
+		addr =  <&pca9542>,"reg:0",
+			<&pca9545>,"reg:0",
+			<&pca9548>,"reg:0";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/i2c-pwm-pca9685a-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/i2c-pwm-pca9685a-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/i2c-pwm-pca9685a-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/i2c-pwm-pca9685a-overlay.dts	2021-07-25 16:45:35.148807558 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// Definitions for NXP PCA9685A I2C PWM controller on ARM I2C bus.
+/dts-v1/;
+/plugin/;
+
+/{
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&i2c_arm>;
+		__overlay__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			status = "okay";
+
+			pca: pca@40 {
+				compatible = "nxp,pca9685-pwm";
+				#pwm-cells = <2>;
+				reg = <0x40>;
+				status = "okay";
+			};
+		};
+	};
+	__overrides__ {
+		addr = <&pca>,"reg:0";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/i2c-rtc-common.dtsi linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/i2c-rtc-common.dtsi
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/i2c-rtc-common.dtsi	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/i2c-rtc-common.dtsi	2021-07-25 16:45:35.148807558 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// Definitions for several I2C based Real Time Clocks
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&i2cbus>;
+		__dormant__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			abx80x: abx80x@69 {
+				compatible = "abracon,abx80x";
+				reg = <0x69>;
+				abracon,tc-diode = "standard";
+				abracon,tc-resistor = <0>;
+			};
+		};
+	};
+
+	fragment@1 {
+		target = <&i2cbus>;
+		__dormant__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			ds1307: ds1307@68 {
+				compatible = "dallas,ds1307";
+				reg = <0x68>;
+			};
+		};
+	};
+
+	fragment@2 {
+		target = <&i2cbus>;
+		__dormant__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			ds1339: ds1339@68 {
+				compatible = "dallas,ds1339";
+				trickle-resistor-ohms = <0>;
+				reg = <0x68>;
+			};
+		};
+	};
+
+	fragment@3 {
+		target = <&i2cbus>;
+		__dormant__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			ds3231: ds3231@68 {
+				compatible = "maxim,ds3231";
+				reg = <0x68>;
+			};
+		};
+	};
+
+	fragment@4 {
+		target = <&i2cbus>;
+		__dormant__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			mcp7940x: mcp7940x@6f {
+				compatible = "microchip,mcp7940x";
+				reg = <0x6f>;
+			};
+		};
+	};
+
+	fragment@5 {
+		target = <&i2cbus>;
+		__dormant__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			mcp7941x: mcp7941x@6f {
+				compatible = "microchip,mcp7941x";
+				reg = <0x6f>;
+			};
+		};
+	};
+
+	fragment@6 {
+		target = <&i2cbus>;
+		__dormant__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			pcf2127@51 {
+				compatible = "nxp,pcf2127";
+				reg = <0x51>;
+			};
+		};
+	};
+
+	fragment@7 {
+		target = <&i2cbus>;
+		__dormant__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			pcf8523: pcf8523@68 {
+				compatible = "nxp,pcf8523";
+				reg = <0x68>;
+			};
+		};
+	};
+
+	fragment@8 {
+		target = <&i2cbus>;
+		__dormant__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			pcf8563: pcf8563@51 {
+				compatible = "nxp,pcf8563";
+				reg = <0x51>;
+			};
+		};
+	};
+
+	fragment@9 {
+		target = <&i2cbus>;
+		__dormant__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			m41t62: m41t62@68 {
+				compatible = "st,m41t62";
+				reg = <0x68>;
+			};
+		};
+	};
+
+	fragment@10 {
+		target = <&i2cbus>;
+		__dormant__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			rv3028: rv3028@52 {
+				compatible = "microcrystal,rv3028";
+				reg = <0x52>;
+			};
+		};
+	};
+
+	fragment@11 {
+		target = <&i2cbus>;
+		__dormant__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			pcf2129@51 {
+				compatible = "nxp,pcf2129";
+				reg = <0x51>;
+			};
+		};
+	};
+
+	fragment@12 {
+		target = <&i2cbus>;
+	       __dormant__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			pcf85363@51 {
+				compatible = "nxp,pcf85363";
+				reg = <0x51>;
+			};
+		};
+	};
+
+	fragment@13 {
+		target = <&i2cbus>;
+		__dormant__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			rv1805: rv1805@69 {
+				compatible = "microcrystal,rv1805";
+				reg = <0x69>;
+				abracon,tc-diode = "standard";
+				abracon,tc-resistor = <0>;
+			};
+		};
+	};
+
+	fragment@14 {
+		target = <&i2cbus>;
+		__dormant__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			sd3078: sd3078@32 {
+				compatible = "whwave,sd3078";
+				reg = <0x32>;
+			};
+		};
+	};
+
+	fragment@15 {
+		target = <&i2cbus>;
+	       __dormant__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			pcf85063@51 {
+				compatible = "nxp,pcf85063";
+				reg = <0x51>;
+			};
+		};
+	};
+
+	fragment@16 {
+		target = <&i2cbus>;
+	       __dormant__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			pcf85063a@51 {
+				compatible = "nxp,pcf85063a";
+				reg = <0x51>;
+			};
+		};
+	};
+
+	fragment@17 {
+		target = <&i2cbus>;
+		__dormant__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			ds1340: ds1340@68 {
+				compatible = "dallas,ds1340";
+				trickle-resistor-ohms = <0>;
+				reg = <0x68>;
+			};
+		};
+	};
+
+	__overrides__ {
+		abx80x = <0>,"+0";
+		ds1307 = <0>,"+1";
+		ds1339 = <0>,"+2";
+		ds1340 = <0>,"+17";
+		ds3231 = <0>,"+3";
+		mcp7940x = <0>,"+4";
+		mcp7941x = <0>,"+5";
+		pcf2127 = <0>,"+6";
+		pcf8523 = <0>,"+7";
+		pcf8563 = <0>,"+8";
+		m41t62 = <0>,"+9";
+		rv3028 = <0>,"+10";
+		pcf2129 = <0>,"+11";
+		pcf85363 = <0>,"+12";
+		rv1805 = <0>,"+13";
+		sd3078 = <0>,"+14";
+		pcf85063 = <0>,"+15";
+		pcf85063a = <0>,"+16";
+
+		addr = <&abx80x>, "reg:0",
+		       <&ds1307>, "reg:0",
+		       <&ds1339>, "reg:0",
+		       <&ds3231>, "reg:0",
+		       <&mcp7940x>, "reg:0",
+		       <&mcp7941x>, "reg:0",
+		       <&pcf8523>, "reg:0",
+		       <&pcf8563>, "reg:0",
+		       <&m41t62>, "reg:0",
+		       <&rv1805>, "reg:0";
+		trickle-diode-type = <&abx80x>,"abracon,tc-diode",
+				     <&rv1805>,"abracon,tc-diode";
+		trickle-resistor-ohms = <&ds1339>,"trickle-resistor-ohms:0",
+					<&ds1340>,"trickle-resistor-ohms:0",
+					<&abx80x>,"abracon,tc-resistor:0",
+					<&rv3028>,"trickle-resistor-ohms:0",
+					<&rv1805>,"abracon,tc-resistor:0";
+		backup-switchover-mode = <&rv3028>,"backup-switchover-mode:0";
+		wakeup-source = <&ds1339>,"wakeup-source?",
+				<&ds3231>,"wakeup-source?",
+				<&mcp7940x>,"wakeup-source?",
+				<&mcp7941x>,"wakeup-source?",
+				<&m41t62>,"wakeup-source?";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/i2c-rtc-gpio-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/i2c-rtc-gpio-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/i2c-rtc-gpio-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/i2c-rtc-gpio-overlay.dts	2021-07-25 16:45:35.148807558 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// Definitions for several I2C based Real Time Clocks
+// Available through i2c-gpio
+/dts-v1/;
+/plugin/;
+
+#include <dt-bindings/gpio/gpio.h>
+
+#include "i2c-rtc-common.dtsi"
+
+/ {
+	fragment@100 {
+		target-path = "/";
+		__overlay__ {
+			i2cbus: i2c-gpio-rtc@0 {
+				compatible = "i2c-gpio";
+				gpios = <&gpio 23 (GPIO_ACTIVE_HIGH|GPIO_OPEN_DRAIN) /* sda */
+					 &gpio 24 (GPIO_ACTIVE_HIGH|GPIO_OPEN_DRAIN) /* scl */
+					>;
+				i2c-gpio,delay-us = <2>;        /* ~100 kHz */
+				#address-cells = <1>;
+				#size-cells = <0>;
+			};
+		};
+	};
+
+	__overrides__ {
+		i2c_gpio_sda = <&i2cbus>,"gpios:4";
+		i2c_gpio_scl = <&i2cbus>,"gpios:16";
+		i2c_gpio_delay_us = <&i2cbus>,"i2c-gpio,delay-us:0";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/i2c-rtc-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/i2c-rtc-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/i2c-rtc-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/i2c-rtc-overlay.dts	2021-07-25 16:45:35.148807558 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// Definitions for several I2C based Real Time Clocks
+/dts-v1/;
+/plugin/;
+
+#include "i2c-rtc-common.dtsi"
+
+/ {
+	frag100: fragment@100 {
+		target = <&i2c_arm>;
+		i2cbus: __overlay__ {
+			status = "okay";
+		};
+	};
+
+	fragment@101 {
+		target = <&i2c0if>;
+		__dormant__ {
+			status = "okay";
+		};
+	};
+
+	fragment@102 {
+		target = <&i2c0mux>;
+		__dormant__ {
+			status = "okay";
+		};
+	};
+
+	__overrides__ {
+		i2c0 = <&frag100>, "target:0=",<&i2c0>;
+		i2c_csi_dsi = <&frag100>, "target:0=",<&i2c_csi_dsi>,
+			      <0>,"+101+102";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/i2c-sensor-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/i2c-sensor-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/i2c-sensor-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/i2c-sensor-overlay.dts	2021-07-25 16:45:35.148807558 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// Definitions for I2C based sensors using the Industrial IO or HWMON interface.
+/dts-v1/;
+/plugin/;
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&i2c_arm>;
+		__dormant__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			status = "okay";
+
+			bme280: bme280@76 {
+				compatible = "bosch,bme280";
+				reg = <0x76>;
+				status = "okay";
+			};
+		};
+	};
+
+	fragment@1 {
+		target = <&i2c_arm>;
+		__dormant__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			status = "okay";
+
+			bmp085: bmp085@77 {
+				compatible = "bosch,bmp085";
+				reg = <0x77>;
+				default-oversampling = <3>;
+				status = "okay";
+			};
+		};
+	};
+
+	fragment@2 {
+		target = <&i2c_arm>;
+		__dormant__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			status = "okay";
+
+			bmp180: bmp180@77 {
+				compatible = "bosch,bmp180";
+				reg = <0x77>;
+				status = "okay";
+			};
+		};
+	};
+
+	fragment@3 {
+		target = <&i2c_arm>;
+		__dormant__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			status = "okay";
+
+			bmp280: bmp280@76 {
+				compatible = "bosch,bmp280";
+				reg = <0x76>;
+				status = "okay";
+			};
+		};
+	};
+
+	fragment@4 {
+		target = <&i2c_arm>;
+		__dormant__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			status = "okay";
+
+			htu21: htu21@40 {
+				compatible = "htu21";
+				reg = <0x40>;
+				status = "okay";
+			};
+		};
+	};
+
+	fragment@5 {
+		target = <&i2c_arm>;
+		__dormant__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			status = "okay";
+
+			lm75: lm75@4f {
+				compatible = "lm75";
+				reg = <0x4f>;
+				status = "okay";
+			};
+		};
+	};
+
+	fragment@6 {
+		target = <&i2c_arm>;
+		__dormant__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			status = "okay";
+
+			si7020: si7020@40 {
+				compatible = "si7020";
+				reg = <0x40>;
+				status = "okay";
+			};
+		};
+	};
+
+	fragment@7 {
+		target = <&i2c_arm>;
+		__dormant__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			status = "okay";
+
+			tmp102: tmp102@48 {
+				compatible = "ti,tmp102";
+				reg = <0x48>;
+				status = "okay";
+			};
+		};
+	};
+
+	fragment@8 {
+		target = <&i2c_arm>;
+		__dormant__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			status = "okay";
+
+			hdc100x: hdc100x@40 {
+				compatible = "hdc100x";
+				reg = <0x40>;
+				status = "okay";
+			};
+		};
+	};
+
+	fragment@9 {
+		target = <&i2c_arm>;
+		__dormant__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			status = "okay";
+
+			tsl4531: tsl4531@29 {
+				compatible = "tsl4531";
+				reg = <0x29>;
+				status = "okay";
+			};
+		};
+	};
+
+	fragment@10 {
+		target = <&i2c_arm>;
+		__dormant__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			status = "okay";
+
+			veml6070: veml6070@38 {
+				compatible = "veml6070";
+				reg = <0x38>;
+				status = "okay";
+			};
+		};
+	};
+
+	fragment@11 {
+		target = <&i2c_arm>;
+		__dormant__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			status = "okay";
+
+			sht3x: sht3x@44 {
+				compatible = "sht3x";
+				reg = <0x44>;
+				status = "okay";
+			};
+		};
+	};
+
+	fragment@12 {
+		target = <&i2c_arm>;
+		__dormant__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			status = "okay";
+
+			ds1621: ds1621@48 {
+				compatible = "ds1621";
+				reg = <0x48>;
+				status = "okay";
+			};
+		};
+	};
+
+	fragment@13 {
+		target = <&i2c_arm>;
+		__dormant__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			status = "okay";
+
+			max17040: max17040@36 {
+				compatible = "maxim,max17040";
+				reg = <0x36>;
+				status = "okay";
+			};
+		};
+	};
+
+	fragment@14 {
+		target = <&i2c_arm>;
+		__dormant__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			status = "okay";
+
+			bme680: bme680@76 {
+				compatible = "bosch,bme680";
+				reg = <0x76>;
+				status = "okay";
+			};
+		};
+	};
+
+	fragment@15 {
+		target = <&i2c_arm>;
+		__dormant__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			status = "okay";
+
+			sps30: sps30@69 {
+				compatible = "sensirion,sps30";
+				reg = <0x69>;
+				status = "okay";
+			};
+		};
+	};
+
+	fragment@16 {
+		target = <&i2c_arm>;
+		__dormant__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			status = "okay";
+
+			sgp30: sgp30@58 {
+				compatible = "sensirion,sgp30";
+				reg = <0x58>;
+				status = "okay";
+			};
+		};
+	};
+
+	fragment@17 {
+		target = <&i2c_arm>;
+		__dormant__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			status = "okay";
+
+			ccs811: ccs811@5b {
+				compatible = "ccs811";
+				reg = <0x5b>;
+				status = "okay";
+			};
+		};
+	};
+
+	fragment@18 {
+		target = <&i2c_arm>;
+		__dormant__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			status = "okay";
+
+			bh1750: bh1750@23 {
+				compatible = "bh1750";
+				reg = <0x23>;
+				status = "okay";
+			};
+		};
+	};
+
+	__overrides__ {
+		addr =  <&bme280>,"reg:0", <&bmp280>,"reg:0", <&tmp102>,"reg:0",
+			<&lm75>,"reg:0", <&hdc100x>,"reg:0", <&sht3x>,"reg:0",
+			<&ds1621>,"reg:0", <&bme680>,"reg:0", <&ccs811>,"reg:0",
+			<&bh1750>,"reg:0";
+		bme280 = <0>,"+0";
+		bmp085 = <0>,"+1";
+		bmp180 = <0>,"+2";
+		bmp280 = <0>,"+3";
+		htu21 = <0>,"+4";
+		lm75 = <0>,"+5";
+		lm75addr = <&lm75>,"reg:0";
+		si7020 = <0>,"+6";
+		tmp102 = <0>,"+7";
+		hdc100x = <0>,"+8";
+		tsl4531 = <0>,"+9";
+		veml6070 = <0>,"+10";
+		sht3x = <0>,"+11";
+		ds1621 = <0>,"+12";
+		max17040 = <0>,"+13";
+		bme680 = <0>,"+14";
+		sps30 = <0>,"+15";
+		sgp30 = <0>,"+16";
+		ccs811 = <0>, "+17";
+		bh1750 = <0>, "+18";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/i2s-gpio28-31-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/i2s-gpio28-31-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/i2s-gpio28-31-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/i2s-gpio28-31-overlay.dts	2021-07-25 16:45:35.148807558 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/*
+ * Device tree overlay to move i2s to gpio 28 to 31 on CM
+ */
+
+/dts-v1/;
+/plugin/;
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&i2s_pins>;
+		__overlay__ {
+			brcm,pins = <28 29 30 31>;
+			brcm,function = <6>; /* alt2 */
+		};
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/ilitek251x-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/ilitek251x-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/ilitek251x-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/ilitek251x-overlay.dts	2021-07-25 16:45:35.148807558 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// Device tree overlay for I2C connected Ilitek multiple touch controller
+/dts-v1/;
+/plugin/;
+
+ / {
+	compatible = "brcm,bcm2835";
+
+ 	fragment@0 {
+		target = <&gpio>;
+		__overlay__ {		
+			ili251x_pins: ili251x_pins {
+				brcm,pins = <4>; // interrupt
+				brcm,function = <0>; // in
+				brcm,pull = <2>; // pull-up //
+			};
+		};
+	};
+
+ 	fragment@1 {
+		target = <&i2c1>;
+		__overlay__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			status = "okay";
+
+ 			ili251x: ili251x@41 {
+				compatible = "ilitek,ili251x";
+				reg = <0x41>;
+				pinctrl-names = "default";
+				pinctrl-0 = <&ili251x_pins>;
+				interrupt-parent = <&gpio>;
+				interrupts = <4 8>; // high-to-low edge triggered
+				touchscreen-size-x = <16384>;
+				touchscreen-size-y = <9600>;
+			};
+		};
+	};
+
+ 	__overrides__ {
+		interrupt = <&ili251x_pins>,"brcm,pins:0",
+			<&ili251x>,"interrupts:0";
+		sizex = <&ili251x>,"touchscreen-size-x:0";
+		sizey = <&ili251x>,"touchscreen-size-y:0";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/imx219-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/imx219-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/imx219-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/imx219-overlay.dts	2021-07-25 16:45:35.148807558 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// SPDX-License-Identifier: GPL-2.0-only
+// Definitions for IMX219 camera module on VC I2C bus
+/dts-v1/;
+/plugin/;
+
+#include <dt-bindings/gpio/gpio.h>
+
+/{
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&i2c_csi_dsi>;
+		__overlay__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			status = "okay";
+
+			imx219: imx219@10 {
+				compatible = "sony,imx219";
+				reg = <0x10>;
+				status = "okay";
+
+				clocks = <&imx219_clk>;
+				clock-names = "xclk";
+
+				VANA-supply = <&cam1_reg>;	/* 2.8v */
+				VDIG-supply = <&imx219_vdig>;	/* 1.8v */
+				VDDL-supply = <&imx219_vddl>;	/* 1.2v */
+
+				rotation = <180>;
+
+				port {
+					imx219_0: endpoint {
+						remote-endpoint = <&csi1_ep>;
+						clock-lanes = <0>;
+						data-lanes = <1 2>;
+						clock-noncontinuous;
+						link-frequencies =
+							/bits/ 64 <456000000>;
+					};
+				};
+			};
+		};
+	};
+
+	fragment@1 {
+		target = <&csi1>;
+		__overlay__ {
+			status = "okay";
+
+			port {
+				csi1_ep: endpoint {
+					remote-endpoint = <&imx219_0>;
+					clock-lanes = <0>;
+					data-lanes = <1 2>;
+					clock-noncontinuous;
+				};
+			};
+		};
+	};
+
+	fragment@2 {
+		target = <&i2c0if>;
+		__overlay__ {
+			status = "okay";
+		};
+	};
+
+	fragment@3 {
+		target-path="/";
+		__overlay__ {
+			imx219_vdig: fixedregulator@1 {
+				compatible = "regulator-fixed";
+				regulator-name = "imx219_vdig";
+				regulator-min-microvolt = <1800000>;
+				regulator-max-microvolt = <1800000>;
+			};
+			imx219_vddl: fixedregulator@2 {
+				compatible = "regulator-fixed";
+				regulator-name = "imx219_vddl";
+				regulator-min-microvolt = <1200000>;
+				regulator-max-microvolt = <1200000>;
+			};
+
+			imx219_clk: camera-clk {
+				compatible = "fixed-clock";
+				#clock-cells = <0>;
+				clock-frequency = <24000000>;
+			};
+		};
+	};
+
+	fragment@4 {
+		target = <&i2c0mux>;
+		__overlay__ {
+			status = "okay";
+		};
+	};
+
+	fragment@5 {
+		target = <&cam1_reg>;
+		__overlay__ {
+			status = "okay";
+			regulator-name = "imx219_vana";
+			regulator-min-microvolt = <2800000>;
+			regulator-max-microvolt = <2800000>;
+		};
+	};
+
+	__overrides__ {
+		rotation = <&imx219>,"rotation:0";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/imx290_327-overlay.dtsi linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/imx290_327-overlay.dtsi
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/imx290_327-overlay.dtsi	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/imx290_327-overlay.dtsi	2021-07-25 16:45:35.148807558 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// SPDX-License-Identifier: GPL-2.0-only
+// Partial definitions for IMX290 or IMX327 camera module on VC I2C bus
+// The compatible string should be set in an overlay that then includes this one
+/dts-v1/;
+/plugin/;
+
+#include <dt-bindings/gpio/gpio.h>
+
+/{
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&i2c_csi_dsi>;
+		__overlay__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			status = "okay";
+
+			imx290: imx290@1a {
+				reg = <0x1a>;
+				status = "okay";
+
+				clocks = <&imx290_clk>;
+				clock-names = "xclk";
+				clock-frequency = <37125000>;
+
+				vdda-supply = <&cam1_reg>;	/* 2.8v */
+				vdddo-supply = <&imx290_vdddo>;	/* 1.8v */
+				vddd-supply = <&imx290_vddd>;	/* 1.5v */
+
+				port {
+					imx290_0: endpoint {
+						remote-endpoint = <&csi1_ep>;
+						clock-lanes = <0>;
+					};
+				};
+			};
+		};
+	};
+
+	fragment@1 {
+		target = <&csi1>;
+		__overlay__ {
+			status = "okay";
+
+			port {
+				csi1_ep: endpoint {
+					remote-endpoint = <&imx290_0>;
+				};
+			};
+		};
+	};
+
+	fragment@2 {
+		target = <&i2c0if>;
+		__overlay__ {
+			status = "okay";
+		};
+	};
+
+	fragment@3 {
+		target-path="/";
+		__overlay__ {
+			imx290_vdddo: fixedregulator@1 {
+				compatible = "regulator-fixed";
+				regulator-name = "imx290_vdddo";
+				regulator-min-microvolt = <1800000>;
+				regulator-max-microvolt = <1800000>;
+			};
+			imx290_vddd: fixedregulator@2 {
+				compatible = "regulator-fixed";
+				regulator-name = "imx290_vddd";
+				regulator-min-microvolt = <1500000>;
+				regulator-max-microvolt = <1500000>;
+			};
+
+			imx290_clk: camera-clk {
+				compatible = "fixed-clock";
+				#clock-cells = <0>;
+				clock-frequency = <37125000>;
+			};
+		};
+	};
+
+	fragment@4 {
+		target = <&i2c0mux>;
+		__overlay__ {
+			status = "okay";
+		};
+	};
+
+	fragment@5 {
+		target = <&cam1_reg>;
+		__overlay__ {
+			status = "okay";
+			regulator-name = "imx290_vdda";
+			regulator-min-microvolt = <2800000>;
+			regulator-max-microvolt = <2800000>;
+		};
+	};
+
+	fragment@6 {
+		target = <&imx290_0>;
+		__overlay__ {
+			data-lanes = <1 2>;
+			link-frequencies =
+				/bits/ 64 <445500000 297000000>;
+		};
+	};
+
+	fragment@7 {
+		target = <&imx290_0>;
+		__dormant__ {
+			data-lanes = <1 2 3 4>;
+			link-frequencies =
+				/bits/ 64 <222750000 148500000>;
+		};
+	};
+
+	fragment@8 {
+		target = <&csi1_ep>;
+		__overlay__ {
+			data-lanes = <1 2>;
+		};
+	};
+
+	fragment@9 {
+		target = <&csi1_ep>;
+		__dormant__ {
+			data-lanes = <1 2 3 4>;
+		};
+	};
+
+	__overrides__ {
+		4lane = <0>, "-6+7-8+9";
+		clock-frequency = <&imx290_clk>,"clock-frequency:0",
+				  <&imx290>,"clock-frequency:0";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/imx290-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/imx290-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/imx290-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/imx290-overlay.dts	2021-07-25 16:45:35.148807558 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// SPDX-License-Identifier: GPL-2.0-only
+// Definitions for IMX290 camera module on VC I2C bus
+/dts-v1/;
+/plugin/;
+
+#include <dt-bindings/gpio/gpio.h>
+#include "imx290_327-overlay.dtsi"
+
+/{
+	compatible = "brcm,bcm2835";
+
+	// Fragment numbers deliberately high to avoid conflicts with the
+	// included imx290_327 overlay file.
+
+	fragment@101 {
+		target = <&imx290>;
+		__overlay__ {
+			compatible = "sony,imx290";
+		};
+	};
+
+	fragment@102 {
+		target = <&imx290>;
+		__dormant__ {
+			compatible = "sony,imx290-mono";
+		};
+	};
+
+	__overrides__ {
+		mono = <0>, "-101+102";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/imx378-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/imx378-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/imx378-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/imx378-overlay.dts	2021-07-25 16:45:35.148807558 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// SPDX-License-Identifier: GPL-2.0-only
+// Definitions for IMX378 camera module on VC I2C bus
+/dts-v1/;
+/plugin/;
+
+#include "imx477_378-overlay.dtsi"
+
+&imx477 {
+	compatible = "sony,imx378";
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/imx477_378-overlay.dtsi linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/imx477_378-overlay.dtsi
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/imx477_378-overlay.dtsi	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/imx477_378-overlay.dtsi	2021-07-25 16:45:35.148807558 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// SPDX-License-Identifier: GPL-2.0-only
+// Definitions for IMX477 camera module on VC I2C bus
+
+/{
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&i2c_csi_dsi>;
+		__overlay__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			status = "okay";
+
+			imx477: imx477@1a {
+				reg = <0x1a>;
+				status = "okay";
+
+				clocks = <&imx477_clk>;
+				clock-names = "xclk";
+
+				VANA-supply = <&cam1_reg>;	/* 2.8v */
+				VDIG-supply = <&imx477_vdig>;	/* 1.05v */
+				VDDL-supply = <&imx477_vddl>;	/* 1.8v */
+
+				rotation = <180>;
+
+				port {
+					imx477_0: endpoint {
+						remote-endpoint = <&csi1_ep>;
+						clock-lanes = <0>;
+						data-lanes = <1 2>;
+						clock-noncontinuous;
+						link-frequencies =
+							/bits/ 64 <450000000>;
+					};
+				};
+			};
+		};
+	};
+
+	fragment@1 {
+		target = <&csi1>;
+		__overlay__ {
+			status = "okay";
+
+			port {
+				csi1_ep: endpoint {
+					remote-endpoint = <&imx477_0>;
+					clock-lanes = <0>;
+					data-lanes = <1 2>;
+					clock-noncontinuous;
+				};
+			};
+		};
+	};
+
+	fragment@2 {
+		target = <&i2c0if>;
+		__overlay__ {
+			status = "okay";
+		};
+	};
+
+	fragment@3 {
+		target-path="/";
+		__overlay__ {
+			imx477_vdig: fixedregulator@0 {
+				compatible = "regulator-fixed";
+				regulator-name = "imx477_vdig";
+				regulator-min-microvolt = <1050000>;
+				regulator-max-microvolt = <1050000>;
+			};
+			imx477_vddl: fixedregulator@1 {
+				compatible = "regulator-fixed";
+				regulator-name = "imx477_vddl";
+				regulator-min-microvolt = <1800000>;
+				regulator-max-microvolt = <1800000>;
+			};
+			imx477_clk: camera-clk {
+				compatible = "fixed-clock";
+				#clock-cells = <0>;
+				clock-frequency = <24000000>;
+			};
+		};
+	};
+
+	fragment@4 {
+		target = <&i2c0mux>;
+		__overlay__ {
+			status = "okay";
+		};
+	};
+
+	fragment@5 {
+		target = <&cam1_reg>;
+		__overlay__ {
+			status = "okay";
+			regulator-name = "imx477_vana";
+			startup-delay-us = <300000>;
+			regulator-min-microvolt = <2800000>;
+			regulator-max-microvolt = <2800000>;
+		};
+	};
+
+	__overrides__ {
+		rotation = <&imx477>,"rotation:0";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/imx477-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/imx477-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/imx477-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/imx477-overlay.dts	2021-07-25 16:45:35.148807558 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// SPDX-License-Identifier: GPL-2.0-only
+// Definitions for IMX477 camera module on VC I2C bus
+/dts-v1/;
+/plugin/;
+
+#include "imx477_378-overlay.dtsi"
+
+&imx477 {
+	compatible = "sony,imx477";
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/iqaudio-codec-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/iqaudio-codec-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/iqaudio-codec-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/iqaudio-codec-overlay.dts	2021-07-25 16:45:35.148807558 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// Definitions for IQaudIO CODEC
+/dts-v1/;
+/plugin/;
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&i2s>;
+		__overlay__ {
+			status = "okay";
+		};
+	};
+
+	fragment@1 {
+		target = <&i2c1>;
+		__overlay__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			status = "okay";
+
+			da2713@1a {
+				#sound-dai-cells = <0>;
+				compatible = "dlg,da7213";
+				reg = <0x1a>;
+				status = "okay";
+			};
+		};
+	};
+
+	fragment@2 {
+		target = <&sound>;
+		iqaudio_dac: __overlay__ {
+			compatible = "iqaudio,iqaudio-codec";
+			i2s-controller = <&i2s>;
+			status = "okay";
+		};
+	};
+
+	__overrides__ {
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/iqaudio-dac-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/iqaudio-dac-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/iqaudio-dac-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/iqaudio-dac-overlay.dts	2021-07-25 16:45:35.148807558 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// Definitions for IQaudIO DAC
+/dts-v1/;
+/plugin/;
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&i2s>;
+		__overlay__ {
+			status = "okay";
+		};
+	};
+
+	fragment@1 {
+		target = <&i2c1>;
+		__overlay__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			status = "okay";
+
+			pcm5122@4c {
+				#sound-dai-cells = <0>;
+				compatible = "ti,pcm5122";
+				reg = <0x4c>;
+				AVDD-supply = <&vdd_3v3_reg>;
+				DVDD-supply = <&vdd_3v3_reg>;
+				CPVDD-supply = <&vdd_3v3_reg>;
+				status = "okay";
+			};
+		};
+	};
+
+	fragment@2 {
+		target = <&sound>;
+		frag2: __overlay__ {
+			compatible = "iqaudio,iqaudio-dac";
+			i2s-controller = <&i2s>;
+			status = "okay";
+		};
+	};
+
+	__overrides__ {
+		24db_digital_gain = <&frag2>,"iqaudio,24db_digital_gain?";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/iqaudio-dacplus-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/iqaudio-dacplus-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/iqaudio-dacplus-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/iqaudio-dacplus-overlay.dts	2021-07-25 16:45:35.148807558 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// Definitions for IQaudIO DAC+
+/dts-v1/;
+/plugin/;
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&i2s>;
+		__overlay__ {
+			status = "okay";
+		};
+	};
+
+	fragment@1 {
+		target = <&i2c1>;
+		__overlay__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			status = "okay";
+
+			pcm5122@4c {
+				#sound-dai-cells = <0>;
+				compatible = "ti,pcm5122";
+				reg = <0x4c>;
+				AVDD-supply = <&vdd_3v3_reg>;
+				DVDD-supply = <&vdd_3v3_reg>;
+				CPVDD-supply = <&vdd_3v3_reg>;
+				status = "okay";
+			};
+		};
+	};
+
+	fragment@2 {
+		target = <&sound>;
+		iqaudio_dac: __overlay__ {
+			compatible = "iqaudio,iqaudio-dac";
+			i2s-controller = <&i2s>;
+			mute-gpios = <&gpio 22 0>;
+			status = "okay";
+		};
+	};
+
+	__overrides__ {
+		24db_digital_gain = <&iqaudio_dac>,"iqaudio,24db_digital_gain?";
+		auto_mute_amp = <&iqaudio_dac>,"iqaudio-dac,auto-mute-amp?";
+		unmute_amp = <&iqaudio_dac>,"iqaudio-dac,unmute-amp?";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/iqaudio-digi-wm8804-audio-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/iqaudio-digi-wm8804-audio-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/iqaudio-digi-wm8804-audio-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/iqaudio-digi-wm8804-audio-overlay.dts	2021-07-25 16:45:35.148807558 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// Definitions for IQAudIO Digi WM8804 audio board
+/dts-v1/;
+/plugin/;
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&i2s>;
+		__overlay__ {
+			status = "okay";
+		};
+	};
+
+	fragment@1 {
+		target = <&i2c1>;
+		__overlay__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			status = "okay";
+
+			wm8804@3b {
+				#sound-dai-cells = <0>;
+				compatible = "wlf,wm8804";
+				reg = <0x3b>;
+				status = "okay";
+				DVDD-supply = <&vdd_3v3_reg>;
+				PVDD-supply = <&vdd_3v3_reg>;
+			};
+		};
+	};
+
+	fragment@2 {
+		target = <&sound>;
+		wm8804_digi: __overlay__ {
+			compatible = "iqaudio,wm8804-digi";
+			i2s-controller = <&i2s>;
+			status = "okay";
+		};
+	};
+
+	__overrides__ {
+		card_name = <&wm8804_digi>,"wm8804-digi,card-name";
+		dai_name = <&wm8804_digi>,"wm8804-digi,dai-name";
+		dai_stream_name = <&wm8804_digi>,"wm8804-digi,dai-stream-name";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/irs1125-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/irs1125-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/irs1125-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/irs1125-overlay.dts	2021-07-25 16:45:35.148807558 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// SPDX-License-Identifier: GPL-2.0-only
+// Definitions for IRS1125 camera module on VC I2C bus
+/dts-v1/;
+/plugin/;
+
+/{
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&i2c_csi_dsi>;
+		__overlay__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			status = "okay";
+
+			irs1125: irs1125@3D {
+				compatible = "infineon,irs1125";
+				reg = <0x3D>;
+				status = "okay";
+
+				pwdn-gpios = <&gpio 5 0>;
+				clocks = <&irs1125_clk>;
+
+				port {
+					irs1125_0: endpoint {
+						remote-endpoint = <&csi1_ep>;
+						clock-lanes = <0>;
+						data-lanes = <1 2>;
+						clock-noncontinuous;
+						link-frequencies =
+							/bits/ 64 <297000000>;
+					};
+				};
+			};
+		};
+	};
+
+	fragment@1 {
+		target = <&csi1>;
+		__overlay__ {
+			status = "okay";
+
+			port {
+				csi1_ep: endpoint {
+					remote-endpoint = <&irs1125_0>;
+					data-lanes = <1 2>;
+					clock-noncontinuous;
+				};
+			};
+		};
+	};
+
+	fragment@2 {
+		target = <&i2c0if>;
+		__overlay__ {
+			status = "okay";
+		};
+	};
+
+	fragment@3 {
+		target = <&i2c0mux>;
+		__overlay__ {
+			status = "okay";
+		};
+	};
+
+	fragment@4 {
+		target-path="/__overrides__";
+		__overlay__ {
+			cam0-pwdn-ctrl = <&irs1125>,"pwdn-gpios:0";
+			cam0-pwdn      = <&irs1125>,"pwdn-gpios:4";
+		};
+	};
+
+	fragment@5 {
+		target-path = "/";
+		__overlay__ {
+			irs1125_clk: camera-clk {
+				compatible = "fixed-clock";
+				#clock-cells = <0>;
+				clock-frequency = <26000000>;
+			};
+		};
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/i-sabre-q2m-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/i-sabre-q2m-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/i-sabre-q2m-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/i-sabre-q2m-overlay.dts	2021-07-25 16:45:35.148807558 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// Definitions for I-Sabre Q2M
+/dts-v1/;
+/plugin/;
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&sound>;
+		frag0: __overlay__ {
+			compatible = "audiophonics,i-sabre-q2m";
+			i2s-controller = <&i2s>;
+			status = "okay";
+		};
+	};
+
+	fragment@1 {
+		target = <&i2s>;
+		__overlay__ {
+			status = "okay";
+		};
+	};
+
+	fragment@2 {
+		target = <&i2c1>;
+		__overlay__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			status = "okay";
+
+			i-sabre-codec@48 {
+				#sound-dai-cells = <0>;
+				compatible = "audiophonics,i-sabre-codec";
+				reg = <0x48>;
+				status = "okay";
+			};
+		};
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/jedec-spi-nor-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/jedec-spi-nor-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/jedec-spi-nor-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/jedec-spi-nor-overlay.dts	2021-07-25 16:45:35.148807558 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// Overlay for JEDEC SPI-NOR Flash Devices (aka m25p80)
+
+// dtparams:
+//     flash-spi<n>-<m>        - Enables flash device on SPI<n>, CS#<m>.
+//     flash-fastr-spi<n>-<m>  - Enables flash device with fast read capability on SPI<n>, CS#<m>.
+//
+// If devices are present on SPI1 or SPI2, those interfaces must be enabled with one of the spi1-1/2/3cs and/or spi2-1/2/3cs overlays.
+//
+// Example: A single flash device with fast read capability on SPI0, CS#0:
+// dtoverlay=jedec-spi-nor:flash-fastr-spi0-0
+
+/dts-v1/;
+/plugin/;
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	// disable spi-dev on spi0.0
+	fragment@0 {
+		target = <&spidev0>;
+		__dormant__ {
+			status = "disabled";
+		};
+	};
+
+	// disable spi-dev on spi0.1
+	fragment@1 {
+		target = <&spidev1>;
+		__dormant__ {
+			status = "disabled";
+		};
+	};
+
+	// disable spi-dev on spi1.0
+	fragment@2 {
+		target-path = "spi1/spidev@0";
+		__dormant__ {
+			status = "disabled";
+		};
+	};
+
+	// disable spi-dev on spi1.1
+	fragment@3 {
+		target-path = "spi1/spidev@1";
+		__dormant__ {
+			status = "disabled";
+		};
+	};
+
+	// disable spi-dev on spi1.2
+	fragment@4 {
+		target-path = "spi1/spidev@2";
+		__dormant__ {
+			status = "disabled";
+		};
+	};
+
+	// disable spi-dev on spi2.0
+	fragment@5 {
+		target-path = "spi2/spidev@0";
+		__dormant__ {
+			status = "disabled";
+		};
+	};
+
+	// disable spi-dev on spi2.1
+	fragment@6 {
+		target-path = "spi2/spidev@1";
+		__dormant__ {
+			status = "disabled";
+		};
+	};
+
+	// disable spi-dev on spi2.2
+	fragment@7 {
+		target-path = "spi2/spidev@2";
+		__dormant__ {
+			status = "disabled";
+		};
+	};
+
+	// enable flash on spi0.0
+	fragment@8 {
+		target = <&spi0>;
+		__dormant__ {
+			status = "okay";
+                        #address-cells = <1>;
+                        #size-cells = <0>;
+			spi_nor_00: spi_nor@0 {
+				#address-cells = <1>;
+				#size-cells = <1>;
+				compatible = "jedec,spi-nor";
+				reg = <0>;
+				spi-max-frequency = <500000>;
+			};
+		};
+	};
+
+	// enable flash on spi0.1
+	fragment@9 {
+		target = <&spi0>;
+		__dormant__ {
+			status = "okay";
+                        #address-cells = <1>;
+                        #size-cells = <0>;
+			spi_nor_01: spi_nor@1 {
+				#address-cells = <1>;
+				#size-cells = <1>;
+				compatible = "jedec,spi-nor";
+				reg = <1>;
+				spi-max-frequency = <500000>;
+			};
+		};
+	};
+
+	// enable flash on spi1.0
+	fragment@10 {
+		target = <&spi1>;
+		__dormant__ {
+			status = "okay";
+                        #address-cells = <1>;
+                        #size-cells = <0>;
+			spi_nor_10: spi_nor@0 {
+				#address-cells = <1>;
+				#size-cells = <1>;
+				compatible = "jedec,spi-nor";
+				reg = <0>;
+				spi-max-frequency = <500000>;
+			};
+		};
+	};
+
+	// enable flash on spi1.1
+	fragment@11 {
+		target = <&spi1>;
+		__dormant__ {
+			status = "okay";
+                        #address-cells = <1>;
+                        #size-cells = <0>;
+			spi_nor_11: spi_nor@1 {
+				#address-cells = <1>;
+				#size-cells = <1>;
+				compatible = "jedec,spi-nor";
+				reg = <1>;
+				spi-max-frequency = <500000>;
+			};
+		};
+	};
+
+	// enable flash on spi1.2
+	fragment@12 {
+		target = <&spi1>;
+		__dormant__ {
+			status = "okay";
+                        #address-cells = <1>;
+                        #size-cells = <0>;
+			spi_nor_12: spi_nor@2 {
+				#address-cells = <1>;
+				#size-cells = <1>;
+				compatible = "jedec,spi-nor";
+				reg = <2>;
+				spi-max-frequency = <500000>;
+			};
+		};
+	};
+
+	// enable flash on spi2.0
+	fragment@13 {
+		target = <&spi2>;
+		__dormant__ {
+			status = "okay";
+                        #address-cells = <1>;
+                        #size-cells = <0>;
+			spi_nor_20: spi_nor@0 {
+				#address-cells = <1>;
+				#size-cells = <1>;
+				compatible = "jedec,spi-nor";
+				reg = <0>;
+				spi-max-frequency = <500000>;
+			};
+		};
+	};
+
+	// enable flash on spi2.1
+	fragment@14 {
+		target = <&spi2>;
+		__dormant__ {
+			status = "okay";
+                        #address-cells = <1>;
+                        #size-cells = <0>;
+			spi_nor_21: spi_nor@1 {
+				#address-cells = <1>;
+				#size-cells = <1>;
+				compatible = "jedec,spi-nor";
+				reg = <1>;
+				spi-max-frequency = <500000>;
+			};
+		};
+	};
+
+	// enable flash on spi2.2
+	fragment@15 {
+		target = <&spi2>;
+		__dormant__ {
+			status = "okay";
+                        #address-cells = <1>;
+                        #size-cells = <0>;
+			spi_nor_22: spi_nor@2 {
+				#address-cells = <1>;
+				#size-cells = <1>;
+				compatible = "jedec,spi-nor";
+				reg = <2>;
+				spi-max-frequency = <500000>;
+			};
+		};
+	};
+
+	// Enable fast read for device on spi0.0.
+	// Use default active low interrupt signalling.
+	fragment@16 {
+		target = <&spi_nor_00>;
+		__dormant__ {
+			m25p,fast-read;
+		};
+	};
+
+	// Enable fast read for device on spi0.1.
+	// Use default active low interrupt signalling.
+	fragment@17 {
+		target = <&spi_nor_01>;
+		__dormant__ {
+			m25p,fast-read;
+		};
+	};
+
+	// Enable fast read for device on spi1.0.
+	// Use default active low interrupt signalling.
+	fragment@18 {
+		target = <&spi_nor_10>;
+		__dormant__ {
+			m25p,fast-read;
+		};
+	};
+
+	// Enable fast read for device on spi1.1.
+	// Use default active low interrupt signalling.
+	fragment@19 {
+		target = <&spi_nor_11>;
+		__dormant__ {
+			m25p,fast-read;
+		};
+	};
+
+	// Enable fast read for device on spi1.2.
+	// Use default active low interrupt signalling.
+	fragment@20 {
+		target = <&spi_nor_12>;
+		__dormant__ {
+			m25p,fast-read;
+		};
+	};
+
+	// Enable fast read for device on spi2.0.
+	// Use default active low interrupt signalling.
+	fragment@21 {
+		target = <&spi_nor_20>;
+		__dormant__ {
+			m25p,fast-read;
+		};
+	};
+
+	// Enable fast read for device on spi2.1.
+	// Use default active low interrupt signalling.
+	fragment@22 {
+		target = <&spi_nor_21>;
+		__dormant__ {
+			m25p,fast-read;
+		};
+	};
+
+	// Enable fast read for device on spi2.2.
+	// Use default active low interrupt signalling.
+	fragment@23 {
+		target = <&spi_nor_22>;
+		__dormant__ {
+			m25p,fast-read;
+		};
+	};
+
+	__overrides__ {
+		flash-spi0-0       = <0>,"+0+8";
+		flash-spi0-1       = <0>,"+1+9";
+		flash-spi1-0       = <0>,"+2+10";
+		flash-spi1-1       = <0>,"+3+11";
+		flash-spi1-2       = <0>,"+4+12";
+		flash-spi2-0       = <0>,"+5+13";
+		flash-spi2-1       = <0>,"+6+14";
+		flash-spi2-2       = <0>,"+7+15";
+		flash-fastr-spi0-0 = <0>,"+0+8+16";
+		flash-fastr-spi0-1 = <0>,"+1+9+17";
+		flash-fastr-spi1-0 = <0>,"+2+10+18";
+		flash-fastr-spi1-1 = <0>,"+3+11+19";
+		flash-fastr-spi1-2 = <0>,"+4+12+20";
+		flash-fastr-spi2-0 = <0>,"+5+13+21";
+		flash-fastr-spi2-1 = <0>,"+6+14+22";
+		flash-fastr-spi2-2 = <0>,"+7+15+23";
+	};
+};
+
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/justboom-both-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/justboom-both-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/justboom-both-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/justboom-both-overlay.dts	2021-07-25 16:45:35.148807558 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// SPDX-License-Identifier: GPL-2.0
+// Definitions for JustBoom Both (Digi+DAC)
+/dts-v1/;
+/plugin/;
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&i2s>;
+		__overlay__ {
+			status = "okay";
+		};
+	};
+
+	fragment@1 {
+		target = <&i2c1>;
+		__overlay__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			status = "okay";
+
+			wm8804@3b {
+				#sound-dai-cells = <0>;
+				compatible = "wlf,wm8804";
+				reg = <0x3b>;
+				PVDD-supply = <&vdd_3v3_reg>;
+				DVDD-supply = <&vdd_3v3_reg>;
+				status = "okay";
+			};
+		};
+	};
+
+	fragment@2 {
+		target = <&i2c1>;
+		__overlay__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			status = "okay";
+
+			pcm5122@4d {
+				#sound-dai-cells = <0>;
+				compatible = "ti,pcm5122";
+				reg = <0x4d>;
+				AVDD-supply = <&vdd_3v3_reg>;
+				DVDD-supply = <&vdd_3v3_reg>;
+				CPVDD-supply = <&vdd_3v3_reg>;
+				status = "okay";
+			};
+		};
+	};
+
+	fragment@3 {
+		target = <&sound>;
+		frag3: __overlay__ {
+			compatible = "justboom,justboom-both";
+			i2s-controller = <&i2s>;
+			status = "okay";
+		};
+	};
+
+	__overrides__ {
+		24db_digital_gain = <&frag3>,"justboom,24db_digital_gain?";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/justboom-dac-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/justboom-dac-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/justboom-dac-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/justboom-dac-overlay.dts	2021-07-25 16:45:35.148807558 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// Definitions for JustBoom DAC
+/dts-v1/;
+/plugin/;
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&i2s>;
+		__overlay__ {
+			status = "okay";
+		};
+	};
+
+	fragment@1 {
+		target = <&i2c1>;
+		__overlay__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			status = "okay";
+
+			pcm5122@4d {
+				#sound-dai-cells = <0>;
+				compatible = "ti,pcm5122";
+				reg = <0x4d>;
+				AVDD-supply = <&vdd_3v3_reg>;
+				DVDD-supply = <&vdd_3v3_reg>;
+				CPVDD-supply = <&vdd_3v3_reg>;
+				status = "okay";
+			};
+		};
+	};
+
+	fragment@2 {
+		target = <&sound>;
+		frag2: __overlay__ {
+			compatible = "justboom,justboom-dac";
+			i2s-controller = <&i2s>;
+			status = "okay";
+		};
+	};
+
+	__overrides__ {
+		24db_digital_gain = <&frag2>,"justboom,24db_digital_gain?";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/justboom-digi-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/justboom-digi-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/justboom-digi-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/justboom-digi-overlay.dts	2021-07-25 16:45:35.148807558 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// Definitions for JustBoom Digi
+/dts-v1/;
+/plugin/;
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&i2s>;
+		__overlay__ {
+			status = "okay";
+		};
+	};
+
+	fragment@1 {
+		target = <&i2c1>;
+		__overlay__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			status = "okay";
+
+			wm8804@3b {
+				#sound-dai-cells = <0>;
+				compatible = "wlf,wm8804";
+				reg = <0x3b>;
+				PVDD-supply = <&vdd_3v3_reg>;
+				DVDD-supply = <&vdd_3v3_reg>;
+				status = "okay";
+			};
+		};
+	};
+
+	fragment@2 {
+		target = <&sound>;
+		__overlay__ {
+			compatible = "justboom,justboom-digi";
+			i2s-controller = <&i2s>;
+			status = "okay";
+		};
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/ltc294x-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/ltc294x-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/ltc294x-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/ltc294x-overlay.dts	2021-07-25 16:45:35.148807558 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/dts-v1/;
+/plugin/;
+
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&i2c_arm>;
+		__dormant__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			status = "okay";
+
+			ltc2941: ltc2941@64 {
+				compatible = "lltc,ltc2941";
+				reg = <0x64>;
+				lltc,resistor-sense = <50>;
+				lltc,prescaler-exponent = <7>; 
+			};
+		};
+	};
+
+	fragment@1 {
+		target = <&i2c_arm>;
+		__dormant__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			status = "okay";
+
+			ltc2942: ltc2942@64 {
+				compatible = "lltc,ltc2942";
+				reg = <0x64>;
+				lltc,resistor-sense = <50>;
+				lltc,prescaler-exponent = <7>; 
+			};
+		};
+	};
+
+	fragment@2 {
+		target = <&i2c_arm>;
+		__dormant__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			status = "okay";
+
+			ltc2943: ltc2943@64 {
+				compatible = "lltc,ltc2943";
+				reg = <0x64>;
+				lltc,resistor-sense = <50>;
+				lltc,prescaler-exponent = <7>; 
+			};
+		};
+	};
+
+	fragment@3 {
+		target = <&i2c_arm>;
+		__dormant__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			status = "okay";
+
+			ltc2944: ltc2944@64 {
+				compatible = "lltc,ltc2944";
+				reg = <0x64>;
+				lltc,resistor-sense = <50>;
+				lltc,prescaler-exponent = <7>; 
+			};
+		};
+	};
+
+	__overrides__ {
+		ltc2941 = <0>,"+0";
+		ltc2942 = <0>,"+1";
+		ltc2943 = <0>,"+2";
+		ltc2944 = <0>,"+3";
+		resistor-sense = <&ltc2941>, "lltc,resistor-sense:0",
+			         <&ltc2942>, "lltc,resistor-sense:0",
+				 <&ltc2943>, "lltc,resistor-sense:0",
+				 <&ltc2944>, "lltc,resistor-sense:0";
+		prescaler-exponent = <&ltc2941>, "lltc,prescaler-exponent:0",
+			         <&ltc2942>, "lltc,prescaler-exponent:0",
+				 <&ltc2943>, "lltc,prescaler-exponent:0",
+				 <&ltc2944>, "lltc,prescaler-exponent:0";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/Makefile linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/Makefile
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/Makefile	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/Makefile	2021-07-25 16:45:35.138807726 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+# Overlays for the Raspberry Pi platform
+
+dtb-$(CONFIG_ARCH_BCM2835) += overlay_map.dtb
+
+dtbo-$(CONFIG_ARCH_BCM2835) += \
+	act-led.dtbo \
+	adafruit18.dtbo \
+	adau1977-adc.dtbo \
+	adau7002-simple.dtbo \
+	ads1015.dtbo \
+	ads1115.dtbo \
+	ads7846.dtbo \
+	adv7282m.dtbo \
+	adv728x-m.dtbo \
+	akkordion-iqdacplus.dtbo \
+	allo-boss-dac-pcm512x-audio.dtbo \
+	allo-boss2-dac-audio.dtbo \
+	allo-digione.dtbo \
+	allo-katana-dac-audio.dtbo \
+	allo-piano-dac-pcm512x-audio.dtbo \
+	allo-piano-dac-plus-pcm512x-audio.dtbo \
+	anyspi.dtbo \
+	apds9960.dtbo \
+	applepi-dac.dtbo \
+	at86rf233.dtbo \
+	audioinjector-addons.dtbo \
+	audioinjector-isolated-soundcard.dtbo \
+	audioinjector-ultra.dtbo \
+	audioinjector-wm8731-audio.dtbo \
+	audiosense-pi.dtbo \
+	audremap.dtbo \
+	balena-fin.dtbo \
+	cap1106.dtbo \
+	chipdip-i2s-master-dac.dtbo \
+	cma.dtbo \
+	dht11.dtbo \
+	dionaudio-loco.dtbo \
+	dionaudio-loco-v2.dtbo \
+	disable-bt.dtbo \
+	disable-wifi.dtbo \
+	dpi18.dtbo \
+	dpi18cpadhi.dtbo \
+	dpi24.dtbo \
+	draws.dtbo \
+	dwc-otg.dtbo \
+	dwc2.dtbo \
+	edt-ft5406.dtbo \
+	enc28j60.dtbo \
+	enc28j60-spi2.dtbo \
+	exc3000.dtbo \
+	fe-pi-audio.dtbo \
+	fsm-demo.dtbo \
+	ghost-amp.dtbo \
+	goodix.dtbo \
+	googlevoicehat-soundcard.dtbo \
+	gpio-fan.dtbo \
+	gpio-ir.dtbo \
+	gpio-ir-tx.dtbo \
+	gpio-key.dtbo \
+	gpio-led.dtbo \
+	gpio-no-bank0-irq.dtbo \
+	gpio-no-irq.dtbo \
+	gpio-poweroff.dtbo \
+	gpio-shutdown.dtbo \
+	hd44780-lcd.dtbo \
+	hdmi-backlight-hwhack-gpio.dtbo \
+	hifiberry-amp.dtbo \
+	hifiberry-amp100.dtbo \
+	hifiberry-dac.dtbo \
+	hifiberry-dacplus.dtbo \
+	hifiberry-dacplusadc.dtbo \
+	hifiberry-dacplusadcpro.dtbo \
+	hifiberry-dacplusdsp.dtbo \
+	hifiberry-dacplushd.dtbo \
+	hifiberry-digi.dtbo \
+	hifiberry-digi-pro.dtbo \
+	highperi.dtbo \
+	hy28a.dtbo \
+	hy28b.dtbo \
+	hy28b-2017.dtbo \
+	i-sabre-q2m.dtbo \
+	i2c-bcm2708.dtbo \
+	i2c-gpio.dtbo \
+	i2c-mux.dtbo \
+	i2c-pwm-pca9685a.dtbo \
+	i2c-rtc.dtbo \
+	i2c-rtc-gpio.dtbo \
+	i2c-sensor.dtbo \
+	i2c0.dtbo \
+	i2c1.dtbo \
+	i2c3.dtbo \
+	i2c4.dtbo \
+	i2c5.dtbo \
+	i2c6.dtbo \
+	i2s-gpio28-31.dtbo \
+	ilitek251x.dtbo \
+	imx219.dtbo \
+	imx290.dtbo \
+	imx378.dtbo \
+	imx477.dtbo \
+	iqaudio-codec.dtbo \
+	iqaudio-dac.dtbo \
+	iqaudio-dacplus.dtbo \
+	iqaudio-digi-wm8804-audio.dtbo \
+	irs1125.dtbo \
+	jedec-spi-nor.dtbo \
+	justboom-both.dtbo \
+	justboom-dac.dtbo \
+	justboom-digi.dtbo \
+	ltc294x.dtbo \
+	max98357a.dtbo \
+	maxtherm.dtbo \
+	mbed-dac.dtbo \
+	mcp23017.dtbo \
+	mcp23s17.dtbo \
+	mcp2515-can0.dtbo \
+	mcp2515-can1.dtbo \
+	mcp251xfd.dtbo \
+	mcp3008.dtbo \
+	mcp3202.dtbo \
+	mcp342x.dtbo \
+	media-center.dtbo \
+	merus-amp.dtbo \
+	midi-uart0.dtbo \
+	midi-uart1.dtbo \
+	minipitft13.dtbo \
+	miniuart-bt.dtbo \
+	mmc.dtbo \
+	mpu6050.dtbo \
+	mz61581.dtbo \
+	ov5647.dtbo \
+	ov7251.dtbo \
+	ov9281.dtbo \
+	papirus.dtbo \
+	pca953x.dtbo \
+	pcie-32bit-dma.dtbo \
+	pibell.dtbo \
+	pifacedigital.dtbo \
+	pifi-40.dtbo \
+	pifi-dac-hd.dtbo \
+	pifi-dac-zero.dtbo \
+	pifi-mini-210.dtbo \
+	piglow.dtbo \
+	piscreen.dtbo \
+	piscreen2r.dtbo \
+	pisound.dtbo \
+	pitft22.dtbo \
+	pitft28-capacitive.dtbo \
+	pitft28-resistive.dtbo \
+	pitft35-resistive.dtbo \
+	pps-gpio.dtbo \
+	pwm.dtbo \
+	pwm-2chan.dtbo \
+	pwm-ir-tx.dtbo \
+	qca7000.dtbo \
+	rotary-encoder.dtbo \
+	rpi-backlight.dtbo \
+	rpi-cirrus-wm5102.dtbo \
+	rpi-dac.dtbo \
+	rpi-display.dtbo \
+	rpi-ft5406.dtbo \
+	rpi-poe.dtbo \
+	rpi-poe-plus.dtbo \
+	rpi-proto.dtbo \
+	rpi-sense.dtbo \
+	rpi-tv.dtbo \
+	rpivid-v4l2.dtbo \
+	rra-digidac1-wm8741-audio.dtbo \
+	sainsmart18.dtbo \
+	sc16is750-i2c.dtbo \
+	sc16is752-i2c.dtbo \
+	sc16is752-spi0.dtbo \
+	sc16is752-spi1.dtbo \
+	sdhost.dtbo \
+	sdio.dtbo \
+	seeed-can-fd-hat-v1.dtbo \
+	seeed-can-fd-hat-v2.dtbo \
+	sh1106-spi.dtbo \
+	si446x-spi0.dtbo \
+	smi.dtbo \
+	smi-dev.dtbo \
+	smi-nand.dtbo \
+	spi-gpio35-39.dtbo \
+	spi-gpio40-45.dtbo \
+	spi-rtc.dtbo \
+	spi0-1cs.dtbo \
+	spi0-2cs.dtbo \
+	spi1-1cs.dtbo \
+	spi1-2cs.dtbo \
+	spi1-3cs.dtbo \
+	spi2-1cs.dtbo \
+	spi2-2cs.dtbo \
+	spi2-3cs.dtbo \
+	spi3-1cs.dtbo \
+	spi3-2cs.dtbo \
+	spi4-1cs.dtbo \
+	spi4-2cs.dtbo \
+	spi5-1cs.dtbo \
+	spi5-2cs.dtbo \
+	spi6-1cs.dtbo \
+	spi6-2cs.dtbo \
+	ssd1306.dtbo \
+	ssd1306-spi.dtbo \
+	ssd1331-spi.dtbo \
+	ssd1351-spi.dtbo \
+	superaudioboard.dtbo \
+	sx150x.dtbo \
+	tc358743.dtbo \
+	tc358743-audio.dtbo \
+	tinylcd35.dtbo \
+	tpm-slb9670.dtbo \
+	uart0.dtbo \
+	uart1.dtbo \
+	uart2.dtbo \
+	uart3.dtbo \
+	uart4.dtbo \
+	uart5.dtbo \
+	udrc.dtbo \
+	ugreen-dabboard.dtbo \
+	upstream.dtbo \
+	upstream-pi4.dtbo \
+	vc4-fkms-v3d.dtbo \
+	vc4-kms-dpi-at056tn53v1.dtbo \
+	vc4-kms-dsi-7inch.dtbo \
+	vc4-kms-dsi-lt070me05000.dtbo \
+	vc4-kms-dsi-lt070me05000-v2.dtbo \
+	vc4-kms-kippah-7inch.dtbo \
+	vc4-kms-v3d.dtbo \
+	vc4-kms-v3d-pi4.dtbo \
+	vc4-kms-vga666.dtbo \
+	vga666.dtbo \
+	w1-gpio.dtbo \
+	w1-gpio-pullup.dtbo \
+	w5500.dtbo \
+	wittypi.dtbo \
+	wm8960-soundcard.dtbo
+
+targets += dtbs dtbs_install
+targets += $(dtbo-y)
+
+always-y	:= $(dtbo-y)
+clean-files	:= *.dtbo
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/max98357a-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/max98357a-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/max98357a-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/max98357a-overlay.dts	2021-07-25 16:45:35.148807558 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// Overlay for Maxim MAX98357A audio DAC
+
+// dtparams:
+//     no-sdmode  - SD_MODE pin not managed by driver.
+//     sdmode-pin - Specify GPIO pin to which SD_MODE is connected (default 4).
+
+/dts-v1/;
+/plugin/;
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	/* Enable I2S */
+	fragment@0 {
+		target = <&i2s>;
+		__overlay__ {
+			status = "okay";
+		};
+	};
+
+	/* DAC whose SD_MODE pin is managed by driver (via GPIO pin) */
+	fragment@1 {
+		target-path = "/";
+		__overlay__ {
+			max98357a_dac: max98357a {
+				compatible = "maxim,max98357a";
+				#sound-dai-cells = <0>;
+				sdmode-gpios = <&gpio 4 0>;   /* 2nd word overwritten by sdmode-pin parameter */
+				status = "okay";
+			};
+		};
+	};
+
+	/* DAC whose SD_MODE pin is not managed by driver */
+	fragment@2 {
+		target-path = "/";
+		__dormant__ {
+			max98357a_nsd: max98357a {
+				compatible = "maxim,max98357a";
+				#sound-dai-cells = <0>;
+				status = "okay";
+			};
+		};
+	};
+
+	/* Soundcard connecting I2S to DAC with SD_MODE */
+	fragment@3 {
+		target = <&sound>;
+		__overlay__ {
+			compatible = "simple-audio-card";
+			simple-audio-card,format = "i2s";
+			simple-audio-card,name = "MAX98357A";
+			status = "okay";
+			simple-audio-card,cpu {
+				sound-dai = <&i2s>;
+			};
+			simple-audio-card,codec {
+				sound-dai = <&max98357a_dac>;
+			};
+		};
+	};
+
+	/* Soundcard connecting I2S to DAC without SD_MODE */
+	fragment@4 {
+		target = <&sound>;
+		__dormant__ {
+			compatible = "simple-audio-card";
+			simple-audio-card,format = "i2s";
+			simple-audio-card,name = "MAX98357A";
+			status = "okay";
+			simple-audio-card,cpu {
+				sound-dai = <&i2s>;
+			};
+			simple-audio-card,codec {
+				sound-dai = <&max98357a_nsd>;
+			};
+		};
+	};
+
+	__overrides__ {
+		no-sdmode  = <0>,"-1+2-3+4";
+		sdmode-pin = <&max98357a_dac>,"sdmode-gpios:4";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/maxtherm-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/maxtherm-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/maxtherm-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/maxtherm-overlay.dts	2021-07-25 16:45:35.148807558 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/*
+ * Universal device tree overlay for SPI devices
+ */
+
+/dts-v1/;
+/plugin/;
+
+#include <dt-bindings/iio/temperature/thermocouple.h>
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&spidev0>;
+		__dormant__ {
+			status = "disabled";
+		};
+	};
+
+	fragment@1 {
+		target = <&spidev1>;
+		__dormant__ {
+			status = "disabled";
+		};
+	};
+
+	fragment@2 {
+		target-path = "spi1/spidev@0";
+		__dormant__ {
+			status = "disabled";
+		};
+	};
+
+	fragment@3 {
+		target-path = "spi1/spidev@1";
+		__dormant__ {
+			status = "disabled";
+		};
+	};
+
+	fragment@4 {
+		target-path = "spi1/spidev@2";
+		__dormant__ {
+			status = "disabled";
+		};
+	};
+
+	fragment@5 {
+		target-path = "spi2/spidev@0";
+		__dormant__ {
+			status = "disabled";
+		};
+	};
+
+	fragment@6 {
+		target-path = "spi2/spidev@1";
+		__dormant__ {
+			status = "disabled";
+		};
+	};
+
+	fragment@7 {
+		target-path = "spi2/spidev@2";
+		__dormant__ {
+			status = "disabled";
+		};
+	};
+
+	maxfrag: fragment@8 {
+		target = <&spi0>;
+		__overlay__ {
+			status = "okay";
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			max: maxtherm@0 {
+				compatible = "maxim,max6675";
+				reg = <0>;
+				spi-max-frequency = <500000>;
+			};
+		};
+	};
+
+	fragment@9 {
+		target = <&max>;
+		__dormant__ {
+			compatible = "maxim,max31855e", "maxim,max31855";
+		};
+	};
+
+	fragment@10 {
+		target = <&max>;
+		__dormant__ {
+			compatible = "maxim,max31855j", "maxim,max31855";
+		};
+	};
+
+	fragment@11 {
+		target = <&max>;
+		__dormant__ {
+			compatible = "maxim,max31855k", "maxim,max31855";
+		};
+	};
+
+	fragment@12 {
+		target = <&max>;
+		__dormant__ {
+			compatible = "maxim,max31855n", "maxim,max31855";
+		};
+	};
+
+	fragment@13 {
+		target = <&max>;
+		__dormant__ {
+			compatible = "maxim,max31855r", "maxim,max31855";
+		};
+	};
+
+	fragment@14 {
+		target = <&max>;
+		__dormant__ {
+			compatible = "maxim,max31855s", "maxim,max31855";
+		};
+	};
+
+	fragment@15 {
+		target = <&max>;
+		__dormant__ {
+			compatible = "maxim,max31855t", "maxim,max31855";
+		};
+	};
+
+	fragment@16 {
+		target = <&max>;
+		__dormant__ {
+			compatible = "maxim,max31856";
+			spi-cpha;
+			thermocouple-type = <THERMOCOUPLE_TYPE_K>;
+		};
+	};
+
+	__overrides__ {
+		spi0-0 = <0>, "+0",
+			 <&maxfrag>,"target:0=",<&spi0>,
+			 <&max>,"reg:0=0";
+		spi0-1 = <0>, "+1",
+			 <&maxfrag>,"target:0=",<&spi0>,
+			 <&max>,"reg:0=1";
+		spi1-0 = <0>, "+2",
+			 <&maxfrag>,"target:0=",<&spi1>,
+			 <&max>,"reg:0=0";
+		spi1-1 = <0>, "+3",
+			 <&maxfrag>,"target:0=",<&spi1>,
+			 <&max>,"reg:0=1";
+		spi1-2 = <0>, "+4",
+			 <&maxfrag>,"target:0=",<&spi1>,
+			 <&max>,"reg:0=2";
+		spi2-0 = <0>, "+5",
+			 <&maxfrag>,"target:0=",<&spi2>,
+			 <&max>,"reg:0=0";
+		spi2-1 = <0>, "+6",
+			 <&maxfrag>,"target:0=",<&spi2>,
+			 <&max>,"reg:0=1";
+		spi2-2 = <0>, "+7",
+			 <&maxfrag>,"target:0=",<&spi2>,
+			 <&max>,"reg:0=2";
+		max6675 = <&max>,"compatible=maxim,max6675";
+		max31855 = <&max>,"compatible=maxim,max31855";
+		max31855e = <0>,"+9";
+		max31855j = <0>,"+10";
+		max31855k = <0>,"+11";
+		max31855n = <0>,"+12";
+		max31855r = <0>,"+13";
+		max31855s = <0>,"+14";
+		max31855t = <0>,"+15";
+		max31856  = <0>,"+16";
+		type_b    = <&max>,"thermocouple-type:0=",<THERMOCOUPLE_TYPE_B>;
+		type_e    = <&max>,"thermocouple-type:0=",<THERMOCOUPLE_TYPE_E>;
+		type_j    = <&max>,"thermocouple-type:0=",<THERMOCOUPLE_TYPE_J>;
+		type_k    = <&max>,"thermocouple-type:0=",<THERMOCOUPLE_TYPE_K>;
+		type_n    = <&max>,"thermocouple-type:0=",<THERMOCOUPLE_TYPE_N>;
+		type_r    = <&max>,"thermocouple-type:0=",<THERMOCOUPLE_TYPE_R>;
+		type_s    = <&max>,"thermocouple-type:0=",<THERMOCOUPLE_TYPE_S>;
+		type_t    = <&max>,"thermocouple-type:0=",<THERMOCOUPLE_TYPE_T>;
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/mbed-dac-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/mbed-dac-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/mbed-dac-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/mbed-dac-overlay.dts	2021-07-25 16:45:35.148807558 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// Definitions for mbed DAC
+/dts-v1/;
+/plugin/;
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&i2s>;
+		__overlay__ {
+			status = "okay";
+		};
+	};
+
+	fragment@1 {
+		target = <&i2c1>;
+		__overlay__ {
+			#address-cells = <1>;
+                        #size-cells = <0>;
+			status = "okay";
+
+			tlv320aic23: codec@1a {
+				#sound-dai-cells = <0>;
+				reg = <0x1a>;
+				compatible = "ti,tlv320aic23";
+				status = "okay";
+			};
+		};
+	};
+
+	fragment@2 {
+		target = <&sound>;
+		__overlay__ {
+			compatible = "simple-audio-card";
+			i2s-controller = <&i2s>;
+			status = "okay";
+
+			simple-audio-card,name = "mbed-DAC";
+
+			simple-audio-card,widgets =
+				"Microphone", "Mic Jack",
+				"Line", "Line In",
+				"Headphone", "Headphone Jack";
+
+			simple-audio-card,routing =
+				"Headphone Jack", "LHPOUT",
+				"Headphone Jack", "RHPOUT",
+				"LLINEIN", "Line In",
+				"RLINEIN", "Line In",
+				"MICIN", "Mic Jack";
+
+			simple-audio-card,format = "i2s";
+
+			simple-audio-card,cpu {
+				sound-dai = <&i2s>;
+			};
+
+			sound_master: simple-audio-card,codec {
+				sound-dai = <&tlv320aic23>;
+				system-clock-frequency = <12288000>;
+			};
+		};
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/mcp23017-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/mcp23017-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/mcp23017-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/mcp23017-overlay.dts	2021-07-25 16:45:35.148807558 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// Definitions for MCP23017 Gpio Extender from Microchip Semiconductor
+
+/dts-v1/;
+/plugin/;
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&i2c1>;
+		__overlay__ {
+			status = "okay";
+		};
+	};
+
+	fragment@1 {
+		target = <&gpio>;
+		__overlay__ {
+			mcp23017_pins: mcp23017_pins@20 {
+				brcm,pins = <4>;
+				brcm,function = <0>;
+			};
+		};
+	};
+
+	fragment@2 {
+		target = <&i2c1>;
+		__overlay__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			mcp23017: mcp@20 {
+				compatible = "microchip,mcp23017";
+				reg = <0x20>;
+				gpio-controller;
+				#gpio-cells = <2>;
+
+				status = "okay";
+			};
+		};
+	};
+
+	fragment@3 {
+		target = <&mcp23017>;
+		__dormant__ {
+			compatible = "microchip,mcp23008";
+		};
+	};
+
+	fragment@4 {
+		target = <&mcp23017>;
+		mcp23017_irq: __overlay__ {
+			#interrupt-cells=<2>;
+			interrupt-parent = <&gpio>;
+			interrupts = <4 2>;
+			interrupt-controller;
+			microchip,irq-mirror;
+		};
+	};
+
+	__overrides__ {
+		gpiopin = <&mcp23017_pins>,"brcm,pins:0",
+				<&mcp23017_irq>,"interrupts:0";
+		addr = <&mcp23017>,"reg:0", <&mcp23017_pins>,"reg:0";
+		mcp23008 = <0>,"=3";
+		noints = <0>,"!1!4";
+	};
+};
+
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/mcp23s17-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/mcp23s17-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/mcp23s17-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/mcp23s17-overlay.dts	2021-07-25 16:45:35.148807558 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// Overlay for MCP23S08/17 GPIO Extenders from Microchip Semiconductor
+
+// dtparams:
+//     s08-spi<n>-<m>-present  - 4-bit integer, bitmap indicating MCP23S08 devices present on SPI<n>, CS#<m>.
+//     s17-spi<n>-<m>-present  - 8-bit integer, bitmap indicating MCP23S17 devices present on SPI<n>, CS#<m>.
+//     s08-spi<n>-<m>-int-gpio - integer, enables interrupts on a single MCP23S08 device on SPI<n>, CS#<m>, specifies the GPIO pin to which INT output is connected.
+//     s17-spi<n>-<m>-int-gpio - integer, enables mirrored interrupts on a single MCP23S17 device on SPI<n>, CS#<m>, specifies the GPIO pin to which either INTA or INTB output is connected.
+//
+// If devices are present on SPI1 or SPI2, those interfaces must be enabled with one of the spi1-1/2/3cs and/or spi2-1/2/3cs overlays.
+// If interrupts are enabled for a device on a given CS# on a SPI bus, that device must be the only one present on that SPI bus/CS#.
+//
+// Example 1: A single MCP23S17 device on SPI0, CS#0 with its SPI addr set to 0 and INTA output connected to GPIO25:
+// dtoverlay=mcp23s17:s17-spi0-0-present=1,s17-spi0-0-int-gpio=25
+//
+// Example 2: Two MCP23S08 devices on SPI1, CS#0 with their addrs set to 2 and 3. Three MCP23S17 devices on SPI1, CS#1 with their addrs set to 0, 1 and 7:
+// dtoverlay=spi1-2cs
+// dtoverlay=mcp23s17:s08-spi1-0-present=12,s17-spi1-1-present=131
+
+/dts-v1/;
+/plugin/;
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	// disable spi-dev on spi0.0
+	fragment@0 {
+		target = <&spidev0>;
+		__dormant__ {
+			status = "disabled";
+		};
+	};
+
+	// disable spi-dev on spi0.1
+	fragment@1 {
+		target = <&spidev1>;
+		__dormant__ {
+			status = "disabled";
+		};
+	};
+
+	// disable spi-dev on spi1.0
+	fragment@2 {
+		target-path = "spi1/spidev@0";
+		__dormant__ {
+			status = "disabled";
+		};
+	};
+
+	// disable spi-dev on spi1.1
+	fragment@3 {
+		target-path = "spi1/spidev@1";
+		__dormant__ {
+			status = "disabled";
+		};
+	};
+
+	// disable spi-dev on spi1.2
+	fragment@4 {
+		target-path = "spi1/spidev@2";
+		__dormant__ {
+			status = "disabled";
+		};
+	};
+
+	// disable spi-dev on spi2.0
+	fragment@5 {
+		target-path = "spi2/spidev@0";
+		__dormant__ {
+			status = "disabled";
+		};
+	};
+
+	// disable spi-dev on spi2.1
+	fragment@6 {
+		target-path = "spi2/spidev@1";
+		__dormant__ {
+			status = "disabled";
+		};
+	};
+
+	// disable spi-dev on spi2.2
+	fragment@7 {
+		target-path = "spi2/spidev@2";
+		__dormant__ {
+			status = "disabled";
+		};
+	};
+
+	// enable one or more mcp23s08s on spi0.0
+	fragment@8 {
+		target = <&spi0>;
+		__dormant__ {
+			status = "okay";
+                        #address-cells = <1>;
+                        #size-cells = <0>;
+			mcp23s08_00: mcp23s08@0 {
+				compatible = "microchip,mcp23s08";
+  				gpio-controller;
+  				#gpio-cells = <2>;
+    				microchip,spi-present-mask = <0x00>;  /* overwritten by mcp23s08-spi0-0-present parameter */
+     				reg = <0>;
+    				spi-max-frequency = <500000>;
+				status = "okay";
+				#interrupt-cells=<2>;
+				interrupts = <0 2>;  /* 1st word overwritten by mcp23s08-spi0-0-int-gpio parameter */
+			};
+		};
+	};
+
+	// enable one or more mcp23s08s on spi0.1
+	fragment@9 {
+		target = <&spi0>;
+		__dormant__ {
+			status = "okay";
+                        #address-cells = <1>;
+                        #size-cells = <0>;
+			mcp23s08_01: mcp23s08@1 {
+				compatible = "microchip,mcp23s08";
+  				gpio-controller;
+  				#gpio-cells = <2>;
+    				microchip,spi-present-mask = <0x00>;  /* overwritten by mcp23s08-spi0-1-present parameter */
+     				reg = <1>;
+    				spi-max-frequency = <500000>;
+				status = "okay";
+				#interrupt-cells=<2>;
+				interrupts = <0 2>;  /* 1st word overwritten by mcp23s08-spi0-1-int-gpio parameter */
+			};
+		};
+	};
+
+	// enable one or more mcp23s08s on spi1.0
+	fragment@10 {
+		target = <&spi1>;
+		__dormant__ {
+			status = "okay";
+                        #address-cells = <1>;
+                        #size-cells = <0>;
+			mcp23s08_10: mcp23s08@0 {
+				compatible = "microchip,mcp23s08";
+  				gpio-controller;
+  				#gpio-cells = <2>;
+    				microchip,spi-present-mask = <0x00>;  /* overwritten by mcp23s08-spi1-0-present parameter */
+     				reg = <0>;
+    				spi-max-frequency = <500000>;
+				status = "okay";
+				#interrupt-cells=<2>;
+				interrupts = <0 2>;  /* 1st word overwritten by mcp23s08-spi1-0-int-gpio parameter */
+			};
+		};
+	};
+
+	// enable one or more mcp23s08s on spi1.1
+	fragment@11 {
+		target = <&spi1>;
+		__dormant__ {
+			status = "okay";
+                        #address-cells = <1>;
+                        #size-cells = <0>;
+			mcp23s08_11: mcp23s08@1 {
+				compatible = "microchip,mcp23s08";
+  				gpio-controller;
+  				#gpio-cells = <2>;
+    				microchip,spi-present-mask = <0x00>;  /* overwritten by mcp23s08-spi1-1-present parameter */
+     				reg = <1>;
+    				spi-max-frequency = <500000>;
+				status = "okay";
+				#interrupt-cells=<2>;
+				interrupts = <0 2>;  /* 1st word overwritten by mcp23s08-spi1-1-int-gpio parameter */
+			};
+		};
+	};
+
+	// enable one or more mcp23s08s on spi1.2
+	fragment@12 {
+		target = <&spi1>;
+		__dormant__ {
+			status = "okay";
+                        #address-cells = <1>;
+                        #size-cells = <0>;
+			mcp23s08_12: mcp23s08@2 {
+				compatible = "microchip,mcp23s08";
+  				gpio-controller;
+  				#gpio-cells = <2>;
+    				microchip,spi-present-mask = <0x00>;  /* overwritten by mcp23s08-spi1-2-present parameter */
+     				reg = <2>;
+    				spi-max-frequency = <500000>;
+				status = "okay";
+				#interrupt-cells=<2>;
+				interrupts = <0 2>;  /* 1st word overwritten by mcp23s08-spi1-2-int-gpio parameter */
+			};
+		};
+	};
+
+	// enable one or more mcp23s08s on spi2.0
+	fragment@13 {
+		target = <&spi2>;
+		__dormant__ {
+			status = "okay";
+                        #address-cells = <1>;
+                        #size-cells = <0>;
+			mcp23s08_20: mcp23s08@0 {
+				compatible = "microchip,mcp23s08";
+  				gpio-controller;
+  				#gpio-cells = <2>;
+    				microchip,spi-present-mask = <0x00>;  /* overwritten by mcp23s08-spi2-0-present parameter */
+     				reg = <0>;
+    				spi-max-frequency = <500000>;
+				status = "okay";
+				#interrupt-cells=<2>;
+				interrupts = <0 2>;  /* 1st word overwritten by mcp23s08-spi2-0-int-gpio parameter */
+			};
+		};
+	};
+
+	// enable one or more mcp23s08s on spi2.1
+	fragment@14 {
+		target = <&spi2>;
+		__dormant__ {
+			status = "okay";
+                        #address-cells = <1>;
+                        #size-cells = <0>;
+			mcp23s08_21: mcp23s08@1 {
+				compatible = "microchip,mcp23s08";
+  				gpio-controller;
+  				#gpio-cells = <2>;
+    				microchip,spi-present-mask = <0x00>;  /* overwritten by mcp23s08-spi2-1-present parameter */
+     				reg = <1>;
+    				spi-max-frequency = <500000>;
+				status = "okay";
+				#interrupt-cells=<2>;
+				interrupts = <0 2>;  /* 1st word overwritten by mcp23s08-spi2-1-int-gpio parameter */
+			};
+		};
+	};
+
+	// enable one or more mcp23s08s on spi2.2
+	fragment@15 {
+		target = <&spi2>;
+		__dormant__ {
+			status = "okay";
+                        #address-cells = <1>;
+                        #size-cells = <0>;
+			mcp23s08_22: mcp23s08@2 {
+				compatible = "microchip,mcp23s08";
+  				gpio-controller;
+  				#gpio-cells = <2>;
+    				microchip,spi-present-mask = <0x00>;  /* overwritten by mcp23s08-spi2-2-present parameter */
+     				reg = <2>;
+    				spi-max-frequency = <500000>;
+				status = "okay";
+				#interrupt-cells=<2>;
+				interrupts = <0 2>;  /* 1st word overwritten by mcp23s08-spi2-2-int-gpio parameter */
+			};
+		};
+	};
+
+	// enable one or more mcp23s17s on spi0.0
+	fragment@16 {
+		target = <&spi0>;
+		__dormant__ {
+			status = "okay";
+                        #address-cells = <1>;
+                        #size-cells = <0>;
+			mcp23s17_00: mcp23s17@0 {
+				compatible = "microchip,mcp23s17";
+  				gpio-controller;
+  				#gpio-cells = <2>;
+    				microchip,spi-present-mask = <0x00>;  /* overwritten by mcp23s17-spi0-0-present parameter */
+     				reg = <0>;
+    				spi-max-frequency = <500000>;
+				status = "okay";
+				#interrupt-cells=<2>;
+				interrupts = <0 2>;  /* 1st word overwritten by mcp23s17-spi0-0-int-gpio parameter */
+			};
+		};
+	};
+
+	// enable one or more mcp23s17s on spi0.1
+	fragment@17 {
+		target = <&spi0>;
+		__dormant__ {
+			status = "okay";
+                        #address-cells = <1>;
+                        #size-cells = <0>;
+			mcp23s17_01: mcp23s17@1 {
+				compatible = "microchip,mcp23s17";
+  				gpio-controller;
+  				#gpio-cells = <2>;
+    				microchip,spi-present-mask = <0x00>;  /* overwritten by mcp23s17-spi0-1-present parameter */
+     				reg = <1>;
+    				spi-max-frequency = <500000>;
+				status = "okay";
+				#interrupt-cells=<2>;
+				interrupts = <0 2>;  /* 1st word overwritten by mcp23s17-spi0-1-int-gpio parameter */
+			};
+		};
+	};
+
+	// enable one or more mcp23s17s on spi1.0
+	fragment@18 {
+		target = <&spi1>;
+		__dormant__ {
+			status = "okay";
+                        #address-cells = <1>;
+                        #size-cells = <0>;
+			mcp23s17_10: mcp23s17@0 {
+				compatible = "microchip,mcp23s17";
+  				gpio-controller;
+  				#gpio-cells = <2>;
+    				microchip,spi-present-mask = <0x00>;  /* overwritten by mcp23s17-spi1-0-present parameter */
+     				reg = <0>;
+    				spi-max-frequency = <500000>;
+				status = "okay";
+				#interrupt-cells=<2>;
+				interrupts = <0 2>;  /* 1st word overwritten by mcp23s17-spi1-0-int-gpio parameter */
+			};
+		};
+	};
+
+	// enable one or more mcp23s17s on spi1.1
+	fragment@19 {
+		target = <&spi1>;
+		__dormant__ {
+			status = "okay";
+                        #address-cells = <1>;
+                        #size-cells = <0>;
+			mcp23s17_11: mcp23s17@1 {
+				compatible = "microchip,mcp23s17";
+  				gpio-controller;
+  				#gpio-cells = <2>;
+    				microchip,spi-present-mask = <0x00>;  /* overwritten by mcp23s17-spi1-1-present parameter */
+     				reg = <1>;
+    				spi-max-frequency = <500000>;
+				status = "okay";
+				#interrupt-cells=<2>;
+				interrupts = <0 2>;  /* 1st word overwritten by mcp23s17-spi1-1-int-gpio parameter */
+			};
+		};
+	};
+
+	// enable one or more mcp23s17s on spi1.2
+	fragment@20 {
+		target = <&spi1>;
+		__dormant__ {
+			status = "okay";
+                        #address-cells = <1>;
+                        #size-cells = <0>;
+			mcp23s17_12: mcp23s17@2 {
+				compatible = "microchip,mcp23s17";
+  				gpio-controller;
+  				#gpio-cells = <2>;
+    				microchip,spi-present-mask = <0x00>;  /* overwritten by mcp23s17-spi1-2-present parameter */
+     				reg = <2>;
+    				spi-max-frequency = <500000>;
+				status = "okay";
+				#interrupt-cells=<2>;
+				interrupts = <0 2>;  /* 1st word overwritten by mcp23s17-spi1-2-int-gpio parameter */
+			};
+		};
+	};
+
+	// enable one or more mcp23s17s on spi2.0
+	fragment@21 {
+		target = <&spi2>;
+		__dormant__ {
+			status = "okay";
+                        #address-cells = <1>;
+                        #size-cells = <0>;
+			mcp23s17_20: mcp23s17@0 {
+				compatible = "microchip,mcp23s17";
+  				gpio-controller;
+  				#gpio-cells = <2>;
+    				microchip,spi-present-mask = <0x00>;  /* overwritten by mcp23s17-spi2-0-present parameter */
+     				reg = <0>;
+    				spi-max-frequency = <500000>;
+				status = "okay";
+				#interrupt-cells=<2>;
+				interrupts = <0 2>;  /* 1st word overwritten by mcp23s17-spi2-0-int-gpio parameter */
+			};
+		};
+	};
+
+	// enable one or more mcp23s17s on spi2.1
+	fragment@22 {
+		target = <&spi2>;
+		__dormant__ {
+			status = "okay";
+                        #address-cells = <1>;
+                        #size-cells = <0>;
+			mcp23s17_21: mcp23s17@1 {
+				compatible = "microchip,mcp23s17";
+  				gpio-controller;
+  				#gpio-cells = <2>;
+    				microchip,spi-present-mask = <0x00>;  /* overwritten by mcp23s17-spi2-1-present parameter */
+     				reg = <1>;
+    				spi-max-frequency = <500000>;
+				status = "okay";
+				#interrupt-cells=<2>;
+				interrupts = <0 2>;  /* 1st word overwritten by mcp23s17-spi2-1-int-gpio parameter */
+			};
+		};
+	};
+
+	// enable one or more mcp23s17s on spi2.2
+	fragment@23 {
+		target = <&spi2>;
+		__dormant__ {
+			status = "okay";
+                        #address-cells = <1>;
+                        #size-cells = <0>;
+			mcp23s17_22: mcp23s17@2 {
+				compatible = "microchip,mcp23s17";
+  				gpio-controller;
+  				#gpio-cells = <2>;
+    				microchip,spi-present-mask = <0x00>;  /* overwritten by mcp23s17-spi2-2-present parameter */
+     				reg = <2>;
+    				spi-max-frequency = <500000>;
+				status = "okay";
+				#interrupt-cells=<2>;
+				interrupts = <0 2>;  /* 1st word overwritten by mcp23s17-spi2-2-int-gpio parameter */
+			};
+		};
+	};
+
+	// Configure GPIO pin connected to INT(A/B) output of mcp23s08/17 on spi0.0 as a input with no pull-up/down
+	fragment@24 {
+		target = <&gpio>;
+		__dormant__ {
+			spi0_0_int_pins: spi0_0_int_pins {
+				brcm,pins = <0>;  /* overwritten by mcp23s08/17-spi0-0-int-gpio parameter */
+				brcm,function = <0>;
+				brcm,pull = <0>;
+			};
+		};
+	};
+
+	// Configure GPIO pin connected to INT(A/B) output of mcp23s08/17 on spi0.1 as a input with no pull-up/down
+	fragment@25 {
+		target = <&gpio>;
+		__dormant__ {
+			spi0_1_int_pins: spi0_1_int_pins {
+				brcm,pins = <0>;  /* overwritten by mcp23s08/17-spi0-1-int-gpio parameter */
+				brcm,function = <0>;
+				brcm,pull = <0>;
+			};
+		};
+	};
+
+	// Configure GPIO pin connected to INT(A/B) output of mcp23s08/17 on spi1.0 as a input with no pull-up/down
+	fragment@26 {
+		target = <&gpio>;
+		__dormant__ {
+			spi1_0_int_pins: spi1_0_int_pins {
+				brcm,pins = <0>;  /* overwritten by mcp23s08/17-spi1-0-int-gpio parameter */
+				brcm,function = <0>;
+				brcm,pull = <0>;
+			};
+		};
+	};
+
+	// Configure GPIO pin connected to INT(A/B) output of mcp23s08/17 on spi1.1 as a input with no pull-up/down
+	fragment@27 {
+		target = <&gpio>;
+		__dormant__ {
+			spi1_1_int_pins: spi1_1_int_pins {
+				brcm,pins = <0>;  /* overwritten by mcp23s08/17-spi1-1-int-gpio parameter */
+				brcm,function = <0>;
+				brcm,pull = <0>;
+			};
+		};
+	};
+
+	// Configure GPIO pin connected to INT(A/B) output of mcp23s08/17 on spi1.2 as a input with no pull-up/down
+	fragment@28 {
+		target = <&gpio>;
+		__dormant__ {
+			spi1_2_int_pins: spi1_2_int_pins {
+				brcm,pins = <0>;  /* overwritten by mcp23s08/17-spi1-2-int-gpio parameter */
+				brcm,function = <0>;
+				brcm,pull = <0>;
+			};
+		};
+	};
+
+	// Configure GPIO pin connected to INT(A/B) output of mcp23s08/17 on spi2.0 as a input with no pull-up/down
+	fragment@29 {
+		target = <&gpio>;
+		__dormant__ {
+			spi2_0_int_pins: spi2_0_int_pins {
+				brcm,pins = <0>;  /* overwritten by mcp23s08/17-spi2-0-int-gpio parameter */
+				brcm,function = <0>;
+				brcm,pull = <0>;
+			};
+		};
+	};
+
+	// Configure GPIO pin connected to INT(A/B) output of mcp23s08/17 on spi2.1 as a input with no pull-up/down
+	fragment@30 {
+		target = <&gpio>;
+		__dormant__ {
+			spi2_1_int_pins: spi2_1_int_pins {
+				brcm,pins = <0>;  /* overwritten by mcp23s08/17-spi2-1-int-gpio parameter */
+				brcm,function = <0>;
+				brcm,pull = <0>;
+			};
+		};
+	};
+
+	// Configure GPIO pin connected to INT(A/B) output of mcp23s08/17 on spi2.2 as a input with no pull-up/down
+	fragment@31 {
+		target = <&gpio>;
+		__dormant__ {
+			spi2_2_int_pins: spi2_2_int_pins {
+				brcm,pins = <0>;  /* overwritten by mcp23s08/17-spi2-2-int-gpio parameter */
+				brcm,function = <0>;
+				brcm,pull = <0>;
+			};
+		};
+	};
+
+	// Enable interrupts for a mcp23s08 on spi0.0.
+	// Use default active low interrupt signalling.
+	fragment@32 {
+		target = <&mcp23s08_00>;
+		__dormant__ {
+			interrupt-parent = <&gpio>;
+			interrupt-controller;
+		};
+	};
+
+	// Enable interrupts for a mcp23s08 on spi0.1.
+	// Use default active low interrupt signalling.
+	fragment@33 {
+		target = <&mcp23s08_01>;
+		__dormant__ {
+			interrupt-parent = <&gpio>;
+			interrupt-controller;
+		};
+	};
+
+	// Enable interrupts for a mcp23s08 on spi1.0.
+	// Use default active low interrupt signalling.
+	fragment@34 {
+		target = <&mcp23s08_10>;
+		__dormant__ {
+			interrupt-parent = <&gpio>;
+			interrupt-controller;
+		};
+	};
+
+	// Enable interrupts for a mcp23s08 on spi1.1.
+	// Use default active low interrupt signalling.
+	fragment@35 {
+		target = <&mcp23s08_11>;
+		__dormant__ {
+			interrupt-parent = <&gpio>;
+			interrupt-controller;
+		};
+	};
+
+	// Enable interrupts for a mcp23s08 on spi1.2.
+	// Use default active low interrupt signalling.
+	fragment@36 {
+		target = <&mcp23s08_12>;
+		__dormant__ {
+			interrupt-parent = <&gpio>;
+			interrupt-controller;
+		};
+	};
+
+	// Enable interrupts for a mcp23s08 on spi2.0.
+	// Use default active low interrupt signalling.
+	fragment@37 {
+		target = <&mcp23s08_20>;
+		__dormant__ {
+			interrupt-parent = <&gpio>;
+			interrupt-controller;
+		};
+	};
+
+	// Enable interrupts for a mcp23s08 on spi2.1.
+	// Use default active low interrupt signalling.
+	fragment@38 {
+		target = <&mcp23s08_21>;
+		__dormant__ {
+			interrupt-parent = <&gpio>;
+			interrupt-controller;
+		};
+	};
+
+	// Enable interrupts for a mcp23s08 on spi2.2.
+	// Use default active low interrupt signalling.
+	fragment@39 {
+		target = <&mcp23s08_22>;
+		__dormant__ {
+			interrupt-parent = <&gpio>;
+			interrupt-controller;
+		};
+	};
+
+	// Enable interrupts for a mcp23s17 on spi0.0.
+	// Enable mirroring so that either INTA or INTB output of mcp23s17 can be connected to the GPIO pin.
+	// Use default active low interrupt signalling.
+	fragment@40 {
+		target = <&mcp23s17_00>;
+		__dormant__ {
+			interrupt-parent = <&gpio>;
+			interrupt-controller;
+			microchip,irq-mirror;
+		};
+	};
+
+	// Enable interrupts for a mcp23s17 on spi0.1.
+	// Enable mirroring so that either INTA or INTB output of mcp23s17 can be connected to the GPIO pin.
+	// Configure INTA/B outputs of mcp23s08/17 as active low.
+	fragment@41 {
+		target = <&mcp23s17_01>;
+		__dormant__ {
+			interrupt-parent = <&gpio>;
+			interrupt-controller;
+			microchip,irq-mirror;
+		};
+	};
+
+	// Enable interrupts for a mcp23s17 on spi1.0.
+	// Enable mirroring so that either INTA or INTB output of mcp23s17 can be connected to the GPIO pin.
+	// Configure INTA/B outputs of mcp23s08/17 as active low.
+	fragment@42 {
+		target = <&mcp23s17_10>;
+		__dormant__ {
+			interrupt-parent = <&gpio>;
+			interrupt-controller;
+			microchip,irq-mirror;
+		};
+	};
+
+	// Enable interrupts for a mcp23s17 on spi1.1.
+	// Enable mirroring so that either INTA or INTB output of mcp23s17 can be connected to the GPIO pin.
+	// Configure INTA/B outputs of mcp23s08/17 as active low.
+	fragment@43 {
+		target = <&mcp23s17_11>;
+		__dormant__ {
+			interrupt-parent = <&gpio>;
+			interrupt-controller;
+			microchip,irq-mirror;
+		};
+	};
+
+	// Enable interrupts for a mcp23s17 on spi1.2.
+	// Enable mirroring so that either INTA or INTB output of mcp23s17 can be connected to the GPIO pin.
+	// Configure INTA/B outputs of mcp23s08/17 as active low.
+	fragment@44 {
+		target = <&mcp23s17_12>;
+		__dormant__ {
+			interrupt-parent = <&gpio>;
+			interrupt-controller;
+			microchip,irq-mirror;
+		};
+	};
+
+	// Enable interrupts for a mcp23s17 on spi2.0.
+	// Enable mirroring so that either INTA or INTB output of mcp23s17 can be connected to the GPIO pin.
+	// Configure INTA/B outputs of mcp23s08/17 as active low.
+	fragment@45 {
+		target = <&mcp23s17_20>;
+		__dormant__ {
+			interrupt-parent = <&gpio>;
+			interrupt-controller;
+			microchip,irq-mirror;
+		};
+	};
+
+	// Enable interrupts for a mcp23s17 on spi2.1.
+	// Enable mirroring so that either INTA or INTB output of mcp23s17 can be connected to the GPIO pin.
+	// Configure INTA/B outputs of mcp23s08/17 as active low.
+	fragment@46 {
+		target = <&mcp23s17_21>;
+		__dormant__ {
+			interrupt-parent = <&gpio>;
+			interrupt-controller;
+			microchip,irq-mirror;
+		};
+	};
+
+	// Enable interrupts for a mcp23s17 on spi2.2.
+	// Enable mirroring so that either INTA or INTB output of mcp23s17 can be connected to the GPIO pin.
+	// Configure INTA/B outputs of mcp23s08/17 as active low.
+	fragment@47 {
+		target = <&mcp23s17_22>;
+		__dormant__ {
+			interrupt-parent = <&gpio>;
+			interrupt-controller;
+			microchip,irq-mirror;
+		};
+	};
+
+	__overrides__ {
+		s08-spi0-0-present = <0>,"+0+8",  <&mcp23s08_00>,"microchip,spi-present-mask:0";
+		s08-spi0-1-present = <0>,"+1+9",  <&mcp23s08_01>,"microchip,spi-present-mask:0";
+		s08-spi1-0-present = <0>,"+2+10", <&mcp23s08_10>,"microchip,spi-present-mask:0";
+		s08-spi1-1-present = <0>,"+3+11", <&mcp23s08_11>,"microchip,spi-present-mask:0";
+		s08-spi1-2-present = <0>,"+4+12", <&mcp23s08_12>,"microchip,spi-present-mask:0";
+		s08-spi2-0-present = <0>,"+5+13", <&mcp23s08_20>,"microchip,spi-present-mask:0";
+		s08-spi2-1-present = <0>,"+6+14", <&mcp23s08_21>,"microchip,spi-present-mask:0";
+		s08-spi2-2-present = <0>,"+7+15", <&mcp23s08_22>,"microchip,spi-present-mask:0";
+		s17-spi0-0-present = <0>,"+0+16", <&mcp23s17_00>,"microchip,spi-present-mask:0";
+		s17-spi0-1-present = <0>,"+1+17", <&mcp23s17_01>,"microchip,spi-present-mask:0";
+		s17-spi1-0-present = <0>,"+2+18", <&mcp23s17_10>,"microchip,spi-present-mask:0";
+		s17-spi1-1-present = <0>,"+3+19", <&mcp23s17_11>,"microchip,spi-present-mask:0";
+		s17-spi1-2-present = <0>,"+4+20", <&mcp23s17_12>,"microchip,spi-present-mask:0";
+		s17-spi2-0-present = <0>,"+5+21", <&mcp23s17_20>,"microchip,spi-present-mask:0";
+		s17-spi2-1-present = <0>,"+6+22", <&mcp23s17_21>,"microchip,spi-present-mask:0";
+		s17-spi2-2-present = <0>,"+7+23", <&mcp23s17_22>,"microchip,spi-present-mask:0";
+		s08-spi0-0-int-gpio = <0>,"+24+32", <&spi0_0_int_pins>,"brcm,pins:0", <&mcp23s08_00>,"interrupts:0";
+		s08-spi0-1-int-gpio = <0>,"+25+33", <&spi0_1_int_pins>,"brcm,pins:0", <&mcp23s08_01>,"interrupts:0";
+		s08-spi1-0-int-gpio = <0>,"+26+34", <&spi1_0_int_pins>,"brcm,pins:0", <&mcp23s08_10>,"interrupts:0";
+		s08-spi1-1-int-gpio = <0>,"+27+35", <&spi1_1_int_pins>,"brcm,pins:0", <&mcp23s08_11>,"interrupts:0";
+		s08-spi1-2-int-gpio = <0>,"+28+36", <&spi1_2_int_pins>,"brcm,pins:0", <&mcp23s08_12>,"interrupts:0";
+		s08-spi2-0-int-gpio = <0>,"+29+37", <&spi2_0_int_pins>,"brcm,pins:0", <&mcp23s08_20>,"interrupts:0";
+		s08-spi2-1-int-gpio = <0>,"+30+38", <&spi2_1_int_pins>,"brcm,pins:0", <&mcp23s08_21>,"interrupts:0";
+		s08-spi2-2-int-gpio = <0>,"+31+39", <&spi2_2_int_pins>,"brcm,pins:0", <&mcp23s08_22>,"interrupts:0";
+		s17-spi0-0-int-gpio = <0>,"+24+40", <&spi0_0_int_pins>,"brcm,pins:0", <&mcp23s17_00>,"interrupts:0";
+		s17-spi0-1-int-gpio = <0>,"+25+41", <&spi0_1_int_pins>,"brcm,pins:0", <&mcp23s17_01>,"interrupts:0";
+		s17-spi1-0-int-gpio = <0>,"+26+42", <&spi1_0_int_pins>,"brcm,pins:0", <&mcp23s17_10>,"interrupts:0";
+		s17-spi1-1-int-gpio = <0>,"+27+43", <&spi1_1_int_pins>,"brcm,pins:0", <&mcp23s17_11>,"interrupts:0";
+		s17-spi1-2-int-gpio = <0>,"+28+44", <&spi1_2_int_pins>,"brcm,pins:0", <&mcp23s17_12>,"interrupts:0";
+		s17-spi2-0-int-gpio = <0>,"+29+45", <&spi2_0_int_pins>,"brcm,pins:0", <&mcp23s17_20>,"interrupts:0";
+		s17-spi2-1-int-gpio = <0>,"+30+46", <&spi2_1_int_pins>,"brcm,pins:0", <&mcp23s17_21>,"interrupts:0";
+		s17-spi2-2-int-gpio = <0>,"+31+47", <&spi2_2_int_pins>,"brcm,pins:0", <&mcp23s17_22>,"interrupts:0";
+	};
+};
+
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/mcp2515-can0-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/mcp2515-can0-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/mcp2515-can0-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/mcp2515-can0-overlay.dts	2021-07-25 16:45:35.148807558 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/*
+ * Device tree overlay for mcp251x/can0 on spi0.0
+ */
+
+/dts-v1/;
+/plugin/;
+
+/ {
+    compatible = "brcm,bcm2835";
+    /* disable spi-dev for spi0.0 */
+    fragment@0 {
+        target = <&spi0>;
+        __overlay__ {
+            status = "okay";
+        };
+    };
+
+    fragment@1 {
+	target = <&spidev0>;
+	__overlay__ {
+	    status = "disabled";
+	};
+    };
+
+    /* the interrupt pin of the can-controller */
+    fragment@2 {
+        target = <&gpio>;
+        __overlay__ {
+            can0_pins: can0_pins {
+                brcm,pins = <25>;
+                brcm,function = <0>; /* input */
+            };
+        };
+    };
+
+    /* the clock/oscillator of the can-controller */
+    fragment@3 {
+        target-path = "/";
+        __overlay__ {
+            /* external oscillator of mcp2515 on SPI0.0 */
+            can0_osc: can0_osc {
+                compatible = "fixed-clock";
+                #clock-cells = <0>;
+                clock-frequency  = <16000000>;
+            };
+        };
+    };
+
+    /* the spi config of the can-controller itself binding everything together */
+    fragment@4 {
+        target = <&spi0>;
+        __overlay__ {
+            /* needed to avoid dtc warning */
+            #address-cells = <1>;
+            #size-cells = <0>;
+            can0: mcp2515@0 {
+                reg = <0>;
+                compatible = "microchip,mcp2515";
+                pinctrl-names = "default";
+                pinctrl-0 = <&can0_pins>;
+                spi-max-frequency = <10000000>;
+                interrupt-parent = <&gpio>;
+                interrupts = <25 8>; /* IRQ_TYPE_LEVEL_LOW */
+                clocks = <&can0_osc>;
+            };
+        };
+    };
+    __overrides__ {
+        oscillator = <&can0_osc>,"clock-frequency:0";
+        spimaxfrequency = <&can0>,"spi-max-frequency:0";
+        interrupt = <&can0_pins>,"brcm,pins:0",<&can0>,"interrupts:0";
+    };
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/mcp2515-can1-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/mcp2515-can1-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/mcp2515-can1-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/mcp2515-can1-overlay.dts	2021-07-25 16:45:35.148807558 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/*
+ * Device tree overlay for mcp251x/can1 on spi0.1 edited by petit_miner
+ */
+
+/dts-v1/;
+/plugin/;
+
+/ {
+    compatible = "brcm,bcm2835";
+    /* disable spi-dev for spi0.1 */
+    fragment@0 {
+        target = <&spi0>;
+        __overlay__ {
+            status = "okay";
+        };
+    };
+
+    fragment@1 {
+	target = <&spidev1>;
+	__overlay__ {
+	    status = "disabled";
+	};
+    };
+
+    /* the interrupt pin of the can-controller */
+    fragment@2 {
+        target = <&gpio>;
+        __overlay__ {
+            can1_pins: can1_pins {
+                brcm,pins = <25>;
+                brcm,function = <0>; /* input */
+            };
+        };
+    };
+
+    /* the clock/oscillator of the can-controller */
+    fragment@3 {
+        target-path = "/";
+        __overlay__ {
+            /* external oscillator of mcp2515 on spi0.1 */
+            can1_osc: can1_osc {
+                compatible = "fixed-clock";
+                #clock-cells = <0>;
+                clock-frequency  = <16000000>;
+            };
+        };
+    };
+
+    /* the spi config of the can-controller itself binding everything together */
+    fragment@4 {
+        target = <&spi0>;
+        __overlay__ {
+            /* needed to avoid dtc warning */
+            #address-cells = <1>;
+            #size-cells = <0>;
+            can1: mcp2515@1 {
+                reg = <1>;
+                compatible = "microchip,mcp2515";
+                pinctrl-names = "default";
+                pinctrl-0 = <&can1_pins>;
+                spi-max-frequency = <10000000>;
+                interrupt-parent = <&gpio>;
+                interrupts = <25 8>; /* IRQ_TYPE_LEVEL_LOW */
+                clocks = <&can1_osc>;
+            };
+        };
+    };
+    __overrides__ {
+        oscillator = <&can1_osc>,"clock-frequency:0";
+        spimaxfrequency = <&can1>,"spi-max-frequency:0";
+        interrupt = <&can1_pins>,"brcm,pins:0",<&can1>,"interrupts:0";
+    };
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/mcp251xfd-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/mcp251xfd-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/mcp251xfd-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/mcp251xfd-overlay.dts	2021-07-25 16:45:35.148807558 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// SPDX-License-Identifier: (GPL-2.0 OR MIT)
+
+/dts-v1/;
+/plugin/;
+
+#include <dt-bindings/gpio/gpio.h>
+#include <dt-bindings/interrupt-controller/irq.h>
+#include <dt-bindings/pinctrl/bcm2835.h>
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&spidev0>;
+		__dormant__ {
+			status = "disabled";
+		};
+	};
+
+	fragment@1 {
+		target = <&spidev1>;
+		__dormant__ {
+			status = "disabled";
+		};
+	};
+
+	fragment@2 {
+		target-path = "spi1/spidev@0";
+		__dormant__ {
+			status = "disabled";
+		};
+	};
+
+	fragment@3 {
+		target-path = "spi1/spidev@1";
+		__dormant__ {
+			status = "disabled";
+		};
+	};
+
+	fragment@4 {
+		target-path = "spi1/spidev@2";
+		__dormant__ {
+			status = "disabled";
+		};
+	};
+
+	fragment@5 {
+		target-path = "spi2/spidev@0";
+		__dormant__ {
+			status = "disabled";
+		};
+	};
+
+	fragment@6 {
+		target-path = "spi2/spidev@1";
+		__dormant__ {
+			status = "disabled";
+		};
+	};
+
+	fragment@7 {
+		target-path = "spi2/spidev@2";
+		__dormant__ {
+			status = "disabled";
+		};
+	};
+
+	fragment@8 {
+		target = <&gpio>;
+		__overlay__ {
+			mcp251xfd_pins: mcp251xfd_pins {
+				brcm,pins = <25>;
+				brcm,function = <BCM2835_FSEL_GPIO_IN>;
+			};
+		};
+	};
+
+	fragment@9 {
+		target-path = "/clocks";
+		__overlay__ {
+			clk_mcp251xfd_osc: mcp251xfd-osc {
+				#clock-cells = <0>;
+				compatible = "fixed-clock";
+				clock-frequency = <40000000>;
+			};
+		};
+	};
+
+	mcp251xfd_frag: fragment@10 {
+		target = <&spi0>;
+		__overlay__ {
+			status = "okay";
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			mcp251xfd: mcp251xfd@0 {
+				compatible = "microchip,mcp251xfd";
+				reg = <0>;
+				pinctrl-names = "default";
+				pinctrl-0 = <&mcp251xfd_pins>;
+				spi-max-frequency = <20000000>;
+				interrupt-parent = <&gpio>;
+				interrupts = <25 IRQ_TYPE_LEVEL_LOW>;
+				clocks = <&clk_mcp251xfd_osc>;
+			};
+		};
+	};
+
+	fragment@11 {
+		target = <&mcp251xfd>;
+		mcp251xfd_rx_int_gpios: __dormant__ {
+			microchip,rx-int-gpios = <&gpio 255 GPIO_ACTIVE_LOW>;
+		};
+	};
+
+	fragment@12 {
+		target = <&gpio>;
+		__dormant__ {
+			mcp251xfd_xceiver_pins: mcp251xfd_xceiver_pins {
+				brcm,pins = <255>;
+				brcm,function = <BCM2835_FSEL_GPIO_OUT>;
+			};
+		};
+	};
+
+	fragment@13 {
+		target-path = "/";
+		__dormant__ {
+			reg_mcp251xfd_xceiver: reg_mcp251xfd_xceiver {
+				compatible = "regulator-fixed";
+				regulator-name = "mcp251xfd_xceiver";
+				regulator-min-microvolt = <3300000>;
+				regulator-max-microvolt = <3300000>;
+				gpio = <&gpio 4 GPIO_ACTIVE_HIGH>;
+				pinctrl-names = "default";
+				pinctrl-0 = <&mcp251xfd_xceiver_pins>;
+			};
+		};
+	};
+
+	fragment@14 {
+		target = <&mcp251xfd>;
+		__dormant__ {
+			xceiver-supply = <&reg_mcp251xfd_xceiver>;
+		};
+	};
+
+	__overrides__ {
+		spi0-0 = <0>, "+0",
+			<&mcp251xfd_frag>, "target:0=", <&spi0>,
+			<&mcp251xfd>, "reg:0=0",
+			<&mcp251xfd_pins>, "name=mcp251xfd_spi0_0_pins",
+			<&clk_mcp251xfd_osc>, "name=mcp251xfd-spi0-0-osc",
+			<&mcp251xfd_xceiver_pins>, "name=mcp251xfd_spi0_0_xceiver_pins",
+			<&reg_mcp251xfd_xceiver>, "name=reg-mcp251xfd-spi0-0-xceiver",
+			<&reg_mcp251xfd_xceiver>, "regulator-name=mcp251xfd-spi0-0-xceiver";
+		spi0-1 = <0>, "+1",
+			<&mcp251xfd_frag>, "target:0=", <&spi0>,
+			<&mcp251xfd>, "reg:0=1",
+			<&mcp251xfd_pins>, "name=mcp251xfd_spi0_1_pins",
+			<&clk_mcp251xfd_osc>, "name=mcp251xfd-spi0-1-osc",
+			<&mcp251xfd_xceiver_pins>, "name=mcp251xfd_spi0_1_xceiver_pins",
+			<&reg_mcp251xfd_xceiver>, "name=reg-mcp251xfd-spi0-1-xceiver",
+			<&reg_mcp251xfd_xceiver>, "regulator-name=mcp251xfd-spi0-1-xceiver";
+		spi1-0 = <0>, "+2",
+			<&mcp251xfd_frag>, "target:0=", <&spi1>,
+			<&mcp251xfd>, "reg:0=0",
+			<&mcp251xfd_pins>, "name=mcp251xfd_spi1_0_pins",
+			<&clk_mcp251xfd_osc>, "name=mcp251xfd-spi1-0-osc",
+			<&mcp251xfd_xceiver_pins>, "name=mcp251xfd_spi1_0_xceiver_pins",
+			<&reg_mcp251xfd_xceiver>, "name=reg-mcp251xfd-spi1-0-xceiver",
+			<&reg_mcp251xfd_xceiver>, "regulator-name=mcp251xfd-spi1-0-xceiver";
+		spi1-1 = <0>, "+3",
+			<&mcp251xfd_frag>, "target:0=", <&spi1>,
+			<&mcp251xfd>, "reg:0=1",
+			<&mcp251xfd_pins>, "name=mcp251xfd_spi1_1_pins",
+			<&clk_mcp251xfd_osc>, "name=mcp251xfd-spi1-1-osc",
+			<&mcp251xfd_xceiver_pins>, "name=mcp251xfd_spi1_1_xceiver_pins",
+			<&reg_mcp251xfd_xceiver>, "name=reg-mcp251xfd-spi1-1-xceiver",
+			<&reg_mcp251xfd_xceiver>, "regulator-name=mcp251xfd-spi1-1-xceiver";
+		spi1-2 = <0>, "+4",
+			<&mcp251xfd_frag>, "target:0=", <&spi1>,
+			<&mcp251xfd>, "reg:0=2",
+			<&mcp251xfd_pins>, "name=mcp251xfd_spi1_2_pins",
+			<&clk_mcp251xfd_osc>, "name=mcp251xfd-spi1-2-osc",
+			<&mcp251xfd_xceiver_pins>, "name=mcp251xfd_spi1_2_xceiver_pins",
+			<&reg_mcp251xfd_xceiver>, "name=reg-mcp251xfd-spi1-2-xceiver",
+			<&reg_mcp251xfd_xceiver>, "regulator-name=mcp251xfd-spi1-2-xceiver";
+		spi2-0 = <0>, "+5",
+			<&mcp251xfd_frag>, "target:0=", <&spi2>,
+			<&mcp251xfd>, "reg:0=0",
+			<&mcp251xfd_pins>, "name=mcp251xfd_spi2_0_pins",
+			<&clk_mcp251xfd_osc>, "name=mcp251xfd-spi2-0-osc",
+			<&mcp251xfd_xceiver_pins>, "name=mcp251xfd_spi2_0_xceiver_pins",
+			<&reg_mcp251xfd_xceiver>, "name=reg-mcp251xfd-spi2-0-xceiver",
+			<&reg_mcp251xfd_xceiver>, "regulator-name=mcp251xfd-spi2-0-xceiver";
+		spi2-1 = <0>, "+6",
+			<&mcp251xfd_frag>, "target:0=", <&spi2>,
+			<&mcp251xfd>, "reg:0=1",
+			<&mcp251xfd_pins>, "name=mcp251xfd_spi2_1_pins",
+			<&clk_mcp251xfd_osc>, "name=mcp251xfd-spi2-1-osc",
+			<&mcp251xfd_xceiver_pins>, "name=mcp251xfd_spi2_1_xceiver_pins",
+			<&reg_mcp251xfd_xceiver>, "name=reg-mcp251xfd-spi2-1-xceiver",
+			<&reg_mcp251xfd_xceiver>, "regulator-name=mcp251xfd-spi2-1-xceiver";
+		spi2-2 = <0>, "+7",
+			<&mcp251xfd_frag>, "target:0=", <&spi2>,
+			<&mcp251xfd>, "reg:0=2",
+			<&mcp251xfd_pins>, "name=mcp251xfd_spi2_2_pins",
+			<&clk_mcp251xfd_osc>, "name=mcp251xfd-spi2-2-osc",
+			<&mcp251xfd_xceiver_pins>, "name=mcp251xfd_spi2_2_xceiver_pins",
+			<&reg_mcp251xfd_xceiver>, "name=reg-mcp251xfd-spi2-2-xceiver",
+			<&reg_mcp251xfd_xceiver>, "regulator-name=mcp251xfd-spi2-2-xceiver";
+		oscillator = <&clk_mcp251xfd_osc>, "clock-frequency:0";
+		speed = <&mcp251xfd>, "spi-max-frequency:0";
+		interrupt = <&mcp251xfd_pins>, "brcm,pins:0",
+			<&mcp251xfd>, "interrupts:0";
+		rx_interrupt = <0>, "+11",
+			<&mcp251xfd_pins>, "brcm,pins:4",
+			<&mcp251xfd_rx_int_gpios>, "microchip,rx-int-gpios:4";
+		xceiver_enable = <0>, "+12+13+14",
+			<&mcp251xfd_xceiver_pins>, "brcm,pins:0",
+			<&reg_mcp251xfd_xceiver>, "gpio:4";
+		xceiver_active_high = <&reg_mcp251xfd_xceiver>, "enable-active-high?";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/mcp3008-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/mcp3008-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/mcp3008-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/mcp3008-overlay.dts	2021-07-25 16:45:35.148807558 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/*
+ * Device tree overlay for Microchip mcp3008 10-Bit A/D Converters
+ */
+
+/dts-v1/;
+/plugin/;
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&spidev0>;
+		__dormant__ {
+			status = "disabled";
+		};
+	};
+
+	fragment@1 {
+		target = <&spidev1>;
+		__dormant__ {
+			status = "disabled";
+		};
+	};
+
+	fragment@2 {
+		target-path = "spi1/spidev@0";
+		__dormant__ {
+			status = "disabled";
+		};
+	};
+
+	fragment@3 {
+		target-path = "spi1/spidev@1";
+		__dormant__ {
+			status = "disabled";
+		};
+	};
+
+	fragment@4 {
+		target-path = "spi1/spidev@2";
+		__dormant__ {
+			status = "disabled";
+		};
+	};
+
+	fragment@5 {
+		target-path = "spi2/spidev@0";
+		__dormant__ {
+			status = "disabled";
+		};
+	};
+
+	fragment@6 {
+		target-path = "spi2/spidev@1";
+		__dormant__ {
+			status = "disabled";
+		};
+	};
+
+	fragment@7 {
+		target-path = "spi2/spidev@2";
+		__dormant__ {
+			status = "disabled";
+		};
+	};
+
+	fragment@8 {
+		target = <&spi0>;
+		__dormant__ {
+			status = "okay";
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			mcp3008_00: mcp3008@0 {
+				compatible = "microchip,mcp3008";
+				reg = <0>;
+				spi-max-frequency = <1600000>;
+			};
+		};
+	};
+
+	fragment@9 {
+		target = <&spi0>;
+		__dormant__ {
+			status = "okay";
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			mcp3008_01: mcp3008@1 {
+				compatible = "microchip,mcp3008";
+				reg = <1>;
+				spi-max-frequency = <1600000>;
+			};
+		};
+	};
+
+	fragment@10 {
+		target = <&spi1>;
+		__dormant__ {
+			status = "okay";
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			mcp3008_10: mcp3008@0 {
+				compatible = "microchip,mcp3008";
+				reg = <0>;
+				spi-max-frequency = <1600000>;
+			};
+		};
+	};
+
+	fragment@11 {
+		target = <&spi1>;
+		__dormant__ {
+			status = "okay";
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			mcp3008_11: mcp3008@1 {
+				compatible = "microchip,mcp3008";
+				reg = <1>;
+				spi-max-frequency = <1600000>;
+			};
+		};
+	};
+
+	fragment@12 {
+		target = <&spi1>;
+		__dormant__ {
+			status = "okay";
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			mcp3008_12: mcp3008@2 {
+				compatible = "microchip,mcp3008";
+				reg = <2>;
+				spi-max-frequency = <1600000>;
+			};
+		};
+	};
+
+	fragment@13 {
+		target = <&spi2>;
+		__dormant__ {
+			status = "okay";
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			mcp3008_20: mcp3008@0 {
+				compatible = "microchip,mcp3008";
+				reg = <0>;
+				spi-max-frequency = <1600000>;
+			};
+		};
+	};
+
+	fragment@14 {
+		target = <&spi2>;
+		__dormant__ {
+			status = "okay";
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			mcp3008_21: mcp3008@1 {
+				compatible = "microchip,mcp3008";
+				reg = <1>;
+				spi-max-frequency = <1600000>;
+			};
+		};
+	};
+
+	fragment@15 {
+		target = <&spi2>;
+		__dormant__ {
+			status = "okay";
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			mcp3008_22: mcp3008@2 {
+				compatible = "microchip,mcp3008";
+				reg = <2>;
+				spi-max-frequency = <1600000>;
+			};
+		};
+	};
+
+	__overrides__ {
+		spi0-0-present = <0>, "+0+8";
+		spi0-1-present = <0>, "+1+9";
+		spi1-0-present = <0>, "+2+10";
+		spi1-1-present = <0>, "+3+11";
+		spi1-2-present = <0>, "+4+12";
+		spi2-0-present = <0>, "+5+13";
+		spi2-1-present = <0>, "+6+14";
+		spi2-2-present = <0>, "+7+15";
+		spi0-0-speed = <&mcp3008_00>, "spi-max-frequency:0";
+		spi0-1-speed = <&mcp3008_01>, "spi-max-frequency:0";
+		spi1-0-speed = <&mcp3008_10>, "spi-max-frequency:0";
+		spi1-1-speed = <&mcp3008_11>, "spi-max-frequency:0";
+		spi1-2-speed = <&mcp3008_12>, "spi-max-frequency:0";
+		spi2-0-speed = <&mcp3008_20>, "spi-max-frequency:0";
+		spi2-1-speed = <&mcp3008_21>, "spi-max-frequency:0";
+		spi2-2-speed = <&mcp3008_22>, "spi-max-frequency:0";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/mcp3202-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/mcp3202-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/mcp3202-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/mcp3202-overlay.dts	2021-07-25 16:45:35.148807558 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/*
+ * Device tree overlay for Microchip mcp3202 12-Bit A/D Converters
+ */
+
+/dts-v1/;
+/plugin/;
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&spidev0>;
+		__dormant__ {
+			status = "disabled";
+		};
+	};
+
+	fragment@1 {
+		target = <&spidev1>;
+		__dormant__ {
+			status = "disabled";
+		};
+	};
+
+	fragment@2 {
+		target-path = "spi1/spidev@0";
+		__dormant__ {
+			status = "disabled";
+		};
+	};
+
+	fragment@3 {
+		target-path = "spi1/spidev@1";
+		__dormant__ {
+			status = "disabled";
+		};
+	};
+
+	fragment@4 {
+		target-path = "spi1/spidev@2";
+		__dormant__ {
+			status = "disabled";
+		};
+	};
+
+	fragment@5 {
+		target-path = "spi2/spidev@0";
+		__dormant__ {
+			status = "disabled";
+		};
+	};
+
+	fragment@6 {
+		target-path = "spi2/spidev@1";
+		__dormant__ {
+			status = "disabled";
+		};
+	};
+
+	fragment@7 {
+		target-path = "spi2/spidev@2";
+		__dormant__ {
+			status = "disabled";
+		};
+	};
+
+	fragment@8 {
+		target = <&spi0>;
+		__dormant__ {
+			status = "okay";
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			mcp3202_00: mcp3202@0 {
+				compatible = "mcp3202";
+				reg = <0>;
+				spi-max-frequency = <1600000>;
+			};
+		};
+	};
+
+	fragment@9 {
+		target = <&spi0>;
+		__dormant__ {
+			status = "okay";
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			mcp3202_01: mcp3202@1 {
+				compatible = "mcp3202";
+				reg = <1>;
+				spi-max-frequency = <1600000>;
+			};
+		};
+	};
+
+	fragment@10 {
+		target = <&spi1>;
+		__dormant__ {
+			status = "okay";
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			mcp3202_10: mcp3202@0 {
+				compatible = "mcp3202";
+				reg = <0>;
+				spi-max-frequency = <1600000>;
+			};
+		};
+	};
+
+	fragment@11 {
+		target = <&spi1>;
+		__dormant__ {
+			status = "okay";
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			mcp3202_11: mcp3202@1 {
+				compatible = "mcp3202";
+				reg = <1>;
+				spi-max-frequency = <1600000>;
+			};
+		};
+	};
+
+	fragment@12 {
+		target = <&spi1>;
+		__dormant__ {
+			status = "okay";
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			mcp3202_12: mcp3202@2 {
+				compatible = "mcp3202";
+				reg = <2>;
+				spi-max-frequency = <1600000>;
+			};
+		};
+	};
+
+	fragment@13 {
+		target = <&spi2>;
+		__dormant__ {
+			status = "okay";
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			mcp3202_20: mcp3202@0 {
+				compatible = "mcp3202";
+				reg = <0>;
+				spi-max-frequency = <1600000>;
+			};
+		};
+	};
+
+	fragment@14 {
+		target = <&spi2>;
+		__dormant__ {
+			status = "okay";
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			mcp3202_21: mcp3202@1 {
+				compatible = "mcp3202";
+				reg = <1>;
+				spi-max-frequency = <1600000>;
+			};
+		};
+	};
+
+	fragment@15 {
+		target = <&spi2>;
+		__dormant__ {
+			status = "okay";
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			mcp3202_22: mcp3202@2 {
+				compatible = "mcp3202";
+				reg = <2>;
+				spi-max-frequency = <1600000>;
+			};
+		};
+	};
+
+	__overrides__ {
+		spi0-0-present = <0>, "+0+8";
+		spi0-1-present = <0>, "+1+9";
+		spi1-0-present = <0>, "+2+10";
+		spi1-1-present = <0>, "+3+11";
+		spi1-2-present = <0>, "+4+12";
+		spi2-0-present = <0>, "+5+13";
+		spi2-1-present = <0>, "+6+14";
+		spi2-2-present = <0>, "+7+15";
+		spi0-0-speed = <&mcp3202_00>, "spi-max-frequency:0";
+		spi0-1-speed = <&mcp3202_01>, "spi-max-frequency:0";
+		spi1-0-speed = <&mcp3202_10>, "spi-max-frequency:0";
+		spi1-1-speed = <&mcp3202_11>, "spi-max-frequency:0";
+		spi1-2-speed = <&mcp3202_12>, "spi-max-frequency:0";
+		spi2-0-speed = <&mcp3202_20>, "spi-max-frequency:0";
+		spi2-1-speed = <&mcp3202_21>, "spi-max-frequency:0";
+		spi2-2-speed = <&mcp3202_22>, "spi-max-frequency:0";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/mcp342x-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/mcp342x-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/mcp342x-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/mcp342x-overlay.dts	2021-07-25 16:45:35.148807558 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// Overlay for MCP3421-8 ADCs from Microchip Semiconductor
+
+/dts-v1/;
+/plugin/;
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&i2c1>;
+		__dormant__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			status = "okay";
+
+			mcp3421: mcp@68 {
+				reg = <0x68>;
+				compatible = "microchip,mcp3421";
+
+				status = "okay";
+			};
+		};
+	};
+
+	fragment@1 {
+		target = <&i2c1>;
+		__dormant__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			status = "okay";
+
+			mcp3422: mcp@68 {
+				reg = <0x68>;
+				compatible = "microchip,mcp3422";
+
+				status = "okay";
+			};
+		};
+	};
+
+	fragment@2 {
+		target = <&i2c1>;
+		__dormant__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			status = "okay";
+
+			mcp3423: mcp@68 {
+				reg = <0x68>;
+				compatible = "microchip,mcp3423";
+
+				status = "okay";
+			};
+		};
+	};
+
+	fragment@3 {
+		target = <&i2c1>;
+		__dormant__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			status = "okay";
+
+			mcp3424: mcp@68 {
+				reg = <0x68>;
+				compatible = "microchip,mcp3424";
+
+				status = "okay";
+			};
+		};
+	};
+
+	fragment@4 {
+		target = <&i2c1>;
+		__dormant__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			status = "okay";
+
+			mcp3425: mcp@68 {
+				reg = <0x68>;
+				compatible = "microchip,mcp3425","mcp3425";
+
+				status = "okay";
+			};
+		};
+	};
+
+	fragment@5 {
+		target = <&i2c1>;
+		__dormant__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			status = "okay";
+
+			mcp3426: mcp@68 {
+				reg = <0x68>;
+				compatible = "microchip,mcp3426";
+
+				status = "okay";
+			};
+		};
+	};
+
+	fragment@6 {
+		target = <&i2c1>;
+		__dormant__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			status = "okay";
+
+			mcp3427: mcp@68 {
+				reg = <0x68>;
+				compatible = "microchip,mcp3427";
+
+				status = "okay";
+			};
+		};
+	};
+
+	fragment@7 {
+		target = <&i2c1>;
+		__dormant__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			status = "okay";
+
+			mcp3428: mcp@68 {
+				reg = <0x68>;
+				compatible = "microchip,mcp3428";
+
+				status = "okay";
+			};
+		};
+	};
+
+	__overrides__ {
+		addr = <&mcp3421>,"reg:0",
+		       <&mcp3422>,"reg:0",
+		       <&mcp3423>,"reg:0",
+		       <&mcp3424>,"reg:0",
+		       <&mcp3425>,"reg:0",
+		       <&mcp3426>,"reg:0",
+		       <&mcp3427>,"reg:0",
+		       <&mcp3428>,"reg:0";
+		mcp3421 = <0>,"=0";
+		mcp3422 = <0>,"=1";
+		mcp3423 = <0>,"=2";
+		mcp3424 = <0>,"=3";
+		mcp3425 = <0>,"=4";
+		mcp3426 = <0>,"=5";
+		mcp3427 = <0>,"=6";
+		mcp3428 = <0>,"=7";
+	};
+};
+
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/media-center-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/media-center-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/media-center-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/media-center-overlay.dts	2021-07-25 16:45:35.148807558 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/*
+ * Device Tree overlay for Media Center HAT by Pi Supply
+ *
+ */
+
+/dts-v1/;
+/plugin/;
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&spi0>;
+		__overlay__ {
+			status = "okay";
+
+			spidev@0{
+				status = "disabled";
+			};
+
+			spidev@1{
+				status = "disabled";
+			};
+		};
+	};
+
+	fragment@1 {
+		target = <&gpio>;
+		__overlay__ {
+			rpi_display_pins: rpi_display_pins {
+				brcm,pins = <12 23 24 25>;
+				brcm,function = <1 1 1 0>; /* out out out in */
+				brcm,pull = <0 0 0 2>; /* - - - up */
+			};
+		};
+	};
+
+	fragment@2 {
+		target = <&spi0>;
+		__overlay__ {
+			/* needed to avoid dtc warning */
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			rpidisplay: rpi-display@0{
+				compatible = "ilitek,ili9341";
+				reg = <0>;
+				pinctrl-names = "default";
+				pinctrl-0 = <&rpi_display_pins>;
+
+				spi-max-frequency = <32000000>;
+				rotate = <90>;
+				bgr;
+				fps = <30>;
+				buswidth = <8>;
+				reset-gpios = <&gpio 23 1>;
+				dc-gpios = <&gpio 24 0>;
+				led-gpios = <&gpio 12 0>;
+				debug = <0>;
+			};
+
+			rpidisplay_ts: rpi-display-ts@1 {
+				compatible = "ti,ads7846";
+				reg = <1>;
+
+				spi-max-frequency = <2000000>;
+				interrupts = <25 2>; /* high-to-low edge triggered */
+				interrupt-parent = <&gpio>;
+				pendown-gpio = <&gpio 25 1>;
+				ti,x-plate-ohms = /bits/ 16 <60>;
+				ti,pressure-max = /bits/ 16 <255>;
+			};
+		};
+	};
+
+	fragment@3 {
+		target-path = "/";
+		__overlay__ {
+			lirc_rpi: lirc_rpi {
+				compatible = "rpi,lirc-rpi";
+				pinctrl-names = "default";
+				pinctrl-0 = <&lirc_pins>;
+				status = "okay";
+
+				// Override autodetection of IR receiver circuit
+				// (0 = active high, 1 = active low, -1 = no override )
+				rpi,sense = <0xffffffff>;
+
+				// Software carrier
+				// (0 = off, 1 = on)
+				rpi,softcarrier = <1>;
+
+				// Invert output
+				// (0 = off, 1 = on)
+				rpi,invert = <0>;
+
+				// Enable debugging messages
+				// (0 = off, 1 = on)
+				rpi,debug = <0>;
+			};
+		};
+	};
+
+	fragment@4 {
+		target = <&gpio>;
+		__overlay__ {
+			lirc_pins: lirc_pins {
+				brcm,pins = <6 5>;
+				brcm,function = <1 0>; // out in
+				brcm,pull = <0 1>; // off down
+			};
+		};
+	};
+
+	__overrides__ {
+		speed =     <&rpidisplay>,"spi-max-frequency:0";
+		rotate =    <&rpidisplay>,"rotate:0";
+		fps =       <&rpidisplay>,"fps:0";
+		debug =     <&rpidisplay>,"debug:0", 
+		            <&lirc_rpi>,"rpi,debug:0";
+		xohms =     <&rpidisplay_ts>,"ti,x-plate-ohms;0";
+		swapxy =    <&rpidisplay_ts>,"ti,swap-xy?";
+		backlight = <&rpidisplay>,"led-gpios:4",
+		            <&rpi_display_pins>,"brcm,pins:0";
+
+		gpio_out_pin =  <&lirc_pins>,"brcm,pins:0";
+		gpio_in_pin =   <&lirc_pins>,"brcm,pins:4";
+		gpio_in_pull =  <&lirc_pins>,"brcm,pull:4";
+
+		sense =         <&lirc_rpi>,"rpi,sense:0";
+		softcarrier =   <&lirc_rpi>,"rpi,softcarrier:0";
+		invert =        <&lirc_rpi>,"rpi,invert:0";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/merus-amp-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/merus-amp-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/merus-amp-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/merus-amp-overlay.dts	2021-07-25 16:45:35.148807558 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// SPDX-License-Identifier: GPL-2.0-only
+// Definitions for Infineon Merus-Amp
+/dts-v1/;
+/plugin/;
+#include <dt-bindings/pinctrl/bcm2835.h>
+#include <dt-bindings/gpio/gpio.h>
+
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&i2s>;
+		__overlay__ {
+			status = "okay";
+		};
+	};
+
+	fragment@1 {
+		target = <&gpio>;
+		__overlay__ {
+			merus_amp_pins: merus_amp_pins {
+				brcm,pins = <23>;
+				brcm,function = <0>; /* in */
+				brcm,pull = <2>; /* up */
+			};
+		};
+	};
+
+	fragment@2 {
+		target = <&i2c1>;
+		__overlay__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			status = "okay";
+
+			merus_amp: ma120x0p@20 {
+				#sound-dai-cells = <0>;
+				compatible = "ma,ma120x0p";
+				reg = <0x20>;
+				status = "okay";
+				pinctrl-names = "default";
+				pinctrl-0 = <&merus_amp_pins>;
+				enable_gp-gpios = <&gpio 14 GPIO_ACTIVE_HIGH>;
+				mute_gp-gpios = <&gpio 15 GPIO_ACTIVE_HIGH>;
+				booster_gp-gpios = <&gpio 17 GPIO_ACTIVE_HIGH>;
+				error_gp-gpios = <&gpio 23 GPIO_ACTIVE_HIGH>;
+			};
+		};
+	};
+
+	fragment@3 {
+		target = <&sound>;
+		__overlay__ {
+			compatible = "merus,merus-amp";
+			i2s-controller = <&i2s>;
+			status = "okay";
+		};
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/midi-uart0-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/midi-uart0-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/midi-uart0-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/midi-uart0-overlay.dts	2021-07-25 16:45:35.148807558 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/dts-v1/;
+/plugin/;
+
+#include <dt-bindings/clock/bcm2835.h>
+
+/*
+ * Fake a higher clock rate to get a larger divisor, and thereby a lower
+ * baudrate. The real clock is 48MHz, which we scale so that requesting
+ * 38.4kHz results in an actual 31.25kHz.
+ *
+ *   48000000*38400/31250 = 58982400
+ */
+
+/{
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target-path = "/";
+		__overlay__ {
+			midi_clk: midi_clk {
+				compatible = "fixed-clock";
+				#clock-cells = <0>;
+				clock-output-names = "uart0_pclk";
+				clock-frequency = <58982400>;
+			};
+		};
+	};
+
+	fragment@1 {
+		target = <&uart0>;
+		__overlay__ {
+			clocks = <&midi_clk>,
+			         <&clocks BCM2835_CLOCK_VPU>;
+		};
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/midi-uart1-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/midi-uart1-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/midi-uart1-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/midi-uart1-overlay.dts	2021-07-25 16:45:35.148807558 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/dts-v1/;
+/plugin/;
+
+#include <dt-bindings/clock/bcm2835-aux.h>
+
+/*
+ * Fake a higher clock rate to get a larger divisor, and thereby a lower
+ * baudrate. The real clock is 48MHz, which we scale so that requesting
+ * 38.4kHz results in an actual 31.25kHz.
+ *
+ *   48000000*38400/31250 = 58982400
+ */
+
+/{
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target-path = "/clocks";
+		__overlay__ {
+			midi_clk: clock@5 {
+				compatible = "fixed-factor-clock";
+				#clock-cells = <0>;
+				clocks = <&aux BCM2835_AUX_CLOCK_UART>;
+				clock-mult = <38400>;
+				clock-div  = <31250>;
+			};
+		};
+	};
+
+	fragment@1 {
+		target = <&uart1>;
+		__overlay__ {
+			clocks = <&midi_clk>;
+		};
+	};
+
+	fragment@2 {
+		target = <&aux>;
+		__overlay__ {
+			clock-output-names = "aux_uart", "aux_spi1", "aux_spi2";
+		};
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/minipitft13-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/minipitft13-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/minipitft13-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/minipitft13-overlay.dts	2021-07-25 16:45:35.148807558 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/*
+ * Device Tree overlay for Adafruit Mini PiTFT 1.3" and 1.5" 240x240 Display
+ *
+ */
+
+/dts-v1/;
+/plugin/;
+
+/ {
+        compatible = "brcm,bcm2835";
+
+        fragment@0 {
+                target = <&spi0>;
+                __overlay__ {
+                        status = "okay";
+
+                        spidev@0{
+                                status = "disabled";
+                        };
+
+                        spidev@1{
+                                status = "disabled";
+                        };
+                };
+        };
+
+        fragment@1 {
+                target = <&gpio>;
+                __overlay__ {
+                        pitft_pins: pitft_pins {
+                                brcm,pins = <25>;
+                                brcm,function = <1>; /* out */
+                                brcm,pull = <0>; /* none */
+                        };
+                };
+        };
+
+        fragment@2 {
+                target = <&spi0>;
+                __overlay__ {
+                        /* needed to avoid dtc warning */
+                        #address-cells = <1>;
+                        #size-cells = <0>;
+
+                        pitft: pitft@0 {
+                                compatible = "fbtft,minipitft13";
+                                reg = <0>;
+                                pinctrl-names = "default";
+                                pinctrl-0 = <&pitft_pins>;
+                                spi-max-frequency = <32000000>;
+                                rotate = <0>;
+                                width = <240>;
+                                height = <240>;
+                                buswidth = <8>;
+                                dc-gpios = <&gpio 25 0>;
+                                led-gpios = <&gpio 26 0>;
+                                debug = <0>;
+                        };
+                };
+        };
+
+        __overrides__ {
+                speed =   <&pitft>,"spi-max-frequency:0";
+                rotate =  <&pitft>,"rotate:0";
+                width =   <&pitft>,"width:0";
+                height =  <&pitft>,"height:0";
+                fps =     <&pitft>,"fps:0";
+                debug =   <&pitft>,"debug:0";
+        };
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/miniuart-bt-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/miniuart-bt-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/miniuart-bt-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/miniuart-bt-overlay.dts	2021-07-25 16:45:35.148807558 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/dts-v1/;
+/plugin/;
+
+/* Switch Pi3 Bluetooth function to use the mini-UART (ttyS0) and restore
+   UART0/ttyAMA0 over GPIOs 14 & 15. Note that this may reduce the maximum
+   usable baudrate.
+
+   It is also necessary to edit /lib/systemd/system/hciuart.service and
+   replace ttyAMA0 with ttyS0, unless you have a system with udev rules
+   that create /dev/serial0 and /dev/serial1, in which case use /dev/serial1
+   instead because it will always be correct.
+
+   If cmdline.txt uses the alias serial0 to refer to the user-accessable port
+   then the firmware will replace with the appropriate port whether or not
+   this overlay is used.
+*/
+
+#include <dt-bindings/gpio/gpio.h>
+
+/{
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&uart0>;
+		__overlay__ {
+			pinctrl-names = "default";
+			pinctrl-0 = <&uart0_pins>;
+			status = "okay";
+		};
+	};
+
+	fragment@1 {
+		target = <&bt>;
+		__overlay__ {
+			status = "disabled";
+		};
+	};
+
+	fragment@2 {
+		target = <&uart1>;
+		__overlay__ {
+			pinctrl-names = "default";
+			pinctrl-0 = <&uart1_pins &bt_pins &fake_bt_cts>;
+			status = "okay";
+		};
+	};
+
+	fragment@3 {
+		target = <&uart0_pins>;
+		__overlay__ {
+			brcm,pins;
+			brcm,function;
+			brcm,pull;
+		};
+	};
+
+	fragment@4 {
+		target = <&uart1_pins>;
+		__overlay__ {
+			brcm,pins = <32 33>;
+			brcm,function = <2>; /* alt5=UART1 */
+			brcm,pull = <0 2>;
+		};
+	};
+
+	fragment@5 {
+		target = <&gpio>;
+		__overlay__ {
+			fake_bt_cts: fake_bt_cts {
+				brcm,pins = <31>;
+				brcm,function = <1>; /* output */
+			};
+		};
+	};
+
+	fragment@6 {
+		target-path = "/aliases";
+		__overlay__ {
+			serial0 = "/soc/serial@7e201000";
+			serial1 = "/soc/serial@7e215040";
+		};
+	};
+
+	fragment@7 {
+		target = <&minibt>;
+		minibt_frag: __overlay__ {
+		};
+	};
+
+	__overrides__ {
+		krnbt = <&minibt_frag>,"status";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/mmc-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/mmc-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/mmc-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/mmc-overlay.dts	2021-07-25 16:45:35.148807558 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/dts-v1/;
+/plugin/;
+
+/{
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&mmc>;
+		frag0: __overlay__ {
+			pinctrl-names = "default";
+			pinctrl-0 = <&mmc_pins>;
+			bus-width = <4>;
+			brcm,overclock-50 = <0>;
+			status = "okay";
+		};
+	};
+
+	fragment@1 {
+		target = <&gpio>;
+		__overlay__ {
+			mmc_pins: mmc_pins {
+				brcm,pins = <48 49 50 51 52 53>;
+				brcm,function = <7>; /* alt3 */
+				brcm,pull = <0 2 2 2 2 2>;
+			};
+		};
+	};
+
+	fragment@2 {
+		target = <&sdhost>;
+		__overlay__ {
+			status = "disabled";
+		};
+	};
+
+	fragment@3 {
+		target = <&mmcnr>;
+		__overlay__ {
+			status = "disabled";
+		};
+	};
+
+	__overrides__ {
+		overclock_50     = <&frag0>,"brcm,overclock-50:0";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/mpu6050-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/mpu6050-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/mpu6050-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/mpu6050-overlay.dts	2021-07-25 16:45:35.148807558 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// Definitions for MPU6050
+/dts-v1/;
+/plugin/;
+
+/ {
+        compatible = "brcm,bcm2835";
+
+        fragment@0 {
+                target = <&i2c1>;
+                __overlay__ {
+                        #address-cells = <1>;
+                        #size-cells = <0>;
+                        status = "okay";
+                        clock-frequency = <400000>;
+
+                        mpu6050: mpu6050@68 {
+                                compatible = "invensense,mpu6050";
+                                reg = <0x68>;
+                                interrupt-parent = <&gpio>;
+                                interrupts = <4 1>;
+                        };
+                };
+        };
+
+        __overrides__ {
+                interrupt = <&mpu6050>,"interrupts:0";
+                addr = <&mpu6050>,"reg:0";
+        };
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/mz61581-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/mz61581-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/mz61581-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/mz61581-overlay.dts	2021-07-25 16:45:35.148807558 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/*
+ * Device Tree overlay for MZ61581-PI-EXT 2014.12.28 by Tontec
+ *
+ */
+
+/dts-v1/;
+/plugin/;
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&spi0>;
+		__overlay__ {
+			status = "okay";
+		};
+	};
+
+	fragment@1 {
+		target = <&spidev0>;
+		__overlay__ {
+			status = "disabled";
+		};
+	};
+
+	fragment@2 {
+		target = <&spidev1>;
+		__overlay__ {
+			status = "disabled";
+		};
+	};
+
+	fragment@3 {
+		target = <&gpio>;
+		__overlay__ {
+			mz61581_pins: mz61581_pins {
+				brcm,pins = <4 15 18 25>;
+				brcm,function = <0 1 1 1>; /* in out out out */
+			};
+		};
+	};
+
+	fragment@4 {
+		target = <&spi0>;
+		__overlay__ {
+			/* needed to avoid dtc warning */
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			mz61581: mz61581@0{
+				compatible = "samsung,s6d02a1";
+				reg = <0>;
+				pinctrl-names = "default";
+				pinctrl-0 = <&mz61581_pins>;
+
+				spi-max-frequency = <128000000>;
+				spi-cpol;
+				spi-cpha;
+
+				width = <320>;
+				height = <480>;
+				rotate = <270>;
+				bgr;
+				fps = <30>;
+				buswidth = <8>;
+				txbuflen = <32768>;
+
+				reset-gpios = <&gpio 15 1>;
+				dc-gpios = <&gpio 25 0>;
+				led-gpios = <&gpio 18 0>;
+
+				init = <0x10000b0 00
+					0x1000011
+					0x20000ff
+					0x10000b3 0x02 0x00 0x00 0x00
+					0x10000c0 0x13 0x3b 0x00 0x02 0x00 0x01 0x00 0x43
+					0x10000c1 0x08 0x16 0x08 0x08
+					0x10000c4 0x11 0x07 0x03 0x03
+					0x10000c6 0x00
+					0x10000c8 0x03 0x03 0x13 0x5c 0x03 0x07 0x14 0x08 0x00 0x21 0x08 0x14 0x07 0x53 0x0c 0x13 0x03 0x03 0x21 0x00
+					0x1000035 0x00
+					0x1000036 0xa0
+					0x100003a 0x55
+					0x1000044 0x00 0x01
+					0x10000d0 0x07 0x07 0x1d 0x03
+					0x10000d1 0x03 0x30 0x10
+					0x10000d2 0x03 0x14 0x04
+					0x1000029
+					0x100002c>;
+
+				/* This is a workaround to make sure the init sequence slows down and doesn't fail */
+				debug = <3>;
+			};
+
+			mz61581_ts: mz61581_ts@1 {
+				compatible = "ti,ads7846";
+				reg = <1>;
+
+				spi-max-frequency = <2000000>;
+				interrupts = <4 2>; /* high-to-low edge triggered */
+				interrupt-parent = <&gpio>;
+				pendown-gpio = <&gpio 4 0>;
+
+				ti,x-plate-ohms = /bits/ 16 <60>;
+				ti,pressure-max = /bits/ 16 <255>;
+			};
+		};
+	};
+	__overrides__ {
+		speed =   <&mz61581>, "spi-max-frequency:0";
+		rotate =  <&mz61581>, "rotate:0";
+		fps =     <&mz61581>, "fps:0";
+		txbuflen = <&mz61581>, "txbuflen:0";
+		debug =   <&mz61581>, "debug:0";
+		xohms =   <&mz61581_ts>,"ti,x-plate-ohms;0";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/ov5647-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/ov5647-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/ov5647-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/ov5647-overlay.dts	2021-07-25 16:45:35.148807558 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// SPDX-License-Identifier: GPL-2.0-only
+// Definitions for OV5647 camera module on VC I2C bus
+/dts-v1/;
+/plugin/;
+
+/{
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&i2c_csi_dsi>;
+		__overlay__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			status = "okay";
+
+			ov5647: ov5647@36 {
+				compatible = "ovti,ov5647";
+				reg = <0x36>;
+				status = "okay";
+
+				pwdn-gpios = <&gpio 41 1>, <&gpio 32 1>;
+				clocks = <&ov5647_clk>;
+
+				rotation = <0>;
+
+				port {
+					ov5647_0: endpoint {
+						remote-endpoint = <&csi1_ep>;
+						clock-lanes = <0>;
+						data-lanes = <1 2>;
+						clock-noncontinuous;
+						link-frequencies =
+							/bits/ 64 <297000000>;
+					};
+				};
+			};
+		};
+	};
+
+	fragment@1 {
+		target = <&csi1>;
+		__overlay__ {
+			status = "okay";
+
+			port {
+				csi1_ep: endpoint {
+					remote-endpoint = <&ov5647_0>;
+					data-lanes = <1 2>;
+				};
+			};
+		};
+	};
+
+	fragment@2 {
+		target = <&i2c0if>;
+		__overlay__ {
+			status = "okay";
+		};
+	};
+
+	fragment@3 {
+		target = <&i2c0mux>;
+		__overlay__ {
+			status = "okay";
+		};
+	};
+
+	fragment@4 {
+		target-path="/__overrides__";
+		__overlay__ {
+			cam0-pwdn-ctrl = <&ov5647>,"pwdn-gpios:0";
+			cam0-pwdn      = <&ov5647>,"pwdn-gpios:4";
+			cam0-led-ctrl  = <&ov5647>,"pwdn-gpios:12";
+			cam0-led       = <&ov5647>,"pwdn-gpios:16";
+		};
+	};
+
+	fragment@5 {
+		target-path = "/";
+		__overlay__ {
+			ov5647_clk: camera-clk {
+				compatible = "fixed-clock";
+				#clock-cells = <0>;
+				clock-frequency = <25000000>;
+			};
+		};
+	};
+
+	__overrides__ {
+		rotation = <&ov5647>,"rotation:0";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/ov7251-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/ov7251-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/ov7251-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/ov7251-overlay.dts	2021-07-25 16:45:35.148807558 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// SPDX-License-Identifier: GPL-2.0-only
+// Definitions for OV7251 camera module on VC I2C bus
+/dts-v1/;
+/plugin/;
+
+#include <dt-bindings/gpio/gpio.h>
+
+/{
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&i2c_csi_dsi>;
+		__overlay__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			status = "okay";
+
+			ov7251: ov7251@60 {
+				compatible = "ovti,ov7251";
+				reg = <0x60>;
+				status = "okay";
+
+				clocks = <&ov7251_clk>;
+				clock-names = "xclk";
+				clock-frequency = <24000000>;
+
+				vdddo-supply = <&ov7251_dovdd>;
+				vdda-supply = <&cam1_reg>;
+				vddd-supply = <&ov7251_dvdd>;
+
+				port {
+					ov7251_0: endpoint {
+						remote-endpoint = <&csi1_ep>;
+						clock-lanes = <0>;
+						data-lanes = <1>;
+						clock-noncontinuous;
+						link-frequencies =
+							/bits/ 64 <456000000>;
+					};
+				};
+			};
+		};
+	};
+
+	fragment@1 {
+		target = <&csi1>;
+		__overlay__ {
+			status = "okay";
+
+			port {
+				csi1_ep: endpoint {
+					remote-endpoint = <&ov7251_0>;
+					data-lanes = <1>;
+				};
+			};
+		};
+	};
+
+	fragment@2 {
+		target = <&i2c0if>;
+		__overlay__ {
+			status = "okay";
+		};
+	};
+
+	fragment@3 {
+		target-path="/";
+		__overlay__ {
+			ov7251_dovdd: fixedregulator@1 {
+				compatible = "regulator-fixed";
+				regulator-name = "ov7251_dovdd";
+				regulator-min-microvolt = <1800000>;
+				regulator-max-microvolt = <1800000>;
+			};
+			ov7251_dvdd: fixedregulator@2 {
+				compatible = "regulator-fixed";
+				regulator-name = "ov7251_dvdd";
+				regulator-min-microvolt = <1200000>;
+				regulator-max-microvolt = <1200000>;
+			};
+			ov7251_clk: ov7251-clk {
+				compatible = "fixed-clock";
+				#clock-cells = <0>;
+				clock-frequency = <24000000>;
+			};
+		};
+	};
+
+	fragment@4 {
+		target = <&i2c0mux>;
+		__overlay__ {
+			status = "okay";
+		};
+	};
+
+	fragment@5 {
+		target = <&cam1_reg>;
+		__overlay__ {
+			status = "okay";
+			regulator-name = "ov7251_avdd";
+			regulator-min-microvolt = <2800000>;
+			regulator-max-microvolt = <2800000>;
+		};
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/ov9281-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/ov9281-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/ov9281-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/ov9281-overlay.dts	2021-07-25 16:45:35.148807558 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// SPDX-License-Identifier: GPL-2.0-only
+// Definitions for OV9281 camera module on VC I2C bus
+/dts-v1/;
+/plugin/;
+
+#include <dt-bindings/gpio/gpio.h>
+
+/{
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&i2c_csi_dsi>;
+		__overlay__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			status = "okay";
+
+			ov9281: ov9281@60 {
+				compatible = "ovti,ov9281";
+				reg = <0x60>;
+				status = "okay";
+
+				clocks = <&ov9281_clk>;
+				clock-names = "xvclk";
+
+				avdd-supply = <&cam1_reg>;
+				dovdd-supply = <&ov9281_dovdd>;
+				dvdd-supply = <&ov9281_dvdd>;
+
+				port {
+					ov9281_0: endpoint {
+						remote-endpoint = <&csi1_ep>;
+						clock-lanes = <0>;
+						data-lanes = <1 2>;
+						clock-noncontinuous;
+						link-frequencies =
+							/bits/ 64 <400000000>;
+					};
+				};
+			};
+		};
+	};
+
+	fragment@1 {
+		target = <&csi1>;
+		__overlay__ {
+			status = "okay";
+
+			port {
+				csi1_ep: endpoint {
+					remote-endpoint = <&ov9281_0>;
+					data-lanes = <1 2>;
+					clock-noncontinuous;
+				};
+			};
+		};
+	};
+
+	fragment@2 {
+		target = <&i2c0if>;
+		__overlay__ {
+			status = "okay";
+		};
+	};
+
+	fragment@3 {
+		target-path="/";
+		__overlay__ {
+			ov9281_dovdd: fixedregulator@1 {
+				compatible = "regulator-fixed";
+				regulator-name = "ov9281_dovdd";
+				regulator-min-microvolt = <1800000>;
+				regulator-max-microvolt = <1800000>;
+			};
+			ov9281_dvdd: fixedregulator@2 {
+				compatible = "regulator-fixed";
+				regulator-name = "ov9281_dvdd";
+				regulator-min-microvolt = <1200000>;
+				regulator-max-microvolt = <1200000>;
+			};
+			ov9281_clk: ov9281-clk {
+				compatible = "fixed-clock";
+				#clock-cells = <0>;
+				clock-frequency = <24000000>;
+			};
+		};
+	};
+
+	fragment@4 {
+		target = <&i2c0mux>;
+		__overlay__ {
+			status = "okay";
+		};
+	};
+
+	fragment@5 {
+		target = <&cam1_reg>;
+		__overlay__ {
+			status = "okay";
+			regulator-name = "ov9281_avdd";
+			regulator-min-microvolt = <2800000>;
+			regulator-max-microvolt = <2800000>;
+		};
+	};
+
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/overlay_map.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/overlay_map.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/overlay_map.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/overlay_map.dts	2021-07-25 16:45:35.148807558 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/dts-v1/;
+
+/ {
+	bmp085_i2c-sensor {
+		deprecated = "use i2c-sensor,bmp085";
+	};
+
+	highperi {
+		bcm2711;
+	};
+
+	i2c0-bcm2708 {
+		deprecated = "use i2c0";
+	};
+
+	i2c1-bcm2708 {
+		deprecated = "use i2c1";
+	};
+
+	i2c3 {
+		bcm2711;
+	};
+
+	i2c4 {
+		bcm2711;
+	};
+
+	i2c5 {
+		bcm2711;
+	};
+
+	i2c6 {
+		bcm2711;
+	};
+
+	lirc-rpi {
+		deprecated = "use gpio-ir";
+	};
+
+	pcie-32bit-dma {
+		bcm2711;
+	};
+
+	pi3-act-led {
+		renamed = "act-led";
+	};
+
+	pi3-disable-bt {
+		renamed = "disable-bt";
+	};
+
+	pi3-disable-wifi {
+		renamed = "disable-wifi";
+	};
+
+	pi3-miniuart-bt {
+		renamed = "miniuart-bt";
+	};
+
+	rpivid-v4l2 {
+		bcm2711;
+	};
+
+	sdio-1bit {
+		deprecated = "use sdio,bus_width=1,gpios_22_25";
+	};
+
+	sdtweak {
+		deprecated = "use 'dtparam=sd_poll_once' etc.";
+	};
+
+	spi0-cs {
+		renamed = "spi0-2cs";
+	};
+
+	spi0-hw-cs {
+		deprecated = "no longer necessary";
+	};
+
+	spi3-1cs {
+		bcm2711;
+	};
+
+	spi3-2cs {
+		bcm2711;
+	};
+
+	spi4-1cs {
+		bcm2711;
+	};
+
+	spi4-2cs {
+		bcm2711;
+	};
+
+	spi5-1cs {
+		bcm2711;
+	};
+
+	spi5-2cs {
+		bcm2711;
+	};
+
+	spi6-1cs {
+		bcm2711;
+	};
+
+	spi6-2cs {
+		bcm2711;
+	};
+
+	uart2 {
+		bcm2711;
+	};
+
+	uart3 {
+		bcm2711;
+	};
+
+	uart4 {
+		bcm2711;
+	};
+
+	uart5 {
+		bcm2711;
+	};
+
+	upstream {
+		bcm2835;
+		bcm2711 = "upstream-pi4";
+	};
+
+	upstream-aux-interrupt {
+		deprecated = "no longer necessary";
+	};
+
+	upstream-pi4 {
+		bcm2711;
+	};
+
+	vc4-kms-v3d {
+		bcm2835;
+		bcm2711 = "vc4-kms-v3d-pi4";
+	};
+
+	vc4-kms-v3d-pi4 {
+		bcm2711;
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/papirus-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/papirus-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/papirus-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/papirus-overlay.dts	2021-07-25 16:45:35.148807558 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/* PaPiRus ePaper Screen by Pi Supply */
+
+/dts-v1/;
+/plugin/;
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&i2c_arm>;
+		__overlay__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			status = "okay";
+
+			display_temp: lm75@48 {
+				compatible = "lm75b";
+				reg = <0x48>;
+				status = "okay";
+				#thermal-sensor-cells = <0>;
+			};
+		};
+	};
+
+	fragment@1 {
+		target-path = "/";
+		__overlay__ {
+			thermal-zones {
+				display {
+					polling-delay-passive = <0>;
+					polling-delay = <0>;
+					thermal-sensors = <&display_temp>;
+				};
+			};
+		};
+	};
+
+	fragment@2 {
+		target = <&spi0>;
+		__overlay__ {
+			status = "okay";
+
+			spidev@0{
+				status = "disabled";
+			};
+		};
+	};
+
+	fragment@3 {
+		target = <&gpio>;
+		__overlay__ {
+			repaper_pins: repaper_pins {
+				brcm,pins = <14 15 23 24 25>;
+				brcm,function = <1 1 1 1 0>; /* out out out out in */
+			};
+		};
+	};
+
+	fragment@4 {
+		target = <&spi0>;
+		__overlay__ {
+			/* needed to avoid dtc warning */
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			repaper: repaper@0{
+				compatible = "not_set";
+				reg = <0>;
+				pinctrl-names = "default";
+				pinctrl-0 = <&repaper_pins>;
+
+				spi-max-frequency = <8000000>;
+
+				panel-on-gpios = <&gpio 23 0>;
+				border-gpios = <&gpio 14 0>;
+				discharge-gpios = <&gpio 15 0>;
+				reset-gpios = <&gpio 24 0>;
+				busy-gpios = <&gpio 25 0>;
+
+				repaper-thermal-zone = "display";
+			};
+		};
+	};
+
+	__overrides__ {
+		panel = <&repaper>, "compatible";
+		speed = <&repaper>, "spi-max-frequency:0";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/pca953x-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/pca953x-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/pca953x-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/pca953x-overlay.dts	2021-07-25 16:45:35.148807558 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// Definitions for NXP PCA953x family of I2C GPIO controllers on ARM I2C bus.
+/dts-v1/;
+/plugin/;
+
+/{
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&i2c_arm>;
+		__overlay__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			status = "okay";
+
+			pca: pca@20 {
+				compatible = "nxp,pca9534";
+				reg = <0x20>;
+				gpio-controller;
+				#gpio-cells = <2>;
+
+				status = "okay";
+			};
+		};
+	};
+
+	fragment@1 {
+		target = <&pca>;
+		__dormant__ {
+			compatible = "nxp,pca6416";
+		};
+	};
+	fragment@2 {
+		target = <&pca>;
+		__dormant__ {
+			compatible = "nxp,pca9505";
+		};
+	};
+	fragment@3 {
+		target = <&pca>;
+		__dormant__ {
+			compatible = "nxp,pca9535";
+		};
+	};
+	fragment@4 {
+		target = <&pca>;
+		__dormant__ {
+			compatible = "nxp,pca9536";
+		};
+	};
+	fragment@5 {
+		target = <&pca>;
+		__dormant__ {
+			compatible = "nxp,pca9537";
+		};
+	};
+	fragment@6 {
+		target = <&pca>;
+		__dormant__ {
+			compatible = "nxp,pca9538";
+		};
+	};
+	fragment@7 {
+		target = <&pca>;
+		__dormant__ {
+			compatible = "nxp,pca9539";
+		};
+	};
+	fragment@8 {
+		target = <&pca>;
+		__dormant__ {
+			compatible = "nxp,pca9554";
+		};
+	};
+	fragment@9 {
+		target = <&pca>;
+		__dormant__ {
+			compatible = "nxp,pca9555";
+		};
+	};
+	fragment@10 {
+		target = <&pca>;
+		__dormant__ {
+			compatible = "nxp,pca9556";
+		};
+	};
+	fragment@11 {
+		target = <&pca>;
+		__dormant__ {
+			compatible = "nxp,pca9557";
+		};
+	};
+	fragment@12 {
+		target = <&pca>;
+		__dormant__ {
+			compatible = "nxp,pca9574";
+		};
+	};
+	fragment@13 {
+		target = <&pca>;
+		__dormant__ {
+			compatible = "nxp,pca9575";
+		};
+	};
+	fragment@14 {
+		target = <&pca>;
+		__dormant__ {
+			compatible = "nxp,pca9698";
+		};
+	};
+	fragment@15 {
+		target = <&pca>;
+		__dormant__ {
+			compatible = "nxp,pca16416";
+		};
+	};
+	fragment@16 {
+		target = <&pca>;
+		__dormant__ {
+			compatible = "nxp,pca16524";
+		};
+	};
+	fragment@17 {
+		target = <&pca>;
+		__dormant__ {
+			compatible = "nxp,pca19555a";
+		};
+	};
+	fragment@18 {
+		target = <&pca>;
+		__dormant__ {
+			compatible = "maxim,max7310";
+		};
+	};
+	fragment@19 {
+		target = <&pca>;
+		__dormant__ {
+			compatible = "maxim,max7312";
+		};
+	};
+	fragment@20 {
+		target = <&pca>;
+		__dormant__ {
+			compatible = "maxim,max7313";
+		};
+	};
+	fragment@21 {
+		target = <&pca>;
+		__dormant__ {
+			compatible = "maxim,max7315";
+		};
+	};
+	fragment@22 {
+		target = <&pca>;
+		__dormant__ {
+			compatible = "ti,pca6107";
+		};
+	};
+	fragment@23 {
+		target = <&pca>;
+		__dormant__ {
+			compatible = "ti,tca6408";
+		};
+	};
+	fragment@24 {
+		target = <&pca>;
+		__dormant__ {
+			compatible = "ti,tca6416";
+		};
+	};
+	fragment@25 {
+		target = <&pca>;
+		__dormant__ {
+			compatible = "ti,tca6424";
+		};
+	};
+	fragment@26 {
+		target = <&pca>;
+		__dormant__ {
+			compatible = "ti,tca9539";
+		};
+	};
+	fragment@27 {
+		target = <&pca>;
+		__dormant__ {
+			compatible = "ti,tca9554";
+		};
+	};
+	fragment@28 {
+		target = <&pca>;
+		__dormant__ {
+			compatible = "onnn,cat9554";
+		};
+	};
+	fragment@29 {
+		target = <&pca>;
+		__dormant__ {
+			compatible = "onnn,pca9654";
+		};
+	};
+	fragment@30 {
+		target = <&pca>;
+		__dormant__ {
+			compatible = "exar,xra1202";
+		};
+	};
+
+	__overrides__ {
+		addr = <&pca>,"reg:0";
+		pca6416 = <0>, "+1";
+		pca9505 = <0>, "+2";
+		pca9535 = <0>, "+3";
+		pca9536 = <0>, "+4";
+		pca9537 = <0>, "+5";
+		pca9538 = <0>, "+6";
+		pca9539 = <0>, "+7";
+		pca9554 = <0>, "+8";
+		pca9555 = <0>, "+9";
+		pca9556 = <0>, "+10";
+		pca9557 = <0>, "+11";
+		pca9574 = <0>, "+12";
+		pca9575 = <0>, "+13";
+		pca9698 = <0>, "+14";
+		pca16416 = <0>, "+15";
+		pca16524 = <0>, "+16";
+		pca19555a = <0>, "+17";
+		max7310 = <0>, "+18";
+		max7312 = <0>, "+19";
+		max7313 = <0>, "+20";
+		max7315 = <0>, "+21";
+		pca6107 = <0>, "+22";
+		tca6408 = <0>, "+23";
+		tca6416 = <0>, "+24";
+		tca6424 = <0>, "+25";
+		tca9539 = <0>, "+26";
+		tca9554 = <0>, "+27";
+		cat9554 = <0>, "+28";
+		pca9654 = <0>, "+29";
+		xra1202 = <0>, "+30";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/pcie-32bit-dma-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/pcie-32bit-dma-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/pcie-32bit-dma-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/pcie-32bit-dma-overlay.dts	2021-07-25 16:45:35.148807558 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/*
+ * pcie-32bit-dma-overlay.dts
+ */
+
+/dts-v1/;
+/plugin/;
+
+/ {
+	compatible = "brcm,bcm2711";
+
+	fragment@0 {
+		target-path = "/aliases";
+		__overlay__ {
+			pcie0 = "";
+		};
+	};
+
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/pibell-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/pibell-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/pibell-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/pibell-overlay.dts	2021-07-25 16:45:35.158807390 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/dts-v1/;
+/plugin/;
+
+/ {
+    compatible = "brcm,bcm2835";
+
+    fragment@0 {
+        target-path = "/";
+        __overlay__ {
+            codec_out: spdif-transmitter {
+                #address-cells = <0>;
+                #size-cells = <0>;
+                #sound-dai-cells = <0>;
+                compatible = "linux,spdif-dit";
+                status = "okay";
+            };
+
+            codec_in: card-codec {
+                #sound-dai-cells = <0>;
+                compatible = "invensense,ics43432";
+                status = "okay";
+            };
+        };
+    };
+
+    fragment@1 {
+        target = <&i2s>;
+        __overlay__ {
+            #sound-dai-cells = <0>;
+            status = "okay";
+        };
+    };
+
+    fragment@2 {
+        target = <&sound>;
+        snd: __overlay__ {
+            compatible = "simple-audio-card";
+            simple-audio-card,name = "PiBell";
+
+            status="okay";
+
+            capture_link: simple-audio-card,dai-link@0 {
+                format = "i2s";
+
+                r_cpu_dai: cpu {
+                    sound-dai = <&i2s>;
+
+/* example TDM slot configuration
+                    dai-tdm-slot-num = <2>;
+                    dai-tdm-slot-width = <32>;
+*/
+                };
+
+                r_codec_dai: codec {
+                    sound-dai = <&codec_in>;
+                };
+            };
+
+            playback_link: simple-audio-card,dai-link@1 {
+                format = "i2s";
+
+                p_cpu_dai: cpu {
+                    sound-dai = <&i2s>;
+
+/* example TDM slot configuration
+                    dai-tdm-slot-num = <2>;
+                    dai-tdm-slot-width = <32>;
+*/
+                };
+
+                p_codec_dai: codec {
+                    sound-dai = <&codec_out>;
+                };
+            };
+        };
+    };
+
+    __overrides__ {
+        alsaname = <&snd>, "simple-audio-card,name";
+    };
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/pifacedigital-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/pifacedigital-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/pifacedigital-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/pifacedigital-overlay.dts	2021-07-25 16:45:35.158807390 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * PiFace Digital, Device Tree Overlay.
+ * Copyright (C) 2020 Thomas Preston <thomas.preston@codethink.co.uk>
+ *
+ * The PiFace Digital is a convenient breakout board for the Microchip mcp23s17
+ * SPI GPIO port expander.
+ *
+ * The first eight GPIOs 0..7 (bank A) are connected to eight output terminals
+ * and LEDs, plus two relays on the first two outputs. These output loads are
+ * active-high.
+ *
+ * The next eight GPIOs 8..15 (bank B) are connected to eight input terminals
+ * with four on-board switches connecting them to ground. Inputs devices are
+ * therefore expected to bridge terminals to ground, so the mcp23s17 pullups are
+ * activated for GPIO bank B.
+ *
+ * On PiFace Digital, the mcp23s17 is connected to the Raspberry Pi's SPI0 CS0
+ * bus. Each SPI bus supports up to eight addressable child devices. The PiFace
+ * Digital only supports addresses 0-4, which can be configured by jumpers JP1
+ * and JP2.
+ *
+ * You can tell the driver about these jumper configurations with the
+ * spi-present-mask bitmask:
+ *
+ *     | JP1 | JP2 | dtoverlay line in /boot/config.txt         |
+ *     | --- | --- | ------------------------------------------ |
+ *     |  0  |  0  | dtoverlay=pifacedigital                    |
+ *     |  0  |  0  | dtoverlay=pifacedigital:spi-present-mask=1 |
+ *     |  0  |  1  | dtoverlay=pifacedigital:spi-present-mask=2 |
+ *     |  1  |  0  | dtoverlay=pifacedigital:spi-present-mask=4 |
+ *     |  1  |  1  | dtoverlay=pifacedigital:spi-present-mask=8 |
+ *
+ * # Example
+ * Set the dtoverlay config in /boot/config.txt and power off the Raspberry Pi:
+ *
+ *     $ grep pifacedigital /boot/config.txt
+ *     dtoverlay=pifacedigital
+ *     $ sudo systemctl poweroff
+ *
+ * Attach the PiFace Digital and power on the Raspberry Pi.
+ * Then use the libgpiod tools to query the device:
+ *
+ *     $ sudo apt install gpiod
+ *     $ gpiodetect | grep mcp23s17
+ *     gpiochip2 [mcp23s17.0] (16 lines)
+ *
+ * Set GPIO outputs 0, 2 and 5:
+ *
+ *     $ gpioset gpiochip2 0=1 2=1 5=1
+ *
+ * Get GPIO status (input GPIO 8..15 are high, because they are active-low):
+ *
+ *     $ gpioget gpiochip2 {8..15}
+ *     1 1 1 1 1 1 1 1
+ *
+ * And even monitor interrupts:
+ *
+ *     $ gpiomon gpiochip2 {8..15}
+ *     event: FALLING EDGE offset: 11 timestamp: [1597361662.926741667]
+ *     event:  RISING EDGE offset: 11 timestamp: [1597361663.062555051]
+ *
+ */
+
+/dts-v1/;
+/plugin/;
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	/* Disable exposing /dev/spidev0.0 */
+	fragment@0 {
+		target = <&spidev0>;
+		__overlay__ {
+			status = "disabled";
+		};
+	};
+
+	/* Add the PiFace Digital device node to the spi0.0 device. */
+	fragment@1 {
+		target = <&spi0>;
+		__overlay__ {
+			status = "okay";
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			pfdigital: pifacedigital@0 {
+				compatible = "microchip,mcp23s17";
+				reg = <0>;
+
+				/* Set devices present with 8-bit mask. */
+				microchip,spi-present-mask = <0x01>;
+				spi-max-frequency = <500000>;
+
+				gpio-controller;
+				#gpio-cells = <2>;
+
+				/* This device can pass through interrupts. */
+				interrupt-controller;
+				#interrupt-cells = <2>;
+
+				/* INTB is connected to GPIO 25.
+				 * 0x8 active-low level-sensitive
+				 */
+				interrupts = <25 0x8>;
+				interrupt-parent = <&gpio>;
+
+				/* Configure pull-ups on bank B GPIOs */
+				pinctrl-0 = <&pfdigital_irq &pfdigital_pullups>;
+				pinctrl-names = "default";
+				pfdigital_pullups: pinmux {
+					pins =
+						"gpio8",
+						"gpio9",
+						"gpio10",
+						"gpio11",
+						"gpio12",
+						"gpio13",
+						"gpio14",
+						"gpio15";
+					bias-pull-up;
+				};
+			};
+		};
+	};
+
+	/* PiFace Digital mcp23s17 INTB pin is connected to GPIO 25. The INTB
+	 * pin is configured active-low (0 on interrupt), so expect to see
+	 * FALLING_EDGE when inputs are bridged to ground (switch is pressed).
+	 */
+	fragment@3 {
+		target = <&gpio>;
+		__overlay__ {
+			pfdigital_irq: pifacedigital_irq {
+				brcm,pins = <25>;
+				brcm,function = <0>; /* input */
+			};
+		};
+	};
+
+	__overrides__ {
+		spi-present-mask = <&pfdigital>, "microchip,spi-present-mask:0";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/pifi-40-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/pifi-40-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/pifi-40-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/pifi-40-overlay.dts	2021-07-25 16:45:35.158807390 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// Definitions for PiFi-40 Amp
+/dts-v1/;
+/plugin/;
+#include <dt-bindings/gpio/gpio.h>
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&i2s>;
+		__overlay__ {
+			status = "okay";
+		};
+	};
+
+	fragment@1 {
+		target = <&i2c1>;
+		__overlay__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			status = "okay";
+
+			tas5711l: audio-codec@1a {
+				compatible = "ti,tas5711";
+				reg = <0x1a>;
+				#sound-dai-cells = <0>;
+				sound-name-prefix = "Left";
+				status = "okay";
+			};
+
+			tas5711r: audio-codec@1b {
+				compatible = "ti,tas5711";
+				reg = <0x1b>;
+				#sound-dai-cells = <0>;
+				sound-name-prefix = "Right";
+				status = "okay";
+			};
+		};
+	};
+
+	fragment@2 {
+		target = <&sound>;
+		pifi_40: __overlay__ {
+			compatible = "pifi,pifi-40";
+			audio-codec = <&tas5711l &tas5711r>;
+			i2s-controller = <&i2s>;
+			pdn-gpios = <&gpio 23 1>;
+			status = "okay";
+		};
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/pifi-dac-hd-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/pifi-dac-hd-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/pifi-dac-hd-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/pifi-dac-hd-overlay.dts	2021-07-25 16:45:35.158807390 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// Overlay for PiFi-DAC-HD
+/dts-v1/;
+/plugin/;
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&i2s>;
+		__overlay__ {
+			status = "okay";
+		};
+	};
+
+	fragment@1 {
+		target = <&i2c1>;
+		__overlay__ {
+			status = "okay";
+			#address-cells = <1>;
+			#size-cells =<0>;
+
+			pcm5142: pcm5142@4c {
+				#sound-dai-cells = <0>;
+				compatible = "ti,pcm5142";
+				reg = <0x4c>;
+				status = "okay";
+			};
+		};
+	};
+
+	fragment@2 {
+		target = <&sound>;
+		__overlay__ {
+			compatible = "simple-audio-card";
+			simple-audio-card,name = "PiFi-DAC-HD";
+			status = "okay";
+
+			simple-audio-card,dai-link@1 {
+				format = "i2s";
+				cpu {
+					sound-dai = <&i2s>;
+				};
+				codec {
+					sound-dai = <&pcm5142>;
+				};
+			};
+		};
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/pifi-dac-zero-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/pifi-dac-zero-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/pifi-dac-zero-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/pifi-dac-zero-overlay.dts	2021-07-25 16:45:35.158807390 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// Overlay for PiFi-DAC-Zero
+/dts-v1/;
+/plugin/;
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&sound>;
+		__overlay__ {
+			compatible = "simple-audio-card";
+			simple-audio-card,name = "PiFi-DAC-Zero";
+			status = "okay";
+
+			simple-audio-card,dai-link@1 {
+				format = "i2s";
+
+				cpu {
+					sound-dai = <&i2s>;
+					dai-tdm-slot-num = <2>;
+					dai-tdm-slot-width = <32>;
+				};
+
+				codec {
+					sound-dai = <&codec_out>;
+				};
+			};
+		};
+	};
+
+	fragment@1 {
+		target-path = "/";
+		__overlay__ {
+			codec_out: pcm5102a-codec {
+				#sound-dai-cells = <0>;
+				compatible = "ti,pcm5102a";
+				status = "okay";
+			};
+		};
+	};
+
+	fragment@2 {
+		target = <&i2s>;
+		__overlay__ {
+			#sound-dai-cells = <0>;
+			status = "okay";
+		};
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/pifi-mini-210-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/pifi-mini-210-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/pifi-mini-210-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/pifi-mini-210-overlay.dts	2021-07-25 16:45:35.158807390 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// Definitions for PiFi Mini 210
+/dts-v1/;
+/plugin/;
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&i2s>;
+		__overlay__ {
+			status = "okay";
+		};
+	};
+
+	fragment@1 {
+		target = <&i2c1>;
+		__overlay__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			status = "okay";
+
+			tas5711@1a {
+				#sound-dai-cells = <0>;
+				compatible = "ti,tas5711";
+				reg = <0x1a>;
+				status = "okay";
+				pdn-gpios = <&gpio 23 1>;
+				reset-gpios = <&gpio 24 1>;
+			};
+		};
+	};
+
+	fragment@2 {
+		target = <&sound>;
+		__overlay__ {
+			compatible = "pifi,pifi-mini-210";
+			i2s-controller = <&i2s>;
+
+			status = "okay";
+		};
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/piglow-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/piglow-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/piglow-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/piglow-overlay.dts	2021-07-25 16:45:35.158807390 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// Definitions for SN3218 LED driver from Si-En Technology on PiGlow
+/dts-v1/;
+/plugin/;
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&i2c_arm>;
+		__overlay__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			status = "okay";
+
+			sn3218@54 {
+				compatible = "si-en,sn3218";
+				reg = <0x54>;
+				#address-cells = <1>;
+				#size-cells = <0>;
+				status = "okay";
+
+				led@1 {
+					reg = <1>;
+					label = "piglow:red:led1";
+				};
+				led@2 {
+					reg = <2>;
+					label = "piglow:orange:led2";
+				};
+				led@3 {
+					reg = <3>;
+					label = "piglow:yellow:led3";
+				};
+				led@4 {
+					reg = <4>;
+					label = "piglow:green:led4";
+				};
+				led@5 {
+					reg = <5>;
+					label = "piglow:blue:led5";
+				};
+				led@6 {
+					reg = <6>;
+					label = "piglow:green:led6";
+				};
+				led@7 {
+					reg = <7>;
+					label = "piglow:red:led7";
+				};
+				led@8 {
+					reg = <8>;
+					label = "piglow:orange:led8";
+				};
+				led@9 {
+					reg = <9>;
+					label = "piglow:yellow:led9";
+				};
+				led@10 {
+					reg = <10>;
+					label = "piglow:white:led10";
+				};
+				led@11 {
+					reg = <11>;
+					label = "piglow:white:led11";
+				};
+				led@12 {
+					reg = <12>;
+					label = "piglow:blue:led12";
+				};
+				led@13 {
+					reg = <13>;
+					label = "piglow:white:led13";
+				};
+				led@14 {
+					reg = <14>;
+					label = "piglow:green:led14";
+				};
+				led@15 {
+					reg = <15>;
+					label = "piglow:blue:led15";
+				};
+				led@16 {
+					reg = <16>;
+					label = "piglow:yellow:led16";
+				};
+				led@17 {
+					reg = <17>;
+					label = "piglow:orange:led17";
+				};
+				led@18 {
+					reg = <18>;
+					label = "piglow:red:led18";
+				};
+			};
+		};
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/piscreen2r-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/piscreen2r-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/piscreen2r-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/piscreen2r-overlay.dts	2021-07-25 16:45:35.158807390 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+ /*
+ * Device Tree overlay for PiScreen2 3.5" TFT with resistive touch  by Ozzmaker.com
+ *
+ */
+
+/dts-v1/;
+/plugin/;
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&spi0>;
+		__overlay__ {
+			status = "okay";
+		};
+	};
+
+	fragment@1 {
+		target = <&spidev0>;
+		__overlay__ {
+			status = "disabled";
+		};
+	};
+
+	fragment@2 {
+		target = <&spidev1>;
+		__overlay__ {
+			status = "disabled";
+		};
+	};
+
+	fragment@3 {
+		target = <&gpio>;
+		__overlay__ {
+			piscreen2_pins: piscreen2_pins {
+				brcm,pins = <17 25 24 22>;
+				brcm,function = <0 1 1 1>; /* in out out out */
+			};
+		};
+	};
+
+	fragment@4 {
+		target = <&spi0>;
+		__overlay__ {
+			/* needed to avoid dtc warning */
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			piscreen2: piscreen2@0{
+				compatible = "ilitek,ili9486";
+				reg = <0>;
+				pinctrl-names = "default";
+				pinctrl-0 = <&piscreen2_pins>;
+				bgr;
+				spi-max-frequency = <64000000>;
+				rotate = <90>;
+				fps = <30>;
+				buswidth = <8>;
+				regwidth = <16>;
+				txbuflen = <32768>;
+				reset-gpios = <&gpio 25 1>;
+				dc-gpios = <&gpio 24 0>;
+				led-gpios = <&gpio 22 0>;
+				debug = <0>;
+
+                                init = <0x10000b0 0x00
+                                        0x1000011
+                                        0x20000ff
+                                        0x100003a 0x55
+                                        0x1000036 0x28
+                                        0x10000c0 0x11 0x09
+                                        0x10000c1 0x41
+                                        0x10000c5 0x00 0x00 0x00 0x00
+                                        0x10000b6 0x00 0x02
+                                        0x10000f7 0xa9 0x51 0x2c 0x2
+                                        0x10000be 0x00 0x04
+                                        0x10000e9 0x00
+                                        0x1000011
+                                        0x1000029>;
+
+			};
+
+			piscreen2_ts: piscreen2-ts@1 {
+				compatible = "ti,ads7846";
+				reg = <1>;
+
+				spi-max-frequency = <2000000>;
+				interrupts = <17 2>; /* high-to-low edge triggered */
+				interrupt-parent = <&gpio>;
+				pendown-gpio = <&gpio 17 0>;
+				ti,swap-xy;
+				ti,x-plate-ohms = /bits/ 16 <100>;
+				ti,pressure-max = /bits/ 16 <255>;
+			};
+		};
+	};
+	__overrides__ {
+		speed =		<&piscreen2>,"spi-max-frequency:0";
+		rotate =	<&piscreen2>,"rotate:0";
+		fps =		<&piscreen2>,"fps:0";
+		debug =		<&piscreen2>,"debug:0";
+		xohms =		<&piscreen2_ts>,"ti,x-plate-ohms;0";
+	};
+};
+
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/piscreen-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/piscreen-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/piscreen-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/piscreen-overlay.dts	2021-07-25 16:45:35.158807390 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/*
+ * Device Tree overlay for PiScreen 3.5" display shield by Ozzmaker
+ *
+ */
+
+/dts-v1/;
+/plugin/;
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&spi0>;
+		__overlay__ {
+			status = "okay";
+		};
+	};
+
+	fragment@1 {
+		target = <&spidev0>;
+		__overlay__ {
+			status = "disabled";
+		};
+	};
+
+	fragment@2 {
+		target = <&spidev1>;
+		__overlay__ {
+			status = "disabled";
+		};
+	};
+
+	fragment@3 {
+		target = <&gpio>;
+		__overlay__ {
+			piscreen_pins: piscreen_pins {
+				brcm,pins = <17 25 24 22>;
+				brcm,function = <0 1 1 1>; /* in out out out */
+			};
+		};
+	};
+
+	fragment@4 {
+		target = <&spi0>;
+		__overlay__ {
+			/* needed to avoid dtc warning */
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			piscreen: piscreen@0{
+				compatible = "ilitek,ili9486";
+				reg = <0>;
+				pinctrl-names = "default";
+				pinctrl-0 = <&piscreen_pins>;
+
+				spi-max-frequency = <24000000>;
+				rotate = <270>;
+				bgr;
+				fps = <30>;
+				buswidth = <8>;
+				regwidth = <16>;
+				reset-gpios = <&gpio 25 1>;
+				dc-gpios = <&gpio 24 0>;
+				led-gpios = <&gpio 22 0>;
+				debug = <0>;
+
+				init = <0x10000b0 0x00
+				        0x1000011
+					0x20000ff
+					0x100003a 0x55
+					0x1000036 0x28
+					0x10000c2 0x44
+					0x10000c5 0x00 0x00 0x00 0x00
+					0x10000e0 0x0f 0x1f 0x1c 0x0c 0x0f 0x08 0x48 0x98 0x37 0x0a 0x13 0x04 0x11 0x0d 0x00
+					0x10000e1 0x0f 0x32 0x2e 0x0b 0x0d 0x05 0x47 0x75 0x37 0x06 0x10 0x03 0x24 0x20 0x00
+					0x10000e2 0x0f 0x32 0x2e 0x0b 0x0d 0x05 0x47 0x75 0x37 0x06 0x10 0x03 0x24 0x20 0x00
+					0x1000011
+					0x1000029>;
+			};
+
+			piscreen_ts: piscreen-ts@1 {
+				compatible = "ti,ads7846";
+				reg = <1>;
+
+				spi-max-frequency = <2000000>;
+				interrupts = <17 2>; /* high-to-low edge triggered */
+				interrupt-parent = <&gpio>;
+				pendown-gpio = <&gpio 17 0>;
+				ti,swap-xy;
+				ti,x-plate-ohms = /bits/ 16 <100>;
+				ti,pressure-max = /bits/ 16 <255>;
+			};
+		};
+	};
+	__overrides__ {
+		speed =		<&piscreen>,"spi-max-frequency:0";
+		rotate =	<&piscreen>,"rotate:0";
+		fps =		<&piscreen>,"fps:0";
+		debug =		<&piscreen>,"debug:0";
+		xohms =		<&piscreen_ts>,"ti,x-plate-ohms;0";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/pisound-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/pisound-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/pisound-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/pisound-overlay.dts	2021-07-25 16:45:35.158807390 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/*
+ * Pisound Linux kernel module.
+ * Copyright (C) 2016-2017  Vilniaus Blokas UAB, https://blokas.io/pisound
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; version 2 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+/dts-v1/;
+/plugin/;
+
+#include <dt-bindings/gpio/gpio.h>
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&spi0>;
+		__overlay__ {
+			status = "okay";
+		};
+	};
+
+	fragment@1 {
+		target = <&spidev0>;
+		__overlay__ {
+			status = "disabled";
+		};
+	};
+
+	fragment@2 {
+		target = <&spidev1>;
+		__overlay__ {
+			status = "okay";
+		};
+	};
+
+	fragment@3 {
+		target = <&spi0>;
+		__overlay__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			pisound_spi: pisound_spi@0{
+				compatible = "blokaslabs,pisound-spi";
+				reg = <0>;
+				pinctrl-names = "default";
+				pinctrl-0 = <&spi0_pins>;
+				spi-max-frequency = <1000000>;
+			};
+		};
+	};
+
+	fragment@4 {
+		target-path = "/";
+		__overlay__ {
+			pcm5102a-codec {
+				#sound-dai-cells = <0>;
+				compatible = "ti,pcm5102a";
+				status = "okay";
+			};
+		};
+	};
+
+	fragment@5 {
+		target = <&sound>;
+		__overlay__ {
+			compatible = "blokaslabs,pisound";
+			i2s-controller = <&i2s>;
+			status = "okay";
+
+			pinctrl-0 = <&pisound_button_pins>;
+
+			osr-gpios =
+				<&gpio 13 GPIO_ACTIVE_HIGH>,
+				<&gpio 26 GPIO_ACTIVE_HIGH>,
+				<&gpio 16 GPIO_ACTIVE_HIGH>;
+
+			reset-gpios =
+				<&gpio 12 GPIO_ACTIVE_HIGH>,
+				<&gpio 24 GPIO_ACTIVE_HIGH>;
+
+			data_available-gpios = <&gpio 25 GPIO_ACTIVE_HIGH>;
+
+			button-gpios = <&gpio 17 GPIO_ACTIVE_LOW>;
+		};
+	};
+
+	fragment@6 {
+		target = <&gpio>;
+		__overlay__ {
+			pinctrl-names = "default";
+			pinctrl-0 = <&pisound_button_pins>;
+
+			pisound_button_pins: pisound_button_pins {
+				brcm,pins = <17>;
+				brcm,function = <0>; // Input
+				brcm,pull = <2>; // Pull-Up
+			};
+		};
+	};
+
+	fragment@7 {
+		target = <&i2s>;
+		__overlay__ {
+			status = "okay";
+		};
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/pitft22-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/pitft22-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/pitft22-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/pitft22-overlay.dts	2021-07-25 16:45:35.158807390 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/*
+ * Device Tree overlay for pitft by Adafruit
+ *
+ */
+
+/dts-v1/;
+/plugin/;
+
+/ {
+        compatible = "brcm,bcm2835";
+
+        fragment@0 {
+                target = <&spi0>;
+                __overlay__ {
+                        status = "okay";
+
+                        spidev@0{
+                                status = "disabled";
+                        };
+
+                        spidev@1{
+                                status = "disabled";
+                        };
+                };
+        };
+
+        fragment@1 {
+                target = <&gpio>;
+                __overlay__ {
+                        pitft_pins: pitft_pins {
+                                brcm,pins = <25>;
+                                brcm,function = <1>; /* out */
+                                brcm,pull = <0>; /* none */
+                        };
+                };
+        };
+
+        fragment@2 {
+                target = <&spi0>;
+                __overlay__ {
+                        /* needed to avoid dtc warning */
+                        #address-cells = <1>;
+                        #size-cells = <0>;
+
+                        pitft: pitft@0{
+                                compatible = "ilitek,ili9340";
+                                reg = <0>;
+                                pinctrl-names = "default";
+                                pinctrl-0 = <&pitft_pins>;
+
+                                spi-max-frequency = <32000000>;
+                                rotate = <90>;
+                                fps = <25>;
+                                bgr;
+                                buswidth = <8>;
+                                dc-gpios = <&gpio 25 0>;
+                                debug = <0>;
+                        };
+
+                };
+        };
+
+        __overrides__ {
+                speed =   <&pitft>,"spi-max-frequency:0";
+                rotate =  <&pitft>,"rotate:0";
+                fps =     <&pitft>,"fps:0";
+                debug =   <&pitft>,"debug:0";
+        };
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/pitft28-capacitive-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/pitft28-capacitive-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/pitft28-capacitive-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/pitft28-capacitive-overlay.dts	2021-07-25 16:45:35.158807390 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/*
+ * Device Tree overlay for Adafruit PiTFT 2.8" capacitive touch screen
+ *
+ */
+
+/dts-v1/;
+/plugin/;
+
+/ {
+        compatible = "brcm,bcm2835";
+
+        fragment@0 {
+                target = <&spi0>;
+                __overlay__ {
+                        status = "okay";
+                };
+        };
+
+	fragment@1 {
+		target = <&spidev0>;
+		__overlay__ {
+			status = "disabled";
+		};
+	};
+
+        fragment@2 {
+                target = <&gpio>;
+                __overlay__ {
+                        pitft_pins: pitft_pins {
+                                brcm,pins = <24 25>;
+                                brcm,function = <0 1>; /* in out */
+                                brcm,pull = <2 0>; /* pullup none */
+                        };
+                };
+        };
+
+        fragment@3 {
+                target = <&spi0>;
+                __overlay__ {
+                        /* needed to avoid dtc warning */
+                        #address-cells = <1>;
+                        #size-cells = <0>;
+
+                        pitft: pitft@0{
+                                compatible = "ilitek,ili9340";
+                                reg = <0>;
+                                pinctrl-names = "default";
+                                pinctrl-0 = <&pitft_pins>;
+
+                                spi-max-frequency = <32000000>;
+                                rotate = <90>;
+                                fps = <25>;
+                                bgr;
+                                buswidth = <8>;
+                                dc-gpios = <&gpio 25 0>;
+                                debug = <0>;
+                        };
+                };
+        };
+
+        fragment@4 {
+                target = <&i2c1>;
+                __overlay__ {
+                        /* needed to avoid dtc warning */
+                        #address-cells = <1>;
+                        #size-cells = <0>;
+
+                        ft6236: ft6236@38 {
+                                compatible = "focaltech,ft6236";
+                                reg = <0x38>;
+
+                                interrupt-parent = <&gpio>;
+                                interrupts = <24 2>;
+                                touchscreen-size-x = <240>;
+                                touchscreen-size-y = <320>;
+                        };
+                };
+        };
+
+        __overrides__ {
+                speed =   <&pitft>,"spi-max-frequency:0";
+                rotate =  <&pitft>,"rotate:0";
+                fps =     <&pitft>,"fps:0";
+                debug =   <&pitft>,"debug:0";
+                touch-sizex = <&ft6236>,"touchscreen-size-x?";
+                touch-sizey = <&ft6236>,"touchscreen-size-y?";
+                touch-invx  = <&ft6236>,"touchscreen-inverted-x?";
+                touch-invy  = <&ft6236>,"touchscreen-inverted-y?";
+                touch-swapxy = <&ft6236>,"touchscreen-swapped-x-y?";
+        };
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/pitft28-resistive-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/pitft28-resistive-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/pitft28-resistive-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/pitft28-resistive-overlay.dts	2021-07-25 16:45:35.158807390 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/*
+ * Device Tree overlay for Adafruit PiTFT 2.8" resistive touch screen
+ *
+ */
+
+/dts-v1/;
+/plugin/;
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&spi0>;
+		__overlay__ {
+			status = "okay";
+		};
+	};
+
+	fragment@1 {
+		target = <&spidev0>;
+		__overlay__ {
+			status = "disabled";
+		};
+	};
+
+	fragment@2 {
+		target = <&spidev1>;
+		__overlay__ {
+			status = "disabled";
+		};
+	};
+
+	fragment@3 {
+		target = <&gpio>;
+		__overlay__ {
+			pitft_pins: pitft_pins {
+				brcm,pins = <24 25>;
+				brcm,function = <0 1>; /* in out */
+				brcm,pull = <2 0>; /* pullup none */
+			};
+		};
+	};
+
+	fragment@4 {
+		target = <&spi0>;
+		__overlay__ {
+			/* needed to avoid dtc warning */
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			pitft: pitft@0{
+				compatible = "ilitek,ili9340";
+				reg = <0>;
+				pinctrl-names = "default";
+				pinctrl-0 = <&pitft_pins>;
+
+				spi-max-frequency = <32000000>;
+				rotate = <90>;
+				fps = <25>;
+				bgr;
+				buswidth = <8>;
+				dc-gpios = <&gpio 25 0>;
+				debug = <0>;
+			};
+
+			pitft_ts@1 {
+				compatible = "st,stmpe610";
+				reg = <1>;
+
+				spi-max-frequency = <500000>;
+				irq-gpio = <&gpio 24 0x2>; /* IRQF_TRIGGER_FALLING */
+				interrupts = <24 2>; /* high-to-low edge triggered */
+				interrupt-parent = <&gpio>;
+				interrupt-controller;
+
+				stmpe_touchscreen {
+					compatible = "st,stmpe-ts";
+					st,sample-time = <4>;
+					st,mod-12b = <1>;
+					st,ref-sel = <0>;
+					st,adc-freq = <2>;
+					st,ave-ctrl = <3>;
+					st,touch-det-delay = <4>;
+					st,settling = <2>;
+					st,fraction-z = <7>;
+					st,i-drive = <0>;
+				};
+
+				stmpe_gpio: stmpe_gpio {
+					#gpio-cells = <2>;
+					compatible = "st,stmpe-gpio";
+					/*
+					 * only GPIO2 is wired/available
+					 * and it is wired to the backlight
+					 */
+					st,norequest-mask = <0x7b>;
+				};
+			};
+		};
+	};
+
+	fragment@5 {
+		target-path = "/soc";
+		__overlay__ {
+			backlight {
+				compatible = "gpio-backlight";
+				gpios = <&stmpe_gpio 2 0>;
+				default-on;
+			};
+		};
+	};
+
+	__overrides__ {
+		speed =   <&pitft>,"spi-max-frequency:0";
+		rotate =  <&pitft>,"rotate:0";
+		fps =     <&pitft>,"fps:0";
+		debug =   <&pitft>,"debug:0";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/pitft35-resistive-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/pitft35-resistive-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/pitft35-resistive-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/pitft35-resistive-overlay.dts	2021-07-25 16:45:35.158807390 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/*
+ * Device Tree overlay for Adafruit PiTFT 3.5" resistive touch screen
+ *
+ */
+
+/dts-v1/;
+/plugin/;
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&spi0>;
+		__overlay__ {
+			status = "okay";
+		};
+	};
+
+	fragment@1 {
+		target = <&spidev0>;
+		__overlay__ {
+			status = "disabled";
+		};
+	};
+
+	fragment@2 {
+		target = <&spidev1>;
+		__overlay__ {
+			status = "disabled";
+		};
+	};
+
+	fragment@3 {
+		target = <&gpio>;
+		__overlay__ {
+			pitft_pins: pitft_pins {
+				brcm,pins = <24 25>;
+				brcm,function = <0 1>; /* in out */
+				brcm,pull = <2 0>; /* pullup none */
+			};
+		};
+	};
+
+	fragment@4 {
+		target = <&spi0>;
+		__overlay__ {
+			/* needed to avoid dtc warning */
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			pitft: pitft@0{
+				compatible = "himax,hx8357d", "adafruit,yx350hv15";
+				reg = <0>;
+				pinctrl-names = "default";
+				pinctrl-0 = <&pitft_pins>;
+
+				spi-max-frequency = <32000000>;
+				rotate = <90>;
+				fps = <25>;
+				bgr;
+				buswidth = <8>;
+				dc-gpios = <&gpio 25 0>;
+				debug = <0>;
+			};
+
+			pitft_ts@1 {
+				compatible = "st,stmpe610";
+				reg = <1>;
+
+				spi-max-frequency = <500000>;
+				irq-gpio = <&gpio 24 0x2>; /* IRQF_TRIGGER_FALLING */
+				interrupts = <24 2>; /* high-to-low edge triggered */
+				interrupt-parent = <&gpio>;
+				interrupt-controller;
+
+				stmpe_touchscreen {
+					compatible = "st,stmpe-ts";
+					st,sample-time = <4>;
+					st,mod-12b = <1>;
+					st,ref-sel = <0>;
+					st,adc-freq = <2>;
+					st,ave-ctrl = <3>;
+					st,touch-det-delay = <4>;
+					st,settling = <2>;
+					st,fraction-z = <7>;
+					st,i-drive = <0>;
+				};
+
+				stmpe_gpio: stmpe_gpio {
+					#gpio-cells = <2>;
+					compatible = "st,stmpe-gpio";
+					/*
+					 * only GPIO2 is wired/available
+					 * and it is wired to the backlight
+					 */
+					st,norequest-mask = <0x7b>;
+				};
+			};
+		};
+	};
+
+	fragment@5 {
+		target-path = "/soc";
+		__overlay__ {
+			backlight {
+				compatible = "gpio-backlight";
+				gpios = <&stmpe_gpio 2 0>;
+				default-on;
+			};
+		};
+	};
+
+	__overrides__ {
+		speed =   <&pitft>,"spi-max-frequency:0";
+		rotate =  <&pitft>,"rotate:0";
+		fps =     <&pitft>,"fps:0";
+		debug =   <&pitft>,"debug:0";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/pps-gpio-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/pps-gpio-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/pps-gpio-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/pps-gpio-overlay.dts	2021-07-25 16:45:35.158807390 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/dts-v1/;
+/plugin/;
+
+/ {
+	compatible = "brcm,bcm2835";
+	fragment@0 {
+		target-path = "/";
+		__overlay__ {
+			pps: pps@12 {
+				compatible = "pps-gpio";
+				pinctrl-names = "default";
+				pinctrl-0 = <&pps_pins>;
+				gpios = <&gpio 18 0>;
+				status = "okay";
+			};
+		};
+	};
+
+	fragment@1 {
+		target = <&gpio>;
+		__overlay__ {
+			pps_pins: pps_pins@12 {
+				brcm,pins =     <18>;
+				brcm,function = <0>;    // in
+				brcm,pull =     <0>;    // off
+			};
+		};
+	};
+
+	__overrides__ {
+		gpiopin = <&pps>,"gpios:4",
+			  <&pps>,"reg:0",
+			  <&pps_pins>,"brcm,pins:0",
+			  <&pps_pins>,"reg:0";
+		assert_falling_edge = <&pps>,"assert-falling-edge?";
+		capture_clear = <&pps>,"capture-clear?";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/pwm-2chan-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/pwm-2chan-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/pwm-2chan-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/pwm-2chan-overlay.dts	2021-07-25 16:45:35.158807390 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/dts-v1/;
+/plugin/;
+
+/*
+This is the 2-channel overlay - only use it if you need both channels.
+
+Legal pin,function combinations for each channel:
+  PWM0: 12,4(Alt0) 18,2(Alt5) 40,4(Alt0)            52,5(Alt1)
+  PWM1: 13,4(Alt0) 19,2(Alt5) 41,4(Alt0) 45,4(Alt0) 53,5(Alt1)
+
+N.B.:
+  1) Pin 18 is the only one available on all platforms, and
+     it is the one used by the I2S audio interface.
+     Pins 12 and 13 might be better choices on an A+, B+ or Pi2.
+  2) The onboard analogue audio output uses both PWM channels.
+  3) So be careful mixing audio and PWM.
+*/
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&gpio>;
+		__overlay__ {
+			pwm_pins: pwm_pins {
+				brcm,pins = <18 19>;
+				brcm,function = <2 2>; /* Alt5 */
+			};
+		};
+	};
+
+	fragment@1 {
+		target = <&pwm>;
+		frag1: __overlay__ {
+			pinctrl-names = "default";
+			pinctrl-0 = <&pwm_pins>;
+			assigned-clock-rates = <100000000>;
+			status = "okay";
+		};
+	};
+
+	__overrides__ {
+		pin   = <&pwm_pins>,"brcm,pins:0";
+		pin2  = <&pwm_pins>,"brcm,pins:4";
+		func  = <&pwm_pins>,"brcm,function:0";
+		func2 = <&pwm_pins>,"brcm,function:4";
+		clock = <&frag1>,"assigned-clock-rates:0";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/pwm-ir-tx-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/pwm-ir-tx-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/pwm-ir-tx-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/pwm-ir-tx-overlay.dts	2021-07-25 16:45:35.158807390 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/dts-v1/;
+/plugin/;
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&gpio>;
+		__overlay__ {
+			pwm0_pins: pwm0_pins {
+				brcm,pins = <18>;
+				brcm,function = <2>; /* Alt5 */
+			};
+		};
+	};
+
+	fragment@1 {
+		target = <&pwm>;
+		__overlay__ {
+			pinctrl-names = "default";
+			pinctrl-0 = <&pwm0_pins>;
+			status = "okay";
+		};
+	};
+
+	fragment@2 {
+		target-path = "/";
+		__overlay__ {
+			pwm-ir-transmitter {
+				compatible = "pwm-ir-tx";
+				pwms = <&pwm 0 100>;
+			};
+		};
+	};
+
+	__overrides__ {
+		gpio_pin = <&pwm0_pins>, "brcm,pins:0";
+		func = <&pwm0_pins>,"brcm,function:0";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/pwm-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/pwm-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/pwm-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/pwm-overlay.dts	2021-07-25 16:45:35.158807390 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/dts-v1/;
+/plugin/;
+
+/*
+Legal pin,function combinations for each channel:
+  PWM0: 12,4(Alt0) 18,2(Alt5) 40,4(Alt0)            52,5(Alt1)
+  PWM1: 13,4(Alt0) 19,2(Alt5) 41,4(Alt0) 45,4(Alt0) 53,5(Alt1)
+
+N.B.:
+  1) Pin 18 is the only one available on all platforms, and
+     it is the one used by the I2S audio interface.
+     Pins 12 and 13 might be better choices on an A+, B+ or Pi2.
+  2) The onboard analogue audio output uses both PWM channels.
+  3) So be careful mixing audio and PWM.
+*/
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&gpio>;
+		__overlay__ {
+			pwm_pins: pwm_pins {
+				brcm,pins = <18>;
+				brcm,function = <2>; /* Alt5 */
+			};
+		};
+	};
+
+	fragment@1 {
+		target = <&pwm>;
+		frag1: __overlay__ {
+			pinctrl-names = "default";
+			pinctrl-0 = <&pwm_pins>;
+			assigned-clock-rates = <100000000>;
+			status = "okay";
+		};
+	};
+
+	__overrides__ {
+		pin   = <&pwm_pins>,"brcm,pins:0";
+		func  = <&pwm_pins>,"brcm,function:0";
+		clock = <&frag1>,"assigned-clock-rates:0";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/qca7000-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/qca7000-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/qca7000-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/qca7000-overlay.dts	2021-07-25 16:45:35.158807390 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// Overlay for the Qualcomm Atheros QCA7000 on I2SE's PLC Stamp micro EVK
+// Visit: https://www.i2se.com/product/plc-stamp-micro-evk for details
+
+/dts-v1/;
+/plugin/;
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&spidev0>;
+		__overlay__ {
+			status = "disabled";
+		};
+	};
+
+	fragment@1 {
+		target = <&spi0>;
+		__overlay__ {
+			/* needed to avoid dtc warning */
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			status = "okay";
+
+			eth1: qca7000@0 {
+				compatible = "qca,qca7000";
+				reg = <0>; /* CE0 */
+				pinctrl-names = "default";
+				pinctrl-0 = <&eth1_pins>;
+				interrupt-parent = <&gpio>;
+				interrupts = <23 0x1>; /* rising edge */
+				spi-max-frequency = <12000000>;
+				status = "okay";
+			};
+		};
+	};
+
+	fragment@2 {
+		target = <&gpio>;
+		__overlay__ {
+			eth1_pins: eth1_pins {
+				brcm,pins = <23>;
+				brcm,function = <0>; /* in */
+				brcm,pull = <0>; /* none */
+			};
+		};
+	};
+
+	__overrides__ {
+		int_pin = <&eth1>, "interrupts:0",
+		          <&eth1_pins>, "brcm,pins:0";
+		speed   = <&eth1>, "spi-max-frequency:0";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/README linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/README
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/README	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/README	2021-07-25 16:45:35.138807726 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+Introduction
+============
+
+This directory contains Device Tree overlays. Device Tree makes it possible
+to support many hardware configurations with a single kernel and without the
+need to explicitly load or blacklist kernel modules. Note that this isn't a
+"pure" Device Tree configuration (c.f. MACH_BCM2835) - some on-board devices
+are still configured by the board support code, but the intention is to
+eventually reach that goal.
+
+On Raspberry Pi, Device Tree usage is controlled from /boot/config.txt. By
+default, the Raspberry Pi kernel boots with device tree enabled. You can
+completely disable DT usage (for now) by adding:
+
+    device_tree=
+
+to your config.txt, which should cause your Pi to revert to the old way of
+doing things after a reboot.
+
+In /boot you will find a .dtb for each base platform. This describes the
+hardware that is part of the Raspberry Pi board. The loader (start.elf and its
+siblings) selects the .dtb file appropriate for the platform by name, and reads
+it into memory. At this point, all of the optional interfaces (i2c, i2s, spi)
+are disabled, but they can be enabled using Device Tree parameters:
+
+    dtparam=i2c=on,i2s=on,spi=on
+
+However, this shouldn't be necessary in many use cases because loading an
+overlay that requires one of those interfaces will cause it to be enabled
+automatically, and it is advisable to only enable interfaces if they are
+needed.
+
+Configuring additional, optional hardware is done using Device Tree overlays
+(see below).
+
+GPIO numbering uses the hardware pin numbering scheme (aka BCM scheme) and
+not the physical pin numbers.
+
+raspi-config
+============
+
+The Advanced Options section of the raspi-config utility can enable and disable
+Device Tree use, as well as toggling the I2C and SPI interfaces. Note that it
+is possible to both enable an interface and blacklist the driver, if for some
+reason you should want to defer the loading.
+
+Modules
+=======
+
+As well as describing the hardware, Device Tree also gives enough information
+to allow suitable driver modules to be located and loaded, with the corollary
+that unneeded modules are not loaded. As a result it should be possible to
+remove lines from /etc/modules, and /etc/modprobe.d/raspi-blacklist.conf can
+have its contents deleted (or commented out).
+
+Using Overlays
+==============
+
+Overlays are loaded using the "dtoverlay" config.txt setting. As an example,
+consider I2C Real Time Clock drivers. In the pre-DT world these would be loaded
+by writing a magic string comprising a device identifier and an I2C address to
+a special file in /sys/class/i2c-adapter, having first loaded the driver for
+the I2C interface and the RTC device - something like this:
+
+    modprobe i2c-bcm2835
+    modprobe rtc-ds1307
+    echo ds1307 0x68 > /sys/class/i2c-adapter/i2c-1/new_device
+
+With DT enabled, this becomes a line in config.txt:
+
+    dtoverlay=i2c-rtc,ds1307
+
+This causes the file /boot/overlays/i2c-rtc.dtbo to be loaded and a "node"
+describing the DS1307 I2C device to be added to the Device Tree for the Pi. By
+default it usees address 0x68, but this can be modified with an additional DT
+parameter:
+
+    dtoverlay=i2c-rtc,ds1307,addr=0x68
+
+Parameters usually have default values, although certain parameters are
+mandatory. See the list of overlays below for a description of the parameters
+and their defaults.
+
+Making new Overlays based on existing Overlays
+==============================================
+
+Recent overlays have been designed in a more general way, so that they can be
+adapted to hardware by changing their parameters. When you have additional
+hardware with more than one device of a kind, you end up using the same overlay
+multiple times with other parameters, e.g.
+
+    # 2 CAN FD interfaces on spi but with different pins
+    dtoverlay=mcp251xfd,spi0-0,interrupt=25
+    dtoverlay=mcp251xfd,spi0-1,interrupt=24
+
+    # a realtime clock on i2c
+    dtoverlay=i2c-rtc,pcf85063
+
+While this approach does work, it requires knowledge about the hardware design.
+It is more feasible to simplify things for the end user by providing a single
+overlay as it is done the traditional way.
+
+A new overlay can be generated by using ovmerge utility.
+https://github.com/raspberrypi/utils/blob/master/ovmerge/ovmerge
+
+To generate an overlay for the above configuration we pass the configuration
+to ovmerge and add the -c flag.
+
+    ovmerge -c mcp251xfd-overlay.dts,spi0-0,interrupt=25 \
+               mcp251xfd-overlay.dts,spi0-1,interrupt=24 \
+               i2c-rtc-overlay.dts,pcf85063 \
+    >> merged-overlay.dts
+
+The -c option writes the command above as a comment into the overlay as
+a marker that this overlay is generated and how it was generated.
+After compiling the overlay it can be loaded in a single line.
+
+    dtoverlay=merged
+
+It does the same as the original configuration but without parameters.
+
+The Overlay and Parameter Reference
+===================================
+
+N.B. When editing this file, please preserve the indentation levels to make it
+simple to parse programmatically. NO HARD TABS.
+
+
+Name:   <The base DTB>
+Info:   Configures the base Raspberry Pi hardware
+Load:   <loaded automatically>
+Params:
+        ant1                    Select antenna 1 (default). CM4 only.
+
+        ant2                    Select antenna 2. CM4 only.
+
+        noant                   Disable both antennas. CM4 only.
+
+        audio                   Set to "on" to enable the onboard ALSA audio
+                                interface (default "off")
+
+        axiperf                 Set to "on" to enable the AXI bus performance
+                                monitors.
+                                See /sys/kernel/debug/raspberrypi_axi_monitor
+                                for the results.
+
+        eee                     Enable Energy Efficient Ethernet support for
+                                compatible devices (default "on"). See also
+                                "tx_lpi_timer". Pi3B+ only.
+
+        eth_downshift_after     Set the number of auto-negotiation failures
+                                after which the 1000Mbps modes are disabled.
+                                Legal values are 2, 3, 4, 5 and 0, where
+                                0 means never downshift (default 2). Pi3B+ only.
+
+        eth_led0                Set mode of LED0 - amber on Pi3B+ (default "1"),
+                                green on Pi4 (default "0").
+                                The legal values are:
+
+                                Pi3B+
+
+                                0=link/activity          1=link1000/activity
+                                2=link100/activity       3=link10/activity
+                                4=link100/1000/activity  5=link10/1000/activity
+                                6=link10/100/activity    14=off    15=on
+
+                                Pi4
+
+                                0=Speed/Activity         1=Speed
+                                2=Flash activity         3=FDX
+                                4=Off                    5=On
+                                6=Alt                    7=Speed/Flash
+                                8=Link                   9=Activity
+
+        eth_led1                Set mode of LED1 - green on Pi3B+ (default "6"),
+                                amber on Pi4 (default "8"). See eth_led0 for
+                                legal values.
+
+        eth_max_speed           Set the maximum speed a link is allowed
+                                to negotiate. Legal values are 10, 100 and
+                                1000 (default 1000). Pi3B+ only.
+
+        i2c_arm                 Set to "on" to enable the ARM's i2c interface
+                                (default "off")
+
+        i2c_vc                  Set to "on" to enable the i2c interface
+                                usually reserved for the VideoCore processor
+                                (default "off")
+
+        i2c                     An alias for i2c_arm
+
+        i2c_arm_baudrate        Set the baudrate of the ARM's i2c interface
+                                (default "100000")
+
+        i2c_vc_baudrate         Set the baudrate of the VideoCore i2c interface
+                                (default "100000")
+
+        i2c_baudrate            An alias for i2c_arm_baudrate
+
+        i2s                     Set to "on" to enable the i2s interface
+                                (default "off")
+
+        krnbt                   Set to "on" to enable autoprobing of Bluetooth
+                                driver without need of hciattach/btattach
+                                (default "off")
+
+        krnbt_baudrate          Set the baudrate of the PL011 UART when used
+                                with krnbt=on
+
+        spi                     Set to "on" to enable the spi interfaces
+                                (default "off")
+
+        spi_dma4                Use to enable 40-bit DMA on spi interfaces
+                                (the assigned value doesn't matter)
+                                (2711 only)
+
+        random                  Set to "on" to enable the hardware random
+                                number generator (default "on")
+
+        sd_overclock            Clock (in MHz) to use when the MMC framework
+                                requests 50MHz
+
+        sd_poll_once            Looks for a card once after booting. Useful
+                                for network booting scenarios to avoid the
+                                overhead of continuous polling. N.B. Using
+                                this option restricts the system to using a
+                                single card per boot (or none at all).
+                                (default off)
+
+        sd_force_pio            Disable DMA support for SD driver (default off)
+
+        sd_pio_limit            Number of blocks above which to use DMA for
+                                SD card (default 1)
+
+        sd_debug                Enable debug output from SD driver (default off)
+
+        sdio_overclock          Clock (in MHz) to use when the MMC framework
+                                requests 50MHz for the SDIO/WiFi interface.
+
+        tx_lpi_timer            Set the delay in microseconds between going idle
+                                and entering the low power state (default 600).
+                                Requires EEE to be enabled - see "eee".
+
+        uart0                   Set to "off" to disable uart0 (default "on")
+
+        uart1                   Set to "on" or "off" to enable or disable uart1
+                                (default varies)
+
+        watchdog                Set to "on" to enable the hardware watchdog
+                                (default "off")
+
+        act_led_trigger         Choose which activity the LED tracks.
+                                Use "heartbeat" for a nice load indicator.
+                                (default "mmc")
+
+        act_led_activelow       Set to "on" to invert the sense of the LED
+                                (default "off")
+                                N.B. For Pi 3B, 3B+, 3A+ and 4B, use the act-led
+                                overlay.
+
+        act_led_gpio            Set which GPIO to use for the activity LED
+                                (in case you want to connect it to an external
+                                device)
+                                (default "16" on a non-Plus board, "47" on a
+                                Plus or Pi 2)
+                                N.B. For Pi 3B, 3B+, 3A+ and 4B, use the act-led
+                                overlay.
+
+        pwr_led_trigger
+        pwr_led_activelow
+        pwr_led_gpio
+                                As for act_led_*, but using the PWR LED.
+                                Not available on Model A/B boards.
+
+        N.B. It is recommended to only enable those interfaces that are needed.
+        Leaving all interfaces enabled can lead to unwanted behaviour (i2c_vc
+        interfering with Pi Camera, I2S and SPI hogging GPIO pins, etc.)
+        Note also that i2c, i2c_arm and i2c_vc are aliases for the physical
+        interfaces i2c0 and i2c1. Use of the numeric variants is still possible
+        but deprecated because the ARM/VC assignments differ between board
+        revisions. The same board-specific mapping applies to i2c_baudrate,
+        and the other i2c baudrate parameters.
+
+
+Name:   act-led
+Info:   Pi 3B, 3B+, 3A+ and 4B use a GPIO expander to drive the LEDs which can
+        only be accessed from the VPU. There is a special driver for this with a
+        separate DT node, which has the unfortunate consequence of breaking the
+        act_led_gpio and act_led_activelow dtparams.
+        This overlay changes the GPIO controller back to the standard one and
+        restores the dtparams.
+Load:   dtoverlay=act-led,<param>=<val>
+Params: activelow               Set to "on" to invert the sense of the LED
+                                (default "off")
+
+        gpio                    Set which GPIO to use for the activity LED
+                                (in case you want to connect it to an external
+                                device)
+                                REQUIRED
+
+
+Name:   adafruit18
+Info:   Overlay for the SPI-connected Adafruit 1.8" display (based on the
+        ST7735R chip). It includes support for the "green tab" version.
+Load:   dtoverlay=adafruit18,<param>=<val>
+Params: green                   Use the adafruit18_green variant.
+        rotate                  Display rotation {0,90,180,270}
+        speed                   SPI bus speed in Hz (default 4000000)
+        fps                     Display frame rate in Hz
+        bgr                     Enable BGR mode (default off)
+        debug                   Debug output level {0-7}
+        dc_pin                  GPIO pin for D/C (default 24)
+        reset_pin               GPIO pin for RESET (default 25)
+        led_pin                 GPIO used to control backlight (default 18)
+
+
+Name:   adau1977-adc
+Info:   Overlay for activation of ADAU1977 ADC codec over I2C for control
+        and I2S for data.
+Load:   dtoverlay=adau1977-adc
+Params: <None>
+
+
+Name:   adau7002-simple
+Info:   Overlay for the activation of ADAU7002 stereo PDM to I2S converter.
+Load:   dtoverlay=adau7002-simple,<param>=<val>
+Params: card-name               Override the default, "adau7002", card name.
+
+
+Name:   ads1015
+Info:   Overlay for activation of Texas Instruments ADS1015 ADC over I2C
+Load:   dtoverlay=ads1015,<param>=<val>
+Params: addr                    I2C bus address of device. Set based on how the
+                                addr pin is wired. (default=0x48 assumes addr
+                                is pulled to GND)
+        cha_enable              Enable virtual channel a. (default=true)
+        cha_cfg                 Set the configuration for virtual channel a.
+                                (default=4 configures this channel for the
+                                voltage at A0 with respect to GND)
+        cha_datarate            Set the datarate (samples/sec) for this channel.
+                                (default=4 sets 1600 sps)
+        cha_gain                Set the gain of the Programmable Gain
+                                Amplifier for this channel. (default=2 sets the
+                                full scale of the channel to 2.048 Volts)
+
+        Channel (ch) parameters can be set for each enabled channel.
+        A maximum of 4 channels can be enabled (letters a thru d).
+        For more information refer to the device datasheet at:
+        http://www.ti.com/lit/ds/symlink/ads1015.pdf
+
+
+Name:   ads1115
+Info:   Texas Instruments ADS1115 ADC
+Load:   dtoverlay=ads1115,<param>[=<val>]
+Params: addr                    I2C bus address of device. Set based on how the
+                                addr pin is wired. (default=0x48 assumes addr
+                                is pulled to GND)
+        cha_enable              Enable virtual channel a.
+        cha_cfg                 Set the configuration for virtual channel a.
+                                (default=4 configures this channel for the
+                                voltage at A0 with respect to GND)
+        cha_datarate            Set the datarate (samples/sec) for this channel.
+                                (default=7 sets 860 sps)
+        cha_gain                Set the gain of the Programmable Gain
+                                Amplifier for this channel. (Default 1 sets the
+                                full scale of the channel to 4.096 Volts)
+
+        Channel parameters can be set for each enabled channel.
+        A maximum of 4 channels can be enabled (letters a thru d).
+        For more information refer to the device datasheet at:
+        http://www.ti.com/lit/ds/symlink/ads1115.pdf
+
+
+Name:   ads7846
+Info:   ADS7846 Touch controller
+Load:   dtoverlay=ads7846,<param>=<val>
+Params: cs                      SPI bus Chip Select (default 1)
+        speed                   SPI bus speed (default 2MHz, max 3.25MHz)
+        penirq                  GPIO used for PENIRQ. REQUIRED
+        penirq_pull             Set GPIO pull (default 0=none, 2=pullup)
+        swapxy                  Swap x and y axis
+        xmin                    Minimum value on the X axis (default 0)
+        ymin                    Minimum value on the Y axis (default 0)
+        xmax                    Maximum value on the X axis (default 4095)
+        ymax                    Maximum value on the Y axis (default 4095)
+        pmin                    Minimum reported pressure value (default 0)
+        pmax                    Maximum reported pressure value (default 65535)
+        xohms                   Touchpanel sensitivity (X-plate resistance)
+                                (default 400)
+
+        penirq is required and usually xohms (60-100) has to be set as well.
+        Apart from that, pmax (255) and swapxy are also common.
+        The rest of the calibration can be done with xinput-calibrator.
+        See: github.com/notro/fbtft/wiki/FBTFT-on-Raspian
+        Device Tree binding document:
+        www.kernel.org/doc/Documentation/devicetree/bindings/input/ads7846.txt
+
+
+Name:   adv7282m
+Info:   Analog Devices ADV7282M analogue video to CSI2 bridge.
+        Uses Unicam1, which is the standard camera connector on most Pi
+        variants.
+Load:   dtoverlay=adv7282m,<param>=<val>
+Params: addr                    Overrides the I2C address (default 0x21)
+
+
+Name:   adv728x-m
+Info:   Analog Devices ADV728[0|1|2]-M analogue video to CSI2 bridges.
+        This is a wrapper for adv7282m, and defaults to ADV7282M.
+Load:   dtoverlay=adv728x-m,<param>=<val>
+Params: addr                    Overrides the I2C address (default 0x21)
+        adv7280m                Select ADV7280-M.
+        adv7281m                Select ADV7281-M.
+        adv7281ma               Select ADV7281-MA.
+
+
+Name:   akkordion-iqdacplus
+Info:   Configures the Digital Dreamtime Akkordion Music Player (based on the
+        OEM IQAudIO DAC+ or DAC Zero module).
+Load:   dtoverlay=akkordion-iqdacplus,<param>=<val>
+Params: 24db_digital_gain       Allow gain to be applied via the PCM512x codec
+                                Digital volume control. Enable with
+                                dtoverlay=akkordion-iqdacplus,24db_digital_gain
+                                (The default behaviour is that the Digital
+                                volume control is limited to a maximum of
+                                0dB. ie. it can attenuate but not provide
+                                gain. For most users, this will be desired
+                                as it will prevent clipping. By appending
+                                the 24db_digital_gain parameter, the Digital
+                                volume control will allow up to 24dB of
+                                gain. If this parameter is enabled, it is the
+                                responsibility of the user to ensure that
+                                the Digital volume control is set to a value
+                                that does not result in clipping/distortion!)
+
+
+Name:   allo-boss-dac-pcm512x-audio
+Info:   Configures the Allo Boss DAC audio cards.
+Load:   dtoverlay=allo-boss-dac-pcm512x-audio,<param>
+Params: 24db_digital_gain       Allow gain to be applied via the PCM512x codec
+                                Digital volume control. Enable with
+                                "dtoverlay=allo-boss-dac-pcm512x-audio,
+                                24db_digital_gain"
+                                (The default behaviour is that the Digital
+                                volume control is limited to a maximum of
+                                0dB. ie. it can attenuate but not provide
+                                gain. For most users, this will be desired
+                                as it will prevent clipping. By appending
+                                the 24db_digital_gain parameter, the Digital
+                                volume control will allow up to 24dB of
+                                gain. If this parameter is enabled, it is the
+                                responsibility of the user to ensure that
+                                the Digital volume control is set to a value
+                                that does not result in clipping/distortion!)
+        slave                   Force Boss DAC into slave mode, using Pi a
+                                master for bit clock and frame clock. Enable
+                                with "dtoverlay=allo-boss-dac-pcm512x-audio,
+                                slave"
+
+
+Name:   allo-boss2-dac-audio
+Info:   Configures the Allo Boss2 DAC audio card
+Load:   dtoverlay=allo-boss2-dac-audio
+Params: <None>
+
+
+Name:   allo-digione
+Info:   Configures the Allo Digione audio card
+Load:   dtoverlay=allo-digione
+Params: <None>
+
+
+Name:   allo-katana-dac-audio
+Info:   Configures the Allo Katana DAC audio card
+Load:   dtoverlay=allo-katana-dac-audio
+Params: <None>
+
+
+Name:   allo-piano-dac-pcm512x-audio
+Info:   Configures the Allo Piano DAC (2.0/2.1) audio cards.
+        (NB. This initial support is for 2.0 channel audio ONLY! ie. stereo.
+        The subwoofer outputs on the Piano 2.1 are not currently supported!)
+Load:   dtoverlay=allo-piano-dac-pcm512x-audio,<param>
+Params: 24db_digital_gain       Allow gain to be applied via the PCM512x codec
+                                Digital volume control.
+                                (The default behaviour is that the Digital
+                                volume control is limited to a maximum of
+                                0dB. ie. it can attenuate but not provide
+                                gain. For most users, this will be desired
+                                as it will prevent clipping. By appending
+                                the 24db_digital_gain parameter, the Digital
+                                volume control will allow up to 24dB of
+                                gain. If this parameter is enabled, it is the
+                                responsibility of the user to ensure that
+                                the Digital volume control is set to a value
+                                that does not result in clipping/distortion!)
+
+
+Name:   allo-piano-dac-plus-pcm512x-audio
+Info:   Configures the Allo Piano DAC (2.1) audio cards.
+Load:   dtoverlay=allo-piano-dac-plus-pcm512x-audio,<param>
+Params: 24db_digital_gain       Allow gain to be applied via the PCM512x codec
+                                Digital volume control.
+                                (The default behaviour is that the Digital
+                                volume control is limited to a maximum of
+                                0dB. ie. it can attenuate but not provide
+                                gain. For most users, this will be desired
+                                as it will prevent clipping. By appending
+                                the 24db_digital_gain parameter, the Digital
+                                volume control will allow up to 24dB of
+                                gain. If this parameter is enabled, it is the
+                                responsibility of the user to ensure that
+                                the Digital volume control is set to a value
+                                that does not result in clipping/distortion!)
+        glb_mclk                This option is only with Kali board. If enabled,
+                                MCLK for Kali is used and PLL is disabled for
+                                better voice quality. (default Off)
+
+
+Name:   anyspi
+Info:   Universal device tree overlay for SPI devices
+
+        Just specify the SPI address and device name ("compatible" property).
+        This overlay lacks any device-specific parameter support!
+
+        For devices on spi1 or spi2, the interfaces should be enabled
+        with one of the spi1-1/2/3cs and/or spi2-1/2/3cs overlays.
+
+        Examples:
+        1. SPI NOR flash on spi0.1, maximum SPI clock frequency 45MHz:
+            dtoverlay=anyspi:spi0-1,dev="jedec,spi-nor",speed=45000000
+        2. MCP3204 ADC on spi1.2, maximum SPI clock frequency 500kHz:
+            dtoverlay=anyspi:spi1-2,dev="microchip,mcp3204"
+Load:   dtoverlay=anyspi,<param>=<val>
+Params: spi<n>-<m>              Configure device at spi<n>, cs<m>
+                                (boolean, required)
+        dev                     Set device name to search compatible module
+                                (string, required)
+        speed                   Set SPI clock frequency in Hz
+                                (integer, optional, default 500000)
+
+
+Name:   apds9960
+Info:   Configures the AVAGO APDS9960 digital proximity, ambient light, RGB and
+        gesture sensor
+Load:   dtoverlay=apds9960,<param>=<val>
+Params: gpiopin                 GPIO used for INT (default 4)
+        noints                  Disable the interrupt GPIO line.
+
+
+Name:   applepi-dac
+Info:   Configures the Orchard Audio ApplePi-DAC audio card
+Load:   dtoverlay=applepi-dac
+Params: <None>
+
+
+Name:   at86rf233
+Info:   Configures the Atmel AT86RF233 802.15.4 low-power WPAN transceiver,
+        connected to spi0.0
+Load:   dtoverlay=at86rf233,<param>=<val>
+Params: interrupt               GPIO used for INT (default 23)
+        reset                   GPIO used for Reset (default 24)
+        sleep                   GPIO used for Sleep (default 25)
+        speed                   SPI bus speed in Hz (default 3000000)
+        trim                    Fine tuning of the internal capacitance
+                                arrays (0=+0pF, 15=+4.5pF, default 15)
+
+
+Name:   audioinjector-addons
+Info:   Configures the audioinjector.net audio add on soundcards
+Load:   dtoverlay=audioinjector-addons,<param>=<val>
+Params: non-stop-clocks         Keeps the clocks running even when the stream
+                                is paused or stopped (default off)
+
+
+Name:   audioinjector-isolated-soundcard
+Info:   Configures the audioinjector.net isolated soundcard
+Load:   dtoverlay=audioinjector-isolated-soundcard
+Params: <None>
+
+
+Name:   audioinjector-ultra
+Info:   Configures the audioinjector.net ultra soundcard
+Load:   dtoverlay=audioinjector-ultra
+Params: <None>
+
+
+Name:   audioinjector-wm8731-audio
+Info:   Configures the audioinjector.net audio add on soundcard
+Load:   dtoverlay=audioinjector-wm8731-audio
+Params: <None>
+
+
+Name:   audiosense-pi
+Info:   Configures the audiosense-pi add on soundcard
+        For more information refer to
+        https://gitlab.com/kakar0t/audiosense-pi
+Load:   dtoverlay=audiosense-pi
+Params: <None>
+
+
+Name:   audremap
+Info:   Switches PWM sound output to GPIOs on the 40-pin header
+Load:   dtoverlay=audremap,<param>=<val>
+Params: swap_lr                 Reverse the channel allocation, which will also
+                                swap the audio jack outputs (default off)
+        enable_jack             Don't switch off the audio jack output
+                                (default off)
+        pins_12_13              Select GPIOs 12 & 13 (default)
+        pins_18_19              Select GPIOs 18 & 19
+
+
+Name:   balena-fin
+Info:   Overlay that enables WiFi, Bluetooth and the GPIO expander on the
+        balenaFin carrier board for the Raspberry Pi Compute Module 3/3+ Lite.
+Load:   dtoverlay=balena-fin
+Params: <None>
+
+
+Name:   bmp085_i2c-sensor
+Info:   This overlay is now deprecated - see i2c-sensor
+Load:   <Deprecated>
+
+
+Name:   cap1106
+Info:   Enables the ability to use the cap1106 touch sensor as a keyboard
+Load:   dtoverlay=cap1106,<param>=<val>
+Params: int_pin                 GPIO pin for interrupt signal (default 23)
+
+
+Name:   chipdip-i2s-master-dac
+Info:   Configures Raspberry PI to work as I2S slave with BCLK=64Fs.
+Load:   dtoverlay=chipdip-i2s-master-dac
+Params: <None>
+
+
+Name:   cma
+Info:   Set custom CMA sizes, only use if you know what you are doing, might
+        clash with other overlays like vc4-fkms-v3d and vc4-kms-v3d.
+Load:   dtoverlay=cma,<param>=<val>
+Params: cma-512                 CMA is 512MB (needs 1GB)
+        cma-448                 CMA is 448MB (needs 1GB)
+        cma-384                 CMA is 384MB (needs 1GB)
+        cma-320                 CMA is 320MB (needs 1GB)
+        cma-256                 CMA is 256MB (needs 1GB)
+        cma-192                 CMA is 192MB (needs 1GB)
+        cma-128                 CMA is 128MB
+        cma-96                  CMA is 96MB
+        cma-64                  CMA is 64MB
+        cma-size                CMA size in bytes, 4MB aligned
+        cma-default             Use upstream's default value
+
+
+Name:   dht11
+Info:   Overlay for the DHT11/DHT21/DHT22 humidity/temperature sensors
+        Also sometimes found with the part number(s) AM230x.
+Load:   dtoverlay=dht11,<param>=<val>
+Params: gpiopin                 GPIO connected to the sensor's DATA output.
+                                (default 4)
+
+
+Name:   dionaudio-loco
+Info:   Configures the Dion Audio LOCO DAC-AMP
+Load:   dtoverlay=dionaudio-loco
+Params: <None>
+
+
+Name:   dionaudio-loco-v2
+Info:   Configures the Dion Audio LOCO-V2 DAC-AMP
+Load:   dtoverlay=dionaudio-loco-v2,<param>=<val>
+Params: 24db_digital_gain       Allow gain to be applied via the PCM512x codec
+                                Digital volume control. Enable with
+                                "dtoverlay=hifiberry-dacplus,24db_digital_gain"
+                                (The default behaviour is that the Digital
+                                volume control is limited to a maximum of
+                                0dB. ie. it can attenuate but not provide
+                                gain. For most users, this will be desired
+                                as it will prevent clipping. By appending
+                                the 24dB_digital_gain parameter, the Digital
+                                volume control will allow up to 24dB of
+                                gain. If this parameter is enabled, it is the
+                                responsibility of the user to ensure that
+                                the Digital volume control is set to a value
+                                that does not result in clipping/distortion!)
+
+
+Name:   disable-bt
+Info:   Disable onboard Bluetooth on Pi 3B, 3B+, 3A+, 4B and Zero W, restoring
+        UART0/ttyAMA0 over GPIOs 14 & 15.
+        N.B. To disable the systemd service that initialises the modem so it
+        doesn't use the UART, use 'sudo systemctl disable hciuart'.
+Load:   dtoverlay=disable-bt
+Params: <None>
+
+
+Name:   disable-wifi
+Info:   Disable onboard WiFi on Pi 3B, 3B+, 3A+, 4B and Zero W.
+Load:   dtoverlay=disable-wifi
+Params: <None>
+
+
+Name:   dpi18
+Info:   Overlay for a generic 18-bit DPI display
+        This uses GPIOs 0-21 (so no I2C, uart etc.), and activates the output
+        2-3 seconds after the kernel has started.
+Load:   dtoverlay=dpi18
+Params: <None>
+
+
+Name:   dpi18cpadhi
+Info:   Overlay for a generic 18-bit DPI display (in 'mode 6' connection scheme)
+        This uses GPIOs 0-9,12-17,20-25 (so no I2C, uart etc.), and activates
+        the output 3-3 seconds after the kernel has started.
+Load:   dtoverlay=dpi18cpadhi
+Params: <None>
+
+
+Name:   dpi24
+Info:   Overlay for a generic 24-bit DPI display
+        This uses GPIOs 0-27 (so no I2C, uart etc.), and activates the output
+        2-3 seconds after the kernel has started.
+Load:   dtoverlay=dpi24
+Params: <None>
+
+
+Name:   draws
+Info:   Configures the NW Digital Radio DRAWS Hat
+
+        The board includes an ADC to measure various board values and also
+        provides two analog user inputs on the expansion header.  The ADC
+        can be configured for various sample rates and gain values to adjust
+        the input range.  Tables describing the two parameters follow.
+
+        ADC Gain Values:
+            0 = +/- 6.144V
+            1 = +/- 4.096V
+            2 = +/- 2.048V
+            3 = +/- 1.024V
+            4 = +/- 0.512V
+            5 = +/- 0.256V
+            6 = +/- 0.256V
+            7 = +/- 0.256V
+
+        ADC Datarate Values:
+            0 = 128sps
+            1 = 250sps
+            2 = 490sps
+            3 = 920sps
+            4 = 1600sps (default)
+            5 = 2400sps
+            6 = 3300sps
+            7 = 3300sps
+Load:   dtoverlay=draws,<param>=<val>
+Params: draws_adc_ch4_gain      Sets the full scale resolution of the ADCs
+                                input voltage sensor (default 1)
+
+        draws_adc_ch4_datarate  Sets the datarate of the ADCs input voltage
+                                sensor
+
+        draws_adc_ch5_gain      Sets the full scale resolution of the ADCs
+                                5V rail voltage sensor (default 1)
+
+        draws_adc_ch5_datarate  Sets the datarate of the ADCs 4V rail voltage
+                                sensor
+
+        draws_adc_ch6_gain      Sets the full scale resolution of the ADCs
+                                AIN2 input (default 2)
+
+        draws_adc_ch6_datarate  Sets the datarate of the ADCs AIN2 input
+
+        draws_adc_ch7_gain      Sets the full scale resolution of the ADCs
+                                AIN3 input (default 2)
+
+        draws_adc_ch7_datarate  Sets the datarate of the ADCs AIN3 input
+
+        alsaname                Name of the ALSA audio device (default "draws")
+
+
+Name:   dwc-otg
+Info:   Selects the dwc_otg USB controller driver which has fiq support. This
+        is the default on all except the Pi Zero which defaults to dwc2.
+Load:   dtoverlay=dwc-otg
+Params: <None>
+
+
+Name:   dwc2
+Info:   Selects the dwc2 USB controller driver
+Load:   dtoverlay=dwc2,<param>=<val>
+Params: dr_mode                 Dual role mode: "host", "peripheral" or "otg"
+
+        g-rx-fifo-size          Size of rx fifo size in gadget mode
+
+        g-np-tx-fifo-size       Size of non-periodic tx fifo size in gadget
+                                mode
+
+
+[ The ds1307-rtc overlay has been deleted. See i2c-rtc. ]
+
+
+Name:   edt-ft5406
+Info:   Overlay for the EDT FT5406 touchscreen on the CSI/DSI I2C interface.
+        This works with the Raspberry Pi 7" touchscreen when not being polled
+        by the firmware.
+        You MUST use either "disable_touchscreen=1" or "ignore_lcd=1" in
+        config.txt to stop the firmware polling the touchscreen.
+Load:   dtoverlay=edt-ft5406,<param>=<val>
+Params: sizex                   Touchscreen size x (default 800)
+        sizey                   Touchscreen size y (default 480)
+        invx                    Touchscreen inverted x axis
+        invy                    Touchscreen inverted y axis
+        swapxy                  Touchscreen swapped x y axis
+
+
+Name:   enc28j60
+Info:   Overlay for the Microchip ENC28J60 Ethernet Controller on SPI0
+Load:   dtoverlay=enc28j60,<param>=<val>
+Params: int_pin                 GPIO used for INT (default 25)
+
+        speed                   SPI bus speed (default 12000000)
+
+
+Name:   enc28j60-spi2
+Info:   Overlay for the Microchip ENC28J60 Ethernet Controller on SPI2
+Load:   dtoverlay=enc28j60-spi2,<param>=<val>
+Params: int_pin                 GPIO used for INT (default 39)
+
+        speed                   SPI bus speed (default 12000000)
+
+
+Name:   exc3000
+Info:   Enables I2C connected EETI EXC3000 multiple touch controller using
+        GPIO 4 (pin 7 on GPIO header) for interrupt.
+Load:   dtoverlay=exc3000,<param>=<val>
+Params: interrupt               GPIO used for interrupt (default 4)
+        sizex                   Touchscreen size x (default 4096)
+        sizey                   Touchscreen size y (default 4096)
+        invx                    Touchscreen inverted x axis
+        invy                    Touchscreen inverted y axis
+        swapxy                  Touchscreen swapped x y axis
+
+
+Name:   fe-pi-audio
+Info:   Configures the Fe-Pi Audio Sound Card
+Load:   dtoverlay=fe-pi-audio
+Params: <None>
+
+
+Name:   fsm-demo
+Info:   A demonstration of the gpio-fsm driver. The GPIOs are chosen to work
+        nicely with a "traffic-light" display of red, amber and green LEDs on
+        GPIOs 7, 8 and 25 respectively.
+Load:   dtoverlay=fsm-demo,<param>=<val>
+Params: fsm_debug               Enable debug logging (default off)
+
+
+Name:   ghost-amp
+Info:   An overlay for the Ghost amplifier.
+Load:   dtoverlay=ghost-amp,<param>=<val>
+Params: fsm_debug               Enable debug logging of the GPIO FSM (default
+                                off)
+
+
+Name:   goodix
+Info:   Enables I2C connected Goodix gt9271 multiple touch controller using
+        GPIOs 4 and 17 (pins 7 and 11 on GPIO header) for interrupt and reset.
+Load:   dtoverlay=goodix,<param>=<val>
+Params: interrupt               GPIO used for interrupt (default 4)
+        reset                   GPIO used for reset (default 17)
+
+
+Name:   googlevoicehat-soundcard
+Info:   Configures the Google voiceHAT soundcard
+Load:   dtoverlay=googlevoicehat-soundcard
+Params: <None>
+
+
+Name:   gpio-fan
+Info:   Configure a GPIO pin to control a cooling fan.
+Load:   dtoverlay=gpio-fan,<param>=<val>
+Params: gpiopin                 GPIO used to control the fan (default 12)
+        temp                    Temperature at which the fan switches on, in
+                                millicelcius (default 55000)
+
+
+Name:   gpio-ir
+Info:   Use GPIO pin as rc-core style infrared receiver input. The rc-core-
+        based gpio_ir_recv driver maps received keys directly to a
+        /dev/input/event* device, all decoding is done by the kernel - LIRC is
+        not required! The key mapping and other decoding parameters can be
+        configured by "ir-keytable" tool.
+Load:   dtoverlay=gpio-ir,<param>=<val>
+Params: gpio_pin                Input pin number. Default is 18.
+
+        gpio_pull               Desired pull-up/down state (off, down, up)
+                                Default is "up".
+
+        invert                  "1" = invert the input (active-low signalling).
+                                "0" = non-inverted input (active-high
+                                signalling). Default is "1".
+
+        rc-map-name             Default rc keymap (can also be changed by
+                                ir-keytable), defaults to "rc-rc6-mce"
+
+
+Name:   gpio-ir-tx
+Info:   Use GPIO pin as bit-banged infrared transmitter output.
+        This is an alternative to "pwm-ir-tx". gpio-ir-tx doesn't require
+        a PWM so it can be used together with onboard analog audio.
+Load:   dtoverlay=gpio-ir-tx,<param>=<val>
+Params: gpio_pin                Output GPIO (default 18)
+
+        invert                  "1" = invert the output (make it active-low).
+                                Default is "0" (active-high).
+
+
+Name:   gpio-key
+Info:   This is a generic overlay for activating GPIO keypresses using
+        the gpio-keys library and this dtoverlay. Multiple keys can be
+        set up using multiple calls to the overlay for configuring
+        additional buttons or joysticks. You can see available keycodes
+        at https://github.com/torvalds/linux/blob/v4.12/include/uapi/
+        linux/input-event-codes.h#L64
+Load:   dtoverlay=gpio-key,<param>=<val>
+Params: gpio                    GPIO pin to trigger on (default 3)
+        active_low              When this is 1 (active low), a falling
+                                edge generates a key down event and a
+                                rising edge generates a key up event.
+                                When this is 0 (active high), this is
+                                reversed. The default is 1 (active low)
+        gpio_pull               Desired pull-up/down state (off, down, up)
+                                Default is "up". Note that the default pin
+                                (GPIO3) has an external pullup
+        label                   Set a label for the key
+        keycode                 Set the key code for the button
+
+
+
+Name:   gpio-led
+Info:   This is a generic overlay for activating LEDs (or any other component)
+        by a GPIO pin. Multiple LEDs can be set up using multiple calls to the
+        overlay. While there are many existing methods to activate LEDs on the
+        RPi, this method offers some advantages:
+        1) Does not require any userspace programs.
+        2) LEDs can be connected to the kernel's led-trigger framework,
+           and drive the LED based on triggers such as cpu load, heartbeat,
+           kernel panic, key input, timers and others.
+        3) LED can be tied to the input state of another GPIO pin.
+        4) The LED is setup early during the kernel boot process (useful
+           for cpu/heartbeat/panic triggers).
+
+        Typical electrical connection is:
+           RPI-GPIO.19  ->  LED  -> 300ohm resister  -> RPI-GND
+        The GPIO pin number can be changed with the 'gpio=' parameter.
+
+        To control an LED from userspace, write a 0 or 1 value:
+           echo 1 > /sys/class/leds/myled1/brightness
+        The 'myled1' name can be changed with the 'label=' parameter.
+
+        To connect the LED to a kernel trigger from userspace:
+           echo cpu > /sys/class/leds/myled1/trigger
+           echo heartbeat > /sys/class/leds/myled1/trigger
+           echo none > /sys/class/leds/myled1/trigger
+        To connect the LED to GPIO.26 pin (physical pin 37):
+           echo gpio > /sys/class/leds/myled1/trigger
+           echo 26 > /sys/class/leds/myled1/gpio
+        Available triggers:
+           cat /sys/class/leds/myled1/trigger
+
+        More information about the Linux kernel LED/Trigger system:
+           https://www.kernel.org/doc/Documentation/leds/leds-class.rst
+           https://www.kernel.org/doc/Documentation/leds/ledtrig-oneshot.rst
+Load:   dtoverlay=gpio-led,<param>=<val>
+Params: gpio                    GPIO pin connected to the LED (default 19)
+        label                   The label for this LED. It will appear under
+                                /sys/class/leds/<label> . Default 'myled1'.
+        trigger                 Set the led-trigger to connect to this LED.
+                                default 'none' (LED is user-controlled).
+                                Some possible triggers:
+                                 cpu - CPU load (all CPUs)
+                                 cpu0 - CPU load of first CPU.
+                                 mmc - disk activity (all disks)
+                                 panic - turn on on kernel panic
+                                 heartbeat - indicate system health
+                                 gpio - connect to a GPIO input pin (note:
+                                        currently the GPIO PIN can not be set
+                                        using overlay parameters, must be
+                                        done in userspace, see examples above.
+        active_low              Set to 1 to turn invert the LED control
+                                (writing 0 to /sys/class/leds/XXX/brightness
+                                will turn on the GPIO/LED). Default '0'.
+
+
+Name:   gpio-no-bank0-irq
+Info:   Use this overlay to disable GPIO interrupts for GPIOs in bank 0 (0-27),
+        which can be useful for UIO drivers.
+        N.B. Using this overlay will trigger a kernel WARN during booting, but
+        this can safely be ignored - the system should work as expected.
+Load:   dtoverlay=gpio-no-bank0-irq
+Params: <None>
+
+
+Name:   gpio-no-irq
+Info:   Use this overlay to disable all GPIO interrupts, which can be useful
+        for user-space GPIO edge detection systems.
+Load:   dtoverlay=gpio-no-irq
+Params: <None>
+
+
+Name:   gpio-poweroff
+Info:   Drives a GPIO high or low on poweroff (including halt). Using this
+        overlay interferes with the normal power-down sequence, preventing the
+        kernel from resetting the SoC (a necessary step in a normal power-off
+        or reboot). This also disables the ability to trigger a boot by driving
+        GPIO3 low.
+
+        Users of this overlay are required to provide an external mechanism to
+        switch off the power supply when signalled - failure to do so results
+        in a kernel BUG, increased power consumption and undefined behaviour.
+Load:   dtoverlay=gpio-poweroff,<param>=<val>
+Params: gpiopin                 GPIO for signalling (default 26)
+
+        active_low              Set if the power control device requires a
+                                high->low transition to trigger a power-down.
+                                Note that this will require the support of a
+                                custom dt-blob.bin to prevent a power-down
+                                during the boot process, and that a reboot
+                                will also cause the pin to go low.
+        input                   Set if the gpio pin should be configured as
+                                an input.
+        export                  Set to export the configured pin to sysfs
+        timeout_ms              Specify (in ms) how long the kernel waits for
+                                power-down before issuing a WARN (default 3000).
+
+
+Name:   gpio-shutdown
+Info:   Initiates a shutdown when GPIO pin changes. The given GPIO pin
+        is configured as an input key that generates KEY_POWER events.
+
+        This event is handled by systemd-logind by initiating a
+        shutdown. Systemd versions older than 225 need an udev rule
+        enable listening to the input device:
+
+                ACTION!="REMOVE", SUBSYSTEM=="input", KERNEL=="event*", \
+                        SUBSYSTEMS=="platform", DRIVERS=="gpio-keys", \
+                        ATTRS{keys}=="116", TAG+="power-switch"
+
+        Alternatively this event can be handled also on systems without
+        systemd, just by traditional SysV init daemon. KEY_POWER event
+        (keycode 116) needs to be mapped to KeyboardSignal on console
+        and then kb::kbrequest inittab action which is triggered by
+        KeyboardSignal from console can be configured to issue system
+        shutdown. Steps for this configuration are:
+
+            Add following lines to the /etc/console-setup/remap.inc file:
+
+                # Key Power as special keypress
+                keycode 116 = KeyboardSignal
+
+            Then add following lines to /etc/inittab file:
+
+                # Action on special keypress (Key Power)
+                kb::kbrequest:/sbin/shutdown -t1 -a -h -P now
+
+            And finally reload configuration by calling following commands:
+
+                # dpkg-reconfigure console-setup
+                # service console-setup reload
+                # init q
+
+        This overlay only handles shutdown. After shutdown, the system
+        can be powered up again by driving GPIO3 low. The default
+        configuration uses GPIO3 with a pullup, so if you connect a
+        button between GPIO3 and GND (pin 5 and 6 on the 40-pin header),
+        you get a shutdown and power-up button. Please note that
+        Raspberry Pi 1 Model B rev 1 uses GPIO1 instead of GPIO3.
+Load:   dtoverlay=gpio-shutdown,<param>=<val>
+Params: gpio_pin                GPIO pin to trigger on (default 3)
+                                For Raspberry Pi 1 Model B rev 1 set this
+                                explicitly to value 1, e.g.:
+
+                                    dtoverlay=gpio-shutdown,gpio_pin=1
+
+        active_low              When this is 1 (active low), a falling
+                                edge generates a key down event and a
+                                rising edge generates a key up event.
+                                When this is 0 (active high), this is
+                                reversed. The default is 1 (active low).
+
+        gpio_pull               Desired pull-up/down state (off, down, up)
+                                Default is "up".
+
+                                Note that the default pin (GPIO3) has an
+                                external pullup. Same applies for GPIO1
+                                on Raspberry Pi 1 Model B rev 1.
+
+        debounce                Specify the debounce interval in milliseconds
+                                (default 100)
+
+
+Name:   hd44780-lcd
+Info:   Configures an HD44780 compatible LCD display. Uses 4 gpio pins for
+        data, 2 gpio pins for enable and register select and 1 optional pin
+        for enabling/disabling the backlight display.
+Load:   dtoverlay=hd44780-lcd,<param>=<val>
+Params: pin_d4                  GPIO pin for data pin D4 (default 6)
+
+        pin_d5                  GPIO pin for data pin D5 (default 13)
+
+        pin_d6                  GPIO pin for data pin D6 (default 19)
+
+        pin_d7                  GPIO pin for data pin D7 (default 26)
+
+        pin_en                  GPIO pin for "Enable" (default 21)
+
+        pin_rs                  GPIO pin for "Register Select" (default 20)
+
+        pin_bl                  Optional pin for enabling/disabling the
+                                display backlight. (default disabled)
+
+        display_height          Height of the display in characters
+
+        display_width           Width of the display in characters
+
+
+Name:   hdmi-backlight-hwhack-gpio
+Info:   Devicetree overlay for GPIO based backlight on/off capability.
+        Use this if you have one of those HDMI displays whose backlight cannot
+        be controlled via DPMS over HDMI and plan to do a little soldering to
+        use an RPi gpio pin for on/off switching. See:
+        https://www.waveshare.com/wiki/7inch_HDMI_LCD_(C)#Backlight_Control
+Load:   dtoverlay=hdmi-backlight-hwhack-gpio,<param>=<val>
+Params: gpio_pin                GPIO pin used (default 17)
+        active_low              Set this to 1 if the display backlight is
+                                switched on when the wire goes low.
+                                Leave the default (value 0) if the backlight
+                                expects a high to switch it on.
+
+
+Name:   hifiberry-amp
+Info:   Configures the HifiBerry Amp and Amp+ audio cards
+Load:   dtoverlay=hifiberry-amp
+Params: <None>
+
+
+Name:   hifiberry-amp100
+Info:   Configures the HifiBerry AMP100 audio card
+Load:   dtoverlay=hifiberry-amp100,<param>=<val>
+Params: 24db_digital_gain       Allow gain to be applied via the PCM512x codec
+                                Digital volume control. Enable with
+                                "dtoverlay=hifiberry-amp100,24db_digital_gain"
+                                (The default behaviour is that the Digital
+                                volume control is limited to a maximum of
+                                0dB. ie. it can attenuate but not provide
+                                gain. For most users, this will be desired
+                                as it will prevent clipping. By appending
+                                the 24dB_digital_gain parameter, the Digital
+                                volume control will allow up to 24dB of
+                                gain. If this parameter is enabled, it is the
+                                responsibility of the user to ensure that
+                                the Digital volume control is set to a value
+                                that does not result in clipping/distortion!)
+        slave                   Force DAC+ Pro into slave mode, using Pi as
+                                master for bit clock and frame clock.
+        leds_off                If set to 'true' the onboard indicator LEDs
+                                are switched off at all times.
+        auto_mute               If set to 'true' the amplifier is automatically
+                                muted when the DAC is not playing.
+        mute_ext_ctl            The amplifier's HW mute control is enabled
+                                in ALSA mixer and set to <val>.
+                                Will be overwritten by ALSA user settings.
+
+
+Name:   hifiberry-dac
+Info:   Configures the HifiBerry DAC audio cards
+Load:   dtoverlay=hifiberry-dac
+Params: <None>
+
+
+Name:   hifiberry-dacplus
+Info:   Configures the HifiBerry DAC+ audio card
+Load:   dtoverlay=hifiberry-dacplus,<param>=<val>
+Params: 24db_digital_gain       Allow gain to be applied via the PCM512x codec
+                                Digital volume control. Enable with
+                                "dtoverlay=hifiberry-dacplus,24db_digital_gain"
+                                (The default behaviour is that the Digital
+                                volume control is limited to a maximum of
+                                0dB. ie. it can attenuate but not provide
+                                gain. For most users, this will be desired
+                                as it will prevent clipping. By appending
+                                the 24dB_digital_gain parameter, the Digital
+                                volume control will allow up to 24dB of
+                                gain. If this parameter is enabled, it is the
+                                responsibility of the user to ensure that
+                                the Digital volume control is set to a value
+                                that does not result in clipping/distortion!)
+        slave                   Force DAC+ Pro into slave mode, using Pi as
+                                master for bit clock and frame clock.
+        leds_off                If set to 'true' the onboard indicator LEDs
+                                are switched off at all times.
+
+
+Name:   hifiberry-dacplusadc
+Info:   Configures the HifiBerry DAC+ADC audio card
+Load:   dtoverlay=hifiberry-dacplusadc,<param>=<val>
+Params: 24db_digital_gain       Allow gain to be applied via the PCM512x codec
+                                Digital volume control. Enable with
+                                "dtoverlay=hifiberry-dacplus,24db_digital_gain"
+                                (The default behaviour is that the Digital
+                                volume control is limited to a maximum of
+                                0dB. ie. it can attenuate but not provide
+                                gain. For most users, this will be desired
+                                as it will prevent clipping. By appending
+                                the 24dB_digital_gain parameter, the Digital
+                                volume control will allow up to 24dB of
+                                gain. If this parameter is enabled, it is the
+                                responsibility of the user to ensure that
+                                the Digital volume control is set to a value
+                                that does not result in clipping/distortion!)
+        slave                   Force DAC+ Pro into slave mode, using Pi as
+                                master for bit clock and frame clock.
+        leds_off                If set to 'true' the onboard indicator LEDs
+                                are switched off at all times.
+
+
+Name:   hifiberry-dacplusadcpro
+Info:   Configures the HifiBerry DAC+ADC PRO audio card
+Load:   dtoverlay=hifiberry-dacplusadcpro,<param>=<val>
+Params: 24db_digital_gain       Allow gain to be applied via the PCM512x codec
+                                Digital volume control. Enable with
+                                "dtoverlay=hifiberry-dacplusadcpro,24db_digital_gain"
+                                (The default behaviour is that the Digital
+                                volume control is limited to a maximum of
+                                0dB. ie. it can attenuate but not provide
+                                gain. For most users, this will be desired
+                                as it will prevent clipping. By appending
+                                the 24dB_digital_gain parameter, the Digital
+                                volume control will allow up to 24dB of
+                                gain. If this parameter is enabled, it is the
+                                responsibility of the user to ensure that
+                                the Digital volume control is set to a value
+                                that does not result in clipping/distortion!)
+        slave                   Force DAC+ADC Pro into slave mode, using Pi as
+                                master for bit clock and frame clock.
+        leds_off                If set to 'true' the onboard indicator LEDs
+                                are switched off at all times.
+
+
+Name:   hifiberry-dacplusdsp
+Info:   Configures the HifiBerry DAC+DSP audio card
+Load:   dtoverlay=hifiberry-dacplusdsp
+Params: <None>
+
+
+Name:   hifiberry-dacplushd
+Info:   Configures the HifiBerry DAC+ HD audio card
+Load:   dtoverlay=hifiberry-dacplushd
+Params: <None>
+
+
+Name:   hifiberry-digi
+Info:   Configures the HifiBerry Digi and Digi+ audio card
+Load:   dtoverlay=hifiberry-digi
+Params: <None>
+
+
+Name:   hifiberry-digi-pro
+Info:   Configures the HifiBerry Digi+ Pro audio card
+Load:   dtoverlay=hifiberry-digi-pro
+Params: <None>
+
+
+Name:   highperi
+Info:   Enables "High Peripheral" mode
+Load:   dtoverlay=highperi
+Params: <None>
+
+
+Name:   hy28a
+Info:   HY28A - 2.8" TFT LCD Display Module by HAOYU Electronics
+        Default values match Texy's display shield
+Load:   dtoverlay=hy28a,<param>=<val>
+Params: speed                   Display SPI bus speed
+
+        rotate                  Display rotation {0,90,180,270}
+
+        fps                     Delay between frame updates
+
+        debug                   Debug output level {0-7}
+
+        xohms                   Touchpanel sensitivity (X-plate resistance)
+
+        resetgpio               GPIO used to reset controller
+
+        ledgpio                 GPIO used to control backlight
+
+
+Name:   hy28b
+Info:   HY28B - 2.8" TFT LCD Display Module by HAOYU Electronics
+        Default values match Texy's display shield
+Load:   dtoverlay=hy28b,<param>=<val>
+Params: speed                   Display SPI bus speed
+
+        rotate                  Display rotation {0,90,180,270}
+
+        fps                     Delay between frame updates
+
+        debug                   Debug output level {0-7}
+
+        xohms                   Touchpanel sensitivity (X-plate resistance)
+
+        resetgpio               GPIO used to reset controller
+
+        ledgpio                 GPIO used to control backlight
+
+
+Name:   hy28b-2017
+Info:   HY28B 2017 version - 2.8" TFT LCD Display Module by HAOYU Electronics
+        Default values match Texy's display shield
+Load:   dtoverlay=hy28b-2017,<param>=<val>
+Params: speed                   Display SPI bus speed
+
+        rotate                  Display rotation {0,90,180,270}
+
+        fps                     Delay between frame updates
+
+        debug                   Debug output level {0-7}
+
+        xohms                   Touchpanel sensitivity (X-plate resistance)
+
+        resetgpio               GPIO used to reset controller
+
+        ledgpio                 GPIO used to control backlight
+
+
+Name:   i-sabre-q2m
+Info:   Configures the Audiophonics I-SABRE Q2M DAC
+Load:   dtoverlay=i-sabre-q2m
+Params: <None>
+
+
+Name:   i2c-bcm2708
+Info:   Fall back to the i2c_bcm2708 driver for the i2c_arm bus.
+Load:   dtoverlay=i2c-bcm2708
+Params: <None>
+
+
+Name:   i2c-gpio
+Info:   Adds support for software i2c controller on gpio pins
+Load:   dtoverlay=i2c-gpio,<param>=<val>
+Params: i2c_gpio_sda            GPIO used for I2C data (default "23")
+
+        i2c_gpio_scl            GPIO used for I2C clock (default "24")
+
+        i2c_gpio_delay_us       Clock delay in microseconds
+                                (default "2" = ~100kHz)
+
+        bus                     Set to a unique, non-zero value if wanting
+                                multiple i2c-gpio busses. If set, will be used
+                                as the preferred bus number (/dev/i2c-<n>). If
+                                not set, the default value is 0, but the bus
+                                number will be dynamically assigned - probably
+                                3.
+
+
+Name:   i2c-mux
+Info:   Adds support for a number of I2C bus multiplexers on i2c_arm
+Load:   dtoverlay=i2c-mux,<param>=<val>
+Params: pca9542                 Select the NXP PCA9542 device
+
+        pca9545                 Select the NXP PCA9545 device
+
+        pca9548                 Select the NXP PCA9548 device
+
+        addr                    Change I2C address of the device (default 0x70)
+
+
+[ The i2c-mux-pca9548a overlay has been deleted. See i2c-mux. ]
+
+
+Name:   i2c-pwm-pca9685a
+Info:   Adds support for an NXP PCA9685A I2C PWM controller on i2c_arm
+Load:   dtoverlay=i2c-pwm-pca9685a,<param>=<val>
+Params: addr                    I2C address of PCA9685A (default 0x40)
+
+
+Name:   i2c-rtc
+Info:   Adds support for a number of I2C Real Time Clock devices
+Load:   dtoverlay=i2c-rtc,<param>=<val>
+Params: abx80x                  Select one of the ABx80x family:
+                                  AB0801, AB0803, AB0804, AB0805,
+                                  AB1801, AB1803, AB1804, AB1805
+
+        ds1307                  Select the DS1307 device
+
+        ds1339                  Select the DS1339 device
+
+        ds1340                  Select the DS1340 device
+
+        ds3231                  Select the DS3231 device
+
+        m41t62                  Select the M41T62 device
+
+        mcp7940x                Select the MCP7940x device
+
+        mcp7941x                Select the MCP7941x device
+
+        pcf2127                 Select the PCF2127 device
+
+        pcf2129                 Select the PCF2129 device
+
+        pcf85063                Select the PCF85063 device
+
+        pcf85063a               Select the PCF85063A device
+
+        pcf8523                 Select the PCF8523 device
+
+        pcf85363                Select the PCF85363 device
+
+        pcf8563                 Select the PCF8563 device
+
+        rv1805                  Select the Micro Crystal RV1805 device
+
+        rv3028                  Select the Micro Crystal RV3028 device
+
+        sd3078                  Select the ZXW Shenzhen whwave SD3078 device
+
+        i2c0                    Choose the I2C0 bus on GPIOs 0&1
+
+        i2c_csi_dsi             Choose the I2C0 bus on GPIOs 44&45
+
+        addr                    Sets the address for the RTC. Note that the
+                                device must be configured to use the specified
+                                address.
+
+        trickle-diode-type      Diode type for trickle charge - "standard" or
+                                "schottky" (ABx80x and RV1805 only)
+
+        trickle-resistor-ohms   Resistor value for trickle charge (DS1339,
+                                ABx80x, RV1805, RV3028)
+
+        wakeup-source           Specify that the RTC can be used as a wakeup
+                                source
+
+        backup-switchover-mode  Backup power supply switch mode. Must be 0 for
+                                off or 1 for Vdd < VBackup (RV3028 only)
+
+
+Name:   i2c-rtc-gpio
+Info:   Adds support for a number of I2C Real Time Clock devices
+        using the software i2c controller
+Load:   dtoverlay=i2c-rtc-gpio,<param>=<val>
+Params: abx80x                  Select one of the ABx80x family:
+                                  AB0801, AB0803, AB0804, AB0805,
+                                  AB1801, AB1803, AB1804, AB1805
+
+        ds1307                  Select the DS1307 device
+
+        ds1339                  Select the DS1339 device
+
+        ds1340                  Select the DS1340 device
+
+        ds3231                  Select the DS3231 device
+
+        m41t62                  Select the M41T62 device
+
+        mcp7940x                Select the MCP7940x device
+
+        mcp7941x                Select the MCP7941x device
+
+        pcf2127                 Select the PCF2127 device
+
+        pcf2129                 Select the PCF2129 device
+
+        pcf85063                Select the PCF85063 device
+
+        pcf85063a               Select the PCF85063A device
+
+        pcf8523                 Select the PCF8523 device
+
+        pcf85363                Select the PCF85363 device
+
+        pcf8563                 Select the PCF8563 device
+
+        rv1805                  Select the Micro Crystal RV1805 device
+
+        rv3028                  Select the Micro Crystal RV3028 device
+
+        sd3078                  Select the ZXW Shenzhen whwave SD3078 device
+
+        addr                    Sets the address for the RTC. Note that the
+                                device must be configured to use the specified
+                                address.
+
+        trickle-diode-type      Diode type for trickle charge - "standard" or
+                                "schottky" (ABx80x and RV1805 only)
+
+        trickle-resistor-ohms   Resistor value for trickle charge (DS1339,
+                                ABx80x, RV1805, RV3028)
+
+        wakeup-source           Specify that the RTC can be used as a wakeup
+                                source
+
+        backup-switchover-mode  Backup power supply switch mode. Must be 0 for
+                                off or 1 for Vdd < VBackup (RV3028 only)
+
+        i2c_gpio_sda            GPIO used for I2C data (default "23")
+
+        i2c_gpio_scl            GPIO used for I2C clock (default "24")
+
+        i2c_gpio_delay_us       Clock delay in microseconds
+                                (default "2" = ~100kHz)
+
+
+Name:   i2c-sensor
+Info:   Adds support for a number of I2C barometric pressure, temperature,
+        light level and chemical sensors on i2c_arm
+Load:   dtoverlay=i2c-sensor,<param>=<val>
+Params: addr                    Set the address for the BH1750, BME280, BME680,
+                                BMP280, CCS811, DS1621, HDC100X, LM75, SHT3x or
+                                TMP102
+
+        bh1750                  Select the Rohm BH1750 ambient light sensor
+                                Valid addresses 0x23 or 0x5c, default 0x23
+
+        bme280                  Select the Bosch Sensortronic BME280
+                                Valid addresses 0x76-0x77, default 0x76
+
+        bme680                  Select the Bosch Sensortronic BME680
+                                Valid addresses 0x76-0x77, default 0x76
+
+        bmp085                  Select the Bosch Sensortronic BMP085
+
+        bmp180                  Select the Bosch Sensortronic BMP180
+
+        bmp280                  Select the Bosch Sensortronic BMP280
+                                Valid addresses 0x76-0x77, default 0x76
+
+        ccs811                  Select the AMS CCS811 digital gas sensor
+                                Valid addresses 0x5a-0x5b, default 0x5b
+
+        ds1621                  Select the Dallas Semiconductors DS1621 temp
+                                sensor. Valid addresses 0x48-0x4f, default 0x48
+
+        hdc100x                 Select the Texas Instruments HDC100x temp sensor
+                                Valid addresses 0x40-0x43, default 0x40
+
+        htu21                   Select the HTU21 temperature and humidity sensor
+
+        lm75                    Select the Maxim LM75 temperature sensor
+                                Valid addresses 0x48-0x4f, default 0x4f
+
+        lm75addr                Deprecated - use addr parameter instead
+
+        max17040                Select the Maxim Integrated MAX17040 battery
+                                monitor
+
+        sht3x                   Select the Sensiron SHT3x temperature and
+                                humidity sensor. Valid addresses 0x44-0x45,
+                                default 0x44
+
+        si7020                  Select the Silicon Labs Si7013/20/21 humidity/
+                                temperature sensor
+
+        sps30                   Select the Sensirion SPS30 particulate matter
+                                sensor. Fixed address 0x69.
+
+        sgp30                   Select the Sensirion SGP30 VOC sensor.
+                                Fixed address 0x58.
+
+        tmp102                  Select the Texas Instruments TMP102 temp sensor
+                                Valid addresses 0x48-0x4b, default 0x48
+
+        tsl4531                 Select the AMS TSL4531 digital ambient light
+                                sensor
+
+        veml6070                Select the Vishay VEML6070 ultraviolet light
+                                sensor
+
+
+Name:   i2c0
+Info:   Change i2c0 pin usage. Not all pin combinations are usable on all
+        platforms - platforms other then Compute Modules can only use this
+        to disable transaction combining.
+        Do NOT use in conjunction with dtparam=i2c_vc=on. From the 5.4 kernel
+        onwards the base DT includes the use of i2c_mux_pinctrl to expose two
+        muxings of BSC0 - GPIOs 0&1, and whichever combination is used for the
+        camera and display connectors. This overlay disables that mux and
+        configures /dev/i2c0 to point at whichever set of pins is requested.
+        dtparam=i2c_vc=on will try and enable the mux, so combining the two
+        will cause conflicts.
+Load:   dtoverlay=i2c0,<param>=<val>
+Params: pins_0_1                Use pins 0 and 1 (default)
+        pins_28_29              Use pins 28 and 29
+        pins_44_45              Use pins 44 and 45
+        pins_46_47              Use pins 46 and 47
+        combine                 Allow transactions to be combined (default
+                                "yes")
+
+
+Name:   i2c0-bcm2708
+Info:   Deprecated, legacy version of i2c0.
+Load:   <Deprecated>
+
+
+Name:   i2c1
+Info:   Change i2c1 pin usage. Not all pin combinations are usable on all
+        platforms - platforms other then Compute Modules can only use this
+        to disable transaction combining.
+Load:   dtoverlay=i2c1,<param>=<val>
+Params: pins_2_3                Use pins 2 and 3 (default)
+        pins_44_45              Use pins 44 and 45
+        combine                 Allow transactions to be combined (default
+                                "yes")
+
+
+Name:   i2c1-bcm2708
+Info:   Deprecated, legacy version of i2c1.
+Load:   <Deprecated>
+
+
+Name:   i2c3
+Info:   Enable the i2c3 bus. BCM2711 only.
+Load:   dtoverlay=i2c3,<param>
+Params: pins_2_3                Use GPIOs 2 and 3
+        pins_4_5                Use GPIOs 4 and 5 (default)
+        baudrate                Set the baudrate for the interface (default
+                                "100000")
+
+
+Name:   i2c4
+Info:   Enable the i2c4 bus. BCM2711 only.
+Load:   dtoverlay=i2c4,<param>
+Params: pins_6_7                Use GPIOs 6 and 7
+        pins_8_9                Use GPIOs 8 and 9 (default)
+        baudrate                Set the baudrate for the interface (default
+                                "100000")
+
+
+Name:   i2c5
+Info:   Enable the i2c5 bus. BCM2711 only.
+Load:   dtoverlay=i2c5,<param>
+Params: pins_10_11              Use GPIOs 10 and 11
+        pins_12_13              Use GPIOs 12 and 13 (default)
+        baudrate                Set the baudrate for the interface (default
+                                "100000")
+
+
+Name:   i2c6
+Info:   Enable the i2c6 bus. BCM2711 only.
+Load:   dtoverlay=i2c6,<param>
+Params: pins_0_1                Use GPIOs 0 and 1
+        pins_22_23              Use GPIOs 22 and 23 (default)
+        baudrate                Set the baudrate for the interface (default
+                                "100000")
+
+
+Name:   i2s-gpio28-31
+Info:   move I2S function block to GPIO 28 to 31
+Load:   dtoverlay=i2s-gpio28-31
+Params: <None>
+
+
+Name:   ilitek251x
+Info:   Enables I2C connected Ilitek 251x multiple touch controller using
+        GPIO 4 (pin 7 on GPIO header) for interrupt.
+Load:   dtoverlay=ilitek251x,<param>=<val>
+Params: interrupt               GPIO used for interrupt (default 4)
+        sizex                   Touchscreen size x, horizontal resolution of
+                                touchscreen (in pixels)
+        sizey                   Touchscreen size y, vertical resolution of
+                                touchscreen (in pixels)
+
+
+Name:   imx219
+Info:   Sony IMX219 camera module.
+        Uses Unicam 1, which is the standard camera connector on most Pi
+        variants.
+Load:   dtoverlay=imx219,<param>=<val>
+Params: rotation                Mounting rotation of the camera sensor (0 or
+                                180, default 180)
+
+
+Name:   imx290
+Info:   Sony IMX290 camera module.
+        Uses Unicam 1, which is the standard camera connector on most Pi
+        variants. NB This currently uses 4 CSI2 data lanes and therefore will
+        only work on a CM.
+Load:   dtoverlay=imx290,<param>
+Params: 4lane                   Enable 4 CSI2 lanes. This requires a Compute
+                                Module (1, 3, or 4).
+        clock-frequency         Sets the clock frequency to match that used on
+                                the board.
+                                Modules from Vision Components use 37.125MHz
+                                (the default), whilst those from Innomaker use
+                                74.25MHz.
+        mono                    Denote that the module is a mono sensor.
+
+
+Name:   imx378
+Info:   Sony IMX378 camera module.
+        Uses Unicam 1, which is the standard camera connector on most Pi
+        variants.
+Load:   dtoverlay=imx378,<param>=<val>
+Params: rotation                Mounting rotation of the camera sensor (0 or
+                                180, default 180)
+
+
+Name:   imx477
+Info:   Sony IMX477 camera module.
+        Uses Unicam 1, which is the standard camera connector on most Pi
+        variants.
+Load:   dtoverlay=imx477,<param>=<val>
+Params: rotation                Mounting rotation of the camera sensor (0 or
+                                180, default 180)
+
+
+Name:   iqaudio-codec
+Info:   Configures the IQaudio Codec audio card
+Load:   dtoverlay=iqaudio-codec
+Params: <None>
+
+
+Name:   iqaudio-dac
+Info:   Configures the IQaudio DAC audio card
+Load:   dtoverlay=iqaudio-dac,<param>
+Params: 24db_digital_gain       Allow gain to be applied via the PCM512x codec
+                                Digital volume control. Enable with
+                                "dtoverlay=iqaudio-dac,24db_digital_gain"
+                                (The default behaviour is that the Digital
+                                volume control is limited to a maximum of
+                                0dB. ie. it can attenuate but not provide
+                                gain. For most users, this will be desired
+                                as it will prevent clipping. By appending
+                                the 24db_digital_gain parameter, the Digital
+                                volume control will allow up to 24dB of
+                                gain. If this parameter is enabled, it is the
+                                responsibility of the user to ensure that
+                                the Digital volume control is set to a value
+                                that does not result in clipping/distortion!)
+
+
+Name:   iqaudio-dacplus
+Info:   Configures the IQaudio DAC+ audio card
+Load:   dtoverlay=iqaudio-dacplus,<param>=<val>
+Params: 24db_digital_gain       Allow gain to be applied via the PCM512x codec
+                                Digital volume control. Enable with
+                                "dtoverlay=iqaudio-dacplus,24db_digital_gain"
+                                (The default behaviour is that the Digital
+                                volume control is limited to a maximum of
+                                0dB. ie. it can attenuate but not provide
+                                gain. For most users, this will be desired
+                                as it will prevent clipping. By appending
+                                the 24db_digital_gain parameter, the Digital
+                                volume control will allow up to 24dB of
+                                gain. If this parameter is enabled, it is the
+                                responsibility of the user to ensure that
+                                the Digital volume control is set to a value
+                                that does not result in clipping/distortion!)
+        auto_mute_amp           If specified, unmute/mute the IQaudIO amp when
+                                starting/stopping audio playback.
+        unmute_amp              If specified, unmute the IQaudIO amp once when
+                                the DAC driver module loads.
+
+
+Name:   iqaudio-digi-wm8804-audio
+Info:   Configures the IQAudIO Digi WM8804 audio card
+Load:   dtoverlay=iqaudio-digi-wm8804-audio,<param>=<val>
+Params: card_name               Override the default, "IQAudIODigi", card name.
+        dai_name                Override the default, "IQAudIO Digi", dai name.
+        dai_stream_name         Override the default, "IQAudIO Digi HiFi",
+                                dai stream name.
+
+
+Name:   irs1125
+Info:   Infineon irs1125 TOF camera module.
+        Uses Unicam 1, which is the standard camera connector on most Pi
+        variants.
+Load:   dtoverlay=irs1125
+Params: <None>
+
+
+Name:   jedec-spi-nor
+Info:   Adds support for JEDEC-compliant SPI NOR flash devices.  (Note: The
+        "jedec,spi-nor" kernel driver was formerly known as "m25p80".)
+Load:   dtoverlay=jedec-spi-nor,<param>=<val>
+Params: flash-spi<n>-<m>        Enables flash device on SPI<n>, CS#<m>.
+        flash-fastr-spi<n>-<m>  Enables flash device with fast read capability
+                                on SPI<n>, CS#<m>.
+
+
+Name:   justboom-both
+Info:   Simultaneous usage of an justboom-dac and justboom-digi based
+        card
+Load:   dtoverlay=justboom-both,<param>=<val>
+Params: 24db_digital_gain       Allow gain to be applied via the PCM512x codec
+                                Digital volume control. Enable with
+                                "dtoverlay=justboom-dac,24db_digital_gain"
+                                (The default behaviour is that the Digital
+                                volume control is limited to a maximum of
+                                0dB. ie. it can attenuate but not provide
+                                gain. For most users, this will be desired
+                                as it will prevent clipping. By appending
+                                the 24dB_digital_gain parameter, the Digital
+                                volume control will allow up to 24dB of
+                                gain. If this parameter is enabled, it is the
+                                responsibility of the user to ensure that
+                                the Digital volume control is set to a value
+                                that does not result in clipping/distortion!)
+
+
+Name:   justboom-dac
+Info:   Configures the JustBoom DAC HAT, Amp HAT, DAC Zero and Amp Zero audio
+        cards
+Load:   dtoverlay=justboom-dac,<param>=<val>
+Params: 24db_digital_gain       Allow gain to be applied via the PCM512x codec
+                                Digital volume control. Enable with
+                                "dtoverlay=justboom-dac,24db_digital_gain"
+                                (The default behaviour is that the Digital
+                                volume control is limited to a maximum of
+                                0dB. ie. it can attenuate but not provide
+                                gain. For most users, this will be desired
+                                as it will prevent clipping. By appending
+                                the 24dB_digital_gain parameter, the Digital
+                                volume control will allow up to 24dB of
+                                gain. If this parameter is enabled, it is the
+                                responsibility of the user to ensure that
+                                the Digital volume control is set to a value
+                                that does not result in clipping/distortion!)
+
+
+Name:   justboom-digi
+Info:   Configures the JustBoom Digi HAT and Digi Zero audio cards
+Load:   dtoverlay=justboom-digi
+Params: <None>
+
+
+Name:   lirc-rpi
+Info:   This overlay has been deprecated and removed - see gpio-ir
+Load:   <Deprecated>
+
+
+Name:   ltc294x
+Info:   Adds support for the ltc294x family of battery gauges
+Load:   dtoverlay=ltc294x,<param>=<val>
+Params: ltc2941                 Select the ltc2941 device
+
+        ltc2942                 Select the ltc2942 device
+
+        ltc2943                 Select the ltc2943 device
+
+        ltc2944                 Select the ltc2944 device
+
+        resistor-sense          The sense resistor value in milli-ohms.
+                                Can be a 32-bit negative value when the battery
+                                has been connected to the wrong end of the
+                                resistor.
+
+        prescaler-exponent      Range and accuracy of the gauge. The value is
+                                programmed into the chip only if it differs
+                                from the current setting.
+                                For LTC2941 only:
+                                - Default value is 128
+                                - the exponent is in the range 0-7 (default 7)
+                                See the datasheet for more information.
+
+
+Name:   max98357a
+Info:   Configures the Maxim MAX98357A I2S DAC
+Load:   dtoverlay=max98357a,<param>=<val>
+Params: no-sdmode               Driver does not manage the state of the DAC's
+                                SD_MODE pin (i.e. chip is always on).
+        sdmode-pin              integer, GPIO pin connected to the SD_MODE input
+                                of the DAC (default GPIO4 if parameter omitted).
+
+
+Name:   maxtherm
+Info:   Configure a MAX6675, MAX31855 or MAX31856 thermocouple as an IIO device.
+
+        For devices on spi1 or spi2, the interfaces should be enabled
+        with one of the spi1-1/2/3cs and/or spi2-1/2/3cs overlays.
+        The overlay expects to disable the relevant spidev node, so also using
+        e.g. cs0_spidev=off is unnecessary.
+
+        Example:
+        MAX31855 on /dev/spidev0.0
+            dtoverlay=maxtherm,spi0-0,max31855
+        MAX31856 using a type J thermocouple on /dev/spidev2.1
+            dtoverlay=spi2-2cs
+            dtoverlay=maxtherm,spi2-1,max31856,type_j
+
+Load:   dtoverlay=maxtherm,<param>=<val>
+Params: spi<n>-<m>              Configure device at spi<n>, cs<m>
+                                (boolean, required)
+        max6675                 Enable support for the MAX6675 (default)
+        max31855                Enable support for the MAX31855
+        max31855e               Enable support for the MAX31855E
+        max31855j               Enable support for the MAX31855J
+        max31855k               Enable support for the MAX31855K
+        max31855n               Enable support for the MAX31855N
+        max31855r               Enable support for the MAX31855R
+        max31855s               Enable support for the MAX31855S
+        max31855t               Enable support for the MAX31855T
+        max31856                Enable support for the MAX31856 (with type K)
+        type_b                  Select a type B sensor for max31856
+        type_e                  Select a type E sensor for max31856
+        type_j                  Select a type J sensor for max31856
+        type_k                  Select a type K sensor for max31856
+        type_n                  Select a type N sensor for max31856
+        type_r                  Select a type R sensor for max31856
+        type_s                  Select a type S sensor for max31856
+        type_t                  Select a type T sensor for max31856
+
+
+Name:   mbed-dac
+Info:   Configures the mbed AudioCODEC (TLV320AIC23B)
+Load:   dtoverlay=mbed-dac
+Params: <None>
+
+
+Name:   mcp23017
+Info:   Configures the MCP23017 I2C GPIO expander
+Load:   dtoverlay=mcp23017,<param>=<val>
+Params: gpiopin                 Gpio pin connected to the INTA output of the
+                                MCP23017 (default: 4)
+
+        addr                    I2C address of the MCP23017 (default: 0x20)
+
+        mcp23008                Configure an MCP23008 instead.
+        noints                  Disable the interrupt GPIO line.
+
+
+Name:   mcp23s17
+Info:   Configures the MCP23S08/17 SPI GPIO expanders.
+        If devices are present on SPI1 or SPI2, those interfaces must be enabled
+        with one of the spi1-1/2/3cs and/or spi2-1/2/3cs overlays.
+        If interrupts are enabled for a device on a given CS# on a SPI bus, that
+        device must be the only one present on that SPI bus/CS#.
+Load:   dtoverlay=mcp23s17,<param>=<val>
+Params: s08-spi<n>-<m>-present  4-bit integer, bitmap indicating MCP23S08
+                                devices present on SPI<n>, CS#<m>
+
+        s17-spi<n>-<m>-present  8-bit integer, bitmap indicating MCP23S17
+                                devices present on SPI<n>, CS#<m>
+
+        s08-spi<n>-<m>-int-gpio integer, enables interrupts on a single
+                                MCP23S08 device on SPI<n>, CS#<m>, specifies
+                                the GPIO pin to which INT output of MCP23S08
+                                is connected.
+
+        s17-spi<n>-<m>-int-gpio integer, enables mirrored interrupts on a
+                                single MCP23S17 device on SPI<n>, CS#<m>,
+                                specifies the GPIO pin to which either INTA
+                                or INTB output of MCP23S17 is connected.
+
+
+Name:   mcp2515-can0
+Info:   Configures the MCP2515 CAN controller on spi0.0
+Load:   dtoverlay=mcp2515-can0,<param>=<val>
+Params: oscillator              Clock frequency for the CAN controller (Hz)
+
+        spimaxfrequency         Maximum SPI frequence (Hz)
+
+        interrupt               GPIO for interrupt signal
+
+
+Name:   mcp2515-can1
+Info:   Configures the MCP2515 CAN controller on spi0.1
+Load:   dtoverlay=mcp2515-can1,<param>=<val>
+Params: oscillator              Clock frequency for the CAN controller (Hz)
+
+        spimaxfrequency         Maximum SPI frequence (Hz)
+
+        interrupt               GPIO for interrupt signal
+
+
+Name:   mcp251xfd
+Info:   Configures the MCP251XFD CAN controller family
+        For devices on spi1 or spi2, the interfaces should be enabled
+        with one of the spi1-1/2/3cs and/or spi2-1/2/3cs overlays.
+Load:   dtoverlay=mcp251xfd,<param>=<val>
+Params: spi<n>-<m>              Configure device at spi<n>, cs<m>
+                                (boolean, required)
+
+        oscillator              Clock frequency for the CAN controller (Hz)
+
+        speed                   Maximum SPI frequence (Hz)
+
+        interrupt               GPIO for interrupt signal
+
+        rx_interrupt            GPIO for RX interrupt signal (nINT1) (optional)
+
+        xceiver_enable          GPIO for CAN transceiver enable (optional)
+
+        xceiver_active_high     specifiy if CAN transceiver enable pin is
+                                active high (optional, default: active low)
+
+
+Name:   mcp3008
+Info:   Configures MCP3008 A/D converters
+        For devices on spi1 or spi2, the interfaces should be enabled
+        with one of the spi1-1/2/3cs and/or spi2-1/2/3cs overlays.
+Load:   dtoverlay=mcp3008,<param>[=<val>]
+Params: spi<n>-<m>-present      boolean, configure device at spi<n>, cs<m>
+        spi<n>-<m>-speed        integer, set the spi bus speed for this device
+
+
+Name:   mcp3202
+Info:   Configures MCP3202 A/D converters
+        For devices on spi1 or spi2, the interfaces should be enabled
+        with one of the spi1-1/2/3cs and/or spi2-1/2/3cs overlays.
+Load:   dtoverlay=mcp3202,<param>[=<val>]
+Params: spi<n>-<m>-present      boolean, configure device at spi<n>, cs<m>
+        spi<n>-<m>-speed        integer, set the spi bus speed for this device
+
+
+Name:   mcp342x
+Info:   Overlay for activation of Microchip MCP3421-3428 ADCs over I2C
+Load:   dtoverlay=mcp342x,<param>=<val>
+Params: addr                    I2C bus address of device, for devices with
+                                addresses that are configurable, e.g. by
+                                hardware links (default=0x68)
+        mcp3421                 The device is an MCP3421
+        mcp3422                 The device is an MCP3422
+        mcp3423                 The device is an MCP3423
+        mcp3424                 The device is an MCP3424
+        mcp3425                 The device is an MCP3425
+        mcp3426                 The device is an MCP3426
+        mcp3427                 The device is an MCP3427
+        mcp3428                 The device is an MCP3428
+
+
+Name:   media-center
+Info:   Media Center HAT - 2.83" Touch Display + extras by Pi Supply
+Load:   dtoverlay=media-center,<param>=<val>
+Params: speed                   Display SPI bus speed
+        rotate                  Display rotation {0,90,180,270}
+        fps                     Delay between frame updates
+        xohms                   Touchpanel sensitivity (X-plate resistance)
+        swapxy                  Swap x and y axis
+        backlight               Change backlight GPIO pin {e.g. 12, 18}
+        gpio_out_pin            GPIO for output (default "17")
+        gpio_in_pin             GPIO for input (default "18")
+        gpio_in_pull            Pull up/down/off on the input pin
+                                (default "down")
+        sense                   Override the IR receive auto-detection logic:
+                                 "0" = force active-high
+                                 "1" = force active-low
+                                 "-1" = use auto-detection
+                                (default "-1")
+        softcarrier             Turn the software carrier "on" or "off"
+                                (default "on")
+        invert                  "on" = invert the output pin (default "off")
+        debug                   "on" = enable additional debug messages
+                                (default "off")
+
+
+Name:   merus-amp
+Info:   Configures the merus-amp audio card
+Load:   dtoverlay=merus-amp
+Params: <None>
+
+
+Name:   midi-uart0
+Info:   Configures UART0 (ttyAMA0) so that a requested 38.4kbaud actually gets
+        31.25kbaud, the frequency required for MIDI
+Load:   dtoverlay=midi-uart0
+Params: <None>
+
+
+Name:   midi-uart1
+Info:   Configures UART1 (ttyS0) so that a requested 38.4kbaud actually gets
+        31.25kbaud, the frequency required for MIDI
+Load:   dtoverlay=midi-uart1
+Params: <None>
+
+
+Name:   minipitft13
+Info:   Overlay for AdaFruit Mini Pi 1.3" TFT via SPI using fbtft driver.
+Load:   dtoverlay=minipitft13,<param>=<val>
+Params: speed                   SPI bus speed (default 32000000)
+        rotate                  Display rotation (0, 90, 180 or 270; default 0)
+        width                   Display width (default 240)
+        height                  Display height (default 240)
+        fps                     Delay between frame updates (default 25)
+        debug                   Debug output level (0-7; default 0)
+
+
+Name:   miniuart-bt
+Info:   Switch the onboard Bluetooth function on Pi 3B, 3B+, 3A+, 4B and Zero W
+        to use the mini-UART (ttyS0) and restore UART0/ttyAMA0 over GPIOs 14 &
+        15. Note that this may reduce the maximum usable baudrate.
+        N.B. It is also necessary to edit /lib/systemd/system/hciuart.service
+        and replace ttyAMA0 with ttyS0, unless using Raspbian or another
+        distribution with udev rules that create /dev/serial0 and /dev/serial1,
+        in which case use /dev/serial1 instead because it will always be
+        correct. Furthermore, you must also set core_freq and core_freq_min to
+        the same value in config.txt or the miniuart will not work.
+Load:   dtoverlay=miniuart-bt,<param>=<val>
+Params: krnbt                   Set to "on" to enable autoprobing of Bluetooth
+                                driver without need of hciattach/btattach
+
+
+Name:   mmc
+Info:   Selects the bcm2835-mmc SD/MMC driver, optionally with overclock
+Load:   dtoverlay=mmc,<param>=<val>
+Params: overclock_50            Clock (in MHz) to use when the MMC framework
+                                requests 50MHz
+
+
+Name:   mpu6050
+Info:   Overlay for i2c connected mpu6050 imu
+Load:   dtoverlay=mpu6050,<param>=<val>
+Params: interrupt               GPIO pin for interrupt (default 4)
+        addr                    I2C address of the device (default 0x68)
+
+
+Name:   mz61581
+Info:   MZ61581 display by Tontec
+Load:   dtoverlay=mz61581,<param>=<val>
+Params: speed                   Display SPI bus speed
+
+        rotate                  Display rotation {0,90,180,270}
+
+        fps                     Delay between frame updates
+
+        txbuflen                Transmit buffer length (default 32768)
+
+        debug                   Debug output level {0-7}
+
+        xohms                   Touchpanel sensitivity (X-plate resistance)
+
+
+Name:   ov5647
+Info:   Omnivision OV5647 camera module.
+        Uses Unicam 1, which is the standard camera connector on most Pi
+        variants.
+Load:   dtoverlay=ov5647,<param>=<val>
+Params: rotation                Mounting rotation of the camera sensor (0 or
+                                180, default 0)
+
+
+Name:   ov7251
+Info:   Omnivision OV7251 camera module.
+        Uses Unicam 1, which is the standard camera connector on most Pi
+        variants.
+Load:   dtoverlay=ov7251
+Params: <None>
+
+
+Name:   ov9281
+Info:   Omnivision OV9281 camera module.
+        Uses Unicam 1, which is the standard camera connector on most Pi
+        variants.
+Load:   dtoverlay=ov9281
+Params: <None>
+
+
+Name:   papirus
+Info:   PaPiRus ePaper Screen by Pi Supply (both HAT and pHAT)
+Load:   dtoverlay=papirus,<param>=<val>
+Params: panel                   Display panel (required):
+                                1.44": e1144cs021
+                                2.0":  e2200cs021
+                                2.7":  e2271cs021
+
+        speed                   Display SPI bus speed
+
+
+Name:   pca953x
+Info:   TI PCA953x family of I2C GPIO expanders. Default is for NXP PCA9534.
+Load:   dtoverlay=pca953x,<param>=<val>
+Params: addr                    I2C address of expander. Default 0x20.
+        pca6416                 Select the NXP PCA6416 (16 bit)
+        pca9505                 Select the NXP PCA9505 (40 bit)
+        pca9535                 Select the NXP PCA9535 (16 bit)
+        pca9536                 Select the NXP PCA9536 or TI PCA9536 (4 bit)
+        pca9537                 Select the NXP PCA9537 (4 bit)
+        pca9538                 Select the NXP PCA9538 (8 bit)
+        pca9539                 Select the NXP PCA9539 (16 bit)
+        pca9554                 Select the NXP PCA9554 (8 bit)
+        pca9555                 Select the NXP PCA9555 (16 bit)
+        pca9556                 Select the NXP PCA9556 (8 bit)
+        pca9557                 Select the NXP PCA9557 (8 bit)
+        pca9574                 Select the NXP PCA9574 (8 bit)
+        pca9575                 Select the NXP PCA9575 (16 bit)
+        pca9698                 Select the NXP PCA9698 (40 bit)
+        pca16416                Select the NXP PCA16416 (16 bit)
+        pca16524                Select the NXP PCA16524 (24 bit)
+        pca19555a               Select the NXP PCA19555A (16 bit)
+        max7310                 Select the Maxim MAX7310 (8 bit)
+        max7312                 Select the Maxim MAX7312 (16 bit)
+        max7313                 Select the Maxim MAX7313 (16 bit)
+        max7315                 Select the Maxim MAX7315 (8 bit)
+        pca6107                 Select the TI PCA6107 (8 bit)
+        tca6408                 Select the TI TCA6408 (8 bit)
+        tca6416                 Select the TI TCA6416 (16 bit)
+        tca6424                 Select the TI TCA6424 (24 bit)
+        tca9539                 Select the TI TCA9539 (16 bit)
+        tca9554                 Select the TI TCA9554 (8 bit)
+        cat9554                 Select the Onnn CAT9554 (8 bit)
+        pca9654                 Select the Onnn PCA9654 (8 bit)
+        xra1202                 Select the Exar XRA1202 (8 bit)
+
+
+Name:   pcie-32bit-dma
+Info:   Force PCIe config to support 32bit DMA addresses at the expense of
+        having to bounce buffers.
+Load:   dtoverlay=pcie-32bit-dma
+Params: <None>
+
+
+[ The pcf2127-rtc overlay has been deleted. See i2c-rtc. ]
+
+
+[ The pcf8523-rtc overlay has been deleted. See i2c-rtc. ]
+
+
+[ The pcf8563-rtc overlay has been deleted. See i2c-rtc. ]
+
+
+Name:   pi3-act-led
+Info:   This overlay has been renamed act-led, keeping pi3-act-led as an alias
+        for backwards compatibility.
+Load:   <Deprecated>
+
+
+Name:   pi3-disable-bt
+Info:   This overlay has been renamed disable-bt, keeping pi3-disable-bt as an
+        alias for backwards compatibility.
+Load:   <Deprecated>
+
+
+Name:   pi3-disable-wifi
+Info:   This overlay has been renamed disable-wifi, keeping pi3-disable-wifi as
+        an alias for backwards compatibility.
+Load:   <Deprecated>
+
+
+Name:   pi3-miniuart-bt
+Info:   This overlay has been renamed miniuart-bt, keeping pi3-miniuart-bt as
+        an alias for backwards compatibility.
+Load:   <Deprecated>
+
+
+Name:   pibell
+Info:   Configures the pibell audio card.
+Load:   dtoverlay=pibell,<param>=<val>
+Params: alsaname                Set the name as it appears in ALSA (default
+                                "PiBell")
+
+
+Name:   pifacedigital
+Info:   Configures the PiFace Digital mcp23s17 GPIO port expander.
+Load:   dtoverlay=pifacedigital,<param>=<val>
+Params: spi-present-mask        8-bit integer, bitmap indicating MCP23S17 SPI0
+                                CS0 address. PiFace Digital supports addresses
+                                0-3, which can be configured with JP1 and JP2.
+
+
+Name:   pifi-40
+Info:   Configures the PiFi 40W stereo amplifier
+Load:   dtoverlay=pifi-40
+Params: <None>
+
+
+Name:   pifi-dac-hd
+Info:   Configures the PiFi DAC HD
+Load:   dtoverlay=pifi-dac-hd
+Params: <None>
+
+
+Name:   pifi-dac-zero
+Info:   Configures the PiFi DAC Zero
+Load:   dtoverlay=pifi-dac-zero
+Params: <None>
+
+
+Name:   pifi-mini-210
+Info:   Configures the PiFi Mini stereo amplifier
+Load:   dtoverlay=pifi-mini-210
+Params: <None>
+
+
+Name:   piglow
+Info:   Configures the PiGlow by pimoroni.com
+Load:   dtoverlay=piglow
+Params: <None>
+
+
+Name:   piscreen
+Info:   PiScreen display by OzzMaker.com
+Load:   dtoverlay=piscreen,<param>=<val>
+Params: speed                   Display SPI bus speed
+
+        rotate                  Display rotation {0,90,180,270}
+
+        fps                     Delay between frame updates
+
+        debug                   Debug output level {0-7}
+
+        xohms                   Touchpanel sensitivity (X-plate resistance)
+
+
+Name:   piscreen2r
+Info:   PiScreen 2 with resistive TP display by OzzMaker.com
+Load:   dtoverlay=piscreen2r,<param>=<val>
+Params: speed                   Display SPI bus speed
+
+        rotate                  Display rotation {0,90,180,270}
+
+        fps                     Delay between frame updates
+
+        debug                   Debug output level {0-7}
+
+        xohms                   Touchpanel sensitivity (X-plate resistance)
+
+
+Name:   pisound
+Info:   Configures the Blokas Labs pisound card
+Load:   dtoverlay=pisound
+Params: <None>
+
+
+Name:   pitft22
+Info:   Adafruit PiTFT 2.2" screen
+Load:   dtoverlay=pitft22,<param>=<val>
+Params: speed                   Display SPI bus speed
+
+        rotate                  Display rotation {0,90,180,270}
+
+        fps                     Delay between frame updates
+
+        debug                   Debug output level {0-7}
+
+
+Name:   pitft28-capacitive
+Info:   Adafruit PiTFT 2.8" capacitive touch screen
+Load:   dtoverlay=pitft28-capacitive,<param>=<val>
+Params: speed                   Display SPI bus speed
+
+        rotate                  Display rotation {0,90,180,270}
+
+        fps                     Delay between frame updates
+
+        debug                   Debug output level {0-7}
+
+        touch-sizex             Touchscreen size x (default 240)
+
+        touch-sizey             Touchscreen size y (default 320)
+
+        touch-invx              Touchscreen inverted x axis
+
+        touch-invy              Touchscreen inverted y axis
+
+        touch-swapxy            Touchscreen swapped x y axis
+
+
+Name:   pitft28-resistive
+Info:   Adafruit PiTFT 2.8" resistive touch screen
+Load:   dtoverlay=pitft28-resistive,<param>=<val>
+Params: speed                   Display SPI bus speed
+
+        rotate                  Display rotation {0,90,180,270}
+
+        fps                     Delay between frame updates
+
+        debug                   Debug output level {0-7}
+
+
+Name:   pitft35-resistive
+Info:   Adafruit PiTFT 3.5" resistive touch screen
+Load:   dtoverlay=pitft35-resistive,<param>=<val>
+Params: speed                   Display SPI bus speed
+
+        rotate                  Display rotation {0,90,180,270}
+
+        fps                     Delay between frame updates
+
+        debug                   Debug output level {0-7}
+
+
+Name:   pps-gpio
+Info:   Configures the pps-gpio (pulse-per-second time signal via GPIO).
+Load:   dtoverlay=pps-gpio,<param>=<val>
+Params: gpiopin                 Input GPIO (default "18")
+        assert_falling_edge     When present, assert is indicated by a falling
+                                edge, rather than by a rising edge (default
+                                off)
+        capture_clear           Generate clear events on the trailing edge
+                                (default off)
+
+
+Name:   pwm
+Info:   Configures a single PWM channel
+        Legal pin,function combinations for each channel:
+          PWM0: 12,4(Alt0) 18,2(Alt5) 40,4(Alt0)            52,5(Alt1)
+          PWM1: 13,4(Alt0) 19,2(Alt5) 41,4(Alt0) 45,4(Alt0) 53,5(Alt1)
+        N.B.:
+          1) Pin 18 is the only one available on all platforms, and
+             it is the one used by the I2S audio interface.
+             Pins 12 and 13 might be better choices on an A+, B+ or Pi2.
+          2) The onboard analogue audio output uses both PWM channels.
+          3) So be careful mixing audio and PWM.
+          4) Currently the clock must have been enabled and configured
+             by other means.
+Load:   dtoverlay=pwm,<param>=<val>
+Params: pin                     Output pin (default 18) - see table
+        func                    Pin function (default 2 = Alt5) - see above
+        clock                   PWM clock frequency (informational)
+
+
+Name:   pwm-2chan
+Info:   Configures both PWM channels
+        Legal pin,function combinations for each channel:
+          PWM0: 12,4(Alt0) 18,2(Alt5) 40,4(Alt0)            52,5(Alt1)
+          PWM1: 13,4(Alt0) 19,2(Alt5) 41,4(Alt0) 45,4(Alt0) 53,5(Alt1)
+        N.B.:
+          1) Pin 18 is the only one available on all platforms, and
+             it is the one used by the I2S audio interface.
+             Pins 12 and 13 might be better choices on an A+, B+ or Pi2.
+          2) The onboard analogue audio output uses both PWM channels.
+          3) So be careful mixing audio and PWM.
+          4) Currently the clock must have been enabled and configured
+             by other means.
+Load:   dtoverlay=pwm-2chan,<param>=<val>
+Params: pin                     Output pin (default 18) - see table
+        pin2                    Output pin for other channel (default 19)
+        func                    Pin function (default 2 = Alt5) - see above
+        func2                   Function for pin2 (default 2 = Alt5)
+        clock                   PWM clock frequency (informational)
+
+
+Name:   pwm-ir-tx
+Info:   Use GPIO pin as pwm-assisted infrared transmitter output.
+        This is an alternative to "gpio-ir-tx". pwm-ir-tx makes use
+        of PWM0 to reduce the CPU load during transmission compared to
+        gpio-ir-tx which uses bit-banging.
+        Legal pin,function combinations are:
+          12,4(Alt0) 18,2(Alt5) 40,4(Alt0) 52,5(Alt1)
+Load:   dtoverlay=pwm-ir-tx,<param>=<val>
+Params: gpio_pin                Output GPIO (default 18)
+
+        func                    Pin function (default 2 = Alt5)
+
+
+Name:   qca7000
+Info:   I2SE's Evaluation Board for PLC Stamp micro
+Load:   dtoverlay=qca7000,<param>=<val>
+Params: int_pin                 GPIO pin for interrupt signal (default 23)
+
+        speed                   SPI bus speed (default 12 MHz)
+
+
+Name:   rotary-encoder
+Info:   Overlay for GPIO connected rotary encoder.
+Load:   dtoverlay=rotary-encoder,<param>=<val>
+Params: pin_a                   GPIO connected to rotary encoder channel A
+                                (default 4).
+        pin_b                   GPIO connected to rotary encoder channel B
+                                (default 17).
+        relative_axis           register a relative axis rather than an
+                                absolute one. Relative axis will only
+                                generate +1/-1 events on the input device,
+                                hence no steps need to be passed.
+        linux_axis              the input subsystem axis to map to this
+                                rotary encoder. Defaults to 0 (ABS_X / REL_X)
+        rollover                Automatic rollover when the rotary value
+                                becomes greater than the specified steps or
+                                smaller than 0. For absolute axis only.
+        steps-per-period        Number of steps (stable states) per period.
+                                The values have the following meaning:
+                                1: Full-period mode (default)
+                                2: Half-period mode
+                                4: Quarter-period mode
+        steps                   Number of steps in a full turnaround of the
+                                encoder. Only relevant for absolute axis.
+                                Defaults to 24 which is a typical value for
+                                such devices.
+        wakeup                  Boolean, rotary encoder can wake up the
+                                system.
+        encoding                String, the method used to encode steps.
+                                Supported are "gray" (the default and more
+                                common) and "binary".
+
+
+Name:   rpi-backlight
+Info:   Raspberry Pi official display backlight driver
+Load:   dtoverlay=rpi-backlight
+Params: <None>
+
+
+Name:   rpi-cirrus-wm5102
+Info:   Configures the Cirrus Logic Audio Card
+Load:   dtoverlay=rpi-cirrus-wm5102
+Params: <None>
+
+
+Name:   rpi-dac
+Info:   Configures the RPi DAC audio card
+Load:   dtoverlay=rpi-dac
+Params: <None>
+
+
+Name:   rpi-display
+Info:   RPi-Display - 2.8" Touch Display by Watterott
+Load:   dtoverlay=rpi-display,<param>=<val>
+Params: speed                   Display SPI bus speed
+        rotate                  Display rotation {0,90,180,270}
+        fps                     Delay between frame updates
+        debug                   Debug output level {0-7}
+        xohms                   Touchpanel sensitivity (X-plate resistance)
+        swapxy                  Swap x and y axis
+        backlight               Change backlight GPIO pin {e.g. 12, 18}
+
+
+Name:   rpi-ft5406
+Info:   Official Raspberry Pi display touchscreen
+Load:   dtoverlay=rpi-ft5406,<param>=<val>
+Params: touchscreen-size-x      Touchscreen X resolution (default 800)
+        touchscreen-size-y      Touchscreen Y resolution (default 600);
+        touchscreen-inverted-x  Invert touchscreen X coordinates (default 0);
+        touchscreen-inverted-y  Invert touchscreen Y coordinates (default 0);
+        touchscreen-swapped-x-y Swap X and Y cordinates (default 0);
+
+
+Name:   rpi-poe
+Info:   Raspberry Pi PoE HAT fan
+Load:   dtoverlay=rpi-poe,<param>[=<val>]
+Params: poe_fan_temp0           Temperature (in millicelcius) at which the fan
+                                turns on (default 40000)
+        poe_fan_temp0_hyst      Temperature delta (in millicelcius) at which
+                                the fan turns off (default 2000)
+        poe_fan_temp1           Temperature (in millicelcius) at which the fan
+                                speeds up (default 45000)
+        poe_fan_temp1_hyst      Temperature delta (in millicelcius) at which
+                                the fan slows down (default 2000)
+        poe_fan_temp2           Temperature (in millicelcius) at which the fan
+                                speeds up (default 50000)
+        poe_fan_temp2_hyst      Temperature delta (in millicelcius) at which
+                                the fan slows down (default 2000)
+        poe_fan_temp3           Temperature (in millicelcius) at which the fan
+                                speeds up (default 55000)
+        poe_fan_temp3_hyst      Temperature delta (in millicelcius) at which
+                                the fan slows down (default 5000)
+
+
+Name:   rpi-poe-plus
+Info:   Raspberry Pi PoE+ HAT fan
+Load:   dtoverlay=rpi-poe-plus,<param>[=<val>]
+Params: poe_fan_temp0           Temperature (in millicelcius) at which the fan
+                                turns on (default 40000)
+        poe_fan_temp0_hyst      Temperature delta (in millicelcius) at which
+                                the fan turns off (default 2000)
+        poe_fan_temp1           Temperature (in millicelcius) at which the fan
+                                speeds up (default 45000)
+        poe_fan_temp1_hyst      Temperature delta (in millicelcius) at which
+                                the fan slows down (default 2000)
+        poe_fan_temp2           Temperature (in millicelcius) at which the fan
+                                speeds up (default 50000)
+        poe_fan_temp2_hyst      Temperature delta (in millicelcius) at which
+                                the fan slows down (default 2000)
+        poe_fan_temp3           Temperature (in millicelcius) at which the fan
+                                speeds up (default 55000)
+        poe_fan_temp3_hyst      Temperature delta (in millicelcius) at which
+                                the fan slows down (default 5000)
+
+
+Name:   rpi-proto
+Info:   Configures the RPi Proto audio card
+Load:   dtoverlay=rpi-proto
+Params: <None>
+
+
+Name:   rpi-sense
+Info:   Raspberry Pi Sense HAT
+Load:   dtoverlay=rpi-sense
+Params: <None>
+
+
+Name:   rpi-tv
+Info:   Raspberry Pi TV HAT
+Load:   dtoverlay=rpi-tv
+Params: <None>
+
+
+Name:   rpivid-v4l2
+Info:   Load the V4L2 stateless video decoder driver for the HEVC block,
+        disabling the memory mapped devices in the process.
+Load:   dtoverlay=rpivid-v4l2
+Params: <None>
+
+
+Name:   rra-digidac1-wm8741-audio
+Info:   Configures the Red Rocks Audio DigiDAC1 soundcard
+Load:   dtoverlay=rra-digidac1-wm8741-audio
+Params: <None>
+
+
+Name:   sainsmart18
+Info:   Overlay for the SPI-connected Sainsmart 1.8" display (based on the
+        ST7735R chip).
+Load:   dtoverlay=sainsmart18,<param>=<val>
+Params: rotate                  Display rotation {0,90,180,270}
+        speed                   SPI bus speed in Hz (default 4000000)
+        fps                     Display frame rate in Hz
+        bgr                     Enable BGR mode (default off)
+        debug                   Debug output level {0-7}
+        dc_pin                  GPIO pin for D/C (default 24)
+        reset_pin               GPIO pin for RESET (default 25)
+
+
+Name:   sc16is750-i2c
+Info:   Overlay for the NXP SC16IS750 UART with I2C Interface
+        Enables the chip on I2C1 at 0x48 (or the "addr" parameter value). To
+        select another address, please refer to table 10 in reference manual.
+Load:   dtoverlay=sc16is750-i2c,<param>=<val>
+Params: int_pin                 GPIO used for IRQ (default 24)
+        addr                    Address (default 0x48)
+        xtal                    On-board crystal frequency (default 14745600)
+
+
+Name:   sc16is752-i2c
+Info:   Overlay for the NXP SC16IS752 dual UART with I2C Interface
+        Enables the chip on I2C1 at 0x48 (or the "addr" parameter value). To
+        select another address, please refer to table 10 in reference manual.
+Load:   dtoverlay=sc16is752-i2c,<param>=<val>
+Params: int_pin                 GPIO used for IRQ (default 24)
+        addr                    Address (default 0x48)
+        xtal                    On-board crystal frequency (default 14745600)
+
+
+Name:   sc16is752-spi0
+Info:   Overlay for the NXP SC16IS752 Dual UART with SPI Interface
+        Enables the chip on SPI0.
+Load:   dtoverlay=sc16is752-spi0,<param>=<val>
+Params: int_pin                 GPIO used for IRQ (default 24)
+        xtal                    On-board crystal frequency (default 14745600)
+
+
+Name:   sc16is752-spi1
+Info:   Overlay for the NXP SC16IS752 Dual UART with SPI Interface
+        Enables the chip on SPI1.
+        N.B.: spi1 is only accessible on devices with a 40pin header, eg:
+              A+, B+, Zero and PI2 B; as well as the Compute Module.
+
+Load:   dtoverlay=sc16is752-spi1,<param>=<val>
+Params: int_pin                 GPIO used for IRQ (default 24)
+        xtal                    On-board crystal frequency (default 14745600)
+
+
+Name:   sdhost
+Info:   Selects the bcm2835-sdhost SD/MMC driver, optionally with overclock.
+        N.B. This overlay is designed for situations where the mmc driver is
+        the default, so it disables the other (mmc) interface - this will kill
+        WiFi on a Pi3. If this isn't what you want, either use the sdtweak
+        overlay or the new sd_* dtparams of the base DTBs.
+Load:   dtoverlay=sdhost,<param>=<val>
+Params: overclock_50            Clock (in MHz) to use when the MMC framework
+                                requests 50MHz
+
+        force_pio               Disable DMA support (default off)
+
+        pio_limit               Number of blocks above which to use DMA
+                                (default 1)
+
+        debug                   Enable debug output (default off)
+
+
+Name:   sdio
+Info:   Selects the bcm2835-sdhost SD/MMC driver, optionally with overclock,
+        and enables SDIO via GPIOs 22-27. An example of use in 1-bit mode is
+        "dtoverlay=sdio,bus_width=1,gpios_22_25"
+Load:   dtoverlay=sdio,<param>=<val>
+Params: sdio_overclock          SDIO Clock (in MHz) to use when the MMC
+                                framework requests 50MHz
+
+        poll_once               Disable SDIO-device polling every second
+                                (default on: polling once at boot-time)
+
+        bus_width               Set the SDIO host bus width (default 4 bits)
+
+        gpios_22_25             Select GPIOs 22-25 for 1-bit mode. Must be used
+                                with bus_width=1. This replaces the sdio-1bit
+                                overlay, which is now deprecated.
+
+        gpios_34_37             Select GPIOs 34-37 for 1-bit mode. Must be used
+                                with bus_width=1.
+
+        gpios_34_39             Select GPIOs 34-39 for 4-bit mode. Must be used
+                                with bus_width=4 (the default).
+
+
+Name:   sdio-1bit
+Info:   This overlay is now deprecated. Use
+        "dtoverlay=sdio,bus_width=1,gpios_22_25" instead.
+Load:   <Deprecated>
+
+
+Name:   sdtweak
+Info:   This overlay is now deprecated. Use the sd_* dtparams in the
+        base DTB, e.g. "dtoverlay=sdtweak,poll_once" becomes
+        "dtparam=sd_poll_once".
+Load:   <Deprecated>
+
+
+Name:   seeed-can-fd-hat-v1
+Info:   Overlay for Seeed Studio CAN BUS FD HAT with two CAN FD
+        channels without RTC. Use this overlay if your HAT has no
+        battery holder.
+        https://www.seeedstudio.com/2-Channel-CAN-BUS-FD-Shield-for-Raspberry-Pi-p-4072.html
+Load:   dtoverlay=seeed-can-fd-hat-v1
+Params: <None>
+
+
+Name:   seeed-can-fd-hat-v2
+Info:   Overlay for Seeed Studio CAN BUS FD HAT with two CAN FD
+        channels and an RTC. Use this overlay if your HAT has a
+        battery holder.
+        https://www.seeedstudio.com/CAN-BUS-FD-HAT-for-Raspberry-Pi-p-4742.html
+Load:   dtoverlay=seeed-can-fd-hat-v2
+Params: <None>
+
+
+Name:   sh1106-spi
+Info:   Overlay for SH1106 OLED via SPI using fbtft staging driver.
+Load:   dtoverlay=sh1106-spi,<param>=<val>
+Params: speed                   SPI bus speed (default 4000000)
+        rotate                  Display rotation (0, 90, 180 or 270; default 0)
+        fps                     Delay between frame updates (default 25)
+        debug                   Debug output level (0-7; default 0)
+        dc_pin                  GPIO pin for D/C (default 24)
+        reset_pin               GPIO pin for RESET (default 25)
+        height                  Display height (32 or 64; default 64)
+
+
+Name:   si446x-spi0
+Info:   Overlay for Si446x UHF Transceiver via SPI using si446x driver.
+        The driver is currently out-of-tree at
+        https://github.com/sunipkmukherjee/silabs.git
+Load:   dtoverlay=si446x-spi0,<param>=<val>
+Params: speed                   SPI bus speed (default 4000000)
+        int_pin                 GPIO pin for interrupts (default 17)
+        reset_pin               GPIO pin for RESET (default 27)
+
+
+Name:   smi
+Info:   Enables the Secondary Memory Interface peripheral. Uses GPIOs 2-25!
+Load:   dtoverlay=smi
+Params: <None>
+
+
+Name:   smi-dev
+Info:   Enables the userspace interface for the SMI driver
+Load:   dtoverlay=smi-dev
+Params: <None>
+
+
+Name:   smi-nand
+Info:   Enables access to NAND flash via the SMI interface
+Load:   dtoverlay=smi-nand
+Params: <None>
+
+
+Name:   spi-gpio35-39
+Info:   Move SPI function block to GPIO 35 to 39
+Load:   dtoverlay=spi-gpio35-39
+Params: <None>
+
+
+Name:   spi-gpio40-45
+Info:   Move SPI function block to GPIOs 40 to 45
+Load:   dtoverlay=spi-gpio40-45
+Params: <None>
+
+
+Name:   spi-rtc
+Info:   Adds support for a number of SPI Real Time Clock devices
+Load:   dtoverlay=spi-rtc,<param>=<val>
+Params: ds3232                  Select the DS3232 device
+        ds3234                  Select the DS3234 device
+        pcf2123                 Select the PCF2123 device
+
+        spi0_0                  Use spi0.0 (default)
+        spi0_1                  Use spi0.1
+        spi1_0                  Use spi1.0
+        spi1_1                  Use spi1.1
+        spi2_0                  Use spi2.0
+        spi2_1                  Use spi2.1
+        cs_high                 This device requires an active-high CS
+
+
+Name:   spi0-1cs
+Info:   Only use one CS pin for SPI0
+Load:   dtoverlay=spi0-1cs,<param>=<val>
+Params: cs0_pin                 GPIO pin for CS0 (default 8)
+        no_miso                 Don't claim and use the MISO pin (9), freeing
+                                it for other uses.
+
+
+Name:   spi0-2cs
+Info:   Change the CS pins for SPI0
+Load:   dtoverlay=spi0-2cs,<param>=<val>
+Params: cs0_pin                 GPIO pin for CS0 (default 8)
+        cs1_pin                 GPIO pin for CS1 (default 7)
+        no_miso                 Don't claim and use the MISO pin (9), freeing
+                                it for other uses.
+
+
+Name:   spi0-cs
+Info:   This overlay has been renamed spi0-2cs, keeping spi0-cs as an
+        alias for backwards compatibility.
+Load:   <Deprecated>
+
+
+Name:   spi0-hw-cs
+Info:   This overlay has been deprecated and removed because it is no longer
+        necessary and has been seen to prevent spi0 from working.
+Load:   <Deprecated>
+
+
+Name:   spi1-1cs
+Info:   Enables spi1 with a single chip select (CS) line and associated spidev
+        dev node. The gpio pin number for the CS line and spidev device node
+        creation are configurable.
+        N.B.: spi1 is only accessible on devices with a 40pin header, eg:
+              A+, B+, Zero and PI2 B; as well as the Compute Module.
+Load:   dtoverlay=spi1-1cs,<param>=<val>
+Params: cs0_pin                 GPIO pin for CS0 (default 18 - BCM SPI1_CE0).
+        cs0_spidev              Set to 'disabled' to stop the creation of a
+                                userspace device node /dev/spidev1.0 (default
+                                is 'okay' or enabled).
+
+
+Name:   spi1-2cs
+Info:   Enables spi1 with two chip select (CS) lines and associated spidev
+        dev nodes. The gpio pin numbers for the CS lines and spidev device node
+        creation are configurable.
+        N.B.: spi1 is only accessible on devices with a 40pin header, eg:
+              A+, B+, Zero and PI2 B; as well as the Compute Module.
+Load:   dtoverlay=spi1-2cs,<param>=<val>
+Params: cs0_pin                 GPIO pin for CS0 (default 18 - BCM SPI1_CE0).
+        cs1_pin                 GPIO pin for CS1 (default 17 - BCM SPI1_CE1).
+        cs0_spidev              Set to 'disabled' to stop the creation of a
+                                userspace device node /dev/spidev1.0 (default
+                                is 'okay' or enabled).
+        cs1_spidev              Set to 'disabled' to stop the creation of a
+                                userspace device node /dev/spidev1.1 (default
+                                is 'okay' or enabled).
+
+
+Name:   spi1-3cs
+Info:   Enables spi1 with three chip select (CS) lines and associated spidev
+        dev nodes. The gpio pin numbers for the CS lines and spidev device node
+        creation are configurable.
+        N.B.: spi1 is only accessible on devices with a 40pin header, eg:
+              A+, B+, Zero and PI2 B; as well as the Compute Module.
+Load:   dtoverlay=spi1-3cs,<param>=<val>
+Params: cs0_pin                 GPIO pin for CS0 (default 18 - BCM SPI1_CE0).
+        cs1_pin                 GPIO pin for CS1 (default 17 - BCM SPI1_CE1).
+        cs2_pin                 GPIO pin for CS2 (default 16 - BCM SPI1_CE2).
+        cs0_spidev              Set to 'disabled' to stop the creation of a
+                                userspace device node /dev/spidev1.0 (default
+                                is 'okay' or enabled).
+        cs1_spidev              Set to 'disabled' to stop the creation of a
+                                userspace device node /dev/spidev1.1 (default
+                                is 'okay' or enabled).
+        cs2_spidev              Set to 'disabled' to stop the creation of a
+                                userspace device node /dev/spidev1.2 (default
+                                is 'okay' or enabled).
+
+
+Name:   spi2-1cs
+Info:   Enables spi2 with a single chip select (CS) line and associated spidev
+        dev node. The gpio pin number for the CS line and spidev device node
+        creation are configurable.
+        N.B.: spi2 is only accessible with the Compute Module.
+Load:   dtoverlay=spi2-1cs,<param>=<val>
+Params: cs0_pin                 GPIO pin for CS0 (default 43 - BCM SPI2_CE0).
+        cs0_spidev              Set to 'disabled' to stop the creation of a
+                                userspace device node /dev/spidev2.0 (default
+                                is 'okay' or enabled).
+
+
+Name:   spi2-2cs
+Info:   Enables spi2 with two chip select (CS) lines and associated spidev
+        dev nodes. The gpio pin numbers for the CS lines and spidev device node
+        creation are configurable.
+        N.B.: spi2 is only accessible with the Compute Module.
+Load:   dtoverlay=spi2-2cs,<param>=<val>
+Params: cs0_pin                 GPIO pin for CS0 (default 43 - BCM SPI2_CE0).
+        cs1_pin                 GPIO pin for CS1 (default 44 - BCM SPI2_CE1).
+        cs0_spidev              Set to 'disabled' to stop the creation of a
+                                userspace device node /dev/spidev2.0 (default
+                                is 'okay' or enabled).
+        cs1_spidev              Set to 'disabled' to stop the creation of a
+                                userspace device node /dev/spidev2.1 (default
+                                is 'okay' or enabled).
+
+
+Name:   spi2-3cs
+Info:   Enables spi2 with three chip select (CS) lines and associated spidev
+        dev nodes. The gpio pin numbers for the CS lines and spidev device node
+        creation are configurable.
+        N.B.: spi2 is only accessible with the Compute Module.
+Load:   dtoverlay=spi2-3cs,<param>=<val>
+Params: cs0_pin                 GPIO pin for CS0 (default 43 - BCM SPI2_CE0).
+        cs1_pin                 GPIO pin for CS1 (default 44 - BCM SPI2_CE1).
+        cs2_pin                 GPIO pin for CS2 (default 45 - BCM SPI2_CE2).
+        cs0_spidev              Set to 'disabled' to stop the creation of a
+                                userspace device node /dev/spidev2.0 (default
+                                is 'okay' or enabled).
+        cs1_spidev              Set to 'disabled' to stop the creation of a
+                                userspace device node /dev/spidev2.1 (default
+                                is 'okay' or enabled).
+        cs2_spidev              Set to 'disabled' to stop the creation of a
+                                userspace device node /dev/spidev2.2 (default
+                                is 'okay' or enabled).
+
+
+Name:   spi3-1cs
+Info:   Enables spi3 with a single chip select (CS) line and associated spidev
+        dev node. The gpio pin number for the CS line and spidev device node
+        creation are configurable. BCM2711 only.
+Load:   dtoverlay=spi3-1cs,<param>=<val>
+Params: cs0_pin                 GPIO pin for CS0 (default 0 - BCM SPI3_CE0).
+        cs0_spidev              Set to 'off' to prevent the creation of a
+                                userspace device node /dev/spidev3.0 (default
+                                is 'on' or enabled).
+
+
+Name:   spi3-2cs
+Info:   Enables spi3 with two chip select (CS) lines and associated spidev
+        dev nodes. The gpio pin numbers for the CS lines and spidev device node
+        creation are configurable. BCM2711 only.
+Load:   dtoverlay=spi3-2cs,<param>=<val>
+Params: cs0_pin                 GPIO pin for CS0 (default 0 - BCM SPI3_CE0).
+        cs1_pin                 GPIO pin for CS1 (default 24 - BCM SPI3_CE1).
+        cs0_spidev              Set to 'off' to prevent the creation of a
+                                userspace device node /dev/spidev3.0 (default
+                                is 'on' or enabled).
+        cs1_spidev              Set to 'off' to prevent the creation of a
+                                userspace device node /dev/spidev3.1 (default
+                                is 'on' or enabled).
+
+
+Name:   spi4-1cs
+Info:   Enables spi4 with a single chip select (CS) line and associated spidev
+        dev node. The gpio pin number for the CS line and spidev device node
+        creation are configurable. BCM2711 only.
+Load:   dtoverlay=spi4-1cs,<param>=<val>
+Params: cs0_pin                 GPIO pin for CS0 (default 4 - BCM SPI4_CE0).
+        cs0_spidev              Set to 'off' to prevent the creation of a
+                                userspace device node /dev/spidev4.0 (default
+                                is 'on' or enabled).
+
+
+Name:   spi4-2cs
+Info:   Enables spi4 with two chip select (CS) lines and associated spidev
+        dev nodes. The gpio pin numbers for the CS lines and spidev device node
+        creation are configurable. BCM2711 only.
+Load:   dtoverlay=spi4-2cs,<param>=<val>
+Params: cs0_pin                 GPIO pin for CS0 (default 4 - BCM SPI4_CE0).
+        cs1_pin                 GPIO pin for CS1 (default 25 - BCM SPI4_CE1).
+        cs0_spidev              Set to 'off' to prevent the creation of a
+                                userspace device node /dev/spidev4.0 (default
+                                is 'on' or enabled).
+        cs1_spidev              Set to 'off' to prevent the creation of a
+                                userspace device node /dev/spidev4.1 (default
+                                is 'on' or enabled).
+
+
+Name:   spi5-1cs
+Info:   Enables spi5 with a single chip select (CS) line and associated spidev
+        dev node. The gpio pin numbers for the CS lines and spidev device node
+        creation are configurable. BCM2711 only.
+Load:   dtoverlay=spi5-1cs,<param>=<val>
+Params: cs0_pin                 GPIO pin for CS0 (default 12 - BCM SPI5_CE0).
+        cs0_spidev              Set to 'off' to prevent the creation of a
+                                userspace device node /dev/spidev5.0 (default
+                                is 'on' or enabled).
+
+
+Name:   spi5-2cs
+Info:   Enables spi5 with two chip select (CS) lines and associated spidev
+        dev nodes. The gpio pin numbers for the CS lines and spidev device node
+        creation are configurable. BCM2711 only.
+Load:   dtoverlay=spi5-2cs,<param>=<val>
+Params: cs0_pin                 GPIO pin for CS0 (default 12 - BCM SPI5_CE0).
+        cs1_pin                 GPIO pin for CS1 (default 26 - BCM SPI5_CE1).
+        cs0_spidev              Set to 'off' to prevent the creation of a
+                                userspace device node /dev/spidev5.0 (default
+                                is 'on' or enabled).
+        cs1_spidev              Set to 'off' to prevent the creation of a
+                                userspace device node /dev/spidev5.1 (default
+                                is 'on' or enabled).
+
+
+Name:   spi6-1cs
+Info:   Enables spi6 with a single chip select (CS) line and associated spidev
+        dev node. The gpio pin number for the CS line and spidev device node
+        creation are configurable. BCM2711 only.
+Load:   dtoverlay=spi6-1cs,<param>=<val>
+Params: cs0_pin                 GPIO pin for CS0 (default 18 - BCM SPI6_CE0).
+        cs0_spidev              Set to 'off' to prevent the creation of a
+                                userspace device node /dev/spidev6.0 (default
+                                is 'on' or enabled).
+
+
+Name:   spi6-2cs
+Info:   Enables spi6 with two chip select (CS) lines and associated spidev
+        dev nodes. The gpio pin numbers for the CS lines and spidev device node
+        creation are configurable. BCM2711 only.
+Load:   dtoverlay=spi6-2cs,<param>=<val>
+Params: cs0_pin                 GPIO pin for CS0 (default 18 - BCM SPI6_CE0).
+        cs1_pin                 GPIO pin for CS1 (default 27 - BCM SPI6_CE1).
+        cs0_spidev              Set to 'off' to prevent the creation of a
+                                userspace device node /dev/spidev6.0 (default
+                                is 'on' or enabled).
+        cs1_spidev              Set to 'off' to prevent the creation of a
+                                userspace device node /dev/spidev6.1 (default
+                                is 'on' or enabled).
+
+
+Name:   ssd1306
+Info:   Overlay for activation of SSD1306 over I2C OLED display framebuffer.
+Load:   dtoverlay=ssd1306,<param>=<val>
+Params: address                 Location in display memory of first character.
+                                (default=0)
+        width                   Width of display. (default=128)
+        height                  Height of display. (default=64)
+        offset                  virtual channel a. (default=0)
+        normal                  Has no effect on displays tested. (default=not
+                                set)
+        sequential              Set this if every other scan line is missing.
+                                (default=not set)
+        remapped                Set this if display is garbled. (default=not
+                                set)
+        inverted                Set this if display is inverted and mirrored.
+                                (default=not set)
+
+        Examples:
+        Typical usage for 128x64 display: dtoverlay=ssd1306,inverted
+
+        Typical usage for 128x32 display: dtoverlay=ssd1306,inverted,sequential
+
+        i2c_baudrate=400000 will speed up the display.
+
+        i2c_baudrate=1000000 seems to work even though it's not officially
+        supported by the hardware, and is faster still.
+
+        For more information refer to the device datasheet at:
+        https://cdn-shop.adafruit.com/datasheets/SSD1306.pdf
+
+
+Name:   ssd1306-spi
+Info:   Overlay for SSD1306 OLED via SPI using fbtft staging driver.
+Load:   dtoverlay=ssd1306-spi,<param>=<val>
+Params: speed                   SPI bus speed (default 10000000)
+        rotate                  Display rotation (0, 90, 180 or 270; default 0)
+        fps                     Delay between frame updates (default 25)
+        debug                   Debug output level (0-7; default 0)
+        dc_pin                  GPIO pin for D/C (default 24)
+        reset_pin               GPIO pin for RESET (default 25)
+        height                  Display height (32 or 64; default 64)
+
+
+Name:   ssd1331-spi
+Info:   Overlay for SSD1331 OLED via SPI using fbtft staging driver.
+Load:   dtoverlay=ssd1331-spi,<param>=<val>
+Params: speed                   SPI bus speed (default 4500000)
+        rotate                  Display rotation (0, 90, 180 or 270; default 0)
+        fps                     Delay between frame updates (default 25)
+        debug                   Debug output level (0-7; default 0)
+        dc_pin                  GPIO pin for D/C (default 24)
+        reset_pin               GPIO pin for RESET (default 25)
+
+
+Name:   ssd1351-spi
+Info:   Overlay for SSD1351 OLED via SPI using fbtft staging driver.
+Load:   dtoverlay=ssd1351-spi,<param>=<val>
+Params: speed                   SPI bus speed (default 4500000)
+        rotate                  Display rotation (0, 90, 180 or 270; default 0)
+        fps                     Delay between frame updates (default 25)
+        debug                   Debug output level (0-7; default 0)
+        dc_pin                  GPIO pin for D/C (default 24)
+        reset_pin               GPIO pin for RESET (default 25)
+
+
+Name:   superaudioboard
+Info:   Configures the SuperAudioBoard sound card
+Load:   dtoverlay=superaudioboard,<param>=<val>
+Params: gpiopin                 GPIO pin for codec reset
+
+
+Name:   sx150x
+Info:   Configures the Semtech SX150X I2C GPIO expanders.
+Load:   dtoverlay=sx150x,<param>=<val>
+Params: sx150<x>-<n>-<m>        Enables SX150X device on I2C#<n> with slave
+                                address <m>. <x> may be 1-9. <n> may be 0 or 1.
+                                Permissible values of <m> (which is denoted in
+                                hex) depend on the device variant. For SX1501,
+                                SX1502, SX1504 and SX1505, <m> may be 20 or 21.
+                                For SX1503 and SX1506, <m> may be 20. For
+                                SX1507 and SX1509, <m> may be 3E, 3F, 70 or 71.
+                                For SX1508, <m> may be 20, 21, 22 or 23.
+
+        sx150<x>-<n>-<m>-int-gpio
+                                Integer, enables interrupts on SX150X device on
+                                I2C#<n> with slave address <m>, specifies
+                                the GPIO pin to which NINT output of SX150X is
+                                connected.
+
+
+Name:   tc358743
+Info:   Toshiba TC358743 HDMI to CSI-2 bridge chip.
+        Uses Unicam 1, which is the standard camera connector on most Pi
+        variants.
+Load:   dtoverlay=tc358743,<param>=<val>
+Params: 4lane                   Use 4 lanes (only applicable to Compute Modules
+                                CAM1 connector).
+
+        link-frequency          Set the link frequency. Only values of 297000000
+                                (574Mbit/s) and 486000000 (972Mbit/s - default)
+                                are supported by the driver.
+
+
+Name:   tc358743-audio
+Info:   Used in combination with the tc358743-fast overlay to route the audio
+        from the TC358743 over I2S to the Pi.
+        Wiring is LRCK/WFS to GPIO 19, BCK/SCK to GPIO 18, and DATA/SD to GPIO
+        20.
+Load:   dtoverlay=tc358743-audio,<param>=<val>
+Params: card-name               Override the default, "tc358743", card name.
+
+
+Name:   tinylcd35
+Info:   3.5" Color TFT Display by www.tinylcd.com
+        Options: Touch, RTC, keypad
+Load:   dtoverlay=tinylcd35,<param>=<val>
+Params: speed                   Display SPI bus speed
+
+        rotate                  Display rotation {0,90,180,270}
+
+        fps                     Delay between frame updates
+
+        debug                   Debug output level {0-7}
+
+        touch                   Enable touch panel
+
+        touchgpio               Touch controller IRQ GPIO
+
+        xohms                   Touchpanel: Resistance of X-plate in ohms
+
+        rtc-pcf                 PCF8563 Real Time Clock
+
+        rtc-ds                  DS1307 Real Time Clock
+
+        keypad                  Enable keypad
+
+        Examples:
+            Display with touchpanel, PCF8563 RTC and keypad:
+                dtoverlay=tinylcd35,touch,rtc-pcf,keypad
+            Old touch display:
+                dtoverlay=tinylcd35,touch,touchgpio=3
+
+
+Name:   tpm-slb9670
+Info:   Enables support for Infineon SLB9670 Trusted Platform Module add-on
+        boards, which can be used as a secure key storage and hwrng,
+        available as "Iridium SLB9670" by Infineon and "LetsTrust TPM" by pi3g.
+Load:   dtoverlay=tpm-slb9670
+Params: <None>
+
+
+Name:   uart0
+Info:   Change the pin usage of uart0
+Load:   dtoverlay=uart0,<param>=<val>
+Params: txd0_pin                GPIO pin for TXD0 (14, 32 or 36 - default 14)
+
+        rxd0_pin                GPIO pin for RXD0 (15, 33 or 37 - default 15)
+
+        pin_func                Alternative pin function - 4(Alt0) for 14&15,
+                                7(Alt3) for 32&33, 6(Alt2) for 36&37
+
+
+Name:   uart1
+Info:   Change the pin usage of uart1
+Load:   dtoverlay=uart1,<param>=<val>
+Params: txd1_pin                GPIO pin for TXD1 (14, 32 or 40 - default 14)
+
+        rxd1_pin                GPIO pin for RXD1 (15, 33 or 41 - default 15)
+
+
+Name:   uart2
+Info:   Enable uart 2 on GPIOs 0-3. BCM2711 only.
+Load:   dtoverlay=uart2,<param>
+Params: ctsrts                  Enable CTS/RTS on GPIOs 2-3 (default off)
+
+
+Name:   uart3
+Info:   Enable uart 3 on GPIOs 4-7. BCM2711 only.
+Load:   dtoverlay=uart3,<param>
+Params: ctsrts                  Enable CTS/RTS on GPIOs 6-7 (default off)
+
+
+Name:   uart4
+Info:   Enable uart 4 on GPIOs 8-11. BCM2711 only.
+Load:   dtoverlay=uart4,<param>
+Params: ctsrts                  Enable CTS/RTS on GPIOs 10-11 (default off)
+
+
+Name:   uart5
+Info:   Enable uart 5 on GPIOs 12-15. BCM2711 only.
+Load:   dtoverlay=uart5,<param>
+Params: ctsrts                  Enable CTS/RTS on GPIOs 14-15 (default off)
+
+
+Name:   udrc
+Info:   Configures the NW Digital Radio UDRC Hat
+Load:   dtoverlay=udrc,<param>=<val>
+Params: alsaname                Name of the ALSA audio device (default "udrc")
+
+
+Name:   ugreen-dabboard
+Info:   Configures the ugreen-dabboard I2S overlay
+        This is a simple overlay based on the simple-audio-card and the dmic
+        codec. It has the speciality that it is configured to use the codec
+        as a master I2S device. It works for example with the Si468x DAB
+        receiver on the uGreen DABBoard.
+Load:   dtoverlay=ugreen-dabboard,<param>=<val>
+Params: card-name               Override the default, "dabboard", card name.
+
+
+Name:   upstream
+Info:   Allow usage of downstream .dtb with upstream kernel. Comprises the
+        vc4-kms-v3d and dwc2 overlays.
+Load:   dtoverlay=upstream
+Params: <None>
+
+
+Name:   upstream-aux-interrupt
+Info:   This overlay has been deprecated and removed because it is no longer
+        necessary.
+Load:   <Deprecated>
+
+
+Name:   upstream-pi4
+Info:   Allow usage of downstream .dtb with upstream kernel on Pi 4. Comprises
+        the vc4-kms-v3d-pi4 and dwc2 overlays.
+Load:   dtoverlay=upstream-pi4
+Params: <None>
+
+
+Name:   vc4-fkms-v3d
+Info:   Enable Eric Anholt's DRM VC4 V3D driver on top of the dispmanx
+        display stack.
+Load:   dtoverlay=vc4-fkms-v3d,<param>
+Params: cma-512                 CMA is 512MB (needs 1GB)
+        cma-448                 CMA is 448MB (needs 1GB)
+        cma-384                 CMA is 384MB (needs 1GB)
+        cma-320                 CMA is 320MB (needs 1GB)
+        cma-256                 CMA is 256MB (needs 1GB)
+        cma-192                 CMA is 192MB (needs 1GB)
+        cma-128                 CMA is 128MB
+        cma-96                  CMA is 96MB
+        cma-64                  CMA is 64MB
+        cma-size                CMA size in bytes, 4MB aligned
+        cma-default             Use upstream's default value
+
+
+Name:   vc4-kms-dpi-at056tn53v1
+Info:   Enable an Innolux 5.6in VGA TFT connected to DPI interface under KMS.
+        Requires vc4-kms-v3d to be loaded.
+Load:   dtoverlay=vc4-kms-dpi-at056tn53v1
+Params: <None>
+
+
+Name:   vc4-kms-dsi-7inch
+Info:   Enable the Raspberry Pi DSI 7" screen.
+        Use edt-ft5406 for the touchscreen element.
+        Requires vc4-kms-v3d to be loaded.
+Load:   dtoverlay=vc4-kms-dsi-7inch
+Params: <None>
+
+
+Name:   vc4-kms-dsi-lt070me05000
+Info:   Enable a JDI LT070ME05000 DSI display on DSI1.
+        Note that this is a 4 lane DSI device, so it will only work on a Compute
+        Module.
+        Requires vc4-kms-v3d to be loaded.
+Load:   dtoverlay=vc4-kms-dsi-lt070me05000,<param>
+Params: reset                   GPIO for the reset signal (default 17)
+        enable                  GPIO for the enable signal (default 4)
+        dcdc-en                 GPIO for the DC-DC converter enable (default 5)
+
+
+Name:   vc4-kms-dsi-lt070me05000-v2
+Info:   Enable a JDI LT070ME05000 DSI display on DSI1 using Harlab's V2
+        interface board.
+        Note that this is a 4 lane DSI device, so it will only work on a Compute
+        Module.
+        Requires vc4-kms-v3d to be loaded.
+Load:   dtoverlay=vc4-kms-dsi-lt070me05000-v2
+Params: <None>
+
+
+Name:   vc4-kms-kippah-7inch
+Info:   Enable the Adafruit DPI Kippah with the 7" Ontat panel attached.
+        Requires vc4-kms-v3d to be loaded.
+Load:   dtoverlay=vc4-kms-kippah-7inch
+Params: <None>
+
+
+Name:   vc4-kms-v3d
+Info:   Enable Eric Anholt's DRM VC4 HDMI/HVS/V3D driver.
+Load:   dtoverlay=vc4-kms-v3d,<param>
+Params: cma-512                 CMA is 512MB (needs 1GB)
+        cma-448                 CMA is 448MB (needs 1GB)
+        cma-384                 CMA is 384MB (needs 1GB)
+        cma-320                 CMA is 320MB (needs 1GB)
+        cma-256                 CMA is 256MB (needs 1GB)
+        cma-192                 CMA is 192MB (needs 1GB)
+        cma-128                 CMA is 128MB
+        cma-96                  CMA is 96MB
+        cma-64                  CMA is 64MB
+        cma-size                CMA size in bytes, 4MB aligned
+        cma-default             Use upstream's default value
+        audio                   Enable or disable audio over HDMI (default "on")
+        noaudio                 Disable all HDMI audio (default "off")
+        nocomposite             Disable the composite video output (default
+                                "off")
+
+
+Name:   vc4-kms-v3d-pi4
+Info:   Enable Eric Anholt's DRM VC4 HDMI/HVS/V3D driver for Pi4.
+Load:   dtoverlay=vc4-kms-v3d-pi4,<param>
+Params: cma-512                 CMA is 512MB
+        cma-448                 CMA is 448MB
+        cma-384                 CMA is 384MB
+        cma-320                 CMA is 320MB
+        cma-256                 CMA is 256MB
+        cma-192                 CMA is 192MB
+        cma-128                 CMA is 128MB
+        cma-96                  CMA is 96MB
+        cma-64                  CMA is 64MB
+        cma-size                CMA size in bytes, 4MB aligned
+        cma-default             Use upstream's default value
+        audio                   Enable or disable audio over HDMI0 (default
+                                "on")
+        audio1                  Enable or disable audio over HDMI1 (default
+                                "on")
+        noaudio                 Disable all HDMI audio (default "off")
+        composite               Enable the composite output (disables all other
+                                outputs)
+
+
+Name:   vc4-kms-vga666
+Info:   Enable the VGA666 (resistor ladder ADC) for the vc4-kms-v3d driver.
+        Requires vc4-kms-v3d to be loaded.
+Load:   dtoverlay=vc4-kms-vga666,<param>
+Params: ddc                     Enables GPIOs 0&1 as the I2C to read the EDID
+                                from the display. NB These are NOT 5V tolerant
+                                GPIOs, therefore level shifters are required.
+
+
+Name:   vga666
+Info:   Overlay for the Fen Logic VGA666 board
+        This uses GPIOs 2-21 (so no I2C), and activates the output 2-3 seconds
+        after the kernel has started.
+        NOT for use with vc4-kms-v3d.
+Load:   dtoverlay=vga666
+Params: <None>
+
+
+Name:   w1-gpio
+Info:   Configures the w1-gpio Onewire interface module.
+        Use this overlay if you *don't* need a GPIO to drive an external pullup.
+Load:   dtoverlay=w1-gpio,<param>=<val>
+Params: gpiopin                 GPIO for I/O (default "4")
+        pullup                  Now enabled by default (ignored)
+
+
+Name:   w1-gpio-pullup
+Info:   Configures the w1-gpio Onewire interface module.
+        Use this overlay if you *do* need a GPIO to drive an external pullup.
+Load:   dtoverlay=w1-gpio-pullup,<param>=<val>
+Params: gpiopin                 GPIO for I/O (default "4")
+        extpullup               GPIO for external pullup (default "5")
+        pullup                  Now enabled by default (ignored)
+
+
+Name:   w5500
+Info:   Overlay for the Wiznet W5500 Ethernet Controller on SPI0
+Load:   dtoverlay=w5500,<param>=<val>
+Params: int_pin                 GPIO used for INT (default 25)
+
+        speed                   SPI bus speed (default 30000000)
+
+        cs                      SPI bus Chip Select (default 0)
+
+
+Name:   wittypi
+Info:   Configures the wittypi RTC module.
+Load:   dtoverlay=wittypi,<param>=<val>
+Params: led_gpio                GPIO for LED (default "17")
+        led_trigger             Choose which activity the LED tracks (default
+                                "default-on")
+
+
+Name:   wm8960-soundcard
+Info:   Overlay for the Waveshare wm8960 soundcard
+Load:   dtoverlay=wm8960-soundcard,<param>=<val>
+Params: alsaname                Changes the card name in ALSA
+        compatible              Changes the codec compatibility
+
+
+Troubleshooting
+===============
+
+If you are experiencing problems that you think are DT-related, enable DT
+diagnostic output by adding this to /boot/config.txt:
+
+    dtdebug=on
+
+and rebooting. Then run:
+
+    sudo vcdbg log msg
+
+and look for relevant messages.
+
+Further reading
+===============
+
+This is only meant to be a quick introduction to the subject of Device Tree on
+Raspberry Pi. There is a more complete explanation here:
+
+http://www.raspberrypi.org/documentation/configuration/device-tree.md
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/rotary-encoder-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/rotary-encoder-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/rotary-encoder-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/rotary-encoder-overlay.dts	2021-07-25 16:45:35.158807390 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// Device tree overlay for GPIO connected rotary encoder.
+/dts-v1/;
+/plugin/;
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&gpio>;
+		__overlay__ {
+			rotary_pins: rotary_pins@4 {
+				brcm,pins = <4 17>; /* gpio 4 17 */
+				brcm,function = <0 0>; /* input */
+				brcm,pull = <2 2>; /* pull-up */
+			};
+
+		};
+	};
+
+	fragment@1 {
+		target-path = "/";
+		__overlay__ {
+			rotary: rotary@4 {
+				compatible = "rotary-encoder";
+				status = "okay";
+				pinctrl-names = "default";
+				pinctrl-0 = <&rotary_pins>;
+				gpios = <&gpio 4 0>, <&gpio 17 0>;
+				linux,axis = <0>; /* REL_X */
+				rotary-encoder,encoding = "gray";
+				rotary-encoder,steps = <24>; /* 24 default */
+				rotary-encoder,steps-per-period = <1>; /* corresponds to full period mode. See README */
+			};
+		};
+
+	};  
+
+	__overrides__ {
+		pin_a =		    <&rotary>,"gpios:4",
+				    <&rotary_pins>,"brcm,pins:0",
+				    /* modify reg values to allow multiple instantiation */
+				    <&rotary>,"reg:0",
+				    <&rotary_pins>,"reg:0";
+		pin_b =		    <&rotary>,"gpios:16",
+				    <&rotary_pins>,"brcm,pins:4";
+		relative_axis =     <&rotary>,"rotary-encoder,relative-axis?";
+		linux_axis =        <&rotary>,"linux,axis:0";
+		rollover =          <&rotary>,"rotary-encoder,rollover?";
+		steps-per-period =  <&rotary>,"rotary-encoder,steps-per-period:0";
+		steps =             <&rotary>,"rotary-encoder,steps:0";
+		wakeup =            <&rotary>,"wakeup-source?";
+		encoding =          <&rotary>,"rotary-encoder,encoding";
+                /* legacy parameters*/
+		rotary0_pin_a =     <&rotary>,"gpios:4",
+		                    <&rotary_pins>,"brcm,pins:0";
+		rotary0_pin_b =     <&rotary>,"gpios:16",
+		                    <&rotary_pins>,"brcm,pins:4";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/rpi-backlight-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/rpi-backlight-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/rpi-backlight-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/rpi-backlight-overlay.dts	2021-07-25 16:45:35.158807390 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/*
+ * Devicetree overlay for mailbox-driven Raspberry Pi DSI Display
+ * backlight controller
+ */
+/dts-v1/;
+/plugin/;
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target-path = "/";
+		__overlay__ {
+			rpi_backlight: rpi_backlight {
+				compatible = "raspberrypi,rpi-backlight";
+				firmware = <&firmware>;
+				status = "okay";
+			};
+		};
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/rpi-cirrus-wm5102-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/rpi-cirrus-wm5102-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/rpi-cirrus-wm5102-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/rpi-cirrus-wm5102-overlay.dts	2021-07-25 16:45:35.158807390 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// Definitions for the Cirrus Logic Audio Card
+/dts-v1/;
+/plugin/;
+#include <dt-bindings/pinctrl/bcm2835.h>
+#include <dt-bindings/gpio/gpio.h>
+#include <dt-bindings/mfd/arizona.h>
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&i2s>;
+		__overlay__ {
+			status = "okay";
+		};
+	};
+
+	fragment@1 {
+		target = <&gpio>;
+		__overlay__ {
+			wlf_5102_pins: wlf_5102_pins {
+				brcm,pins = <17 22 27>;
+				brcm,function = <
+					BCM2835_FSEL_GPIO_OUT
+					BCM2835_FSEL_GPIO_OUT
+					BCM2835_FSEL_GPIO_IN
+				>;
+			};
+			wlf_8804_pins: wlf_8804_pins {
+				brcm,pins = <8>;
+				brcm,function = <BCM2835_FSEL_GPIO_OUT>;
+			};
+		};
+	};
+
+	fragment@2 {
+		target = <&spi0_cs_pins>;
+		__overlay__ {
+			brcm,pins = <7>;
+			brcm,function = <BCM2835_FSEL_GPIO_OUT>;
+		};
+	};
+
+
+	fragment@3 {
+		target-path = "/";
+		__overlay__ {
+			rpi_cirrus_reg_1v8: rpi_cirrus_reg_1v8 {
+				compatible = "regulator-fixed";
+				regulator-name = "RPi-Cirrus 1v8";
+				regulator-min-microvolt = <1800000>;
+				regulator-max-microvolt = <1800000>;
+				regulator-always-on;
+			};
+		};
+	};
+
+	fragment@4 {
+		target = <&spidev0>;
+		__overlay__ {
+			status = "disabled";
+		};
+	};
+
+	fragment@5 {
+		target = <&spidev1>;
+		__overlay__ {
+			status = "disabled";
+		};
+	};
+
+	fragment@6 {
+		target = <&spi0>;
+		__overlay__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			status = "okay";
+			cs-gpios = <&gpio 7 GPIO_ACTIVE_LOW>;
+
+			wm5102@0{
+				compatible = "wlf,wm5102";
+				reg = <0>;
+
+				pinctrl-names = "default";
+				pinctrl-0 = <&wlf_5102_pins>;
+
+				spi-max-frequency = <500000>;
+
+				interrupt-parent = <&gpio>;
+				interrupts = <27 8>;
+				interrupt-controller;
+				#interrupt-cells = <2>;
+
+				gpio-controller;
+				#gpio-cells = <2>;
+
+				LDOVDD-supply = <&rpi_cirrus_reg_1v8>;
+				AVDD-supply = <&rpi_cirrus_reg_1v8>;
+				DBVDD1-supply = <&rpi_cirrus_reg_1v8>;
+				DBVDD2-supply = <&vdd_3v3_reg>;
+				DBVDD3-supply = <&vdd_3v3_reg>;
+				CPVDD-supply = <&rpi_cirrus_reg_1v8>;
+				SPKVDDL-supply = <&vdd_5v0_reg>;
+				SPKVDDR-supply = <&vdd_5v0_reg>;
+				DCVDD-supply = <&arizona_ldo1>;
+
+				reset-gpios = <&gpio 17 GPIO_ACTIVE_HIGH>;
+				wlf,ldoena = <&gpio 22 GPIO_ACTIVE_HIGH>;
+				wlf,gpio-defaults = <
+					ARIZONA_GP_DEFAULT
+					ARIZONA_GP_DEFAULT
+					ARIZONA_GP_DEFAULT
+					ARIZONA_GP_DEFAULT
+					ARIZONA_GP_DEFAULT
+				>;
+				wlf,micd-configs = <0 1 0>;
+				wlf,dmic-ref = <
+					ARIZONA_DMIC_MICVDD
+					ARIZONA_DMIC_MICBIAS2
+					ARIZONA_DMIC_MICVDD
+					ARIZONA_DMIC_MICVDD
+				>;
+				wlf,inmode = <
+					ARIZONA_INMODE_DIFF
+					ARIZONA_INMODE_DMIC
+					ARIZONA_INMODE_SE
+					ARIZONA_INMODE_DIFF
+				>;
+				status = "okay";
+
+				arizona_ldo1: ldo1 {
+					regulator-name = "LDO1";
+					// default constraints as in
+					// arizona-ldo1.c
+					regulator-min-microvolt = <1200000>;
+					regulator-max-microvolt = <1800000>;
+				};
+			};
+		};
+	};
+
+	fragment@7 {
+		target = <&i2c1>;
+		__overlay__ {
+			status = "okay";
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			wm8804@3b {
+				compatible = "wlf,wm8804";
+				reg = <0x3b>;
+				status = "okay";
+
+				pinctrl-names = "default";
+				pinctrl-0 = <&wlf_8804_pins>;
+
+				PVDD-supply = <&vdd_3v3_reg>;
+				DVDD-supply = <&vdd_3v3_reg>;
+				wlf,reset-gpio = <&gpio 8 GPIO_ACTIVE_HIGH>;
+			};
+		};
+	};
+
+	fragment@8 {
+		target = <&sound>;
+		__overlay__ {
+			compatible = "wlf,rpi-cirrus";
+			i2s-controller = <&i2s>;
+			status = "okay";
+		};
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/rpi-dac-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/rpi-dac-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/rpi-dac-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/rpi-dac-overlay.dts	2021-07-25 16:45:35.158807390 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// Definitions for RPi DAC
+/dts-v1/;
+/plugin/;
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&i2s>;
+		__overlay__ {
+			status = "okay";
+		};
+	};
+
+	fragment@1 {
+		target-path = "/";
+		__overlay__ {
+			pcm1794a-codec {
+				#sound-dai-cells = <0>;
+				compatible = "ti,pcm1794a";
+				status = "okay";
+			};
+		};
+	};
+
+	fragment@2 {
+		target = <&sound>;
+		__overlay__ {
+			compatible = "rpi,rpi-dac";
+			i2s-controller = <&i2s>;
+			status = "okay";
+		};
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/rpi-display-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/rpi-display-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/rpi-display-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/rpi-display-overlay.dts	2021-07-25 16:45:35.158807390 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/*
+ * Device Tree overlay for rpi-display by Watterott
+ *
+ */
+
+/dts-v1/;
+/plugin/;
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&spi0>;
+		__overlay__ {
+			status = "okay";
+		};
+	};
+
+	fragment@1 {
+		target = <&spidev0>;
+		__overlay__ {
+			status = "disabled";
+		};
+	};
+
+	fragment@2 {
+		target = <&spidev1>;
+		__overlay__ {
+			status = "disabled";
+		};
+	};
+
+	fragment@3 {
+		target = <&gpio>;
+		__overlay__ {
+			rpi_display_pins: rpi_display_pins {
+				brcm,pins = <18 23 24 25>;
+				brcm,function = <1 1 1 0>; /* out out out in */
+				brcm,pull = <0 0 0 2>; /* - - - up */
+			};
+		};
+	};
+
+	fragment@4 {
+		target = <&spi0>;
+		__overlay__ {
+			/* needed to avoid dtc warning */
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			rpidisplay: rpi-display@0{
+				compatible = "ilitek,ili9341";
+				reg = <0>;
+				pinctrl-names = "default";
+				pinctrl-0 = <&rpi_display_pins>;
+
+				spi-max-frequency = <32000000>;
+				rotate = <270>;
+				bgr;
+				fps = <30>;
+				buswidth = <8>;
+				reset-gpios = <&gpio 23 1>;
+				dc-gpios = <&gpio 24 0>;
+				led-gpios = <&gpio 18 0>;
+				debug = <0>;
+			};
+
+			rpidisplay_ts: rpi-display-ts@1 {
+				compatible = "ti,ads7846";
+				reg = <1>;
+
+				spi-max-frequency = <2000000>;
+				interrupts = <25 2>; /* high-to-low edge triggered */
+				interrupt-parent = <&gpio>;
+				pendown-gpio = <&gpio 25 1>;
+				ti,x-plate-ohms = /bits/ 16 <60>;
+				ti,pressure-max = /bits/ 16 <255>;
+			};
+		};
+	};
+	__overrides__ {
+		speed =     <&rpidisplay>,"spi-max-frequency:0";
+		rotate =    <&rpidisplay>,"rotate:0";
+		fps =       <&rpidisplay>,"fps:0";
+		debug =     <&rpidisplay>,"debug:0";
+		xohms =     <&rpidisplay_ts>,"ti,x-plate-ohms;0";
+		swapxy =    <&rpidisplay_ts>,"ti,swap-xy?";
+		backlight = <&rpidisplay>,"led-gpios:4",
+		            <&rpi_display_pins>,"brcm,pins:0";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/rpi-ft5406-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/rpi-ft5406-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/rpi-ft5406-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/rpi-ft5406-overlay.dts	2021-07-25 16:45:35.158807390 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/dts-v1/;
+/plugin/;
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target-path = "/soc/firmware";
+		__overlay__ {
+			ts: touchscreen {
+				compatible = "raspberrypi,firmware-ts";
+				touchscreen-size-x = <800>;
+				touchscreen-size-y = <480>;
+			};
+		};
+	};
+
+	__overrides__ {
+		touchscreen-size-x = <&ts>,"touchscreen-size-x:0";
+		touchscreen-size-y = <&ts>,"touchscreen-size-y:0";
+		touchscreen-inverted-x = <&ts>,"touchscreen-inverted-x?";
+		touchscreen-inverted-y = <&ts>,"touchscreen-inverted-y?";
+		touchscreen-swapped-x-y = <&ts>,"touchscreen-swapped-x-y?";
+        };
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/rpi-poe-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/rpi-poe-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/rpi-poe-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/rpi-poe-overlay.dts	2021-07-25 16:45:35.158807390 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/*
+ * Overlay for the Raspberry Pi POE HAT.
+ */
+/dts-v1/;
+/plugin/;
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target-path = "/";
+		__overlay__ {
+			fan0: rpi-poe-fan@0 {
+				compatible = "raspberrypi,rpi-poe-fan";
+				firmware = <&firmware>;
+				cooling-min-state = <0>;
+				cooling-max-state = <4>;
+				#cooling-cells = <2>;
+				cooling-levels = <0 1 10 100 255>;
+				status = "okay";
+			};
+		};
+	};
+
+	fragment@1 {
+		target = <&cpu_thermal>;
+		__overlay__ {
+			trips {
+				trip0: trip0 {
+					temperature = <40000>;
+					hysteresis = <2000>;
+					type = "active";
+				};
+				trip1: trip1 {
+					temperature = <45000>;
+					hysteresis = <2000>;
+					type = "active";
+				};
+				trip2: trip2 {
+					temperature = <50000>;
+					hysteresis = <2000>;
+					type = "active";
+				};
+				trip3: trip3 {
+					temperature = <55000>;
+					hysteresis = <5000>;
+					type = "active";
+				};
+			};
+			cooling-maps {
+				map0 {
+					trip = <&trip0>;
+					cooling-device = <&fan0 0 1>;
+				};
+				map1 {
+					trip = <&trip1>;
+					cooling-device = <&fan0 1 2>;
+				};
+				map2 {
+					trip = <&trip2>;
+					cooling-device = <&fan0 2 3>;
+				};
+				map3 {
+					trip = <&trip3>;
+					cooling-device = <&fan0 3 4>;
+				};
+			};
+		};
+	};
+
+	fragment@2 {
+		target-path = "/__overrides__";
+		__overlay__ {
+			poe_fan_temp0 =		<&trip0>,"temperature:0";
+			poe_fan_temp0_hyst =	<&trip0>,"hysteresis:0";
+			poe_fan_temp1 =		<&trip1>,"temperature:0";
+			poe_fan_temp1_hyst =	<&trip1>,"hysteresis:0";
+			poe_fan_temp2 =		<&trip2>,"temperature:0";
+			poe_fan_temp2_hyst =	<&trip2>,"hysteresis:0";
+			poe_fan_temp3 =		<&trip3>,"temperature:0";
+			poe_fan_temp3_hyst =	<&trip3>,"hysteresis:0";
+		};
+	};
+
+	__overrides__ {
+		poe_fan_temp0 =		<&trip0>,"temperature:0";
+		poe_fan_temp0_hyst =	<&trip0>,"hysteresis:0";
+		poe_fan_temp1 =		<&trip1>,"temperature:0";
+		poe_fan_temp1_hyst =	<&trip1>,"hysteresis:0";
+		poe_fan_temp2 =		<&trip2>,"temperature:0";
+		poe_fan_temp2_hyst =	<&trip2>,"hysteresis:0";
+		poe_fan_temp3 =		<&trip3>,"temperature:0";
+		poe_fan_temp3_hyst =	<&trip3>,"hysteresis:0";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/rpi-poe-plus-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/rpi-poe-plus-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/rpi-poe-plus-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/rpi-poe-plus-overlay.dts	2021-07-25 16:45:35.158807390 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// SPDX-License-Identifier: (GPL-2.0 OR MIT)
+// Overlay for the Raspberry Pi PoE+ HAT.
+
+#include "rpi-poe-overlay.dts"
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@3 {
+		target-path = "/";
+		__overlay__ {
+			rpi_poe_power_supply: rpi-poe-power-supply@0 {
+				compatible = "raspberrypi,rpi-poe-power-supply";
+				firmware = <&firmware>;
+				status = "okay";
+			};
+		};
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/rpi-proto-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/rpi-proto-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/rpi-proto-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/rpi-proto-overlay.dts	2021-07-25 16:45:35.158807390 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// Definitions for Rpi-Proto
+/dts-v1/;
+/plugin/;
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&i2s>;
+		__overlay__ {
+			status = "okay";
+		};
+	};
+
+	fragment@1 {
+		target = <&i2c1>;
+		__overlay__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			status = "okay";
+
+			wm8731@1a {
+				#sound-dai-cells = <0>;
+				compatible = "wlf,wm8731";
+				reg = <0x1a>;
+				status = "okay";
+			};
+		};
+	};
+
+	fragment@2 {
+		target = <&sound>;
+		__overlay__ {
+			compatible = "rpi,rpi-proto";
+			i2s-controller = <&i2s>;
+			status = "okay";
+		};
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/rpi-sense-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/rpi-sense-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/rpi-sense-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/rpi-sense-overlay.dts	2021-07-25 16:45:35.158807390 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// rpi-sense HAT
+/dts-v1/;
+/plugin/;
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&i2c1>;
+		__overlay__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			status = "okay";
+
+			rpi-sense@46 {
+				compatible = "rpi,rpi-sense";
+				reg = <0x46>;
+				keys-int-gpios = <&gpio 23 1>;
+				status = "okay";
+			};
+
+			lsm9ds1-magn@1c {
+				compatible = "st,lsm9ds1-magn";
+				reg = <0x1c>;
+				status = "okay";
+			};
+
+			lsm9ds1-accel6a {
+				compatible = "st,lsm9ds1-accel";
+				reg = <0x6a>;
+				status = "okay";
+			};
+
+			lps25h-press@5c {
+				compatible = "st,lps25h-press";
+				reg = <0x5c>;
+				status = "okay";
+			};
+
+			hts221-humid@5f {
+				compatible = "st,hts221-humid", "st,hts221";
+				reg = <0x5f>;
+				status = "okay";
+			};
+		};
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/rpi-tv-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/rpi-tv-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/rpi-tv-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/rpi-tv-overlay.dts	2021-07-25 16:45:35.158807390 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// rpi-tv HAT
+
+/dts-v1/;
+/plugin/;
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&spidev0>;
+		__overlay__ {
+			status = "disabled";
+		};
+	};
+
+	fragment@1 {
+		target = <&spi0>;
+		__overlay__ {
+			/* needed to avoid dtc warning */
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			status = "okay";
+
+			cxd2880@0 {
+				compatible = "sony,cxd2880";
+				reg = <0>; /* CE0 */
+				spi-max-frequency = <50000000>;
+				status = "okay";
+			};
+		};
+	};
+
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/rpivid-v4l2-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/rpivid-v4l2-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/rpivid-v4l2-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/rpivid-v4l2-overlay.dts	2021-07-25 16:45:35.158807390 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// SPDX-License-Identifier: GPL-2.0-only
+// Definitions for Raspberry Pi video decode engine
+/dts-v1/;
+/plugin/;
+
+#include <dt-bindings/interrupt-controller/arm-gic.h>
+
+/{
+	compatible = "brcm,bcm2711";
+
+	fragment@0 {
+		target = <&scb>;
+		__overlay__ {
+			/* needed to avoid dtc warning */
+			#address-cells = <2>;
+			#size-cells = <2>;
+
+			codec@7eb10000 {
+				compatible = "raspberrypi,rpivid-vid-decoder";
+				reg = <0x0 0x7eb10000  0x0 0x1000>,  /* INTC */
+				      <0x0 0x7eb00000  0x0 0x10000>; /* HEVC */
+				reg-names = "intc",
+					    "hevc";
+
+				interrupts = <GIC_SPI 98 IRQ_TYPE_LEVEL_HIGH>;
+
+				clocks = <&firmware_clocks 11>;
+				clock-names = "hevc";
+			};
+		};
+	};
+
+	fragment@1 {
+		target = <&scb>;
+		__overlay__ {
+			hevc-decoder@7eb00000 {
+				status = "disabled";
+			};
+			rpivid-local-intc@7eb10000 {
+				status = "disabled";
+			};
+			h264-decoder@7eb20000 {
+				status = "disabled";
+			};
+			vp9-decoder@7eb30000 {
+				status = "disabled";
+			};
+		};
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/rra-digidac1-wm8741-audio-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/rra-digidac1-wm8741-audio-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/rra-digidac1-wm8741-audio-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/rra-digidac1-wm8741-audio-overlay.dts	2021-07-25 16:45:35.158807390 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// Definitions for RRA DigiDAC1 Audio card
+/dts-v1/;
+/plugin/;
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&i2s>;
+		__overlay__ {
+			status = "okay";
+		};
+	};
+
+	fragment@1 {
+		target = <&i2c1>;
+		__overlay__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			status = "okay";
+
+			wm8804@3b {
+				#sound-dai-cells = <0>;
+				compatible = "wlf,wm8804";
+				reg = <0x3b>;
+				status = "okay";
+				PVDD-supply = <&vdd_3v3_reg>;
+				DVDD-supply = <&vdd_3v3_reg>;
+			};
+
+			wm8742: wm8741@1a {
+				compatible = "wlf,wm8741";
+				reg = <0x1a>;
+				status = "okay";
+				AVDD-supply = <&vdd_5v0_reg>;
+				DVDD-supply = <&vdd_3v3_reg>;
+			};
+		};
+	};
+
+	fragment@2 {
+		target = <&sound>;
+		__overlay__ {
+			compatible = "rra,digidac1-soundcard";
+			i2s-controller = <&i2s>;
+			status = "okay";
+		};
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/sainsmart18-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/sainsmart18-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/sainsmart18-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/sainsmart18-overlay.dts	2021-07-25 16:45:35.158807390 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/*
+ * Device Tree overlay for the Sainsmart 1.8" TFT LCD with ST7735R chip 160x128
+ */
+
+/dts-v1/;
+/plugin/;
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&spidev0>;
+		__overlay__ {
+			status = "disabled";
+		};
+	};
+
+	fragment@1 {
+		target = <&spi0>;
+		__overlay__ {
+			/* needed to avoid dtc warning */
+			#address-cells = <1>;
+			#size-cells = <0>;
+			status = "okay";
+
+			ss18: sainsmart18@0 {
+				compatible = "fbtft,sainsmart18";
+				reg = <0>;
+				pinctrl-names = "default";
+				spi-max-frequency = <40000000>;
+				rotate = <90>;
+				buswidth = <8>;
+				fps = <50>;
+				height = <160>;
+				width = <128>;
+				reset-gpios = <&gpio 25 1>;
+				dc-gpios = <&gpio 24 0>;
+				debug = <0>;
+			};
+		};
+	};
+
+	__overrides__ {
+		speed     = <&ss18>,"spi-max-frequency:0";
+		rotate    = <&ss18>,"rotate:0";
+		fps       = <&ss18>,"fps:0";
+		bgr       = <&ss18>,"bgr?";
+		debug     = <&ss18>,"debug:0";
+		dc_pin    = <&ss18>,"dc-gpios:4";
+		reset_pin = <&ss18>,"reset-gpios:4";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/sc16is750-i2c-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/sc16is750-i2c-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/sc16is750-i2c-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/sc16is750-i2c-overlay.dts	2021-07-25 16:45:35.158807390 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/dts-v1/;
+/plugin/;
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&i2c_arm>;
+		__overlay__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			status = "okay";
+
+			sc16is750: sc16is750@48 {
+				compatible = "nxp,sc16is750";
+				reg = <0x48>; /* i2c address */
+				clocks = <&sc16is750_clk>;
+				interrupt-parent = <&gpio>;
+				interrupts = <24 2>; /* IRQ_TYPE_EDGE_FALLING */
+				gpio-controller;
+				#gpio-cells = <2>;
+				i2c-max-frequency = <400000>;
+			};
+		};
+	};
+
+	fragment@1 {
+		target-path = "/";
+		__overlay__ {
+			sc16is750_clk: sc16is750_i2c_clk@48 {
+				compatible = "fixed-clock";
+				#clock-cells = <0>;
+				clock-frequency = <14745600>;
+			};
+		};
+	};
+
+	__overrides__ {
+		int_pin = <&sc16is750>,"interrupts:0";
+		addr = <&sc16is750>,"reg:0", <&sc16is750_clk>,"name";
+		xtal = <&sc16is750_clk>,"clock-frequency:0";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/sc16is752-i2c-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/sc16is752-i2c-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/sc16is752-i2c-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/sc16is752-i2c-overlay.dts	2021-07-25 16:45:35.158807390 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/dts-v1/;
+/plugin/;
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&i2c_arm>;
+		__overlay__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			status = "okay";
+
+			sc16is752: sc16is752@48 {
+				compatible = "nxp,sc16is752";
+				reg = <0x48>; /* i2c address */
+				clocks = <&sc16is752_clk>;
+				interrupt-parent = <&gpio>;
+				interrupts = <24 2>; /* IRQ_TYPE_EDGE_FALLING */
+				gpio-controller;
+				#gpio-cells = <2>;
+				i2c-max-frequency = <400000>;
+			};
+		};
+	};
+
+	fragment@1 {
+		target-path = "/";
+		__overlay__ {
+			sc16is752_clk: sc16is752_i2c_clk@48 {
+				compatible = "fixed-clock";
+				#clock-cells = <0>;
+				clock-frequency = <14745600>;
+			};
+		};
+	};
+
+	__overrides__ {
+		int_pin = <&sc16is752>,"interrupts:0";
+		addr = <&sc16is752>,"reg:0",<&sc16is752_clk>,"name";
+		xtal = <&sc16is752_clk>,"clock-frequency:0";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/sc16is752-spi0-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/sc16is752-spi0-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/sc16is752-spi0-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/sc16is752-spi0-overlay.dts	2021-07-25 16:45:35.158807390 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/dts-v1/;
+/plugin/;
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&spi0>;
+		__overlay__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			status = "okay";
+
+			sc16is752: sc16is752@0 {
+				compatible = "nxp,sc16is752";
+				reg = <0>; /* CE0 */
+				clocks = <&sc16is752_clk>;
+				interrupt-parent = <&gpio>;
+				interrupts = <24 2>; /* IRQ_TYPE_EDGE_FALLING */
+				gpio-controller;
+				#gpio-cells = <2>;
+				spi-max-frequency = <4000000>;
+			};
+		};
+	};
+
+	fragment@1 {
+		target = <&spidev0>;
+		__overlay__ {
+			status = "disabled";
+		};
+	};
+
+	fragment@2 {
+		target-path = "/";
+		__overlay__ {
+			sc16is752_clk: sc16is752_spi0_0_clk {
+				compatible = "fixed-clock";
+				#clock-cells = <0>;
+				clock-frequency = <14745600>;
+			};
+		};
+	};
+
+	__overrides__ {
+		int_pin = <&sc16is752>,"interrupts:0";
+		xtal = <&sc16is752_clk>,"clock-frequency:0";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/sc16is752-spi1-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/sc16is752-spi1-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/sc16is752-spi1-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/sc16is752-spi1-overlay.dts	2021-07-25 16:45:35.158807390 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/dts-v1/;
+/plugin/;
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&gpio>;
+		__overlay__ {
+			spi1_pins: spi1_pins {
+				brcm,pins = <19 20 21>;
+				brcm,function = <3>; /* alt4 */
+			};
+
+			spi1_cs_pins: spi1_cs_pins {
+				brcm,pins = <18>;
+				brcm,function = <1>; /* output */
+			};
+		};
+	};
+
+	fragment@1 {
+		target = <&spi1>;
+		__overlay__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			pinctrl-names = "default";
+			pinctrl-0 = <&spi1_pins &spi1_cs_pins>;
+			cs-gpios = <&gpio 18 1>;
+			status = "okay";
+
+			sc16is752: sc16is752@0 {
+				compatible = "nxp,sc16is752";
+				reg = <0>; /* CE0 */
+				clocks = <&sc16is752_clk>;
+				interrupt-parent = <&gpio>;
+				interrupts = <24 2>; /* IRQ_TYPE_EDGE_FALLING */
+				gpio-controller;
+				#gpio-cells = <2>;
+				spi-max-frequency = <4000000>;
+			};
+		};
+	};
+
+	fragment@2 {
+		target = <&aux>;
+		__overlay__ {
+			status = "okay";
+		};
+	};
+
+	fragment@3 {
+		target-path = "/";
+		__overlay__ {
+			sc16is752_clk: sc16is752_spi1_0_clk {
+				compatible = "fixed-clock";
+				#clock-cells = <0>;
+				clock-frequency = <14745600>;
+			};
+		};
+	};
+
+	__overrides__ {
+		int_pin = <&sc16is752>,"interrupts:0";
+		xtal = <&sc16is752_clk>,"clock-frequency:0";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/sdhost-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/sdhost-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/sdhost-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/sdhost-overlay.dts	2021-07-25 16:45:35.158807390 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/dts-v1/;
+/plugin/;
+
+/* Provide backwards compatible aliases for the old sdhost dtparams. */
+
+/{
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&sdhost>;
+		frag0: __overlay__ {
+			brcm,overclock-50 = <0>;
+			brcm,pio-limit = <1>;
+			status = "okay";
+		};
+	};
+
+	fragment@1 {
+		target = <&mmc>;
+		__overlay__ {
+			status = "disabled";
+		};
+	};
+
+	fragment@2 {
+		target = <&mmcnr>;
+		__overlay__ {
+			status = "disabled";
+		};
+	};
+
+	__overrides__ {
+		overclock_50     = <&frag0>,"brcm,overclock-50:0";
+		force_pio        = <&frag0>,"brcm,force-pio?";
+		pio_limit        = <&frag0>,"brcm,pio-limit:0";
+		debug            = <&frag0>,"brcm,debug?";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/sdio-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/sdio-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/sdio-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/sdio-overlay.dts	2021-07-25 16:45:35.158807390 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/dts-v1/;
+/plugin/;
+
+/* Enable SDIO from MMC interface via various GPIO groups */
+
+/{
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&mmcnr>;
+		__overlay__ {
+			status = "disabled";
+		};
+	};
+
+	fragment@1 {
+		target = <&mmc>;
+		sdio_ovl: __overlay__ {
+			pinctrl-0 = <&sdio_ovl_pins>;
+			pinctrl-names = "default";
+			non-removable;
+			bus-width = <4>;
+			status = "okay";
+		};
+	};
+
+	fragment@2 {
+		target = <&gpio>;
+		__overlay__ {
+			sdio_ovl_pins: sdio_ovl_pins {
+				brcm,pins = <22 23 24 25 26 27>;
+				brcm,function = <7>; /* ALT3 = SD1 */
+				brcm,pull = <0 2 2 2 2 2>;
+			};
+		};
+	};
+
+	fragment@3 {
+		target = <&sdio_ovl_pins>;
+		__dormant__ {
+			brcm,pins = <22 23 24 25>;
+			brcm,pull = <0 2 2 2>;
+		};
+	};
+
+	fragment@4 {
+		target = <&sdio_ovl_pins>;
+		__dormant__ {
+			brcm,pins = <34 35 36 37>;
+			brcm,pull = <0 2 2 2>;
+		};
+	};
+
+	fragment@5 {
+		target = <&sdio_ovl_pins>;
+		__dormant__ {
+			brcm,pins = <34 35 36 37 38 39>;
+			brcm,pull = <0 2 2 2 2 2>;
+		};
+	};
+
+	fragment@6 {
+		target-path = "/aliases";
+		__overlay__ {
+			mmc1 = "/soc/mmc@7e300000";
+		};
+	};
+
+	__overrides__ {
+		poll_once = <&sdio_ovl>,"non-removable?";
+		bus_width = <&sdio_ovl>,"bus-width:0";
+		sdio_overclock = <&sdio_ovl>,"brcm,overclock-50:0";
+		gpios_22_25 = <0>,"=3";
+		gpios_34_37 = <0>,"=4";
+		gpios_34_39 = <0>,"=5";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/seeed-can-fd-hat-v1-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/seeed-can-fd-hat-v1-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/seeed-can-fd-hat-v1-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/seeed-can-fd-hat-v1-overlay.dts	2021-07-25 16:45:35.158807390 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// redo: ovmerge -c spi1-1cs-overlay.dts,cs0_pin=18,cs0_spidev=false mcp251xfd-overlay.dts,spi0-0,interrupt=25 mcp251xfd-overlay.dts,spi1-0,interrupt=24
+
+// Device tree overlay for https://www.seeedstudio.com/2-Channel-CAN-BUS-FD-Shield-for-Raspberry-Pi-p-4072.html
+
+/dts-v1/;
+/plugin/;
+
+#include <dt-bindings/gpio/gpio.h>
+#include <dt-bindings/interrupt-controller/irq.h>
+#include <dt-bindings/pinctrl/bcm2835.h>
+
+/ {
+	compatible = "brcm,bcm2835";
+	fragment@0 {
+		target = <&gpio>;
+		__overlay__ {
+			spi1_pins: spi1_pins {
+				brcm,pins = <19 20 21>;
+				brcm,function = <3>;
+			};
+			spi1_cs_pins: spi1_cs_pins {
+				brcm,pins = <18>;
+				brcm,function = <1>;
+			};
+		};
+	};
+	fragment@1 {
+		target = <&spi1>;
+		__overlay__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			pinctrl-names = "default";
+			pinctrl-0 = <&spi1_pins &spi1_cs_pins>;
+			cs-gpios = <&gpio 18 1>;
+			status = "okay";
+			spidev@0 {
+				compatible = "spidev";
+				reg = <0>;
+				#address-cells = <1>;
+				#size-cells = <0>;
+				spi-max-frequency = <125000000>;
+				status = "disabled";
+			};
+		};
+	};
+	fragment@2 {
+		target = <&aux>;
+		__overlay__ {
+			status = "okay";
+		};
+	};
+	fragment@3 {
+		target = <&spidev0>;
+		__overlay__ {
+			status = "disabled";
+		};
+	};
+	fragment@4 {
+		target = <&gpio>;
+		__overlay__ {
+			mcp251xfd_pins: mcp251xfd_spi0_0_pins {
+				brcm,pins = <25>;
+				brcm,function = <BCM2835_FSEL_GPIO_IN>;
+			};
+		};
+	};
+	fragment@5 {
+		target-path = "/clocks";
+		__overlay__ {
+			clk_mcp251xfd_osc: mcp251xfd-spi0-0-osc {
+				#clock-cells = <0>;
+				compatible = "fixed-clock";
+				clock-frequency = <40000000>;
+			};
+		};
+	};
+	fragment@6 {
+		target = <&spi0>;
+		__overlay__ {
+			status = "okay";
+			#address-cells = <1>;
+			#size-cells = <0>;
+			mcp251xfd@0 {
+				compatible = "microchip,mcp251xfd";
+				reg = <0>;
+				pinctrl-names = "default";
+				pinctrl-0 = <&mcp251xfd_pins>;
+				spi-max-frequency = <20000000>;
+				interrupt-parent = <&gpio>;
+				interrupts = <25 IRQ_TYPE_LEVEL_LOW>;
+				clocks = <&clk_mcp251xfd_osc>;
+			};
+		};
+	};
+	fragment@7 {
+		target-path = "spi1/spidev@0";
+		__overlay__ {
+			status = "disabled";
+		};
+	};
+	fragment@8 {
+		target = <&gpio>;
+		__overlay__ {
+			mcp251xfd_pins_1: mcp251xfd_spi1_0_pins {
+				brcm,pins = <24>;
+				brcm,function = <BCM2835_FSEL_GPIO_IN>;
+			};
+		};
+	};
+	fragment@9 {
+		target-path = "/clocks";
+		__overlay__ {
+			clk_mcp251xfd_osc_1: mcp251xfd-spi1-0-osc {
+				#clock-cells = <0>;
+				compatible = "fixed-clock";
+				clock-frequency = <40000000>;
+			};
+		};
+	};
+	fragment@10 {
+		target = <&spi1>;
+		__overlay__ {
+			status = "okay";
+			#address-cells = <1>;
+			#size-cells = <0>;
+			mcp251xfd@0 {
+				compatible = "microchip,mcp251xfd";
+				reg = <0>;
+				pinctrl-names = "default";
+				pinctrl-0 = <&mcp251xfd_pins_1>;
+				spi-max-frequency = <20000000>;
+				interrupt-parent = <&gpio>;
+				interrupts = <24 IRQ_TYPE_LEVEL_LOW>;
+				clocks = <&clk_mcp251xfd_osc_1>;
+			};
+		};
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/seeed-can-fd-hat-v2-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/seeed-can-fd-hat-v2-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/seeed-can-fd-hat-v2-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/seeed-can-fd-hat-v2-overlay.dts	2021-07-25 16:45:35.158807390 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// redo: ovmerge -c mcp251xfd-overlay.dts,spi0-0,interrupt=25 mcp251xfd-overlay.dts,spi0-1,interrupt=24 i2c-rtc-overlay.dts,pcf85063
+
+// Device tree overlay for https://www.seeedstudio.com/CAN-BUS-FD-HAT-for-Raspberry-Pi-p-4742.html 
+
+/dts-v1/;
+/plugin/;
+
+#include <dt-bindings/gpio/gpio.h>
+#include <dt-bindings/interrupt-controller/irq.h>
+#include <dt-bindings/pinctrl/bcm2835.h>
+
+/ {
+	compatible = "brcm,bcm2835";
+	fragment@0 {
+		target = <&spidev0>;
+		__overlay__ {
+			status = "disabled";
+		};
+	};
+	fragment@1 {
+		target = <&gpio>;
+		__overlay__ {
+			mcp251xfd_pins: mcp251xfd_spi0_0_pins {
+				brcm,pins = <25>;
+				brcm,function = <BCM2835_FSEL_GPIO_IN>;
+			};
+		};
+	};
+	fragment@2 {
+		target-path = "/clocks";
+		__overlay__ {
+			clk_mcp251xfd_osc: mcp251xfd-spi0-0-osc {
+				#clock-cells = <0>;
+				compatible = "fixed-clock";
+				clock-frequency = <40000000>;
+			};
+		};
+	};
+	fragment@3 {
+		target = <&spi0>;
+		__overlay__ {
+			status = "okay";
+			#address-cells = <1>;
+			#size-cells = <0>;
+			mcp251xfd@0 {
+				compatible = "microchip,mcp251xfd";
+				reg = <0>;
+				pinctrl-names = "default";
+				pinctrl-0 = <&mcp251xfd_pins>;
+				spi-max-frequency = <20000000>;
+				interrupt-parent = <&gpio>;
+				interrupts = <25 IRQ_TYPE_LEVEL_LOW>;
+				clocks = <&clk_mcp251xfd_osc>;
+			};
+		};
+	};
+	fragment@4 {
+		target = <&spidev1>;
+		__overlay__ {
+			status = "disabled";
+		};
+	};
+	fragment@5 {
+		target = <&gpio>;
+		__overlay__ {
+			mcp251xfd_pins_1: mcp251xfd_spi0_1_pins {
+				brcm,pins = <24>;
+				brcm,function = <BCM2835_FSEL_GPIO_IN>;
+			};
+		};
+	};
+	fragment@6 {
+		target-path = "/clocks";
+		__overlay__ {
+			clk_mcp251xfd_osc_1: mcp251xfd-spi0-1-osc {
+				#clock-cells = <0>;
+				compatible = "fixed-clock";
+				clock-frequency = <40000000>;
+			};
+		};
+	};
+	fragment@7 {
+		target = <&spi0>;
+		__overlay__ {
+			status = "okay";
+			#address-cells = <1>;
+			#size-cells = <0>;
+			mcp251xfd@1 {
+				compatible = "microchip,mcp251xfd";
+				reg = <1>;
+				pinctrl-names = "default";
+				pinctrl-0 = <&mcp251xfd_pins_1>;
+				spi-max-frequency = <20000000>;
+				interrupt-parent = <&gpio>;
+				interrupts = <24 IRQ_TYPE_LEVEL_LOW>;
+				clocks = <&clk_mcp251xfd_osc_1>;
+			};
+		};
+	};
+	fragment@8 {
+		target = <&i2cbus>;
+		__overlay__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			pcf85063@51 {
+				compatible = "nxp,pcf85063";
+				reg = <0x51>;
+			};
+		};
+	};
+	fragment@9 {
+		target = <&i2c_arm>;
+		i2cbus: __overlay__ {
+			status = "okay";
+		};
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/sh1106-spi-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/sh1106-spi-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/sh1106-spi-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/sh1106-spi-overlay.dts	2021-07-25 16:45:35.158807390 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/*
+ * Device Tree overlay for SH1106 based SPI OLED display
+ *
+ */
+
+/dts-v1/;
+/plugin/;
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&spi0>;
+		__overlay__ {
+			status = "okay";
+		};
+	};
+
+	fragment@1 {
+		target = <&spidev0>;
+		__overlay__ {
+			status = "disabled";
+		};
+	};
+
+	fragment@2 {
+		target = <&spidev1>;
+		__overlay__ {
+			status = "disabled";
+		};
+	};
+
+	fragment@3 {
+		target = <&gpio>;
+		__overlay__ {
+			sh1106_pins: sh1106_pins {
+                                brcm,pins = <25 24>;
+                                brcm,function = <1 1>; /* out out */
+			};
+		};
+	};
+
+	fragment@4 {
+		target = <&spi0>;
+		__overlay__ {
+			/* needed to avoid dtc warning */
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			sh1106: sh1106@0{
+				compatible = "sinowealth,sh1106";
+				reg = <0>;
+				pinctrl-names = "default";
+				pinctrl-0 = <&sh1106_pins>;
+
+				spi-max-frequency = <4000000>;
+				bgr = <0>;
+				bpp = <1>;
+				rotate = <0>;
+				fps = <25>;
+				buswidth = <8>;
+				reset-gpios = <&gpio 25 1>;
+				dc-gpios = <&gpio 24 0>;
+				debug = <0>;
+
+				sinowealth,height = <64>;
+				sinowealth,width = <128>;
+				sinowealth,page-offset = <0>;
+			};
+		};
+	};
+
+	__overrides__ {
+		speed     = <&sh1106>,"spi-max-frequency:0";
+		rotate    = <&sh1106>,"rotate:0";
+		fps       = <&sh1106>,"fps:0";
+		debug     = <&sh1106>,"debug:0";
+		dc_pin    = <&sh1106>,"dc-gpios:4",
+		            <&sh1106_pins>,"brcm,pins:4";
+		reset_pin = <&sh1106>,"reset-gpios:4",
+		            <&sh1106_pins>,"brcm,pins:0";
+		height    = <&sh1106>,"sinowealth,height:0";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/si446x-spi0-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/si446x-spi0-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/si446x-spi0-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/si446x-spi0-overlay.dts	2021-07-25 16:45:35.158807390 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// Overlay for the SiLabs Si446X Controller - SPI0
+// Default Interrupt Pin: 17
+// Default SDN Pin: 27
+/dts-v1/;
+/plugin/;
+
+   / {
+    compatible = "brcm,bcm2835";
+
+    fragment@0 {
+        target = <&spi0>;
+        __overlay__ {
+            // needed to avoid dtc warning
+            #address-cells = <1>;
+            #size-cells = <0>;
+
+            status = "okay";
+
+            uhf0: si446x@0{
+                compatible = "silabs,si446x";
+                reg = <0>; // CE0
+                pinctrl-names = "default";
+                pinctrl-0 = <&uhf0_pins>;
+                interrupt-parent = <&gpio>;
+                interrupts = <17 0x2>; // falling edge
+                spi-max-frequency = <4000000>;
+                sdn_pin = <27>;
+                irq_pin = <17>;
+                status = "okay";
+            };
+        };
+    };
+
+    fragment@1 {
+        target = <&gpio>;
+        __overlay__ {
+            uhf0_pins: uhf0_pins {
+                brcm,pins = <17 27>;
+                brcm,function = <0 1>; // in, out
+                brcm,pull = <2 0>; // high, none
+            };
+        };
+    };
+
+    __overrides__ {
+        int_pin = <&uhf0>, "interrupts:0",
+                  <&uhf0>, "irq_pin:0",
+                  <&uhf0_pins>, "brcm,pins:0";
+        reset_pin = <&uhf0>, "sdn_pin:0",
+                    <&uhf0_pins>, "brcm,pins:4";
+        speed   = <&uhf0>, "spi-max-frequency:0";
+    };
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/smi-dev-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/smi-dev-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/smi-dev-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/smi-dev-overlay.dts	2021-07-25 16:45:35.158807390 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// Description: Overlay to enable character device interface for SMI.
+// Author:	Luke Wren <luke@raspberrypi.org>
+
+/dts-v1/;
+/plugin/;
+
+/{
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&soc>;
+		__overlay__ {
+			smi_dev {
+				compatible = "brcm,bcm2835-smi-dev";
+				smi_handle = <&smi>;
+				status = "okay";
+			};
+		};
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/smi-nand-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/smi-nand-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/smi-nand-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/smi-nand-overlay.dts	2021-07-25 16:45:35.158807390 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// Description: Overlay to enable NAND flash through
+// the secondary memory interface
+// Author:	Luke Wren
+
+/dts-v1/;
+/plugin/;
+
+/{
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&smi>;
+		__overlay__ {
+			pinctrl-names = "default";
+			pinctrl-0 = <&smi_pins>;
+			status = "okay";
+		};
+	};
+
+	fragment@1 {
+		target = <&soc>;
+		__overlay__ {
+			nand: flash@0 {
+				compatible = "brcm,bcm2835-smi-nand";
+				smi_handle = <&smi>;
+				#address-cells = <1>;
+				#size-cells = <1>;
+				status = "okay";
+
+				partition@0 {
+					label = "stage2";
+					// 128k
+					reg = <0 0x20000>;
+					read-only;
+				};
+				partition@1 {
+					label = "firmware";
+					// 16M
+					reg = <0x20000 0x1000000>;
+					read-only;
+				};
+				partition@2 {
+					label = "root";
+					// 2G (will need to use 64 bit for >=4G)
+					reg = <0x1020000 0x80000000>;
+				};
+			};
+		};
+	};
+
+	fragment@2 {
+		target = <&gpio>;
+		__overlay__ {
+			smi_pins: smi_pins {
+				brcm,pins = <0 1 2 3 4 5 6 7 8 9 10 11
+					12 13 14 15>;
+				/* Alt 1: SMI */
+				brcm,function = <5 5 5 5 5 5 5 5 5 5 5
+					5 5 5 5 5>;
+				/* /CS, /WE and /OE are pulled high, as they are
+				   generally active low signals */
+				brcm,pull = <2 2 2 2 2 2 2 2 0 0 0 0 0 0 0 0>;
+			};
+		};
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/smi-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/smi-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/smi-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/smi-overlay.dts	2021-07-25 16:45:35.158807390 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// Description:	Overlay to enable the secondary memory interface peripheral
+// Author:	Luke Wren
+
+/dts-v1/;
+/plugin/;
+
+/{
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&smi>;
+		__overlay__ {
+			pinctrl-names = "default";
+			pinctrl-0 = <&smi_pins>;
+			status = "okay";
+		};
+	};
+
+	fragment@1 {
+		target = <&gpio>;
+		__overlay__ {
+			smi_pins: smi_pins {
+				/* Don't configure the top two address bits, as
+				   these are already used as ID_SD and ID_SC */
+				brcm,pins = <2 3 4 5 6 7 8 9 10 11 12 13 14 15
+					     16 17 18 19 20 21 22 23 24 25>;
+				/* Alt 1: SMI */
+				brcm,function = <5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
+						 5 5 5 5 5 5 5 5 5>;
+				/* /CS, /WE and /OE are pulled high, as they are
+				   generally active low signals */
+				brcm,pull = <2 2 2 2 2 2 0 0 0 0 0 0 0 0 0 0 0
+					     0 0 0 0 0 0 0>;
+			};
+		};
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/spi0-1cs-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/spi0-1cs-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/spi0-1cs-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/spi0-1cs-overlay.dts	2021-07-25 16:45:35.158807390 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/dts-v1/;
+/plugin/;
+
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&spi0_cs_pins>;
+		frag0: __overlay__ {
+			brcm,pins = <8>;
+		};
+	};
+
+	fragment@1 {
+		target = <&spi0>;
+		frag1: __overlay__ {
+			cs-gpios = <&gpio 8 1>;
+			status = "okay";
+		};
+	};
+
+	fragment@2 {
+		target = <&spidev1>;
+		__overlay__ {
+			status = "disabled";
+		};
+	};
+
+	fragment@3 {
+		target = <&spi0_pins>;
+		__dormant__ {
+			brcm,pins = <10 11>;
+		};
+	};
+
+	__overrides__ {
+		cs0_pin  = <&frag0>,"brcm,pins:0",
+			   <&frag1>,"cs-gpios:4";
+		no_miso = <0>,"=3";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/spi0-2cs-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/spi0-2cs-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/spi0-2cs-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/spi0-2cs-overlay.dts	2021-07-25 16:45:35.158807390 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/dts-v1/;
+/plugin/;
+
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&spi0_cs_pins>;
+		frag0: __overlay__ {
+			brcm,pins = <8 7>;
+		};
+	};
+
+	fragment@1 {
+		target = <&spi0>;
+		frag1: __overlay__ {
+			cs-gpios = <&gpio 8 1>, <&gpio 7 1>;
+			status = "okay";
+		};
+	};
+
+	fragment@2 {
+		target = <&spi0_pins>;
+		__dormant__ {
+			brcm,pins = <10 11>;
+		};
+	};
+
+	__overrides__ {
+		cs0_pin  = <&frag0>,"brcm,pins:0",
+			   <&frag1>,"cs-gpios:4";
+		cs1_pin  = <&frag0>,"brcm,pins:4",
+			   <&frag1>,"cs-gpios:16";
+		no_miso = <0>,"=2";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/spi1-1cs-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/spi1-1cs-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/spi1-1cs-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/spi1-1cs-overlay.dts	2021-07-25 16:45:35.158807390 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/dts-v1/;
+/plugin/;
+
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&gpio>;
+		__overlay__ {
+			spi1_pins: spi1_pins {
+				brcm,pins = <19 20 21>;
+				brcm,function = <3>; /* alt4 */
+			};
+
+			spi1_cs_pins: spi1_cs_pins {
+				brcm,pins = <18>;
+				brcm,function = <1>; /* output */
+			};
+		};
+	};
+
+	fragment@1 {
+		target = <&spi1>;
+		frag1: __overlay__ {
+			/* needed to avoid dtc warning */
+			#address-cells = <1>;
+			#size-cells = <0>;
+			pinctrl-names = "default";
+			pinctrl-0 = <&spi1_pins &spi1_cs_pins>;
+			cs-gpios = <&gpio 18 1>;
+			status = "okay";
+
+			spidev1_0: spidev@0 {
+				compatible = "spidev";
+				reg = <0>;      /* CE0 */
+				#address-cells = <1>;
+				#size-cells = <0>;
+				spi-max-frequency = <125000000>;
+				status = "okay";
+			};
+		};
+	};
+
+	fragment@2 {
+		target = <&aux>;
+		__overlay__ {
+			status = "okay";
+		};
+	};
+
+	__overrides__ {
+		cs0_pin  = <&spi1_cs_pins>,"brcm,pins:0",
+			   <&frag1>,"cs-gpios:4";
+		cs0_spidev = <&spidev1_0>,"status";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/spi1-2cs-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/spi1-2cs-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/spi1-2cs-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/spi1-2cs-overlay.dts	2021-07-25 16:45:35.158807390 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/dts-v1/;
+/plugin/;
+
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&gpio>;
+		__overlay__ {
+			spi1_pins: spi1_pins {
+				brcm,pins = <19 20 21>;
+				brcm,function = <3>; /* alt4 */
+			};
+
+			spi1_cs_pins: spi1_cs_pins {
+				brcm,pins = <18 17>;
+				brcm,function = <1>; /* output */
+			};
+		};
+	};
+
+	fragment@1 {
+		target = <&spi1>;
+		frag1: __overlay__ {
+			/* needed to avoid dtc warning */
+			#address-cells = <1>;
+			#size-cells = <0>;
+			pinctrl-names = "default";
+			pinctrl-0 = <&spi1_pins &spi1_cs_pins>;
+			cs-gpios = <&gpio 18 1>, <&gpio 17 1>;
+			status = "okay";
+
+			spidev1_0: spidev@0 {
+				compatible = "spidev";
+				reg = <0>;      /* CE0 */
+				#address-cells = <1>;
+				#size-cells = <0>;
+				spi-max-frequency = <125000000>;
+				status = "okay";
+			};
+
+			spidev1_1: spidev@1 {
+				compatible = "spidev";
+				reg = <1>;      /* CE1 */
+				#address-cells = <1>;
+				#size-cells = <0>;
+				spi-max-frequency = <125000000>;
+				status = "okay";
+			};
+		};
+	};
+
+	fragment@2 {
+		target = <&aux>;
+		__overlay__ {
+			status = "okay";
+		};
+	};
+
+	__overrides__ {
+		cs0_pin  = <&spi1_cs_pins>,"brcm,pins:0",
+			   <&frag1>,"cs-gpios:4";
+		cs1_pin  = <&spi1_cs_pins>,"brcm,pins:4",
+			   <&frag1>,"cs-gpios:16";
+		cs0_spidev = <&spidev1_0>,"status";
+		cs1_spidev = <&spidev1_1>,"status";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/spi1-3cs-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/spi1-3cs-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/spi1-3cs-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/spi1-3cs-overlay.dts	2021-07-25 16:45:35.158807390 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/dts-v1/;
+/plugin/;
+
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&gpio>;
+		__overlay__ {
+			spi1_pins: spi1_pins {
+				brcm,pins = <19 20 21>;
+				brcm,function = <3>; /* alt4 */
+			};
+
+			spi1_cs_pins: spi1_cs_pins {
+				brcm,pins = <18 17 16>;
+				brcm,function = <1>; /* output */
+			};
+		};
+	};
+
+	fragment@1 {
+		target = <&spi1>;
+		frag1: __overlay__ {
+			/* needed to avoid dtc warning */
+			#address-cells = <1>;
+			#size-cells = <0>;
+			pinctrl-names = "default";
+			pinctrl-0 = <&spi1_pins &spi1_cs_pins>;
+			cs-gpios = <&gpio 18 1>, <&gpio 17 1>, <&gpio 16 1>;
+			status = "okay";
+
+			spidev1_0: spidev@0 {
+				compatible = "spidev";
+				reg = <0>;      /* CE0 */
+				#address-cells = <1>;
+				#size-cells = <0>;
+				spi-max-frequency = <125000000>;
+				status = "okay";
+			};
+
+			spidev1_1: spidev@1 {
+				compatible = "spidev";
+				reg = <1>;      /* CE1 */
+				#address-cells = <1>;
+				#size-cells = <0>;
+				spi-max-frequency = <125000000>;
+				status = "okay";
+			};
+
+			spidev1_2: spidev@2 {
+				compatible = "spidev";
+				reg = <2>;      /* CE2 */
+				#address-cells = <1>;
+				#size-cells = <0>;
+				spi-max-frequency = <125000000>;
+				status = "okay";
+			};
+		};
+	};
+
+	fragment@2 {
+		target = <&aux>;
+		__overlay__ {
+			status = "okay";
+		};
+	};
+
+	__overrides__ {
+		cs0_pin  = <&spi1_cs_pins>,"brcm,pins:0",
+			   <&frag1>,"cs-gpios:4";
+		cs1_pin  = <&spi1_cs_pins>,"brcm,pins:4",
+			   <&frag1>,"cs-gpios:16";
+		cs2_pin  = <&spi1_cs_pins>,"brcm,pins:8",
+			   <&frag1>,"cs-gpios:28";
+		cs0_spidev = <&spidev1_0>,"status";
+		cs1_spidev = <&spidev1_1>,"status";
+		cs2_spidev = <&spidev1_2>,"status";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/spi2-1cs-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/spi2-1cs-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/spi2-1cs-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/spi2-1cs-overlay.dts	2021-07-25 16:45:35.158807390 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/dts-v1/;
+/plugin/;
+
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&gpio>;
+		__overlay__ {
+			spi2_pins: spi2_pins {
+				brcm,pins = <40 41 42>;
+				brcm,function = <3>; /* alt4 */
+			};
+
+			spi2_cs_pins: spi2_cs_pins {
+				brcm,pins = <43>;
+				brcm,function = <1>; /* output */
+			};
+		};
+	};
+
+	fragment@1 {
+		target = <&spi2>;
+		frag1: __overlay__ {
+			/* needed to avoid dtc warning */
+			#address-cells = <1>;
+			#size-cells = <0>;
+			pinctrl-names = "default";
+			pinctrl-0 = <&spi2_pins &spi2_cs_pins>;
+			cs-gpios = <&gpio 43 1>;
+			status = "okay";
+
+			spidev2_0: spidev@0 {
+				compatible = "spidev";
+				reg = <0>;      /* CE0 */
+				#address-cells = <1>;
+				#size-cells = <0>;
+				spi-max-frequency = <125000000>;
+				status = "okay";
+			};
+		};
+	};
+
+	fragment@2 {
+		target = <&aux>;
+		__overlay__ {
+			status = "okay";
+		};
+	};
+
+	__overrides__ {
+		cs0_pin  = <&spi2_cs_pins>,"brcm,pins:0",
+			   <&frag1>,"cs-gpios:4";
+		cs0_spidev = <&spidev2_0>,"status";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/spi2-2cs-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/spi2-2cs-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/spi2-2cs-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/spi2-2cs-overlay.dts	2021-07-25 16:45:35.158807390 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/dts-v1/;
+/plugin/;
+
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&gpio>;
+		__overlay__ {
+			spi2_pins: spi2_pins {
+				brcm,pins = <40 41 42>;
+				brcm,function = <3>; /* alt4 */
+			};
+
+			spi2_cs_pins: spi2_cs_pins {
+				brcm,pins = <43 44>;
+				brcm,function = <1>; /* output */
+			};
+		};
+	};
+
+	fragment@1 {
+		target = <&spi2>;
+		frag1: __overlay__ {
+			/* needed to avoid dtc warning */
+			#address-cells = <1>;
+			#size-cells = <0>;
+			pinctrl-names = "default";
+			pinctrl-0 = <&spi2_pins &spi2_cs_pins>;
+			cs-gpios = <&gpio 43 1>, <&gpio 44 1>;
+			status = "okay";
+
+			spidev2_0: spidev@0 {
+				compatible = "spidev";
+				reg = <0>;      /* CE0 */
+				#address-cells = <1>;
+				#size-cells = <0>;
+				spi-max-frequency = <125000000>;
+				status = "okay";
+			};
+
+			spidev2_1: spidev@1 {
+				compatible = "spidev";
+				reg = <1>;      /* CE1 */
+				#address-cells = <1>;
+				#size-cells = <0>;
+				spi-max-frequency = <125000000>;
+				status = "okay";
+			};
+		};
+	};
+
+	fragment@2 {
+		target = <&aux>;
+		__overlay__ {
+			status = "okay";
+		};
+	};
+
+	__overrides__ {
+		cs0_pin  = <&spi2_cs_pins>,"brcm,pins:0",
+			   <&frag1>,"cs-gpios:4";
+		cs1_pin  = <&spi2_cs_pins>,"brcm,pins:4",
+			   <&frag1>,"cs-gpios:16";
+		cs0_spidev = <&spidev2_0>,"status";
+		cs1_spidev = <&spidev2_1>,"status";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/spi2-3cs-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/spi2-3cs-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/spi2-3cs-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/spi2-3cs-overlay.dts	2021-07-25 16:45:35.158807390 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/dts-v1/;
+/plugin/;
+
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&gpio>;
+		__overlay__ {
+			spi2_pins: spi2_pins {
+				brcm,pins = <40 41 42>;
+				brcm,function = <3>; /* alt4 */
+			};
+
+			spi2_cs_pins: spi2_cs_pins {
+				brcm,pins = <43 44 45>;
+				brcm,function = <1>; /* output */
+			};
+		};
+	};
+
+	fragment@1 {
+		target = <&spi2>;
+		frag1: __overlay__ {
+			/* needed to avoid dtc warning */
+			#address-cells = <1>;
+			#size-cells = <0>;
+			pinctrl-names = "default";
+			pinctrl-0 = <&spi2_pins &spi2_cs_pins>;
+			cs-gpios = <&gpio 43 1>, <&gpio 44 1>, <&gpio 45 1>;
+			status = "okay";
+
+			spidev2_0: spidev@0 {
+				compatible = "spidev";
+				reg = <0>;      /* CE0 */
+				#address-cells = <1>;
+				#size-cells = <0>;
+				spi-max-frequency = <125000000>;
+				status = "okay";
+			};
+
+			spidev2_1: spidev@1 {
+				compatible = "spidev";
+				reg = <1>;      /* CE1 */
+				#address-cells = <1>;
+				#size-cells = <0>;
+				spi-max-frequency = <125000000>;
+				status = "okay";
+			};
+
+			spidev2_2: spidev@2 {
+				compatible = "spidev";
+				reg = <2>;      /* CE2 */
+				#address-cells = <1>;
+				#size-cells = <0>;
+				spi-max-frequency = <125000000>;
+				status = "okay";
+			};
+		};
+	};
+
+	fragment@2 {
+		target = <&aux>;
+		__overlay__ {
+			status = "okay";
+		};
+	};
+
+	__overrides__ {
+		cs0_pin  = <&spi2_cs_pins>,"brcm,pins:0",
+			   <&frag1>,"cs-gpios:4";
+		cs1_pin  = <&spi2_cs_pins>,"brcm,pins:4",
+			   <&frag1>,"cs-gpios:16";
+		cs2_pin  = <&spi2_cs_pins>,"brcm,pins:8",
+			   <&frag1>,"cs-gpios:28";
+		cs0_spidev = <&spidev2_0>,"status";
+		cs1_spidev = <&spidev2_1>,"status";
+		cs2_spidev = <&spidev2_2>,"status";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/spi3-1cs-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/spi3-1cs-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/spi3-1cs-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/spi3-1cs-overlay.dts	2021-07-25 16:45:35.158807390 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/dts-v1/;
+/plugin/;
+
+
+/ {
+	compatible = "brcm,bcm2711";
+
+	fragment@0 {
+		target = <&spi3_cs_pins>;
+		frag0: __overlay__ {
+			brcm,pins = <0>;
+			brcm,function = <1>; /* output */
+		};
+	};
+
+	fragment@1 {
+		target = <&spi3>;
+		frag1: __overlay__ {
+			/* needed to avoid dtc warning */
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			pinctrl-names = "default";
+		        pinctrl-0 = <&spi3_pins &spi3_cs_pins>;
+			cs-gpios = <&gpio 0 1>;
+			status = "okay";
+
+			spidev3_0: spidev@0 {
+				compatible = "spidev";
+				reg = <0>;      /* CE0 */
+				#address-cells = <1>;
+				#size-cells = <0>;
+				spi-max-frequency = <125000000>;
+				status = "okay";
+			};
+		};
+	};
+
+	__overrides__ {
+		cs0_pin  = <&frag0>,"brcm,pins:0",
+			   <&frag1>,"cs-gpios:4";
+		cs0_spidev = <&spidev3_0>,"status";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/spi3-2cs-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/spi3-2cs-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/spi3-2cs-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/spi3-2cs-overlay.dts	2021-07-25 16:45:35.158807390 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/dts-v1/;
+/plugin/;
+
+
+/ {
+	compatible = "brcm,bcm2711";
+
+	fragment@0 {
+		target = <&spi3_cs_pins>;
+		frag0: __overlay__ {
+			brcm,pins = <0 24>;
+			brcm,function = <1>; /* output */
+		};
+	};
+
+	fragment@1 {
+		target = <&spi3>;
+		frag1: __overlay__ {
+			/* needed to avoid dtc warning */
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			pinctrl-names = "default";
+		        pinctrl-0 = <&spi3_pins &spi3_cs_pins>;
+			cs-gpios = <&gpio 0 1>, <&gpio 24 1>;
+			status = "okay";
+
+			spidev3_0: spidev@0 {
+				compatible = "spidev";
+				reg = <0>;      /* CE0 */
+				#address-cells = <1>;
+				#size-cells = <0>;
+				spi-max-frequency = <125000000>;
+				status = "okay";
+			};
+
+			spidev3_1: spidev@1 {
+				compatible = "spidev";
+				reg = <1>;      /* CE1 */
+				#address-cells = <1>;
+				#size-cells = <0>;
+				spi-max-frequency = <125000000>;
+				status = "okay";
+			};
+		};
+	};
+
+	__overrides__ {
+		cs0_pin  = <&frag0>,"brcm,pins:0",
+			   <&frag1>,"cs-gpios:4";
+		cs1_pin  = <&frag0>,"brcm,pins:4",
+			   <&frag1>,"cs-gpios:16";
+		cs0_spidev = <&spidev3_0>,"status";
+		cs1_spidev = <&spidev3_1>,"status";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/spi4-1cs-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/spi4-1cs-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/spi4-1cs-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/spi4-1cs-overlay.dts	2021-07-25 16:45:35.158807390 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/dts-v1/;
+/plugin/;
+
+
+/ {
+	compatible = "brcm,bcm2711";
+
+	fragment@0 {
+		target = <&spi4_cs_pins>;
+		frag0: __overlay__ {
+			brcm,pins = <4>;
+			brcm,function = <1>; /* output */
+		};
+	};
+
+	fragment@1 {
+		target = <&spi4>;
+		frag1: __overlay__ {
+			/* needed to avoid dtc warning */
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			pinctrl-names = "default";
+		        pinctrl-0 = <&spi4_pins &spi4_cs_pins>;
+			cs-gpios = <&gpio 4 1>;
+			status = "okay";
+
+			spidev4_0: spidev@0 {
+				compatible = "spidev";
+				reg = <0>;      /* CE0 */
+				#address-cells = <1>;
+				#size-cells = <0>;
+				spi-max-frequency = <125000000>;
+				status = "okay";
+			};
+		};
+	};
+
+	__overrides__ {
+		cs0_pin  = <&frag0>,"brcm,pins:0",
+			   <&frag1>,"cs-gpios:4";
+		cs0_spidev = <&spidev4_0>,"status";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/spi4-2cs-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/spi4-2cs-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/spi4-2cs-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/spi4-2cs-overlay.dts	2021-07-25 16:45:35.158807390 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/dts-v1/;
+/plugin/;
+
+
+/ {
+	compatible = "brcm,bcm2711";
+
+	fragment@0 {
+		target = <&spi4_cs_pins>;
+		frag0: __overlay__ {
+			brcm,pins = <4 25>;
+			brcm,function = <1>; /* output */
+		};
+	};
+
+	fragment@1 {
+		target = <&spi4>;
+		frag1: __overlay__ {
+			/* needed to avoid dtc warning */
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			pinctrl-names = "default";
+		        pinctrl-0 = <&spi4_pins &spi4_cs_pins>;
+			cs-gpios = <&gpio 4 1>, <&gpio 25 1>;
+			status = "okay";
+
+			spidev4_0: spidev@0 {
+				compatible = "spidev";
+				reg = <0>;      /* CE0 */
+				#address-cells = <1>;
+				#size-cells = <0>;
+				spi-max-frequency = <125000000>;
+				status = "okay";
+			};
+
+			spidev4_1: spidev@1 {
+				compatible = "spidev";
+				reg = <1>;      /* CE1 */
+				#address-cells = <1>;
+				#size-cells = <0>;
+				spi-max-frequency = <125000000>;
+				status = "okay";
+			};
+		};
+	};
+
+	__overrides__ {
+		cs0_pin  = <&frag0>,"brcm,pins:0",
+			   <&frag1>,"cs-gpios:4";
+		cs1_pin  = <&frag0>,"brcm,pins:4",
+			   <&frag1>,"cs-gpios:16";
+		cs0_spidev = <&spidev4_0>,"status";
+		cs1_spidev = <&spidev4_1>,"status";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/spi5-1cs-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/spi5-1cs-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/spi5-1cs-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/spi5-1cs-overlay.dts	2021-07-25 16:45:35.158807390 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/dts-v1/;
+/plugin/;
+
+
+/ {
+	compatible = "brcm,bcm2711";
+
+	fragment@0 {
+		target = <&spi5_cs_pins>;
+		frag0: __overlay__ {
+			brcm,pins = <12>;
+			brcm,function = <1>; /* output */
+		};
+	};
+
+	fragment@1 {
+		target = <&spi5>;
+		frag1: __overlay__ {
+			/* needed to avoid dtc warning */
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			pinctrl-names = "default";
+		        pinctrl-0 = <&spi5_pins &spi5_cs_pins>;
+			cs-gpios = <&gpio 12 1>;
+			status = "okay";
+
+			spidev5_0: spidev@0 {
+				compatible = "spidev";
+				reg = <0>;      /* CE0 */
+				#address-cells = <1>;
+				#size-cells = <0>;
+				spi-max-frequency = <125000000>;
+				status = "okay";
+			};
+		};
+	};
+
+	__overrides__ {
+		cs0_pin  = <&frag0>,"brcm,pins:0",
+			   <&frag1>,"cs-gpios:4";
+		cs0_spidev = <&spidev5_0>,"status";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/spi5-2cs-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/spi5-2cs-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/spi5-2cs-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/spi5-2cs-overlay.dts	2021-07-25 16:45:35.158807390 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/dts-v1/;
+/plugin/;
+
+
+/ {
+	compatible = "brcm,bcm2711";
+
+	fragment@0 {
+		target = <&spi5_cs_pins>;
+		frag0: __overlay__ {
+			brcm,pins = <12 26>;
+			brcm,function = <1>; /* output */
+		};
+	};
+
+	fragment@1 {
+		target = <&spi5>;
+		frag1: __overlay__ {
+			/* needed to avoid dtc warning */
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			pinctrl-names = "default";
+		        pinctrl-0 = <&spi5_pins &spi5_cs_pins>;
+			cs-gpios = <&gpio 12 1>, <&gpio 26 1>;
+			status = "okay";
+
+			spidev5_0: spidev@0 {
+				compatible = "spidev";
+				reg = <0>;      /* CE0 */
+				#address-cells = <1>;
+				#size-cells = <0>;
+				spi-max-frequency = <125000000>;
+				status = "okay";
+			};
+
+			spidev5_1: spidev@1 {
+				compatible = "spidev";
+				reg = <1>;      /* CE1 */
+				#address-cells = <1>;
+				#size-cells = <0>;
+				spi-max-frequency = <125000000>;
+				status = "okay";
+			};
+		};
+	};
+
+	__overrides__ {
+		cs0_pin  = <&frag0>,"brcm,pins:0",
+			   <&frag1>,"cs-gpios:4";
+		cs1_pin  = <&frag0>,"brcm,pins:4",
+			   <&frag1>,"cs-gpios:16";
+		cs0_spidev = <&spidev5_0>,"status";
+		cs1_spidev = <&spidev5_1>,"status";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/spi6-1cs-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/spi6-1cs-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/spi6-1cs-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/spi6-1cs-overlay.dts	2021-07-25 16:45:35.158807390 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/dts-v1/;
+/plugin/;
+
+
+/ {
+	compatible = "brcm,bcm2711";
+
+	fragment@0 {
+		target = <&spi6_cs_pins>;
+		frag0: __overlay__ {
+			brcm,pins = <18>;
+			brcm,function = <1>; /* output */
+		};
+	};
+
+	fragment@1 {
+		target = <&spi6>;
+		frag1: __overlay__ {
+			/* needed to avoid dtc warning */
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			pinctrl-names = "default";
+		        pinctrl-0 = <&spi6_pins &spi6_cs_pins>;
+			cs-gpios = <&gpio 18 1>;
+			status = "okay";
+
+			spidev6_0: spidev@0 {
+				compatible = "spidev";
+				reg = <0>;      /* CE0 */
+				#address-cells = <1>;
+				#size-cells = <0>;
+				spi-max-frequency = <125000000>;
+				status = "okay";
+			};
+		};
+	};
+
+	__overrides__ {
+		cs0_pin  = <&frag0>,"brcm,pins:0",
+			   <&frag1>,"cs-gpios:4";
+		cs0_spidev = <&spidev6_0>,"status";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/spi6-2cs-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/spi6-2cs-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/spi6-2cs-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/spi6-2cs-overlay.dts	2021-07-25 16:45:35.158807390 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/dts-v1/;
+/plugin/;
+
+
+/ {
+	compatible = "brcm,bcm2711";
+
+	fragment@0 {
+		target = <&spi6_cs_pins>;
+		frag0: __overlay__ {
+			brcm,pins = <18 27>;
+			brcm,function = <1>; /* output */
+		};
+	};
+
+	fragment@1 {
+		target = <&spi6>;
+		frag1: __overlay__ {
+			/* needed to avoid dtc warning */
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			pinctrl-names = "default";
+		        pinctrl-0 = <&spi6_pins &spi6_cs_pins>;
+			cs-gpios = <&gpio 18 1>, <&gpio 27 1>;
+			status = "okay";
+
+			spidev6_0: spidev@0 {
+				compatible = "spidev";
+				reg = <0>;      /* CE0 */
+				#address-cells = <1>;
+				#size-cells = <0>;
+				spi-max-frequency = <125000000>;
+				status = "okay";
+			};
+
+			spidev6_1: spidev@1 {
+				compatible = "spidev";
+				reg = <1>;      /* CE1 */
+				#address-cells = <1>;
+				#size-cells = <0>;
+				spi-max-frequency = <125000000>;
+				status = "okay";
+			};
+		};
+	};
+
+	__overrides__ {
+		cs0_pin  = <&frag0>,"brcm,pins:0",
+			   <&frag1>,"cs-gpios:4";
+		cs1_pin  = <&frag0>,"brcm,pins:4",
+			   <&frag1>,"cs-gpios:16";
+		cs0_spidev = <&spidev6_0>,"status";
+		cs1_spidev = <&spidev6_1>,"status";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/spi-gpio35-39-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/spi-gpio35-39-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/spi-gpio35-39-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/spi-gpio35-39-overlay.dts	2021-07-25 16:45:35.158807390 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/*
+ * Device tree overlay to move spi0 to gpio 35 to 39 on CM
+ */
+
+/dts-v1/;
+/plugin/;
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&spi0>;
+		__overlay__ {
+			cs-gpios = <&gpio 36 1>, <&gpio 35 1>;
+		};
+	};
+
+	fragment@1 {
+		target = <&spi0_cs_pins>;
+		__overlay__ {
+			brcm,pins = <36 35>;
+		};
+	};
+
+	fragment@2 {
+		target = <&spi0_pins>;
+		__overlay__ {
+			brcm,pins = <37 38 39>;
+		};
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/spi-gpio40-45-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/spi-gpio40-45-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/spi-gpio40-45-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/spi-gpio40-45-overlay.dts	2021-07-25 16:45:35.158807390 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/*
+ * Boot EEPROM overlay
+ */
+
+/dts-v1/;
+/plugin/;
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&spi0>;
+		__overlay__ {
+			cs-gpios = <&gpio 43 1>, <&gpio 44 1>, <&gpio 45 1>;
+			status = "okay";
+		};
+	};
+
+	fragment@1 {
+		target = <&spi0_cs_pins>;
+		__overlay__ {
+			brcm,pins = <45 44 43>;
+			brcm,function = <1>; /* output */
+			status = "okay";
+		};
+	};
+
+	fragment@2 {
+		target = <&spi0_pins>;
+		__overlay__ {
+			brcm,pins = <40 41 42>;
+			brcm,function = <3>; /* alt4 */
+			status = "okay";
+		};
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/spi-rtc-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/spi-rtc-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/spi-rtc-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/spi-rtc-overlay.dts	2021-07-25 16:45:35.158807390 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// Definitions for several SPI-based Real Time Clocks
+/dts-v1/;
+/plugin/;
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&rtc>;
+		__dormant__ {
+			compatible = "maxim,ds3232";
+		};
+	};
+
+	fragment@1 {
+		target = <&rtc>;
+		__dormant__ {
+			compatible = "maxim,ds3234";
+		};
+	};
+
+	fragment@2 {
+		target = <&rtc>;
+		__dormant__ {
+			compatible = "nxp,rtc-pcf2123";
+		};
+	};
+
+	spidev: fragment@100 {
+		target = <&spidev0>;
+		__overlay__ {
+			status = "disabled";
+		};
+	};
+
+	frag101: fragment@101 {
+		target = <&spi0>;
+		__overlay__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			status = "okay";
+
+			rtc: rtc@0 {
+				reg = <0>;
+				spi-max-frequency = <5000000>;
+			};
+		};
+	};
+
+	__overrides__ {
+		spi0_0 = <&spidev>, "target:0=",<&spidev0>,
+		         <&frag101>, "target:0=",<&spi0>,
+		         <&rtc>, "reg:0=0";
+		spi0_1 = <&spidev>, "target:0=",<&spidev1>,
+		         <&frag101>, "target:0=",<&spi0>,
+		         <&rtc>, "reg:0=1";
+		spi1_0 = <0>,"-100",
+		         <&frag101>, "target:0=",<&spi1>,
+		         <&rtc>, "reg:0=0";
+		spi1_1 = <0>,"-100",
+		         <&frag101>, "target:0=",<&spi1>,
+		         <&rtc>, "reg:0=1";
+		spi2_0 = <0>,"-100",
+		         <&frag101>, "target:0=",<&spi2>,
+		         <&rtc>, "reg:0=0";
+		spi2_1 = <0>,"-100",
+		         <&frag101>, "target:0=",<&spi2>,
+		         <&rtc>, "reg:0=1";
+		cs_high = <&rtc>, "spi-cs-high?";
+
+		ds3232 = <0>,"+0";
+		ds3234 = <0>,"+1";
+		pcf2123 = <0>,"+2";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/ssd1306-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/ssd1306-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/ssd1306-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/ssd1306-overlay.dts	2021-07-25 16:45:35.158807390 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// Overlay for SSD1306 128x64 and 128x32 OLED displays
+/dts-v1/;
+/plugin/;
+
+/ {
+    compatible = "brcm,bcm2835";
+
+    fragment@0 {
+	target = <&i2c1>;
+	__overlay__ {
+	    status = "okay";
+
+	    #address-cells = <1>;
+	    #size-cells = <0>;
+
+	    ssd1306: oled@3c{
+		compatible = "solomon,ssd1306fb-i2c";
+		reg = <0x3c>;
+		solomon,width = <128>;
+		solomon,height = <64>;
+		solomon,page-offset = <0>;
+	    };
+	};
+    };
+
+    __overrides__ {
+	address = <&ssd1306>,"reg:0";
+	width = <&ssd1306>,"solomon,width:0";
+	height = <&ssd1306>,"solomon,height:0";
+	offset = <&ssd1306>,"solomon,page-offset:0";
+	normal = <&ssd1306>,"solomon,segment-no-remap?";
+	sequential = <&ssd1306>,"solomon,com-seq?";
+	remapped = <&ssd1306>,"solomon,com-lrremap?";
+	inverted = <&ssd1306>,"solomon,com-invdir?";
+    };
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/ssd1306-spi-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/ssd1306-spi-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/ssd1306-spi-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/ssd1306-spi-overlay.dts	2021-07-25 16:45:35.158807390 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/*
+ * Device Tree overlay for SSD1306 based SPI OLED display
+ *
+ */
+
+/dts-v1/;
+/plugin/;
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&spi0>;
+		__overlay__ {
+			status = "okay";
+		};
+	};
+
+	fragment@1 {
+		target = <&spidev0>;
+		__overlay__ {
+			status = "disabled";
+		};
+	};
+
+	fragment@2 {
+		target = <&spidev1>;
+		__overlay__ {
+			status = "disabled";
+		};
+	};
+
+	fragment@3 {
+		target = <&gpio>;
+		__overlay__ {
+			ssd1306_pins: ssd1306_pins {
+                                brcm,pins = <25 24>;
+                                brcm,function = <1 1>; /* out out */
+			};
+		};
+	};
+
+	fragment@4 {
+		target = <&spi0>;
+		__overlay__ {
+			/* needed to avoid dtc warning */
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			ssd1306: ssd1306@0{
+				compatible = "solomon,ssd1306";
+				reg = <0>;
+				pinctrl-names = "default";
+				pinctrl-0 = <&ssd1306_pins>;
+
+				spi-max-frequency = <10000000>;
+				bgr = <0>;
+				bpp = <1>;
+				rotate = <0>;
+				fps = <25>;
+				buswidth = <8>;
+				reset-gpios = <&gpio 25 1>;
+				dc-gpios = <&gpio 24 0>;
+				debug = <0>;
+
+				solomon,height = <64>;
+				solomon,width = <128>;
+				solomon,page-offset = <0>;
+			};
+		};
+	};
+
+	__overrides__ {
+		speed     = <&ssd1306>,"spi-max-frequency:0";
+		rotate    = <&ssd1306>,"rotate:0";
+		fps       = <&ssd1306>,"fps:0";
+		debug     = <&ssd1306>,"debug:0";
+		dc_pin    = <&ssd1306>,"dc-gpios:4",
+		            <&ssd1306_pins>,"brcm,pins:4";
+		reset_pin = <&ssd1306>,"reset-gpios:4",
+		            <&ssd1306_pins>,"brcm,pins:0";
+		height    = <&ssd1306>,"solomon,height:0";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/ssd1331-spi-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/ssd1331-spi-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/ssd1331-spi-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/ssd1331-spi-overlay.dts	2021-07-25 16:45:35.158807390 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/*
+ * Device Tree overlay for SSD1331 based SPI OLED display
+ *
+ */
+
+/dts-v1/;
+/plugin/;
+
+/ {
+        compatible = "brcm,bcm2835";
+
+        fragment@0 {
+                target = <&spi0>;
+                __overlay__ {
+                        status = "okay";
+                };
+        };
+
+        fragment@1 {
+                target = <&spidev0>;
+                __overlay__ {
+                        status = "disabled";
+                };
+        };
+
+        fragment@2 {
+                target = <&spidev1>;
+                __overlay__ {
+                        status = "disabled";
+                };
+        };
+
+        fragment@3 {
+                target = <&gpio>;
+                __overlay__ {
+                        ssd1331_pins: ssd1331_pins {
+                                brcm,pins = <25 24>;
+                                brcm,function = <1 1>; /* out out */
+                        };
+                };
+        };
+
+        fragment@4 {
+                target = <&spi0>;
+                __overlay__ {
+                        /* needed to avoid dtc warning */
+                        #address-cells = <1>;
+                        #size-cells = <0>;
+
+                        ssd1331: ssd1331@0{
+                                compatible = "solomon,ssd1331";
+                                reg = <0>;
+                                pinctrl-names = "default";
+                                pinctrl-0 = <&ssd1331_pins>;
+
+                                spi-max-frequency = <4500000>;
+                                bgr = <0>;
+                                bpp = <16>;
+                                rotate = <0>;
+                                fps = <25>;
+                                buswidth = <8>;
+                                reset-gpios = <&gpio 25 1>;
+                                dc-gpios = <&gpio 24 0>;
+                                debug = <0>;
+
+                                solomon,height = <64>;
+                                solomon,width = <96>;
+                                solomon,page-offset = <0>;
+                        };
+                };
+        };
+
+        __overrides__ {
+                speed     = <&ssd1331>,"spi-max-frequency:0";
+                rotate    = <&ssd1331>,"rotate:0";
+                fps       = <&ssd1331>,"fps:0";
+                debug     = <&ssd1331>,"debug:0";
+                dc_pin    = <&ssd1331>,"dc-gpios:4",
+                            <&ssd1331_pins>,"brcm,pins:4";
+                reset_pin = <&ssd1331>,"reset-gpios:4",
+                            <&ssd1331_pins>,"brcm,pins:0";
+        };
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/ssd1351-spi-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/ssd1351-spi-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/ssd1351-spi-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/ssd1351-spi-overlay.dts	2021-07-25 16:45:35.158807390 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/*
+ * Device Tree overlay for SSD1351 based SPI OLED display
+ *
+ */
+
+/dts-v1/;
+/plugin/;
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&spi0>;
+		__overlay__ {
+			status = "okay";
+		};
+	};
+
+	fragment@1 {
+		target = <&spidev0>;
+		__overlay__ {
+			status = "disabled";
+		};
+	};
+
+	fragment@2 {
+		target = <&spidev1>;
+		__overlay__ {
+			status = "disabled";
+		};
+	};
+
+	fragment@3 {
+		target = <&gpio>;
+		__overlay__ {
+			ssd1351_pins: ssd1351_pins {
+                                brcm,pins = <25 24>;
+                                brcm,function = <1 1>; /* out out */
+			};
+		};
+	};
+
+	fragment@4 {
+		target = <&spi0>;
+		__overlay__ {
+			/* needed to avoid dtc warning */
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			ssd1351: ssd1351@0{
+				compatible = "solomon,ssd1351";
+				reg = <0>;
+				pinctrl-names = "default";
+				pinctrl-0 = <&ssd1351_pins>;
+
+				spi-max-frequency = <4500000>;
+				bgr = <0>;
+				bpp = <16>;
+				rotate = <0>;
+				fps = <25>;
+				buswidth = <8>;
+				reset-gpios = <&gpio 25 1>;
+				dc-gpios = <&gpio 24 0>;
+				debug = <0>;
+
+				solomon,height = <128>;
+				solomon,width = <128>;
+				solomon,page-offset = <0>;
+			};
+		};
+	};
+
+	__overrides__ {
+		speed     = <&ssd1351>,"spi-max-frequency:0";
+		rotate    = <&ssd1351>,"rotate:0";
+		fps       = <&ssd1351>,"fps:0";
+		debug     = <&ssd1351>,"debug:0";
+		dc_pin    = <&ssd1351>,"dc-gpios:4",
+		            <&ssd1351_pins>,"brcm,pins:4";
+		reset_pin = <&ssd1351>,"reset-gpios:4",
+		            <&ssd1351_pins>,"brcm,pins:0";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/superaudioboard-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/superaudioboard-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/superaudioboard-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/superaudioboard-overlay.dts	2021-07-25 16:45:35.158807390 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// Definitions for SuperAudioBoard
+/dts-v1/;
+/plugin/;
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&sound>;
+		__overlay__ {
+			compatible = "simple-audio-card";
+			i2s-controller = <&i2s>;
+			status = "okay";
+
+			simple-audio-card,name = "SuperAudioBoard";
+
+			simple-audio-card,widgets =
+				"Line", "Line In",
+				"Line", "Line Out";
+
+			simple-audio-card,routing =
+				"Line Out","AOUTA+",
+				"Line Out","AOUTA-",
+				"Line Out","AOUTB+",
+				"Line Out","AOUTB-",
+				"AINA","Line In",
+				"AINB","Line In";
+
+			simple-audio-card,format = "i2s";
+
+			simple-audio-card,bitclock-master = <&sound_master>;
+			simple-audio-card,frame-master = <&sound_master>;
+
+			simple-audio-card,cpu {
+				sound-dai = <&i2s>;
+				dai-tdm-slot-num = <2>;
+				dai-tdm-slot-width = <32>;
+			};
+
+			sound_master: simple-audio-card,codec {
+				sound-dai = <&cs4271>;
+				system-clock-frequency = <24576000>;
+			};
+		};
+	};
+
+	fragment@1 {
+		target = <&i2s>;
+		__overlay__ {
+			status = "okay";
+		};
+	};
+    
+	fragment@2 {
+		target = <&i2c1>;
+		__overlay__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			status = "okay";
+
+			cs4271: cs4271@10 {
+				#sound-dai-cells = <0>;
+				compatible = "cirrus,cs4271";
+				reg = <0x10>;
+				status = "okay";
+				reset-gpio = <&gpio 26 0>; /* Pin 26, active high */
+			};
+		};
+	};
+	__overrides__ {
+		gpiopin = <&cs4271>,"reset-gpio:4";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/sx150x-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/sx150x-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/sx150x-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/sx150x-overlay.dts	2021-07-25 16:45:35.158807390 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// Definitions for SX150x I2C GPIO Expanders from Semtech
+
+// dtparams:
+//     sx150<x>-<n>-<m>          - Enables SX150X device on I2C#<n> with slave address <m>. <x> may be 1-9.
+//                                 <n> may be 0 or 1.  Permissible values of <m> (which is denoted in hex)
+//                                 depend on the device variant.
+//                                 For SX1501, SX1502, SX1504 and SX1505, <m> may be 20 or 21.
+//                                 For SX1503 and SX1506, <m> may be 20.
+//                                 For SX1507 and SX1509, <m> may be 3E, 3F, 70 or 71.
+//                                 For SX1508, <m> may be 20, 21, 22 or 23.
+//     sx150<x>-<n>-<m>-int-gpio - Integer, enables interrupts on SX150X device on I2C#<n> with slave address <m>,
+//                                 specifies the GPIO pin to which NINT output of SX150X is connected.
+//
+//
+// Example 1: A single SX1505 device on I2C#1 with its slave address set to 0x20 and NINT output connected to GPIO25:
+// dtoverlay=sx150x:sx1505-1-20,sx1505-1-20-int-gpio=25
+//
+// Example 2: Two SX1507 devices on I2C#0 with their slave addresses set to 0x3E and 0x70 (interrupts not used):
+// dtoverlay=sx150x:sx1507-0-3E,sx1507-0-70
+
+/dts-v1/;
+/plugin/;
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	// Enable I2C#0 interface
+	fragment@0 {
+		target = <&i2c0>;
+		__dormant__ {
+			status = "okay";
+		};
+	};
+
+	// Enable I2C#1 interface
+	fragment@1 {
+		target = <&i2c1>;
+		__dormant__ {
+			status = "okay";
+		};
+	};
+
+	// Enable a SX1501 on I2C#0 at slave addr 0x20
+	fragment@2 {
+		target = <&i2c0>;
+		__dormant__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			sx1501_0_20: sx150x@20 {
+				compatible = "semtech,sx1501q";
+				reg = <0x20>;
+				gpio-controller;
+				#gpio-cells = <2>;
+				#interrupt-cells = <2>;
+				interrupts = <25 2>; /* 1st word overwritten by sx1501-0-20-int-gpio parameter
+				                        2nd word is 2 for falling-edge triggered */
+				status = "okay";
+			};
+		};
+	};
+
+	// Enable a SX1501 on I2C#1 at slave addr 0x20
+	fragment@3 {
+		target = <&i2c1>;
+		__dormant__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			sx1501_1_20: sx150x@20 {
+				compatible = "semtech,sx1501q";
+				reg = <0x20>;
+				gpio-controller;
+				#gpio-cells = <2>;
+				#interrupt-cells = <2>;
+				interrupts = <25 2>; /* 1st word overwritten by sx1501-1-20-int-gpio parameter
+				                        2nd word is 2 for falling-edge triggered */
+				status = "okay";
+			};
+		};
+	};
+
+	// Enable a SX1501 on I2C#0 at slave addr 0x21
+	fragment@4 {
+		target = <&i2c0>;
+		__dormant__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			sx1501_0_21: sx150x@21 {
+				compatible = "semtech,sx1501q";
+				reg = <0x21>;
+				gpio-controller;
+				#gpio-cells = <2>;
+				#interrupt-cells = <2>;
+				interrupts = <25 2>; /* 1st word overwritten by sx1501-0-21-int-gpio parameter
+				                        2nd word is 2 for falling-edge triggered */
+				status = "okay";
+			};
+		};
+	};
+
+	// Enable a SX1501 on I2C#1 at slave addr 0x21
+	fragment@5 {
+		target = <&i2c1>;
+		__dormant__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			sx1501_1_21: sx150x@21 {
+				compatible = "semtech,sx1501q";
+				reg = <0x21>;
+				gpio-controller;
+				#gpio-cells = <2>;
+				#interrupt-cells = <2>;
+				interrupts = <25 2>; /* 1st word overwritten by sx1501-1-21-int-gpio parameter
+				                        2nd word is 2 for falling-edge triggered */
+				status = "okay";
+			};
+		};
+	};
+
+	// Enable a SX1502 on I2C#0 at slave addr 0x20
+	fragment@6 {
+		target = <&i2c0>;
+		__dormant__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			sx1502_0_20: sx150x@20 {
+				compatible = "semtech,sx1502q";
+				reg = <0x20>;
+				gpio-controller;
+				#gpio-cells = <2>;
+				#interrupt-cells = <2>;
+				interrupts = <25 2>; /* 1st word overwritten by sx1502-0-20-int-gpio parameter
+				                        2nd word is 2 for falling-edge triggered */
+				status = "okay";
+			};
+		};
+	};
+
+	// Enable a SX1502 on I2C#1 at slave addr 0x20
+	fragment@7 {
+		target = <&i2c1>;
+		__dormant__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			sx1502_1_20: sx150x@20 {
+				compatible = "semtech,sx1502q";
+				reg = <0x20>;
+				gpio-controller;
+				#gpio-cells = <2>;
+				#interrupt-cells = <2>;
+				interrupts = <25 2>; /* 1st word overwritten by sx1502-1-20-int-gpio parameter
+				                        2nd word is 2 for falling-edge triggered */
+				status = "okay";
+			};
+		};
+	};
+
+	// Enable a SX1502 on I2C#0 at slave addr 0x21
+	fragment@8 {
+		target = <&i2c0>;
+		__dormant__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			sx1502_0_21: sx150x@21 {
+				compatible = "semtech,sx1502q";
+				reg = <0x21>;
+				gpio-controller;
+				#gpio-cells = <2>;
+				#interrupt-cells = <2>;
+				interrupts = <25 2>; /* 1st word overwritten by sx1502-0-21-int-gpio parameter
+				                        2nd word is 2 for falling-edge triggered */
+				status = "okay";
+			};
+		};
+	};
+
+	// Enable a SX1502 on I2C#1 at slave addr 0x21
+	fragment@9 {
+		target = <&i2c1>;
+		__dormant__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			sx1502_1_21: sx150x@21 {
+				compatible = "semtech,sx1502q";
+				reg = <0x21>;
+				gpio-controller;
+				#gpio-cells = <2>;
+				#interrupt-cells = <2>;
+				interrupts = <25 2>; /* 1st word overwritten by sx1501-1-21-int-gpio parameter
+				                        2nd word is 2 for falling-edge triggered */
+				status = "okay";
+			};
+		};
+	};
+
+	// Enable a SX1503 on I2C#0 at slave addr 0x20
+	fragment@10 {
+		target = <&i2c0>;
+		__dormant__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			sx1503_0_20: sx150x@20 {
+				compatible = "semtech,sx1503q";
+				reg = <0x20>;
+				gpio-controller;
+				#gpio-cells = <2>;
+				#interrupt-cells = <2>;
+				interrupts = <25 2>; /* 1st word overwritten by sx1503-0-20-int-gpio parameter
+				                        2nd word is 2 for falling-edge triggered */
+				status = "okay";
+			};
+		};
+	};
+
+	// Enable a SX1503 on I2C#1 at slave addr 0x20
+	fragment@11 {
+		target = <&i2c1>;
+		__dormant__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			sx1503_1_20: sx150x@20 {
+				compatible = "semtech,sx1503q";
+				reg = <0x20>;
+				gpio-controller;
+				#gpio-cells = <2>;
+				#interrupt-cells = <2>;
+				interrupts = <25 2>; /* 1st word overwritten by sx1503-1-20-int-gpio parameter
+				                        2nd word is 2 for falling-edge triggered */
+				status = "okay";
+			};
+		};
+	};
+
+	// Enable a SX1504 on I2C#0 at slave addr 0x20
+	fragment@12 {
+		target = <&i2c0>;
+		__dormant__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			sx1504_0_20: sx150x@20 {
+				compatible = "semtech,sx1504q";
+				reg = <0x20>;
+				gpio-controller;
+				#gpio-cells = <2>;
+				#interrupt-cells = <2>;
+				interrupts = <25 2>; /* 1st word overwritten by sx1504-0-20-int-gpio parameter
+				                        2nd word is 2 for falling-edge triggered */
+				status = "okay";
+			};
+		};
+	};
+
+	// Enable a SX1504 on I2C#1 at slave addr 0x20
+	fragment@13 {
+		target = <&i2c1>;
+		__dormant__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			sx1504_1_20: sx150x@20 {
+				compatible = "semtech,sx1504q";
+				reg = <0x20>;
+				gpio-controller;
+				#gpio-cells = <2>;
+				#interrupt-cells = <2>;
+				interrupts = <25 2>; /* 1st word overwritten by sx1504-1-20-int-gpio parameter
+				                        2nd word is 2 for falling-edge triggered */
+				status = "okay";
+			};
+		};
+	};
+
+	// Enable a SX1504 on I2C#0 at slave addr 0x21
+	fragment@14 {
+		target = <&i2c0>;
+		__dormant__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			sx1504_0_21: sx150x@21 {
+				compatible = "semtech,sx1504q";
+				reg = <0x21>;
+				gpio-controller;
+				#gpio-cells = <2>;
+				#interrupt-cells = <2>;
+				interrupts = <25 2>; /* 1st word overwritten by sx1504-0-21-int-gpio parameter
+				                        2nd word is 2 for falling-edge triggered */
+				status = "okay";
+			};
+		};
+	};
+
+	// Enable a SX1504 on I2C#1 at slave addr 0x21
+	fragment@15 {
+		target = <&i2c1>;
+		__dormant__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			sx1504_1_21: sx150x@21 {
+				compatible = "semtech,sx1504q";
+				reg = <0x21>;
+				gpio-controller;
+				#gpio-cells = <2>;
+				#interrupt-cells = <2>;
+				interrupts = <25 2>; /* 1st word overwritten by sx1504-1-20-int-gpio parameter
+				                        2nd word is 2 for falling-edge triggered */
+				status = "okay";
+			};
+		};
+	};
+
+	// Enable a SX1505 on I2C#0 at slave addr 0x20
+	fragment@16 {
+		target = <&i2c0>;
+		__dormant__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			sx1505_0_20: sx150x@20 {
+				compatible = "semtech,sx1505q";
+				reg = <0x20>;
+				gpio-controller;
+				#gpio-cells = <2>;
+				#interrupt-cells = <2>;
+				interrupts = <25 2>; /* 1st word overwritten by sx1505-0-20-int-gpio parameter
+				                        2nd word is 2 for falling-edge triggered */
+				status = "okay";
+			};
+		};
+	};
+
+	// Enable a SX1505 on I2C#1 at slave addr 0x20
+	fragment@17 {
+		target = <&i2c1>;
+		__dormant__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			sx1505_1_20: sx150x@20 {
+				compatible = "semtech,sx1505q";
+				reg = <0x20>;
+				gpio-controller;
+				#gpio-cells = <2>;
+				#interrupt-cells = <2>;
+				interrupts = <25 2>; /* 1st word overwritten by sx1505-1-20-int-gpio parameter
+				                        2nd word is 2 for falling-edge triggered */
+				status = "okay";
+			};
+		};
+	};
+
+	// Enable a SX1505 on I2C#0 at slave addr 0x21
+	fragment@18 {
+		target = <&i2c0>;
+		__dormant__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			sx1505_0_21: sx150x@21 {
+				compatible = "semtech,sx1505q";
+				reg = <0x21>;
+				gpio-controller;
+				#gpio-cells = <2>;
+				#interrupt-cells = <2>;
+				interrupts = <25 2>; /* 1st word overwritten by sx1505-0-21-int-gpio parameter
+				                        2nd word is 2 for falling-edge triggered */
+				status = "okay";
+			};
+		};
+	};
+
+	// Enable a SX1505 on I2C#1 at slave addr 0x21
+	fragment@19 {
+		target = <&i2c1>;
+		__dormant__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			sx1505_1_21: sx150x@21 {
+				compatible = "semtech,sx1505q";
+				reg = <0x21>;
+				gpio-controller;
+				#gpio-cells = <2>;
+				#interrupt-cells = <2>;
+				interrupts = <25 2>; /* 1st word overwritten by sx1505-1-21-int-gpio parameter
+				                        2nd word is 2 for falling-edge triggered */
+				status = "okay";
+			};
+		};
+	};
+
+	// Enable a SX1506 on I2C#0 at slave addr 0x20
+	fragment@20 {
+		target = <&i2c0>;
+		__dormant__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			sx1506_0_20: sx150x@20 {
+				compatible = "semtech,sx1506q";
+				reg = <0x20>;
+				gpio-controller;
+				#gpio-cells = <2>;
+				#interrupt-cells = <2>;
+				interrupts = <25 2>; /* 1st word overwritten by sx1506-0-20-int-gpio parameter
+				                        2nd word is 2 for falling-edge triggered */
+				status = "okay";
+			};
+		};
+	};
+
+	// Enable a SX1506 on I2C#1 at slave addr 0x20
+	fragment@21 {
+		target = <&i2c1>;
+		__dormant__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			sx1506_1_20: sx150x@20 {
+				compatible = "semtech,sx1506q";
+				reg = <0x20>;
+				gpio-controller;
+				#gpio-cells = <2>;
+				#interrupt-cells = <2>;
+				interrupts = <25 2>; /* 1st word overwritten by sx1506-1-20-int-gpio parameter
+				                        2nd word is 2 for falling-edge triggered */
+				status = "okay";
+			};
+		};
+	};
+
+	// Enable a SX1507 on I2C#0 at slave addr 0x3E
+	fragment@22 {
+		target = <&i2c0>;
+		__dormant__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			sx1507_0_3E: sx150x@3E {
+				compatible = "semtech,sx1507q";
+				reg = <0x3E>;
+				gpio-controller;
+				#gpio-cells = <2>;
+				#interrupt-cells = <2>;
+				interrupts = <25 2>; /* 1st word overwritten by sx1507_0_3E-int-gpio parameter
+				                        2nd word is 2 for falling-edge triggered */
+				status = "okay";
+			};
+		};
+	};
+
+	// Enable a SX1507 on I2C#1 at slave addr 0x3E
+	fragment@23 {
+		target = <&i2c1>;
+		__dormant__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			sx1507_1_3E: sx150x@3E {
+				compatible = "semtech,sx1507q";
+				reg = <0x3E>;
+				gpio-controller;
+				#gpio-cells = <2>;
+				#interrupt-cells = <2>;
+				interrupts = <25 2>; /* 1st word overwritten by sx1507_1_3E-int-gpio parameter
+				                        2nd word is 2 for falling-edge triggered */
+				status = "okay";
+			};
+		};
+	};
+
+	// Enable a SX1507 on I2C#0 at slave addr 0x3F
+	fragment@24 {
+		target = <&i2c0>;
+		__dormant__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			sx1507_0_3F: sx150x@3F {
+				compatible = "semtech,sx1507q";
+				reg = <0x3F>;
+				gpio-controller;
+				#gpio-cells = <2>;
+				#interrupt-cells = <2>;
+				interrupts = <25 2>; /* 1st word overwritten by sx1507_0_3F-int-gpio parameter
+				                        2nd word is 2 for falling-edge triggered */
+				status = "okay";
+			};
+		};
+	};
+
+	// Enable a SX1507 on I2C#1 at slave addr 0x3F
+	fragment@25 {
+		target = <&i2c1>;
+		__dormant__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			sx1507_1_3F: sx150x@3F {
+				compatible = "semtech,sx1507q";
+				reg = <0x3F>;
+				gpio-controller;
+				#gpio-cells = <2>;
+				#interrupt-cells = <2>;
+				interrupts = <25 2>; /* 1st word overwritten by sx1507_1_3F-int-gpio parameter
+				                        2nd word is 2 for falling-edge triggered */
+				status = "okay";
+			};
+		};
+	};
+
+	// Enable a SX1507 on I2C#0 at slave addr 0x70
+	fragment@26 {
+		target = <&i2c0>;
+		__dormant__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			sx1507_0_70: sx150x@70 {
+				compatible = "semtech,sx1507q";
+				reg = <0x70>;
+				gpio-controller;
+				#gpio-cells = <2>;
+				#interrupt-cells = <2>;
+				interrupts = <25 2>; /* 1st word overwritten by sx1507-0-70-int-gpio parameter
+				                        2nd word is 2 for falling-edge triggered */
+				status = "okay";
+			};
+		};
+	};
+
+	// Enable a SX1507 on I2C#1 at slave addr 0x70
+	fragment@27 {
+		target = <&i2c1>;
+		__dormant__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			sx1507_1_70: sx150x@70 {
+				compatible = "semtech,sx1507q";
+				reg = <0x70>;
+				gpio-controller;
+				#gpio-cells = <2>;
+				#interrupt-cells = <2>;
+				interrupts = <25 2>; /* 1st word overwritten by sx1507-1-70-int-gpio parameter
+				                        2nd word is 2 for falling-edge triggered */
+				status = "okay";
+			};
+		};
+	};
+
+	// Enable a SX1507 on I2C#0 at slave addr 0x71
+	fragment@28 {
+		target = <&i2c0>;
+		__dormant__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			sx1507_0_71: sx150x@71 {
+				compatible = "semtech,sx1507q";
+				reg = <0x71>;
+				gpio-controller;
+				#gpio-cells = <2>;
+				#interrupt-cells = <2>;
+				interrupts = <25 2>; /* 1st word overwritten by sx1507-0-71-int-gpio parameter
+				                        2nd word is 2 for falling-edge triggered */
+				status = "okay";
+			};
+		};
+	};
+
+	// Enable a SX1507 on I2C#1 at slave addr 0x71
+	fragment@29 {
+		target = <&i2c1>;
+		__dormant__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			sx1507_1_71: sx150x@71 {
+				compatible = "semtech,sx1507q";
+				reg = <0x71>;
+				gpio-controller;
+				#gpio-cells = <2>;
+				#interrupt-cells = <2>;
+				interrupts = <25 2>; /* 1st word overwritten by sx1507-1-71-int-gpio parameter
+				                        2nd word is 2 for falling-edge triggered */
+				status = "okay";
+			};
+		};
+	};
+
+	// Enable a SX1508 on I2C#0 at slave addr 0x20
+	fragment@30 {
+		target = <&i2c0>;
+		__dormant__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			sx1508_0_20: sx150x@20 {
+				compatible = "semtech,sx1508q";
+				reg = <0x20>;
+				gpio-controller;
+				#gpio-cells = <2>;
+				#interrupt-cells = <2>;
+				interrupts = <25 2>; /* 1st word overwritten by sx1508-0-20-int-gpio parameter
+				                        2nd word is 2 for falling-edge triggered */
+				status = "okay";
+			};
+		};
+	};
+
+	// Enable a SX1508 on I2C#1 at slave addr 0x20
+	fragment@31 {
+		target = <&i2c1>;
+		__dormant__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			sx1508_1_20: sx150x@20 {
+				compatible = "semtech,sx1508q";
+				reg = <0x20>;
+				gpio-controller;
+				#gpio-cells = <2>;
+				#interrupt-cells = <2>;
+				interrupts = <25 2>; /* 1st word overwritten by sx1508-1-20-int-gpio parameter
+				                        2nd word is 2 for falling-edge triggered */
+				status = "okay";
+			};
+		};
+	};
+
+	// Enable a SX1508 on I2C#0 at slave addr 0x21
+	fragment@32 {
+		target = <&i2c0>;
+		__dormant__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			sx1508_0_21: sx150x@21 {
+				compatible = "semtech,sx1508q";
+				reg = <0x21>;
+				gpio-controller;
+				#gpio-cells = <2>;
+				#interrupt-cells = <2>;
+				interrupts = <25 2>; /* 1st word overwritten by sx1508-0-21-int-gpio parameter
+				                        2nd word is 2 for falling-edge triggered */
+				status = "okay";
+			};
+		};
+	};
+
+	// Enable a SX1508 on I2C#1 at slave addr 0x21
+	fragment@33 {
+		target = <&i2c1>;
+		__dormant__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			sx1508_1_21: sx150x@21 {
+				compatible = "semtech,sx1508q";
+				reg = <0x21>;
+				gpio-controller;
+				#gpio-cells = <2>;
+				#interrupt-cells = <2>;
+				interrupts = <25 2>; /* 1st word overwritten by sx1508-1-21-int-gpio parameter
+				                        2nd word is 2 for falling-edge triggered */
+				status = "okay";
+			};
+		};
+	};
+
+	// Enable a SX1508 on I2C#0 at slave addr 0x22
+	fragment@34 {
+		target = <&i2c0>;
+		__dormant__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			sx1508_0_22: sx150x@22 {
+				compatible = "semtech,sx1508q";
+				reg = <0x22>;
+				gpio-controller;
+				#gpio-cells = <2>;
+				#interrupt-cells = <2>;
+				interrupts = <25 2>; /* 1st word overwritten by sx1508-0-22-int-gpio parameter
+				                        2nd word is 2 for falling-edge triggered */
+				status = "okay";
+			};
+		};
+	};
+
+	// Enable a SX1508 on I2C#1 at slave addr 0x22
+	fragment@35 {
+		target = <&i2c1>;
+		__dormant__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			sx1508_1_22: sx150x@22 {
+				compatible = "semtech,sx1508q";
+				reg = <0x22>;
+				gpio-controller;
+				#gpio-cells = <2>;
+				#interrupt-cells = <2>;
+				interrupts = <25 2>; /* 1st word overwritten by sx1508-1-22-int-gpio parameter
+				                        2nd word is 2 for falling-edge triggered */
+				status = "okay";
+			};
+		};
+	};
+
+	// Enable a SX1508 on I2C#0 at slave addr 0x23
+	fragment@36 {
+		target = <&i2c0>;
+		__dormant__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			sx1508_0_23: sx150x@23 {
+				compatible = "semtech,sx1508q";
+				reg = <0x23>;
+				gpio-controller;
+				#gpio-cells = <2>;
+				#interrupt-cells = <2>;
+				interrupts = <25 2>; /* 1st word overwritten by sx1508-0-23-int-gpio parameter
+				                        2nd word is 2 for falling-edge triggered */
+				status = "okay";
+			};
+		};
+	};
+
+	// Enable a SX1508 on I2C#1 at slave addr 0x23
+	fragment@37 {
+		target = <&i2c1>;
+		__dormant__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			sx1508_1_23: sx150x@23 {
+				compatible = "semtech,sx1508q";
+				reg = <0x23>;
+				gpio-controller;
+				#gpio-cells = <2>;
+				#interrupt-cells = <2>;
+				interrupts = <25 2>; /* 1st word overwritten by sx1508-1-23-int-gpio parameter
+				                        2nd word is 2 for falling-edge triggered */
+				status = "okay";
+			};
+		};
+	};
+
+	// Enable a SX1509 on I2C#0 at slave addr 0x3E
+	fragment@38 {
+		target = <&i2c0>;
+		__dormant__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			sx1509_0_3E: sx150x@3E {
+				compatible = "semtech,sx1509q";
+				reg = <0x3E>;
+				gpio-controller;
+				#gpio-cells = <2>;
+				#interrupt-cells = <2>;
+				interrupts = <25 2>; /* 1st word overwritten by sx1509_0_3E-int-gpio parameter
+				                        2nd word is 2 for falling-edge triggered */
+				status = "okay";
+			};
+		};
+	};
+
+	// Enable a SX1509 on I2C#1 at slave addr 0x3E
+	fragment@39 {
+		target = <&i2c1>;
+		__dormant__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			sx1509_1_3E: sx150x@3E {
+				compatible = "semtech,sx1509q";
+				reg = <0x3E>;
+				gpio-controller;
+				#gpio-cells = <2>;
+				#interrupt-cells = <2>;
+				interrupts = <25 2>; /* 1st word overwritten by sx1509_1_3E-int-gpio parameter
+				                        2nd word is 2 for falling-edge triggered */
+				status = "okay";
+			};
+		};
+	};
+
+	// Enable a SX1509 on I2C#0 at slave addr 0x3F
+	fragment@40 {
+		target = <&i2c0>;
+		__dormant__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			sx1509_0_3F: sx150x@3F {
+				compatible = "semtech,sx1509q";
+				reg = <0x3F>;
+				gpio-controller;
+				#gpio-cells = <2>;
+				#interrupt-cells = <2>;
+				interrupts = <25 2>; /* 1st word overwritten by sx1509_0_3F-int-gpio parameter
+				                        2nd word is 2 for falling-edge triggered */
+				status = "okay";
+			};
+		};
+	};
+
+	// Enable a SX1509 on I2C#1 at slave addr 0x3F
+	fragment@41 {
+		target = <&i2c1>;
+		__dormant__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			sx1509_1_3F: sx150x@3F {
+				compatible = "semtech,sx1509q";
+				reg = <0x3F>;
+				gpio-controller;
+				#gpio-cells = <2>;
+				#interrupt-cells = <2>;
+				interrupts = <25 2>; /* 1st word overwritten by sx1509_1_3F-int-gpio parameter
+				                        2nd word is 2 for falling-edge triggered */
+				status = "okay";
+			};
+		};
+	};
+
+	// Enable a SX1509 on I2C#0 at slave addr 0x70
+	fragment@42 {
+		target = <&i2c0>;
+		__dormant__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			sx1509_0_70: sx150x@70 {
+				compatible = "semtech,sx1509q";
+				reg = <0x70>;
+				gpio-controller;
+				#gpio-cells = <2>;
+				#interrupt-cells = <2>;
+				interrupts = <25 2>; /* 1st word overwritten by sx1509-0-70-int-gpio parameter
+				                        2nd word is 2 for falling-edge triggered */
+				status = "okay";
+			};
+		};
+	};
+
+	// Enable a SX1509 on I2C#1 at slave addr 0x70
+	fragment@43 {
+		target = <&i2c1>;
+		__dormant__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			sx1509_1_70: sx150x@70 {
+				compatible = "semtech,sx1509q";
+				reg = <0x70>;
+				gpio-controller;
+				#gpio-cells = <2>;
+				#interrupt-cells = <2>;
+				interrupts = <25 2>; /* 1st word overwritten by sx1509-1-70-int-gpio parameter
+				                        2nd word is 2 for falling-edge triggered */
+				status = "okay";
+			};
+		};
+	};
+
+	// Enable a SX1509 on I2C#0 at slave addr 0x71
+	fragment@44 {
+		target = <&i2c0>;
+		__dormant__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			sx1509_0_71: sx150x@71 {
+				compatible = "semtech,sx1509q";
+				reg = <0x71>;
+				gpio-controller;
+				#gpio-cells = <2>;
+				#interrupt-cells = <2>;
+				interrupts = <25 2>; /* 1st word overwritten by sx1509-0-71-int-gpio parameter
+				                        2nd word is 2 for falling-edge triggered */
+				status = "okay";
+			};
+		};
+	};
+
+	// Enable a SX1509 on I2C#1 at slave addr 0x71
+	fragment@45 {
+		target = <&i2c1>;
+		__dormant__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			sx1509_1_71: sx150x@71 {
+				compatible = "semtech,sx1509q";
+				reg = <0x71>;
+				gpio-controller;
+				#gpio-cells = <2>;
+				#interrupt-cells = <2>;
+				interrupts = <25 2>; /* 1st word overwritten by sx1509-1-71-int-gpio parameter
+				                        2nd word is 2 for falling-edge triggered */
+				status = "okay";
+			};
+		};
+	};
+
+	// Enable interrupts for a SX1501 on I2C#0 at slave addr 0x20
+	fragment@46 {
+		target = <&sx1501_0_20>;
+		__dormant__ {
+			interrupt-parent = <&gpio>;
+			interrupt-controller;
+			pinctrl-names = "default";
+			pinctrl-0 = <&sx150x_0_20_pins>;
+		};
+	};
+
+	// Enable interrupts for a SX1501 on I2C#1 at slave addr 0x20
+	fragment@47 {
+		target = <&sx1501_1_20>;
+		__dormant__ {
+			interrupt-parent = <&gpio>;
+			interrupt-controller;
+			pinctrl-names = "default";
+			pinctrl-0 = <&sx150x_1_20_pins>;
+		};
+	};
+
+	// Enable interrupts for a SX1501 on I2C#0 at slave addr 0x21
+	fragment@48 {
+		target = <&sx1501_0_21>;
+		__dormant__ {
+			interrupt-parent = <&gpio>;
+			interrupt-controller;
+			pinctrl-names = "default";
+			pinctrl-0 = <&sx150x_0_21_pins>;
+		};
+	};
+
+	// Enable interrupts for a SX1501 on I2C#1 at slave addr 0x21
+	fragment@49 {
+		target = <&sx1501_1_21>;
+		__dormant__ {
+			interrupt-parent = <&gpio>;
+			interrupt-controller;
+			pinctrl-names = "default";
+			pinctrl-0 = <&sx150x_1_21_pins>;
+		};
+	};
+
+	// Enable interrupts for a SX1502 on I2C#0 at slave addr 0x20
+	fragment@50 {
+		target = <&sx1502_0_20>;
+		__dormant__ {
+			interrupt-parent = <&gpio>;
+			interrupt-controller;
+			pinctrl-names = "default";
+			pinctrl-0 = <&sx150x_0_20_pins>;
+		};
+	};
+
+	// Enable interrupts for a SX1502 on I2C#1 at slave addr 0x20
+	fragment@51 {
+		target = <&sx1502_1_20>;
+		__dormant__ {
+			interrupt-parent = <&gpio>;
+			interrupt-controller;
+			pinctrl-names = "default";
+			pinctrl-0 = <&sx150x_1_20_pins>;
+		};
+	};
+
+	// Enable interrupts for a SX1502 on I2C#0 at slave addr 0x21
+	fragment@52 {
+		target = <&sx1502_0_21>;
+		__dormant__ {
+			interrupt-parent = <&gpio>;
+			interrupt-controller;
+			pinctrl-names = "default";
+			pinctrl-0 = <&sx150x_0_21_pins>;
+		};
+	};
+
+	// Enable interrupts for a SX1502 on I2C#1 at slave addr 0x21
+	fragment@53 {
+		target = <&sx1502_1_21>;
+		__dormant__ {
+			interrupt-parent = <&gpio>;
+			interrupt-controller;
+			pinctrl-names = "default";
+			pinctrl-0 = <&sx150x_1_21_pins>;
+		};
+	};
+
+	// Enable interrupts for a SX1503 on I2C#0 at slave addr 0x20
+	fragment@54 {
+		target = <&sx1503_0_20>;
+		__dormant__ {
+			interrupt-parent = <&gpio>;
+			interrupt-controller;
+			pinctrl-names = "default";
+			pinctrl-0 = <&sx150x_0_20_pins>;
+		};
+	};
+
+	// Enable interrupts for a SX1503 on I2C#1 at slave addr 0x20
+	fragment@55 {
+		target = <&sx1503_1_20>;
+		__dormant__ {
+			interrupt-parent = <&gpio>;
+			interrupt-controller;
+			pinctrl-names = "default";
+			pinctrl-0 = <&sx150x_1_20_pins>;
+		};
+	};
+
+	// Enable interrupts for a SX1504 on I2C#0 at slave addr 0x20
+	fragment@56 {
+		target = <&sx1504_0_20>;
+		__dormant__ {
+			interrupt-parent = <&gpio>;
+			interrupt-controller;
+			pinctrl-names = "default";
+			pinctrl-0 = <&sx150x_0_20_pins>;
+		};
+	};
+
+	// Enable interrupts for a SX1504 on I2C#1 at slave addr 0x20
+	fragment@57 {
+		target = <&sx1504_1_20>;
+		__dormant__ {
+			interrupt-parent = <&gpio>;
+			interrupt-controller;
+			pinctrl-names = "default";
+			pinctrl-0 = <&sx150x_1_20_pins>;
+		};
+	};
+
+	// Enable interrupts for a SX1504 on I2C#0 at slave addr 0x21
+	fragment@58 {
+		target = <&sx1504_0_21>;
+		__dormant__ {
+			interrupt-parent = <&gpio>;
+			interrupt-controller;
+			pinctrl-names = "default";
+			pinctrl-0 = <&sx150x_0_21_pins>;
+		};
+	};
+
+	// Enable interrupts for a SX1504 on I2C#1 at slave addr 0x21
+	fragment@59 {
+		target = <&sx1504_1_21>;
+		__dormant__ {
+			interrupt-parent = <&gpio>;
+			interrupt-controller;
+			pinctrl-names = "default";
+			pinctrl-0 = <&sx150x_1_21_pins>;
+		};
+	};
+
+	// Enable interrupts for a SX1505 on I2C#0 at slave addr 0x20
+	fragment@60 {
+		target = <&sx1505_0_20>;
+		__dormant__ {
+			interrupt-parent = <&gpio>;
+			interrupt-controller;
+			pinctrl-names = "default";
+			pinctrl-0 = <&sx150x_0_20_pins>;
+		};
+	};
+
+	// Enable interrupts for a SX1505 on I2C#1 at slave addr 0x20
+	fragment@61 {
+		target = <&sx1505_1_20>;
+		__dormant__ {
+			interrupt-parent = <&gpio>;
+			interrupt-controller;
+			pinctrl-names = "default";
+			pinctrl-0 = <&sx150x_1_20_pins>;
+		};
+	};
+
+	// Enable interrupts for a SX1505 on I2C#0 at slave addr 0x21
+	fragment@62 {
+		target = <&sx1505_0_21>;
+		__dormant__ {
+			interrupt-parent = <&gpio>;
+			interrupt-controller;
+			pinctrl-names = "default";
+			pinctrl-0 = <&sx150x_0_21_pins>;
+		};
+	};
+
+	// Enable interrupts for a SX1505 on I2C#1 at slave addr 0x21
+	fragment@63 {
+		target = <&sx1505_1_21>;
+		__dormant__ {
+			interrupt-parent = <&gpio>;
+			interrupt-controller;
+			pinctrl-names = "default";
+			pinctrl-0 = <&sx150x_1_21_pins>;
+		};
+	};
+
+	// Enable interrupts for a SX1506 on I2C#0 at slave addr 0x20
+	fragment@64 {
+		target = <&sx1506_0_20>;
+		__dormant__ {
+			interrupt-parent = <&gpio>;
+			interrupt-controller;
+			pinctrl-names = "default";
+			pinctrl-0 = <&sx150x_0_20_pins>;
+		};
+	};
+
+	// Enable interrupts for a SX1506 on I2C#1 at slave addr 0x20
+	fragment@65 {
+		target = <&sx1506_1_20>;
+		__dormant__ {
+			interrupt-parent = <&gpio>;
+			interrupt-controller;
+			pinctrl-names = "default";
+			pinctrl-0 = <&sx150x_1_20_pins>;
+		};
+	};
+
+	// Enable interrupts for a SX1507 on I2C#0 at slave addr 0x3E
+	fragment@66 {
+		target = <&sx1507_0_3E>;
+		__dormant__ {
+			interrupt-parent = <&gpio>;
+			interrupt-controller;
+			pinctrl-names = "default";
+			pinctrl-0 = <&sx150x_0_3E_pins>;
+		};
+	};
+
+	// Enable interrupts for a SX1507 on I2C#1 at slave addr 0x3E
+	fragment@67 {
+		target = <&sx1507_1_3E>;
+		__dormant__ {
+			interrupt-parent = <&gpio>;
+			interrupt-controller;
+			pinctrl-names = "default";
+			pinctrl-0 = <&sx150x_1_3E_pins>;
+		};
+	};
+
+	// Enable interrupts for a SX1507 on I2C#0 at slave addr 0x3F
+	fragment@68 {
+		target = <&sx1507_0_3F>;
+		__dormant__ {
+			interrupt-parent = <&gpio>;
+			interrupt-controller;
+			pinctrl-names = "default";
+			pinctrl-0 = <&sx150x_0_3F_pins>;
+		};
+	};
+
+	// Enable interrupts for a SX1507 on I2C#1 at slave addr 0x3F
+	fragment@69 {
+		target = <&sx1507_1_3F>;
+		__dormant__ {
+			interrupt-parent = <&gpio>;
+			interrupt-controller;
+			pinctrl-names = "default";
+			pinctrl-0 = <&sx150x_1_3F_pins>;
+		};
+	};
+
+	// Enable interrupts for a SX1507 on I2C#0 at slave addr 0x70
+	fragment@70 {
+		target = <&sx1507_0_70>;
+		__dormant__ {
+			interrupt-parent = <&gpio>;
+			interrupt-controller;
+			pinctrl-names = "default";
+			pinctrl-0 = <&sx150x_1_70_pins>;
+		};
+	};
+
+	// Enable interrupts for a SX1507 on I2C#1 at slave addr 0x70
+	fragment@71 {
+		target = <&sx1507_1_70>;
+		__dormant__ {
+			interrupt-parent = <&gpio>;
+			interrupt-controller;
+			pinctrl-names = "default";
+			pinctrl-0 = <&sx150x_1_70_pins>;
+		};
+	};
+
+	// Enable interrupts for a SX1507 on I2C#0 at slave addr 0x71
+	fragment@72 {
+		target = <&sx1507_0_71>;
+		__dormant__ {
+			interrupt-parent = <&gpio>;
+			interrupt-controller;
+			pinctrl-names = "default";
+			pinctrl-0 = <&sx150x_0_71_pins>;
+		};
+	};
+
+	// Enable interrupts for a SX1507 on I2C#1 at slave addr 0x71
+	fragment@73 {
+		target = <&sx1507_1_71>;
+		__dormant__ {
+			interrupt-parent = <&gpio>;
+			interrupt-controller;
+			pinctrl-names = "default";
+			pinctrl-0 = <&sx150x_1_71_pins>;
+		};
+	};
+
+	// Enable interrupts for a SX1508 on I2C#0 at slave addr 0x20
+	fragment@74 {
+		target = <&sx1508_0_20>;
+		__dormant__ {
+			interrupt-parent = <&gpio>;
+			interrupt-controller;
+			pinctrl-names = "default";
+			pinctrl-0 = <&sx150x_0_20_pins>;
+		};
+	};
+
+	// Enable interrupts for a SX1508 on I2C#1 at slave addr 0x20
+	fragment@75 {
+		target = <&sx1508_1_20>;
+		__dormant__ {
+			interrupt-parent = <&gpio>;
+			interrupt-controller;
+			pinctrl-names = "default";
+			pinctrl-0 = <&sx150x_1_20_pins>;
+		};
+	};
+
+	// Enable interrupts for a SX1508 on I2C#0 at slave addr 0x21
+	fragment@76 {
+		target = <&sx1508_0_21>;
+		__dormant__ {
+			interrupt-parent = <&gpio>;
+			interrupt-controller;
+			pinctrl-names = "default";
+			pinctrl-0 = <&sx150x_0_21_pins>;
+		};
+	};
+
+	// Enable interrupts for a SX1508 on I2C#1 at slave addr 0x21
+	fragment@77 {
+		target = <&sx1508_1_21>;
+		__dormant__ {
+			interrupt-parent = <&gpio>;
+			interrupt-controller;
+			pinctrl-names = "default";
+			pinctrl-0 = <&sx150x_1_21_pins>;
+		};
+	};
+
+	// Enable interrupts for a SX1508 on I2C#0 at slave addr 0x22
+	fragment@78 {
+		target = <&sx1508_0_22>;
+		__dormant__ {
+			interrupt-parent = <&gpio>;
+			interrupt-controller;
+			pinctrl-names = "default";
+			pinctrl-0 = <&sx150x_0_22_pins>;
+		};
+	};
+
+	// Enable interrupts for a SX1508 on I2C#1 at slave addr 0x22
+	fragment@79 {
+		target = <&sx1508_1_22>;
+		__dormant__ {
+			interrupt-parent = <&gpio>;
+			interrupt-controller;
+			pinctrl-names = "default";
+			pinctrl-0 = <&sx150x_1_22_pins>;
+		};
+	};
+
+	// Enable interrupts for a SX1508 on I2C#0 at slave addr 0x23
+	fragment@80 {
+		target = <&sx1508_0_23>;
+		__dormant__ {
+			interrupt-parent = <&gpio>;
+			interrupt-controller;
+			pinctrl-names = "default";
+			pinctrl-0 = <&sx150x_0_23_pins>;
+		};
+	};
+
+	// Enable interrupts for a SX1508 on I2C#1 at slave addr 0x23
+	fragment@81 {
+		target = <&sx1508_1_23>;
+		__dormant__ {
+			interrupt-parent = <&gpio>;
+			interrupt-controller;
+			pinctrl-names = "default";
+			pinctrl-0 = <&sx150x_1_23_pins>;
+		};
+	};
+
+	// Enable interrupts for a SX1509 on I2C#0 at slave addr 0x3E
+	fragment@82 {
+		target = <&sx1509_0_3E>;
+		__dormant__ {
+			interrupt-parent = <&gpio>;
+			interrupt-controller;
+			pinctrl-names = "default";
+			pinctrl-0 = <&sx150x_0_3E_pins>;
+		};
+	};
+
+	// Enable interrupts for a SX1509 on I2C#1 at slave addr 0x3E
+	fragment@83 {
+		target = <&sx1509_1_3E>;
+		__dormant__ {
+			interrupt-parent = <&gpio>;
+			interrupt-controller;
+			pinctrl-names = "default";
+			pinctrl-0 = <&sx150x_1_3E_pins>;
+		};
+	};
+
+	// Enable interrupts for a SX1509 on I2C#0 at slave addr 0x3F
+	fragment@84 {
+		target = <&sx1509_0_3F>;
+		__dormant__ {
+			interrupt-parent = <&gpio>;
+			interrupt-controller;
+			pinctrl-names = "default";
+			pinctrl-0 = <&sx150x_0_3F_pins>;
+		};
+	};
+
+	// Enable interrupts for a SX1509 on I2C#1 at slave addr 0x3F
+	fragment@85 {
+		target = <&sx1509_1_3F>;
+		__dormant__ {
+			interrupt-parent = <&gpio>;
+			interrupt-controller;
+			pinctrl-names = "default";
+			pinctrl-0 = <&sx150x_1_3F_pins>;
+		};
+	};
+
+	// Enable interrupts for a SX1509 on I2C#0 at slave addr 0x70
+	fragment@86 {
+		target = <&sx1509_0_70>;
+		__dormant__ {
+			interrupt-parent = <&gpio>;
+			interrupt-controller;
+			pinctrl-names = "default";
+			pinctrl-0 = <&sx150x_0_70_pins>;
+		};
+	};
+
+	// Enable interrupts for a SX1509 on I2C#1 at slave addr 0x70
+	fragment@87 {
+		target = <&sx1509_1_70>;
+		__dormant__ {
+			interrupt-parent = <&gpio>;
+			interrupt-controller;
+			pinctrl-names = "default";
+			pinctrl-0 = <&sx150x_1_70_pins>;
+		};
+	};
+
+	// Enable interrupts for a SX1509 on I2C#0 at slave addr 0x71
+	fragment@88 {
+		target = <&sx1509_0_71>;
+		__dormant__ {
+			interrupt-parent = <&gpio>;
+			interrupt-controller;
+			pinctrl-names = "default";
+			pinctrl-0 = <&sx150x_0_71_pins>;
+		};
+	};
+
+	// Enable interrupts for a SX1509 on I2C#1 at slave addr 0x71
+	fragment@89 {
+		target = <&sx1509_1_71>;
+		__dormant__ {
+			interrupt-parent = <&gpio>;
+			interrupt-controller;
+			pinctrl-names = "default";
+			pinctrl-0 = <&sx150x_1_71_pins>;
+		};
+	};
+
+	// Configure GPIO pin connected to NINT output of a SX150x on I2C#0 interface at slave addr 0x20
+        // Configure as a input with no pull-up/down
+	fragment@90 {
+		target = <&gpio>;
+		__dormant__ {
+			sx150x_0_20_pins: sx150x_0_20_pins {
+				brcm,pins = <0>;  /* overwritten by sx150x-0-20-int-gpio parameter */
+				brcm,function = <0>;
+				brcm,pull = <0>;
+			};
+		};
+	};
+
+	// Configure GPIO pin connected to NINT output of a SX150x on I2C#1 interface at slave addr 0x20
+        // Configure as a input with no pull-up/down
+	fragment@91 {
+		target = <&gpio>;
+		__dormant__ {
+			sx150x_1_20_pins: sx150x_1_20_pins {
+				brcm,pins = <0>;  /* overwritten by sx150x-1-20-int-gpio parameter */
+				brcm,function = <0>;
+				brcm,pull = <0>;
+			};
+		};
+	};
+
+	// Configure GPIO pin connected to NINT output of a SX150x on I2C#0 interface at slave addr 0x21
+        // Configure as a input with no pull-up/down
+	fragment@92 {
+		target = <&gpio>;
+		__dormant__ {
+			sx150x_0_21_pins: sx150x_0_21_pins {
+				brcm,pins = <0>;  /* overwritten by sx150x-0-21-int-gpio parameter */
+				brcm,function = <0>;
+				brcm,pull = <0>;
+			};
+		};
+	};
+
+	// Configure GPIO pin connected to NINT output of a SX150x on I2C#1 interface at slave addr 0x21
+        // Configure as a input with no pull-up/down
+	fragment@93 {
+		target = <&gpio>;
+		__dormant__ {
+			sx150x_1_21_pins: sx150x_1_21_pins {
+				brcm,pins = <0>;  /* overwritten by sx150x-1-21-int-gpio parameter */
+				brcm,function = <0>;
+				brcm,pull = <0>;
+			};
+		};
+	};
+
+	// Configure GPIO pin connected to NINT output of a SX150x on I2C#0 interface at slave addr 0x22
+        // Configure as a input with no pull-up/down
+	fragment@94 {
+		target = <&gpio>;
+		__dormant__ {
+			sx150x_0_22_pins: sx150x_0_22_pins {
+				brcm,pins = <0>;  /* overwritten by sx150x-0-22-int-gpio parameter */
+				brcm,function = <0>;
+				brcm,pull = <0>;
+			};
+		};
+	};
+
+	// Configure GPIO pin connected to NINT output of a SX150x on I2C#1 interface at slave addr 0x22
+        // Configure as a input with no pull-up/down
+	fragment@95 {
+		target = <&gpio>;
+		__dormant__ {
+			sx150x_1_22_pins: sx150x_1_22_pins {
+				brcm,pins = <0>;  /* overwritten by sx150x-1-22-int-gpio parameter */
+				brcm,function = <0>;
+				brcm,pull = <0>;
+			};
+		};
+	};
+
+	// Configure GPIO pin connected to NINT output of a SX150x on I2C#0 interface at slave addr 0x23
+        // Configure as a input with no pull-up/down
+	fragment@96 {
+		target = <&gpio>;
+		__dormant__ {
+			sx150x_0_23_pins: sx150x_0_23_pins {
+				brcm,pins = <0>;  /* overwritten by sx150x-0-23-int-gpio parameter */
+				brcm,function = <0>;
+				brcm,pull = <0>;
+			};
+		};
+	};
+
+	// Configure GPIO pin connected to NINT output of a SX150x on I2C#1 interface at slave addr 0x23
+        // Configure as a input with no pull-up/down
+	fragment@97 {
+		target = <&gpio>;
+		__dormant__ {
+			sx150x_1_23_pins: sx150x_1_23_pins {
+				brcm,pins = <0>;  /* overwritten by sx150x-1-23-int-gpio parameter */
+				brcm,function = <0>;
+				brcm,pull = <0>;
+			};
+		};
+	};
+
+	// Configure GPIO pin connected to NINT output of a SX150x on I2C#0 interface at slave addr 0x3E
+        // Configure as a input with no pull-up/down
+	fragment@98 {
+		target = <&gpio>;
+		__dormant__ {
+			sx150x_0_3E_pins: sx150x_0_3E_pins {
+				brcm,pins = <0>;  /* overwritten by sx150x-0-3E-int-gpio parameter */
+				brcm,function = <0>;
+				brcm,pull = <0>;
+			};
+		};
+	};
+
+	// Configure GPIO pin connected to NINT output of a SX150x on I2C#1 interface at slave addr 0x3E
+        // Configure as a input with no pull-up/down
+	fragment@99 {
+		target = <&gpio>;
+		__dormant__ {
+			sx150x_1_3E_pins: sx150x_1_3E_pins {
+				brcm,pins = <0>;  /* overwritten by sx150x-1-3E-int-gpio parameter */
+				brcm,function = <0>;
+				brcm,pull = <0>;
+			};
+		};
+	};
+
+	// Configure GPIO pin connected to NINT output of a SX150x on I2C#0 interface at slave addr 0x3F
+        // Configure as a input with no pull-up/down
+	fragment@100 {
+		target = <&gpio>;
+		__dormant__ {
+			sx150x_0_3F_pins: sx150x_0_3F_pins {
+				brcm,pins = <0>;  /* overwritten by sx150x-0-3F-int-gpio parameter */
+				brcm,function = <0>;
+				brcm,pull = <0>;
+			};
+		};
+	};
+
+	// Configure GPIO pin connected to NINT output of a SX150x on I2C#1 interface at slave addr 0x3F
+        // Configure as a input with no pull-up/down
+	fragment@101 {
+		target = <&gpio>;
+		__dormant__ {
+			sx150x_1_3F_pins: sx150x_1_3F_pins {
+				brcm,pins = <0>;  /* overwritten by sx150x-1-3F-int-gpio parameter */
+				brcm,function = <0>;
+				brcm,pull = <0>;
+			};
+		};
+	};
+
+	// Configure GPIO pin connected to NINT output of a SX150x on I2C#0 interface at slave addr 0x70
+        // Configure as a input with no pull-up/down
+	fragment@102 {
+		target = <&gpio>;
+		__dormant__ {
+			sx150x_0_70_pins: sx150x_0_70_pins {
+				brcm,pins = <0>;  /* overwritten by sx150x-0-70-int-gpio parameter */
+				brcm,function = <0>;
+				brcm,pull = <0>;
+			};
+		};
+	};
+
+	// Configure GPIO pin connected to NINT output of a SX150x on I2C#1 interface at slave addr 0x70
+        // Configure as a input with no pull-up/down
+	fragment@103 {
+		target = <&gpio>;
+		__dormant__ {
+			sx150x_1_70_pins: sx150x_1_70_pins {
+				brcm,pins = <0>;  /* overwritten by sx150x-1-70-int-gpio parameter */
+				brcm,function = <0>;
+				brcm,pull = <0>;
+			};
+		};
+	};
+
+	// Configure GPIO pin connected to NINT output of a SX150x on I2C#0 interface at slave addr 0x71
+        // Configure as a input with no pull-up/down
+	fragment@104 {
+		target = <&gpio>;
+		__dormant__ {
+			sx150x_0_71_pins: sx150x_0_71_pins {
+				brcm,pins = <0>;  /* overwritten by sx150x-0-71-int-gpio parameter */
+				brcm,function = <0>;
+				brcm,pull = <0>;
+			};
+		};
+	};
+
+	// Configure GPIO pin connected to NINT output of a SX150x on I2C#1 interface at slave addr 0x71
+        // Configure as a input with no pull-up/down
+	fragment@105 {
+		target = <&gpio>;
+		__dormant__ {
+			sx150x_1_71_pins: sx150x_1_71_pins {
+				brcm,pins = <0>;  /* overwritten by sx150x-1-71-int-gpio parameter */
+				brcm,function = <0>;
+				brcm,pull = <0>;
+			};
+		};
+	};
+
+	__overrides__ {
+		sx1501-0-20          = <0>,"+0+2";
+		sx1501-1-20          = <0>,"+1+3";
+		sx1501-0-21          = <0>,"+0+4";
+		sx1501-1-21          = <0>,"+1+5";
+		sx1502-0-20          = <0>,"+0+6";
+		sx1502-1-20          = <0>,"+1+7";
+		sx1502-0-21          = <0>,"+0+8";
+		sx1502-1-21          = <0>,"+1+9";
+		sx1503-0-20          = <0>,"+0+10";
+		sx1503-1-20          = <0>,"+1+11";
+		sx1504-0-20          = <0>,"+0+12";
+		sx1504-1-20          = <0>,"+1+13";
+		sx1504-0-21          = <0>,"+0+14";
+		sx1504-1-21          = <0>,"+1+15";
+		sx1505-0-20          = <0>,"+0+16";
+		sx1505-1-20          = <0>,"+1+17";
+		sx1505-0-21          = <0>,"+0+18";
+		sx1505-1-21          = <0>,"+1+19";
+		sx1506-0-20          = <0>,"+0+20";
+		sx1506-1-20          = <0>,"+1+21";
+		sx1507-0-3E          = <0>,"+0+22";
+		sx1507-1-3E          = <0>,"+1+23";
+		sx1507-0-3F          = <0>,"+0+24";
+		sx1507-1-3F          = <0>,"+1+25";
+		sx1507-0-70          = <0>,"+0+26";
+		sx1507-1-70          = <0>,"+1+27";
+		sx1507-0-71          = <0>,"+0+28";
+		sx1507-1-71          = <0>,"+1+29";
+		sx1508-0-20          = <0>,"+0+30";
+		sx1508-1-20          = <0>,"+1+31";
+		sx1508-0-21          = <0>,"+0+32";
+		sx1508-1-21          = <0>,"+1+33";
+		sx1508-0-22          = <0>,"+0+34";
+		sx1508-1-22          = <0>,"+1+35";
+		sx1508-0-23          = <0>,"+0+36";
+		sx1508-1-23          = <0>,"+1+37";
+		sx1509-0-3E          = <0>,"+0+38";
+		sx1509-1-3E          = <0>,"+1+39";
+		sx1509-0-3F          = <0>,"+0+40";
+		sx1509-1-3F          = <0>,"+1+41";
+		sx1509-0-70          = <0>,"+0+42";
+		sx1509-1-70          = <0>,"+1+43";
+		sx1509-0-71          = <0>,"+0+44";
+		sx1509-1-71          = <0>,"+1+45";
+		sx1501-0-20-int-gpio = <0>,"+46+90",  <&sx150x_0_20_pins>,"brcm,pins:0", <&sx1501_0_20>,"interrupts:0";
+		sx1501-1-20-int-gpio = <0>,"+47+91",  <&sx150x_1_20_pins>,"brcm,pins:0", <&sx1501_1_20>,"interrupts:0";
+		sx1501-0-21-int-gpio = <0>,"+48+92",  <&sx150x_0_21_pins>,"brcm,pins:0", <&sx1501_0_21>,"interrupts:0";
+		sx1501-1-21-int-gpio = <0>,"+49+93",  <&sx150x_1_21_pins>,"brcm,pins:0", <&sx1501_1_21>,"interrupts:0";
+		sx1502-0-20-int-gpio = <0>,"+50+90",  <&sx150x_0_20_pins>,"brcm,pins:0", <&sx1502_0_20>,"interrupts:0";
+		sx1502-1-20-int-gpio = <0>,"+51+91",  <&sx150x_1_20_pins>,"brcm,pins:0", <&sx1502_1_20>,"interrupts:0";
+		sx1502-0-21-int-gpio = <0>,"+52+92",  <&sx150x_0_21_pins>,"brcm,pins:0", <&sx1502_0_21>,"interrupts:0";
+		sx1502-1-21-int-gpio = <0>,"+53+93",  <&sx150x_1_21_pins>,"brcm,pins:0", <&sx1502_1_21>,"interrupts:0";
+		sx1503-0-20-int-gpio = <0>,"+54+90",  <&sx150x_0_20_pins>,"brcm,pins:0", <&sx1503_0_20>,"interrupts:0";
+		sx1503-1-20-int-gpio = <0>,"+55+91",  <&sx150x_1_20_pins>,"brcm,pins:0", <&sx1503_1_20>,"interrupts:0";
+		sx1504-0-20-int-gpio = <0>,"+56+90",  <&sx150x_0_20_pins>,"brcm,pins:0", <&sx1504_0_20>,"interrupts:0";
+		sx1504-1-20-int-gpio = <0>,"+57+91",  <&sx150x_1_20_pins>,"brcm,pins:0", <&sx1504_1_20>,"interrupts:0";
+		sx1504-0-21-int-gpio = <0>,"+58+92",  <&sx150x_0_21_pins>,"brcm,pins:0", <&sx1504_0_21>,"interrupts:0";
+		sx1504-1-21-int-gpio = <0>,"+59+93",  <&sx150x_1_21_pins>,"brcm,pins:0", <&sx1504_1_21>,"interrupts:0";
+		sx1505-0-20-int-gpio = <0>,"+60+90",  <&sx150x_0_20_pins>,"brcm,pins:0", <&sx1505_0_20>,"interrupts:0";
+		sx1505-1-20-int-gpio = <0>,"+61+91",  <&sx150x_1_20_pins>,"brcm,pins:0", <&sx1505_1_20>,"interrupts:0";
+		sx1505-0-21-int-gpio = <0>,"+62+92",  <&sx150x_0_21_pins>,"brcm,pins:0", <&sx1505_0_21>,"interrupts:0";
+		sx1505-1-21-int-gpio = <0>,"+63+93",  <&sx150x_1_21_pins>,"brcm,pins:0", <&sx1505_1_21>,"interrupts:0";
+		sx1506-0-20-int-gpio = <0>,"+64+90",  <&sx150x_0_20_pins>,"brcm,pins:0", <&sx1506_0_20>,"interrupts:0";
+		sx1506-1-20-int-gpio = <0>,"+65+91",  <&sx150x_1_20_pins>,"brcm,pins:0", <&sx1506_1_20>,"interrupts:0";
+		sx1507-0-3E-int-gpio = <0>,"+66+98",  <&sx150x_0_3E_pins>,"brcm,pins:0", <&sx1507_0_3E>,"interrupts:0";
+		sx1507-1-3E-int-gpio = <0>,"+67+99",  <&sx150x_1_3E_pins>,"brcm,pins:0", <&sx1507_1_3E>,"interrupts:0";
+		sx1507-0-3F-int-gpio = <0>,"+68+100", <&sx150x_0_3F_pins>,"brcm,pins:0", <&sx1507_0_3F>,"interrupts:0";
+		sx1507-1-3F-int-gpio = <0>,"+69+101", <&sx150x_1_3F_pins>,"brcm,pins:0", <&sx1507_1_3F>,"interrupts:0";
+		sx1507-0-70-int-gpio = <0>,"+60+102", <&sx150x_0_70_pins>,"brcm,pins:0", <&sx1507_0_70>,"interrupts:0";
+		sx1507-1-70-int-gpio = <0>,"+71+103", <&sx150x_1_70_pins>,"brcm,pins:0", <&sx1507_1_70>,"interrupts:0";
+		sx1507-0-71-int-gpio = <0>,"+72+104", <&sx150x_0_71_pins>,"brcm,pins:0", <&sx1507_0_71>,"interrupts:0";
+		sx1507-1-71-int-gpio = <0>,"+73+105", <&sx150x_1_71_pins>,"brcm,pins:0", <&sx1507_1_71>,"interrupts:0";
+		sx1508-0-20-int-gpio = <0>,"+74+90",  <&sx150x_0_20_pins>,"brcm,pins:0", <&sx1508_0_20>,"interrupts:0";
+		sx1508-1-20-int-gpio = <0>,"+75+91",  <&sx150x_1_20_pins>,"brcm,pins:0", <&sx1508_1_20>,"interrupts:0";
+		sx1508-0-21-int-gpio = <0>,"+76+92",  <&sx150x_0_21_pins>,"brcm,pins:0", <&sx1508_0_21>,"interrupts:0";
+		sx1508-1-21-int-gpio = <0>,"+77+93",  <&sx150x_1_21_pins>,"brcm,pins:0", <&sx1508_1_21>,"interrupts:0";
+		sx1508-0-22-int-gpio = <0>,"+78+94",  <&sx150x_0_22_pins>,"brcm,pins:0", <&sx1508_0_22>,"interrupts:0";
+		sx1508-1-22-int-gpio = <0>,"+79+95",  <&sx150x_1_22_pins>,"brcm,pins:0", <&sx1508_1_22>,"interrupts:0";
+		sx1508-0-23-int-gpio = <0>,"+80+96",  <&sx150x_0_23_pins>,"brcm,pins:0", <&sx1508_0_23>,"interrupts:0";
+		sx1508-1-23-int-gpio = <0>,"+81+97",  <&sx150x_1_23_pins>,"brcm,pins:0", <&sx1508_1_23>,"interrupts:0";
+		sx1509-0-3E-int-gpio = <0>,"+82+98",  <&sx150x_0_3E_pins>,"brcm,pins:0", <&sx1509_0_3E>,"interrupts:0";
+		sx1509-1-3E-int-gpio = <0>,"+83+99",  <&sx150x_1_3E_pins>,"brcm,pins:0", <&sx1509_1_3E>,"interrupts:0";
+		sx1509-0-3F-int-gpio = <0>,"+84+100", <&sx150x_0_3F_pins>,"brcm,pins:0", <&sx1509_0_3F>,"interrupts:0";
+		sx1509-1-3F-int-gpio = <0>,"+85+101", <&sx150x_1_3F_pins>,"brcm,pins:0", <&sx1509_1_3F>,"interrupts:0";
+		sx1509-0-70-int-gpio = <0>,"+86+102", <&sx150x_0_70_pins>,"brcm,pins:0", <&sx1509_0_70>,"interrupts:0";
+		sx1509-1-70-int-gpio = <0>,"+87+103", <&sx150x_1_70_pins>,"brcm,pins:0", <&sx1509_1_70>,"interrupts:0";
+		sx1509-0-71-int-gpio = <0>,"+88+104", <&sx150x_0_71_pins>,"brcm,pins:0", <&sx1509_0_71>,"interrupts:0";
+		sx1509-1-71-int-gpio = <0>,"+89+105", <&sx150x_1_71_pins>,"brcm,pins:0", <&sx1509_1_71>,"interrupts:0";
+	};
+};
+
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/tc358743-audio-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/tc358743-audio-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/tc358743-audio-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/tc358743-audio-overlay.dts	2021-07-25 16:45:35.158807390 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// SPDX-License-Identifier: GPL-2.0-only
+// Definitions to add I2S audio from the Toshiba TC358743 HDMI to CSI2 bridge.
+// Requires tc358743 overlay to have been loaded to actually function.
+/dts-v1/;
+/plugin/;
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&i2s>;
+		__overlay__ {
+			status = "okay";
+		};
+	};
+
+	fragment@1 {
+		target-path = "/";
+		__overlay__ {
+			tc358743_codec: tc358743-codec {
+				#sound-dai-cells = <0>;
+				compatible = "linux,spdif-dir";
+				status = "okay";
+			};
+		};
+	};
+
+	fragment@2 {
+		target = <&sound>;
+		sound_overlay: __overlay__ {
+			compatible = "simple-audio-card";
+			simple-audio-card,format = "i2s";
+			simple-audio-card,name = "tc358743";
+			simple-audio-card,bitclock-master = <&dailink0_slave>;
+			simple-audio-card,frame-master = <&dailink0_slave>;
+			status = "okay";
+
+			simple-audio-card,cpu {
+				sound-dai = <&i2s>;
+				dai-tdm-slot-num = <2>;
+				dai-tdm-slot-width = <32>;
+			};
+			dailink0_slave: simple-audio-card,codec {
+				sound-dai = <&tc358743_codec>;
+			};
+		};
+	};
+
+	__overrides__ {
+		card-name = <&sound_overlay>,"simple-audio-card,name";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/tc358743-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/tc358743-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/tc358743-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/tc358743-overlay.dts	2021-07-25 16:45:35.158807390 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// SPDX-License-Identifier: GPL-2.0-only
+// Definitions for Toshiba TC358743 HDMI to CSI2 bridge on VC I2C bus
+/dts-v1/;
+/plugin/;
+
+/{
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&i2c_csi_dsi>;
+		__overlay__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			status = "okay";
+
+			tc358743@0f {
+				compatible = "toshiba,tc358743";
+				reg = <0x0f>;
+				status = "okay";
+
+				clocks = <&tc358743_clk>;
+				clock-names = "refclk";
+
+				port {
+					tc358743: endpoint {
+						remote-endpoint = <&csi1_ep>;
+						clock-lanes = <0>;
+						clock-noncontinuous;
+						link-frequencies =
+							/bits/ 64 <486000000>;
+					};
+				};
+			};
+		};
+	};
+
+	fragment@1 {
+		target = <&csi1>;
+		__overlay__ {
+			status = "okay";
+
+			port {
+				csi1_ep: endpoint {
+					remote-endpoint = <&tc358743>;
+				};
+			};
+		};
+	};
+
+	fragment@2 {
+		target = <&tc358743>;
+		__overlay__ {
+			data-lanes = <1 2>;
+		};
+	};
+
+	fragment@3 {
+		target = <&tc358743>;
+		__dormant__ {
+			data-lanes = <1 2 3 4>;
+		};
+	};
+
+	fragment@4 {
+		target = <&i2c0if>;
+		__overlay__ {
+			status = "okay";
+		};
+	};
+
+	fragment@5 {
+		target = <&i2c0mux>;
+		__overlay__ {
+			status = "okay";
+		};
+	};
+
+	fragment@6 {
+		target-path = "/";
+		__overlay__ {
+			tc358743_clk: bridge-clk {
+				compatible = "fixed-clock";
+				#clock-cells = <0>;
+				clock-frequency = <27000000>;
+			};
+		};
+	};
+
+	fragment@7 {
+		target = <&csi1_ep>;
+		__overlay__ {
+			data-lanes = <1 2>;
+		};
+	};
+
+	fragment@8 {
+		target = <&csi1_ep>;
+		__dormant__ {
+			data-lanes = <1 2 3 4>;
+		};
+	};
+
+	__overrides__ {
+		4lane = <0>, "-2+3-7+8";
+		link-frequency = <&tc358743>,"link-frequencies#0";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/tinylcd35-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/tinylcd35-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/tinylcd35-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/tinylcd35-overlay.dts	2021-07-25 16:45:35.158807390 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/*
+ * tinylcd35-overlay.dts
+ *
+ * -------------------------------------------------
+ * www.tinlylcd.com
+ * -------------------------------------------------
+ * Device---Driver-----BUS       GPIO's
+ * display  tinylcd35  spi0.0    25 24 18
+ * touch    ads7846    spi0.1    5
+ * rtc      ds1307     i2c1-0068
+ * rtc      pcf8563    i2c1-0051
+ * keypad   gpio-keys  --------- 17 22 27 23 28
+ *
+ *
+ * TinyLCD.com 3.5 inch TFT
+ *
+ *  Version 001
+ *  5/3/2015  -- Noralf Trønnes     Initial Device tree framework
+ *  10/3/2015 -- tinylcd@gmail.com  added ds1307 support.
+ *
+ */
+
+/dts-v1/;
+/plugin/;
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&spi0>;
+		__overlay__ {
+			status = "okay";
+		};
+	};
+
+	fragment@1 {
+		target = <&spidev0>;
+		__overlay__ {
+			status = "disabled";
+		};
+	};
+
+	fragment@2 {
+		target = <&spidev1>;
+		__overlay__ {
+			status = "disabled";
+		};
+	};
+
+	fragment@3 {
+		target = <&gpio>;
+		__overlay__ {
+			tinylcd35_pins: tinylcd35_pins {
+				brcm,pins = <25 24 18>;
+				brcm,function = <1>; /* out */
+			};
+			tinylcd35_ts_pins: tinylcd35_ts_pins {
+				brcm,pins = <5>;
+				brcm,function = <0>; /* in */
+			};
+			keypad_pins: keypad_pins {
+				brcm,pins = <4 17 22 23 27>;
+				brcm,function = <0>; /* in */
+				brcm,pull = <1>; /* down */
+			};
+		};
+	};
+
+	fragment@4 {
+		target = <&spi0>;
+		__overlay__ {
+			/* needed to avoid dtc warning */
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			tinylcd35: tinylcd35@0{
+				compatible = "neosec,tinylcd";
+				reg = <0>;
+				pinctrl-names = "default";
+				pinctrl-0 = <&tinylcd35_pins>,
+					    <&tinylcd35_ts_pins>;
+
+				spi-max-frequency = <48000000>;
+				rotate = <270>;
+				fps = <20>;
+				bgr;
+				buswidth = <8>;
+				reset-gpios = <&gpio 25 1>;
+				dc-gpios = <&gpio 24 0>;
+				led-gpios = <&gpio 18 0>;
+				debug = <0>;
+
+				init = <0x10000B0 0x80
+					0x10000C0 0x0A 0x0A
+					0x10000C1 0x01 0x01
+					0x10000C2 0x33
+					0x10000C5 0x00 0x42 0x80
+					0x10000B1 0xD0 0x11
+					0x10000B4 0x02
+					0x10000B6 0x00 0x22 0x3B
+					0x10000B7 0x07
+					0x1000036 0x58
+					0x10000F0 0x36 0xA5 0xD3
+					0x10000E5 0x80
+					0x10000E5 0x01
+					0x10000B3 0x00
+					0x10000E5 0x00
+					0x10000F0 0x36 0xA5 0x53
+					0x10000E0 0x00 0x35 0x33 0x00 0x00 0x00 0x00 0x35 0x33 0x00 0x00 0x00
+					0x100003A 0x55
+					0x1000011
+					0x2000001
+					0x1000029>;
+			};
+
+			tinylcd35_ts: tinylcd35_ts@1 {
+				compatible = "ti,ads7846";
+				reg = <1>;
+				status = "disabled";
+
+				spi-max-frequency = <2000000>;
+				interrupts = <5 2>; /* high-to-low edge triggered */
+				interrupt-parent = <&gpio>;
+				pendown-gpio = <&gpio 5 0>;
+				ti,x-plate-ohms = /bits/ 16 <100>;
+				ti,pressure-max = /bits/ 16 <255>;
+			};
+		};
+	};
+
+	/*  RTC    */
+
+	fragment@5 {
+		target = <&i2c1>;
+		__dormant__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			status = "okay";
+
+			pcf8563: pcf8563@51 {
+				compatible = "nxp,pcf8563";
+				reg = <0x51>;
+				status = "okay";
+			};
+		};
+	};
+
+	fragment@6 {
+		target = <&i2c1>;
+		__dormant__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			status = "okay";
+
+			ds1307: ds1307@68 {
+				compatible = "dallas,ds1307";
+				reg = <0x68>;
+				status = "okay";
+			};
+		};
+	};
+
+	/*
+	 * Values for input event code is found under the
+	 * 'Keys and buttons' heading in include/uapi/linux/input.h
+	 */
+	fragment@7 {
+		target-path = "/soc";
+		__overlay__ {
+			keypad: keypad {
+				compatible = "gpio-keys";
+				pinctrl-names = "default";
+				pinctrl-0 = <&keypad_pins>;
+				status = "disabled";
+				autorepeat;
+
+				button@17 {
+					label = "GPIO KEY_UP";
+					linux,code = <103>;
+					gpios = <&gpio 17 0>;
+				};
+				button@22 {
+					label = "GPIO KEY_DOWN";
+					linux,code = <108>;
+					gpios = <&gpio 22 0>;
+				};
+				button@27 {
+					label = "GPIO KEY_LEFT";
+					linux,code = <105>;
+					gpios = <&gpio 27 0>;
+				};
+				button@23 {
+					label = "GPIO KEY_RIGHT";
+					linux,code = <106>;
+					gpios = <&gpio 23 0>;
+				};
+				button@4 {
+					label = "GPIO KEY_ENTER";
+					linux,code = <28>;
+					gpios = <&gpio 4 0>;
+				};
+			};
+		};
+	};
+
+	__overrides__ {
+		speed =      <&tinylcd35>,"spi-max-frequency:0";
+		rotate =     <&tinylcd35>,"rotate:0";
+		fps =        <&tinylcd35>,"fps:0";
+		debug =      <&tinylcd35>,"debug:0";
+		touch =      <&tinylcd35_ts>,"status";
+		touchgpio =  <&tinylcd35_ts_pins>,"brcm,pins:0",
+			     <&tinylcd35_ts>,"interrupts:0",
+			     <&tinylcd35_ts>,"pendown-gpio:4";
+		xohms =      <&tinylcd35_ts>,"ti,x-plate-ohms;0";
+		rtc-pcf =    <0>,"=5";
+		rtc-ds =     <0>,"=6";
+		keypad =     <&keypad>,"status";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/tpm-slb9670-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/tpm-slb9670-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/tpm-slb9670-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/tpm-slb9670-overlay.dts	2021-07-25 16:45:35.158807390 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/*
+ * Device Tree overlay for the Infineon SLB9670 Trusted Platform Module add-on
+ * boards, which can be used as a secure key storage and hwrng.
+ * available as "Iridium SLB9670" by Infineon and "LetsTrust TPM" by pi3g.
+ */
+
+/dts-v1/;
+/plugin/;
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&spi0>;
+		__overlay__ {
+			status = "okay";
+		};
+	};
+
+	fragment@1 {
+		target = <&spidev1>;
+		__overlay__ {
+			status = "disabled";
+		};
+	};
+
+	fragment@2 {
+		target = <&spi0>;
+		__overlay__ {
+			/* needed to avoid dtc warning */
+			#address-cells = <1>;
+			#size-cells = <0>;
+			slb9670: slb9670@1 {
+				compatible = "infineon,slb9670";
+				reg = <1>;	/* CE1 */
+				#address-cells = <1>;
+				#size-cells = <0>;
+				spi-max-frequency = <32000000>;
+				status = "okay";
+			};
+
+		};
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/uart0-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/uart0-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/uart0-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/uart0-overlay.dts	2021-07-25 16:45:35.158807390 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/dts-v1/;
+/plugin/;
+
+/{
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&uart0>;
+		__overlay__ {
+			pinctrl-names = "default";
+			pinctrl-0 = <&uart0_pins>;
+			status = "okay";
+		};
+	};
+
+	fragment@1 {
+		target = <&gpio>;
+		__overlay__ {
+			uart0_pins: uart0_pins {
+				brcm,pins = <14 15>;
+				brcm,function = <4>; /* alt0 */
+				brcm,pull = <0 2>;
+			};
+		};
+	};
+
+	__overrides__ {
+		txd0_pin = <&uart0_pins>,"brcm,pins:0";
+		rxd0_pin = <&uart0_pins>,"brcm,pins:4";
+		pin_func = <&uart0_pins>,"brcm,function:0";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/uart1-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/uart1-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/uart1-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/uart1-overlay.dts	2021-07-25 16:45:35.158807390 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/dts-v1/;
+/plugin/;
+
+/{
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&uart1>;
+		__overlay__ {
+			pinctrl-names = "default";
+			pinctrl-0 = <&uart1_pins>;
+			status = "okay";
+		};
+	};
+
+	fragment@1 {
+		target = <&gpio>;
+		__overlay__ {
+			uart1_pins: uart1_pins {
+				brcm,pins = <14 15>;
+				brcm,function = <2>; /* alt5 */
+				brcm,pull = <0 2>;
+			};
+		};
+	};
+
+	fragment@2 {
+		target-path = "/chosen";
+		__overlay__ {
+			bootargs = "8250.nr_uarts=1";
+		};
+	};
+
+	__overrides__ {
+		txd1_pin = <&uart1_pins>,"brcm,pins:0";
+		rxd1_pin = <&uart1_pins>,"brcm,pins:4";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/uart2-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/uart2-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/uart2-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/uart2-overlay.dts	2021-07-25 16:45:35.158807390 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/dts-v1/;
+/plugin/;
+
+/{
+	compatible = "brcm,bcm2711";
+
+	fragment@0 {
+		target = <&uart2>;
+		__overlay__ {
+			pinctrl-names = "default";
+			pinctrl-0 = <&uart2_pins>;
+			status = "okay";
+		};
+	};
+
+	fragment@1 {
+		target = <&uart2_pins>;
+		__dormant__ {
+			brcm,pins = <0 1 2 3>;
+			brcm,pull = <0 2 2 0>;
+		};
+	};
+
+	__overrides__ {
+		ctsrts = <0>,"=1";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/uart3-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/uart3-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/uart3-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/uart3-overlay.dts	2021-07-25 16:45:35.158807390 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/dts-v1/;
+/plugin/;
+
+/{
+	compatible = "brcm,bcm2711";
+
+	fragment@0 {
+		target = <&uart3>;
+		__overlay__ {
+			pinctrl-names = "default";
+			pinctrl-0 = <&uart3_pins>;
+			status = "okay";
+		};
+	};
+
+	fragment@1 {
+		target = <&uart3_pins>;
+		__dormant__ {
+			brcm,pins = <4 5 6 7>;
+			brcm,pull = <0 2 2 0>;
+		};
+	};
+
+	__overrides__ {
+		ctsrts = <0>,"=1";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/uart4-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/uart4-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/uart4-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/uart4-overlay.dts	2021-07-25 16:45:35.158807390 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/dts-v1/;
+/plugin/;
+
+/{
+	compatible = "brcm,bcm2711";
+
+	fragment@0 {
+		target = <&uart4>;
+		__overlay__ {
+			pinctrl-names = "default";
+			pinctrl-0 = <&uart4_pins>;
+			status = "okay";
+		};
+	};
+
+	fragment@1 {
+		target = <&uart4_pins>;
+		__dormant__ {
+			brcm,pins = <8 9 10 11>;
+			brcm,pull = <0 2 2 0>;
+		};
+	};
+
+	__overrides__ {
+		ctsrts = <0>,"=1";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/uart5-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/uart5-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/uart5-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/uart5-overlay.dts	2021-07-25 16:45:35.158807390 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/dts-v1/;
+/plugin/;
+
+/{
+	compatible = "brcm,bcm2711";
+
+	fragment@0 {
+		target = <&uart5>;
+		__overlay__ {
+			pinctrl-names = "default";
+			pinctrl-0 = <&uart5_pins>;
+			status = "okay";
+		};
+	};
+
+	fragment@1 {
+		target = <&uart5_pins>;
+		__dormant__ {
+			brcm,pins = <12 13 14 15>;
+			brcm,pull = <0 2 2 0>;
+		};
+	};
+
+	__overrides__ {
+		ctsrts = <0>,"=1";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/udrc-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/udrc-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/udrc-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/udrc-overlay.dts	2021-07-25 16:45:35.168807223 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+#include <dt-bindings/clock/bcm2835.h>
+/*
+ * Device tree overlay for the Universal Digital Radio Controller
+ */
+
+/dts-v1/;
+/plugin/;
+
+/ {
+    compatible = "brcm,bcm2835";
+    fragment@0 {
+        target = <&i2s>;
+        __overlay__ {
+            clocks = <&clocks BCM2835_CLOCK_PCM>;
+            clock-names = "pcm";
+            status = "okay";
+        };
+    };
+
+    fragment@1 {
+        target-path = "/";
+        __overlay__ {
+            regulators {
+                compatible = "simple-bus";
+                #address-cells = <1>;
+                #size-cells = <0>;
+
+                udrc0_ldoin: udrc0_ldoin {
+                    compatible = "regulator-fixed";
+                    regulator-name = "ldoin";
+                    regulator-min-microvolt = <3300000>;
+                    regulator-max-microvolt = <3300000>;
+                    regulator-always-on;
+                };
+            };
+        };
+    };
+
+    fragment@2 {
+        target = <&i2c1>;
+        __overlay__ {
+            #address-cells = <1>;
+            #size-cells = <0>;
+            status = "okay";
+            clocks = <&clocks BCM2835_CLOCK_VPU>;
+            clock-frequency = <400000>;
+
+            tlv320aic32x4: tlv320aic32x4@18 {
+                compatible = "ti,tlv320aic32x4";
+                #sound-dai-cells = <0>;
+                reg = <0x18>;
+                status = "okay";
+
+                clocks = <&clocks BCM2835_CLOCK_GP0>;
+                clock-names = "mclk";
+                assigned-clocks = <&clocks BCM2835_CLOCK_GP0>;
+                assigned-clock-rates = <25000000>;
+
+                pinctrl-names = "default";
+                pinctrl-0 = <&gpclk0_pin &aic3204_reset>;
+
+                reset-gpios = <&gpio 13 0>;
+
+                iov-supply = <&udrc0_ldoin>;
+                ldoin-supply = <&udrc0_ldoin>;
+            };
+        };
+    };
+
+    fragment@3 {
+        target = <&sound>;
+        snd: __overlay__ {
+            compatible = "simple-audio-card";
+            i2s-controller = <&i2s>;
+            status = "okay";
+
+            simple-audio-card,name = "udrc";
+            simple-audio-card,format = "i2s";
+
+            simple-audio-card,bitclock-master = <&dailink0_master>;
+            simple-audio-card,frame-master = <&dailink0_master>;
+
+            simple-audio-card,widgets =
+                "Line", "Line In",
+                "Line", "Line Out";
+
+            simple-audio-card,routing =
+                "IN1_R", "Line In",
+                "IN1_L", "Line In",
+                "CM_L", "Line In",
+                "CM_R", "Line In",
+                "Line Out", "LOR",
+                "Line Out", "LOL";
+
+            dailink0_master: simple-audio-card,cpu {
+                sound-dai = <&i2s>;
+            };
+
+            simple-audio-card,codec {
+                sound-dai = <&tlv320aic32x4>;
+            };
+        };
+    };
+
+    fragment@4 {
+        target = <&gpio>;
+        __overlay__ {
+            gpclk0_pin: gpclk0_pin {
+                brcm,pins = <4>;
+                brcm,function = <4>;
+            };
+
+            aic3204_reset: aic3204_reset {
+                brcm,pins = <13>;
+                brcm,function = <1>;
+                brcm,pull = <1>;
+            };
+
+            aic3204_gpio: aic3204_gpio {
+                brcm,pins = <26>;
+            };
+        };
+    };
+
+    __overrides__ {
+        alsaname = <&snd>, "simple-audio-card,name";
+    };
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/ugreen-dabboard-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/ugreen-dabboard-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/ugreen-dabboard-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/ugreen-dabboard-overlay.dts	2021-07-25 16:45:35.168807223 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// Definitions for the ugreen dabboard I2S
+/dts-v1/;
+/plugin/;
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&i2s>;
+		__overlay__ {
+			status = "okay";
+		};
+	};
+
+	fragment@1 {
+		target-path = "/";
+		__overlay__ {
+			dmic_codec: dmic-codec {
+				#sound-dai-cells = <0>;
+				compatible = "dmic-codec";
+				status = "okay";
+			};
+		};
+	};
+
+	fragment@2 {
+		target = <&sound>;
+		sound_overlay: __overlay__ {
+			compatible = "simple-audio-card";
+			simple-audio-card,format = "i2s";
+			simple-audio-card,name = "dabboard";
+			simple-audio-card,bitclock-master = <&dailink0_slave>;
+			simple-audio-card,frame-master = <&dailink0_slave>;
+			simple-audio-card,widgets = "Microphone", "Microphone Jack";
+			status = "okay";
+			simple-audio-card,cpu {
+				sound-dai = <&i2s>;
+			};
+			dailink0_slave: simple-audio-card,codec {
+				#sound-dai-cells = <0>;
+				sound-dai = <&dmic_codec>;
+			};
+		};
+	};
+
+	__overrides__ {
+		card-name = <&sound_overlay>,"simple-audio-card,name";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/upstream-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/upstream-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/upstream-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/upstream-overlay.dts	2021-07-25 16:45:35.168807223 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// redo: ovmerge -c vc4-kms-v3d-overlay.dts,cma-default dwc2-overlay.dts,dr_mode=otg
+
+/dts-v1/;
+/plugin/;
+
+#include <dt-bindings/clock/bcm2835.h>
+
+/ {
+	compatible = "brcm,bcm2835";
+	fragment@0 {
+		target = <&i2c2>;
+		__overlay__ {
+			status = "okay";
+		};
+	};
+	fragment@1 {
+		target = <&fb>;
+		__overlay__ {
+			status = "disabled";
+		};
+	};
+	fragment@2 {
+		target = <&pixelvalve0>;
+		__overlay__ {
+			status = "okay";
+		};
+	};
+	fragment@3 {
+		target = <&pixelvalve1>;
+		__overlay__ {
+			status = "okay";
+		};
+	};
+	fragment@4 {
+		target = <&pixelvalve2>;
+		__overlay__ {
+			status = "okay";
+		};
+	};
+	fragment@5 {
+		target = <&hvs>;
+		__overlay__ {
+			status = "okay";
+		};
+	};
+	fragment@6 {
+		target = <&hdmi>;
+		__overlay__ {
+			status = "okay";
+		};
+	};
+	fragment@7 {
+		target = <&v3d>;
+		__overlay__ {
+			status = "okay";
+		};
+	};
+	fragment@8 {
+		target = <&vc4>;
+		__overlay__ {
+			status = "okay";
+		};
+	};
+	fragment@9 {
+		target = <&clocks>;
+		__overlay__ {
+			claim-clocks = <BCM2835_PLLD_DSI0 BCM2835_PLLD_DSI1 BCM2835_PLLH_AUX BCM2835_PLLH_PIX>;
+		};
+	};
+	fragment@10 {
+		target = <&vec>;
+		__overlay__ {
+			status = "okay";
+		};
+	};
+	fragment@11 {
+		target = <&txp>;
+		__overlay__ {
+			status = "okay";
+		};
+	};
+	fragment@12 {
+		target = <&audio>;
+		__overlay__ {
+			brcm,disable-hdmi;
+		};
+	};
+	fragment@13 {
+		target = <&usb>;
+		#address-cells = <1>;
+		#size-cells = <1>;
+		__overlay__ {
+			compatible = "brcm,bcm2835-usb";
+			dr_mode = "otg";
+			g-np-tx-fifo-size = <32>;
+			g-rx-fifo-size = <558>;
+			g-tx-fifo-size = <512 512 512 512 512 256 256>;
+			status = "okay";
+		};
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/upstream-pi4-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/upstream-pi4-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/upstream-pi4-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/upstream-pi4-overlay.dts	2021-07-25 16:45:35.168807223 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// redo: ovmerge -c vc4-kms-v3d-pi4-overlay.dts,cma-default dwc2-overlay.dts,dr_mode=otg
+
+/dts-v1/;
+/plugin/;
+
+#include <dt-bindings/clock/bcm2835.h>
+
+/ {
+	compatible = "brcm,bcm2835";
+	fragment@0 {
+		target = <&ddc0>;
+		__overlay__ {
+			status = "okay";
+		};
+	};
+	fragment@1 {
+		target = <&ddc1>;
+		__overlay__ {
+			status = "okay";
+		};
+	};
+	fragment@2 {
+		target = <&hdmi0>;
+		__overlay__ {
+			status = "okay";
+		};
+	};
+	fragment@3 {
+		target = <&hdmi1>;
+		__overlay__ {
+			status = "okay";
+		};
+	};
+	fragment@4 {
+		target = <&hvs>;
+		__overlay__ {
+			status = "okay";
+		};
+	};
+	fragment@5 {
+		target = <&pixelvalve0>;
+		__overlay__ {
+			status = "okay";
+		};
+	};
+	fragment@6 {
+		target = <&pixelvalve1>;
+		__overlay__ {
+			status = "okay";
+		};
+	};
+	fragment@7 {
+		target = <&pixelvalve2>;
+		__overlay__ {
+			status = "okay";
+		};
+	};
+	fragment@8 {
+		target = <&pixelvalve3>;
+		__overlay__ {
+			status = "okay";
+		};
+	};
+	fragment@9 {
+		target = <&pixelvalve4>;
+		__overlay__ {
+			status = "okay";
+		};
+	};
+	fragment@10 {
+		target = <&v3d>;
+		__overlay__ {
+			status = "okay";
+		};
+	};
+	fragment@11 {
+		target = <&vc4>;
+		__overlay__ {
+			status = "okay";
+		};
+	};
+	fragment@12 {
+		target = <&txp>;
+		__overlay__ {
+			status = "okay";
+		};
+	};
+	fragment@13 {
+		target = <&fb>;
+		__overlay__ {
+			status = "disabled";
+		};
+	};
+	fragment@14 {
+		target = <&firmwarekms>;
+		__overlay__ {
+			status = "disabled";
+		};
+	};
+	fragment@15 {
+		target = <&vec>;
+		__overlay__ {
+			status = "disabled";
+		};
+	};
+	fragment@16 {
+		target = <&audio>;
+		__overlay__ {
+			brcm,disable-hdmi;
+		};
+	};
+	fragment@17 {
+		target = <&dvp>;
+		__overlay__ {
+			status = "okay";
+		};
+	};
+	fragment@18 {
+		target = <&aon_intr>;
+		__overlay__ {
+			status = "okay";
+		};
+	};
+	fragment@19 {
+		target = <&usb>;
+		#address-cells = <1>;
+		#size-cells = <1>;
+		__overlay__ {
+			compatible = "brcm,bcm2835-usb";
+			dr_mode = "otg";
+			g-np-tx-fifo-size = <32>;
+			g-rx-fifo-size = <558>;
+			g-tx-fifo-size = <512 512 512 512 512 256 256>;
+			status = "okay";
+		};
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/vc4-fkms-v3d-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/vc4-fkms-v3d-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/vc4-fkms-v3d-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/vc4-fkms-v3d-overlay.dts	2021-07-25 16:45:35.168807223 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/*
+ * vc4-fkms-v3d-overlay.dts
+ */
+
+/dts-v1/;
+/plugin/;
+
+#include "cma-overlay.dts"
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@1 {
+		target = <&fb>;
+		__overlay__  {
+			status = "disabled";
+		};
+	};
+
+	fragment@2 {
+		target = <&firmwarekms>;
+		__overlay__  {
+			status = "okay";
+		};
+	};
+
+	fragment@3 {
+		target = <&v3d>;
+		__overlay__  {
+			status = "okay";
+		};
+	};
+
+	fragment@4 {
+		target = <&vc4>;
+		__overlay__  {
+			status = "okay";
+		};
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/vc4-kms-dpi-at056tn53v1-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/vc4-kms-dpi-at056tn53v1-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/vc4-kms-dpi-at056tn53v1-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/vc4-kms-dpi-at056tn53v1-overlay.dts	2021-07-25 16:45:35.168807223 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/*
+ * vc4-kms-dpi-at056tn53v1-overlay.dts
+ */
+
+/dts-v1/;
+/plugin/;
+
+#include <dt-bindings/gpio/gpio.h>
+#include <dt-bindings/pinctrl/bcm2835.h>
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target-path = "/";
+		__overlay__ {
+			panel: panel {
+				compatible = "innolux,at056tn53v1", "simple-panel";
+
+				port {
+					panel_in: endpoint {
+						remote-endpoint = <&dpi_out>;
+					};
+				};
+			};
+		};
+	};
+
+	fragment@1 {
+		target = <&dpi>;
+		__overlay__  {
+			status = "okay";
+
+			pinctrl-names = "default";
+			pinctrl-0 = <&dpi_18bit_cpadhi_gpio0>;
+
+			port {
+				dpi_out: endpoint {
+					remote-endpoint = <&panel_in>;
+				};
+			};
+		};
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/vc4-kms-dsi-7inch-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/vc4-kms-dsi-7inch-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/vc4-kms-dsi-7inch-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/vc4-kms-dsi-7inch-overlay.dts	2021-07-25 16:45:35.168807223 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/*
+ * Device Tree overlay for RaspberryPi 7" Touchscreen panel
+ *
+ */
+
+/dts-v1/;
+/plugin/;
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&dsi1>;
+		__overlay__ {
+			#address-cells = <1>; size-cells = <0>;
+			status = "okay";
+			port {
+				dsi_out_port: endpoint {
+					remote-endpoint = <&panel_dsi_port>;
+				};
+			};
+		};
+	};
+
+	fragment@1 {
+		target = <&i2c_csi_dsi>;
+		__overlay__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			status = "okay";
+			lcd@45 {
+				compatible = "raspberrypi,7inch-touchscreen-panel";
+				reg = <0x45>;
+				port {
+					panel_dsi_port: endpoint {
+						remote-endpoint = <&dsi_out_port>;
+					};
+				};
+			};
+		};
+	};
+
+	fragment@2 {
+		target = <&i2c0if>;
+		__overlay__ {
+			status = "okay";
+		};
+	};
+
+	fragment@3 {
+		target = <&i2c0mux>;
+		__overlay__ {
+			status = "okay";
+		};
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/vc4-kms-dsi-lt070me05000-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/vc4-kms-dsi-lt070me05000-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/vc4-kms-dsi-lt070me05000-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/vc4-kms-dsi-lt070me05000-overlay.dts	2021-07-25 16:45:35.168807223 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/*
+ * Device Tree overlay to connect a JDI LT070ME05000 DSI panel to DSI1.
+ * This uses 4 DSI data lanes, so can only be used with a Compute Module.
+ *
+ * Credit to forum user gizmomouse on
+ * https://www.raspberrypi.org/forums/viewtopic.php?f=98&t=253912 and
+ * Andrey Vostrukhin of Harlab for the overlay.
+ *
+ * Refer to https://github.com/harlab/CM4_LCD_LT070ME05000 for schematics and
+ * other documentation.
+ */
+
+/dts-v1/;
+/plugin/;
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&dsi1>;
+		__overlay__{
+			status = "okay";
+			#address-cells = <1>;
+			#size-cells = <0>;
+			port {
+				dsi_out_port:endpoint {
+					remote-endpoint = <&panel_dsi_port>;
+				};
+			};
+
+			lt070me05000:lt070me05000@0 {
+				compatible    = "jdi,lt070me05000";
+				status        = "okay";
+				reg           = <0>;
+				reset-gpios   = <&gpio 17 1>;   // LCD RST
+				enable-gpios  = <&gpio 4 0>;    // LCD Enable
+				dcdc-en-gpios = <&gpio 5 0>;    // LCD DC-DC Enable
+				port {
+					panel_dsi_port: endpoint {
+						remote-endpoint = <&dsi_out_port>;
+					};
+				};
+			};
+		};
+	};
+
+	fragment@1 {
+		target = <&gpio>;
+		__overlay__ {
+			lt070me05000_pins: lt070me05000_pins {
+				brcm,pins = <4 5 17>;
+				brcm,function = <1 1 1>; // out
+				brcm,pull = <0 0 0>; // off
+			};
+		};
+
+	};
+
+	__overrides__ {
+		reset = <&lt070me05000_pins>,"brcm,pins:8",
+			<&lt070me05000>,"reset-gpios:4";
+
+		enable = <&lt070me05000_pins>,"brcm,pins:0",
+			<&lt070me05000>,"enable-gpios:4";
+
+		dcdc-en = <&lt070me05000_pins>,"brcm,pins:4",
+			<&lt070me05000>,"dcdc-en-gpios:4";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/vc4-kms-dsi-lt070me05000-v2-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/vc4-kms-dsi-lt070me05000-v2-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/vc4-kms-dsi-lt070me05000-v2-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/vc4-kms-dsi-lt070me05000-v2-overlay.dts	2021-07-25 16:45:35.168807223 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/*
+ * Device Tree overlay to connect a JDI LT070ME05000 DSI panel to DSI1.
+ * This uses 4 DSI data lanes, so can only be used with a Compute Module.
+ *
+ * The overlay is for V2 of Harlab's interface board that uses a PCA9536 to
+ * handle the panel's control GPIOs instead of wiring it back to Pi GPIOs.
+ *
+ * Credit to Andrey Vostrukhin of Harlab for the overlay.
+ *
+ * Refer to https://github.com/harlab/CM4_LCD_LT070ME05000 for schematics and
+ * other documentation.
+ */
+
+/dts-v1/;
+/plugin/;
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&i2c_csi_dsi>;
+		__overlay__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			status = "okay";
+
+			pca: pca@41 {
+				compatible = "nxp,pca9536";
+				reg = <0x41>;
+				gpio-controller;
+				#gpio-cells = <2>;
+				status = "okay";
+			};
+		};
+	};
+
+	fragment@1 {
+		target = <&dsi1>;
+		__overlay__{
+			status = "okay";
+			#address-cells = <1>;
+			#size-cells = <0>;
+			port {
+				dsi_out_port:endpoint {
+					remote-endpoint = <&panel_dsi_port>;
+				};
+			};
+
+			lt070me05000:lt070me05000@0 {
+				compatible    = "jdi,lt070me05000";
+				status        = "okay";
+				reg           = <0>;
+				reset-gpios   = <&pca 0 1>;
+				enable-gpios  = <&pca 2 0>;
+				dcdc-en-gpios = <&pca 1 0>;
+				port {
+					panel_dsi_port: endpoint {
+						remote-endpoint = <&dsi_out_port>;
+					};
+				};
+			};
+		};
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/vc4-kms-kippah-7inch-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/vc4-kms-kippah-7inch-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/vc4-kms-kippah-7inch-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/vc4-kms-kippah-7inch-overlay.dts	2021-07-25 16:45:35.168807223 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/*
+ * vc4-kms-v3d-overlay.dts
+ */
+
+/dts-v1/;
+/plugin/;
+
+#include <dt-bindings/pinctrl/bcm2835.h>
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target-path = "/";
+		__overlay__ {
+			panel: panel {
+				compatible = "ontat,yx700wv03", "simple-panel";
+
+				port {
+					panel_in: endpoint {
+						remote-endpoint = <&dpi_out>;
+					};
+				};
+			};
+		};
+	};
+
+	fragment@1 {
+		target = <&dpi>;
+		__overlay__  {
+			status = "okay";
+
+			pinctrl-names = "default";
+			pinctrl-0 = <&dpi_18bit_gpio0>;
+
+			port {
+				dpi_out: endpoint@0 {
+					remote-endpoint = <&panel_in>;
+				};
+			};
+		};
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/vc4-kms-v3d-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/vc4-kms-v3d-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/vc4-kms-v3d-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/vc4-kms-v3d-overlay.dts	2021-07-25 16:45:35.168807223 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/*
+ * vc4-kms-v3d-overlay.dts
+ */
+
+/dts-v1/;
+/plugin/;
+
+#include <dt-bindings/clock/bcm2835.h>
+
+#include "cma-overlay.dts"
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@1 {
+		target = <&i2c2>;
+		__overlay__  {
+			status = "okay";
+		};
+	};
+
+	fragment@2 {
+		target = <&fb>;
+		__overlay__  {
+			status = "disabled";
+		};
+	};
+
+	fragment@3 {
+		target = <&pixelvalve0>;
+		__overlay__  {
+			status = "okay";
+		};
+	};
+
+	fragment@4 {
+		target = <&pixelvalve1>;
+		__overlay__  {
+			status = "okay";
+		};
+	};
+
+	fragment@5 {
+		target = <&pixelvalve2>;
+		__overlay__  {
+			status = "okay";
+		};
+	};
+
+	fragment@6 {
+		target = <&hvs>;
+		__overlay__  {
+			status = "okay";
+		};
+	};
+
+	fragment@7 {
+		target = <&hdmi>;
+		__overlay__  {
+			status = "okay";
+		};
+	};
+
+	fragment@8 {
+		target = <&v3d>;
+		__overlay__  {
+			status = "okay";
+		};
+	};
+
+	fragment@9 {
+		target = <&vc4>;
+		__overlay__  {
+			status = "okay";
+		};
+	};
+
+	fragment@10 {
+		target = <&clocks>;
+		__overlay__  {
+			claim-clocks = <
+				BCM2835_PLLD_DSI0
+				BCM2835_PLLD_DSI1
+				BCM2835_PLLH_AUX
+				BCM2835_PLLH_PIX
+			>;
+		};
+	};
+
+	fragment@11 {
+		target = <&vec>;
+		__overlay__  {
+			status = "okay";
+		};
+	};
+
+	fragment@12 {
+		target = <&txp>;
+		__overlay__  {
+			status = "okay";
+		};
+	};
+
+	fragment@13 {
+		target = <&hdmi>;
+		__dormant__  {
+			dmas;
+		};
+	};
+
+	fragment@14 {
+		target = <&audio>;
+		__overlay__  {
+		    brcm,disable-hdmi;
+		};
+	};
+
+	__overrides__ {
+		audio   = <0>,"!13", <0>,"=14";
+		noaudio = <0>,"=13", <0>,"!14";
+		nocomposite = <0>, "!11";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/vc4-kms-v3d-pi4-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/vc4-kms-v3d-pi4-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/vc4-kms-v3d-pi4-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/vc4-kms-v3d-pi4-overlay.dts	2021-07-25 16:45:35.168807223 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/*
+ * vc4-kms-v3d-pi4-overlay.dts
+ */
+
+/dts-v1/;
+/plugin/;
+
+#include <dt-bindings/clock/bcm2835.h>
+
+#include "cma-overlay.dts"
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@1 {
+		target = <&ddc0>;
+		__overlay__  {
+			status = "okay";
+		};
+	};
+
+	fragment@2 {
+		target = <&ddc1>;
+		__overlay__  {
+			status = "okay";
+		};
+	};
+
+	fragment@3 {
+		target = <&hdmi0>;
+		__overlay__  {
+			status = "okay";
+		};
+	};
+
+	fragment@4 {
+		target = <&hdmi1>;
+		__overlay__  {
+			status = "okay";
+		};
+	};
+
+	fragment@5 {
+		target = <&hvs>;
+		__overlay__  {
+			status = "okay";
+		};
+	};
+
+	fragment@6 {
+		target = <&pixelvalve0>;
+		__overlay__  {
+			status = "okay";
+		};
+	};
+
+	fragment@7 {
+		target = <&pixelvalve1>;
+		__overlay__  {
+			status = "okay";
+		};
+	};
+
+	fragment@8 {
+		target = <&pixelvalve2>;
+		__overlay__  {
+			status = "okay";
+		};
+	};
+
+	fragment@9 {
+		target = <&pixelvalve3>;
+		__overlay__  {
+			status = "okay";
+		};
+	};
+
+	fragment@10 {
+		target = <&pixelvalve4>;
+		__overlay__  {
+			status = "okay";
+		};
+	};
+
+	fragment@11 {
+		target = <&v3d>;
+		__overlay__  {
+			status = "okay";
+		};
+	};
+
+	fragment@12 {
+		target = <&vc4>;
+		__overlay__  {
+			status = "okay";
+		};
+	};
+
+	fragment@13 {
+		target = <&txp>;
+		__overlay__  {
+			status = "okay";
+		};
+	};
+
+	fragment@14 {
+		target = <&fb>;
+		__overlay__  {
+			status = "disabled";
+		};
+	};
+
+	fragment@15 {
+		target = <&firmwarekms>;
+		__overlay__  {
+			status = "disabled";
+		};
+	};
+
+	fragment@16 {
+		target = <&vec>;
+		__overlay__  {
+			status = "disabled";
+		};
+	};
+
+	fragment@17 {
+		target = <&hdmi0>;
+		__dormant__  {
+			dmas;
+		};
+	};
+
+	fragment@18 {
+		target = <&hdmi1>;
+		__dormant__  {
+			dmas;
+		};
+	};
+
+	fragment@19 {
+		target = <&audio>;
+		__overlay__  {
+		    brcm,disable-hdmi;
+		};
+	};
+
+	fragment@20 {
+		target = <&dvp>;
+		__overlay__  {
+			status = "okay";
+		};
+	};
+
+	fragment@21 {
+		target = <&pixelvalve3>;
+		__dormant__  {
+			status = "okay";
+		};
+	};
+
+	fragment@22 {
+		target = <&vec>;
+		__dormant__  {
+			status = "okay";
+		};
+	};
+
+	fragment@23 {
+		target = <&aon_intr>;
+		__overlay__  {
+			status = "okay";
+		};
+	};
+
+	__overrides__ {
+		audio   = <0>,"!17";
+		audio1   = <0>,"!18";
+		noaudio = <0>,"=17", <0>,"=18", <0>,"!19";
+		composite = <0>, "!1",
+			    <0>, "!2",
+			    <0>, "!3",
+			    <0>, "!4",
+			    <0>, "!6",
+			    <0>, "!7",
+			    <0>, "!8",
+			    <0>, "!9",
+			    <0>, "!10",
+			    <0>, "!16",
+			    <0>, "=21",
+			    <0>, "=22";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/vc4-kms-vga666-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/vc4-kms-vga666-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/vc4-kms-vga666-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/vc4-kms-vga666-overlay.dts	2021-07-25 16:45:35.168807223 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/*
+ * vc4-kms-vga666-overlay.dts
+ * Configures a FenLogic or similar VGA666 DPI adapter when using the
+ * vc4-kms-v3d driver.
+ * If a suitable I2C level shifter is connected to GPIOs 0&1 and the VGA
+ * ID1/SDA (pin 12) and ID3/SCL (pin 15) lines, then there is the option to
+ * enable reading the EDID from the display.
+ */
+
+/dts-v1/;
+/plugin/;
+
+#include <dt-bindings/pinctrl/bcm2835.h>
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target-path = "/";
+		__overlay__ {
+			vga_connector: vga_connector {
+				compatible = "vga-connector";
+				label = "vga";
+
+				port {
+					vga_con_in: endpoint {
+						remote-endpoint = <&vga666_out>;
+					};
+				};
+			};
+
+			vga_dac {
+				compatible = "dumb-vga-dac";
+
+				ports {
+					#address-cells = <1>;
+					#size-cells = <0>;
+
+					port@0 {
+						reg = <0>;
+
+						vga666_in: endpoint {
+							remote-endpoint = <&dpi_out>;
+						};
+					};
+
+					port@1 {
+						reg = <1>;
+
+						vga666_out: endpoint {
+							remote-endpoint = <&vga_con_in>;
+						};
+					};
+				};
+			};
+
+		};
+	};
+
+	fragment@1 {
+		target = <&dpi>;
+		__overlay__  {
+			status = "okay";
+
+			pinctrl-names = "default";
+			pinctrl-0 = <&dpi_18bit_gpio2>;
+
+			port {
+				dpi_out: endpoint@0 {
+					remote-endpoint = <&vga666_in>;
+				};
+			};
+		};
+	};
+
+	fragment@2 {
+		target = <&vga_connector>;
+		__dormant__  {
+			ddc-i2c-bus = <&i2c_vc>;
+		};
+	};
+
+	fragment@3 {
+		target = <&i2c0if>;
+		__dormant__ {
+			status = "okay";
+		};
+	};
+
+	fragment@4 {
+		target = <&i2c0mux>;
+		__dormant__ {
+			status = "okay";
+		};
+	};
+
+	__overrides__ {
+		ddc = <0>,"=2", <0>,"=3", <0>,"=4";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/vga666-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/vga666-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/vga666-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/vga666-overlay.dts	2021-07-25 16:45:35.168807223 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/dts-v1/;
+/plugin/;
+
+/{
+	compatible = "brcm,bcm2835";
+
+	// There is no VGA driver module, but we need a platform device
+	// node (that doesn't already use pinctrl) to hang the pinctrl
+	// reference on - leds will do
+
+	fragment@0 {
+		target = <&leds>;
+		__overlay__ {
+			pinctrl-names = "default";
+			pinctrl-0 = <&vga666_pins>;
+		};
+	};
+
+	fragment@1 {
+		target = <&gpio>;
+		__overlay__ {
+			vga666_pins: vga666_pins {
+				brcm,pins = <2 3 4 5 6 7 8 9 10 11 12
+					     13 14 15 16 17 18 19 20 21>;
+				brcm,function = <6>; /* alt2 */
+				brcm,pull = <0>; /* no pull */
+			};
+		};
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/w1-gpio-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/w1-gpio-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/w1-gpio-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/w1-gpio-overlay.dts	2021-07-25 16:45:35.168807223 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// Definitions for w1-gpio module (without external pullup)
+/dts-v1/;
+/plugin/;
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target-path = "/";
+		__overlay__ {
+
+			w1: onewire@0 {
+				compatible = "w1-gpio";
+				pinctrl-names = "default";
+				pinctrl-0 = <&w1_pins>;
+				gpios = <&gpio 4 0>;
+				status = "okay";
+			};
+		};
+	};
+
+	fragment@1 {
+		target = <&gpio>;
+		__overlay__ {
+			w1_pins: w1_pins@0 {
+				brcm,pins = <4>;
+				brcm,function = <0>; // in (initially)
+				brcm,pull = <0>; // off
+			};
+		};
+	};
+
+	__overrides__ {
+		gpiopin =       <&w1>,"gpios:4",
+				<&w1>,"reg:0",
+				<&w1_pins>,"brcm,pins:0",
+				<&w1_pins>,"reg:0";
+		pullup;		// Silently ignore unneeded parameter
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/w1-gpio-pullup-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/w1-gpio-pullup-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/w1-gpio-pullup-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/w1-gpio-pullup-overlay.dts	2021-07-25 16:45:35.168807223 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// Definitions for w1-gpio module (with external pullup)
+/dts-v1/;
+/plugin/;
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target-path = "/";
+		__overlay__ {
+
+			w1: onewire@0 {
+				compatible = "w1-gpio";
+				pinctrl-names = "default";
+				pinctrl-0 = <&w1_pins>;
+				gpios = <&gpio 4 0>, <&gpio 5 1>;
+				status = "okay";
+			};
+		};
+	};
+
+	fragment@1 {
+		target = <&gpio>;
+		__overlay__ {
+			w1_pins: w1_pins@0 {
+				brcm,pins = <4 5>;
+				brcm,function = <0 1>; // in out
+				brcm,pull = <0 0>; // off off
+			};
+		};
+	};
+
+	__overrides__ {
+		gpiopin =       <&w1>,"gpios:4",
+				<&w1>,"reg:0",
+				<&w1_pins>,"brcm,pins:0",
+				<&w1_pins>,"reg:0";
+		extpullup =     <&w1>,"gpios:16",
+				<&w1_pins>,"brcm,pins:4";
+		pullup;		// Silently ignore unneeded parameter
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/w5500-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/w5500-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/w5500-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/w5500-overlay.dts	2021-07-25 16:45:35.168807223 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// Overlay for the Wiznet w5500 Ethernet Controller
+/dts-v1/;
+/plugin/;
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&spidev0>;
+		__overlay__ {
+			status = "disabled";
+		};
+	};
+
+	fragment@1 {
+		target = <&spidev1>;
+		__dormant__ {
+			status = "disabled";
+		};
+	};
+
+	fragment@2 {
+		target = <&spi0>;
+		__overlay__ {
+			/* needed to avoid dtc warning */
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			status = "okay";
+
+			eth1: w5500@0{
+				compatible = "wiznet,w5500";
+				reg = <0>; /* CE0 */
+				pinctrl-names = "default";
+				pinctrl-0 = <&eth1_pins>;
+				interrupt-parent = <&gpio>;
+				interrupts = <25 0x8>;
+				spi-max-frequency = <30000000>;
+//				local-mac-address = [aa bb cc dd ee ff];
+				status = "okay";
+			};
+		};
+	};
+
+	fragment@3 {
+		target = <&gpio>;
+		__overlay__ {
+			eth1_pins: eth1_pins {
+				brcm,pins = <25>;
+				brcm,function = <0>; /* in */
+				brcm,pull = <0>; /* none */
+			};
+		};
+	};
+
+	__overrides__ {
+		int_pin = <&eth1>, "interrupts:0",
+		          <&eth1_pins>, "brcm,pins:0";
+		speed   = <&eth1>, "spi-max-frequency:0";
+		cs      = <&eth1>, "reg:0",
+			  <0>, "!0=1";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/wittypi-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/wittypi-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/wittypi-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/wittypi-overlay.dts	2021-07-25 16:45:35.168807223 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/*
+ * Device Tree overlay for Witty Pi extension board by UUGear
+ *
+ */
+
+/dts-v1/;
+/plugin/;
+
+/ {
+
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&leds>;
+		__overlay__ {
+			compatible = "gpio-leds";
+			wittypi_led: wittypi_led {
+				label = "wittypi_led";
+				linux,default-trigger = "default-on";
+				gpios = <&gpio 17 0>;
+			};
+		};
+	};
+
+	fragment@1 {
+		target = <&i2c1>;
+		__overlay__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			rtc: ds1337@68 {
+				compatible = "dallas,ds1337";
+				reg = <0x68>;
+				wakeup-source;
+			};
+		};
+	};
+
+	__overrides__ {
+		led_gpio =	<&wittypi_led>,"gpios:4";
+		led_trigger =	<&wittypi_led>,"linux,default-trigger";
+	};
+
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/boot/dts/overlays/wm8960-soundcard-overlay.dts linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/wm8960-soundcard-overlay.dts
--- linux-5.10.52-orig/arch/arm/boot/dts/overlays/wm8960-soundcard-overlay.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/boot/dts/overlays/wm8960-soundcard-overlay.dts	2021-07-25 16:45:35.168807223 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// Definitions for Waveshare WM8960 https://github.com/waveshare/WM8960-Audio-HAT
+/dts-v1/;
+/plugin/;
+
+/ {
+	compatible = "brcm,bcm2835";
+
+	fragment@0 {
+		target = <&i2s>;
+		__overlay__ {
+			status = "okay";
+		};
+	};
+
+	fragment@1 {
+		target-path="/";
+		__overlay__ {
+			wm8960_mclk: wm8960_mclk {
+				compatible = "fixed-clock";
+				#clock-cells = <0>;
+				clock-frequency = <12288000>;
+			};
+		};
+	};
+	fragment@2 {
+		target = <&i2c1>;
+		__overlay__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			status = "okay";
+
+			wm8960: wm8960 {
+				compatible = "wlf,wm8960";
+				reg = <0x1a>;
+				#sound-dai-cells = <0>;
+				AVDD-supply = <&vdd_5v0_reg>;
+				DVDD-supply = <&vdd_3v3_reg>;
+			};
+		};
+	};
+
+
+	fragment@3 {
+		target = <&sound>;
+		slave_overlay: __overlay__ {
+			compatible = "simple-audio-card";
+			simple-audio-card,format = "i2s";
+			simple-audio-card,name = "wm8960-soundcard"; 
+			status = "okay";
+
+			simple-audio-card,widgets =
+				"Microphone", "Mic Jack",
+				"Line", "Line In",
+				"Line", "Line Out",
+				"Speaker", "Speaker",
+				"Headphone", "Headphone Jack";
+			simple-audio-card,routing =
+				"Headphone Jack", "HP_L",
+				"Headphone Jack", "HP_R",
+				"Speaker", "SPK_LP",
+				"Speaker", "SPK_LN",
+				"LINPUT1", "Mic Jack",
+				"LINPUT3", "Mic Jack",
+				"RINPUT1", "Mic Jack",
+				"RINPUT2", "Mic Jack";
+
+			simple-audio-card,cpu {
+				sound-dai = <&i2s>;
+			};
+			dailink0_slave: simple-audio-card,codec {
+				sound-dai = <&wm8960>;
+				clocks = <&wm8960_mclk>;
+				clock-names = "mclk";
+			};
+		};
+	};
+
+	__overrides__ {
+		alsaname = <&slave_overlay>,"simple-audio-card,name";
+		compatible = <&wm8960>,"compatible";
+	};
+};
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/configs/bcm2709_defconfig linux-5.10.52-v7l+/arch/arm/configs/bcm2709_defconfig
--- linux-5.10.52-orig/arch/arm/configs/bcm2709_defconfig	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/configs/bcm2709_defconfig	2021-07-25 16:45:35.368803870 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+CONFIG_LOCALVERSION="-v7"
+# CONFIG_LOCALVERSION_AUTO is not set
+CONFIG_SYSVIPC=y
+CONFIG_POSIX_MQUEUE=y
+CONFIG_GENERIC_IRQ_DEBUGFS=y
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_PREEMPT_VOLUNTARY=y
+CONFIG_BSD_PROCESS_ACCT=y
+CONFIG_BSD_PROCESS_ACCT_V3=y
+CONFIG_TASKSTATS=y
+CONFIG_TASK_DELAY_ACCT=y
+CONFIG_TASK_XACCT=y
+CONFIG_TASK_IO_ACCOUNTING=y
+CONFIG_IKCONFIG=m
+CONFIG_IKCONFIG_PROC=y
+CONFIG_MEMCG=y
+CONFIG_BLK_CGROUP=y
+CONFIG_CFS_BANDWIDTH=y
+CONFIG_CGROUP_PIDS=y
+CONFIG_CGROUP_FREEZER=y
+CONFIG_CPUSETS=y
+CONFIG_CGROUP_DEVICE=y
+CONFIG_CGROUP_CPUACCT=y
+CONFIG_CGROUP_PERF=y
+CONFIG_CGROUP_BPF=y
+CONFIG_NAMESPACES=y
+CONFIG_USER_NS=y
+CONFIG_SCHED_AUTOGROUP=y
+CONFIG_BLK_DEV_INITRD=y
+CONFIG_BPF_SYSCALL=y
+CONFIG_EMBEDDED=y
+# CONFIG_COMPAT_BRK is not set
+CONFIG_PROFILING=y
+CONFIG_ARCH_BCM=y
+CONFIG_ARCH_BCM2835=y
+# CONFIG_CACHE_L2X0 is not set
+CONFIG_SMP=y
+CONFIG_VMSPLIT_2G=y
+# CONFIG_CPU_SW_DOMAIN_PAN is not set
+CONFIG_UACCESS_WITH_MEMCPY=y
+# CONFIG_ATAGS is not set
+CONFIG_CMDLINE="console=ttyAMA0,115200 kgdboc=ttyAMA0,115200 root=/dev/mmcblk0p2 rootfstype=ext4 rootwait"
+CONFIG_CPU_FREQ=y
+CONFIG_CPU_FREQ_STAT=y
+CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE=y
+CONFIG_CPU_FREQ_GOV_PERFORMANCE=y
+CONFIG_CPU_FREQ_GOV_USERSPACE=y
+CONFIG_CPU_FREQ_GOV_ONDEMAND=y
+CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y
+CONFIG_CPU_FREQ_GOV_SCHEDUTIL=y
+CONFIG_CPUFREQ_DT=y
+CONFIG_ARM_RASPBERRYPI_CPUFREQ=y
+CONFIG_VFP=y
+CONFIG_NEON=y
+CONFIG_KERNEL_MODE_NEON=y
+# CONFIG_SUSPEND is not set
+CONFIG_PM=y
+CONFIG_RASPBERRYPI_FIRMWARE=y
+CONFIG_CRYPTO_SHA1_ARM_NEON=m
+CONFIG_CRYPTO_AES_ARM=m
+CONFIG_CRYPTO_AES_ARM_BS=m
+CONFIG_OPROFILE=m
+CONFIG_JUMP_LABEL=y
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+CONFIG_MODVERSIONS=y
+CONFIG_MODULE_SRCVERSION_ALL=y
+CONFIG_PARTITION_ADVANCED=y
+CONFIG_MAC_PARTITION=y
+CONFIG_BINFMT_MISC=m
+CONFIG_CLEANCACHE=y
+CONFIG_FRONTSWAP=y
+CONFIG_CMA=y
+CONFIG_ZSWAP=y
+CONFIG_Z3FOLD=m
+CONFIG_ZSMALLOC=m
+CONFIG_NET=y
+CONFIG_PACKET=y
+CONFIG_UNIX=y
+CONFIG_XFRM_USER=y
+CONFIG_NET_KEY=m
+CONFIG_INET=y
+CONFIG_IP_MULTICAST=y
+CONFIG_IP_ADVANCED_ROUTER=y
+CONFIG_IP_MULTIPLE_TABLES=y
+CONFIG_IP_ROUTE_MULTIPATH=y
+CONFIG_IP_ROUTE_VERBOSE=y
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+CONFIG_IP_PNP_RARP=y
+CONFIG_NET_IPIP=m
+CONFIG_NET_IPGRE_DEMUX=m
+CONFIG_NET_IPGRE=m
+CONFIG_IP_MROUTE=y
+CONFIG_IP_MROUTE_MULTIPLE_TABLES=y
+CONFIG_IP_PIMSM_V1=y
+CONFIG_IP_PIMSM_V2=y
+CONFIG_NET_IPVTI=m
+CONFIG_NET_FOU=m
+CONFIG_INET_AH=m
+CONFIG_INET_ESP=m
+CONFIG_INET_IPCOMP=m
+CONFIG_INET_DIAG=m
+CONFIG_TCP_CONG_ADVANCED=y
+CONFIG_TCP_CONG_BBR=m
+CONFIG_IPV6=m
+CONFIG_IPV6_ROUTER_PREF=y
+CONFIG_IPV6_ROUTE_INFO=y
+CONFIG_INET6_AH=m
+CONFIG_INET6_ESP=m
+CONFIG_INET6_ESP_OFFLOAD=m
+CONFIG_INET6_IPCOMP=m
+CONFIG_IPV6_ILA=m
+CONFIG_IPV6_VTI=m
+CONFIG_IPV6_SIT_6RD=y
+CONFIG_IPV6_GRE=m
+CONFIG_IPV6_MULTIPLE_TABLES=y
+CONFIG_IPV6_SUBTREES=y
+CONFIG_IPV6_MROUTE=y
+CONFIG_IPV6_MROUTE_MULTIPLE_TABLES=y
+CONFIG_IPV6_PIMSM_V2=y
+CONFIG_NETFILTER=y
+CONFIG_BRIDGE_NETFILTER=m
+CONFIG_NF_CONNTRACK=m
+CONFIG_NF_LOG_NETDEV=m
+CONFIG_NF_CONNTRACK_ZONES=y
+CONFIG_NF_CONNTRACK_EVENTS=y
+CONFIG_NF_CONNTRACK_TIMESTAMP=y
+CONFIG_NF_CONNTRACK_AMANDA=m
+CONFIG_NF_CONNTRACK_FTP=m
+CONFIG_NF_CONNTRACK_H323=m
+CONFIG_NF_CONNTRACK_IRC=m
+CONFIG_NF_CONNTRACK_NETBIOS_NS=m
+CONFIG_NF_CONNTRACK_SNMP=m
+CONFIG_NF_CONNTRACK_PPTP=m
+CONFIG_NF_CONNTRACK_SANE=m
+CONFIG_NF_CONNTRACK_SIP=m
+CONFIG_NF_CONNTRACK_TFTP=m
+CONFIG_NF_CT_NETLINK=m
+CONFIG_NF_TABLES=m
+CONFIG_NF_TABLES_INET=y
+CONFIG_NF_TABLES_NETDEV=y
+CONFIG_NFT_NUMGEN=m
+CONFIG_NFT_CT=m
+CONFIG_NFT_FLOW_OFFLOAD=m
+CONFIG_NFT_COUNTER=m
+CONFIG_NFT_CONNLIMIT=m
+CONFIG_NFT_LOG=m
+CONFIG_NFT_LIMIT=m
+CONFIG_NFT_MASQ=m
+CONFIG_NFT_REDIR=m
+CONFIG_NFT_NAT=m
+CONFIG_NFT_TUNNEL=m
+CONFIG_NFT_OBJREF=m
+CONFIG_NFT_QUEUE=m
+CONFIG_NFT_QUOTA=m
+CONFIG_NFT_REJECT=m
+CONFIG_NFT_COMPAT=m
+CONFIG_NFT_HASH=m
+CONFIG_NFT_FIB_INET=m
+CONFIG_NFT_SOCKET=m
+CONFIG_NFT_OSF=m
+CONFIG_NFT_TPROXY=m
+CONFIG_NFT_DUP_NETDEV=m
+CONFIG_NFT_FWD_NETDEV=m
+CONFIG_NFT_FIB_NETDEV=m
+CONFIG_NF_FLOW_TABLE_INET=m
+CONFIG_NF_FLOW_TABLE=m
+CONFIG_NETFILTER_XT_SET=m
+CONFIG_NETFILTER_XT_TARGET_CHECKSUM=m
+CONFIG_NETFILTER_XT_TARGET_CLASSIFY=m
+CONFIG_NETFILTER_XT_TARGET_CONNMARK=m
+CONFIG_NETFILTER_XT_TARGET_DSCP=m
+CONFIG_NETFILTER_XT_TARGET_HMARK=m
+CONFIG_NETFILTER_XT_TARGET_IDLETIMER=m
+CONFIG_NETFILTER_XT_TARGET_LED=m
+CONFIG_NETFILTER_XT_TARGET_LOG=m
+CONFIG_NETFILTER_XT_TARGET_MARK=m
+CONFIG_NETFILTER_XT_TARGET_NFLOG=m
+CONFIG_NETFILTER_XT_TARGET_NFQUEUE=m
+CONFIG_NETFILTER_XT_TARGET_NOTRACK=m
+CONFIG_NETFILTER_XT_TARGET_TEE=m
+CONFIG_NETFILTER_XT_TARGET_TPROXY=m
+CONFIG_NETFILTER_XT_TARGET_TRACE=m
+CONFIG_NETFILTER_XT_TARGET_TCPMSS=m
+CONFIG_NETFILTER_XT_TARGET_TCPOPTSTRIP=m
+CONFIG_NETFILTER_XT_MATCH_ADDRTYPE=m
+CONFIG_NETFILTER_XT_MATCH_BPF=m
+CONFIG_NETFILTER_XT_MATCH_CLUSTER=m
+CONFIG_NETFILTER_XT_MATCH_COMMENT=m
+CONFIG_NETFILTER_XT_MATCH_CONNBYTES=m
+CONFIG_NETFILTER_XT_MATCH_CONNLABEL=m
+CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=m
+CONFIG_NETFILTER_XT_MATCH_CONNMARK=m
+CONFIG_NETFILTER_XT_MATCH_CONNTRACK=m
+CONFIG_NETFILTER_XT_MATCH_CPU=m
+CONFIG_NETFILTER_XT_MATCH_DCCP=m
+CONFIG_NETFILTER_XT_MATCH_DEVGROUP=m
+CONFIG_NETFILTER_XT_MATCH_DSCP=m
+CONFIG_NETFILTER_XT_MATCH_ESP=m
+CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=m
+CONFIG_NETFILTER_XT_MATCH_HELPER=m
+CONFIG_NETFILTER_XT_MATCH_IPRANGE=m
+CONFIG_NETFILTER_XT_MATCH_IPVS=m
+CONFIG_NETFILTER_XT_MATCH_LENGTH=m
+CONFIG_NETFILTER_XT_MATCH_LIMIT=m
+CONFIG_NETFILTER_XT_MATCH_MAC=m
+CONFIG_NETFILTER_XT_MATCH_MARK=m
+CONFIG_NETFILTER_XT_MATCH_MULTIPORT=m
+CONFIG_NETFILTER_XT_MATCH_NFACCT=m
+CONFIG_NETFILTER_XT_MATCH_OSF=m
+CONFIG_NETFILTER_XT_MATCH_OWNER=m
+CONFIG_NETFILTER_XT_MATCH_POLICY=m
+CONFIG_NETFILTER_XT_MATCH_PHYSDEV=m
+CONFIG_NETFILTER_XT_MATCH_PKTTYPE=m
+CONFIG_NETFILTER_XT_MATCH_QUOTA=m
+CONFIG_NETFILTER_XT_MATCH_RATEEST=m
+CONFIG_NETFILTER_XT_MATCH_REALM=m
+CONFIG_NETFILTER_XT_MATCH_RECENT=m
+CONFIG_NETFILTER_XT_MATCH_SOCKET=m
+CONFIG_NETFILTER_XT_MATCH_STATE=m
+CONFIG_NETFILTER_XT_MATCH_STATISTIC=m
+CONFIG_NETFILTER_XT_MATCH_STRING=m
+CONFIG_NETFILTER_XT_MATCH_TCPMSS=m
+CONFIG_NETFILTER_XT_MATCH_TIME=m
+CONFIG_NETFILTER_XT_MATCH_U32=m
+CONFIG_IP_SET=m
+CONFIG_IP_SET_BITMAP_IP=m
+CONFIG_IP_SET_BITMAP_IPMAC=m
+CONFIG_IP_SET_BITMAP_PORT=m
+CONFIG_IP_SET_HASH_IP=m
+CONFIG_IP_SET_HASH_IPPORT=m
+CONFIG_IP_SET_HASH_IPPORTIP=m
+CONFIG_IP_SET_HASH_IPPORTNET=m
+CONFIG_IP_SET_HASH_NET=m
+CONFIG_IP_SET_HASH_NETPORT=m
+CONFIG_IP_SET_HASH_NETIFACE=m
+CONFIG_IP_SET_LIST_SET=m
+CONFIG_IP_VS=m
+CONFIG_IP_VS_PROTO_TCP=y
+CONFIG_IP_VS_PROTO_UDP=y
+CONFIG_IP_VS_PROTO_ESP=y
+CONFIG_IP_VS_PROTO_AH=y
+CONFIG_IP_VS_PROTO_SCTP=y
+CONFIG_IP_VS_RR=m
+CONFIG_IP_VS_WRR=m
+CONFIG_IP_VS_LC=m
+CONFIG_IP_VS_WLC=m
+CONFIG_IP_VS_LBLC=m
+CONFIG_IP_VS_LBLCR=m
+CONFIG_IP_VS_DH=m
+CONFIG_IP_VS_SH=m
+CONFIG_IP_VS_SED=m
+CONFIG_IP_VS_NQ=m
+CONFIG_IP_VS_FTP=m
+CONFIG_IP_VS_PE_SIP=m
+CONFIG_NFT_DUP_IPV4=m
+CONFIG_NFT_FIB_IPV4=m
+CONFIG_NF_TABLES_ARP=y
+CONFIG_NF_FLOW_TABLE_IPV4=m
+CONFIG_NF_LOG_ARP=m
+CONFIG_IP_NF_IPTABLES=m
+CONFIG_IP_NF_MATCH_AH=m
+CONFIG_IP_NF_MATCH_ECN=m
+CONFIG_IP_NF_MATCH_RPFILTER=m
+CONFIG_IP_NF_MATCH_TTL=m
+CONFIG_IP_NF_FILTER=m
+CONFIG_IP_NF_TARGET_REJECT=m
+CONFIG_IP_NF_NAT=m
+CONFIG_IP_NF_TARGET_MASQUERADE=m
+CONFIG_IP_NF_TARGET_NETMAP=m
+CONFIG_IP_NF_TARGET_REDIRECT=m
+CONFIG_IP_NF_MANGLE=m
+CONFIG_IP_NF_TARGET_CLUSTERIP=m
+CONFIG_IP_NF_TARGET_ECN=m
+CONFIG_IP_NF_TARGET_TTL=m
+CONFIG_IP_NF_RAW=m
+CONFIG_IP_NF_ARPTABLES=m
+CONFIG_IP_NF_ARPFILTER=m
+CONFIG_IP_NF_ARP_MANGLE=m
+CONFIG_NFT_DUP_IPV6=m
+CONFIG_NFT_FIB_IPV6=m
+CONFIG_NF_FLOW_TABLE_IPV6=m
+CONFIG_IP6_NF_IPTABLES=m
+CONFIG_IP6_NF_MATCH_AH=m
+CONFIG_IP6_NF_MATCH_EUI64=m
+CONFIG_IP6_NF_MATCH_FRAG=m
+CONFIG_IP6_NF_MATCH_OPTS=m
+CONFIG_IP6_NF_MATCH_HL=m
+CONFIG_IP6_NF_MATCH_IPV6HEADER=m
+CONFIG_IP6_NF_MATCH_MH=m
+CONFIG_IP6_NF_MATCH_RPFILTER=m
+CONFIG_IP6_NF_MATCH_RT=m
+CONFIG_IP6_NF_MATCH_SRH=m
+CONFIG_IP6_NF_TARGET_HL=m
+CONFIG_IP6_NF_FILTER=m
+CONFIG_IP6_NF_TARGET_REJECT=m
+CONFIG_IP6_NF_TARGET_SYNPROXY=m
+CONFIG_IP6_NF_MANGLE=m
+CONFIG_IP6_NF_RAW=m
+CONFIG_IP6_NF_SECURITY=m
+CONFIG_IP6_NF_NAT=m
+CONFIG_IP6_NF_TARGET_MASQUERADE=m
+CONFIG_IP6_NF_TARGET_NPT=m
+CONFIG_NF_TABLES_BRIDGE=m
+CONFIG_NFT_BRIDGE_REJECT=m
+CONFIG_NF_LOG_BRIDGE=m
+CONFIG_BRIDGE_NF_EBTABLES=m
+CONFIG_BRIDGE_EBT_BROUTE=m
+CONFIG_BRIDGE_EBT_T_FILTER=m
+CONFIG_BRIDGE_EBT_T_NAT=m
+CONFIG_BRIDGE_EBT_802_3=m
+CONFIG_BRIDGE_EBT_AMONG=m
+CONFIG_BRIDGE_EBT_ARP=m
+CONFIG_BRIDGE_EBT_IP=m
+CONFIG_BRIDGE_EBT_IP6=m
+CONFIG_BRIDGE_EBT_LIMIT=m
+CONFIG_BRIDGE_EBT_MARK=m
+CONFIG_BRIDGE_EBT_PKTTYPE=m
+CONFIG_BRIDGE_EBT_STP=m
+CONFIG_BRIDGE_EBT_VLAN=m
+CONFIG_BRIDGE_EBT_ARPREPLY=m
+CONFIG_BRIDGE_EBT_DNAT=m
+CONFIG_BRIDGE_EBT_MARK_T=m
+CONFIG_BRIDGE_EBT_REDIRECT=m
+CONFIG_BRIDGE_EBT_SNAT=m
+CONFIG_BRIDGE_EBT_LOG=m
+CONFIG_BRIDGE_EBT_NFLOG=m
+CONFIG_SCTP_COOKIE_HMAC_SHA1=y
+CONFIG_ATM=m
+CONFIG_L2TP=m
+CONFIG_L2TP_V3=y
+CONFIG_L2TP_IP=m
+CONFIG_L2TP_ETH=m
+CONFIG_BRIDGE=m
+CONFIG_VLAN_8021Q=m
+CONFIG_VLAN_8021Q_GVRP=y
+CONFIG_ATALK=m
+CONFIG_6LOWPAN=m
+CONFIG_IEEE802154=m
+CONFIG_IEEE802154_6LOWPAN=m
+CONFIG_MAC802154=m
+CONFIG_NET_SCHED=y
+CONFIG_NET_SCH_CBQ=m
+CONFIG_NET_SCH_HTB=m
+CONFIG_NET_SCH_HFSC=m
+CONFIG_NET_SCH_ATM=m
+CONFIG_NET_SCH_PRIO=m
+CONFIG_NET_SCH_MULTIQ=m
+CONFIG_NET_SCH_RED=m
+CONFIG_NET_SCH_SFB=m
+CONFIG_NET_SCH_SFQ=m
+CONFIG_NET_SCH_TEQL=m
+CONFIG_NET_SCH_TBF=m
+CONFIG_NET_SCH_GRED=m
+CONFIG_NET_SCH_DSMARK=m
+CONFIG_NET_SCH_NETEM=m
+CONFIG_NET_SCH_DRR=m
+CONFIG_NET_SCH_MQPRIO=m
+CONFIG_NET_SCH_CHOKE=m
+CONFIG_NET_SCH_QFQ=m
+CONFIG_NET_SCH_CODEL=m
+CONFIG_NET_SCH_FQ_CODEL=m
+CONFIG_NET_SCH_CAKE=m
+CONFIG_NET_SCH_FQ=m
+CONFIG_NET_SCH_HHF=m
+CONFIG_NET_SCH_PIE=m
+CONFIG_NET_SCH_INGRESS=m
+CONFIG_NET_SCH_PLUG=m
+CONFIG_NET_CLS_BASIC=m
+CONFIG_NET_CLS_TCINDEX=m
+CONFIG_NET_CLS_ROUTE4=m
+CONFIG_NET_CLS_FW=m
+CONFIG_NET_CLS_U32=m
+CONFIG_CLS_U32_MARK=y
+CONFIG_NET_CLS_RSVP=m
+CONFIG_NET_CLS_RSVP6=m
+CONFIG_NET_CLS_FLOW=m
+CONFIG_NET_CLS_CGROUP=m
+CONFIG_NET_EMATCH=y
+CONFIG_NET_EMATCH_CMP=m
+CONFIG_NET_EMATCH_NBYTE=m
+CONFIG_NET_EMATCH_U32=m
+CONFIG_NET_EMATCH_META=m
+CONFIG_NET_EMATCH_TEXT=m
+CONFIG_NET_EMATCH_IPSET=m
+CONFIG_NET_CLS_ACT=y
+CONFIG_NET_ACT_POLICE=m
+CONFIG_NET_ACT_GACT=m
+CONFIG_GACT_PROB=y
+CONFIG_NET_ACT_MIRRED=m
+CONFIG_NET_ACT_IPT=m
+CONFIG_NET_ACT_NAT=m
+CONFIG_NET_ACT_PEDIT=m
+CONFIG_NET_ACT_SIMP=m
+CONFIG_NET_ACT_SKBEDIT=m
+CONFIG_NET_ACT_CSUM=m
+CONFIG_BATMAN_ADV=m
+CONFIG_OPENVSWITCH=m
+CONFIG_CGROUP_NET_PRIO=y
+CONFIG_NET_PKTGEN=m
+CONFIG_HAMRADIO=y
+CONFIG_AX25=m
+CONFIG_NETROM=m
+CONFIG_ROSE=m
+CONFIG_MKISS=m
+CONFIG_6PACK=m
+CONFIG_BPQETHER=m
+CONFIG_BAYCOM_SER_FDX=m
+CONFIG_BAYCOM_SER_HDX=m
+CONFIG_YAM=m
+CONFIG_CAN=m
+CONFIG_CAN_J1939=m
+CONFIG_CAN_ISOTP=m
+CONFIG_CAN_VCAN=m
+CONFIG_CAN_SLCAN=m
+CONFIG_CAN_MCP251X=m
+CONFIG_CAN_MCP251XFD=m
+CONFIG_CAN_EMS_USB=m
+CONFIG_CAN_GS_USB=m
+CONFIG_BT=m
+CONFIG_BT_RFCOMM=m
+CONFIG_BT_RFCOMM_TTY=y
+CONFIG_BT_BNEP=m
+CONFIG_BT_BNEP_MC_FILTER=y
+CONFIG_BT_BNEP_PROTO_FILTER=y
+CONFIG_BT_HIDP=m
+CONFIG_BT_6LOWPAN=m
+CONFIG_BT_HCIBTUSB=m
+CONFIG_BT_HCIUART=m
+CONFIG_BT_HCIUART_3WIRE=y
+CONFIG_BT_HCIUART_BCM=y
+CONFIG_BT_HCIBCM203X=m
+CONFIG_BT_HCIBPA10X=m
+CONFIG_BT_HCIBFUSB=m
+CONFIG_BT_HCIVHCI=m
+CONFIG_BT_MRVL=m
+CONFIG_BT_MRVL_SDIO=m
+CONFIG_BT_ATH3K=m
+CONFIG_CFG80211=m
+CONFIG_CFG80211_WEXT=y
+CONFIG_MAC80211=m
+CONFIG_MAC80211_MESH=y
+CONFIG_WIMAX=m
+CONFIG_RFKILL=m
+CONFIG_RFKILL_INPUT=y
+CONFIG_NET_9P=m
+CONFIG_NFC=m
+CONFIG_UEVENT_HELPER=y
+CONFIG_DEVTMPFS=y
+CONFIG_DEVTMPFS_MOUNT=y
+CONFIG_MTD=m
+CONFIG_MTD_BLOCK=m
+CONFIG_MTD_BLOCK2MTD=m
+CONFIG_MTD_SPI_NOR=m
+CONFIG_MTD_UBI=m
+CONFIG_OF_CONFIGFS=y
+CONFIG_ZRAM=m
+CONFIG_BLK_DEV_LOOP=y
+CONFIG_BLK_DEV_CRYPTOLOOP=m
+CONFIG_BLK_DEV_DRBD=m
+CONFIG_BLK_DEV_NBD=m
+CONFIG_BLK_DEV_RAM=y
+CONFIG_CDROM_PKTCDVD=m
+CONFIG_ATA_OVER_ETH=m
+CONFIG_EEPROM_AT24=m
+CONFIG_TI_ST=m
+CONFIG_SCSI=y
+# CONFIG_SCSI_PROC_FS is not set
+CONFIG_BLK_DEV_SD=y
+CONFIG_CHR_DEV_ST=m
+CONFIG_BLK_DEV_SR=m
+CONFIG_CHR_DEV_SG=m
+CONFIG_SCSI_ISCSI_ATTRS=y
+CONFIG_ISCSI_TCP=m
+CONFIG_ISCSI_BOOT_SYSFS=m
+CONFIG_MD=y
+CONFIG_MD_LINEAR=m
+CONFIG_BLK_DEV_DM=m
+CONFIG_DM_CRYPT=m
+CONFIG_DM_SNAPSHOT=m
+CONFIG_DM_THIN_PROVISIONING=m
+CONFIG_DM_CACHE=m
+CONFIG_DM_MIRROR=m
+CONFIG_DM_LOG_USERSPACE=m
+CONFIG_DM_RAID=m
+CONFIG_DM_ZERO=m
+CONFIG_DM_DELAY=m
+CONFIG_NETDEVICES=y
+CONFIG_BONDING=m
+CONFIG_DUMMY=m
+CONFIG_WIREGUARD=m
+CONFIG_IFB=m
+CONFIG_MACVLAN=m
+CONFIG_IPVLAN=m
+CONFIG_VXLAN=m
+CONFIG_NETCONSOLE=m
+CONFIG_TUN=m
+CONFIG_VETH=m
+CONFIG_NET_VRF=m
+CONFIG_ENC28J60=m
+CONFIG_QCA7000_SPI=m
+CONFIG_QCA7000_UART=m
+CONFIG_WIZNET_W5100=m
+CONFIG_WIZNET_W5100_SPI=m
+CONFIG_MDIO_BITBANG=m
+CONFIG_PPP=m
+CONFIG_PPP_BSDCOMP=m
+CONFIG_PPP_DEFLATE=m
+CONFIG_PPP_FILTER=y
+CONFIG_PPP_MPPE=m
+CONFIG_PPP_MULTILINK=y
+CONFIG_PPPOATM=m
+CONFIG_PPPOE=m
+CONFIG_PPPOL2TP=m
+CONFIG_PPP_ASYNC=m
+CONFIG_PPP_SYNC_TTY=m
+CONFIG_SLIP=m
+CONFIG_SLIP_COMPRESSED=y
+CONFIG_SLIP_SMART=y
+CONFIG_USB_CATC=m
+CONFIG_USB_KAWETH=m
+CONFIG_USB_PEGASUS=m
+CONFIG_USB_RTL8150=m
+CONFIG_USB_RTL8152=m
+CONFIG_USB_LAN78XX=y
+CONFIG_USB_USBNET=y
+CONFIG_USB_NET_AX8817X=m
+CONFIG_USB_NET_AX88179_178A=m
+CONFIG_USB_NET_CDCETHER=m
+CONFIG_USB_NET_CDC_EEM=m
+CONFIG_USB_NET_CDC_NCM=m
+CONFIG_USB_NET_HUAWEI_CDC_NCM=m
+CONFIG_USB_NET_CDC_MBIM=m
+CONFIG_USB_NET_DM9601=m
+CONFIG_USB_NET_SR9700=m
+CONFIG_USB_NET_SR9800=m
+CONFIG_USB_NET_SMSC75XX=m
+CONFIG_USB_NET_SMSC95XX=y
+CONFIG_USB_NET_GL620A=m
+CONFIG_USB_NET_NET1080=m
+CONFIG_USB_NET_PLUSB=m
+CONFIG_USB_NET_MCS7830=m
+CONFIG_USB_NET_CDC_SUBSET=m
+CONFIG_USB_ALI_M5632=y
+CONFIG_USB_AN2720=y
+CONFIG_USB_EPSON2888=y
+CONFIG_USB_KC2190=y
+CONFIG_USB_NET_ZAURUS=m
+CONFIG_USB_NET_CX82310_ETH=m
+CONFIG_USB_NET_KALMIA=m
+CONFIG_USB_NET_QMI_WWAN=m
+CONFIG_USB_HSO=m
+CONFIG_USB_NET_INT51X1=m
+CONFIG_USB_IPHETH=m
+CONFIG_USB_SIERRA_NET=m
+CONFIG_USB_VL600=m
+CONFIG_USB_NET_AQC111=m
+CONFIG_ATH9K=m
+CONFIG_ATH9K_HTC=m
+CONFIG_CARL9170=m
+CONFIG_ATH6KL=m
+CONFIG_ATH6KL_USB=m
+CONFIG_AR5523=m
+CONFIG_AT76C50X_USB=m
+CONFIG_B43=m
+# CONFIG_B43_PHY_N is not set
+CONFIG_B43LEGACY=m
+CONFIG_BRCMFMAC=m
+CONFIG_BRCMFMAC_USB=y
+CONFIG_BRCMDBG=y
+CONFIG_HOSTAP=m
+CONFIG_P54_COMMON=m
+CONFIG_P54_USB=m
+CONFIG_LIBERTAS=m
+CONFIG_LIBERTAS_USB=m
+CONFIG_LIBERTAS_SDIO=m
+CONFIG_LIBERTAS_THINFIRM=m
+CONFIG_LIBERTAS_THINFIRM_USB=m
+CONFIG_MWIFIEX=m
+CONFIG_MWIFIEX_SDIO=m
+CONFIG_MT7601U=m
+CONFIG_MT76x0U=m
+CONFIG_MT76x2U=m
+CONFIG_RT2X00=m
+CONFIG_RT2500USB=m
+CONFIG_RT73USB=m
+CONFIG_RT2800USB=m
+CONFIG_RT2800USB_RT3573=y
+CONFIG_RT2800USB_RT53XX=y
+CONFIG_RT2800USB_RT55XX=y
+CONFIG_RT2800USB_UNKNOWN=y
+CONFIG_RTL8187=m
+CONFIG_RTL8192CU=m
+CONFIG_RTL8XXXU=m
+CONFIG_USB_ZD1201=m
+CONFIG_ZD1211RW=m
+CONFIG_MAC80211_HWSIM=m
+CONFIG_USB_NET_RNDIS_WLAN=m
+CONFIG_WIMAX_I2400M_USB=m
+CONFIG_IEEE802154_AT86RF230=m
+CONFIG_IEEE802154_MRF24J40=m
+CONFIG_IEEE802154_CC2520=m
+CONFIG_INPUT_MOUSEDEV=y
+CONFIG_INPUT_JOYDEV=m
+CONFIG_INPUT_EVDEV=y
+# CONFIG_KEYBOARD_ATKBD is not set
+CONFIG_KEYBOARD_GPIO=m
+CONFIG_KEYBOARD_MATRIX=m
+CONFIG_KEYBOARD_CAP11XX=m
+# CONFIG_INPUT_MOUSE is not set
+CONFIG_INPUT_JOYSTICK=y
+CONFIG_JOYSTICK_IFORCE=m
+CONFIG_JOYSTICK_IFORCE_USB=m
+CONFIG_JOYSTICK_XPAD=m
+CONFIG_JOYSTICK_XPAD_FF=y
+CONFIG_JOYSTICK_XPAD_LEDS=y
+CONFIG_JOYSTICK_PSXPAD_SPI=m
+CONFIG_JOYSTICK_PSXPAD_SPI_FF=y
+CONFIG_JOYSTICK_RPISENSE=m
+CONFIG_INPUT_TOUCHSCREEN=y
+CONFIG_TOUCHSCREEN_ADS7846=m
+CONFIG_TOUCHSCREEN_EGALAX=m
+CONFIG_TOUCHSCREEN_EXC3000=m
+CONFIG_TOUCHSCREEN_GOODIX=m
+CONFIG_TOUCHSCREEN_ILI210X=m
+CONFIG_TOUCHSCREEN_EDT_FT5X06=m
+CONFIG_TOUCHSCREEN_RASPBERRYPI_FW=m
+CONFIG_TOUCHSCREEN_USB_COMPOSITE=m
+CONFIG_TOUCHSCREEN_STMPE=m
+CONFIG_INPUT_MISC=y
+CONFIG_INPUT_AD714X=m
+CONFIG_INPUT_ATI_REMOTE2=m
+CONFIG_INPUT_KEYSPAN_REMOTE=m
+CONFIG_INPUT_POWERMATE=m
+CONFIG_INPUT_YEALINK=m
+CONFIG_INPUT_CM109=m
+CONFIG_INPUT_UINPUT=m
+CONFIG_INPUT_GPIO_ROTARY_ENCODER=m
+CONFIG_INPUT_ADXL34X=m
+CONFIG_INPUT_CMA3000=m
+CONFIG_SERIO=m
+CONFIG_SERIO_RAW=m
+CONFIG_GAMEPORT=m
+CONFIG_GAMEPORT_NS558=m
+CONFIG_GAMEPORT_L4=m
+CONFIG_BRCM_CHAR_DRIVERS=y
+CONFIG_BCM_VCIO=y
+CONFIG_BCM2835_DEVGPIOMEM=y
+# CONFIG_LEGACY_PTYS is not set
+CONFIG_SERIAL_8250=y
+# CONFIG_SERIAL_8250_DEPRECATED_OPTIONS is not set
+CONFIG_SERIAL_8250_CONSOLE=y
+# CONFIG_SERIAL_8250_DMA is not set
+CONFIG_SERIAL_8250_NR_UARTS=1
+CONFIG_SERIAL_8250_RUNTIME_UARTS=0
+CONFIG_SERIAL_8250_EXTENDED=y
+CONFIG_SERIAL_8250_SHARE_IRQ=y
+CONFIG_SERIAL_8250_BCM2835AUX=y
+CONFIG_SERIAL_OF_PLATFORM=y
+CONFIG_SERIAL_AMBA_PL011=y
+CONFIG_SERIAL_AMBA_PL011_CONSOLE=y
+CONFIG_SERIAL_SC16IS7XX=m
+CONFIG_SERIAL_SC16IS7XX_SPI=y
+CONFIG_SERIAL_DEV_BUS=y
+CONFIG_TTY_PRINTK=y
+CONFIG_HW_RANDOM=y
+CONFIG_RAW_DRIVER=y
+CONFIG_TCG_TPM=m
+CONFIG_TCG_TIS_SPI=m
+CONFIG_I2C=y
+CONFIG_I2C_CHARDEV=m
+CONFIG_I2C_MUX_GPMUX=m
+CONFIG_I2C_MUX_PCA954x=m
+CONFIG_I2C_MUX_PINCTRL=m
+CONFIG_I2C_BCM2708=m
+CONFIG_I2C_BCM2835=m
+# CONFIG_I2C_BRCMSTB is not set
+CONFIG_I2C_GPIO=m
+CONFIG_I2C_ROBOTFUZZ_OSIF=m
+CONFIG_I2C_TINY_USB=m
+CONFIG_SPI=y
+CONFIG_SPI_BCM2835=m
+CONFIG_SPI_BCM2835AUX=m
+CONFIG_SPI_GPIO=m
+CONFIG_SPI_SPIDEV=m
+CONFIG_SPI_SLAVE=y
+CONFIG_PPS=m
+CONFIG_PPS_CLIENT_LDISC=m
+CONFIG_PPS_CLIENT_GPIO=m
+CONFIG_PINCTRL_MCP23S08=m
+CONFIG_GPIO_SYSFS=y
+CONFIG_GPIO_BCM_VIRT=y
+CONFIG_GPIO_PCA953X=m
+CONFIG_GPIO_PCA953X_IRQ=y
+CONFIG_GPIO_PCF857X=m
+CONFIG_GPIO_ARIZONA=m
+CONFIG_GPIO_FSM=m
+CONFIG_GPIO_STMPE=y
+CONFIG_GPIO_MOCKUP=m
+CONFIG_W1=m
+CONFIG_W1_MASTER_DS2490=m
+CONFIG_W1_MASTER_DS2482=m
+CONFIG_W1_MASTER_DS1WM=m
+CONFIG_W1_MASTER_GPIO=m
+CONFIG_W1_SLAVE_THERM=m
+CONFIG_W1_SLAVE_SMEM=m
+CONFIG_W1_SLAVE_DS2408=m
+CONFIG_W1_SLAVE_DS2413=m
+CONFIG_W1_SLAVE_DS2406=m
+CONFIG_W1_SLAVE_DS2423=m
+CONFIG_W1_SLAVE_DS2431=m
+CONFIG_W1_SLAVE_DS2433=m
+CONFIG_W1_SLAVE_DS2438=m
+CONFIG_W1_SLAVE_DS2780=m
+CONFIG_W1_SLAVE_DS2781=m
+CONFIG_W1_SLAVE_DS28E04=m
+CONFIG_W1_SLAVE_DS28E17=m
+CONFIG_POWER_RESET=y
+CONFIG_POWER_RESET_GPIO=y
+CONFIG_RPI_POE_POWER=m
+CONFIG_BATTERY_DS2760=m
+CONFIG_BATTERY_MAX17040=m
+CONFIG_BATTERY_GAUGE_LTC2941=m
+CONFIG_SENSORS_DS1621=m
+CONFIG_SENSORS_GPIO_FAN=m
+CONFIG_SENSORS_IIO_HWMON=m
+CONFIG_SENSORS_JC42=m
+CONFIG_SENSORS_LM75=m
+CONFIG_SENSORS_RASPBERRYPI_HWMON=m
+CONFIG_SENSORS_RPI_POE_FAN=m
+CONFIG_SENSORS_SHT21=m
+CONFIG_SENSORS_SHT3x=m
+CONFIG_SENSORS_SHTC1=m
+CONFIG_SENSORS_INA2XX=m
+CONFIG_SENSORS_TMP102=m
+CONFIG_THERMAL=y
+CONFIG_BCM2835_THERMAL=y
+CONFIG_WATCHDOG=y
+CONFIG_GPIO_WATCHDOG=m
+CONFIG_BCM2835_WDT=y
+CONFIG_MFD_STMPE=y
+CONFIG_STMPE_SPI=y
+CONFIG_MFD_ARIZONA_I2C=m
+CONFIG_MFD_ARIZONA_SPI=m
+CONFIG_MFD_WM5102=y
+CONFIG_REGULATOR=y
+CONFIG_REGULATOR_FIXED_VOLTAGE=m
+CONFIG_REGULATOR_ARIZONA_LDO1=m
+CONFIG_REGULATOR_ARIZONA_MICSUPP=m
+CONFIG_RC_CORE=y
+CONFIG_LIRC=y
+CONFIG_BPF_LIRC_MODE2=y
+CONFIG_RC_DECODERS=y
+CONFIG_IR_NEC_DECODER=m
+CONFIG_IR_RC5_DECODER=m
+CONFIG_IR_RC6_DECODER=m
+CONFIG_IR_JVC_DECODER=m
+CONFIG_IR_SONY_DECODER=m
+CONFIG_IR_SANYO_DECODER=m
+CONFIG_IR_SHARP_DECODER=m
+CONFIG_IR_MCE_KBD_DECODER=m
+CONFIG_IR_XMP_DECODER=m
+CONFIG_IR_IMON_DECODER=m
+CONFIG_RC_DEVICES=y
+CONFIG_RC_ATI_REMOTE=m
+CONFIG_IR_IMON=m
+CONFIG_IR_MCEUSB=m
+CONFIG_IR_REDRAT3=m
+CONFIG_IR_STREAMZAP=m
+CONFIG_IR_IGUANA=m
+CONFIG_IR_TTUSBIR=m
+CONFIG_RC_LOOPBACK=m
+CONFIG_IR_GPIO_CIR=m
+CONFIG_IR_GPIO_TX=m
+CONFIG_IR_PWM_TX=m
+CONFIG_IR_TOY=m
+CONFIG_MEDIA_CEC_RC=y
+CONFIG_MEDIA_SUPPORT=m
+CONFIG_MEDIA_USB_SUPPORT=y
+CONFIG_USB_VIDEO_CLASS=m
+CONFIG_USB_M5602=m
+CONFIG_USB_STV06XX=m
+CONFIG_USB_GL860=m
+CONFIG_USB_GSPCA_BENQ=m
+CONFIG_USB_GSPCA_CONEX=m
+CONFIG_USB_GSPCA_CPIA1=m
+CONFIG_USB_GSPCA_DTCS033=m
+CONFIG_USB_GSPCA_ETOMS=m
+CONFIG_USB_GSPCA_FINEPIX=m
+CONFIG_USB_GSPCA_JEILINJ=m
+CONFIG_USB_GSPCA_JL2005BCD=m
+CONFIG_USB_GSPCA_KINECT=m
+CONFIG_USB_GSPCA_KONICA=m
+CONFIG_USB_GSPCA_MARS=m
+CONFIG_USB_GSPCA_MR97310A=m
+CONFIG_USB_GSPCA_NW80X=m
+CONFIG_USB_GSPCA_OV519=m
+CONFIG_USB_GSPCA_OV534=m
+CONFIG_USB_GSPCA_OV534_9=m
+CONFIG_USB_GSPCA_PAC207=m
+CONFIG_USB_GSPCA_PAC7302=m
+CONFIG_USB_GSPCA_PAC7311=m
+CONFIG_USB_GSPCA_SE401=m
+CONFIG_USB_GSPCA_SN9C2028=m
+CONFIG_USB_GSPCA_SN9C20X=m
+CONFIG_USB_GSPCA_SONIXB=m
+CONFIG_USB_GSPCA_SONIXJ=m
+CONFIG_USB_GSPCA_SPCA500=m
+CONFIG_USB_GSPCA_SPCA501=m
+CONFIG_USB_GSPCA_SPCA505=m
+CONFIG_USB_GSPCA_SPCA506=m
+CONFIG_USB_GSPCA_SPCA508=m
+CONFIG_USB_GSPCA_SPCA561=m
+CONFIG_USB_GSPCA_SPCA1528=m
+CONFIG_USB_GSPCA_SQ905=m
+CONFIG_USB_GSPCA_SQ905C=m
+CONFIG_USB_GSPCA_SQ930X=m
+CONFIG_USB_GSPCA_STK014=m
+CONFIG_USB_GSPCA_STK1135=m
+CONFIG_USB_GSPCA_STV0680=m
+CONFIG_USB_GSPCA_SUNPLUS=m
+CONFIG_USB_GSPCA_T613=m
+CONFIG_USB_GSPCA_TOPRO=m
+CONFIG_USB_GSPCA_TV8532=m
+CONFIG_USB_GSPCA_VC032X=m
+CONFIG_USB_GSPCA_VICAM=m
+CONFIG_USB_GSPCA_XIRLINK_CIT=m
+CONFIG_USB_GSPCA_ZC3XX=m
+CONFIG_USB_PWC=m
+CONFIG_VIDEO_CPIA2=m
+CONFIG_USB_ZR364XX=m
+CONFIG_USB_STKWEBCAM=m
+CONFIG_USB_S2255=m
+CONFIG_VIDEO_USBTV=m
+CONFIG_VIDEO_PVRUSB2=m
+CONFIG_VIDEO_HDPVR=m
+CONFIG_VIDEO_STK1160_COMMON=m
+CONFIG_VIDEO_GO7007=m
+CONFIG_VIDEO_GO7007_USB=m
+CONFIG_VIDEO_GO7007_USB_S2250_BOARD=m
+CONFIG_VIDEO_AU0828=m
+CONFIG_VIDEO_AU0828_RC=y
+CONFIG_VIDEO_CX231XX=m
+CONFIG_VIDEO_CX231XX_ALSA=m
+CONFIG_VIDEO_CX231XX_DVB=m
+CONFIG_VIDEO_TM6000=m
+CONFIG_VIDEO_TM6000_ALSA=m
+CONFIG_VIDEO_TM6000_DVB=m
+CONFIG_DVB_USB=m
+CONFIG_DVB_USB_A800=m
+CONFIG_DVB_USB_DIBUSB_MB=m
+CONFIG_DVB_USB_DIBUSB_MB_FAULTY=y
+CONFIG_DVB_USB_DIBUSB_MC=m
+CONFIG_DVB_USB_DIB0700=m
+CONFIG_DVB_USB_UMT_010=m
+CONFIG_DVB_USB_CXUSB=m
+CONFIG_DVB_USB_M920X=m
+CONFIG_DVB_USB_DIGITV=m
+CONFIG_DVB_USB_VP7045=m
+CONFIG_DVB_USB_VP702X=m
+CONFIG_DVB_USB_GP8PSK=m
+CONFIG_DVB_USB_NOVA_T_USB2=m
+CONFIG_DVB_USB_TTUSB2=m
+CONFIG_DVB_USB_DTT200U=m
+CONFIG_DVB_USB_OPERA1=m
+CONFIG_DVB_USB_AF9005=m
+CONFIG_DVB_USB_AF9005_REMOTE=m
+CONFIG_DVB_USB_PCTV452E=m
+CONFIG_DVB_USB_DW2102=m
+CONFIG_DVB_USB_CINERGY_T2=m
+CONFIG_DVB_USB_DTV5100=m
+CONFIG_DVB_USB_AZ6027=m
+CONFIG_DVB_USB_TECHNISAT_USB2=m
+CONFIG_DVB_USB_V2=m
+CONFIG_DVB_USB_AF9015=m
+CONFIG_DVB_USB_AF9035=m
+CONFIG_DVB_USB_ANYSEE=m
+CONFIG_DVB_USB_AU6610=m
+CONFIG_DVB_USB_AZ6007=m
+CONFIG_DVB_USB_CE6230=m
+CONFIG_DVB_USB_EC168=m
+CONFIG_DVB_USB_GL861=m
+CONFIG_DVB_USB_LME2510=m
+CONFIG_DVB_USB_MXL111SF=m
+CONFIG_DVB_USB_RTL28XXU=m
+CONFIG_DVB_USB_DVBSKY=m
+CONFIG_SMS_USB_DRV=m
+CONFIG_DVB_B2C2_FLEXCOP_USB=m
+CONFIG_DVB_AS102=m
+CONFIG_VIDEO_EM28XX=m
+CONFIG_VIDEO_EM28XX_V4L2=m
+CONFIG_VIDEO_EM28XX_ALSA=m
+CONFIG_VIDEO_EM28XX_DVB=m
+CONFIG_RADIO_SI470X=m
+CONFIG_USB_SI470X=m
+CONFIG_I2C_SI470X=m
+CONFIG_RADIO_SI4713=m
+CONFIG_I2C_SI4713=m
+CONFIG_USB_MR800=m
+CONFIG_USB_DSBR=m
+CONFIG_RADIO_SHARK=m
+CONFIG_RADIO_SHARK2=m
+CONFIG_USB_KEENE=m
+CONFIG_USB_MA901=m
+CONFIG_RADIO_TEA5764=m
+CONFIG_RADIO_SAA7706H=m
+CONFIG_RADIO_TEF6862=m
+CONFIG_RADIO_WL1273=m
+CONFIG_RADIO_WL128X=m
+CONFIG_V4L_PLATFORM_DRIVERS=y
+CONFIG_VIDEO_BCM2835_UNICAM=m
+CONFIG_VIDEO_UDA1342=m
+CONFIG_VIDEO_SONY_BTF_MPX=m
+CONFIG_VIDEO_ADV7180=m
+CONFIG_VIDEO_TC358743=m
+CONFIG_VIDEO_TVP5150=m
+CONFIG_VIDEO_TW2804=m
+CONFIG_VIDEO_TW9903=m
+CONFIG_VIDEO_TW9906=m
+CONFIG_VIDEO_IMX219=m
+CONFIG_VIDEO_IMX290=m
+CONFIG_VIDEO_IMX477=m
+CONFIG_VIDEO_OV5647=m
+CONFIG_VIDEO_OV7251=m
+CONFIG_VIDEO_OV7640=m
+CONFIG_VIDEO_OV9281=m
+CONFIG_VIDEO_IRS1125=m
+CONFIG_VIDEO_MT9V011=m
+CONFIG_DRM=m
+CONFIG_DRM_LOAD_EDID_FIRMWARE=y
+CONFIG_DRM_UDL=m
+CONFIG_DRM_PANEL_SIMPLE=m
+CONFIG_DRM_PANEL_JDI_LT070ME05000=m
+CONFIG_DRM_PANEL_RASPBERRYPI_TOUCHSCREEN=m
+CONFIG_DRM_DISPLAY_CONNECTOR=m
+CONFIG_DRM_SIMPLE_BRIDGE=m
+CONFIG_DRM_VC4=m
+CONFIG_DRM_VC4_HDMI_CEC=y
+CONFIG_TINYDRM_ILI9225=m
+CONFIG_TINYDRM_ILI9341=m
+CONFIG_TINYDRM_MI0283QT=m
+CONFIG_TINYDRM_REPAPER=m
+CONFIG_TINYDRM_ST7586=m
+CONFIG_TINYDRM_ST7735R=m
+CONFIG_DRM_GUD=m
+CONFIG_FB=y
+CONFIG_FB_BCM2708=y
+CONFIG_FB_UDL=m
+CONFIG_FB_SIMPLE=y
+CONFIG_FB_SSD1307=m
+CONFIG_FB_RPISENSE=m
+CONFIG_BACKLIGHT_RPI=m
+CONFIG_BACKLIGHT_GPIO=m
+CONFIG_FRAMEBUFFER_CONSOLE=y
+CONFIG_FRAMEBUFFER_CONSOLE_ROTATION=y
+CONFIG_LOGO=y
+# CONFIG_LOGO_LINUX_MONO is not set
+# CONFIG_LOGO_LINUX_VGA16 is not set
+CONFIG_SOUND=y
+CONFIG_SND=m
+CONFIG_SND_OSSEMUL=y
+CONFIG_SND_PCM_OSS=m
+CONFIG_SND_HRTIMER=m
+CONFIG_SND_SEQUENCER=m
+CONFIG_SND_SEQ_DUMMY=m
+CONFIG_SND_DUMMY=m
+CONFIG_SND_ALOOP=m
+CONFIG_SND_VIRMIDI=m
+CONFIG_SND_MTPAV=m
+CONFIG_SND_SERIAL_U16550=m
+CONFIG_SND_MPU401=m
+CONFIG_SND_USB_AUDIO=m
+CONFIG_SND_USB_UA101=m
+CONFIG_SND_USB_CAIAQ=m
+CONFIG_SND_USB_CAIAQ_INPUT=y
+CONFIG_SND_USB_6FIRE=m
+CONFIG_SND_USB_HIFACE=m
+CONFIG_SND_SOC=m
+CONFIG_SND_BCM2835_SOC_I2S=m
+CONFIG_SND_BCM2708_SOC_GOOGLEVOICEHAT_SOUNDCARD=m
+CONFIG_SND_BCM2708_SOC_HIFIBERRY_DAC=m
+CONFIG_SND_BCM2708_SOC_HIFIBERRY_DACPLUS=m
+CONFIG_SND_BCM2708_SOC_HIFIBERRY_DACPLUSHD=m
+CONFIG_SND_BCM2708_SOC_HIFIBERRY_DACPLUSADC=m
+CONFIG_SND_BCM2708_SOC_HIFIBERRY_DACPLUSADCPRO=m
+CONFIG_SND_BCM2708_SOC_HIFIBERRY_DACPLUSDSP=m
+CONFIG_SND_BCM2708_SOC_HIFIBERRY_DIGI=m
+CONFIG_SND_BCM2708_SOC_HIFIBERRY_AMP=m
+CONFIG_SND_BCM2708_SOC_PIFI_40=m
+CONFIG_SND_BCM2708_SOC_RPI_CIRRUS=m
+CONFIG_SND_BCM2708_SOC_RPI_DAC=m
+CONFIG_SND_BCM2708_SOC_RPI_PROTO=m
+CONFIG_SND_BCM2708_SOC_JUSTBOOM_BOTH=m
+CONFIG_SND_BCM2708_SOC_JUSTBOOM_DAC=m
+CONFIG_SND_BCM2708_SOC_JUSTBOOM_DIGI=m
+CONFIG_SND_BCM2708_SOC_IQAUDIO_CODEC=m
+CONFIG_SND_BCM2708_SOC_IQAUDIO_DAC=m
+CONFIG_SND_BCM2708_SOC_IQAUDIO_DIGI=m
+CONFIG_SND_BCM2708_SOC_I_SABRE_Q2M=m
+CONFIG_SND_BCM2708_SOC_ADAU1977_ADC=m
+CONFIG_SND_AUDIOINJECTOR_PI_SOUNDCARD=m
+CONFIG_SND_AUDIOINJECTOR_OCTO_SOUNDCARD=m
+CONFIG_SND_AUDIOINJECTOR_ISOLATED_SOUNDCARD=m
+CONFIG_SND_AUDIOSENSE_PI=m
+CONFIG_SND_DIGIDAC1_SOUNDCARD=m
+CONFIG_SND_BCM2708_SOC_DIONAUDIO_LOCO=m
+CONFIG_SND_BCM2708_SOC_DIONAUDIO_LOCO_V2=m
+CONFIG_SND_BCM2708_SOC_ALLO_PIANO_DAC=m
+CONFIG_SND_BCM2708_SOC_ALLO_PIANO_DAC_PLUS=m
+CONFIG_SND_BCM2708_SOC_ALLO_BOSS_DAC=m
+CONFIG_SND_BCM2708_SOC_ALLO_BOSS2_DAC=m
+CONFIG_SND_BCM2708_SOC_ALLO_DIGIONE=m
+CONFIG_SND_BCM2708_SOC_ALLO_KATANA_DAC=m
+CONFIG_SND_BCM2708_SOC_FE_PI_AUDIO=m
+CONFIG_SND_PISOUND=m
+CONFIG_SND_SOC_AD193X_SPI=m
+CONFIG_SND_SOC_AD193X_I2C=m
+CONFIG_SND_SOC_ADAU1701=m
+CONFIG_SND_SOC_ADAU7002=m
+CONFIG_SND_SOC_AK4554=m
+CONFIG_SND_SOC_CS4265=m
+CONFIG_SND_SOC_ICS43432=m
+CONFIG_SND_SOC_MA120X0P=m
+CONFIG_SND_SOC_MAX98357A=m
+CONFIG_SND_SOC_SPDIF=m
+CONFIG_SND_SOC_WM8804_I2C=m
+CONFIG_SND_SOC_WM8960=m
+CONFIG_SND_SIMPLE_CARD=m
+CONFIG_HID_BATTERY_STRENGTH=y
+CONFIG_HIDRAW=y
+CONFIG_UHID=m
+CONFIG_HID_A4TECH=m
+CONFIG_HID_ACRUX=m
+CONFIG_HID_APPLE=m
+CONFIG_HID_ASUS=m
+CONFIG_HID_BELKIN=m
+CONFIG_HID_BETOP_FF=m
+CONFIG_HID_BIGBEN_FF=m
+CONFIG_HID_CHERRY=m
+CONFIG_HID_CHICONY=m
+CONFIG_HID_CYPRESS=m
+CONFIG_HID_DRAGONRISE=m
+CONFIG_HID_EMS_FF=m
+CONFIG_HID_ELECOM=m
+CONFIG_HID_ELO=m
+CONFIG_HID_EZKEY=m
+CONFIG_HID_GEMBIRD=m
+CONFIG_HID_HOLTEK=m
+CONFIG_HID_KEYTOUCH=m
+CONFIG_HID_KYE=m
+CONFIG_HID_UCLOGIC=m
+CONFIG_HID_WALTOP=m
+CONFIG_HID_GYRATION=m
+CONFIG_HID_TWINHAN=m
+CONFIG_HID_KENSINGTON=m
+CONFIG_HID_LCPOWER=m
+CONFIG_HID_LOGITECH=m
+CONFIG_HID_LOGITECH_DJ=m
+CONFIG_LOGITECH_FF=y
+CONFIG_LOGIRUMBLEPAD2_FF=y
+CONFIG_LOGIG940_FF=y
+CONFIG_HID_MAGICMOUSE=m
+CONFIG_HID_MICROSOFT=m
+CONFIG_HID_MONTEREY=m
+CONFIG_HID_MULTITOUCH=m
+CONFIG_HID_NTRIG=m
+CONFIG_HID_ORTEK=m
+CONFIG_HID_PANTHERLORD=m
+CONFIG_HID_PETALYNX=m
+CONFIG_HID_PICOLCD=m
+CONFIG_HID_ROCCAT=m
+CONFIG_HID_SAMSUNG=m
+CONFIG_HID_SONY=m
+CONFIG_SONY_FF=y
+CONFIG_HID_SPEEDLINK=m
+CONFIG_HID_STEAM=m
+CONFIG_HID_SUNPLUS=m
+CONFIG_HID_GREENASIA=m
+CONFIG_HID_SMARTJOYPLUS=m
+CONFIG_HID_TOPSEED=m
+CONFIG_HID_THINGM=m
+CONFIG_HID_THRUSTMASTER=m
+CONFIG_HID_WACOM=m
+CONFIG_HID_WIIMOTE=m
+CONFIG_HID_XINMO=m
+CONFIG_HID_ZEROPLUS=m
+CONFIG_HID_ZYDACRON=m
+CONFIG_HID_PID=y
+CONFIG_USB_HIDDEV=y
+CONFIG_I2C_HID=m
+CONFIG_USB=y
+CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
+CONFIG_USB_MON=m
+CONFIG_USB_DWCOTG=y
+CONFIG_USB_PRINTER=m
+CONFIG_USB_TMC=m
+CONFIG_USB_STORAGE=y
+CONFIG_USB_STORAGE_REALTEK=m
+CONFIG_USB_STORAGE_DATAFAB=m
+CONFIG_USB_STORAGE_FREECOM=m
+CONFIG_USB_STORAGE_ISD200=m
+CONFIG_USB_STORAGE_USBAT=m
+CONFIG_USB_STORAGE_SDDR09=m
+CONFIG_USB_STORAGE_SDDR55=m
+CONFIG_USB_STORAGE_JUMPSHOT=m
+CONFIG_USB_STORAGE_ALAUDA=m
+CONFIG_USB_STORAGE_ONETOUCH=m
+CONFIG_USB_STORAGE_KARMA=m
+CONFIG_USB_STORAGE_CYPRESS_ATACB=m
+CONFIG_USB_STORAGE_ENE_UB6250=m
+CONFIG_USB_UAS=m
+CONFIG_USB_MDC800=m
+CONFIG_USB_MICROTEK=m
+CONFIG_USBIP_CORE=m
+CONFIG_USBIP_VHCI_HCD=m
+CONFIG_USBIP_HOST=m
+CONFIG_USBIP_VUDC=m
+CONFIG_USB_DWC2=m
+CONFIG_USB_SERIAL=m
+CONFIG_USB_SERIAL_GENERIC=y
+CONFIG_USB_SERIAL_AIRCABLE=m
+CONFIG_USB_SERIAL_ARK3116=m
+CONFIG_USB_SERIAL_BELKIN=m
+CONFIG_USB_SERIAL_CH341=m
+CONFIG_USB_SERIAL_WHITEHEAT=m
+CONFIG_USB_SERIAL_DIGI_ACCELEPORT=m
+CONFIG_USB_SERIAL_CP210X=m
+CONFIG_USB_SERIAL_CYPRESS_M8=m
+CONFIG_USB_SERIAL_EMPEG=m
+CONFIG_USB_SERIAL_FTDI_SIO=m
+CONFIG_USB_SERIAL_VISOR=m
+CONFIG_USB_SERIAL_IPAQ=m
+CONFIG_USB_SERIAL_IR=m
+CONFIG_USB_SERIAL_EDGEPORT=m
+CONFIG_USB_SERIAL_EDGEPORT_TI=m
+CONFIG_USB_SERIAL_F81232=m
+CONFIG_USB_SERIAL_GARMIN=m
+CONFIG_USB_SERIAL_IPW=m
+CONFIG_USB_SERIAL_IUU=m
+CONFIG_USB_SERIAL_KEYSPAN_PDA=m
+CONFIG_USB_SERIAL_KEYSPAN=m
+CONFIG_USB_SERIAL_KLSI=m
+CONFIG_USB_SERIAL_KOBIL_SCT=m
+CONFIG_USB_SERIAL_MCT_U232=m
+CONFIG_USB_SERIAL_METRO=m
+CONFIG_USB_SERIAL_MOS7720=m
+CONFIG_USB_SERIAL_MOS7840=m
+CONFIG_USB_SERIAL_NAVMAN=m
+CONFIG_USB_SERIAL_PL2303=m
+CONFIG_USB_SERIAL_OTI6858=m
+CONFIG_USB_SERIAL_QCAUX=m
+CONFIG_USB_SERIAL_QUALCOMM=m
+CONFIG_USB_SERIAL_SPCP8X5=m
+CONFIG_USB_SERIAL_SAFE=m
+CONFIG_USB_SERIAL_SIERRAWIRELESS=m
+CONFIG_USB_SERIAL_SYMBOL=m
+CONFIG_USB_SERIAL_TI=m
+CONFIG_USB_SERIAL_CYBERJACK=m
+CONFIG_USB_SERIAL_XIRCOM=m
+CONFIG_USB_SERIAL_OPTION=m
+CONFIG_USB_SERIAL_OMNINET=m
+CONFIG_USB_SERIAL_OPTICON=m
+CONFIG_USB_SERIAL_XSENS_MT=m
+CONFIG_USB_SERIAL_WISHBONE=m
+CONFIG_USB_SERIAL_SSU100=m
+CONFIG_USB_SERIAL_QT2=m
+CONFIG_USB_SERIAL_DEBUG=m
+CONFIG_USB_EMI62=m
+CONFIG_USB_EMI26=m
+CONFIG_USB_ADUTUX=m
+CONFIG_USB_SEVSEG=m
+CONFIG_USB_LEGOTOWER=m
+CONFIG_USB_LCD=m
+CONFIG_USB_CYPRESS_CY7C63=m
+CONFIG_USB_CYTHERM=m
+CONFIG_USB_IDMOUSE=m
+CONFIG_USB_FTDI_ELAN=m
+CONFIG_USB_APPLEDISPLAY=m
+CONFIG_USB_LD=m
+CONFIG_USB_TRANCEVIBRATOR=m
+CONFIG_USB_IOWARRIOR=m
+CONFIG_USB_TEST=m
+CONFIG_USB_ISIGHTFW=m
+CONFIG_USB_YUREX=m
+CONFIG_USB_ATM=m
+CONFIG_USB_SPEEDTOUCH=m
+CONFIG_USB_CXACRU=m
+CONFIG_USB_UEAGLEATM=m
+CONFIG_USB_XUSBATM=m
+CONFIG_USB_GADGET=m
+CONFIG_USB_ZERO=m
+CONFIG_USB_AUDIO=m
+CONFIG_USB_ETH=m
+CONFIG_USB_GADGETFS=m
+CONFIG_USB_MASS_STORAGE=m
+CONFIG_USB_G_SERIAL=m
+CONFIG_USB_MIDI_GADGET=m
+CONFIG_USB_G_PRINTER=m
+CONFIG_USB_CDC_COMPOSITE=m
+CONFIG_USB_G_ACM_MS=m
+CONFIG_USB_G_MULTI=m
+CONFIG_USB_G_HID=m
+CONFIG_USB_G_WEBCAM=m
+CONFIG_MMC=y
+CONFIG_MMC_BLOCK_MINORS=32
+CONFIG_MMC_BCM2835_MMC=y
+CONFIG_MMC_BCM2835_DMA=y
+CONFIG_MMC_BCM2835_SDHOST=y
+CONFIG_MMC_SDHCI=y
+CONFIG_MMC_SDHCI_PLTFM=y
+CONFIG_MMC_SPI=m
+CONFIG_LEDS_CLASS=y
+CONFIG_LEDS_PCA9532=m
+CONFIG_LEDS_GPIO=y
+CONFIG_LEDS_PCA955X=m
+CONFIG_LEDS_PCA963X=m
+CONFIG_LEDS_IS31FL32XX=m
+CONFIG_LEDS_TRIGGER_TIMER=y
+CONFIG_LEDS_TRIGGER_ONESHOT=y
+CONFIG_LEDS_TRIGGER_HEARTBEAT=y
+CONFIG_LEDS_TRIGGER_BACKLIGHT=y
+CONFIG_LEDS_TRIGGER_CPU=y
+CONFIG_LEDS_TRIGGER_GPIO=y
+CONFIG_LEDS_TRIGGER_DEFAULT_ON=y
+CONFIG_LEDS_TRIGGER_TRANSIENT=m
+CONFIG_LEDS_TRIGGER_CAMERA=m
+CONFIG_LEDS_TRIGGER_INPUT=y
+CONFIG_LEDS_TRIGGER_PANIC=y
+CONFIG_LEDS_TRIGGER_NETDEV=m
+CONFIG_LEDS_TRIGGER_ACTPWR=y
+CONFIG_ACCESSIBILITY=y
+CONFIG_SPEAKUP=m
+CONFIG_SPEAKUP_SYNTH_SOFT=m
+CONFIG_RTC_CLASS=y
+CONFIG_RTC_DRV_ABX80X=m
+CONFIG_RTC_DRV_DS1307=m
+CONFIG_RTC_DRV_DS1374=m
+CONFIG_RTC_DRV_DS1672=m
+CONFIG_RTC_DRV_MAX6900=m
+CONFIG_RTC_DRV_RS5C372=m
+CONFIG_RTC_DRV_ISL1208=m
+CONFIG_RTC_DRV_ISL12022=m
+CONFIG_RTC_DRV_X1205=m
+CONFIG_RTC_DRV_PCF8523=m
+CONFIG_RTC_DRV_PCF85063=m
+CONFIG_RTC_DRV_PCF85363=m
+CONFIG_RTC_DRV_PCF8563=m
+CONFIG_RTC_DRV_PCF8583=m
+CONFIG_RTC_DRV_M41T80=m
+CONFIG_RTC_DRV_BQ32K=m
+CONFIG_RTC_DRV_S35390A=m
+CONFIG_RTC_DRV_FM3130=m
+CONFIG_RTC_DRV_RX8581=m
+CONFIG_RTC_DRV_RX8025=m
+CONFIG_RTC_DRV_EM3027=m
+CONFIG_RTC_DRV_RV3028=m
+CONFIG_RTC_DRV_SD3078=m
+CONFIG_RTC_DRV_M41T93=m
+CONFIG_RTC_DRV_M41T94=m
+CONFIG_RTC_DRV_DS1302=m
+CONFIG_RTC_DRV_DS1305=m
+CONFIG_RTC_DRV_DS1390=m
+CONFIG_RTC_DRV_R9701=m
+CONFIG_RTC_DRV_RX4581=m
+CONFIG_RTC_DRV_RS5C348=m
+CONFIG_RTC_DRV_MAX6902=m
+CONFIG_RTC_DRV_PCF2123=m
+CONFIG_RTC_DRV_DS3232=m
+CONFIG_RTC_DRV_PCF2127=m
+CONFIG_RTC_DRV_RV3029C2=m
+CONFIG_DMADEVICES=y
+CONFIG_DMA_BCM2835=y
+CONFIG_DMA_BCM2708=y
+CONFIG_DMABUF_HEAPS=y
+CONFIG_DMABUF_HEAPS_SYSTEM=y
+CONFIG_DMABUF_HEAPS_CMA=y
+CONFIG_AUXDISPLAY=y
+CONFIG_HD44780=m
+CONFIG_UIO=m
+CONFIG_UIO_PDRV_GENIRQ=m
+CONFIG_STAGING=y
+CONFIG_PRISM2_USB=m
+CONFIG_R8712U=m
+CONFIG_R8188EU=m
+CONFIG_VT6656=m
+CONFIG_STAGING_MEDIA=y
+CONFIG_FB_TFT=m
+CONFIG_FB_TFT_AGM1264K_FL=m
+CONFIG_FB_TFT_BD663474=m
+CONFIG_FB_TFT_HX8340BN=m
+CONFIG_FB_TFT_HX8347D=m
+CONFIG_FB_TFT_HX8353D=m
+CONFIG_FB_TFT_HX8357D=m
+CONFIG_FB_TFT_ILI9163=m
+CONFIG_FB_TFT_ILI9320=m
+CONFIG_FB_TFT_ILI9325=m
+CONFIG_FB_TFT_ILI9340=m
+CONFIG_FB_TFT_ILI9341=m
+CONFIG_FB_TFT_ILI9481=m
+CONFIG_FB_TFT_ILI9486=m
+CONFIG_FB_TFT_PCD8544=m
+CONFIG_FB_TFT_RA8875=m
+CONFIG_FB_TFT_S6D02A1=m
+CONFIG_FB_TFT_S6D1121=m
+CONFIG_FB_TFT_SH1106=m
+CONFIG_FB_TFT_SSD1289=m
+CONFIG_FB_TFT_SSD1306=m
+CONFIG_FB_TFT_SSD1331=m
+CONFIG_FB_TFT_SSD1351=m
+CONFIG_FB_TFT_ST7735R=m
+CONFIG_FB_TFT_ST7789V=m
+CONFIG_FB_TFT_TINYLCD=m
+CONFIG_FB_TFT_TLS8204=m
+CONFIG_FB_TFT_UC1701=m
+CONFIG_FB_TFT_UPD161704=m
+CONFIG_FB_TFT_WATTEROTT=m
+CONFIG_BCM2835_VCHIQ=y
+CONFIG_SND_BCM2835=m
+CONFIG_VIDEO_BCM2835=m
+CONFIG_VIDEO_CODEC_BCM2835=m
+CONFIG_VIDEO_ISP_BCM2835=m
+CONFIG_CLK_RASPBERRYPI=y
+CONFIG_MAILBOX=y
+CONFIG_BCM2835_MBOX=y
+# CONFIG_IOMMU_SUPPORT is not set
+CONFIG_RASPBERRYPI_POWER=y
+CONFIG_EXTCON=m
+CONFIG_EXTCON_ARIZONA=m
+CONFIG_IIO=m
+CONFIG_IIO_BUFFER_CB=m
+CONFIG_MCP320X=m
+CONFIG_MCP3422=m
+CONFIG_TI_ADS1015=m
+CONFIG_BME680=m
+CONFIG_CCS811=m
+CONFIG_SENSIRION_SGP30=m
+CONFIG_SPS30=m
+CONFIG_DHT11=m
+CONFIG_HDC100X=m
+CONFIG_HTU21=m
+CONFIG_INV_MPU6050_I2C=m
+CONFIG_APDS9960=m
+CONFIG_BH1750=m
+CONFIG_TSL4531=m
+CONFIG_VEML6070=m
+CONFIG_BMP280=m
+CONFIG_MAXIM_THERMOCOUPLE=m
+CONFIG_MAX31856=m
+CONFIG_PWM_BCM2835=m
+CONFIG_PWM_PCA9685=m
+CONFIG_RPI_AXIPERF=m
+CONFIG_EXT4_FS=y
+CONFIG_EXT4_FS_POSIX_ACL=y
+CONFIG_EXT4_FS_SECURITY=y
+CONFIG_REISERFS_FS=m
+CONFIG_REISERFS_FS_XATTR=y
+CONFIG_REISERFS_FS_POSIX_ACL=y
+CONFIG_REISERFS_FS_SECURITY=y
+CONFIG_JFS_FS=m
+CONFIG_JFS_POSIX_ACL=y
+CONFIG_JFS_SECURITY=y
+CONFIG_JFS_STATISTICS=y
+CONFIG_XFS_FS=m
+CONFIG_XFS_QUOTA=y
+CONFIG_XFS_POSIX_ACL=y
+CONFIG_XFS_RT=y
+CONFIG_GFS2_FS=m
+CONFIG_OCFS2_FS=m
+CONFIG_BTRFS_FS=m
+CONFIG_BTRFS_FS_POSIX_ACL=y
+CONFIG_NILFS2_FS=m
+CONFIG_F2FS_FS=y
+CONFIG_FS_ENCRYPTION=y
+CONFIG_FANOTIFY=y
+CONFIG_QFMT_V1=m
+CONFIG_QFMT_V2=m
+CONFIG_AUTOFS4_FS=y
+CONFIG_FUSE_FS=m
+CONFIG_CUSE=m
+CONFIG_OVERLAY_FS=m
+CONFIG_FSCACHE=y
+CONFIG_FSCACHE_STATS=y
+CONFIG_FSCACHE_HISTOGRAM=y
+CONFIG_CACHEFILES=y
+CONFIG_ISO9660_FS=m
+CONFIG_JOLIET=y
+CONFIG_ZISOFS=y
+CONFIG_UDF_FS=m
+CONFIG_MSDOS_FS=y
+CONFIG_VFAT_FS=y
+CONFIG_FAT_DEFAULT_IOCHARSET="ascii"
+CONFIG_EXFAT_FS=m
+CONFIG_NTFS_FS=m
+CONFIG_NTFS_RW=y
+CONFIG_TMPFS=y
+CONFIG_TMPFS_POSIX_ACL=y
+CONFIG_ECRYPT_FS=m
+CONFIG_HFS_FS=m
+CONFIG_HFSPLUS_FS=m
+CONFIG_JFFS2_FS=m
+CONFIG_JFFS2_SUMMARY=y
+CONFIG_UBIFS_FS=m
+CONFIG_SQUASHFS=m
+CONFIG_SQUASHFS_XATTR=y
+CONFIG_SQUASHFS_LZO=y
+CONFIG_SQUASHFS_XZ=y
+CONFIG_NFS_FS=y
+CONFIG_NFS_V3_ACL=y
+CONFIG_NFS_V4=y
+CONFIG_NFS_SWAP=y
+CONFIG_NFS_V4_1=y
+CONFIG_NFS_V4_2=y
+CONFIG_ROOT_NFS=y
+CONFIG_NFS_FSCACHE=y
+CONFIG_NFSD=m
+CONFIG_NFSD_V3_ACL=y
+CONFIG_NFSD_V4=y
+CONFIG_CIFS=m
+CONFIG_CIFS_WEAK_PW_HASH=y
+CONFIG_CIFS_UPCALL=y
+CONFIG_CIFS_XATTR=y
+CONFIG_CIFS_DFS_UPCALL=y
+CONFIG_CIFS_FSCACHE=y
+CONFIG_9P_FS=m
+CONFIG_9P_FS_POSIX_ACL=y
+CONFIG_NLS_DEFAULT="utf8"
+CONFIG_NLS_CODEPAGE_437=y
+CONFIG_NLS_CODEPAGE_737=m
+CONFIG_NLS_CODEPAGE_775=m
+CONFIG_NLS_CODEPAGE_850=m
+CONFIG_NLS_CODEPAGE_852=m
+CONFIG_NLS_CODEPAGE_855=m
+CONFIG_NLS_CODEPAGE_857=m
+CONFIG_NLS_CODEPAGE_860=m
+CONFIG_NLS_CODEPAGE_861=m
+CONFIG_NLS_CODEPAGE_862=m
+CONFIG_NLS_CODEPAGE_863=m
+CONFIG_NLS_CODEPAGE_864=m
+CONFIG_NLS_CODEPAGE_865=m
+CONFIG_NLS_CODEPAGE_866=m
+CONFIG_NLS_CODEPAGE_869=m
+CONFIG_NLS_CODEPAGE_936=m
+CONFIG_NLS_CODEPAGE_950=m
+CONFIG_NLS_CODEPAGE_932=m
+CONFIG_NLS_CODEPAGE_949=m
+CONFIG_NLS_CODEPAGE_874=m
+CONFIG_NLS_ISO8859_8=m
+CONFIG_NLS_CODEPAGE_1250=m
+CONFIG_NLS_CODEPAGE_1251=m
+CONFIG_NLS_ASCII=y
+CONFIG_NLS_ISO8859_1=m
+CONFIG_NLS_ISO8859_2=m
+CONFIG_NLS_ISO8859_3=m
+CONFIG_NLS_ISO8859_4=m
+CONFIG_NLS_ISO8859_5=m
+CONFIG_NLS_ISO8859_6=m
+CONFIG_NLS_ISO8859_7=m
+CONFIG_NLS_ISO8859_9=m
+CONFIG_NLS_ISO8859_13=m
+CONFIG_NLS_ISO8859_14=m
+CONFIG_NLS_ISO8859_15=m
+CONFIG_NLS_KOI8_R=m
+CONFIG_NLS_KOI8_U=m
+CONFIG_DLM=m
+CONFIG_SECURITY=y
+CONFIG_SECURITY_APPARMOR=y
+CONFIG_LSM=""
+CONFIG_CRYPTO_USER=m
+CONFIG_CRYPTO_CHACHA20POLY1305=m
+CONFIG_CRYPTO_ADIANTUM=m
+CONFIG_CRYPTO_XCBC=m
+CONFIG_CRYPTO_TGR192=m
+CONFIG_CRYPTO_WP512=m
+CONFIG_CRYPTO_CAST5=m
+CONFIG_CRYPTO_DES=y
+CONFIG_CRYPTO_LZ4=m
+CONFIG_CRYPTO_USER_API_HASH=m
+CONFIG_CRYPTO_USER_API_SKCIPHER=m
+CONFIG_CRYPTO_USER_API_RNG=m
+CONFIG_CRYPTO_USER_API_AEAD=m
+# CONFIG_CRYPTO_HW is not set
+CONFIG_CRC_ITU_T=y
+CONFIG_LIBCRC32C=y
+CONFIG_DMA_CMA=y
+CONFIG_CMA_SIZE_MBYTES=5
+CONFIG_PRINTK_TIME=y
+CONFIG_BOOT_PRINTK_DELAY=y
+CONFIG_KGDB=y
+CONFIG_KGDB_KDB=y
+CONFIG_KDB_KEYBOARD=y
+CONFIG_DEBUG_MEMORY_INIT=y
+CONFIG_DETECT_HUNG_TASK=y
+# CONFIG_RCU_TRACE is not set
+CONFIG_LATENCYTOP=y
+CONFIG_FUNCTION_PROFILER=y
+CONFIG_STACK_TRACER=y
+CONFIG_IRQSOFF_TRACER=y
+CONFIG_SCHED_TRACER=y
+CONFIG_BLK_DEV_IO_TRACE=y
+# CONFIG_UPROBE_EVENTS is not set
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/configs/bcm2711_defconfig linux-5.10.52-v7l+/arch/arm/configs/bcm2711_defconfig
--- linux-5.10.52-orig/arch/arm/configs/bcm2711_defconfig	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/configs/bcm2711_defconfig	2021-07-25 16:45:35.368803870 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+CONFIG_LOCALVERSION="-v7l"
+# CONFIG_LOCALVERSION_AUTO is not set
+CONFIG_SYSVIPC=y
+CONFIG_POSIX_MQUEUE=y
+CONFIG_GENERIC_IRQ_DEBUGFS=y
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_PREEMPT_VOLUNTARY=y
+CONFIG_BSD_PROCESS_ACCT=y
+CONFIG_BSD_PROCESS_ACCT_V3=y
+CONFIG_TASKSTATS=y
+CONFIG_TASK_DELAY_ACCT=y
+CONFIG_TASK_XACCT=y
+CONFIG_TASK_IO_ACCOUNTING=y
+CONFIG_IKCONFIG=m
+CONFIG_IKCONFIG_PROC=y
+CONFIG_MEMCG=y
+CONFIG_BLK_CGROUP=y
+CONFIG_CFS_BANDWIDTH=y
+CONFIG_CGROUP_PIDS=y
+CONFIG_CGROUP_FREEZER=y
+CONFIG_CPUSETS=y
+CONFIG_CGROUP_DEVICE=y
+CONFIG_CGROUP_CPUACCT=y
+CONFIG_CGROUP_PERF=y
+CONFIG_CGROUP_BPF=y
+CONFIG_NAMESPACES=y
+CONFIG_USER_NS=y
+CONFIG_SCHED_AUTOGROUP=y
+CONFIG_BLK_DEV_INITRD=y
+CONFIG_BPF_SYSCALL=y
+CONFIG_EMBEDDED=y
+# CONFIG_COMPAT_BRK is not set
+CONFIG_PROFILING=y
+CONFIG_ARCH_BCM=y
+CONFIG_ARCH_BCM2835=y
+CONFIG_ARM_LPAE=y
+# CONFIG_CACHE_L2X0 is not set
+CONFIG_SMP=y
+CONFIG_HIGHMEM=y
+CONFIG_UACCESS_WITH_MEMCPY=y
+# CONFIG_ATAGS is not set
+CONFIG_CMDLINE="console=ttyAMA0,115200 kgdboc=ttyAMA0,115200 root=/dev/mmcblk0p2 rootfstype=ext4 rootwait"
+CONFIG_CPU_FREQ=y
+CONFIG_CPU_FREQ_STAT=y
+CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE=y
+CONFIG_CPU_FREQ_GOV_PERFORMANCE=y
+CONFIG_CPU_FREQ_GOV_USERSPACE=y
+CONFIG_CPU_FREQ_GOV_ONDEMAND=y
+CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y
+CONFIG_CPU_FREQ_GOV_SCHEDUTIL=y
+CONFIG_CPUFREQ_DT=y
+CONFIG_ARM_RASPBERRYPI_CPUFREQ=y
+CONFIG_VFP=y
+CONFIG_NEON=y
+CONFIG_KERNEL_MODE_NEON=y
+# CONFIG_SUSPEND is not set
+CONFIG_PM=y
+CONFIG_RASPBERRYPI_FIRMWARE=y
+CONFIG_CRYPTO_SHA1_ARM_NEON=m
+CONFIG_CRYPTO_AES_ARM=m
+CONFIG_CRYPTO_AES_ARM_BS=m
+CONFIG_OPROFILE=m
+CONFIG_JUMP_LABEL=y
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+CONFIG_MODVERSIONS=y
+CONFIG_MODULE_SRCVERSION_ALL=y
+CONFIG_BLK_DEV_THROTTLING=y
+CONFIG_PARTITION_ADVANCED=y
+CONFIG_MAC_PARTITION=y
+CONFIG_BINFMT_MISC=m
+CONFIG_CLEANCACHE=y
+CONFIG_FRONTSWAP=y
+CONFIG_CMA=y
+CONFIG_ZSWAP=y
+CONFIG_Z3FOLD=m
+CONFIG_ZSMALLOC=m
+CONFIG_NET=y
+CONFIG_PACKET=y
+CONFIG_UNIX=y
+CONFIG_XFRM_USER=y
+CONFIG_NET_KEY=m
+CONFIG_INET=y
+CONFIG_IP_MULTICAST=y
+CONFIG_IP_ADVANCED_ROUTER=y
+CONFIG_IP_MULTIPLE_TABLES=y
+CONFIG_IP_ROUTE_MULTIPATH=y
+CONFIG_IP_ROUTE_VERBOSE=y
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+CONFIG_IP_PNP_RARP=y
+CONFIG_NET_IPIP=m
+CONFIG_NET_IPGRE_DEMUX=m
+CONFIG_NET_IPGRE=m
+CONFIG_IP_MROUTE=y
+CONFIG_IP_MROUTE_MULTIPLE_TABLES=y
+CONFIG_IP_PIMSM_V1=y
+CONFIG_IP_PIMSM_V2=y
+CONFIG_NET_IPVTI=m
+CONFIG_NET_FOU=m
+CONFIG_INET_AH=m
+CONFIG_INET_ESP=m
+CONFIG_INET_IPCOMP=m
+CONFIG_INET_DIAG=m
+CONFIG_TCP_CONG_ADVANCED=y
+CONFIG_TCP_CONG_BBR=m
+CONFIG_IPV6=m
+CONFIG_IPV6_ROUTER_PREF=y
+CONFIG_IPV6_ROUTE_INFO=y
+CONFIG_INET6_AH=m
+CONFIG_INET6_ESP=m
+CONFIG_INET6_ESP_OFFLOAD=m
+CONFIG_INET6_IPCOMP=m
+CONFIG_IPV6_ILA=m
+CONFIG_IPV6_VTI=m
+CONFIG_IPV6_SIT_6RD=y
+CONFIG_IPV6_GRE=m
+CONFIG_IPV6_MULTIPLE_TABLES=y
+CONFIG_IPV6_SUBTREES=y
+CONFIG_IPV6_MROUTE=y
+CONFIG_IPV6_MROUTE_MULTIPLE_TABLES=y
+CONFIG_IPV6_PIMSM_V2=y
+CONFIG_NETFILTER=y
+CONFIG_BRIDGE_NETFILTER=m
+CONFIG_NF_CONNTRACK=m
+CONFIG_NF_LOG_NETDEV=m
+CONFIG_NF_CONNTRACK_ZONES=y
+CONFIG_NF_CONNTRACK_EVENTS=y
+CONFIG_NF_CONNTRACK_TIMESTAMP=y
+CONFIG_NF_CONNTRACK_AMANDA=m
+CONFIG_NF_CONNTRACK_FTP=m
+CONFIG_NF_CONNTRACK_H323=m
+CONFIG_NF_CONNTRACK_IRC=m
+CONFIG_NF_CONNTRACK_NETBIOS_NS=m
+CONFIG_NF_CONNTRACK_SNMP=m
+CONFIG_NF_CONNTRACK_PPTP=m
+CONFIG_NF_CONNTRACK_SANE=m
+CONFIG_NF_CONNTRACK_SIP=m
+CONFIG_NF_CONNTRACK_TFTP=m
+CONFIG_NF_CT_NETLINK=m
+CONFIG_NF_TABLES=m
+CONFIG_NF_TABLES_INET=y
+CONFIG_NF_TABLES_NETDEV=y
+CONFIG_NFT_NUMGEN=m
+CONFIG_NFT_CT=m
+CONFIG_NFT_FLOW_OFFLOAD=m
+CONFIG_NFT_COUNTER=m
+CONFIG_NFT_CONNLIMIT=m
+CONFIG_NFT_LOG=m
+CONFIG_NFT_LIMIT=m
+CONFIG_NFT_MASQ=m
+CONFIG_NFT_REDIR=m
+CONFIG_NFT_NAT=m
+CONFIG_NFT_TUNNEL=m
+CONFIG_NFT_OBJREF=m
+CONFIG_NFT_QUEUE=m
+CONFIG_NFT_QUOTA=m
+CONFIG_NFT_REJECT=m
+CONFIG_NFT_COMPAT=m
+CONFIG_NFT_HASH=m
+CONFIG_NFT_FIB_INET=m
+CONFIG_NFT_SOCKET=m
+CONFIG_NFT_OSF=m
+CONFIG_NFT_TPROXY=m
+CONFIG_NFT_DUP_NETDEV=m
+CONFIG_NFT_FWD_NETDEV=m
+CONFIG_NFT_FIB_NETDEV=m
+CONFIG_NF_FLOW_TABLE_INET=m
+CONFIG_NF_FLOW_TABLE=m
+CONFIG_NETFILTER_XT_SET=m
+CONFIG_NETFILTER_XT_TARGET_CHECKSUM=m
+CONFIG_NETFILTER_XT_TARGET_CLASSIFY=m
+CONFIG_NETFILTER_XT_TARGET_CONNMARK=m
+CONFIG_NETFILTER_XT_TARGET_DSCP=m
+CONFIG_NETFILTER_XT_TARGET_HMARK=m
+CONFIG_NETFILTER_XT_TARGET_IDLETIMER=m
+CONFIG_NETFILTER_XT_TARGET_LED=m
+CONFIG_NETFILTER_XT_TARGET_LOG=m
+CONFIG_NETFILTER_XT_TARGET_MARK=m
+CONFIG_NETFILTER_XT_TARGET_NFLOG=m
+CONFIG_NETFILTER_XT_TARGET_NFQUEUE=m
+CONFIG_NETFILTER_XT_TARGET_NOTRACK=m
+CONFIG_NETFILTER_XT_TARGET_TEE=m
+CONFIG_NETFILTER_XT_TARGET_TPROXY=m
+CONFIG_NETFILTER_XT_TARGET_TRACE=m
+CONFIG_NETFILTER_XT_TARGET_TCPMSS=m
+CONFIG_NETFILTER_XT_TARGET_TCPOPTSTRIP=m
+CONFIG_NETFILTER_XT_MATCH_ADDRTYPE=m
+CONFIG_NETFILTER_XT_MATCH_BPF=m
+CONFIG_NETFILTER_XT_MATCH_CLUSTER=m
+CONFIG_NETFILTER_XT_MATCH_COMMENT=m
+CONFIG_NETFILTER_XT_MATCH_CONNBYTES=m
+CONFIG_NETFILTER_XT_MATCH_CONNLABEL=m
+CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=m
+CONFIG_NETFILTER_XT_MATCH_CONNMARK=m
+CONFIG_NETFILTER_XT_MATCH_CONNTRACK=m
+CONFIG_NETFILTER_XT_MATCH_CPU=m
+CONFIG_NETFILTER_XT_MATCH_DCCP=m
+CONFIG_NETFILTER_XT_MATCH_DEVGROUP=m
+CONFIG_NETFILTER_XT_MATCH_DSCP=m
+CONFIG_NETFILTER_XT_MATCH_ESP=m
+CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=m
+CONFIG_NETFILTER_XT_MATCH_HELPER=m
+CONFIG_NETFILTER_XT_MATCH_IPRANGE=m
+CONFIG_NETFILTER_XT_MATCH_IPVS=m
+CONFIG_NETFILTER_XT_MATCH_LENGTH=m
+CONFIG_NETFILTER_XT_MATCH_LIMIT=m
+CONFIG_NETFILTER_XT_MATCH_MAC=m
+CONFIG_NETFILTER_XT_MATCH_MARK=m
+CONFIG_NETFILTER_XT_MATCH_MULTIPORT=m
+CONFIG_NETFILTER_XT_MATCH_NFACCT=m
+CONFIG_NETFILTER_XT_MATCH_OSF=m
+CONFIG_NETFILTER_XT_MATCH_OWNER=m
+CONFIG_NETFILTER_XT_MATCH_POLICY=m
+CONFIG_NETFILTER_XT_MATCH_PHYSDEV=m
+CONFIG_NETFILTER_XT_MATCH_PKTTYPE=m
+CONFIG_NETFILTER_XT_MATCH_QUOTA=m
+CONFIG_NETFILTER_XT_MATCH_RATEEST=m
+CONFIG_NETFILTER_XT_MATCH_REALM=m
+CONFIG_NETFILTER_XT_MATCH_RECENT=m
+CONFIG_NETFILTER_XT_MATCH_SOCKET=m
+CONFIG_NETFILTER_XT_MATCH_STATE=m
+CONFIG_NETFILTER_XT_MATCH_STATISTIC=m
+CONFIG_NETFILTER_XT_MATCH_STRING=m
+CONFIG_NETFILTER_XT_MATCH_TCPMSS=m
+CONFIG_NETFILTER_XT_MATCH_TIME=m
+CONFIG_NETFILTER_XT_MATCH_U32=m
+CONFIG_IP_SET=m
+CONFIG_IP_SET_BITMAP_IP=m
+CONFIG_IP_SET_BITMAP_IPMAC=m
+CONFIG_IP_SET_BITMAP_PORT=m
+CONFIG_IP_SET_HASH_IP=m
+CONFIG_IP_SET_HASH_IPPORT=m
+CONFIG_IP_SET_HASH_IPPORTIP=m
+CONFIG_IP_SET_HASH_IPPORTNET=m
+CONFIG_IP_SET_HASH_NET=m
+CONFIG_IP_SET_HASH_NETPORT=m
+CONFIG_IP_SET_HASH_NETIFACE=m
+CONFIG_IP_SET_LIST_SET=m
+CONFIG_IP_VS=m
+CONFIG_IP_VS_PROTO_TCP=y
+CONFIG_IP_VS_PROTO_UDP=y
+CONFIG_IP_VS_PROTO_ESP=y
+CONFIG_IP_VS_PROTO_AH=y
+CONFIG_IP_VS_PROTO_SCTP=y
+CONFIG_IP_VS_RR=m
+CONFIG_IP_VS_WRR=m
+CONFIG_IP_VS_LC=m
+CONFIG_IP_VS_WLC=m
+CONFIG_IP_VS_LBLC=m
+CONFIG_IP_VS_LBLCR=m
+CONFIG_IP_VS_DH=m
+CONFIG_IP_VS_SH=m
+CONFIG_IP_VS_SED=m
+CONFIG_IP_VS_NQ=m
+CONFIG_IP_VS_FTP=m
+CONFIG_IP_VS_PE_SIP=m
+CONFIG_NFT_DUP_IPV4=m
+CONFIG_NFT_FIB_IPV4=m
+CONFIG_NF_TABLES_ARP=y
+CONFIG_NF_FLOW_TABLE_IPV4=m
+CONFIG_NF_LOG_ARP=m
+CONFIG_IP_NF_IPTABLES=m
+CONFIG_IP_NF_MATCH_AH=m
+CONFIG_IP_NF_MATCH_ECN=m
+CONFIG_IP_NF_MATCH_RPFILTER=m
+CONFIG_IP_NF_MATCH_TTL=m
+CONFIG_IP_NF_FILTER=m
+CONFIG_IP_NF_TARGET_REJECT=m
+CONFIG_IP_NF_NAT=m
+CONFIG_IP_NF_TARGET_MASQUERADE=m
+CONFIG_IP_NF_TARGET_NETMAP=m
+CONFIG_IP_NF_TARGET_REDIRECT=m
+CONFIG_IP_NF_MANGLE=m
+CONFIG_IP_NF_TARGET_CLUSTERIP=m
+CONFIG_IP_NF_TARGET_ECN=m
+CONFIG_IP_NF_TARGET_TTL=m
+CONFIG_IP_NF_RAW=m
+CONFIG_IP_NF_ARPTABLES=m
+CONFIG_IP_NF_ARPFILTER=m
+CONFIG_IP_NF_ARP_MANGLE=m
+CONFIG_NFT_DUP_IPV6=m
+CONFIG_NFT_FIB_IPV6=m
+CONFIG_NF_FLOW_TABLE_IPV6=m
+CONFIG_IP6_NF_IPTABLES=m
+CONFIG_IP6_NF_MATCH_AH=m
+CONFIG_IP6_NF_MATCH_EUI64=m
+CONFIG_IP6_NF_MATCH_FRAG=m
+CONFIG_IP6_NF_MATCH_OPTS=m
+CONFIG_IP6_NF_MATCH_HL=m
+CONFIG_IP6_NF_MATCH_IPV6HEADER=m
+CONFIG_IP6_NF_MATCH_MH=m
+CONFIG_IP6_NF_MATCH_RPFILTER=m
+CONFIG_IP6_NF_MATCH_RT=m
+CONFIG_IP6_NF_MATCH_SRH=m
+CONFIG_IP6_NF_TARGET_HL=m
+CONFIG_IP6_NF_FILTER=m
+CONFIG_IP6_NF_TARGET_REJECT=m
+CONFIG_IP6_NF_TARGET_SYNPROXY=m
+CONFIG_IP6_NF_MANGLE=m
+CONFIG_IP6_NF_RAW=m
+CONFIG_IP6_NF_SECURITY=m
+CONFIG_IP6_NF_NAT=m
+CONFIG_IP6_NF_TARGET_MASQUERADE=m
+CONFIG_IP6_NF_TARGET_NPT=m
+CONFIG_NF_TABLES_BRIDGE=m
+CONFIG_NFT_BRIDGE_REJECT=m
+CONFIG_NF_LOG_BRIDGE=m
+CONFIG_BRIDGE_NF_EBTABLES=m
+CONFIG_BRIDGE_EBT_BROUTE=m
+CONFIG_BRIDGE_EBT_T_FILTER=m
+CONFIG_BRIDGE_EBT_T_NAT=m
+CONFIG_BRIDGE_EBT_802_3=m
+CONFIG_BRIDGE_EBT_AMONG=m
+CONFIG_BRIDGE_EBT_ARP=m
+CONFIG_BRIDGE_EBT_IP=m
+CONFIG_BRIDGE_EBT_IP6=m
+CONFIG_BRIDGE_EBT_LIMIT=m
+CONFIG_BRIDGE_EBT_MARK=m
+CONFIG_BRIDGE_EBT_PKTTYPE=m
+CONFIG_BRIDGE_EBT_STP=m
+CONFIG_BRIDGE_EBT_VLAN=m
+CONFIG_BRIDGE_EBT_ARPREPLY=m
+CONFIG_BRIDGE_EBT_DNAT=m
+CONFIG_BRIDGE_EBT_MARK_T=m
+CONFIG_BRIDGE_EBT_REDIRECT=m
+CONFIG_BRIDGE_EBT_SNAT=m
+CONFIG_BRIDGE_EBT_LOG=m
+CONFIG_BRIDGE_EBT_NFLOG=m
+CONFIG_SCTP_COOKIE_HMAC_SHA1=y
+CONFIG_ATM=m
+CONFIG_L2TP=m
+CONFIG_L2TP_V3=y
+CONFIG_L2TP_IP=m
+CONFIG_L2TP_ETH=m
+CONFIG_BRIDGE=m
+CONFIG_VLAN_8021Q=m
+CONFIG_VLAN_8021Q_GVRP=y
+CONFIG_ATALK=m
+CONFIG_6LOWPAN=m
+CONFIG_IEEE802154=m
+CONFIG_IEEE802154_6LOWPAN=m
+CONFIG_MAC802154=m
+CONFIG_NET_SCHED=y
+CONFIG_NET_SCH_CBQ=m
+CONFIG_NET_SCH_HTB=m
+CONFIG_NET_SCH_HFSC=m
+CONFIG_NET_SCH_ATM=m
+CONFIG_NET_SCH_PRIO=m
+CONFIG_NET_SCH_MULTIQ=m
+CONFIG_NET_SCH_RED=m
+CONFIG_NET_SCH_SFB=m
+CONFIG_NET_SCH_SFQ=m
+CONFIG_NET_SCH_TEQL=m
+CONFIG_NET_SCH_TBF=m
+CONFIG_NET_SCH_GRED=m
+CONFIG_NET_SCH_DSMARK=m
+CONFIG_NET_SCH_NETEM=m
+CONFIG_NET_SCH_DRR=m
+CONFIG_NET_SCH_MQPRIO=m
+CONFIG_NET_SCH_CHOKE=m
+CONFIG_NET_SCH_QFQ=m
+CONFIG_NET_SCH_CODEL=m
+CONFIG_NET_SCH_FQ_CODEL=m
+CONFIG_NET_SCH_CAKE=m
+CONFIG_NET_SCH_FQ=m
+CONFIG_NET_SCH_HHF=m
+CONFIG_NET_SCH_PIE=m
+CONFIG_NET_SCH_INGRESS=m
+CONFIG_NET_SCH_PLUG=m
+CONFIG_NET_CLS_BASIC=m
+CONFIG_NET_CLS_TCINDEX=m
+CONFIG_NET_CLS_ROUTE4=m
+CONFIG_NET_CLS_FW=m
+CONFIG_NET_CLS_U32=m
+CONFIG_CLS_U32_MARK=y
+CONFIG_NET_CLS_RSVP=m
+CONFIG_NET_CLS_RSVP6=m
+CONFIG_NET_CLS_FLOW=m
+CONFIG_NET_CLS_CGROUP=m
+CONFIG_NET_EMATCH=y
+CONFIG_NET_EMATCH_CMP=m
+CONFIG_NET_EMATCH_NBYTE=m
+CONFIG_NET_EMATCH_U32=m
+CONFIG_NET_EMATCH_META=m
+CONFIG_NET_EMATCH_TEXT=m
+CONFIG_NET_EMATCH_IPSET=m
+CONFIG_NET_CLS_ACT=y
+CONFIG_NET_ACT_POLICE=m
+CONFIG_NET_ACT_GACT=m
+CONFIG_GACT_PROB=y
+CONFIG_NET_ACT_MIRRED=m
+CONFIG_NET_ACT_IPT=m
+CONFIG_NET_ACT_NAT=m
+CONFIG_NET_ACT_PEDIT=m
+CONFIG_NET_ACT_SIMP=m
+CONFIG_NET_ACT_SKBEDIT=m
+CONFIG_NET_ACT_CSUM=m
+CONFIG_BATMAN_ADV=m
+CONFIG_OPENVSWITCH=m
+CONFIG_CGROUP_NET_PRIO=y
+CONFIG_NET_PKTGEN=m
+CONFIG_HAMRADIO=y
+CONFIG_AX25=m
+CONFIG_NETROM=m
+CONFIG_ROSE=m
+CONFIG_MKISS=m
+CONFIG_6PACK=m
+CONFIG_BPQETHER=m
+CONFIG_BAYCOM_SER_FDX=m
+CONFIG_BAYCOM_SER_HDX=m
+CONFIG_YAM=m
+CONFIG_CAN=m
+CONFIG_CAN_J1939=m
+CONFIG_CAN_ISOTP=m
+CONFIG_CAN_VCAN=m
+CONFIG_CAN_SLCAN=m
+CONFIG_CAN_MCP251X=m
+CONFIG_CAN_MCP251XFD=m
+CONFIG_CAN_EMS_USB=m
+CONFIG_CAN_GS_USB=m
+CONFIG_BT=m
+CONFIG_BT_RFCOMM=m
+CONFIG_BT_RFCOMM_TTY=y
+CONFIG_BT_BNEP=m
+CONFIG_BT_BNEP_MC_FILTER=y
+CONFIG_BT_BNEP_PROTO_FILTER=y
+CONFIG_BT_HIDP=m
+CONFIG_BT_6LOWPAN=m
+CONFIG_BT_HCIBTUSB=m
+CONFIG_BT_HCIUART=m
+CONFIG_BT_HCIUART_3WIRE=y
+CONFIG_BT_HCIUART_BCM=y
+CONFIG_BT_HCIBCM203X=m
+CONFIG_BT_HCIBPA10X=m
+CONFIG_BT_HCIBFUSB=m
+CONFIG_BT_HCIVHCI=m
+CONFIG_BT_MRVL=m
+CONFIG_BT_MRVL_SDIO=m
+CONFIG_BT_ATH3K=m
+CONFIG_CFG80211=m
+CONFIG_CFG80211_WEXT=y
+CONFIG_MAC80211=m
+CONFIG_MAC80211_MESH=y
+CONFIG_WIMAX=m
+CONFIG_RFKILL=m
+CONFIG_RFKILL_INPUT=y
+CONFIG_NET_9P=m
+CONFIG_NFC=m
+CONFIG_PCI=y
+CONFIG_PCIEPORTBUS=y
+# CONFIG_PCIEASPM is not set
+CONFIG_PCI_MSI=y
+CONFIG_PCIE_BRCMSTB=y
+CONFIG_UEVENT_HELPER=y
+CONFIG_DEVTMPFS=y
+CONFIG_DEVTMPFS_MOUNT=y
+CONFIG_MTD=m
+CONFIG_MTD_BLOCK=m
+CONFIG_MTD_BLOCK2MTD=m
+CONFIG_MTD_SPI_NOR=m
+CONFIG_MTD_UBI=m
+CONFIG_OF_CONFIGFS=y
+CONFIG_ZRAM=m
+CONFIG_BLK_DEV_LOOP=y
+CONFIG_BLK_DEV_CRYPTOLOOP=m
+CONFIG_BLK_DEV_DRBD=m
+CONFIG_BLK_DEV_NBD=m
+CONFIG_BLK_DEV_RAM=y
+CONFIG_CDROM_PKTCDVD=m
+CONFIG_ATA_OVER_ETH=m
+CONFIG_BLK_DEV_NVME=y
+CONFIG_EEPROM_AT24=m
+CONFIG_TI_ST=m
+CONFIG_SCSI=y
+# CONFIG_SCSI_PROC_FS is not set
+CONFIG_BLK_DEV_SD=y
+CONFIG_CHR_DEV_ST=m
+CONFIG_BLK_DEV_SR=m
+CONFIG_CHR_DEV_SG=m
+CONFIG_SCSI_ISCSI_ATTRS=y
+CONFIG_ISCSI_TCP=m
+CONFIG_ISCSI_BOOT_SYSFS=m
+CONFIG_ATA=m
+CONFIG_SATA_AHCI=m
+CONFIG_SATA_MV=m
+CONFIG_MD=y
+CONFIG_MD_LINEAR=m
+CONFIG_BLK_DEV_DM=m
+CONFIG_DM_CRYPT=m
+CONFIG_DM_SNAPSHOT=m
+CONFIG_DM_THIN_PROVISIONING=m
+CONFIG_DM_CACHE=m
+CONFIG_DM_MIRROR=m
+CONFIG_DM_LOG_USERSPACE=m
+CONFIG_DM_RAID=m
+CONFIG_DM_ZERO=m
+CONFIG_DM_DELAY=m
+CONFIG_NETDEVICES=y
+CONFIG_BONDING=m
+CONFIG_DUMMY=m
+CONFIG_WIREGUARD=m
+CONFIG_IFB=m
+CONFIG_MACVLAN=m
+CONFIG_IPVLAN=m
+CONFIG_VXLAN=m
+CONFIG_NETCONSOLE=m
+CONFIG_TUN=m
+CONFIG_VETH=m
+CONFIG_NET_VRF=m
+CONFIG_BCMGENET=y
+CONFIG_ENC28J60=m
+CONFIG_QCA7000_SPI=m
+CONFIG_QCA7000_UART=m
+CONFIG_WIZNET_W5100=m
+CONFIG_WIZNET_W5100_SPI=m
+CONFIG_MICREL_PHY=y
+CONFIG_MDIO_BITBANG=m
+CONFIG_PPP=m
+CONFIG_PPP_BSDCOMP=m
+CONFIG_PPP_DEFLATE=m
+CONFIG_PPP_FILTER=y
+CONFIG_PPP_MPPE=m
+CONFIG_PPP_MULTILINK=y
+CONFIG_PPPOATM=m
+CONFIG_PPPOE=m
+CONFIG_PPPOL2TP=m
+CONFIG_PPP_ASYNC=m
+CONFIG_PPP_SYNC_TTY=m
+CONFIG_SLIP=m
+CONFIG_SLIP_COMPRESSED=y
+CONFIG_SLIP_SMART=y
+CONFIG_USB_CATC=m
+CONFIG_USB_KAWETH=m
+CONFIG_USB_PEGASUS=m
+CONFIG_USB_RTL8150=m
+CONFIG_USB_RTL8152=y
+CONFIG_USB_LAN78XX=y
+CONFIG_USB_USBNET=y
+CONFIG_USB_NET_AX8817X=m
+CONFIG_USB_NET_AX88179_178A=m
+CONFIG_USB_NET_CDCETHER=m
+CONFIG_USB_NET_CDC_EEM=m
+CONFIG_USB_NET_CDC_NCM=m
+CONFIG_USB_NET_HUAWEI_CDC_NCM=m
+CONFIG_USB_NET_CDC_MBIM=m
+CONFIG_USB_NET_DM9601=m
+CONFIG_USB_NET_SR9700=m
+CONFIG_USB_NET_SR9800=m
+CONFIG_USB_NET_SMSC75XX=m
+CONFIG_USB_NET_SMSC95XX=y
+CONFIG_USB_NET_GL620A=m
+CONFIG_USB_NET_NET1080=m
+CONFIG_USB_NET_PLUSB=m
+CONFIG_USB_NET_MCS7830=m
+CONFIG_USB_NET_CDC_SUBSET=m
+CONFIG_USB_ALI_M5632=y
+CONFIG_USB_AN2720=y
+CONFIG_USB_EPSON2888=y
+CONFIG_USB_KC2190=y
+CONFIG_USB_NET_ZAURUS=m
+CONFIG_USB_NET_CX82310_ETH=m
+CONFIG_USB_NET_KALMIA=m
+CONFIG_USB_NET_QMI_WWAN=m
+CONFIG_USB_HSO=m
+CONFIG_USB_NET_INT51X1=m
+CONFIG_USB_IPHETH=m
+CONFIG_USB_SIERRA_NET=m
+CONFIG_USB_VL600=m
+CONFIG_USB_NET_AQC111=m
+CONFIG_ATH9K=m
+CONFIG_ATH9K_HTC=m
+CONFIG_CARL9170=m
+CONFIG_ATH6KL=m
+CONFIG_ATH6KL_USB=m
+CONFIG_AR5523=m
+CONFIG_AT76C50X_USB=m
+CONFIG_B43=m
+# CONFIG_B43_PHY_N is not set
+CONFIG_B43LEGACY=m
+CONFIG_BRCMFMAC=m
+CONFIG_BRCMFMAC_USB=y
+CONFIG_BRCMDBG=y
+CONFIG_HOSTAP=m
+CONFIG_P54_COMMON=m
+CONFIG_P54_USB=m
+CONFIG_LIBERTAS=m
+CONFIG_LIBERTAS_USB=m
+CONFIG_LIBERTAS_SDIO=m
+CONFIG_LIBERTAS_THINFIRM=m
+CONFIG_LIBERTAS_THINFIRM_USB=m
+CONFIG_MWIFIEX=m
+CONFIG_MWIFIEX_SDIO=m
+CONFIG_MT7601U=m
+CONFIG_MT76x0U=m
+CONFIG_MT76x2U=m
+CONFIG_RT2X00=m
+CONFIG_RT2500USB=m
+CONFIG_RT73USB=m
+CONFIG_RT2800USB=m
+CONFIG_RT2800USB_RT3573=y
+CONFIG_RT2800USB_RT53XX=y
+CONFIG_RT2800USB_RT55XX=y
+CONFIG_RT2800USB_UNKNOWN=y
+CONFIG_RTL8187=m
+CONFIG_RTL8192CU=m
+CONFIG_RTL8XXXU=m
+CONFIG_USB_ZD1201=m
+CONFIG_ZD1211RW=m
+CONFIG_MAC80211_HWSIM=m
+CONFIG_USB_NET_RNDIS_WLAN=m
+CONFIG_WIMAX_I2400M_USB=m
+CONFIG_IEEE802154_AT86RF230=m
+CONFIG_IEEE802154_MRF24J40=m
+CONFIG_IEEE802154_CC2520=m
+CONFIG_INPUT_MOUSEDEV=y
+CONFIG_INPUT_JOYDEV=m
+CONFIG_INPUT_EVDEV=y
+# CONFIG_KEYBOARD_ATKBD is not set
+CONFIG_KEYBOARD_GPIO=m
+CONFIG_KEYBOARD_MATRIX=m
+CONFIG_KEYBOARD_CAP11XX=m
+# CONFIG_INPUT_MOUSE is not set
+CONFIG_INPUT_JOYSTICK=y
+CONFIG_JOYSTICK_IFORCE=m
+CONFIG_JOYSTICK_IFORCE_USB=m
+CONFIG_JOYSTICK_XPAD=m
+CONFIG_JOYSTICK_XPAD_FF=y
+CONFIG_JOYSTICK_XPAD_LEDS=y
+CONFIG_JOYSTICK_PSXPAD_SPI=m
+CONFIG_JOYSTICK_PSXPAD_SPI_FF=y
+CONFIG_JOYSTICK_RPISENSE=m
+CONFIG_INPUT_TOUCHSCREEN=y
+CONFIG_TOUCHSCREEN_ADS7846=m
+CONFIG_TOUCHSCREEN_EGALAX=m
+CONFIG_TOUCHSCREEN_EXC3000=m
+CONFIG_TOUCHSCREEN_GOODIX=m
+CONFIG_TOUCHSCREEN_ILI210X=m
+CONFIG_TOUCHSCREEN_EDT_FT5X06=m
+CONFIG_TOUCHSCREEN_RASPBERRYPI_FW=m
+CONFIG_TOUCHSCREEN_USB_COMPOSITE=m
+CONFIG_TOUCHSCREEN_STMPE=m
+CONFIG_INPUT_MISC=y
+CONFIG_INPUT_AD714X=m
+CONFIG_INPUT_ATI_REMOTE2=m
+CONFIG_INPUT_KEYSPAN_REMOTE=m
+CONFIG_INPUT_POWERMATE=m
+CONFIG_INPUT_YEALINK=m
+CONFIG_INPUT_CM109=m
+CONFIG_INPUT_UINPUT=m
+CONFIG_INPUT_GPIO_ROTARY_ENCODER=m
+CONFIG_INPUT_ADXL34X=m
+CONFIG_INPUT_CMA3000=m
+CONFIG_SERIO=m
+CONFIG_SERIO_RAW=m
+CONFIG_GAMEPORT=m
+CONFIG_GAMEPORT_NS558=m
+CONFIG_GAMEPORT_L4=m
+CONFIG_BRCM_CHAR_DRIVERS=y
+CONFIG_BCM_VCIO=y
+CONFIG_BCM2835_DEVGPIOMEM=y
+CONFIG_RPIVID_MEM=m
+# CONFIG_LEGACY_PTYS is not set
+CONFIG_SERIAL_8250=y
+# CONFIG_SERIAL_8250_DEPRECATED_OPTIONS is not set
+CONFIG_SERIAL_8250_CONSOLE=y
+# CONFIG_SERIAL_8250_DMA is not set
+CONFIG_SERIAL_8250_NR_UARTS=1
+CONFIG_SERIAL_8250_RUNTIME_UARTS=0
+CONFIG_SERIAL_8250_EXTENDED=y
+CONFIG_SERIAL_8250_SHARE_IRQ=y
+CONFIG_SERIAL_8250_BCM2835AUX=y
+CONFIG_SERIAL_OF_PLATFORM=y
+CONFIG_SERIAL_AMBA_PL011=y
+CONFIG_SERIAL_AMBA_PL011_CONSOLE=y
+CONFIG_SERIAL_SC16IS7XX=m
+CONFIG_SERIAL_SC16IS7XX_SPI=y
+CONFIG_SERIAL_DEV_BUS=y
+CONFIG_TTY_PRINTK=y
+CONFIG_HW_RANDOM=y
+CONFIG_RAW_DRIVER=y
+CONFIG_TCG_TPM=m
+CONFIG_TCG_TIS_SPI=m
+CONFIG_I2C=y
+CONFIG_I2C_CHARDEV=m
+CONFIG_I2C_MUX_GPMUX=m
+CONFIG_I2C_MUX_PCA954x=m
+CONFIG_I2C_MUX_PINCTRL=m
+CONFIG_I2C_BCM2708=m
+CONFIG_I2C_BCM2835=m
+CONFIG_I2C_BRCMSTB=m
+CONFIG_I2C_GPIO=m
+CONFIG_I2C_ROBOTFUZZ_OSIF=m
+CONFIG_I2C_TINY_USB=m
+CONFIG_SPI=y
+CONFIG_SPI_BCM2835=m
+CONFIG_SPI_BCM2835AUX=m
+CONFIG_SPI_GPIO=m
+CONFIG_SPI_SPIDEV=m
+CONFIG_SPI_SLAVE=y
+CONFIG_PPS=m
+CONFIG_PPS_CLIENT_LDISC=m
+CONFIG_PPS_CLIENT_GPIO=m
+CONFIG_PINCTRL_MCP23S08=m
+CONFIG_GPIO_SYSFS=y
+CONFIG_GPIO_BCM_VIRT=y
+CONFIG_GPIO_PCA953X=m
+CONFIG_GPIO_PCA953X_IRQ=y
+CONFIG_GPIO_PCF857X=m
+CONFIG_GPIO_ARIZONA=m
+CONFIG_GPIO_FSM=m
+CONFIG_GPIO_STMPE=y
+CONFIG_GPIO_MOCKUP=m
+CONFIG_W1=m
+CONFIG_W1_MASTER_DS2490=m
+CONFIG_W1_MASTER_DS2482=m
+CONFIG_W1_MASTER_DS1WM=m
+CONFIG_W1_MASTER_GPIO=m
+CONFIG_W1_SLAVE_THERM=m
+CONFIG_W1_SLAVE_SMEM=m
+CONFIG_W1_SLAVE_DS2408=m
+CONFIG_W1_SLAVE_DS2413=m
+CONFIG_W1_SLAVE_DS2406=m
+CONFIG_W1_SLAVE_DS2423=m
+CONFIG_W1_SLAVE_DS2431=m
+CONFIG_W1_SLAVE_DS2433=m
+CONFIG_W1_SLAVE_DS2438=m
+CONFIG_W1_SLAVE_DS2780=m
+CONFIG_W1_SLAVE_DS2781=m
+CONFIG_W1_SLAVE_DS28E04=m
+CONFIG_W1_SLAVE_DS28E17=m
+CONFIG_POWER_RESET=y
+CONFIG_POWER_RESET_GPIO=y
+CONFIG_RPI_POE_POWER=m
+CONFIG_BATTERY_DS2760=m
+CONFIG_BATTERY_MAX17040=m
+CONFIG_BATTERY_GAUGE_LTC2941=m
+CONFIG_SENSORS_DS1621=m
+CONFIG_SENSORS_GPIO_FAN=m
+CONFIG_SENSORS_IIO_HWMON=m
+CONFIG_SENSORS_JC42=m
+CONFIG_SENSORS_LM75=m
+CONFIG_SENSORS_RASPBERRYPI_HWMON=m
+CONFIG_SENSORS_RPI_POE_FAN=m
+CONFIG_SENSORS_SHT21=m
+CONFIG_SENSORS_SHT3x=m
+CONFIG_SENSORS_SHTC1=m
+CONFIG_SENSORS_INA2XX=m
+CONFIG_SENSORS_TMP102=m
+CONFIG_THERMAL=y
+CONFIG_BCM2711_THERMAL=y
+CONFIG_WATCHDOG=y
+CONFIG_GPIO_WATCHDOG=m
+CONFIG_BCM2835_WDT=y
+CONFIG_MFD_STMPE=y
+CONFIG_STMPE_SPI=y
+CONFIG_MFD_ARIZONA_I2C=m
+CONFIG_MFD_ARIZONA_SPI=m
+CONFIG_MFD_WM5102=y
+CONFIG_REGULATOR=y
+CONFIG_REGULATOR_FIXED_VOLTAGE=y
+CONFIG_REGULATOR_ARIZONA_LDO1=m
+CONFIG_REGULATOR_ARIZONA_MICSUPP=m
+CONFIG_REGULATOR_GPIO=y
+CONFIG_RC_CORE=y
+CONFIG_LIRC=y
+CONFIG_BPF_LIRC_MODE2=y
+CONFIG_RC_DECODERS=y
+CONFIG_IR_NEC_DECODER=m
+CONFIG_IR_RC5_DECODER=m
+CONFIG_IR_RC6_DECODER=m
+CONFIG_IR_JVC_DECODER=m
+CONFIG_IR_SONY_DECODER=m
+CONFIG_IR_SANYO_DECODER=m
+CONFIG_IR_SHARP_DECODER=m
+CONFIG_IR_MCE_KBD_DECODER=m
+CONFIG_IR_XMP_DECODER=m
+CONFIG_IR_IMON_DECODER=m
+CONFIG_RC_DEVICES=y
+CONFIG_RC_ATI_REMOTE=m
+CONFIG_IR_IMON=m
+CONFIG_IR_MCEUSB=m
+CONFIG_IR_REDRAT3=m
+CONFIG_IR_STREAMZAP=m
+CONFIG_IR_IGUANA=m
+CONFIG_IR_TTUSBIR=m
+CONFIG_RC_LOOPBACK=m
+CONFIG_IR_GPIO_CIR=m
+CONFIG_IR_GPIO_TX=m
+CONFIG_IR_PWM_TX=m
+CONFIG_IR_TOY=m
+CONFIG_MEDIA_CEC_RC=y
+CONFIG_MEDIA_SUPPORT=m
+CONFIG_MEDIA_USB_SUPPORT=y
+CONFIG_USB_VIDEO_CLASS=m
+CONFIG_USB_M5602=m
+CONFIG_USB_STV06XX=m
+CONFIG_USB_GL860=m
+CONFIG_USB_GSPCA_BENQ=m
+CONFIG_USB_GSPCA_CONEX=m
+CONFIG_USB_GSPCA_CPIA1=m
+CONFIG_USB_GSPCA_DTCS033=m
+CONFIG_USB_GSPCA_ETOMS=m
+CONFIG_USB_GSPCA_FINEPIX=m
+CONFIG_USB_GSPCA_JEILINJ=m
+CONFIG_USB_GSPCA_JL2005BCD=m
+CONFIG_USB_GSPCA_KINECT=m
+CONFIG_USB_GSPCA_KONICA=m
+CONFIG_USB_GSPCA_MARS=m
+CONFIG_USB_GSPCA_MR97310A=m
+CONFIG_USB_GSPCA_NW80X=m
+CONFIG_USB_GSPCA_OV519=m
+CONFIG_USB_GSPCA_OV534=m
+CONFIG_USB_GSPCA_OV534_9=m
+CONFIG_USB_GSPCA_PAC207=m
+CONFIG_USB_GSPCA_PAC7302=m
+CONFIG_USB_GSPCA_PAC7311=m
+CONFIG_USB_GSPCA_SE401=m
+CONFIG_USB_GSPCA_SN9C2028=m
+CONFIG_USB_GSPCA_SN9C20X=m
+CONFIG_USB_GSPCA_SONIXB=m
+CONFIG_USB_GSPCA_SONIXJ=m
+CONFIG_USB_GSPCA_SPCA500=m
+CONFIG_USB_GSPCA_SPCA501=m
+CONFIG_USB_GSPCA_SPCA505=m
+CONFIG_USB_GSPCA_SPCA506=m
+CONFIG_USB_GSPCA_SPCA508=m
+CONFIG_USB_GSPCA_SPCA561=m
+CONFIG_USB_GSPCA_SPCA1528=m
+CONFIG_USB_GSPCA_SQ905=m
+CONFIG_USB_GSPCA_SQ905C=m
+CONFIG_USB_GSPCA_SQ930X=m
+CONFIG_USB_GSPCA_STK014=m
+CONFIG_USB_GSPCA_STK1135=m
+CONFIG_USB_GSPCA_STV0680=m
+CONFIG_USB_GSPCA_SUNPLUS=m
+CONFIG_USB_GSPCA_T613=m
+CONFIG_USB_GSPCA_TOPRO=m
+CONFIG_USB_GSPCA_TV8532=m
+CONFIG_USB_GSPCA_VC032X=m
+CONFIG_USB_GSPCA_VICAM=m
+CONFIG_USB_GSPCA_XIRLINK_CIT=m
+CONFIG_USB_GSPCA_ZC3XX=m
+CONFIG_USB_PWC=m
+CONFIG_VIDEO_CPIA2=m
+CONFIG_USB_ZR364XX=m
+CONFIG_USB_STKWEBCAM=m
+CONFIG_USB_S2255=m
+CONFIG_VIDEO_USBTV=m
+CONFIG_VIDEO_PVRUSB2=m
+CONFIG_VIDEO_HDPVR=m
+CONFIG_VIDEO_STK1160_COMMON=m
+CONFIG_VIDEO_GO7007=m
+CONFIG_VIDEO_GO7007_USB=m
+CONFIG_VIDEO_GO7007_USB_S2250_BOARD=m
+CONFIG_VIDEO_AU0828=m
+CONFIG_VIDEO_AU0828_RC=y
+CONFIG_VIDEO_CX231XX=m
+CONFIG_VIDEO_CX231XX_ALSA=m
+CONFIG_VIDEO_CX231XX_DVB=m
+CONFIG_VIDEO_TM6000=m
+CONFIG_VIDEO_TM6000_ALSA=m
+CONFIG_VIDEO_TM6000_DVB=m
+CONFIG_DVB_USB=m
+CONFIG_DVB_USB_A800=m
+CONFIG_DVB_USB_DIBUSB_MB=m
+CONFIG_DVB_USB_DIBUSB_MB_FAULTY=y
+CONFIG_DVB_USB_DIBUSB_MC=m
+CONFIG_DVB_USB_DIB0700=m
+CONFIG_DVB_USB_UMT_010=m
+CONFIG_DVB_USB_CXUSB=m
+CONFIG_DVB_USB_M920X=m
+CONFIG_DVB_USB_DIGITV=m
+CONFIG_DVB_USB_VP7045=m
+CONFIG_DVB_USB_VP702X=m
+CONFIG_DVB_USB_GP8PSK=m
+CONFIG_DVB_USB_NOVA_T_USB2=m
+CONFIG_DVB_USB_TTUSB2=m
+CONFIG_DVB_USB_DTT200U=m
+CONFIG_DVB_USB_OPERA1=m
+CONFIG_DVB_USB_AF9005=m
+CONFIG_DVB_USB_AF9005_REMOTE=m
+CONFIG_DVB_USB_PCTV452E=m
+CONFIG_DVB_USB_DW2102=m
+CONFIG_DVB_USB_CINERGY_T2=m
+CONFIG_DVB_USB_DTV5100=m
+CONFIG_DVB_USB_AZ6027=m
+CONFIG_DVB_USB_TECHNISAT_USB2=m
+CONFIG_DVB_USB_V2=m
+CONFIG_DVB_USB_AF9015=m
+CONFIG_DVB_USB_AF9035=m
+CONFIG_DVB_USB_ANYSEE=m
+CONFIG_DVB_USB_AU6610=m
+CONFIG_DVB_USB_AZ6007=m
+CONFIG_DVB_USB_CE6230=m
+CONFIG_DVB_USB_EC168=m
+CONFIG_DVB_USB_GL861=m
+CONFIG_DVB_USB_LME2510=m
+CONFIG_DVB_USB_MXL111SF=m
+CONFIG_DVB_USB_RTL28XXU=m
+CONFIG_DVB_USB_DVBSKY=m
+CONFIG_SMS_USB_DRV=m
+CONFIG_DVB_B2C2_FLEXCOP_USB=m
+CONFIG_DVB_AS102=m
+CONFIG_VIDEO_EM28XX=m
+CONFIG_VIDEO_EM28XX_V4L2=m
+CONFIG_VIDEO_EM28XX_ALSA=m
+CONFIG_VIDEO_EM28XX_DVB=m
+CONFIG_RADIO_SI470X=m
+CONFIG_USB_SI470X=m
+CONFIG_I2C_SI470X=m
+CONFIG_RADIO_SI4713=m
+CONFIG_I2C_SI4713=m
+CONFIG_USB_MR800=m
+CONFIG_USB_DSBR=m
+CONFIG_RADIO_SHARK=m
+CONFIG_RADIO_SHARK2=m
+CONFIG_USB_KEENE=m
+CONFIG_USB_MA901=m
+CONFIG_RADIO_TEA5764=m
+CONFIG_RADIO_SAA7706H=m
+CONFIG_RADIO_TEF6862=m
+CONFIG_RADIO_WL1273=m
+CONFIG_RADIO_WL128X=m
+CONFIG_V4L_PLATFORM_DRIVERS=y
+CONFIG_VIDEO_BCM2835_UNICAM=m
+CONFIG_VIDEO_UDA1342=m
+CONFIG_VIDEO_SONY_BTF_MPX=m
+CONFIG_VIDEO_ADV7180=m
+CONFIG_VIDEO_TC358743=m
+CONFIG_VIDEO_TVP5150=m
+CONFIG_VIDEO_TW2804=m
+CONFIG_VIDEO_TW9903=m
+CONFIG_VIDEO_TW9906=m
+CONFIG_VIDEO_IMX219=m
+CONFIG_VIDEO_IMX290=m
+CONFIG_VIDEO_IMX477=m
+CONFIG_VIDEO_OV5647=m
+CONFIG_VIDEO_OV7251=m
+CONFIG_VIDEO_OV7640=m
+CONFIG_VIDEO_OV9281=m
+CONFIG_VIDEO_IRS1125=m
+CONFIG_VIDEO_MT9V011=m
+CONFIG_DRM=m
+CONFIG_DRM_LOAD_EDID_FIRMWARE=y
+CONFIG_DRM_UDL=m
+CONFIG_DRM_PANEL_SIMPLE=m
+CONFIG_DRM_PANEL_JDI_LT070ME05000=m
+CONFIG_DRM_PANEL_RASPBERRYPI_TOUCHSCREEN=m
+CONFIG_DRM_DISPLAY_CONNECTOR=m
+CONFIG_DRM_SIMPLE_BRIDGE=m
+CONFIG_DRM_V3D=m
+CONFIG_DRM_VC4=m
+CONFIG_DRM_VC4_HDMI_CEC=y
+CONFIG_TINYDRM_ILI9225=m
+CONFIG_TINYDRM_ILI9341=m
+CONFIG_TINYDRM_MI0283QT=m
+CONFIG_TINYDRM_REPAPER=m
+CONFIG_TINYDRM_ST7586=m
+CONFIG_TINYDRM_ST7735R=m
+CONFIG_DRM_GUD=m
+CONFIG_FB=y
+CONFIG_FB_BCM2708=y
+CONFIG_FB_UDL=m
+CONFIG_FB_SIMPLE=y
+CONFIG_FB_SSD1307=m
+CONFIG_FB_RPISENSE=m
+CONFIG_BACKLIGHT_RPI=m
+CONFIG_BACKLIGHT_GPIO=m
+CONFIG_FRAMEBUFFER_CONSOLE=y
+CONFIG_FRAMEBUFFER_CONSOLE_ROTATION=y
+CONFIG_LOGO=y
+# CONFIG_LOGO_LINUX_MONO is not set
+# CONFIG_LOGO_LINUX_VGA16 is not set
+CONFIG_SOUND=y
+CONFIG_SND=m
+CONFIG_SND_OSSEMUL=y
+CONFIG_SND_PCM_OSS=m
+CONFIG_SND_HRTIMER=m
+CONFIG_SND_SEQUENCER=m
+CONFIG_SND_SEQ_DUMMY=m
+CONFIG_SND_DUMMY=m
+CONFIG_SND_ALOOP=m
+CONFIG_SND_VIRMIDI=m
+CONFIG_SND_MTPAV=m
+CONFIG_SND_SERIAL_U16550=m
+CONFIG_SND_MPU401=m
+CONFIG_SND_USB_AUDIO=m
+CONFIG_SND_USB_UA101=m
+CONFIG_SND_USB_CAIAQ=m
+CONFIG_SND_USB_CAIAQ_INPUT=y
+CONFIG_SND_USB_6FIRE=m
+CONFIG_SND_USB_HIFACE=m
+CONFIG_SND_SOC=m
+CONFIG_SND_BCM2835_SOC_I2S=m
+CONFIG_SND_BCM2708_SOC_GOOGLEVOICEHAT_SOUNDCARD=m
+CONFIG_SND_BCM2708_SOC_HIFIBERRY_DAC=m
+CONFIG_SND_BCM2708_SOC_HIFIBERRY_DACPLUS=m
+CONFIG_SND_BCM2708_SOC_HIFIBERRY_DACPLUSHD=m
+CONFIG_SND_BCM2708_SOC_HIFIBERRY_DACPLUSADC=m
+CONFIG_SND_BCM2708_SOC_HIFIBERRY_DACPLUSADCPRO=m
+CONFIG_SND_BCM2708_SOC_HIFIBERRY_DACPLUSDSP=m
+CONFIG_SND_BCM2708_SOC_HIFIBERRY_DIGI=m
+CONFIG_SND_BCM2708_SOC_HIFIBERRY_AMP=m
+CONFIG_SND_BCM2708_SOC_PIFI_40=m
+CONFIG_SND_BCM2708_SOC_RPI_CIRRUS=m
+CONFIG_SND_BCM2708_SOC_RPI_DAC=m
+CONFIG_SND_BCM2708_SOC_RPI_PROTO=m
+CONFIG_SND_BCM2708_SOC_JUSTBOOM_BOTH=m
+CONFIG_SND_BCM2708_SOC_JUSTBOOM_DAC=m
+CONFIG_SND_BCM2708_SOC_JUSTBOOM_DIGI=m
+CONFIG_SND_BCM2708_SOC_IQAUDIO_CODEC=m
+CONFIG_SND_BCM2708_SOC_IQAUDIO_DAC=m
+CONFIG_SND_BCM2708_SOC_IQAUDIO_DIGI=m
+CONFIG_SND_BCM2708_SOC_I_SABRE_Q2M=m
+CONFIG_SND_BCM2708_SOC_ADAU1977_ADC=m
+CONFIG_SND_AUDIOINJECTOR_PI_SOUNDCARD=m
+CONFIG_SND_AUDIOINJECTOR_OCTO_SOUNDCARD=m
+CONFIG_SND_AUDIOINJECTOR_ISOLATED_SOUNDCARD=m
+CONFIG_SND_AUDIOSENSE_PI=m
+CONFIG_SND_DIGIDAC1_SOUNDCARD=m
+CONFIG_SND_BCM2708_SOC_DIONAUDIO_LOCO=m
+CONFIG_SND_BCM2708_SOC_DIONAUDIO_LOCO_V2=m
+CONFIG_SND_BCM2708_SOC_ALLO_PIANO_DAC=m
+CONFIG_SND_BCM2708_SOC_ALLO_PIANO_DAC_PLUS=m
+CONFIG_SND_BCM2708_SOC_ALLO_BOSS_DAC=m
+CONFIG_SND_BCM2708_SOC_ALLO_BOSS2_DAC=m
+CONFIG_SND_BCM2708_SOC_ALLO_DIGIONE=m
+CONFIG_SND_BCM2708_SOC_ALLO_KATANA_DAC=m
+CONFIG_SND_BCM2708_SOC_FE_PI_AUDIO=m
+CONFIG_SND_PISOUND=m
+CONFIG_SND_SOC_AD193X_SPI=m
+CONFIG_SND_SOC_AD193X_I2C=m
+CONFIG_SND_SOC_ADAU1701=m
+CONFIG_SND_SOC_ADAU7002=m
+CONFIG_SND_SOC_AK4554=m
+CONFIG_SND_SOC_CS4265=m
+CONFIG_SND_SOC_ICS43432=m
+CONFIG_SND_SOC_MA120X0P=m
+CONFIG_SND_SOC_MAX98357A=m
+CONFIG_SND_SOC_SPDIF=m
+CONFIG_SND_SOC_WM8804_I2C=m
+CONFIG_SND_SOC_WM8960=m
+CONFIG_SND_SIMPLE_CARD=m
+CONFIG_HID_BATTERY_STRENGTH=y
+CONFIG_HIDRAW=y
+CONFIG_UHID=m
+CONFIG_HID_A4TECH=m
+CONFIG_HID_ACRUX=m
+CONFIG_HID_APPLE=m
+CONFIG_HID_ASUS=m
+CONFIG_HID_BELKIN=m
+CONFIG_HID_BETOP_FF=m
+CONFIG_HID_BIGBEN_FF=m
+CONFIG_HID_CHERRY=m
+CONFIG_HID_CHICONY=m
+CONFIG_HID_CYPRESS=m
+CONFIG_HID_DRAGONRISE=m
+CONFIG_HID_EMS_FF=m
+CONFIG_HID_ELECOM=m
+CONFIG_HID_ELO=m
+CONFIG_HID_EZKEY=m
+CONFIG_HID_GEMBIRD=m
+CONFIG_HID_HOLTEK=m
+CONFIG_HID_KEYTOUCH=m
+CONFIG_HID_KYE=m
+CONFIG_HID_UCLOGIC=m
+CONFIG_HID_WALTOP=m
+CONFIG_HID_GYRATION=m
+CONFIG_HID_TWINHAN=m
+CONFIG_HID_KENSINGTON=m
+CONFIG_HID_LCPOWER=m
+CONFIG_HID_LOGITECH=m
+CONFIG_HID_LOGITECH_DJ=m
+CONFIG_LOGITECH_FF=y
+CONFIG_LOGIRUMBLEPAD2_FF=y
+CONFIG_LOGIG940_FF=y
+CONFIG_HID_MAGICMOUSE=m
+CONFIG_HID_MICROSOFT=m
+CONFIG_HID_MONTEREY=m
+CONFIG_HID_MULTITOUCH=m
+CONFIG_HID_NTRIG=m
+CONFIG_HID_ORTEK=m
+CONFIG_HID_PANTHERLORD=m
+CONFIG_HID_PETALYNX=m
+CONFIG_HID_PICOLCD=m
+CONFIG_HID_ROCCAT=m
+CONFIG_HID_SAMSUNG=m
+CONFIG_HID_SONY=m
+CONFIG_SONY_FF=y
+CONFIG_HID_SPEEDLINK=m
+CONFIG_HID_STEAM=m
+CONFIG_HID_SUNPLUS=m
+CONFIG_HID_GREENASIA=m
+CONFIG_HID_SMARTJOYPLUS=m
+CONFIG_HID_TOPSEED=m
+CONFIG_HID_THINGM=m
+CONFIG_HID_THRUSTMASTER=m
+CONFIG_HID_WACOM=m
+CONFIG_HID_WIIMOTE=m
+CONFIG_HID_XINMO=m
+CONFIG_HID_ZEROPLUS=m
+CONFIG_HID_ZYDACRON=m
+CONFIG_HID_PID=y
+CONFIG_USB_HIDDEV=y
+CONFIG_I2C_HID=m
+CONFIG_USB=y
+CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
+CONFIG_USB_MON=m
+CONFIG_USB_XHCI_HCD=y
+CONFIG_USB_XHCI_PLATFORM=y
+CONFIG_USB_DWCOTG=y
+CONFIG_USB_PRINTER=m
+CONFIG_USB_TMC=m
+CONFIG_USB_STORAGE=y
+CONFIG_USB_STORAGE_REALTEK=m
+CONFIG_USB_STORAGE_DATAFAB=m
+CONFIG_USB_STORAGE_FREECOM=m
+CONFIG_USB_STORAGE_ISD200=m
+CONFIG_USB_STORAGE_USBAT=m
+CONFIG_USB_STORAGE_SDDR09=m
+CONFIG_USB_STORAGE_SDDR55=m
+CONFIG_USB_STORAGE_JUMPSHOT=m
+CONFIG_USB_STORAGE_ALAUDA=m
+CONFIG_USB_STORAGE_ONETOUCH=m
+CONFIG_USB_STORAGE_KARMA=m
+CONFIG_USB_STORAGE_CYPRESS_ATACB=m
+CONFIG_USB_STORAGE_ENE_UB6250=m
+CONFIG_USB_UAS=y
+CONFIG_USB_MDC800=m
+CONFIG_USB_MICROTEK=m
+CONFIG_USBIP_CORE=m
+CONFIG_USBIP_VHCI_HCD=m
+CONFIG_USBIP_HOST=m
+CONFIG_USBIP_VUDC=m
+CONFIG_USB_DWC2=m
+CONFIG_USB_SERIAL=m
+CONFIG_USB_SERIAL_GENERIC=y
+CONFIG_USB_SERIAL_AIRCABLE=m
+CONFIG_USB_SERIAL_ARK3116=m
+CONFIG_USB_SERIAL_BELKIN=m
+CONFIG_USB_SERIAL_CH341=m
+CONFIG_USB_SERIAL_WHITEHEAT=m
+CONFIG_USB_SERIAL_DIGI_ACCELEPORT=m
+CONFIG_USB_SERIAL_CP210X=m
+CONFIG_USB_SERIAL_CYPRESS_M8=m
+CONFIG_USB_SERIAL_EMPEG=m
+CONFIG_USB_SERIAL_FTDI_SIO=m
+CONFIG_USB_SERIAL_VISOR=m
+CONFIG_USB_SERIAL_IPAQ=m
+CONFIG_USB_SERIAL_IR=m
+CONFIG_USB_SERIAL_EDGEPORT=m
+CONFIG_USB_SERIAL_EDGEPORT_TI=m
+CONFIG_USB_SERIAL_F81232=m
+CONFIG_USB_SERIAL_GARMIN=m
+CONFIG_USB_SERIAL_IPW=m
+CONFIG_USB_SERIAL_IUU=m
+CONFIG_USB_SERIAL_KEYSPAN_PDA=m
+CONFIG_USB_SERIAL_KEYSPAN=m
+CONFIG_USB_SERIAL_KLSI=m
+CONFIG_USB_SERIAL_KOBIL_SCT=m
+CONFIG_USB_SERIAL_MCT_U232=m
+CONFIG_USB_SERIAL_METRO=m
+CONFIG_USB_SERIAL_MOS7720=m
+CONFIG_USB_SERIAL_MOS7840=m
+CONFIG_USB_SERIAL_NAVMAN=m
+CONFIG_USB_SERIAL_PL2303=m
+CONFIG_USB_SERIAL_OTI6858=m
+CONFIG_USB_SERIAL_QCAUX=m
+CONFIG_USB_SERIAL_QUALCOMM=m
+CONFIG_USB_SERIAL_SPCP8X5=m
+CONFIG_USB_SERIAL_SAFE=m
+CONFIG_USB_SERIAL_SIERRAWIRELESS=m
+CONFIG_USB_SERIAL_SYMBOL=m
+CONFIG_USB_SERIAL_TI=m
+CONFIG_USB_SERIAL_CYBERJACK=m
+CONFIG_USB_SERIAL_XIRCOM=m
+CONFIG_USB_SERIAL_OPTION=m
+CONFIG_USB_SERIAL_OMNINET=m
+CONFIG_USB_SERIAL_OPTICON=m
+CONFIG_USB_SERIAL_XSENS_MT=m
+CONFIG_USB_SERIAL_WISHBONE=m
+CONFIG_USB_SERIAL_SSU100=m
+CONFIG_USB_SERIAL_QT2=m
+CONFIG_USB_SERIAL_DEBUG=m
+CONFIG_USB_EMI62=m
+CONFIG_USB_EMI26=m
+CONFIG_USB_ADUTUX=m
+CONFIG_USB_SEVSEG=m
+CONFIG_USB_LEGOTOWER=m
+CONFIG_USB_LCD=m
+CONFIG_USB_CYPRESS_CY7C63=m
+CONFIG_USB_CYTHERM=m
+CONFIG_USB_IDMOUSE=m
+CONFIG_USB_FTDI_ELAN=m
+CONFIG_USB_APPLEDISPLAY=m
+CONFIG_USB_LD=m
+CONFIG_USB_TRANCEVIBRATOR=m
+CONFIG_USB_IOWARRIOR=m
+CONFIG_USB_TEST=m
+CONFIG_USB_ISIGHTFW=m
+CONFIG_USB_YUREX=m
+CONFIG_USB_ATM=m
+CONFIG_USB_SPEEDTOUCH=m
+CONFIG_USB_CXACRU=m
+CONFIG_USB_UEAGLEATM=m
+CONFIG_USB_XUSBATM=m
+CONFIG_NOP_USB_XCEIV=y
+CONFIG_USB_GADGET=y
+CONFIG_USB_CONFIGFS=m
+CONFIG_USB_CONFIGFS_SERIAL=y
+CONFIG_USB_CONFIGFS_ACM=y
+CONFIG_USB_CONFIGFS_OBEX=y
+CONFIG_USB_CONFIGFS_NCM=y
+CONFIG_USB_CONFIGFS_ECM=y
+CONFIG_USB_CONFIGFS_ECM_SUBSET=y
+CONFIG_USB_CONFIGFS_RNDIS=y
+CONFIG_USB_CONFIGFS_EEM=y
+CONFIG_USB_CONFIGFS_MASS_STORAGE=y
+CONFIG_USB_CONFIGFS_F_LB_SS=y
+CONFIG_USB_CONFIGFS_F_FS=y
+CONFIG_USB_CONFIGFS_F_UAC1=y
+CONFIG_USB_CONFIGFS_F_UAC2=y
+CONFIG_USB_CONFIGFS_F_MIDI=y
+CONFIG_USB_CONFIGFS_F_HID=y
+CONFIG_USB_CONFIGFS_F_UVC=y
+CONFIG_USB_CONFIGFS_F_PRINTER=y
+CONFIG_USB_ZERO=m
+CONFIG_USB_AUDIO=m
+CONFIG_USB_ETH=m
+CONFIG_USB_GADGETFS=m
+CONFIG_USB_MASS_STORAGE=m
+CONFIG_USB_G_SERIAL=m
+CONFIG_USB_MIDI_GADGET=m
+CONFIG_USB_G_PRINTER=m
+CONFIG_USB_CDC_COMPOSITE=m
+CONFIG_USB_G_ACM_MS=m
+CONFIG_USB_G_MULTI=m
+CONFIG_USB_G_HID=m
+CONFIG_USB_G_WEBCAM=m
+CONFIG_MMC=y
+CONFIG_MMC_BLOCK_MINORS=32
+CONFIG_MMC_BCM2835_MMC=y
+CONFIG_MMC_BCM2835_DMA=y
+CONFIG_MMC_BCM2835_SDHOST=y
+CONFIG_MMC_SDHCI=y
+CONFIG_MMC_SDHCI_PLTFM=y
+CONFIG_MMC_SDHCI_IPROC=y
+CONFIG_MMC_SPI=m
+CONFIG_LEDS_CLASS=y
+CONFIG_LEDS_PCA9532=m
+CONFIG_LEDS_GPIO=y
+CONFIG_LEDS_PCA955X=m
+CONFIG_LEDS_PCA963X=m
+CONFIG_LEDS_IS31FL32XX=m
+CONFIG_LEDS_TRIGGER_TIMER=y
+CONFIG_LEDS_TRIGGER_ONESHOT=y
+CONFIG_LEDS_TRIGGER_HEARTBEAT=y
+CONFIG_LEDS_TRIGGER_BACKLIGHT=y
+CONFIG_LEDS_TRIGGER_CPU=y
+CONFIG_LEDS_TRIGGER_GPIO=y
+CONFIG_LEDS_TRIGGER_DEFAULT_ON=y
+CONFIG_LEDS_TRIGGER_TRANSIENT=m
+CONFIG_LEDS_TRIGGER_CAMERA=m
+CONFIG_LEDS_TRIGGER_INPUT=y
+CONFIG_LEDS_TRIGGER_PANIC=y
+CONFIG_LEDS_TRIGGER_NETDEV=m
+CONFIG_LEDS_TRIGGER_ACTPWR=y
+CONFIG_ACCESSIBILITY=y
+CONFIG_SPEAKUP=m
+CONFIG_SPEAKUP_SYNTH_SOFT=m
+CONFIG_RTC_CLASS=y
+CONFIG_RTC_DRV_ABX80X=m
+CONFIG_RTC_DRV_DS1307=m
+CONFIG_RTC_DRV_DS1374=m
+CONFIG_RTC_DRV_DS1672=m
+CONFIG_RTC_DRV_MAX6900=m
+CONFIG_RTC_DRV_RS5C372=m
+CONFIG_RTC_DRV_ISL1208=m
+CONFIG_RTC_DRV_ISL12022=m
+CONFIG_RTC_DRV_X1205=m
+CONFIG_RTC_DRV_PCF8523=m
+CONFIG_RTC_DRV_PCF85063=m
+CONFIG_RTC_DRV_PCF85363=m
+CONFIG_RTC_DRV_PCF8563=m
+CONFIG_RTC_DRV_PCF8583=m
+CONFIG_RTC_DRV_M41T80=m
+CONFIG_RTC_DRV_BQ32K=m
+CONFIG_RTC_DRV_S35390A=m
+CONFIG_RTC_DRV_FM3130=m
+CONFIG_RTC_DRV_RX8581=m
+CONFIG_RTC_DRV_RX8025=m
+CONFIG_RTC_DRV_EM3027=m
+CONFIG_RTC_DRV_RV3028=m
+CONFIG_RTC_DRV_SD3078=m
+CONFIG_RTC_DRV_M41T93=m
+CONFIG_RTC_DRV_M41T94=m
+CONFIG_RTC_DRV_DS1302=m
+CONFIG_RTC_DRV_DS1305=m
+CONFIG_RTC_DRV_DS1390=m
+CONFIG_RTC_DRV_R9701=m
+CONFIG_RTC_DRV_RX4581=m
+CONFIG_RTC_DRV_RS5C348=m
+CONFIG_RTC_DRV_MAX6902=m
+CONFIG_RTC_DRV_PCF2123=m
+CONFIG_RTC_DRV_DS3232=m
+CONFIG_RTC_DRV_PCF2127=m
+CONFIG_RTC_DRV_RV3029C2=m
+CONFIG_DMADEVICES=y
+CONFIG_DMA_BCM2835=y
+CONFIG_DMA_BCM2708=y
+CONFIG_DMABUF_HEAPS=y
+CONFIG_DMABUF_HEAPS_SYSTEM=y
+CONFIG_DMABUF_HEAPS_CMA=y
+CONFIG_AUXDISPLAY=y
+CONFIG_HD44780=m
+CONFIG_UIO=m
+CONFIG_UIO_PDRV_GENIRQ=m
+CONFIG_STAGING=y
+CONFIG_PRISM2_USB=m
+CONFIG_R8712U=m
+CONFIG_R8188EU=m
+CONFIG_VT6656=m
+CONFIG_STAGING_MEDIA=y
+CONFIG_VIDEO_RPIVID=m
+CONFIG_FB_TFT=m
+CONFIG_FB_TFT_AGM1264K_FL=m
+CONFIG_FB_TFT_BD663474=m
+CONFIG_FB_TFT_HX8340BN=m
+CONFIG_FB_TFT_HX8347D=m
+CONFIG_FB_TFT_HX8353D=m
+CONFIG_FB_TFT_HX8357D=m
+CONFIG_FB_TFT_ILI9163=m
+CONFIG_FB_TFT_ILI9320=m
+CONFIG_FB_TFT_ILI9325=m
+CONFIG_FB_TFT_ILI9340=m
+CONFIG_FB_TFT_ILI9341=m
+CONFIG_FB_TFT_ILI9481=m
+CONFIG_FB_TFT_ILI9486=m
+CONFIG_FB_TFT_PCD8544=m
+CONFIG_FB_TFT_RA8875=m
+CONFIG_FB_TFT_S6D02A1=m
+CONFIG_FB_TFT_S6D1121=m
+CONFIG_FB_TFT_SH1106=m
+CONFIG_FB_TFT_SSD1289=m
+CONFIG_FB_TFT_SSD1306=m
+CONFIG_FB_TFT_SSD1331=m
+CONFIG_FB_TFT_SSD1351=m
+CONFIG_FB_TFT_ST7735R=m
+CONFIG_FB_TFT_ST7789V=m
+CONFIG_FB_TFT_TINYLCD=m
+CONFIG_FB_TFT_TLS8204=m
+CONFIG_FB_TFT_UC1701=m
+CONFIG_FB_TFT_UPD161704=m
+CONFIG_FB_TFT_WATTEROTT=m
+CONFIG_BCM2835_VCHIQ=y
+CONFIG_SND_BCM2835=m
+CONFIG_VIDEO_BCM2835=m
+CONFIG_VIDEO_CODEC_BCM2835=m
+CONFIG_VIDEO_ISP_BCM2835=m
+CONFIG_CLK_RASPBERRYPI=y
+CONFIG_MAILBOX=y
+CONFIG_BCM2835_MBOX=y
+# CONFIG_IOMMU_SUPPORT is not set
+CONFIG_RASPBERRYPI_POWER=y
+CONFIG_EXTCON_ARIZONA=m
+CONFIG_IIO=m
+CONFIG_IIO_BUFFER_CB=m
+CONFIG_MCP320X=m
+CONFIG_MCP3422=m
+CONFIG_TI_ADS1015=m
+CONFIG_BME680=m
+CONFIG_CCS811=m
+CONFIG_SENSIRION_SGP30=m
+CONFIG_SPS30=m
+CONFIG_DHT11=m
+CONFIG_HDC100X=m
+CONFIG_HTU21=m
+CONFIG_INV_MPU6050_I2C=m
+CONFIG_APDS9960=m
+CONFIG_BH1750=m
+CONFIG_TSL4531=m
+CONFIG_VEML6070=m
+CONFIG_BMP280=m
+CONFIG_MAXIM_THERMOCOUPLE=m
+CONFIG_MAX31856=m
+CONFIG_PWM_BCM2835=m
+CONFIG_PWM_PCA9685=m
+CONFIG_RPI_AXIPERF=m
+CONFIG_NVMEM_RMEM=m
+CONFIG_EXT4_FS=y
+CONFIG_EXT4_FS_POSIX_ACL=y
+CONFIG_EXT4_FS_SECURITY=y
+CONFIG_REISERFS_FS=m
+CONFIG_REISERFS_FS_XATTR=y
+CONFIG_REISERFS_FS_POSIX_ACL=y
+CONFIG_REISERFS_FS_SECURITY=y
+CONFIG_JFS_FS=m
+CONFIG_JFS_POSIX_ACL=y
+CONFIG_JFS_SECURITY=y
+CONFIG_JFS_STATISTICS=y
+CONFIG_XFS_FS=m
+CONFIG_XFS_QUOTA=y
+CONFIG_XFS_POSIX_ACL=y
+CONFIG_XFS_RT=y
+CONFIG_GFS2_FS=m
+CONFIG_OCFS2_FS=m
+CONFIG_BTRFS_FS=m
+CONFIG_BTRFS_FS_POSIX_ACL=y
+CONFIG_NILFS2_FS=m
+CONFIG_F2FS_FS=y
+CONFIG_FS_ENCRYPTION=y
+CONFIG_FANOTIFY=y
+CONFIG_QFMT_V1=m
+CONFIG_QFMT_V2=m
+CONFIG_AUTOFS4_FS=y
+CONFIG_FUSE_FS=m
+CONFIG_CUSE=m
+CONFIG_OVERLAY_FS=m
+CONFIG_FSCACHE=y
+CONFIG_FSCACHE_STATS=y
+CONFIG_FSCACHE_HISTOGRAM=y
+CONFIG_CACHEFILES=y
+CONFIG_ISO9660_FS=m
+CONFIG_JOLIET=y
+CONFIG_ZISOFS=y
+CONFIG_UDF_FS=m
+CONFIG_MSDOS_FS=y
+CONFIG_VFAT_FS=y
+CONFIG_FAT_DEFAULT_IOCHARSET="ascii"
+CONFIG_EXFAT_FS=m
+CONFIG_NTFS_FS=m
+CONFIG_NTFS_RW=y
+CONFIG_TMPFS=y
+CONFIG_TMPFS_POSIX_ACL=y
+CONFIG_ECRYPT_FS=m
+CONFIG_HFS_FS=m
+CONFIG_HFSPLUS_FS=m
+CONFIG_JFFS2_FS=m
+CONFIG_JFFS2_SUMMARY=y
+CONFIG_UBIFS_FS=m
+CONFIG_SQUASHFS=m
+CONFIG_SQUASHFS_XATTR=y
+CONFIG_SQUASHFS_LZO=y
+CONFIG_SQUASHFS_XZ=y
+CONFIG_NFS_FS=y
+CONFIG_NFS_V3_ACL=y
+CONFIG_NFS_V4=y
+CONFIG_NFS_SWAP=y
+CONFIG_NFS_V4_1=y
+CONFIG_NFS_V4_2=y
+CONFIG_ROOT_NFS=y
+CONFIG_NFS_FSCACHE=y
+CONFIG_NFSD=m
+CONFIG_NFSD_V3_ACL=y
+CONFIG_NFSD_V4=y
+CONFIG_CIFS=m
+CONFIG_CIFS_WEAK_PW_HASH=y
+CONFIG_CIFS_UPCALL=y
+CONFIG_CIFS_XATTR=y
+CONFIG_CIFS_POSIX=y
+CONFIG_CIFS_DFS_UPCALL=y
+CONFIG_CIFS_FSCACHE=y
+CONFIG_9P_FS=m
+CONFIG_9P_FS_POSIX_ACL=y
+CONFIG_NLS_DEFAULT="utf8"
+CONFIG_NLS_CODEPAGE_437=y
+CONFIG_NLS_CODEPAGE_737=m
+CONFIG_NLS_CODEPAGE_775=m
+CONFIG_NLS_CODEPAGE_850=m
+CONFIG_NLS_CODEPAGE_852=m
+CONFIG_NLS_CODEPAGE_855=m
+CONFIG_NLS_CODEPAGE_857=m
+CONFIG_NLS_CODEPAGE_860=m
+CONFIG_NLS_CODEPAGE_861=m
+CONFIG_NLS_CODEPAGE_862=m
+CONFIG_NLS_CODEPAGE_863=m
+CONFIG_NLS_CODEPAGE_864=m
+CONFIG_NLS_CODEPAGE_865=m
+CONFIG_NLS_CODEPAGE_866=m
+CONFIG_NLS_CODEPAGE_869=m
+CONFIG_NLS_CODEPAGE_936=m
+CONFIG_NLS_CODEPAGE_950=m
+CONFIG_NLS_CODEPAGE_932=m
+CONFIG_NLS_CODEPAGE_949=m
+CONFIG_NLS_CODEPAGE_874=m
+CONFIG_NLS_ISO8859_8=m
+CONFIG_NLS_CODEPAGE_1250=m
+CONFIG_NLS_CODEPAGE_1251=m
+CONFIG_NLS_ASCII=y
+CONFIG_NLS_ISO8859_1=m
+CONFIG_NLS_ISO8859_2=m
+CONFIG_NLS_ISO8859_3=m
+CONFIG_NLS_ISO8859_4=m
+CONFIG_NLS_ISO8859_5=m
+CONFIG_NLS_ISO8859_6=m
+CONFIG_NLS_ISO8859_7=m
+CONFIG_NLS_ISO8859_9=m
+CONFIG_NLS_ISO8859_13=m
+CONFIG_NLS_ISO8859_14=m
+CONFIG_NLS_ISO8859_15=m
+CONFIG_NLS_KOI8_R=m
+CONFIG_NLS_KOI8_U=m
+CONFIG_DLM=m
+CONFIG_SECURITY=y
+CONFIG_SECURITY_APPARMOR=y
+CONFIG_LSM=""
+CONFIG_CRYPTO_USER=m
+CONFIG_CRYPTO_CHACHA20POLY1305=m
+CONFIG_CRYPTO_ADIANTUM=m
+CONFIG_CRYPTO_XCBC=m
+CONFIG_CRYPTO_TGR192=m
+CONFIG_CRYPTO_WP512=m
+CONFIG_CRYPTO_CAST5=m
+CONFIG_CRYPTO_DES=y
+CONFIG_CRYPTO_LZ4=m
+CONFIG_CRYPTO_USER_API_HASH=m
+CONFIG_CRYPTO_USER_API_SKCIPHER=m
+CONFIG_CRYPTO_USER_API_RNG=m
+CONFIG_CRYPTO_USER_API_AEAD=m
+# CONFIG_CRYPTO_HW is not set
+CONFIG_CRC_ITU_T=y
+CONFIG_LIBCRC32C=y
+CONFIG_DMA_CMA=y
+CONFIG_CMA_SIZE_MBYTES=5
+CONFIG_PRINTK_TIME=y
+CONFIG_BOOT_PRINTK_DELAY=y
+CONFIG_KGDB=y
+CONFIG_KGDB_KDB=y
+CONFIG_KDB_KEYBOARD=y
+CONFIG_DEBUG_MEMORY_INIT=y
+CONFIG_DETECT_HUNG_TASK=y
+# CONFIG_RCU_TRACE is not set
+CONFIG_LATENCYTOP=y
+CONFIG_FUNCTION_PROFILER=y
+CONFIG_STACK_TRACER=y
+CONFIG_IRQSOFF_TRACER=y
+CONFIG_SCHED_TRACER=y
+CONFIG_BLK_DEV_IO_TRACE=y
+# CONFIG_UPROBE_EVENTS is not set
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/configs/bcmrpi_defconfig linux-5.10.52-v7l+/arch/arm/configs/bcmrpi_defconfig
--- linux-5.10.52-orig/arch/arm/configs/bcmrpi_defconfig	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/configs/bcmrpi_defconfig	2021-07-25 16:45:35.368803870 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+# CONFIG_LOCALVERSION_AUTO is not set
+CONFIG_SYSVIPC=y
+CONFIG_POSIX_MQUEUE=y
+CONFIG_GENERIC_IRQ_DEBUGFS=y
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_PREEMPT_VOLUNTARY=y
+CONFIG_BSD_PROCESS_ACCT=y
+CONFIG_BSD_PROCESS_ACCT_V3=y
+CONFIG_TASKSTATS=y
+CONFIG_TASK_DELAY_ACCT=y
+CONFIG_TASK_XACCT=y
+CONFIG_TASK_IO_ACCOUNTING=y
+CONFIG_IKCONFIG=m
+CONFIG_IKCONFIG_PROC=y
+CONFIG_MEMCG=y
+CONFIG_BLK_CGROUP=y
+CONFIG_CFS_BANDWIDTH=y
+CONFIG_CGROUP_PIDS=y
+CONFIG_CGROUP_FREEZER=y
+CONFIG_CGROUP_DEVICE=y
+CONFIG_CGROUP_CPUACCT=y
+CONFIG_CGROUP_PERF=y
+CONFIG_CGROUP_BPF=y
+CONFIG_NAMESPACES=y
+CONFIG_USER_NS=y
+CONFIG_SCHED_AUTOGROUP=y
+CONFIG_BLK_DEV_INITRD=y
+CONFIG_BPF_SYSCALL=y
+CONFIG_EMBEDDED=y
+# CONFIG_COMPAT_BRK is not set
+CONFIG_PROFILING=y
+CONFIG_ARCH_MULTI_V6=y
+# CONFIG_ARCH_MULTI_V7 is not set
+CONFIG_ARCH_BCM=y
+CONFIG_ARCH_BCM2835=y
+# CONFIG_CACHE_L2X0 is not set
+# CONFIG_CPU_SW_DOMAIN_PAN is not set
+CONFIG_UACCESS_WITH_MEMCPY=y
+# CONFIG_ATAGS is not set
+CONFIG_CMDLINE="console=ttyAMA0,115200 kgdboc=ttyAMA0,115200 root=/dev/mmcblk0p2 rootfstype=ext4 rootwait"
+CONFIG_CPU_FREQ=y
+CONFIG_CPU_FREQ_STAT=y
+CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE=y
+CONFIG_CPU_FREQ_GOV_PERFORMANCE=y
+CONFIG_CPU_FREQ_GOV_USERSPACE=y
+CONFIG_CPU_FREQ_GOV_ONDEMAND=y
+CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y
+CONFIG_CPUFREQ_DT=y
+CONFIG_ARM_RASPBERRYPI_CPUFREQ=y
+CONFIG_VFP=y
+# CONFIG_SUSPEND is not set
+CONFIG_PM=y
+CONFIG_RASPBERRYPI_FIRMWARE=y
+CONFIG_CRYPTO_SHA1_ARM=m
+CONFIG_CRYPTO_AES_ARM=m
+CONFIG_OPROFILE=m
+CONFIG_JUMP_LABEL=y
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+CONFIG_MODVERSIONS=y
+CONFIG_MODULE_SRCVERSION_ALL=y
+CONFIG_PARTITION_ADVANCED=y
+CONFIG_MAC_PARTITION=y
+CONFIG_BINFMT_MISC=m
+CONFIG_CLEANCACHE=y
+CONFIG_FRONTSWAP=y
+CONFIG_CMA=y
+CONFIG_ZSWAP=y
+CONFIG_Z3FOLD=m
+CONFIG_ZSMALLOC=m
+CONFIG_NET=y
+CONFIG_PACKET=y
+CONFIG_UNIX=y
+CONFIG_XFRM_USER=y
+CONFIG_NET_KEY=m
+CONFIG_INET=y
+CONFIG_IP_MULTICAST=y
+CONFIG_IP_ADVANCED_ROUTER=y
+CONFIG_IP_MULTIPLE_TABLES=y
+CONFIG_IP_ROUTE_MULTIPATH=y
+CONFIG_IP_ROUTE_VERBOSE=y
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+CONFIG_IP_PNP_RARP=y
+CONFIG_NET_IPIP=m
+CONFIG_NET_IPGRE_DEMUX=m
+CONFIG_NET_IPGRE=m
+CONFIG_IP_MROUTE=y
+CONFIG_IP_MROUTE_MULTIPLE_TABLES=y
+CONFIG_IP_PIMSM_V1=y
+CONFIG_IP_PIMSM_V2=y
+CONFIG_NET_IPVTI=m
+CONFIG_NET_FOU=m
+CONFIG_INET_AH=m
+CONFIG_INET_ESP=m
+CONFIG_INET_IPCOMP=m
+CONFIG_INET_DIAG=m
+CONFIG_TCP_CONG_ADVANCED=y
+CONFIG_TCP_CONG_BBR=m
+CONFIG_IPV6=m
+CONFIG_IPV6_ROUTER_PREF=y
+CONFIG_IPV6_ROUTE_INFO=y
+CONFIG_INET6_AH=m
+CONFIG_INET6_ESP=m
+CONFIG_INET6_ESP_OFFLOAD=m
+CONFIG_INET6_IPCOMP=m
+CONFIG_IPV6_ILA=m
+CONFIG_IPV6_VTI=m
+CONFIG_IPV6_SIT_6RD=y
+CONFIG_IPV6_GRE=m
+CONFIG_IPV6_MULTIPLE_TABLES=y
+CONFIG_IPV6_SUBTREES=y
+CONFIG_IPV6_MROUTE=y
+CONFIG_IPV6_MROUTE_MULTIPLE_TABLES=y
+CONFIG_IPV6_PIMSM_V2=y
+CONFIG_NETFILTER=y
+CONFIG_BRIDGE_NETFILTER=m
+CONFIG_NF_CONNTRACK=m
+CONFIG_NF_LOG_NETDEV=m
+CONFIG_NF_CONNTRACK_ZONES=y
+CONFIG_NF_CONNTRACK_EVENTS=y
+CONFIG_NF_CONNTRACK_TIMESTAMP=y
+CONFIG_NF_CONNTRACK_AMANDA=m
+CONFIG_NF_CONNTRACK_FTP=m
+CONFIG_NF_CONNTRACK_H323=m
+CONFIG_NF_CONNTRACK_IRC=m
+CONFIG_NF_CONNTRACK_NETBIOS_NS=m
+CONFIG_NF_CONNTRACK_SNMP=m
+CONFIG_NF_CONNTRACK_PPTP=m
+CONFIG_NF_CONNTRACK_SANE=m
+CONFIG_NF_CONNTRACK_SIP=m
+CONFIG_NF_CONNTRACK_TFTP=m
+CONFIG_NF_CT_NETLINK=m
+CONFIG_NF_TABLES=m
+CONFIG_NF_TABLES_INET=y
+CONFIG_NF_TABLES_NETDEV=y
+CONFIG_NFT_NUMGEN=m
+CONFIG_NFT_CT=m
+CONFIG_NFT_FLOW_OFFLOAD=m
+CONFIG_NFT_COUNTER=m
+CONFIG_NFT_CONNLIMIT=m
+CONFIG_NFT_LOG=m
+CONFIG_NFT_LIMIT=m
+CONFIG_NFT_MASQ=m
+CONFIG_NFT_REDIR=m
+CONFIG_NFT_NAT=m
+CONFIG_NFT_TUNNEL=m
+CONFIG_NFT_OBJREF=m
+CONFIG_NFT_QUEUE=m
+CONFIG_NFT_QUOTA=m
+CONFIG_NFT_REJECT=m
+CONFIG_NFT_COMPAT=m
+CONFIG_NFT_HASH=m
+CONFIG_NFT_FIB_INET=m
+CONFIG_NFT_SOCKET=m
+CONFIG_NFT_OSF=m
+CONFIG_NFT_TPROXY=m
+CONFIG_NFT_DUP_NETDEV=m
+CONFIG_NFT_FWD_NETDEV=m
+CONFIG_NFT_FIB_NETDEV=m
+CONFIG_NF_FLOW_TABLE_INET=m
+CONFIG_NF_FLOW_TABLE=m
+CONFIG_NETFILTER_XT_SET=m
+CONFIG_NETFILTER_XT_TARGET_CHECKSUM=m
+CONFIG_NETFILTER_XT_TARGET_CLASSIFY=m
+CONFIG_NETFILTER_XT_TARGET_CONNMARK=m
+CONFIG_NETFILTER_XT_TARGET_DSCP=m
+CONFIG_NETFILTER_XT_TARGET_HMARK=m
+CONFIG_NETFILTER_XT_TARGET_IDLETIMER=m
+CONFIG_NETFILTER_XT_TARGET_LED=m
+CONFIG_NETFILTER_XT_TARGET_LOG=m
+CONFIG_NETFILTER_XT_TARGET_MARK=m
+CONFIG_NETFILTER_XT_TARGET_NFLOG=m
+CONFIG_NETFILTER_XT_TARGET_NFQUEUE=m
+CONFIG_NETFILTER_XT_TARGET_NOTRACK=m
+CONFIG_NETFILTER_XT_TARGET_TEE=m
+CONFIG_NETFILTER_XT_TARGET_TPROXY=m
+CONFIG_NETFILTER_XT_TARGET_TRACE=m
+CONFIG_NETFILTER_XT_TARGET_TCPMSS=m
+CONFIG_NETFILTER_XT_TARGET_TCPOPTSTRIP=m
+CONFIG_NETFILTER_XT_MATCH_ADDRTYPE=m
+CONFIG_NETFILTER_XT_MATCH_BPF=m
+CONFIG_NETFILTER_XT_MATCH_CLUSTER=m
+CONFIG_NETFILTER_XT_MATCH_COMMENT=m
+CONFIG_NETFILTER_XT_MATCH_CONNBYTES=m
+CONFIG_NETFILTER_XT_MATCH_CONNLABEL=m
+CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=m
+CONFIG_NETFILTER_XT_MATCH_CONNMARK=m
+CONFIG_NETFILTER_XT_MATCH_CONNTRACK=m
+CONFIG_NETFILTER_XT_MATCH_CPU=m
+CONFIG_NETFILTER_XT_MATCH_DCCP=m
+CONFIG_NETFILTER_XT_MATCH_DEVGROUP=m
+CONFIG_NETFILTER_XT_MATCH_DSCP=m
+CONFIG_NETFILTER_XT_MATCH_ESP=m
+CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=m
+CONFIG_NETFILTER_XT_MATCH_HELPER=m
+CONFIG_NETFILTER_XT_MATCH_IPRANGE=m
+CONFIG_NETFILTER_XT_MATCH_IPVS=m
+CONFIG_NETFILTER_XT_MATCH_LENGTH=m
+CONFIG_NETFILTER_XT_MATCH_LIMIT=m
+CONFIG_NETFILTER_XT_MATCH_MAC=m
+CONFIG_NETFILTER_XT_MATCH_MARK=m
+CONFIG_NETFILTER_XT_MATCH_MULTIPORT=m
+CONFIG_NETFILTER_XT_MATCH_NFACCT=m
+CONFIG_NETFILTER_XT_MATCH_OSF=m
+CONFIG_NETFILTER_XT_MATCH_OWNER=m
+CONFIG_NETFILTER_XT_MATCH_POLICY=m
+CONFIG_NETFILTER_XT_MATCH_PHYSDEV=m
+CONFIG_NETFILTER_XT_MATCH_PKTTYPE=m
+CONFIG_NETFILTER_XT_MATCH_QUOTA=m
+CONFIG_NETFILTER_XT_MATCH_RATEEST=m
+CONFIG_NETFILTER_XT_MATCH_REALM=m
+CONFIG_NETFILTER_XT_MATCH_RECENT=m
+CONFIG_NETFILTER_XT_MATCH_SOCKET=m
+CONFIG_NETFILTER_XT_MATCH_STATE=m
+CONFIG_NETFILTER_XT_MATCH_STATISTIC=m
+CONFIG_NETFILTER_XT_MATCH_STRING=m
+CONFIG_NETFILTER_XT_MATCH_TCPMSS=m
+CONFIG_NETFILTER_XT_MATCH_TIME=m
+CONFIG_NETFILTER_XT_MATCH_U32=m
+CONFIG_IP_SET=m
+CONFIG_IP_SET_BITMAP_IP=m
+CONFIG_IP_SET_BITMAP_IPMAC=m
+CONFIG_IP_SET_BITMAP_PORT=m
+CONFIG_IP_SET_HASH_IP=m
+CONFIG_IP_SET_HASH_IPPORT=m
+CONFIG_IP_SET_HASH_IPPORTIP=m
+CONFIG_IP_SET_HASH_IPPORTNET=m
+CONFIG_IP_SET_HASH_NET=m
+CONFIG_IP_SET_HASH_NETPORT=m
+CONFIG_IP_SET_HASH_NETIFACE=m
+CONFIG_IP_SET_LIST_SET=m
+CONFIG_IP_VS=m
+CONFIG_IP_VS_PROTO_TCP=y
+CONFIG_IP_VS_PROTO_UDP=y
+CONFIG_IP_VS_PROTO_ESP=y
+CONFIG_IP_VS_PROTO_AH=y
+CONFIG_IP_VS_PROTO_SCTP=y
+CONFIG_IP_VS_RR=m
+CONFIG_IP_VS_WRR=m
+CONFIG_IP_VS_LC=m
+CONFIG_IP_VS_WLC=m
+CONFIG_IP_VS_LBLC=m
+CONFIG_IP_VS_LBLCR=m
+CONFIG_IP_VS_DH=m
+CONFIG_IP_VS_SH=m
+CONFIG_IP_VS_SED=m
+CONFIG_IP_VS_NQ=m
+CONFIG_IP_VS_FTP=m
+CONFIG_IP_VS_PE_SIP=m
+CONFIG_NFT_DUP_IPV4=m
+CONFIG_NFT_FIB_IPV4=m
+CONFIG_NF_TABLES_ARP=y
+CONFIG_NF_FLOW_TABLE_IPV4=m
+CONFIG_NF_LOG_ARP=m
+CONFIG_IP_NF_IPTABLES=m
+CONFIG_IP_NF_MATCH_AH=m
+CONFIG_IP_NF_MATCH_ECN=m
+CONFIG_IP_NF_MATCH_RPFILTER=m
+CONFIG_IP_NF_MATCH_TTL=m
+CONFIG_IP_NF_FILTER=m
+CONFIG_IP_NF_TARGET_REJECT=m
+CONFIG_IP_NF_NAT=m
+CONFIG_IP_NF_TARGET_MASQUERADE=m
+CONFIG_IP_NF_TARGET_NETMAP=m
+CONFIG_IP_NF_TARGET_REDIRECT=m
+CONFIG_IP_NF_MANGLE=m
+CONFIG_IP_NF_TARGET_CLUSTERIP=m
+CONFIG_IP_NF_TARGET_ECN=m
+CONFIG_IP_NF_TARGET_TTL=m
+CONFIG_IP_NF_RAW=m
+CONFIG_IP_NF_ARPTABLES=m
+CONFIG_IP_NF_ARPFILTER=m
+CONFIG_IP_NF_ARP_MANGLE=m
+CONFIG_NFT_DUP_IPV6=m
+CONFIG_NFT_FIB_IPV6=m
+CONFIG_NF_FLOW_TABLE_IPV6=m
+CONFIG_IP6_NF_IPTABLES=m
+CONFIG_IP6_NF_MATCH_AH=m
+CONFIG_IP6_NF_MATCH_EUI64=m
+CONFIG_IP6_NF_MATCH_FRAG=m
+CONFIG_IP6_NF_MATCH_OPTS=m
+CONFIG_IP6_NF_MATCH_HL=m
+CONFIG_IP6_NF_MATCH_IPV6HEADER=m
+CONFIG_IP6_NF_MATCH_MH=m
+CONFIG_IP6_NF_MATCH_RPFILTER=m
+CONFIG_IP6_NF_MATCH_RT=m
+CONFIG_IP6_NF_MATCH_SRH=m
+CONFIG_IP6_NF_TARGET_HL=m
+CONFIG_IP6_NF_FILTER=m
+CONFIG_IP6_NF_TARGET_REJECT=m
+CONFIG_IP6_NF_TARGET_SYNPROXY=m
+CONFIG_IP6_NF_MANGLE=m
+CONFIG_IP6_NF_RAW=m
+CONFIG_IP6_NF_SECURITY=m
+CONFIG_IP6_NF_NAT=m
+CONFIG_IP6_NF_TARGET_MASQUERADE=m
+CONFIG_IP6_NF_TARGET_NPT=m
+CONFIG_NF_TABLES_BRIDGE=m
+CONFIG_NFT_BRIDGE_REJECT=m
+CONFIG_NF_LOG_BRIDGE=m
+CONFIG_BRIDGE_NF_EBTABLES=m
+CONFIG_BRIDGE_EBT_BROUTE=m
+CONFIG_BRIDGE_EBT_T_FILTER=m
+CONFIG_BRIDGE_EBT_T_NAT=m
+CONFIG_BRIDGE_EBT_802_3=m
+CONFIG_BRIDGE_EBT_AMONG=m
+CONFIG_BRIDGE_EBT_ARP=m
+CONFIG_BRIDGE_EBT_IP=m
+CONFIG_BRIDGE_EBT_IP6=m
+CONFIG_BRIDGE_EBT_LIMIT=m
+CONFIG_BRIDGE_EBT_MARK=m
+CONFIG_BRIDGE_EBT_PKTTYPE=m
+CONFIG_BRIDGE_EBT_STP=m
+CONFIG_BRIDGE_EBT_VLAN=m
+CONFIG_BRIDGE_EBT_ARPREPLY=m
+CONFIG_BRIDGE_EBT_DNAT=m
+CONFIG_BRIDGE_EBT_MARK_T=m
+CONFIG_BRIDGE_EBT_REDIRECT=m
+CONFIG_BRIDGE_EBT_SNAT=m
+CONFIG_BRIDGE_EBT_LOG=m
+CONFIG_BRIDGE_EBT_NFLOG=m
+CONFIG_SCTP_COOKIE_HMAC_SHA1=y
+CONFIG_ATM=m
+CONFIG_L2TP=m
+CONFIG_L2TP_V3=y
+CONFIG_L2TP_IP=m
+CONFIG_L2TP_ETH=m
+CONFIG_BRIDGE=m
+CONFIG_VLAN_8021Q=m
+CONFIG_VLAN_8021Q_GVRP=y
+CONFIG_ATALK=m
+CONFIG_6LOWPAN=m
+CONFIG_IEEE802154=m
+CONFIG_IEEE802154_6LOWPAN=m
+CONFIG_MAC802154=m
+CONFIG_NET_SCHED=y
+CONFIG_NET_SCH_CBQ=m
+CONFIG_NET_SCH_HTB=m
+CONFIG_NET_SCH_HFSC=m
+CONFIG_NET_SCH_ATM=m
+CONFIG_NET_SCH_PRIO=m
+CONFIG_NET_SCH_MULTIQ=m
+CONFIG_NET_SCH_RED=m
+CONFIG_NET_SCH_SFB=m
+CONFIG_NET_SCH_SFQ=m
+CONFIG_NET_SCH_TEQL=m
+CONFIG_NET_SCH_TBF=m
+CONFIG_NET_SCH_GRED=m
+CONFIG_NET_SCH_DSMARK=m
+CONFIG_NET_SCH_NETEM=m
+CONFIG_NET_SCH_DRR=m
+CONFIG_NET_SCH_MQPRIO=m
+CONFIG_NET_SCH_CHOKE=m
+CONFIG_NET_SCH_QFQ=m
+CONFIG_NET_SCH_CODEL=m
+CONFIG_NET_SCH_FQ_CODEL=m
+CONFIG_NET_SCH_CAKE=m
+CONFIG_NET_SCH_FQ=m
+CONFIG_NET_SCH_HHF=m
+CONFIG_NET_SCH_PIE=m
+CONFIG_NET_SCH_INGRESS=m
+CONFIG_NET_SCH_PLUG=m
+CONFIG_NET_CLS_BASIC=m
+CONFIG_NET_CLS_TCINDEX=m
+CONFIG_NET_CLS_ROUTE4=m
+CONFIG_NET_CLS_FW=m
+CONFIG_NET_CLS_U32=m
+CONFIG_CLS_U32_MARK=y
+CONFIG_NET_CLS_RSVP=m
+CONFIG_NET_CLS_RSVP6=m
+CONFIG_NET_CLS_FLOW=m
+CONFIG_NET_CLS_CGROUP=m
+CONFIG_NET_EMATCH=y
+CONFIG_NET_EMATCH_CMP=m
+CONFIG_NET_EMATCH_NBYTE=m
+CONFIG_NET_EMATCH_U32=m
+CONFIG_NET_EMATCH_META=m
+CONFIG_NET_EMATCH_TEXT=m
+CONFIG_NET_EMATCH_IPSET=m
+CONFIG_NET_CLS_ACT=y
+CONFIG_NET_ACT_POLICE=m
+CONFIG_NET_ACT_GACT=m
+CONFIG_GACT_PROB=y
+CONFIG_NET_ACT_MIRRED=m
+CONFIG_NET_ACT_IPT=m
+CONFIG_NET_ACT_NAT=m
+CONFIG_NET_ACT_PEDIT=m
+CONFIG_NET_ACT_SIMP=m
+CONFIG_NET_ACT_SKBEDIT=m
+CONFIG_NET_ACT_CSUM=m
+CONFIG_BATMAN_ADV=m
+CONFIG_OPENVSWITCH=m
+CONFIG_CGROUP_NET_PRIO=y
+CONFIG_NET_PKTGEN=m
+CONFIG_HAMRADIO=y
+CONFIG_AX25=m
+CONFIG_NETROM=m
+CONFIG_ROSE=m
+CONFIG_MKISS=m
+CONFIG_6PACK=m
+CONFIG_BPQETHER=m
+CONFIG_BAYCOM_SER_FDX=m
+CONFIG_BAYCOM_SER_HDX=m
+CONFIG_YAM=m
+CONFIG_CAN=m
+CONFIG_CAN_J1939=m
+CONFIG_CAN_ISOTP=m
+CONFIG_CAN_VCAN=m
+CONFIG_CAN_SLCAN=m
+CONFIG_CAN_MCP251X=m
+CONFIG_CAN_MCP251XFD=m
+CONFIG_CAN_EMS_USB=m
+CONFIG_CAN_GS_USB=m
+CONFIG_BT=m
+CONFIG_BT_RFCOMM=m
+CONFIG_BT_RFCOMM_TTY=y
+CONFIG_BT_BNEP=m
+CONFIG_BT_BNEP_MC_FILTER=y
+CONFIG_BT_BNEP_PROTO_FILTER=y
+CONFIG_BT_HIDP=m
+CONFIG_BT_6LOWPAN=m
+CONFIG_BT_HCIBTUSB=m
+CONFIG_BT_HCIUART=m
+CONFIG_BT_HCIUART_3WIRE=y
+CONFIG_BT_HCIUART_BCM=y
+CONFIG_BT_HCIBCM203X=m
+CONFIG_BT_HCIBPA10X=m
+CONFIG_BT_HCIBFUSB=m
+CONFIG_BT_HCIVHCI=m
+CONFIG_BT_MRVL=m
+CONFIG_BT_MRVL_SDIO=m
+CONFIG_BT_ATH3K=m
+CONFIG_CFG80211=m
+CONFIG_CFG80211_WEXT=y
+CONFIG_MAC80211=m
+CONFIG_MAC80211_MESH=y
+CONFIG_WIMAX=m
+CONFIG_RFKILL=m
+CONFIG_RFKILL_INPUT=y
+CONFIG_NET_9P=m
+CONFIG_NFC=m
+CONFIG_UEVENT_HELPER=y
+CONFIG_DEVTMPFS=y
+CONFIG_DEVTMPFS_MOUNT=y
+CONFIG_MTD=m
+CONFIG_MTD_BLOCK=m
+CONFIG_MTD_BLOCK2MTD=m
+CONFIG_MTD_SPI_NOR=m
+CONFIG_MTD_UBI=m
+CONFIG_OF_CONFIGFS=y
+CONFIG_ZRAM=m
+CONFIG_BLK_DEV_LOOP=y
+CONFIG_BLK_DEV_CRYPTOLOOP=m
+CONFIG_BLK_DEV_DRBD=m
+CONFIG_BLK_DEV_NBD=m
+CONFIG_BLK_DEV_RAM=y
+CONFIG_CDROM_PKTCDVD=m
+CONFIG_ATA_OVER_ETH=m
+CONFIG_EEPROM_AT24=m
+CONFIG_TI_ST=m
+CONFIG_SCSI=y
+# CONFIG_SCSI_PROC_FS is not set
+CONFIG_BLK_DEV_SD=y
+CONFIG_CHR_DEV_ST=m
+CONFIG_BLK_DEV_SR=m
+CONFIG_CHR_DEV_SG=m
+CONFIG_SCSI_ISCSI_ATTRS=y
+CONFIG_ISCSI_TCP=m
+CONFIG_ISCSI_BOOT_SYSFS=m
+CONFIG_MD=y
+CONFIG_MD_LINEAR=m
+CONFIG_BLK_DEV_DM=m
+CONFIG_DM_CRYPT=m
+CONFIG_DM_SNAPSHOT=m
+CONFIG_DM_THIN_PROVISIONING=m
+CONFIG_DM_CACHE=m
+CONFIG_DM_MIRROR=m
+CONFIG_DM_LOG_USERSPACE=m
+CONFIG_DM_RAID=m
+CONFIG_DM_ZERO=m
+CONFIG_DM_DELAY=m
+CONFIG_NETDEVICES=y
+CONFIG_BONDING=m
+CONFIG_DUMMY=m
+CONFIG_WIREGUARD=m
+CONFIG_IFB=m
+CONFIG_MACVLAN=m
+CONFIG_IPVLAN=m
+CONFIG_VXLAN=m
+CONFIG_NETCONSOLE=m
+CONFIG_TUN=m
+CONFIG_VETH=m
+CONFIG_NET_VRF=m
+CONFIG_ENC28J60=m
+CONFIG_QCA7000_SPI=m
+CONFIG_QCA7000_UART=m
+CONFIG_WIZNET_W5100=m
+CONFIG_WIZNET_W5100_SPI=m
+CONFIG_MDIO_BITBANG=m
+CONFIG_PPP=m
+CONFIG_PPP_BSDCOMP=m
+CONFIG_PPP_DEFLATE=m
+CONFIG_PPP_FILTER=y
+CONFIG_PPP_MPPE=m
+CONFIG_PPP_MULTILINK=y
+CONFIG_PPPOATM=m
+CONFIG_PPPOE=m
+CONFIG_PPPOL2TP=m
+CONFIG_PPP_ASYNC=m
+CONFIG_PPP_SYNC_TTY=m
+CONFIG_SLIP=m
+CONFIG_SLIP_COMPRESSED=y
+CONFIG_SLIP_SMART=y
+CONFIG_USB_CATC=m
+CONFIG_USB_KAWETH=m
+CONFIG_USB_PEGASUS=m
+CONFIG_USB_RTL8150=m
+CONFIG_USB_RTL8152=m
+CONFIG_USB_LAN78XX=m
+CONFIG_USB_USBNET=y
+CONFIG_USB_NET_AX8817X=m
+CONFIG_USB_NET_AX88179_178A=m
+CONFIG_USB_NET_CDCETHER=m
+CONFIG_USB_NET_CDC_EEM=m
+CONFIG_USB_NET_CDC_NCM=m
+CONFIG_USB_NET_HUAWEI_CDC_NCM=m
+CONFIG_USB_NET_CDC_MBIM=m
+CONFIG_USB_NET_DM9601=m
+CONFIG_USB_NET_SR9700=m
+CONFIG_USB_NET_SR9800=m
+CONFIG_USB_NET_SMSC75XX=m
+CONFIG_USB_NET_SMSC95XX=y
+CONFIG_USB_NET_GL620A=m
+CONFIG_USB_NET_NET1080=m
+CONFIG_USB_NET_PLUSB=m
+CONFIG_USB_NET_MCS7830=m
+CONFIG_USB_NET_CDC_SUBSET=m
+CONFIG_USB_ALI_M5632=y
+CONFIG_USB_AN2720=y
+CONFIG_USB_EPSON2888=y
+CONFIG_USB_KC2190=y
+CONFIG_USB_NET_ZAURUS=m
+CONFIG_USB_NET_CX82310_ETH=m
+CONFIG_USB_NET_KALMIA=m
+CONFIG_USB_NET_QMI_WWAN=m
+CONFIG_USB_HSO=m
+CONFIG_USB_NET_INT51X1=m
+CONFIG_USB_IPHETH=m
+CONFIG_USB_SIERRA_NET=m
+CONFIG_USB_VL600=m
+CONFIG_USB_NET_AQC111=m
+CONFIG_ATH9K=m
+CONFIG_ATH9K_HTC=m
+CONFIG_CARL9170=m
+CONFIG_ATH6KL=m
+CONFIG_ATH6KL_USB=m
+CONFIG_AR5523=m
+CONFIG_AT76C50X_USB=m
+CONFIG_B43=m
+# CONFIG_B43_PHY_N is not set
+CONFIG_B43LEGACY=m
+CONFIG_BRCMFMAC=m
+CONFIG_BRCMFMAC_USB=y
+CONFIG_BRCMDBG=y
+CONFIG_HOSTAP=m
+CONFIG_P54_COMMON=m
+CONFIG_P54_USB=m
+CONFIG_LIBERTAS=m
+CONFIG_LIBERTAS_USB=m
+CONFIG_LIBERTAS_SDIO=m
+CONFIG_LIBERTAS_THINFIRM=m
+CONFIG_LIBERTAS_THINFIRM_USB=m
+CONFIG_MWIFIEX=m
+CONFIG_MWIFIEX_SDIO=m
+CONFIG_MT7601U=m
+CONFIG_MT76x0U=m
+CONFIG_MT76x2U=m
+CONFIG_RT2X00=m
+CONFIG_RT2500USB=m
+CONFIG_RT73USB=m
+CONFIG_RT2800USB=m
+CONFIG_RT2800USB_RT3573=y
+CONFIG_RT2800USB_RT53XX=y
+CONFIG_RT2800USB_RT55XX=y
+CONFIG_RT2800USB_UNKNOWN=y
+CONFIG_RTL8187=m
+CONFIG_RTL8192CU=m
+CONFIG_RTL8XXXU=m
+CONFIG_USB_ZD1201=m
+CONFIG_ZD1211RW=m
+CONFIG_MAC80211_HWSIM=m
+CONFIG_USB_NET_RNDIS_WLAN=m
+CONFIG_WIMAX_I2400M_USB=m
+CONFIG_IEEE802154_AT86RF230=m
+CONFIG_IEEE802154_MRF24J40=m
+CONFIG_IEEE802154_CC2520=m
+CONFIG_INPUT_MOUSEDEV=y
+CONFIG_INPUT_JOYDEV=m
+CONFIG_INPUT_EVDEV=y
+# CONFIG_KEYBOARD_ATKBD is not set
+CONFIG_KEYBOARD_GPIO=m
+CONFIG_KEYBOARD_MATRIX=m
+CONFIG_KEYBOARD_CAP11XX=m
+# CONFIG_INPUT_MOUSE is not set
+CONFIG_INPUT_JOYSTICK=y
+CONFIG_JOYSTICK_IFORCE=m
+CONFIG_JOYSTICK_IFORCE_USB=m
+CONFIG_JOYSTICK_XPAD=m
+CONFIG_JOYSTICK_XPAD_FF=y
+CONFIG_JOYSTICK_XPAD_LEDS=y
+CONFIG_JOYSTICK_PSXPAD_SPI=m
+CONFIG_JOYSTICK_PSXPAD_SPI_FF=y
+CONFIG_JOYSTICK_RPISENSE=m
+CONFIG_INPUT_TOUCHSCREEN=y
+CONFIG_TOUCHSCREEN_ADS7846=m
+CONFIG_TOUCHSCREEN_EGALAX=m
+CONFIG_TOUCHSCREEN_EXC3000=m
+CONFIG_TOUCHSCREEN_GOODIX=m
+CONFIG_TOUCHSCREEN_ILI210X=m
+CONFIG_TOUCHSCREEN_EDT_FT5X06=m
+CONFIG_TOUCHSCREEN_RASPBERRYPI_FW=m
+CONFIG_TOUCHSCREEN_USB_COMPOSITE=m
+CONFIG_TOUCHSCREEN_STMPE=m
+CONFIG_INPUT_MISC=y
+CONFIG_INPUT_AD714X=m
+CONFIG_INPUT_ATI_REMOTE2=m
+CONFIG_INPUT_KEYSPAN_REMOTE=m
+CONFIG_INPUT_POWERMATE=m
+CONFIG_INPUT_YEALINK=m
+CONFIG_INPUT_CM109=m
+CONFIG_INPUT_UINPUT=m
+CONFIG_INPUT_GPIO_ROTARY_ENCODER=m
+CONFIG_INPUT_ADXL34X=m
+CONFIG_INPUT_CMA3000=m
+CONFIG_SERIO=m
+CONFIG_SERIO_RAW=m
+CONFIG_GAMEPORT=m
+CONFIG_GAMEPORT_NS558=m
+CONFIG_GAMEPORT_L4=m
+CONFIG_BRCM_CHAR_DRIVERS=y
+CONFIG_BCM_VCIO=y
+CONFIG_BCM2835_DEVGPIOMEM=y
+# CONFIG_LEGACY_PTYS is not set
+CONFIG_SERIAL_8250=y
+# CONFIG_SERIAL_8250_DEPRECATED_OPTIONS is not set
+CONFIG_SERIAL_8250_CONSOLE=y
+# CONFIG_SERIAL_8250_DMA is not set
+CONFIG_SERIAL_8250_NR_UARTS=1
+CONFIG_SERIAL_8250_RUNTIME_UARTS=0
+CONFIG_SERIAL_8250_EXTENDED=y
+CONFIG_SERIAL_8250_SHARE_IRQ=y
+CONFIG_SERIAL_8250_BCM2835AUX=y
+CONFIG_SERIAL_OF_PLATFORM=y
+CONFIG_SERIAL_AMBA_PL011=y
+CONFIG_SERIAL_AMBA_PL011_CONSOLE=y
+CONFIG_SERIAL_SC16IS7XX=m
+CONFIG_SERIAL_SC16IS7XX_SPI=y
+CONFIG_SERIAL_DEV_BUS=y
+CONFIG_TTY_PRINTK=y
+CONFIG_HW_RANDOM=y
+CONFIG_RAW_DRIVER=y
+CONFIG_TCG_TPM=m
+CONFIG_TCG_TIS_SPI=m
+CONFIG_I2C=y
+CONFIG_I2C_CHARDEV=m
+CONFIG_I2C_MUX_GPMUX=m
+CONFIG_I2C_MUX_PCA954x=m
+CONFIG_I2C_MUX_PINCTRL=m
+CONFIG_I2C_BCM2708=m
+CONFIG_I2C_BCM2835=m
+# CONFIG_I2C_BRCMSTB is not set
+CONFIG_I2C_GPIO=m
+CONFIG_I2C_ROBOTFUZZ_OSIF=m
+CONFIG_I2C_TINY_USB=m
+CONFIG_SPI=y
+CONFIG_SPI_BCM2835=m
+CONFIG_SPI_BCM2835AUX=m
+CONFIG_SPI_GPIO=m
+CONFIG_SPI_SPIDEV=m
+CONFIG_SPI_SLAVE=y
+CONFIG_PPS=m
+CONFIG_PPS_CLIENT_LDISC=m
+CONFIG_PPS_CLIENT_GPIO=m
+CONFIG_PINCTRL_MCP23S08=m
+CONFIG_GPIO_SYSFS=y
+CONFIG_GPIO_PCA953X=m
+CONFIG_GPIO_PCA953X_IRQ=y
+CONFIG_GPIO_PCF857X=m
+CONFIG_GPIO_ARIZONA=m
+CONFIG_GPIO_FSM=m
+CONFIG_GPIO_STMPE=y
+CONFIG_GPIO_MOCKUP=m
+CONFIG_W1=m
+CONFIG_W1_MASTER_DS2490=m
+CONFIG_W1_MASTER_DS2482=m
+CONFIG_W1_MASTER_DS1WM=m
+CONFIG_W1_MASTER_GPIO=m
+CONFIG_W1_SLAVE_THERM=m
+CONFIG_W1_SLAVE_SMEM=m
+CONFIG_W1_SLAVE_DS2408=m
+CONFIG_W1_SLAVE_DS2413=m
+CONFIG_W1_SLAVE_DS2406=m
+CONFIG_W1_SLAVE_DS2423=m
+CONFIG_W1_SLAVE_DS2431=m
+CONFIG_W1_SLAVE_DS2433=m
+CONFIG_W1_SLAVE_DS2438=m
+CONFIG_W1_SLAVE_DS2780=m
+CONFIG_W1_SLAVE_DS2781=m
+CONFIG_W1_SLAVE_DS28E04=m
+CONFIG_W1_SLAVE_DS28E17=m
+CONFIG_POWER_RESET=y
+CONFIG_POWER_RESET_GPIO=y
+CONFIG_RPI_POE_POWER=m
+CONFIG_BATTERY_DS2760=m
+CONFIG_BATTERY_MAX17040=m
+CONFIG_BATTERY_GAUGE_LTC2941=m
+CONFIG_SENSORS_DS1621=m
+CONFIG_SENSORS_GPIO_FAN=m
+CONFIG_SENSORS_IIO_HWMON=m
+CONFIG_SENSORS_JC42=m
+CONFIG_SENSORS_LM75=m
+CONFIG_SENSORS_RASPBERRYPI_HWMON=m
+CONFIG_SENSORS_RPI_POE_FAN=m
+CONFIG_SENSORS_SHT21=m
+CONFIG_SENSORS_SHT3x=m
+CONFIG_SENSORS_SHTC1=m
+CONFIG_SENSORS_INA2XX=m
+CONFIG_SENSORS_TMP102=m
+CONFIG_THERMAL=y
+CONFIG_BCM2835_THERMAL=y
+CONFIG_WATCHDOG=y
+CONFIG_GPIO_WATCHDOG=m
+CONFIG_BCM2835_WDT=y
+CONFIG_MFD_STMPE=y
+CONFIG_STMPE_SPI=y
+CONFIG_MFD_ARIZONA_I2C=m
+CONFIG_MFD_ARIZONA_SPI=m
+CONFIG_MFD_WM5102=y
+CONFIG_REGULATOR=y
+CONFIG_REGULATOR_FIXED_VOLTAGE=m
+CONFIG_REGULATOR_ARIZONA_LDO1=m
+CONFIG_REGULATOR_ARIZONA_MICSUPP=m
+CONFIG_RC_CORE=y
+CONFIG_LIRC=y
+CONFIG_BPF_LIRC_MODE2=y
+CONFIG_RC_DECODERS=y
+CONFIG_IR_NEC_DECODER=m
+CONFIG_IR_RC5_DECODER=m
+CONFIG_IR_RC6_DECODER=m
+CONFIG_IR_JVC_DECODER=m
+CONFIG_IR_SONY_DECODER=m
+CONFIG_IR_SANYO_DECODER=m
+CONFIG_IR_SHARP_DECODER=m
+CONFIG_IR_MCE_KBD_DECODER=m
+CONFIG_IR_XMP_DECODER=m
+CONFIG_IR_IMON_DECODER=m
+CONFIG_RC_DEVICES=y
+CONFIG_RC_ATI_REMOTE=m
+CONFIG_IR_IMON=m
+CONFIG_IR_MCEUSB=m
+CONFIG_IR_REDRAT3=m
+CONFIG_IR_STREAMZAP=m
+CONFIG_IR_IGUANA=m
+CONFIG_IR_TTUSBIR=m
+CONFIG_RC_LOOPBACK=m
+CONFIG_IR_GPIO_CIR=m
+CONFIG_IR_GPIO_TX=m
+CONFIG_IR_PWM_TX=m
+CONFIG_IR_TOY=m
+CONFIG_MEDIA_CEC_RC=y
+CONFIG_MEDIA_SUPPORT=m
+CONFIG_MEDIA_USB_SUPPORT=y
+CONFIG_USB_VIDEO_CLASS=m
+CONFIG_USB_M5602=m
+CONFIG_USB_STV06XX=m
+CONFIG_USB_GL860=m
+CONFIG_USB_GSPCA_BENQ=m
+CONFIG_USB_GSPCA_CONEX=m
+CONFIG_USB_GSPCA_CPIA1=m
+CONFIG_USB_GSPCA_DTCS033=m
+CONFIG_USB_GSPCA_ETOMS=m
+CONFIG_USB_GSPCA_FINEPIX=m
+CONFIG_USB_GSPCA_JEILINJ=m
+CONFIG_USB_GSPCA_JL2005BCD=m
+CONFIG_USB_GSPCA_KINECT=m
+CONFIG_USB_GSPCA_KONICA=m
+CONFIG_USB_GSPCA_MARS=m
+CONFIG_USB_GSPCA_MR97310A=m
+CONFIG_USB_GSPCA_NW80X=m
+CONFIG_USB_GSPCA_OV519=m
+CONFIG_USB_GSPCA_OV534=m
+CONFIG_USB_GSPCA_OV534_9=m
+CONFIG_USB_GSPCA_PAC207=m
+CONFIG_USB_GSPCA_PAC7302=m
+CONFIG_USB_GSPCA_PAC7311=m
+CONFIG_USB_GSPCA_SE401=m
+CONFIG_USB_GSPCA_SN9C2028=m
+CONFIG_USB_GSPCA_SN9C20X=m
+CONFIG_USB_GSPCA_SONIXB=m
+CONFIG_USB_GSPCA_SONIXJ=m
+CONFIG_USB_GSPCA_SPCA500=m
+CONFIG_USB_GSPCA_SPCA501=m
+CONFIG_USB_GSPCA_SPCA505=m
+CONFIG_USB_GSPCA_SPCA506=m
+CONFIG_USB_GSPCA_SPCA508=m
+CONFIG_USB_GSPCA_SPCA561=m
+CONFIG_USB_GSPCA_SPCA1528=m
+CONFIG_USB_GSPCA_SQ905=m
+CONFIG_USB_GSPCA_SQ905C=m
+CONFIG_USB_GSPCA_SQ930X=m
+CONFIG_USB_GSPCA_STK014=m
+CONFIG_USB_GSPCA_STK1135=m
+CONFIG_USB_GSPCA_STV0680=m
+CONFIG_USB_GSPCA_SUNPLUS=m
+CONFIG_USB_GSPCA_T613=m
+CONFIG_USB_GSPCA_TOPRO=m
+CONFIG_USB_GSPCA_TV8532=m
+CONFIG_USB_GSPCA_VC032X=m
+CONFIG_USB_GSPCA_VICAM=m
+CONFIG_USB_GSPCA_XIRLINK_CIT=m
+CONFIG_USB_GSPCA_ZC3XX=m
+CONFIG_USB_PWC=m
+CONFIG_VIDEO_CPIA2=m
+CONFIG_USB_ZR364XX=m
+CONFIG_USB_STKWEBCAM=m
+CONFIG_USB_S2255=m
+CONFIG_VIDEO_USBTV=m
+CONFIG_VIDEO_PVRUSB2=m
+CONFIG_VIDEO_HDPVR=m
+CONFIG_VIDEO_STK1160_COMMON=m
+CONFIG_VIDEO_GO7007=m
+CONFIG_VIDEO_GO7007_USB=m
+CONFIG_VIDEO_GO7007_USB_S2250_BOARD=m
+CONFIG_VIDEO_AU0828=m
+CONFIG_VIDEO_AU0828_RC=y
+CONFIG_VIDEO_CX231XX=m
+CONFIG_VIDEO_CX231XX_ALSA=m
+CONFIG_VIDEO_CX231XX_DVB=m
+CONFIG_VIDEO_TM6000=m
+CONFIG_VIDEO_TM6000_ALSA=m
+CONFIG_VIDEO_TM6000_DVB=m
+CONFIG_DVB_USB=m
+CONFIG_DVB_USB_A800=m
+CONFIG_DVB_USB_DIBUSB_MB=m
+CONFIG_DVB_USB_DIBUSB_MB_FAULTY=y
+CONFIG_DVB_USB_DIBUSB_MC=m
+CONFIG_DVB_USB_DIB0700=m
+CONFIG_DVB_USB_UMT_010=m
+CONFIG_DVB_USB_CXUSB=m
+CONFIG_DVB_USB_M920X=m
+CONFIG_DVB_USB_DIGITV=m
+CONFIG_DVB_USB_VP7045=m
+CONFIG_DVB_USB_VP702X=m
+CONFIG_DVB_USB_GP8PSK=m
+CONFIG_DVB_USB_NOVA_T_USB2=m
+CONFIG_DVB_USB_TTUSB2=m
+CONFIG_DVB_USB_DTT200U=m
+CONFIG_DVB_USB_OPERA1=m
+CONFIG_DVB_USB_AF9005=m
+CONFIG_DVB_USB_AF9005_REMOTE=m
+CONFIG_DVB_USB_PCTV452E=m
+CONFIG_DVB_USB_DW2102=m
+CONFIG_DVB_USB_CINERGY_T2=m
+CONFIG_DVB_USB_DTV5100=m
+CONFIG_DVB_USB_AZ6027=m
+CONFIG_DVB_USB_TECHNISAT_USB2=m
+CONFIG_DVB_USB_V2=m
+CONFIG_DVB_USB_AF9015=m
+CONFIG_DVB_USB_AF9035=m
+CONFIG_DVB_USB_ANYSEE=m
+CONFIG_DVB_USB_AU6610=m
+CONFIG_DVB_USB_AZ6007=m
+CONFIG_DVB_USB_CE6230=m
+CONFIG_DVB_USB_EC168=m
+CONFIG_DVB_USB_GL861=m
+CONFIG_DVB_USB_LME2510=m
+CONFIG_DVB_USB_MXL111SF=m
+CONFIG_DVB_USB_RTL28XXU=m
+CONFIG_DVB_USB_DVBSKY=m
+CONFIG_SMS_USB_DRV=m
+CONFIG_DVB_B2C2_FLEXCOP_USB=m
+CONFIG_DVB_AS102=m
+CONFIG_VIDEO_EM28XX=m
+CONFIG_VIDEO_EM28XX_V4L2=m
+CONFIG_VIDEO_EM28XX_ALSA=m
+CONFIG_VIDEO_EM28XX_DVB=m
+CONFIG_RADIO_SI470X=m
+CONFIG_USB_SI470X=m
+CONFIG_I2C_SI470X=m
+CONFIG_RADIO_SI4713=m
+CONFIG_I2C_SI4713=m
+CONFIG_USB_MR800=m
+CONFIG_USB_DSBR=m
+CONFIG_RADIO_SHARK=m
+CONFIG_RADIO_SHARK2=m
+CONFIG_USB_KEENE=m
+CONFIG_USB_MA901=m
+CONFIG_RADIO_TEA5764=m
+CONFIG_RADIO_SAA7706H=m
+CONFIG_RADIO_TEF6862=m
+CONFIG_RADIO_WL1273=m
+CONFIG_RADIO_WL128X=m
+CONFIG_V4L_PLATFORM_DRIVERS=y
+CONFIG_VIDEO_BCM2835_UNICAM=m
+CONFIG_VIDEO_UDA1342=m
+CONFIG_VIDEO_SONY_BTF_MPX=m
+CONFIG_VIDEO_ADV7180=m
+CONFIG_VIDEO_TC358743=m
+CONFIG_VIDEO_TVP5150=m
+CONFIG_VIDEO_TW2804=m
+CONFIG_VIDEO_TW9903=m
+CONFIG_VIDEO_TW9906=m
+CONFIG_VIDEO_IMX219=m
+CONFIG_VIDEO_IMX290=m
+CONFIG_VIDEO_IMX477=m
+CONFIG_VIDEO_OV5647=m
+CONFIG_VIDEO_OV7251=m
+CONFIG_VIDEO_OV7640=m
+CONFIG_VIDEO_OV9281=m
+CONFIG_VIDEO_IRS1125=m
+CONFIG_VIDEO_MT9V011=m
+CONFIG_DRM=m
+CONFIG_DRM_LOAD_EDID_FIRMWARE=y
+CONFIG_DRM_UDL=m
+CONFIG_DRM_PANEL_SIMPLE=m
+CONFIG_DRM_PANEL_JDI_LT070ME05000=m
+CONFIG_DRM_PANEL_RASPBERRYPI_TOUCHSCREEN=m
+CONFIG_DRM_DISPLAY_CONNECTOR=m
+CONFIG_DRM_SIMPLE_BRIDGE=m
+CONFIG_DRM_VC4=m
+CONFIG_DRM_VC4_HDMI_CEC=y
+CONFIG_TINYDRM_ILI9225=m
+CONFIG_TINYDRM_ILI9341=m
+CONFIG_TINYDRM_MI0283QT=m
+CONFIG_TINYDRM_REPAPER=m
+CONFIG_TINYDRM_ST7586=m
+CONFIG_TINYDRM_ST7735R=m
+CONFIG_DRM_GUD=m
+CONFIG_FB=y
+CONFIG_FB_BCM2708=y
+CONFIG_FB_UDL=m
+CONFIG_FB_SIMPLE=y
+CONFIG_FB_SSD1307=m
+CONFIG_FB_RPISENSE=m
+CONFIG_BACKLIGHT_RPI=m
+CONFIG_BACKLIGHT_GPIO=m
+CONFIG_FRAMEBUFFER_CONSOLE=y
+CONFIG_FRAMEBUFFER_CONSOLE_ROTATION=y
+CONFIG_LOGO=y
+# CONFIG_LOGO_LINUX_MONO is not set
+# CONFIG_LOGO_LINUX_VGA16 is not set
+CONFIG_SOUND=y
+CONFIG_SND=m
+CONFIG_SND_OSSEMUL=y
+CONFIG_SND_PCM_OSS=m
+CONFIG_SND_HRTIMER=m
+CONFIG_SND_SEQUENCER=m
+CONFIG_SND_SEQ_DUMMY=m
+CONFIG_SND_DUMMY=m
+CONFIG_SND_ALOOP=m
+CONFIG_SND_VIRMIDI=m
+CONFIG_SND_MTPAV=m
+CONFIG_SND_SERIAL_U16550=m
+CONFIG_SND_MPU401=m
+CONFIG_SND_USB_AUDIO=m
+CONFIG_SND_USB_UA101=m
+CONFIG_SND_USB_CAIAQ=m
+CONFIG_SND_USB_CAIAQ_INPUT=y
+CONFIG_SND_USB_6FIRE=m
+CONFIG_SND_USB_HIFACE=m
+CONFIG_SND_SOC=m
+CONFIG_SND_BCM2835_SOC_I2S=m
+CONFIG_SND_BCM2708_SOC_GOOGLEVOICEHAT_SOUNDCARD=m
+CONFIG_SND_BCM2708_SOC_HIFIBERRY_DAC=m
+CONFIG_SND_BCM2708_SOC_HIFIBERRY_DACPLUS=m
+CONFIG_SND_BCM2708_SOC_HIFIBERRY_DACPLUSHD=m
+CONFIG_SND_BCM2708_SOC_HIFIBERRY_DACPLUSADC=m
+CONFIG_SND_BCM2708_SOC_HIFIBERRY_DACPLUSADCPRO=m
+CONFIG_SND_BCM2708_SOC_HIFIBERRY_DACPLUSDSP=m
+CONFIG_SND_BCM2708_SOC_HIFIBERRY_DIGI=m
+CONFIG_SND_BCM2708_SOC_HIFIBERRY_AMP=m
+CONFIG_SND_BCM2708_SOC_PIFI_40=m
+CONFIG_SND_BCM2708_SOC_RPI_CIRRUS=m
+CONFIG_SND_BCM2708_SOC_RPI_DAC=m
+CONFIG_SND_BCM2708_SOC_RPI_PROTO=m
+CONFIG_SND_BCM2708_SOC_JUSTBOOM_BOTH=m
+CONFIG_SND_BCM2708_SOC_JUSTBOOM_DAC=m
+CONFIG_SND_BCM2708_SOC_JUSTBOOM_DIGI=m
+CONFIG_SND_BCM2708_SOC_IQAUDIO_CODEC=m
+CONFIG_SND_BCM2708_SOC_IQAUDIO_DAC=m
+CONFIG_SND_BCM2708_SOC_IQAUDIO_DIGI=m
+CONFIG_SND_BCM2708_SOC_I_SABRE_Q2M=m
+CONFIG_SND_BCM2708_SOC_ADAU1977_ADC=m
+CONFIG_SND_AUDIOINJECTOR_PI_SOUNDCARD=m
+CONFIG_SND_AUDIOINJECTOR_OCTO_SOUNDCARD=m
+CONFIG_SND_AUDIOINJECTOR_ISOLATED_SOUNDCARD=m
+CONFIG_SND_AUDIOSENSE_PI=m
+CONFIG_SND_DIGIDAC1_SOUNDCARD=m
+CONFIG_SND_BCM2708_SOC_DIONAUDIO_LOCO=m
+CONFIG_SND_BCM2708_SOC_DIONAUDIO_LOCO_V2=m
+CONFIG_SND_BCM2708_SOC_ALLO_PIANO_DAC=m
+CONFIG_SND_BCM2708_SOC_ALLO_PIANO_DAC_PLUS=m
+CONFIG_SND_BCM2708_SOC_ALLO_BOSS_DAC=m
+CONFIG_SND_BCM2708_SOC_ALLO_BOSS2_DAC=m
+CONFIG_SND_BCM2708_SOC_ALLO_DIGIONE=m
+CONFIG_SND_BCM2708_SOC_ALLO_KATANA_DAC=m
+CONFIG_SND_BCM2708_SOC_FE_PI_AUDIO=m
+CONFIG_SND_PISOUND=m
+CONFIG_SND_SOC_AD193X_SPI=m
+CONFIG_SND_SOC_AD193X_I2C=m
+CONFIG_SND_SOC_ADAU1701=m
+CONFIG_SND_SOC_ADAU7002=m
+CONFIG_SND_SOC_AK4554=m
+CONFIG_SND_SOC_CS4265=m
+CONFIG_SND_SOC_ICS43432=m
+CONFIG_SND_SOC_MA120X0P=m
+CONFIG_SND_SOC_MAX98357A=m
+CONFIG_SND_SOC_SPDIF=m
+CONFIG_SND_SOC_WM8804_I2C=m
+CONFIG_SND_SOC_WM8960=m
+CONFIG_SND_SIMPLE_CARD=m
+CONFIG_HID_BATTERY_STRENGTH=y
+CONFIG_HIDRAW=y
+CONFIG_UHID=m
+CONFIG_HID_A4TECH=m
+CONFIG_HID_ACRUX=m
+CONFIG_HID_APPLE=m
+CONFIG_HID_ASUS=m
+CONFIG_HID_BELKIN=m
+CONFIG_HID_BETOP_FF=m
+CONFIG_HID_BIGBEN_FF=m
+CONFIG_HID_CHERRY=m
+CONFIG_HID_CHICONY=m
+CONFIG_HID_CYPRESS=m
+CONFIG_HID_DRAGONRISE=m
+CONFIG_HID_EMS_FF=m
+CONFIG_HID_ELECOM=m
+CONFIG_HID_ELO=m
+CONFIG_HID_EZKEY=m
+CONFIG_HID_GEMBIRD=m
+CONFIG_HID_HOLTEK=m
+CONFIG_HID_KEYTOUCH=m
+CONFIG_HID_KYE=m
+CONFIG_HID_UCLOGIC=m
+CONFIG_HID_WALTOP=m
+CONFIG_HID_GYRATION=m
+CONFIG_HID_TWINHAN=m
+CONFIG_HID_KENSINGTON=m
+CONFIG_HID_LCPOWER=m
+CONFIG_HID_LOGITECH=m
+CONFIG_HID_LOGITECH_DJ=m
+CONFIG_LOGITECH_FF=y
+CONFIG_LOGIRUMBLEPAD2_FF=y
+CONFIG_LOGIG940_FF=y
+CONFIG_HID_MAGICMOUSE=m
+CONFIG_HID_MICROSOFT=m
+CONFIG_HID_MONTEREY=m
+CONFIG_HID_MULTITOUCH=m
+CONFIG_HID_NTRIG=m
+CONFIG_HID_ORTEK=m
+CONFIG_HID_PANTHERLORD=m
+CONFIG_HID_PETALYNX=m
+CONFIG_HID_PICOLCD=m
+CONFIG_HID_ROCCAT=m
+CONFIG_HID_SAMSUNG=m
+CONFIG_HID_SONY=m
+CONFIG_SONY_FF=y
+CONFIG_HID_SPEEDLINK=m
+CONFIG_HID_STEAM=m
+CONFIG_HID_SUNPLUS=m
+CONFIG_HID_GREENASIA=m
+CONFIG_HID_SMARTJOYPLUS=m
+CONFIG_HID_TOPSEED=m
+CONFIG_HID_THINGM=m
+CONFIG_HID_THRUSTMASTER=m
+CONFIG_HID_WACOM=m
+CONFIG_HID_WIIMOTE=m
+CONFIG_HID_XINMO=m
+CONFIG_HID_ZEROPLUS=m
+CONFIG_HID_ZYDACRON=m
+CONFIG_HID_PID=y
+CONFIG_USB_HIDDEV=y
+CONFIG_I2C_HID=m
+CONFIG_USB=y
+CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
+CONFIG_USB_MON=m
+CONFIG_USB_DWCOTG=y
+CONFIG_USB_PRINTER=m
+CONFIG_USB_TMC=m
+CONFIG_USB_STORAGE=y
+CONFIG_USB_STORAGE_REALTEK=m
+CONFIG_USB_STORAGE_DATAFAB=m
+CONFIG_USB_STORAGE_FREECOM=m
+CONFIG_USB_STORAGE_ISD200=m
+CONFIG_USB_STORAGE_USBAT=m
+CONFIG_USB_STORAGE_SDDR09=m
+CONFIG_USB_STORAGE_SDDR55=m
+CONFIG_USB_STORAGE_JUMPSHOT=m
+CONFIG_USB_STORAGE_ALAUDA=m
+CONFIG_USB_STORAGE_ONETOUCH=m
+CONFIG_USB_STORAGE_KARMA=m
+CONFIG_USB_STORAGE_CYPRESS_ATACB=m
+CONFIG_USB_STORAGE_ENE_UB6250=m
+CONFIG_USB_UAS=m
+CONFIG_USB_MDC800=m
+CONFIG_USB_MICROTEK=m
+CONFIG_USBIP_CORE=m
+CONFIG_USBIP_VHCI_HCD=m
+CONFIG_USBIP_HOST=m
+CONFIG_USBIP_VUDC=m
+CONFIG_USB_DWC2=m
+CONFIG_USB_SERIAL=m
+CONFIG_USB_SERIAL_GENERIC=y
+CONFIG_USB_SERIAL_AIRCABLE=m
+CONFIG_USB_SERIAL_ARK3116=m
+CONFIG_USB_SERIAL_BELKIN=m
+CONFIG_USB_SERIAL_CH341=m
+CONFIG_USB_SERIAL_WHITEHEAT=m
+CONFIG_USB_SERIAL_DIGI_ACCELEPORT=m
+CONFIG_USB_SERIAL_CP210X=m
+CONFIG_USB_SERIAL_CYPRESS_M8=m
+CONFIG_USB_SERIAL_EMPEG=m
+CONFIG_USB_SERIAL_FTDI_SIO=m
+CONFIG_USB_SERIAL_VISOR=m
+CONFIG_USB_SERIAL_IPAQ=m
+CONFIG_USB_SERIAL_IR=m
+CONFIG_USB_SERIAL_EDGEPORT=m
+CONFIG_USB_SERIAL_EDGEPORT_TI=m
+CONFIG_USB_SERIAL_F81232=m
+CONFIG_USB_SERIAL_GARMIN=m
+CONFIG_USB_SERIAL_IPW=m
+CONFIG_USB_SERIAL_IUU=m
+CONFIG_USB_SERIAL_KEYSPAN_PDA=m
+CONFIG_USB_SERIAL_KEYSPAN=m
+CONFIG_USB_SERIAL_KLSI=m
+CONFIG_USB_SERIAL_KOBIL_SCT=m
+CONFIG_USB_SERIAL_MCT_U232=m
+CONFIG_USB_SERIAL_METRO=m
+CONFIG_USB_SERIAL_MOS7720=m
+CONFIG_USB_SERIAL_MOS7840=m
+CONFIG_USB_SERIAL_NAVMAN=m
+CONFIG_USB_SERIAL_PL2303=m
+CONFIG_USB_SERIAL_OTI6858=m
+CONFIG_USB_SERIAL_QCAUX=m
+CONFIG_USB_SERIAL_QUALCOMM=m
+CONFIG_USB_SERIAL_SPCP8X5=m
+CONFIG_USB_SERIAL_SAFE=m
+CONFIG_USB_SERIAL_SIERRAWIRELESS=m
+CONFIG_USB_SERIAL_SYMBOL=m
+CONFIG_USB_SERIAL_TI=m
+CONFIG_USB_SERIAL_CYBERJACK=m
+CONFIG_USB_SERIAL_XIRCOM=m
+CONFIG_USB_SERIAL_OPTION=m
+CONFIG_USB_SERIAL_OMNINET=m
+CONFIG_USB_SERIAL_OPTICON=m
+CONFIG_USB_SERIAL_XSENS_MT=m
+CONFIG_USB_SERIAL_WISHBONE=m
+CONFIG_USB_SERIAL_SSU100=m
+CONFIG_USB_SERIAL_QT2=m
+CONFIG_USB_SERIAL_DEBUG=m
+CONFIG_USB_EMI62=m
+CONFIG_USB_EMI26=m
+CONFIG_USB_ADUTUX=m
+CONFIG_USB_SEVSEG=m
+CONFIG_USB_LEGOTOWER=m
+CONFIG_USB_LCD=m
+CONFIG_USB_CYPRESS_CY7C63=m
+CONFIG_USB_CYTHERM=m
+CONFIG_USB_IDMOUSE=m
+CONFIG_USB_FTDI_ELAN=m
+CONFIG_USB_APPLEDISPLAY=m
+CONFIG_USB_LD=m
+CONFIG_USB_TRANCEVIBRATOR=m
+CONFIG_USB_IOWARRIOR=m
+CONFIG_USB_TEST=m
+CONFIG_USB_ISIGHTFW=m
+CONFIG_USB_YUREX=m
+CONFIG_USB_ATM=m
+CONFIG_USB_SPEEDTOUCH=m
+CONFIG_USB_CXACRU=m
+CONFIG_USB_UEAGLEATM=m
+CONFIG_USB_XUSBATM=m
+CONFIG_USB_GADGET=m
+CONFIG_USB_CONFIGFS=m
+CONFIG_USB_CONFIGFS_SERIAL=y
+CONFIG_USB_CONFIGFS_ACM=y
+CONFIG_USB_CONFIGFS_OBEX=y
+CONFIG_USB_CONFIGFS_NCM=y
+CONFIG_USB_CONFIGFS_ECM=y
+CONFIG_USB_CONFIGFS_ECM_SUBSET=y
+CONFIG_USB_CONFIGFS_RNDIS=y
+CONFIG_USB_CONFIGFS_EEM=y
+CONFIG_USB_CONFIGFS_MASS_STORAGE=y
+CONFIG_USB_CONFIGFS_F_LB_SS=y
+CONFIG_USB_CONFIGFS_F_FS=y
+CONFIG_USB_CONFIGFS_F_UAC1=y
+CONFIG_USB_CONFIGFS_F_UAC2=y
+CONFIG_USB_CONFIGFS_F_MIDI=y
+CONFIG_USB_CONFIGFS_F_HID=y
+CONFIG_USB_CONFIGFS_F_UVC=y
+CONFIG_USB_CONFIGFS_F_PRINTER=y
+CONFIG_USB_ZERO=m
+CONFIG_USB_AUDIO=m
+CONFIG_USB_ETH=m
+CONFIG_USB_GADGETFS=m
+CONFIG_USB_MASS_STORAGE=m
+CONFIG_USB_G_SERIAL=m
+CONFIG_USB_MIDI_GADGET=m
+CONFIG_USB_G_PRINTER=m
+CONFIG_USB_CDC_COMPOSITE=m
+CONFIG_USB_G_ACM_MS=m
+CONFIG_USB_G_MULTI=m
+CONFIG_USB_G_HID=m
+CONFIG_USB_G_WEBCAM=m
+CONFIG_MMC=y
+CONFIG_MMC_BLOCK_MINORS=32
+CONFIG_MMC_BCM2835_MMC=y
+CONFIG_MMC_BCM2835_DMA=y
+CONFIG_MMC_BCM2835_SDHOST=y
+CONFIG_MMC_SDHCI=y
+CONFIG_MMC_SDHCI_PLTFM=y
+CONFIG_MMC_SPI=m
+CONFIG_LEDS_CLASS=y
+CONFIG_LEDS_PCA9532=m
+CONFIG_LEDS_GPIO=y
+CONFIG_LEDS_PCA955X=m
+CONFIG_LEDS_PCA963X=m
+CONFIG_LEDS_IS31FL32XX=m
+CONFIG_LEDS_TRIGGER_TIMER=y
+CONFIG_LEDS_TRIGGER_ONESHOT=y
+CONFIG_LEDS_TRIGGER_HEARTBEAT=y
+CONFIG_LEDS_TRIGGER_BACKLIGHT=y
+CONFIG_LEDS_TRIGGER_CPU=y
+CONFIG_LEDS_TRIGGER_GPIO=y
+CONFIG_LEDS_TRIGGER_DEFAULT_ON=y
+CONFIG_LEDS_TRIGGER_TRANSIENT=m
+CONFIG_LEDS_TRIGGER_CAMERA=m
+CONFIG_LEDS_TRIGGER_INPUT=y
+CONFIG_LEDS_TRIGGER_PANIC=y
+CONFIG_LEDS_TRIGGER_NETDEV=m
+CONFIG_LEDS_TRIGGER_ACTPWR=y
+CONFIG_ACCESSIBILITY=y
+CONFIG_SPEAKUP=m
+CONFIG_SPEAKUP_SYNTH_SOFT=m
+CONFIG_RTC_CLASS=y
+CONFIG_RTC_DRV_ABX80X=m
+CONFIG_RTC_DRV_DS1307=m
+CONFIG_RTC_DRV_DS1374=m
+CONFIG_RTC_DRV_DS1672=m
+CONFIG_RTC_DRV_MAX6900=m
+CONFIG_RTC_DRV_RS5C372=m
+CONFIG_RTC_DRV_ISL1208=m
+CONFIG_RTC_DRV_ISL12022=m
+CONFIG_RTC_DRV_X1205=m
+CONFIG_RTC_DRV_PCF8523=m
+CONFIG_RTC_DRV_PCF85063=m
+CONFIG_RTC_DRV_PCF85363=m
+CONFIG_RTC_DRV_PCF8563=m
+CONFIG_RTC_DRV_PCF8583=m
+CONFIG_RTC_DRV_M41T80=m
+CONFIG_RTC_DRV_BQ32K=m
+CONFIG_RTC_DRV_S35390A=m
+CONFIG_RTC_DRV_FM3130=m
+CONFIG_RTC_DRV_RX8581=m
+CONFIG_RTC_DRV_RX8025=m
+CONFIG_RTC_DRV_EM3027=m
+CONFIG_RTC_DRV_RV3028=m
+CONFIG_RTC_DRV_SD3078=m
+CONFIG_RTC_DRV_M41T93=m
+CONFIG_RTC_DRV_M41T94=m
+CONFIG_RTC_DRV_DS1302=m
+CONFIG_RTC_DRV_DS1305=m
+CONFIG_RTC_DRV_DS1390=m
+CONFIG_RTC_DRV_R9701=m
+CONFIG_RTC_DRV_RX4581=m
+CONFIG_RTC_DRV_RS5C348=m
+CONFIG_RTC_DRV_MAX6902=m
+CONFIG_RTC_DRV_PCF2123=m
+CONFIG_RTC_DRV_DS3232=m
+CONFIG_RTC_DRV_PCF2127=m
+CONFIG_RTC_DRV_RV3029C2=m
+CONFIG_DMADEVICES=y
+CONFIG_DMA_BCM2835=y
+CONFIG_DMA_BCM2708=y
+CONFIG_DMABUF_HEAPS=y
+CONFIG_DMABUF_HEAPS_SYSTEM=y
+CONFIG_DMABUF_HEAPS_CMA=y
+CONFIG_AUXDISPLAY=y
+CONFIG_HD44780=m
+CONFIG_UIO=m
+CONFIG_UIO_PDRV_GENIRQ=m
+CONFIG_STAGING=y
+CONFIG_PRISM2_USB=m
+CONFIG_R8712U=m
+CONFIG_R8188EU=m
+CONFIG_VT6656=m
+CONFIG_STAGING_MEDIA=y
+CONFIG_FB_TFT=m
+CONFIG_FB_TFT_AGM1264K_FL=m
+CONFIG_FB_TFT_BD663474=m
+CONFIG_FB_TFT_HX8340BN=m
+CONFIG_FB_TFT_HX8347D=m
+CONFIG_FB_TFT_HX8353D=m
+CONFIG_FB_TFT_HX8357D=m
+CONFIG_FB_TFT_ILI9163=m
+CONFIG_FB_TFT_ILI9320=m
+CONFIG_FB_TFT_ILI9325=m
+CONFIG_FB_TFT_ILI9340=m
+CONFIG_FB_TFT_ILI9341=m
+CONFIG_FB_TFT_ILI9481=m
+CONFIG_FB_TFT_ILI9486=m
+CONFIG_FB_TFT_PCD8544=m
+CONFIG_FB_TFT_RA8875=m
+CONFIG_FB_TFT_S6D02A1=m
+CONFIG_FB_TFT_S6D1121=m
+CONFIG_FB_TFT_SH1106=m
+CONFIG_FB_TFT_SSD1289=m
+CONFIG_FB_TFT_SSD1306=m
+CONFIG_FB_TFT_SSD1331=m
+CONFIG_FB_TFT_SSD1351=m
+CONFIG_FB_TFT_ST7735R=m
+CONFIG_FB_TFT_ST7789V=m
+CONFIG_FB_TFT_TINYLCD=m
+CONFIG_FB_TFT_TLS8204=m
+CONFIG_FB_TFT_UC1701=m
+CONFIG_FB_TFT_UPD161704=m
+CONFIG_FB_TFT_WATTEROTT=m
+CONFIG_BCM2835_VCHIQ=y
+CONFIG_SND_BCM2835=m
+CONFIG_VIDEO_BCM2835=m
+CONFIG_VIDEO_CODEC_BCM2835=m
+CONFIG_VIDEO_ISP_BCM2835=m
+CONFIG_CLK_RASPBERRYPI=y
+CONFIG_MAILBOX=y
+CONFIG_BCM2835_MBOX=y
+# CONFIG_IOMMU_SUPPORT is not set
+CONFIG_RASPBERRYPI_POWER=y
+CONFIG_EXTCON=m
+CONFIG_EXTCON_ARIZONA=m
+CONFIG_IIO=m
+CONFIG_IIO_BUFFER_CB=m
+CONFIG_MCP320X=m
+CONFIG_MCP3422=m
+CONFIG_TI_ADS1015=m
+CONFIG_BME680=m
+CONFIG_CCS811=m
+CONFIG_SENSIRION_SGP30=m
+CONFIG_SPS30=m
+CONFIG_DHT11=m
+CONFIG_HDC100X=m
+CONFIG_HTU21=m
+CONFIG_INV_MPU6050_I2C=m
+CONFIG_APDS9960=m
+CONFIG_BH1750=m
+CONFIG_TSL4531=m
+CONFIG_VEML6070=m
+CONFIG_BMP280=m
+CONFIG_MAXIM_THERMOCOUPLE=m
+CONFIG_MAX31856=m
+CONFIG_PWM_BCM2835=m
+CONFIG_PWM_PCA9685=m
+CONFIG_RPI_AXIPERF=m
+CONFIG_EXT4_FS=y
+CONFIG_EXT4_FS_POSIX_ACL=y
+CONFIG_EXT4_FS_SECURITY=y
+CONFIG_REISERFS_FS=m
+CONFIG_REISERFS_FS_XATTR=y
+CONFIG_REISERFS_FS_POSIX_ACL=y
+CONFIG_REISERFS_FS_SECURITY=y
+CONFIG_JFS_FS=m
+CONFIG_JFS_POSIX_ACL=y
+CONFIG_JFS_SECURITY=y
+CONFIG_JFS_STATISTICS=y
+CONFIG_XFS_FS=m
+CONFIG_XFS_QUOTA=y
+CONFIG_XFS_POSIX_ACL=y
+CONFIG_XFS_RT=y
+CONFIG_GFS2_FS=m
+CONFIG_OCFS2_FS=m
+CONFIG_BTRFS_FS=m
+CONFIG_BTRFS_FS_POSIX_ACL=y
+CONFIG_NILFS2_FS=m
+CONFIG_F2FS_FS=y
+CONFIG_FANOTIFY=y
+CONFIG_QFMT_V1=m
+CONFIG_QFMT_V2=m
+CONFIG_AUTOFS4_FS=y
+CONFIG_FUSE_FS=m
+CONFIG_CUSE=m
+CONFIG_OVERLAY_FS=m
+CONFIG_FSCACHE=y
+CONFIG_FSCACHE_STATS=y
+CONFIG_FSCACHE_HISTOGRAM=y
+CONFIG_CACHEFILES=y
+CONFIG_ISO9660_FS=m
+CONFIG_JOLIET=y
+CONFIG_ZISOFS=y
+CONFIG_UDF_FS=m
+CONFIG_MSDOS_FS=y
+CONFIG_VFAT_FS=y
+CONFIG_FAT_DEFAULT_IOCHARSET="ascii"
+CONFIG_EXFAT_FS=m
+CONFIG_NTFS_FS=m
+CONFIG_NTFS_RW=y
+CONFIG_TMPFS=y
+CONFIG_TMPFS_POSIX_ACL=y
+CONFIG_ECRYPT_FS=m
+CONFIG_HFS_FS=m
+CONFIG_HFSPLUS_FS=m
+CONFIG_JFFS2_FS=m
+CONFIG_JFFS2_SUMMARY=y
+CONFIG_UBIFS_FS=m
+CONFIG_SQUASHFS=m
+CONFIG_SQUASHFS_XATTR=y
+CONFIG_SQUASHFS_LZO=y
+CONFIG_SQUASHFS_XZ=y
+CONFIG_NFS_FS=y
+CONFIG_NFS_V3_ACL=y
+CONFIG_NFS_V4=y
+CONFIG_NFS_SWAP=y
+CONFIG_NFS_V4_1=y
+CONFIG_NFS_V4_2=y
+CONFIG_ROOT_NFS=y
+CONFIG_NFS_FSCACHE=y
+CONFIG_NFSD=m
+CONFIG_NFSD_V3_ACL=y
+CONFIG_NFSD_V4=y
+CONFIG_CIFS=m
+CONFIG_CIFS_WEAK_PW_HASH=y
+CONFIG_CIFS_UPCALL=y
+CONFIG_CIFS_XATTR=y
+CONFIG_CIFS_DFS_UPCALL=y
+CONFIG_CIFS_FSCACHE=y
+CONFIG_9P_FS=m
+CONFIG_9P_FS_POSIX_ACL=y
+CONFIG_NLS_DEFAULT="utf8"
+CONFIG_NLS_CODEPAGE_437=y
+CONFIG_NLS_CODEPAGE_737=m
+CONFIG_NLS_CODEPAGE_775=m
+CONFIG_NLS_CODEPAGE_850=m
+CONFIG_NLS_CODEPAGE_852=m
+CONFIG_NLS_CODEPAGE_855=m
+CONFIG_NLS_CODEPAGE_857=m
+CONFIG_NLS_CODEPAGE_860=m
+CONFIG_NLS_CODEPAGE_861=m
+CONFIG_NLS_CODEPAGE_862=m
+CONFIG_NLS_CODEPAGE_863=m
+CONFIG_NLS_CODEPAGE_864=m
+CONFIG_NLS_CODEPAGE_865=m
+CONFIG_NLS_CODEPAGE_866=m
+CONFIG_NLS_CODEPAGE_869=m
+CONFIG_NLS_CODEPAGE_936=m
+CONFIG_NLS_CODEPAGE_950=m
+CONFIG_NLS_CODEPAGE_932=m
+CONFIG_NLS_CODEPAGE_949=m
+CONFIG_NLS_CODEPAGE_874=m
+CONFIG_NLS_ISO8859_8=m
+CONFIG_NLS_CODEPAGE_1250=m
+CONFIG_NLS_CODEPAGE_1251=m
+CONFIG_NLS_ASCII=y
+CONFIG_NLS_ISO8859_1=m
+CONFIG_NLS_ISO8859_2=m
+CONFIG_NLS_ISO8859_3=m
+CONFIG_NLS_ISO8859_4=m
+CONFIG_NLS_ISO8859_5=m
+CONFIG_NLS_ISO8859_6=m
+CONFIG_NLS_ISO8859_7=m
+CONFIG_NLS_ISO8859_9=m
+CONFIG_NLS_ISO8859_13=m
+CONFIG_NLS_ISO8859_14=m
+CONFIG_NLS_ISO8859_15=m
+CONFIG_NLS_KOI8_R=m
+CONFIG_NLS_KOI8_U=m
+CONFIG_DLM=m
+CONFIG_SECURITY=y
+CONFIG_SECURITY_APPARMOR=y
+CONFIG_LSM=""
+CONFIG_CRYPTO_USER=m
+CONFIG_CRYPTO_CRYPTD=m
+CONFIG_CRYPTO_CHACHA20POLY1305=m
+CONFIG_CRYPTO_CBC=y
+CONFIG_CRYPTO_CTS=m
+CONFIG_CRYPTO_XTS=m
+CONFIG_CRYPTO_ADIANTUM=m
+CONFIG_CRYPTO_XCBC=m
+CONFIG_CRYPTO_TGR192=m
+CONFIG_CRYPTO_WP512=m
+CONFIG_CRYPTO_CAST5=m
+CONFIG_CRYPTO_DES=y
+CONFIG_CRYPTO_LZ4=m
+CONFIG_CRYPTO_USER_API_HASH=m
+CONFIG_CRYPTO_USER_API_SKCIPHER=m
+CONFIG_CRYPTO_USER_API_RNG=m
+CONFIG_CRYPTO_USER_API_AEAD=m
+# CONFIG_CRYPTO_HW is not set
+CONFIG_CRC_ITU_T=y
+CONFIG_LIBCRC32C=y
+CONFIG_DMA_CMA=y
+CONFIG_CMA_SIZE_MBYTES=5
+CONFIG_PRINTK_TIME=y
+CONFIG_BOOT_PRINTK_DELAY=y
+CONFIG_KGDB=y
+CONFIG_KGDB_KDB=y
+CONFIG_KDB_KEYBOARD=y
+CONFIG_DEBUG_MEMORY_INIT=y
+CONFIG_DETECT_HUNG_TASK=y
+CONFIG_LATENCYTOP=y
+CONFIG_FUNCTION_PROFILER=y
+CONFIG_STACK_TRACER=y
+CONFIG_IRQSOFF_TRACER=y
+CONFIG_SCHED_TRACER=y
+CONFIG_BLK_DEV_IO_TRACE=y
+# CONFIG_UPROBE_EVENTS is not set
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/configs/multi_v7_defconfig linux-5.10.52-v7l+/arch/arm/configs/multi_v7_defconfig
--- linux-5.10.52-orig/arch/arm/configs/multi_v7_defconfig	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/arch/arm/configs/multi_v7_defconfig	2021-07-25 16:45:35.378803702 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1092 @
 CONFIG_NVMEM_SUNXI_SID=y
 CONFIG_NVMEM_VF610_OCOTP=y
 CONFIG_MESON_MX_EFUSE=m
+CONFIG_NVMEM_RMEM=m
 CONFIG_FSI=m
 CONFIG_FSI_MASTER_GPIO=m
 CONFIG_FSI_MASTER_HUB=m
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/include/asm/cacheflush.h linux-5.10.52-v7l+/arch/arm/include/asm/cacheflush.h
--- linux-5.10.52-orig/arch/arm/include/asm/cacheflush.h	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/arch/arm/include/asm/cacheflush.h	2021-07-25 16:45:35.408803199 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:94 @
  *	DMA Cache Coherency
  *	===================
  *
+ *	dma_inv_range(start, end)
+ *
+ *		Invalidate (discard) the specified virtual address range.
+ *		May not write back any entries.  If 'start' or 'end'
+ *		are not cache line aligned, those lines must be written
+ *		back.
+ *		- start  - virtual start address
+ *		- end    - virtual end address
+ *
+ *	dma_clean_range(start, end)
+ *
+ *		Clean (write back) the specified virtual address range.
+ *		- start  - virtual start address
+ *		- end    - virtual end address
+ *
  *	dma_flush_range(start, end)
  *
  *		Clean and invalidate the specified virtual address range.
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:130 @
 	void (*dma_map_area)(const void *, size_t, int);
 	void (*dma_unmap_area)(const void *, size_t, int);
 
+	void (*dma_inv_range)(const void *, const void *);
+	void (*dma_clean_range)(const void *, const void *);
 	void (*dma_flush_range)(const void *, const void *);
 } __no_randomize_layout;
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:157 @
  * is visible to DMA, or data written by DMA to system memory is
  * visible to the CPU.
  */
+#define dmac_inv_range			cpu_cache.dma_inv_range
+#define dmac_clean_range		cpu_cache.dma_clean_range
 #define dmac_flush_range		cpu_cache.dma_flush_range
 
 #else
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:178 @
  * is visible to DMA, or data written by DMA to system memory is
  * visible to the CPU.
  */
+extern void dmac_inv_range(const void *, const void *);
+extern void dmac_clean_range(const void *, const void *);
 extern void dmac_flush_range(const void *, const void *);
 
 #endif
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/include/asm/glue-cache.h linux-5.10.52-v7l+/arch/arm/include/asm/glue-cache.h
--- linux-5.10.52-orig/arch/arm/include/asm/glue-cache.h	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/arch/arm/include/asm/glue-cache.h	2021-07-25 16:45:35.418803031 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:158 @
 #define __cpuc_coherent_user_range	__glue(_CACHE,_coherent_user_range)
 #define __cpuc_flush_dcache_area	__glue(_CACHE,_flush_kern_dcache_area)
 
+#define dmac_inv_range			__glue(_CACHE,_dma_inv_range)
+#define dmac_clean_range		__glue(_CACHE,_dma_clean_range)
 #define dmac_flush_range		__glue(_CACHE,_dma_flush_range)
 #endif
 
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/include/asm/irqflags.h linux-5.10.52-v7l+/arch/arm/include/asm/irqflags.h
--- linux-5.10.52-orig/arch/arm/include/asm/irqflags.h	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/arch/arm/include/asm/irqflags.h	2021-07-25 16:45:35.418803031 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:166 @
 }
 
 /*
- * restore saved IRQ & FIQ state
+ * restore saved IRQ state
  */
 #define arch_local_irq_restore arch_local_irq_restore
 static inline void arch_local_irq_restore(unsigned long flags)
 {
-	asm volatile(
-		"	msr	" IRQMASK_REG_NAME_W ", %0	@ local_irq_restore"
+	unsigned long temp = 0;
+	flags &= ~(1 << 6);
+	asm volatile (
+		" mrs %0, cpsr"
+		: "=r" (temp)
+		:
+		: "memory", "cc");
+		/* Preserve FIQ bit */
+		temp &= (1 << 6);
+		flags = flags | temp;
+	asm volatile (
+		"    msr    cpsr_c, %0    @ local_irq_restore"
 		:
 		: "r" (flags)
 		: "memory", "cc");
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/include/asm/string.h linux-5.10.52-v7l+/arch/arm/include/asm/string.h
--- linux-5.10.52-orig/arch/arm/include/asm/string.h	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/arch/arm/include/asm/string.h	2021-07-25 16:45:35.438802696 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:42 @
 	return __memset64(p, v, n * 8, v >> 32);
 }
 
+#ifdef CONFIG_BCM2835_FAST_MEMCPY
+#define __HAVE_ARCH_MEMCMP
+extern int memcmp(const void *, const void *, size_t);
+#endif
+
 #endif
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/include/asm/uaccess.h linux-5.10.52-v7l+/arch/arm/include/asm/uaccess.h
--- linux-5.10.52-orig/arch/arm/include/asm/uaccess.h	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/arch/arm/include/asm/uaccess.h	2021-07-25 16:45:35.438802696 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:519 @
 extern unsigned long __must_check
 arm_copy_from_user(void *to, const void __user *from, unsigned long n);
 
+extern unsigned long __must_check
+__copy_from_user_std(void *to, const void __user *from, unsigned long n);
+
 static inline unsigned long __must_check
 raw_copy_from_user(void *to, const void __user *from, unsigned long n)
 {
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/kernel/fiqasm.S linux-5.10.52-v7l+/arch/arm/kernel/fiqasm.S
--- linux-5.10.52-orig/arch/arm/kernel/fiqasm.S	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/arch/arm/kernel/fiqasm.S	2021-07-25 16:45:35.458802361 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:50 @
 	mov	r0, r0		@ avoid hazard prior to ARMv4
 	ret	lr
 ENDPROC(__get_fiq_regs)
+
+ENTRY(__FIQ_Branch)
+	mov pc, r8
+ENDPROC(__FIQ_Branch)
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/kernel/fiq.c linux-5.10.52-v7l+/arch/arm/kernel/fiq.c
--- linux-5.10.52-orig/arch/arm/kernel/fiq.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/arch/arm/kernel/fiq.c	2021-07-25 16:45:35.458802361 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:59 @
 static unsigned long dfl_fiq_insn;
 static struct pt_regs dfl_fiq_regs;
 
+extern int irq_activate(struct irq_desc *desc);
+
 /* Default reacquire function
  * - we always relinquish FIQ control
  * - we always reacquire FIQ control
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:145 @
 
 void enable_fiq(int fiq)
 {
+	struct irq_desc *desc = irq_to_desc(fiq + fiq_start);
+	irq_activate(desc);
 	enable_irq(fiq + fiq_start);
 }
 
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/kernel/reboot.c linux-5.10.52-v7l+/arch/arm/kernel/reboot.c
--- linux-5.10.52-orig/arch/arm/kernel/reboot.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/arch/arm/kernel/reboot.c	2021-07-25 16:45:35.468802193 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:105 @
  */
 void machine_halt(void)
 {
-	local_irq_disable();
-	smp_send_stop();
-	while (1);
+	machine_power_off();
 }
 
 /*
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/kernel/setup.c linux-5.10.52-v7l+/arch/arm/kernel/setup.c
--- linux-5.10.52-orig/arch/arm/kernel/setup.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/arch/arm/kernel/setup.c	2021-07-25 16:45:35.468802193 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1258 @
 {
 	int i, j;
 	u32 cpuid;
+	struct device_node *np;
+	const char *model;
 
 	for_each_online_cpu(i) {
 		/*
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1319 @
 	seq_printf(m, "Revision\t: %04x\n", system_rev);
 	seq_printf(m, "Serial\t\t: %s\n", system_serial);
 
+	np = of_find_node_by_path("/");
+	if (np) {
+		if (!of_property_read_string(np, "model",
+					     &model))
+			seq_printf(m, "Model\t\t: %s\n", model);
+		of_node_put(np);
+	}
+
 	return 0;
 }
 
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/lib/arm-mem.h linux-5.10.52-v7l+/arch/arm/lib/arm-mem.h
--- linux-5.10.52-orig/arch/arm/lib/arm-mem.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/lib/arm-mem.h	2021-07-25 16:45:35.478802025 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/*
+Copyright (c) 2013, Raspberry Pi Foundation
+Copyright (c) 2013, RISC OS Open Ltd
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above copyright
+      notice, this list of conditions and the following disclaimer in the
+      documentation and/or other materials provided with the distribution.
+    * Neither the name of the copyright holder nor the
+      names of its contributors may be used to endorse or promote products
+      derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+.macro myfunc fname
+ .func fname
+ .global fname
+fname:
+.endm
+
+.macro preload_leading_step1  backwards, ptr, base
+/* If the destination is already 16-byte aligned, then we need to preload
+ * between 0 and prefetch_distance (inclusive) cache lines ahead so there
+ * are no gaps when the inner loop starts.
+ */
+ .if backwards
+        sub     ptr, base, #1
+        bic     ptr, ptr, #31
+ .else
+        bic     ptr, base, #31
+ .endif
+ .set OFFSET, 0
+ .rept prefetch_distance+1
+        pld     [ptr, #OFFSET]
+  .if backwards
+   .set OFFSET, OFFSET-32
+  .else
+   .set OFFSET, OFFSET+32
+  .endif
+ .endr
+.endm
+
+.macro preload_leading_step2  backwards, ptr, base, leading_bytes, tmp
+/* However, if the destination is not 16-byte aligned, we may need to
+ * preload one more cache line than that. The question we need to ask is:
+ * are the leading bytes more than the amount by which the source
+ * pointer will be rounded down for preloading, and if so, by how many
+ * cache lines?
+ */
+ .if backwards
+/* Here we compare against how many bytes we are into the
+ * cache line, counting down from the highest such address.
+ * Effectively, we want to calculate
+ *     leading_bytes = dst&15
+ *     cacheline_offset = 31-((src-leading_bytes-1)&31)
+ *     extra_needed = leading_bytes - cacheline_offset
+ * and test if extra_needed is <= 0, or rearranging:
+ *     leading_bytes + (src-leading_bytes-1)&31 <= 31
+ */
+        mov     tmp, base, lsl #32-5
+        sbc     tmp, tmp, leading_bytes, lsl #32-5
+        adds    tmp, tmp, leading_bytes, lsl #32-5
+        bcc     61f
+        pld     [ptr, #-32*(prefetch_distance+1)]
+ .else
+/* Effectively, we want to calculate
+ *     leading_bytes = (-dst)&15
+ *     cacheline_offset = (src+leading_bytes)&31
+ *     extra_needed = leading_bytes - cacheline_offset
+ * and test if extra_needed is <= 0.
+ */
+        mov     tmp, base, lsl #32-5
+        add     tmp, tmp, leading_bytes, lsl #32-5
+        rsbs    tmp, tmp, leading_bytes, lsl #32-5
+        bls     61f
+        pld     [ptr, #32*(prefetch_distance+1)]
+ .endif
+61:
+.endm
+
+.macro preload_trailing  backwards, base, remain, tmp
+        /* We need either 0, 1 or 2 extra preloads */
+ .if backwards
+        rsb     tmp, base, #0
+        mov     tmp, tmp, lsl #32-5
+ .else
+        mov     tmp, base, lsl #32-5
+ .endif
+        adds    tmp, tmp, remain, lsl #32-5
+        adceqs  tmp, tmp, #0
+        /* The instruction above has two effects: ensures Z is only
+         * set if C was clear (so Z indicates that both shifted quantities
+         * were 0), and clears C if Z was set (so C indicates that the sum
+         * of the shifted quantities was greater and not equal to 32) */
+        beq     82f
+ .if backwards
+        sub     tmp, base, #1
+        bic     tmp, tmp, #31
+ .else
+        bic     tmp, base, #31
+ .endif
+        bcc     81f
+ .if backwards
+        pld     [tmp, #-32*(prefetch_distance+1)]
+81:
+        pld     [tmp, #-32*prefetch_distance]
+ .else
+        pld     [tmp, #32*(prefetch_distance+2)]
+81:
+        pld     [tmp, #32*(prefetch_distance+1)]
+ .endif
+82:
+.endm
+
+.macro preload_all    backwards, narrow_case, shift, base, remain, tmp0, tmp1
+ .if backwards
+        sub     tmp0, base, #1
+        bic     tmp0, tmp0, #31
+        pld     [tmp0]
+        sub     tmp1, base, remain, lsl #shift
+ .else
+        bic     tmp0, base, #31
+        pld     [tmp0]
+        add     tmp1, base, remain, lsl #shift
+        sub     tmp1, tmp1, #1
+ .endif
+        bic     tmp1, tmp1, #31
+        cmp     tmp1, tmp0
+        beq     92f
+ .if narrow_case
+        /* In this case, all the data fits in either 1 or 2 cache lines */
+        pld     [tmp1]
+ .else
+91:
+  .if backwards
+        sub     tmp0, tmp0, #32
+  .else
+        add     tmp0, tmp0, #32
+  .endif
+        cmp     tmp0, tmp1
+        pld     [tmp0]
+        bne     91b
+ .endif
+92:
+.endm
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/lib/copy_from_user.S linux-5.10.52-v7l+/arch/arm/lib/copy_from_user.S
--- linux-5.10.52-orig/arch/arm/lib/copy_from_user.S	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/arch/arm/lib/copy_from_user.S	2021-07-25 16:45:35.478802025 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:110 @
 
 	.text
 
-ENTRY(arm_copy_from_user)
+ENTRY(__copy_from_user_std)
+WEAK(arm_copy_from_user)
 #ifdef CONFIG_CPU_SPECTRE
 	get_thread_info r3
 	ldr	r3, [r3, #TI_ADDR_LIMIT]
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:121 @
 #include "copy_template.S"
 
 ENDPROC(arm_copy_from_user)
+ENDPROC(__copy_from_user_std)
 
 	.pushsection .text.fixup,"ax"
 	.align 0
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/lib/exports_rpi.c linux-5.10.52-v7l+/arch/arm/lib/exports_rpi.c
--- linux-5.10.52-orig/arch/arm/lib/exports_rpi.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/lib/exports_rpi.c	2021-07-25 16:45:35.478802025 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/**
+ * Copyright (c) 2014, Raspberry Pi (Trading) Ltd.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The names of the above-listed copyright holders may not be used
+ *    to endorse or promote products derived from this software without
+ *    specific prior written permission.
+ *
+ * ALTERNATIVELY, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2, as published by the Free
+ * Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+
+EXPORT_SYMBOL(memcmp);
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/lib/Makefile linux-5.10.52-v7l+/arch/arm/lib/Makefile
--- linux-5.10.52-orig/arch/arm/lib/Makefile	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/arch/arm/lib/Makefile	2021-07-25 16:45:35.478802025 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:10 @
 
 lib-y		:= changebit.o csumipv6.o csumpartial.o               \
 		   csumpartialcopy.o csumpartialcopyuser.o clearbit.o \
-		   delay.o delay-loop.o findbit.o memchr.o memcpy.o   \
-		   memmove.o memset.o setbit.o                        \
+		   delay.o delay-loop.o findbit.o memchr.o            \
+		   setbit.o                                           \
 		   strchr.o strrchr.o                                 \
 		   testchangebit.o testclearbit.o testsetbit.o        \
 		   ashldi3.o ashrdi3.o lshrdi3.o muldi3.o             \
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:28 @
   lib-y	+= backtrace.o
 endif
 
+# Choose optimised implementations for Raspberry Pi
+ifeq ($(CONFIG_BCM2835_FAST_MEMCPY),y)
+  CFLAGS_uaccess_with_memcpy.o += -DCOPY_FROM_USER_THRESHOLD=1600
+  CFLAGS_uaccess_with_memcpy.o += -DCOPY_TO_USER_THRESHOLD=672
+  obj-$(CONFIG_MODULES) += exports_rpi.o
+  lib-y        += memcpy_rpi.o memmove_rpi.o memset_rpi.o memcmp_rpi.o
+else
+  lib-y        += memcpy.o memmove.o memset.o
+endif
+
 # using lib_ here won't override already available weak symbols
 obj-$(CONFIG_UACCESS_WITH_MEMCPY) += uaccess_with_memcpy.o
 
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/lib/memcmp_rpi.S linux-5.10.52-v7l+/arch/arm/lib/memcmp_rpi.S
--- linux-5.10.52-orig/arch/arm/lib/memcmp_rpi.S	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/lib/memcmp_rpi.S	2021-07-25 16:45:35.488801858 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/*
+Copyright (c) 2013, Raspberry Pi Foundation
+Copyright (c) 2013, RISC OS Open Ltd
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above copyright
+      notice, this list of conditions and the following disclaimer in the
+      documentation and/or other materials provided with the distribution.
+    * Neither the name of the copyright holder nor the
+      names of its contributors may be used to endorse or promote products
+      derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include <linux/linkage.h>
+#include "arm-mem.h"
+
+/* Prevent the stack from becoming executable */
+#if defined(__linux__) && defined(__ELF__)
+.section .note.GNU-stack,"",%progbits
+#endif
+
+    .text
+    .arch armv6
+    .object_arch armv4
+    .arm
+    .altmacro
+    .p2align 2
+
+.macro memcmp_process_head  unaligned
+ .if unaligned
+        ldr     DAT0, [S_1], #4
+        ldr     DAT1, [S_1], #4
+        ldr     DAT2, [S_1], #4
+        ldr     DAT3, [S_1], #4
+ .else
+        ldmia   S_1!, {DAT0, DAT1, DAT2, DAT3}
+ .endif
+        ldmia   S_2!, {DAT4, DAT5, DAT6, DAT7}
+.endm
+
+.macro memcmp_process_tail
+        cmp     DAT0, DAT4
+        cmpeq   DAT1, DAT5
+        cmpeq   DAT2, DAT6
+        cmpeq   DAT3, DAT7
+        bne     200f
+.endm
+
+.macro memcmp_leading_31bytes
+        movs    DAT0, OFF, lsl #31
+        ldrmib  DAT0, [S_1], #1
+        ldrcsh  DAT1, [S_1], #2
+        ldrmib  DAT4, [S_2], #1
+        ldrcsh  DAT5, [S_2], #2
+        movpl   DAT0, #0
+        movcc   DAT1, #0
+        movpl   DAT4, #0
+        movcc   DAT5, #0
+        submi   N, N, #1
+        subcs   N, N, #2
+        cmp     DAT0, DAT4
+        cmpeq   DAT1, DAT5
+        bne     200f
+        movs    DAT0, OFF, lsl #29
+        ldrmi   DAT0, [S_1], #4
+        ldrcs   DAT1, [S_1], #4
+        ldrcs   DAT2, [S_1], #4
+        ldrmi   DAT4, [S_2], #4
+        ldmcsia S_2!, {DAT5, DAT6}
+        movpl   DAT0, #0
+        movcc   DAT1, #0
+        movcc   DAT2, #0
+        movpl   DAT4, #0
+        movcc   DAT5, #0
+        movcc   DAT6, #0
+        submi   N, N, #4
+        subcs   N, N, #8
+        cmp     DAT0, DAT4
+        cmpeq   DAT1, DAT5
+        cmpeq   DAT2, DAT6
+        bne     200f
+        tst     OFF, #16
+        beq     105f
+        memcmp_process_head  1
+        sub     N, N, #16
+        memcmp_process_tail
+105:
+.endm
+
+.macro memcmp_trailing_15bytes  unaligned
+        movs    N, N, lsl #29
+ .if unaligned
+        ldrcs   DAT0, [S_1], #4
+        ldrcs   DAT1, [S_1], #4
+ .else
+        ldmcsia S_1!, {DAT0, DAT1}
+ .endif
+        ldrmi   DAT2, [S_1], #4
+        ldmcsia S_2!, {DAT4, DAT5}
+        ldrmi   DAT6, [S_2], #4
+        movcc   DAT0, #0
+        movcc   DAT1, #0
+        movpl   DAT2, #0
+        movcc   DAT4, #0
+        movcc   DAT5, #0
+        movpl   DAT6, #0
+        cmp     DAT0, DAT4
+        cmpeq   DAT1, DAT5
+        cmpeq   DAT2, DAT6
+        bne     200f
+        movs    N, N, lsl #2
+        ldrcsh  DAT0, [S_1], #2
+        ldrmib  DAT1, [S_1]
+        ldrcsh  DAT4, [S_2], #2
+        ldrmib  DAT5, [S_2]
+        movcc   DAT0, #0
+        movpl   DAT1, #0
+        movcc   DAT4, #0
+        movpl   DAT5, #0
+        cmp     DAT0, DAT4
+        cmpeq   DAT1, DAT5
+        bne     200f
+.endm
+
+.macro memcmp_long_inner_loop  unaligned
+110:
+        memcmp_process_head  unaligned
+        pld     [S_2, #prefetch_distance*32 + 16]
+        memcmp_process_tail
+        memcmp_process_head  unaligned
+        pld     [S_1, OFF]
+        memcmp_process_tail
+        subs    N, N, #32
+        bhs     110b
+        /* Just before the final (prefetch_distance+1) 32-byte blocks,
+         * deal with final preloads */
+        preload_trailing  0, S_1, N, DAT0
+        preload_trailing  0, S_2, N, DAT0
+        add     N, N, #(prefetch_distance+2)*32 - 16
+120:
+        memcmp_process_head  unaligned
+        memcmp_process_tail
+        subs    N, N, #16
+        bhs     120b
+        /* Trailing words and bytes */
+        tst     N, #15
+        beq     199f
+        memcmp_trailing_15bytes  unaligned
+199:    /* Reached end without detecting a difference */
+        mov     a1, #0
+        setend  le
+        pop     {DAT1-DAT6, pc}
+.endm
+
+.macro memcmp_short_inner_loop  unaligned
+        subs    N, N, #16     /* simplifies inner loop termination */
+        blo     122f
+120:
+        memcmp_process_head  unaligned
+        memcmp_process_tail
+        subs    N, N, #16
+        bhs     120b
+122:    /* Trailing words and bytes */
+        tst     N, #15
+        beq     199f
+        memcmp_trailing_15bytes  unaligned
+199:    /* Reached end without detecting a difference */
+        mov     a1, #0
+        setend  le
+        pop     {DAT1-DAT6, pc}
+.endm
+
+/*
+ * int memcmp(const void *s1, const void *s2, size_t n);
+ * On entry:
+ * a1 = pointer to buffer 1
+ * a2 = pointer to buffer 2
+ * a3 = number of bytes to compare (as unsigned chars)
+ * On exit:
+ * a1 = >0/=0/<0 if s1 >/=/< s2
+ */
+
+.set prefetch_distance, 2
+
+ENTRY(memcmp)
+        S_1     .req    a1
+        S_2     .req    a2
+        N       .req    a3
+        DAT0    .req    a4
+        DAT1    .req    v1
+        DAT2    .req    v2
+        DAT3    .req    v3
+        DAT4    .req    v4
+        DAT5    .req    v5
+        DAT6    .req    v6
+        DAT7    .req    ip
+        OFF     .req    lr
+
+        push    {DAT1-DAT6, lr}
+        setend  be /* lowest-addressed bytes are most significant */
+
+        /* To preload ahead as we go, we need at least (prefetch_distance+2) 32-byte blocks */
+        cmp     N, #(prefetch_distance+3)*32 - 1
+        blo     170f
+
+        /* Long case */
+        /* Adjust N so that the decrement instruction can also test for
+         * inner loop termination. We want it to stop when there are
+         * (prefetch_distance+1) complete blocks to go. */
+        sub     N, N, #(prefetch_distance+2)*32
+        preload_leading_step1  0, DAT0, S_1
+        preload_leading_step1  0, DAT1, S_2
+        tst     S_2, #31
+        beq     154f
+        rsb     OFF, S_2, #0 /* no need to AND with 15 here */
+        preload_leading_step2  0, DAT0, S_1, OFF, DAT2
+        preload_leading_step2  0, DAT1, S_2, OFF, DAT2
+        memcmp_leading_31bytes
+154:    /* Second source now cacheline (32-byte) aligned; we have at
+         * least one prefetch to go. */
+        /* Prefetch offset is best selected such that it lies in the
+         * first 8 of each 32 bytes - but it's just as easy to aim for
+         * the first one */
+        and     OFF, S_1, #31
+        rsb     OFF, OFF, #32*prefetch_distance
+        tst     S_1, #3
+        bne     140f
+        memcmp_long_inner_loop  0
+140:    memcmp_long_inner_loop  1
+
+170:    /* Short case */
+        teq     N, #0
+        beq     199f
+        preload_all 0, 0, 0, S_1, N, DAT0, DAT1
+        preload_all 0, 0, 0, S_2, N, DAT0, DAT1
+        tst     S_2, #3
+        beq     174f
+172:    subs    N, N, #1
+        blo     199f
+        ldrb    DAT0, [S_1], #1
+        ldrb    DAT4, [S_2], #1
+        cmp     DAT0, DAT4
+        bne     200f
+        tst     S_2, #3
+        bne     172b
+174:    /* Second source now 4-byte aligned; we have 0 or more bytes to go */
+        tst     S_1, #3
+        bne     140f
+        memcmp_short_inner_loop  0
+140:    memcmp_short_inner_loop  1
+
+200:    /* Difference found: determine sign. */
+        movhi   a1, #1
+        movlo   a1, #-1
+        setend  le
+        pop     {DAT1-DAT6, pc}
+
+        .unreq  S_1
+        .unreq  S_2
+        .unreq  N
+        .unreq  DAT0
+        .unreq  DAT1
+        .unreq  DAT2
+        .unreq  DAT3
+        .unreq  DAT4
+        .unreq  DAT5
+        .unreq  DAT6
+        .unreq  DAT7
+        .unreq  OFF
+ENDPROC(memcmp)
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/lib/memcpymove.h linux-5.10.52-v7l+/arch/arm/lib/memcpymove.h
--- linux-5.10.52-orig/arch/arm/lib/memcpymove.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/lib/memcpymove.h	2021-07-25 16:45:35.488801858 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/*
+Copyright (c) 2013, Raspberry Pi Foundation
+Copyright (c) 2013, RISC OS Open Ltd
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above copyright
+      notice, this list of conditions and the following disclaimer in the
+      documentation and/or other materials provided with the distribution.
+    * Neither the name of the copyright holder nor the
+      names of its contributors may be used to endorse or promote products
+      derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+.macro unaligned_words  backwards, align, use_pld, words, r0, r1, r2, r3, r4, r5, r6, r7, r8
+ .if words == 1
+  .if backwards
+        mov     r1, r0, lsl #32-align*8
+        ldr     r0, [S, #-4]!
+        orr     r1, r1, r0, lsr #align*8
+        str     r1, [D, #-4]!
+  .else
+        mov     r0, r1, lsr #align*8
+        ldr     r1, [S, #4]!
+        orr     r0, r0, r1, lsl #32-align*8
+        str     r0, [D], #4
+  .endif
+ .elseif words == 2
+  .if backwards
+        ldr     r1, [S, #-4]!
+        mov     r2, r0, lsl #32-align*8
+        ldr     r0, [S, #-4]!
+        orr     r2, r2, r1, lsr #align*8
+        mov     r1, r1, lsl #32-align*8
+        orr     r1, r1, r0, lsr #align*8
+        stmdb   D!, {r1, r2}
+  .else
+        ldr     r1, [S, #4]!
+        mov     r0, r2, lsr #align*8
+        ldr     r2, [S, #4]!
+        orr     r0, r0, r1, lsl #32-align*8
+        mov     r1, r1, lsr #align*8
+        orr     r1, r1, r2, lsl #32-align*8
+        stmia   D!, {r0, r1}
+  .endif
+ .elseif words == 4
+  .if backwards
+        ldmdb   S!, {r2, r3}
+        mov     r4, r0, lsl #32-align*8
+        ldmdb   S!, {r0, r1}
+        orr     r4, r4, r3, lsr #align*8
+        mov     r3, r3, lsl #32-align*8
+        orr     r3, r3, r2, lsr #align*8
+        mov     r2, r2, lsl #32-align*8
+        orr     r2, r2, r1, lsr #align*8
+        mov     r1, r1, lsl #32-align*8
+        orr     r1, r1, r0, lsr #align*8
+        stmdb   D!, {r1, r2, r3, r4}
+  .else
+        ldmib   S!, {r1, r2}
+        mov     r0, r4, lsr #align*8
+        ldmib   S!, {r3, r4}
+        orr     r0, r0, r1, lsl #32-align*8
+        mov     r1, r1, lsr #align*8
+        orr     r1, r1, r2, lsl #32-align*8
+        mov     r2, r2, lsr #align*8
+        orr     r2, r2, r3, lsl #32-align*8
+        mov     r3, r3, lsr #align*8
+        orr     r3, r3, r4, lsl #32-align*8
+        stmia   D!, {r0, r1, r2, r3}
+  .endif
+ .elseif words == 8
+  .if backwards
+        ldmdb   S!, {r4, r5, r6, r7}
+        mov     r8, r0, lsl #32-align*8
+        ldmdb   S!, {r0, r1, r2, r3}
+   .if use_pld
+        pld     [S, OFF]
+   .endif
+        orr     r8, r8, r7, lsr #align*8
+        mov     r7, r7, lsl #32-align*8
+        orr     r7, r7, r6, lsr #align*8
+        mov     r6, r6, lsl #32-align*8
+        orr     r6, r6, r5, lsr #align*8
+        mov     r5, r5, lsl #32-align*8
+        orr     r5, r5, r4, lsr #align*8
+        mov     r4, r4, lsl #32-align*8
+        orr     r4, r4, r3, lsr #align*8
+        mov     r3, r3, lsl #32-align*8
+        orr     r3, r3, r2, lsr #align*8
+        mov     r2, r2, lsl #32-align*8
+        orr     r2, r2, r1, lsr #align*8
+        mov     r1, r1, lsl #32-align*8
+        orr     r1, r1, r0, lsr #align*8
+        stmdb   D!, {r5, r6, r7, r8}
+        stmdb   D!, {r1, r2, r3, r4}
+  .else
+        ldmib   S!, {r1, r2, r3, r4}
+        mov     r0, r8, lsr #align*8
+        ldmib   S!, {r5, r6, r7, r8}
+   .if use_pld
+        pld     [S, OFF]
+   .endif
+        orr     r0, r0, r1, lsl #32-align*8
+        mov     r1, r1, lsr #align*8
+        orr     r1, r1, r2, lsl #32-align*8
+        mov     r2, r2, lsr #align*8
+        orr     r2, r2, r3, lsl #32-align*8
+        mov     r3, r3, lsr #align*8
+        orr     r3, r3, r4, lsl #32-align*8
+        mov     r4, r4, lsr #align*8
+        orr     r4, r4, r5, lsl #32-align*8
+        mov     r5, r5, lsr #align*8
+        orr     r5, r5, r6, lsl #32-align*8
+        mov     r6, r6, lsr #align*8
+        orr     r6, r6, r7, lsl #32-align*8
+        mov     r7, r7, lsr #align*8
+        orr     r7, r7, r8, lsl #32-align*8
+        stmia   D!, {r0, r1, r2, r3}
+        stmia   D!, {r4, r5, r6, r7}
+  .endif
+ .endif
+.endm
+
+.macro memcpy_leading_15bytes  backwards, align
+        movs    DAT1, DAT2, lsl #31
+        sub     N, N, DAT2
+ .if backwards
+        ldrmib  DAT0, [S, #-1]!
+        ldrcsh  DAT1, [S, #-2]!
+        strmib  DAT0, [D, #-1]!
+        strcsh  DAT1, [D, #-2]!
+ .else
+        ldrmib  DAT0, [S], #1
+        ldrcsh  DAT1, [S], #2
+        strmib  DAT0, [D], #1
+        strcsh  DAT1, [D], #2
+ .endif
+        movs    DAT1, DAT2, lsl #29
+ .if backwards
+        ldrmi   DAT0, [S, #-4]!
+  .if align == 0
+        ldmcsdb S!, {DAT1, DAT2}
+  .else
+        ldrcs   DAT2, [S, #-4]!
+        ldrcs   DAT1, [S, #-4]!
+  .endif
+        strmi   DAT0, [D, #-4]!
+        stmcsdb D!, {DAT1, DAT2}
+ .else
+        ldrmi   DAT0, [S], #4
+  .if align == 0
+        ldmcsia S!, {DAT1, DAT2}
+  .else
+        ldrcs   DAT1, [S], #4
+        ldrcs   DAT2, [S], #4
+  .endif
+        strmi   DAT0, [D], #4
+        stmcsia D!, {DAT1, DAT2}
+ .endif
+.endm
+
+.macro memcpy_trailing_15bytes  backwards, align
+        movs    N, N, lsl #29
+ .if backwards
+  .if align == 0
+        ldmcsdb S!, {DAT0, DAT1}
+  .else
+        ldrcs   DAT1, [S, #-4]!
+        ldrcs   DAT0, [S, #-4]!
+  .endif
+        ldrmi   DAT2, [S, #-4]!
+        stmcsdb D!, {DAT0, DAT1}
+        strmi   DAT2, [D, #-4]!
+ .else
+  .if align == 0
+        ldmcsia S!, {DAT0, DAT1}
+  .else
+        ldrcs   DAT0, [S], #4
+        ldrcs   DAT1, [S], #4
+  .endif
+        ldrmi   DAT2, [S], #4
+        stmcsia D!, {DAT0, DAT1}
+        strmi   DAT2, [D], #4
+ .endif
+        movs    N, N, lsl #2
+ .if backwards
+        ldrcsh  DAT0, [S, #-2]!
+        ldrmib  DAT1, [S, #-1]
+        strcsh  DAT0, [D, #-2]!
+        strmib  DAT1, [D, #-1]
+ .else
+        ldrcsh  DAT0, [S], #2
+        ldrmib  DAT1, [S]
+        strcsh  DAT0, [D], #2
+        strmib  DAT1, [D]
+ .endif
+.endm
+
+.macro memcpy_long_inner_loop  backwards, align
+ .if align != 0
+  .if backwards
+        ldr     DAT0, [S, #-align]!
+  .else
+        ldr     LAST, [S, #-align]!
+  .endif
+ .endif
+110:
+ .if align == 0
+  .if backwards
+        ldmdb   S!, {DAT0, DAT1, DAT2, DAT3, DAT4, DAT5, DAT6, LAST}
+        pld     [S, OFF]
+        stmdb   D!, {DAT4, DAT5, DAT6, LAST}
+        stmdb   D!, {DAT0, DAT1, DAT2, DAT3}
+  .else
+        ldmia   S!, {DAT0, DAT1, DAT2, DAT3, DAT4, DAT5, DAT6, LAST}
+        pld     [S, OFF]
+        stmia   D!, {DAT0, DAT1, DAT2, DAT3}
+        stmia   D!, {DAT4, DAT5, DAT6, LAST}
+  .endif
+ .else
+        unaligned_words  backwards, align, 1, 8, DAT0, DAT1, DAT2, DAT3, DAT4, DAT5, DAT6, DAT7, LAST
+ .endif
+        subs    N, N, #32
+        bhs     110b
+        /* Just before the final (prefetch_distance+1) 32-byte blocks, deal with final preloads */
+        preload_trailing  backwards, S, N, OFF
+        add     N, N, #(prefetch_distance+2)*32 - 32
+120:
+ .if align == 0
+  .if backwards
+        ldmdb   S!, {DAT0, DAT1, DAT2, DAT3, DAT4, DAT5, DAT6, LAST}
+        stmdb   D!, {DAT4, DAT5, DAT6, LAST}
+        stmdb   D!, {DAT0, DAT1, DAT2, DAT3}
+  .else
+        ldmia   S!, {DAT0, DAT1, DAT2, DAT3, DAT4, DAT5, DAT6, LAST}
+        stmia   D!, {DAT0, DAT1, DAT2, DAT3}
+        stmia   D!, {DAT4, DAT5, DAT6, LAST}
+  .endif
+ .else
+        unaligned_words  backwards, align, 0, 8, DAT0, DAT1, DAT2, DAT3, DAT4, DAT5, DAT6, DAT7, LAST
+ .endif
+        subs    N, N, #32
+        bhs     120b
+        tst     N, #16
+ .if align == 0
+  .if backwards
+        ldmnedb S!, {DAT0, DAT1, DAT2, LAST}
+        stmnedb D!, {DAT0, DAT1, DAT2, LAST}
+  .else
+        ldmneia S!, {DAT0, DAT1, DAT2, LAST}
+        stmneia D!, {DAT0, DAT1, DAT2, LAST}
+  .endif
+ .else
+        beq     130f
+        unaligned_words  backwards, align, 0, 4, DAT0, DAT1, DAT2, DAT3, LAST
+130:
+ .endif
+        /* Trailing words and bytes */
+        tst      N, #15
+        beq      199f
+ .if align != 0
+        add     S, S, #align
+ .endif
+        memcpy_trailing_15bytes  backwards, align
+199:
+        pop     {DAT3, DAT4, DAT5, DAT6, DAT7}
+        pop     {D, DAT1, DAT2, pc}
+.endm
+
+.macro memcpy_medium_inner_loop  backwards, align
+120:
+ .if backwards
+  .if align == 0
+        ldmdb   S!, {DAT0, DAT1, DAT2, LAST}
+  .else
+        ldr     LAST, [S, #-4]!
+        ldr     DAT2, [S, #-4]!
+        ldr     DAT1, [S, #-4]!
+        ldr     DAT0, [S, #-4]!
+  .endif
+        stmdb   D!, {DAT0, DAT1, DAT2, LAST}
+ .else
+  .if align == 0
+        ldmia   S!, {DAT0, DAT1, DAT2, LAST}
+  .else
+        ldr     DAT0, [S], #4
+        ldr     DAT1, [S], #4
+        ldr     DAT2, [S], #4
+        ldr     LAST, [S], #4
+  .endif
+        stmia   D!, {DAT0, DAT1, DAT2, LAST}
+ .endif
+        subs     N, N, #16
+        bhs      120b
+        /* Trailing words and bytes */
+        tst      N, #15
+        beq      199f
+        memcpy_trailing_15bytes  backwards, align
+199:
+        pop     {D, DAT1, DAT2, pc}
+.endm
+
+.macro memcpy_short_inner_loop  backwards, align
+        tst     N, #16
+ .if backwards
+  .if align == 0
+        ldmnedb S!, {DAT0, DAT1, DAT2, LAST}
+  .else
+        ldrne   LAST, [S, #-4]!
+        ldrne   DAT2, [S, #-4]!
+        ldrne   DAT1, [S, #-4]!
+        ldrne   DAT0, [S, #-4]!
+  .endif
+        stmnedb D!, {DAT0, DAT1, DAT2, LAST}
+ .else
+  .if align == 0
+        ldmneia S!, {DAT0, DAT1, DAT2, LAST}
+  .else
+        ldrne   DAT0, [S], #4
+        ldrne   DAT1, [S], #4
+        ldrne   DAT2, [S], #4
+        ldrne   LAST, [S], #4
+  .endif
+        stmneia D!, {DAT0, DAT1, DAT2, LAST}
+ .endif
+        memcpy_trailing_15bytes  backwards, align
+199:
+        pop     {D, DAT1, DAT2, pc}
+.endm
+
+.macro memcpy backwards
+        D       .req    a1
+        S       .req    a2
+        N       .req    a3
+        DAT0    .req    a4
+        DAT1    .req    v1
+        DAT2    .req    v2
+        DAT3    .req    v3
+        DAT4    .req    v4
+        DAT5    .req    v5
+        DAT6    .req    v6
+        DAT7    .req    sl
+        LAST    .req    ip
+        OFF     .req    lr
+
+        .cfi_startproc
+
+        push    {D, DAT1, DAT2, lr}
+
+        .cfi_def_cfa_offset 16
+        .cfi_rel_offset D, 0
+        .cfi_undefined  S
+        .cfi_undefined  N
+        .cfi_undefined  DAT0
+        .cfi_rel_offset DAT1, 4
+        .cfi_rel_offset DAT2, 8
+        .cfi_undefined  LAST
+        .cfi_rel_offset lr, 12
+
+ .if backwards
+        add     D, D, N
+        add     S, S, N
+ .endif
+
+        /* See if we're guaranteed to have at least one 16-byte aligned 16-byte write */
+        cmp     N, #31
+        blo     170f
+        /* To preload ahead as we go, we need at least (prefetch_distance+2) 32-byte blocks */
+        cmp     N, #(prefetch_distance+3)*32 - 1
+        blo     160f
+
+        /* Long case */
+        push    {DAT3, DAT4, DAT5, DAT6, DAT7}
+
+        .cfi_def_cfa_offset 36
+        .cfi_rel_offset D, 20
+        .cfi_rel_offset DAT1, 24
+        .cfi_rel_offset DAT2, 28
+        .cfi_rel_offset DAT3, 0
+        .cfi_rel_offset DAT4, 4
+        .cfi_rel_offset DAT5, 8
+        .cfi_rel_offset DAT6, 12
+        .cfi_rel_offset DAT7, 16
+        .cfi_rel_offset lr, 32
+
+        /* Adjust N so that the decrement instruction can also test for
+         * inner loop termination. We want it to stop when there are
+         * (prefetch_distance+1) complete blocks to go. */
+        sub     N, N, #(prefetch_distance+2)*32
+        preload_leading_step1  backwards, DAT0, S
+ .if backwards
+        /* Bug in GAS: it accepts, but mis-assembles the instruction
+         * ands    DAT2, D, #60, 2
+         * which sets DAT2 to the number of leading bytes until destination is aligned and also clears C (sets borrow)
+         */
+        .word   0xE210513C
+        beq     154f
+ .else
+        ands    DAT2, D, #15
+        beq     154f
+        rsb     DAT2, DAT2, #16 /* number of leading bytes until destination aligned */
+ .endif
+        preload_leading_step2  backwards, DAT0, S, DAT2, OFF
+        memcpy_leading_15bytes backwards, 1
+154:    /* Destination now 16-byte aligned; we have at least one prefetch as well as at least one 16-byte output block */
+        /* Prefetch offset is best selected such that it lies in the first 8 of each 32 bytes - but it's just as easy to aim for the first one */
+ .if backwards
+        rsb     OFF, S, #3
+        and     OFF, OFF, #28
+        sub     OFF, OFF, #32*(prefetch_distance+1)
+ .else
+        and     OFF, S, #28
+        rsb     OFF, OFF, #32*prefetch_distance
+ .endif
+        movs    DAT0, S, lsl #31
+        bhi     157f
+        bcs     156f
+        bmi     155f
+        memcpy_long_inner_loop  backwards, 0
+155:    memcpy_long_inner_loop  backwards, 1
+156:    memcpy_long_inner_loop  backwards, 2
+157:    memcpy_long_inner_loop  backwards, 3
+
+        .cfi_def_cfa_offset 16
+        .cfi_rel_offset D, 0
+        .cfi_rel_offset DAT1, 4
+        .cfi_rel_offset DAT2, 8
+        .cfi_same_value DAT3
+        .cfi_same_value DAT4
+        .cfi_same_value DAT5
+        .cfi_same_value DAT6
+        .cfi_same_value DAT7
+        .cfi_rel_offset lr, 12
+
+160:    /* Medium case */
+        preload_all  backwards, 0, 0, S, N, DAT2, OFF
+        sub     N, N, #16     /* simplifies inner loop termination */
+ .if backwards
+        ands    DAT2, D, #15
+        beq     164f
+ .else
+        ands    DAT2, D, #15
+        beq     164f
+        rsb     DAT2, DAT2, #16
+ .endif
+        memcpy_leading_15bytes backwards, align
+164:    /* Destination now 16-byte aligned; we have at least one 16-byte output block */
+        tst     S, #3
+        bne     140f
+        memcpy_medium_inner_loop  backwards, 0
+140:    memcpy_medium_inner_loop  backwards, 1
+
+170:    /* Short case, less than 31 bytes, so no guarantee of at least one 16-byte block */
+        teq     N, #0
+        beq     199f
+        preload_all  backwards, 1, 0, S, N, DAT2, LAST
+        tst     D, #3
+        beq     174f
+172:    subs    N, N, #1
+        blo     199f
+ .if backwards
+        ldrb    DAT0, [S, #-1]!
+        strb    DAT0, [D, #-1]!
+ .else
+        ldrb    DAT0, [S], #1
+        strb    DAT0, [D], #1
+ .endif
+        tst     D, #3
+        bne     172b
+174:    /* Destination now 4-byte aligned; we have 0 or more output bytes to go */
+        tst     S, #3
+        bne     140f
+        memcpy_short_inner_loop  backwards, 0
+140:    memcpy_short_inner_loop  backwards, 1
+
+        .cfi_endproc
+
+        .unreq  D
+        .unreq  S
+        .unreq  N
+        .unreq  DAT0
+        .unreq  DAT1
+        .unreq  DAT2
+        .unreq  DAT3
+        .unreq  DAT4
+        .unreq  DAT5
+        .unreq  DAT6
+        .unreq  DAT7
+        .unreq  LAST
+        .unreq  OFF
+.endm
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/lib/memcpy_rpi.S linux-5.10.52-v7l+/arch/arm/lib/memcpy_rpi.S
--- linux-5.10.52-orig/arch/arm/lib/memcpy_rpi.S	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/lib/memcpy_rpi.S	2021-07-25 16:45:35.488801858 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/*
+Copyright (c) 2013, Raspberry Pi Foundation
+Copyright (c) 2013, RISC OS Open Ltd
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above copyright
+      notice, this list of conditions and the following disclaimer in the
+      documentation and/or other materials provided with the distribution.
+    * Neither the name of the copyright holder nor the
+      names of its contributors may be used to endorse or promote products
+      derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include <linux/linkage.h>
+#include "arm-mem.h"
+#include "memcpymove.h"
+
+/* Prevent the stack from becoming executable */
+#if defined(__linux__) && defined(__ELF__)
+.section .note.GNU-stack,"",%progbits
+#endif
+
+    .text
+    .arch armv6
+    .object_arch armv4
+    .arm
+    .altmacro
+    .p2align 2
+
+/*
+ * void *memcpy(void * restrict s1, const void * restrict s2, size_t n);
+ * On entry:
+ * a1 = pointer to destination
+ * a2 = pointer to source
+ * a3 = number of bytes to copy
+ * On exit:
+ * a1 preserved
+ */
+
+.set prefetch_distance, 3
+
+ENTRY(mmiocpy)
+ENTRY(memcpy)
+        memcpy  0
+ENDPROC(memcpy)
+ENDPROC(mmiocpy)
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/lib/memmove_rpi.S linux-5.10.52-v7l+/arch/arm/lib/memmove_rpi.S
--- linux-5.10.52-orig/arch/arm/lib/memmove_rpi.S	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/lib/memmove_rpi.S	2021-07-25 16:45:35.488801858 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/*
+Copyright (c) 2013, Raspberry Pi Foundation
+Copyright (c) 2013, RISC OS Open Ltd
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above copyright
+      notice, this list of conditions and the following disclaimer in the
+      documentation and/or other materials provided with the distribution.
+    * Neither the name of the copyright holder nor the
+      names of its contributors may be used to endorse or promote products
+      derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include <linux/linkage.h>
+#include "arm-mem.h"
+#include "memcpymove.h"
+
+/* Prevent the stack from becoming executable */
+#if defined(__linux__) && defined(__ELF__)
+.section .note.GNU-stack,"",%progbits
+#endif
+
+    .text
+    .arch armv6
+    .object_arch armv4
+    .arm
+    .altmacro
+    .p2align 2
+
+/*
+ * void *memmove(void *s1, const void *s2, size_t n);
+ * On entry:
+ * a1 = pointer to destination
+ * a2 = pointer to source
+ * a3 = number of bytes to copy
+ * On exit:
+ * a1 preserved
+ */
+
+.set prefetch_distance, 3
+
+ENTRY(memmove)
+        cmp     a2, a1
+        bpl     memcpy  /* pl works even over -1 - 0 and 0x7fffffff - 0x80000000 boundaries */
+        memcpy  1
+ENDPROC(memmove)
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/lib/memset_rpi.S linux-5.10.52-v7l+/arch/arm/lib/memset_rpi.S
--- linux-5.10.52-orig/arch/arm/lib/memset_rpi.S	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm/lib/memset_rpi.S	2021-07-25 16:45:35.488801858 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/*
+Copyright (c) 2013, Raspberry Pi Foundation
+Copyright (c) 2013, RISC OS Open Ltd
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above copyright
+      notice, this list of conditions and the following disclaimer in the
+      documentation and/or other materials provided with the distribution.
+    * Neither the name of the copyright holder nor the
+      names of its contributors may be used to endorse or promote products
+      derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include <linux/linkage.h>
+#include "arm-mem.h"
+
+/* Prevent the stack from becoming executable */
+#if defined(__linux__) && defined(__ELF__)
+.section .note.GNU-stack,"",%progbits
+#endif
+
+    .text
+    .arch armv6
+    .object_arch armv4
+    .arm
+    .altmacro
+    .p2align 2
+
+/*
+ *  void *memset(void *s, int c, size_t n);
+ *  On entry:
+ *  a1 = pointer to buffer to fill
+ *  a2 = byte pattern to fill with (caller-narrowed)
+ *  a3 = number of bytes to fill
+ *  On exit:
+ *  a1 preserved
+ */
+ENTRY(mmioset)
+ENTRY(memset)
+ENTRY(__memset32)
+ENTRY(__memset64)
+
+        S       .req    a1
+        DAT0    .req    a2
+        N       .req    a3
+        DAT1    .req    a4
+        DAT2    .req    ip
+        DAT3    .req    lr
+
+        orr     DAT0, DAT0, DAT0, lsl #8
+        push    {S, lr}
+        orr     DAT0, DAT0, DAT0, lsl #16
+        mov     DAT1, DAT0
+
+        /* See if we're guaranteed to have at least one 16-byte aligned 16-byte write */
+        cmp     N, #31
+        blo     170f
+
+161:    sub     N, N, #16     /* simplifies inner loop termination */
+        /* Leading words and bytes */
+        tst     S, #15
+        beq     164f
+        rsb     DAT3, S, #0   /* bits 0-3 = number of leading bytes until aligned */
+        movs    DAT2, DAT3, lsl #31
+        submi   N, N, #1
+        strmib  DAT0, [S], #1
+        subcs   N, N, #2
+        strcsh  DAT0, [S], #2
+        movs    DAT2, DAT3, lsl #29
+        submi   N, N, #4
+        strmi   DAT0, [S], #4
+        subcs   N, N, #8
+        stmcsia S!, {DAT0, DAT1}
+164:    /* Delayed set up of DAT2 and DAT3 so we could use them as scratch registers above */
+        mov     DAT2, DAT0
+        mov     DAT3, DAT0
+        /* Now the inner loop of 16-byte stores */
+165:    stmia   S!, {DAT0, DAT1, DAT2, DAT3}
+        subs    N, N, #16
+        bhs     165b
+166:    /* Trailing words and bytes */
+        movs    N, N, lsl #29
+        stmcsia S!, {DAT0, DAT1}
+        strmi   DAT0, [S], #4
+        movs    N, N, lsl #2
+        strcsh  DAT0, [S], #2
+        strmib  DAT0, [S]
+199:    pop     {S, pc}
+
+170:    /* Short case */
+        mov     DAT2, DAT0
+        mov     DAT3, DAT0
+        tst     S, #3
+        beq     174f
+172:    subs    N, N, #1
+        blo     199b
+        strb    DAT0, [S], #1
+        tst     S, #3
+        bne     172b
+174:    tst     N, #16
+        stmneia S!, {DAT0, DAT1, DAT2, DAT3}
+        b       166b
+
+        .unreq  S
+        .unreq  DAT0
+        .unreq  N
+        .unreq  DAT1
+        .unreq  DAT2
+        .unreq  DAT3
+ENDPROC(__memset64)
+ENDPROC(__memset32)
+ENDPROC(memset)
+ENDPROC(mmioset)
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/lib/uaccess_with_memcpy.c linux-5.10.52-v7l+/arch/arm/lib/uaccess_with_memcpy.c
--- linux-5.10.52-orig/arch/arm/lib/uaccess_with_memcpy.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/arch/arm/lib/uaccess_with_memcpy.c	2021-07-25 16:45:35.488801858 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:22 @
 #include <asm/current.h>
 #include <asm/page.h>
 
+#ifndef COPY_FROM_USER_THRESHOLD
+#define COPY_FROM_USER_THRESHOLD 64
+#endif
+
+#ifndef COPY_TO_USER_THRESHOLD
+#define COPY_TO_USER_THRESHOLD 64
+#endif
+
 static int
 pin_page_for_write(const void __user *_addr, pte_t **ptep, spinlock_t **ptlp)
 {
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:54 @
 		return 0;
 
 	pmd = pmd_offset(pud, addr);
-	if (unlikely(pmd_none(*pmd)))
+	if (unlikely(pmd_none(*pmd) || pmd_bad(*pmd)))
 		return 0;
 
 	/*
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:97 @
 	return 1;
 }
 
-static unsigned long noinline
+static int
+pin_page_for_read(const void __user *_addr, pte_t **ptep, spinlock_t **ptlp)
+{
+	unsigned long addr = (unsigned long)_addr;
+	pgd_t *pgd;
+	p4d_t *p4d;
+	pmd_t *pmd;
+	pte_t *pte;
+	pud_t *pud;
+	spinlock_t *ptl;
+
+	pgd = pgd_offset(current->mm, addr);
+	if (unlikely(pgd_none(*pgd) || pgd_bad(*pgd)))
+		return 0;
+
+	p4d = p4d_offset(pgd, addr);
+	if (unlikely(p4d_none(*p4d) || p4d_bad(*p4d)))
+		return 0;
+
+	pud = pud_offset(p4d, addr);
+	if (unlikely(pud_none(*pud) || pud_bad(*pud)))
+		return 0;
+
+	pmd = pmd_offset(pud, addr);
+	if (unlikely(pmd_none(*pmd) || pmd_bad(*pmd)))
+		return 0;
+
+	pte = pte_offset_map_lock(current->mm, pmd, addr, &ptl);
+	if (unlikely(!pte_present(*pte) || !pte_young(*pte))) {
+		pte_unmap_unlock(pte, ptl);
+		return 0;
+	}
+
+	*ptep = pte;
+	*ptlp = ptl;
+
+	return 1;
+}
+
+unsigned long noinline
 __copy_to_user_memcpy(void __user *to, const void *from, unsigned long n)
 {
 	unsigned long ua_flags;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:189 @
 	return n;
 }
 
+unsigned long noinline
+__copy_from_user_memcpy(void *to, const void __user *from, unsigned long n)
+{
+	unsigned long ua_flags;
+	int atomic;
+
+	if (unlikely(uaccess_kernel())) {
+		memcpy(to, (const void *)from, n);
+		return 0;
+	}
+
+	/* the mmap semaphore is taken only if not in an atomic context */
+	atomic = in_atomic();
+
+	if (!atomic)
+		mmap_read_lock(current->mm);
+	while (n) {
+		pte_t *pte;
+		spinlock_t *ptl;
+		int tocopy;
+
+		while (!pin_page_for_read(from, &pte, &ptl)) {
+			char temp;
+			if (!atomic)
+				mmap_read_unlock(current->mm);
+			if (__get_user(temp, (char __user *)from))
+				goto out;
+			if (!atomic)
+				mmap_read_lock(current->mm);
+		}
+
+		tocopy = (~(unsigned long)from & ~PAGE_MASK) + 1;
+		if (tocopy > n)
+			tocopy = n;
+
+		ua_flags = uaccess_save_and_enable();
+		memcpy(to, (const void *)from, tocopy);
+		uaccess_restore(ua_flags);
+		to += tocopy;
+		from += tocopy;
+		n -= tocopy;
+
+		pte_unmap_unlock(pte, ptl);
+	}
+	if (!atomic)
+		mmap_read_unlock(current->mm);
+
+out:
+	return n;
+}
+
 unsigned long
 arm_copy_to_user(void __user *to, const void *from, unsigned long n)
 {
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:250 @
 	 * With frame pointer disabled, tail call optimization kicks in
 	 * as well making this test almost invisible.
 	 */
-	if (n < 64) {
+	if (n < COPY_TO_USER_THRESHOLD) {
 		unsigned long ua_flags = uaccess_save_and_enable();
 		n = __copy_to_user_std(to, from, n);
 		uaccess_restore(ua_flags);
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:260 @
 	}
 	return n;
 }
+
+unsigned long __must_check
+arm_copy_from_user(void *to, const void __user *from, unsigned long n)
+{
+#ifdef CONFIG_BCM2835_FAST_MEMCPY
+	/*
+	 * This test is stubbed out of the main function above to keep
+	 * the overhead for small copies low by avoiding a large
+	 * register dump on the stack just to reload them right away.
+	 * With frame pointer disabled, tail call optimization kicks in
+	 * as well making this test almost invisible.
+	 */
+	if (n < COPY_TO_USER_THRESHOLD) {
+		unsigned long ua_flags = uaccess_save_and_enable();
+		n = __copy_from_user_std(to, from, n);
+		uaccess_restore(ua_flags);
+	} else {
+		n = __copy_from_user_memcpy(to, from, n);
+	}
+#else
+	unsigned long ua_flags = uaccess_save_and_enable();
+	n = __copy_from_user_std(to, from, n);
+	uaccess_restore(ua_flags);
+#endif
+	return n;
+}
 	
 static unsigned long noinline
 __clear_user_memset(void __user *addr, unsigned long n)
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/mach-bcm/board_bcm2835.c linux-5.10.52-v7l+/arch/arm/mach-bcm/board_bcm2835.c
--- linux-5.10.52-orig/arch/arm/mach-bcm/board_bcm2835.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/arch/arm/mach-bcm/board_bcm2835.c	2021-07-25 16:45:35.498801690 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:8 @
 
 #include <linux/init.h>
 #include <linux/irqchip.h>
+#include <linux/mm.h>
 #include <linux/of_address.h>
+#include <linux/of_fdt.h>
+#include <asm/system_info.h>
 
 #include <asm/mach/arch.h>
 #include <asm/mach/map.h>
+#include <asm/memory.h>
+#include <asm/pgtable.h>
 
 #include "platsmp.h"
 
+#define BCM2835_USB_VIRT_BASE   (VMALLOC_START)
+#define BCM2835_USB_VIRT_MPHI   (VMALLOC_START + 0x10000)
+
+static void __init bcm2835_init(void)
+{
+	struct device_node *np = of_find_node_by_path("/system");
+	u32 val;
+	u64 val64;
+
+	if (!of_property_read_u32(np, "linux,revision", &val))
+		system_rev = val;
+	if (!of_property_read_u64(np, "linux,serial", &val64))
+		system_serial_low = val64;
+}
+
+/*
+ * We need to map registers that are going to be accessed by the FIQ
+ * very early, before any kernel threads are spawned. Because if done
+ * later, the mapping tables are not updated instantly but lazily upon
+ * first access through a data abort handler. While that is fine
+ * when executing regular kernel code, if the first access in a specific
+ * thread happens while running FIQ code this will result in a panic.
+ *
+ * For more background see the following old mailing list thread:
+ * https://www.spinics.net/lists/arm-kernel/msg325250.html
+ */
+static int __init bcm2835_map_usb(unsigned long node, const char *uname,
+					int depth, void *data)
+{
+	struct map_desc map[2];
+	const __be32 *reg;
+	int len;
+	unsigned long p2b_offset = *((unsigned long *) data);
+
+	if (!of_flat_dt_is_compatible(node, "brcm,bcm2708-usb"))
+		return 0;
+	reg = of_get_flat_dt_prop(node, "reg", &len);
+	if (!reg || len != (sizeof(unsigned long) * 4))
+		return 0;
+
+	/* Use information about the physical addresses of the
+	 * registers from the device tree, but use legacy
+	 * iotable_init() static mapping function to map them,
+	 * as ioremap() is not functional at this stage in boot.
+	 */
+	map[0].virtual = (unsigned long) BCM2835_USB_VIRT_BASE;
+	map[0].pfn = __phys_to_pfn(be32_to_cpu(reg[0]) - p2b_offset);
+	map[0].length = be32_to_cpu(reg[1]);
+	map[0].type = MT_DEVICE;
+	map[1].virtual = (unsigned long) BCM2835_USB_VIRT_MPHI;
+	map[1].pfn = __phys_to_pfn(be32_to_cpu(reg[2]) - p2b_offset);
+	map[1].length = be32_to_cpu(reg[3]);
+	map[1].type = MT_DEVICE;
+		iotable_init(map, 2);
+
+	return 1;
+}
+
+static void __init bcm2835_map_io(void)
+{
+	const __be32 *ranges, *address_cells;
+	unsigned long root, addr_cells;
+	int soc, len;
+	unsigned long p2b_offset;
+
+	debug_ll_io_init();
+
+	root = of_get_flat_dt_root();
+	/* Find out how to map bus to physical address first from soc/ranges */
+	soc = of_get_flat_dt_subnode_by_name(root, "soc");
+	if (soc < 0)
+		return;
+	address_cells = of_get_flat_dt_prop(root, "#address-cells", &len);
+	if (!address_cells || len < (sizeof(unsigned long)))
+		return;
+	addr_cells = be32_to_cpu(address_cells[0]);
+	ranges = of_get_flat_dt_prop(soc, "ranges", &len);
+	if (!ranges || len < (sizeof(unsigned long) * (2 + addr_cells)))
+		return;
+	p2b_offset = be32_to_cpu(ranges[0]) - be32_to_cpu(ranges[addr_cells]);
+
+	/* Now search for bcm2708-usb node in device tree */
+	of_scan_flat_dt(bcm2835_map_usb, &p2b_offset);
+}
+
 static const char * const bcm2835_compat[] = {
 #ifdef CONFIG_ARCH_MULTI_V6
 	"brcm,bcm2835",
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:117 @
 };
 
 DT_MACHINE_START(BCM2835, "BCM2835")
+	.map_io = bcm2835_map_io,
+	.init_machine = bcm2835_init,
 	.dt_compat = bcm2835_compat,
 	.smp = smp_ops(bcm2836_smp_ops),
 MACHINE_END
+
+static const char * const bcm2711_compat[] = {
+#ifdef CONFIG_ARCH_MULTI_V7
+	"brcm,bcm2711",
+#endif
+	NULL
+};
+
+DT_MACHINE_START(BCM2711, "BCM2711")
+#if defined(CONFIG_ZONE_DMA) && defined(CONFIG_ARM_LPAE)
+	.dma_zone_size	= SZ_1G,
+#endif
+	.map_io = bcm2835_map_io,
+	.init_machine = bcm2835_init,
+	.dt_compat = bcm2711_compat,
+	.smp = smp_ops(bcm2836_smp_ops),
+MACHINE_END
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/mach-bcm/Kconfig linux-5.10.52-v7l+/arch/arm/mach-bcm/Kconfig
--- linux-5.10.52-orig/arch/arm/mach-bcm/Kconfig	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/arch/arm/mach-bcm/Kconfig	2021-07-25 16:45:35.498801690 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:164 @
 	select ARM_TIMER_SP804
 	select HAVE_ARM_ARCH_TIMER if ARCH_MULTI_V7
 	select BCM2835_TIMER
+	select BRCMSTB_L2_IRQ
+	select FIQ
 	select PINCTRL
 	select PINCTRL_BCM2835
 	select MFD_CORE
+	select MFD_SYSCON if ARCH_MULTI_V7
 	help
 	  This enables support for the Broadcom BCM2711 and BCM283x SoCs.
 	  This SoC is used in the Raspberry Pi and Roku 2 devices.
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:188 @
 	  The base chip is BCM53573 and there are some packaging modifications
 	  like BCM47189 and BCM47452.
 
+config BCM2835_FAST_MEMCPY
+	bool "Enable optimized __copy_to_user and __copy_from_user"
+	depends on ARCH_BCM2835 && ARCH_MULTI_V6
+	default y
+	help
+	  Optimized versions of __copy_to_user and __copy_from_user for Pi1.
+
 config ARCH_BCM_63XX
 	bool "Broadcom BCM63xx DSL SoC"
 	depends on ARCH_MULTI_V7
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/mm/cache-v6.S linux-5.10.52-v7l+/arch/arm/mm/cache-v6.S
--- linux-5.10.52-orig/arch/arm/mm/cache-v6.S	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/arch/arm/mm/cache-v6.S	2021-07-25 16:45:35.848795822 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:201 @
  *	- start   - virtual start address of region
  *	- end     - virtual end address of region
  */
-v6_dma_inv_range:
+ENTRY(v6_dma_inv_range)
 #ifdef CONFIG_DMA_CACHE_RWFO
 	ldrb	r2, [r0]			@ read for ownership
 	strb	r2, [r0]			@ write for ownership
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:246 @
  *	- start   - virtual start address of region
  *	- end     - virtual end address of region
  */
-v6_dma_clean_range:
+ENTRY(v6_dma_clean_range)
 	bic	r0, r0, #D_CACHE_LINE_SIZE - 1
 1:
 #ifdef CONFIG_DMA_CACHE_RWFO
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/mm/cache-v7.S linux-5.10.52-v7l+/arch/arm/mm/cache-v7.S
--- linux-5.10.52-orig/arch/arm/mm/cache-v7.S	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/arch/arm/mm/cache-v7.S	2021-07-25 16:45:35.848795822 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:366 @
  *	- start   - virtual start address of region
  *	- end     - virtual end address of region
  */
-v7_dma_inv_range:
+ENTRY(b15_dma_inv_range)
+ENTRY(v7_dma_inv_range)
 	dcache_line_size r2, r3
 	sub	r3, r2, #1
 	tst	r0, r3
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:397 @
  *	- start   - virtual start address of region
  *	- end     - virtual end address of region
  */
-v7_dma_clean_range:
+ENTRY(b15_dma_clean_range)
+ENTRY(v7_dma_clean_range)
 	dcache_line_size r2, r3
 	sub	r3, r2, #1
 	bic	r0, r0, r3
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/mm/proc-macros.S linux-5.10.52-v7l+/arch/arm/mm/proc-macros.S
--- linux-5.10.52-orig/arch/arm/mm/proc-macros.S	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/arch/arm/mm/proc-macros.S	2021-07-25 16:45:35.868795487 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:337 @
 	.long	\name\()_flush_kern_dcache_area
 	.long	\name\()_dma_map_area
 	.long	\name\()_dma_unmap_area
+	.long	\name\()_dma_inv_range
+	.long	\name\()_dma_clean_range
 	.long	\name\()_dma_flush_range
 	.size	\name\()_cache_fns, . - \name\()_cache_fns
 .endm
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/mm/proc-syms.c linux-5.10.52-v7l+/arch/arm/mm/proc-syms.c
--- linux-5.10.52-orig/arch/arm/mm/proc-syms.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/arch/arm/mm/proc-syms.c	2021-07-25 16:45:35.868795487 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:30 @
 EXPORT_SYMBOL(__cpuc_flush_user_range);
 EXPORT_SYMBOL(__cpuc_coherent_kern_range);
 EXPORT_SYMBOL(__cpuc_flush_dcache_area);
+EXPORT_SYMBOL(dmac_inv_range);
+EXPORT_SYMBOL(dmac_clean_range);
+EXPORT_SYMBOL(dmac_flush_range);
 #else
 EXPORT_SYMBOL(cpu_cache);
 #endif
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/mm/proc-v6.S linux-5.10.52-v7l+/arch/arm/mm/proc-v6.S
--- linux-5.10.52-orig/arch/arm/mm/proc-v6.S	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/arch/arm/mm/proc-v6.S	2021-07-25 16:45:35.868795487 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:73 @
  *
  *	IRQs are already disabled.
  */
+
+/* See jira SW-5991 for details of this workaround */
 ENTRY(cpu_v6_do_idle)
-	mov	r1, #0
-	mcr	p15, 0, r1, c7, c10, 4		@ DWB - WFI may enter a low-power mode
-	mcr	p15, 0, r1, c7, c0, 4		@ wait for interrupt
+	.align 5
+	mov     r1, #2
+1:	subs	r1, #1
+	nop
+	mcreq	p15, 0, r1, c7, c10, 4		@ DWB - WFI may enter a low-power mode
+	mcreq	p15, 0, r1, c7, c0, 4		@ wait for interrupt
+	nop
+	nop
+	nop
+	bne 1b
 	ret	lr
 
 ENTRY(cpu_v6_dcache_clean_area)
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/mm/proc-v7.S linux-5.10.52-v7l+/arch/arm/mm/proc-v7.S
--- linux-5.10.52-orig/arch/arm/mm/proc-v7.S	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/arch/arm/mm/proc-v7.S	2021-07-25 16:45:35.868795487 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:290 @
 	mov	r10, #0
 1:	adr	r0, __v7_setup_stack_ptr
 	ldr	r12, [r0]
+	tst	r12, #0x1f
+	addeq	r12, r12, #4
 	add	r12, r12, r0			@ the local stack
 	stmia	r12, {r1-r6, lr}		@ v7_invalidate_l1 touches r0-r6
 	bl      v7_invalidate_l1
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:479 @
 	adr	r0, __v7_setup_stack_ptr
 	ldr	r12, [r0]
 	add	r12, r12, r0			@ the local stack
+	tst	r12, #0x1f
+	addeq	r12, r12, #4
 	stmia	r12, {r1-r6, lr}		@ v7_invalidate_l1 touches r0-r6
 	bl      v7_invalidate_l1
 	ldmia	r12, {r1-r6, lr}
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:564 @
 	.bss
 	.align	2
 __v7_setup_stack:
-	.space	4 * 7				@ 7 registers
+	.space	4 * 8				@ 7 registers + 1 spare
 
 	__INITDATA
 
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm/vfp/vfpmodule.c linux-5.10.52-v7l+/arch/arm/vfp/vfpmodule.c
--- linux-5.10.52-orig/arch/arm/vfp/vfpmodule.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/arch/arm/vfp/vfpmodule.c	2021-07-25 16:45:35.898794984 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:179 @
 		 * case the thread migrates to a different CPU. The
 		 * restoring is done lazily.
 		 */
-		if ((fpexc & FPEXC_EN) && vfp_current_hw_state[cpu])
+		if ((fpexc & FPEXC_EN) && vfp_current_hw_state[cpu]) {
+			/* vfp_save_state oopses on VFP11 if EX bit set */
+			fmxr(FPEXC, fpexc & ~FPEXC_EX);
 			vfp_save_state(vfp_current_hw_state[cpu], fpexc);
+		}
 #endif
 
 		/*
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:460 @
 	/* if vfp is on, then save state for resumption */
 	if (fpexc & FPEXC_EN) {
 		pr_debug("%s: saving vfp state\n", __func__);
+		/* vfp_save_state oopses on VFP11 if EX bit set */
+		fmxr(FPEXC, fpexc & ~FPEXC_EX);
 		vfp_save_state(&ti->vfpstate, fpexc);
 
 		/* disable, just in case */
 		fmxr(FPEXC, fmrx(FPEXC) & ~FPEXC_EN);
 	} else if (vfp_current_hw_state[ti->cpu]) {
 #ifndef CONFIG_SMP
-		fmxr(FPEXC, fpexc | FPEXC_EN);
+		/* vfp_save_state oopses on VFP11 if EX bit set */
+		fmxr(FPEXC, (fpexc & ~FPEXC_EX) | FPEXC_EN);
 		vfp_save_state(vfp_current_hw_state[ti->cpu], fpexc);
 		fmxr(FPEXC, fpexc);
 #endif
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:532 @
 		/*
 		 * Save the last VFP state on this CPU.
 		 */
-		fmxr(FPEXC, fpexc | FPEXC_EN);
+		/* vfp_save_state oopses on VFP11 if EX bit set */
+		fmxr(FPEXC, (fpexc & ~FPEXC_EX) | FPEXC_EN);
 		vfp_save_state(&thread->vfpstate, fpexc | FPEXC_EN);
 		fmxr(FPEXC, fpexc);
 	}
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:599 @
 	struct thread_info *thread = current_thread_info();
 	struct vfp_hard_struct *hwstate = &thread->vfpstate.hard;
 	unsigned long fpexc;
+	u32 fpsid = fmrx(FPSID);
 
 	/* Disable VFP to avoid corrupting the new thread state. */
 	vfp_flush_hwstate(thread);
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:622 @
 	/* Ensure the VFP is enabled. */
 	fpexc |= FPEXC_EN;
 
-	/* Ensure FPINST2 is invalid and the exception flag is cleared. */
-	fpexc &= ~(FPEXC_EX | FPEXC_FP2V);
+	/* Mask FPXEC_EX and FPEXC_FP2V if not required by VFP arch */
+	if ((fpsid & FPSID_ARCH_MASK) != (1 << FPSID_ARCH_BIT)) {
+		/* Ensure FPINST2 is invalid and the exception flag is cleared. */
+		fpexc &= ~(FPEXC_EX | FPEXC_FP2V);
+	}
+
 	hwstate->fpexc = fpexc;
 
 	hwstate->fpinst = ufp_exc->fpinst;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:741 @
 	cpu = get_cpu();
 
 	fpexc = fmrx(FPEXC) | FPEXC_EN;
-	fmxr(FPEXC, fpexc);
+	/* vfp_save_state oopses on VFP11 if EX bit set */
+	fmxr(FPEXC, fpexc & ~FPEXC_EX);
 
 	/*
 	 * Save the userland NEON/VFP state. Under UP,
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm64/boot/dts/broadcom/bcm2710-rpi-2-b.dts linux-5.10.52-v7l+/arch/arm64/boot/dts/broadcom/bcm2710-rpi-2-b.dts
--- linux-5.10.52-orig/arch/arm64/boot/dts/broadcom/bcm2710-rpi-2-b.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm64/boot/dts/broadcom/bcm2710-rpi-2-b.dts	2021-07-25 16:45:35.938794313 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1 @
+#include "../../../../arm/boot/dts/bcm2710-rpi-2-b.dts"
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm64/boot/dts/broadcom/bcm2710-rpi-3-b.dts linux-5.10.52-v7l+/arch/arm64/boot/dts/broadcom/bcm2710-rpi-3-b.dts
--- linux-5.10.52-orig/arch/arm64/boot/dts/broadcom/bcm2710-rpi-3-b.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm64/boot/dts/broadcom/bcm2710-rpi-3-b.dts	2021-07-25 16:45:35.938794313 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1 @
+#include "../../../../arm/boot/dts/bcm2710-rpi-3-b.dts"
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm64/boot/dts/broadcom/bcm2710-rpi-3-b-plus.dts linux-5.10.52-v7l+/arch/arm64/boot/dts/broadcom/bcm2710-rpi-3-b-plus.dts
--- linux-5.10.52-orig/arch/arm64/boot/dts/broadcom/bcm2710-rpi-3-b-plus.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm64/boot/dts/broadcom/bcm2710-rpi-3-b-plus.dts	2021-07-25 16:45:35.938794313 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1 @
+#include "../../../../arm/boot/dts/bcm2710-rpi-3-b-plus.dts"
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm64/boot/dts/broadcom/bcm2710-rpi-cm3.dts linux-5.10.52-v7l+/arch/arm64/boot/dts/broadcom/bcm2710-rpi-cm3.dts
--- linux-5.10.52-orig/arch/arm64/boot/dts/broadcom/bcm2710-rpi-cm3.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm64/boot/dts/broadcom/bcm2710-rpi-cm3.dts	2021-07-25 16:45:35.938794313 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1 @
+#include "../../../../arm/boot/dts/bcm2710-rpi-cm3.dts"
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm64/boot/dts/broadcom/bcm2711-rpi-400.dts linux-5.10.52-v7l+/arch/arm64/boot/dts/broadcom/bcm2711-rpi-400.dts
--- linux-5.10.52-orig/arch/arm64/boot/dts/broadcom/bcm2711-rpi-400.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm64/boot/dts/broadcom/bcm2711-rpi-400.dts	2021-07-25 16:45:35.938794313 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1 @
+#include "../../../../arm/boot/dts/bcm2711-rpi-400.dts"
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm64/boot/dts/broadcom/bcm2711-rpi-4-b.dts linux-5.10.52-v7l+/arch/arm64/boot/dts/broadcom/bcm2711-rpi-4-b.dts
--- linux-5.10.52-orig/arch/arm64/boot/dts/broadcom/bcm2711-rpi-4-b.dts	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/arch/arm64/boot/dts/broadcom/bcm2711-rpi-4-b.dts	2021-07-25 16:45:35.938794313 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1 @
-// SPDX-License-Identifier: GPL-2.0
-#include "arm/bcm2711-rpi-4-b.dts"
+#include "../../../../arm/boot/dts/bcm2711-rpi-4-b.dts"
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm64/boot/dts/broadcom/bcm2711-rpi-cm4.dts linux-5.10.52-v7l+/arch/arm64/boot/dts/broadcom/bcm2711-rpi-cm4.dts
--- linux-5.10.52-orig/arch/arm64/boot/dts/broadcom/bcm2711-rpi-cm4.dts	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm64/boot/dts/broadcom/bcm2711-rpi-cm4.dts	2021-07-25 16:45:35.938794313 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1 @
+#include "../../../../arm/boot/dts/bcm2711-rpi-cm4.dts"
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm64/boot/dts/broadcom/Makefile linux-5.10.52-v7l+/arch/arm64/boot/dts/broadcom/Makefile
--- linux-5.10.52-orig/arch/arm64/boot/dts/broadcom/Makefile	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/arch/arm64/boot/dts/broadcom/Makefile	2021-07-25 16:45:35.938794313 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
 # SPDX-License-Identifier: GPL-2.0
-dtb-$(CONFIG_ARCH_BCM2835) += bcm2711-rpi-4-b.dtb \
-			      bcm2837-rpi-3-a-plus.dtb \
+dtb-$(CONFIG_ARCH_BCM2835) += bcm2837-rpi-3-a-plus.dtb \
 			      bcm2837-rpi-3-b.dtb \
 			      bcm2837-rpi-3-b-plus.dtb \
 			      bcm2837-rpi-cm3-io3.dtb
+dtb-$(CONFIG_ARCH_BCM2835) += bcm2710-rpi-2-b.dtb
+dtb-$(CONFIG_ARCH_BCM2835) += bcm2710-rpi-3-b.dtb
+dtb-$(CONFIG_ARCH_BCM2835) += bcm2710-rpi-3-b-plus.dtb
+dtb-$(CONFIG_ARCH_BCM2835) += bcm2711-rpi-4-b.dtb
+dtb-$(CONFIG_ARCH_BCM2835) += bcm2711-rpi-400.dtb
+dtb-$(CONFIG_ARCH_BCM2835) += bcm2710-rpi-cm3.dtb
+dtb-$(CONFIG_ARCH_BCM2835) += bcm2711-rpi-cm4.dtb
 
 subdir-y	+= northstar2
 subdir-y	+= stingray
+
+# Enable fixups to support overlays on BCM2835 platforms
+ifeq ($(CONFIG_ARCH_BCM2835),y)
+	DTC_FLAGS ?= -@
+endif
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm64/boot/dts/Makefile linux-5.10.52-v7l+/arch/arm64/boot/dts/Makefile
--- linux-5.10.52-orig/arch/arm64/boot/dts/Makefile	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/arch/arm64/boot/dts/Makefile	2021-07-25 16:45:35.898794984 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:33 @
 subdir-y += toshiba
 subdir-y += xilinx
 subdir-y += zte
+
+subdir-y += overlays
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm64/configs/bcm2711_defconfig linux-5.10.52-v7l+/arch/arm64/configs/bcm2711_defconfig
--- linux-5.10.52-orig/arch/arm64/configs/bcm2711_defconfig	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm64/configs/bcm2711_defconfig	2021-07-25 16:45:36.088791798 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+CONFIG_LOCALVERSION="-v8"
+# CONFIG_LOCALVERSION_AUTO is not set
+CONFIG_SYSVIPC=y
+CONFIG_POSIX_MQUEUE=y
+CONFIG_GENERIC_IRQ_DEBUGFS=y
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_PREEMPT=y
+CONFIG_BSD_PROCESS_ACCT=y
+CONFIG_BSD_PROCESS_ACCT_V3=y
+CONFIG_TASK_XACCT=y
+CONFIG_TASK_IO_ACCOUNTING=y
+CONFIG_IKCONFIG=m
+CONFIG_IKCONFIG_PROC=y
+CONFIG_MEMCG=y
+CONFIG_BLK_CGROUP=y
+CONFIG_CFS_BANDWIDTH=y
+CONFIG_CGROUP_PIDS=y
+CONFIG_CGROUP_FREEZER=y
+CONFIG_CPUSETS=y
+CONFIG_CGROUP_DEVICE=y
+CONFIG_CGROUP_CPUACCT=y
+CONFIG_CGROUP_PERF=y
+CONFIG_CGROUP_BPF=y
+CONFIG_NAMESPACES=y
+CONFIG_USER_NS=y
+CONFIG_SCHED_AUTOGROUP=y
+CONFIG_BLK_DEV_INITRD=y
+CONFIG_BPF_SYSCALL=y
+CONFIG_EMBEDDED=y
+# CONFIG_COMPAT_BRK is not set
+CONFIG_PROFILING=y
+CONFIG_ARCH_BCM2835=y
+# CONFIG_CAVIUM_ERRATUM_22375 is not set
+# CONFIG_CAVIUM_ERRATUM_23154 is not set
+# CONFIG_CAVIUM_ERRATUM_27456 is not set
+CONFIG_COMPAT=y
+CONFIG_ARMV8_DEPRECATED=y
+CONFIG_SWP_EMULATION=y
+CONFIG_CP15_BARRIER_EMULATION=y
+CONFIG_SETEND_EMULATION=y
+CONFIG_CMDLINE="console=ttyAMA0,115200 kgdboc=ttyAMA0,115200 root=/dev/mmcblk0p2 rootfstype=ext4 rootwait"
+# CONFIG_SUSPEND is not set
+CONFIG_PM=y
+CONFIG_CPU_IDLE=y
+CONFIG_ARM_CPUIDLE=y
+CONFIG_CPU_FREQ=y
+CONFIG_CPU_FREQ_STAT=y
+CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE=y
+CONFIG_CPU_FREQ_GOV_PERFORMANCE=y
+CONFIG_CPU_FREQ_GOV_USERSPACE=y
+CONFIG_CPU_FREQ_GOV_ONDEMAND=y
+CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y
+CONFIG_CPU_FREQ_GOV_SCHEDUTIL=y
+CONFIG_CPUFREQ_DT=y
+CONFIG_ARM_RASPBERRYPI_CPUFREQ=y
+CONFIG_RASPBERRYPI_FIRMWARE=y
+CONFIG_VIRTUALIZATION=y
+CONFIG_KVM=y
+CONFIG_CRYPTO_AES_ARM64_BS=m
+CONFIG_JUMP_LABEL=y
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+CONFIG_MODVERSIONS=y
+CONFIG_MODULE_SRCVERSION_ALL=y
+CONFIG_BLK_DEV_THROTTLING=y
+CONFIG_PARTITION_ADVANCED=y
+CONFIG_MAC_PARTITION=y
+CONFIG_BINFMT_MISC=m
+CONFIG_CLEANCACHE=y
+CONFIG_FRONTSWAP=y
+CONFIG_CMA=y
+CONFIG_ZSWAP=y
+CONFIG_Z3FOLD=m
+CONFIG_ZSMALLOC=m
+CONFIG_NET=y
+CONFIG_PACKET=y
+CONFIG_UNIX=y
+CONFIG_XFRM_USER=y
+CONFIG_NET_KEY=m
+CONFIG_INET=y
+CONFIG_IP_MULTICAST=y
+CONFIG_IP_ADVANCED_ROUTER=y
+CONFIG_IP_MULTIPLE_TABLES=y
+CONFIG_IP_ROUTE_MULTIPATH=y
+CONFIG_IP_ROUTE_VERBOSE=y
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+CONFIG_IP_PNP_RARP=y
+CONFIG_NET_IPIP=m
+CONFIG_NET_IPGRE_DEMUX=m
+CONFIG_NET_IPGRE=m
+CONFIG_IP_MROUTE=y
+CONFIG_IP_MROUTE_MULTIPLE_TABLES=y
+CONFIG_IP_PIMSM_V1=y
+CONFIG_IP_PIMSM_V2=y
+CONFIG_NET_IPVTI=m
+CONFIG_NET_FOU=m
+CONFIG_INET_AH=m
+CONFIG_INET_ESP=m
+CONFIG_INET_IPCOMP=m
+CONFIG_INET_DIAG=m
+CONFIG_TCP_CONG_ADVANCED=y
+CONFIG_TCP_CONG_BBR=m
+CONFIG_IPV6=m
+CONFIG_IPV6_ROUTER_PREF=y
+CONFIG_IPV6_ROUTE_INFO=y
+CONFIG_INET6_AH=m
+CONFIG_INET6_ESP=m
+CONFIG_INET6_ESP_OFFLOAD=m
+CONFIG_INET6_IPCOMP=m
+CONFIG_IPV6_ILA=m
+CONFIG_IPV6_VTI=m
+CONFIG_IPV6_SIT_6RD=y
+CONFIG_IPV6_GRE=m
+CONFIG_IPV6_MULTIPLE_TABLES=y
+CONFIG_IPV6_SUBTREES=y
+CONFIG_IPV6_MROUTE=y
+CONFIG_IPV6_MROUTE_MULTIPLE_TABLES=y
+CONFIG_IPV6_PIMSM_V2=y
+CONFIG_NETFILTER=y
+CONFIG_BRIDGE_NETFILTER=m
+CONFIG_NF_CONNTRACK=m
+CONFIG_NF_LOG_NETDEV=m
+CONFIG_NF_CONNTRACK_ZONES=y
+CONFIG_NF_CONNTRACK_EVENTS=y
+CONFIG_NF_CONNTRACK_TIMESTAMP=y
+CONFIG_NF_CONNTRACK_AMANDA=m
+CONFIG_NF_CONNTRACK_FTP=m
+CONFIG_NF_CONNTRACK_H323=m
+CONFIG_NF_CONNTRACK_IRC=m
+CONFIG_NF_CONNTRACK_NETBIOS_NS=m
+CONFIG_NF_CONNTRACK_SNMP=m
+CONFIG_NF_CONNTRACK_PPTP=m
+CONFIG_NF_CONNTRACK_SANE=m
+CONFIG_NF_CONNTRACK_SIP=m
+CONFIG_NF_CONNTRACK_TFTP=m
+CONFIG_NF_CT_NETLINK=m
+CONFIG_NF_TABLES=m
+CONFIG_NF_TABLES_INET=y
+CONFIG_NF_TABLES_NETDEV=y
+CONFIG_NFT_NUMGEN=m
+CONFIG_NFT_CT=m
+CONFIG_NFT_FLOW_OFFLOAD=m
+CONFIG_NFT_COUNTER=m
+CONFIG_NFT_CONNLIMIT=m
+CONFIG_NFT_LOG=m
+CONFIG_NFT_LIMIT=m
+CONFIG_NFT_MASQ=m
+CONFIG_NFT_REDIR=m
+CONFIG_NFT_NAT=m
+CONFIG_NFT_TUNNEL=m
+CONFIG_NFT_OBJREF=m
+CONFIG_NFT_QUEUE=m
+CONFIG_NFT_QUOTA=m
+CONFIG_NFT_REJECT=m
+CONFIG_NFT_COMPAT=m
+CONFIG_NFT_HASH=m
+CONFIG_NFT_FIB_INET=m
+CONFIG_NFT_SOCKET=m
+CONFIG_NFT_OSF=m
+CONFIG_NFT_TPROXY=m
+CONFIG_NFT_DUP_NETDEV=m
+CONFIG_NFT_FWD_NETDEV=m
+CONFIG_NFT_FIB_NETDEV=m
+CONFIG_NF_FLOW_TABLE_INET=m
+CONFIG_NF_FLOW_TABLE=m
+CONFIG_NETFILTER_XT_SET=m
+CONFIG_NETFILTER_XT_TARGET_CHECKSUM=m
+CONFIG_NETFILTER_XT_TARGET_CLASSIFY=m
+CONFIG_NETFILTER_XT_TARGET_CONNMARK=m
+CONFIG_NETFILTER_XT_TARGET_DSCP=m
+CONFIG_NETFILTER_XT_TARGET_HMARK=m
+CONFIG_NETFILTER_XT_TARGET_IDLETIMER=m
+CONFIG_NETFILTER_XT_TARGET_LED=m
+CONFIG_NETFILTER_XT_TARGET_LOG=m
+CONFIG_NETFILTER_XT_TARGET_MARK=m
+CONFIG_NETFILTER_XT_TARGET_NFLOG=m
+CONFIG_NETFILTER_XT_TARGET_NFQUEUE=m
+CONFIG_NETFILTER_XT_TARGET_NOTRACK=m
+CONFIG_NETFILTER_XT_TARGET_TEE=m
+CONFIG_NETFILTER_XT_TARGET_TPROXY=m
+CONFIG_NETFILTER_XT_TARGET_TRACE=m
+CONFIG_NETFILTER_XT_TARGET_TCPMSS=m
+CONFIG_NETFILTER_XT_TARGET_TCPOPTSTRIP=m
+CONFIG_NETFILTER_XT_MATCH_ADDRTYPE=m
+CONFIG_NETFILTER_XT_MATCH_BPF=m
+CONFIG_NETFILTER_XT_MATCH_CLUSTER=m
+CONFIG_NETFILTER_XT_MATCH_COMMENT=m
+CONFIG_NETFILTER_XT_MATCH_CONNBYTES=m
+CONFIG_NETFILTER_XT_MATCH_CONNLABEL=m
+CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=m
+CONFIG_NETFILTER_XT_MATCH_CONNMARK=m
+CONFIG_NETFILTER_XT_MATCH_CONNTRACK=m
+CONFIG_NETFILTER_XT_MATCH_CPU=m
+CONFIG_NETFILTER_XT_MATCH_DCCP=m
+CONFIG_NETFILTER_XT_MATCH_DEVGROUP=m
+CONFIG_NETFILTER_XT_MATCH_DSCP=m
+CONFIG_NETFILTER_XT_MATCH_ESP=m
+CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=m
+CONFIG_NETFILTER_XT_MATCH_HELPER=m
+CONFIG_NETFILTER_XT_MATCH_IPRANGE=m
+CONFIG_NETFILTER_XT_MATCH_IPVS=m
+CONFIG_NETFILTER_XT_MATCH_LENGTH=m
+CONFIG_NETFILTER_XT_MATCH_LIMIT=m
+CONFIG_NETFILTER_XT_MATCH_MAC=m
+CONFIG_NETFILTER_XT_MATCH_MARK=m
+CONFIG_NETFILTER_XT_MATCH_MULTIPORT=m
+CONFIG_NETFILTER_XT_MATCH_NFACCT=m
+CONFIG_NETFILTER_XT_MATCH_OSF=m
+CONFIG_NETFILTER_XT_MATCH_OWNER=m
+CONFIG_NETFILTER_XT_MATCH_POLICY=m
+CONFIG_NETFILTER_XT_MATCH_PHYSDEV=m
+CONFIG_NETFILTER_XT_MATCH_PKTTYPE=m
+CONFIG_NETFILTER_XT_MATCH_QUOTA=m
+CONFIG_NETFILTER_XT_MATCH_RATEEST=m
+CONFIG_NETFILTER_XT_MATCH_REALM=m
+CONFIG_NETFILTER_XT_MATCH_RECENT=m
+CONFIG_NETFILTER_XT_MATCH_SOCKET=m
+CONFIG_NETFILTER_XT_MATCH_STATE=m
+CONFIG_NETFILTER_XT_MATCH_STATISTIC=m
+CONFIG_NETFILTER_XT_MATCH_STRING=m
+CONFIG_NETFILTER_XT_MATCH_TCPMSS=m
+CONFIG_NETFILTER_XT_MATCH_TIME=m
+CONFIG_NETFILTER_XT_MATCH_U32=m
+CONFIG_IP_SET=m
+CONFIG_IP_SET_BITMAP_IP=m
+CONFIG_IP_SET_BITMAP_IPMAC=m
+CONFIG_IP_SET_BITMAP_PORT=m
+CONFIG_IP_SET_HASH_IP=m
+CONFIG_IP_SET_HASH_IPPORT=m
+CONFIG_IP_SET_HASH_IPPORTIP=m
+CONFIG_IP_SET_HASH_IPPORTNET=m
+CONFIG_IP_SET_HASH_NET=m
+CONFIG_IP_SET_HASH_NETPORT=m
+CONFIG_IP_SET_HASH_NETIFACE=m
+CONFIG_IP_SET_LIST_SET=m
+CONFIG_IP_VS=m
+CONFIG_IP_VS_PROTO_TCP=y
+CONFIG_IP_VS_PROTO_UDP=y
+CONFIG_IP_VS_PROTO_ESP=y
+CONFIG_IP_VS_PROTO_AH=y
+CONFIG_IP_VS_PROTO_SCTP=y
+CONFIG_IP_VS_RR=m
+CONFIG_IP_VS_WRR=m
+CONFIG_IP_VS_LC=m
+CONFIG_IP_VS_WLC=m
+CONFIG_IP_VS_LBLC=m
+CONFIG_IP_VS_LBLCR=m
+CONFIG_IP_VS_DH=m
+CONFIG_IP_VS_SH=m
+CONFIG_IP_VS_SED=m
+CONFIG_IP_VS_NQ=m
+CONFIG_IP_VS_FTP=m
+CONFIG_IP_VS_PE_SIP=m
+CONFIG_NFT_DUP_IPV4=m
+CONFIG_NFT_FIB_IPV4=m
+CONFIG_NF_TABLES_ARP=y
+CONFIG_NF_FLOW_TABLE_IPV4=m
+CONFIG_NF_LOG_ARP=m
+CONFIG_IP_NF_IPTABLES=m
+CONFIG_IP_NF_MATCH_AH=m
+CONFIG_IP_NF_MATCH_ECN=m
+CONFIG_IP_NF_MATCH_RPFILTER=m
+CONFIG_IP_NF_MATCH_TTL=m
+CONFIG_IP_NF_FILTER=m
+CONFIG_IP_NF_TARGET_REJECT=m
+CONFIG_IP_NF_NAT=m
+CONFIG_IP_NF_TARGET_MASQUERADE=m
+CONFIG_IP_NF_TARGET_NETMAP=m
+CONFIG_IP_NF_TARGET_REDIRECT=m
+CONFIG_IP_NF_MANGLE=m
+CONFIG_IP_NF_TARGET_CLUSTERIP=m
+CONFIG_IP_NF_TARGET_ECN=m
+CONFIG_IP_NF_TARGET_TTL=m
+CONFIG_IP_NF_RAW=m
+CONFIG_IP_NF_ARPTABLES=m
+CONFIG_IP_NF_ARPFILTER=m
+CONFIG_IP_NF_ARP_MANGLE=m
+CONFIG_NFT_DUP_IPV6=m
+CONFIG_NFT_FIB_IPV6=m
+CONFIG_NF_FLOW_TABLE_IPV6=m
+CONFIG_IP6_NF_IPTABLES=m
+CONFIG_IP6_NF_MATCH_AH=m
+CONFIG_IP6_NF_MATCH_EUI64=m
+CONFIG_IP6_NF_MATCH_FRAG=m
+CONFIG_IP6_NF_MATCH_OPTS=m
+CONFIG_IP6_NF_MATCH_HL=m
+CONFIG_IP6_NF_MATCH_IPV6HEADER=m
+CONFIG_IP6_NF_MATCH_MH=m
+CONFIG_IP6_NF_MATCH_RPFILTER=m
+CONFIG_IP6_NF_MATCH_RT=m
+CONFIG_IP6_NF_MATCH_SRH=m
+CONFIG_IP6_NF_TARGET_HL=m
+CONFIG_IP6_NF_FILTER=m
+CONFIG_IP6_NF_TARGET_REJECT=m
+CONFIG_IP6_NF_TARGET_SYNPROXY=m
+CONFIG_IP6_NF_MANGLE=m
+CONFIG_IP6_NF_RAW=m
+CONFIG_IP6_NF_SECURITY=m
+CONFIG_IP6_NF_NAT=m
+CONFIG_IP6_NF_TARGET_MASQUERADE=m
+CONFIG_IP6_NF_TARGET_NPT=m
+CONFIG_NF_TABLES_BRIDGE=m
+CONFIG_NFT_BRIDGE_REJECT=m
+CONFIG_NF_LOG_BRIDGE=m
+CONFIG_BRIDGE_NF_EBTABLES=m
+CONFIG_BRIDGE_EBT_BROUTE=m
+CONFIG_BRIDGE_EBT_T_FILTER=m
+CONFIG_BRIDGE_EBT_T_NAT=m
+CONFIG_BRIDGE_EBT_802_3=m
+CONFIG_BRIDGE_EBT_AMONG=m
+CONFIG_BRIDGE_EBT_ARP=m
+CONFIG_BRIDGE_EBT_IP=m
+CONFIG_BRIDGE_EBT_IP6=m
+CONFIG_BRIDGE_EBT_LIMIT=m
+CONFIG_BRIDGE_EBT_MARK=m
+CONFIG_BRIDGE_EBT_PKTTYPE=m
+CONFIG_BRIDGE_EBT_STP=m
+CONFIG_BRIDGE_EBT_VLAN=m
+CONFIG_BRIDGE_EBT_ARPREPLY=m
+CONFIG_BRIDGE_EBT_DNAT=m
+CONFIG_BRIDGE_EBT_MARK_T=m
+CONFIG_BRIDGE_EBT_REDIRECT=m
+CONFIG_BRIDGE_EBT_SNAT=m
+CONFIG_BRIDGE_EBT_LOG=m
+CONFIG_BRIDGE_EBT_NFLOG=m
+CONFIG_SCTP_COOKIE_HMAC_SHA1=y
+CONFIG_ATM=m
+CONFIG_L2TP=m
+CONFIG_L2TP_V3=y
+CONFIG_L2TP_IP=m
+CONFIG_L2TP_ETH=m
+CONFIG_BRIDGE=m
+CONFIG_VLAN_8021Q=m
+CONFIG_VLAN_8021Q_GVRP=y
+CONFIG_ATALK=m
+CONFIG_6LOWPAN=m
+CONFIG_IEEE802154=m
+CONFIG_IEEE802154_6LOWPAN=m
+CONFIG_MAC802154=m
+CONFIG_NET_SCHED=y
+CONFIG_NET_SCH_CBQ=m
+CONFIG_NET_SCH_HTB=m
+CONFIG_NET_SCH_HFSC=m
+CONFIG_NET_SCH_ATM=m
+CONFIG_NET_SCH_PRIO=m
+CONFIG_NET_SCH_MULTIQ=m
+CONFIG_NET_SCH_RED=m
+CONFIG_NET_SCH_SFB=m
+CONFIG_NET_SCH_SFQ=m
+CONFIG_NET_SCH_TEQL=m
+CONFIG_NET_SCH_TBF=m
+CONFIG_NET_SCH_GRED=m
+CONFIG_NET_SCH_DSMARK=m
+CONFIG_NET_SCH_NETEM=m
+CONFIG_NET_SCH_DRR=m
+CONFIG_NET_SCH_MQPRIO=m
+CONFIG_NET_SCH_CHOKE=m
+CONFIG_NET_SCH_QFQ=m
+CONFIG_NET_SCH_CODEL=m
+CONFIG_NET_SCH_FQ_CODEL=m
+CONFIG_NET_SCH_CAKE=m
+CONFIG_NET_SCH_FQ=m
+CONFIG_NET_SCH_HHF=m
+CONFIG_NET_SCH_PIE=m
+CONFIG_NET_SCH_INGRESS=m
+CONFIG_NET_SCH_PLUG=m
+CONFIG_NET_CLS_BASIC=m
+CONFIG_NET_CLS_TCINDEX=m
+CONFIG_NET_CLS_ROUTE4=m
+CONFIG_NET_CLS_FW=m
+CONFIG_NET_CLS_U32=m
+CONFIG_CLS_U32_MARK=y
+CONFIG_NET_CLS_RSVP=m
+CONFIG_NET_CLS_RSVP6=m
+CONFIG_NET_CLS_FLOW=m
+CONFIG_NET_CLS_CGROUP=m
+CONFIG_NET_EMATCH=y
+CONFIG_NET_EMATCH_CMP=m
+CONFIG_NET_EMATCH_NBYTE=m
+CONFIG_NET_EMATCH_U32=m
+CONFIG_NET_EMATCH_META=m
+CONFIG_NET_EMATCH_TEXT=m
+CONFIG_NET_EMATCH_IPSET=m
+CONFIG_NET_CLS_ACT=y
+CONFIG_NET_ACT_POLICE=m
+CONFIG_NET_ACT_GACT=m
+CONFIG_GACT_PROB=y
+CONFIG_NET_ACT_MIRRED=m
+CONFIG_NET_ACT_IPT=m
+CONFIG_NET_ACT_NAT=m
+CONFIG_NET_ACT_PEDIT=m
+CONFIG_NET_ACT_SIMP=m
+CONFIG_NET_ACT_SKBEDIT=m
+CONFIG_NET_ACT_CSUM=m
+CONFIG_BATMAN_ADV=m
+CONFIG_OPENVSWITCH=m
+CONFIG_CGROUP_NET_PRIO=y
+CONFIG_NET_PKTGEN=m
+CONFIG_HAMRADIO=y
+CONFIG_AX25=m
+CONFIG_NETROM=m
+CONFIG_ROSE=m
+CONFIG_MKISS=m
+CONFIG_6PACK=m
+CONFIG_BPQETHER=m
+CONFIG_BAYCOM_SER_FDX=m
+CONFIG_BAYCOM_SER_HDX=m
+CONFIG_YAM=m
+CONFIG_CAN=m
+CONFIG_CAN_J1939=m
+CONFIG_CAN_ISOTP=m
+CONFIG_CAN_VCAN=m
+CONFIG_CAN_SLCAN=m
+CONFIG_CAN_MCP251X=m
+CONFIG_CAN_MCP251XFD=m
+CONFIG_CAN_EMS_USB=m
+CONFIG_CAN_GS_USB=m
+CONFIG_BT=m
+CONFIG_BT_RFCOMM=m
+CONFIG_BT_RFCOMM_TTY=y
+CONFIG_BT_BNEP=m
+CONFIG_BT_BNEP_MC_FILTER=y
+CONFIG_BT_BNEP_PROTO_FILTER=y
+CONFIG_BT_HIDP=m
+CONFIG_BT_6LOWPAN=m
+CONFIG_BT_HCIBTUSB=m
+CONFIG_BT_HCIUART=m
+CONFIG_BT_HCIUART_3WIRE=y
+CONFIG_BT_HCIUART_BCM=y
+CONFIG_BT_HCIBCM203X=m
+CONFIG_BT_HCIBPA10X=m
+CONFIG_BT_HCIBFUSB=m
+CONFIG_BT_HCIVHCI=m
+CONFIG_BT_MRVL=m
+CONFIG_BT_MRVL_SDIO=m
+CONFIG_BT_ATH3K=m
+CONFIG_CFG80211=m
+CONFIG_CFG80211_WEXT=y
+CONFIG_MAC80211=m
+CONFIG_MAC80211_MESH=y
+CONFIG_WIMAX=m
+CONFIG_RFKILL=m
+CONFIG_RFKILL_INPUT=y
+CONFIG_NET_9P=m
+CONFIG_NFC=m
+CONFIG_PCI=y
+CONFIG_PCIE_BRCMSTB=y
+CONFIG_UEVENT_HELPER=y
+CONFIG_DEVTMPFS=y
+CONFIG_DEVTMPFS_MOUNT=y
+CONFIG_MTD=m
+CONFIG_MTD_BLOCK=m
+CONFIG_MTD_BLOCK2MTD=m
+CONFIG_MTD_SPI_NOR=m
+CONFIG_MTD_UBI=m
+CONFIG_OF_CONFIGFS=y
+CONFIG_ZRAM=m
+CONFIG_BLK_DEV_LOOP=y
+CONFIG_BLK_DEV_CRYPTOLOOP=m
+CONFIG_BLK_DEV_DRBD=m
+CONFIG_BLK_DEV_NBD=m
+CONFIG_BLK_DEV_RAM=y
+CONFIG_CDROM_PKTCDVD=m
+CONFIG_ATA_OVER_ETH=m
+CONFIG_BLK_DEV_NVME=y
+CONFIG_EEPROM_AT24=m
+CONFIG_TI_ST=m
+CONFIG_SCSI=y
+# CONFIG_SCSI_PROC_FS is not set
+CONFIG_BLK_DEV_SD=y
+CONFIG_CHR_DEV_ST=m
+CONFIG_BLK_DEV_SR=m
+CONFIG_CHR_DEV_SG=m
+CONFIG_SCSI_ISCSI_ATTRS=y
+CONFIG_ISCSI_TCP=m
+CONFIG_ISCSI_BOOT_SYSFS=m
+CONFIG_ATA=m
+CONFIG_SATA_AHCI=m
+CONFIG_SATA_MV=m
+CONFIG_MD=y
+CONFIG_MD_LINEAR=m
+CONFIG_BLK_DEV_DM=m
+CONFIG_DM_CRYPT=m
+CONFIG_DM_SNAPSHOT=m
+CONFIG_DM_THIN_PROVISIONING=m
+CONFIG_DM_CACHE=m
+CONFIG_DM_MIRROR=m
+CONFIG_DM_LOG_USERSPACE=m
+CONFIG_DM_RAID=m
+CONFIG_DM_ZERO=m
+CONFIG_DM_DELAY=m
+CONFIG_NETDEVICES=y
+CONFIG_BONDING=m
+CONFIG_DUMMY=m
+CONFIG_WIREGUARD=m
+CONFIG_IFB=m
+CONFIG_MACVLAN=m
+CONFIG_MACVTAP=m
+CONFIG_IPVLAN=m
+CONFIG_VXLAN=m
+CONFIG_NETCONSOLE=m
+CONFIG_TUN=m
+CONFIG_VETH=m
+CONFIG_NET_VRF=m
+CONFIG_BCMGENET=y
+CONFIG_ENC28J60=m
+CONFIG_QCA7000_SPI=m
+CONFIG_QCA7000_UART=m
+CONFIG_WIZNET_W5100=m
+CONFIG_WIZNET_W5100_SPI=m
+CONFIG_MICREL_PHY=y
+CONFIG_MDIO_BITBANG=m
+CONFIG_PPP=m
+CONFIG_PPP_BSDCOMP=m
+CONFIG_PPP_DEFLATE=m
+CONFIG_PPP_FILTER=y
+CONFIG_PPP_MPPE=m
+CONFIG_PPP_MULTILINK=y
+CONFIG_PPPOATM=m
+CONFIG_PPPOE=m
+CONFIG_PPPOL2TP=m
+CONFIG_PPP_ASYNC=m
+CONFIG_PPP_SYNC_TTY=m
+CONFIG_SLIP=m
+CONFIG_SLIP_COMPRESSED=y
+CONFIG_SLIP_SMART=y
+CONFIG_USB_CATC=m
+CONFIG_USB_KAWETH=m
+CONFIG_USB_PEGASUS=m
+CONFIG_USB_RTL8150=m
+CONFIG_USB_RTL8152=y
+CONFIG_USB_LAN78XX=y
+CONFIG_USB_USBNET=y
+CONFIG_USB_NET_AX8817X=m
+CONFIG_USB_NET_AX88179_178A=m
+CONFIG_USB_NET_CDCETHER=m
+CONFIG_USB_NET_CDC_EEM=m
+CONFIG_USB_NET_CDC_NCM=m
+CONFIG_USB_NET_HUAWEI_CDC_NCM=m
+CONFIG_USB_NET_CDC_MBIM=m
+CONFIG_USB_NET_DM9601=m
+CONFIG_USB_NET_SR9700=m
+CONFIG_USB_NET_SR9800=m
+CONFIG_USB_NET_SMSC75XX=m
+CONFIG_USB_NET_SMSC95XX=y
+CONFIG_USB_NET_GL620A=m
+CONFIG_USB_NET_NET1080=m
+CONFIG_USB_NET_PLUSB=m
+CONFIG_USB_NET_MCS7830=m
+CONFIG_USB_NET_CDC_SUBSET=m
+CONFIG_USB_ALI_M5632=y
+CONFIG_USB_AN2720=y
+CONFIG_USB_EPSON2888=y
+CONFIG_USB_KC2190=y
+CONFIG_USB_NET_ZAURUS=m
+CONFIG_USB_NET_CX82310_ETH=m
+CONFIG_USB_NET_KALMIA=m
+CONFIG_USB_NET_QMI_WWAN=m
+CONFIG_USB_HSO=m
+CONFIG_USB_NET_INT51X1=m
+CONFIG_USB_IPHETH=m
+CONFIG_USB_SIERRA_NET=m
+CONFIG_USB_VL600=m
+CONFIG_USB_NET_AQC111=m
+CONFIG_ATH9K=m
+CONFIG_ATH9K_HTC=m
+CONFIG_CARL9170=m
+CONFIG_ATH6KL=m
+CONFIG_ATH6KL_USB=m
+CONFIG_AR5523=m
+CONFIG_AT76C50X_USB=m
+CONFIG_B43=m
+# CONFIG_B43_PHY_N is not set
+CONFIG_B43LEGACY=m
+CONFIG_BRCMFMAC=m
+CONFIG_BRCMFMAC_USB=y
+CONFIG_BRCMDBG=y
+CONFIG_HOSTAP=m
+CONFIG_P54_COMMON=m
+CONFIG_P54_USB=m
+CONFIG_LIBERTAS=m
+CONFIG_LIBERTAS_USB=m
+CONFIG_LIBERTAS_SDIO=m
+CONFIG_LIBERTAS_THINFIRM=m
+CONFIG_LIBERTAS_THINFIRM_USB=m
+CONFIG_MWIFIEX=m
+CONFIG_MWIFIEX_SDIO=m
+CONFIG_MT7601U=m
+CONFIG_MT76x0U=m
+CONFIG_MT76x2U=m
+CONFIG_RT2X00=m
+CONFIG_RT2500USB=m
+CONFIG_RT73USB=m
+CONFIG_RT2800USB=m
+CONFIG_RT2800USB_RT3573=y
+CONFIG_RT2800USB_RT53XX=y
+CONFIG_RT2800USB_RT55XX=y
+CONFIG_RT2800USB_UNKNOWN=y
+CONFIG_RTL8187=m
+CONFIG_RTL8192CU=m
+CONFIG_RTL8XXXU=m
+CONFIG_USB_ZD1201=m
+CONFIG_ZD1211RW=m
+CONFIG_MAC80211_HWSIM=m
+CONFIG_USB_NET_RNDIS_WLAN=m
+CONFIG_WIMAX_I2400M_USB=m
+CONFIG_IEEE802154_AT86RF230=m
+CONFIG_IEEE802154_MRF24J40=m
+CONFIG_IEEE802154_CC2520=m
+CONFIG_INPUT_MOUSEDEV=y
+CONFIG_INPUT_JOYDEV=m
+CONFIG_INPUT_EVDEV=y
+# CONFIG_KEYBOARD_ATKBD is not set
+CONFIG_KEYBOARD_GPIO=m
+CONFIG_KEYBOARD_MATRIX=m
+CONFIG_KEYBOARD_CAP11XX=m
+# CONFIG_INPUT_MOUSE is not set
+CONFIG_INPUT_JOYSTICK=y
+CONFIG_JOYSTICK_IFORCE=m
+CONFIG_JOYSTICK_IFORCE_USB=m
+CONFIG_JOYSTICK_XPAD=m
+CONFIG_JOYSTICK_XPAD_FF=y
+CONFIG_JOYSTICK_XPAD_LEDS=y
+CONFIG_JOYSTICK_PSXPAD_SPI=m
+CONFIG_JOYSTICK_PSXPAD_SPI_FF=y
+CONFIG_JOYSTICK_RPISENSE=m
+CONFIG_INPUT_TOUCHSCREEN=y
+CONFIG_TOUCHSCREEN_ADS7846=m
+CONFIG_TOUCHSCREEN_EGALAX=m
+CONFIG_TOUCHSCREEN_EXC3000=m
+CONFIG_TOUCHSCREEN_GOODIX=m
+CONFIG_TOUCHSCREEN_ILI210X=m
+CONFIG_TOUCHSCREEN_EDT_FT5X06=m
+CONFIG_TOUCHSCREEN_RASPBERRYPI_FW=m
+CONFIG_TOUCHSCREEN_USB_COMPOSITE=m
+CONFIG_TOUCHSCREEN_STMPE=m
+CONFIG_INPUT_MISC=y
+CONFIG_INPUT_AD714X=m
+CONFIG_INPUT_ATI_REMOTE2=m
+CONFIG_INPUT_KEYSPAN_REMOTE=m
+CONFIG_INPUT_POWERMATE=m
+CONFIG_INPUT_YEALINK=m
+CONFIG_INPUT_CM109=m
+CONFIG_INPUT_UINPUT=m
+CONFIG_INPUT_GPIO_ROTARY_ENCODER=m
+CONFIG_INPUT_ADXL34X=m
+CONFIG_INPUT_CMA3000=m
+CONFIG_SERIO=m
+CONFIG_SERIO_RAW=m
+CONFIG_GAMEPORT=m
+CONFIG_GAMEPORT_NS558=m
+CONFIG_GAMEPORT_L4=m
+CONFIG_BRCM_CHAR_DRIVERS=y
+CONFIG_BCM_VCIO=y
+CONFIG_BCM2835_DEVGPIOMEM=y
+CONFIG_RPIVID_MEM=m
+# CONFIG_LEGACY_PTYS is not set
+CONFIG_SERIAL_8250=y
+# CONFIG_SERIAL_8250_DEPRECATED_OPTIONS is not set
+CONFIG_SERIAL_8250_CONSOLE=y
+# CONFIG_SERIAL_8250_DMA is not set
+CONFIG_SERIAL_8250_NR_UARTS=1
+CONFIG_SERIAL_8250_RUNTIME_UARTS=0
+CONFIG_SERIAL_8250_EXTENDED=y
+CONFIG_SERIAL_8250_SHARE_IRQ=y
+CONFIG_SERIAL_8250_BCM2835AUX=y
+CONFIG_SERIAL_OF_PLATFORM=y
+CONFIG_SERIAL_AMBA_PL011=y
+CONFIG_SERIAL_AMBA_PL011_CONSOLE=y
+CONFIG_SERIAL_SC16IS7XX=m
+CONFIG_SERIAL_SC16IS7XX_SPI=y
+CONFIG_SERIAL_DEV_BUS=y
+CONFIG_TTY_PRINTK=y
+CONFIG_HW_RANDOM=y
+CONFIG_RAW_DRIVER=y
+CONFIG_TCG_TPM=m
+CONFIG_TCG_TIS_SPI=m
+CONFIG_I2C=y
+CONFIG_I2C_CHARDEV=m
+CONFIG_I2C_MUX_GPMUX=m
+CONFIG_I2C_MUX_PCA954x=m
+CONFIG_I2C_MUX_PINCTRL=m
+CONFIG_I2C_BCM2708=m
+CONFIG_I2C_BCM2835=m
+CONFIG_I2C_BRCMSTB=m
+CONFIG_I2C_GPIO=m
+CONFIG_I2C_ROBOTFUZZ_OSIF=m
+CONFIG_I2C_TINY_USB=m
+CONFIG_SPI=y
+CONFIG_SPI_BCM2835=m
+CONFIG_SPI_BCM2835AUX=m
+CONFIG_SPI_GPIO=m
+CONFIG_SPI_SPIDEV=m
+CONFIG_SPI_SLAVE=y
+CONFIG_PPS=m
+CONFIG_PPS_CLIENT_LDISC=m
+CONFIG_PPS_CLIENT_GPIO=m
+CONFIG_PINCTRL_MCP23S08=m
+CONFIG_GPIO_SYSFS=y
+CONFIG_GPIO_BCM_VIRT=y
+CONFIG_GPIO_PCA953X=m
+CONFIG_GPIO_PCA953X_IRQ=y
+CONFIG_GPIO_PCF857X=m
+CONFIG_GPIO_ARIZONA=m
+CONFIG_GPIO_FSM=m
+CONFIG_GPIO_STMPE=y
+CONFIG_GPIO_MOCKUP=m
+CONFIG_W1=m
+CONFIG_W1_MASTER_DS2490=m
+CONFIG_W1_MASTER_DS2482=m
+CONFIG_W1_MASTER_DS1WM=m
+CONFIG_W1_MASTER_GPIO=m
+CONFIG_W1_SLAVE_THERM=m
+CONFIG_W1_SLAVE_SMEM=m
+CONFIG_W1_SLAVE_DS2408=m
+CONFIG_W1_SLAVE_DS2413=m
+CONFIG_W1_SLAVE_DS2406=m
+CONFIG_W1_SLAVE_DS2423=m
+CONFIG_W1_SLAVE_DS2431=m
+CONFIG_W1_SLAVE_DS2433=m
+CONFIG_W1_SLAVE_DS2438=m
+CONFIG_W1_SLAVE_DS2780=m
+CONFIG_W1_SLAVE_DS2781=m
+CONFIG_W1_SLAVE_DS28E04=m
+CONFIG_W1_SLAVE_DS28E17=m
+CONFIG_POWER_RESET_GPIO=y
+CONFIG_RPI_POE_POWER=m
+CONFIG_BATTERY_DS2760=m
+CONFIG_BATTERY_MAX17040=m
+CONFIG_BATTERY_GAUGE_LTC2941=m
+CONFIG_SENSORS_DS1621=m
+CONFIG_SENSORS_GPIO_FAN=m
+CONFIG_SENSORS_IIO_HWMON=m
+CONFIG_SENSORS_JC42=m
+CONFIG_SENSORS_LM75=m
+CONFIG_SENSORS_RASPBERRYPI_HWMON=m
+CONFIG_SENSORS_RPI_POE_FAN=m
+CONFIG_SENSORS_SHT21=m
+CONFIG_SENSORS_SHT3x=m
+CONFIG_SENSORS_SHTC1=m
+CONFIG_SENSORS_INA2XX=m
+CONFIG_SENSORS_TMP102=m
+CONFIG_THERMAL=y
+CONFIG_BCM2711_THERMAL=y
+CONFIG_BCM2835_THERMAL=y
+CONFIG_WATCHDOG=y
+CONFIG_GPIO_WATCHDOG=m
+CONFIG_BCM2835_WDT=y
+CONFIG_MFD_STMPE=y
+CONFIG_STMPE_SPI=y
+CONFIG_MFD_SYSCON=y
+CONFIG_MFD_ARIZONA_I2C=m
+CONFIG_MFD_ARIZONA_SPI=m
+CONFIG_MFD_WM5102=y
+CONFIG_REGULATOR=y
+CONFIG_REGULATOR_FIXED_VOLTAGE=y
+CONFIG_REGULATOR_ARIZONA_LDO1=m
+CONFIG_REGULATOR_ARIZONA_MICSUPP=m
+CONFIG_REGULATOR_GPIO=y
+CONFIG_RC_CORE=y
+CONFIG_LIRC=y
+CONFIG_BPF_LIRC_MODE2=y
+CONFIG_RC_DECODERS=y
+CONFIG_IR_NEC_DECODER=m
+CONFIG_IR_RC5_DECODER=m
+CONFIG_IR_RC6_DECODER=m
+CONFIG_IR_JVC_DECODER=m
+CONFIG_IR_SONY_DECODER=m
+CONFIG_IR_SANYO_DECODER=m
+CONFIG_IR_SHARP_DECODER=m
+CONFIG_IR_MCE_KBD_DECODER=m
+CONFIG_IR_XMP_DECODER=m
+CONFIG_IR_IMON_DECODER=m
+CONFIG_RC_DEVICES=y
+CONFIG_RC_ATI_REMOTE=m
+CONFIG_IR_IMON=m
+CONFIG_IR_MCEUSB=m
+CONFIG_IR_REDRAT3=m
+CONFIG_IR_STREAMZAP=m
+CONFIG_IR_IGUANA=m
+CONFIG_IR_TTUSBIR=m
+CONFIG_RC_LOOPBACK=m
+CONFIG_IR_GPIO_CIR=m
+CONFIG_IR_GPIO_TX=m
+CONFIG_IR_PWM_TX=m
+CONFIG_IR_TOY=m
+CONFIG_MEDIA_CEC_RC=y
+CONFIG_MEDIA_SUPPORT=m
+CONFIG_MEDIA_USB_SUPPORT=y
+CONFIG_USB_VIDEO_CLASS=m
+CONFIG_USB_M5602=m
+CONFIG_USB_STV06XX=m
+CONFIG_USB_GL860=m
+CONFIG_USB_GSPCA_BENQ=m
+CONFIG_USB_GSPCA_CONEX=m
+CONFIG_USB_GSPCA_CPIA1=m
+CONFIG_USB_GSPCA_DTCS033=m
+CONFIG_USB_GSPCA_ETOMS=m
+CONFIG_USB_GSPCA_FINEPIX=m
+CONFIG_USB_GSPCA_JEILINJ=m
+CONFIG_USB_GSPCA_JL2005BCD=m
+CONFIG_USB_GSPCA_KINECT=m
+CONFIG_USB_GSPCA_KONICA=m
+CONFIG_USB_GSPCA_MARS=m
+CONFIG_USB_GSPCA_MR97310A=m
+CONFIG_USB_GSPCA_NW80X=m
+CONFIG_USB_GSPCA_OV519=m
+CONFIG_USB_GSPCA_OV534=m
+CONFIG_USB_GSPCA_OV534_9=m
+CONFIG_USB_GSPCA_PAC207=m
+CONFIG_USB_GSPCA_PAC7302=m
+CONFIG_USB_GSPCA_PAC7311=m
+CONFIG_USB_GSPCA_SE401=m
+CONFIG_USB_GSPCA_SN9C2028=m
+CONFIG_USB_GSPCA_SN9C20X=m
+CONFIG_USB_GSPCA_SONIXB=m
+CONFIG_USB_GSPCA_SONIXJ=m
+CONFIG_USB_GSPCA_SPCA500=m
+CONFIG_USB_GSPCA_SPCA501=m
+CONFIG_USB_GSPCA_SPCA505=m
+CONFIG_USB_GSPCA_SPCA506=m
+CONFIG_USB_GSPCA_SPCA508=m
+CONFIG_USB_GSPCA_SPCA561=m
+CONFIG_USB_GSPCA_SPCA1528=m
+CONFIG_USB_GSPCA_SQ905=m
+CONFIG_USB_GSPCA_SQ905C=m
+CONFIG_USB_GSPCA_SQ930X=m
+CONFIG_USB_GSPCA_STK014=m
+CONFIG_USB_GSPCA_STK1135=m
+CONFIG_USB_GSPCA_STV0680=m
+CONFIG_USB_GSPCA_SUNPLUS=m
+CONFIG_USB_GSPCA_T613=m
+CONFIG_USB_GSPCA_TOPRO=m
+CONFIG_USB_GSPCA_TV8532=m
+CONFIG_USB_GSPCA_VC032X=m
+CONFIG_USB_GSPCA_VICAM=m
+CONFIG_USB_GSPCA_XIRLINK_CIT=m
+CONFIG_USB_GSPCA_ZC3XX=m
+CONFIG_USB_PWC=m
+CONFIG_VIDEO_CPIA2=m
+CONFIG_USB_ZR364XX=m
+CONFIG_USB_STKWEBCAM=m
+CONFIG_USB_S2255=m
+CONFIG_VIDEO_USBTV=m
+CONFIG_VIDEO_PVRUSB2=m
+CONFIG_VIDEO_HDPVR=m
+CONFIG_VIDEO_STK1160_COMMON=m
+CONFIG_VIDEO_GO7007=m
+CONFIG_VIDEO_GO7007_USB=m
+CONFIG_VIDEO_GO7007_USB_S2250_BOARD=m
+CONFIG_VIDEO_AU0828=m
+CONFIG_VIDEO_AU0828_RC=y
+CONFIG_VIDEO_CX231XX=m
+CONFIG_VIDEO_CX231XX_ALSA=m
+CONFIG_VIDEO_CX231XX_DVB=m
+CONFIG_VIDEO_TM6000=m
+CONFIG_VIDEO_TM6000_ALSA=m
+CONFIG_VIDEO_TM6000_DVB=m
+CONFIG_DVB_USB=m
+CONFIG_DVB_USB_A800=m
+CONFIG_DVB_USB_DIBUSB_MB=m
+CONFIG_DVB_USB_DIBUSB_MB_FAULTY=y
+CONFIG_DVB_USB_DIBUSB_MC=m
+CONFIG_DVB_USB_DIB0700=m
+CONFIG_DVB_USB_UMT_010=m
+CONFIG_DVB_USB_CXUSB=m
+CONFIG_DVB_USB_M920X=m
+CONFIG_DVB_USB_DIGITV=m
+CONFIG_DVB_USB_VP7045=m
+CONFIG_DVB_USB_VP702X=m
+CONFIG_DVB_USB_GP8PSK=m
+CONFIG_DVB_USB_NOVA_T_USB2=m
+CONFIG_DVB_USB_TTUSB2=m
+CONFIG_DVB_USB_DTT200U=m
+CONFIG_DVB_USB_OPERA1=m
+CONFIG_DVB_USB_AF9005=m
+CONFIG_DVB_USB_AF9005_REMOTE=m
+CONFIG_DVB_USB_PCTV452E=m
+CONFIG_DVB_USB_DW2102=m
+CONFIG_DVB_USB_CINERGY_T2=m
+CONFIG_DVB_USB_DTV5100=m
+CONFIG_DVB_USB_AZ6027=m
+CONFIG_DVB_USB_TECHNISAT_USB2=m
+CONFIG_DVB_USB_V2=m
+CONFIG_DVB_USB_AF9015=m
+CONFIG_DVB_USB_AF9035=m
+CONFIG_DVB_USB_ANYSEE=m
+CONFIG_DVB_USB_AU6610=m
+CONFIG_DVB_USB_AZ6007=m
+CONFIG_DVB_USB_CE6230=m
+CONFIG_DVB_USB_EC168=m
+CONFIG_DVB_USB_GL861=m
+CONFIG_DVB_USB_LME2510=m
+CONFIG_DVB_USB_MXL111SF=m
+CONFIG_DVB_USB_RTL28XXU=m
+CONFIG_DVB_USB_DVBSKY=m
+CONFIG_SMS_USB_DRV=m
+CONFIG_DVB_B2C2_FLEXCOP_USB=m
+CONFIG_DVB_AS102=m
+CONFIG_VIDEO_EM28XX=m
+CONFIG_VIDEO_EM28XX_V4L2=m
+CONFIG_VIDEO_EM28XX_ALSA=m
+CONFIG_VIDEO_EM28XX_DVB=m
+CONFIG_RADIO_SI470X=m
+CONFIG_USB_SI470X=m
+CONFIG_I2C_SI470X=m
+CONFIG_RADIO_SI4713=m
+CONFIG_I2C_SI4713=m
+CONFIG_USB_MR800=m
+CONFIG_USB_DSBR=m
+CONFIG_RADIO_SHARK=m
+CONFIG_RADIO_SHARK2=m
+CONFIG_USB_KEENE=m
+CONFIG_USB_MA901=m
+CONFIG_RADIO_TEA5764=m
+CONFIG_RADIO_SAA7706H=m
+CONFIG_RADIO_TEF6862=m
+CONFIG_RADIO_WL1273=m
+CONFIG_RADIO_WL128X=m
+CONFIG_V4L_PLATFORM_DRIVERS=y
+CONFIG_VIDEO_BCM2835_UNICAM=m
+CONFIG_V4L_TEST_DRIVERS=y
+CONFIG_VIDEO_VIMC=m
+CONFIG_VIDEO_VIVID=m
+CONFIG_VIDEO_VIM2M=m
+CONFIG_VIDEO_VICODEC=m
+CONFIG_VIDEO_UDA1342=m
+CONFIG_VIDEO_SONY_BTF_MPX=m
+CONFIG_VIDEO_ADV7180=m
+CONFIG_VIDEO_TC358743=m
+CONFIG_VIDEO_TVP5150=m
+CONFIG_VIDEO_TW2804=m
+CONFIG_VIDEO_TW9903=m
+CONFIG_VIDEO_TW9906=m
+CONFIG_VIDEO_IMX219=m
+CONFIG_VIDEO_IMX290=m
+CONFIG_VIDEO_IMX477=m
+CONFIG_VIDEO_OV5647=m
+CONFIG_VIDEO_OV7251=m
+CONFIG_VIDEO_OV7640=m
+CONFIG_VIDEO_OV9281=m
+CONFIG_VIDEO_IRS1125=m
+CONFIG_VIDEO_MT9V011=m
+CONFIG_DRM=m
+CONFIG_DRM_LOAD_EDID_FIRMWARE=y
+CONFIG_DRM_UDL=m
+CONFIG_DRM_PANEL_SIMPLE=m
+CONFIG_DRM_PANEL_JDI_LT070ME05000=m
+CONFIG_DRM_PANEL_RASPBERRYPI_TOUCHSCREEN=m
+CONFIG_DRM_DISPLAY_CONNECTOR=m
+CONFIG_DRM_SIMPLE_BRIDGE=m
+CONFIG_DRM_V3D=m
+CONFIG_DRM_VC4=m
+CONFIG_DRM_VC4_HDMI_CEC=y
+CONFIG_TINYDRM_ILI9225=m
+CONFIG_TINYDRM_ILI9341=m
+CONFIG_TINYDRM_MI0283QT=m
+CONFIG_TINYDRM_REPAPER=m
+CONFIG_TINYDRM_ST7586=m
+CONFIG_TINYDRM_ST7735R=m
+CONFIG_DRM_GUD=m
+CONFIG_FB=y
+CONFIG_FB_BCM2708=y
+CONFIG_FB_UDL=m
+CONFIG_FB_SIMPLE=y
+CONFIG_FB_SSD1307=m
+CONFIG_FB_RPISENSE=m
+CONFIG_BACKLIGHT_RPI=m
+CONFIG_BACKLIGHT_GPIO=m
+CONFIG_FRAMEBUFFER_CONSOLE=y
+CONFIG_FRAMEBUFFER_CONSOLE_ROTATION=y
+CONFIG_LOGO=y
+# CONFIG_LOGO_LINUX_MONO is not set
+# CONFIG_LOGO_LINUX_VGA16 is not set
+CONFIG_SOUND=y
+CONFIG_SND=m
+CONFIG_SND_OSSEMUL=y
+CONFIG_SND_PCM_OSS=m
+CONFIG_SND_HRTIMER=m
+CONFIG_SND_SEQUENCER=m
+CONFIG_SND_SEQ_DUMMY=m
+CONFIG_SND_DUMMY=m
+CONFIG_SND_ALOOP=m
+CONFIG_SND_VIRMIDI=m
+CONFIG_SND_MTPAV=m
+CONFIG_SND_SERIAL_U16550=m
+CONFIG_SND_MPU401=m
+CONFIG_SND_USB_AUDIO=m
+CONFIG_SND_USB_UA101=m
+CONFIG_SND_USB_CAIAQ=m
+CONFIG_SND_USB_CAIAQ_INPUT=y
+CONFIG_SND_USB_6FIRE=m
+CONFIG_SND_USB_HIFACE=m
+CONFIG_SND_SOC=m
+CONFIG_SND_BCM2835_SOC_I2S=m
+CONFIG_SND_BCM2708_SOC_GOOGLEVOICEHAT_SOUNDCARD=m
+CONFIG_SND_BCM2708_SOC_HIFIBERRY_DAC=m
+CONFIG_SND_BCM2708_SOC_HIFIBERRY_DACPLUS=m
+CONFIG_SND_BCM2708_SOC_HIFIBERRY_DACPLUSHD=m
+CONFIG_SND_BCM2708_SOC_HIFIBERRY_DACPLUSADC=m
+CONFIG_SND_BCM2708_SOC_HIFIBERRY_DACPLUSADCPRO=m
+CONFIG_SND_BCM2708_SOC_HIFIBERRY_DACPLUSDSP=m
+CONFIG_SND_BCM2708_SOC_HIFIBERRY_DIGI=m
+CONFIG_SND_BCM2708_SOC_HIFIBERRY_AMP=m
+CONFIG_SND_BCM2708_SOC_PIFI_40=m
+CONFIG_SND_BCM2708_SOC_RPI_CIRRUS=m
+CONFIG_SND_BCM2708_SOC_RPI_DAC=m
+CONFIG_SND_BCM2708_SOC_RPI_PROTO=m
+CONFIG_SND_BCM2708_SOC_JUSTBOOM_BOTH=m
+CONFIG_SND_BCM2708_SOC_JUSTBOOM_DAC=m
+CONFIG_SND_BCM2708_SOC_JUSTBOOM_DIGI=m
+CONFIG_SND_BCM2708_SOC_IQAUDIO_CODEC=m
+CONFIG_SND_BCM2708_SOC_IQAUDIO_DAC=m
+CONFIG_SND_BCM2708_SOC_IQAUDIO_DIGI=m
+CONFIG_SND_BCM2708_SOC_I_SABRE_Q2M=m
+CONFIG_SND_BCM2708_SOC_ADAU1977_ADC=m
+CONFIG_SND_AUDIOINJECTOR_PI_SOUNDCARD=m
+CONFIG_SND_AUDIOINJECTOR_OCTO_SOUNDCARD=m
+CONFIG_SND_AUDIOINJECTOR_ISOLATED_SOUNDCARD=m
+CONFIG_SND_AUDIOSENSE_PI=m
+CONFIG_SND_DIGIDAC1_SOUNDCARD=m
+CONFIG_SND_BCM2708_SOC_DIONAUDIO_LOCO=m
+CONFIG_SND_BCM2708_SOC_DIONAUDIO_LOCO_V2=m
+CONFIG_SND_BCM2708_SOC_ALLO_PIANO_DAC=m
+CONFIG_SND_BCM2708_SOC_ALLO_PIANO_DAC_PLUS=m
+CONFIG_SND_BCM2708_SOC_ALLO_BOSS_DAC=m
+CONFIG_SND_BCM2708_SOC_ALLO_BOSS2_DAC=m
+CONFIG_SND_BCM2708_SOC_ALLO_DIGIONE=m
+CONFIG_SND_BCM2708_SOC_ALLO_KATANA_DAC=m
+CONFIG_SND_BCM2708_SOC_FE_PI_AUDIO=m
+CONFIG_SND_PISOUND=m
+CONFIG_SND_SOC_AD193X_SPI=m
+CONFIG_SND_SOC_AD193X_I2C=m
+CONFIG_SND_SOC_ADAU1701=m
+CONFIG_SND_SOC_ADAU7002=m
+CONFIG_SND_SOC_AK4554=m
+CONFIG_SND_SOC_CS4265=m
+CONFIG_SND_SOC_ICS43432=m
+CONFIG_SND_SOC_MA120X0P=m
+CONFIG_SND_SOC_MAX98357A=m
+CONFIG_SND_SOC_SPDIF=m
+CONFIG_SND_SOC_WM8804_I2C=m
+CONFIG_SND_SOC_WM8960=m
+CONFIG_SND_SIMPLE_CARD=m
+CONFIG_HID_BATTERY_STRENGTH=y
+CONFIG_HIDRAW=y
+CONFIG_UHID=m
+CONFIG_HID_A4TECH=m
+CONFIG_HID_ACRUX=m
+CONFIG_HID_APPLE=m
+CONFIG_HID_ASUS=m
+CONFIG_HID_BELKIN=m
+CONFIG_HID_BETOP_FF=m
+CONFIG_HID_BIGBEN_FF=m
+CONFIG_HID_CHERRY=m
+CONFIG_HID_CHICONY=m
+CONFIG_HID_CYPRESS=m
+CONFIG_HID_DRAGONRISE=m
+CONFIG_HID_EMS_FF=m
+CONFIG_HID_ELECOM=m
+CONFIG_HID_ELO=m
+CONFIG_HID_EZKEY=m
+CONFIG_HID_GEMBIRD=m
+CONFIG_HID_HOLTEK=m
+CONFIG_HID_KEYTOUCH=m
+CONFIG_HID_KYE=m
+CONFIG_HID_UCLOGIC=m
+CONFIG_HID_WALTOP=m
+CONFIG_HID_GYRATION=m
+CONFIG_HID_TWINHAN=m
+CONFIG_HID_KENSINGTON=m
+CONFIG_HID_LCPOWER=m
+CONFIG_HID_LOGITECH=m
+CONFIG_HID_LOGITECH_DJ=m
+CONFIG_LOGITECH_FF=y
+CONFIG_LOGIRUMBLEPAD2_FF=y
+CONFIG_LOGIG940_FF=y
+CONFIG_HID_MAGICMOUSE=m
+CONFIG_HID_MICROSOFT=m
+CONFIG_HID_MONTEREY=m
+CONFIG_HID_MULTITOUCH=m
+CONFIG_HID_NTRIG=m
+CONFIG_HID_ORTEK=m
+CONFIG_HID_PANTHERLORD=m
+CONFIG_HID_PETALYNX=m
+CONFIG_HID_PICOLCD=m
+CONFIG_HID_ROCCAT=m
+CONFIG_HID_SAMSUNG=m
+CONFIG_HID_SONY=m
+CONFIG_SONY_FF=y
+CONFIG_HID_SPEEDLINK=m
+CONFIG_HID_STEAM=m
+CONFIG_HID_SUNPLUS=m
+CONFIG_HID_GREENASIA=m
+CONFIG_HID_SMARTJOYPLUS=m
+CONFIG_HID_TOPSEED=m
+CONFIG_HID_THINGM=m
+CONFIG_HID_THRUSTMASTER=m
+CONFIG_HID_WACOM=m
+CONFIG_HID_WIIMOTE=m
+CONFIG_HID_XINMO=m
+CONFIG_HID_ZEROPLUS=m
+CONFIG_HID_ZYDACRON=m
+CONFIG_HID_PID=y
+CONFIG_USB_HIDDEV=y
+CONFIG_I2C_HID=m
+CONFIG_USB=y
+CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
+CONFIG_USB_MON=m
+CONFIG_USB_XHCI_HCD=y
+CONFIG_USB_XHCI_PLATFORM=y
+CONFIG_USB_DWCOTG=y
+CONFIG_USB_PRINTER=m
+CONFIG_USB_TMC=m
+CONFIG_USB_STORAGE=y
+CONFIG_USB_STORAGE_REALTEK=m
+CONFIG_USB_STORAGE_DATAFAB=m
+CONFIG_USB_STORAGE_FREECOM=m
+CONFIG_USB_STORAGE_ISD200=m
+CONFIG_USB_STORAGE_USBAT=m
+CONFIG_USB_STORAGE_SDDR09=m
+CONFIG_USB_STORAGE_SDDR55=m
+CONFIG_USB_STORAGE_JUMPSHOT=m
+CONFIG_USB_STORAGE_ALAUDA=m
+CONFIG_USB_STORAGE_ONETOUCH=m
+CONFIG_USB_STORAGE_KARMA=m
+CONFIG_USB_STORAGE_CYPRESS_ATACB=m
+CONFIG_USB_STORAGE_ENE_UB6250=m
+CONFIG_USB_UAS=y
+CONFIG_USB_MDC800=m
+CONFIG_USB_MICROTEK=m
+CONFIG_USBIP_CORE=m
+CONFIG_USBIP_VHCI_HCD=m
+CONFIG_USBIP_HOST=m
+CONFIG_USBIP_VUDC=m
+CONFIG_USB_DWC2=m
+CONFIG_USB_SERIAL=m
+CONFIG_USB_SERIAL_GENERIC=y
+CONFIG_USB_SERIAL_AIRCABLE=m
+CONFIG_USB_SERIAL_ARK3116=m
+CONFIG_USB_SERIAL_BELKIN=m
+CONFIG_USB_SERIAL_CH341=m
+CONFIG_USB_SERIAL_WHITEHEAT=m
+CONFIG_USB_SERIAL_DIGI_ACCELEPORT=m
+CONFIG_USB_SERIAL_CP210X=m
+CONFIG_USB_SERIAL_CYPRESS_M8=m
+CONFIG_USB_SERIAL_EMPEG=m
+CONFIG_USB_SERIAL_FTDI_SIO=m
+CONFIG_USB_SERIAL_VISOR=m
+CONFIG_USB_SERIAL_IPAQ=m
+CONFIG_USB_SERIAL_IR=m
+CONFIG_USB_SERIAL_EDGEPORT=m
+CONFIG_USB_SERIAL_EDGEPORT_TI=m
+CONFIG_USB_SERIAL_F81232=m
+CONFIG_USB_SERIAL_GARMIN=m
+CONFIG_USB_SERIAL_IPW=m
+CONFIG_USB_SERIAL_IUU=m
+CONFIG_USB_SERIAL_KEYSPAN_PDA=m
+CONFIG_USB_SERIAL_KEYSPAN=m
+CONFIG_USB_SERIAL_KLSI=m
+CONFIG_USB_SERIAL_KOBIL_SCT=m
+CONFIG_USB_SERIAL_MCT_U232=m
+CONFIG_USB_SERIAL_METRO=m
+CONFIG_USB_SERIAL_MOS7720=m
+CONFIG_USB_SERIAL_MOS7840=m
+CONFIG_USB_SERIAL_NAVMAN=m
+CONFIG_USB_SERIAL_PL2303=m
+CONFIG_USB_SERIAL_OTI6858=m
+CONFIG_USB_SERIAL_QCAUX=m
+CONFIG_USB_SERIAL_QUALCOMM=m
+CONFIG_USB_SERIAL_SPCP8X5=m
+CONFIG_USB_SERIAL_SAFE=m
+CONFIG_USB_SERIAL_SIERRAWIRELESS=m
+CONFIG_USB_SERIAL_SYMBOL=m
+CONFIG_USB_SERIAL_TI=m
+CONFIG_USB_SERIAL_CYBERJACK=m
+CONFIG_USB_SERIAL_XIRCOM=m
+CONFIG_USB_SERIAL_OPTION=m
+CONFIG_USB_SERIAL_OMNINET=m
+CONFIG_USB_SERIAL_OPTICON=m
+CONFIG_USB_SERIAL_XSENS_MT=m
+CONFIG_USB_SERIAL_WISHBONE=m
+CONFIG_USB_SERIAL_SSU100=m
+CONFIG_USB_SERIAL_QT2=m
+CONFIG_USB_SERIAL_DEBUG=m
+CONFIG_USB_EMI62=m
+CONFIG_USB_EMI26=m
+CONFIG_USB_ADUTUX=m
+CONFIG_USB_SEVSEG=m
+CONFIG_USB_LEGOTOWER=m
+CONFIG_USB_LCD=m
+CONFIG_USB_CYPRESS_CY7C63=m
+CONFIG_USB_CYTHERM=m
+CONFIG_USB_IDMOUSE=m
+CONFIG_USB_FTDI_ELAN=m
+CONFIG_USB_APPLEDISPLAY=m
+CONFIG_USB_LD=m
+CONFIG_USB_TRANCEVIBRATOR=m
+CONFIG_USB_IOWARRIOR=m
+CONFIG_USB_TEST=m
+CONFIG_USB_ISIGHTFW=m
+CONFIG_USB_YUREX=m
+CONFIG_USB_ATM=m
+CONFIG_USB_SPEEDTOUCH=m
+CONFIG_USB_CXACRU=m
+CONFIG_USB_UEAGLEATM=m
+CONFIG_USB_XUSBATM=m
+CONFIG_NOP_USB_XCEIV=y
+CONFIG_USB_GADGET=y
+CONFIG_USB_CONFIGFS=m
+CONFIG_USB_CONFIGFS_SERIAL=y
+CONFIG_USB_CONFIGFS_ACM=y
+CONFIG_USB_CONFIGFS_OBEX=y
+CONFIG_USB_CONFIGFS_NCM=y
+CONFIG_USB_CONFIGFS_ECM=y
+CONFIG_USB_CONFIGFS_ECM_SUBSET=y
+CONFIG_USB_CONFIGFS_RNDIS=y
+CONFIG_USB_CONFIGFS_EEM=y
+CONFIG_USB_CONFIGFS_MASS_STORAGE=y
+CONFIG_USB_CONFIGFS_F_LB_SS=y
+CONFIG_USB_CONFIGFS_F_FS=y
+CONFIG_USB_CONFIGFS_F_UAC1=y
+CONFIG_USB_CONFIGFS_F_UAC2=y
+CONFIG_USB_CONFIGFS_F_MIDI=y
+CONFIG_USB_CONFIGFS_F_HID=y
+CONFIG_USB_CONFIGFS_F_UVC=y
+CONFIG_USB_CONFIGFS_F_PRINTER=y
+CONFIG_USB_ZERO=m
+CONFIG_USB_AUDIO=m
+CONFIG_USB_ETH=m
+CONFIG_USB_GADGETFS=m
+CONFIG_USB_MASS_STORAGE=m
+CONFIG_USB_G_SERIAL=m
+CONFIG_USB_MIDI_GADGET=m
+CONFIG_USB_G_PRINTER=m
+CONFIG_USB_CDC_COMPOSITE=m
+CONFIG_USB_G_ACM_MS=m
+CONFIG_USB_G_MULTI=m
+CONFIG_USB_G_HID=m
+CONFIG_USB_G_WEBCAM=m
+CONFIG_MMC=y
+CONFIG_MMC_BLOCK_MINORS=32
+CONFIG_MMC_BCM2835_MMC=y
+CONFIG_MMC_BCM2835_DMA=y
+CONFIG_MMC_BCM2835_SDHOST=y
+CONFIG_MMC_SDHCI=y
+CONFIG_MMC_SDHCI_PLTFM=y
+CONFIG_MMC_SDHCI_IPROC=y
+CONFIG_MMC_SPI=m
+CONFIG_LEDS_CLASS=y
+CONFIG_LEDS_PCA9532=m
+CONFIG_LEDS_GPIO=y
+CONFIG_LEDS_PCA955X=m
+CONFIG_LEDS_PCA963X=m
+CONFIG_LEDS_IS31FL32XX=m
+CONFIG_LEDS_TRIGGER_TIMER=y
+CONFIG_LEDS_TRIGGER_ONESHOT=y
+CONFIG_LEDS_TRIGGER_HEARTBEAT=y
+CONFIG_LEDS_TRIGGER_BACKLIGHT=y
+CONFIG_LEDS_TRIGGER_CPU=y
+CONFIG_LEDS_TRIGGER_GPIO=y
+CONFIG_LEDS_TRIGGER_DEFAULT_ON=y
+CONFIG_LEDS_TRIGGER_TRANSIENT=m
+CONFIG_LEDS_TRIGGER_CAMERA=m
+CONFIG_LEDS_TRIGGER_INPUT=y
+CONFIG_LEDS_TRIGGER_PANIC=y
+CONFIG_LEDS_TRIGGER_NETDEV=m
+CONFIG_LEDS_TRIGGER_ACTPWR=y
+CONFIG_ACCESSIBILITY=y
+CONFIG_SPEAKUP=m
+CONFIG_SPEAKUP_SYNTH_SOFT=m
+CONFIG_RTC_CLASS=y
+CONFIG_RTC_DRV_ABX80X=m
+CONFIG_RTC_DRV_DS1307=m
+CONFIG_RTC_DRV_DS1374=m
+CONFIG_RTC_DRV_DS1672=m
+CONFIG_RTC_DRV_MAX6900=m
+CONFIG_RTC_DRV_RS5C372=m
+CONFIG_RTC_DRV_ISL1208=m
+CONFIG_RTC_DRV_ISL12022=m
+CONFIG_RTC_DRV_X1205=m
+CONFIG_RTC_DRV_PCF8523=m
+CONFIG_RTC_DRV_PCF85063=m
+CONFIG_RTC_DRV_PCF85363=m
+CONFIG_RTC_DRV_PCF8563=m
+CONFIG_RTC_DRV_PCF8583=m
+CONFIG_RTC_DRV_M41T80=m
+CONFIG_RTC_DRV_BQ32K=m
+CONFIG_RTC_DRV_S35390A=m
+CONFIG_RTC_DRV_FM3130=m
+CONFIG_RTC_DRV_RX8581=m
+CONFIG_RTC_DRV_RX8025=m
+CONFIG_RTC_DRV_EM3027=m
+CONFIG_RTC_DRV_RV3028=m
+CONFIG_RTC_DRV_SD3078=m
+CONFIG_RTC_DRV_M41T93=m
+CONFIG_RTC_DRV_M41T94=m
+CONFIG_RTC_DRV_DS1302=m
+CONFIG_RTC_DRV_DS1305=m
+CONFIG_RTC_DRV_DS1390=m
+CONFIG_RTC_DRV_R9701=m
+CONFIG_RTC_DRV_RX4581=m
+CONFIG_RTC_DRV_RS5C348=m
+CONFIG_RTC_DRV_MAX6902=m
+CONFIG_RTC_DRV_PCF2123=m
+CONFIG_RTC_DRV_DS3232=m
+CONFIG_RTC_DRV_PCF2127=m
+CONFIG_RTC_DRV_RV3029C2=m
+CONFIG_DMADEVICES=y
+CONFIG_DMA_BCM2835=y
+CONFIG_DMA_BCM2708=y
+CONFIG_DMABUF_HEAPS=y
+CONFIG_DMABUF_HEAPS_SYSTEM=y
+CONFIG_DMABUF_HEAPS_CMA=y
+CONFIG_AUXDISPLAY=y
+CONFIG_HD44780=m
+CONFIG_UIO=m
+CONFIG_UIO_PDRV_GENIRQ=m
+CONFIG_VHOST_NET=m
+CONFIG_VHOST_CROSS_ENDIAN_LEGACY=y
+CONFIG_STAGING=y
+CONFIG_PRISM2_USB=m
+CONFIG_R8712U=m
+CONFIG_R8188EU=m
+CONFIG_VT6656=m
+CONFIG_STAGING_MEDIA=y
+CONFIG_VIDEO_RPIVID=m
+CONFIG_ASHMEM=y
+CONFIG_FB_TFT=m
+CONFIG_FB_TFT_AGM1264K_FL=m
+CONFIG_FB_TFT_BD663474=m
+CONFIG_FB_TFT_HX8340BN=m
+CONFIG_FB_TFT_HX8347D=m
+CONFIG_FB_TFT_HX8353D=m
+CONFIG_FB_TFT_HX8357D=m
+CONFIG_FB_TFT_ILI9163=m
+CONFIG_FB_TFT_ILI9320=m
+CONFIG_FB_TFT_ILI9325=m
+CONFIG_FB_TFT_ILI9340=m
+CONFIG_FB_TFT_ILI9341=m
+CONFIG_FB_TFT_ILI9481=m
+CONFIG_FB_TFT_ILI9486=m
+CONFIG_FB_TFT_PCD8544=m
+CONFIG_FB_TFT_RA8875=m
+CONFIG_FB_TFT_S6D02A1=m
+CONFIG_FB_TFT_S6D1121=m
+CONFIG_FB_TFT_SH1106=m
+CONFIG_FB_TFT_SSD1289=m
+CONFIG_FB_TFT_SSD1306=m
+CONFIG_FB_TFT_SSD1331=m
+CONFIG_FB_TFT_SSD1351=m
+CONFIG_FB_TFT_ST7735R=m
+CONFIG_FB_TFT_ST7789V=m
+CONFIG_FB_TFT_TINYLCD=m
+CONFIG_FB_TFT_TLS8204=m
+CONFIG_FB_TFT_UC1701=m
+CONFIG_FB_TFT_UPD161704=m
+CONFIG_FB_TFT_WATTEROTT=m
+CONFIG_BCM2835_VCHIQ=y
+CONFIG_SND_BCM2835=m
+CONFIG_VIDEO_BCM2835=m
+CONFIG_VIDEO_CODEC_BCM2835=m
+CONFIG_VIDEO_ISP_BCM2835=m
+CONFIG_CLK_RASPBERRYPI=y
+CONFIG_MAILBOX=y
+CONFIG_BCM2835_MBOX=y
+# CONFIG_IOMMU_SUPPORT is not set
+CONFIG_RASPBERRYPI_POWER=y
+CONFIG_EXTCON_ARIZONA=m
+CONFIG_IIO=m
+CONFIG_IIO_BUFFER_CB=m
+CONFIG_MCP320X=m
+CONFIG_MCP3422=m
+CONFIG_TI_ADS1015=m
+CONFIG_BME680=m
+CONFIG_CCS811=m
+CONFIG_SENSIRION_SGP30=m
+CONFIG_SPS30=m
+CONFIG_DHT11=m
+CONFIG_HDC100X=m
+CONFIG_HTU21=m
+CONFIG_INV_MPU6050_I2C=m
+CONFIG_APDS9960=m
+CONFIG_BH1750=m
+CONFIG_TSL4531=m
+CONFIG_VEML6070=m
+CONFIG_BMP280=m
+CONFIG_MAXIM_THERMOCOUPLE=m
+CONFIG_MAX31856=m
+CONFIG_PWM_BCM2835=m
+CONFIG_PWM_PCA9685=m
+CONFIG_RPI_AXIPERF=m
+CONFIG_ANDROID=y
+CONFIG_ANDROID_BINDER_IPC=y
+CONFIG_ANDROID_BINDERFS=y
+CONFIG_NVMEM_RMEM=m
+CONFIG_EXT4_FS=y
+CONFIG_EXT4_FS_POSIX_ACL=y
+CONFIG_EXT4_FS_SECURITY=y
+CONFIG_REISERFS_FS=m
+CONFIG_REISERFS_FS_XATTR=y
+CONFIG_REISERFS_FS_POSIX_ACL=y
+CONFIG_REISERFS_FS_SECURITY=y
+CONFIG_JFS_FS=m
+CONFIG_JFS_POSIX_ACL=y
+CONFIG_JFS_SECURITY=y
+CONFIG_JFS_STATISTICS=y
+CONFIG_XFS_FS=m
+CONFIG_XFS_QUOTA=y
+CONFIG_XFS_POSIX_ACL=y
+CONFIG_XFS_RT=y
+CONFIG_GFS2_FS=m
+CONFIG_OCFS2_FS=m
+CONFIG_BTRFS_FS=m
+CONFIG_BTRFS_FS_POSIX_ACL=y
+CONFIG_NILFS2_FS=m
+CONFIG_F2FS_FS=y
+CONFIG_FS_ENCRYPTION=y
+CONFIG_FANOTIFY=y
+CONFIG_QFMT_V1=m
+CONFIG_QFMT_V2=m
+CONFIG_AUTOFS4_FS=y
+CONFIG_FUSE_FS=m
+CONFIG_CUSE=m
+CONFIG_OVERLAY_FS=m
+CONFIG_FSCACHE=y
+CONFIG_FSCACHE_STATS=y
+CONFIG_FSCACHE_HISTOGRAM=y
+CONFIG_CACHEFILES=y
+CONFIG_ISO9660_FS=m
+CONFIG_JOLIET=y
+CONFIG_ZISOFS=y
+CONFIG_UDF_FS=m
+CONFIG_MSDOS_FS=y
+CONFIG_VFAT_FS=y
+CONFIG_FAT_DEFAULT_IOCHARSET="ascii"
+CONFIG_EXFAT_FS=m
+CONFIG_NTFS_FS=m
+CONFIG_NTFS_RW=y
+CONFIG_TMPFS=y
+CONFIG_TMPFS_POSIX_ACL=y
+CONFIG_ECRYPT_FS=m
+CONFIG_HFS_FS=m
+CONFIG_HFSPLUS_FS=m
+CONFIG_JFFS2_FS=m
+CONFIG_JFFS2_SUMMARY=y
+CONFIG_UBIFS_FS=m
+CONFIG_SQUASHFS=m
+CONFIG_SQUASHFS_XATTR=y
+CONFIG_SQUASHFS_LZO=y
+CONFIG_SQUASHFS_XZ=y
+CONFIG_NFS_FS=y
+CONFIG_NFS_V3_ACL=y
+CONFIG_NFS_V4=y
+CONFIG_NFS_SWAP=y
+CONFIG_NFS_V4_1=y
+CONFIG_NFS_V4_2=y
+CONFIG_ROOT_NFS=y
+CONFIG_NFS_FSCACHE=y
+CONFIG_NFSD=m
+CONFIG_NFSD_V3_ACL=y
+CONFIG_NFSD_V4=y
+CONFIG_CIFS=m
+CONFIG_CIFS_WEAK_PW_HASH=y
+CONFIG_CIFS_UPCALL=y
+CONFIG_CIFS_XATTR=y
+CONFIG_CIFS_POSIX=y
+CONFIG_CIFS_DFS_UPCALL=y
+CONFIG_CIFS_FSCACHE=y
+CONFIG_9P_FS=m
+CONFIG_9P_FS_POSIX_ACL=y
+CONFIG_NLS_DEFAULT="utf8"
+CONFIG_NLS_CODEPAGE_437=y
+CONFIG_NLS_CODEPAGE_737=m
+CONFIG_NLS_CODEPAGE_775=m
+CONFIG_NLS_CODEPAGE_850=m
+CONFIG_NLS_CODEPAGE_852=m
+CONFIG_NLS_CODEPAGE_855=m
+CONFIG_NLS_CODEPAGE_857=m
+CONFIG_NLS_CODEPAGE_860=m
+CONFIG_NLS_CODEPAGE_861=m
+CONFIG_NLS_CODEPAGE_862=m
+CONFIG_NLS_CODEPAGE_863=m
+CONFIG_NLS_CODEPAGE_864=m
+CONFIG_NLS_CODEPAGE_865=m
+CONFIG_NLS_CODEPAGE_866=m
+CONFIG_NLS_CODEPAGE_869=m
+CONFIG_NLS_CODEPAGE_936=m
+CONFIG_NLS_CODEPAGE_950=m
+CONFIG_NLS_CODEPAGE_932=m
+CONFIG_NLS_CODEPAGE_949=m
+CONFIG_NLS_CODEPAGE_874=m
+CONFIG_NLS_ISO8859_8=m
+CONFIG_NLS_CODEPAGE_1250=m
+CONFIG_NLS_CODEPAGE_1251=m
+CONFIG_NLS_ASCII=y
+CONFIG_NLS_ISO8859_1=m
+CONFIG_NLS_ISO8859_2=m
+CONFIG_NLS_ISO8859_3=m
+CONFIG_NLS_ISO8859_4=m
+CONFIG_NLS_ISO8859_5=m
+CONFIG_NLS_ISO8859_6=m
+CONFIG_NLS_ISO8859_7=m
+CONFIG_NLS_ISO8859_9=m
+CONFIG_NLS_ISO8859_13=m
+CONFIG_NLS_ISO8859_14=m
+CONFIG_NLS_ISO8859_15=m
+CONFIG_NLS_KOI8_R=m
+CONFIG_NLS_KOI8_U=m
+CONFIG_DLM=m
+CONFIG_SECURITY=y
+CONFIG_SECURITY_APPARMOR=y
+CONFIG_LSM=""
+CONFIG_CRYPTO_USER=m
+CONFIG_CRYPTO_CHACHA20POLY1305=m
+CONFIG_CRYPTO_ADIANTUM=m
+CONFIG_CRYPTO_XCBC=m
+CONFIG_CRYPTO_TGR192=m
+CONFIG_CRYPTO_WP512=m
+CONFIG_CRYPTO_CAST5=m
+CONFIG_CRYPTO_DES=y
+CONFIG_CRYPTO_LZ4=m
+CONFIG_CRYPTO_USER_API_HASH=m
+CONFIG_CRYPTO_USER_API_SKCIPHER=m
+CONFIG_CRYPTO_USER_API_RNG=m
+CONFIG_CRYPTO_USER_API_AEAD=m
+# CONFIG_CRYPTO_HW is not set
+CONFIG_CRC_ITU_T=y
+CONFIG_LIBCRC32C=y
+CONFIG_DMA_CMA=y
+CONFIG_CMA_SIZE_MBYTES=5
+CONFIG_PRINTK_TIME=y
+CONFIG_BOOT_PRINTK_DELAY=y
+CONFIG_KGDB=y
+CONFIG_KGDB_KDB=y
+CONFIG_KDB_KEYBOARD=y
+CONFIG_DEBUG_MEMORY_INIT=y
+CONFIG_DETECT_HUNG_TASK=y
+CONFIG_LATENCYTOP=y
+CONFIG_FUNCTION_PROFILER=y
+CONFIG_STACK_TRACER=y
+CONFIG_IRQSOFF_TRACER=y
+CONFIG_SCHED_TRACER=y
+CONFIG_BLK_DEV_IO_TRACE=y
+# CONFIG_UPROBE_EVENTS is not set
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm64/configs/bcmrpi3_defconfig linux-5.10.52-v7l+/arch/arm64/configs/bcmrpi3_defconfig
--- linux-5.10.52-orig/arch/arm64/configs/bcmrpi3_defconfig	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/arch/arm64/configs/bcmrpi3_defconfig	2021-07-25 16:45:36.088791798 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+CONFIG_LOCALVERSION="-v8"
+# CONFIG_LOCALVERSION_AUTO is not set
+CONFIG_SYSVIPC=y
+CONFIG_POSIX_MQUEUE=y
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_PREEMPT=y
+CONFIG_BSD_PROCESS_ACCT=y
+CONFIG_BSD_PROCESS_ACCT_V3=y
+CONFIG_TASKSTATS=y
+CONFIG_TASK_DELAY_ACCT=y
+CONFIG_TASK_XACCT=y
+CONFIG_TASK_IO_ACCOUNTING=y
+CONFIG_IKCONFIG=m
+CONFIG_IKCONFIG_PROC=y
+CONFIG_MEMCG=y
+CONFIG_BLK_CGROUP=y
+CONFIG_CFS_BANDWIDTH=y
+CONFIG_CGROUP_PIDS=y
+CONFIG_CGROUP_FREEZER=y
+CONFIG_CPUSETS=y
+CONFIG_CGROUP_DEVICE=y
+CONFIG_CGROUP_CPUACCT=y
+CONFIG_CGROUP_PERF=y
+CONFIG_CGROUP_BPF=y
+CONFIG_NAMESPACES=y
+CONFIG_USER_NS=y
+CONFIG_SCHED_AUTOGROUP=y
+CONFIG_BLK_DEV_INITRD=y
+CONFIG_BPF_SYSCALL=y
+CONFIG_EMBEDDED=y
+# CONFIG_COMPAT_BRK is not set
+CONFIG_PROFILING=y
+CONFIG_ARCH_BCM2835=y
+# CONFIG_CAVIUM_ERRATUM_22375 is not set
+# CONFIG_CAVIUM_ERRATUM_23154 is not set
+# CONFIG_CAVIUM_ERRATUM_27456 is not set
+CONFIG_SCHED_MC=y
+CONFIG_NR_CPUS=4
+CONFIG_HZ_1000=y
+CONFIG_COMPAT=y
+CONFIG_ARMV8_DEPRECATED=y
+CONFIG_SWP_EMULATION=y
+CONFIG_CP15_BARRIER_EMULATION=y
+CONFIG_SETEND_EMULATION=y
+CONFIG_RANDOMIZE_BASE=y
+CONFIG_CMDLINE="console=ttyAMA0,115200 kgdboc=ttyAMA0,115200 root=/dev/mmcblk0p2 rootfstype=ext4 rootwait"
+# CONFIG_SUSPEND is not set
+CONFIG_PM=y
+CONFIG_CPU_IDLE=y
+CONFIG_ARM_CPUIDLE=y
+CONFIG_CPU_FREQ=y
+CONFIG_CPU_FREQ_STAT=y
+CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE=y
+CONFIG_CPU_FREQ_GOV_PERFORMANCE=y
+CONFIG_CPU_FREQ_GOV_USERSPACE=y
+CONFIG_CPU_FREQ_GOV_ONDEMAND=y
+CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y
+CONFIG_CPU_FREQ_GOV_SCHEDUTIL=y
+CONFIG_CPUFREQ_DT=y
+CONFIG_ARM_RASPBERRYPI_CPUFREQ=y
+CONFIG_RASPBERRYPI_FIRMWARE=y
+CONFIG_CRYPTO_AES_ARM64_BS=m
+CONFIG_JUMP_LABEL=y
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+CONFIG_MODVERSIONS=y
+CONFIG_MODULE_SRCVERSION_ALL=y
+CONFIG_PARTITION_ADVANCED=y
+CONFIG_MAC_PARTITION=y
+CONFIG_BINFMT_MISC=y
+CONFIG_CLEANCACHE=y
+CONFIG_FRONTSWAP=y
+CONFIG_CMA=y
+CONFIG_ZSWAP=y
+CONFIG_Z3FOLD=m
+CONFIG_ZSMALLOC=m
+CONFIG_NET=y
+CONFIG_PACKET=y
+CONFIG_UNIX=y
+CONFIG_XFRM_USER=y
+CONFIG_NET_KEY=m
+CONFIG_INET=y
+CONFIG_IP_MULTICAST=y
+CONFIG_IP_ADVANCED_ROUTER=y
+CONFIG_IP_MULTIPLE_TABLES=y
+CONFIG_IP_ROUTE_MULTIPATH=y
+CONFIG_IP_ROUTE_VERBOSE=y
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+CONFIG_IP_PNP_RARP=y
+CONFIG_NET_IPIP=m
+CONFIG_NET_IPGRE_DEMUX=m
+CONFIG_NET_IPGRE=m
+CONFIG_IP_MROUTE=y
+CONFIG_IP_MROUTE_MULTIPLE_TABLES=y
+CONFIG_IP_PIMSM_V1=y
+CONFIG_IP_PIMSM_V2=y
+CONFIG_NET_FOU=m
+CONFIG_INET_AH=m
+CONFIG_INET_ESP=m
+CONFIG_INET_IPCOMP=m
+CONFIG_INET_DIAG=m
+CONFIG_TCP_CONG_ADVANCED=y
+CONFIG_TCP_CONG_BBR=m
+CONFIG_IPV6=m
+CONFIG_IPV6_ROUTER_PREF=y
+CONFIG_INET6_AH=m
+CONFIG_INET6_ESP=m
+CONFIG_INET6_ESP_OFFLOAD=m
+CONFIG_INET6_IPCOMP=m
+CONFIG_IPV6_ILA=m
+CONFIG_IPV6_VTI=m
+CONFIG_IPV6_GRE=m
+CONFIG_IPV6_MULTIPLE_TABLES=y
+CONFIG_IPV6_SUBTREES=y
+CONFIG_IPV6_MROUTE=y
+CONFIG_IPV6_MROUTE_MULTIPLE_TABLES=y
+CONFIG_IPV6_PIMSM_V2=y
+CONFIG_NETFILTER=y
+CONFIG_BRIDGE_NETFILTER=m
+CONFIG_NF_CONNTRACK=m
+CONFIG_NF_LOG_NETDEV=m
+CONFIG_NF_CONNTRACK_ZONES=y
+CONFIG_NF_CONNTRACK_EVENTS=y
+CONFIG_NF_CONNTRACK_TIMESTAMP=y
+CONFIG_NF_CONNTRACK_AMANDA=m
+CONFIG_NF_CONNTRACK_FTP=m
+CONFIG_NF_CONNTRACK_H323=m
+CONFIG_NF_CONNTRACK_IRC=m
+CONFIG_NF_CONNTRACK_NETBIOS_NS=m
+CONFIG_NF_CONNTRACK_SNMP=m
+CONFIG_NF_CONNTRACK_PPTP=m
+CONFIG_NF_CONNTRACK_SANE=m
+CONFIG_NF_CONNTRACK_SIP=m
+CONFIG_NF_CONNTRACK_TFTP=m
+CONFIG_NF_CT_NETLINK=m
+CONFIG_NF_TABLES=m
+CONFIG_NF_TABLES_INET=y
+CONFIG_NF_TABLES_NETDEV=y
+CONFIG_NFT_NUMGEN=m
+CONFIG_NFT_CT=m
+CONFIG_NFT_FLOW_OFFLOAD=m
+CONFIG_NFT_COUNTER=m
+CONFIG_NFT_CONNLIMIT=m
+CONFIG_NFT_LOG=m
+CONFIG_NFT_LIMIT=m
+CONFIG_NFT_MASQ=m
+CONFIG_NFT_REDIR=m
+CONFIG_NFT_NAT=m
+CONFIG_NFT_TUNNEL=m
+CONFIG_NFT_OBJREF=m
+CONFIG_NFT_QUEUE=m
+CONFIG_NFT_QUOTA=m
+CONFIG_NFT_REJECT=m
+CONFIG_NFT_COMPAT=m
+CONFIG_NFT_HASH=m
+CONFIG_NFT_FIB_INET=m
+CONFIG_NFT_SOCKET=m
+CONFIG_NFT_OSF=m
+CONFIG_NFT_TPROXY=m
+CONFIG_NFT_DUP_NETDEV=m
+CONFIG_NFT_FWD_NETDEV=m
+CONFIG_NFT_FIB_NETDEV=m
+CONFIG_NF_FLOW_TABLE_INET=m
+CONFIG_NF_FLOW_TABLE=m
+CONFIG_NETFILTER_XT_SET=m
+CONFIG_NETFILTER_XT_TARGET_CHECKSUM=m
+CONFIG_NETFILTER_XT_TARGET_CLASSIFY=m
+CONFIG_NETFILTER_XT_TARGET_CONNMARK=m
+CONFIG_NETFILTER_XT_TARGET_DSCP=m
+CONFIG_NETFILTER_XT_TARGET_HMARK=m
+CONFIG_NETFILTER_XT_TARGET_IDLETIMER=m
+CONFIG_NETFILTER_XT_TARGET_LED=m
+CONFIG_NETFILTER_XT_TARGET_LOG=m
+CONFIG_NETFILTER_XT_TARGET_MARK=m
+CONFIG_NETFILTER_XT_TARGET_NFLOG=m
+CONFIG_NETFILTER_XT_TARGET_NFQUEUE=m
+CONFIG_NETFILTER_XT_TARGET_NOTRACK=m
+CONFIG_NETFILTER_XT_TARGET_TEE=m
+CONFIG_NETFILTER_XT_TARGET_TPROXY=m
+CONFIG_NETFILTER_XT_TARGET_TRACE=m
+CONFIG_NETFILTER_XT_TARGET_TCPMSS=m
+CONFIG_NETFILTER_XT_TARGET_TCPOPTSTRIP=m
+CONFIG_NETFILTER_XT_MATCH_ADDRTYPE=m
+CONFIG_NETFILTER_XT_MATCH_BPF=m
+CONFIG_NETFILTER_XT_MATCH_CLUSTER=m
+CONFIG_NETFILTER_XT_MATCH_COMMENT=m
+CONFIG_NETFILTER_XT_MATCH_CONNBYTES=m
+CONFIG_NETFILTER_XT_MATCH_CONNLABEL=m
+CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=m
+CONFIG_NETFILTER_XT_MATCH_CONNMARK=m
+CONFIG_NETFILTER_XT_MATCH_CONNTRACK=m
+CONFIG_NETFILTER_XT_MATCH_CPU=m
+CONFIG_NETFILTER_XT_MATCH_DCCP=m
+CONFIG_NETFILTER_XT_MATCH_DEVGROUP=m
+CONFIG_NETFILTER_XT_MATCH_DSCP=m
+CONFIG_NETFILTER_XT_MATCH_ESP=m
+CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=m
+CONFIG_NETFILTER_XT_MATCH_HELPER=m
+CONFIG_NETFILTER_XT_MATCH_IPRANGE=m
+CONFIG_NETFILTER_XT_MATCH_IPVS=m
+CONFIG_NETFILTER_XT_MATCH_LENGTH=m
+CONFIG_NETFILTER_XT_MATCH_LIMIT=m
+CONFIG_NETFILTER_XT_MATCH_MAC=m
+CONFIG_NETFILTER_XT_MATCH_MARK=m
+CONFIG_NETFILTER_XT_MATCH_MULTIPORT=m
+CONFIG_NETFILTER_XT_MATCH_NFACCT=m
+CONFIG_NETFILTER_XT_MATCH_OSF=m
+CONFIG_NETFILTER_XT_MATCH_OWNER=m
+CONFIG_NETFILTER_XT_MATCH_POLICY=m
+CONFIG_NETFILTER_XT_MATCH_PHYSDEV=m
+CONFIG_NETFILTER_XT_MATCH_PKTTYPE=m
+CONFIG_NETFILTER_XT_MATCH_QUOTA=m
+CONFIG_NETFILTER_XT_MATCH_RATEEST=m
+CONFIG_NETFILTER_XT_MATCH_REALM=m
+CONFIG_NETFILTER_XT_MATCH_RECENT=m
+CONFIG_NETFILTER_XT_MATCH_SOCKET=m
+CONFIG_NETFILTER_XT_MATCH_STATE=m
+CONFIG_NETFILTER_XT_MATCH_STATISTIC=m
+CONFIG_NETFILTER_XT_MATCH_STRING=m
+CONFIG_NETFILTER_XT_MATCH_TCPMSS=m
+CONFIG_NETFILTER_XT_MATCH_TIME=m
+CONFIG_NETFILTER_XT_MATCH_U32=m
+CONFIG_IP_SET=m
+CONFIG_IP_SET_BITMAP_IP=m
+CONFIG_IP_SET_BITMAP_IPMAC=m
+CONFIG_IP_SET_BITMAP_PORT=m
+CONFIG_IP_SET_HASH_IP=m
+CONFIG_IP_SET_HASH_IPPORT=m
+CONFIG_IP_SET_HASH_IPPORTIP=m
+CONFIG_IP_SET_HASH_IPPORTNET=m
+CONFIG_IP_SET_HASH_NET=m
+CONFIG_IP_SET_HASH_NETPORT=m
+CONFIG_IP_SET_HASH_NETIFACE=m
+CONFIG_IP_SET_LIST_SET=m
+CONFIG_IP_VS=m
+CONFIG_IP_VS_PROTO_TCP=y
+CONFIG_IP_VS_PROTO_UDP=y
+CONFIG_IP_VS_PROTO_ESP=y
+CONFIG_IP_VS_PROTO_AH=y
+CONFIG_IP_VS_PROTO_SCTP=y
+CONFIG_IP_VS_RR=m
+CONFIG_IP_VS_WRR=m
+CONFIG_IP_VS_LC=m
+CONFIG_IP_VS_WLC=m
+CONFIG_IP_VS_LBLC=m
+CONFIG_IP_VS_LBLCR=m
+CONFIG_IP_VS_DH=m
+CONFIG_IP_VS_SH=m
+CONFIG_IP_VS_SED=m
+CONFIG_IP_VS_NQ=m
+CONFIG_IP_VS_FTP=m
+CONFIG_IP_VS_PE_SIP=m
+CONFIG_NFT_DUP_IPV4=m
+CONFIG_NFT_FIB_IPV4=m
+CONFIG_NF_TABLES_ARP=y
+CONFIG_NF_FLOW_TABLE_IPV4=m
+CONFIG_NF_LOG_ARP=m
+CONFIG_IP_NF_IPTABLES=m
+CONFIG_IP_NF_MATCH_AH=m
+CONFIG_IP_NF_MATCH_ECN=m
+CONFIG_IP_NF_MATCH_RPFILTER=m
+CONFIG_IP_NF_MATCH_TTL=m
+CONFIG_IP_NF_FILTER=m
+CONFIG_IP_NF_TARGET_REJECT=m
+CONFIG_IP_NF_NAT=m
+CONFIG_IP_NF_TARGET_MASQUERADE=m
+CONFIG_IP_NF_TARGET_NETMAP=m
+CONFIG_IP_NF_TARGET_REDIRECT=m
+CONFIG_IP_NF_MANGLE=m
+CONFIG_IP_NF_TARGET_CLUSTERIP=m
+CONFIG_IP_NF_TARGET_ECN=m
+CONFIG_IP_NF_TARGET_TTL=m
+CONFIG_IP_NF_RAW=m
+CONFIG_IP_NF_ARPTABLES=m
+CONFIG_IP_NF_ARPFILTER=m
+CONFIG_IP_NF_ARP_MANGLE=m
+CONFIG_NFT_DUP_IPV6=m
+CONFIG_NFT_FIB_IPV6=m
+CONFIG_NF_FLOW_TABLE_IPV6=m
+CONFIG_IP6_NF_IPTABLES=m
+CONFIG_IP6_NF_MATCH_AH=m
+CONFIG_IP6_NF_MATCH_EUI64=m
+CONFIG_IP6_NF_MATCH_FRAG=m
+CONFIG_IP6_NF_MATCH_OPTS=m
+CONFIG_IP6_NF_MATCH_HL=m
+CONFIG_IP6_NF_MATCH_IPV6HEADER=m
+CONFIG_IP6_NF_MATCH_MH=m
+CONFIG_IP6_NF_MATCH_RPFILTER=m
+CONFIG_IP6_NF_MATCH_RT=m
+CONFIG_IP6_NF_MATCH_SRH=m
+CONFIG_IP6_NF_TARGET_HL=m
+CONFIG_IP6_NF_FILTER=m
+CONFIG_IP6_NF_TARGET_REJECT=m
+CONFIG_IP6_NF_TARGET_SYNPROXY=m
+CONFIG_IP6_NF_MANGLE=m
+CONFIG_IP6_NF_RAW=m
+CONFIG_IP6_NF_SECURITY=m
+CONFIG_IP6_NF_NAT=m
+CONFIG_IP6_NF_TARGET_MASQUERADE=m
+CONFIG_IP6_NF_TARGET_NPT=m
+CONFIG_NF_TABLES_BRIDGE=m
+CONFIG_NFT_BRIDGE_REJECT=m
+CONFIG_NF_LOG_BRIDGE=m
+CONFIG_BRIDGE_NF_EBTABLES=m
+CONFIG_BRIDGE_EBT_BROUTE=m
+CONFIG_BRIDGE_EBT_T_FILTER=m
+CONFIG_BRIDGE_EBT_T_NAT=m
+CONFIG_BRIDGE_EBT_802_3=m
+CONFIG_BRIDGE_EBT_AMONG=m
+CONFIG_BRIDGE_EBT_ARP=m
+CONFIG_BRIDGE_EBT_IP=m
+CONFIG_BRIDGE_EBT_IP6=m
+CONFIG_BRIDGE_EBT_LIMIT=m
+CONFIG_BRIDGE_EBT_MARK=m
+CONFIG_BRIDGE_EBT_PKTTYPE=m
+CONFIG_BRIDGE_EBT_STP=m
+CONFIG_BRIDGE_EBT_VLAN=m
+CONFIG_BRIDGE_EBT_ARPREPLY=m
+CONFIG_BRIDGE_EBT_DNAT=m
+CONFIG_BRIDGE_EBT_MARK_T=m
+CONFIG_BRIDGE_EBT_REDIRECT=m
+CONFIG_BRIDGE_EBT_SNAT=m
+CONFIG_BRIDGE_EBT_LOG=m
+CONFIG_BRIDGE_EBT_NFLOG=m
+CONFIG_SCTP_COOKIE_HMAC_SHA1=y
+CONFIG_ATM=m
+CONFIG_L2TP=m
+CONFIG_L2TP_V3=y
+CONFIG_L2TP_IP=m
+CONFIG_L2TP_ETH=m
+CONFIG_BRIDGE=m
+CONFIG_VLAN_8021Q=m
+CONFIG_VLAN_8021Q_GVRP=y
+CONFIG_ATALK=m
+CONFIG_6LOWPAN=m
+CONFIG_IEEE802154=m
+CONFIG_IEEE802154_6LOWPAN=m
+CONFIG_MAC802154=m
+CONFIG_NET_SCHED=y
+CONFIG_NET_SCH_CBQ=m
+CONFIG_NET_SCH_HTB=m
+CONFIG_NET_SCH_HFSC=m
+CONFIG_NET_SCH_PRIO=m
+CONFIG_NET_SCH_MULTIQ=m
+CONFIG_NET_SCH_RED=m
+CONFIG_NET_SCH_SFB=m
+CONFIG_NET_SCH_SFQ=m
+CONFIG_NET_SCH_TEQL=m
+CONFIG_NET_SCH_TBF=m
+CONFIG_NET_SCH_GRED=m
+CONFIG_NET_SCH_DSMARK=m
+CONFIG_NET_SCH_NETEM=m
+CONFIG_NET_SCH_DRR=m
+CONFIG_NET_SCH_MQPRIO=m
+CONFIG_NET_SCH_CHOKE=m
+CONFIG_NET_SCH_QFQ=m
+CONFIG_NET_SCH_CODEL=m
+CONFIG_NET_SCH_FQ_CODEL=m
+CONFIG_NET_SCH_CAKE=m
+CONFIG_NET_SCH_FQ=m
+CONFIG_NET_SCH_INGRESS=m
+CONFIG_NET_SCH_PLUG=m
+CONFIG_NET_CLS_BASIC=m
+CONFIG_NET_CLS_TCINDEX=m
+CONFIG_NET_CLS_ROUTE4=m
+CONFIG_NET_CLS_FW=m
+CONFIG_NET_CLS_U32=m
+CONFIG_CLS_U32_MARK=y
+CONFIG_NET_CLS_RSVP=m
+CONFIG_NET_CLS_RSVP6=m
+CONFIG_NET_CLS_FLOW=m
+CONFIG_NET_CLS_CGROUP=m
+CONFIG_NET_EMATCH=y
+CONFIG_NET_EMATCH_CMP=m
+CONFIG_NET_EMATCH_NBYTE=m
+CONFIG_NET_EMATCH_U32=m
+CONFIG_NET_EMATCH_META=m
+CONFIG_NET_EMATCH_TEXT=m
+CONFIG_NET_EMATCH_IPSET=m
+CONFIG_NET_CLS_ACT=y
+CONFIG_NET_ACT_POLICE=m
+CONFIG_NET_ACT_GACT=m
+CONFIG_GACT_PROB=y
+CONFIG_NET_ACT_MIRRED=m
+CONFIG_NET_ACT_IPT=m
+CONFIG_NET_ACT_NAT=m
+CONFIG_NET_ACT_PEDIT=m
+CONFIG_NET_ACT_SIMP=m
+CONFIG_NET_ACT_SKBEDIT=m
+CONFIG_NET_ACT_CSUM=m
+CONFIG_BATMAN_ADV=m
+CONFIG_OPENVSWITCH=m
+CONFIG_CGROUP_NET_PRIO=y
+CONFIG_NET_PKTGEN=m
+CONFIG_HAMRADIO=y
+CONFIG_AX25=m
+CONFIG_NETROM=m
+CONFIG_ROSE=m
+CONFIG_MKISS=m
+CONFIG_6PACK=m
+CONFIG_BPQETHER=m
+CONFIG_BAYCOM_SER_FDX=m
+CONFIG_BAYCOM_SER_HDX=m
+CONFIG_YAM=m
+CONFIG_CAN=m
+CONFIG_CAN_J1939=m
+CONFIG_CAN_ISOTP=m
+CONFIG_CAN_VCAN=m
+CONFIG_CAN_MCP251X=m
+CONFIG_CAN_MCP251XFD=m
+CONFIG_CAN_EMS_USB=m
+CONFIG_BT=m
+CONFIG_BT_RFCOMM=m
+CONFIG_BT_RFCOMM_TTY=y
+CONFIG_BT_BNEP=m
+CONFIG_BT_BNEP_MC_FILTER=y
+CONFIG_BT_BNEP_PROTO_FILTER=y
+CONFIG_BT_HIDP=m
+CONFIG_BT_6LOWPAN=m
+CONFIG_BT_HCIBTUSB=m
+CONFIG_BT_HCIUART=m
+CONFIG_BT_HCIUART_3WIRE=y
+CONFIG_BT_HCIUART_BCM=y
+CONFIG_BT_HCIBCM203X=m
+CONFIG_BT_HCIBPA10X=m
+CONFIG_BT_HCIBFUSB=m
+CONFIG_BT_HCIVHCI=m
+CONFIG_BT_MRVL=m
+CONFIG_BT_MRVL_SDIO=m
+CONFIG_BT_ATH3K=m
+CONFIG_CFG80211=m
+CONFIG_CFG80211_WEXT=y
+CONFIG_MAC80211=m
+CONFIG_MAC80211_MESH=y
+CONFIG_WIMAX=m
+CONFIG_RFKILL=m
+CONFIG_RFKILL_INPUT=y
+CONFIG_NET_9P=m
+CONFIG_NFC=m
+CONFIG_UEVENT_HELPER=y
+CONFIG_DEVTMPFS=y
+CONFIG_DEVTMPFS_MOUNT=y
+CONFIG_MTD=m
+CONFIG_MTD_BLOCK=m
+CONFIG_MTD_UBI=m
+CONFIG_OF_CONFIGFS=y
+CONFIG_ZRAM=m
+CONFIG_BLK_DEV_LOOP=y
+CONFIG_BLK_DEV_CRYPTOLOOP=m
+CONFIG_BLK_DEV_DRBD=m
+CONFIG_BLK_DEV_NBD=m
+CONFIG_BLK_DEV_RAM=y
+CONFIG_CDROM_PKTCDVD=m
+CONFIG_ATA_OVER_ETH=m
+CONFIG_EEPROM_AT24=m
+CONFIG_TI_ST=m
+CONFIG_SCSI=y
+# CONFIG_SCSI_PROC_FS is not set
+CONFIG_BLK_DEV_SD=y
+CONFIG_CHR_DEV_ST=m
+CONFIG_BLK_DEV_SR=m
+CONFIG_CHR_DEV_SG=m
+CONFIG_SCSI_ISCSI_ATTRS=y
+CONFIG_ISCSI_TCP=m
+CONFIG_ISCSI_BOOT_SYSFS=m
+CONFIG_MD=y
+CONFIG_MD_LINEAR=m
+CONFIG_BLK_DEV_DM=m
+CONFIG_DM_CRYPT=m
+CONFIG_DM_SNAPSHOT=m
+CONFIG_DM_THIN_PROVISIONING=m
+CONFIG_DM_CACHE=m
+CONFIG_DM_MIRROR=m
+CONFIG_DM_LOG_USERSPACE=m
+CONFIG_DM_RAID=m
+CONFIG_DM_ZERO=m
+CONFIG_DM_DELAY=m
+CONFIG_NETDEVICES=y
+CONFIG_BONDING=m
+CONFIG_DUMMY=m
+CONFIG_WIREGUARD=m
+CONFIG_IFB=m
+CONFIG_MACVLAN=m
+CONFIG_MACVTAP=m
+CONFIG_IPVLAN=m
+CONFIG_VXLAN=m
+CONFIG_NETCONSOLE=m
+CONFIG_TUN=m
+CONFIG_VETH=m
+CONFIG_NET_VRF=m
+CONFIG_ENC28J60=m
+CONFIG_QCA7000_SPI=m
+CONFIG_QCA7000_UART=m
+CONFIG_WIZNET_W5100=m
+CONFIG_WIZNET_W5100_SPI=m
+CONFIG_MDIO_BITBANG=m
+CONFIG_PPP=m
+CONFIG_PPP_BSDCOMP=m
+CONFIG_PPP_DEFLATE=m
+CONFIG_PPP_FILTER=y
+CONFIG_PPP_MPPE=m
+CONFIG_PPP_MULTILINK=y
+CONFIG_PPPOATM=m
+CONFIG_PPPOE=m
+CONFIG_PPPOL2TP=m
+CONFIG_PPP_ASYNC=m
+CONFIG_PPP_SYNC_TTY=m
+CONFIG_SLIP=m
+CONFIG_SLIP_COMPRESSED=y
+CONFIG_SLIP_SMART=y
+CONFIG_USB_CATC=m
+CONFIG_USB_KAWETH=m
+CONFIG_USB_PEGASUS=m
+CONFIG_USB_RTL8150=m
+CONFIG_USB_RTL8152=m
+CONFIG_USB_LAN78XX=y
+CONFIG_USB_USBNET=y
+CONFIG_USB_NET_AX8817X=m
+CONFIG_USB_NET_AX88179_178A=m
+CONFIG_USB_NET_CDCETHER=m
+CONFIG_USB_NET_CDC_EEM=m
+CONFIG_USB_NET_CDC_NCM=m
+CONFIG_USB_NET_HUAWEI_CDC_NCM=m
+CONFIG_USB_NET_CDC_MBIM=m
+CONFIG_USB_NET_DM9601=m
+CONFIG_USB_NET_SR9700=m
+CONFIG_USB_NET_SR9800=m
+CONFIG_USB_NET_SMSC75XX=m
+CONFIG_USB_NET_SMSC95XX=y
+CONFIG_USB_NET_GL620A=m
+CONFIG_USB_NET_NET1080=m
+CONFIG_USB_NET_PLUSB=m
+CONFIG_USB_NET_MCS7830=m
+CONFIG_USB_NET_CDC_SUBSET=m
+CONFIG_USB_ALI_M5632=y
+CONFIG_USB_AN2720=y
+CONFIG_USB_EPSON2888=y
+CONFIG_USB_KC2190=y
+CONFIG_USB_NET_ZAURUS=m
+CONFIG_USB_NET_CX82310_ETH=m
+CONFIG_USB_NET_KALMIA=m
+CONFIG_USB_NET_QMI_WWAN=m
+CONFIG_USB_HSO=m
+CONFIG_USB_NET_INT51X1=m
+CONFIG_USB_IPHETH=m
+CONFIG_USB_SIERRA_NET=m
+CONFIG_USB_VL600=m
+CONFIG_USB_NET_AQC111=m
+CONFIG_ATH9K=m
+CONFIG_ATH9K_HTC=m
+CONFIG_CARL9170=m
+CONFIG_ATH6KL=m
+CONFIG_ATH6KL_USB=m
+CONFIG_AR5523=m
+CONFIG_AT76C50X_USB=m
+CONFIG_B43=m
+# CONFIG_B43_PHY_N is not set
+CONFIG_B43LEGACY=m
+CONFIG_BRCMFMAC=m
+CONFIG_BRCMFMAC_USB=y
+CONFIG_HOSTAP=m
+CONFIG_P54_COMMON=m
+CONFIG_P54_USB=m
+CONFIG_LIBERTAS=m
+CONFIG_LIBERTAS_USB=m
+CONFIG_LIBERTAS_SDIO=m
+CONFIG_LIBERTAS_THINFIRM=m
+CONFIG_LIBERTAS_THINFIRM_USB=m
+CONFIG_MWIFIEX=m
+CONFIG_MWIFIEX_SDIO=m
+CONFIG_MT7601U=m
+CONFIG_MT76x0U=m
+CONFIG_MT76x2U=m
+CONFIG_RT2X00=m
+CONFIG_RT2500USB=m
+CONFIG_RT73USB=m
+CONFIG_RT2800USB=m
+CONFIG_RT2800USB_RT3573=y
+CONFIG_RT2800USB_RT53XX=y
+CONFIG_RT2800USB_RT55XX=y
+CONFIG_RT2800USB_UNKNOWN=y
+CONFIG_RTL8187=m
+CONFIG_RTL8192CU=m
+CONFIG_USB_ZD1201=m
+CONFIG_ZD1211RW=m
+CONFIG_MAC80211_HWSIM=m
+CONFIG_USB_NET_RNDIS_WLAN=m
+CONFIG_WIMAX_I2400M_USB=m
+CONFIG_IEEE802154_AT86RF230=m
+CONFIG_IEEE802154_MRF24J40=m
+CONFIG_IEEE802154_CC2520=m
+CONFIG_INPUT_JOYDEV=m
+CONFIG_INPUT_EVDEV=y
+# CONFIG_KEYBOARD_ATKBD is not set
+CONFIG_KEYBOARD_GPIO=m
+CONFIG_KEYBOARD_MATRIX=m
+CONFIG_KEYBOARD_CAP11XX=m
+# CONFIG_INPUT_MOUSE is not set
+CONFIG_INPUT_JOYSTICK=y
+CONFIG_JOYSTICK_IFORCE=m
+CONFIG_JOYSTICK_IFORCE_USB=m
+CONFIG_JOYSTICK_XPAD=m
+CONFIG_JOYSTICK_XPAD_FF=y
+CONFIG_JOYSTICK_XPAD_LEDS=y
+CONFIG_JOYSTICK_RPISENSE=m
+CONFIG_INPUT_TOUCHSCREEN=y
+CONFIG_TOUCHSCREEN_ADS7846=m
+CONFIG_TOUCHSCREEN_EGALAX=m
+CONFIG_TOUCHSCREEN_ILI210X=m
+CONFIG_TOUCHSCREEN_EKTF2127=m
+CONFIG_TOUCHSCREEN_RASPBERRYPI_FW=m
+CONFIG_TOUCHSCREEN_USB_COMPOSITE=m
+CONFIG_TOUCHSCREEN_STMPE=m
+CONFIG_INPUT_MISC=y
+CONFIG_INPUT_AD714X=m
+CONFIG_INPUT_ATI_REMOTE2=m
+CONFIG_INPUT_KEYSPAN_REMOTE=m
+CONFIG_INPUT_POWERMATE=m
+CONFIG_INPUT_YEALINK=m
+CONFIG_INPUT_CM109=m
+CONFIG_INPUT_UINPUT=m
+CONFIG_INPUT_GPIO_ROTARY_ENCODER=m
+CONFIG_INPUT_ADXL34X=m
+CONFIG_INPUT_CMA3000=m
+CONFIG_SERIO=m
+CONFIG_SERIO_RAW=m
+CONFIG_GAMEPORT=m
+CONFIG_GAMEPORT_NS558=m
+CONFIG_GAMEPORT_L4=m
+CONFIG_BRCM_CHAR_DRIVERS=y
+CONFIG_BCM_VCIO=y
+CONFIG_BCM2835_DEVGPIOMEM=y
+# CONFIG_BCM2835_SMI_DEV is not set
+# CONFIG_LEGACY_PTYS is not set
+CONFIG_SERIAL_8250=y
+# CONFIG_SERIAL_8250_DEPRECATED_OPTIONS is not set
+CONFIG_SERIAL_8250_CONSOLE=y
+# CONFIG_SERIAL_8250_DMA is not set
+CONFIG_SERIAL_8250_NR_UARTS=1
+CONFIG_SERIAL_8250_RUNTIME_UARTS=0
+CONFIG_SERIAL_8250_EXTENDED=y
+CONFIG_SERIAL_8250_SHARE_IRQ=y
+CONFIG_SERIAL_8250_BCM2835AUX=y
+CONFIG_SERIAL_OF_PLATFORM=y
+CONFIG_SERIAL_AMBA_PL011=y
+CONFIG_SERIAL_AMBA_PL011_CONSOLE=y
+CONFIG_SERIAL_SC16IS7XX=m
+CONFIG_SERIAL_SC16IS7XX_SPI=y
+CONFIG_SERIAL_DEV_BUS=y
+CONFIG_TTY_PRINTK=y
+CONFIG_HW_RANDOM=y
+CONFIG_RAW_DRIVER=y
+CONFIG_TCG_TPM=m
+CONFIG_TCG_TIS_SPI=m
+CONFIG_I2C=y
+CONFIG_I2C_CHARDEV=m
+CONFIG_I2C_MUX=m
+CONFIG_I2C_MUX_GPMUX=m
+CONFIG_I2C_MUX_PCA954x=m
+CONFIG_I2C_MUX_PINCTRL=m
+CONFIG_I2C_BCM2708=m
+CONFIG_I2C_BCM2835=m
+# CONFIG_I2C_BRCMSTB is not set
+CONFIG_I2C_GPIO=m
+CONFIG_SPI=y
+CONFIG_SPI_BCM2835=m
+CONFIG_SPI_BCM2835AUX=m
+CONFIG_SPI_SPIDEV=y
+CONFIG_PPS=m
+CONFIG_PPS_CLIENT_LDISC=m
+CONFIG_PPS_CLIENT_GPIO=m
+CONFIG_GPIO_SYSFS=y
+CONFIG_GPIO_BCM_VIRT=y
+CONFIG_GPIO_PCA953X=m
+CONFIG_GPIO_PCA953X_IRQ=y
+CONFIG_GPIO_ARIZONA=m
+CONFIG_GPIO_FSM=m
+CONFIG_GPIO_STMPE=y
+CONFIG_GPIO_MOCKUP=m
+CONFIG_W1=m
+CONFIG_W1_MASTER_DS2490=m
+CONFIG_W1_MASTER_DS2482=m
+CONFIG_W1_MASTER_DS1WM=m
+CONFIG_W1_MASTER_GPIO=m
+CONFIG_W1_SLAVE_THERM=m
+CONFIG_W1_SLAVE_SMEM=m
+CONFIG_W1_SLAVE_DS2408=m
+CONFIG_W1_SLAVE_DS2413=m
+CONFIG_W1_SLAVE_DS2406=m
+CONFIG_W1_SLAVE_DS2423=m
+CONFIG_W1_SLAVE_DS2431=m
+CONFIG_W1_SLAVE_DS2433=m
+CONFIG_W1_SLAVE_DS2780=m
+CONFIG_W1_SLAVE_DS2781=m
+CONFIG_W1_SLAVE_DS28E04=m
+CONFIG_W1_SLAVE_DS28E17=m
+CONFIG_POWER_RESET_GPIO=y
+CONFIG_RPI_POE_POWER=m
+CONFIG_BATTERY_DS2760=m
+CONFIG_BATTERY_MAX17040=m
+CONFIG_SENSORS_IIO_HWMON=m
+CONFIG_SENSORS_LM75=m
+CONFIG_SENSORS_RASPBERRYPI_HWMON=m
+CONFIG_SENSORS_RPI_POE_FAN=m
+CONFIG_SENSORS_SHT21=m
+CONFIG_SENSORS_SHTC1=m
+CONFIG_SENSORS_INA2XX=m
+CONFIG_THERMAL=y
+CONFIG_BCM2835_THERMAL=y
+CONFIG_WATCHDOG=y
+CONFIG_BCM2835_WDT=y
+CONFIG_MFD_STMPE=y
+CONFIG_STMPE_SPI=y
+CONFIG_MFD_ARIZONA_I2C=m
+CONFIG_MFD_ARIZONA_SPI=m
+CONFIG_MFD_WM5102=y
+CONFIG_RC_CORE=y
+CONFIG_LIRC=y
+CONFIG_BPF_LIRC_MODE2=y
+CONFIG_RC_DECODERS=y
+CONFIG_IR_NEC_DECODER=m
+CONFIG_IR_RC5_DECODER=m
+CONFIG_IR_RC6_DECODER=m
+CONFIG_IR_JVC_DECODER=m
+CONFIG_IR_SONY_DECODER=m
+CONFIG_IR_SANYO_DECODER=m
+CONFIG_IR_SHARP_DECODER=m
+CONFIG_IR_MCE_KBD_DECODER=m
+CONFIG_IR_XMP_DECODER=m
+CONFIG_IR_IMON_DECODER=m
+CONFIG_RC_DEVICES=y
+CONFIG_RC_ATI_REMOTE=m
+CONFIG_IR_IMON=m
+CONFIG_IR_MCEUSB=m
+CONFIG_IR_REDRAT3=m
+CONFIG_IR_STREAMZAP=m
+CONFIG_IR_IGUANA=m
+CONFIG_IR_TTUSBIR=m
+CONFIG_RC_LOOPBACK=m
+CONFIG_IR_GPIO_CIR=m
+CONFIG_IR_GPIO_TX=m
+CONFIG_IR_PWM_TX=m
+CONFIG_IR_TOY=m
+CONFIG_MEDIA_CEC_RC=y
+CONFIG_MEDIA_SUPPORT=m
+CONFIG_MEDIA_USB_SUPPORT=y
+CONFIG_USB_VIDEO_CLASS=m
+CONFIG_USB_M5602=m
+CONFIG_USB_STV06XX=m
+CONFIG_USB_GL860=m
+CONFIG_USB_GSPCA_BENQ=m
+CONFIG_USB_GSPCA_CONEX=m
+CONFIG_USB_GSPCA_CPIA1=m
+CONFIG_USB_GSPCA_DTCS033=m
+CONFIG_USB_GSPCA_ETOMS=m
+CONFIG_USB_GSPCA_FINEPIX=m
+CONFIG_USB_GSPCA_JEILINJ=m
+CONFIG_USB_GSPCA_JL2005BCD=m
+CONFIG_USB_GSPCA_KINECT=m
+CONFIG_USB_GSPCA_KONICA=m
+CONFIG_USB_GSPCA_MARS=m
+CONFIG_USB_GSPCA_MR97310A=m
+CONFIG_USB_GSPCA_NW80X=m
+CONFIG_USB_GSPCA_OV519=m
+CONFIG_USB_GSPCA_OV534=m
+CONFIG_USB_GSPCA_OV534_9=m
+CONFIG_USB_GSPCA_PAC207=m
+CONFIG_USB_GSPCA_PAC7302=m
+CONFIG_USB_GSPCA_PAC7311=m
+CONFIG_USB_GSPCA_SE401=m
+CONFIG_USB_GSPCA_SN9C2028=m
+CONFIG_USB_GSPCA_SN9C20X=m
+CONFIG_USB_GSPCA_SONIXB=m
+CONFIG_USB_GSPCA_SONIXJ=m
+CONFIG_USB_GSPCA_SPCA500=m
+CONFIG_USB_GSPCA_SPCA501=m
+CONFIG_USB_GSPCA_SPCA505=m
+CONFIG_USB_GSPCA_SPCA506=m
+CONFIG_USB_GSPCA_SPCA508=m
+CONFIG_USB_GSPCA_SPCA561=m
+CONFIG_USB_GSPCA_SPCA1528=m
+CONFIG_USB_GSPCA_SQ905=m
+CONFIG_USB_GSPCA_SQ905C=m
+CONFIG_USB_GSPCA_SQ930X=m
+CONFIG_USB_GSPCA_STK014=m
+CONFIG_USB_GSPCA_STK1135=m
+CONFIG_USB_GSPCA_STV0680=m
+CONFIG_USB_GSPCA_SUNPLUS=m
+CONFIG_USB_GSPCA_T613=m
+CONFIG_USB_GSPCA_TOPRO=m
+CONFIG_USB_GSPCA_TV8532=m
+CONFIG_USB_GSPCA_VC032X=m
+CONFIG_USB_GSPCA_VICAM=m
+CONFIG_USB_GSPCA_XIRLINK_CIT=m
+CONFIG_USB_GSPCA_ZC3XX=m
+CONFIG_USB_PWC=m
+CONFIG_VIDEO_CPIA2=m
+CONFIG_USB_ZR364XX=m
+CONFIG_USB_STKWEBCAM=m
+CONFIG_USB_S2255=m
+CONFIG_VIDEO_USBTV=m
+CONFIG_VIDEO_PVRUSB2=m
+CONFIG_VIDEO_HDPVR=m
+CONFIG_VIDEO_STK1160_COMMON=m
+CONFIG_VIDEO_GO7007=m
+CONFIG_VIDEO_GO7007_USB=m
+CONFIG_VIDEO_GO7007_USB_S2250_BOARD=m
+CONFIG_VIDEO_AU0828=m
+CONFIG_DVB_USB_V2=m
+CONFIG_DVB_USB_AF9035=m
+CONFIG_DVB_USB_ANYSEE=m
+CONFIG_DVB_USB_AU6610=m
+CONFIG_DVB_USB_AZ6007=m
+CONFIG_DVB_USB_CE6230=m
+CONFIG_DVB_USB_EC168=m
+CONFIG_DVB_USB_GL861=m
+CONFIG_DVB_USB_MXL111SF=m
+CONFIG_DVB_USB_DVBSKY=m
+CONFIG_SMS_USB_DRV=m
+CONFIG_DVB_B2C2_FLEXCOP_USB=m
+CONFIG_DVB_AS102=m
+CONFIG_VIDEO_EM28XX=m
+CONFIG_VIDEO_EM28XX_V4L2=m
+CONFIG_VIDEO_EM28XX_ALSA=m
+CONFIG_VIDEO_EM28XX_DVB=m
+CONFIG_RADIO_SI470X=m
+CONFIG_USB_SI470X=m
+CONFIG_I2C_SI470X=m
+CONFIG_RADIO_SI4713=m
+CONFIG_I2C_SI4713=m
+CONFIG_USB_MR800=m
+CONFIG_USB_DSBR=m
+CONFIG_RADIO_SHARK=m
+CONFIG_RADIO_SHARK2=m
+CONFIG_USB_KEENE=m
+CONFIG_USB_MA901=m
+CONFIG_RADIO_TEA5764=m
+CONFIG_RADIO_SAA7706H=m
+CONFIG_RADIO_TEF6862=m
+CONFIG_RADIO_WL1273=m
+CONFIG_RADIO_WL128X=m
+CONFIG_V4L_PLATFORM_DRIVERS=y
+CONFIG_VIDEO_BCM2835_UNICAM=m
+CONFIG_V4L_TEST_DRIVERS=y
+CONFIG_VIDEO_VIMC=m
+CONFIG_VIDEO_VIVID=m
+CONFIG_VIDEO_VIM2M=m
+CONFIG_VIDEO_VICODEC=m
+CONFIG_VIDEO_UDA1342=m
+CONFIG_VIDEO_SONY_BTF_MPX=m
+CONFIG_VIDEO_ADV7180=m
+CONFIG_VIDEO_TC358743=m
+CONFIG_VIDEO_TVP5150=m
+CONFIG_VIDEO_TW2804=m
+CONFIG_VIDEO_TW9903=m
+CONFIG_VIDEO_TW9906=m
+CONFIG_VIDEO_IMX219=m
+CONFIG_VIDEO_IMX290=m
+CONFIG_VIDEO_IMX477=m
+CONFIG_VIDEO_OV5647=m
+CONFIG_VIDEO_OV7251=m
+CONFIG_VIDEO_OV7640=m
+CONFIG_VIDEO_OV9281=m
+CONFIG_VIDEO_IRS1125=m
+CONFIG_VIDEO_MT9V011=m
+CONFIG_DRM=m
+CONFIG_DRM_LOAD_EDID_FIRMWARE=y
+CONFIG_DRM_UDL=m
+CONFIG_DRM_PANEL_SIMPLE=m
+CONFIG_DRM_PANEL_JDI_LT070ME05000=m
+CONFIG_DRM_PANEL_RASPBERRYPI_TOUCHSCREEN=m
+CONFIG_DRM_DISPLAY_CONNECTOR=m
+CONFIG_DRM_SIMPLE_BRIDGE=m
+CONFIG_DRM_VC4=m
+CONFIG_DRM_VC4_HDMI_CEC=y
+CONFIG_DRM_GUD=m
+CONFIG_FB=y
+CONFIG_FB_BCM2708=y
+CONFIG_FB_UDL=m
+CONFIG_FB_SSD1307=m
+CONFIG_FB_RPISENSE=m
+CONFIG_BACKLIGHT_RPI=m
+CONFIG_BACKLIGHT_GPIO=m
+CONFIG_FRAMEBUFFER_CONSOLE=y
+CONFIG_LOGO=y
+# CONFIG_LOGO_LINUX_MONO is not set
+# CONFIG_LOGO_LINUX_VGA16 is not set
+CONFIG_SOUND=y
+CONFIG_SND=m
+CONFIG_SND_OSSEMUL=y
+CONFIG_SND_PCM_OSS=m
+CONFIG_SND_HRTIMER=m
+CONFIG_SND_SEQUENCER=m
+CONFIG_SND_SEQ_DUMMY=m
+CONFIG_SND_DUMMY=m
+CONFIG_SND_ALOOP=m
+CONFIG_SND_VIRMIDI=m
+CONFIG_SND_MTPAV=m
+CONFIG_SND_SERIAL_U16550=m
+CONFIG_SND_MPU401=m
+CONFIG_SND_USB_AUDIO=m
+CONFIG_SND_USB_UA101=m
+CONFIG_SND_USB_CAIAQ=m
+CONFIG_SND_USB_CAIAQ_INPUT=y
+CONFIG_SND_USB_6FIRE=m
+CONFIG_SND_SOC=m
+CONFIG_SND_BCM2835_SOC_I2S=m
+CONFIG_SND_BCM2708_SOC_HIFIBERRY_DAC=m
+CONFIG_SND_BCM2708_SOC_HIFIBERRY_DACPLUS=m
+CONFIG_SND_BCM2708_SOC_HIFIBERRY_DACPLUSHD=m
+CONFIG_SND_BCM2708_SOC_HIFIBERRY_DACPLUSADC=m
+CONFIG_SND_BCM2708_SOC_HIFIBERRY_DACPLUSADCPRO=m
+CONFIG_SND_BCM2708_SOC_HIFIBERRY_DACPLUSDSP=m
+CONFIG_SND_BCM2708_SOC_HIFIBERRY_DIGI=m
+CONFIG_SND_BCM2708_SOC_HIFIBERRY_AMP=m
+CONFIG_SND_BCM2708_SOC_PIFI_40=m
+CONFIG_SND_BCM2708_SOC_RPI_DAC=m
+CONFIG_SND_BCM2708_SOC_RPI_PROTO=m
+CONFIG_SND_BCM2708_SOC_JUSTBOOM_BOTH=m
+CONFIG_SND_BCM2708_SOC_JUSTBOOM_DAC=m
+CONFIG_SND_BCM2708_SOC_JUSTBOOM_DIGI=m
+CONFIG_SND_BCM2708_SOC_IQAUDIO_CODEC=m
+CONFIG_SND_BCM2708_SOC_IQAUDIO_DAC=m
+CONFIG_SND_BCM2708_SOC_IQAUDIO_DIGI=m
+CONFIG_SND_BCM2708_SOC_I_SABRE_Q2M=m
+CONFIG_SND_BCM2708_SOC_ADAU1977_ADC=m
+CONFIG_SND_AUDIOINJECTOR_PI_SOUNDCARD=m
+CONFIG_SND_AUDIOINJECTOR_ISOLATED_SOUNDCARD=m
+CONFIG_SND_AUDIOSENSE_PI=m
+CONFIG_SND_DIGIDAC1_SOUNDCARD=m
+CONFIG_SND_BCM2708_SOC_DIONAUDIO_LOCO=m
+CONFIG_SND_BCM2708_SOC_ALLO_PIANO_DAC=m
+CONFIG_SND_BCM2708_SOC_ALLO_BOSS2_DAC=m
+CONFIG_SND_PISOUND=m
+CONFIG_SND_SOC_AD193X_SPI=m
+CONFIG_SND_SOC_AD193X_I2C=m
+CONFIG_SND_SOC_ADAU1701=m
+CONFIG_SND_SOC_AK4554=m
+CONFIG_SND_SOC_CS4265=m
+CONFIG_SND_SOC_ICS43432=m
+CONFIG_SND_SOC_MA120X0P=m
+CONFIG_SND_SOC_MAX98357A=m
+CONFIG_SND_SOC_WM8804_I2C=m
+CONFIG_SND_SOC_WM8960=m
+CONFIG_SND_SIMPLE_CARD=m
+CONFIG_HIDRAW=y
+CONFIG_UHID=m
+CONFIG_HID_A4TECH=m
+CONFIG_HID_ACRUX=m
+CONFIG_HID_APPLE=m
+CONFIG_HID_BELKIN=m
+CONFIG_HID_BETOP_FF=m
+CONFIG_HID_CHERRY=m
+CONFIG_HID_CHICONY=m
+CONFIG_HID_CYPRESS=m
+CONFIG_HID_DRAGONRISE=m
+CONFIG_HID_EMS_FF=m
+CONFIG_HID_ELECOM=m
+CONFIG_HID_ELO=m
+CONFIG_HID_EZKEY=m
+CONFIG_HID_GEMBIRD=m
+CONFIG_HID_HOLTEK=m
+CONFIG_HID_KEYTOUCH=m
+CONFIG_HID_KYE=m
+CONFIG_HID_UCLOGIC=m
+CONFIG_HID_WALTOP=m
+CONFIG_HID_GYRATION=m
+CONFIG_HID_TWINHAN=m
+CONFIG_HID_KENSINGTON=m
+CONFIG_HID_LCPOWER=m
+CONFIG_HID_LOGITECH=m
+CONFIG_HID_LOGITECH_DJ=m
+CONFIG_LOGITECH_FF=y
+CONFIG_LOGIRUMBLEPAD2_FF=y
+CONFIG_LOGIG940_FF=y
+CONFIG_HID_MAGICMOUSE=m
+CONFIG_HID_MICROSOFT=m
+CONFIG_HID_MONTEREY=m
+CONFIG_HID_MULTITOUCH=m
+CONFIG_HID_NTRIG=m
+CONFIG_HID_ORTEK=m
+CONFIG_HID_PANTHERLORD=m
+CONFIG_HID_PETALYNX=m
+CONFIG_HID_PICOLCD=m
+CONFIG_HID_ROCCAT=m
+CONFIG_HID_SAMSUNG=m
+CONFIG_HID_SONY=m
+CONFIG_HID_SPEEDLINK=m
+CONFIG_HID_STEAM=m
+CONFIG_HID_SUNPLUS=m
+CONFIG_HID_GREENASIA=m
+CONFIG_HID_SMARTJOYPLUS=m
+CONFIG_HID_TOPSEED=m
+CONFIG_HID_THINGM=m
+CONFIG_HID_THRUSTMASTER=m
+CONFIG_HID_WACOM=m
+CONFIG_HID_WIIMOTE=m
+CONFIG_HID_XINMO=m
+CONFIG_HID_ZEROPLUS=m
+CONFIG_HID_ZYDACRON=m
+CONFIG_HID_PID=y
+CONFIG_USB_HIDDEV=y
+CONFIG_I2C_HID=m
+CONFIG_USB=y
+CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
+CONFIG_USB_MON=m
+CONFIG_USB_DWCOTG=y
+CONFIG_USB_PRINTER=m
+CONFIG_USB_TMC=m
+CONFIG_USB_STORAGE=y
+CONFIG_USB_STORAGE_REALTEK=m
+CONFIG_USB_STORAGE_DATAFAB=m
+CONFIG_USB_STORAGE_FREECOM=m
+CONFIG_USB_STORAGE_ISD200=m
+CONFIG_USB_STORAGE_USBAT=m
+CONFIG_USB_STORAGE_SDDR09=m
+CONFIG_USB_STORAGE_SDDR55=m
+CONFIG_USB_STORAGE_JUMPSHOT=m
+CONFIG_USB_STORAGE_ALAUDA=m
+CONFIG_USB_STORAGE_ONETOUCH=m
+CONFIG_USB_STORAGE_KARMA=m
+CONFIG_USB_STORAGE_CYPRESS_ATACB=m
+CONFIG_USB_STORAGE_ENE_UB6250=m
+CONFIG_USB_UAS=m
+CONFIG_USB_MDC800=m
+CONFIG_USB_MICROTEK=m
+CONFIG_USBIP_CORE=m
+CONFIG_USBIP_VHCI_HCD=m
+CONFIG_USBIP_HOST=m
+CONFIG_USB_DWC2=y
+CONFIG_USB_SERIAL=m
+CONFIG_USB_SERIAL_GENERIC=y
+CONFIG_USB_SERIAL_AIRCABLE=m
+CONFIG_USB_SERIAL_ARK3116=m
+CONFIG_USB_SERIAL_BELKIN=m
+CONFIG_USB_SERIAL_CH341=m
+CONFIG_USB_SERIAL_WHITEHEAT=m
+CONFIG_USB_SERIAL_DIGI_ACCELEPORT=m
+CONFIG_USB_SERIAL_CP210X=m
+CONFIG_USB_SERIAL_CYPRESS_M8=m
+CONFIG_USB_SERIAL_EMPEG=m
+CONFIG_USB_SERIAL_FTDI_SIO=m
+CONFIG_USB_SERIAL_VISOR=m
+CONFIG_USB_SERIAL_IPAQ=m
+CONFIG_USB_SERIAL_IR=m
+CONFIG_USB_SERIAL_EDGEPORT=m
+CONFIG_USB_SERIAL_EDGEPORT_TI=m
+CONFIG_USB_SERIAL_F81232=m
+CONFIG_USB_SERIAL_GARMIN=m
+CONFIG_USB_SERIAL_IPW=m
+CONFIG_USB_SERIAL_IUU=m
+CONFIG_USB_SERIAL_KEYSPAN_PDA=m
+CONFIG_USB_SERIAL_KEYSPAN=m
+CONFIG_USB_SERIAL_KLSI=m
+CONFIG_USB_SERIAL_KOBIL_SCT=m
+CONFIG_USB_SERIAL_MCT_U232=m
+CONFIG_USB_SERIAL_METRO=m
+CONFIG_USB_SERIAL_MOS7720=m
+CONFIG_USB_SERIAL_MOS7840=m
+CONFIG_USB_SERIAL_NAVMAN=m
+CONFIG_USB_SERIAL_PL2303=m
+CONFIG_USB_SERIAL_OTI6858=m
+CONFIG_USB_SERIAL_QCAUX=m
+CONFIG_USB_SERIAL_QUALCOMM=m
+CONFIG_USB_SERIAL_SPCP8X5=m
+CONFIG_USB_SERIAL_SAFE=m
+CONFIG_USB_SERIAL_SIERRAWIRELESS=m
+CONFIG_USB_SERIAL_SYMBOL=m
+CONFIG_USB_SERIAL_TI=m
+CONFIG_USB_SERIAL_CYBERJACK=m
+CONFIG_USB_SERIAL_XIRCOM=m
+CONFIG_USB_SERIAL_OPTION=m
+CONFIG_USB_SERIAL_OMNINET=m
+CONFIG_USB_SERIAL_OPTICON=m
+CONFIG_USB_SERIAL_XSENS_MT=m
+CONFIG_USB_SERIAL_WISHBONE=m
+CONFIG_USB_SERIAL_SSU100=m
+CONFIG_USB_SERIAL_QT2=m
+CONFIG_USB_SERIAL_DEBUG=m
+CONFIG_USB_EMI62=m
+CONFIG_USB_EMI26=m
+CONFIG_USB_ADUTUX=m
+CONFIG_USB_SEVSEG=m
+CONFIG_USB_LEGOTOWER=m
+CONFIG_USB_LCD=m
+CONFIG_USB_CYPRESS_CY7C63=m
+CONFIG_USB_CYTHERM=m
+CONFIG_USB_IDMOUSE=m
+CONFIG_USB_FTDI_ELAN=m
+CONFIG_USB_APPLEDISPLAY=m
+CONFIG_USB_LD=m
+CONFIG_USB_TRANCEVIBRATOR=m
+CONFIG_USB_IOWARRIOR=m
+CONFIG_USB_TEST=m
+CONFIG_USB_ISIGHTFW=m
+CONFIG_USB_YUREX=m
+CONFIG_USB_ATM=m
+CONFIG_USB_SPEEDTOUCH=m
+CONFIG_USB_CXACRU=m
+CONFIG_USB_UEAGLEATM=m
+CONFIG_USB_XUSBATM=m
+CONFIG_USB_GADGET=m
+CONFIG_MMC=y
+CONFIG_MMC_BLOCK_MINORS=32
+CONFIG_MMC_BCM2835_MMC=y
+CONFIG_MMC_BCM2835_DMA=y
+CONFIG_MMC_BCM2835_SDHOST=y
+CONFIG_MMC_SDHCI=y
+CONFIG_MMC_SDHCI_PLTFM=y
+CONFIG_MMC_SDHCI_IPROC=m
+CONFIG_MMC_SPI=m
+CONFIG_LEDS_CLASS=y
+CONFIG_LEDS_PCA9532=m
+CONFIG_LEDS_GPIO=y
+CONFIG_LEDS_PCA955X=m
+CONFIG_LEDS_PCA963X=m
+CONFIG_LEDS_IS31FL32XX=m
+CONFIG_LEDS_TRIGGER_TIMER=y
+CONFIG_LEDS_TRIGGER_ONESHOT=y
+CONFIG_LEDS_TRIGGER_HEARTBEAT=y
+CONFIG_LEDS_TRIGGER_BACKLIGHT=y
+CONFIG_LEDS_TRIGGER_CPU=y
+CONFIG_LEDS_TRIGGER_GPIO=y
+CONFIG_LEDS_TRIGGER_DEFAULT_ON=y
+CONFIG_LEDS_TRIGGER_TRANSIENT=m
+CONFIG_LEDS_TRIGGER_CAMERA=m
+CONFIG_LEDS_TRIGGER_INPUT=y
+CONFIG_LEDS_TRIGGER_PANIC=y
+CONFIG_LEDS_TRIGGER_NETDEV=m
+CONFIG_LEDS_TRIGGER_ACTPWR=y
+CONFIG_ACCESSIBILITY=y
+CONFIG_SPEAKUP=m
+CONFIG_SPEAKUP_SYNTH_SOFT=m
+CONFIG_RTC_CLASS=y
+CONFIG_RTC_DRV_ABX80X=m
+CONFIG_RTC_DRV_DS1307=m
+CONFIG_RTC_DRV_DS1374=m
+CONFIG_RTC_DRV_DS1672=m
+CONFIG_RTC_DRV_MAX6900=m
+CONFIG_RTC_DRV_RS5C372=m
+CONFIG_RTC_DRV_ISL1208=m
+CONFIG_RTC_DRV_ISL12022=m
+CONFIG_RTC_DRV_X1205=m
+CONFIG_RTC_DRV_PCF8523=m
+CONFIG_RTC_DRV_PCF85063=m
+CONFIG_RTC_DRV_PCF85363=m
+CONFIG_RTC_DRV_PCF8563=m
+CONFIG_RTC_DRV_PCF8583=m
+CONFIG_RTC_DRV_M41T80=m
+CONFIG_RTC_DRV_BQ32K=m
+CONFIG_RTC_DRV_S35390A=m
+CONFIG_RTC_DRV_FM3130=m
+CONFIG_RTC_DRV_RX8581=m
+CONFIG_RTC_DRV_RX8025=m
+CONFIG_RTC_DRV_EM3027=m
+CONFIG_RTC_DRV_RV3028=m
+CONFIG_RTC_DRV_SD3078=m
+CONFIG_RTC_DRV_M41T93=m
+CONFIG_RTC_DRV_M41T94=m
+CONFIG_RTC_DRV_DS1302=m
+CONFIG_RTC_DRV_DS1305=m
+CONFIG_RTC_DRV_DS1390=m
+CONFIG_RTC_DRV_R9701=m
+CONFIG_RTC_DRV_RX4581=m
+CONFIG_RTC_DRV_RS5C348=m
+CONFIG_RTC_DRV_MAX6902=m
+CONFIG_RTC_DRV_PCF2123=m
+CONFIG_RTC_DRV_DS3232=m
+CONFIG_RTC_DRV_PCF2127=m
+CONFIG_RTC_DRV_RV3029C2=m
+CONFIG_DMADEVICES=y
+CONFIG_DMA_BCM2835=y
+CONFIG_DMA_BCM2708=y
+CONFIG_DMABUF_HEAPS=y
+CONFIG_DMABUF_HEAPS_SYSTEM=y
+CONFIG_DMABUF_HEAPS_CMA=y
+CONFIG_UIO=m
+CONFIG_UIO_PDRV_GENIRQ=m
+CONFIG_STAGING=y
+CONFIG_PRISM2_USB=m
+CONFIG_R8712U=m
+CONFIG_R8188EU=m
+CONFIG_VT6656=m
+CONFIG_STAGING_MEDIA=y
+CONFIG_ASHMEM=y
+CONFIG_FB_TFT=m
+CONFIG_FB_TFT_AGM1264K_FL=m
+CONFIG_FB_TFT_BD663474=m
+CONFIG_FB_TFT_HX8340BN=m
+CONFIG_FB_TFT_HX8347D=m
+CONFIG_FB_TFT_HX8353D=m
+CONFIG_FB_TFT_HX8357D=m
+CONFIG_FB_TFT_ILI9163=m
+CONFIG_FB_TFT_ILI9320=m
+CONFIG_FB_TFT_ILI9325=m
+CONFIG_FB_TFT_ILI9340=m
+CONFIG_FB_TFT_ILI9341=m
+CONFIG_FB_TFT_ILI9481=m
+CONFIG_FB_TFT_ILI9486=m
+CONFIG_FB_TFT_PCD8544=m
+CONFIG_FB_TFT_RA8875=m
+CONFIG_FB_TFT_S6D02A1=m
+CONFIG_FB_TFT_S6D1121=m
+CONFIG_FB_TFT_SH1106=m
+CONFIG_FB_TFT_SSD1289=m
+CONFIG_FB_TFT_SSD1306=m
+CONFIG_FB_TFT_SSD1331=m
+CONFIG_FB_TFT_SSD1351=m
+CONFIG_FB_TFT_ST7735R=m
+CONFIG_FB_TFT_TINYLCD=m
+CONFIG_FB_TFT_TLS8204=m
+CONFIG_FB_TFT_UC1701=m
+CONFIG_FB_TFT_UPD161704=m
+CONFIG_FB_TFT_WATTEROTT=m
+CONFIG_BCM2835_VCHIQ=y
+CONFIG_SND_BCM2835=m
+CONFIG_VIDEO_BCM2835=m
+CONFIG_VIDEO_CODEC_BCM2835=m
+CONFIG_VIDEO_ISP_BCM2835=m
+CONFIG_CLK_RASPBERRYPI=y
+CONFIG_MAILBOX=y
+CONFIG_BCM2835_MBOX=y
+# CONFIG_IOMMU_SUPPORT is not set
+CONFIG_RASPBERRYPI_POWER=y
+CONFIG_EXTCON=m
+CONFIG_EXTCON_ARIZONA=m
+CONFIG_IIO=m
+CONFIG_IIO_BUFFER_CB=m
+CONFIG_MCP320X=m
+CONFIG_MCP3422=m
+CONFIG_BME680=m
+CONFIG_CCS811=m
+CONFIG_SENSIRION_SGP30=m
+CONFIG_SPS30=m
+CONFIG_DHT11=m
+CONFIG_HTU21=m
+CONFIG_APDS9960=m
+CONFIG_BH1750=m
+CONFIG_MAXIM_THERMOCOUPLE=m
+CONFIG_MAX31856=m
+CONFIG_PWM_BCM2835=m
+CONFIG_PWM_PCA9685=m
+CONFIG_ANDROID=y
+CONFIG_ANDROID_BINDER_IPC=y
+CONFIG_ANDROID_BINDERFS=y
+CONFIG_EXT4_FS=y
+CONFIG_EXT4_FS_POSIX_ACL=y
+CONFIG_EXT4_FS_SECURITY=y
+CONFIG_REISERFS_FS=m
+CONFIG_REISERFS_FS_XATTR=y
+CONFIG_REISERFS_FS_POSIX_ACL=y
+CONFIG_REISERFS_FS_SECURITY=y
+CONFIG_JFS_FS=m
+CONFIG_JFS_POSIX_ACL=y
+CONFIG_JFS_SECURITY=y
+CONFIG_JFS_STATISTICS=y
+CONFIG_XFS_FS=m
+CONFIG_XFS_QUOTA=y
+CONFIG_XFS_POSIX_ACL=y
+CONFIG_XFS_RT=y
+CONFIG_GFS2_FS=m
+CONFIG_OCFS2_FS=m
+CONFIG_BTRFS_FS=m
+CONFIG_BTRFS_FS_POSIX_ACL=y
+CONFIG_NILFS2_FS=m
+CONFIG_F2FS_FS=y
+CONFIG_FS_ENCRYPTION=y
+CONFIG_FANOTIFY=y
+CONFIG_QFMT_V1=m
+CONFIG_QFMT_V2=m
+CONFIG_AUTOFS4_FS=y
+CONFIG_FUSE_FS=m
+CONFIG_CUSE=m
+CONFIG_OVERLAY_FS=m
+CONFIG_FSCACHE=y
+CONFIG_FSCACHE_STATS=y
+CONFIG_FSCACHE_HISTOGRAM=y
+CONFIG_CACHEFILES=y
+CONFIG_ISO9660_FS=m
+CONFIG_JOLIET=y
+CONFIG_ZISOFS=y
+CONFIG_UDF_FS=m
+CONFIG_MSDOS_FS=y
+CONFIG_VFAT_FS=y
+CONFIG_FAT_DEFAULT_IOCHARSET="ascii"
+CONFIG_EXFAT_FS=m
+CONFIG_NTFS_FS=m
+CONFIG_NTFS_RW=y
+CONFIG_TMPFS=y
+CONFIG_TMPFS_POSIX_ACL=y
+CONFIG_ECRYPT_FS=m
+CONFIG_HFS_FS=m
+CONFIG_HFSPLUS_FS=m
+CONFIG_JFFS2_FS=m
+CONFIG_JFFS2_SUMMARY=y
+CONFIG_UBIFS_FS=m
+CONFIG_SQUASHFS=m
+CONFIG_SQUASHFS_XATTR=y
+CONFIG_SQUASHFS_LZO=y
+CONFIG_SQUASHFS_XZ=y
+CONFIG_NFS_FS=y
+CONFIG_NFS_V3_ACL=y
+CONFIG_NFS_V4=y
+CONFIG_NFS_SWAP=y
+CONFIG_NFS_V4_1=y
+CONFIG_NFS_V4_2=y
+CONFIG_ROOT_NFS=y
+CONFIG_NFS_FSCACHE=y
+CONFIG_NFSD=m
+CONFIG_NFSD_V3_ACL=y
+CONFIG_NFSD_V4=y
+CONFIG_CIFS=m
+CONFIG_CIFS_WEAK_PW_HASH=y
+CONFIG_CIFS_UPCALL=y
+CONFIG_CIFS_XATTR=y
+CONFIG_CIFS_DFS_UPCALL=y
+CONFIG_CIFS_FSCACHE=y
+CONFIG_9P_FS=m
+CONFIG_9P_FS_POSIX_ACL=y
+CONFIG_NLS_DEFAULT="utf8"
+CONFIG_NLS_CODEPAGE_437=y
+CONFIG_NLS_CODEPAGE_737=m
+CONFIG_NLS_CODEPAGE_775=m
+CONFIG_NLS_CODEPAGE_850=m
+CONFIG_NLS_CODEPAGE_852=m
+CONFIG_NLS_CODEPAGE_855=m
+CONFIG_NLS_CODEPAGE_857=m
+CONFIG_NLS_CODEPAGE_860=m
+CONFIG_NLS_CODEPAGE_861=m
+CONFIG_NLS_CODEPAGE_862=m
+CONFIG_NLS_CODEPAGE_863=m
+CONFIG_NLS_CODEPAGE_864=m
+CONFIG_NLS_CODEPAGE_865=m
+CONFIG_NLS_CODEPAGE_866=m
+CONFIG_NLS_CODEPAGE_869=m
+CONFIG_NLS_CODEPAGE_936=m
+CONFIG_NLS_CODEPAGE_950=m
+CONFIG_NLS_CODEPAGE_932=m
+CONFIG_NLS_CODEPAGE_949=m
+CONFIG_NLS_CODEPAGE_874=m
+CONFIG_NLS_ISO8859_8=m
+CONFIG_NLS_CODEPAGE_1250=m
+CONFIG_NLS_CODEPAGE_1251=m
+CONFIG_NLS_ASCII=y
+CONFIG_NLS_ISO8859_1=m
+CONFIG_NLS_ISO8859_2=m
+CONFIG_NLS_ISO8859_3=m
+CONFIG_NLS_ISO8859_4=m
+CONFIG_NLS_ISO8859_5=m
+CONFIG_NLS_ISO8859_6=m
+CONFIG_NLS_ISO8859_7=m
+CONFIG_NLS_ISO8859_9=m
+CONFIG_NLS_ISO8859_13=m
+CONFIG_NLS_ISO8859_14=m
+CONFIG_NLS_ISO8859_15=m
+CONFIG_NLS_KOI8_R=m
+CONFIG_NLS_KOI8_U=m
+CONFIG_DLM=m
+CONFIG_SECURITY=y
+CONFIG_SECURITY_APPARMOR=y
+CONFIG_LSM=""
+CONFIG_CRYPTO_USER=m
+CONFIG_CRYPTO_CHACHA20POLY1305=m
+CONFIG_CRYPTO_ADIANTUM=m
+CONFIG_CRYPTO_XCBC=m
+CONFIG_CRYPTO_TGR192=m
+CONFIG_CRYPTO_WP512=m
+CONFIG_CRYPTO_CAST5=m
+CONFIG_CRYPTO_DES=y
+CONFIG_CRYPTO_LZ4=m
+CONFIG_CRYPTO_USER_API_HASH=m
+CONFIG_CRYPTO_USER_API_SKCIPHER=m
+CONFIG_CRYPTO_USER_API_RNG=m
+CONFIG_CRYPTO_USER_API_AEAD=m
+CONFIG_CRC_ITU_T=y
+CONFIG_LIBCRC32C=y
+CONFIG_DMA_CMA=y
+CONFIG_CMA_SIZE_MBYTES=5
+CONFIG_PRINTK_TIME=y
+CONFIG_BOOT_PRINTK_DELAY=y
+CONFIG_KGDB=y
+CONFIG_KGDB_KDB=y
+CONFIG_KDB_KEYBOARD=y
+CONFIG_DEBUG_MEMORY_INIT=y
+CONFIG_DETECT_HUNG_TASK=y
+CONFIG_LATENCYTOP=y
+CONFIG_FUNCTION_PROFILER=y
+CONFIG_STACK_TRACER=y
+CONFIG_IRQSOFF_TRACER=y
+CONFIG_SCHED_TRACER=y
+CONFIG_BLK_DEV_IO_TRACE=y
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm64/configs/defconfig linux-5.10.52-v7l+/arch/arm64/configs/defconfig
--- linux-5.10.52-orig/arch/arm64/configs/defconfig	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/arch/arm64/configs/defconfig	2021-07-25 16:45:36.088791798 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1030 @
 CONFIG_NVMEM_SUNXI_SID=y
 CONFIG_UNIPHIER_EFUSE=y
 CONFIG_MESON_EFUSE=m
+CONFIG_NVMEM_RMEM=m
 CONFIG_FPGA=y
 CONFIG_FPGA_MGR_STRATIX10_SOC=m
 CONFIG_FPGA_BRIDGE=m
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm64/crypto/aes-cipher-glue.c linux-5.10.52-v7l+/arch/arm64/crypto/aes-cipher-glue.c
--- linux-5.10.52-orig/arch/arm64/crypto/aes-cipher-glue.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/arch/arm64/crypto/aes-cipher-glue.c	2021-07-25 16:45:36.098791631 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:12 @
 #include <linux/crypto.h>
 #include <linux/module.h>
 
+MODULE_ALIAS_CRYPTO("ecb(aes)");
+MODULE_ALIAS_CRYPTO("cbc(aes)");
+MODULE_ALIAS_CRYPTO("ctr(aes)");
+MODULE_ALIAS_CRYPTO("xts(aes)");
+MODULE_ALIAS_CRYPTO("cts(cbc(aes))");
+MODULE_ALIAS_CRYPTO("essiv(cbc(aes),sha256)");
+MODULE_ALIAS_CRYPTO("cmac(aes)");
+MODULE_ALIAS_CRYPTO("xcbc(aes)");
+MODULE_ALIAS_CRYPTO("cbcmac(aes)");
+
 asmlinkage void __aes_arm64_encrypt(u32 *rk, u8 *out, const u8 *in, int rounds);
 asmlinkage void __aes_arm64_decrypt(u32 *rk, u8 *out, const u8 *in, int rounds);
 
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm64/crypto/aes-glue.c linux-5.10.52-v7l+/arch/arm64/crypto/aes-glue.c
--- linux-5.10.52-orig/arch/arm64/crypto/aes-glue.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/arch/arm64/crypto/aes-glue.c	2021-07-25 16:45:36.098791631 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:58 @
 #define aes_mac_update		neon_aes_mac_update
 MODULE_DESCRIPTION("AES-ECB/CBC/CTR/XTS using ARMv8 NEON");
 #endif
-#if defined(USE_V8_CRYPTO_EXTENSIONS) || !IS_ENABLED(CONFIG_CRYPTO_AES_ARM64_BS)
+#if defined(USE_V8_CRYPTO_EXTENSIONS)
 MODULE_ALIAS_CRYPTO("ecb(aes)");
 MODULE_ALIAS_CRYPTO("cbc(aes)");
 MODULE_ALIAS_CRYPTO("ctr(aes)");
 MODULE_ALIAS_CRYPTO("xts(aes)");
-#endif
 MODULE_ALIAS_CRYPTO("cts(cbc(aes))");
 MODULE_ALIAS_CRYPTO("essiv(cbc(aes),sha256)");
 MODULE_ALIAS_CRYPTO("cmac(aes)");
 MODULE_ALIAS_CRYPTO("xcbc(aes)");
 MODULE_ALIAS_CRYPTO("cbcmac(aes)");
+#endif
 
 MODULE_AUTHOR("Ard Biesheuvel <ard.biesheuvel@linaro.org>");
 MODULE_LICENSE("GPL v2");
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm64/crypto/aes-neonbs-glue.c linux-5.10.52-v7l+/arch/arm64/crypto/aes-neonbs-glue.c
--- linux-5.10.52-orig/arch/arm64/crypto/aes-neonbs-glue.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/arch/arm64/crypto/aes-neonbs-glue.c	2021-07-25 16:45:36.098791631 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:21 @
 MODULE_AUTHOR("Ard Biesheuvel <ard.biesheuvel@linaro.org>");
 MODULE_LICENSE("GPL v2");
 
-MODULE_ALIAS_CRYPTO("ecb(aes)");
-MODULE_ALIAS_CRYPTO("cbc(aes)");
-MODULE_ALIAS_CRYPTO("ctr(aes)");
-MODULE_ALIAS_CRYPTO("xts(aes)");
-
 asmlinkage void aesbs_convert_key(u8 out[], u32 const rk[], int rounds);
 
 asmlinkage void aesbs_ecb_encrypt(u8 out[], u8 const in[], u8 const rk[],
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm64/Kconfig.platforms linux-5.10.52-v7l+/arch/arm64/Kconfig.platforms
--- linux-5.10.52-orig/arch/arm64/Kconfig.platforms	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/arch/arm64/Kconfig.platforms	2021-07-25 16:45:35.898794984 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:42 @
 	select ARM_AMBA
 	select ARM_GIC
 	select ARM_TIMER_SP804
+	select BRCMSTB_L2_IRQ
 	help
 	  This enables support for the Broadcom BCM2837 and BCM2711 SoC.
 	  These SoCs are used in the Raspberry Pi 3 and 4 devices.
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm64/kernel/armv8_deprecated.c linux-5.10.52-v7l+/arch/arm64/kernel/armv8_deprecated.c
--- linux-5.10.52-orig/arch/arm64/kernel/armv8_deprecated.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/arch/arm64/kernel/armv8_deprecated.c	2021-07-25 16:45:36.138790960 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:185 @
 
 	switch (ops->status) {
 	case INSN_DEPRECATED:
+#if 0
 		insn->current_mode = INSN_EMULATE;
 		/* Disable the HW mode if it was turned on at early boot time */
 		run_all_cpu_set_hw_mode(insn, false);
+#else
+		insn->current_mode = INSN_HW;
+		run_all_cpu_set_hw_mode(insn, true);
 		insn->max = INSN_HW;
+#endif
 		break;
 	case INSN_OBSOLETE:
 		insn->current_mode = INSN_UNDEF;
diff -Nur --no-dereference linux-5.10.52-orig/arch/arm64/kernel/cpuinfo.c linux-5.10.52-v7l+/arch/arm64/kernel/cpuinfo.c
--- linux-5.10.52-orig/arch/arm64/kernel/cpuinfo.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/arch/arm64/kernel/cpuinfo.c	2021-07-25 16:45:36.148790792 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:20 @
 #include <linux/elf.h>
 #include <linux/init.h>
 #include <linux/kernel.h>
+#include <linux/of_platform.h>
 #include <linux/personality.h>
 #include <linux/preempt.h>
 #include <linux/printk.h>
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:141 @
 {
 	int i, j;
 	bool compat = personality(current->personality) == PER_LINUX32;
+	struct device_node *np;
+	const char *model;
+	const char *serial;
+	u32 revision;
 
 	for_each_online_cpu(i) {
 		struct cpuinfo_arm64 *cpuinfo = &per_cpu(cpu_data, i);
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:205 @
 		seq_printf(m, "CPU revision\t: %d\n\n", MIDR_REVISION(midr));
 	}
 
+	seq_printf(m, "Hardware\t: BCM2835\n");
+
+	np = of_find_node_by_path("/system");
+	if (np) {
+		if (!of_property_read_u32(np, "linux,revision", &revision))
+			seq_printf(m, "Revision\t: %04x\n", revision);
+		of_node_put(np);
+	}
+
+	np = of_find_node_by_path("/");
+	if (np) {
+		if (!of_property_read_string(np, "serial-number",
+					     &serial))
+			seq_printf(m, "Serial\t\t: %s\n", serial);
+		if (!of_property_read_string(np, "model",
+					     &model))
+			seq_printf(m, "Model\t\t: %s\n", model);
+		of_node_put(np);
+	}
+
 	return 0;
 }
 
diff -Nur --no-dereference linux-5.10.52-orig/Documentation/admin-guide/media/bcm2835-isp.rst linux-5.10.52-v7l+/Documentation/admin-guide/media/bcm2835-isp.rst
--- linux-5.10.52-orig/Documentation/admin-guide/media/bcm2835-isp.rst	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/Documentation/admin-guide/media/bcm2835-isp.rst	2021-07-25 16:45:33.208840083 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+.. SPDX-License-Identifier: GPL-2.0
+
+BCM2835 ISP Driver
+==================
+
+Introduction
+------------
+
+The BCM2835 Image Sensor Pipeline (ISP) is a fixed function hardware pipeline
+for performing image processing operations.  Images are fed to the input
+of the ISP through memory frame buffers.  These images may be in various YUV,
+RGB, or Bayer formats.  A typical use case would have Bayer images obtained from
+an image sensor by the BCM2835 Unicam peripheral, written to a memory
+frame buffer, and finally fed into the input of the ISP.  Two concurrent output
+images may be generated in YUV or RGB format at different resolutions.
+Statistics output is also generated for Bayer input images.
+
+The bcm2835-isp driver exposes the following media pads as V4L2 device nodes:
+
+.. tabularcolumns:: |l|l|l|l|
+
+.. cssclass: longtable
+
+.. flat-table::
+
+    * - *Pad*
+      - *Direction*
+      - *Purpose*
+      - *Formats*
+
+    * - "bcm2835-isp0-output0"
+      - sink
+      - Accepts Bayer, RGB or YUV format frame buffers as input to the ISP HW
+        pipeline.
+      - :ref:`RAW8 <V4L2-PIX-FMT-SRGGB8>`,
+        :ref:`RAW10P <V4L2-PIX-FMT-SRGGB10P>`,
+        :ref:`RAW12P <V4L2-PIX-FMT-SRGGB12P>`,
+        :ref:`RAW14P <V4L2-PIX-FMT-SRGGB14P>`,
+        :ref:`RAW16 <V4L2-PIX-FMT-SRGGB16>`,
+        :ref:`RGB24/BGR24 <V4L2-PIX-FMT-RGB24>`,
+        :ref:`YUYV <V4L2-PIX-FMT-YUYV>`,
+        :ref:`YVYU <V4L2-PIX-FMT-YVYU>`,
+        :ref:`UYVY <V4L2-PIX-FMT-UYVY>`,
+        :ref:`VYUY <V4L2-PIX-FMT-VYUY>`,
+        :ref:`YUV420/YVU420 <V4L2-PIX-FMT-YUV420>`
+
+    * - "bcm2835-isp0-capture1"
+      - source
+      - High resolution YUV or RGB processed output from the ISP.
+      - :ref:`RGB565 <V4L2-PIX-FMT-RGB565>`,
+        :ref:`RGB24/BGR24 <V4L2-PIX-FMT-RGB24>`,
+        :ref:`ABGR32 <V4L2-PIX-FMT-ABGR32>`,
+        :ref:`YUYV <V4L2-PIX-FMT-YUYV>`,
+        :ref:`YVYU <V4L2-PIX-FMT-YVYU>`,
+        :ref:`UYVY <V4L2-PIX-FMT-UYVY>`,
+        :ref:`VYUY <V4L2-PIX-FMT-VYUY>`.
+        :ref:`YUV420/YVU420 <V4L2-PIX-FMT-YUV420>`,
+        :ref:`NV12/NV21 <V4L2-PIX-FMT-NV12>`,
+
+    * - "bcm2835-isp0-capture2"
+      - source
+      - Low resolution YUV processed output from the ISP. The output of
+        this pad cannot have a resolution larger than the "bcm2835-isp0-capture1" pad in any dimension.
+      - :ref:`YUYV <V4L2-PIX-FMT-YUYV>`,
+        :ref:`YVYU <V4L2-PIX-FMT-YVYU>`,
+        :ref:`UYVY <V4L2-PIX-FMT-UYVY>`,
+        :ref:`VYUY <V4L2-PIX-FMT-VYUY>`.
+        :ref:`YUV420/YVU420 <V4L2-PIX-FMT-YUV420>`,
+        :ref:`NV12/NV21 <V4L2-PIX-FMT-NV12>`,
+
+    * - "bcm2835-isp0-capture1"
+      - source
+      - Image statistics calculated from the input image provided on the
+        "bcm2835-isp0-output0" pad.  Statistics are only available for Bayer
+        format input images.
+      - :ref:`v4l2-meta-fmt-bcm2835-isp-stats`.
+
+Pipeline Configuration
+----------------------
+
+The ISP pipeline can be configure through user-space by calling
+:ref:`VIDIOC_S_EXT_CTRLS <VIDIOC_G_EXT_CTRLS>` on the “bcm2835-isp0-output0”
+node with the appropriate parameters as shown in the table below.
+
+.. tabularcolumns:: |p{2cm}|p{5.0cm}|
+
+.. cssclass: longtable
+
+.. flat-table::
+
+    * - *id*
+      - *Parameter*
+
+    * - ``V4L2_CID_USER_BCM2835_ISP_CC_MATRIX``
+      - struct :c:type:`bcm2835_isp_custom_ccm`
+
+    * - ``V4L2_CID_USER_BCM2835_ISP_LENS_SHADING``
+      - struct :c:type:`bcm2835_isp_lens_shading`
+
+    * - ``V4L2_CID_USER_BCM2835_ISP_BLACK_LEVEL``
+      - struct :c:type:`bcm2835_isp_black_level`
+
+    * - ``V4L2_CID_USER_BCM2835_ISP_GEQ``
+      - struct :c:type:`bcm2835_isp_geq`
+
+    * - ``V4L2_CID_USER_BCM2835_ISP_GAMMA``
+      - struct :c:type:`bcm2835_isp_gamma`
+
+    * - ``V4L2_CID_USER_BCM2835_ISP_DENOISE``
+      - struct :c:type:`bcm2835_isp_denoise`
+
+    * - ``V4L2_CID_USER_BCM2835_ISP_SHARPEN``
+      - struct :c:type:`bcm2835_isp_sharpen`
+
+    * - ``V4L2_CID_USER_BCM2835_ISP_DPC``
+      - struct :c:type:`bcm2835_isp_dpc`
+
+++++++++++++++++++++++++
+Configuration Parameters
+++++++++++++++++++++++++
+
+.. kernel-doc:: include/uapi/linux/bcm2835-isp.h
+   :functions: bcm2835_isp_rational bcm2835_isp_ccm bcm2835_isp_custom_ccm
+                bcm2835_isp_gain_format bcm2835_isp_lens_shading
+                bcm2835_isp_black_level bcm2835_isp_geq bcm2835_isp_gamma
+                bcm2835_isp_denoise bcm2835_isp_sharpen
+                bcm2835_isp_dpc_mode bcm2835_isp_dpc
diff -Nur --no-dereference linux-5.10.52-orig/Documentation/devicetree/bindings/clock/raspberrypi,firmware-clocks.yaml linux-5.10.52-v7l+/Documentation/devicetree/bindings/clock/raspberrypi,firmware-clocks.yaml
--- linux-5.10.52-orig/Documentation/devicetree/bindings/clock/raspberrypi,firmware-clocks.yaml	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/Documentation/devicetree/bindings/clock/raspberrypi,firmware-clocks.yaml	2021-07-25 16:45:33.378837233 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/clock/raspberrypi,firmware-clocks.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: RaspberryPi Firmware Clocks Device Tree Bindings
+
+maintainers:
+  - Maxime Ripard <mripard@kernel.org>
+
+properties:
+  "#clock-cells":
+    const: 1
+
+  compatible:
+    const: raspberrypi,firmware-clocks
+
+required:
+  - "#clock-cells"
+  - compatible
+
+additionalProperties: false
+
+examples:
+  - |
+    firmware_clocks: firmware-clocks {
+        compatible = "raspberrypi,firmware-clocks";
+        #clock-cells = <1>;
+    };
+
+...
diff -Nur --no-dereference linux-5.10.52-orig/Documentation/devicetree/bindings/display/brcm,bcm2711-hdmi.yaml linux-5.10.52-v7l+/Documentation/devicetree/bindings/display/brcm,bcm2711-hdmi.yaml
--- linux-5.10.52-orig/Documentation/devicetree/bindings/display/brcm,bcm2711-hdmi.yaml	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/Documentation/devicetree/bindings/display/brcm,bcm2711-hdmi.yaml	2021-07-25 16:45:33.408836730 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:56 @
       - const: audio
       - const: cec
 
+  interrupts:
+    items:
+      - description: CEC TX interrupt
+      - description: CEC RX interrupt
+      - description: CEC stuck at low interrupt
+      - description: Wake-up interrupt
+      - description: Hotplug connected interrupt
+      - description: Hotplug removed interrupt
+
+  interrupt-names:
+    items:
+      - const: cec-tx
+      - const: cec-rx
+      - const: cec-low
+      - const: wakeup
+      - const: hpd-connected
+      - const: hpd-removed
+
   ddc:
     allOf:
       - $ref: /schemas/types.yaml#/definitions/phandle
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:111 @
   - resets
   - ddc
 
-additionalProperties: false
+unevaluatedProperties: false
 
 examples:
   - |
diff -Nur --no-dereference linux-5.10.52-orig/Documentation/devicetree/bindings/display/brcm,bcm2835-dsi0.yaml linux-5.10.52-v7l+/Documentation/devicetree/bindings/display/brcm,bcm2835-dsi0.yaml
--- linux-5.10.52-orig/Documentation/devicetree/bindings/display/brcm,bcm2835-dsi0.yaml	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/Documentation/devicetree/bindings/display/brcm,bcm2835-dsi0.yaml	2021-07-25 16:45:33.408836730 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:23 @
     enum:
       - brcm,bcm2835-dsi0
       - brcm,bcm2835-dsi1
+      - brcm,bcm2711-dsi1
 
   reg:
     maxItems: 1
diff -Nur --no-dereference linux-5.10.52-orig/Documentation/devicetree/bindings/display/brcm,bcm2835-hdmi.yaml linux-5.10.52-v7l+/Documentation/devicetree/bindings/display/brcm,bcm2835-hdmi.yaml
--- linux-5.10.52-orig/Documentation/devicetree/bindings/display/brcm,bcm2835-hdmi.yaml	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/Documentation/devicetree/bindings/display/brcm,bcm2835-hdmi.yaml	2021-07-25 16:45:33.408836730 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:14 @
 
 properties:
   compatible:
-    const: brcm,bcm2835-hdmi
+    enum:
+      - brcm,bcm2835-hdmi
+      - brcm,bcm2711-hdmi0
+      - brcm,bcm2711-hdmi1
 
   reg:
+    oneOf:
+      - items:
+        - description: HDMI register range
+        - description: HD register range
+
+      - items:
+        - description: HDMI controller register range
+        - description: DVP register range
+        - description: HDMI PHY register range
+        - description: Rate Manager register range
+        - description: Packet RAM register range
+        - description: Metadata RAM register range
+        - description: CSC register range
+        - description: CEC register range
+        - description: HD register range
+
+  reg-names:
     items:
-      - description: HDMI register range
-      - description: HD register range
+      - const: hdmi
+      - const: dvp
+      - const: phy
+      - const: rm
+      - const: packet
+      - const: metadata
+      - const: csc
+      - const: cec
+      - const: hd
 
   interrupts:
     minItems: 2
 
   clocks:
-    items:
-      - description: The pixel clock
-      - description: The HDMI state machine clock
+    oneOf:
+      - items:
+        - description: The pixel clock
+        - description: The HDMI state machine clock
+
+      - items:
+        - description: The HDMI state machine clock
 
   clock-names:
-    items:
-      - const: pixel
+    oneOf:
+      - items:
+        - const: pixel
+        - const: hdmi
+
       - const: hdmi
 
   ddc:
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:87 @
   dma-names:
     const: audio-rx
 
+  resets:
+    maxItems: 1
+
 required:
   - compatible
   - reg
-  - interrupts
   - clocks
   - ddc
 
 additionalProperties: false
 
+if:
+  properties:
+    compatible:
+      contains:
+        enum:
+          - brcm,bcm2711-hdmi0
+          - brcm,bcm2711-hdmi1
+
+then:
+  properties:
+    reg:
+      minItems: 9
+
+    clocks:
+      maxItems: 1
+
+    clock-names:
+      maxItems: 1
+
+  required:
+    - reg-names
+    - resets
+
+else:
+  properties:
+    reg:
+      maxItems: 2
+
+    clocks:
+      minItems: 2
+
+    clock-names:
+      minItems: 2
+
+  required:
+    - interrupts
+
 examples:
   - |
     #include <dt-bindings/clock/bcm2835.h>
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:152 @
         clock-names = "pixel", "hdmi";
     };
 
+  - |
+    hdmi0: hdmi@7ef00700 {
+        compatible = "brcm,bcm2711-hdmi0";
+        reg = <0x7ef00700 0x300>,
+              <0x7ef00300 0x200>,
+              <0x7ef00f00 0x80>,
+              <0x7ef00f80 0x80>,
+              <0x7ef01b00 0x200>,
+              <0x7ef01f00 0x400>,
+              <0x7ef00200 0x80>,
+              <0x7ef04300 0x100>,
+              <0x7ef20000 0x100>;
+        reg-names = "hdmi",
+                    "dvp",
+                    "phy",
+                    "rm",
+                    "packet",
+                    "metadata",
+                    "csc",
+                    "cec",
+                    "hd";
+        clocks = <&firmware_clocks 13>;
+        clock-names = "hdmi";
+        resets = <&dvp 0>;
+        ddc = <&ddc0>;
+    };
+
 ...
diff -Nur --no-dereference linux-5.10.52-orig/Documentation/devicetree/bindings/display/brcm,bcm2835-vc4.yaml linux-5.10.52-v7l+/Documentation/devicetree/bindings/display/brcm,bcm2835-vc4.yaml
--- linux-5.10.52-orig/Documentation/devicetree/bindings/display/brcm,bcm2835-vc4.yaml	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/Documentation/devicetree/bindings/display/brcm,bcm2835-vc4.yaml	2021-07-25 16:45:33.408836730 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:24 @
       - brcm,bcm2835-vc4
       - brcm,cygnus-vc4
 
+  raspberrypi,firmware:
+    $ref: /schemas/types.yaml#/definitions/phandle
+    description: >
+      Phandle to the mailbox node to communicate with the firmware.
+
 required:
   - compatible
 
diff -Nur --no-dereference linux-5.10.52-orig/Documentation/devicetree/bindings/display/brcm,bcm2835-vec.yaml linux-5.10.52-v7l+/Documentation/devicetree/bindings/display/brcm,bcm2835-vec.yaml
--- linux-5.10.52-orig/Documentation/devicetree/bindings/display/brcm,bcm2835-vec.yaml	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/Documentation/devicetree/bindings/display/brcm,bcm2835-vec.yaml	2021-07-25 16:45:33.408836730 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:14 @
 
 properties:
   compatible:
-    const: brcm,bcm2835-vec
+    enum:
+      - brcm,bcm2835-vec
+      - brcm,bcm2711-vec
 
   reg:
     maxItems: 1
diff -Nur --no-dereference linux-5.10.52-orig/Documentation/devicetree/bindings/display/panel/panel-simple.yaml linux-5.10.52-v7l+/Documentation/devicetree/bindings/display/panel/panel-simple.yaml
--- linux-5.10.52-orig/Documentation/devicetree/bindings/display/panel/panel-simple.yaml	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/Documentation/devicetree/bindings/display/panel/panel-simple.yaml	2021-07-25 16:45:33.428836395 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:150 @
       - ivo,m133nwf4-r0
         # Innolux AT043TN24 4.3" WQVGA TFT LCD panel
       - innolux,at043tn24
+        # Innolux AT056tN53V1 5.6" VGA (640x480) TFT LCD panel
+      - innolux,at056tn53v1
         # Innolux AT070TN92 7.0" WQVGA TFT LCD panel
       - innolux,at070tn92
         # Innolux G070Y2-L01 7" WVGA (800x480) TFT LCD panel
diff -Nur --no-dereference linux-5.10.52-orig/Documentation/devicetree/bindings/hwmon/rpi-poe-fan.txt linux-5.10.52-v7l+/Documentation/devicetree/bindings/hwmon/rpi-poe-fan.txt
--- linux-5.10.52-orig/Documentation/devicetree/bindings/hwmon/rpi-poe-fan.txt	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/Documentation/devicetree/bindings/hwmon/rpi-poe-fan.txt	2021-07-25 16:45:33.478835557 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+Bindings for the Raspberry Pi PoE HAT fan
+
+Required properties:
+- compatible	: "raspberrypi,rpi-poe-fan"
+- firmware	: Reference to the RPi firmware device node
+- pwms		: the PWM that is used to control the PWM fan
+- cooling-levels      : PWM duty cycle values in a range from 0 to 255
+			which correspond to thermal cooling states
+
+Example:
+	fan0: rpi-poe-fan@0 {
+		compatible = "raspberrypi,rpi-poe-fan";
+		firmware = <&firmware>;
+		cooling-min-state = <0>;
+		cooling-max-state = <3>;
+		#cooling-cells = <2>;
+		cooling-levels = <0 50 150 255>;
+		status = "okay";
+	};
+
+	thermal-zones {
+		cpu_thermal: cpu-thermal {
+			trips {
+				threshold: trip-point@0 {
+					temperature = <45000>;
+					hysteresis = <5000>;
+					type = "active";
+				};
+				target: trip-point@1 {
+					temperature = <50000>;
+					hysteresis = <2000>;
+					type = "active";
+				};
+				cpu_hot: cpu_hot@0 {
+					temperature = <55000>;
+					hysteresis = <2000>;
+					type = "active";
+				};
+			};
+			cooling-maps {
+				map0 {
+					trip = <&threshold>;
+					cooling-device = <&fan0 0 1>;
+				};
+				map1 {
+					trip = <&target>;
+					cooling-device = <&fan0 1 2>;
+				};
+				map2 {
+					trip = <&cpu_hot>;
+					cooling-device = <&fan0 2 3>;
+				};
+			};
+		};
+	};
diff -Nur --no-dereference linux-5.10.52-orig/Documentation/devicetree/bindings/media/bcm2835-unicam.txt linux-5.10.52-v7l+/Documentation/devicetree/bindings/media/bcm2835-unicam.txt
--- linux-5.10.52-orig/Documentation/devicetree/bindings/media/bcm2835-unicam.txt	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/Documentation/devicetree/bindings/media/bcm2835-unicam.txt	2021-07-25 16:45:33.568834048 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+Broadcom BCM283x Camera Interface (Unicam)
+------------------------------------------
+
+The Unicam block on BCM283x SoCs is the receiver for either
+CSI-2 or CCP2 data from image sensors or similar devices.
+
+The main platform using this SoC is the Raspberry Pi family of boards.
+On the Pi the VideoCore firmware can also control this hardware block,
+and driving it from two different processors will cause issues.
+To avoid this, the firmware checks the device tree configuration
+during boot. If it finds device tree nodes called csi0 or csi1 then
+it will stop the firmware accessing the block, and it can then
+safely be used via the device tree binding.
+
+Required properties:
+===================
+- compatible	: must be "brcm,bcm2835-unicam".
+- reg		: physical base address and length of the register sets for the
+		  device.
+- interrupts	: should contain the IRQ line for this Unicam instance.
+- clocks	: list of clock specifiers, corresponding to entries in
+		  clock-names property.
+- clock-names	: must contain "lp" and "vpu" entries, matching entries in the
+		  clocks property.
+
+Unicam supports a single port node. It should contain one 'port' child node
+with child 'endpoint' node. Please refer to the bindings defined in
+Documentation/devicetree/bindings/media/video-interfaces.txt.
+
+Within the endpoint node the "remote-endpoint" and "data-lanes" properties
+are mandatory.
+Data lane reordering is not supported so the data lanes must be in order,
+starting at 1. The number of data lanes should represent the number of
+usable lanes for the hardware block. That may be limited by either the SoC or
+how the platform presents the interface, and the lower value must be used.
+
+Lane reordering is not supported on the clock lane either, so the optional
+property "clock-lane" will implicitly be <0>.
+Similarly lane inversion is not supported, therefore "lane-polarities" will
+implicitly be <0 0 0 0 0>.
+Neither of these values will be checked.
+
+Example:
+	csi1: csi1@7e801000 {
+		compatible = "brcm,bcm2835-unicam";
+		reg = <0x7e801000 0x800>,
+		      <0x7e802004 0x4>;
+		interrupts = <2 7>;
+		clocks = <&clocks BCM2835_CLOCK_CAM1>,
+			 <&firmware_clocks 4>;
+		clock-names = "lp", "vpu";
+		port {
+			csi1_ep: endpoint {
+				remote-endpoint = <&tc358743_0>;
+				data-lanes = <1 2>;
+			};
+		};
+	};
+
+	i2c0: i2c@7e205000 {
+		tc358743: csi-hdmi-bridge@0f {
+			compatible = "toshiba,tc358743";
+			reg = <0x0f>;
+
+			clocks = <&tc358743_clk>;
+			clock-names = "refclk";
+
+			tc358743_clk: bridge-clk {
+				compatible = "fixed-clock";
+				#clock-cells = <0>;
+				clock-frequency = <27000000>;
+			};
+
+			port {
+				tc358743_0: endpoint {
+					remote-endpoint = <&csi1_ep>;
+					clock-lanes = <0>;
+					data-lanes = <1 2>;
+					clock-noncontinuous;
+					link-frequencies =
+						/bits/ 64 <297000000>;
+				};
+			};
+		};
+	};
diff -Nur --no-dereference linux-5.10.52-orig/Documentation/devicetree/bindings/media/i2c/imx219.txt linux-5.10.52-v7l+/Documentation/devicetree/bindings/media/i2c/imx219.txt
--- linux-5.10.52-orig/Documentation/devicetree/bindings/media/i2c/imx219.txt	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/Documentation/devicetree/bindings/media/i2c/imx219.txt	2021-07-25 16:45:33.568834048 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+* Sony 1/4.0-Inch 8Mpixel CMOS Digital Image Sensor
+
+The Sony imx219 is a 1/4.0-inch CMOS active pixel digital image sensor with
+an active array size of 3280H x 2464V. It is programmable through I2C
+interface. The I2C address is fixed to 0x10 as per sensor data sheet.
+Image data is sent through MIPI CSI-2, which is configured as either 2 or 4
+data lanes.
+
+Required Properties:
+- compatible: value should be "sony,imx219" for imx219 sensor
+- reg: I2C bus address of the device
+- clocks: reference to the xclk input clock.
+- clock-names: should be "xclk".
+- DOVDD-supply: Digital I/O voltage supply, 1.8 volts
+- AVDD-supply: Analog voltage supply, 2.8 volts
+- DVDD-supply: Digital core voltage supply, 1.2 volts
+
+Optional Properties:
+- xclr-gpios: reference to the GPIO connected to the xclr pin, if any. Must be
+	      released after all supplies are applied.
+	      This is an active high signal to the imx219.
+
+The imx219 device node should contain one 'port' child node with
+an 'endpoint' subnode. For further reading on port node refer to
+Documentation/devicetree/bindings/media/video-interfaces.txt.
+
+Endpoint node required properties for CSI-2 connection are:
+- remote-endpoint: a phandle to the bus receiver's endpoint node.
+- clock-lanes: should be set to <0> (clock lane on hardware lane 0)
+- data-lanes: should be set to <1 2>, or  <1 2 3 4> (two or four lane CSI-2
+  supported)
+
+Example:
+	sensor@10 {
+		compatible = "sony,imx219";
+		reg = <0x10>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+		clocks = <&imx219_clk>;
+		clock-names = "xclk";
+		xclr-gpios = <&gpio_sensor 0 0>;
+		DOVDD-supply = <&vgen4_reg>; /* 1.8v */
+		AVDD-supply = <&vgen3_reg>;  /* 2.8v */
+		DVDD-supply = <&vgen2_reg>;  /* 1.2v */
+
+		imx219_clk: camera-clk {
+			compatible = "fixed-clock";
+			#clock-cells = <0>;
+			clock-frequency = <24000000>;
+		};
+
+		port {
+			sensor_out: endpoint {
+				remote-endpoint = <&csiss_in>;
+				clock-lanes = <0>;
+				data-lanes = <1 2>;
+			};
+		};
+	};
diff -Nur --no-dereference linux-5.10.52-orig/Documentation/devicetree/bindings/media/i2c/imx290.txt linux-5.10.52-v7l+/Documentation/devicetree/bindings/media/i2c/imx290.txt
--- linux-5.10.52-orig/Documentation/devicetree/bindings/media/i2c/imx290.txt	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/Documentation/devicetree/bindings/media/i2c/imx290.txt	2021-07-25 16:45:33.568834048 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
 * Sony IMX290 1/2.8-Inch CMOS Image Sensor
 
 The Sony IMX290 is a 1/2.8-Inch CMOS Solid-state image sensor with
-Square Pixel for Color Cameras. It is programmable through I2C and 4-wire
-interfaces. The sensor output is available via CMOS logic parallel SDR output,
+Square Pixel for Color or Monochrome Cameras. It is programmable through I2C
+and 4-wire interfaces.
+The sensor output is available via CMOS logic parallel SDR output,
 Low voltage LVDS DDR output and CSI-2 serial data output. The CSI-2 bus is the
 default. No bindings have been defined for the other busses.
 
 Required Properties:
-- compatible: Should be "sony,imx290"
+- compatible: Should be "sony,imx290", or "sony,imx290-mono"
 - reg: I2C bus address of the device
 - clocks: Reference to the xclk clock.
 - clock-names: Should be "xclk".
diff -Nur --no-dereference linux-5.10.52-orig/Documentation/devicetree/bindings/media/i2c/imx378.yaml linux-5.10.52-v7l+/Documentation/devicetree/bindings/media/i2c/imx378.yaml
--- linux-5.10.52-orig/Documentation/devicetree/bindings/media/i2c/imx378.yaml	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/Documentation/devicetree/bindings/media/i2c/imx378.yaml	2021-07-25 16:45:33.568834048 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/media/i2c/imx378.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Sony 1/2.3-Inch 12Mpixel CMOS Digital Image Sensor
+
+maintainers:
+  - Naushir Patuck <naush@raspberypi.com>
+
+description: |-
+  The Sony IMX378 is a 1/2.3-inch CMOS active pixel digital image sensor
+  with an active array size of 4056H x 3040V. It is programmable through
+  I2C interface. The I2C address is fixed to 0x1A as per sensor data sheet.
+  Image data is sent through MIPI CSI-2, which is configured as either 2 or
+  4 data lanes.
+
+properties:
+  compatible:
+    const: sony,imx378
+
+  reg:
+    description: I2C device address
+    maxItems: 1
+
+  clocks:
+    maxItems: 1
+
+  VDIG-supply:
+    description:
+      Digital I/O voltage supply, 1.05 volts
+
+  VANA-supply:
+    description:
+      Analog voltage supply, 2.8 volts
+
+  VDDL-supply:
+    description:
+      Digital core voltage supply, 1.8 volts
+
+  reset-gpios:
+    description: |-
+      Reference to the GPIO connected to the xclr pin, if any.
+      Must be released (set high) after all supplies and INCK are applied.
+
+  # See ../video-interfaces.txt for more details
+  port:
+    type: object
+    properties:
+      endpoint:
+        type: object
+        properties:
+          data-lanes:
+            description: |-
+              The sensor supports either two-lane, or four-lane operation.
+              For two-lane operation the property must be set to <1 2>.
+            items:
+              - const: 1
+              - const: 2
+
+          clock-noncontinuous:
+            type: boolean
+            description: |-
+              MIPI CSI-2 clock is non-continuous if this property is present,
+              otherwise it's continuous.
+
+          link-frequencies:
+            allOf:
+              - $ref: /schemas/types.yaml#/definitions/uint64-array
+            description:
+              Allowed data bus frequencies.
+
+        required:
+          - link-frequencies
+
+required:
+  - compatible
+  - reg
+  - clocks
+  - VANA-supply
+  - VDIG-supply
+  - VDDL-supply
+  - port
+
+additionalProperties: false
+
+examples:
+  - |
+    i2c0 {
+        #address-cells = <1>;
+        #size-cells = <0>;
+
+        imx378: sensor@10 {
+            compatible = "sony,imx378";
+            reg = <0x1a>;
+            clocks = <&imx378_clk>;
+            VANA-supply = <&imx378_vana>;   /* 2.8v */
+            VDIG-supply = <&imx378_vdig>;   /* 1.05v */
+            VDDL-supply = <&imx378_vddl>;   /* 1.8v */
+
+            port {
+                imx378_0: endpoint {
+                    remote-endpoint = <&csi1_ep>;
+                    data-lanes = <1 2>;
+                    clock-noncontinuous;
+                    link-frequencies = /bits/ 64 <450000000>;
+                };
+            };
+        };
+    };
+
+...
diff -Nur --no-dereference linux-5.10.52-orig/Documentation/devicetree/bindings/media/i2c/imx477.yaml linux-5.10.52-v7l+/Documentation/devicetree/bindings/media/i2c/imx477.yaml
--- linux-5.10.52-orig/Documentation/devicetree/bindings/media/i2c/imx477.yaml	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/Documentation/devicetree/bindings/media/i2c/imx477.yaml	2021-07-25 16:45:33.568834048 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/media/i2c/imx477.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Sony 1/2.3-Inch 12Mpixel CMOS Digital Image Sensor
+
+maintainers:
+  - Naushir Patuck <naush@raspberypi.com>
+
+description: |-
+  The Sony IMX477 is a 1/2.3-inch CMOS active pixel digital image sensor
+  with an active array size of 4056H x 3040V. It is programmable through
+  I2C interface. The I2C address is fixed to 0x1A as per sensor data sheet.
+  Image data is sent through MIPI CSI-2, which is configured as either 2 or
+  4 data lanes.
+
+properties:
+  compatible:
+    const: sony,imx477
+
+  reg:
+    description: I2C device address
+    maxItems: 1
+
+  clocks:
+    maxItems: 1
+
+  VDIG-supply:
+    description:
+      Digital I/O voltage supply, 1.05 volts
+
+  VANA-supply:
+    description:
+      Analog voltage supply, 2.8 volts
+
+  VDDL-supply:
+    description:
+      Digital core voltage supply, 1.8 volts
+
+  reset-gpios:
+    description: |-
+      Reference to the GPIO connected to the xclr pin, if any.
+      Must be released (set high) after all all supplies and INCK are applied.
+
+  # See ../video-interfaces.txt for more details
+  port:
+    type: object
+    properties:
+      endpoint:
+        type: object
+        properties:
+          data-lanes:
+            description: |-
+              The sensor supports either two-lane, or four-lane operation.
+              For two-lane operation the property must be set to <1 2>.
+            items:
+              - const: 1
+              - const: 2
+
+          clock-noncontinuous:
+            type: boolean
+            description: |-
+              MIPI CSI-2 clock is non-continuous if this property is present,
+              otherwise it's continuous.
+
+          link-frequencies:
+            allOf:
+              - $ref: /schemas/types.yaml#/definitions/uint64-array
+            description:
+              Allowed data bus frequencies.
+
+        required:
+          - link-frequencies
+
+required:
+  - compatible
+  - reg
+  - clocks
+  - VANA-supply
+  - VDIG-supply
+  - VDDL-supply
+  - port
+
+additionalProperties: false
+
+examples:
+  - |
+    i2c0 {
+        #address-cells = <1>;
+        #size-cells = <0>;
+
+        imx477: sensor@10 {
+            compatible = "sony,imx477";
+            reg = <0x1a>;
+            clocks = <&imx477_clk>;
+            VANA-supply = <&imx477_vana>;   /* 2.8v */
+            VDIG-supply = <&imx477_vdig>;   /* 1.05v */
+            VDDL-supply = <&imx477_vddl>;   /* 1.8v */
+
+            port {
+                imx477_0: endpoint {
+                    remote-endpoint = <&csi1_ep>;
+                    data-lanes = <1 2>;
+                    clock-noncontinuous;
+                    link-frequencies = /bits/ 64 <450000000>;
+                };
+            };
+        };
+    };
+
+...
diff -Nur --no-dereference linux-5.10.52-orig/Documentation/devicetree/bindings/media/i2c/irs1125.txt linux-5.10.52-v7l+/Documentation/devicetree/bindings/media/i2c/irs1125.txt
--- linux-5.10.52-orig/Documentation/devicetree/bindings/media/i2c/irs1125.txt	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/Documentation/devicetree/bindings/media/i2c/irs1125.txt	2021-07-25 16:45:33.568834048 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+* Infineon irs1125 time of flight sensor
+
+The Infineon irs1125 is a time of flight digital image sensor with
+an active array size of 352H x 286V. It is programmable through I2C
+interface. The I2C address defaults to 0x3D, but can be reconfigured
+to address 0x3C or 0x41 via I2C commands. Image data is sent through
+MIPI CSI-2, which is configured as either 1 or 2 data lanes.
+
+Required Properties:
+- compatible: value should be "infineon,irs1125" for irs1125 sensor
+- reg: I2C bus address of the device
+- clocks: reference to the xclk input clock.
+- pwdn-gpios: reference to the GPIO connected to the reset pin.
+	      This is an active low signal to the iirs1125.
+
+The irs1125 device node should contain one 'port' child node with
+an 'endpoint' subnode. For further reading on port node refer to
+Documentation/devicetree/bindings/media/video-interfaces.txt.
+
+Endpoint node required properties for CSI-2 connection are:
+- remote-endpoint: a phandle to the bus receiver's endpoint node.
+- clock-lanes: should be set to <0> (clock lane on hardware lane 0)
+- data-lanes: should be set to <1> or <1 2> (one or two lane CSI-2
+  supported)
+
+Example:
+	sensor@10 {
+		compatible = "infineon,irs1125";
+		reg = <0x3D>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+		clocks = <&irs1125_clk>;
+		pwdn-gpios = <&gpio 5 0>;
+
+		irs1125_clk: camera-clk {
+			compatible = "fixed-clock";
+			#clock-cells = <0>;
+			clock-frequency = <26000000>;
+		};
+
+		port {
+			sensor_out: endpoint {
+				remote-endpoint = <&csiss_in>;
+				clock-lanes = <0>;
+				data-lanes = <1 2>;
+			};
+		};
+	};
diff -Nur --no-dereference linux-5.10.52-orig/Documentation/devicetree/bindings/media/rpivid_hevc.yaml linux-5.10.52-v7l+/Documentation/devicetree/bindings/media/rpivid_hevc.yaml
--- linux-5.10.52-orig/Documentation/devicetree/bindings/media/rpivid_hevc.yaml	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/Documentation/devicetree/bindings/media/rpivid_hevc.yaml	2021-07-25 16:45:33.578833880 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+# SPDX-License-Identifier: GPL-2.0-only
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/media/rpivid_hevc.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Raspberry Pi HEVC Decoder
+
+maintainers:
+  - Raspberry Pi <kernel-list@raspberrypi.com>
+
+description: |-
+  The Camera Adaptation Layer (CAL) is a key component for image capture
+  applications. The capture module provides the system interface and the
+  processing capability to connect CSI2 image-sensor modules to the
+  DRA72x device.
+
+properties:
+  compatible:
+    enum:
+      - raspberrypi,rpivid-vid-decoder
+
+  reg:
+    minItems: 2
+    items:
+      - description: The HEVC main register region
+      - description: The Interrupt controller register region
+
+  reg-names:
+    minItems: 2
+    items:
+      - const: hevc
+      - const: intc
+
+  interrupts:
+    maxItems: 1
+
+  clocks:
+    items:
+      - description: The HEVC block clock
+
+  clock-names:
+    items:
+      - const: hevc
+
+required:
+  - compatible
+  - reg
+  - reg-names
+  - interrupts
+  - clocks
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/interrupt-controller/arm-gic.h>
+
+    video-codec@7eb10000 {
+        compatible = "raspberrypi,rpivid-vid-decoder";
+        reg = <0x0 0x7eb10000 0x1000>,	/* INTC */
+              <0x0 0x7eb00000 0x10000>; /* HEVC */
+        reg-names = "intc",
+                    "hevc";
+
+        interrupts = <GIC_SPI 98 IRQ_TYPE_LEVEL_HIGH>;
+
+        clocks = <&clk 0>;
+        clock-names = "hevc";
+    };
+
+...
diff -Nur --no-dereference linux-5.10.52-orig/Documentation/devicetree/bindings/misc/brcm,bcm2835-smi-dev.txt linux-5.10.52-v7l+/Documentation/devicetree/bindings/misc/brcm,bcm2835-smi-dev.txt
--- linux-5.10.52-orig/Documentation/devicetree/bindings/misc/brcm,bcm2835-smi-dev.txt	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/Documentation/devicetree/bindings/misc/brcm,bcm2835-smi-dev.txt	2021-07-25 16:45:33.608833377 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+* Broadcom BCM2835 SMI character device driver.
+
+SMI or secondary memory interface is a peripheral specific to certain Broadcom
+SOCs, and is helpful for talking to things like parallel-interface displays
+and NAND flashes (in fact, most things with a parallel register interface).
+
+This driver adds a character device which provides a user-space interface to
+an instance of the SMI driver.
+
+Required properties:
+- compatible: "brcm,bcm2835-smi-dev"
+- smi_handle: a phandle to the smi node.
+
+Optional properties:
+- None.
+
+
diff -Nur --no-dereference linux-5.10.52-orig/Documentation/devicetree/bindings/misc/brcm,bcm2835-smi.txt linux-5.10.52-v7l+/Documentation/devicetree/bindings/misc/brcm,bcm2835-smi.txt
--- linux-5.10.52-orig/Documentation/devicetree/bindings/misc/brcm,bcm2835-smi.txt	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/Documentation/devicetree/bindings/misc/brcm,bcm2835-smi.txt	2021-07-25 16:45:33.608833377 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+* Broadcom BCM2835 SMI driver.
+
+SMI or secondary memory interface is a peripheral specific to certain Broadcom
+SOCs, and is helpful for talking to things like parallel-interface displays
+and NAND flashes (in fact, most things with a parallel register interface).
+
+Required properties:
+- compatible: "brcm,bcm2835-smi"
+- reg: Should contain location and length of SMI registers and SMI clkman regs
+- interrupts: *the* SMI interrupt.
+- pinctrl-names: should be "default".
+- pinctrl-0: the phandle of the gpio pin node.
+- brcm,smi-clock-source: the clock source for clkman
+- brcm,smi-clock-divisor: the integer clock divisor for clkman
+- dmas: the dma controller phandle and the DREQ number (4 on a 2835)
+- dma-names: the name used by the driver to request its channel.
+  Should be "rx-tx".
+
+Optional properties:
+- None.
+
+Examples:
+
+8 data pin configuration:
+
+smi: smi@7e600000 {
+	compatible = "brcm,bcm2835-smi";
+	reg = <0x7e600000 0x44>, <0x7e1010b0 0x8>;
+	interrupts = <2 16>;
+	pinctrl-names = "default";
+	pinctrl-0 = <&smi_pins>;
+	brcm,smi-clock-source = <6>;
+	brcm,smi-clock-divisor = <4>;
+	dmas = <&dma 4>;
+	dma-names = "rx-tx";
+
+	status = "okay";
+};
+
+smi_pins: smi_pins {
+	brcm,pins = <2 3 4 5 6 7 8 9 10 11 12 13 14 15>;
+	/* Alt 1: SMI */
+	brcm,function = <5 5 5 5 5 5 5 5 5 5 5 5 5 5>;
+	/* /CS, /WE and /OE are pulled high, as they are
+	   generally active low signals */
+	brcm,pull = <2 2 2 2 2 2 0 0 0 0 0 0 0 0>;
+};
+
diff -Nur --no-dereference linux-5.10.52-orig/Documentation/devicetree/bindings/net/microchip,lan78xx.txt linux-5.10.52-v7l+/Documentation/devicetree/bindings/net/microchip,lan78xx.txt
--- linux-5.10.52-orig/Documentation/devicetree/bindings/net/microchip,lan78xx.txt	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/Documentation/devicetree/bindings/net/microchip,lan78xx.txt	2021-07-25 16:45:33.658832539 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:17 @
 - microchip,led-modes: a 0..4 element vector, with each element configuring
   the operating mode of an LED. Omitted LEDs are turned off. Allowed values
   are defined in "include/dt-bindings/net/microchip-lan78xx.h".
+- microchip,downshift-after: sets the number of failed auto-negotiation
+  attempts after which the link is downgraded from 1000BASE-T. Should be one of
+  2, 3, 4, 5 or 0, where 0 means never downshift.
 
 Example:
 
diff -Nur --no-dereference linux-5.10.52-orig/Documentation/devicetree/bindings/nvmem/rmem.yaml linux-5.10.52-v7l+/Documentation/devicetree/bindings/nvmem/rmem.yaml
--- linux-5.10.52-orig/Documentation/devicetree/bindings/nvmem/rmem.yaml	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/Documentation/devicetree/bindings/nvmem/rmem.yaml	2021-07-25 16:45:33.668832371 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/nvmem/rmem.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Reserved Memory Based nvmem Device
+
+maintainers:
+  - Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
+
+allOf:
+  - $ref: "nvmem.yaml#"
+
+properties:
+  compatible:
+    items:
+      - enum:
+        - raspberrypi,bootloader-config
+      - const: nvmem-rmem
+
+  no-map:
+    $ref: /schemas/types.yaml#/definitions/flag
+    description:
+      Avoid creating a virtual mapping of the region as part of the OS'
+      standard mapping of system memory.
+
+required:
+  - compatible
+  - no-map
+
+unevaluatedProperties: false
+
+examples:
+  - |
+        reserved-memory {
+                #address-cells = <1>;
+                #size-cells = <1>;
+
+                blconfig: nvram@10000000 {
+                        compatible = "raspberrypi,bootloader-config", "nvmem-rmem";
+                        #address-cells = <1>;
+                        #size-cells = <1>;
+                        reg = <0x10000000 0x1000>;
+                        no-map;
+                };
+        };
+
+...
diff -Nur --no-dereference linux-5.10.52-orig/Documentation/devicetree/bindings/pci/brcmstb-pcie.txt linux-5.10.52-v7l+/Documentation/devicetree/bindings/pci/brcmstb-pcie.txt
--- linux-5.10.52-orig/Documentation/devicetree/bindings/pci/brcmstb-pcie.txt	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/Documentation/devicetree/bindings/pci/brcmstb-pcie.txt	2021-07-25 16:45:33.678832204 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+Brcmstb PCIe Host Controller Device Tree Bindings
+
+Required Properties:
+- compatible
+  "brcm,bcm7425-pcie" -- for 7425 family MIPS-based SOCs.
+  "brcm,bcm7435-pcie" -- for 7435 family MIPS-based SOCs.
+  "brcm,bcm7445-pcie" -- for 7445 and later ARM based SOCs (not including
+      the 7278).
+  "brcm,bcm7278-pcie"  -- for 7278 family ARM-based SOCs.
+
+- reg -- the register start address and length for the PCIe reg block.
+- interrupts -- two interrupts are specified; the first interrupt is for
+     the PCI host controller and the second is for MSI if the built-in
+     MSI controller is to be used.
+- interrupt-names -- names of the interrupts (above): "pcie" and "msi".
+- #address-cells -- set to <3>.
+- #size-cells -- set to <2>.
+- #interrupt-cells: set to <1>.
+- interrupt-map-mask and interrupt-map, standard PCI properties to define the
+     mapping of the PCIe interface to interrupt numbers.
+- ranges: ranges for the PCI memory and I/O regions.
+- linux,pci-domain -- should be unique per host controller.
+
+Optional Properties:
+- clocks -- phandle of pcie clock.
+- clock-names -- set to "sw_pcie" if clocks is used.
+- dma-ranges -- Specifies the inbound memory mapping regions when
+     an "identity map" is not possible.
+- msi-controller -- this property is typically specified to have the
+     PCIe controller use its internal MSI controller.
+- msi-parent -- set to use an external MSI interrupt controller.
+- brcm,enable-ssc -- (boolean) indicates usage of spread-spectrum clocking.
+- max-link-speed --  (integer) indicates desired generation of link:
+     1 => 2.5 Gbps (gen1), 2 => 5.0 Gbps (gen2), 3 => 8.0 Gbps (gen3).
+
+Example Node:
+
+pcie0: pcie@f0460000 {
+		reg = <0x0 0xf0460000 0x0 0x9310>;
+		interrupts = <0x0 0x0 0x4>;
+		compatible = "brcm,bcm7445-pcie";
+		#address-cells = <3>;
+		#size-cells = <2>;
+		ranges = <0x02000000 0x00000000 0x00000000 0x00000000 0xc0000000 0x00000000 0x08000000
+			  0x02000000 0x00000000 0x08000000 0x00000000 0xc8000000 0x00000000 0x08000000>;
+		#interrupt-cells = <1>;
+		interrupt-map-mask = <0 0 0 7>;
+		interrupt-map = <0 0 0 1 &intc 0 47 3
+				 0 0 0 2 &intc 0 48 3
+				 0 0 0 3 &intc 0 49 3
+				 0 0 0 4 &intc 0 50 3>;
+		clocks = <&sw_pcie0>;
+		clock-names = "sw_pcie";
+		msi-parent = <&pcie0>;  /* use PCIe's internal MSI controller */
+		msi-controller;         /* use PCIe's internal MSI controller */
+		brcm,ssc;
+		max-link-speed = <1>;
+		linux,pci-domain = <0>;
+	};
diff -Nur --no-dereference linux-5.10.52-orig/Documentation/devicetree/bindings/power/reset/gpio-poweroff.txt linux-5.10.52-v7l+/Documentation/devicetree/bindings/power/reset/gpio-poweroff.txt
--- linux-5.10.52-orig/Documentation/devicetree/bindings/power/reset/gpio-poweroff.txt	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/Documentation/devicetree/bindings/power/reset/gpio-poweroff.txt	2021-07-25 16:45:33.728831365 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:34 @
 - inactive-delay-ms: Delay (default 100) to wait after driving gpio inactive
 - timeout-ms: Time to wait before asserting a WARN_ON(1). If nothing is
               specified, 3000 ms is used.
+- export : Export the GPIO line to the sysfs system
 
 Examples:
 
diff -Nur --no-dereference linux-5.10.52-orig/Documentation/devicetree/bindings/serial/pl011.yaml linux-5.10.52-v7l+/Documentation/devicetree/bindings/serial/pl011.yaml
--- linux-5.10.52-orig/Documentation/devicetree/bindings/serial/pl011.yaml	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/Documentation/devicetree/bindings/serial/pl011.yaml	2021-07-25 16:45:33.798830192 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:101 @
     $ref: /schemas/types.yaml#/definitions/uint32
     default: 3000
 
+  cts-event-workaround:
+    description:
+      Enables the (otherwise vendor-specific) workaround for the
+      CTS-induced TX lockup.
+    type: boolean
+
 required:
   - compatible
   - reg
diff -Nur --no-dereference linux-5.10.52-orig/Documentation/devicetree/bindings/vendor-prefixes.txt linux-5.10.52-v7l+/Documentation/devicetree/bindings/vendor-prefixes.txt
--- linux-5.10.52-orig/Documentation/devicetree/bindings/vendor-prefixes.txt	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/Documentation/devicetree/bindings/vendor-prefixes.txt	2021-07-25 16:45:33.898828515 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+Device tree binding vendor prefix registry.  Keep list in alphabetical order.
+
+This isn't an exhaustive list, but you should add new prefixes to it before
+using them to avoid name-space collisions.
+
+abilis	Abilis Systems
+abracon	Abracon Corporation
+actions	Actions Semiconductor Co., Ltd.
+active-semi	Active-Semi International Inc
+ad	Avionic Design GmbH
+adafruit	Adafruit Industries, LLC
+adapteva	Adapteva, Inc.
+adaptrum	Adaptrum, Inc.
+adh	AD Holdings Plc.
+adi	Analog Devices, Inc.
+advantech	Advantech Corporation
+aeroflexgaisler	Aeroflex Gaisler AB
+al	Annapurna Labs
+allo	Allo.com
+allwinner	Allwinner Technology Co., Ltd.
+alphascale	AlphaScale Integrated Circuits Systems, Inc.
+altr	Altera Corp.
+amarula	Amarula Solutions
+amazon	Amazon.com, Inc.
+amcc	Applied Micro Circuits Corporation (APM, formally AMCC)
+amd	Advanced Micro Devices (AMD), Inc.
+amediatech	Shenzhen Amediatech Technology Co., Ltd
+amlogic	Amlogic, Inc.
+ampire	Ampire Co., Ltd.
+ams	AMS AG
+amstaos	AMS-Taos Inc.
+analogix	Analogix Semiconductor, Inc.
+andestech	Andes Technology Corporation
+apm	Applied Micro Circuits Corporation (APM)
+aptina	Aptina Imaging
+arasan	Arasan Chip Systems
+archermind ArcherMind Technology (Nanjing) Co., Ltd.
+arctic	Arctic Sand
+aries	Aries Embedded GmbH
+arm	ARM Ltd.
+armadeus	ARMadeus Systems SARL
+arrow	Arrow Electronics
+artesyn	Artesyn Embedded Technologies Inc.
+asahi-kasei	Asahi Kasei Corp.
+aspeed	ASPEED Technology Inc.
+asus	AsusTek Computer Inc.
+atlas	Atlas Scientific LLC
+atmel	Atmel Corporation
+auo	AU Optronics Corporation
+auvidea Auvidea GmbH
+avago	Avago Technologies
+avia	avia semiconductor
+avic	Shanghai AVIC Optoelectronics Co., Ltd.
+avnet	Avnet, Inc.
+axentia	Axentia Technologies AB
+axis	Axis Communications AB
+bananapi BIPAI KEJI LIMITED
+bhf	Beckhoff Automation GmbH & Co. KG
+bitmain	Bitmain Technologies
+blokaslabs	Vilniaus Blokas UAB
+boe	BOE Technology Group Co., Ltd.
+bosch	Bosch Sensortec GmbH
+boundary	Boundary Devices Inc.
+brcm	Broadcom Corporation
+buffalo	Buffalo, Inc.
+bticino Bticino International
+calxeda	Calxeda
+capella	Capella Microsystems, Inc
+cascoda	Cascoda, Ltd.
+catalyst	Catalyst Semiconductor, Inc.
+cavium	Cavium, Inc.
+cdns	Cadence Design Systems Inc.
+cdtech	CDTech(H.K.) Electronics Limited
+ceva	Ceva, Inc.
+chipidea	Chipidea, Inc
+chipone		ChipOne
+chipspark	ChipSPARK
+chrp	Common Hardware Reference Platform
+chunghwa	Chunghwa Picture Tubes Ltd.
+ciaa	Computadora Industrial Abierta Argentina
+cirrus	Cirrus Logic, Inc.
+cloudengines	Cloud Engines, Inc.
+cnm	Chips&Media, Inc.
+cnxt	Conexant Systems, Inc.
+compulab	CompuLab Ltd.
+cortina	Cortina Systems, Inc.
+cosmic	Cosmic Circuits
+crane	Crane Connectivity Solutions
+creative	Creative Technology Ltd
+crystalfontz	Crystalfontz America, Inc.
+csky	Hangzhou C-SKY Microsystems Co., Ltd
+cubietech	Cubietech, Ltd.
+cypress	Cypress Semiconductor Corporation
+cznic	CZ.NIC, z.s.p.o.
+dallas	Maxim Integrated Products (formerly Dallas Semiconductor)
+dataimage	DataImage, Inc.
+davicom	DAVICOM Semiconductor, Inc.
+delta	Delta Electronics, Inc.
+denx	Denx Software Engineering
+devantech	Devantech, Ltd.
+dh	DH electronics GmbH
+digi	Digi International Inc.
+digilent	Diglent, Inc.
+dioo	Dioo Microcircuit Co., Ltd
+dlc	DLC Display Co., Ltd.
+dlg	Dialog Semiconductor
+dlink	D-Link Corporation
+dmo	Data Modul AG
+domintech	Domintech Co., Ltd.
+dongwoon	Dongwoon Anatech
+dptechnics	DPTechnics
+dragino	Dragino Technology Co., Limited
+ea	Embedded Artists AB
+ebs-systart EBS-SYSTART GmbH
+ebv	EBV Elektronik
+eckelmann	Eckelmann AG
+edt	Emerging Display Technologies
+eeti	eGalax_eMPIA Technology Inc
+elan	Elan Microelectronic Corp.
+elgin	Elgin S/A.
+embest	Shenzhen Embest Technology Co., Ltd.
+emlid	Emlid, Ltd.
+emmicro	EM Microelectronic
+emtrion	emtrion GmbH
+endless	Endless Mobile, Inc.
+energymicro	Silicon Laboratories (formerly Energy Micro AS)
+engicam	Engicam S.r.l.
+epcos	EPCOS AG
+epfl	Ecole Polytechnique Fédérale de Lausanne
+epson	Seiko Epson Corp.
+est	ESTeem Wireless Modems
+ettus	NI Ettus Research
+eukrea  Eukréa Electromatique
+everest	Everest Semiconductor Co. Ltd.
+everspin	Everspin Technologies, Inc.
+exar	Exar Corporation
+excito	Excito
+ezchip	EZchip Semiconductor
+facebook	Facebook
+fairphone	Fairphone B.V.
+faraday	Faraday Technology Corporation
+fastrax	Fastrax Oy
+fcs	Fairchild Semiconductor
+feiyang	Shenzhen Fly Young Technology Co.,LTD.
+firefly	Firefly
+focaltech	FocalTech Systems Co.,Ltd
+friendlyarm	Guangzhou FriendlyARM Computer Tech Co., Ltd
+fsl	Freescale Semiconductor
+fujitsu	Fujitsu Ltd.
+gateworks	Gateworks Corporation
+gcw Game Consoles Worldwide
+ge	General Electric Company
+geekbuying	GeekBuying
+gef	GE Fanuc Intelligent Platforms Embedded Systems, Inc.
+GEFanuc	GE Fanuc Intelligent Platforms Embedded Systems, Inc.
+geniatech	Geniatech, Inc.
+giantec	Giantec Semiconductor, Inc.
+giantplus	Giantplus Technology Co., Ltd.
+globalscale	Globalscale Technologies, Inc.
+globaltop	GlobalTop Technology, Inc.
+gmt	Global Mixed-mode Technology, Inc.
+goodix	Shenzhen Huiding Technology Co., Ltd.
+google	Google, Inc.
+grinn	Grinn
+grmn	Garmin Limited
+gumstix	Gumstix, Inc.
+gw	Gateworks Corporation
+hannstar	HannStar Display Corporation
+haoyu	Haoyu Microelectronic Co. Ltd.
+hardkernel	Hardkernel Co., Ltd
+hideep	HiDeep Inc.
+himax	Himax Technologies, Inc.
+hisilicon	Hisilicon Limited.
+hit	Hitachi Ltd.
+hitex	Hitex Development Tools
+holt	Holt Integrated Circuits, Inc.
+honeywell	Honeywell
+hp	Hewlett Packard
+holtek	Holtek Semiconductor, Inc.
+hwacom	HwaCom Systems Inc.
+i2se	I2SE GmbH
+ibm	International Business Machines (IBM)
+icplus	IC Plus Corp.
+idt	Integrated Device Technologies, Inc.
+ifi	Ingenieurburo Fur Ic-Technologie (I/F/I)
+ilitek	ILI Technology Corporation (ILITEK)
+img	Imagination Technologies Ltd.
+infineon Infineon Technologies
+inforce	Inforce Computing
+ingenic	Ingenic Semiconductor
+innolux	Innolux Corporation
+inside-secure	INSIDE Secure
+intel	Intel Corporation
+intercontrol	Inter Control Group
+invensense	InvenSense Inc.
+inversepath	Inverse Path
+iom	Iomega Corporation
+isee	ISEE 2007 S.L.
+isil	Intersil
+issi	Integrated Silicon Solutions Inc.
+itead	ITEAD Intelligent Systems Co.Ltd
+iwave  iWave Systems Technologies Pvt. Ltd.
+jdi	Japan Display Inc.
+jedec	JEDEC Solid State Technology Association
+jianda	Jiandangjing Technology Co., Ltd.
+karo	Ka-Ro electronics GmbH
+keithkoep	Keith & Koep GmbH
+keymile	Keymile GmbH
+khadas	Khadas
+kiebackpeter    Kieback & Peter GmbH
+kinetic Kinetic Technologies
+kingdisplay	King & Display Technology Co., Ltd.
+kingnovel	Kingnovel Technology Co., Ltd.
+koe	Kaohsiung Opto-Electronics Inc.
+kosagi	Sutajio Ko-Usagi PTE Ltd.
+kyo	Kyocera Corporation
+lacie	LaCie
+laird	Laird PLC
+lantiq	Lantiq Semiconductor
+lattice	Lattice Semiconductor
+lego	LEGO Systems A/S
+lemaker	Shenzhen LeMaker Technology Co., Ltd.
+lenovo	Lenovo Group Ltd.
+lg	LG Corporation
+libretech	Shenzhen Libre Technology Co., Ltd
+licheepi	Lichee Pi
+linaro	Linaro Limited
+linksys	Belkin International, Inc. (Linksys)
+linux	Linux-specific binding
+linx	Linx Technologies
+lltc	Linear Technology Corporation
+logicpd	Logic PD, Inc.
+lsi	LSI Corp. (LSI Logic)
+lwn	Liebherr-Werk Nenzing GmbH
+macnica	Macnica Americas
+marvell	Marvell Technology Group Ltd.
+maxim	Maxim Integrated Products
+mbvl	Mobiveil Inc.
+mcube	mCube
+meas	Measurement Specialties
+mediatek	MediaTek Inc.
+megachips	MegaChips
+mele	Shenzhen MeLE Digital Technology Ltd.
+melexis	Melexis N.V.
+melfas	MELFAS Inc.
+mellanox	Mellanox Technologies
+memsic	MEMSIC Inc.
+merrii	Merrii Technology Co., Ltd.
+micrel	Micrel Inc.
+microchip	Microchip Technology Inc.
+microcrystal	Micro Crystal AG
+micron	Micron Technology Inc.
+mikroe		MikroElektronika d.o.o.
+minix	MINIX Technology Ltd.
+miramems	MiraMEMS Sensing Technology Co., Ltd.
+mitsubishi	Mitsubishi Electric Corporation
+mosaixtech	Mosaix Technologies, Inc.
+motorola	Motorola, Inc.
+moxa	Moxa Inc.
+mpl	MPL AG
+mqmaker	mqmaker Inc.
+mscc	Microsemi Corporation
+msi	Micro-Star International Co. Ltd.
+mti	Imagination Technologies Ltd. (formerly MIPS Technologies Inc.)
+multi-inno	Multi-Inno Technology Co.,Ltd
+mundoreader	Mundo Reader S.L.
+murata	Murata Manufacturing Co., Ltd.
+mxicy	Macronix International Co., Ltd.
+myir	MYIR Tech Limited
+national	National Semiconductor
+nec	NEC LCD Technologies, Ltd.
+neonode		Neonode Inc.
+netgear	NETGEAR
+netlogic	Broadcom Corporation (formerly NetLogic Microsystems)
+netron-dy	Netron DY
+netxeon		Shenzhen Netxeon Technology CO., LTD
+nexbox	Nexbox
+nextthing	Next Thing Co.
+newhaven	Newhaven Display International
+ni	National Instruments
+nintendo	Nintendo
+nlt	NLT Technologies, Ltd.
+nokia	Nokia
+nordic	Nordic Semiconductor
+novtech NovTech, Inc.
+nutsboard	NutsBoard
+nuvoton	Nuvoton Technology Corporation
+nvd	New Vision Display
+nvidia	NVIDIA
+nxp	NXP Semiconductors
+okaya	Okaya Electric America, Inc.
+oki	Oki Electric Industry Co., Ltd.
+olimex	OLIMEX Ltd.
+olpc	One Laptop Per Child
+onion	Onion Corporation
+onnn	ON Semiconductor Corp.
+ontat	On Tat Industrial Company
+opalkelly	Opal Kelly Incorporated
+opencores	OpenCores.org
+openrisc	OpenRISC.io
+option	Option NV
+oranth	Shenzhen Oranth Technology Co., Ltd.
+ORCL	Oracle Corporation
+orisetech	Orise Technology
+ortustech	Ortus Technology Co., Ltd.
+ovti	OmniVision Technologies
+oxsemi	Oxford Semiconductor, Ltd.
+panasonic	Panasonic Corporation
+parade	Parade Technologies Inc.
+pda	Precision Design Associates, Inc.
+pericom	Pericom Technology Inc.
+pervasive	Pervasive Displays, Inc.
+phicomm PHICOMM Co., Ltd.
+phytec	PHYTEC Messtechnik GmbH
+picochip	Picochip Ltd
+pine64	Pine64
+pixcir  PIXCIR MICROELECTRONICS Co., Ltd
+plantower Plantower Co., Ltd
+plathome	Plat'Home Co., Ltd.
+plda	PLDA
+plx	Broadcom Corporation (formerly PLX Technology)
+pni	PNI Sensor Corporation
+portwell	Portwell Inc.
+poslab	Poslab Technology Co., Ltd.
+powervr	PowerVR (deprecated, use img)
+probox2	PROBOX2 (by W2COMP Co., Ltd.)
+pulsedlight	PulsedLight, Inc
+qca	Qualcomm Atheros, Inc.
+qcom	Qualcomm Technologies, Inc
+qemu	QEMU, a generic and open source machine emulator and virtualizer
+qi	Qi Hardware
+qiaodian	QiaoDian XianShi Corporation
+qnap	QNAP Systems, Inc.
+radxa	Radxa
+raidsonic	RaidSonic Technology GmbH
+ralink	Mediatek/Ralink Technology Corp.
+ramtron	Ramtron International
+raspberrypi	Raspberry Pi Foundation
+raydium	Raydium Semiconductor Corp.
+rda	Unisoc Communications, Inc.
+realtek Realtek Semiconductor Corp.
+renesas	Renesas Electronics Corporation
+richtek	Richtek Technology Corporation
+ricoh	Ricoh Co. Ltd.
+rikomagic	Rikomagic Tech Corp. Ltd
+riscv	RISC-V Foundation
+rockchip	Fuzhou Rockchip Electronics Co., Ltd
+rohm	ROHM Semiconductor Co., Ltd
+roofull	Shenzhen Roofull Technology Co, Ltd
+samsung	Samsung Semiconductor
+samtec	Samtec/Softing company
+sancloud	Sancloud Ltd
+sandisk	Sandisk Corporation
+sbs	Smart Battery System
+schindler	Schindler
+seagate	Seagate Technology PLC
+semtech	Semtech Corporation
+sensirion	Sensirion AG
+sff	Small Form Factor Committee
+sgd	Solomon Goldentek Display Corporation
+sgx	SGX Sensortech
+sharp	Sharp Corporation
+shimafuji	Shimafuji Electric, Inc.
+si-en	Si-En Technology Ltd.
+sifive	SiFive, Inc.
+sigma	Sigma Designs, Inc.
+sii	Seiko Instruments, Inc.
+sil	Silicon Image
+silabs	Silicon Laboratories
+silead	Silead Inc.
+silergy	Silergy Corp.
+siliconmitus	Silicon Mitus, Inc.
+simtek
+sirf	SiRF Technology, Inc.
+sis	Silicon Integrated Systems Corp.
+sitronix	Sitronix Technology Corporation
+skyworks	Skyworks Solutions, Inc.
+smsc	Standard Microsystems Corporation
+snps	Synopsys, Inc.
+socionext	Socionext Inc.
+solidrun	SolidRun
+solomon        Solomon Systech Limited
+sony	Sony Corporation
+spansion	Spansion Inc.
+sprd	Spreadtrum Communications Inc.
+sst	Silicon Storage Technology, Inc.
+st	STMicroelectronics
+starry	Starry Electronic Technology (ShenZhen) Co., LTD
+startek	Startek
+ste	ST-Ericsson
+stericsson	ST-Ericsson
+summit	Summit microelectronics
+sunchip	Shenzhen Sunchip Technology Co., Ltd
+SUNW	Sun Microsystems, Inc
+swir	Sierra Wireless
+syna	Synaptics Inc.
+synology	Synology, Inc.
+tbs	TBS Technologies
+tbs-biometrics	Touchless Biometric Systems AG
+tcg	Trusted Computing Group
+tcl	Toby Churchill Ltd.
+technexion	TechNexion
+technologic	Technologic Systems
+tempo	Tempo Semiconductor
+techstar	Shenzhen Techstar Electronics Co., Ltd.
+terasic	Terasic Inc.
+thine	THine Electronics, Inc.
+ti	Texas Instruments
+tianma	Tianma Micro-electronics Co., Ltd.
+tlm	Trusted Logic Mobility
+tmt	Tecon Microprocessor Technologies, LLC.
+topeet  Topeet
+toradex	Toradex AG
+toshiba	Toshiba Corporation
+toumaz	Toumaz
+tpk	TPK U.S.A. LLC
+tplink	TP-LINK Technologies Co., Ltd.
+tpo	TPO
+tronfy	Tronfy
+tronsmart	Tronsmart
+truly	Truly Semiconductors Limited
+tsd	Theobroma Systems Design und Consulting GmbH
+tyan	Tyan Computer Corporation
+u-blox	u-blox
+ucrobotics	uCRobotics
+ubnt	Ubiquiti Networks
+udoo	Udoo
+uniwest	United Western Technologies Corp (UniWest)
+upisemi	uPI Semiconductor Corp.
+urt	United Radiant Technology Corporation
+usi	Universal Scientific Industrial Co., Ltd.
+v3	V3 Semiconductor
+vamrs	Vamrs Ltd.
+variscite	Variscite Ltd.
+via	VIA Technologies, Inc.
+virtio	Virtual I/O Device Specification, developed by the OASIS consortium
+vishay	Vishay Intertechnology, Inc
+vitesse	Vitesse Semiconductor Corporation
+vivante	Vivante Corporation
+vocore VoCore Studio
+voipac	Voipac Technologies s.r.o.
+vot	Vision Optical Technology Co., Ltd.
+wd	Western Digital Corp.
+wetek	WeTek Electronics, limited.
+wexler	Wexler
+whwave  Shenzhen whwave Electronics, Inc.
+wi2wi	Wi2Wi, Inc.
+winbond Winbond Electronics corp.
+winstar	Winstar Display Corp.
+wlf	Wolfson Microelectronics
+wm	Wondermedia Technologies, Inc.
+x-powers	X-Powers
+xes	Extreme Engineering Solutions (X-ES)
+xillybus	Xillybus Ltd.
+xlnx	Xilinx
+xunlong	Shenzhen Xunlong Software CO.,Limited
+ysoft	Y Soft Corporation a.s.
+zarlink	Zarlink Semiconductor
+zeitec	ZEITEC Semiconductor Co., LTD.
+zidoo	Shenzhen Zidoo Technology Co., Ltd.
+zii	Zodiac Inflight Innovations
+zte	ZTE Corp.
+zyxel	ZyXEL Communications Corp.
diff -Nur --no-dereference linux-5.10.52-orig/Documentation/devicetree/bindings/vendor-prefixes.yaml linux-5.10.52-v7l+/Documentation/devicetree/bindings/vendor-prefixes.yaml
--- linux-5.10.52-orig/Documentation/devicetree/bindings/vendor-prefixes.yaml	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/Documentation/devicetree/bindings/vendor-prefixes.yaml	2021-07-25 16:45:33.898828515 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:162 @
     description: Beckhoff Automation GmbH & Co. KG
   "^bitmain,.*":
     description: Bitmain Technologies
+  "^blokaslabs,.*":
+    description: Vilniaus Blokas UAB
   "^boe,.*":
     description: BOE Technology Group Co., Ltd.
   "^bosch,.*":
diff -Nur --no-dereference linux-5.10.52-orig/Documentation/devicetree/configfs-overlays.txt linux-5.10.52-v7l+/Documentation/devicetree/configfs-overlays.txt
--- linux-5.10.52-orig/Documentation/devicetree/configfs-overlays.txt	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/Documentation/devicetree/configfs-overlays.txt	2021-07-25 16:45:33.908828347 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+Howto use the configfs overlay interface.
+
+A device-tree configfs entry is created in /config/device-tree/overlays
+and and it is manipulated using standard file system I/O.
+Note that this is a debug level interface, for use by developers and
+not necessarily something accessed by normal users due to the
+security implications of having direct access to the kernel's device tree.
+
+* To create an overlay you mkdir the directory:
+
+	# mkdir /config/device-tree/overlays/foo
+
+* Either you echo the overlay firmware file to the path property file.
+
+	# echo foo.dtbo >/config/device-tree/overlays/foo/path
+
+* Or you cat the contents of the overlay to the dtbo file
+
+	# cat foo.dtbo >/config/device-tree/overlays/foo/dtbo
+
+The overlay file will be applied, and devices will be created/destroyed
+as required.
+
+To remove it simply rmdir the directory.
+
+	# rmdir /config/device-tree/overlays/foo
+
+The rationalle of the dual interface (firmware & direct copy) is that each is
+better suited to different use patterns. The firmware interface is what's
+intended to be used by hardware managers in the kernel, while the copy interface
+make sense for developers (since it avoids problems with namespaces).
diff -Nur --no-dereference linux-5.10.52-orig/Documentation/hwmon/rpi-poe-fan linux-5.10.52-v7l+/Documentation/hwmon/rpi-poe-fan
--- linux-5.10.52-orig/Documentation/hwmon/rpi-poe-fan	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/Documentation/hwmon/rpi-poe-fan	2021-07-25 16:45:34.078825497 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+Kernel driver rpi-poe-fan
+=====================
+
+This driver enables the use of the Raspberry Pi PoE HAT fan.
+
+Author: Serge Schneider <serge@raspberrypi.org>
+
+Description
+-----------
+
+The driver implements a simple interface for driving the Raspberry Pi PoE
+(Power over Ethernet) HAT fan. The driver passes commands to the Raspberry Pi
+firmware through the mailbox property interface. The firmware then forwards
+the commands to the board over I2C on the ID_EEPROM pins. The driver exposes
+the fan to the user space through the hwmon sysfs interface.
diff -Nur --no-dereference linux-5.10.52-orig/Documentation/userspace-api/media/drivers/index.rst linux-5.10.52-v7l+/Documentation/userspace-api/media/drivers/index.rst
--- linux-5.10.52-orig/Documentation/userspace-api/media/drivers/index.rst	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/Documentation/userspace-api/media/drivers/index.rst	2021-07-25 16:45:34.368820635 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:36 @
 
 	cx2341x-uapi
 	imx-uapi
+	bcm2835-isp
 	max2175
 	meye-uapi
 	omap3isp-uapi
diff -Nur --no-dereference linux-5.10.52-orig/Documentation/userspace-api/media/v4l/ext-ctrls-codec.rst linux-5.10.52-v7l+/Documentation/userspace-api/media/v4l/ext-ctrls-codec.rst
--- linux-5.10.52-orig/Documentation/userspace-api/media/v4l/ext-ctrls-codec.rst	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/Documentation/userspace-api/media/v4l/ext-ctrls-codec.rst	2021-07-25 16:45:34.418819797 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4114 @
     * - __u32
       - ``data_bit_offset``
       - Offset (in bits) to the video data in the current slice data.
+    * - __u32
+      - ``slice_segment_addr``
+      -
     * - __u8
       - ``nal_unit_type``
       -
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4194 @
       - ``num_rps_poc_lt_curr``
       - The number of reference pictures in the long-term set.
     * - __u8
-      - ``padding[7]``
+      - ``padding[5]``
       - Applications and drivers must set this to zero.
     * - struct :c:type:`v4l2_hevc_dpb_entry`
       - ``dpb[V4L2_HEVC_DPB_ENTRIES_NUM_MAX]``
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4325 @
       - ``padding[6]``
       - Applications and drivers must set this to zero.
 
+``V4L2_CID_MPEG_VIDEO_HEVC_SCALING_MATRIX (struct)``
+    Specifies the scaling matrix (as extracted from the bitstream) for
+    the associated HEVC slice data. The bitstream parameters are
+    defined according to :ref:`hevc`, section 7.4.5 "Scaling list
+    data semantics". For further documentation, refer to the above
+    specification, unless there is an explicit comment stating
+    otherwise.
+
+    .. note::
+
+       This compound control is not yet part of the public kernel API and
+       it is expected to change.
+
+.. c:type:: v4l2_ctrl_hevc_scaling_matrix
+
+.. cssclass:: longtable
+
+.. flat-table:: struct v4l2_ctrl_hevc_scaling_matrix
+    :header-rows:  0
+    :stub-columns: 0
+    :widths:       1 1 2
+
+    * - __u8
+      - ``scaling_list_4x4[6][16]``
+      -
+    * - __u8
+      - ``scaling_list_8x8[6][64]``
+      -
+    * - __u8
+      - ``scaling_list_16x16[6][64]``
+      -
+    * - __u8
+      - ``scaling_list_32x32[2][64]``
+      -
+    * - __u8
+      - ``scaling_list_dc_coef_16x16[6]``
+      -
+    * - __u8
+      - ``scaling_list_dc_coef_32x32[2]``
+      -
+
 ``V4L2_CID_MPEG_VIDEO_HEVC_DECODE_MODE (enum)``
     Specifies the decoding mode to use. Currently exposes slice-based and
     frame-based decoding but new modes might be added later on.
diff -Nur --no-dereference linux-5.10.52-orig/Documentation/userspace-api/media/v4l/meta-formats.rst linux-5.10.52-v7l+/Documentation/userspace-api/media/v4l/meta-formats.rst
--- linux-5.10.52-orig/Documentation/userspace-api/media/v4l/meta-formats.rst	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/Documentation/userspace-api/media/v4l/meta-formats.rst	2021-07-25 16:45:34.428819629 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:15 @
 .. toctree::
     :maxdepth: 1
 
+    pixfmt-meta-bcm2835-isp-stats
     pixfmt-meta-d4xx
     pixfmt-meta-intel-ipu3
     pixfmt-meta-rkisp1
+    pixfmt-meta-sensor-data
     pixfmt-meta-uvc
     pixfmt-meta-vsp1-hgo
     pixfmt-meta-vsp1-hgt
diff -Nur --no-dereference linux-5.10.52-orig/Documentation/userspace-api/media/v4l/pixfmt-compressed.rst linux-5.10.52-v7l+/Documentation/userspace-api/media/v4l/pixfmt-compressed.rst
--- linux-5.10.52-orig/Documentation/userspace-api/media/v4l/pixfmt-compressed.rst	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/Documentation/userspace-api/media/v4l/pixfmt-compressed.rst	2021-07-25 16:45:34.428819629 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:203 @
         * ``V4L2_CID_MPEG_VIDEO_HEVC_SPS``
         * ``V4L2_CID_MPEG_VIDEO_HEVC_PPS``
         * ``V4L2_CID_MPEG_VIDEO_HEVC_SLICE_PARAMS``
+        * ``V4L2_CID_MPEG_VIDEO_HEVC_SCALING_MATRIX``
 	See the :ref:`associated Codec Control IDs <v4l2-mpeg-hevc>`.
 	Buffers associated with this pixel format must contain the appropriate
 	number of macroblocks to decode a full corresponding frame.
diff -Nur --no-dereference linux-5.10.52-orig/Documentation/userspace-api/media/v4l/pixfmt-meta-bcm2835-isp-stats.rst linux-5.10.52-v7l+/Documentation/userspace-api/media/v4l/pixfmt-meta-bcm2835-isp-stats.rst
--- linux-5.10.52-orig/Documentation/userspace-api/media/v4l/pixfmt-meta-bcm2835-isp-stats.rst	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/Documentation/userspace-api/media/v4l/pixfmt-meta-bcm2835-isp-stats.rst	2021-07-25 16:45:34.428819629 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+.. Permission is granted to copy, distribute and/or modify this
+.. document under the terms of the GNU Free Documentation License,
+.. Version 1.1 or any later version published by the Free Software
+.. Foundation, with no Invariant Sections, no Front-Cover Texts
+.. and no Back-Cover Texts. A copy of the license is included at
+.. Documentation/media/uapi/fdl-appendix.rst.
+..
+.. TODO: replace it to GFDL-1.1-or-later WITH no-invariant-sections
+
+.. _v4l2-meta-fmt-bcm2835-isp-stats:
+
+*****************************************
+V4L2_META_FMT_BCM2835_ISP_STATS  ('BSTA')
+*****************************************
+
+BCM2835 ISP Statistics
+
+Description
+===========
+
+The BCM2835 ISP hardware calculate image statistics for an input Bayer frame.
+These statistics are obtained from the "bcm2835-isp0-capture3" device node
+using the :c:type:`v4l2_meta_format` interface. They are formatted as described
+by the :c:type:`bcm2835_isp_stats` structure below.
+
+.. code-block:: c
+
+	#define DEFAULT_AWB_REGIONS_X 16
+	#define DEFAULT_AWB_REGIONS_Y 12
+
+	#define NUM_HISTOGRAMS 2
+	#define NUM_HISTOGRAM_BINS 128
+	#define AWB_REGIONS (DEFAULT_AWB_REGIONS_X * DEFAULT_AWB_REGIONS_Y)
+	#define FLOATING_REGIONS 16
+	#define AGC_REGIONS 16
+	#define FOCUS_REGIONS 12
+
+.. kernel-doc:: include/uapi/linux/bcm2835-isp.h
+   :functions: bcm2835_isp_stats_hist bcm2835_isp_stats_region
+	             bcm2835_isp_stats_focus bcm2835_isp_stats
+
diff -Nur --no-dereference linux-5.10.52-orig/Documentation/userspace-api/media/v4l/pixfmt-meta-sensor-data.rst linux-5.10.52-v7l+/Documentation/userspace-api/media/v4l/pixfmt-meta-sensor-data.rst
--- linux-5.10.52-orig/Documentation/userspace-api/media/v4l/pixfmt-meta-sensor-data.rst	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/Documentation/userspace-api/media/v4l/pixfmt-meta-sensor-data.rst	2021-07-25 16:45:34.428819629 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+.. Permission is granted to copy, distribute and/or modify this
+.. document under the terms of the GNU Free Documentation License,
+.. Version 1.1 or any later version published by the Free Software
+.. Foundation, with no Invariant Sections, no Front-Cover Texts
+.. and no Back-Cover Texts. A copy of the license is included at
+.. Documentation/media/uapi/fdl-appendix.rst.
+..
+.. TODO: replace it to GFDL-1.1-or-later WITH no-invariant-sections
+
+.. _v4l2-meta-fmt-sensor-data:
+
+***********************************
+V4L2_META_FMT_SENSOR_DATA  ('SENS')
+***********************************
+
+Sensor Ancillary Metadata
+
+Description
+===========
+
+This format describes ancillary data generated by a camera sensor and
+transmitted over a stream on the camera bus. Sensor vendors generally have their
+own custom format for this ancillary data. Some vendors follow a generic
+CSI-2/SMIA embedded data format as described in the `CSI-2 specification.
+<https://mipi.org/specifications/csi-2>`_
+
+The size of the embedded buffer is defined as a single line with a pixel width
+width specified in bytes. This is obtained by a call to the
+:c:type:`VIDIOC_SUBDEV_G_FMT` ioctl on the sensor subdevice where the ``pad``
+field in :c:type:`v4l2_subdev_format` is set to 1.  Note that this size is fixed
+and cannot be modified with a call to :c:type:`VIDIOC_SUBDEV_S_FMT`.
+
diff -Nur --no-dereference linux-5.10.52-orig/Documentation/userspace-api/media/v4l/pixfmt-nv12-col128.rst linux-5.10.52-v7l+/Documentation/userspace-api/media/v4l/pixfmt-nv12-col128.rst
--- linux-5.10.52-orig/Documentation/userspace-api/media/v4l/pixfmt-nv12-col128.rst	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/Documentation/userspace-api/media/v4l/pixfmt-nv12-col128.rst	2021-07-25 16:45:34.428819629 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+.. Permission is granted to copy, distribute and/or modify this
+.. document under the terms of the GNU Free Documentation License,
+.. Version 1.1 or any later version published by the Free Software
+.. Foundation, with no Invariant Sections, no Front-Cover Texts
+.. and no Back-Cover Texts. A copy of the license is included at
+.. Documentation/media/uapi/fdl-appendix.rst.
+..
+.. TODO: replace it to GFDL-1.1-or-later WITH no-invariant-sections
+
+.. _V4L2_PIX_FMT_NV12_COL128:
+.. _V4L2_PIX_FMT_NV12_10_COL128:
+
+********************************************************************************
+V4L2_PIX_FMT_NV12_COL128, V4L2_PIX_FMT_NV12_10_COL128
+********************************************************************************
+
+
+V4L2_PIX_FMT_NV21_COL128
+Formats with ½ horizontal and vertical chroma resolution. This format
+has two planes - one for luminance and one for chrominance. Chroma
+samples are interleaved. The difference to ``V4L2_PIX_FMT_NV12`` is the
+memory layout. The image is split into columns of 128 bytes wide rather than
+being in raster order.
+
+V4L2_PIX_FMT_NV12_10_COL128
+Follows the same pattern as ``V4L2_PIX_FMT_NV21_COL128`` with 128 byte, but is
+a 10bit format with 3 10-bit samples being packed into 4 bytes. Each 128 byte
+wide column therefore contains 96 samples.
+
+
+Description
+===========
+
+This is the two-plane versions of the YUV 4:2:0 format where data is
+grouped into 128 byte wide columns. The three components are separated into
+two sub-images or planes. The Y plane has one byte per pixel and pixels
+are grouped into 128 byte wide columns. The CbCr plane has the same width,
+in bytes, as the Y plane (and the image), but is half as tall in pixels.
+The chroma plane is also in 128 byte columns, reflecting 64 Cb and 64 Cr
+samples.
+
+The chroma samples for a column follow the luma samples. If there is any
+paddding, then that will be reflected via the selection API.
+The luma height must be a multiple of 2 lines.
+
+The normal bytesperline is effectively fixed at 128. However the format
+requires knowledge of the stride between columns, therefore the bytesperline
+value has been repurposed to denote the number of 128 byte long lines between
+the start of each column.
+
+**Byte Order.**
+
+
+.. flat-table::
+    :header-rows:  0
+    :stub-columns: 0
+    :widths: 12 12 12 12 12 4 12 12 12 12
+
+    * - start + 0:
+      - Y'\ :sub:`0,0`
+      - Y'\ :sub:`0,1`
+      - Y'\ :sub:`0,2`
+      - Y'\ :sub:`0,3`
+      - ...
+      - Y'\ :sub:`0,124`
+      - Y'\ :sub:`0,125`
+      - Y'\ :sub:`0,126`
+      - Y'\ :sub:`0,127`
+    * - start + 128:
+      - Y'\ :sub:`1,0`
+      - Y'\ :sub:`1,1`
+      - Y'\ :sub:`1,2`
+      - Y'\ :sub:`1,3`
+      - ...
+      - Y'\ :sub:`1,124`
+      - Y'\ :sub:`1,125`
+      - Y'\ :sub:`1,126`
+      - Y'\ :sub:`1,127`
+    * - start + 256:
+      - Y'\ :sub:`2,0`
+      - Y'\ :sub:`2,1`
+      - Y'\ :sub:`2,2`
+      - Y'\ :sub:`2,3`
+      - ...
+      - Y'\ :sub:`2,124`
+      - Y'\ :sub:`2,125`
+      - Y'\ :sub:`2,126`
+      - Y'\ :sub:`2,127`
+    * - ...
+      - ...
+      - ...
+      - ...
+      - ...
+      - ...
+      - ...
+      - ...
+    * - start + ((height-1) * 128):
+      - Y'\ :sub:`height-1,0`
+      - Y'\ :sub:`height-1,1`
+      - Y'\ :sub:`height-1,2`
+      - Y'\ :sub:`height-1,3`
+      - ...
+      - Y'\ :sub:`height-1,124`
+      - Y'\ :sub:`height-1,125`
+      - Y'\ :sub:`height-1,126`
+      - Y'\ :sub:`height-1,127`
+    * - start + ((height) * 128):
+      - Cb\ :sub:`0,0`
+      - Cr\ :sub:`0,0`
+      - Cb\ :sub:`0,1`
+      - Cr\ :sub:`0,1`
+      - ...
+      - Cb\ :sub:`0,62`
+      - Cr\ :sub:`0,62`
+      - Cb\ :sub:`0,63`
+      - Cr\ :sub:`0,63`
+    * - start + ((height+1) * 128):
+      - Cb\ :sub:`1,0`
+      - Cr\ :sub:`1,0`
+      - Cb\ :sub:`1,1`
+      - Cr\ :sub:`1,1`
+      - ...
+      - Cb\ :sub:`1,62`
+      - Cr\ :sub:`1,62`
+      - Cb\ :sub:`1,63`
+      - Cr\ :sub:`1,63`
+    * - ...
+      - ...
+      - ...
+      - ...
+      - ...
+      - ...
+      - ...
+      - ...
+    * - start + ((height+(height/2)-1) * 128):
+      - Cb\ :sub:`(height/2)-1,0`
+      - Cr\ :sub:`(height/2)-1,0`
+      - Cb\ :sub:`(height/2)-1,1`
+      - Cr\ :sub:`(height/2)-1,1`
+      - ...
+      - Cb\ :sub:`(height/2)-1,62`
+      - Cr\ :sub:`(height/2)-1,62`
+      - Cb\ :sub:`(height/2)-1,63`
+      - Cr\ :sub:`(height/2)-1,63`
+    * - start + (bytesperline * 128):
+      - Y'\ :sub:`0,128`
+      - Y'\ :sub:`0,129`
+      - Y'\ :sub:`0,130`
+      - Y'\ :sub:`0,131`
+      - ...
+      - Y'\ :sub:`0,252`
+      - Y'\ :sub:`0,253`
+      - Y'\ :sub:`0,254`
+      - Y'\ :sub:`0,255`
+    * - ...
+      - ...
+      - ...
+      - ...
+      - ...
+      - ...
+      - ...
+      - ...
+
+V4L2_PIX_FMT_NV12_10_COL128 uses the same 128 byte column structure, but
+encodes 10-bit YUV.
+3 10-bit values are packed into 4 bytes as bits 9:0, 19:10, and 29:20, with
+bits 30 & 31 unused. For the luma plane, bits 9:0 are Y0, 19:10 are Y1, and
+29:20 are Y2. For the chroma plane the samples always come in pairs of Cr
+and Cb, so it needs to be considered 6 values packed in 8 bytes.
+
+Bit-packed representation.
+
+.. raw:: latex
+
+    \small
+
+.. tabularcolumns:: |p{1.2cm}||p{1.2cm}||p{1.2cm}||p{1.2cm}|p{3.2cm}|p{3.2cm}|
+
+.. flat-table::
+    :header-rows:  0
+    :stub-columns: 0
+    :widths: 8 8 8 8
+
+    * - Y'\ :sub:`00[7:0]`
+      - Y'\ :sub:`01[5:0] (bits 7--2)` Y'\ :sub:`00[9:8]`\ (bits 1--0)
+      - Y'\ :sub:`02[3:0] (bits 7--4)` Y'\ :sub:`01[9:6]`\ (bits 3--0)
+      - unused (bits 7--6)` Y'\ :sub:`02[9:4]`\ (bits 5--0)
+
+.. raw:: latex
+
+    \small
+
+.. tabularcolumns:: |p{1.2cm}||p{1.2cm}||p{1.2cm}||p{1.2cm}|p{3.2cm}|p{3.2cm}|
+
+.. flat-table::
+    :header-rows:  0
+    :stub-columns: 0
+    :widths: 12 12 12 12 12 12 12 12
+
+    * - Cb\ :sub:`00[7:0]`
+      - Cr\ :sub:`00[5:0]`\ (bits 7--2) Cb\ :sub:`00[9:8]`\ (bits 1--0)
+      - Cb\ :sub:`01[3:0]`\ (bits 7--4) Cr\ :sub:`00[9:6]`\ (bits 3--0)
+      - unused (bits 7--6) Cb\ :sub:`02[9:4]`\ (bits 5--0)
+      - Cr\ :sub:`01[7:0]`
+      - Cb\ :sub:`02[5:0]`\ (bits 7--2) Cr\ :sub:`01[9:8]`\ (bits 1--0)
+      - Cr\ :sub:`02[3:0]`\ (bits 7--4) Cb\ :sub:`02[9:6]`\ (bits 3--0)
+      - unused (bits 7--6) Cr\ :sub:`02[9:4]`\ (bits 5--0)
+
+.. raw:: latex
+
+    \normalsize
+
+
+
+
diff -Nur --no-dereference linux-5.10.52-orig/Documentation/userspace-api/media/v4l/pixfmt-nv12.rst linux-5.10.52-v7l+/Documentation/userspace-api/media/v4l/pixfmt-nv12.rst
--- linux-5.10.52-orig/Documentation/userspace-api/media/v4l/pixfmt-nv12.rst	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/Documentation/userspace-api/media/v4l/pixfmt-nv12.rst	2021-07-25 16:45:34.428819629 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:6 @
 .. _V4L2-PIX-FMT-NV12:
 .. _V4L2-PIX-FMT-NV21:
 
-******************************************************
-V4L2_PIX_FMT_NV12 ('NV12'), V4L2_PIX_FMT_NV21 ('NV21')
-******************************************************
+********************************************************************************
+V4L2_PIX_FMT_NV12 ('NV12'), V4L2_PIX_FMT_NV21 ('NV21'), V4L2_PIX_FMT_NV12_COL128
+********************************************************************************
 
 
 V4L2_PIX_FMT_NV21
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:34 @
 If the Y plane has pad bytes after each row, then the CbCr plane has as
 many pad bytes after its rows.
 
+``V4L2_PIX_FMT_NV12_COL128`` is the tiled version of
+``V4L2_PIX_FMT_NV12`` with the image broken down into 128 pixel wide columns of
+Y followed by the associated combined CbCr plane.
+The normal bytesperline is effectively fixed at 128. However the format
+requires knowledge of the stride between columns, therefore the bytesperline
+value has been repurposed to denote the number of 128 byte long lines between
+the start of each column.
+
 **Byte Order.**
 Each cell is one byte.
 
diff -Nur --no-dereference linux-5.10.52-orig/Documentation/userspace-api/media/v4l/pixfmt-y12p.rst linux-5.10.52-v7l+/Documentation/userspace-api/media/v4l/pixfmt-y12p.rst
--- linux-5.10.52-orig/Documentation/userspace-api/media/v4l/pixfmt-y12p.rst	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/Documentation/userspace-api/media/v4l/pixfmt-y12p.rst	2021-07-25 16:45:34.438819462 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+.. Permission is granted to copy, distribute and/or modify this
+.. document under the terms of the GNU Free Documentation License,
+.. Version 1.1 or any later version published by the Free Software
+.. Foundation, with no Invariant Sections, no Front-Cover Texts
+.. and no Back-Cover Texts. A copy of the license is included at
+.. Documentation/media/uapi/fdl-appendix.rst.
+..
+.. TODO: replace it to GFDL-1.1-or-later WITH no-invariant-sections
+
+.. _V4L2-PIX-FMT-Y12P:
+
+******************************
+V4L2_PIX_FMT_Y12P ('Y12P')
+******************************
+
+Grey-scale image as a MIPI RAW12 packed array
+
+
+Description
+===========
+
+This is a packed grey-scale image format with a depth of 12 bits per
+pixel. Two consecutive pixels are packed into 3 bytes. The first 2 bytes
+contain the 8 high order bits of the pixels, and the 3rd byte contains the 4
+least significants bits of each pixel, in the same order.
+
+**Byte Order.**
+Each cell is one byte.
+
+.. tabularcolumns:: |p{2.2cm}|p{1.2cm}|p{1.2cm}|p{3.1cm}|
+
+
+.. flat-table::
+    :header-rows:  0
+    :stub-columns: 0
+    :widths:       2 1 1 1
+
+
+    -  -  start + 0:
+       -  Y'\ :sub:`00high`
+       -  Y'\ :sub:`01high`
+       -  Y'\ :sub:`01low`\ (bits 7--4)
+
+          Y'\ :sub:`00low`\ (bits 3--0)
+
diff -Nur --no-dereference linux-5.10.52-orig/Documentation/userspace-api/media/v4l/pixfmt-y14p.rst linux-5.10.52-v7l+/Documentation/userspace-api/media/v4l/pixfmt-y14p.rst
--- linux-5.10.52-orig/Documentation/userspace-api/media/v4l/pixfmt-y14p.rst	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/Documentation/userspace-api/media/v4l/pixfmt-y14p.rst	2021-07-25 16:45:34.438819462 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+.. Permission is granted to copy, distribute and/or modify this
+.. document under the terms of the GNU Free Documentation License,
+.. Version 1.1 or any later version published by the Free Software
+.. Foundation, with no Invariant Sections, no Front-Cover Texts
+.. and no Back-Cover Texts. A copy of the license is included at
+.. Documentation/media/uapi/fdl-appendix.rst.
+..
+.. TODO: replace it to GFDL-1.1-or-later WITH no-invariant-sections
+
+.. _V4L2-PIX-FMT-Y14P:
+
+**************************
+V4L2_PIX_FMT_Y14P ('Y14P')
+**************************
+
+Grey-scale image as a MIPI RAW14 packed array
+
+
+Description
+===========
+
+This is a packed grey-scale image format with a depth of 14 bits per
+pixel. Every four consecutive samples are packed into seven bytes. Each
+of the first four bytes contain the eight high order bits of the pixels,
+and the three following bytes contains the six least significants bits of
+each pixel, in the same order.
+
+**Byte Order.**
+Each cell is one byte.
+
+.. tabularcolumns:: |p{1.8cm}|p{1.0cm}|p{1.0cm}|p{1.0cm}|p{1.1cm}|p{3.3cm}|p{3.3cm}|p{3.3cm}|
+
+.. flat-table::
+    :header-rows:  0
+    :stub-columns: 0
+    :widths:       2 1 1 1 1 3 3 3
+
+
+    -  -  start + 0:
+       -  Y'\ :sub:`00high`
+       -  Y'\ :sub:`01high`
+       -  Y'\ :sub:`02high`
+       -  Y'\ :sub:`03high`
+       -  Y'\ :sub:`01low bits 1--0`\ (bits 7--6)
+
+	  Y'\ :sub:`00low bits 5--0`\ (bits 5--0)
+
+       -  Y'\ :sub:`02low bits 3--0`\ (bits 7--4)
+
+	  Y'\ :sub:`01low bits 5--2`\ (bits 3--0)
+
+       -  Y'\ :sub:`03low bits 5--0`\ (bits 7--2)
+
+	  Y'\ :sub:`02low bits 5--4`\ (bits 1--0)
diff -Nur --no-dereference linux-5.10.52-orig/Documentation/userspace-api/media/v4l/subdev-formats.rst linux-5.10.52-v7l+/Documentation/userspace-api/media/v4l/subdev-formats.rst
--- linux-5.10.52-orig/Documentation/userspace-api/media/v4l/subdev-formats.rst	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/Documentation/userspace-api/media/v4l/subdev-formats.rst	2021-07-25 16:45:34.448819294 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:911 @
       - g\ :sub:`5`
       - g\ :sub:`4`
       - g\ :sub:`3`
+    * .. _MEDIA-BUS-FMT-BGR666-1X18:
+
+      - MEDIA_BUS_FMT_RGB666_1X18
+      - 0x101f
+      -
+      -
+      -
+      -
+      -
+      -
+      -
+      -
+      -
+      -
+      -
+      -
+      -
+      -
+      -
+      - b\ :sub:`5`
+      - b\ :sub:`4`
+      - b\ :sub:`3`
+      - b\ :sub:`2`
+      - b\ :sub:`1`
+      - b\ :sub:`0`
+      - g\ :sub:`5`
+      - g\ :sub:`4`
+      - g\ :sub:`3`
+      - g\ :sub:`2`
+      - g\ :sub:`1`
+      - g\ :sub:`0`
+      - r\ :sub:`5`
+      - r\ :sub:`4`
+      - r\ :sub:`3`
+      - r\ :sub:`2`
+      - r\ :sub:`1`
+      - r\ :sub:`0`
     * .. _MEDIA-BUS-FMT-RGB666-1X18:
 
       - MEDIA_BUS_FMT_RGB666_1X18
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1022 @
       - g\ :sub:`2`
       - g\ :sub:`1`
       - g\ :sub:`0`
+    * .. _MEDIA-BUS-FMT-BGR666-1X24_CPADHI:
+
+      - MEDIA_BUS_FMT_BGR666_1X24_CPADHI
+      - 0x101e
+      -
+      -
+      -
+      -
+      -
+      -
+      -
+      -
+      -
+      - 0
+      - 0
+      - b\ :sub:`5`
+      - b\ :sub:`4`
+      - b\ :sub:`3`
+      - b\ :sub:`2`
+      - b\ :sub:`1`
+      - b\ :sub:`0`
+      - 0
+      - 0
+      - g\ :sub:`5`
+      - g\ :sub:`4`
+      - g\ :sub:`3`
+      - g\ :sub:`2`
+      - g\ :sub:`1`
+      - g\ :sub:`0`
+      - 0
+      - 0
+      - r\ :sub:`5`
+      - r\ :sub:`4`
+      - r\ :sub:`3`
+      - r\ :sub:`2`
+      - r\ :sub:`1`
+      - r\ :sub:`0`
     * .. _MEDIA-BUS-FMT-RGB666-1X24_CPADHI:
 
       - MEDIA_BUS_FMT_RGB666_1X24_CPADHI
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:7976 @
       - 0x5001
       - Interleaved raw UYVY and JPEG image format with embedded meta-data
 	used by Samsung S3C73MX camera sensors.
+
+
+
+.. _v4l2-mbus-sensor-data:
+
+Sensor Ancillary Metadata Formats
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+This section lists ancillary data generated by a camera sensor and
+transmitted over a stream on the camera bus.
+
+The following table lists the existing sensor ancillary metadata formats:
+
+
+.. _v4l2-mbus-pixelcode-sensor-metadata:
+
+.. tabularcolumns:: |p{8.0cm}|p{1.4cm}|p{7.7cm}|
+
+.. flat-table:: Sensor ancillary metadata formats
+    :header-rows:  1
+    :stub-columns: 0
+
+    * - Identifier
+      - Code
+      - Comments
+    * .. _MEDIA_BUS_FMT_SENSOR_DATA:
+
+      - MEDIA_BUS_FMT_SENSOR_DATA
+      - 0x7001
+      - Sensor vendor specific ancillary metadata. Some vendors follow a generic
+        CSI-2/SMIA embedded data format as described in the `CSI-2 specification.
+	<https://mipi.org/specifications/csi-2>`_
+
diff -Nur --no-dereference linux-5.10.52-orig/Documentation/userspace-api/media/v4l/yuv-formats.rst linux-5.10.52-v7l+/Documentation/userspace-api/media/v4l/yuv-formats.rst
--- linux-5.10.52-orig/Documentation/userspace-api/media/v4l/yuv-formats.rst	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/Documentation/userspace-api/media/v4l/yuv-formats.rst	2021-07-25 16:45:34.468818959 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:31 @
     pixfmt-grey
     pixfmt-y10
     pixfmt-y12
+    pixfmt-y12p
     pixfmt-y14
+    pixfmt-y14p
     pixfmt-y10b
     pixfmt-y10p
     pixfmt-y16
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:56 @
     pixfmt-nv12
     pixfmt-nv12m
     pixfmt-nv12mt
+    pixfmt-nv12-col128
     pixfmt-nv16
     pixfmt-nv16m
     pixfmt-nv24
diff -Nur --no-dereference linux-5.10.52-orig/drivers/bluetooth/hci_h5.c linux-5.10.52-v7l+/drivers/bluetooth/hci_h5.c
--- linux-5.10.52-orig/drivers/bluetooth/hci_h5.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/bluetooth/hci_h5.c	2021-07-25 16:45:44.408652309 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:346 @
 		h5_link_control(hu, conf_req, 3);
 	} else if (memcmp(data, conf_req, 2) == 0) {
 		h5_link_control(hu, conf_rsp, 2);
-		h5_link_control(hu, conf_req, 3);
+		if (h5->state != H5_ACTIVE)
+		    h5_link_control(hu, conf_req, 3);
 	} else if (memcmp(data, conf_rsp, 2) == 0) {
 		if (H5_HDR_LEN(hdr) > 2)
 			h5->tx_win = (data[2] & 0x07);
diff -Nur --no-dereference linux-5.10.52-orig/drivers/char/broadcom/bcm2835-gpiomem.c linux-5.10.52-v7l+/drivers/char/broadcom/bcm2835-gpiomem.c
--- linux-5.10.52-orig/drivers/char/broadcom/bcm2835-gpiomem.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/char/broadcom/bcm2835-gpiomem.c	2021-07-25 16:45:44.568649626 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/**
+ * GPIO memory device driver
+ *
+ * Creates a chardev /dev/gpiomem which will provide user access to
+ * the BCM2835's GPIO registers when it is mmap()'d.
+ * No longer need root for user GPIO access, but without relaxing permissions
+ * on /dev/mem.
+ *
+ * Written by Luke Wren <luke@raspberrypi.org>
+ * Copyright (c) 2015, Raspberry Pi (Trading) Ltd.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The names of the above-listed copyright holders may not be used
+ *    to endorse or promote products derived from this software without
+ *    specific prior written permission.
+ *
+ * ALTERNATIVELY, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2, as published by the Free
+ * Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/cdev.h>
+#include <linux/pagemap.h>
+#include <linux/io.h>
+
+#define DEVICE_NAME "bcm2835-gpiomem"
+#define DRIVER_NAME "gpiomem-bcm2835"
+#define DEVICE_MINOR 0
+
+struct bcm2835_gpiomem_instance {
+	unsigned long gpio_regs_phys;
+	struct device *dev;
+};
+
+static struct cdev bcm2835_gpiomem_cdev;
+static dev_t bcm2835_gpiomem_devid;
+static struct class *bcm2835_gpiomem_class;
+static struct device *bcm2835_gpiomem_dev;
+static struct bcm2835_gpiomem_instance *inst;
+
+
+/****************************************************************************
+*
+*   GPIO mem chardev file ops
+*
+***************************************************************************/
+
+static int bcm2835_gpiomem_open(struct inode *inode, struct file *file)
+{
+	int dev = iminor(inode);
+	int ret = 0;
+
+	if (dev != DEVICE_MINOR) {
+		dev_err(inst->dev, "Unknown minor device: %d", dev);
+		ret = -ENXIO;
+	}
+	return ret;
+}
+
+static int bcm2835_gpiomem_release(struct inode *inode, struct file *file)
+{
+	int dev = iminor(inode);
+	int ret = 0;
+
+	if (dev != DEVICE_MINOR) {
+		dev_err(inst->dev, "Unknown minor device %d", dev);
+		ret = -ENXIO;
+	}
+	return ret;
+}
+
+static const struct vm_operations_struct bcm2835_gpiomem_vm_ops = {
+#ifdef CONFIG_HAVE_IOREMAP_PROT
+	.access = generic_access_phys
+#endif
+};
+
+static int bcm2835_gpiomem_mmap(struct file *file, struct vm_area_struct *vma)
+{
+	/* Ignore what the user says - they're getting the GPIO regs
+	   whether they like it or not! */
+	unsigned long gpio_page = inst->gpio_regs_phys >> PAGE_SHIFT;
+
+	vma->vm_page_prot = phys_mem_access_prot(file, gpio_page,
+						 PAGE_SIZE,
+						 vma->vm_page_prot);
+	vma->vm_ops = &bcm2835_gpiomem_vm_ops;
+	if (remap_pfn_range(vma, vma->vm_start,
+			gpio_page,
+			PAGE_SIZE,
+			vma->vm_page_prot)) {
+		return -EAGAIN;
+	}
+	return 0;
+}
+
+static const struct file_operations
+bcm2835_gpiomem_fops = {
+	.owner = THIS_MODULE,
+	.open = bcm2835_gpiomem_open,
+	.release = bcm2835_gpiomem_release,
+	.mmap = bcm2835_gpiomem_mmap,
+};
+
+
+ /****************************************************************************
+*
+*   Probe and remove functions
+*
+***************************************************************************/
+
+
+static int bcm2835_gpiomem_probe(struct platform_device *pdev)
+{
+	int err;
+	void *ptr_err;
+	struct device *dev = &pdev->dev;
+	struct resource *ioresource;
+
+	/* Allocate buffers and instance data */
+
+	inst = kzalloc(sizeof(struct bcm2835_gpiomem_instance), GFP_KERNEL);
+
+	if (!inst) {
+		err = -ENOMEM;
+		goto failed_inst_alloc;
+	}
+
+	inst->dev = dev;
+
+	ioresource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (ioresource) {
+		inst->gpio_regs_phys = ioresource->start;
+	} else {
+		dev_err(inst->dev, "failed to get IO resource");
+		err = -ENOENT;
+		goto failed_get_resource;
+	}
+
+	/* Create character device entries */
+
+	err = alloc_chrdev_region(&bcm2835_gpiomem_devid,
+				  DEVICE_MINOR, 1, DEVICE_NAME);
+	if (err != 0) {
+		dev_err(inst->dev, "unable to allocate device number");
+		goto failed_alloc_chrdev;
+	}
+	cdev_init(&bcm2835_gpiomem_cdev, &bcm2835_gpiomem_fops);
+	bcm2835_gpiomem_cdev.owner = THIS_MODULE;
+	err = cdev_add(&bcm2835_gpiomem_cdev, bcm2835_gpiomem_devid, 1);
+	if (err != 0) {
+		dev_err(inst->dev, "unable to register device");
+		goto failed_cdev_add;
+	}
+
+	/* Create sysfs entries */
+
+	bcm2835_gpiomem_class = class_create(THIS_MODULE, DEVICE_NAME);
+	ptr_err = bcm2835_gpiomem_class;
+	if (IS_ERR(ptr_err))
+		goto failed_class_create;
+
+	bcm2835_gpiomem_dev = device_create(bcm2835_gpiomem_class, NULL,
+					bcm2835_gpiomem_devid, NULL,
+					"gpiomem");
+	ptr_err = bcm2835_gpiomem_dev;
+	if (IS_ERR(ptr_err))
+		goto failed_device_create;
+
+	dev_info(inst->dev, "Initialised: Registers at 0x%08lx",
+		inst->gpio_regs_phys);
+
+	return 0;
+
+failed_device_create:
+	class_destroy(bcm2835_gpiomem_class);
+failed_class_create:
+	cdev_del(&bcm2835_gpiomem_cdev);
+	err = PTR_ERR(ptr_err);
+failed_cdev_add:
+	unregister_chrdev_region(bcm2835_gpiomem_devid, 1);
+failed_alloc_chrdev:
+failed_get_resource:
+	kfree(inst);
+failed_inst_alloc:
+	dev_err(inst->dev, "could not load bcm2835_gpiomem");
+	return err;
+}
+
+static int bcm2835_gpiomem_remove(struct platform_device *pdev)
+{
+	struct device *dev = inst->dev;
+
+	kfree(inst);
+	device_destroy(bcm2835_gpiomem_class, bcm2835_gpiomem_devid);
+	class_destroy(bcm2835_gpiomem_class);
+	cdev_del(&bcm2835_gpiomem_cdev);
+	unregister_chrdev_region(bcm2835_gpiomem_devid, 1);
+
+	dev_info(dev, "GPIO mem driver removed - OK");
+	return 0;
+}
+
+ /****************************************************************************
+*
+*   Register the driver with device tree
+*
+***************************************************************************/
+
+static const struct of_device_id bcm2835_gpiomem_of_match[] = {
+	{.compatible = "brcm,bcm2835-gpiomem",},
+	{ /* sentinel */ },
+};
+
+MODULE_DEVICE_TABLE(of, bcm2835_gpiomem_of_match);
+
+static struct platform_driver bcm2835_gpiomem_driver = {
+	.probe = bcm2835_gpiomem_probe,
+	.remove = bcm2835_gpiomem_remove,
+	.driver = {
+		   .name = DRIVER_NAME,
+		   .owner = THIS_MODULE,
+		   .of_match_table = bcm2835_gpiomem_of_match,
+		   },
+};
+
+module_platform_driver(bcm2835_gpiomem_driver);
+
+MODULE_ALIAS("platform:gpiomem-bcm2835");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("gpiomem driver for accessing GPIO from userspace");
+MODULE_AUTHOR("Luke Wren <luke@raspberrypi.org>");
diff -Nur --no-dereference linux-5.10.52-orig/drivers/char/broadcom/bcm2835_smi_dev.c linux-5.10.52-v7l+/drivers/char/broadcom/bcm2835_smi_dev.c
--- linux-5.10.52-orig/drivers/char/broadcom/bcm2835_smi_dev.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/char/broadcom/bcm2835_smi_dev.c	2021-07-25 16:45:44.568649626 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/**
+ * Character device driver for Broadcom Secondary Memory Interface
+ *
+ * Written by Luke Wren <luke@raspberrypi.org>
+ * Copyright (c) 2015, Raspberry Pi (Trading) Ltd.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The names of the above-listed copyright holders may not be used
+ *    to endorse or promote products derived from this software without
+ *    specific prior written permission.
+ *
+ * ALTERNATIVELY, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2, as published by the Free
+ * Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/pagemap.h>
+#include <linux/fs.h>
+#include <linux/cdev.h>
+#include <linux/fs.h>
+
+#include <linux/broadcom/bcm2835_smi.h>
+
+#define DEVICE_NAME "bcm2835-smi-dev"
+#define DRIVER_NAME "smi-dev-bcm2835"
+#define DEVICE_MINOR 0
+
+static struct cdev bcm2835_smi_cdev;
+static dev_t bcm2835_smi_devid;
+static struct class *bcm2835_smi_class;
+static struct device *bcm2835_smi_dev;
+
+struct bcm2835_smi_dev_instance {
+	struct device *dev;
+};
+
+static struct bcm2835_smi_instance *smi_inst;
+static struct bcm2835_smi_dev_instance *inst;
+
+static const char *const ioctl_names[] = {
+	"READ_SETTINGS",
+	"WRITE_SETTINGS",
+	"ADDRESS"
+};
+
+/****************************************************************************
+*
+*   SMI chardev file ops
+*
+***************************************************************************/
+static long
+bcm2835_smi_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+	long ret = 0;
+
+	dev_info(inst->dev, "serving ioctl...");
+
+	switch (cmd) {
+	case BCM2835_SMI_IOC_GET_SETTINGS:{
+		struct smi_settings *settings;
+
+		dev_info(inst->dev, "Reading SMI settings to user.");
+		settings = bcm2835_smi_get_settings_from_regs(smi_inst);
+		if (copy_to_user((void *)arg, settings,
+				 sizeof(struct smi_settings)))
+			dev_err(inst->dev, "settings copy failed.");
+		break;
+	}
+	case BCM2835_SMI_IOC_WRITE_SETTINGS:{
+		struct smi_settings *settings;
+
+		dev_info(inst->dev, "Setting user's SMI settings.");
+		settings = bcm2835_smi_get_settings_from_regs(smi_inst);
+		if (copy_from_user(settings, (void *)arg,
+				   sizeof(struct smi_settings)))
+			dev_err(inst->dev, "settings copy failed.");
+		else
+			bcm2835_smi_set_regs_from_settings(smi_inst);
+		break;
+	}
+	case BCM2835_SMI_IOC_ADDRESS:
+		dev_info(inst->dev, "SMI address set: 0x%02x", (int)arg);
+		bcm2835_smi_set_address(smi_inst, arg);
+		break;
+	default:
+		dev_err(inst->dev, "invalid ioctl cmd: %d", cmd);
+		ret = -ENOTTY;
+		break;
+	}
+
+	return ret;
+}
+
+static int bcm2835_smi_open(struct inode *inode, struct file *file)
+{
+	int dev = iminor(inode);
+
+	dev_dbg(inst->dev, "SMI device opened.");
+
+	if (dev != DEVICE_MINOR) {
+		dev_err(inst->dev,
+			"bcm2835_smi_release: Unknown minor device: %d",
+			dev);
+		return -ENXIO;
+	}
+
+	return 0;
+}
+
+static int bcm2835_smi_release(struct inode *inode, struct file *file)
+{
+	int dev = iminor(inode);
+
+	if (dev != DEVICE_MINOR) {
+		dev_err(inst->dev,
+			"bcm2835_smi_release: Unknown minor device %d", dev);
+		return -ENXIO;
+	}
+
+	return 0;
+}
+
+static ssize_t dma_bounce_user(
+	enum dma_transfer_direction dma_dir,
+	char __user *user_ptr,
+	size_t count,
+	struct bcm2835_smi_bounce_info *bounce)
+{
+	int chunk_size;
+	int chunk_no = 0;
+	int count_left = count;
+
+	while (count_left) {
+		int rv;
+		void *buf;
+
+		/* Wait for current chunk to complete: */
+		if (down_timeout(&bounce->callback_sem,
+			msecs_to_jiffies(1000))) {
+			dev_err(inst->dev, "DMA bounce timed out");
+			count -= (count_left);
+			break;
+		}
+
+		if (bounce->callback_sem.count >= DMA_BOUNCE_BUFFER_COUNT - 1)
+			dev_err(inst->dev, "WARNING: Ring buffer overflow");
+		chunk_size = count_left > DMA_BOUNCE_BUFFER_SIZE ?
+			DMA_BOUNCE_BUFFER_SIZE : count_left;
+		buf = bounce->buffer[chunk_no % DMA_BOUNCE_BUFFER_COUNT];
+		if (dma_dir == DMA_DEV_TO_MEM)
+			rv = copy_to_user(user_ptr, buf, chunk_size);
+		else
+			rv = copy_from_user(buf, user_ptr, chunk_size);
+		if (rv)
+			dev_err(inst->dev, "copy_*_user() failed!: %d", rv);
+		user_ptr += chunk_size;
+		count_left -= chunk_size;
+		chunk_no++;
+	}
+	return count;
+}
+
+static ssize_t
+bcm2835_read_file(struct file *f, char __user *user_ptr,
+		  size_t count, loff_t *offs)
+{
+	int odd_bytes;
+
+	dev_dbg(inst->dev, "User reading %zu bytes from SMI.", count);
+	/* We don't want to DMA a number of bytes % 4 != 0 (32 bit FIFO) */
+	if (count > DMA_THRESHOLD_BYTES)
+		odd_bytes = count & 0x3;
+	else
+		odd_bytes = count;
+	count -= odd_bytes;
+	if (count) {
+		struct bcm2835_smi_bounce_info *bounce;
+
+		count = bcm2835_smi_user_dma(smi_inst,
+			DMA_DEV_TO_MEM, user_ptr, count,
+			&bounce);
+		if (count)
+			count = dma_bounce_user(DMA_DEV_TO_MEM, user_ptr,
+				count, bounce);
+	}
+	if (odd_bytes) {
+		/* Read from FIFO directly if not using DMA */
+		uint8_t buf[DMA_THRESHOLD_BYTES];
+
+		bcm2835_smi_read_buf(smi_inst, buf, odd_bytes);
+		if (copy_to_user(user_ptr, buf, odd_bytes))
+			dev_err(inst->dev, "copy_to_user() failed.");
+		count += odd_bytes;
+
+	}
+	return count;
+}
+
+static ssize_t
+bcm2835_write_file(struct file *f, const char __user *user_ptr,
+		   size_t count, loff_t *offs)
+{
+	int odd_bytes;
+
+	dev_dbg(inst->dev, "User writing %zu bytes to SMI.", count);
+	if (count > DMA_THRESHOLD_BYTES)
+		odd_bytes = count & 0x3;
+	else
+		odd_bytes = count;
+	count -= odd_bytes;
+	if (count) {
+		struct bcm2835_smi_bounce_info *bounce;
+
+		count = bcm2835_smi_user_dma(smi_inst,
+			DMA_MEM_TO_DEV, (char __user *)user_ptr, count,
+			&bounce);
+		if (count)
+			count = dma_bounce_user(DMA_MEM_TO_DEV,
+				(char __user *)user_ptr,
+				count, bounce);
+	}
+	if (odd_bytes) {
+		uint8_t buf[DMA_THRESHOLD_BYTES];
+
+		if (copy_from_user(buf, user_ptr, odd_bytes))
+			dev_err(inst->dev, "copy_from_user() failed.");
+		else
+			bcm2835_smi_write_buf(smi_inst, buf, odd_bytes);
+		count += odd_bytes;
+	}
+	return count;
+}
+
+static const struct file_operations
+bcm2835_smi_fops = {
+	.owner = THIS_MODULE,
+	.unlocked_ioctl = bcm2835_smi_ioctl,
+	.open = bcm2835_smi_open,
+	.release = bcm2835_smi_release,
+	.read = bcm2835_read_file,
+	.write = bcm2835_write_file,
+};
+
+
+/****************************************************************************
+*
+*   bcm2835_smi_probe - called when the driver is loaded.
+*
+***************************************************************************/
+
+static int bcm2835_smi_dev_probe(struct platform_device *pdev)
+{
+	int err;
+	void *ptr_err;
+	struct device *dev = &pdev->dev;
+	struct device_node *node = dev->of_node, *smi_node;
+
+	if (!node) {
+		dev_err(dev, "No device tree node supplied!");
+		return -EINVAL;
+	}
+
+	smi_node = of_parse_phandle(node, "smi_handle", 0);
+
+	if (!smi_node) {
+		dev_err(dev, "No such property: smi_handle");
+		return -ENXIO;
+	}
+
+	smi_inst = bcm2835_smi_get(smi_node);
+
+	if (!smi_inst)
+		return -EPROBE_DEFER;
+
+	/* Allocate buffers and instance data */
+
+	inst = devm_kzalloc(dev, sizeof(*inst), GFP_KERNEL);
+
+	if (!inst)
+		return -ENOMEM;
+
+	inst->dev = dev;
+
+	/* Create character device entries */
+
+	err = alloc_chrdev_region(&bcm2835_smi_devid,
+				  DEVICE_MINOR, 1, DEVICE_NAME);
+	if (err != 0) {
+		dev_err(inst->dev, "unable to allocate device number");
+		return -ENOMEM;
+	}
+	cdev_init(&bcm2835_smi_cdev, &bcm2835_smi_fops);
+	bcm2835_smi_cdev.owner = THIS_MODULE;
+	err = cdev_add(&bcm2835_smi_cdev, bcm2835_smi_devid, 1);
+	if (err != 0) {
+		dev_err(inst->dev, "unable to register device");
+		err = -ENOMEM;
+		goto failed_cdev_add;
+	}
+
+	/* Create sysfs entries */
+
+	bcm2835_smi_class = class_create(THIS_MODULE, DEVICE_NAME);
+	ptr_err = bcm2835_smi_class;
+	if (IS_ERR(ptr_err))
+		goto failed_class_create;
+
+	bcm2835_smi_dev = device_create(bcm2835_smi_class, NULL,
+					bcm2835_smi_devid, NULL,
+					"smi");
+	ptr_err = bcm2835_smi_dev;
+	if (IS_ERR(ptr_err))
+		goto failed_device_create;
+
+	dev_info(inst->dev, "initialised");
+
+	return 0;
+
+failed_device_create:
+	class_destroy(bcm2835_smi_class);
+failed_class_create:
+	cdev_del(&bcm2835_smi_cdev);
+	err = PTR_ERR(ptr_err);
+failed_cdev_add:
+	unregister_chrdev_region(bcm2835_smi_devid, 1);
+	dev_err(dev, "could not load bcm2835_smi_dev");
+	return err;
+}
+
+/****************************************************************************
+*
+*   bcm2835_smi_remove - called when the driver is unloaded.
+*
+***************************************************************************/
+
+static int bcm2835_smi_dev_remove(struct platform_device *pdev)
+{
+	device_destroy(bcm2835_smi_class, bcm2835_smi_devid);
+	class_destroy(bcm2835_smi_class);
+	cdev_del(&bcm2835_smi_cdev);
+	unregister_chrdev_region(bcm2835_smi_devid, 1);
+
+	dev_info(inst->dev, "SMI character dev removed - OK");
+	return 0;
+}
+
+/****************************************************************************
+*
+*   Register the driver with device tree
+*
+***************************************************************************/
+
+static const struct of_device_id bcm2835_smi_dev_of_match[] = {
+	{.compatible = "brcm,bcm2835-smi-dev",},
+	{ /* sentinel */ },
+};
+
+MODULE_DEVICE_TABLE(of, bcm2835_smi_dev_of_match);
+
+static struct platform_driver bcm2835_smi_dev_driver = {
+	.probe = bcm2835_smi_dev_probe,
+	.remove = bcm2835_smi_dev_remove,
+	.driver = {
+		   .name = DRIVER_NAME,
+		   .owner = THIS_MODULE,
+		   .of_match_table = bcm2835_smi_dev_of_match,
+		   },
+};
+
+module_platform_driver(bcm2835_smi_dev_driver);
+
+MODULE_ALIAS("platform:smi-dev-bcm2835");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION(
+	"Character device driver for BCM2835's secondary memory interface");
+MODULE_AUTHOR("Luke Wren <luke@raspberrypi.org>");
diff -Nur --no-dereference linux-5.10.52-orig/drivers/char/broadcom/Kconfig linux-5.10.52-v7l+/drivers/char/broadcom/Kconfig
--- linux-5.10.52-orig/drivers/char/broadcom/Kconfig	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/char/broadcom/Kconfig	2021-07-25 16:45:44.568649626 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+#
+# Broadcom char driver config
+#
+
+menuconfig BRCM_CHAR_DRIVERS
+	bool "Broadcom Char Drivers"
+	help
+	  Broadcom's char drivers
+
+if BRCM_CHAR_DRIVERS
+
+config BCM2708_VCMEM
+	bool "Videocore Memory"
+        default y
+        help
+          Helper for videocore memory access and total size allocation.
+
+config BCM_VCIO
+	tristate "Mailbox userspace access"
+	depends on BCM2835_MBOX
+	help
+	  Gives access to the mailbox property channel from userspace.
+
+endif
+
+config BCM2835_DEVGPIOMEM
+	tristate "/dev/gpiomem rootless GPIO access via mmap() on the BCM2835"
+	default m
+	help
+		Provides users with root-free access to the GPIO registers
+		on the 2835. Calling mmap(/dev/gpiomem) will map the GPIO
+		register page to the user's pointer.
+
+config BCM2835_SMI_DEV
+	tristate "Character device driver for BCM2835 Secondary Memory Interface"
+	depends on BCM2835_SMI
+	default m
+	help
+		This driver provides a character device interface (ioctl + read/write) to
+		Broadcom's Secondary Memory interface. The low-level functionality is provided
+		by the SMI driver itself.
+
+config RPIVID_MEM
+	tristate "Character device driver for the Raspberry Pi RPIVid video decoder hardware"
+	default n
+	help
+		This driver provides a character device interface for memory-map operations
+		so userspace tools can access the control and status registers of the
+		Raspberry Pi RPiVid video decoder hardware.
diff -Nur --no-dereference linux-5.10.52-orig/drivers/char/broadcom/Makefile linux-5.10.52-v7l+/drivers/char/broadcom/Makefile
--- linux-5.10.52-orig/drivers/char/broadcom/Makefile	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/char/broadcom/Makefile	2021-07-25 16:45:44.568649626 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:2 @
+obj-$(CONFIG_BCM2708_VCMEM)	+= vc_mem.o
+obj-$(CONFIG_BCM_VCIO)		+= vcio.o
+obj-$(CONFIG_BCM2835_DEVGPIOMEM)+= bcm2835-gpiomem.o
+obj-$(CONFIG_BCM2835_SMI_DEV)	+= bcm2835_smi_dev.o
+obj-$(CONFIG_RPIVID_MEM)	+= rpivid-mem.o
diff -Nur --no-dereference linux-5.10.52-orig/drivers/char/broadcom/rpivid-mem.c linux-5.10.52-v7l+/drivers/char/broadcom/rpivid-mem.c
--- linux-5.10.52-orig/drivers/char/broadcom/rpivid-mem.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/char/broadcom/rpivid-mem.c	2021-07-25 16:45:44.588649291 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+/**
+ * rpivid-mem.c - character device access to the RPiVid decoder registers
+ *
+ * Based on bcm2835-gpiomem.c. Provides IO memory access to the decoder
+ * register blocks such that ffmpeg plugins can access the hardware.
+ *
+ * Jonathan Bell <jonathan@raspberrypi.org>
+ * Copyright (c) 2019, Raspberry Pi (Trading) Ltd.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The names of the above-listed copyright holders may not be used
+ *    to endorse or promote products derived from this software without
+ *    specific prior written permission.
+ *
+ * ALTERNATIVELY, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2, as published by the Free
+ * Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/cdev.h>
+#include <linux/pagemap.h>
+#include <linux/io.h>
+
+#define DRIVER_NAME "rpivid-mem"
+#define DEVICE_MINOR 0
+
+struct rpivid_mem_priv {
+	dev_t devid;
+	struct class *class;
+	struct cdev rpivid_mem_cdev;
+	unsigned long regs_phys;
+	unsigned long mem_window_len;
+	struct device *dev;
+	const char *name;
+};
+
+static int rpivid_mem_open(struct inode *inode, struct file *file)
+{
+	int dev = iminor(inode);
+	int ret = 0;
+	struct rpivid_mem_priv *priv;
+
+	if (dev != DEVICE_MINOR && dev != DEVICE_MINOR + 1)
+		ret = -ENXIO;
+
+	priv = container_of(inode->i_cdev, struct rpivid_mem_priv,
+				rpivid_mem_cdev);
+	if (!priv)
+		return -EINVAL;
+	file->private_data = priv;
+	return ret;
+}
+
+static int rpivid_mem_release(struct inode *inode, struct file *file)
+{
+	int dev = iminor(inode);
+	int ret = 0;
+
+	if (dev != DEVICE_MINOR && dev != DEVICE_MINOR + 1)
+		ret = -ENXIO;
+
+	return ret;
+}
+
+static const struct vm_operations_struct rpivid_mem_vm_ops = {
+#ifdef CONFIG_HAVE_IOREMAP_PROT
+	.access = generic_access_phys
+#endif
+};
+
+static int rpivid_mem_mmap(struct file *file, struct vm_area_struct *vma)
+{
+	struct rpivid_mem_priv *priv;
+	unsigned long pages;
+	unsigned long len;
+
+	priv = file->private_data;
+	pages = priv->regs_phys >> PAGE_SHIFT;
+	/*
+	 * The address decode is far larger than the actual number of registers.
+	 * Just map the whole lot in.
+	 */
+	len = min(vma->vm_end - vma->vm_start, priv->mem_window_len);
+	vma->vm_page_prot = phys_mem_access_prot(file, pages, len,
+						 vma->vm_page_prot);
+	vma->vm_ops = &rpivid_mem_vm_ops;
+	if (remap_pfn_range(vma, vma->vm_start,
+			    pages, len,
+			    vma->vm_page_prot)) {
+		return -EAGAIN;
+	}
+	return 0;
+}
+
+static const struct file_operations
+rpivid_mem_fops = {
+	.owner = THIS_MODULE,
+	.open = rpivid_mem_open,
+	.release = rpivid_mem_release,
+	.mmap = rpivid_mem_mmap,
+};
+
+static const struct of_device_id rpivid_mem_of_match[];
+static int rpivid_mem_probe(struct platform_device *pdev)
+{
+	int err;
+	const struct of_device_id *id;
+	struct device *dev = &pdev->dev;
+	struct resource *ioresource;
+	struct rpivid_mem_priv *priv;
+
+	/* Allocate buffers and instance data */
+
+	priv = kzalloc(sizeof(struct rpivid_mem_priv), GFP_KERNEL);
+
+	if (!priv) {
+		err = -ENOMEM;
+		goto failed_inst_alloc;
+	}
+	platform_set_drvdata(pdev, priv);
+
+	priv->dev = dev;
+	id = of_match_device(rpivid_mem_of_match, dev);
+	if (!id)
+		return -EINVAL;
+	priv->name = id->data;
+
+	ioresource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (ioresource) {
+		priv->regs_phys = ioresource->start;
+		priv->mem_window_len = (ioresource->end + 1) - ioresource->start;
+	} else {
+		dev_err(priv->dev, "failed to get IO resource");
+		err = -ENOENT;
+		goto failed_get_resource;
+	}
+
+	/* Create character device entries */
+
+	err = alloc_chrdev_region(&priv->devid,
+				  DEVICE_MINOR, 2, priv->name);
+	if (err != 0) {
+		dev_err(priv->dev, "unable to allocate device number");
+		goto failed_alloc_chrdev;
+	}
+	cdev_init(&priv->rpivid_mem_cdev, &rpivid_mem_fops);
+	priv->rpivid_mem_cdev.owner = THIS_MODULE;
+	err = cdev_add(&priv->rpivid_mem_cdev, priv->devid, 2);
+	if (err != 0) {
+		dev_err(priv->dev, "unable to register device");
+		goto failed_cdev_add;
+	}
+
+	/* Create sysfs entries */
+
+	priv->class = class_create(THIS_MODULE, priv->name);
+	if (IS_ERR(priv->class)) {
+		err = PTR_ERR(priv->class);
+		goto failed_class_create;
+	}
+
+	dev = device_create(priv->class, NULL, priv->devid, NULL, priv->name);
+	if (IS_ERR(dev)) {
+		err = PTR_ERR(dev);
+		goto failed_device_create;
+	}
+
+	dev_info(priv->dev, "%s initialised: Registers at 0x%08lx length 0x%08lx",
+		priv->name, priv->regs_phys, priv->mem_window_len);
+
+	return 0;
+
+failed_device_create:
+	class_destroy(priv->class);
+failed_class_create:
+	cdev_del(&priv->rpivid_mem_cdev);
+failed_cdev_add:
+	unregister_chrdev_region(priv->devid, 1);
+failed_alloc_chrdev:
+failed_get_resource:
+	kfree(priv);
+failed_inst_alloc:
+	dev_err(&pdev->dev, "could not load rpivid_mem");
+	return err;
+}
+
+static int rpivid_mem_remove(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct rpivid_mem_priv *priv = platform_get_drvdata(pdev);
+
+	device_destroy(priv->class, priv->devid);
+	class_destroy(priv->class);
+	cdev_del(&priv->rpivid_mem_cdev);
+	unregister_chrdev_region(priv->devid, 1);
+	kfree(priv);
+
+	dev_info(dev, "%s driver removed - OK", priv->name);
+	return 0;
+}
+
+static const struct of_device_id rpivid_mem_of_match[] = {
+	{
+		.compatible = "raspberrypi,rpivid-hevc-decoder",
+		.data = "rpivid-hevcmem",
+	},
+	{
+		.compatible = "raspberrypi,rpivid-h264-decoder",
+		.data = "rpivid-h264mem",
+	},
+	{
+		.compatible = "raspberrypi,rpivid-vp9-decoder",
+		.data = "rpivid-vp9mem",
+	},
+	/* The "intc" is included as this block of hardware contains the
+	 * "frame done" status flags.
+	 */
+	{
+		.compatible = "raspberrypi,rpivid-local-intc",
+		.data = "rpivid-intcmem",
+	},
+	{ /* sentinel */ },
+};
+
+MODULE_DEVICE_TABLE(of, rpivid_mem_of_match);
+
+static struct platform_driver rpivid_mem_driver = {
+	.probe = rpivid_mem_probe,
+	.remove = rpivid_mem_remove,
+	.driver = {
+		   .name = DRIVER_NAME,
+		   .owner = THIS_MODULE,
+		   .of_match_table = rpivid_mem_of_match,
+		   },
+};
+
+module_platform_driver(rpivid_mem_driver);
+
+MODULE_ALIAS("platform:rpivid-mem");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Driver for accessing RPiVid decoder registers from userspace");
+MODULE_AUTHOR("Jonathan Bell <jonathan@raspberrypi.org>");
diff -Nur --no-dereference linux-5.10.52-orig/drivers/char/broadcom/vcio.c linux-5.10.52-v7l+/drivers/char/broadcom/vcio.c
--- linux-5.10.52-orig/drivers/char/broadcom/vcio.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/char/broadcom/vcio.c	2021-07-25 16:45:44.588649291 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/*
+ *  Copyright (C) 2010 Broadcom
+ *  Copyright (C) 2015 Noralf Trønnes
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/cdev.h>
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/ioctl.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/compat.h>
+#include <soc/bcm2835/raspberrypi-firmware.h>
+
+#define MBOX_CHAN_PROPERTY 8
+
+#define VCIO_IOC_MAGIC 100
+#define IOCTL_MBOX_PROPERTY _IOWR(VCIO_IOC_MAGIC, 0, char *)
+#ifdef CONFIG_COMPAT
+#define IOCTL_MBOX_PROPERTY32 _IOWR(VCIO_IOC_MAGIC, 0, compat_uptr_t)
+#endif
+
+static struct {
+	dev_t devt;
+	struct cdev cdev;
+	struct class *class;
+	struct rpi_firmware *fw;
+} vcio;
+
+static int vcio_user_property_list(void *user)
+{
+	u32 *buf, size;
+	int ret;
+
+	/* The first 32-bit is the size of the buffer */
+	if (copy_from_user(&size, user, sizeof(size)))
+		return -EFAULT;
+
+	buf = kmalloc(size, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	if (copy_from_user(buf, user, size)) {
+		kfree(buf);
+		return -EFAULT;
+	}
+
+	/* Strip off protocol encapsulation */
+	ret = rpi_firmware_property_list(vcio.fw, &buf[2], size - 12);
+	if (ret) {
+		kfree(buf);
+		return ret;
+	}
+
+	buf[1] = RPI_FIRMWARE_STATUS_SUCCESS;
+	if (copy_to_user(user, buf, size))
+		ret = -EFAULT;
+
+	kfree(buf);
+
+	return ret;
+}
+
+static int vcio_device_open(struct inode *inode, struct file *file)
+{
+	try_module_get(THIS_MODULE);
+
+	return 0;
+}
+
+static int vcio_device_release(struct inode *inode, struct file *file)
+{
+	module_put(THIS_MODULE);
+
+	return 0;
+}
+
+static long vcio_device_ioctl(struct file *file, unsigned int ioctl_num,
+			      unsigned long ioctl_param)
+{
+	switch (ioctl_num) {
+	case IOCTL_MBOX_PROPERTY:
+		return vcio_user_property_list((void *)ioctl_param);
+	default:
+		pr_err("unknown ioctl: %x\n", ioctl_num);
+		return -EINVAL;
+	}
+}
+
+#ifdef CONFIG_COMPAT
+static long vcio_device_compat_ioctl(struct file *file, unsigned int ioctl_num,
+				     unsigned long ioctl_param)
+{
+	switch (ioctl_num) {
+	case IOCTL_MBOX_PROPERTY32:
+		return vcio_user_property_list(compat_ptr(ioctl_param));
+	default:
+		pr_err("unknown ioctl: %x\n", ioctl_num);
+		return -EINVAL;
+	}
+}
+#endif
+
+const struct file_operations vcio_fops = {
+	.unlocked_ioctl = vcio_device_ioctl,
+#ifdef CONFIG_COMPAT
+	.compat_ioctl = vcio_device_compat_ioctl,
+#endif
+	.open = vcio_device_open,
+	.release = vcio_device_release,
+};
+
+static int __init vcio_init(void)
+{
+	struct device_node *np;
+	static struct device *dev;
+	int ret;
+
+	np = of_find_compatible_node(NULL, NULL,
+				     "raspberrypi,bcm2835-firmware");
+	if (!of_device_is_available(np))
+		return -ENODEV;
+
+	vcio.fw = rpi_firmware_get(np);
+	if (!vcio.fw)
+		return -ENODEV;
+
+	ret = alloc_chrdev_region(&vcio.devt, 0, 1, "vcio");
+	if (ret) {
+		pr_err("failed to allocate device number\n");
+		return ret;
+	}
+
+	cdev_init(&vcio.cdev, &vcio_fops);
+	vcio.cdev.owner = THIS_MODULE;
+	ret = cdev_add(&vcio.cdev, vcio.devt, 1);
+	if (ret) {
+		pr_err("failed to register device\n");
+		goto err_unregister_chardev;
+	}
+
+	/*
+	 * Create sysfs entries
+	 * 'bcm2708_vcio' is used for backwards compatibility so we don't break
+	 * userspace. Raspian has a udev rule that changes the permissions.
+	 */
+	vcio.class = class_create(THIS_MODULE, "bcm2708_vcio");
+	if (IS_ERR(vcio.class)) {
+		ret = PTR_ERR(vcio.class);
+		pr_err("failed to create class\n");
+		goto err_cdev_del;
+	}
+
+	dev = device_create(vcio.class, NULL, vcio.devt, NULL, "vcio");
+	if (IS_ERR(dev)) {
+		ret = PTR_ERR(dev);
+		pr_err("failed to create device\n");
+		goto err_class_destroy;
+	}
+
+	return 0;
+
+err_class_destroy:
+	class_destroy(vcio.class);
+err_cdev_del:
+	cdev_del(&vcio.cdev);
+err_unregister_chardev:
+	unregister_chrdev_region(vcio.devt, 1);
+
+	return ret;
+}
+module_init(vcio_init);
+
+static void __exit vcio_exit(void)
+{
+	device_destroy(vcio.class, vcio.devt);
+	class_destroy(vcio.class);
+	cdev_del(&vcio.cdev);
+	unregister_chrdev_region(vcio.devt, 1);
+}
+module_exit(vcio_exit);
+
+MODULE_AUTHOR("Gray Girling");
+MODULE_AUTHOR("Noralf Trønnes");
+MODULE_DESCRIPTION("Mailbox userspace access");
+MODULE_LICENSE("GPL");
diff -Nur --no-dereference linux-5.10.52-orig/drivers/char/broadcom/vc_mem.c linux-5.10.52-v7l+/drivers/char/broadcom/vc_mem.c
--- linux-5.10.52-orig/drivers/char/broadcom/vc_mem.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/char/broadcom/vc_mem.c	2021-07-25 16:45:44.588649291 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/*
+ * Copyright 2010 - 2011 Broadcom Corporation.  All rights reserved.
+ *
+ * Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2, available at
+ * http://www.broadcom.com/licenses/GPLv2.php (the "GPL").
+ *
+ * Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a
+ * license other than the GPL, without Broadcom's express prior written
+ * consent.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/device.h>
+#include <linux/cdev.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/debugfs.h>
+#include <linux/uaccess.h>
+#include <linux/dma-mapping.h>
+#include <linux/broadcom/vc_mem.h>
+
+#define DRIVER_NAME  "vc-mem"
+
+/* Device (/dev) related variables */
+static dev_t vc_mem_devnum;
+static struct class *vc_mem_class;
+static struct cdev vc_mem_cdev;
+static int vc_mem_inited;
+
+#ifdef CONFIG_DEBUG_FS
+static struct dentry *vc_mem_debugfs_entry;
+#endif
+
+/*
+ * Videocore memory addresses and size
+ *
+ * Drivers that wish to know the videocore memory addresses and sizes should
+ * use these variables instead of the MM_IO_BASE and MM_ADDR_IO defines in
+ * headers. This allows the other drivers to not be tied down to a a certain
+ * address/size at compile time.
+ *
+ * In the future, the goal is to have the videocore memory virtual address and
+ * size be calculated at boot time rather than at compile time. The decision of
+ * where the videocore memory resides and its size would be in the hands of the
+ * bootloader (and/or kernel). When that happens, the values of these variables
+ * would be calculated and assigned in the init function.
+ */
+/* In the 2835 VC in mapped above ARM, but ARM has full access to VC space */
+unsigned long mm_vc_mem_phys_addr;
+EXPORT_SYMBOL(mm_vc_mem_phys_addr);
+unsigned int mm_vc_mem_size;
+EXPORT_SYMBOL(mm_vc_mem_size);
+unsigned int mm_vc_mem_base;
+EXPORT_SYMBOL(mm_vc_mem_base);
+
+static uint phys_addr;
+static uint mem_size;
+static uint mem_base;
+
+static int
+vc_mem_open(struct inode *inode, struct file *file)
+{
+	(void)inode;
+
+	pr_debug("%s: called file = 0x%p\n", __func__, file);
+
+	return 0;
+}
+
+static int
+vc_mem_release(struct inode *inode, struct file *file)
+{
+	(void)inode;
+
+	pr_debug("%s: called file = 0x%p\n", __func__, file);
+
+	return 0;
+}
+
+static void
+vc_mem_get_size(void)
+{
+}
+
+static void
+vc_mem_get_base(void)
+{
+}
+
+int
+vc_mem_get_current_size(void)
+{
+	return mm_vc_mem_size;
+}
+EXPORT_SYMBOL_GPL(vc_mem_get_current_size);
+
+static long
+vc_mem_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+	int rc = 0;
+
+	(void) cmd;
+	(void) arg;
+
+	pr_debug("%s: called file = 0x%p, cmd %08x\n", __func__, file, cmd);
+
+	switch (cmd) {
+	case VC_MEM_IOC_MEM_PHYS_ADDR:
+		{
+			pr_debug("%s: VC_MEM_IOC_MEM_PHYS_ADDR=0x%p\n",
+				__func__, (void *)mm_vc_mem_phys_addr);
+
+			if (copy_to_user((void *)arg, &mm_vc_mem_phys_addr,
+					 sizeof(mm_vc_mem_phys_addr))) {
+				rc = -EFAULT;
+			}
+			break;
+		}
+	case VC_MEM_IOC_MEM_SIZE:
+		{
+			/* Get the videocore memory size first */
+			vc_mem_get_size();
+
+			pr_debug("%s: VC_MEM_IOC_MEM_SIZE=%x\n", __func__,
+				 mm_vc_mem_size);
+
+			if (copy_to_user((void *)arg, &mm_vc_mem_size,
+					 sizeof(mm_vc_mem_size))) {
+				rc = -EFAULT;
+			}
+			break;
+		}
+	case VC_MEM_IOC_MEM_BASE:
+		{
+			/* Get the videocore memory base */
+			vc_mem_get_base();
+
+			pr_debug("%s: VC_MEM_IOC_MEM_BASE=%x\n", __func__,
+				 mm_vc_mem_base);
+
+			if (copy_to_user((void *)arg, &mm_vc_mem_base,
+					 sizeof(mm_vc_mem_base))) {
+				rc = -EFAULT;
+			}
+			break;
+		}
+	case VC_MEM_IOC_MEM_LOAD:
+		{
+			/* Get the videocore memory base */
+			vc_mem_get_base();
+
+			pr_debug("%s: VC_MEM_IOC_MEM_LOAD=%x\n", __func__,
+				mm_vc_mem_base);
+
+			if (copy_to_user((void *)arg, &mm_vc_mem_base,
+					 sizeof(mm_vc_mem_base))) {
+				rc = -EFAULT;
+			}
+			break;
+		}
+	default:
+		{
+			return -ENOTTY;
+		}
+	}
+	pr_debug("%s: file = 0x%p returning %d\n", __func__, file, rc);
+
+	return rc;
+}
+
+#ifdef CONFIG_COMPAT
+static long
+vc_mem_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+	int rc = 0;
+
+	switch (cmd) {
+	case VC_MEM_IOC_MEM_PHYS_ADDR32:
+		pr_debug("%s: VC_MEM_IOC_MEM_PHYS_ADDR32=0x%p\n",
+			 __func__, (void *)mm_vc_mem_phys_addr);
+
+		/* This isn't correct, but will cover us for now as
+		 * VideoCore is 32bit only.
+		 */
+		if (copy_to_user((void *)arg, &mm_vc_mem_phys_addr,
+				 sizeof(compat_ulong_t)))
+			rc = -EFAULT;
+
+		break;
+
+	default:
+		rc = vc_mem_ioctl(file, cmd, arg);
+		break;
+	}
+
+	return rc;
+}
+#endif
+
+static int
+vc_mem_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+	int rc = 0;
+	unsigned long length = vma->vm_end - vma->vm_start;
+	unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
+
+	pr_debug("%s: vm_start = 0x%08lx vm_end = 0x%08lx vm_pgoff = 0x%08lx\n",
+		 __func__, (long)vma->vm_start, (long)vma->vm_end,
+		 (long)vma->vm_pgoff);
+
+	if (offset + length > mm_vc_mem_size) {
+		pr_err("%s: length %ld is too big\n", __func__, length);
+		return -EINVAL;
+	}
+	/* Do not cache the memory map */
+	vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+
+	rc = remap_pfn_range(vma, vma->vm_start,
+			     (mm_vc_mem_phys_addr >> PAGE_SHIFT) +
+			     vma->vm_pgoff, length, vma->vm_page_prot);
+	if (rc)
+		pr_err("%s: remap_pfn_range failed (rc=%d)\n", __func__, rc);
+
+	return rc;
+}
+
+/* File Operations for the driver. */
+static const struct file_operations vc_mem_fops = {
+	.owner = THIS_MODULE,
+	.open = vc_mem_open,
+	.release = vc_mem_release,
+	.unlocked_ioctl = vc_mem_ioctl,
+#ifdef CONFIG_COMPAT
+	.compat_ioctl = vc_mem_compat_ioctl,
+#endif
+	.mmap = vc_mem_mmap,
+};
+
+#ifdef CONFIG_DEBUG_FS
+static void vc_mem_debugfs_deinit(void)
+{
+	debugfs_remove_recursive(vc_mem_debugfs_entry);
+	vc_mem_debugfs_entry = NULL;
+}
+
+
+static int vc_mem_debugfs_init(
+	struct device *dev)
+{
+	vc_mem_debugfs_entry = debugfs_create_dir(DRIVER_NAME, NULL);
+	if (!vc_mem_debugfs_entry) {
+		dev_warn(dev, "could not create debugfs entry\n");
+		return -EFAULT;
+	}
+
+	debugfs_create_x32("vc_mem_phys_addr",
+				0444,
+				vc_mem_debugfs_entry,
+				(u32 *)&mm_vc_mem_phys_addr);
+	debugfs_create_x32("vc_mem_size",
+				0444,
+				vc_mem_debugfs_entry,
+				(u32 *)&mm_vc_mem_size);
+	debugfs_create_x32("vc_mem_base",
+				0444,
+				vc_mem_debugfs_entry,
+				(u32 *)&mm_vc_mem_base);
+
+	return 0;
+}
+
+#endif /* CONFIG_DEBUG_FS */
+
+/* Module load/unload functions */
+
+static int __init
+vc_mem_init(void)
+{
+	int rc = -EFAULT;
+	struct device *dev;
+
+	pr_debug("%s: called\n", __func__);
+
+	mm_vc_mem_phys_addr = phys_addr;
+	mm_vc_mem_size = mem_size;
+	mm_vc_mem_base = mem_base;
+
+	vc_mem_get_size();
+
+	pr_info("vc-mem: phys_addr:0x%08lx mem_base=0x%08x mem_size:0x%08x(%u MiB)\n",
+		mm_vc_mem_phys_addr, mm_vc_mem_base, mm_vc_mem_size,
+		mm_vc_mem_size / (1024 * 1024));
+
+	rc = alloc_chrdev_region(&vc_mem_devnum, 0, 1, DRIVER_NAME);
+	if (rc < 0) {
+		pr_err("%s: alloc_chrdev_region failed (rc=%d)\n",
+		       __func__, rc);
+		goto out_err;
+	}
+
+	cdev_init(&vc_mem_cdev, &vc_mem_fops);
+	rc = cdev_add(&vc_mem_cdev, vc_mem_devnum, 1);
+	if (rc) {
+		pr_err("%s: cdev_add failed (rc=%d)\n", __func__, rc);
+		goto out_unregister;
+	}
+
+	vc_mem_class = class_create(THIS_MODULE, DRIVER_NAME);
+	if (IS_ERR(vc_mem_class)) {
+		rc = PTR_ERR(vc_mem_class);
+		pr_err("%s: class_create failed (rc=%d)\n", __func__, rc);
+		goto out_cdev_del;
+	}
+
+	dev = device_create(vc_mem_class, NULL, vc_mem_devnum, NULL,
+			    DRIVER_NAME);
+	if (IS_ERR(dev)) {
+		rc = PTR_ERR(dev);
+		pr_err("%s: device_create failed (rc=%d)\n", __func__, rc);
+		goto out_class_destroy;
+	}
+
+#ifdef CONFIG_DEBUG_FS
+	/* don't fail if the debug entries cannot be created */
+	vc_mem_debugfs_init(dev);
+#endif
+
+	vc_mem_inited = 1;
+	return 0;
+
+	device_destroy(vc_mem_class, vc_mem_devnum);
+
+out_class_destroy:
+	class_destroy(vc_mem_class);
+	vc_mem_class = NULL;
+
+out_cdev_del:
+	cdev_del(&vc_mem_cdev);
+
+out_unregister:
+	unregister_chrdev_region(vc_mem_devnum, 1);
+
+out_err:
+	return -1;
+}
+
+static void __exit
+vc_mem_exit(void)
+{
+	pr_debug("%s: called\n", __func__);
+
+	if (vc_mem_inited) {
+#if CONFIG_DEBUG_FS
+		vc_mem_debugfs_deinit();
+#endif
+		device_destroy(vc_mem_class, vc_mem_devnum);
+		class_destroy(vc_mem_class);
+		cdev_del(&vc_mem_cdev);
+		unregister_chrdev_region(vc_mem_devnum, 1);
+	}
+}
+
+module_init(vc_mem_init);
+module_exit(vc_mem_exit);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Broadcom Corporation");
+
+module_param(phys_addr, uint, 0644);
+module_param(mem_size, uint, 0644);
+module_param(mem_base, uint, 0644);
diff -Nur --no-dereference linux-5.10.52-orig/drivers/char/hw_random/bcm2835-rng.c linux-5.10.52-v7l+/drivers/char/hw_random/bcm2835-rng.c
--- linux-5.10.52-orig/drivers/char/hw_random/bcm2835-rng.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/char/hw_random/bcm2835-rng.c	2021-07-25 16:45:44.588649291 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:105 @
 	}
 
 	/* set warm-up count & enable */
-	rng_writel(priv, RNG_WARMUP_COUNT, RNG_STATUS);
-	rng_writel(priv, RNG_RBGEN, RNG_CTRL);
+	if (!(rng_readl(priv, RNG_CTRL) & RNG_RBGEN)) {
+		rng_writel(priv, RNG_WARMUP_COUNT, RNG_STATUS);
+		rng_writel(priv, RNG_RBGEN, RNG_CTRL);
+	}
 
 	return ret;
 }
diff -Nur --no-dereference linux-5.10.52-orig/drivers/char/hw_random/iproc-rng200.c linux-5.10.52-v7l+/drivers/char/hw_random/iproc-rng200.c
--- linux-5.10.52-orig/drivers/char/hw_random/iproc-rng200.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/char/hw_random/iproc-rng200.c	2021-07-25 16:45:44.608648956 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:32 @
 #define RNG_CTRL_RNG_RBGEN_MASK				0x00001FFF
 #define RNG_CTRL_RNG_RBGEN_ENABLE			0x00000001
 #define RNG_CTRL_RNG_RBGEN_DISABLE			0x00000000
+#define RNG_CTRL_RNG_DIV_CTRL_SHIFT			13
 
 #define RNG_SOFT_RESET_OFFSET				0x04
 #define RNG_SOFT_RESET					0x00000001
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:40 @
 #define RBG_SOFT_RESET_OFFSET				0x08
 #define RBG_SOFT_RESET					0x00000001
 
+#define RNG_TOTAL_BIT_COUNT_OFFSET			0x0C
+
+#define RNG_TOTAL_BIT_COUNT_THRESHOLD_OFFSET		0x10
+
 #define RNG_INT_STATUS_OFFSET				0x18
 #define RNG_INT_STATUS_MASTER_FAIL_LOCKOUT_IRQ_MASK	0x80000000
 #define RNG_INT_STATUS_STARTUP_TRANSITIONS_MET_IRQ_MASK	0x00020000
 #define RNG_INT_STATUS_NIST_FAIL_IRQ_MASK		0x00000020
 #define RNG_INT_STATUS_TOTAL_BITS_COUNT_IRQ_MASK	0x00000001
 
+#define RNG_INT_ENABLE_OFFSET				0x1C
+
 #define RNG_FIFO_DATA_OFFSET				0x20
 
 #define RNG_FIFO_COUNT_OFFSET				0x24
 #define RNG_FIFO_COUNT_RNG_FIFO_COUNT_MASK		0x000000FF
+#define RNG_FIFO_COUNT_RNG_FIFO_THRESHOLD_SHIFT		8
 
 struct iproc_rng200_dev {
 	struct hwrng rng;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:177 @
 	return 0;
 }
 
+static int bcm2711_rng200_read(struct hwrng *rng, void *buf, size_t max,
+			       bool wait)
+{
+	struct iproc_rng200_dev *priv = to_rng_priv(rng);
+	u32 max_words = max / sizeof(u32);
+	u32 num_words, count, val;
+
+	/* ensure warm up period has elapsed */
+	while (1) {
+		val = ioread32(priv->base + RNG_TOTAL_BIT_COUNT_OFFSET);
+		if (val > 16)
+			break;
+		cpu_relax();
+	}
+
+	/* ensure fifo is not empty */
+	while (1) {
+		num_words = ioread32(priv->base + RNG_FIFO_COUNT_OFFSET) &
+			    RNG_FIFO_COUNT_RNG_FIFO_COUNT_MASK;
+		if (num_words)
+			break;
+		if (!wait)
+			return 0;
+		cpu_relax();
+	}
+
+	if (num_words > max_words)
+		num_words = max_words;
+
+	for (count = 0; count < num_words; count++) {
+		((u32 *)buf)[count] = ioread32(priv->base +
+					       RNG_FIFO_DATA_OFFSET);
+	}
+
+	return num_words * sizeof(u32);
+}
+
+static int bcm2711_rng200_init(struct hwrng *rng)
+{
+	struct iproc_rng200_dev *priv = to_rng_priv(rng);
+	uint32_t val;
+
+	if (ioread32(priv->base + RNG_CTRL_OFFSET) & RNG_CTRL_RNG_RBGEN_MASK)
+		return 0;
+
+	/* initial numbers generated are "less random" so will be discarded */
+	val = 0x40000;
+	iowrite32(val, priv->base + RNG_TOTAL_BIT_COUNT_THRESHOLD_OFFSET);
+	/* min fifo count to generate full interrupt */
+	val = 2 << RNG_FIFO_COUNT_RNG_FIFO_THRESHOLD_SHIFT;
+	iowrite32(val, priv->base + RNG_FIFO_COUNT_OFFSET);
+	/* enable the rng - 1Mhz sample rate */
+	val = (0x3 << RNG_CTRL_RNG_DIV_CTRL_SHIFT) | RNG_CTRL_RNG_RBGEN_MASK;
+	iowrite32(val, priv->base + RNG_CTRL_OFFSET);
+
+	return 0;
+}
+
 static void iproc_rng200_cleanup(struct hwrng *rng)
 {
 	struct iproc_rng200_dev *priv = to_rng_priv(rng);
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:264 @
 		return PTR_ERR(priv->base);
 	}
 
-	priv->rng.name = "iproc-rng200";
-	priv->rng.read = iproc_rng200_read;
-	priv->rng.init = iproc_rng200_init;
+	priv->rng.name = pdev->name;
 	priv->rng.cleanup = iproc_rng200_cleanup;
 
+	if (of_device_is_compatible(dev->of_node, "brcm,bcm2711-rng200")) {
+		priv->rng.init = bcm2711_rng200_init;
+		priv->rng.read = bcm2711_rng200_read;
+	} else {
+		priv->rng.init = iproc_rng200_init;
+		priv->rng.read = iproc_rng200_read;
+	}
+
 	/* Register driver */
 	ret = devm_hwrng_register(dev, &priv->rng);
 	if (ret) {
diff -Nur --no-dereference linux-5.10.52-orig/drivers/char/hw_random/Kconfig linux-5.10.52-v7l+/drivers/char/hw_random/Kconfig
--- linux-5.10.52-orig/drivers/char/hw_random/Kconfig	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/char/hw_random/Kconfig	2021-07-25 16:45:44.588649291 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:107 @
 	default HW_RANDOM
 	help
 	  This driver provides kernel-side support for the RNG200
-	  hardware found on the Broadcom iProc and STB SoCs.
+	  hardware found on the Broadcom iProc, BCM2711 and STB SoCs.
 
 	  To compile this driver as a module, choose M here: the
 	  module will be called iproc-rng200
diff -Nur --no-dereference linux-5.10.52-orig/drivers/char/Kconfig linux-5.10.52-v7l+/drivers/char/Kconfig
--- linux-5.10.52-orig/drivers/char/Kconfig	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/char/Kconfig	2021-07-25 16:45:44.528650297 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:8 @
 
 menu "Character devices"
 
+source "drivers/char/broadcom/Kconfig"
+
 source "drivers/tty/Kconfig"
 
 config TTY_PRINTK
diff -Nur --no-dereference linux-5.10.52-orig/drivers/char/Makefile linux-5.10.52-v7l+/drivers/char/Makefile
--- linux-5.10.52-orig/drivers/char/Makefile	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/char/Makefile	2021-07-25 16:45:44.528650297 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:50 @
 obj-$(CONFIG_XILLYBUS)		+= xillybus/
 obj-$(CONFIG_POWERNV_OP_PANEL)	+= powernv-op-panel.o
 obj-$(CONFIG_ADI)		+= adi.o
+obj-$(CONFIG_BRCM_CHAR_DRIVERS) += broadcom/
diff -Nur --no-dereference linux-5.10.52-orig/drivers/clk/bcm/clk-bcm2835.c linux-5.10.52-v7l+/drivers/clk/bcm/clk-bcm2835.c
--- linux-5.10.52-orig/drivers/clk/bcm/clk-bcm2835.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/clk/bcm/clk-bcm2835.c	2021-07-25 16:45:44.828645267 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:38 @
 #include <linux/platform_device.h>
 #include <linux/slab.h>
 #include <dt-bindings/clock/bcm2835.h>
+#include <soc/bcm2835/raspberrypi-firmware.h>
 
 #define CM_PASSWORD		0x5a000000
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:299 @
 #define SOC_BCM2711		BIT(1)
 #define SOC_ALL			(SOC_BCM2835 | SOC_BCM2711)
 
+#define VCMSG_ID_CORE_CLOCK     4
+
 /*
  * Names of clocks used within the driver that need to be replaced
  * with an external parent's name.  This array is in the order that
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:319 @
 struct bcm2835_cprman {
 	struct device *dev;
 	void __iomem *regs;
+	struct rpi_firmware *fw;
 	spinlock_t regs_lock; /* spinlock for all clocks */
 	unsigned int soc;
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:647 @
 	spin_unlock(&cprman->regs_lock);
 
 	/* Wait for the PLL to lock. */
-	timeout = ktime_add_ns(ktime_get(), LOCK_TIMEOUT_NS);
-	while (!(cprman_read(cprman, CM_LOCK) & data->lock_mask)) {
-		if (ktime_after(ktime_get(), timeout)) {
-			dev_err(cprman->dev, "%s: couldn't lock PLL\n",
-				clk_hw_get_name(hw));
-			return -ETIMEDOUT;
-		}
+	if (strcmp(data->name, "pllh")) {
+		timeout = ktime_add_ns(ktime_get(), LOCK_TIMEOUT_NS);
+		while (!(cprman_read(cprman, CM_LOCK) & data->lock_mask)) {
+			if (ktime_after(ktime_get(), timeout)) {
+				dev_err(cprman->dev, "%s: couldn't lock PLL\n",
+					clk_hw_get_name(hw));
+				return -ETIMEDOUT;
+			}
 
-		cpu_relax();
+			cpu_relax();
+		}
 	}
 
 	cprman_write(cprman, data->a2w_ctrl_reg,
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1025 @
 	return bcm2835_clock_rate_from_divisor(clock, parent_rate, div);
 }
 
+static unsigned long bcm2835_clock_get_rate_vpu(struct clk_hw *hw,
+						unsigned long parent_rate)
+{
+	struct bcm2835_clock *clock = bcm2835_clock_from_hw(hw);
+	struct bcm2835_cprman *cprman = clock->cprman;
+
+	if (cprman->fw) {
+		struct {
+			u32 id;
+			u32 val;
+		} packet;
+
+		packet.id = VCMSG_ID_CORE_CLOCK;
+		packet.val = 0;
+
+		if (!rpi_firmware_property(cprman->fw,
+					   RPI_FIRMWARE_GET_MAX_CLOCK_RATE,
+					   &packet, sizeof(packet)))
+			return packet.val;
+	}
+
+	return bcm2835_clock_get_rate(hw, parent_rate);
+}
+
 static void bcm2835_clock_wait_busy(struct bcm2835_clock *clock)
 {
 	struct bcm2835_cprman *cprman = clock->cprman;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1107 @
 	return 0;
 }
 
-static int bcm2835_clock_set_rate(struct clk_hw *hw,
-				  unsigned long rate, unsigned long parent_rate)
+static int bcm2835_clock_set_rate_and_parent(struct clk_hw *hw,
+					     unsigned long rate,
+					     unsigned long parent_rate,
+					     u8 parent)
 {
 	struct bcm2835_clock *clock = bcm2835_clock_from_hw(hw);
 	struct bcm2835_cprman *cprman = clock->cprman;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1120 @
 
 	spin_lock(&cprman->regs_lock);
 
-	/*
-	 * Setting up frac support
-	 *
-	 * In principle it is recommended to stop/start the clock first,
-	 * but as we set CLK_SET_RATE_GATE during registration of the
-	 * clock this requirement should be take care of by the
-	 * clk-framework.
-	 */
-	ctl = cprman_read(cprman, data->ctl_reg) & ~CM_FRAC;
+	ctl = cprman_read(cprman, data->ctl_reg);
+
+	/* If the clock is running, we have to pause clock generation while
+	 * updating the control and div regs.  This is glitchless (no clock
+	 * signals generated faster than the rate) but each reg access is two
+	 * OSC cycles so the clock will slow down for a moment.
+	 */
+	if (ctl & CM_ENABLE) {
+		cprman_write(cprman, data->ctl_reg, ctl & ~CM_ENABLE);
+		bcm2835_clock_wait_busy(clock);
+	}
+
+	if (parent != 0xff) {
+		ctl &= ~(CM_SRC_MASK << CM_SRC_SHIFT);
+		ctl |= parent << CM_SRC_SHIFT;
+	}
+
+	ctl &= ~CM_FRAC;
 	ctl |= (div & CM_DIV_FRAC_MASK) ? CM_FRAC : 0;
 	cprman_write(cprman, data->ctl_reg, ctl);
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1148 @
 	return 0;
 }
 
+static int bcm2835_clock_set_rate(struct clk_hw *hw,
+				  unsigned long rate, unsigned long parent_rate)
+{
+	return bcm2835_clock_set_rate_and_parent(hw, rate, parent_rate, 0xff);
+}
+
 static bool
 bcm2835_clk_is_pllc(struct clk_hw *hw)
 {
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1337 @
 	.unprepare = bcm2835_clock_off,
 	.recalc_rate = bcm2835_clock_get_rate,
 	.set_rate = bcm2835_clock_set_rate,
+	.set_rate_and_parent = bcm2835_clock_set_rate_and_parent,
 	.determine_rate = bcm2835_clock_determine_rate,
 	.set_parent = bcm2835_clock_set_parent,
 	.get_parent = bcm2835_clock_get_parent,
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1355 @
  */
 static const struct clk_ops bcm2835_vpu_clock_clk_ops = {
 	.is_prepared = bcm2835_vpu_clock_is_on,
-	.recalc_rate = bcm2835_clock_get_rate,
+	.recalc_rate = bcm2835_clock_get_rate_vpu,
 	.set_rate = bcm2835_clock_set_rate,
 	.determine_rate = bcm2835_clock_determine_rate,
 	.set_parent = bcm2835_clock_set_parent,
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1363 @
 	.debug_init = bcm2835_clock_debug_init,
 };
 
+static bool bcm2835_clk_is_claimed(const char *name);
+
 static struct clk_hw *bcm2835_register_pll(struct bcm2835_cprman *cprman,
 					   const void *data)
 {
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1382 @
 	init.ops = &bcm2835_pll_clk_ops;
 	init.flags = pll_data->flags | CLK_IGNORE_UNUSED;
 
+	if (!bcm2835_clk_is_claimed(pll_data->name))
+		init.flags |= CLK_IS_CRITICAL;
+
 	pll = kzalloc(sizeof(*pll), GFP_KERNEL);
 	if (!pll)
 		return NULL;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1440 @
 	divider->div.hw.init = &init;
 	divider->div.table = NULL;
 
+	if (!(cprman_read(cprman, divider_data->cm_reg) & divider_data->hold_mask)) {
+		if (!bcm2835_clk_is_claimed(divider_data->source_pll))
+			init.flags |= CLK_IS_CRITICAL;
+		if (!bcm2835_clk_is_claimed(divider_data->name))
+			divider->div.flags |= CLK_IS_CRITICAL;
+	}
+
 	divider->cprman = cprman;
 	divider->data = divider_data;
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1501 @
 	init.flags = clock_data->flags | CLK_IGNORE_UNUSED;
 
 	/*
+	 * Some GPIO clocks for ethernet/wifi PLLs are marked as
+	 * critical (since some platforms use them), but if the
+	 * firmware didn't have them turned on then they clearly
+	 * aren't actually critical.
+	 */
+	if ((cprman_read(cprman, clock_data->ctl_reg) & CM_ENABLE) == 0)
+		init.flags &= ~CLK_IS_CRITICAL;
+
+	/*
 	 * Pass the CLK_SET_RATE_PARENT flag if we are allowed to propagate
 	 * rate changes on at least of the parents.
 	 */
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1520 @
 		init.ops = &bcm2835_vpu_clock_clk_ops;
 	} else {
 		init.ops = &bcm2835_clock_clk_ops;
-		init.flags |= CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE;
 
 		/* If the clock wasn't actually enabled at boot, it's not
 		 * critical.
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1744 @
 		.hold_mask = CM_PLLA_HOLDCORE,
 		.fixed_divider = 1,
 		.flags = CLK_SET_RATE_PARENT),
-	[BCM2835_PLLA_PER]	= REGISTER_PLL_DIV(
-		SOC_ALL,
-		.name = "plla_per",
-		.source_pll = "plla",
-		.cm_reg = CM_PLLA,
-		.a2w_reg = A2W_PLLA_PER,
-		.load_mask = CM_PLLA_LOADPER,
-		.hold_mask = CM_PLLA_HOLDPER,
-		.fixed_divider = 1,
-		.flags = CLK_SET_RATE_PARENT),
+
+	/*
+	 * PLLA_PER is used for gpu clocks. Controlled by firmware, see
+	 * clk-raspberrypi.c.
+	 */
+
 	[BCM2835_PLLA_DSI0]	= REGISTER_PLL_DIV(
 		SOC_ALL,
 		.name = "plla_dsi0",
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:2050 @
 		.int_bits = 6,
 		.frac_bits = 0,
 		.tcnt_mux = 3),
-	[BCM2835_CLOCK_V3D]	= REGISTER_VPU_CLK(
-		SOC_ALL,
-		.name = "v3d",
-		.ctl_reg = CM_V3DCTL,
-		.div_reg = CM_V3DDIV,
-		.int_bits = 4,
-		.frac_bits = 8,
-		.tcnt_mux = 4),
+
+	/*
+	 * CLOCK_V3D is used for v3d clock. Controlled by firmware, see
+	 * clk-raspberrypi.c.
+	 */
+
 	/*
 	 * VPU clock.  This doesn't have an enable bit, since it drives
 	 * the bus for everything else, and is special so it doesn't need
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:2281 @
 		.ctl_reg = CM_PERIICTL),
 };
 
+static bool bcm2835_clk_claimed[ARRAY_SIZE(clk_desc_array)];
+
 /*
  * Permanently take a reference on the parent of the SDRAM clock.
  *
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:2302 @
 	return clk_prepare_enable(parent);
 }
 
+static bool bcm2835_clk_is_claimed(const char *name)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(clk_desc_array); i++) {
+		if (clk_desc_array[i].data) {
+			const char *clk_name = *(const char **)(clk_desc_array[i].data);
+			if (!strcmp(name, clk_name))
+				return bcm2835_clk_claimed[i];
+		}
+	}
+
+	return false;
+}
+
 static int bcm2835_clk_probe(struct platform_device *pdev)
 {
 	struct device *dev = &pdev->dev;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:2325 @
 	const struct bcm2835_clk_desc *desc;
 	const size_t asize = ARRAY_SIZE(clk_desc_array);
 	const struct cprman_plat_data *pdata;
+	struct device_node *fw_node;
 	size_t i;
+	u32 clk_id;
 	int ret;
 
 	pdata = of_device_get_match_data(&pdev->dev);
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:2346 @
 	if (IS_ERR(cprman->regs))
 		return PTR_ERR(cprman->regs);
 
+	fw_node = of_parse_phandle(dev->of_node, "firmware", 0);
+	if (fw_node) {
+		struct rpi_firmware *fw = rpi_firmware_get(NULL);
+		if (!fw)
+			return -EPROBE_DEFER;
+		cprman->fw = fw;
+	}
+
+	memset(bcm2835_clk_claimed, 0, sizeof(bcm2835_clk_claimed));
+	for (i = 0;
+	     !of_property_read_u32_index(pdev->dev.of_node, "claim-clocks",
+					 i, &clk_id);
+	     i++)
+		bcm2835_clk_claimed[clk_id]= true;
+
 	memcpy(cprman->real_parent_names, cprman_parent_names,
 	       sizeof(cprman_parent_names));
 	of_clk_parent_fill(dev->of_node, cprman->real_parent_names,
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:2394 @
 	if (ret)
 		return ret;
 
-	return of_clk_add_hw_provider(dev->of_node, of_clk_hw_onecell_get,
+	ret = of_clk_add_hw_provider(dev->of_node, of_clk_hw_onecell_get,
 				      &cprman->onecell);
+	if (ret)
+		return ret;
+
+	/* note that we have registered all the clocks */
+	dev_dbg(dev, "registered %zd clocks\n", asize);
+
+	return 0;
 }
 
 static const struct cprman_plat_data cprman_bcm2835_plat_data = {
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:2428 @
 	.probe          = bcm2835_clk_probe,
 };
 
-builtin_platform_driver(bcm2835_clk_driver);
+static int __init __bcm2835_clk_driver_init(void)
+{
+	return platform_driver_register(&bcm2835_clk_driver);
+}
+postcore_initcall(__bcm2835_clk_driver_init);
 
 MODULE_AUTHOR("Eric Anholt <eric@anholt.net>");
 MODULE_DESCRIPTION("BCM2835 clock driver");
diff -Nur --no-dereference linux-5.10.52-orig/drivers/clk/bcm/clk-raspberrypi.c linux-5.10.52-v7l+/drivers/clk/bcm/clk-raspberrypi.c
--- linux-5.10.52-orig/drivers/clk/bcm/clk-raspberrypi.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/clk/bcm/clk-raspberrypi.c	2021-07-25 16:45:44.828645267 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:100 @
 	struct raspberrypi_firmware_prop msg = {
 		.id = cpu_to_le32(data->id),
 		.val = cpu_to_le32(*val),
-		.disable_turbo = cpu_to_le32(1),
+		.disable_turbo = cpu_to_le32(0),
 	};
 	int ret;
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:274 @
 		case RPI_FIRMWARE_CORE_CLK_ID:
 		case RPI_FIRMWARE_M2MC_CLK_ID:
 		case RPI_FIRMWARE_V3D_CLK_ID:
+		case RPI_FIRMWARE_HEVC_CLK_ID:
 		case RPI_FIRMWARE_PIXEL_BVB_CLK_ID:
 			hw = raspberrypi_clk_register(rpi, clks->parent,
 						      clks->id);
diff -Nur --no-dereference linux-5.10.52-orig/drivers/clk/clk-allo-dac.c linux-5.10.52-v7l+/drivers/clk/clk-allo-dac.c
--- linux-5.10.52-orig/drivers/clk/clk-allo-dac.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/clk/clk-allo-dac.c	2021-07-25 16:45:44.848644932 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/*
+ * Clock Driver for Allo DAC
+ *
+ * Author:	Baswaraj K <jaikumar@cem-solutions.net>
+ *		Copyright 2016
+ *		based on code by Stuart MacLean
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+
+/* Clock rate of CLK44EN attached to GPIO6 pin */
+#define CLK_44EN_RATE 45158400UL
+/* Clock rate of CLK48EN attached to GPIO3 pin */
+#define CLK_48EN_RATE 49152000UL
+
+/**
+ * struct allo_dac_clk - Common struct to the Allo DAC
+ * @hw: clk_hw for the common clk framework
+ * @mode: 0 => CLK44EN, 1 => CLK48EN
+ */
+struct clk_allo_hw {
+	struct clk_hw hw;
+	uint8_t mode;
+};
+
+#define to_allo_clk(_hw) container_of(_hw, struct clk_allo_hw, hw)
+
+static const struct of_device_id clk_allo_dac_dt_ids[] = {
+	{ .compatible = "allo,dac-clk",},
+	{ }
+};
+MODULE_DEVICE_TABLE(of, clk_allo_dac_dt_ids);
+
+static unsigned long clk_allo_dac_recalc_rate(struct clk_hw *hw,
+	unsigned long parent_rate)
+{
+	return (to_allo_clk(hw)->mode == 0) ? CLK_44EN_RATE :
+		CLK_48EN_RATE;
+}
+
+static long clk_allo_dac_round_rate(struct clk_hw *hw,
+	unsigned long rate, unsigned long *parent_rate)
+{
+	long actual_rate;
+
+	if (rate <= CLK_44EN_RATE) {
+		actual_rate = (long)CLK_44EN_RATE;
+	} else if (rate >= CLK_48EN_RATE) {
+		actual_rate = (long)CLK_48EN_RATE;
+	} else {
+		long diff44Rate = (long)(rate - CLK_44EN_RATE);
+		long diff48Rate = (long)(CLK_48EN_RATE - rate);
+
+		if (diff44Rate < diff48Rate)
+			actual_rate = (long)CLK_44EN_RATE;
+		else
+			actual_rate = (long)CLK_48EN_RATE;
+	}
+	return actual_rate;
+}
+
+
+static int clk_allo_dac_set_rate(struct clk_hw *hw,
+	unsigned long rate, unsigned long parent_rate)
+{
+	unsigned long actual_rate;
+	struct clk_allo_hw *clk = to_allo_clk(hw);
+
+	actual_rate = (unsigned long)clk_allo_dac_round_rate(hw, rate,
+		&parent_rate);
+	clk->mode = (actual_rate == CLK_44EN_RATE) ? 0 : 1;
+	return 0;
+}
+
+
+const struct clk_ops clk_allo_dac_rate_ops = {
+	.recalc_rate = clk_allo_dac_recalc_rate,
+	.round_rate = clk_allo_dac_round_rate,
+	.set_rate = clk_allo_dac_set_rate,
+};
+
+static int clk_allo_dac_probe(struct platform_device *pdev)
+{
+	int ret;
+	struct clk_allo_hw *proclk;
+	struct clk *clk;
+	struct device *dev;
+	struct clk_init_data init;
+
+	dev = &pdev->dev;
+
+	proclk = kzalloc(sizeof(struct clk_allo_hw), GFP_KERNEL);
+	if (!proclk)
+		return -ENOMEM;
+
+	init.name = "clk-allo-dac";
+	init.ops = &clk_allo_dac_rate_ops;
+	init.flags = 0;
+	init.parent_names = NULL;
+	init.num_parents = 0;
+
+	proclk->mode = 0;
+	proclk->hw.init = &init;
+
+	clk = devm_clk_register(dev, &proclk->hw);
+	if (!IS_ERR(clk)) {
+		ret = of_clk_add_provider(dev->of_node, of_clk_src_simple_get,
+			clk);
+	} else {
+		dev_err(dev, "Fail to register clock driver\n");
+		kfree(proclk);
+		ret = PTR_ERR(clk);
+	}
+	return ret;
+}
+
+static int clk_allo_dac_remove(struct platform_device *pdev)
+{
+	of_clk_del_provider(pdev->dev.of_node);
+	return 0;
+}
+
+static struct platform_driver clk_allo_dac_driver = {
+	.probe = clk_allo_dac_probe,
+	.remove = clk_allo_dac_remove,
+	.driver = {
+		.name = "clk-allo-dac",
+		.of_match_table = clk_allo_dac_dt_ids,
+	},
+};
+
+static int __init clk_allo_dac_init(void)
+{
+	return platform_driver_register(&clk_allo_dac_driver);
+}
+core_initcall(clk_allo_dac_init);
+
+static void __exit clk_allo_dac_exit(void)
+{
+	platform_driver_unregister(&clk_allo_dac_driver);
+}
+module_exit(clk_allo_dac_exit);
+
+MODULE_DESCRIPTION("Allo DAC clock driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:clk-allo-dac");
diff -Nur --no-dereference linux-5.10.52-orig/drivers/clk/clk.c linux-5.10.52-v7l+/drivers/clk/clk.c
--- linux-5.10.52-orig/drivers/clk/clk.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/clk/clk.c	2021-07-25 16:45:44.918643758 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:80 @
 	unsigned int		protect_count;
 	unsigned long		min_rate;
 	unsigned long		max_rate;
+	unsigned long		default_request_rate;
 	unsigned long		accuracy;
 	int			phase;
 	struct clk_duty		duty;
 	struct hlist_head	children;
 	struct hlist_node	child_node;
 	struct hlist_head	clks;
+	struct list_head	pending_requests;
 	unsigned int		notifier_count;
 #ifdef CONFIG_DEBUG_FS
 	struct dentry		*dentry;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:110 @
 	struct hlist_node clks_node;
 };
 
+struct clk_request {
+	struct list_head list;
+	struct clk *clk;
+	unsigned long rate;
+};
+
 /***           runtime pm          ***/
 static int clk_pm_runtime_get(struct clk_core *core)
 {
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1319 @
 	if (!core)
 		return 0;
 
+	req->rate = clamp(req->rate, req->min_rate, req->max_rate);
+
 	/*
 	 * At this point, core protection will be disabled if
 	 * - if the provider is not protected at all
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1426 @
 {
 	int ret;
 	struct clk_rate_request req;
+	struct clk_request *clk_req;
 
 	clk_core_get_boundaries(hw->core, &req.min_rate, &req.max_rate);
 	req.rate = rate;
 
+	list_for_each_entry(clk_req, &hw->core->pending_requests, list)
+		req.min_rate = max(clk_req->rate, req.min_rate);
+
 	ret = clk_core_round_rate_nolock(hw->core, &req);
 	if (ret)
 		return 0;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1454 @
 long clk_round_rate(struct clk *clk, unsigned long rate)
 {
 	struct clk_rate_request req;
+	struct clk_request *clk_req;
 	int ret;
 
 	if (!clk)
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1468 @
 	clk_core_get_boundaries(clk->core, &req.min_rate, &req.max_rate);
 	req.rate = rate;
 
+	list_for_each_entry(clk_req, &clk->core->pending_requests, list)
+		req.min_rate = max(clk_req->rate, req.min_rate);
+
 	ret = clk_core_round_rate_nolock(clk->core, &req);
 
 	if (clk->exclusive_count)
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1938 @
 	unsigned long new_rate;
 	unsigned long min_rate;
 	unsigned long max_rate;
+	struct clk_request *req;
 	int p_index = 0;
 	long ret;
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1953 @
 
 	clk_core_get_boundaries(core, &min_rate, &max_rate);
 
+	list_for_each_entry(req, &core->pending_requests, list)
+		min_rate = max(req->rate, min_rate);
+
 	/* find the closest rate and parent clk/rate */
 	if (clk_core_can_round(core)) {
 		struct clk_rate_request req;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:2160 @
 {
 	int ret, cnt;
 	struct clk_rate_request req;
+	struct clk_request *clk_req;
 
 	lockdep_assert_held(&prepare_lock);
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:2175 @
 	clk_core_get_boundaries(core, &req.min_rate, &req.max_rate);
 	req.rate = req_rate;
 
+	list_for_each_entry(clk_req, &core->pending_requests, list)
+		req.min_rate = max(clk_req->rate, req.min_rate);
+
 	ret = clk_core_round_rate_nolock(core, &req);
 
 	/* restore the protection */
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:2271 @
 
 	ret = clk_core_set_rate_nolock(clk->core, rate);
 
+	if (!list_empty(&clk->core->pending_requests))
+		clk->core->default_request_rate = rate;
+
 	if (clk->exclusive_count)
 		clk_core_rate_protect(clk->core);
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:2434 @
 EXPORT_SYMBOL_GPL(clk_set_max_rate);
 
 /**
+ * clk_request_start - Request a rate to be enforced temporarily
+ * @clk: the clk to act on
+ * @rate: the new rate asked for
+ *
+ * This function will create a request to temporarily increase the rate
+ * of the clock to a given rate to a certain minimum.
+ *
+ * This is meant as a best effort mechanism and while the rate of the
+ * clock will be guaranteed to be equal or higher than the requested
+ * rate, there's none on what the actual rate will be due to other
+ * factors (other requests previously set, clock boundaries, etc.).
+ *
+ * Once the request is marked as done through clk_request_done(), the
+ * rate will be reverted back to what the rate was before the request.
+ *
+ * The reported boundaries of the clock will also be adjusted so that
+ * clk_round_rate() take those requests into account. A call to
+ * clk_set_rate() during a request will affect the rate the clock will
+ * return to after the requests on that clock are done.
+ *
+ * Returns 0 on success, an ERR_PTR otherwise.
+ */
+struct clk_request *clk_request_start(struct clk *clk, unsigned long rate)
+{
+	struct clk_request *req;
+	int ret;
+
+	if (!clk)
+		return ERR_PTR(-EINVAL);
+
+	req = kzalloc(sizeof(*req), GFP_KERNEL);
+	if (!req)
+		return ERR_PTR(-ENOMEM);
+
+	clk_prepare_lock();
+
+	req->clk = clk;
+	req->rate = rate;
+
+	if (list_empty(&clk->core->pending_requests))
+		clk->core->default_request_rate = clk_core_get_rate_recalc(clk->core);
+
+	ret = clk_core_set_rate_nolock(clk->core, rate);
+	if (ret) {
+		clk_prepare_unlock();
+		kfree(req);
+		return ERR_PTR(ret);
+	}
+
+	list_add_tail(&req->list, &clk->core->pending_requests);
+	clk_prepare_unlock();
+
+	return req;
+}
+EXPORT_SYMBOL_GPL(clk_request_start);
+
+/**
+ * clk_request_done - Mark a clk_request as done
+ * @req: the request to mark done
+ *
+ * This function will remove the rate request from the clock and adjust
+ * the clock rate back to either to what it was before the request
+ * started, or if there's any other request on that clock to a proper
+ * rate for them.
+ */
+void clk_request_done(struct clk_request *req)
+{
+	struct clk_core *core;
+
+	if (!req)
+		return;
+	core = req->clk->core;
+
+	clk_prepare_lock();
+
+	list_del(&req->list);
+
+	if (list_empty(&core->pending_requests)) {
+		clk_core_set_rate_nolock(core, core->default_request_rate);
+		core->default_request_rate = 0;
+	} else {
+		struct clk_request *cur_req;
+		unsigned long new_rate = 0;
+
+		list_for_each_entry(cur_req, &core->pending_requests, list)
+			new_rate = max(new_rate, cur_req->rate);
+
+		clk_core_set_rate_nolock(core, new_rate);
+	}
+
+	clk_prepare_unlock();
+
+	kfree(req);
+}
+EXPORT_SYMBOL_GPL(clk_request_done);
+
+/**
  * clk_get_parent - return the parent of a clk
  * @clk: the clk whose parent gets returned
  *
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:3940 @
 		goto fail_parents;
 
 	INIT_HLIST_HEAD(&core->clks);
+	INIT_LIST_HEAD(&core->pending_requests);
 
 	/*
 	 * Don't call clk_hw_create_clk() here because that would pin the
diff -Nur --no-dereference linux-5.10.52-orig/drivers/clk/clk-hifiberry-dachd.c linux-5.10.52-v7l+/drivers/clk/clk-hifiberry-dachd.c
--- linux-5.10.52-orig/drivers/clk/clk-hifiberry-dachd.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/clk/clk-hifiberry-dachd.c	2021-07-25 16:45:44.868644597 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Clock Driver for HiFiBerry DAC+ HD
+ *
+ * Author: Joerg Schambacher, i2Audio GmbH for HiFiBerry
+ *         Copyright 2020
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/clk.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+
+#define NO_PLL_RESET			0
+#define PLL_RESET			1
+#define HIFIBERRY_PLL_MAX_REGISTER	256
+#define DEFAULT_RATE			44100
+
+static struct reg_default hifiberry_pll_reg_defaults[] = {
+	{0x02, 0x53}, {0x03, 0x00}, {0x07, 0x20}, {0x0F, 0x00},
+	{0x10, 0x0D}, {0x11, 0x1D}, {0x12, 0x0D}, {0x13, 0x8C},
+	{0x14, 0x8C}, {0x15, 0x8C}, {0x16, 0x8C}, {0x17, 0x8C},
+	{0x18, 0x2A}, {0x1C, 0x00}, {0x1D, 0x0F}, {0x1F, 0x00},
+	{0x2A, 0x00}, {0x2C, 0x00}, {0x2F, 0x00}, {0x30, 0x00},
+	{0x31, 0x00}, {0x32, 0x00}, {0x34, 0x00}, {0x37, 0x00},
+	{0x38, 0x00}, {0x39, 0x00}, {0x3A, 0x00}, {0x3B, 0x01},
+	{0x3E, 0x00}, {0x3F, 0x00}, {0x40, 0x00}, {0x41, 0x00},
+	{0x5A, 0x00}, {0x5B, 0x00}, {0x95, 0x00}, {0x96, 0x00},
+	{0x97, 0x00}, {0x98, 0x00}, {0x99, 0x00}, {0x9A, 0x00},
+	{0x9B, 0x00}, {0xA2, 0x00}, {0xA3, 0x00}, {0xA4, 0x00},
+	{0xB7, 0x92},
+	{0x1A, 0x3D}, {0x1B, 0x09}, {0x1E, 0xF3}, {0x20, 0x13},
+	{0x21, 0x75}, {0x2B, 0x04}, {0x2D, 0x11}, {0x2E, 0xE0},
+	{0x3D, 0x7A},
+	{0x35, 0x9D}, {0x36, 0x00}, {0x3C, 0x42},
+	{ 177, 0xAC},
+};
+static struct reg_default common_pll_regs[HIFIBERRY_PLL_MAX_REGISTER];
+static int num_common_pll_regs;
+static struct reg_default dedicated_192k_pll_regs[HIFIBERRY_PLL_MAX_REGISTER];
+static int num_dedicated_192k_pll_regs;
+static struct reg_default dedicated_96k_pll_regs[HIFIBERRY_PLL_MAX_REGISTER];
+static int num_dedicated_96k_pll_regs;
+static struct reg_default dedicated_48k_pll_regs[HIFIBERRY_PLL_MAX_REGISTER];
+static int num_dedicated_48k_pll_regs;
+static struct reg_default dedicated_176k4_pll_regs[HIFIBERRY_PLL_MAX_REGISTER];
+static int num_dedicated_176k4_pll_regs;
+static struct reg_default dedicated_88k2_pll_regs[HIFIBERRY_PLL_MAX_REGISTER];
+static int num_dedicated_88k2_pll_regs;
+static struct reg_default dedicated_44k1_pll_regs[HIFIBERRY_PLL_MAX_REGISTER];
+static int num_dedicated_44k1_pll_regs;
+
+/**
+ * struct clk_hifiberry_drvdata - Common struct to the HiFiBerry DAC HD Clk
+ * @hw: clk_hw for the common clk framework
+ */
+struct clk_hifiberry_drvdata {
+	struct regmap *regmap;
+	struct clk *clk;
+	struct clk_hw hw;
+	unsigned long rate;
+};
+
+#define to_hifiberry_clk(_hw) \
+	container_of(_hw, struct clk_hifiberry_drvdata, hw)
+
+static int clk_hifiberry_dachd_write_pll_regs(struct regmap *regmap,
+				struct reg_default *regs,
+				int num, int do_pll_reset)
+{
+	int i;
+	int ret = 0;
+	char pll_soft_reset[] = { 177, 0xAC, };
+
+	for (i = 0; i < num; i++) {
+		ret |= regmap_write(regmap, regs[i].reg, regs[i].def);
+		if (ret)
+			return ret;
+	}
+	if (do_pll_reset) {
+		ret |= regmap_write(regmap, pll_soft_reset[0],
+						pll_soft_reset[1]);
+		mdelay(10);
+	}
+	return ret;
+}
+
+static unsigned long clk_hifiberry_dachd_recalc_rate(struct clk_hw *hw,
+	unsigned long parent_rate)
+{
+	return to_hifiberry_clk(hw)->rate;
+}
+
+static long clk_hifiberry_dachd_round_rate(struct clk_hw *hw,
+	unsigned long rate, unsigned long *parent_rate)
+{
+	return rate;
+}
+
+static int clk_hifiberry_dachd_set_rate(struct clk_hw *hw,
+	unsigned long rate, unsigned long parent_rate)
+{
+	int ret;
+	struct clk_hifiberry_drvdata *drvdata = to_hifiberry_clk(hw);
+
+	switch (rate) {
+	case 44100:
+		ret = clk_hifiberry_dachd_write_pll_regs(drvdata->regmap,
+			dedicated_44k1_pll_regs, num_dedicated_44k1_pll_regs,
+			PLL_RESET);
+		break;
+	case 88200:
+		ret = clk_hifiberry_dachd_write_pll_regs(drvdata->regmap,
+			dedicated_88k2_pll_regs, num_dedicated_88k2_pll_regs,
+			PLL_RESET);
+		break;
+	case 176400:
+		ret = clk_hifiberry_dachd_write_pll_regs(drvdata->regmap,
+			dedicated_176k4_pll_regs, num_dedicated_176k4_pll_regs,
+			PLL_RESET);
+		break;
+	case 48000:
+		ret = clk_hifiberry_dachd_write_pll_regs(drvdata->regmap,
+			dedicated_48k_pll_regs,	num_dedicated_48k_pll_regs,
+			PLL_RESET);
+		break;
+	case 96000:
+		ret = clk_hifiberry_dachd_write_pll_regs(drvdata->regmap,
+			dedicated_96k_pll_regs,	num_dedicated_96k_pll_regs,
+			PLL_RESET);
+		break;
+	case 192000:
+		ret = clk_hifiberry_dachd_write_pll_regs(drvdata->regmap,
+			dedicated_192k_pll_regs, num_dedicated_192k_pll_regs,
+			PLL_RESET);
+		break;
+	default:
+		ret = -EINVAL;
+		break;
+	}
+	to_hifiberry_clk(hw)->rate = rate;
+
+	return ret;
+}
+
+const struct clk_ops clk_hifiberry_dachd_rate_ops = {
+	.recalc_rate = clk_hifiberry_dachd_recalc_rate,
+	.round_rate = clk_hifiberry_dachd_round_rate,
+	.set_rate = clk_hifiberry_dachd_set_rate,
+};
+
+static int clk_hifiberry_get_prop_values(struct device *dev,
+					char *prop_name,
+					struct reg_default *regs)
+{
+	int ret;
+	int i;
+	u8 tmp[2 * HIFIBERRY_PLL_MAX_REGISTER];
+
+	ret = of_property_read_variable_u8_array(dev->of_node, prop_name,
+			tmp, 0, 2 * HIFIBERRY_PLL_MAX_REGISTER);
+	if (ret < 0)
+		return ret;
+	if (ret & 1) {
+		dev_err(dev,
+			"%s <%s> -> #%i odd number of bytes for reg/val pairs!",
+			__func__,
+			prop_name,
+			ret);
+		return -EINVAL;
+	}
+	ret /= 2;
+	for (i = 0; i < ret; i++) {
+		regs[i].reg = (u32)tmp[2 * i];
+		regs[i].def = (u32)tmp[2 * i + 1];
+	}
+	return ret;
+}
+
+
+static int clk_hifiberry_dachd_dt_parse(struct device *dev)
+{
+	num_common_pll_regs = clk_hifiberry_get_prop_values(dev,
+				"common_pll_regs", common_pll_regs);
+	num_dedicated_44k1_pll_regs = clk_hifiberry_get_prop_values(dev,
+				"44k1_pll_regs", dedicated_44k1_pll_regs);
+	num_dedicated_88k2_pll_regs = clk_hifiberry_get_prop_values(dev,
+				"88k2_pll_regs", dedicated_88k2_pll_regs);
+	num_dedicated_176k4_pll_regs = clk_hifiberry_get_prop_values(dev,
+				"176k4_pll_regs", dedicated_176k4_pll_regs);
+	num_dedicated_48k_pll_regs = clk_hifiberry_get_prop_values(dev,
+				"48k_pll_regs", dedicated_48k_pll_regs);
+	num_dedicated_96k_pll_regs = clk_hifiberry_get_prop_values(dev,
+				"96k_pll_regs", dedicated_96k_pll_regs);
+	num_dedicated_192k_pll_regs = clk_hifiberry_get_prop_values(dev,
+				"192k_pll_regs", dedicated_192k_pll_regs);
+	return 0;
+}
+
+
+static int clk_hifiberry_dachd_remove(struct device *dev)
+{
+	of_clk_del_provider(dev->of_node);
+	return 0;
+}
+
+const struct regmap_config hifiberry_pll_regmap = {
+	.reg_bits = 8,
+	.val_bits = 8,
+	.max_register = HIFIBERRY_PLL_MAX_REGISTER,
+	.reg_defaults = hifiberry_pll_reg_defaults,
+	.num_reg_defaults = ARRAY_SIZE(hifiberry_pll_reg_defaults),
+	.cache_type = REGCACHE_RBTREE,
+};
+EXPORT_SYMBOL_GPL(hifiberry_pll_regmap);
+
+
+static int clk_hifiberry_dachd_i2c_probe(struct i2c_client *i2c,
+			     const struct i2c_device_id *id)
+{
+	struct clk_hifiberry_drvdata *hdclk;
+	int ret = 0;
+	struct clk_init_data init;
+	struct device *dev = &i2c->dev;
+	struct device_node *dev_node = dev->of_node;
+	struct regmap_config config = hifiberry_pll_regmap;
+
+	hdclk = devm_kzalloc(&i2c->dev,
+			sizeof(struct clk_hifiberry_drvdata), GFP_KERNEL);
+	if (!hdclk)
+		return -ENOMEM;
+
+	i2c_set_clientdata(i2c, hdclk);
+
+	hdclk->regmap = devm_regmap_init_i2c(i2c, &config);
+
+	if (IS_ERR(hdclk->regmap))
+		return PTR_ERR(hdclk->regmap);
+
+	/* start PLL to allow detection of DAC */
+	ret = clk_hifiberry_dachd_write_pll_regs(hdclk->regmap,
+				hifiberry_pll_reg_defaults,
+				ARRAY_SIZE(hifiberry_pll_reg_defaults),
+				PLL_RESET);
+	if (ret)
+		return ret;
+
+	clk_hifiberry_dachd_dt_parse(dev);
+
+	/* restart PLL with configs from DTB */
+	ret = clk_hifiberry_dachd_write_pll_regs(hdclk->regmap, common_pll_regs,
+					num_common_pll_regs, PLL_RESET);
+	if (ret)
+		return ret;
+
+	init.name = "clk-hifiberry-dachd";
+	init.ops = &clk_hifiberry_dachd_rate_ops;
+	init.flags = 0;
+	init.parent_names = NULL;
+	init.num_parents = 0;
+
+	hdclk->hw.init = &init;
+
+	hdclk->clk = devm_clk_register(dev, &hdclk->hw);
+	if (IS_ERR(hdclk->clk)) {
+		dev_err(dev, "unable to register %s\n",	init.name);
+		return PTR_ERR(hdclk->clk);
+	}
+
+	ret = of_clk_add_provider(dev_node, of_clk_src_simple_get, hdclk->clk);
+	if (ret != 0) {
+		dev_err(dev, "Cannot of_clk_add_provider");
+		return ret;
+	}
+
+	ret = clk_set_rate(hdclk->hw.clk, DEFAULT_RATE);
+	if (ret != 0) {
+		dev_err(dev, "Cannot set rate : %d\n",	ret);
+		return -EINVAL;
+	}
+
+	return ret;
+}
+
+static int clk_hifiberry_dachd_i2c_remove(struct i2c_client *i2c)
+{
+	clk_hifiberry_dachd_remove(&i2c->dev);
+	return 0;
+}
+
+static const struct i2c_device_id clk_hifiberry_dachd_i2c_id[] = {
+	{ "dachd-clk", },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, clk_hifiberry_dachd_i2c_id);
+
+static const struct of_device_id clk_hifiberry_dachd_of_match[] = {
+	{ .compatible = "hifiberry,dachd-clk", },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, clk_hifiberry_dachd_of_match);
+
+static struct i2c_driver clk_hifiberry_dachd_i2c_driver = {
+	.probe		= clk_hifiberry_dachd_i2c_probe,
+	.remove		= clk_hifiberry_dachd_i2c_remove,
+	.id_table	= clk_hifiberry_dachd_i2c_id,
+	.driver		= {
+		.name	= "dachd-clk",
+		.of_match_table = of_match_ptr(clk_hifiberry_dachd_of_match),
+	},
+};
+
+module_i2c_driver(clk_hifiberry_dachd_i2c_driver);
+
+
+MODULE_DESCRIPTION("HiFiBerry DAC+ HD clock driver");
+MODULE_AUTHOR("Joerg Schambacher <joerg@i2audio.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:clk-hifiberry-dachd");
diff -Nur --no-dereference linux-5.10.52-orig/drivers/clk/clk-hifiberry-dacpro.c linux-5.10.52-v7l+/drivers/clk/clk-hifiberry-dacpro.c
--- linux-5.10.52-orig/drivers/clk/clk-hifiberry-dacpro.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/clk/clk-hifiberry-dacpro.c	2021-07-25 16:45:44.868644597 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/*
+ * Clock Driver for HiFiBerry DAC Pro
+ *
+ * Author: Stuart MacLean
+ *         Copyright 2015
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+
+/* Clock rate of CLK44EN attached to GPIO6 pin */
+#define CLK_44EN_RATE 22579200UL
+/* Clock rate of CLK48EN attached to GPIO3 pin */
+#define CLK_48EN_RATE 24576000UL
+
+/**
+ * struct hifiberry_dacpro_clk - Common struct to the HiFiBerry DAC Pro
+ * @hw: clk_hw for the common clk framework
+ * @mode: 0 => CLK44EN, 1 => CLK48EN
+ */
+struct clk_hifiberry_hw {
+	struct clk_hw hw;
+	uint8_t mode;
+};
+
+#define to_hifiberry_clk(_hw) container_of(_hw, struct clk_hifiberry_hw, hw)
+
+static const struct of_device_id clk_hifiberry_dacpro_dt_ids[] = {
+	{ .compatible = "hifiberry,dacpro-clk",},
+	{ }
+};
+MODULE_DEVICE_TABLE(of, clk_hifiberry_dacpro_dt_ids);
+
+static unsigned long clk_hifiberry_dacpro_recalc_rate(struct clk_hw *hw,
+	unsigned long parent_rate)
+{
+	return (to_hifiberry_clk(hw)->mode == 0) ? CLK_44EN_RATE :
+		CLK_48EN_RATE;
+}
+
+static long clk_hifiberry_dacpro_round_rate(struct clk_hw *hw,
+	unsigned long rate, unsigned long *parent_rate)
+{
+	long actual_rate;
+
+	if (rate <= CLK_44EN_RATE) {
+		actual_rate = (long)CLK_44EN_RATE;
+	} else if (rate >= CLK_48EN_RATE) {
+		actual_rate = (long)CLK_48EN_RATE;
+	} else {
+		long diff44Rate = (long)(rate - CLK_44EN_RATE);
+		long diff48Rate = (long)(CLK_48EN_RATE - rate);
+
+		if (diff44Rate < diff48Rate)
+			actual_rate = (long)CLK_44EN_RATE;
+		else
+			actual_rate = (long)CLK_48EN_RATE;
+	}
+	return actual_rate;
+}
+
+
+static int clk_hifiberry_dacpro_set_rate(struct clk_hw *hw,
+	unsigned long rate, unsigned long parent_rate)
+{
+	unsigned long actual_rate;
+	struct clk_hifiberry_hw *clk = to_hifiberry_clk(hw);
+
+	actual_rate = (unsigned long)clk_hifiberry_dacpro_round_rate(hw, rate,
+		&parent_rate);
+	clk->mode = (actual_rate == CLK_44EN_RATE) ? 0 : 1;
+	return 0;
+}
+
+
+const struct clk_ops clk_hifiberry_dacpro_rate_ops = {
+	.recalc_rate = clk_hifiberry_dacpro_recalc_rate,
+	.round_rate = clk_hifiberry_dacpro_round_rate,
+	.set_rate = clk_hifiberry_dacpro_set_rate,
+};
+
+static int clk_hifiberry_dacpro_probe(struct platform_device *pdev)
+{
+	int ret;
+	struct clk_hifiberry_hw *proclk;
+	struct clk *clk;
+	struct device *dev;
+	struct clk_init_data init;
+
+	dev = &pdev->dev;
+
+	proclk = kzalloc(sizeof(struct clk_hifiberry_hw), GFP_KERNEL);
+	if (!proclk)
+		return -ENOMEM;
+
+	init.name = "clk-hifiberry-dacpro";
+	init.ops = &clk_hifiberry_dacpro_rate_ops;
+	init.flags = 0;
+	init.parent_names = NULL;
+	init.num_parents = 0;
+
+	proclk->mode = 0;
+	proclk->hw.init = &init;
+
+	clk = devm_clk_register(dev, &proclk->hw);
+	if (!IS_ERR(clk)) {
+		ret = of_clk_add_provider(dev->of_node, of_clk_src_simple_get,
+			clk);
+	} else {
+		dev_err(dev, "Fail to register clock driver\n");
+		kfree(proclk);
+		ret = PTR_ERR(clk);
+	}
+	return ret;
+}
+
+static int clk_hifiberry_dacpro_remove(struct platform_device *pdev)
+{
+	of_clk_del_provider(pdev->dev.of_node);
+	return 0;
+}
+
+static struct platform_driver clk_hifiberry_dacpro_driver = {
+	.probe = clk_hifiberry_dacpro_probe,
+	.remove = clk_hifiberry_dacpro_remove,
+	.driver = {
+		.name = "clk-hifiberry-dacpro",
+		.of_match_table = clk_hifiberry_dacpro_dt_ids,
+	},
+};
+
+static int __init clk_hifiberry_dacpro_init(void)
+{
+	return platform_driver_register(&clk_hifiberry_dacpro_driver);
+}
+core_initcall(clk_hifiberry_dacpro_init);
+
+static void __exit clk_hifiberry_dacpro_exit(void)
+{
+	platform_driver_unregister(&clk_hifiberry_dacpro_driver);
+}
+module_exit(clk_hifiberry_dacpro_exit);
+
+MODULE_DESCRIPTION("HiFiBerry DAC Pro clock driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:clk-hifiberry-dacpro");
diff -Nur --no-dereference linux-5.10.52-orig/drivers/clk/Kconfig linux-5.10.52-v7l+/drivers/clk/Kconfig
--- linux-5.10.52-orig/drivers/clk/Kconfig	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/clk/Kconfig	2021-07-25 16:45:44.768646273 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:89 @
 	  multi-function device has one fixed-rate oscillator, clocked
 	  at 32KHz.
 
+config COMMON_CLK_HIFIBERRY_DACPLUSHD
+	tristate
+
+config COMMON_CLK_HIFIBERRY_DACPRO
+	tristate
+
 config COMMON_CLK_SCMI
 	tristate "Clock driver controlled via SCMI interface"
 	depends on ARM_SCMI_PROTOCOL || COMPILE_TEST
diff -Nur --no-dereference linux-5.10.52-orig/drivers/clk/Makefile linux-5.10.52-v7l+/drivers/clk/Makefile
--- linux-5.10.52-orig/drivers/clk/Makefile	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/clk/Makefile	2021-07-25 16:45:44.768646273 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:21 @
 
 # hardware specific clock types
 # please keep this section sorted lexicographically by file path name
+obj-$(CONFIG_SND_BCM2708_SOC_ALLO_BOSS_DAC)	+= clk-allo-dac.o
 obj-$(CONFIG_MACH_ASM9260)		+= clk-asm9260.o
 obj-$(CONFIG_COMMON_CLK_AXI_CLKGEN)	+= clk-axi-clkgen.o
 obj-$(CONFIG_ARCH_AXXIA)		+= clk-axm5516.o
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:41 @
 obj-$(CONFIG_ARCH_HIGHBANK)		+= clk-highbank.o
 obj-$(CONFIG_CLK_HSDK)			+= clk-hsdk-pll.o
 obj-$(CONFIG_COMMON_CLK_LOCHNAGAR)	+= clk-lochnagar.o
+obj-$(CONFIG_COMMON_CLK_HIFIBERRY_DACPRO)	+= clk-hifiberry-dacpro.o
+obj-$(CONFIG_COMMON_CLK_HIFIBERRY_DACPLUSHD)	+= clk-hifiberry-dachd.o
 obj-$(CONFIG_COMMON_CLK_MAX77686)	+= clk-max77686.o
 obj-$(CONFIG_COMMON_CLK_MAX9485)	+= clk-max9485.o
 obj-$(CONFIG_ARCH_MILBEAUT_M10V)	+= clk-milbeaut.o
diff -Nur --no-dereference linux-5.10.52-orig/drivers/dma/bcm2708-dmaengine.c linux-5.10.52-v7l+/drivers/dma/bcm2708-dmaengine.c
--- linux-5.10.52-orig/drivers/dma/bcm2708-dmaengine.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/dma/bcm2708-dmaengine.c	2021-07-25 16:45:46.338619951 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/*
+ * BCM2708 legacy DMA API
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/platform_data/dma-bcm2708.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/spinlock.h>
+
+#include "virt-dma.h"
+
+#define CACHE_LINE_MASK 31
+#define DEFAULT_DMACHAN_BITMAP 0x10  /* channel 4 only */
+
+/* valid only for channels 0 - 14, 15 has its own base address */
+#define BCM2708_DMA_CHAN(n)	((n) << 8) /* base address */
+#define BCM2708_DMA_CHANIO(dma_base, n) \
+	((void __iomem *)((char *)(dma_base) + BCM2708_DMA_CHAN(n)))
+
+struct vc_dmaman {
+	void __iomem *dma_base;
+	u32 chan_available; /* bitmap of available channels */
+	u32 has_feature[BCM_DMA_FEATURE_COUNT]; /* bitmap of feature presence */
+	struct mutex lock;
+};
+
+static struct device *dmaman_dev;	/* we assume there's only one! */
+static struct vc_dmaman *g_dmaman;	/* DMA manager */
+
+/* DMA Auxiliary Functions */
+
+/* A DMA buffer on an arbitrary boundary may separate a cache line into a
+   section inside the DMA buffer and another section outside it.
+   Even if we flush DMA buffers from the cache there is always the chance that
+   during a DMA someone will access the part of a cache line that is outside
+   the DMA buffer - which will then bring in unwelcome data.
+   Without being able to dictate our own buffer pools we must insist that
+   DMA buffers consist of a whole number of cache lines.
+*/
+extern int bcm_sg_suitable_for_dma(struct scatterlist *sg_ptr, int sg_len)
+{
+	int i;
+
+	for (i = 0; i < sg_len; i++) {
+		if (sg_ptr[i].offset & CACHE_LINE_MASK ||
+		    sg_ptr[i].length & CACHE_LINE_MASK)
+			return 0;
+	}
+
+	return 1;
+}
+EXPORT_SYMBOL_GPL(bcm_sg_suitable_for_dma);
+
+extern void bcm_dma_start(void __iomem *dma_chan_base,
+			  dma_addr_t control_block)
+{
+	dsb(sy);	/* ARM data synchronization (push) operation */
+
+	writel(control_block, dma_chan_base + BCM2708_DMA_ADDR);
+	writel(BCM2708_DMA_ACTIVE, dma_chan_base + BCM2708_DMA_CS);
+}
+EXPORT_SYMBOL_GPL(bcm_dma_start);
+
+extern void bcm_dma_wait_idle(void __iomem *dma_chan_base)
+{
+	dsb(sy);
+
+	/* ugly busy wait only option for now */
+	while (readl(dma_chan_base + BCM2708_DMA_CS) & BCM2708_DMA_ACTIVE)
+		cpu_relax();
+}
+EXPORT_SYMBOL_GPL(bcm_dma_wait_idle);
+
+extern bool bcm_dma_is_busy(void __iomem *dma_chan_base)
+{
+	dsb(sy);
+
+	return readl(dma_chan_base + BCM2708_DMA_CS) & BCM2708_DMA_ACTIVE;
+}
+EXPORT_SYMBOL_GPL(bcm_dma_is_busy);
+
+/* Complete an ongoing DMA (assuming its results are to be ignored)
+   Does nothing if there is no DMA in progress.
+   This routine waits for the current AXI transfer to complete before
+   terminating the current DMA. If the current transfer is hung on a DREQ used
+   by an uncooperative peripheral the AXI transfer may never complete.	In this
+   case the routine times out and return a non-zero error code.
+   Use of this routine doesn't guarantee that the ongoing or aborted DMA
+   does not produce an interrupt.
+*/
+extern int bcm_dma_abort(void __iomem *dma_chan_base)
+{
+	unsigned long int cs;
+	int rc = 0;
+
+	cs = readl(dma_chan_base + BCM2708_DMA_CS);
+
+	if (BCM2708_DMA_ACTIVE & cs) {
+		long int timeout = 10000;
+
+		/* write 0 to the active bit - pause the DMA */
+		writel(0, dma_chan_base + BCM2708_DMA_CS);
+
+		/* wait for any current AXI transfer to complete */
+		while (0 != (cs & BCM2708_DMA_ISPAUSED) && --timeout >= 0)
+			cs = readl(dma_chan_base + BCM2708_DMA_CS);
+
+		if (0 != (cs & BCM2708_DMA_ISPAUSED)) {
+			/* we'll un-pause when we set of our next DMA */
+			rc = -ETIMEDOUT;
+
+		} else if (BCM2708_DMA_ACTIVE & cs) {
+			/* terminate the control block chain */
+			writel(0, dma_chan_base + BCM2708_DMA_NEXTCB);
+
+			/* abort the whole DMA */
+			writel(BCM2708_DMA_ABORT | BCM2708_DMA_ACTIVE,
+			       dma_chan_base + BCM2708_DMA_CS);
+		}
+	}
+
+	return rc;
+}
+EXPORT_SYMBOL_GPL(bcm_dma_abort);
+
+ /* DMA Manager Device Methods */
+
+static void vc_dmaman_init(struct vc_dmaman *dmaman, void __iomem *dma_base,
+			   u32 chans_available)
+{
+	dmaman->dma_base = dma_base;
+	dmaman->chan_available = chans_available;
+	dmaman->has_feature[BCM_DMA_FEATURE_FAST_ORD] = 0x0c;  /* 2 & 3 */
+	dmaman->has_feature[BCM_DMA_FEATURE_BULK_ORD] = 0x01;  /* 0 */
+	dmaman->has_feature[BCM_DMA_FEATURE_NORMAL_ORD] = 0xfe;  /* 1 to 7 */
+	dmaman->has_feature[BCM_DMA_FEATURE_LITE_ORD] = 0x7f00;  /* 8 to 14 */
+}
+
+static int vc_dmaman_chan_alloc(struct vc_dmaman *dmaman,
+				unsigned required_feature_set)
+{
+	u32 chans;
+	int chan = 0;
+	int feature;
+
+	chans = dmaman->chan_available;
+	for (feature = 0; feature < BCM_DMA_FEATURE_COUNT; feature++)
+		/* select the subset of available channels with the desired
+		   features */
+		if (required_feature_set & (1 << feature))
+			chans &= dmaman->has_feature[feature];
+
+	if (!chans)
+		return -ENOENT;
+
+	/* return the ordinal of the first channel in the bitmap */
+	while (chans != 0 && (chans & 1) == 0) {
+		chans >>= 1;
+		chan++;
+	}
+	/* claim the channel */
+	dmaman->chan_available &= ~(1 << chan);
+
+	return chan;
+}
+
+static int vc_dmaman_chan_free(struct vc_dmaman *dmaman, int chan)
+{
+	if (chan < 0)
+		return -EINVAL;
+
+	if ((1 << chan) & dmaman->chan_available)
+		return -EIDRM;
+
+	dmaman->chan_available |= (1 << chan);
+
+	return 0;
+}
+
+/* DMA Manager Monitor */
+
+extern int bcm_dma_chan_alloc(unsigned required_feature_set,
+			      void __iomem **out_dma_base, int *out_dma_irq)
+{
+	struct vc_dmaman *dmaman = g_dmaman;
+	struct platform_device *pdev = to_platform_device(dmaman_dev);
+	struct resource *r;
+	int chan;
+
+	if (!dmaman_dev)
+		return -ENODEV;
+
+	mutex_lock(&dmaman->lock);
+	chan = vc_dmaman_chan_alloc(dmaman, required_feature_set);
+	if (chan < 0)
+		goto out;
+
+	r = platform_get_resource(pdev, IORESOURCE_IRQ, (unsigned int)chan);
+	if (!r) {
+		dev_err(dmaman_dev, "failed to get irq for DMA channel %d\n",
+			chan);
+		vc_dmaman_chan_free(dmaman, chan);
+		chan = -ENOENT;
+		goto out;
+	}
+
+	*out_dma_base = BCM2708_DMA_CHANIO(dmaman->dma_base, chan);
+	*out_dma_irq = r->start;
+	dev_dbg(dmaman_dev,
+		"Legacy API allocated channel=%d, base=%p, irq=%i\n",
+		chan, *out_dma_base, *out_dma_irq);
+
+out:
+	mutex_unlock(&dmaman->lock);
+
+	return chan;
+}
+EXPORT_SYMBOL_GPL(bcm_dma_chan_alloc);
+
+extern int bcm_dma_chan_free(int channel)
+{
+	struct vc_dmaman *dmaman = g_dmaman;
+	int rc;
+
+	if (!dmaman_dev)
+		return -ENODEV;
+
+	mutex_lock(&dmaman->lock);
+	rc = vc_dmaman_chan_free(dmaman, channel);
+	mutex_unlock(&dmaman->lock);
+
+	return rc;
+}
+EXPORT_SYMBOL_GPL(bcm_dma_chan_free);
+
+int bcm_dmaman_probe(struct platform_device *pdev, void __iomem *base,
+		     u32 chans_available)
+{
+	struct device *dev = &pdev->dev;
+	struct vc_dmaman *dmaman;
+
+	dmaman = devm_kzalloc(dev, sizeof(*dmaman), GFP_KERNEL);
+	if (!dmaman)
+		return -ENOMEM;
+
+	mutex_init(&dmaman->lock);
+	vc_dmaman_init(dmaman, base, chans_available);
+	g_dmaman = dmaman;
+	dmaman_dev = dev;
+
+	dev_info(dev, "DMA legacy API manager, dmachans=0x%x\n",
+		 chans_available);
+
+	return 0;
+}
+EXPORT_SYMBOL(bcm_dmaman_probe);
+
+int bcm_dmaman_remove(struct platform_device *pdev)
+{
+	dmaman_dev = NULL;
+
+	return 0;
+}
+EXPORT_SYMBOL(bcm_dmaman_remove);
+
+MODULE_LICENSE("GPL");
diff -Nur --no-dereference linux-5.10.52-orig/drivers/dma/bcm2835-dma.c linux-5.10.52-v7l+/drivers/dma/bcm2835-dma.c
--- linux-5.10.52-orig/drivers/dma/bcm2835-dma.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/dma/bcm2835-dma.c	2021-07-25 16:45:46.358619616 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:28 @
 #include <linux/interrupt.h>
 #include <linux/list.h>
 #include <linux/module.h>
+#include <linux/platform_data/dma-bcm2708.h>
 #include <linux/platform_device.h>
 #include <linux/slab.h>
 #include <linux/io.h>
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:40 @
 
 #define BCM2835_DMA_MAX_DMA_CHAN_SUPPORTED 14
 #define BCM2835_DMA_CHAN_NAME_SIZE 8
+#define BCM2835_DMA_BULK_MASK  BIT(0)
+#define BCM2711_DMA_MEMCPY_CHAN 14
+
+struct bcm2835_dma_cfg_data {
+	u64	dma_mask;
+	u32	chan_40bit_mask;
+};
 
 /**
  * struct bcm2835_dmadev - BCM2835 DMA controller
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:59 @
 	struct dma_device ddev;
 	void __iomem *base;
 	dma_addr_t zero_page;
+	const struct bcm2835_dma_cfg_data *cfg_data;
 };
 
 struct bcm2835_dma_cb {
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:72 @
 	uint32_t pad[2];
 };
 
+struct bcm2711_dma40_scb {
+	uint32_t ti;
+	uint32_t src;
+	uint32_t srci;
+	uint32_t dst;
+	uint32_t dsti;
+	uint32_t len;
+	uint32_t next_cb;
+	uint32_t rsvd;
+};
+
 struct bcm2835_cb_entry {
 	struct bcm2835_dma_cb *cb;
 	dma_addr_t paddr;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:103 @
 	unsigned int irq_flags;
 
 	bool is_lite_channel;
+	bool is_40bit_channel;
 };
 
 struct bcm2835_desc {
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:161 @
 #define BCM2835_DMA_S_DREQ	BIT(10) /* enable SREQ for source */
 #define BCM2835_DMA_S_IGNORE	BIT(11) /* ignore source reads - read 0 */
 #define BCM2835_DMA_BURST_LENGTH(x) ((x & 15) << 12)
+#define BCM2835_DMA_CS_FLAGS(x) (x & (BCM2835_DMA_PRIORITY(15) | \
+				      BCM2835_DMA_PANIC_PRIORITY(15) | \
+				      BCM2835_DMA_WAIT_FOR_WRITES | \
+				      BCM2835_DMA_DIS_DEBUG))
 #define BCM2835_DMA_PER_MAP(x)	((x & 31) << 16) /* REQ source */
 #define BCM2835_DMA_WAIT(x)	((x & 31) << 21) /* add DMA-wait cycles */
 #define BCM2835_DMA_NO_WIDE_BURSTS BIT(26) /* no 2 beat write bursts */
 
+/* A fake bit to request that the driver doesn't set the WAIT_RESP bit. */
+#define BCM2835_DMA_NO_WAIT_RESP BIT(27)
+#define WAIT_RESP(x) ((x & BCM2835_DMA_NO_WAIT_RESP) ? \
+		      0 : BCM2835_DMA_WAIT_RESP)
+
+/* A fake bit to request that the driver requires wide reads */
+#define BCM2835_DMA_WIDE_SOURCE BIT(24)
+#define WIDE_SOURCE(x) ((x & BCM2835_DMA_WIDE_SOURCE) ? \
+		      BCM2835_DMA_S_WIDTH : 0)
+
+/* A fake bit to request that the driver requires wide writes */
+#define BCM2835_DMA_WIDE_DEST BIT(25)
+#define WIDE_DEST(x) ((x & BCM2835_DMA_WIDE_DEST) ? \
+		      BCM2835_DMA_D_WIDTH : 0)
+
+
 /* debug register bits */
 #define BCM2835_DMA_DEBUG_LAST_NOT_SET_ERR	BIT(0)
 #define BCM2835_DMA_DEBUG_FIFO_ERR		BIT(1)
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:209 @
 #define BCM2835_DMA_DATA_TYPE_S128	16
 
 /* Valid only for channels 0 - 14, 15 has its own base address */
-#define BCM2835_DMA_CHAN(n)	((n) << 8) /* Base address */
+#define BCM2835_DMA_CHAN_SIZE	0x100
+#define BCM2835_DMA_CHAN(n)	((n) * BCM2835_DMA_CHAN_SIZE) /* Base address */
 #define BCM2835_DMA_CHANIO(base, n) ((base) + BCM2835_DMA_CHAN(n))
 
 /* the max dma length for different channels */
 #define MAX_DMA_LEN SZ_1G
 #define MAX_LITE_DMA_LEN (SZ_64K - 4)
 
+/* 40-bit DMA support */
+#define BCM2711_DMA40_CS	0x00
+#define BCM2711_DMA40_CB	0x04
+#define BCM2711_DMA40_DEBUG	0x0c
+#define BCM2711_DMA40_TI	0x10
+#define BCM2711_DMA40_SRC	0x14
+#define BCM2711_DMA40_SRCI	0x18
+#define BCM2711_DMA40_DEST	0x1c
+#define BCM2711_DMA40_DESTI	0x20
+#define BCM2711_DMA40_LEN	0x24
+#define BCM2711_DMA40_NEXT_CB	0x28
+#define BCM2711_DMA40_DEBUG2	0x2c
+
+#define BCM2711_DMA40_ACTIVE		BIT(0)
+#define BCM2711_DMA40_END		BIT(1)
+#define BCM2711_DMA40_INT		BIT(2)
+#define BCM2711_DMA40_DREQ		BIT(3)  /* DREQ state */
+#define BCM2711_DMA40_RD_PAUSED		BIT(4)  /* Reading is paused */
+#define BCM2711_DMA40_WR_PAUSED		BIT(5)  /* Writing is paused */
+#define BCM2711_DMA40_DREQ_PAUSED	BIT(6)  /* Is paused by DREQ flow control */
+#define BCM2711_DMA40_WAITING_FOR_WRITES BIT(7)  /* Waiting for last write */
+#define BCM2711_DMA40_ERR		BIT(10)
+#define BCM2711_DMA40_QOS(x)		(((x) & 0x1f) << 16)
+#define BCM2711_DMA40_PANIC_QOS(x)	(((x) & 0x1f) << 20)
+#define BCM2711_DMA40_WAIT_FOR_WRITES	BIT(28)
+#define BCM2711_DMA40_DISDEBUG		BIT(29)
+#define BCM2711_DMA40_ABORT		BIT(30)
+#define BCM2711_DMA40_HALT		BIT(31)
+#define BCM2711_DMA40_CS_FLAGS(x) (x & (BCM2711_DMA40_QOS(15) | \
+					BCM2711_DMA40_PANIC_QOS(15) | \
+					BCM2711_DMA40_WAIT_FOR_WRITES |	\
+					BCM2711_DMA40_DISDEBUG))
+
+/* Transfer information bits */
+#define BCM2711_DMA40_INTEN		BIT(0)
+#define BCM2711_DMA40_TDMODE		BIT(1) /* 2D-Mode */
+#define BCM2711_DMA40_WAIT_RESP		BIT(2) /* wait for AXI write to be acked */
+#define BCM2711_DMA40_WAIT_RD_RESP	BIT(3) /* wait for AXI read to complete */
+#define BCM2711_DMA40_PER_MAP(x)	((x & 31) << 9) /* REQ source */
+#define BCM2711_DMA40_S_DREQ		BIT(14) /* enable SREQ for source */
+#define BCM2711_DMA40_D_DREQ		BIT(15) /* enable DREQ for destination */
+#define BCM2711_DMA40_S_WAIT(x)		((x & 0xff) << 16) /* add DMA read-wait cycles */
+#define BCM2711_DMA40_D_WAIT(x)		((x & 0xff) << 24) /* add DMA write-wait cycles */
+
+/* debug register bits */
+#define BCM2711_DMA40_DEBUG_WRITE_ERR		BIT(0)
+#define BCM2711_DMA40_DEBUG_FIFO_ERR		BIT(1)
+#define BCM2711_DMA40_DEBUG_READ_ERR		BIT(2)
+#define BCM2711_DMA40_DEBUG_READ_CB_ERR		BIT(3)
+#define BCM2711_DMA40_DEBUG_IN_ON_ERR		BIT(8)
+#define BCM2711_DMA40_DEBUG_ABORT_ON_ERR	BIT(9)
+#define BCM2711_DMA40_DEBUG_HALT_ON_ERR		BIT(10)
+#define BCM2711_DMA40_DEBUG_DISABLE_CLK_GATE	BIT(11)
+#define BCM2711_DMA40_DEBUG_RSTATE_SHIFT	14
+#define BCM2711_DMA40_DEBUG_RSTATE_BITS		4
+#define BCM2711_DMA40_DEBUG_WSTATE_SHIFT	18
+#define BCM2711_DMA40_DEBUG_WSTATE_BITS		4
+#define BCM2711_DMA40_DEBUG_RESET		BIT(23)
+#define BCM2711_DMA40_DEBUG_ID_SHIFT		24
+#define BCM2711_DMA40_DEBUG_ID_BITS		4
+#define BCM2711_DMA40_DEBUG_VERSION_SHIFT	28
+#define BCM2711_DMA40_DEBUG_VERSION_BITS	4
+
+/* Valid only for channels 0 - 3 (11 - 14) */
+#define BCM2711_DMA40_CHAN(n)	(((n) + 11) << 8) /* Base address */
+#define BCM2711_DMA40_CHANIO(base, n) ((base) + BCM2711_DMA_CHAN(n))
+
+/* the max dma length for different channels */
+#define MAX_DMA40_LEN SZ_1G
+
+#define BCM2711_DMA40_BURST_LEN(x)	((min(x,16) - 1) << 8)
+#define BCM2711_DMA40_INC		BIT(12)
+#define BCM2711_DMA40_SIZE_32		(0 << 13)
+#define BCM2711_DMA40_SIZE_64		(1 << 13)
+#define BCM2711_DMA40_SIZE_128		(2 << 13)
+#define BCM2711_DMA40_SIZE_256		(3 << 13)
+#define BCM2711_DMA40_IGNORE		BIT(15)
+#define BCM2711_DMA40_STRIDE(x)		((x) << 16) /* For 2D mode */
+
+#define BCM2711_DMA40_MEMCPY_FLAGS \
+	(BCM2711_DMA40_QOS(0) | \
+	 BCM2711_DMA40_PANIC_QOS(0) | \
+	 BCM2711_DMA40_WAIT_FOR_WRITES | \
+	 BCM2711_DMA40_DISDEBUG)
+
+#define BCM2711_DMA40_MEMCPY_XFER_INFO \
+	(BCM2711_DMA40_SIZE_128 | \
+	 BCM2711_DMA40_INC | \
+	 BCM2711_DMA40_BURST_LEN(16))
+
+struct bcm2835_dmadev *memcpy_parent;
+static void __iomem *memcpy_chan;
+static struct bcm2711_dma40_scb *memcpy_scb;
+static dma_addr_t memcpy_scb_dma;
+DEFINE_SPINLOCK(memcpy_lock);
+
+static const struct bcm2835_dma_cfg_data bcm2835_dma_cfg = {
+	.chan_40bit_mask = 0,
+	.dma_mask = DMA_BIT_MASK(32),
+};
+
+static const struct bcm2835_dma_cfg_data bcm2711_dma_cfg = {
+	.chan_40bit_mask = BIT(11) | BIT(12) | BIT(13) | BIT(14),
+	.dma_mask = DMA_BIT_MASK(36),
+};
+
 static inline size_t bcm2835_dma_max_frame_length(struct bcm2835_chan *c)
 {
 	/* lite and normal channels have different max frame length */
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:352 @
 	return container_of(t, struct bcm2835_desc, vd.tx);
 }
 
+static inline uint32_t to_bcm2711_ti(uint32_t info)
+{
+	return ((info & BCM2835_DMA_INT_EN) ? BCM2711_DMA40_INTEN : 0) |
+		((info & BCM2835_DMA_WAIT_RESP) ? BCM2711_DMA40_WAIT_RESP : 0) |
+		((info & BCM2835_DMA_S_DREQ) ?
+		 (BCM2711_DMA40_S_DREQ | BCM2711_DMA40_WAIT_RD_RESP) : 0) |
+		((info & BCM2835_DMA_D_DREQ) ? BCM2711_DMA40_D_DREQ : 0) |
+		BCM2711_DMA40_PER_MAP((info >> 16) & 0x1f);
+}
+
+static inline uint32_t to_bcm2711_srci(uint32_t info)
+{
+	return ((info & BCM2835_DMA_S_INC) ? BCM2711_DMA40_INC : 0);
+}
+
+static inline uint32_t to_bcm2711_dsti(uint32_t info)
+{
+	return ((info & BCM2835_DMA_D_INC) ? BCM2711_DMA40_INC : 0);
+}
+
+static inline uint32_t to_bcm2711_cbaddr(dma_addr_t addr)
+{
+	BUG_ON(addr & 0x1f);
+	return (addr >> 5);
+}
+
 static void bcm2835_dma_free_cb_chain(struct bcm2835_desc *desc)
 {
 	size_t i;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:396 @
 }
 
 static void bcm2835_dma_create_cb_set_length(
-	struct bcm2835_chan *chan,
+	struct bcm2835_chan *c,
 	struct bcm2835_dma_cb *control_block,
 	size_t len,
 	size_t period_len,
 	size_t *total_len,
 	u32 finalextrainfo)
 {
-	size_t max_len = bcm2835_dma_max_frame_length(chan);
+	size_t max_len = bcm2835_dma_max_frame_length(c);
+	uint32_t cb_len;
 
 	/* set the length taking lite-channel limitations into account */
-	control_block->length = min_t(u32, len, max_len);
+	cb_len = min_t(u32, len, max_len);
 
-	/* finished if we have no period_length */
-	if (!period_len)
-		return;
+	if (period_len) {
+		/*
+		 * period_len means: that we need to generate
+		 * transfers that are terminating at every
+		 * multiple of period_len - this is typically
+		 * used to set the interrupt flag in info
+		 * which is required during cyclic transfers
+		 */
 
-	/*
-	 * period_len means: that we need to generate
-	 * transfers that are terminating at every
-	 * multiple of period_len - this is typically
-	 * used to set the interrupt flag in info
-	 * which is required during cyclic transfers
-	 */
+		/* have we filled in period_length yet? */
+		if (*total_len + cb_len < period_len) {
+			/* update number of bytes in this period so far */
+			*total_len += cb_len;
+		} else {
+			/* calculate the length that remains to reach period_len */
+			cb_len = period_len - *total_len;
 
-	/* have we filled in period_length yet? */
-	if (*total_len + control_block->length < period_len) {
-		/* update number of bytes in this period so far */
-		*total_len += control_block->length;
-		return;
+			/* reset total_length for next period */
+			*total_len = 0;
+		}
 	}
 
-	/* calculate the length that remains to reach period_length */
-	control_block->length = period_len - *total_len;
-
-	/* reset total_length for next period */
-	*total_len = 0;
-
-	/* add extrainfo bits in info */
-	control_block->info |= finalextrainfo;
+	if (c->is_40bit_channel) {
+		struct bcm2711_dma40_scb *scb =
+			(struct bcm2711_dma40_scb *)control_block;
+
+		scb->len = cb_len;
+		/* add extrainfo bits to ti */
+		scb->ti |= to_bcm2711_ti(finalextrainfo);
+	} else {
+		control_block->length = cb_len;
+		/* add extrainfo bits to info */
+		control_block->info |= finalextrainfo;
+	}
 }
 
 static inline size_t bcm2835_dma_count_frames_for_sg(
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:465 @
 /**
  * bcm2835_dma_create_cb_chain - create a control block and fills data in
  *
- * @chan:           the @dma_chan for which we run this
+ * @c:              the @bcm2835_chan for which we run this
  * @direction:      the direction in which we transfer
  * @cyclic:         it is a cyclic transfer
  * @info:           the default info bits to apply per controlblock
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:483 @
  * @gfp:            the GFP flag to use for allocation
  */
 static struct bcm2835_desc *bcm2835_dma_create_cb_chain(
-	struct dma_chan *chan, enum dma_transfer_direction direction,
+	struct bcm2835_chan *c, enum dma_transfer_direction direction,
 	bool cyclic, u32 info, u32 finalextrainfo, size_t frames,
 	dma_addr_t src, dma_addr_t dst, size_t buf_len,
 	size_t period_len, gfp_t gfp)
 {
-	struct bcm2835_chan *c = to_bcm2835_dma_chan(chan);
 	size_t len = buf_len, total_len;
 	size_t frame;
 	struct bcm2835_desc *d;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:519 @
 
 		/* fill in the control block */
 		control_block = cb_entry->cb;
-		control_block->info = info;
-		control_block->src = src;
-		control_block->dst = dst;
-		control_block->stride = 0;
-		control_block->next = 0;
+		if (c->is_40bit_channel) {
+			struct bcm2711_dma40_scb *scb =
+				(struct bcm2711_dma40_scb *)control_block;
+			scb->ti = to_bcm2711_ti(info);
+			scb->src = lower_32_bits(src);
+			scb->srci= upper_32_bits(src) | to_bcm2711_srci(info);
+			scb->dst = lower_32_bits(dst);
+			scb->dsti = upper_32_bits(dst) | to_bcm2711_dsti(info);
+			scb->next_cb = 0;
+		} else {
+			control_block->info = info;
+			control_block->src = src;
+			control_block->dst = dst;
+			control_block->stride = 0;
+			control_block->next = 0;
+		}
+
 		/* set up length in control_block if requested */
 		if (buf_len) {
 			/* calculate length honoring period_length */
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:549 @
 		}
 
 		/* link this the last controlblock */
-		if (frame)
+		if (frame && c->is_40bit_channel)
+			((struct bcm2711_dma40_scb *)
+			 d->cb_list[frame - 1].cb)->next_cb =
+				to_bcm2711_cbaddr(cb_entry->paddr);
+		if (frame && !c->is_40bit_channel)
 			d->cb_list[frame - 1].cb->next = cb_entry->paddr;
 
 		/* update src and dst and length */
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:563 @
 			dst += control_block->length;
 
 		/* Length of total transfer */
-		d->size += control_block->length;
+		if (c->is_40bit_channel)
+			d->size += ((struct bcm2711_dma40_scb *)control_block)->len;
+		else
+			d->size += control_block->length;
 	}
 
 	/* the last frame requires extra flags */
-	d->cb_list[d->frames - 1].cb->info |= finalextrainfo;
+	if (c->is_40bit_channel) {
+		struct bcm2711_dma40_scb *scb =
+			(struct bcm2711_dma40_scb *)d->cb_list[d->frames-1].cb;
+
+		scb->ti |= to_bcm2711_ti(finalextrainfo);
+	} else {
+		d->cb_list[d->frames - 1].cb->info |= finalextrainfo;
+	}
 
 	/* detect a size missmatch */
 	if (buf_len && (d->size != buf_len))
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:591 @
 }
 
 static void bcm2835_dma_fill_cb_chain_with_sg(
-	struct dma_chan *chan,
+	struct bcm2835_chan *c,
 	enum dma_transfer_direction direction,
 	struct bcm2835_cb_entry *cb,
 	struct scatterlist *sgl,
 	unsigned int sg_len)
 {
-	struct bcm2835_chan *c = to_bcm2835_dma_chan(chan);
 	size_t len, max_len;
 	unsigned int i;
 	dma_addr_t addr;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:604 @
 
 	max_len = bcm2835_dma_max_frame_length(c);
 	for_each_sg(sgl, sgent, sg_len, i) {
-		for (addr = sg_dma_address(sgent), len = sg_dma_len(sgent);
-		     len > 0;
-		     addr += cb->cb->length, len -= cb->cb->length, cb++) {
-			if (direction == DMA_DEV_TO_MEM)
-				cb->cb->dst = addr;
-			else
-				cb->cb->src = addr;
-			cb->cb->length = min(len, max_len);
+		if (c->is_40bit_channel) {
+			struct bcm2711_dma40_scb *scb;
+
+			for (addr = sg_dma_address(sgent),
+				     len = sg_dma_len(sgent);
+				     len > 0;
+			     addr += scb->len, len -= scb->len, cb++) {
+				scb = (struct bcm2711_dma40_scb *)cb->cb;
+				if (direction == DMA_DEV_TO_MEM) {
+					scb->dst = lower_32_bits(addr);
+					scb->dsti = upper_32_bits(addr) | BCM2711_DMA40_INC;
+				} else {
+					scb->src = lower_32_bits(addr);
+					scb->srci = upper_32_bits(addr) | BCM2711_DMA40_INC;
+				}
+				scb->len = min(len, max_len);
+			}
+		} else {
+			for (addr = sg_dma_address(sgent),
+				     len = sg_dma_len(sgent);
+			     len > 0;
+			     addr += cb->cb->length, len -= cb->cb->length,
+			     cb++) {
+				if (direction == DMA_DEV_TO_MEM)
+					cb->cb->dst = addr;
+				else
+					cb->cb->src = addr;
+				cb->cb->length = min(len, max_len);
+			}
 		}
 	}
 }
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:641 @
 {
 	void __iomem *chan_base = c->chan_base;
 	long int timeout = 10000;
+	u32 wait_mask = BCM2835_DMA_WAITING_FOR_WRITES;
+
+	if (c->is_40bit_channel)
+		wait_mask = BCM2711_DMA40_WAITING_FOR_WRITES;
 
 	/*
 	 * A zero control block address means the channel is idle.
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:657 @
 	writel(0, chan_base + BCM2835_DMA_CS);
 
 	/* Wait for any current AXI transfer to complete */
-	while ((readl(chan_base + BCM2835_DMA_CS) &
-		BCM2835_DMA_WAITING_FOR_WRITES) && --timeout)
+	while ((readl(chan_base + BCM2835_DMA_CS) & wait_mask) && --timeout)
 		cpu_relax();
 
 	/* Peripheral might be stuck and fail to signal AXI write responses */
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:682 @
 
 	c->desc = d = to_bcm2835_dma_desc(&vd->tx);
 
-	writel(d->cb_list[0].paddr, c->chan_base + BCM2835_DMA_ADDR);
-	writel(BCM2835_DMA_ACTIVE, c->chan_base + BCM2835_DMA_CS);
+	if (c->is_40bit_channel) {
+		writel(to_bcm2711_cbaddr(d->cb_list[0].paddr),
+		       c->chan_base + BCM2711_DMA40_CB);
+		writel(BCM2711_DMA40_ACTIVE | BCM2711_DMA40_CS_FLAGS(c->dreq),
+		       c->chan_base + BCM2711_DMA40_CS);
+	} else {
+		writel(d->cb_list[0].paddr, c->chan_base + BCM2835_DMA_ADDR);
+		writel(BCM2835_DMA_ACTIVE | BCM2835_DMA_CS_FLAGS(c->dreq),
+		       c->chan_base + BCM2835_DMA_CS);
+	}
 }
 
 static irqreturn_t bcm2835_dma_callback(int irq, void *data)
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:718 @
 	 * if this IRQ handler is threaded.) If the channel is finished, it
 	 * will remain idle despite the ACTIVE flag being set.
 	 */
-	writel(BCM2835_DMA_INT | BCM2835_DMA_ACTIVE,
+	writel(BCM2835_DMA_INT | BCM2835_DMA_ACTIVE | BCM2835_DMA_CS_FLAGS(c->dreq),
 	       c->chan_base + BCM2835_DMA_CS);
 
 	d = c->desc;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:821 @
 		struct bcm2835_desc *d = c->desc;
 		dma_addr_t pos;
 
-		if (d->dir == DMA_MEM_TO_DEV)
+		if (d->dir == DMA_MEM_TO_DEV && c->is_40bit_channel)
+			pos = readl(c->chan_base + BCM2711_DMA40_SRC) +
+				((readl(c->chan_base + BCM2711_DMA40_SRCI) &
+				  0xff) << 8);
+		else if (d->dir == DMA_MEM_TO_DEV && !c->is_40bit_channel)
 			pos = readl(c->chan_base + BCM2835_DMA_SOURCE_AD);
-		else if (d->dir == DMA_DEV_TO_MEM)
+		else if (d->dir == DMA_DEV_TO_MEM && c->is_40bit_channel)
+			pos = readl(c->chan_base + BCM2711_DMA40_DEST) +
+				((readl(c->chan_base + BCM2711_DMA40_DESTI) &
+				  0xff) << 8);
+		else if (d->dir == DMA_DEV_TO_MEM && !c->is_40bit_channel)
 			pos = readl(c->chan_base + BCM2835_DMA_DEST_AD);
 		else
 			pos = 0;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:864 @
 {
 	struct bcm2835_chan *c = to_bcm2835_dma_chan(chan);
 	struct bcm2835_desc *d;
-	u32 info = BCM2835_DMA_D_INC | BCM2835_DMA_S_INC;
-	u32 extra = BCM2835_DMA_INT_EN | BCM2835_DMA_WAIT_RESP;
+	u32 info = BCM2835_DMA_D_INC | BCM2835_DMA_S_INC | WAIT_RESP(c->dreq) |
+		   WIDE_SOURCE(c->dreq) | WIDE_DEST(c->dreq);
+	u32 extra = BCM2835_DMA_INT_EN;
 	size_t max_len = bcm2835_dma_max_frame_length(c);
 	size_t frames;
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:878 @
 	frames = bcm2835_dma_frames_for_length(len, max_len);
 
 	/* allocate the CB chain - this also fills in the pointers */
-	d = bcm2835_dma_create_cb_chain(chan, DMA_MEM_TO_MEM, false,
+	d = bcm2835_dma_create_cb_chain(c, DMA_MEM_TO_MEM, false,
 					info, extra, frames,
 					src, dst, len, 0, GFP_KERNEL);
 	if (!d)
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:896 @
 	struct bcm2835_chan *c = to_bcm2835_dma_chan(chan);
 	struct bcm2835_desc *d;
 	dma_addr_t src = 0, dst = 0;
-	u32 info = BCM2835_DMA_WAIT_RESP;
+	u32 info = WAIT_RESP(c->dreq) |
+		   WIDE_SOURCE(c->dreq) | WIDE_DEST(c->dreq);
 	u32 extra = BCM2835_DMA_INT_EN;
 	size_t frames;
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:914 @
 		if (c->cfg.src_addr_width != DMA_SLAVE_BUSWIDTH_4_BYTES)
 			return NULL;
 		src = c->cfg.src_addr;
+		/*
+		 * One would think it ought to be possible to get the physical
+		 * to dma address mapping information from the dma-ranges DT
+		 * property, but I've not found a way yet that doesn't involve
+		 * open-coding the whole thing.
+		 */
+		if (c->is_40bit_channel)
+		    src |= 0x400000000ull;
 		info |= BCM2835_DMA_S_DREQ | BCM2835_DMA_D_INC;
 	} else {
 		if (c->cfg.dst_addr_width != DMA_SLAVE_BUSWIDTH_4_BYTES)
 			return NULL;
 		dst = c->cfg.dst_addr;
+		if (c->is_40bit_channel)
+		    dst |= 0x400000000ull;
 		info |= BCM2835_DMA_D_DREQ | BCM2835_DMA_S_INC;
 	}
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:936 @
 	frames = bcm2835_dma_count_frames_for_sg(c, sgl, sg_len);
 
 	/* allocate the CB chain */
-	d = bcm2835_dma_create_cb_chain(chan, direction, false,
+	d = bcm2835_dma_create_cb_chain(c, direction, false,
 					info, extra,
 					frames, src, dst, 0, 0,
 					GFP_NOWAIT);
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:944 @
 		return NULL;
 
 	/* fill in frames with scatterlist pointers */
-	bcm2835_dma_fill_cb_chain_with_sg(chan, direction, d->cb_list,
+	bcm2835_dma_fill_cb_chain_with_sg(c, direction, d->cb_list,
 					  sgl, sg_len);
 
 	return vchan_tx_prep(&c->vc, &d->vd, flags);
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:959 @
 	struct bcm2835_chan *c = to_bcm2835_dma_chan(chan);
 	struct bcm2835_desc *d;
 	dma_addr_t src, dst;
-	u32 info = BCM2835_DMA_WAIT_RESP;
+	u32 info = WAIT_RESP(c->dreq) | WIDE_SOURCE(c->dreq) | WIDE_DEST(c->dreq);
 	u32 extra = 0;
 	size_t max_len = bcm2835_dma_max_frame_length(c);
 	size_t frames;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:998 @
 		if (c->cfg.src_addr_width != DMA_SLAVE_BUSWIDTH_4_BYTES)
 			return NULL;
 		src = c->cfg.src_addr;
+		if (c->is_40bit_channel)
+		    src |= 0x400000000ull;
 		dst = buf_addr;
 		info |= BCM2835_DMA_S_DREQ | BCM2835_DMA_D_INC;
 	} else {
 		if (c->cfg.dst_addr_width != DMA_SLAVE_BUSWIDTH_4_BYTES)
 			return NULL;
 		dst = c->cfg.dst_addr;
+		if (c->is_40bit_channel)
+		    dst |= 0x400000000ull;
 		src = buf_addr;
 		info |= BCM2835_DMA_D_DREQ | BCM2835_DMA_S_INC;
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1027 @
 	 * note that we need to use GFP_NOWAIT, as the ALSA i2s dmaengine
 	 * implementation calls prep_dma_cyclic with interrupts disabled.
 	 */
-	d = bcm2835_dma_create_cb_chain(chan, direction, true,
+	d = bcm2835_dma_create_cb_chain(c, direction, true,
 					info, extra,
 					frames, src, dst, buf_len,
 					period_len, GFP_NOWAIT);
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1035 @
 		return NULL;
 
 	/* wrap around into a loop */
-	d->cb_list[d->frames - 1].cb->next = d->cb_list[0].paddr;
+	if (c->is_40bit_channel)
+		((struct bcm2711_dma40_scb *)
+		 d->cb_list[frames - 1].cb)->next_cb =
+			to_bcm2711_cbaddr(d->cb_list[0].paddr);
+	else
+		d->cb_list[d->frames - 1].cb->next = d->cb_list[0].paddr;
 
 	return vchan_tx_prep(&c->vc, &d->vd, flags);
 }
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1101 @
 	c->irq_number = irq;
 	c->irq_flags = irq_flags;
 
-	/* check in DEBUG register if this is a LITE channel */
-	if (readl(c->chan_base + BCM2835_DMA_DEBUG) &
-		BCM2835_DMA_DEBUG_LITE)
+	/* check for 40bit and lite channels */
+	if (d->cfg_data->chan_40bit_mask & BIT(chan_id))
+		c->is_40bit_channel = true;
+	else if (readl(c->chan_base + BCM2835_DMA_DEBUG) &
+		 BCM2835_DMA_DEBUG_LITE)
 		c->is_lite_channel = true;
 
 	return 0;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1125 @
 			     DMA_TO_DEVICE, DMA_ATTR_SKIP_CPU_SYNC);
 }
 
+int bcm2711_dma40_memcpy_init(void)
+{
+	if (!memcpy_parent)
+		return -EPROBE_DEFER;
+
+	if (!memcpy_chan)
+		return -EINVAL;
+
+	if (!memcpy_scb)
+		return -ENOMEM;
+
+	return 0;
+}
+EXPORT_SYMBOL(bcm2711_dma40_memcpy_init);
+
+void bcm2711_dma40_memcpy(dma_addr_t dst, dma_addr_t src, size_t size)
+{
+	struct bcm2711_dma40_scb *scb = memcpy_scb;
+	unsigned long flags;
+
+	if (!scb) {
+		pr_err("bcm2711_dma40_memcpy not initialised!\n");
+		return;
+	}
+
+	spin_lock_irqsave(&memcpy_lock, flags);
+
+	scb->ti = 0;
+	scb->src = lower_32_bits(src);
+	scb->srci = upper_32_bits(src) | BCM2711_DMA40_MEMCPY_XFER_INFO;
+	scb->dst = lower_32_bits(dst);
+	scb->dsti = upper_32_bits(dst) | BCM2711_DMA40_MEMCPY_XFER_INFO;
+	scb->len = size;
+	scb->next_cb = 0;
+
+	writel((u32)(memcpy_scb_dma >> 5), memcpy_chan + BCM2711_DMA40_CB);
+	writel(BCM2711_DMA40_MEMCPY_FLAGS + BCM2711_DMA40_ACTIVE,
+	       memcpy_chan + BCM2711_DMA40_CS);
+
+	/* Poll for completion */
+	while (!(readl(memcpy_chan + BCM2711_DMA40_CS) & BCM2711_DMA40_END))
+		cpu_relax();
+
+	writel(BCM2711_DMA40_END, memcpy_chan + BCM2711_DMA40_CS);
+
+	spin_unlock_irqrestore(&memcpy_lock, flags);
+}
+EXPORT_SYMBOL(bcm2711_dma40_memcpy);
+
 static const struct of_device_id bcm2835_dma_of_match[] = {
-	{ .compatible = "brcm,bcm2835-dma", },
+	{ .compatible = "brcm,bcm2835-dma", .data = &bcm2835_dma_cfg },
+	{ .compatible = "brcm,bcm2711-dma", .data = &bcm2711_dma_cfg },
 	{},
 };
 MODULE_DEVICE_TABLE(of, bcm2835_dma_of_match);
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1199 @
 
 static int bcm2835_dma_probe(struct platform_device *pdev)
 {
+	const struct bcm2835_dma_cfg_data *cfg_data;
+	const struct of_device_id *of_id;
 	struct bcm2835_dmadev *od;
 	struct resource *res;
 	void __iomem *base;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1210 @
 	int irq_flags;
 	uint32_t chans_available;
 	char chan_name[BCM2835_DMA_CHAN_NAME_SIZE];
+	int chan_count, chan_start, chan_end;
+
+	of_id = of_match_node(bcm2835_dma_of_match, pdev->dev.of_node);
+	if (!of_id) {
+		dev_err(&pdev->dev, "Failed to match compatible string\n");
+		return -EINVAL;
+	}
+
+	cfg_data = of_id->data;
 
 	if (!pdev->dev.dma_mask)
 		pdev->dev.dma_mask = &pdev->dev.coherent_dma_mask;
 
-	rc = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
+	rc = dma_set_mask_and_coherent(&pdev->dev, cfg_data->dma_mask);
 	if (rc) {
 		dev_err(&pdev->dev, "Unable to set DMA mask\n");
 		return rc;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1240 @
 	if (IS_ERR(base))
 		return PTR_ERR(base);
 
+	/* The set of channels can be split across multiple instances. */
+	chan_start = ((u32)(uintptr_t)base / BCM2835_DMA_CHAN_SIZE) & 0xf;
+	base -= BCM2835_DMA_CHAN(chan_start);
+	chan_count = resource_size(res) / BCM2835_DMA_CHAN_SIZE;
+	chan_end = min(chan_start + chan_count,
+			 BCM2835_DMA_MAX_DMA_CHAN_SUPPORTED + 1);
+
 	od->base = base;
 
 	dma_cap_set(DMA_SLAVE, od->ddev.cap_mask);
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1282 @
 		return -ENOMEM;
 	}
 
+	of_id = of_match_node(bcm2835_dma_of_match, pdev->dev.of_node);
+	if (!of_id) {
+		dev_err(&pdev->dev, "Failed to match compatible string\n");
+		return -EINVAL;
+	}
+
+	od->cfg_data = cfg_data;
+
 	/* Request DMA channel mask from device tree */
 	if (of_property_read_u32(pdev->dev.of_node,
 			"brcm,dma-channel-mask",
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1299 @
 		goto err_no_dma;
 	}
 
+#ifdef CONFIG_DMA_BCM2708
+	/* One channel is reserved for the legacy API */
+	if (chans_available & BCM2835_DMA_BULK_MASK) {
+		rc = bcm_dmaman_probe(pdev, base,
+				      chans_available & BCM2835_DMA_BULK_MASK);
+		if (rc)
+			dev_err(&pdev->dev,
+				"Failed to initialize the legacy API\n");
+
+		chans_available &= ~BCM2835_DMA_BULK_MASK;
+	}
+#endif
+
+	/* And possibly one for the 40-bit DMA memcpy API */
+	if (chans_available & od->cfg_data->chan_40bit_mask &
+	    BIT(BCM2711_DMA_MEMCPY_CHAN)) {
+		memcpy_parent = od;
+		memcpy_chan = BCM2835_DMA_CHANIO(base, BCM2711_DMA_MEMCPY_CHAN);
+		memcpy_scb = dma_alloc_coherent(memcpy_parent->ddev.dev,
+						sizeof(*memcpy_scb),
+						&memcpy_scb_dma, GFP_KERNEL);
+		if (!memcpy_scb)
+			dev_warn(&pdev->dev,
+				 "Failed to allocated memcpy scb\n");
+
+		chans_available &= ~BIT(BCM2711_DMA_MEMCPY_CHAN);
+	}
+
 	/* get irqs for each channel that we support */
-	for (i = 0; i <= BCM2835_DMA_MAX_DMA_CHAN_SUPPORTED; i++) {
+	for (i = chan_start; i < chan_end; i++) {
 		/* skip masked out channels */
 		if (!(chans_available & (1 << i))) {
 			irq[i] = -1;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1351 @
 		irq[i] = platform_get_irq(pdev, i < 11 ? i : 11);
 	}
 
+	chan_count = 0;
+
 	/* get irqs for each channel */
-	for (i = 0; i <= BCM2835_DMA_MAX_DMA_CHAN_SUPPORTED; i++) {
+	for (i = chan_start; i < chan_end; i++) {
 		/* skip channels without irq */
 		if (irq[i] < 0)
 			continue;
 
 		/* check if there are other channels that also use this irq */
+		/* FIXME: This will fail if interrupts are shared across
+		   instances */
 		irq_flags = 0;
 		for (j = 0; j <= BCM2835_DMA_MAX_DMA_CHAN_SUPPORTED; j++)
 			if ((i != j) && (irq[j] == irq[i])) {
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1373 @
 		rc = bcm2835_dma_chan_init(od, i, irq[i], irq_flags);
 		if (rc)
 			goto err_no_dma;
+		chan_count++;
 	}
 
-	dev_dbg(&pdev->dev, "Initialized %i DMA channels\n", i);
+	dev_dbg(&pdev->dev, "Initialized %i DMA channels\n", chan_count);
 
 	/* Device-tree DMA controller registration */
 	rc = of_dma_controller_register(pdev->dev.of_node,
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1406 @
 {
 	struct bcm2835_dmadev *od = platform_get_drvdata(pdev);
 
+	bcm_dmaman_remove(pdev);
 	dma_async_device_unregister(&od->ddev);
+	if (memcpy_parent == od) {
+		dma_free_coherent(&pdev->dev, sizeof(*memcpy_scb), memcpy_scb,
+				  memcpy_scb_dma);
+		memcpy_parent = NULL;
+		memcpy_scb = NULL;
+		memcpy_chan = NULL;
+	}
 	bcm2835_dma_free(od);
 
 	return 0;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1429 @
 	},
 };
 
-module_platform_driver(bcm2835_dma_driver);
+static int bcm2835_dma_init(void)
+{
+	return platform_driver_register(&bcm2835_dma_driver);
+}
+
+static void bcm2835_dma_exit(void)
+{
+	platform_driver_unregister(&bcm2835_dma_driver);
+}
+
+/*
+ * Load after serial driver (arch_initcall) so we see the messages if it fails,
+ * but before drivers (module_init) that need a DMA channel.
+ */
+subsys_initcall(bcm2835_dma_init);
+module_exit(bcm2835_dma_exit);
 
 MODULE_ALIAS("platform:bcm2835-dma");
 MODULE_DESCRIPTION("BCM2835 DMA engine driver");
diff -Nur --no-dereference linux-5.10.52-orig/drivers/dma/Kconfig linux-5.10.52-v7l+/drivers/dma/Kconfig
--- linux-5.10.52-orig/drivers/dma/Kconfig	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/dma/Kconfig	2021-07-25 16:45:46.318620287 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:655 @
 	  UniPhier platform. This DMA controller can transfer data from
 	  memory to memory, memory to peripheral and peripheral to memory.
 
+config DMA_BCM2708
+	tristate "BCM2708 DMA legacy API support"
+	depends on DMA_BCM2835
+
 config XGENE_DMA
 	tristate "APM X-Gene DMA support"
 	depends on ARCH_XGENE || COMPILE_TEST
diff -Nur --no-dereference linux-5.10.52-orig/drivers/dma/Makefile linux-5.10.52-v7l+/drivers/dma/Makefile
--- linux-5.10.52-orig/drivers/dma/Makefile	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/dma/Makefile	2021-07-25 16:45:46.318620287 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:24 @
 obj-$(CONFIG_AXI_DMAC) += dma-axi-dmac.o
 obj-$(CONFIG_BCM_SBA_RAID) += bcm-sba-raid.o
 obj-$(CONFIG_COH901318) += coh901318.o coh901318_lli.o
+obj-$(CONFIG_DMA_BCM2708) += bcm2708-dmaengine.o
 obj-$(CONFIG_DMA_BCM2835) += bcm2835-dma.o
 obj-$(CONFIG_DMA_JZ4780) += dma-jz4780.o
 obj-$(CONFIG_DMA_SA11X0) += sa11x0-dma.o
diff -Nur --no-dereference linux-5.10.52-orig/drivers/firmware/raspberrypi.c linux-5.10.52-v7l+/drivers/firmware/raspberrypi.c
--- linux-5.10.52-orig/drivers/firmware/raspberrypi.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/firmware/raspberrypi.c	2021-07-25 16:45:46.948609724 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:14 @
 #include <linux/module.h>
 #include <linux/of_platform.h>
 #include <linux/platform_device.h>
+#include <linux/reboot.h>
 #include <linux/slab.h>
 #include <soc/bcm2835/raspberrypi-firmware.h>
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:31 @
 	struct mbox_chan *chan; /* The property channel. */
 	struct completion c;
 	u32 enabled;
+	u32 get_throttled;
 };
 
+static struct platform_device *g_pdev;
+
 static DEFINE_MUTEX(transaction_lock);
 
 static void response_callback(struct mbox_client *cl, void *msg)
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:48 @
  * Sends a request to the firmware through the BCM2835 mailbox driver,
  * and synchronously waits for the reply.
  */
-static int
+int
 rpi_firmware_transaction(struct rpi_firmware *fw, u32 chan, u32 data)
 {
 	u32 message = MBOX_MSG(chan, data);
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:73 @
 
 	return ret;
 }
+EXPORT_SYMBOL_GPL(rpi_firmware_transaction);
 
 /**
  * rpi_firmware_property_list - Submit firmware property list
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:178 @
 
 	kfree(data);
 
+	if ((tag == RPI_FIRMWARE_GET_THROTTLED) &&
+	     memcmp(&fw->get_throttled, tag_data, sizeof(fw->get_throttled))) {
+		memcpy(&fw->get_throttled, tag_data, sizeof(fw->get_throttled));
+		sysfs_notify(&fw->cl.dev->kobj, NULL, "get_throttled");
+	}
+
 	return ret;
 }
 EXPORT_SYMBOL_GPL(rpi_firmware_property);
 
+static int rpi_firmware_notify_reboot(struct notifier_block *nb,
+				      unsigned long action,
+				      void *data)
+{
+	struct rpi_firmware *fw;
+	struct platform_device *pdev = g_pdev;
+	u32 reboot_flags = 0;
+
+	if (!pdev)
+		return 0;
+
+	fw = platform_get_drvdata(pdev);
+	if (!fw)
+		return 0;
+
+	// The partition id is the first parameter followed by zero or
+	// more flags separated by spaces indicating the reason for the reboot.
+	//
+	// 'tryboot': Sets a one-shot flag which is cleared upon reboot and
+	//            causes the tryboot.txt to be loaded instead of config.txt
+	//            by the bootloader and the start.elf firmware.
+	//
+	//            This is intended to allow automatic fallback to a known
+	//            good image if an OS/FW upgrade fails.
+	//
+	// N.B. The firmware mechanism for storing reboot flags may vary
+	// on different Raspberry Pi models.
+	if (data && strstr(data, " tryboot"))
+		reboot_flags |= 0x1;
+
+	// The mailbox might have been called earlier, directly via vcmailbox
+	// so only overwrite if reboot flags are passed to the reboot command.
+	if (reboot_flags)
+		(void)rpi_firmware_property(fw, RPI_FIRMWARE_SET_REBOOT_FLAGS,
+				&reboot_flags, sizeof(reboot_flags));
+
+	(void)rpi_firmware_property(fw, RPI_FIRMWARE_NOTIFY_REBOOT, NULL, 0);
+
+	return 0;
+}
+
+static ssize_t get_throttled_show(struct device *dev,
+				  struct device_attribute *attr, char *buf)
+{
+	struct rpi_firmware *fw = dev_get_drvdata(dev);
+
+	WARN_ONCE(1, "deprecated, use hwmon sysfs instead\n");
+
+	return sprintf(buf, "%x\n", fw->get_throttled);
+}
+
+static DEVICE_ATTR_RO(get_throttled);
+
+static struct attribute *rpi_firmware_dev_attrs[] = {
+	&dev_attr_get_throttled.attr,
+	NULL,
+};
+
+static const struct attribute_group rpi_firmware_dev_group = {
+	.attrs = rpi_firmware_dev_attrs,
+};
+
 static void
 rpi_firmware_print_firmware_revision(struct rpi_firmware *fw)
 {
 	time64_t date_and_time;
 	u32 packet;
+	static const char * const variant_strs[] = {
+		"unknown",
+		"start",
+		"start_x",
+		"start_db",
+		"start_cd",
+	};
+	const char *variant_str = "cmd unsupported";
+	u32 variant;
 	int ret = rpi_firmware_property(fw,
 					RPI_FIRMWARE_GET_FIRMWARE_REVISION,
 					&packet, sizeof(packet));
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:273 @
 
 	/* This is not compatible with y2038 */
 	date_and_time = packet;
-	dev_info(fw->cl.dev, "Attached to firmware from %ptT\n", &date_and_time);
+
+	ret = rpi_firmware_property(fw, RPI_FIRMWARE_GET_FIRMWARE_VARIANT,
+				    &variant, sizeof(variant));
+
+	if (!ret) {
+		if (variant >= ARRAY_SIZE(variant_strs))
+			variant = 0;
+		variant_str = variant_strs[variant];
+	}
+
+	dev_info(fw->cl.dev,
+		 "Attached to firmware from %ptT, variant %s\n",
+		 &date_and_time, variant_str);
+}
+
+static void
+rpi_firmware_print_firmware_hash(struct rpi_firmware *fw)
+{
+	u32 hash[5];
+	int ret = rpi_firmware_property(fw,
+					RPI_FIRMWARE_GET_FIRMWARE_HASH,
+					hash, sizeof(hash));
+
+	if (ret)
+		return;
+
+	dev_info(fw->cl.dev,
+		 "Firmware hash is %08x%08x%08x%08x%08x\n",
+		 hash[0], hash[1], hash[2], hash[3], hash[4]);
 }
 
 static void
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:316 @
 
 	rpi_hwmon = platform_device_register_data(dev, "raspberrypi-hwmon",
 						  -1, NULL, 0);
+
+	if (!IS_ERR_OR_NULL(rpi_hwmon)) {
+		if (devm_device_add_group(dev, &rpi_firmware_dev_group))
+			dev_err(dev, "Failed to create get_trottled attr\n");
+	}
 }
 
 static void rpi_register_clk_driver(struct device *dev)
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:367 @
 	init_completion(&fw->c);
 
 	platform_set_drvdata(pdev, fw);
+	g_pdev = pdev;
 
 	rpi_firmware_print_firmware_revision(fw);
+	rpi_firmware_print_firmware_hash(fw);
 	rpi_register_hwmon_driver(dev, fw);
 	rpi_register_clk_driver(dev);
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:396 @
 	platform_device_unregister(rpi_clk);
 	rpi_clk = NULL;
 	mbox_free_channel(fw->chan);
+	g_pdev = NULL;
 
 	return 0;
 }
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:409 @
  */
 struct rpi_firmware *rpi_firmware_get(struct device_node *firmware_node)
 {
-	struct platform_device *pdev = of_find_device_by_node(firmware_node);
+	struct platform_device *pdev = g_pdev;
 
 	if (!pdev)
 		return NULL;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:433 @
 	.shutdown	= rpi_firmware_shutdown,
 	.remove		= rpi_firmware_remove,
 };
-module_platform_driver(rpi_firmware_driver);
+
+static struct notifier_block rpi_firmware_reboot_notifier = {
+	.notifier_call = rpi_firmware_notify_reboot,
+};
+
+static int __init rpi_firmware_init(void)
+{
+	int ret = register_reboot_notifier(&rpi_firmware_reboot_notifier);
+	if (ret)
+		goto out1;
+	ret = platform_driver_register(&rpi_firmware_driver);
+	if (ret)
+		goto out2;
+
+	return 0;
+
+out2:
+	unregister_reboot_notifier(&rpi_firmware_reboot_notifier);
+out1:
+	return ret;
+}
+core_initcall(rpi_firmware_init);
+
+static void __init rpi_firmware_exit(void)
+{
+	platform_driver_unregister(&rpi_firmware_driver);
+	unregister_reboot_notifier(&rpi_firmware_reboot_notifier);
+}
+module_exit(rpi_firmware_exit);
 
 MODULE_AUTHOR("Eric Anholt <eric@anholt.net>");
 MODULE_DESCRIPTION("Raspberry Pi firmware driver");
diff -Nur --no-dereference linux-5.10.52-orig/drivers/gpio/gpio-bcm-virt.c linux-5.10.52-v7l+/drivers/gpio/gpio-bcm-virt.c
--- linux-5.10.52-orig/drivers/gpio/gpio-bcm-virt.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/gpio/gpio-bcm-virt.c	2021-07-25 16:45:47.068607712 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/*
+ *  brcmvirt GPIO driver
+ *
+ *  Copyright (C) 2012,2013 Dom Cobley <popcornmix@gmail.com>
+ *  Based on gpio-clps711x.c by Alexander Shiyan <shc_work@mail.ru>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/err.h>
+#include <linux/gpio.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <soc/bcm2835/raspberrypi-firmware.h>
+
+#define MODULE_NAME "brcmvirt-gpio"
+#define NUM_GPIO 2
+
+struct brcmvirt_gpio {
+	struct gpio_chip	gc;
+	u32 __iomem		*ts_base;
+	/* two packed 16-bit counts of enabled and disables
+           Allows host to detect a brief enable that was missed */
+	u32			enables_disables[NUM_GPIO];
+	dma_addr_t		bus_addr;
+};
+
+static int brcmvirt_gpio_dir_in(struct gpio_chip *gc, unsigned off)
+{
+	struct brcmvirt_gpio *gpio;
+	gpio = container_of(gc, struct brcmvirt_gpio, gc);
+	return -EINVAL;
+}
+
+static int brcmvirt_gpio_dir_out(struct gpio_chip *gc, unsigned off, int val)
+{
+	struct brcmvirt_gpio *gpio;
+	gpio = container_of(gc, struct brcmvirt_gpio, gc);
+	return 0;
+}
+
+static int brcmvirt_gpio_get(struct gpio_chip *gc, unsigned off)
+{
+	struct brcmvirt_gpio *gpio;
+	unsigned v;
+	gpio = container_of(gc, struct brcmvirt_gpio, gc);
+	v = readl(gpio->ts_base + off);
+	return (v >> off) & 1;
+}
+
+static void brcmvirt_gpio_set(struct gpio_chip *gc, unsigned off, int val)
+{
+	struct brcmvirt_gpio *gpio;
+	u16 enables, disables;
+	s16 diff;
+	bool lit;
+	gpio = container_of(gc, struct brcmvirt_gpio, gc);
+	enables  = gpio->enables_disables[off] >> 16;
+	disables = gpio->enables_disables[off] >>  0;
+	diff = (s16)(enables - disables);
+	lit = diff > 0;
+	if ((val && lit) || (!val && !lit))
+		return;
+	if (val)
+		enables++;
+	else
+		disables++;
+	diff = (s16)(enables - disables);
+	BUG_ON(diff != 0 && diff != 1);
+	gpio->enables_disables[off] = (enables << 16) | (disables << 0);
+	writel(gpio->enables_disables[off], gpio->ts_base + off);
+}
+
+static int brcmvirt_gpio_probe(struct platform_device *pdev)
+{
+	int err = 0;
+	struct device *dev = &pdev->dev;
+	struct device_node *np = dev->of_node;
+	struct device_node *fw_node;
+	struct rpi_firmware *fw;
+	struct brcmvirt_gpio *ucb;
+	u32 gpiovirtbuf;
+
+	fw_node = of_parse_phandle(np, "firmware", 0);
+	if (!fw_node) {
+		dev_err(dev, "Missing firmware node\n");
+		return -ENOENT;
+	}
+
+	fw = rpi_firmware_get(fw_node);
+	if (!fw)
+		return -EPROBE_DEFER;
+
+	ucb = devm_kzalloc(dev, sizeof *ucb, GFP_KERNEL);
+	if (!ucb) {
+		err = -EINVAL;
+		goto out;
+	}
+
+	ucb->ts_base = dma_alloc_coherent(dev, PAGE_SIZE, &ucb->bus_addr, GFP_KERNEL);
+	if (!ucb->ts_base) {
+		pr_err("[%s]: failed to dma_alloc_coherent(%ld)\n",
+				__func__, PAGE_SIZE);
+		err = -ENOMEM;
+		goto out;
+	}
+
+	gpiovirtbuf = (u32)ucb->bus_addr;
+	err = rpi_firmware_property(fw, RPI_FIRMWARE_FRAMEBUFFER_SET_GPIOVIRTBUF,
+				    &gpiovirtbuf, sizeof(gpiovirtbuf));
+
+	if (err || gpiovirtbuf != 0) {
+		dev_warn(dev, "Failed to set gpiovirtbuf, trying to get err:%x\n", err);
+		dma_free_coherent(dev, PAGE_SIZE, ucb->ts_base, ucb->bus_addr);
+		ucb->ts_base = 0;
+		ucb->bus_addr = 0;
+	}
+
+	if (!ucb->ts_base) {
+		err = rpi_firmware_property(fw, RPI_FIRMWARE_FRAMEBUFFER_GET_GPIOVIRTBUF,
+					    &gpiovirtbuf, sizeof(gpiovirtbuf));
+
+		if (err) {
+			dev_err(dev, "Failed to get gpiovirtbuf\n");
+			goto out;
+		}
+
+		if (!gpiovirtbuf) {
+			dev_err(dev, "No virtgpio buffer\n");
+			err = -ENOENT;
+			goto out;
+		}
+
+		// mmap the physical memory
+		gpiovirtbuf &= ~0xc0000000;
+		ucb->ts_base = ioremap(gpiovirtbuf, 4096);
+		if (ucb->ts_base == NULL) {
+			dev_err(dev, "Failed to map physical address\n");
+			err = -ENOENT;
+			goto out;
+		}
+		ucb->bus_addr = 0;
+	}
+	ucb->gc.label = MODULE_NAME;
+	ucb->gc.owner = THIS_MODULE;
+	//ucb->gc.dev = dev;
+	ucb->gc.of_node = np;
+	ucb->gc.base = 100;
+	ucb->gc.ngpio = NUM_GPIO;
+
+	ucb->gc.direction_input = brcmvirt_gpio_dir_in;
+	ucb->gc.direction_output = brcmvirt_gpio_dir_out;
+	ucb->gc.get = brcmvirt_gpio_get;
+	ucb->gc.set = brcmvirt_gpio_set;
+	ucb->gc.can_sleep = true;
+
+	err = gpiochip_add(&ucb->gc);
+	if (err)
+		goto out;
+
+	platform_set_drvdata(pdev, ucb);
+
+	return 0;
+out:
+	if (ucb->bus_addr) {
+		dma_free_coherent(dev, PAGE_SIZE, ucb->ts_base, ucb->bus_addr);
+		ucb->bus_addr = 0;
+		ucb->ts_base = NULL;
+	} else if (ucb->ts_base) {
+		iounmap(ucb->ts_base);
+		ucb->ts_base = NULL;
+	}
+	return err;
+}
+
+static int brcmvirt_gpio_remove(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	int err = 0;
+	struct brcmvirt_gpio *ucb = platform_get_drvdata(pdev);
+
+	gpiochip_remove(&ucb->gc);
+	if (ucb->bus_addr)
+		dma_free_coherent(dev, PAGE_SIZE, ucb->ts_base, ucb->bus_addr);
+	else if (ucb->ts_base)
+		iounmap(ucb->ts_base);
+	return err;
+}
+
+static const struct of_device_id __maybe_unused brcmvirt_gpio_ids[] = {
+	{ .compatible = "brcm,bcm2835-virtgpio" },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, brcmvirt_gpio_ids);
+
+static struct platform_driver brcmvirt_gpio_driver = {
+	.driver	= {
+		.name		= MODULE_NAME,
+		.owner		= THIS_MODULE,
+		.of_match_table	= of_match_ptr(brcmvirt_gpio_ids),
+	},
+	.probe	= brcmvirt_gpio_probe,
+	.remove	= brcmvirt_gpio_remove,
+};
+module_platform_driver(brcmvirt_gpio_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Dom Cobley <popcornmix@gmail.com>");
+MODULE_DESCRIPTION("brcmvirt GPIO driver");
+MODULE_ALIAS("platform:brcmvirt-gpio");
diff -Nur --no-dereference linux-5.10.52-orig/drivers/gpio/gpio-fsm.c linux-5.10.52-v7l+/drivers/gpio/gpio-fsm.c
--- linux-5.10.52-orig/drivers/gpio/gpio-fsm.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/gpio/gpio-fsm.c	2021-07-25 16:45:47.108607042 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ *  GPIO FSM driver
+ *
+ *  This driver implements simple state machines that allow real GPIOs to be
+ *  controlled in response to inputs from other GPIOs - real and soft/virtual -
+ *  and time delays. It can:
+ *  + create dummy GPIOs for drivers that demand them
+ *  + drive multiple GPIOs from a single input,  with optional delays
+ *  + add a debounce circuit to an input
+ *  + drive pattern sequences onto LEDs
+ *  etc.
+ *
+ *  Copyright (C) 2020 Raspberry Pi (Trading) Ltd.
+ */
+
+#include <linux/err.h>
+#include <linux/gpio.h>
+#include <linux/gpio/driver.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/sysfs.h>
+
+#include <dt-bindings/gpio/gpio-fsm.h>
+
+#define MODULE_NAME "gpio-fsm"
+
+#define GF_IO_TYPE(x) ((u32)(x) & 0xffff)
+#define GF_IO_INDEX(x) ((u32)(x) >> 16)
+
+enum {
+	SIGNAL_GPIO,
+	SIGNAL_SOFT
+};
+
+enum {
+	INPUT_GPIO,
+	INPUT_SOFT
+};
+
+enum {
+	SYM_UNDEFINED,
+	SYM_NAME,
+	SYM_SET,
+	SYM_START,
+	SYM_SHUTDOWN,
+
+	SYM_MAX
+};
+
+struct soft_gpio {
+	int dir;
+	int value;
+};
+
+struct input_gpio_state {
+	struct gpio_fsm *gf;
+	struct gpio_desc  *desc;
+	struct fsm_state *target;
+	int index;
+	int value;
+	int irq;
+	bool enabled;
+	bool active_low;
+};
+
+struct gpio_event {
+	int index;
+	int value;
+	struct fsm_state *target;
+};
+
+struct symtab_entry {
+	const char *name;
+	void *value;
+	struct symtab_entry *next;
+};
+
+struct output_signal {
+	u8 type;
+	u8 value;
+	u16 index;
+};
+
+struct fsm_state {
+	const char *name;
+	struct output_signal *signals;
+	struct gpio_event *gpio_events;
+	struct gpio_event *soft_events;
+	struct fsm_state *delay_target;
+	struct fsm_state *shutdown_target;
+	unsigned int num_signals;
+	unsigned int num_gpio_events;
+	unsigned int num_soft_events;
+	unsigned int delay_ms;
+	unsigned int shutdown_ms;
+};
+
+struct gpio_fsm {
+	struct gpio_chip gc;
+	struct device *dev;
+	spinlock_t spinlock;
+	struct work_struct work;
+	struct timer_list timer;
+	wait_queue_head_t shutdown_event;
+	struct fsm_state *states;
+	struct input_gpio_state *input_gpio_states;
+	struct gpio_descs *input_gpios;
+	struct gpio_descs *output_gpios;
+	struct soft_gpio *soft_gpios;
+	struct fsm_state *start_state;
+	struct fsm_state *shutdown_state;
+	unsigned int num_states;
+	unsigned int num_output_gpios;
+	unsigned int num_input_gpios;
+	unsigned int num_soft_gpios;
+	unsigned int shutdown_timeout_ms;
+	unsigned int shutdown_jiffies;
+
+	struct fsm_state *current_state;
+	struct fsm_state *next_state;
+	struct fsm_state *delay_target_state;
+	unsigned int delay_jiffies;
+	int delay_ms;
+	unsigned int debug;
+	bool shutting_down;
+	struct symtab_entry *symtab;
+};
+
+static struct symtab_entry *do_add_symbol(struct symtab_entry **symtab,
+					  const char *name, void *value)
+{
+	struct symtab_entry **p = symtab;
+
+	while (*p && strcmp((*p)->name, name))
+		p = &(*p)->next;
+
+	if (*p) {
+		/* This is an existing symbol */
+		if ((*p)->value) {
+			/* Already defined */
+			if (value) {
+				if ((uintptr_t)value < SYM_MAX)
+					return ERR_PTR(-EINVAL);
+				else
+					return ERR_PTR(-EEXIST);
+			}
+		} else {
+			/* Undefined */
+			(*p)->value = value;
+		}
+	} else {
+		/* This is a new symbol */
+		*p = kmalloc(sizeof(struct symtab_entry), GFP_KERNEL);
+		if (*p) {
+			(*p)->name = name;
+			(*p)->value = value;
+			(*p)->next = NULL;
+		}
+	}
+	return *p;
+}
+
+static int add_symbol(struct symtab_entry **symtab,
+		      const char *name, void *value)
+{
+	struct symtab_entry *sym = do_add_symbol(symtab, name, value);
+
+	return PTR_ERR_OR_ZERO(sym);
+}
+
+static struct symtab_entry *get_symbol(struct symtab_entry **symtab,
+				       const char *name)
+{
+	struct symtab_entry *sym = do_add_symbol(symtab, name, NULL);
+
+	if (IS_ERR(sym))
+		return NULL;
+	return sym;
+}
+
+static void free_symbols(struct symtab_entry **symtab)
+{
+	struct symtab_entry *sym = *symtab;
+	void *p;
+
+	*symtab = NULL;
+	while (sym) {
+		p = sym;
+		sym = sym->next;
+		kfree(p);
+	}
+}
+
+static int gpio_fsm_get_direction(struct gpio_chip *gc, unsigned int off)
+{
+	struct gpio_fsm *gf = gpiochip_get_data(gc);
+	struct soft_gpio *sg;
+
+	if (off >= gf->num_soft_gpios)
+		return -EINVAL;
+	sg = &gf->soft_gpios[off];
+
+	return sg->dir;
+}
+
+static int gpio_fsm_get(struct gpio_chip *gc, unsigned int off)
+{
+	struct gpio_fsm *gf = gpiochip_get_data(gc);
+	struct soft_gpio *sg;
+
+	if (off >= gf->num_soft_gpios)
+		return -EINVAL;
+	sg = &gf->soft_gpios[off];
+
+	return sg->value;
+}
+
+static void gpio_fsm_go_to_state(struct gpio_fsm *gf,
+				   struct fsm_state *new_state)
+{
+	struct input_gpio_state *inp_state;
+	struct gpio_event *gp_ev;
+	struct fsm_state *state;
+	int i;
+
+	dev_dbg(gf->dev, "go_to_state(%s)\n",
+		  new_state ? new_state->name : "<unset>");
+
+	spin_lock(&gf->spinlock);
+
+	if (gf->next_state) {
+		/* Something else has already requested a transition */
+		spin_unlock(&gf->spinlock);
+		return;
+	}
+
+	gf->next_state = new_state;
+	state = gf->current_state;
+	gf->delay_target_state = NULL;
+
+	if (state) {
+		/* Disarm any GPIO IRQs */
+		for (i = 0; i < state->num_gpio_events; i++) {
+			gp_ev = &state->gpio_events[i];
+			inp_state = &gf->input_gpio_states[gp_ev->index];
+			inp_state->target = NULL;
+		}
+	}
+
+	spin_unlock(&gf->spinlock);
+
+	if (new_state)
+		schedule_work(&gf->work);
+}
+
+static void gpio_fsm_set_soft(struct gpio_fsm *gf,
+				unsigned int off, int val)
+{
+	struct soft_gpio *sg = &gf->soft_gpios[off];
+	struct gpio_event *gp_ev;
+	struct fsm_state *state;
+	int i;
+
+	dev_dbg(gf->dev, "set(%d,%d)\n", off, val);
+	state = gf->current_state;
+	sg->value = val;
+	for (i = 0; i < state->num_soft_events; i++) {
+		gp_ev = &state->soft_events[i];
+		if (gp_ev->index == off && gp_ev->value == val) {
+			if (gf->debug)
+				dev_info(gf->dev,
+					 "GF_SOFT %d->%d -> %s\n", gp_ev->index,
+					 gp_ev->value, gp_ev->target->name);
+			gpio_fsm_go_to_state(gf, gp_ev->target);
+			break;
+		}
+	}
+}
+
+static int gpio_fsm_direction_input(struct gpio_chip *gc, unsigned int off)
+{
+	struct gpio_fsm *gf = gpiochip_get_data(gc);
+	struct soft_gpio *sg;
+
+	if (off >= gf->num_soft_gpios)
+		return -EINVAL;
+	sg = &gf->soft_gpios[off];
+	sg->dir = GPIOF_DIR_IN;
+
+	return 0;
+}
+
+static int gpio_fsm_direction_output(struct gpio_chip *gc, unsigned int off,
+				       int value)
+{
+	struct gpio_fsm *gf = gpiochip_get_data(gc);
+	struct soft_gpio *sg;
+
+	if (off >= gf->num_soft_gpios)
+		return -EINVAL;
+	sg = &gf->soft_gpios[off];
+	sg->dir = GPIOF_DIR_OUT;
+	gpio_fsm_set_soft(gf, off, value);
+
+	return 0;
+}
+
+static void gpio_fsm_set(struct gpio_chip *gc, unsigned int off, int val)
+{
+	struct gpio_fsm *gf;
+
+	gf = gpiochip_get_data(gc);
+	if (off < gf->num_soft_gpios)
+		gpio_fsm_set_soft(gf, off, val);
+}
+
+static void gpio_fsm_enter_state(struct gpio_fsm *gf,
+				   struct fsm_state *state)
+{
+	struct input_gpio_state *inp_state;
+	struct output_signal *signal;
+	struct gpio_event *event;
+	struct gpio_desc *gpiod;
+	struct soft_gpio *soft;
+	int value;
+	int i;
+
+	dev_dbg(gf->dev, "enter_state(%s)\n", state->name);
+
+	gf->current_state = state;
+
+	// 1. Apply any listed signals
+	for (i = 0; i < state->num_signals; i++) {
+		signal = &state->signals[i];
+
+		if (gf->debug)
+			dev_info(gf->dev, "  set %s %d->%d\n",
+				 (signal->type == SIGNAL_GPIO) ? "GF_OUT" :
+				 "GF_SOFT",
+				 signal->index, signal->value);
+		switch (signal->type) {
+		case SIGNAL_GPIO:
+			gpiod = gf->output_gpios->desc[signal->index];
+			gpiod_set_value_cansleep(gpiod, signal->value);
+			break;
+		case SIGNAL_SOFT:
+			soft = &gf->soft_gpios[signal->index];
+			gpio_fsm_set_soft(gf, signal->index, signal->value);
+			break;
+		}
+	}
+
+	// 2. Exit if successfully reached shutdown state
+	if (gf->shutting_down && state == state->shutdown_target) {
+		wake_up(&gf->shutdown_event);
+		return;
+	}
+
+	// 3. Schedule a timer callback if shutting down
+	if (state->shutdown_target) {
+		// Remember the absolute shutdown time in case remove is called
+		// at a later time.
+		gf->shutdown_jiffies =
+			jiffies + msecs_to_jiffies(state->shutdown_ms);
+
+		if (gf->shutting_down) {
+			gf->delay_jiffies = gf->shutdown_jiffies;
+			gf->delay_target_state = state->shutdown_target;
+			gf->delay_ms = state->shutdown_ms;
+			mod_timer(&gf->timer, gf->delay_jiffies);
+		}
+	}
+
+	// During shutdown, skip everything else
+	if (gf->shutting_down)
+		return;
+
+	// Otherwise record what the shutdown time would be
+	gf->shutdown_jiffies = jiffies + msecs_to_jiffies(state->shutdown_ms);
+
+	// 4. Check soft inputs for transitions to take
+	for (i = 0; i < state->num_soft_events; i++) {
+		event = &state->soft_events[i];
+		if (gf->soft_gpios[event->index].value == event->value) {
+			if (gf->debug)
+				dev_info(gf->dev,
+					 "GF_SOFT %d=%d -> %s\n", event->index,
+					 event->value, event->target->name);
+			gpio_fsm_go_to_state(gf, event->target);
+			return;
+		}
+	}
+
+	// 5. Check GPIOs for transitions to take, enabling the IRQs
+	for (i = 0; i < state->num_gpio_events; i++) {
+		event = &state->gpio_events[i];
+		inp_state = &gf->input_gpio_states[event->index];
+		inp_state->target = event->target;
+		inp_state->value = event->value;
+		inp_state->enabled = true;
+
+		value = gpiod_get_value(gf->input_gpios->desc[event->index]);
+
+		// Clear stale event state
+		disable_irq(inp_state->irq);
+
+		irq_set_irq_type(inp_state->irq,
+				 (inp_state->value ^ inp_state->active_low) ?
+				 IRQF_TRIGGER_RISING : IRQF_TRIGGER_FALLING);
+		enable_irq(inp_state->irq);
+
+		if (value == event->value && inp_state->target) {
+			if (gf->debug)
+				dev_info(gf->dev,
+					 "GF_IN %d=%d -> %s\n", event->index,
+					 event->value, event->target->name);
+			gpio_fsm_go_to_state(gf, event->target);
+			return;
+		}
+	}
+
+	// 6. Schedule a timer callback if delay_target
+	if (state->delay_target) {
+		gf->delay_target_state = state->delay_target;
+		gf->delay_jiffies = jiffies +
+			msecs_to_jiffies(state->delay_ms);
+		gf->delay_ms = state->delay_ms;
+		mod_timer(&gf->timer, gf->delay_jiffies);
+	}
+}
+
+static void gpio_fsm_work(struct work_struct *work)
+{
+	struct input_gpio_state *inp_state;
+	struct fsm_state *new_state;
+	struct fsm_state *state;
+	struct gpio_event *gp_ev;
+	struct gpio_fsm *gf;
+	int i;
+
+	gf = container_of(work, struct gpio_fsm, work);
+	spin_lock(&gf->spinlock);
+	state = gf->current_state;
+	new_state = gf->next_state;
+	if (!new_state)
+		new_state = gf->delay_target_state;
+	gf->next_state = NULL;
+	gf->delay_target_state = NULL;
+	spin_unlock(&gf->spinlock);
+
+	if (state) {
+		/* Disable any enabled GPIO IRQs */
+		for (i = 0; i < state->num_gpio_events; i++) {
+			gp_ev = &state->gpio_events[i];
+			inp_state = &gf->input_gpio_states[gp_ev->index];
+			if (inp_state->enabled) {
+				inp_state->enabled = false;
+				irq_set_irq_type(inp_state->irq,
+						 IRQF_TRIGGER_NONE);
+			}
+		}
+	}
+
+	if (new_state)
+		gpio_fsm_enter_state(gf, new_state);
+}
+
+static irqreturn_t gpio_fsm_gpio_irq_handler(int irq, void *dev_id)
+{
+	struct input_gpio_state *inp_state = dev_id;
+	struct gpio_fsm *gf = inp_state->gf;
+	struct fsm_state *target;
+
+	target = inp_state->target;
+	if (!target)
+		return IRQ_NONE;
+
+	/* If the IRQ has fired then the desired state _must_ have occurred */
+	inp_state->enabled = false;
+	irq_set_irq_type(inp_state->irq, IRQF_TRIGGER_NONE);
+	if (gf->debug)
+		dev_info(gf->dev, "GF_IN %d->%d -> %s\n",
+			 inp_state->index, inp_state->value, target->name);
+	gpio_fsm_go_to_state(gf, target);
+	return IRQ_HANDLED;
+}
+
+static void gpio_fsm_timer(struct timer_list *timer)
+{
+	struct gpio_fsm *gf = container_of(timer, struct gpio_fsm, timer);
+	struct fsm_state *target;
+
+	target = gf->delay_target_state;
+	if (!target)
+		return;
+
+	if (gf->debug)
+		dev_info(gf->dev, "GF_DELAY %d -> %s\n", gf->delay_ms,
+			 target->name);
+
+	gpio_fsm_go_to_state(gf, target);
+}
+
+int gpio_fsm_parse_signals(struct gpio_fsm *gf, struct fsm_state *state,
+			     struct property *prop)
+{
+	const __be32 *cells = prop->value;
+	struct output_signal *signal;
+	u32 io;
+	u32 type;
+	u32 index;
+	u32 value;
+	int ret = 0;
+	int i;
+
+	if (prop->length % 8) {
+		dev_err(gf->dev, "malformed set in state %s\n",
+			state->name);
+		return -EINVAL;
+	}
+
+	state->num_signals = prop->length/8;
+	state->signals = devm_kcalloc(gf->dev, state->num_signals,
+				      sizeof(struct output_signal),
+				      GFP_KERNEL);
+	for (i = 0; i < state->num_signals; i++) {
+		signal = &state->signals[i];
+		io = be32_to_cpu(cells[0]);
+		type = GF_IO_TYPE(io);
+		index = GF_IO_INDEX(io);
+		value = be32_to_cpu(cells[1]);
+
+		if (type != GF_OUT && type != GF_SOFT) {
+			dev_err(gf->dev,
+				"invalid set type %d in state %s\n",
+				type, state->name);
+			ret = -EINVAL;
+			break;
+		}
+		if (type == GF_OUT && index >= gf->num_output_gpios) {
+			dev_err(gf->dev,
+				"invalid GF_OUT number %d in state %s\n",
+				index, state->name);
+			ret = -EINVAL;
+			break;
+		}
+		if (type == GF_SOFT && index >= gf->num_soft_gpios) {
+			dev_err(gf->dev,
+				"invalid GF_SOFT number %d in state %s\n",
+				index, state->name);
+			ret = -EINVAL;
+			break;
+		}
+		if (value != 0 && value != 1) {
+			dev_err(gf->dev,
+				"invalid set value %d in state %s\n",
+				value, state->name);
+			ret = -EINVAL;
+			break;
+		}
+		signal->type = (type == GF_OUT) ? SIGNAL_GPIO : SIGNAL_SOFT;
+		signal->index = index;
+		signal->value = value;
+		cells += 2;
+	}
+
+	return ret;
+}
+
+struct gpio_event *new_event(struct gpio_event **events, int *num_events)
+{
+	int num = ++(*num_events);
+	*events = krealloc(*events, num * sizeof(struct gpio_event),
+			   GFP_KERNEL);
+	return *events ? *events + (num - 1) : NULL;
+}
+
+int gpio_fsm_parse_events(struct gpio_fsm *gf, struct fsm_state *state,
+			    struct property *prop)
+{
+	const __be32 *cells = prop->value;
+	struct symtab_entry *sym;
+	int num_cells;
+	int ret = 0;
+	int i;
+
+	if (prop->length % 8) {
+		dev_err(gf->dev,
+			"malformed transitions from state %s to state %s\n",
+			state->name, prop->name);
+		return -EINVAL;
+	}
+
+	sym = get_symbol(&gf->symtab, prop->name);
+	num_cells = prop->length / 4;
+	i = 0;
+	while (i < num_cells) {
+		struct gpio_event *gp_ev;
+		u32 event, param;
+		u32 index;
+
+		event = be32_to_cpu(cells[i++]);
+		param = be32_to_cpu(cells[i++]);
+		index = GF_IO_INDEX(event);
+
+		switch (GF_IO_TYPE(event)) {
+		case GF_IN:
+			if (index >= gf->num_input_gpios) {
+				dev_err(gf->dev,
+					"invalid GF_IN %d in transitions from state %s to state %s\n",
+					index, state->name, prop->name);
+				return -EINVAL;
+			}
+			if (param > 1) {
+				dev_err(gf->dev,
+					"invalid GF_IN value %d in transitions from state %s to state %s\n",
+					param, state->name, prop->name);
+				return -EINVAL;
+			}
+			gp_ev = new_event(&state->gpio_events,
+					  &state->num_gpio_events);
+			if (!gp_ev)
+				return -ENOMEM;
+			gp_ev->index = index;
+			gp_ev->value = param;
+			gp_ev->target = (struct fsm_state *)sym;
+			break;
+
+		case GF_SOFT:
+			if (index >= gf->num_soft_gpios) {
+				dev_err(gf->dev,
+					"invalid GF_SOFT %d in transitions from state %s to state %s\n",
+					index, state->name, prop->name);
+				return -EINVAL;
+			}
+			if (param > 1) {
+				dev_err(gf->dev,
+					"invalid GF_SOFT value %d in transitions from state %s to state %s\n",
+					param, state->name, prop->name);
+				return -EINVAL;
+			}
+			gp_ev = new_event(&state->soft_events,
+					  &state->num_soft_events);
+			if (!gp_ev)
+				return -ENOMEM;
+			gp_ev->index = index;
+			gp_ev->value = param;
+			gp_ev->target = (struct fsm_state *)sym;
+			break;
+
+		case GF_DELAY:
+			if (state->delay_target) {
+				dev_err(gf->dev,
+					"state %s has multiple GF_DELAYs\n",
+					state->name);
+				return -EINVAL;
+			}
+			state->delay_target = (struct fsm_state *)sym;
+			state->delay_ms = param;
+			break;
+
+		case GF_SHUTDOWN:
+			if (state->shutdown_target == state) {
+				dev_err(gf->dev,
+					"shutdown state %s has GF_SHUTDOWN\n",
+					state->name);
+				return -EINVAL;
+			} else if (state->shutdown_target) {
+				dev_err(gf->dev,
+					"state %s has multiple GF_SHUTDOWNs\n",
+					state->name);
+				return -EINVAL;
+			}
+			state->shutdown_target =
+				(struct fsm_state *)sym;
+			state->shutdown_ms = param;
+			break;
+
+		default:
+			dev_err(gf->dev,
+				"invalid event %08x in transitions from state %s to state %s\n",
+				event, state->name, prop->name);
+			return -EINVAL;
+		}
+	}
+	if (i != num_cells) {
+		dev_err(gf->dev,
+			"malformed transitions from state %s to state %s\n",
+			state->name, prop->name);
+		return -EINVAL;
+	}
+
+	return ret;
+}
+
+int gpio_fsm_parse_state(struct gpio_fsm *gf,
+			   struct fsm_state *state,
+			   struct device_node *np)
+{
+	struct symtab_entry *sym;
+	struct property *prop;
+	int ret;
+
+	state->name = np->name;
+	ret = add_symbol(&gf->symtab, np->name, state);
+	if (ret) {
+		switch (ret) {
+		case -EINVAL:
+			dev_err(gf->dev, "'%s' is not a valid state name\n",
+				np->name);
+			break;
+		case -EEXIST:
+			dev_err(gf->dev, "state %s already defined\n",
+				np->name);
+			break;
+		default:
+			dev_err(gf->dev, "error %d adding state %s symbol\n",
+				ret, np->name);
+			break;
+		}
+		return ret;
+	}
+
+	for_each_property_of_node(np, prop) {
+		sym = get_symbol(&gf->symtab, prop->name);
+		if (!sym) {
+			ret = -ENOMEM;
+			break;
+		}
+
+		switch ((uintptr_t)sym->value) {
+		case SYM_SET:
+			ret = gpio_fsm_parse_signals(gf, state, prop);
+			break;
+		case SYM_START:
+			if (gf->start_state) {
+				dev_err(gf->dev, "multiple start states\n");
+				ret = -EINVAL;
+			} else {
+				gf->start_state = state;
+			}
+			break;
+		case SYM_SHUTDOWN:
+			state->shutdown_target = state;
+			gf->shutdown_state = state;
+			break;
+		case SYM_NAME:
+			/* Ignore */
+			break;
+		default:
+			/* A set of transition events to this state */
+			ret = gpio_fsm_parse_events(gf, state, prop);
+			break;
+		}
+	}
+
+	return ret;
+}
+
+static void dump_all(struct gpio_fsm *gf)
+{
+	int i, j;
+
+	dev_info(gf->dev, "Input GPIOs:\n");
+	for (i = 0; i < gf->num_input_gpios; i++)
+		dev_info(gf->dev, "  %d: %p\n", i,
+			 gf->input_gpios->desc[i]);
+
+	dev_info(gf->dev, "Output GPIOs:\n");
+	for (i = 0; i < gf->num_output_gpios; i++)
+		dev_info(gf->dev, "  %d: %p\n", i,
+			 gf->output_gpios->desc[i]);
+
+	dev_info(gf->dev, "Soft GPIOs:\n");
+	for (i = 0; i < gf->num_soft_gpios; i++)
+		dev_info(gf->dev, "  %d: %s %d\n", i,
+			 (gf->soft_gpios[i].dir == GPIOF_DIR_IN) ? "IN" : "OUT",
+			 gf->soft_gpios[i].value);
+
+	dev_info(gf->dev, "Start state: %s\n",
+		 gf->start_state ? gf->start_state->name : "-");
+
+	dev_info(gf->dev, "Shutdown timeout: %d ms\n",
+		 gf->shutdown_timeout_ms);
+
+	for (i = 0; i < gf->num_states; i++) {
+		struct fsm_state *state = &gf->states[i];
+
+		dev_info(gf->dev, "State %s:\n", state->name);
+
+		if (state->shutdown_target == state)
+			dev_info(gf->dev, "  Shutdown state\n");
+
+		dev_info(gf->dev, "  Signals:\n");
+		for (j = 0; j < state->num_signals; j++) {
+			struct output_signal *signal = &state->signals[j];
+
+			dev_info(gf->dev, "    %d: %s %d=%d\n", j,
+				 (signal->type == SIGNAL_GPIO) ? "GPIO" :
+								 "SOFT",
+				 signal->index, signal->value);
+		}
+
+		dev_info(gf->dev, "  GPIO events:\n");
+		for (j = 0; j < state->num_gpio_events; j++) {
+			struct gpio_event *event = &state->gpio_events[j];
+
+			dev_info(gf->dev, "    %d: %d=%d -> %s\n", j,
+				 event->index, event->value,
+				 event->target->name);
+		}
+
+		dev_info(gf->dev, "  Soft events:\n");
+		for (j = 0; j < state->num_soft_events; j++) {
+			struct gpio_event *event = &state->soft_events[j];
+
+			dev_info(gf->dev, "    %d: %d=%d -> %s\n", j,
+				 event->index, event->value,
+				 event->target->name);
+		}
+
+		if (state->delay_target)
+			dev_info(gf->dev, "  Delay: %d ms -> %s\n",
+				 state->delay_ms, state->delay_target->name);
+
+		if (state->shutdown_target && state->shutdown_target != state)
+			dev_info(gf->dev, "  Shutdown: %d ms -> %s\n",
+				 state->shutdown_ms,
+				 state->shutdown_target->name);
+	}
+	dev_info(gf->dev, "\n");
+}
+
+static int resolve_sym_to_state(struct gpio_fsm *gf, struct fsm_state **pstate)
+{
+	struct symtab_entry *sym = (struct symtab_entry *)*pstate;
+
+	if (!sym)
+		return -ENOMEM;
+
+	*pstate = sym->value;
+
+	if (!*pstate) {
+		dev_err(gf->dev, "state %s not defined\n",
+			sym->name);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+
+/*
+ * /sys/class/gpio-fsm/<fsm-name>/
+ *   /state ... the current state
+ */
+
+static ssize_t state_show(struct device *dev,
+			  struct device_attribute *attr, char *buf)
+{
+	const struct gpio_fsm *gf = dev_get_drvdata(dev);
+
+	return sprintf(buf, "%s\n", gf->current_state->name);
+}
+static DEVICE_ATTR_RO(state);
+
+static ssize_t delay_state_show(struct device *dev,
+			  struct device_attribute *attr, char *buf)
+{
+	const struct gpio_fsm *gf = dev_get_drvdata(dev);
+
+	return sprintf(buf, "%s\n",
+		       gf->delay_target_state ? gf->delay_target_state->name :
+		       "-");
+}
+
+static DEVICE_ATTR_RO(delay_state);
+
+static ssize_t delay_ms_show(struct device *dev,
+			     struct device_attribute *attr, char *buf)
+{
+	const struct gpio_fsm *gf = dev_get_drvdata(dev);
+	int jiffies_left;
+
+	jiffies_left = gf->delay_jiffies - jiffies;
+	return sprintf(buf,
+		       gf->delay_target_state ? "%u\n" : "-\n",
+		       jiffies_to_msecs(jiffies_left));
+}
+static DEVICE_ATTR_RO(delay_ms);
+
+static struct attribute *gpio_fsm_attrs[] = {
+	&dev_attr_state.attr,
+	&dev_attr_delay_state.attr,
+	&dev_attr_delay_ms.attr,
+	NULL,
+};
+
+static const struct attribute_group gpio_fsm_group = {
+	.attrs = gpio_fsm_attrs,
+	//.is_visible = gpio_is_visible,
+};
+
+static const struct attribute_group *gpio_fsm_groups[] = {
+	&gpio_fsm_group,
+	NULL
+};
+
+static struct attribute *gpio_fsm_class_attrs[] = {
+	// There are no top-level attributes
+	NULL,
+};
+ATTRIBUTE_GROUPS(gpio_fsm_class);
+
+static struct class gpio_fsm_class = {
+	.name =		MODULE_NAME,
+	.owner =	THIS_MODULE,
+
+	.class_groups = gpio_fsm_class_groups,
+};
+
+static int gpio_fsm_probe(struct platform_device *pdev)
+{
+	struct input_gpio_state *inp_state;
+	struct device *dev = &pdev->dev;
+	struct device *sysfs_dev;
+	struct device_node *np = dev->of_node;
+	struct device_node *cp;
+	struct gpio_fsm *gf;
+	u32 debug = 0;
+	int num_states;
+	u32 num_soft_gpios;
+	int ret;
+	int i;
+	static const char *const reserved_symbols[] = {
+		[SYM_NAME] = "name",
+		[SYM_SET] = "set",
+		[SYM_START] = "start_state",
+		[SYM_SHUTDOWN] = "shutdown_state",
+	};
+
+	if (of_property_read_u32(np, "num-swgpios", &num_soft_gpios) &&
+	    of_property_read_u32(np, "num-soft-gpios", &num_soft_gpios)) {
+		dev_err(dev, "missing 'num-swgpios' property\n");
+		return -EINVAL;
+	}
+
+	of_property_read_u32(np, "debug", &debug);
+
+	gf = devm_kzalloc(dev, sizeof(*gf), GFP_KERNEL);
+	if (!gf)
+		return -ENOMEM;
+
+	gf->dev = dev;
+	gf->debug = debug;
+
+	if (of_property_read_u32(np, "shutdown-timeout-ms",
+				 &gf->shutdown_timeout_ms))
+		gf->shutdown_timeout_ms = 5000;
+
+	gf->num_soft_gpios = num_soft_gpios;
+	gf->soft_gpios = devm_kcalloc(dev, num_soft_gpios,
+				      sizeof(struct soft_gpio), GFP_KERNEL);
+	if (!gf->soft_gpios)
+		return -ENOMEM;
+	for (i = 0; i < num_soft_gpios; i++) {
+		struct soft_gpio *sg = &gf->soft_gpios[i];
+
+		sg->dir = GPIOF_DIR_IN;
+		sg->value = 0;
+	}
+
+	gf->input_gpios = devm_gpiod_get_array_optional(dev, "input", GPIOD_IN);
+	if (IS_ERR(gf->input_gpios)) {
+		ret = PTR_ERR(gf->input_gpios);
+		dev_err(dev, "failed to get input gpios from DT - %d\n", ret);
+		return ret;
+	}
+	gf->num_input_gpios = (gf->input_gpios ? gf->input_gpios->ndescs : 0);
+
+	gf->input_gpio_states = devm_kcalloc(dev, gf->num_input_gpios,
+					     sizeof(struct input_gpio_state),
+					     GFP_KERNEL);
+	if (!gf->input_gpio_states)
+		return -ENOMEM;
+	for (i = 0; i < gf->num_input_gpios; i++) {
+		inp_state = &gf->input_gpio_states[i];
+		inp_state->desc = gf->input_gpios->desc[i];
+		inp_state->gf = gf;
+		inp_state->index = i;
+		inp_state->irq = gpiod_to_irq(inp_state->desc);
+		inp_state->active_low = gpiod_is_active_low(inp_state->desc);
+		if (inp_state->irq >= 0)
+			ret = devm_request_irq(gf->dev, inp_state->irq,
+					       gpio_fsm_gpio_irq_handler,
+					       IRQF_TRIGGER_NONE,
+					       dev_name(dev),
+					       inp_state);
+		else
+			ret = inp_state->irq;
+
+		if (ret) {
+			dev_err(dev,
+				"failed to get IRQ for input gpio - %d\n",
+				ret);
+			return ret;
+		}
+	}
+
+	gf->output_gpios = devm_gpiod_get_array_optional(dev, "output",
+							 GPIOD_OUT_LOW);
+	if (IS_ERR(gf->output_gpios)) {
+		ret = PTR_ERR(gf->output_gpios);
+		dev_err(dev, "failed to get output gpios from DT - %d\n", ret);
+		return ret;
+	}
+	gf->num_output_gpios = (gf->output_gpios ? gf->output_gpios->ndescs :
+				0);
+
+	num_states = of_get_child_count(np);
+	if (!num_states) {
+		dev_err(dev, "no states declared\n");
+		return -EINVAL;
+	}
+	gf->states = devm_kcalloc(dev, num_states,
+				  sizeof(struct fsm_state), GFP_KERNEL);
+	if (!gf->states)
+		return -ENOMEM;
+
+	// add reserved words to the symbol table
+	for (i = 0; i < ARRAY_SIZE(reserved_symbols); i++) {
+		if (reserved_symbols[i])
+			add_symbol(&gf->symtab, reserved_symbols[i],
+				   (void *)(uintptr_t)i);
+	}
+
+	// parse the state
+	for_each_child_of_node(np, cp) {
+		struct fsm_state *state = &gf->states[gf->num_states];
+
+		ret = gpio_fsm_parse_state(gf, state, cp);
+		if (ret)
+			return ret;
+		gf->num_states++;
+	}
+
+	if (!gf->start_state) {
+		dev_err(gf->dev, "no start state defined\n");
+		return -EINVAL;
+	}
+
+	// resolve symbol pointers into state pointers
+	for (i = 0; !ret && i < gf->num_states; i++) {
+		struct fsm_state *state = &gf->states[i];
+		int j;
+
+		for (j = 0; !ret && j < state->num_gpio_events; j++) {
+			struct gpio_event *ev = &state->gpio_events[j];
+
+			ret = resolve_sym_to_state(gf, &ev->target);
+		}
+
+		for (j = 0; !ret && j < state->num_soft_events; j++) {
+			struct gpio_event *ev = &state->soft_events[j];
+
+			ret = resolve_sym_to_state(gf, &ev->target);
+		}
+
+		if (!ret) {
+			resolve_sym_to_state(gf, &state->delay_target);
+			if (state->shutdown_target != state)
+				resolve_sym_to_state(gf,
+						     &state->shutdown_target);
+		}
+	}
+
+	if (!ret && gf->debug > 1)
+		dump_all(gf);
+
+	free_symbols(&gf->symtab);
+
+	if (ret)
+		return ret;
+
+	gf->gc.parent = dev;
+	gf->gc.label = np->name;
+	gf->gc.owner = THIS_MODULE;
+	gf->gc.of_node = np;
+	gf->gc.base = -1;
+	gf->gc.ngpio = num_soft_gpios;
+
+	gf->gc.get_direction = gpio_fsm_get_direction;
+	gf->gc.direction_input = gpio_fsm_direction_input;
+	gf->gc.direction_output = gpio_fsm_direction_output;
+	gf->gc.get = gpio_fsm_get;
+	gf->gc.set = gpio_fsm_set;
+	gf->gc.can_sleep = true;
+	spin_lock_init(&gf->spinlock);
+	INIT_WORK(&gf->work, gpio_fsm_work);
+	timer_setup(&gf->timer, gpio_fsm_timer, 0);
+	init_waitqueue_head(&gf->shutdown_event);
+
+	platform_set_drvdata(pdev, gf);
+
+	sysfs_dev = device_create_with_groups(&gpio_fsm_class, dev,
+					      MKDEV(0, 0), gf,
+					      gpio_fsm_groups,
+					      "%s", np->name);
+	if (IS_ERR(sysfs_dev))
+		dev_err(gf->dev, "Error creating sysfs entry\n");
+
+	if (gf->debug)
+		dev_info(gf->dev, "Start -> %s\n", gf->start_state->name);
+
+	gpio_fsm_go_to_state(gf, gf->start_state);
+
+	return devm_gpiochip_add_data(dev, &gf->gc, gf);
+}
+
+static int gpio_fsm_remove(struct platform_device *pdev)
+{
+	struct gpio_fsm *gf = platform_get_drvdata(pdev);
+	int i;
+
+	if (gf->shutdown_state) {
+		if (gf->debug)
+			dev_info(gf->dev, "Shutting down...\n");
+
+		spin_lock(&gf->spinlock);
+		gf->shutting_down = true;
+		if (gf->current_state->shutdown_target &&
+		    gf->current_state->shutdown_target != gf->current_state) {
+			gf->delay_target_state =
+				gf->current_state->shutdown_target;
+			mod_timer(&gf->timer, gf->shutdown_jiffies);
+		}
+		spin_unlock(&gf->spinlock);
+
+		wait_event_timeout(gf->shutdown_event,
+				   gf->current_state->shutdown_target ==
+				   gf->current_state,
+				   msecs_to_jiffies(gf->shutdown_timeout_ms));
+		/* On failure to reach a shutdown state, jump to one */
+		if (gf->current_state->shutdown_target != gf->current_state)
+			gpio_fsm_enter_state(gf, gf->shutdown_state);
+	}
+	cancel_work_sync(&gf->work);
+	del_timer_sync(&gf->timer);
+
+	/* Events aren't allocated from managed storage */
+	for (i = 0; i < gf->num_states; i++) {
+		kfree(gf->states[i].gpio_events);
+		kfree(gf->states[i].soft_events);
+	}
+	if (gf->debug)
+		dev_info(gf->dev, "Exiting\n");
+
+	return 0;
+}
+
+static void gpio_fsm_shutdown(struct platform_device *pdev)
+{
+	gpio_fsm_remove(pdev);
+}
+
+static const struct of_device_id gpio_fsm_ids[] = {
+	{ .compatible = "rpi,gpio-fsm" },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, gpio_fsm_ids);
+
+static struct platform_driver gpio_fsm_driver = {
+	.driver	= {
+		.name		= MODULE_NAME,
+		.of_match_table	= of_match_ptr(gpio_fsm_ids),
+	},
+	.probe = gpio_fsm_probe,
+	.remove = gpio_fsm_remove,
+	.shutdown = gpio_fsm_shutdown,
+};
+
+static int gpio_fsm_init(void)
+{
+	int ret;
+
+	ret = class_register(&gpio_fsm_class);
+	if (ret)
+		return ret;
+
+	ret = platform_driver_register(&gpio_fsm_driver);
+	if (ret)
+		class_unregister(&gpio_fsm_class);
+
+	return ret;
+}
+module_init(gpio_fsm_init);
+
+static void gpio_fsm_exit(void)
+{
+	platform_driver_unregister(&gpio_fsm_driver);
+	class_unregister(&gpio_fsm_class);
+}
+module_exit(gpio_fsm_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Phil Elwell <phil@raspberrypi.com>");
+MODULE_DESCRIPTION("GPIO FSM driver");
+MODULE_ALIAS("platform:gpio-fsm");
diff -Nur --no-dereference linux-5.10.52-orig/drivers/gpio/gpiolib.c linux-5.10.52-v7l+/drivers/gpio/gpiolib.c
--- linux-5.10.52-orig/drivers/gpio/gpiolib.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/gpio/gpiolib.c	2021-07-25 16:45:47.458601174 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:54 @
 #define	extra_checks	0
 #endif
 
+#define dont_test_bit(b,d) (0)
+
 /* Device and char device-related information */
 static DEFINE_IDA(gpio_ida);
 static dev_t gpio_devt;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:2449 @
 		value = !!value;
 
 	/* GPIOs used for enabled IRQs shall not be set as output */
-	if (test_bit(FLAG_USED_AS_IRQ, &desc->flags) &&
-	    test_bit(FLAG_IRQ_IS_ENABLED, &desc->flags)) {
+	if (dont_test_bit(FLAG_USED_AS_IRQ, &desc->flags) &&
+	    dont_test_bit(FLAG_IRQ_IS_ENABLED, &desc->flags)) {
 		gpiod_err(desc,
 			  "%s: tried to set a GPIO tied to an IRQ as output\n",
 			  __func__);
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:3255 @
 	}
 
 	/* To be valid for IRQ the line needs to be input or open drain */
-	if (test_bit(FLAG_IS_OUT, &desc->flags) &&
-	    !test_bit(FLAG_OPEN_DRAIN, &desc->flags)) {
+	if (dont_test_bit(FLAG_IS_OUT, &desc->flags) &&
+	    !dont_test_bit(FLAG_OPEN_DRAIN, &desc->flags)) {
 		chip_err(gc,
 			 "%s: tried to flag a GPIO set as output for IRQ\n",
 			 __func__);
diff -Nur --no-dereference linux-5.10.52-orig/drivers/gpio/Kconfig linux-5.10.52-v7l+/drivers/gpio/Kconfig
--- linux-5.10.52-orig/drivers/gpio/Kconfig	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/gpio/Kconfig	2021-07-25 16:45:47.058607880 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:196 @
 	help
 	  Say yes here to enable GPIO support for Broadcom XGS iProc SoCs.
 
+config GPIO_BCM_VIRT
+	bool "Broadcom Virt GPIO"
+	depends on OF_GPIO && RASPBERRYPI_FIRMWARE && (ARCH_BCM2835 || COMPILE_TEST)
+	help
+	  Turn on virtual GPIO support for Broadcom BCM283X chips.
+
 config GPIO_BRCMSTB
 	tristate "BRCMSTB GPIO support"
 	default y if (ARCH_BRCMSTB || BMIPS_GENERIC)
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1167 @
 	  several HTC phones.  It provides basic support for input
 	  pins, output pins, and irqs.
 
+config GPIO_FSM
+	tristate "GPIO FSM support"
+	help
+	  The GPIO FSM driver allows the creation of state machines for
+	  manipulating GPIOs (both real and virtual), with state transitions
+	  triggered by GPIO edges or delays.
+
+	  If unsure, say N.
+
 config GPIO_JANZ_TTL
 	tristate "Janz VMOD-TTL Digital IO Module"
 	depends on MFD_JANZ_CMODIO
diff -Nur --no-dereference linux-5.10.52-orig/drivers/gpio/Makefile linux-5.10.52-v7l+/drivers/gpio/Makefile
--- linux-5.10.52-orig/drivers/gpio/Makefile	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/gpio/Makefile	2021-07-25 16:45:47.058607880 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:41 @
 obj-$(CONFIG_GPIO_ATH79)		+= gpio-ath79.o
 obj-$(CONFIG_GPIO_BCM_KONA)		+= gpio-bcm-kona.o
 obj-$(CONFIG_GPIO_BCM_XGS_IPROC)	+= gpio-xgs-iproc.o
+obj-$(CONFIG_GPIO_BCM_VIRT)		+= gpio-bcm-virt.o
 obj-$(CONFIG_GPIO_BD70528)		+= gpio-bd70528.o
 obj-$(CONFIG_GPIO_BD71828)		+= gpio-bd71828.o
 obj-$(CONFIG_GPIO_BD9571MWV)		+= gpio-bd9571mwv.o
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:63 @
 obj-$(CONFIG_GPIO_EXAR)			+= gpio-exar.o
 obj-$(CONFIG_GPIO_F7188X)		+= gpio-f7188x.o
 obj-$(CONFIG_GPIO_FTGPIO010)		+= gpio-ftgpio010.o
+obj-$(CONFIG_GPIO_FSM)			+= gpio-fsm.o
 obj-$(CONFIG_GPIO_GE_FPGA)		+= gpio-ge.o
 obj-$(CONFIG_GPIO_GPIO_MM)		+= gpio-gpio-mm.o
 obj-$(CONFIG_GPIO_GRGPIO)		+= gpio-grgpio.o
diff -Nur --no-dereference linux-5.10.52-orig/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c linux-5.10.52-v7l+/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
--- linux-5.10.52-orig/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c	2021-07-25 16:45:48.548582900 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4991 @
 static const struct drm_crtc_funcs amdgpu_dm_crtc_funcs = {
 	.reset = dm_crtc_reset_state,
 	.destroy = amdgpu_dm_crtc_destroy,
-	.gamma_set = drm_atomic_helper_legacy_gamma_set,
 	.set_config = drm_atomic_helper_set_config,
 	.page_flip = drm_atomic_helper_page_flip,
 	.atomic_duplicate_state = dm_crtc_duplicate_state,
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:5474 @
 	return 0;
 }
 
-static bool
-is_hdr_metadata_different(const struct drm_connector_state *old_state,
-			  const struct drm_connector_state *new_state)
-{
-	struct drm_property_blob *old_blob = old_state->hdr_output_metadata;
-	struct drm_property_blob *new_blob = new_state->hdr_output_metadata;
-
-	if (old_blob != new_blob) {
-		if (old_blob && new_blob &&
-		    old_blob->length == new_blob->length)
-			return memcmp(old_blob->data, new_blob->data,
-				      old_blob->length);
-
-		return true;
-	}
-
-	return false;
-}
-
 static int
 amdgpu_dm_connector_atomic_check(struct drm_connector *conn,
 				 struct drm_atomic_state *state)
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:5489 @
 	if (!crtc)
 		return 0;
 
-	if (is_hdr_metadata_different(old_con_state, new_con_state)) {
+	if (!drm_connector_atomic_hdr_metadata_equal(old_con_state, new_con_state)) {
 		struct dc_info_packet hdr_infopacket;
 
 		ret = fill_hdr_info_packet(new_con_state, &hdr_infopacket);
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:5584 @
 }
 
 static int dm_crtc_helper_atomic_check(struct drm_crtc *crtc,
-				       struct drm_crtc_state *state)
+				       struct drm_atomic_state *state)
 {
+	struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state,
+									  crtc);
 	struct amdgpu_device *adev = drm_to_adev(crtc->dev);
 	struct dc *dc = adev->dm.dc;
-	struct dm_crtc_state *dm_crtc_state = to_dm_crtc_state(state);
+	struct dm_crtc_state *dm_crtc_state = to_dm_crtc_state(crtc_state);
 	int ret = -EINVAL;
 
-	dm_update_crtc_active_planes(crtc, state);
+	dm_update_crtc_active_planes(crtc, crtc_state);
 
 	if (unlikely(!dm_crtc_state->stream &&
-		     modeset_required(state, NULL, dm_crtc_state->stream))) {
+		     modeset_required(crtc_state, NULL, dm_crtc_state->stream))) {
 		WARN_ON(1);
 		return ret;
 	}
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:5607 @
 	 * planes are disabled, which is not supported by the hardware. And there is legacy
 	 * userspace which stops using the HW cursor altogether in response to the resulting EINVAL.
 	 */
-	if (state->enable &&
-	    !(state->plane_mask & drm_plane_mask(crtc->primary)))
+	if (crtc_state->enable &&
+	    !(crtc_state->plane_mask & drm_plane_mask(crtc->primary)))
 		return -EINVAL;
 
 	/* In some use cases, like reset, no stream is attached */
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:6507 @
 	if (connector_type == DRM_MODE_CONNECTOR_HDMIA ||
 	    connector_type == DRM_MODE_CONNECTOR_DisplayPort ||
 	    connector_type == DRM_MODE_CONNECTOR_eDP) {
-		drm_object_attach_property(
-			&aconnector->base.base,
-			dm->ddev->mode_config.hdr_output_metadata_property, 0);
+		drm_connector_attach_hdr_output_metadata_property(&aconnector->base);
 
 		if (!aconnector->mst_port)
 			drm_connector_attach_vrr_capable_property(&aconnector->base);
diff -Nur --no-dereference linux-5.10.52-orig/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c linux-5.10.52-v7l+/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c
--- linux-5.10.52-orig/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c	2021-07-25 16:45:48.548582900 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:27 @
  */
 
 #include <linux/version.h>
+#include <drm/drm_atomic.h>
 #include <drm/drm_atomic_helper.h>
 #include <drm/drm_dp_mst_helper.h>
 #include <drm/drm_dp_helper.h>
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:256 @
 
 static struct drm_encoder *
 dm_mst_atomic_best_encoder(struct drm_connector *connector,
-			   struct drm_connector_state *connector_state)
+			   struct drm_atomic_state *state)
 {
+	struct drm_connector_state *connector_state = drm_atomic_get_new_connector_state(state,
+											 connector);
 	struct drm_device *dev = connector->dev;
 	struct amdgpu_device *adev = drm_to_adev(dev);
 	struct amdgpu_crtc *acrtc = to_amdgpu_crtc(connector_state->crtc);
diff -Nur --no-dereference linux-5.10.52-orig/drivers/gpu/drm/arc/arcpgu_crtc.c linux-5.10.52-v7l+/drivers/gpu/drm/arc/arcpgu_crtc.c
--- linux-5.10.52-orig/drivers/gpu/drm/arc/arcpgu_crtc.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/gpu/drm/arc/arcpgu_crtc.c	2021-07-25 16:45:59.918392277 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:119 @
 }
 
 static void arc_pgu_crtc_atomic_enable(struct drm_crtc *crtc,
-				       struct drm_crtc_state *old_state)
+				       struct drm_atomic_state *state)
 {
 	struct arcpgu_drm_private *arcpgu = crtc_to_arcpgu_priv(crtc);
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:130 @
 }
 
 static void arc_pgu_crtc_atomic_disable(struct drm_crtc *crtc,
-					struct drm_crtc_state *old_state)
+					struct drm_atomic_state *state)
 {
 	struct arcpgu_drm_private *arcpgu = crtc_to_arcpgu_priv(crtc);
 
diff -Nur --no-dereference linux-5.10.52-orig/drivers/gpu/drm/arm/display/komeda/komeda_crtc.c linux-5.10.52-v7l+/drivers/gpu/drm/arm/display/komeda/komeda_crtc.c
--- linux-5.10.52-orig/drivers/gpu/drm/arm/display/komeda/komeda_crtc.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/gpu/drm/arm/display/komeda/komeda_crtc.c	2021-07-25 16:45:59.918392277 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:77 @
  */
 static int
 komeda_crtc_atomic_check(struct drm_crtc *crtc,
-			 struct drm_crtc_state *state)
+			 struct drm_atomic_state *state)
 {
+	struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state,
+									  crtc);
 	struct komeda_crtc *kcrtc = to_kcrtc(crtc);
-	struct komeda_crtc_state *kcrtc_st = to_kcrtc_st(state);
+	struct komeda_crtc_state *kcrtc_st = to_kcrtc_st(crtc_state);
 	int err;
 
-	if (drm_atomic_crtc_needs_modeset(state))
+	if (drm_atomic_crtc_needs_modeset(crtc_state))
 		komeda_crtc_update_clock_ratio(kcrtc_st);
 
-	if (state->active) {
+	if (crtc_state->active) {
 		err = komeda_build_display_data_flow(kcrtc, kcrtc_st);
 		if (err)
 			return err;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:278 @
 
 static void
 komeda_crtc_atomic_enable(struct drm_crtc *crtc,
-			  struct drm_crtc_state *old)
+			  struct drm_atomic_state *state)
 {
+	struct drm_crtc_state *old = drm_atomic_get_old_crtc_state(state,
+								   crtc);
 	pm_runtime_get_sync(crtc->dev->dev);
 	komeda_crtc_prepare(to_kcrtc(crtc));
 	drm_crtc_vblank_on(crtc);
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:326 @
 
 static void
 komeda_crtc_atomic_disable(struct drm_crtc *crtc,
-			   struct drm_crtc_state *old)
+			   struct drm_atomic_state *state)
 {
+	struct drm_crtc_state *old = drm_atomic_get_old_crtc_state(state,
+								   crtc);
 	struct komeda_crtc *kcrtc = to_kcrtc(crtc);
 	struct komeda_crtc_state *old_st = to_kcrtc_st(old);
 	struct komeda_pipeline *master = kcrtc->master;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:388 @
 
 static void
 komeda_crtc_atomic_flush(struct drm_crtc *crtc,
-			 struct drm_crtc_state *old)
+			 struct drm_atomic_state *state)
 {
+	struct drm_crtc_state *old = drm_atomic_get_old_crtc_state(state,
+								   crtc);
 	/* commit with modeset will be handled in enable/disable */
 	if (drm_atomic_crtc_needs_modeset(crtc->state))
 		return;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:551 @
 }
 
 static const struct drm_crtc_funcs komeda_crtc_funcs = {
-	.gamma_set		= drm_atomic_helper_legacy_gamma_set,
 	.destroy		= drm_crtc_cleanup,
 	.set_config		= drm_atomic_helper_set_config,
 	.page_flip		= drm_atomic_helper_page_flip,
diff -Nur --no-dereference linux-5.10.52-orig/drivers/gpu/drm/arm/hdlcd_crtc.c linux-5.10.52-v7l+/drivers/gpu/drm/arm/hdlcd_crtc.c
--- linux-5.10.52-orig/drivers/gpu/drm/arm/hdlcd_crtc.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/gpu/drm/arm/hdlcd_crtc.c	2021-07-25 16:45:59.928392109 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:171 @
 }
 
 static void hdlcd_crtc_atomic_enable(struct drm_crtc *crtc,
-				     struct drm_crtc_state *old_state)
+				     struct drm_atomic_state *state)
 {
 	struct hdlcd_drm_private *hdlcd = crtc_to_hdlcd_priv(crtc);
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:182 @
 }
 
 static void hdlcd_crtc_atomic_disable(struct drm_crtc *crtc,
-				      struct drm_crtc_state *old_state)
+				      struct drm_atomic_state *state)
 {
 	struct hdlcd_drm_private *hdlcd = crtc_to_hdlcd_priv(crtc);
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:208 @
 }
 
 static void hdlcd_crtc_atomic_begin(struct drm_crtc *crtc,
-				    struct drm_crtc_state *state)
+				    struct drm_atomic_state *state)
 {
 	struct drm_pending_vblank_event *event = crtc->state->event;
 
diff -Nur --no-dereference linux-5.10.52-orig/drivers/gpu/drm/arm/malidp_crtc.c linux-5.10.52-v7l+/drivers/gpu/drm/arm/malidp_crtc.c
--- linux-5.10.52-orig/drivers/gpu/drm/arm/malidp_crtc.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/gpu/drm/arm/malidp_crtc.c	2021-07-25 16:45:59.928392109 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:49 @
 }
 
 static void malidp_crtc_atomic_enable(struct drm_crtc *crtc,
-				      struct drm_crtc_state *old_state)
+				      struct drm_atomic_state *state)
 {
 	struct malidp_drm *malidp = crtc_to_malidp_device(crtc);
 	struct malidp_hw_device *hwdev = malidp->dev;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:73 @
 }
 
 static void malidp_crtc_atomic_disable(struct drm_crtc *crtc,
-				       struct drm_crtc_state *old_state)
+				       struct drm_atomic_state *state)
 {
+	struct drm_crtc_state *old_state = drm_atomic_get_old_crtc_state(state,
+									 crtc);
 	struct malidp_drm *malidp = crtc_to_malidp_device(crtc);
 	struct malidp_hw_device *hwdev = malidp->dev;
 	int err;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:340 @
 }
 
 static int malidp_crtc_atomic_check(struct drm_crtc *crtc,
-				    struct drm_crtc_state *state)
+				    struct drm_atomic_state *state)
 {
+	struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state,
+									  crtc);
 	struct malidp_drm *malidp = crtc_to_malidp_device(crtc);
 	struct malidp_hw_device *hwdev = malidp->dev;
 	struct drm_plane *plane;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:378 @
 	 */
 
 	/* first count the number of rotated planes */
-	drm_atomic_crtc_state_for_each_plane_state(plane, pstate, state) {
+	drm_atomic_crtc_state_for_each_plane_state(plane, pstate, crtc_state) {
 		struct drm_framebuffer *fb = pstate->fb;
 
 		if ((pstate->rotation & MALIDP_ROTATED_MASK) || fb->modifier)
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:394 @
 		rot_mem_free += hwdev->rotation_memory[1];
 
 	/* now validate the rotation memory requirements */
-	drm_atomic_crtc_state_for_each_plane_state(plane, pstate, state) {
+	drm_atomic_crtc_state_for_each_plane_state(plane, pstate, crtc_state) {
 		struct malidp_plane *mp = to_malidp_plane(plane);
 		struct malidp_plane_state *ms = to_malidp_plane_state(pstate);
 		struct drm_framebuffer *fb = pstate->fb;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:422 @
 	}
 
 	/* If only the writeback routing has changed, we don't need a modeset */
-	if (state->connectors_changed) {
+	if (crtc_state->connectors_changed) {
 		u32 old_mask = crtc->state->connector_mask;
-		u32 new_mask = state->connector_mask;
+		u32 new_mask = crtc_state->connector_mask;
 
 		if ((old_mask ^ new_mask) ==
 		    (1 << drm_connector_index(&malidp->mw_connector.base)))
-			state->connectors_changed = false;
+			crtc_state->connectors_changed = false;
 	}
 
-	ret = malidp_crtc_atomic_check_gamma(crtc, state);
-	ret = ret ? ret : malidp_crtc_atomic_check_ctm(crtc, state);
-	ret = ret ? ret : malidp_crtc_atomic_check_scaling(crtc, state);
+	ret = malidp_crtc_atomic_check_gamma(crtc, crtc_state);
+	ret = ret ? ret : malidp_crtc_atomic_check_ctm(crtc, crtc_state);
+	ret = ret ? ret : malidp_crtc_atomic_check_scaling(crtc, crtc_state);
 
 	return ret;
 }
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:513 @
 }
 
 static const struct drm_crtc_funcs malidp_crtc_funcs = {
-	.gamma_set = drm_atomic_helper_legacy_gamma_set,
 	.destroy = drm_crtc_cleanup,
 	.set_config = drm_atomic_helper_set_config,
 	.page_flip = drm_atomic_helper_page_flip,
diff -Nur --no-dereference linux-5.10.52-orig/drivers/gpu/drm/armada/armada_crtc.c linux-5.10.52-v7l+/drivers/gpu/drm/armada/armada_crtc.c
--- linux-5.10.52-orig/drivers/gpu/drm/armada/armada_crtc.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/gpu/drm/armada/armada_crtc.c	2021-07-25 16:45:59.928392109 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:416 @
 }
 
 static int armada_drm_crtc_atomic_check(struct drm_crtc *crtc,
-					struct drm_crtc_state *state)
+					struct drm_atomic_state *state)
 {
+	struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state,
+									  crtc);
 	DRM_DEBUG_KMS("[CRTC:%d:%s]\n", crtc->base.id, crtc->name);
 
-	if (state->gamma_lut && drm_color_lut_size(state->gamma_lut) != 256)
+	if (crtc_state->gamma_lut && drm_color_lut_size(crtc_state->gamma_lut) != 256)
 		return -EINVAL;
 
-	if (state->color_mgmt_changed)
-		state->planes_changed = true;
+	if (crtc_state->color_mgmt_changed)
+		crtc_state->planes_changed = true;
 
 	return 0;
 }
 
 static void armada_drm_crtc_atomic_begin(struct drm_crtc *crtc,
-					 struct drm_crtc_state *old_crtc_state)
+					 struct drm_atomic_state *state)
 {
 	struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:446 @
 }
 
 static void armada_drm_crtc_atomic_flush(struct drm_crtc *crtc,
-					 struct drm_crtc_state *old_crtc_state)
+					 struct drm_atomic_state *state)
 {
 	struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:472 @
 }
 
 static void armada_drm_crtc_atomic_disable(struct drm_crtc *crtc,
-					   struct drm_crtc_state *old_state)
+					   struct drm_atomic_state *state)
 {
+	struct drm_crtc_state *old_state = drm_atomic_get_old_crtc_state(state,
+									 crtc);
 	struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
 	struct drm_pending_vblank_event *event;
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:510 @
 }
 
 static void armada_drm_crtc_atomic_enable(struct drm_crtc *crtc,
-					  struct drm_crtc_state *old_state)
+					  struct drm_atomic_state *state)
 {
+	struct drm_crtc_state *old_state = drm_atomic_get_old_crtc_state(state,
+									 crtc);
 	struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
 
 	DRM_DEBUG_KMS("[CRTC:%d:%s]\n", crtc->base.id, crtc->name);
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:819 @
 	.cursor_set	= armada_drm_crtc_cursor_set,
 	.cursor_move	= armada_drm_crtc_cursor_move,
 	.destroy	= armada_drm_crtc_destroy,
-	.gamma_set	= drm_atomic_helper_legacy_gamma_set,
 	.set_config	= drm_atomic_helper_set_config,
 	.page_flip	= drm_atomic_helper_page_flip,
 	.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
diff -Nur --no-dereference linux-5.10.52-orig/drivers/gpu/drm/ast/ast_mode.c linux-5.10.52-v7l+/drivers/gpu/drm/ast/ast_mode.c
--- linux-5.10.52-orig/drivers/gpu/drm/ast/ast_mode.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/gpu/drm/ast/ast_mode.c	2021-07-25 16:45:59.938391942 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:754 @
 }
 
 static int ast_crtc_helper_atomic_check(struct drm_crtc *crtc,
-					struct drm_crtc_state *state)
+					struct drm_atomic_state *state)
 {
+	struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state,
+									  crtc);
 	struct drm_device *dev = crtc->dev;
 	struct ast_crtc_state *ast_state;
 	const struct drm_format_info *format;
 	bool succ;
 
-	if (!state->enable)
+	if (!crtc_state->enable)
 		return 0; /* no mode checks if CRTC is being disabled */
 
-	ast_state = to_ast_crtc_state(state);
+	ast_state = to_ast_crtc_state(crtc_state);
 
 	format = ast_state->format;
 	if (drm_WARN_ON_ONCE(dev, !format))
 		return -EINVAL; /* BUG: We didn't set format in primary check(). */
 
-	succ = ast_get_vbios_mode_info(format, &state->mode,
-				       &state->adjusted_mode,
+	succ = ast_get_vbios_mode_info(format, &crtc_state->mode,
+				       &crtc_state->adjusted_mode,
 				       &ast_state->vbios_mode_info);
 	if (!succ)
 		return -EINVAL;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:798 @
 
 static void
 ast_crtc_helper_atomic_enable(struct drm_crtc *crtc,
-			      struct drm_crtc_state *old_crtc_state)
+			      struct drm_atomic_state *state)
 {
 	struct drm_device *dev = crtc->dev;
 	struct ast_private *ast = to_ast_private(dev);
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:821 @
 
 static void
 ast_crtc_helper_atomic_disable(struct drm_crtc *crtc,
-			       struct drm_crtc_state *old_crtc_state)
+			       struct drm_atomic_state *state)
 {
+	struct drm_crtc_state *old_crtc_state = drm_atomic_get_old_crtc_state(state,
+									      crtc);
 	struct drm_device *dev = crtc->dev;
 	struct ast_private *ast = to_ast_private(dev);
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:901 @
 
 static const struct drm_crtc_funcs ast_crtc_funcs = {
 	.reset = ast_crtc_reset,
-	.gamma_set = drm_atomic_helper_legacy_gamma_set,
 	.destroy = drm_crtc_cleanup,
 	.set_config = drm_atomic_helper_set_config,
 	.page_flip = drm_atomic_helper_page_flip,
diff -Nur --no-dereference linux-5.10.52-orig/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c linux-5.10.52-v7l+/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c
--- linux-5.10.52-orig/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c	2021-07-25 16:45:59.938391942 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:168 @
 }
 
 static void atmel_hlcdc_crtc_atomic_disable(struct drm_crtc *c,
-					    struct drm_crtc_state *old_state)
+					    struct drm_atomic_state *state)
 {
 	struct drm_device *dev = c->dev;
 	struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c);
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:203 @
 }
 
 static void atmel_hlcdc_crtc_atomic_enable(struct drm_crtc *c,
-					   struct drm_crtc_state *old_state)
+					   struct drm_atomic_state *state)
 {
 	struct drm_device *dev = c->dev;
 	struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c);
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:328 @
 }
 
 static int atmel_hlcdc_crtc_atomic_check(struct drm_crtc *c,
-					 struct drm_crtc_state *s)
+					 struct drm_atomic_state *state)
 {
+	struct drm_crtc_state *s = drm_atomic_get_new_crtc_state(state, c);
 	int ret;
 
 	ret = atmel_hlcdc_crtc_select_output_mode(s);
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:345 @
 }
 
 static void atmel_hlcdc_crtc_atomic_begin(struct drm_crtc *c,
-					  struct drm_crtc_state *old_s)
+					  struct drm_atomic_state *state)
 {
 	struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c);
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:360 @
 }
 
 static void atmel_hlcdc_crtc_atomic_flush(struct drm_crtc *crtc,
-					  struct drm_crtc_state *old_s)
+					  struct drm_atomic_state *state)
 {
 	/* TODO: write common plane control register if available */
 }
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:476 @
 	.atomic_destroy_state = atmel_hlcdc_crtc_destroy_state,
 	.enable_vblank = atmel_hlcdc_crtc_enable_vblank,
 	.disable_vblank = atmel_hlcdc_crtc_disable_vblank,
-	.gamma_set = drm_atomic_helper_legacy_gamma_set,
 };
 
 int atmel_hlcdc_crtc_create(struct drm_device *dev)
diff -Nur --no-dereference linux-5.10.52-orig/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c linux-5.10.52-v7l+/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
--- linux-5.10.52-orig/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c	2021-07-25 16:45:59.958391607 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:2398 @
 	return ret;
 }
 
-static bool hdr_metadata_equal(const struct drm_connector_state *old_state,
-			       const struct drm_connector_state *new_state)
-{
-	struct drm_property_blob *old_blob = old_state->hdr_output_metadata;
-	struct drm_property_blob *new_blob = new_state->hdr_output_metadata;
-
-	if (!old_blob || !new_blob)
-		return old_blob == new_blob;
-
-	if (old_blob->length != new_blob->length)
-		return false;
-
-	return !memcmp(old_blob->data, new_blob->data, old_blob->length);
-}
-
 static int dw_hdmi_connector_atomic_check(struct drm_connector *connector,
 					  struct drm_atomic_state *state)
 {
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:2411 @
 	if (!crtc)
 		return 0;
 
-	if (!hdr_metadata_equal(old_state, new_state)) {
+	if (!drm_connector_atomic_hdr_metadata_equal(old_state, new_state)) {
 		crtc_state = drm_atomic_get_crtc_state(state, crtc);
 		if (IS_ERR(crtc_state))
 			return PTR_ERR(crtc_state);
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:2480 @
 	drm_connector_attach_max_bpc_property(connector, 8, 16);
 
 	if (hdmi->version >= 0x200a && hdmi->plat_data->use_drm_infoframe)
-		drm_object_attach_property(&connector->base,
-			connector->dev->mode_config.hdr_output_metadata_property, 0);
+		drm_connector_attach_hdr_output_metadata_property(connector);
 
 	drm_connector_attach_encoder(connector, hdmi->bridge.encoder);
 
diff -Nur --no-dereference linux-5.10.52-orig/drivers/gpu/drm/drm_atomic_helper.c linux-5.10.52-v7l+/drivers/gpu/drm/drm_atomic_helper.c
--- linux-5.10.52-orig/drivers/gpu/drm/drm_atomic_helper.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/gpu/drm/drm_atomic_helper.c	2021-07-25 16:45:59.968391439 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:125 @
 			continue;
 
 		if (funcs->atomic_best_encoder)
-			new_encoder = funcs->atomic_best_encoder(connector, new_conn_state);
+			new_encoder = funcs->atomic_best_encoder(connector,
+								 state);
 		else if (funcs->best_encoder)
 			new_encoder = funcs->best_encoder(connector);
 		else
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:349 @
 	funcs = connector->helper_private;
 
 	if (funcs->atomic_best_encoder)
-		new_encoder = funcs->atomic_best_encoder(connector,
-							 new_connector_state);
+		new_encoder = funcs->atomic_best_encoder(connector, state);
 	else if (funcs->best_encoder)
 		new_encoder = funcs->best_encoder(connector);
 	else
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:433 @
 		new_crtc_state =
 			drm_atomic_get_new_crtc_state(state, new_conn_state->crtc);
 
+		if (!new_crtc_state->mode_changed &&
+		    !new_crtc_state->connectors_changed) {
+			continue;
+		}
+
 		/*
 		 * Each encoder has at most one connector (since we always steal
 		 * it away), so we won't call ->mode_fixup twice.
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:926 @
 		if (!funcs || !funcs->atomic_check)
 			continue;
 
-		ret = funcs->atomic_check(crtc, new_crtc_state);
+		ret = funcs->atomic_check(crtc, state);
 		if (ret) {
 			DRM_DEBUG_ATOMIC("[CRTC:%d:%s] atomic driver check failed\n",
 					 crtc->base.id, crtc->name);
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1101 @
 		if (new_crtc_state->enable && funcs->prepare)
 			funcs->prepare(crtc);
 		else if (funcs->atomic_disable)
-			funcs->atomic_disable(crtc, old_crtc_state);
+			funcs->atomic_disable(crtc, old_state);
 		else if (funcs->disable)
 			funcs->disable(crtc);
 		else if (funcs->dpms)
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1321 @
 
 		if (new_conn_state->writeback_job && new_conn_state->writeback_job->fb) {
 			WARN_ON(connector->connector_type != DRM_MODE_CONNECTOR_WRITEBACK);
-			funcs->atomic_commit(connector, new_conn_state);
+			funcs->atomic_commit(connector, old_state);
 		}
 	}
 }
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1366 @
 			DRM_DEBUG_ATOMIC("enabling [CRTC:%d:%s]\n",
 					 crtc->base.id, crtc->name);
 			if (funcs->atomic_enable)
-				funcs->atomic_enable(crtc, old_crtc_state);
+				funcs->atomic_enable(crtc, old_state);
 			else if (funcs->commit)
 				funcs->commit(crtc);
 		}
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:2042 @
  * should always call this function from their
  * &drm_mode_config_funcs.atomic_commit hook.
  *
+ * Drivers that need to extend the commit setup to private objects can use the
+ * &drm_mode_config_helper_funcs.atomic_commit_setup hook.
+ *
  * To be able to use this support drivers need to use a few more helper
  * functions. drm_atomic_helper_wait_for_dependencies() must be called before
  * actually committing the hardware state, and for nonblocking commits this call
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:2088 @
 	struct drm_plane *plane;
 	struct drm_plane_state *old_plane_state, *new_plane_state;
 	struct drm_crtc_commit *commit;
+	const struct drm_mode_config_helper_funcs *funcs;
 	int i, ret;
 
+	funcs = state->dev->mode_config.helper_private;
+
 	for_each_oldnew_crtc_in_state(state, crtc, old_crtc_state, new_crtc_state, i) {
 		commit = kzalloc(sizeof(*commit), GFP_KERNEL);
 		if (!commit)
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:2169 @
 		new_plane_state->commit = drm_crtc_commit_get(commit);
 	}
 
+	if (funcs && funcs->atomic_commit_setup)
+		return funcs->atomic_commit_setup(state);
+
 	return 0;
 }
 EXPORT_SYMBOL(drm_atomic_helper_setup_commit);
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:2524 @
 		if (active_only && !new_crtc_state->active)
 			continue;
 
-		funcs->atomic_begin(crtc, old_crtc_state);
+		funcs->atomic_begin(crtc, old_state);
 	}
 
 	for_each_oldnew_plane_in_state(old_state, plane, old_plane_state, new_plane_state, i) {
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:2582 @
 		if (active_only && !new_crtc_state->active)
 			continue;
 
-		funcs->atomic_flush(crtc, old_crtc_state);
+		funcs->atomic_flush(crtc, old_state);
 	}
 }
 EXPORT_SYMBOL(drm_atomic_helper_commit_planes);
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:2620 @
 
 	crtc_funcs = crtc->helper_private;
 	if (crtc_funcs && crtc_funcs->atomic_begin)
-		crtc_funcs->atomic_begin(crtc, old_crtc_state);
+		crtc_funcs->atomic_begin(crtc, old_state);
 
 	drm_for_each_plane_mask(plane, crtc->dev, plane_mask) {
 		struct drm_plane_state *old_plane_state =
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:2646 @
 	}
 
 	if (crtc_funcs && crtc_funcs->atomic_flush)
-		crtc_funcs->atomic_flush(crtc, old_crtc_state);
+		crtc_funcs->atomic_flush(crtc, old_state);
 }
 EXPORT_SYMBOL(drm_atomic_helper_commit_planes_on_crtc);
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:3503 @
 EXPORT_SYMBOL(drm_atomic_helper_page_flip_target);
 
 /**
- * drm_atomic_helper_legacy_gamma_set - set the legacy gamma correction table
- * @crtc: CRTC object
- * @red: red correction table
- * @green: green correction table
- * @blue: green correction table
- * @size: size of the tables
- * @ctx: lock acquire context
- *
- * Implements support for legacy gamma correction table for drivers
- * that support color management through the DEGAMMA_LUT/GAMMA_LUT
- * properties. See drm_crtc_enable_color_mgmt() and the containing chapter for
- * how the atomic color management and gamma tables work.
- */
-int drm_atomic_helper_legacy_gamma_set(struct drm_crtc *crtc,
-				       u16 *red, u16 *green, u16 *blue,
-				       uint32_t size,
-				       struct drm_modeset_acquire_ctx *ctx)
-{
-	struct drm_device *dev = crtc->dev;
-	struct drm_atomic_state *state;
-	struct drm_crtc_state *crtc_state;
-	struct drm_property_blob *blob = NULL;
-	struct drm_color_lut *blob_data;
-	int i, ret = 0;
-	bool replaced;
-
-	state = drm_atomic_state_alloc(crtc->dev);
-	if (!state)
-		return -ENOMEM;
-
-	blob = drm_property_create_blob(dev,
-					sizeof(struct drm_color_lut) * size,
-					NULL);
-	if (IS_ERR(blob)) {
-		ret = PTR_ERR(blob);
-		blob = NULL;
-		goto fail;
-	}
-
-	/* Prepare GAMMA_LUT with the legacy values. */
-	blob_data = blob->data;
-	for (i = 0; i < size; i++) {
-		blob_data[i].red = red[i];
-		blob_data[i].green = green[i];
-		blob_data[i].blue = blue[i];
-	}
-
-	state->acquire_ctx = ctx;
-	crtc_state = drm_atomic_get_crtc_state(state, crtc);
-	if (IS_ERR(crtc_state)) {
-		ret = PTR_ERR(crtc_state);
-		goto fail;
-	}
-
-	/* Reset DEGAMMA_LUT and CTM properties. */
-	replaced  = drm_property_replace_blob(&crtc_state->degamma_lut, NULL);
-	replaced |= drm_property_replace_blob(&crtc_state->ctm, NULL);
-	replaced |= drm_property_replace_blob(&crtc_state->gamma_lut, blob);
-	crtc_state->color_mgmt_changed |= replaced;
-
-	ret = drm_atomic_commit(state);
-
-fail:
-	drm_atomic_state_put(state);
-	drm_property_blob_put(blob);
-	return ret;
-}
-EXPORT_SYMBOL(drm_atomic_helper_legacy_gamma_set);
-
-/**
  * drm_atomic_helper_bridge_propagate_bus_fmt() - Propagate output format to
  *						  the input end of a bridge
  * @bridge: bridge control structure
diff -Nur --no-dereference linux-5.10.52-orig/drivers/gpu/drm/drm_color_mgmt.c linux-5.10.52-v7l+/drivers/gpu/drm/drm_color_mgmt.c
--- linux-5.10.52-orig/drivers/gpu/drm/drm_color_mgmt.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/gpu/drm/drm_color_mgmt.c	2021-07-25 16:45:59.978391271 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:25 @
 
 #include <linux/uaccess.h>
 
+#include <drm/drm_atomic.h>
 #include <drm/drm_color_mgmt.h>
 #include <drm/drm_crtc.h>
 #include <drm/drm_device.h>
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:93 @
  *	modes) appropriately.
  *
  * There is also support for a legacy gamma table, which is set up by calling
- * drm_mode_crtc_set_gamma_size(). Drivers which support both should use
- * drm_atomic_helper_legacy_gamma_set() to alias the legacy gamma ramp with the
- * "GAMMA_LUT" property above.
+ * drm_mode_crtc_set_gamma_size(). The DRM core will then alias the legacy gamma
+ * ramp with "GAMMA_LUT".
  *
  * Support for different non RGB color encodings is controlled through
  * &drm_plane specific COLOR_ENCODING and COLOR_RANGE properties. They
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:159 @
  * optional. The gamma and degamma properties are only attached if
  * their size is not 0 and ctm_property is only attached if has_ctm is
  * true.
- *
- * Drivers should use drm_atomic_helper_legacy_gamma_set() to implement the
- * legacy &drm_crtc_funcs.gamma_set callback.
  */
 void drm_crtc_enable_color_mgmt(struct drm_crtc *crtc,
 				uint degamma_lut_size,
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:232 @
 EXPORT_SYMBOL(drm_mode_crtc_set_gamma_size);
 
 /**
+ * drm_crtc_supports_legacy_gamma - does the crtc support legacy gamma correction table
+ * @crtc: CRTC object
+ *
+ * Returns true/false if the given crtc supports setting the legacy gamma
+ * correction table.
+ */
+static bool drm_crtc_supports_legacy_gamma(struct drm_crtc *crtc)
+{
+	u32 gamma_id = crtc->dev->mode_config.gamma_lut_property->base.id;
+
+	if (!crtc->gamma_size)
+		return false;
+
+	if (crtc->funcs->gamma_set)
+		return true;
+
+	return !!drm_mode_obj_find_prop_id(&crtc->base, gamma_id);
+}
+
+/**
+ * drm_crtc_legacy_gamma_set - set the legacy gamma correction table
+ * @crtc: CRTC object
+ * @red: red correction table
+ * @green: green correction table
+ * @blue: green correction table
+ * @size: size of the tables
+ * @ctx: lock acquire context
+ *
+ * Implements support for legacy gamma correction table for drivers
+ * that have set drm_crtc_funcs.gamma_set or that support color management
+ * through the DEGAMMA_LUT/GAMMA_LUT properties. See
+ * drm_crtc_enable_color_mgmt() and the containing chapter for
+ * how the atomic color management and gamma tables work.
+ *
+ * This function sets the gamma using drm_crtc_funcs.gamma_set if set, or
+ * alternatively using crtc color management properties.
+ */
+static int drm_crtc_legacy_gamma_set(struct drm_crtc *crtc,
+				     u16 *red, u16 *green, u16 *blue,
+				     u32 size,
+				     struct drm_modeset_acquire_ctx *ctx)
+{
+	struct drm_device *dev = crtc->dev;
+	struct drm_atomic_state *state;
+	struct drm_crtc_state *crtc_state;
+	struct drm_property_blob *blob;
+	struct drm_color_lut *blob_data;
+	int i, ret = 0;
+	bool replaced;
+
+	if (crtc->funcs->gamma_set)
+		return crtc->funcs->gamma_set(crtc, red, green, blue, size, ctx);
+
+	state = drm_atomic_state_alloc(crtc->dev);
+	if (!state)
+		return -ENOMEM;
+
+	blob = drm_property_create_blob(dev,
+					sizeof(struct drm_color_lut) * size,
+					NULL);
+	if (IS_ERR(blob)) {
+		ret = PTR_ERR(blob);
+		blob = NULL;
+		goto fail;
+	}
+
+	/* Prepare GAMMA_LUT with the legacy values. */
+	blob_data = blob->data;
+	for (i = 0; i < size; i++) {
+		blob_data[i].red = red[i];
+		blob_data[i].green = green[i];
+		blob_data[i].blue = blue[i];
+	}
+
+	state->acquire_ctx = ctx;
+	crtc_state = drm_atomic_get_crtc_state(state, crtc);
+	if (IS_ERR(crtc_state)) {
+		ret = PTR_ERR(crtc_state);
+		goto fail;
+	}
+
+	/* Set GAMMA_LUT and reset DEGAMMA_LUT and CTM */
+	replaced = drm_property_replace_blob(&crtc_state->degamma_lut, NULL);
+	replaced |= drm_property_replace_blob(&crtc_state->ctm, NULL);
+	replaced |= drm_property_replace_blob(&crtc_state->gamma_lut, blob);
+	crtc_state->color_mgmt_changed |= replaced;
+
+	ret = drm_atomic_commit(state);
+
+fail:
+	drm_atomic_state_put(state);
+	drm_property_blob_put(blob);
+	return ret;
+}
+
+/**
  * drm_mode_gamma_set_ioctl - set the gamma table
  * @dev: DRM device
  * @data: ioctl data
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:358 @
 	if (!crtc)
 		return -ENOENT;
 
-	if (crtc->funcs->gamma_set == NULL)
+	if (!drm_crtc_supports_legacy_gamma(crtc))
 		return -ENOSYS;
 
 	/* memcpy into gamma store */
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:386 @
 		goto out;
 	}
 
-	ret = crtc->funcs->gamma_set(crtc, r_base, g_base, b_base,
-				     crtc->gamma_size, &ctx);
+	ret = drm_crtc_legacy_gamma_set(crtc, r_base, g_base, b_base,
+					crtc->gamma_size, &ctx);
 
 out:
 	DRM_MODESET_LOCK_ALL_END(dev, ctx, ret);
diff -Nur --no-dereference linux-5.10.52-orig/drivers/gpu/drm/drm_connector.c linux-5.10.52-v7l+/drivers/gpu/drm/drm_connector.c
--- linux-5.10.52-orig/drivers/gpu/drm/drm_connector.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/gpu/drm/drm_connector.c	2021-07-25 16:45:59.978391271 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:97 @
 	{ DRM_MODE_CONNECTOR_DPI, "DPI" },
 	{ DRM_MODE_CONNECTOR_WRITEBACK, "Writeback" },
 	{ DRM_MODE_CONNECTOR_SPI, "SPI" },
+	{ DRM_MODE_CONNECTOR_USB, "USB" },
 };
 
 void drm_connector_ida_init(void)
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:2148 @
 EXPORT_SYMBOL(drm_connector_attach_max_bpc_property);
 
 /**
+ * drm_connector_attach_hdr_output_metadata_property - attach "HDR_OUTPUT_METADA" property
+ * @connector: connector to attach the property on.
+ *
+ * This is used to allow the userspace to send HDR Metadata to the
+ * driver.
+ *
+ * Returns:
+ * Zero on success, negative errno on failure.
+ */
+int drm_connector_attach_hdr_output_metadata_property(struct drm_connector *connector)
+{
+	struct drm_device *dev = connector->dev;
+	struct drm_property *prop = dev->mode_config.hdr_output_metadata_property;
+
+	drm_object_attach_property(&connector->base, prop, 0);
+
+	return 0;
+}
+EXPORT_SYMBOL(drm_connector_attach_hdr_output_metadata_property);
+
+/**
+ * drm_connector_attach_colorspace_property - attach "Colorspace" property
+ * @connector: connector to attach the property on.
+ *
+ * This is used to allow the userspace to signal the output colorspace
+ * to the driver.
+ *
+ * Returns:
+ * Zero on success, negative errno on failure.
+ */
+int drm_connector_attach_colorspace_property(struct drm_connector *connector)
+{
+	struct drm_property *prop = connector->colorspace_property;
+
+	drm_object_attach_property(&connector->base, prop, DRM_MODE_COLORIMETRY_DEFAULT);
+
+	return 0;
+}
+EXPORT_SYMBOL(drm_connector_attach_colorspace_property);
+
+/**
+ * drm_connector_atomic_hdr_metadata_equal - checks if the hdr metadata changed
+ * @old_state: old connector state to compare
+ * @new_state: new connector state to compare
+ *
+ * This is used by HDR-enabled drivers to test whether the HDR metadata
+ * have changed between two different connector state (and thus probably
+ * requires a full blown mode change).
+ *
+ * Returns:
+ * True if the metadata are equal, False otherwise
+ */
+bool drm_connector_atomic_hdr_metadata_equal(struct drm_connector_state *old_state,
+					     struct drm_connector_state *new_state)
+{
+	struct drm_property_blob *old_blob = old_state->hdr_output_metadata;
+	struct drm_property_blob *new_blob = new_state->hdr_output_metadata;
+
+	if (!old_blob || !new_blob)
+		return old_blob == new_blob;
+
+	if (old_blob->length != new_blob->length)
+		return false;
+
+	return !memcmp(old_blob->data, new_blob->data, old_blob->length);
+}
+EXPORT_SYMBOL(drm_connector_atomic_hdr_metadata_equal);
+
+/**
  * drm_connector_set_vrr_capable_property - sets the variable refresh rate
  * capable property for a connector
  * @connector: drm connector
diff -Nur --no-dereference linux-5.10.52-orig/drivers/gpu/drm/drm_fourcc.c linux-5.10.52-v7l+/drivers/gpu/drm/drm_fourcc.c
--- linux-5.10.52-orig/drivers/gpu/drm/drm_fourcc.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/gpu/drm/drm_fourcc.c	2021-07-25 16:45:59.998390936 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:289 @
 		  .num_planes = 3, .char_per_block = { 2, 2, 2 },
 		  .block_w = { 1, 1, 1 }, .block_h = { 1, 1, 1 }, .hsub = 0,
 		  .vsub = 0, .is_yuv = true },
+		{ .format = DRM_FORMAT_P030,            .depth = 0,  .num_planes = 2,
+		  .char_per_block = { 4, 4, 0 }, .block_w = { 3, 0, 0 }, .block_h = { 1, 0, 0 },
+		  .hsub = 2, .vsub = 2, .is_yuv = true},
 	};
 
 	unsigned int i;
diff -Nur --no-dereference linux-5.10.52-orig/drivers/gpu/drm/drm_framebuffer.c linux-5.10.52-v7l+/drivers/gpu/drm/drm_framebuffer.c
--- linux-5.10.52-orig/drivers/gpu/drm/drm_framebuffer.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/gpu/drm/drm_framebuffer.c	2021-07-25 16:45:59.998390936 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:220 @
 		if (min_pitch > UINT_MAX)
 			return -ERANGE;
 
-		if ((uint64_t) height * r->pitches[i] + r->offsets[i] > UINT_MAX)
-			return -ERANGE;
+		if (r->modifier[i] == DRM_FORMAT_MOD_LINEAR) {
+			if ((uint64_t)height * r->pitches[i] + r->offsets[i] >
+								UINT_MAX)
+				return -ERANGE;
 
-		if (block_size && r->pitches[i] < min_pitch) {
-			DRM_DEBUG_KMS("bad pitch %u for plane %d\n", r->pitches[i], i);
-			return -EINVAL;
+			if (block_size && r->pitches[i] < min_pitch) {
+				DRM_DEBUG_KMS("bad pitch %u for plane %d\n",
+					      r->pitches[i], i);
+				return -EINVAL;
+			}
 		}
 
 		if (r->modifier[i] && !(r->flags & DRM_MODE_FB_MODIFIERS)) {
diff -Nur --no-dereference linux-5.10.52-orig/drivers/gpu/drm/drm_simple_kms_helper.c linux-5.10.52-v7l+/drivers/gpu/drm/drm_simple_kms_helper.c
--- linux-5.10.52-orig/drivers/gpu/drm/drm_simple_kms_helper.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/gpu/drm/drm_simple_kms_helper.c	2021-07-25 16:46:00.008390768 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:89 @
 }
 
 static int drm_simple_kms_crtc_check(struct drm_crtc *crtc,
-				     struct drm_crtc_state *state)
+				     struct drm_atomic_state *state)
 {
-	bool has_primary = state->plane_mask &
+	struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state,
+									  crtc);
+	bool has_primary = crtc_state->plane_mask &
 			   drm_plane_mask(crtc->primary);
 
 	/* We always want to have an active plane with an active CRTC */
-	if (has_primary != state->enable)
+	if (has_primary != crtc_state->enable)
 		return -EINVAL;
 
-	return drm_atomic_add_affected_planes(state->state, crtc);
+	return drm_atomic_add_affected_planes(state, crtc);
 }
 
 static void drm_simple_kms_crtc_enable(struct drm_crtc *crtc,
-				       struct drm_crtc_state *old_state)
+				       struct drm_atomic_state *state)
 {
 	struct drm_plane *plane;
 	struct drm_simple_display_pipe *pipe;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:118 @
 }
 
 static void drm_simple_kms_crtc_disable(struct drm_crtc *crtc,
-					struct drm_crtc_state *old_state)
+					struct drm_atomic_state *state)
 {
 	struct drm_simple_display_pipe *pipe;
 
diff -Nur --no-dereference linux-5.10.52-orig/drivers/gpu/drm/exynos/exynos_drm_crtc.c linux-5.10.52-v7l+/drivers/gpu/drm/exynos/exynos_drm_crtc.c
--- linux-5.10.52-orig/drivers/gpu/drm/exynos/exynos_drm_crtc.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/gpu/drm/exynos/exynos_drm_crtc.c	2021-07-25 16:46:00.028390433 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:22 @
 #include "exynos_drm_plane.h"
 
 static void exynos_drm_crtc_atomic_enable(struct drm_crtc *crtc,
-					  struct drm_crtc_state *old_state)
+					  struct drm_atomic_state *state)
 {
 	struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:33 @
 }
 
 static void exynos_drm_crtc_atomic_disable(struct drm_crtc *crtc,
-					   struct drm_crtc_state *old_state)
+					   struct drm_atomic_state *state)
 {
 	struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:52 @
 }
 
 static int exynos_crtc_atomic_check(struct drm_crtc *crtc,
-				     struct drm_crtc_state *state)
+				     struct drm_atomic_state *state)
 {
+	struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state,
+									  crtc);
 	struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
 
-	if (!state->enable)
+	if (!crtc_state->enable)
 		return 0;
 
 	if (exynos_crtc->ops->atomic_check)
-		return exynos_crtc->ops->atomic_check(exynos_crtc, state);
+		return exynos_crtc->ops->atomic_check(exynos_crtc, crtc_state);
 
 	return 0;
 }
 
 static void exynos_crtc_atomic_begin(struct drm_crtc *crtc,
-				     struct drm_crtc_state *old_crtc_state)
+				     struct drm_atomic_state *state)
 {
 	struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:77 @
 }
 
 static void exynos_crtc_atomic_flush(struct drm_crtc *crtc,
-				     struct drm_crtc_state *old_crtc_state)
+				     struct drm_atomic_state *state)
 {
 	struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
 
diff -Nur --no-dereference linux-5.10.52-orig/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_crtc.c linux-5.10.52-v7l+/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_crtc.c
--- linux-5.10.52-orig/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_crtc.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_crtc.c	2021-07-25 16:46:00.048390098 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:24 @
 #include "fsl_dcu_drm_plane.h"
 
 static void fsl_dcu_drm_crtc_atomic_flush(struct drm_crtc *crtc,
-					  struct drm_crtc_state *old_crtc_state)
+					  struct drm_atomic_state *state)
 {
 	struct drm_device *dev = crtc->dev;
 	struct fsl_dcu_drm_device *fsl_dev = dev->dev_private;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:46 @
 }
 
 static void fsl_dcu_drm_crtc_atomic_disable(struct drm_crtc *crtc,
-					struct drm_crtc_state *old_crtc_state)
+					struct drm_atomic_state *state)
 {
+	struct drm_crtc_state *old_crtc_state = drm_atomic_get_old_crtc_state(state,
+									      crtc);
 	struct drm_device *dev = crtc->dev;
 	struct fsl_dcu_drm_device *fsl_dev = dev->dev_private;
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:67 @
 }
 
 static void fsl_dcu_drm_crtc_atomic_enable(struct drm_crtc *crtc,
-					   struct drm_crtc_state *old_state)
+					   struct drm_atomic_state *state)
 {
 	struct drm_device *dev = crtc->dev;
 	struct fsl_dcu_drm_device *fsl_dev = dev->dev_private;
diff -Nur --no-dereference linux-5.10.52-orig/drivers/gpu/drm/gud/gud_connector.c linux-5.10.52-v7l+/drivers/gpu/drm/gud/gud_connector.c
--- linux-5.10.52-orig/drivers/gpu/drm/gud/gud_connector.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/gpu/drm/gud/gud_connector.c	2021-07-25 16:46:00.078389595 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright 2020 Noralf Trønnes
+ */
+
+#include <linux/backlight.h>
+#include <linux/workqueue.h>
+
+#include <drm/drm_atomic.h>
+#include <drm/drm_atomic_state_helper.h>
+#include <drm/drm_connector.h>
+#include <drm/drm_drv.h>
+#include <drm/drm_encoder.h>
+#include <drm/drm_file.h>
+#include <drm/drm_modeset_helper_vtables.h>
+#include <drm/drm_print.h>
+#include <drm/drm_probe_helper.h>
+#include <drm/drm_simple_kms_helper.h>
+#include <drm/gud.h>
+
+#include "gud_internal.h"
+
+struct gud_connector {
+	struct drm_connector connector;
+	struct drm_encoder encoder;
+	struct backlight_device *backlight;
+	struct work_struct backlight_work;
+
+	/* Supported properties */
+	u16 *properties;
+	unsigned int num_properties;
+
+	/* Initial gadget tv state if applicable, applied on state reset */
+	struct drm_tv_connector_state initial_tv_state;
+
+	/*
+	 * Initial gadget backlight brightness if applicable, applied on state reset.
+	 * The value -ENODEV is used to signal no backlight.
+	 */
+	int initial_brightness;
+};
+
+static inline struct gud_connector *to_gud_connector(struct drm_connector *connector)
+{
+	return container_of(connector, struct gud_connector, connector);
+}
+
+static void gud_conn_err(struct drm_connector *connector, const char *msg, int ret)
+{
+	dev_err(connector->dev->dev, "%s: %s (ret=%d)\n", connector->name, msg, ret);
+}
+
+/*
+ * Use a worker to avoid taking kms locks inside the backlight lock.
+ * Other display drivers use backlight within their kms locks.
+ * This avoids inconsistent locking rules, which would upset lockdep.
+ */
+static void gud_connector_backlight_update_status_work(struct work_struct *work)
+{
+	struct gud_connector *gconn = container_of(work, struct gud_connector, backlight_work);
+	struct drm_connector *connector = &gconn->connector;
+	struct drm_connector_state *connector_state;
+	struct drm_device *drm = connector->dev;
+	struct drm_modeset_acquire_ctx ctx;
+	struct drm_atomic_state *state;
+	int idx, ret;
+
+	if (!drm_dev_enter(drm, &idx))
+		return;
+
+	state = drm_atomic_state_alloc(drm);
+	if (!state) {
+		ret = -ENOMEM;
+		goto exit;
+	}
+
+	drm_modeset_acquire_init(&ctx, 0);
+	state->acquire_ctx = &ctx;
+retry:
+	connector_state = drm_atomic_get_connector_state(state, connector);
+	if (IS_ERR(connector_state)) {
+		ret = PTR_ERR(connector_state);
+		goto out;
+	}
+
+	/* Reuse tv.brightness to avoid having to subclass */
+	connector_state->tv.brightness = gconn->backlight->props.brightness;
+
+	ret = drm_atomic_commit(state);
+out:
+	if (ret == -EDEADLK) {
+		drm_atomic_state_clear(state);
+		drm_modeset_backoff(&ctx);
+		goto retry;
+	}
+
+	drm_atomic_state_put(state);
+
+	drm_modeset_drop_locks(&ctx);
+	drm_modeset_acquire_fini(&ctx);
+exit:
+	drm_dev_exit(idx);
+
+	if (ret)
+		dev_err(drm->dev, "Failed to update backlight, err=%d\n", ret);
+}
+
+static int gud_connector_backlight_update_status(struct backlight_device *bd)
+{
+	struct drm_connector *connector = bl_get_data(bd);
+	struct gud_connector *gconn = to_gud_connector(connector);
+
+	/* The USB timeout is 5 seconds so use system_long_wq for worst case scenario */
+	queue_work(system_long_wq, &gconn->backlight_work);
+
+	return 0;
+}
+
+static const struct backlight_ops gud_connector_backlight_ops = {
+	.update_status	= gud_connector_backlight_update_status,
+};
+
+static int gud_connector_backlight_register(struct gud_connector *gconn)
+{
+	struct drm_connector *connector = &gconn->connector;
+	struct backlight_device *bd;
+	const char *name;
+	const struct backlight_properties props = {
+		.type = BACKLIGHT_RAW,
+		.scale = BACKLIGHT_SCALE_NON_LINEAR,
+		.max_brightness = 100,
+		.brightness = gconn->initial_brightness,
+	};
+
+	name = kasprintf(GFP_KERNEL, "card%d-%s-backlight",
+			 connector->dev->primary->index, connector->name);
+	if (!name)
+		return -ENOMEM;
+
+	bd = backlight_device_register(name, connector->kdev, connector,
+				       &gud_connector_backlight_ops, &props);
+	kfree(name);
+	if (IS_ERR(bd))
+		return PTR_ERR(bd);
+
+	gconn->backlight = bd;
+
+	return 0;
+}
+
+static int gud_connector_detect(struct drm_connector *connector,
+				struct drm_modeset_acquire_ctx *ctx, bool force)
+{
+	struct gud_device *gdrm = to_gud_device(connector->dev);
+	int idx, ret;
+	u8 status;
+
+	if (!drm_dev_enter(connector->dev, &idx))
+		return connector_status_disconnected;
+
+	if (force) {
+		ret = gud_usb_set(gdrm, GUD_REQ_SET_CONNECTOR_FORCE_DETECT,
+				  connector->index, NULL, 0);
+		if (ret) {
+			ret = connector_status_unknown;
+			goto exit;
+		}
+	}
+
+	ret = gud_usb_get_u8(gdrm, GUD_REQ_GET_CONNECTOR_STATUS, connector->index, &status);
+	if (ret) {
+		ret = connector_status_unknown;
+		goto exit;
+	}
+
+	switch (status & GUD_CONNECTOR_STATUS_CONNECTED_MASK) {
+	case GUD_CONNECTOR_STATUS_DISCONNECTED:
+		ret = connector_status_disconnected;
+		break;
+	case GUD_CONNECTOR_STATUS_CONNECTED:
+		ret = connector_status_connected;
+		break;
+	default:
+		ret = connector_status_unknown;
+		break;
+	}
+
+	if (status & GUD_CONNECTOR_STATUS_CHANGED)
+		connector->epoch_counter += 1;
+exit:
+	drm_dev_exit(idx);
+
+	return ret;
+}
+
+struct gud_connector_get_edid_ctx {
+	void *buf;
+	size_t len;
+	bool edid_override;
+};
+
+static int gud_connector_get_edid_block(void *data, u8 *buf, unsigned int block, size_t len)
+{
+	struct gud_connector_get_edid_ctx *ctx = data;
+	size_t start = block * EDID_LENGTH;
+
+	ctx->edid_override = false;
+
+	if (start + len > ctx->len)
+		return -1;
+
+	memcpy(buf, ctx->buf + start, len);
+
+	return 0;
+}
+
+static int gud_connector_get_modes(struct drm_connector *connector)
+{
+	struct gud_device *gdrm = to_gud_device(connector->dev);
+	struct gud_display_mode_req *reqmodes = NULL;
+	struct gud_connector_get_edid_ctx edid_ctx;
+	unsigned int i, num_modes = 0;
+	struct edid *edid = NULL;
+	int idx, ret;
+
+	if (!drm_dev_enter(connector->dev, &idx))
+		return 0;
+
+	edid_ctx.edid_override = true;
+	edid_ctx.buf = kmalloc(GUD_CONNECTOR_MAX_EDID_LEN, GFP_KERNEL);
+	if (!edid_ctx.buf)
+		goto out;
+
+	ret = gud_usb_get(gdrm, GUD_REQ_GET_CONNECTOR_EDID, connector->index,
+			  edid_ctx.buf, GUD_CONNECTOR_MAX_EDID_LEN);
+	if (ret > 0 && ret % EDID_LENGTH) {
+		gud_conn_err(connector, "Invalid EDID size", ret);
+	} else if (ret > 0) {
+		edid_ctx.len = ret;
+		edid = drm_do_get_edid(connector, gud_connector_get_edid_block, &edid_ctx);
+	}
+
+	kfree(edid_ctx.buf);
+	drm_connector_update_edid_property(connector, edid);
+
+	if (edid && edid_ctx.edid_override)
+		goto out;
+
+	reqmodes = kmalloc_array(GUD_CONNECTOR_MAX_NUM_MODES, sizeof(*reqmodes), GFP_KERNEL);
+	if (!reqmodes)
+		goto out;
+
+	ret = gud_usb_get(gdrm, GUD_REQ_GET_CONNECTOR_MODES, connector->index,
+			  reqmodes, GUD_CONNECTOR_MAX_NUM_MODES * sizeof(*reqmodes));
+	if (ret <= 0)
+		goto out;
+	if (ret % sizeof(*reqmodes)) {
+		gud_conn_err(connector, "Invalid display mode array size", ret);
+		goto out;
+	}
+
+	num_modes = ret / sizeof(*reqmodes);
+
+	for (i = 0; i < num_modes; i++) {
+		struct drm_display_mode *mode;
+
+		mode = drm_mode_create(connector->dev);
+		if (!mode) {
+			num_modes = i;
+			goto out;
+		}
+
+		gud_to_display_mode(mode, &reqmodes[i]);
+		drm_mode_probed_add(connector, mode);
+	}
+out:
+	if (!num_modes)
+		num_modes = drm_add_edid_modes(connector, edid);
+
+	kfree(reqmodes);
+	kfree(edid);
+	drm_dev_exit(idx);
+
+	return num_modes;
+}
+
+static int gud_connector_atomic_check(struct drm_connector *connector,
+				      struct drm_atomic_state *state)
+{
+	struct drm_connector_state *new_state;
+	struct drm_crtc_state *new_crtc_state;
+	struct drm_connector_state *old_state;
+
+	new_state = drm_atomic_get_new_connector_state(state, connector);
+	if (!new_state->crtc)
+		return 0;
+
+	old_state = drm_atomic_get_old_connector_state(state, connector);
+	new_crtc_state = drm_atomic_get_new_crtc_state(state, new_state->crtc);
+
+	if (old_state->tv.margins.left != new_state->tv.margins.left ||
+	    old_state->tv.margins.right != new_state->tv.margins.right ||
+	    old_state->tv.margins.top != new_state->tv.margins.top ||
+	    old_state->tv.margins.bottom != new_state->tv.margins.bottom ||
+	    old_state->tv.mode != new_state->tv.mode ||
+	    old_state->tv.brightness != new_state->tv.brightness ||
+	    old_state->tv.contrast != new_state->tv.contrast ||
+	    old_state->tv.flicker_reduction != new_state->tv.flicker_reduction ||
+	    old_state->tv.overscan != new_state->tv.overscan ||
+	    old_state->tv.saturation != new_state->tv.saturation ||
+	    old_state->tv.hue != new_state->tv.hue)
+		new_crtc_state->connectors_changed = true;
+
+	return 0;
+}
+
+static const struct drm_connector_helper_funcs gud_connector_helper_funcs = {
+	.detect_ctx = gud_connector_detect,
+	.get_modes = gud_connector_get_modes,
+	.atomic_check = gud_connector_atomic_check,
+};
+
+static int gud_connector_late_register(struct drm_connector *connector)
+{
+	struct gud_connector *gconn = to_gud_connector(connector);
+
+	if (gconn->initial_brightness < 0)
+		return 0;
+
+	return gud_connector_backlight_register(gconn);
+}
+
+static void gud_connector_early_unregister(struct drm_connector *connector)
+{
+	struct gud_connector *gconn = to_gud_connector(connector);
+
+	backlight_device_unregister(gconn->backlight);
+	cancel_work_sync(&gconn->backlight_work);
+}
+
+static void gud_connector_destroy(struct drm_connector *connector)
+{
+	struct gud_connector *gconn = to_gud_connector(connector);
+
+	drm_connector_cleanup(connector);
+	kfree(gconn->properties);
+	kfree(gconn);
+}
+
+static void gud_connector_reset(struct drm_connector *connector)
+{
+	struct gud_connector *gconn = to_gud_connector(connector);
+
+	drm_atomic_helper_connector_reset(connector);
+	connector->state->tv = gconn->initial_tv_state;
+	/* Set margins from command line */
+	drm_atomic_helper_connector_tv_reset(connector);
+	if (gconn->initial_brightness >= 0)
+		connector->state->tv.brightness = gconn->initial_brightness;
+}
+
+static const struct drm_connector_funcs gud_connector_funcs = {
+	.fill_modes = drm_helper_probe_single_connector_modes,
+	.late_register = gud_connector_late_register,
+	.early_unregister = gud_connector_early_unregister,
+	.destroy = gud_connector_destroy,
+	.reset = gud_connector_reset,
+	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+};
+
+/*
+ * The tv.mode property is shared among the connectors and its enum names are
+ * driver specific. This means that if more than one connector uses tv.mode,
+ * the enum names has to be the same.
+ */
+static int gud_connector_add_tv_mode(struct gud_device *gdrm, struct drm_connector *connector)
+{
+	size_t buf_len = GUD_CONNECTOR_TV_MODE_MAX_NUM * GUD_CONNECTOR_TV_MODE_NAME_LEN;
+	const char *modes[GUD_CONNECTOR_TV_MODE_MAX_NUM];
+	unsigned int i, num_modes;
+	char *buf;
+	int ret;
+
+	buf = kmalloc(buf_len, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	ret = gud_usb_get(gdrm, GUD_REQ_GET_CONNECTOR_TV_MODE_VALUES,
+			  connector->index, buf, buf_len);
+	if (ret < 0)
+		goto free;
+	if (!ret || ret % GUD_CONNECTOR_TV_MODE_NAME_LEN) {
+		ret = -EIO;
+		goto free;
+	}
+
+	num_modes = ret / GUD_CONNECTOR_TV_MODE_NAME_LEN;
+	for (i = 0; i < num_modes; i++)
+		modes[i] = &buf[i * GUD_CONNECTOR_TV_MODE_NAME_LEN];
+
+	ret = drm_mode_create_tv_properties(connector->dev, num_modes, modes);
+free:
+	kfree(buf);
+	if (ret < 0)
+		gud_conn_err(connector, "Failed to add TV modes", ret);
+
+	return ret;
+}
+
+static struct drm_property *
+gud_connector_property_lookup(struct drm_connector *connector, u16 prop)
+{
+	struct drm_mode_config *config = &connector->dev->mode_config;
+
+	switch (prop) {
+	case GUD_PROPERTY_TV_LEFT_MARGIN:
+		return config->tv_left_margin_property;
+	case GUD_PROPERTY_TV_RIGHT_MARGIN:
+		return config->tv_right_margin_property;
+	case GUD_PROPERTY_TV_TOP_MARGIN:
+		return config->tv_top_margin_property;
+	case GUD_PROPERTY_TV_BOTTOM_MARGIN:
+		return config->tv_bottom_margin_property;
+	case GUD_PROPERTY_TV_MODE:
+		return config->tv_mode_property;
+	case GUD_PROPERTY_TV_BRIGHTNESS:
+		return config->tv_brightness_property;
+	case GUD_PROPERTY_TV_CONTRAST:
+		return config->tv_contrast_property;
+	case GUD_PROPERTY_TV_FLICKER_REDUCTION:
+		return config->tv_flicker_reduction_property;
+	case GUD_PROPERTY_TV_OVERSCAN:
+		return config->tv_overscan_property;
+	case GUD_PROPERTY_TV_SATURATION:
+		return config->tv_saturation_property;
+	case GUD_PROPERTY_TV_HUE:
+		return config->tv_hue_property;
+	default:
+		return ERR_PTR(-EINVAL);
+	}
+}
+
+static unsigned int *gud_connector_tv_state_val(u16 prop, struct drm_tv_connector_state *state)
+{
+	switch (prop) {
+	case GUD_PROPERTY_TV_LEFT_MARGIN:
+		return &state->margins.left;
+	case GUD_PROPERTY_TV_RIGHT_MARGIN:
+		return &state->margins.right;
+	case GUD_PROPERTY_TV_TOP_MARGIN:
+		return &state->margins.top;
+	case GUD_PROPERTY_TV_BOTTOM_MARGIN:
+		return &state->margins.bottom;
+	case GUD_PROPERTY_TV_MODE:
+		return &state->mode;
+	case GUD_PROPERTY_TV_BRIGHTNESS:
+		return &state->brightness;
+	case GUD_PROPERTY_TV_CONTRAST:
+		return &state->contrast;
+	case GUD_PROPERTY_TV_FLICKER_REDUCTION:
+		return &state->flicker_reduction;
+	case GUD_PROPERTY_TV_OVERSCAN:
+		return &state->overscan;
+	case GUD_PROPERTY_TV_SATURATION:
+		return &state->saturation;
+	case GUD_PROPERTY_TV_HUE:
+		return &state->hue;
+	default:
+		return ERR_PTR(-EINVAL);
+	}
+}
+
+static int gud_connector_add_properties(struct gud_device *gdrm, struct gud_connector *gconn)
+{
+	struct drm_connector *connector = &gconn->connector;
+	struct drm_device *drm = &gdrm->drm;
+	struct gud_property_req *properties;
+	unsigned int i, num_properties;
+	int ret;
+
+	properties = kcalloc(GUD_CONNECTOR_PROPERTIES_MAX_NUM, sizeof(*properties), GFP_KERNEL);
+	if (!properties)
+		return -ENOMEM;
+
+	ret = gud_usb_get(gdrm, GUD_REQ_GET_CONNECTOR_PROPERTIES, connector->index,
+			  properties, GUD_CONNECTOR_PROPERTIES_MAX_NUM * sizeof(*properties));
+	if (ret <= 0)
+		goto out;
+	if (ret % sizeof(*properties)) {
+		ret = -EIO;
+		goto out;
+	}
+
+	num_properties = ret / sizeof(*properties);
+	ret = 0;
+
+	gconn->properties = kcalloc(num_properties, sizeof(*gconn->properties), GFP_KERNEL);
+	if (!gconn->properties) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	for (i = 0; i < num_properties; i++) {
+		u16 prop = le16_to_cpu(properties[i].prop);
+		u64 val = le64_to_cpu(properties[i].val);
+		struct drm_property *property;
+		unsigned int *state_val;
+
+		drm_dbg(drm, "property: %u = %llu(0x%llx)\n", prop, val, val);
+
+		switch (prop) {
+		case GUD_PROPERTY_TV_LEFT_MARGIN:
+			fallthrough;
+		case GUD_PROPERTY_TV_RIGHT_MARGIN:
+			fallthrough;
+		case GUD_PROPERTY_TV_TOP_MARGIN:
+			fallthrough;
+		case GUD_PROPERTY_TV_BOTTOM_MARGIN:
+			ret = drm_mode_create_tv_margin_properties(drm);
+			if (ret)
+				goto out;
+			break;
+		case GUD_PROPERTY_TV_MODE:
+			ret = gud_connector_add_tv_mode(gdrm, connector);
+			if (ret)
+				goto out;
+			break;
+		case GUD_PROPERTY_TV_BRIGHTNESS:
+			fallthrough;
+		case GUD_PROPERTY_TV_CONTRAST:
+			fallthrough;
+		case GUD_PROPERTY_TV_FLICKER_REDUCTION:
+			fallthrough;
+		case GUD_PROPERTY_TV_OVERSCAN:
+			fallthrough;
+		case GUD_PROPERTY_TV_SATURATION:
+			fallthrough;
+		case GUD_PROPERTY_TV_HUE:
+			/* This is a no-op if already added. */
+			ret = drm_mode_create_tv_properties(drm, 0, NULL);
+			if (ret)
+				goto out;
+			break;
+		case GUD_PROPERTY_BACKLIGHT_BRIGHTNESS:
+			if (val > 100) {
+				ret = -EINVAL;
+				goto out;
+			}
+			gconn->initial_brightness = val;
+			break;
+		default:
+			/* New ones might show up in future devices, skip those we don't know. */
+			drm_dbg(drm, "Ignoring unknown property: %u\n", prop);
+			continue;
+		}
+
+		gconn->properties[gconn->num_properties++] = prop;
+
+		if (prop == GUD_PROPERTY_BACKLIGHT_BRIGHTNESS)
+			continue; /* not a DRM property */
+
+		property = gud_connector_property_lookup(connector, prop);
+		if (WARN_ON(IS_ERR(property)))
+			continue;
+
+		state_val = gud_connector_tv_state_val(prop, &gconn->initial_tv_state);
+		if (WARN_ON(IS_ERR(state_val)))
+			continue;
+
+		*state_val = val;
+		drm_object_attach_property(&connector->base, property, 0);
+	}
+out:
+	kfree(properties);
+
+	return ret;
+}
+
+int gud_connector_fill_properties(struct drm_connector_state *connector_state,
+				  struct gud_property_req *properties)
+{
+	struct gud_connector *gconn = to_gud_connector(connector_state->connector);
+	unsigned int i;
+
+	for (i = 0; i < gconn->num_properties; i++) {
+		u16 prop = gconn->properties[i];
+		u64 val;
+
+		if (prop == GUD_PROPERTY_BACKLIGHT_BRIGHTNESS) {
+			val = connector_state->tv.brightness;
+		} else {
+			unsigned int *state_val;
+
+			state_val = gud_connector_tv_state_val(prop, &connector_state->tv);
+			if (WARN_ON_ONCE(IS_ERR(state_val)))
+				return PTR_ERR(state_val);
+
+			val = *state_val;
+		}
+
+		properties[i].prop = cpu_to_le16(prop);
+		properties[i].val = cpu_to_le64(val);
+	}
+
+	return gconn->num_properties;
+}
+
+static int gud_connector_create(struct gud_device *gdrm, unsigned int index,
+				struct gud_connector_descriptor_req *desc)
+{
+	struct drm_device *drm = &gdrm->drm;
+	struct gud_connector *gconn;
+	struct drm_connector *connector;
+	struct drm_encoder *encoder;
+	int ret, connector_type;
+	u32 flags;
+
+	gconn = kzalloc(sizeof(*gconn), GFP_KERNEL);
+	if (!gconn)
+		return -ENOMEM;
+
+	INIT_WORK(&gconn->backlight_work, gud_connector_backlight_update_status_work);
+	gconn->initial_brightness = -ENODEV;
+	flags = le32_to_cpu(desc->flags);
+	connector = &gconn->connector;
+
+	drm_dbg(drm, "Connector: index=%u type=%u flags=0x%x\n", index, desc->connector_type, flags);
+
+	switch (desc->connector_type) {
+	case GUD_CONNECTOR_TYPE_PANEL:
+		connector_type = DRM_MODE_CONNECTOR_USB;
+		break;
+	case GUD_CONNECTOR_TYPE_VGA:
+		connector_type = DRM_MODE_CONNECTOR_VGA;
+		break;
+	case GUD_CONNECTOR_TYPE_DVI:
+		connector_type = DRM_MODE_CONNECTOR_DVID;
+		break;
+	case GUD_CONNECTOR_TYPE_COMPOSITE:
+		connector_type = DRM_MODE_CONNECTOR_Composite;
+		break;
+	case GUD_CONNECTOR_TYPE_SVIDEO:
+		connector_type = DRM_MODE_CONNECTOR_SVIDEO;
+		break;
+	case GUD_CONNECTOR_TYPE_COMPONENT:
+		connector_type = DRM_MODE_CONNECTOR_Component;
+		break;
+	case GUD_CONNECTOR_TYPE_DISPLAYPORT:
+		connector_type = DRM_MODE_CONNECTOR_DisplayPort;
+		break;
+	case GUD_CONNECTOR_TYPE_HDMI:
+		connector_type = DRM_MODE_CONNECTOR_HDMIA;
+		break;
+	default: /* future types */
+		connector_type = DRM_MODE_CONNECTOR_USB;
+		break;
+	}
+
+	drm_connector_helper_add(connector, &gud_connector_helper_funcs);
+	ret = drm_connector_init(drm, connector, &gud_connector_funcs, connector_type);
+	if (ret) {
+		kfree(connector);
+		return ret;
+	}
+
+	if (WARN_ON(connector->index != index))
+		return -EINVAL;
+
+	if (flags & GUD_CONNECTOR_FLAGS_POLL_STATUS)
+		connector->polled = (DRM_CONNECTOR_POLL_CONNECT | DRM_CONNECTOR_POLL_DISCONNECT);
+	if (flags & GUD_CONNECTOR_FLAGS_INTERLACE)
+		connector->interlace_allowed = true;
+	if (flags & GUD_CONNECTOR_FLAGS_DOUBLESCAN)
+		connector->doublescan_allowed = true;
+
+	ret = gud_connector_add_properties(gdrm, gconn);
+	if (ret) {
+		gud_conn_err(connector, "Failed to add properties", ret);
+		return ret;
+	}
+
+	/* The first connector is attached to the existing simple pipe encoder */
+	if (!connector->index) {
+		encoder = &gdrm->pipe.encoder;
+	} else {
+		encoder = &gconn->encoder;
+
+		ret = drm_simple_encoder_init(drm, encoder, DRM_MODE_ENCODER_NONE);
+		if (ret)
+			return ret;
+
+		encoder->possible_crtcs = 1;
+	}
+
+	return drm_connector_attach_encoder(connector, encoder);
+}
+
+int gud_get_connectors(struct gud_device *gdrm)
+{
+	struct gud_connector_descriptor_req *descs;
+	unsigned int i, num_connectors;
+	int ret;
+
+	descs = kmalloc_array(GUD_CONNECTORS_MAX_NUM, sizeof(*descs), GFP_KERNEL);
+	if (!descs)
+		return -ENOMEM;
+
+	ret = gud_usb_get(gdrm, GUD_REQ_GET_CONNECTORS, 0,
+			  descs, GUD_CONNECTORS_MAX_NUM * sizeof(*descs));
+	if (ret < 0)
+		goto free;
+	if (!ret || ret % sizeof(*descs)) {
+		ret = -EIO;
+		goto free;
+	}
+
+	num_connectors = ret / sizeof(*descs);
+
+	for (i = 0; i < num_connectors; i++) {
+		ret = gud_connector_create(gdrm, i, &descs[i]);
+		if (ret)
+			goto free;
+	}
+free:
+	kfree(descs);
+
+	return ret;
+}
diff -Nur --no-dereference linux-5.10.52-orig/drivers/gpu/drm/gud/gud_drv.c linux-5.10.52-v7l+/drivers/gpu/drm/gud/gud_drv.c
--- linux-5.10.52-orig/drivers/gpu/drm/gud/gud_drv.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/gpu/drm/gud/gud_drv.c	2021-07-25 16:46:00.078389595 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright 2020 Noralf Trønnes
+ */
+
+#include <linux/dma-buf.h>
+#include <linux/dma-mapping.h>
+#include <linux/lz4.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/string_helpers.h>
+#include <linux/usb.h>
+#include <linux/vmalloc.h>
+#include <linux/workqueue.h>
+
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_damage_helper.h>
+#include <drm/drm_debugfs.h>
+#include <drm/drm_drv.h>
+#include <drm/drm_fb_helper.h>
+#include <drm/drm_fourcc.h>
+#include <drm/drm_gem_framebuffer_helper.h>
+#include <drm/drm_gem_shmem_helper.h>
+#include <drm/drm_managed.h>
+#include <drm/drm_print.h>
+#include <drm/drm_probe_helper.h>
+#include <drm/drm_simple_kms_helper.h>
+#include <drm/gud.h>
+
+#include "gud_internal.h"
+
+/* Only used internally */
+static const struct drm_format_info gud_drm_format_r1 = {
+	.format = GUD_DRM_FORMAT_R1,
+	.num_planes = 1,
+	.char_per_block = { 1, 0, 0 },
+	.block_w = { 8, 0, 0 },
+	.block_h = { 1, 0, 0 },
+	.hsub = 1,
+	.vsub = 1,
+};
+
+static const struct drm_format_info gud_drm_format_xrgb1111 = {
+	.format = GUD_DRM_FORMAT_XRGB1111,
+	.num_planes = 1,
+	.char_per_block = { 1, 0, 0 },
+	.block_w = { 2, 0, 0 },
+	.block_h = { 1, 0, 0 },
+	.hsub = 1,
+	.vsub = 1,
+};
+
+static int gud_usb_control_msg(struct usb_interface *intf, bool in,
+			       u8 request, u16 value, void *buf, size_t len)
+{
+	u8 requesttype = USB_TYPE_VENDOR | USB_RECIP_INTERFACE;
+	u8 ifnum = intf->cur_altsetting->desc.bInterfaceNumber;
+	struct usb_device *usb = interface_to_usbdev(intf);
+	unsigned int pipe;
+
+	if (len && !buf)
+		return -EINVAL;
+
+	if (in) {
+		pipe = usb_rcvctrlpipe(usb, 0);
+		requesttype |= USB_DIR_IN;
+	} else {
+		pipe = usb_sndctrlpipe(usb, 0);
+		requesttype |= USB_DIR_OUT;
+	}
+
+	return usb_control_msg(usb, pipe, request, requesttype, value,
+			       ifnum, buf, len, USB_CTRL_GET_TIMEOUT);
+}
+
+static int gud_get_display_descriptor(struct usb_interface *intf,
+				      struct gud_display_descriptor_req *desc)
+{
+	void *buf;
+	int ret;
+
+	buf = kmalloc(sizeof(*desc), GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	ret = gud_usb_control_msg(intf, true, GUD_REQ_GET_DESCRIPTOR, 0, buf, sizeof(*desc));
+	memcpy(desc, buf, sizeof(*desc));
+	kfree(buf);
+	if (ret < 0)
+		return ret;
+	if (ret != sizeof(*desc))
+		return -EIO;
+
+	if (desc->magic != le32_to_cpu(GUD_DISPLAY_MAGIC))
+		return -ENODATA;
+
+	DRM_DEV_DEBUG_DRIVER(&intf->dev,
+			     "version=%u flags=0x%x compression=0x%x max_buffer_size=%u\n",
+			     desc->version, le32_to_cpu(desc->flags), desc->compression,
+			     le32_to_cpu(desc->max_buffer_size));
+
+	if (!desc->version || !desc->max_width || !desc->max_height ||
+	    le32_to_cpu(desc->min_width) > le32_to_cpu(desc->max_width) ||
+	    le32_to_cpu(desc->min_height) > le32_to_cpu(desc->max_height))
+		return -EINVAL;
+
+	return 0;
+}
+
+static int gud_status_to_errno(u8 status)
+{
+	switch (status) {
+	case GUD_STATUS_OK:
+		return 0;
+	case GUD_STATUS_BUSY:
+		return -EBUSY;
+	case GUD_STATUS_REQUEST_NOT_SUPPORTED:
+		return -EOPNOTSUPP;
+	case GUD_STATUS_PROTOCOL_ERROR:
+		return -EPROTO;
+	case GUD_STATUS_INVALID_PARAMETER:
+		return -EINVAL;
+	case GUD_STATUS_ERROR:
+		return -EREMOTEIO;
+	default:
+		return -EREMOTEIO;
+	}
+}
+
+static int gud_usb_get_status(struct usb_interface *intf)
+{
+	int ret, status = -EIO;
+	u8 *buf;
+
+	buf = kmalloc(sizeof(*buf), GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	ret = gud_usb_control_msg(intf, true, GUD_REQ_GET_STATUS, 0, buf, sizeof(*buf));
+	if (ret == sizeof(*buf))
+		status = gud_status_to_errno(*buf);
+	kfree(buf);
+
+	if (ret < 0)
+		return ret;
+
+	return status;
+}
+
+static int gud_usb_transfer(struct gud_device *gdrm, bool in, u8 request, u16 index,
+			    void *buf, size_t len)
+{
+	struct usb_interface *intf = to_usb_interface(gdrm->drm.dev);
+	int idx, ret;
+
+	drm_dbg(&gdrm->drm, "%s: request=0x%x index=%u len=%zu\n",
+		in ? "get" : "set", request, index, len);
+
+	if (!drm_dev_enter(&gdrm->drm, &idx))
+		return -ENODEV;
+
+	mutex_lock(&gdrm->ctrl_lock);
+
+	ret = gud_usb_control_msg(intf, in, request, index, buf, len);
+	if (ret == -EPIPE || ((gdrm->flags & GUD_DISPLAY_FLAG_STATUS_ON_SET) && !in && ret >= 0)) {
+		int status;
+
+		status = gud_usb_get_status(intf);
+		if (status < 0) {
+			ret = status;
+		} else if (ret < 0) {
+			dev_err_once(gdrm->drm.dev,
+				     "Unexpected status OK for failed transfer\n");
+			ret = -EPIPE;
+		}
+	}
+
+	if (ret < 0) {
+		drm_dbg(&gdrm->drm, "ret=%d\n", ret);
+		gdrm->stats_num_errors++;
+	}
+
+	mutex_unlock(&gdrm->ctrl_lock);
+	drm_dev_exit(idx);
+
+	return ret;
+}
+
+/*
+ * @buf cannot be allocated on the stack.
+ * Returns number of bytes received or negative error code on failure.
+ */
+int gud_usb_get(struct gud_device *gdrm, u8 request, u16 index, void *buf, size_t max_len)
+{
+	return gud_usb_transfer(gdrm, true, request, index, buf, max_len);
+}
+
+/*
+ * @buf can be allocated on the stack or NULL.
+ * Returns zero on success or negative error code on failure.
+ */
+int gud_usb_set(struct gud_device *gdrm, u8 request, u16 index, void *buf, size_t len)
+{
+	void *trbuf = NULL;
+	int ret;
+
+	if (buf && len) {
+		trbuf = kmemdup(buf, len, GFP_KERNEL);
+		if (!trbuf)
+			return -ENOMEM;
+	}
+
+	ret = gud_usb_transfer(gdrm, false, request, index, trbuf, len);
+	kfree(trbuf);
+	if (ret < 0)
+		return ret;
+
+	return ret != len ? -EIO : 0;
+}
+
+/*
+ * @val can be allocated on the stack.
+ * Returns zero on success or negative error code on failure.
+ */
+int gud_usb_get_u8(struct gud_device *gdrm, u8 request, u16 index, u8 *val)
+{
+	u8 *buf;
+	int ret;
+
+	buf = kmalloc(sizeof(*val), GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	ret = gud_usb_get(gdrm, request, index, buf, sizeof(*val));
+	*val = *buf;
+	kfree(buf);
+	if (ret < 0)
+		return ret;
+
+	return ret != sizeof(*val) ? -EIO : 0;
+}
+
+/* Returns zero on success or negative error code on failure. */
+int gud_usb_set_u8(struct gud_device *gdrm, u8 request, u8 val)
+{
+	return gud_usb_set(gdrm, request, 0, &val, sizeof(val));
+}
+
+static int gud_get_properties(struct gud_device *gdrm)
+{
+	struct gud_property_req *properties;
+	unsigned int i, num_properties;
+	int ret;
+
+	properties = kcalloc(GUD_PROPERTIES_MAX_NUM, sizeof(*properties), GFP_KERNEL);
+	if (!properties)
+		return -ENOMEM;
+
+	ret = gud_usb_get(gdrm, GUD_REQ_GET_PROPERTIES, 0,
+			  properties, GUD_PROPERTIES_MAX_NUM * sizeof(*properties));
+	if (ret <= 0)
+		goto out;
+	if (ret % sizeof(*properties)) {
+		ret = -EIO;
+		goto out;
+	}
+
+	num_properties = ret / sizeof(*properties);
+	ret = 0;
+
+	gdrm->properties = drmm_kcalloc(&gdrm->drm, num_properties, sizeof(*gdrm->properties),
+					GFP_KERNEL);
+	if (!gdrm->properties) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	for (i = 0; i < num_properties; i++) {
+		u16 prop = le16_to_cpu(properties[i].prop);
+		u64 val = le64_to_cpu(properties[i].val);
+
+		switch (prop) {
+		case GUD_PROPERTY_ROTATION:
+			/*
+			 * DRM UAPI matches the protocol so use the value directly,
+			 * but mask out any additions on future devices.
+			 */
+			val &= GUD_ROTATION_MASK;
+			ret = drm_plane_create_rotation_property(&gdrm->pipe.plane,
+								 DRM_MODE_ROTATE_0, val);
+			break;
+		default:
+			/* New ones might show up in future devices, skip those we don't know. */
+			drm_dbg(&gdrm->drm, "Ignoring unknown property: %u\n", prop);
+			continue;
+		}
+
+		if (ret)
+			goto out;
+
+		gdrm->properties[gdrm->num_properties++] = prop;
+	}
+out:
+	kfree(properties);
+
+	return ret;
+}
+
+static struct drm_gem_object *gud_gem_create_object(struct drm_device *dev, size_t size)
+{
+	struct drm_gem_shmem_object *shmem;
+
+	shmem = kzalloc(sizeof(*shmem), GFP_KERNEL);
+	if (!shmem)
+		return NULL;
+
+	shmem->map_cached = true;
+
+	return &shmem->base;
+}
+
+/*
+ * FIXME: Dma-buf sharing requires DMA support by the importing device.
+ *        This function is a workaround to make USB devices work as well.
+ *        See todo.rst for how to fix the issue in the dma-buf framework.
+ */
+static struct drm_gem_object *gud_gem_prime_import(struct drm_device *drm, struct dma_buf *dma_buf)
+{
+	struct gud_device *gdrm = to_gud_device(drm);
+
+	if (!gdrm->dmadev)
+		return ERR_PTR(-ENODEV);
+
+	return drm_gem_prime_import_dev(drm, dma_buf, gdrm->dmadev);
+}
+
+static int gud_stats_debugfs(struct seq_file *m, void *data)
+{
+	struct drm_info_node *node = m->private;
+	struct gud_device *gdrm = to_gud_device(node->minor->dev);
+	char buf[10];
+
+	string_get_size(gdrm->bulk_len, 1, STRING_UNITS_2, buf, sizeof(buf));
+	seq_printf(m, "Max buffer size: %s\n", buf);
+	seq_printf(m, "Number of errors:  %u\n", gdrm->stats_num_errors);
+
+	seq_puts(m, "Compression:      ");
+	if (gdrm->compression & GUD_COMPRESSION_LZ4)
+		seq_puts(m, " lz4");
+	if (!gdrm->compression)
+		seq_puts(m, " none");
+	seq_puts(m, "\n");
+
+	if (gdrm->compression) {
+		u64 remainder;
+		u64 ratio = div64_u64_rem(gdrm->stats_length, gdrm->stats_actual_length,
+					  &remainder);
+		u64 ratio_frac = div64_u64(remainder * 10, gdrm->stats_actual_length);
+
+		seq_printf(m, "Compression ratio: %llu.%llu\n", ratio, ratio_frac);
+	}
+
+	return 0;
+}
+
+static const struct drm_info_list gud_debugfs_list[] = {
+	{ "stats", gud_stats_debugfs, 0, NULL },
+};
+
+static void gud_debugfs_init(struct drm_minor *minor)
+{
+	drm_debugfs_create_files(gud_debugfs_list, ARRAY_SIZE(gud_debugfs_list),
+				 minor->debugfs_root, minor);
+}
+
+static const struct drm_simple_display_pipe_funcs gud_pipe_funcs = {
+	.check      = gud_pipe_check,
+	.update	    = gud_pipe_update,
+	.prepare_fb = drm_gem_fb_simple_display_pipe_prepare_fb,
+};
+
+static const struct drm_mode_config_funcs gud_mode_config_funcs = {
+	.fb_create = drm_gem_fb_create_with_dirty,
+	.atomic_check = drm_atomic_helper_check,
+	.atomic_commit = drm_atomic_helper_commit,
+};
+
+static const u64 gud_pipe_modifiers[] = {
+	DRM_FORMAT_MOD_LINEAR,
+	DRM_FORMAT_MOD_INVALID
+};
+
+DEFINE_DRM_GEM_FOPS(gud_fops);
+
+static struct drm_driver gud_drm_driver = {
+	.driver_features	= DRIVER_MODESET | DRIVER_GEM | DRIVER_ATOMIC,
+	.fops			= &gud_fops,
+	DRM_GEM_SHMEM_DRIVER_OPS,
+	.gem_create_object	= gud_gem_create_object,
+	.gem_prime_import	= gud_gem_prime_import,
+	.debugfs_init		= gud_debugfs_init,
+
+	.name			= "gud",
+	.desc			= "Generic USB Display",
+	.date			= "20200422",
+	.major			= 1,
+	.minor			= 0,
+};
+
+static int gud_alloc_bulk_buffer(struct gud_device *gdrm)
+{
+	unsigned int i, num_pages;
+	struct page **pages;
+	void *ptr;
+	int ret;
+
+	gdrm->bulk_buf = vmalloc_32(gdrm->bulk_len);
+	if (!gdrm->bulk_buf)
+		return -ENOMEM;
+
+	num_pages = DIV_ROUND_UP(gdrm->bulk_len, PAGE_SIZE);
+	pages = kmalloc_array(num_pages, sizeof(struct page *), GFP_KERNEL);
+	if (!pages)
+		return -ENOMEM;
+
+	for (i = 0, ptr = gdrm->bulk_buf; i < num_pages; i++, ptr += PAGE_SIZE)
+		pages[i] = vmalloc_to_page(ptr);
+
+	ret = sg_alloc_table_from_pages(&gdrm->bulk_sgt, pages, num_pages,
+					0, gdrm->bulk_len, GFP_KERNEL);
+	kfree(pages);
+
+	return ret;
+}
+
+static void gud_free_buffers_and_mutex(void *data)
+{
+	struct gud_device *gdrm = data;
+
+	vfree(gdrm->compress_buf);
+	gdrm->compress_buf = NULL;
+	sg_free_table(&gdrm->bulk_sgt);
+	vfree(gdrm->bulk_buf);
+	gdrm->bulk_buf = NULL;
+	mutex_destroy(&gdrm->ctrl_lock);
+}
+
+static int gud_probe(struct usb_interface *intf, const struct usb_device_id *id)
+{
+	const struct drm_format_info *xrgb8888_emulation_format = NULL;
+	bool rgb565_supported = false, xrgb8888_supported = false;
+	unsigned int num_formats_dev, num_formats = 0;
+	struct usb_endpoint_descriptor *bulk_out;
+	struct gud_display_descriptor_req desc;
+	struct device *dev = &intf->dev;
+	size_t max_buffer_size = 0;
+	struct gud_device *gdrm;
+	struct drm_device *drm;
+	u8 *formats_dev;
+	u32 *formats;
+	int ret, i;
+
+	ret = usb_find_bulk_out_endpoint(intf->cur_altsetting, &bulk_out);
+	if (ret)
+		return ret;
+
+	ret = gud_get_display_descriptor(intf, &desc);
+	if (ret) {
+		DRM_DEV_DEBUG_DRIVER(dev, "Not a display interface: ret=%d\n", ret);
+		return -ENODEV;
+	}
+
+	if (desc.version > 1) {
+		dev_err(dev, "Protocol version %u is not supported\n", desc.version);
+		return -ENODEV;
+	}
+
+	gdrm = devm_drm_dev_alloc(dev, &gud_drm_driver, struct gud_device, drm);
+	if (IS_ERR(gdrm))
+		return PTR_ERR(gdrm);
+
+	drm = &gdrm->drm;
+	drm->mode_config.funcs = &gud_mode_config_funcs;
+	ret = drmm_mode_config_init(drm);
+	if (ret)
+		return ret;
+
+	gdrm->flags = le32_to_cpu(desc.flags);
+	gdrm->compression = desc.compression & GUD_COMPRESSION_LZ4;
+
+	if (gdrm->flags & GUD_DISPLAY_FLAG_FULL_UPDATE && gdrm->compression)
+		return -EINVAL;
+
+	mutex_init(&gdrm->ctrl_lock);
+	mutex_init(&gdrm->damage_lock);
+	INIT_WORK(&gdrm->work, gud_flush_work);
+	gud_clear_damage(gdrm);
+
+	ret = devm_add_action(dev, gud_free_buffers_and_mutex, gdrm);
+	if (ret)
+		return ret;
+
+	drm->mode_config.min_width = le32_to_cpu(desc.min_width);
+	drm->mode_config.max_width = le32_to_cpu(desc.max_width);
+	drm->mode_config.min_height = le32_to_cpu(desc.min_height);
+	drm->mode_config.max_height = le32_to_cpu(desc.max_height);
+
+	formats_dev = devm_kmalloc(dev, GUD_FORMATS_MAX_NUM, GFP_KERNEL);
+	/* Add room for emulated XRGB8888 */
+	formats = devm_kmalloc_array(dev, GUD_FORMATS_MAX_NUM + 1, sizeof(*formats), GFP_KERNEL);
+	if (!formats_dev || !formats)
+		return -ENOMEM;
+
+	ret = gud_usb_get(gdrm, GUD_REQ_GET_FORMATS, 0, formats_dev, GUD_FORMATS_MAX_NUM);
+	if (ret < 0)
+		return ret;
+
+	num_formats_dev = ret;
+	for (i = 0; i < num_formats_dev; i++) {
+		const struct drm_format_info *info;
+		size_t fmt_buf_size;
+		u32 format;
+
+		format = gud_to_fourcc(formats_dev[i]);
+		if (!format) {
+			drm_dbg(drm, "Unsupported format: 0x%02x\n", formats_dev[i]);
+			continue;
+		}
+
+		if (format == GUD_DRM_FORMAT_R1)
+			info = &gud_drm_format_r1;
+		else if (format == GUD_DRM_FORMAT_XRGB1111)
+			info = &gud_drm_format_xrgb1111;
+		else
+			info = drm_format_info(format);
+
+		switch (format) {
+		case GUD_DRM_FORMAT_R1:
+			fallthrough;
+		case GUD_DRM_FORMAT_XRGB1111:
+			if (!xrgb8888_emulation_format)
+				xrgb8888_emulation_format = info;
+			break;
+		case DRM_FORMAT_RGB565:
+			rgb565_supported = true;
+			if (!xrgb8888_emulation_format)
+				xrgb8888_emulation_format = info;
+			break;
+		case DRM_FORMAT_XRGB8888:
+			xrgb8888_supported = true;
+			break;
+		}
+
+		fmt_buf_size = drm_format_info_min_pitch(info, 0, drm->mode_config.max_width) *
+			       drm->mode_config.max_height;
+		max_buffer_size = max(max_buffer_size, fmt_buf_size);
+
+		if (format == GUD_DRM_FORMAT_R1 || format == GUD_DRM_FORMAT_XRGB1111)
+			continue; /* Internal not for userspace */
+
+		formats[num_formats++] = format;
+	}
+
+	if (!num_formats && !xrgb8888_emulation_format) {
+		dev_err(dev, "No supported pixel formats found\n");
+		return -EINVAL;
+	}
+
+	/* Prefer speed over color depth */
+	if (rgb565_supported)
+		drm->mode_config.preferred_depth = 16;
+
+	if (!xrgb8888_supported && xrgb8888_emulation_format) {
+		gdrm->xrgb8888_emulation_format = xrgb8888_emulation_format;
+		formats[num_formats++] = DRM_FORMAT_XRGB8888;
+	}
+
+	if (desc.max_buffer_size)
+		max_buffer_size = le32_to_cpu(desc.max_buffer_size);
+	/* Prevent a misbehaving device from allocating loads of RAM. 4096x4096@XRGB8888 = 64 MB */
+	if (max_buffer_size > SZ_64M)
+		max_buffer_size = SZ_64M;
+
+	gdrm->bulk_pipe = usb_sndbulkpipe(interface_to_usbdev(intf), usb_endpoint_num(bulk_out));
+	gdrm->bulk_len = max_buffer_size;
+
+	ret = gud_alloc_bulk_buffer(gdrm);
+	if (ret)
+		return ret;
+
+	if (gdrm->compression & GUD_COMPRESSION_LZ4) {
+		gdrm->lz4_comp_mem = devm_kmalloc(dev, LZ4_MEM_COMPRESS, GFP_KERNEL);
+		if (!gdrm->lz4_comp_mem)
+			return -ENOMEM;
+
+		gdrm->compress_buf = vmalloc(gdrm->bulk_len);
+		if (!gdrm->compress_buf)
+			return -ENOMEM;
+	}
+
+	ret = drm_simple_display_pipe_init(drm, &gdrm->pipe, &gud_pipe_funcs,
+					   formats, num_formats,
+					   gud_pipe_modifiers, NULL);
+	if (ret)
+		return ret;
+
+	devm_kfree(dev, formats);
+	devm_kfree(dev, formats_dev);
+
+	ret = gud_get_properties(gdrm);
+	if (ret) {
+		dev_err(dev, "Failed to get properties (error=%d)\n", ret);
+		return ret;
+	}
+
+	drm_plane_enable_fb_damage_clips(&gdrm->pipe.plane);
+
+	ret = gud_get_connectors(gdrm);
+	if (ret) {
+		dev_err(dev, "Failed to get connectors (error=%d)\n", ret);
+		return ret;
+	}
+
+	drm_mode_config_reset(drm);
+
+	usb_set_intfdata(intf, gdrm);
+
+	gdrm->dmadev = usb_intf_get_dma_device(intf);
+	if (!gdrm->dmadev)
+		dev_warn(dev, "buffer sharing not supported");
+
+	ret = drm_dev_register(drm, 0);
+	if (ret) {
+		put_device(gdrm->dmadev);
+		return ret;
+	}
+
+	drm_kms_helper_poll_init(drm);
+
+	drm_fbdev_generic_setup(drm, 0);
+
+	return 0;
+}
+
+static void gud_disconnect(struct usb_interface *interface)
+{
+	struct gud_device *gdrm = usb_get_intfdata(interface);
+	struct drm_device *drm = &gdrm->drm;
+
+	drm_dbg(drm, "%s:\n", __func__);
+
+	drm_kms_helper_poll_fini(drm);
+	drm_dev_unplug(drm);
+	drm_atomic_helper_shutdown(drm);
+	put_device(gdrm->dmadev);
+	gdrm->dmadev = NULL;
+}
+
+static int gud_suspend(struct usb_interface *intf, pm_message_t message)
+{
+	struct gud_device *gdrm = usb_get_intfdata(intf);
+
+	return drm_mode_config_helper_suspend(&gdrm->drm);
+}
+
+static int gud_resume(struct usb_interface *intf)
+{
+	struct gud_device *gdrm = usb_get_intfdata(intf);
+
+	drm_mode_config_helper_resume(&gdrm->drm);
+
+	return 0;
+}
+
+static const struct usb_device_id gud_id_table[] = {
+	{ USB_DEVICE_INTERFACE_CLASS(0x1d50, 0x614d, USB_CLASS_VENDOR_SPEC) },
+	{ USB_DEVICE_INTERFACE_CLASS(0x16d0, 0x10a9, USB_CLASS_VENDOR_SPEC) },
+	{ }
+};
+
+MODULE_DEVICE_TABLE(usb, gud_id_table);
+
+static struct usb_driver gud_usb_driver = {
+	.name		= "gud",
+	.probe		= gud_probe,
+	.disconnect	= gud_disconnect,
+	.id_table	= gud_id_table,
+	.suspend	= gud_suspend,
+	.resume		= gud_resume,
+	.reset_resume	= gud_resume,
+};
+
+module_usb_driver(gud_usb_driver);
+
+MODULE_AUTHOR("Noralf Trønnes");
+MODULE_LICENSE("Dual MIT/GPL");
diff -Nur --no-dereference linux-5.10.52-orig/drivers/gpu/drm/gud/gud_internal.h linux-5.10.52-v7l+/drivers/gpu/drm/gud/gud_internal.h
--- linux-5.10.52-orig/drivers/gpu/drm/gud/gud_internal.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/gpu/drm/gud/gud_internal.h	2021-07-25 16:46:00.078389595 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/* SPDX-License-Identifier: MIT */
+
+#ifndef __LINUX_GUD_INTERNAL_H
+#define __LINUX_GUD_INTERNAL_H
+
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/scatterlist.h>
+#include <linux/usb.h>
+#include <linux/workqueue.h>
+#include <uapi/drm/drm_fourcc.h>
+
+#include <drm/drm_modes.h>
+#include <drm/drm_simple_kms_helper.h>
+
+struct gud_device {
+	struct drm_device drm;
+	struct drm_simple_display_pipe pipe;
+	struct device *dmadev;
+	struct work_struct work;
+	u32 flags;
+	const struct drm_format_info *xrgb8888_emulation_format;
+
+	u16 *properties;
+	unsigned int num_properties;
+
+	unsigned int bulk_pipe;
+	void *bulk_buf;
+	size_t bulk_len;
+	struct sg_table bulk_sgt;
+
+	u8 compression;
+	void *lz4_comp_mem;
+	void *compress_buf;
+
+	u64 stats_length;
+	u64 stats_actual_length;
+	unsigned int stats_num_errors;
+
+	struct mutex ctrl_lock; /* Serialize get/set and status transfers */
+
+	struct mutex damage_lock; /* Protects the following members: */
+	struct drm_framebuffer *fb;
+	struct drm_rect damage;
+	bool prev_flush_failed;
+};
+
+static inline struct gud_device *to_gud_device(struct drm_device *drm)
+{
+	return container_of(drm, struct gud_device, drm);
+}
+
+static inline struct usb_device *gud_to_usb_device(struct gud_device *gdrm)
+{
+	return interface_to_usbdev(to_usb_interface(gdrm->drm.dev));
+}
+
+int gud_usb_get(struct gud_device *gdrm, u8 request, u16 index, void *buf, size_t len);
+int gud_usb_set(struct gud_device *gdrm, u8 request, u16 index, void *buf, size_t len);
+int gud_usb_get_u8(struct gud_device *gdrm, u8 request, u16 index, u8 *val);
+int gud_usb_set_u8(struct gud_device *gdrm, u8 request, u8 val);
+
+void gud_clear_damage(struct gud_device *gdrm);
+void gud_flush_work(struct work_struct *work);
+int gud_pipe_check(struct drm_simple_display_pipe *pipe,
+		   struct drm_plane_state *new_plane_state,
+		   struct drm_crtc_state *new_crtc_state);
+void gud_pipe_update(struct drm_simple_display_pipe *pipe,
+		     struct drm_plane_state *old_state);
+int gud_connector_fill_properties(struct drm_connector_state *connector_state,
+				  struct gud_property_req *properties);
+int gud_get_connectors(struct gud_device *gdrm);
+
+/* Driver internal fourcc transfer formats */
+#define GUD_DRM_FORMAT_R1		0x00000122
+#define GUD_DRM_FORMAT_XRGB1111		0x03121722
+
+static inline u8 gud_from_fourcc(u32 fourcc)
+{
+	switch (fourcc) {
+	case GUD_DRM_FORMAT_R1:
+		return GUD_PIXEL_FORMAT_R1;
+	case GUD_DRM_FORMAT_XRGB1111:
+		return GUD_PIXEL_FORMAT_XRGB1111;
+	case DRM_FORMAT_RGB565:
+		return GUD_PIXEL_FORMAT_RGB565;
+	case DRM_FORMAT_XRGB8888:
+		return GUD_PIXEL_FORMAT_XRGB8888;
+	case DRM_FORMAT_ARGB8888:
+		return GUD_PIXEL_FORMAT_ARGB8888;
+	}
+
+	return 0;
+}
+
+static inline u32 gud_to_fourcc(u8 format)
+{
+	switch (format) {
+	case GUD_PIXEL_FORMAT_R1:
+		return GUD_DRM_FORMAT_R1;
+	case GUD_PIXEL_FORMAT_XRGB1111:
+		return GUD_DRM_FORMAT_XRGB1111;
+	case GUD_PIXEL_FORMAT_RGB565:
+		return DRM_FORMAT_RGB565;
+	case GUD_PIXEL_FORMAT_XRGB8888:
+		return DRM_FORMAT_XRGB8888;
+	case GUD_PIXEL_FORMAT_ARGB8888:
+		return DRM_FORMAT_ARGB8888;
+	}
+
+	return 0;
+}
+
+static inline void gud_from_display_mode(struct gud_display_mode_req *dst,
+					 const struct drm_display_mode *src)
+{
+	u32 flags = src->flags & GUD_DISPLAY_MODE_FLAG_USER_MASK;
+
+	if (src->type & DRM_MODE_TYPE_PREFERRED)
+		flags |= GUD_DISPLAY_MODE_FLAG_PREFERRED;
+
+	dst->clock = cpu_to_le32(src->clock);
+	dst->hdisplay = cpu_to_le16(src->hdisplay);
+	dst->hsync_start = cpu_to_le16(src->hsync_start);
+	dst->hsync_end = cpu_to_le16(src->hsync_end);
+	dst->htotal = cpu_to_le16(src->htotal);
+	dst->vdisplay = cpu_to_le16(src->vdisplay);
+	dst->vsync_start = cpu_to_le16(src->vsync_start);
+	dst->vsync_end = cpu_to_le16(src->vsync_end);
+	dst->vtotal = cpu_to_le16(src->vtotal);
+	dst->flags = cpu_to_le32(flags);
+}
+
+static inline void gud_to_display_mode(struct drm_display_mode *dst,
+				       const struct gud_display_mode_req *src)
+{
+	u32 flags = le32_to_cpu(src->flags);
+
+	memset(dst, 0, sizeof(*dst));
+	dst->clock = le32_to_cpu(src->clock);
+	dst->hdisplay = le16_to_cpu(src->hdisplay);
+	dst->hsync_start = le16_to_cpu(src->hsync_start);
+	dst->hsync_end = le16_to_cpu(src->hsync_end);
+	dst->htotal = le16_to_cpu(src->htotal);
+	dst->vdisplay = le16_to_cpu(src->vdisplay);
+	dst->vsync_start = le16_to_cpu(src->vsync_start);
+	dst->vsync_end = le16_to_cpu(src->vsync_end);
+	dst->vtotal = le16_to_cpu(src->vtotal);
+	dst->flags = flags & GUD_DISPLAY_MODE_FLAG_USER_MASK;
+	dst->type = DRM_MODE_TYPE_DRIVER;
+	if (flags & GUD_DISPLAY_MODE_FLAG_PREFERRED)
+		dst->type |= DRM_MODE_TYPE_PREFERRED;
+	drm_mode_set_name(dst);
+}
+
+#endif
diff -Nur --no-dereference linux-5.10.52-orig/drivers/gpu/drm/gud/gud_pipe.c linux-5.10.52-v7l+/drivers/gpu/drm/gud/gud_pipe.c
--- linux-5.10.52-orig/drivers/gpu/drm/gud/gud_pipe.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/gpu/drm/gud/gud_pipe.c	2021-07-25 16:46:00.078389595 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright 2020 Noralf Trønnes
+ */
+
+#include <linux/dma-buf.h>
+#include <linux/lz4.h>
+#include <linux/usb.h>
+#include <linux/workqueue.h>
+
+#include <drm/drm_atomic.h>
+#include <drm/drm_connector.h>
+#include <drm/drm_damage_helper.h>
+#include <drm/drm_drv.h>
+#include <drm/drm_format_helper.h>
+#include <drm/drm_fourcc.h>
+#include <drm/drm_framebuffer.h>
+#include <drm/drm_gem_shmem_helper.h>
+#include <drm/drm_print.h>
+#include <drm/drm_rect.h>
+#include <drm/drm_simple_kms_helper.h>
+#include <drm/gud.h>
+
+#include "gud_internal.h"
+
+/*
+ * Some userspace rendering loops runs all displays in the same loop.
+ * This means that a fast display will have to wait for a slow one.
+ * For this reason gud does flushing asynchronous by default.
+ * The down side is that in e.g. a single display setup userspace thinks
+ * the display is insanely fast since the driver reports back immediately
+ * that the flush/pageflip is done. This wastes CPU and power.
+ * Such users might want to set this module parameter to false.
+ */
+static bool gud_async_flush = true;
+module_param_named(async_flush, gud_async_flush, bool, 0644);
+MODULE_PARM_DESC(async_flush, "Enable asynchronous flushing [default=true]");
+
+/*
+ * FIXME: The driver is probably broken on Big Endian machines.
+ * See discussion:
+ * https://lore.kernel.org/dri-devel/CAKb7UvihLX0hgBOP3VBG7O+atwZcUVCPVuBdfmDMpg0NjXe-cQ@mail.gmail.com/
+ */
+
+static bool gud_is_big_endian(void)
+{
+#if defined(__BIG_ENDIAN)
+	return true;
+#else
+	return false;
+#endif
+}
+
+static size_t gud_xrgb8888_to_r124(u8 *dst, const struct drm_format_info *format,
+				   void *src, struct drm_framebuffer *fb,
+				   struct drm_rect *rect)
+{
+	unsigned int block_width = drm_format_info_block_width(format, 0);
+	unsigned int bits_per_pixel = 8 / block_width;
+	unsigned int x, y, width, height;
+	u8 pix, *pix8, *block = dst; /* Assign to silence compiler warning */
+	size_t len;
+	void *buf;
+
+	WARN_ON_ONCE(format->char_per_block[0] != 1);
+
+	/* Start on a byte boundary */
+	rect->x1 = ALIGN_DOWN(rect->x1, block_width);
+	width = drm_rect_width(rect);
+	height = drm_rect_height(rect);
+	len = drm_format_info_min_pitch(format, 0, width) * height;
+
+	buf = kmalloc(width * height, GFP_KERNEL);
+	if (!buf)
+		return 0;
+
+	drm_fb_xrgb8888_to_gray8(buf, src, fb, rect);
+	pix8 = buf;
+
+	for (y = 0; y < height; y++) {
+		for (x = 0; x < width; x++) {
+			unsigned int pixpos = x % block_width; /* within byte from the left */
+			unsigned int pixshift = (block_width - pixpos - 1) * bits_per_pixel;
+
+			if (!pixpos) {
+				block = dst++;
+				*block = 0;
+			}
+
+			pix = (*pix8++) >> (8 - bits_per_pixel);
+			*block |= pix << pixshift;
+		}
+	}
+
+	kfree(buf);
+
+	return len;
+}
+
+static size_t gud_xrgb8888_to_color(u8 *dst, const struct drm_format_info *format,
+				    void *src, struct drm_framebuffer *fb,
+				    struct drm_rect *rect)
+{
+	unsigned int block_width = drm_format_info_block_width(format, 0);
+	unsigned int bits_per_pixel = 8 / block_width;
+	u8 r, g, b, pix, *block = dst; /* Assign to silence compiler warning */
+	unsigned int x, y, width;
+	u32 *pix32;
+	size_t len;
+
+	/* Start on a byte boundary */
+	rect->x1 = ALIGN_DOWN(rect->x1, block_width);
+	width = drm_rect_width(rect);
+	len = drm_format_info_min_pitch(format, 0, width) * drm_rect_height(rect);
+
+	for (y = rect->y1; y < rect->y2; y++) {
+		pix32 = src + (y * fb->pitches[0]);
+		pix32 += rect->x1;
+
+		for (x = 0; x < width; x++) {
+			unsigned int pixpos = x % block_width; /* within byte from the left */
+			unsigned int pixshift = (block_width - pixpos - 1) * bits_per_pixel;
+
+			if (!pixpos) {
+				block = dst++;
+				*block = 0;
+			}
+
+			r = *pix32 >> 16;
+			g = *pix32 >> 8;
+			b = *pix32++;
+
+			switch (format->format) {
+			case GUD_DRM_FORMAT_XRGB1111:
+				pix = ((r >> 7) << 2) | ((g >> 7) << 1) | (b >> 7);
+				break;
+			default:
+				WARN_ON_ONCE(1);
+				return len;
+			}
+
+			*block |= pix << pixshift;
+		}
+	}
+
+	return len;
+}
+
+static int gud_prep_flush(struct gud_device *gdrm, struct drm_framebuffer *fb,
+			  const struct drm_format_info *format, struct drm_rect *rect,
+			  struct gud_set_buffer_req *req)
+{
+	struct dma_buf_attachment *import_attach = fb->obj[0]->import_attach;
+	u8 compression = gdrm->compression;
+	void *vmap, *vaddr, *buf;
+	size_t pitch, len;
+	int ret = 0;
+
+	pitch = drm_format_info_min_pitch(format, 0, drm_rect_width(rect));
+	len = pitch * drm_rect_height(rect);
+	if (len > gdrm->bulk_len)
+		return -E2BIG;
+
+	vmap = drm_gem_shmem_vmap(fb->obj[0]);
+	if (!vmap)
+		return -ENOMEM;
+
+	vaddr = vmap + fb->offsets[0];
+
+	if (import_attach) {
+		ret = dma_buf_begin_cpu_access(import_attach->dmabuf, DMA_FROM_DEVICE);
+		if (ret)
+			goto vunmap;
+	}
+retry:
+	if (compression)
+		buf = gdrm->compress_buf;
+	else
+		buf = gdrm->bulk_buf;
+
+	/*
+	 * Imported buffers are assumed to be write-combined and thus uncached
+	 * with slow reads (at least on ARM).
+	 */
+	if (format != fb->format) {
+		if (format->format == GUD_DRM_FORMAT_R1) {
+			len = gud_xrgb8888_to_r124(buf, format, vaddr, fb, rect);
+			if (!len) {
+				ret = -ENOMEM;
+				goto end_cpu_access;
+			}
+		} else if (format->format == DRM_FORMAT_RGB565) {
+			drm_fb_xrgb8888_to_rgb565(buf, vaddr, fb, rect, gud_is_big_endian());
+		} else {
+			len = gud_xrgb8888_to_color(buf, format, vaddr, fb, rect);
+		}
+	} else if (gud_is_big_endian() && format->cpp[0] > 1) {
+		drm_fb_swab(buf, vaddr, fb, rect, !import_attach);
+	} else if (compression && !import_attach && pitch == fb->pitches[0]) {
+		/* can compress directly from the framebuffer */
+		buf = vaddr + rect->y1 * pitch;
+	} else {
+		drm_fb_memcpy(buf, vaddr, fb, rect);
+	}
+
+	memset(req, 0, sizeof(*req));
+	req->x = cpu_to_le32(rect->x1);
+	req->y = cpu_to_le32(rect->y1);
+	req->width = cpu_to_le32(drm_rect_width(rect));
+	req->height = cpu_to_le32(drm_rect_height(rect));
+	req->length = cpu_to_le32(len);
+
+	if (compression & GUD_COMPRESSION_LZ4) {
+		int complen;
+
+		complen = LZ4_compress_default(buf, gdrm->bulk_buf, len, len, gdrm->lz4_comp_mem);
+		if (complen <= 0) {
+			compression = 0;
+			goto retry;
+		}
+
+		req->compression = GUD_COMPRESSION_LZ4;
+		req->compressed_length = cpu_to_le32(complen);
+	}
+
+end_cpu_access:
+	if (import_attach)
+		dma_buf_end_cpu_access(import_attach->dmabuf, DMA_FROM_DEVICE);
+vunmap:
+	drm_gem_shmem_vunmap(fb->obj[0], vmap);
+
+	return ret;
+}
+
+struct gud_usb_bulk_context {
+	struct timer_list timer;
+	struct usb_sg_request sgr;
+};
+
+static void gud_usb_bulk_timeout(struct timer_list *t)
+{
+	struct gud_usb_bulk_context *ctx = from_timer(ctx, t, timer);
+
+	usb_sg_cancel(&ctx->sgr);
+}
+
+static int gud_usb_bulk(struct gud_device *gdrm, size_t len)
+{
+	struct gud_usb_bulk_context ctx;
+	int ret;
+
+	ret = usb_sg_init(&ctx.sgr, gud_to_usb_device(gdrm), gdrm->bulk_pipe, 0,
+			  gdrm->bulk_sgt.sgl, gdrm->bulk_sgt.nents, len, GFP_KERNEL);
+	if (ret)
+		return ret;
+
+	timer_setup_on_stack(&ctx.timer, gud_usb_bulk_timeout, 0);
+	mod_timer(&ctx.timer, jiffies + msecs_to_jiffies(3000));
+
+	usb_sg_wait(&ctx.sgr);
+
+	if (!del_timer_sync(&ctx.timer))
+		ret = -ETIMEDOUT;
+	else if (ctx.sgr.status < 0)
+		ret = ctx.sgr.status;
+	else if (ctx.sgr.bytes != len)
+		ret = -EIO;
+
+	destroy_timer_on_stack(&ctx.timer);
+
+	return ret;
+}
+
+static int gud_flush_rect(struct gud_device *gdrm, struct drm_framebuffer *fb,
+			  const struct drm_format_info *format, struct drm_rect *rect)
+{
+	struct gud_set_buffer_req req;
+	size_t len, trlen;
+	int ret;
+
+	drm_dbg(&gdrm->drm, "Flushing [FB:%d] " DRM_RECT_FMT "\n", fb->base.id, DRM_RECT_ARG(rect));
+
+	ret = gud_prep_flush(gdrm, fb, format, rect, &req);
+	if (ret)
+		return ret;
+
+	len = le32_to_cpu(req.length);
+
+	if (req.compression)
+		trlen = le32_to_cpu(req.compressed_length);
+	else
+		trlen = len;
+
+	gdrm->stats_length += len;
+	/* Did it wrap around? */
+	if (gdrm->stats_length <= len && gdrm->stats_actual_length) {
+		gdrm->stats_length = len;
+		gdrm->stats_actual_length = 0;
+	}
+	gdrm->stats_actual_length += trlen;
+
+	if (!(gdrm->flags & GUD_DISPLAY_FLAG_FULL_UPDATE) || gdrm->prev_flush_failed) {
+		ret = gud_usb_set(gdrm, GUD_REQ_SET_BUFFER, 0, &req, sizeof(req));
+		if (ret)
+			return ret;
+	}
+
+	ret = gud_usb_bulk(gdrm, trlen);
+	if (ret)
+		gdrm->stats_num_errors++;
+
+	return ret;
+}
+
+void gud_clear_damage(struct gud_device *gdrm)
+{
+	gdrm->damage.x1 = INT_MAX;
+	gdrm->damage.y1 = INT_MAX;
+	gdrm->damage.x2 = 0;
+	gdrm->damage.y2 = 0;
+}
+
+static void gud_add_damage(struct gud_device *gdrm, struct drm_rect *damage)
+{
+	gdrm->damage.x1 = min(gdrm->damage.x1, damage->x1);
+	gdrm->damage.y1 = min(gdrm->damage.y1, damage->y1);
+	gdrm->damage.x2 = max(gdrm->damage.x2, damage->x2);
+	gdrm->damage.y2 = max(gdrm->damage.y2, damage->y2);
+}
+
+static void gud_retry_failed_flush(struct gud_device *gdrm, struct drm_framebuffer *fb,
+				   struct drm_rect *damage)
+{
+	/*
+	 * pipe_update waits for the worker when the display mode is going to change.
+	 * This ensures that the width and height is still the same making it safe to
+	 * add back the damage.
+	 */
+
+	mutex_lock(&gdrm->damage_lock);
+	if (!gdrm->fb) {
+		drm_framebuffer_get(fb);
+		gdrm->fb = fb;
+	}
+	gud_add_damage(gdrm, damage);
+	mutex_unlock(&gdrm->damage_lock);
+
+	/* Retry only once to avoid a possible storm in case of continues errors. */
+	if (!gdrm->prev_flush_failed)
+		queue_work(system_long_wq, &gdrm->work);
+	gdrm->prev_flush_failed = true;
+}
+
+void gud_flush_work(struct work_struct *work)
+{
+	struct gud_device *gdrm = container_of(work, struct gud_device, work);
+	const struct drm_format_info *format;
+	struct drm_framebuffer *fb;
+	struct drm_rect damage;
+	unsigned int i, lines;
+	int idx, ret = 0;
+	size_t pitch;
+
+	if (!drm_dev_enter(&gdrm->drm, &idx))
+		return;
+
+	mutex_lock(&gdrm->damage_lock);
+	fb = gdrm->fb;
+	gdrm->fb = NULL;
+	damage = gdrm->damage;
+	gud_clear_damage(gdrm);
+	mutex_unlock(&gdrm->damage_lock);
+
+	if (!fb)
+		goto out;
+
+	format = fb->format;
+	if (format->format == DRM_FORMAT_XRGB8888 && gdrm->xrgb8888_emulation_format)
+		format = gdrm->xrgb8888_emulation_format;
+
+	/* Split update if it's too big */
+	pitch = drm_format_info_min_pitch(format, 0, drm_rect_width(&damage));
+	lines = drm_rect_height(&damage);
+
+	if (gdrm->bulk_len < lines * pitch)
+		lines = gdrm->bulk_len / pitch;
+
+	for (i = 0; i < DIV_ROUND_UP(drm_rect_height(&damage), lines); i++) {
+		struct drm_rect rect = damage;
+
+		rect.y1 += i * lines;
+		rect.y2 = min_t(u32, rect.y1 + lines, damage.y2);
+
+		ret = gud_flush_rect(gdrm, fb, format, &rect);
+		if (ret) {
+			if (ret != -ENODEV && ret != -ECONNRESET &&
+			    ret != -ESHUTDOWN && ret != -EPROTO) {
+				bool prev_flush_failed = gdrm->prev_flush_failed;
+
+				gud_retry_failed_flush(gdrm, fb, &damage);
+				if (!prev_flush_failed)
+					dev_err_ratelimited(fb->dev->dev,
+							    "Failed to flush framebuffer: error=%d\n", ret);
+			}
+			break;
+		}
+
+		gdrm->prev_flush_failed = false;
+	}
+
+	drm_framebuffer_put(fb);
+out:
+	drm_dev_exit(idx);
+}
+
+static void gud_fb_queue_damage(struct gud_device *gdrm, struct drm_framebuffer *fb,
+				struct drm_rect *damage)
+{
+	struct drm_framebuffer *old_fb = NULL;
+
+	mutex_lock(&gdrm->damage_lock);
+
+	if (fb != gdrm->fb) {
+		old_fb = gdrm->fb;
+		drm_framebuffer_get(fb);
+		gdrm->fb = fb;
+	}
+
+	gud_add_damage(gdrm, damage);
+
+	mutex_unlock(&gdrm->damage_lock);
+
+	queue_work(system_long_wq, &gdrm->work);
+
+	if (old_fb)
+		drm_framebuffer_put(old_fb);
+}
+
+int gud_pipe_check(struct drm_simple_display_pipe *pipe,
+		   struct drm_plane_state *new_plane_state,
+		   struct drm_crtc_state *new_crtc_state)
+{
+	struct gud_device *gdrm = to_gud_device(pipe->crtc.dev);
+	struct drm_plane_state *old_plane_state = pipe->plane.state;
+	const struct drm_display_mode *mode = &new_crtc_state->mode;
+	struct drm_atomic_state *state = new_plane_state->state;
+	struct drm_framebuffer *old_fb = old_plane_state->fb;
+	struct drm_connector_state *connector_state = NULL;
+	struct drm_framebuffer *fb = new_plane_state->fb;
+	const struct drm_format_info *format = fb->format;
+	struct drm_connector *connector;
+	unsigned int i, num_properties;
+	struct gud_state_req *req;
+	int idx, ret;
+	size_t len;
+
+	if (WARN_ON_ONCE(!fb))
+		return -EINVAL;
+
+	if (old_plane_state->rotation != new_plane_state->rotation)
+		new_crtc_state->mode_changed = true;
+
+	if (old_fb && old_fb->format != format)
+		new_crtc_state->mode_changed = true;
+
+	if (!new_crtc_state->mode_changed && !new_crtc_state->connectors_changed)
+		return 0;
+
+	/* Only one connector is supported */
+	if (hweight32(new_crtc_state->connector_mask) != 1)
+		return -EINVAL;
+
+	if (format->format == DRM_FORMAT_XRGB8888 && gdrm->xrgb8888_emulation_format)
+		format = gdrm->xrgb8888_emulation_format;
+
+	for_each_new_connector_in_state(state, connector, connector_state, i) {
+		if (connector_state->crtc)
+			break;
+	}
+
+	/*
+	 * DRM_IOCTL_MODE_OBJ_SETPROPERTY on the rotation property will not have
+	 * the connector included in the state.
+	 */
+	if (!connector_state) {
+		struct drm_connector_list_iter conn_iter;
+
+		drm_connector_list_iter_begin(pipe->crtc.dev, &conn_iter);
+		drm_for_each_connector_iter(connector, &conn_iter) {
+			if (connector->state->crtc) {
+				connector_state = connector->state;
+				break;
+			}
+		}
+		drm_connector_list_iter_end(&conn_iter);
+	}
+
+	if (WARN_ON_ONCE(!connector_state))
+		return -ENOENT;
+
+	len = struct_size(req, properties,
+			  GUD_PROPERTIES_MAX_NUM + GUD_CONNECTOR_PROPERTIES_MAX_NUM);
+	req = kzalloc(len, GFP_KERNEL);
+	if (!req)
+		return -ENOMEM;
+
+	gud_from_display_mode(&req->mode, mode);
+
+	req->format = gud_from_fourcc(format->format);
+	if (WARN_ON_ONCE(!req->format)) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	req->connector = drm_connector_index(connector_state->connector);
+
+	ret = gud_connector_fill_properties(connector_state, req->properties);
+	if (ret < 0)
+		goto out;
+
+	num_properties = ret;
+	for (i = 0; i < gdrm->num_properties; i++) {
+		u16 prop = gdrm->properties[i];
+		u64 val;
+
+		switch (prop) {
+		case GUD_PROPERTY_ROTATION:
+			/* DRM UAPI matches the protocol so use value directly */
+			val = new_plane_state->rotation;
+			break;
+		default:
+			WARN_ON_ONCE(1);
+			ret = -EINVAL;
+			goto out;
+		}
+
+		req->properties[num_properties + i].prop = cpu_to_le16(prop);
+		req->properties[num_properties + i].val = cpu_to_le64(val);
+		num_properties++;
+	}
+
+	if (drm_dev_enter(fb->dev, &idx)) {
+		len = struct_size(req, properties, num_properties);
+		ret = gud_usb_set(gdrm, GUD_REQ_SET_STATE_CHECK, 0, req, len);
+		drm_dev_exit(idx);
+	}  else {
+		ret = -ENODEV;
+	}
+out:
+	kfree(req);
+
+	return ret;
+}
+
+void gud_pipe_update(struct drm_simple_display_pipe *pipe,
+		     struct drm_plane_state *old_state)
+{
+	struct drm_device *drm = pipe->crtc.dev;
+	struct gud_device *gdrm = to_gud_device(drm);
+	struct drm_plane_state *state = pipe->plane.state;
+	struct drm_framebuffer *fb = state->fb;
+	struct drm_crtc *crtc = &pipe->crtc;
+	struct drm_rect damage;
+	int idx;
+
+	if (crtc->state->mode_changed || !crtc->state->enable) {
+		cancel_work_sync(&gdrm->work);
+		mutex_lock(&gdrm->damage_lock);
+		if (gdrm->fb) {
+			drm_framebuffer_put(gdrm->fb);
+			gdrm->fb = NULL;
+		}
+		gud_clear_damage(gdrm);
+		mutex_unlock(&gdrm->damage_lock);
+	}
+
+	if (!drm_dev_enter(drm, &idx))
+		return;
+
+	if (!old_state->fb)
+		gud_usb_set_u8(gdrm, GUD_REQ_SET_CONTROLLER_ENABLE, 1);
+
+	if (fb && (crtc->state->mode_changed || crtc->state->connectors_changed))
+		gud_usb_set(gdrm, GUD_REQ_SET_STATE_COMMIT, 0, NULL, 0);
+
+	if (crtc->state->active_changed)
+		gud_usb_set_u8(gdrm, GUD_REQ_SET_DISPLAY_ENABLE, crtc->state->active);
+
+	if (drm_atomic_helper_damage_merged(old_state, state, &damage)) {
+		if (gdrm->flags & GUD_DISPLAY_FLAG_FULL_UPDATE)
+			drm_rect_init(&damage, 0, 0, fb->width, fb->height);
+		gud_fb_queue_damage(gdrm, fb, &damage);
+		if (!gud_async_flush)
+			flush_work(&gdrm->work);
+	}
+
+	if (!crtc->state->enable)
+		gud_usb_set_u8(gdrm, GUD_REQ_SET_CONTROLLER_ENABLE, 0);
+
+	drm_dev_exit(idx);
+}
diff -Nur --no-dereference linux-5.10.52-orig/drivers/gpu/drm/gud/Kconfig linux-5.10.52-v7l+/drivers/gpu/drm/gud/Kconfig
--- linux-5.10.52-orig/drivers/gpu/drm/gud/Kconfig	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/gpu/drm/gud/Kconfig	2021-07-25 16:46:00.078389595 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+# SPDX-License-Identifier: GPL-2.0
+
+config DRM_GUD
+	tristate "GUD USB Display"
+	depends on DRM && USB
+	select LZ4_COMPRESS
+	select DRM_KMS_HELPER
+	select DRM_GEM_SHMEM_HELPER
+	select BACKLIGHT_CLASS_DEVICE
+	help
+	  This is a DRM display driver for GUD USB Displays or display
+	  adapters.
+
+	  If M is selected the module will be called gud.
diff -Nur --no-dereference linux-5.10.52-orig/drivers/gpu/drm/gud/Makefile linux-5.10.52-v7l+/drivers/gpu/drm/gud/Makefile
--- linux-5.10.52-orig/drivers/gpu/drm/gud/Makefile	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/gpu/drm/gud/Makefile	2021-07-25 16:46:00.078389595 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1 @
+# SPDX-License-Identifier: GPL-2.0
+
+gud-y				:= gud_drv.o gud_pipe.o gud_connector.o
+obj-$(CONFIG_DRM_GUD)		+= gud.o
diff -Nur --no-dereference linux-5.10.52-orig/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.c linux-5.10.52-v7l+/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.c
--- linux-5.10.52-orig/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.c	2021-07-25 16:46:00.078389595 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:175 @
 }
 
 static void hibmc_crtc_atomic_enable(struct drm_crtc *crtc,
-				     struct drm_crtc_state *old_state)
+				     struct drm_atomic_state *state)
 {
 	unsigned int reg;
 	struct hibmc_drm_private *priv = crtc->dev->dev_private;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:194 @
 }
 
 static void hibmc_crtc_atomic_disable(struct drm_crtc *crtc,
-				      struct drm_crtc_state *old_state)
+				      struct drm_atomic_state *state)
 {
 	unsigned int reg;
 	struct hibmc_drm_private *priv = crtc->dev->dev_private;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:396 @
 }
 
 static void hibmc_crtc_atomic_begin(struct drm_crtc *crtc,
-				    struct drm_crtc_state *old_state)
+				    struct drm_atomic_state *state)
 {
 	unsigned int reg;
 	struct drm_device *dev = crtc->dev;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:416 @
 }
 
 static void hibmc_crtc_atomic_flush(struct drm_crtc *crtc,
-				    struct drm_crtc_state *old_state)
+				    struct drm_atomic_state *state)
 
 {
 	unsigned long flags;
diff -Nur --no-dereference linux-5.10.52-orig/drivers/gpu/drm/hisilicon/kirin/kirin_drm_ade.c linux-5.10.52-v7l+/drivers/gpu/drm/hisilicon/kirin/kirin_drm_ade.c
--- linux-5.10.52-orig/drivers/gpu/drm/hisilicon/kirin/kirin_drm_ade.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/gpu/drm/hisilicon/kirin/kirin_drm_ade.c	2021-07-25 16:46:00.078389595 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:439 @
 #endif
 
 static void ade_crtc_atomic_enable(struct drm_crtc *crtc,
-				   struct drm_crtc_state *old_state)
+				   struct drm_atomic_state *state)
 {
 	struct kirin_crtc *kcrtc = to_kirin_crtc(crtc);
 	struct ade_hw_ctx *ctx = kcrtc->hw_ctx;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:462 @
 }
 
 static void ade_crtc_atomic_disable(struct drm_crtc *crtc,
-				    struct drm_crtc_state *old_state)
+				    struct drm_atomic_state *state)
 {
 	struct kirin_crtc *kcrtc = to_kirin_crtc(crtc);
 	struct ade_hw_ctx *ctx = kcrtc->hw_ctx;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:488 @
 }
 
 static void ade_crtc_atomic_begin(struct drm_crtc *crtc,
-				  struct drm_crtc_state *old_state)
+				  struct drm_atomic_state *state)
 {
 	struct kirin_crtc *kcrtc = to_kirin_crtc(crtc);
 	struct ade_hw_ctx *ctx = kcrtc->hw_ctx;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:501 @
 }
 
 static void ade_crtc_atomic_flush(struct drm_crtc *crtc,
-				  struct drm_crtc_state *old_state)
+				  struct drm_atomic_state *state)
 
 {
 	struct kirin_crtc *kcrtc = to_kirin_crtc(crtc);
diff -Nur --no-dereference linux-5.10.52-orig/drivers/gpu/drm/i915/display/intel_atomic.c linux-5.10.52-v7l+/drivers/gpu/drm/i915/display/intel_atomic.c
--- linux-5.10.52-orig/drivers/gpu/drm/i915/display/intel_atomic.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/gpu/drm/i915/display/intel_atomic.c	2021-07-25 16:46:00.088389427 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:112 @
 	return -EINVAL;
 }
 
-static bool blob_equal(const struct drm_property_blob *a,
-		       const struct drm_property_blob *b)
-{
-	if (a && b)
-		return a->length == b->length &&
-			!memcmp(a->data, b->data, a->length);
-
-	return !a == !b;
-}
-
 int intel_digital_connector_atomic_check(struct drm_connector *conn,
 					 struct drm_atomic_state *state)
 {
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:143 @
 	    new_conn_state->base.picture_aspect_ratio != old_conn_state->base.picture_aspect_ratio ||
 	    new_conn_state->base.content_type != old_conn_state->base.content_type ||
 	    new_conn_state->base.scaling_mode != old_conn_state->base.scaling_mode ||
-	    !blob_equal(new_conn_state->base.hdr_output_metadata,
-			old_conn_state->base.hdr_output_metadata))
+	    !drm_connector_atomic_hdr_metadata_equal(old_state, new_state))
 		crtc_state->mode_changed = true;
 
 	return 0;
diff -Nur --no-dereference linux-5.10.52-orig/drivers/gpu/drm/i915/display/intel_connector.c linux-5.10.52-v7l+/drivers/gpu/drm/i915/display/intel_connector.c
--- linux-5.10.52-orig/drivers/gpu/drm/i915/display/intel_connector.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/gpu/drm/i915/display/intel_connector.c	2021-07-25 16:46:00.098389259 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:300 @
 		return;
 	}
 
-	drm_object_attach_property(&connector->base,
-				   connector->colorspace_property, 0);
+	drm_connector_attach_colorspace_property(connector);
 }
diff -Nur --no-dereference linux-5.10.52-orig/drivers/gpu/drm/i915/display/intel_display.c linux-5.10.52-v7l+/drivers/gpu/drm/i915/display/intel_display.c
--- linux-5.10.52-orig/drivers/gpu/drm/i915/display/intel_display.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/gpu/drm/i915/display/intel_display.c	2021-07-25 16:46:00.108389092 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:16565 @
 }
 
 #define INTEL_CRTC_FUNCS \
-	.gamma_set = drm_atomic_helper_legacy_gamma_set, \
 	.set_config = drm_atomic_helper_set_config, \
 	.destroy = intel_crtc_destroy, \
 	.page_flip = drm_atomic_helper_page_flip, \
diff -Nur --no-dereference linux-5.10.52-orig/drivers/gpu/drm/i915/display/intel_dp_mst.c linux-5.10.52-v7l+/drivers/gpu/drm/i915/display/intel_dp_mst.c
--- linux-5.10.52-orig/drivers/gpu/drm/i915/display/intel_dp_mst.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/gpu/drm/i915/display/intel_dp_mst.c	2021-07-25 16:46:00.118388924 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:26 @
  *
  */
 
+#include <drm/drm_atomic.h>
 #include <drm/drm_atomic_helper.h>
 #include <drm/drm_edid.h>
 #include <drm/drm_probe_helper.h>
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:712 @
 }
 
 static struct drm_encoder *intel_mst_atomic_best_encoder(struct drm_connector *connector,
-							 struct drm_connector_state *state)
+							 struct drm_atomic_state *state)
 {
+	struct drm_connector_state *connector_state = drm_atomic_get_new_connector_state(state,
+											 connector);
 	struct intel_connector *intel_connector = to_intel_connector(connector);
 	struct intel_dp *intel_dp = intel_connector->mst_port;
-	struct intel_crtc *crtc = to_intel_crtc(state->crtc);
+	struct intel_crtc *crtc = to_intel_crtc(connector_state->crtc);
 
 	return &intel_dp->mst_encoders[crtc->pipe]->base.base;
 }
diff -Nur --no-dereference linux-5.10.52-orig/drivers/gpu/drm/i915/display/intel_hdmi.c linux-5.10.52-v7l+/drivers/gpu/drm/i915/display/intel_hdmi.c
--- linux-5.10.52-orig/drivers/gpu/drm/i915/display/intel_hdmi.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/gpu/drm/i915/display/intel_hdmi.c	2021-07-25 16:46:00.138388589 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:2974 @
 	drm_connector_attach_content_type_property(connector);
 
 	if (INTEL_GEN(dev_priv) >= 10 || IS_GEMINILAKE(dev_priv))
-		drm_object_attach_property(&connector->base,
-			connector->dev->mode_config.hdr_output_metadata_property, 0);
+		drm_connector_attach_hdr_output_metadata_property(connector);
 
 	if (!HAS_GMCH(dev_priv))
 		drm_connector_attach_max_bpc_property(connector, 8, 12);
diff -Nur --no-dereference linux-5.10.52-orig/drivers/gpu/drm/imx/dcss/dcss-crtc.c linux-5.10.52-v7l+/drivers/gpu/drm/imx/dcss/dcss-crtc.c
--- linux-5.10.52-orig/drivers/gpu/drm/imx/dcss/dcss-crtc.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/gpu/drm/imx/dcss/dcss-crtc.c	2021-07-25 16:46:00.298385906 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:6 @
  * Copyright 2019 NXP.
  */
 
+#include <drm/drm_atomic.h>
 #include <drm/drm_atomic_helper.h>
 #include <drm/drm_vblank.h>
 #include <linux/platform_device.h>
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:56 @
 };
 
 static void dcss_crtc_atomic_begin(struct drm_crtc *crtc,
-				   struct drm_crtc_state *old_crtc_state)
+				   struct drm_atomic_state *state)
 {
 	drm_crtc_vblank_on(crtc);
 }
 
 static void dcss_crtc_atomic_flush(struct drm_crtc *crtc,
-				   struct drm_crtc_state *old_crtc_state)
+				   struct drm_atomic_state *state)
 {
 	struct dcss_crtc *dcss_crtc = container_of(crtc, struct dcss_crtc,
 						   base);
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:81 @
 }
 
 static void dcss_crtc_atomic_enable(struct drm_crtc *crtc,
-				    struct drm_crtc_state *old_crtc_state)
+				    struct drm_atomic_state *state)
 {
+	struct drm_crtc_state *old_crtc_state = drm_atomic_get_old_crtc_state(state,
+									      crtc);
 	struct dcss_crtc *dcss_crtc = container_of(crtc, struct dcss_crtc,
 						   base);
 	struct dcss_dev *dcss = dcss_crtc->base.dev->dev_private;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:117 @
 }
 
 static void dcss_crtc_atomic_disable(struct drm_crtc *crtc,
-				     struct drm_crtc_state *old_crtc_state)
+				     struct drm_atomic_state *state)
 {
+	struct drm_crtc_state *old_crtc_state = drm_atomic_get_old_crtc_state(state,
+									      crtc);
 	struct dcss_crtc *dcss_crtc = container_of(crtc, struct dcss_crtc,
 						   base);
 	struct dcss_dev *dcss = dcss_crtc->base.dev->dev_private;
diff -Nur --no-dereference linux-5.10.52-orig/drivers/gpu/drm/imx/ipuv3-crtc.c linux-5.10.52-v7l+/drivers/gpu/drm/imx/ipuv3-crtc.c
--- linux-5.10.52-orig/drivers/gpu/drm/imx/ipuv3-crtc.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/gpu/drm/imx/ipuv3-crtc.c	2021-07-25 16:46:00.298385906 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:50 @
 }
 
 static void ipu_crtc_atomic_enable(struct drm_crtc *crtc,
-				   struct drm_crtc_state *old_state)
+				   struct drm_atomic_state *state)
 {
 	struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc);
 	struct ipu_soc *ipu = dev_get_drvdata(ipu_crtc->dev->parent);
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:82 @
 }
 
 static void ipu_crtc_atomic_disable(struct drm_crtc *crtc,
-				    struct drm_crtc_state *old_crtc_state)
+				    struct drm_atomic_state *state)
 {
+	struct drm_crtc_state *old_crtc_state = drm_atomic_get_old_crtc_state(state,
+									      crtc);
 	struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc);
 	struct ipu_soc *ipu = dev_get_drvdata(ipu_crtc->dev->parent);
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:230 @
 }
 
 static int ipu_crtc_atomic_check(struct drm_crtc *crtc,
-				 struct drm_crtc_state *state)
+				 struct drm_atomic_state *state)
 {
+	struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state,
+									  crtc);
 	u32 primary_plane_mask = drm_plane_mask(crtc->primary);
 
-	if (state->active && (primary_plane_mask & state->plane_mask) == 0)
+	if (crtc_state->active && (primary_plane_mask & crtc_state->plane_mask) == 0)
 		return -EINVAL;
 
 	return 0;
 }
 
 static void ipu_crtc_atomic_begin(struct drm_crtc *crtc,
-				  struct drm_crtc_state *old_crtc_state)
+				  struct drm_atomic_state *state)
 {
 	drm_crtc_vblank_on(crtc);
 }
 
 static void ipu_crtc_atomic_flush(struct drm_crtc *crtc,
-				  struct drm_crtc_state *old_crtc_state)
+				  struct drm_atomic_state *state)
 {
 	spin_lock_irq(&crtc->dev->event_lock);
 	if (crtc->state->event) {
diff -Nur --no-dereference linux-5.10.52-orig/drivers/gpu/drm/ingenic/ingenic-drm-drv.c linux-5.10.52-v7l+/drivers/gpu/drm/ingenic/ingenic-drm-drv.c
--- linux-5.10.52-orig/drivers/gpu/drm/ingenic/ingenic-drm-drv.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/gpu/drm/ingenic/ingenic-drm-drv.c	2021-07-25 16:46:00.298385906 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:115 @
 }
 
 static void ingenic_drm_crtc_atomic_enable(struct drm_crtc *crtc,
-					   struct drm_crtc_state *state)
+					   struct drm_atomic_state *state)
 {
 	struct ingenic_drm *priv = drm_crtc_get_priv(crtc);
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:129 @
 }
 
 static void ingenic_drm_crtc_atomic_disable(struct drm_crtc *crtc,
-					    struct drm_crtc_state *state)
+					    struct drm_atomic_state *state)
 {
 	struct ingenic_drm *priv = drm_crtc_get_priv(crtc);
 	unsigned int var;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:198 @
 }
 
 static int ingenic_drm_crtc_atomic_check(struct drm_crtc *crtc,
-					 struct drm_crtc_state *state)
+					 struct drm_atomic_state *state)
 {
+	struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state,
+									  crtc);
 	struct ingenic_drm *priv = drm_crtc_get_priv(crtc);
 	struct drm_plane_state *f1_state, *f0_state, *ipu_state = NULL;
 
-	if (drm_atomic_crtc_needs_modeset(state) && priv->soc_info->has_osd) {
-		f1_state = drm_atomic_get_plane_state(state->state, &priv->f1);
+	if (drm_atomic_crtc_needs_modeset(crtc_state) && priv->soc_info->has_osd) {
+		f1_state = drm_atomic_get_plane_state(crtc_state->state,
+						      &priv->f1);
 		if (IS_ERR(f1_state))
 			return PTR_ERR(f1_state);
 
-		f0_state = drm_atomic_get_plane_state(state->state, &priv->f0);
+		f0_state = drm_atomic_get_plane_state(crtc_state->state,
+						      &priv->f0);
 		if (IS_ERR(f0_state))
 			return PTR_ERR(f0_state);
 
 		if (IS_ENABLED(CONFIG_DRM_INGENIC_IPU) && priv->ipu_plane) {
-			ipu_state = drm_atomic_get_plane_state(state->state, priv->ipu_plane);
+			ipu_state = drm_atomic_get_plane_state(crtc_state->state,
+							       priv->ipu_plane);
 			if (IS_ERR(ipu_state))
 				return PTR_ERR(ipu_state);
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:256 @
 }
 
 static void ingenic_drm_crtc_atomic_begin(struct drm_crtc *crtc,
-					  struct drm_crtc_state *oldstate)
+					  struct drm_atomic_state *state)
 {
 	struct ingenic_drm *priv = drm_crtc_get_priv(crtc);
 	u32 ctrl = 0;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:276 @
 }
 
 static void ingenic_drm_crtc_atomic_flush(struct drm_crtc *crtc,
-					  struct drm_crtc_state *oldstate)
+					  struct drm_atomic_state *state)
 {
 	struct ingenic_drm *priv = drm_crtc_get_priv(crtc);
-	struct drm_crtc_state *state = crtc->state;
-	struct drm_pending_vblank_event *event = state->event;
+	struct drm_crtc_state *crtc_state = crtc->state;
+	struct drm_pending_vblank_event *event = crtc_state->event;
 
-	if (drm_atomic_crtc_needs_modeset(state)) {
-		ingenic_drm_crtc_update_timings(priv, &state->mode);
+	if (drm_atomic_crtc_needs_modeset(crtc_state)) {
+		ingenic_drm_crtc_update_timings(priv, &crtc_state->mode);
 
 		clk_set_rate(priv->pix_clk, state->adjusted_mode.clock * 1000);
 	}
 
 	if (event) {
-		state->event = NULL;
+		crtc_state->event = NULL;
 
 		spin_lock_irq(&crtc->dev->event_lock);
 		if (drm_crtc_vblank_get(crtc) == 0)
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:650 @
 
 	.enable_vblank		= ingenic_drm_enable_vblank,
 	.disable_vblank		= ingenic_drm_disable_vblank,
-
-	.gamma_set		= drm_atomic_helper_legacy_gamma_set,
 };
 
 static const struct drm_plane_helper_funcs ingenic_drm_plane_helper_funcs = {
diff -Nur --no-dereference linux-5.10.52-orig/drivers/gpu/drm/Kconfig linux-5.10.52-v7l+/drivers/gpu/drm/Kconfig
--- linux-5.10.52-orig/drivers/gpu/drm/Kconfig	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/gpu/drm/Kconfig	2021-07-25 16:45:47.458601174 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:395 @
 
 source "drivers/gpu/drm/xlnx/Kconfig"
 
+source "drivers/gpu/drm/gud/Kconfig"
+
 # Keep legacy drivers last
 
 menuconfig DRM_LEGACY
diff -Nur --no-dereference linux-5.10.52-orig/drivers/gpu/drm/Makefile linux-5.10.52-v7l+/drivers/gpu/drm/Makefile
--- linux-5.10.52-orig/drivers/gpu/drm/Makefile	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/gpu/drm/Makefile	2021-07-25 16:45:47.458601174 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:127 @
 obj-$(CONFIG_DRM_MCDE) += mcde/
 obj-$(CONFIG_DRM_TIDSS) += tidss/
 obj-y			+= xlnx/
+obj-y			+= gud/
diff -Nur --no-dereference linux-5.10.52-orig/drivers/gpu/drm/mediatek/mtk_drm_crtc.c linux-5.10.52-v7l+/drivers/gpu/drm/mediatek/mtk_drm_crtc.c
--- linux-5.10.52-orig/drivers/gpu/drm/mediatek/mtk_drm_crtc.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/gpu/drm/mediatek/mtk_drm_crtc.c	2021-07-25 16:46:00.318385571 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:520 @
 }
 
 static void mtk_drm_crtc_atomic_enable(struct drm_crtc *crtc,
-				       struct drm_crtc_state *old_state)
+				       struct drm_atomic_state *state)
 {
 	struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(crtc);
 	struct mtk_ddp_comp *comp = mtk_crtc->ddp_comp[0];
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:545 @
 }
 
 static void mtk_drm_crtc_atomic_disable(struct drm_crtc *crtc,
-					struct drm_crtc_state *old_state)
+					struct drm_atomic_state *state)
 {
 	struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(crtc);
 	struct mtk_ddp_comp *comp = mtk_crtc->ddp_comp[0];
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:578 @
 }
 
 static void mtk_drm_crtc_atomic_begin(struct drm_crtc *crtc,
-				      struct drm_crtc_state *old_crtc_state)
+				      struct drm_atomic_state *state)
 {
-	struct mtk_crtc_state *state = to_mtk_crtc_state(crtc->state);
+	struct mtk_crtc_state *crtc_state = to_mtk_crtc_state(crtc->state);
 	struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(crtc);
 
-	if (mtk_crtc->event && state->base.event)
+	if (mtk_crtc->event && crtc_state->base.event)
 		DRM_ERROR("new event while there is still a pending event\n");
 
-	if (state->base.event) {
-		state->base.event->pipe = drm_crtc_index(crtc);
+	if (crtc_state->base.event) {
+		crtc_state->base.event->pipe = drm_crtc_index(crtc);
 		WARN_ON(drm_crtc_vblank_get(crtc) != 0);
-		mtk_crtc->event = state->base.event;
-		state->base.event = NULL;
+		mtk_crtc->event = crtc_state->base.event;
+		crtc_state->base.event = NULL;
 	}
 }
 
 static void mtk_drm_crtc_atomic_flush(struct drm_crtc *crtc,
-				      struct drm_crtc_state *old_crtc_state)
+				      struct drm_atomic_state *state)
 {
 	struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(crtc);
 	int i;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:617 @
 	.reset			= mtk_drm_crtc_reset,
 	.atomic_duplicate_state	= mtk_drm_crtc_duplicate_state,
 	.atomic_destroy_state	= mtk_drm_crtc_destroy_state,
-	.gamma_set		= drm_atomic_helper_legacy_gamma_set,
 	.enable_vblank		= mtk_drm_crtc_enable_vblank,
 	.disable_vblank		= mtk_drm_crtc_disable_vblank,
 };
diff -Nur --no-dereference linux-5.10.52-orig/drivers/gpu/drm/meson/meson_crtc.c linux-5.10.52-v7l+/drivers/gpu/drm/meson/meson_crtc.c
--- linux-5.10.52-orig/drivers/gpu/drm/meson/meson_crtc.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/gpu/drm/meson/meson_crtc.c	2021-07-25 16:46:00.318385571 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:85 @
 };
 
 static void meson_g12a_crtc_atomic_enable(struct drm_crtc *crtc,
-					  struct drm_crtc_state *old_state)
+					  struct drm_atomic_state *state)
 {
 	struct meson_crtc *meson_crtc = to_meson_crtc(crtc);
 	struct drm_crtc_state *crtc_state = crtc->state;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:121 @
 }
 
 static void meson_crtc_atomic_enable(struct drm_crtc *crtc,
-				     struct drm_crtc_state *old_state)
+				     struct drm_atomic_state *state)
 {
 	struct meson_crtc *meson_crtc = to_meson_crtc(crtc);
 	struct drm_crtc_state *crtc_state = crtc->state;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:149 @
 }
 
 static void meson_g12a_crtc_atomic_disable(struct drm_crtc *crtc,
-					   struct drm_crtc_state *old_state)
+					   struct drm_atomic_state *state)
 {
 	struct meson_crtc *meson_crtc = to_meson_crtc(crtc);
 	struct meson_drm *priv = meson_crtc->priv;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:174 @
 }
 
 static void meson_crtc_atomic_disable(struct drm_crtc *crtc,
-				      struct drm_crtc_state *old_state)
+				      struct drm_atomic_state *state)
 {
 	struct meson_crtc *meson_crtc = to_meson_crtc(crtc);
 	struct meson_drm *priv = meson_crtc->priv;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:204 @
 }
 
 static void meson_crtc_atomic_begin(struct drm_crtc *crtc,
-				    struct drm_crtc_state *state)
+				    struct drm_atomic_state *state)
 {
 	struct meson_crtc *meson_crtc = to_meson_crtc(crtc);
 	unsigned long flags;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:220 @
 }
 
 static void meson_crtc_atomic_flush(struct drm_crtc *crtc,
-				    struct drm_crtc_state *old_crtc_state)
+				    struct drm_atomic_state *state)
 {
 	struct meson_crtc *meson_crtc = to_meson_crtc(crtc);
 	struct meson_drm *priv = meson_crtc->priv;
diff -Nur --no-dereference linux-5.10.52-orig/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c linux-5.10.52-v7l+/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c
--- linux-5.10.52-orig/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c	2021-07-25 16:46:00.378384565 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:14 @
 #include <linux/ktime.h>
 #include <linux/bits.h>
 
+#include <drm/drm_atomic.h>
 #include <drm/drm_crtc.h>
 #include <drm/drm_flip_work.h>
 #include <drm/drm_mode.h>
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:489 @
 }
 
 static void dpu_crtc_atomic_begin(struct drm_crtc *crtc,
-		struct drm_crtc_state *old_state)
+		struct drm_atomic_state *state)
 {
 	struct dpu_crtc_state *cstate = to_dpu_crtc_state(crtc->state);
 	struct drm_encoder *encoder;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:530 @
 }
 
 static void dpu_crtc_atomic_flush(struct drm_crtc *crtc,
-		struct drm_crtc_state *old_crtc_state)
+		struct drm_atomic_state *state)
 {
 	struct dpu_crtc *dpu_crtc;
 	struct drm_device *dev;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:710 @
 }
 
 static void dpu_crtc_disable(struct drm_crtc *crtc,
-			     struct drm_crtc_state *old_crtc_state)
+			     struct drm_atomic_state *state)
 {
 	struct dpu_crtc *dpu_crtc = to_dpu_crtc(crtc);
 	struct dpu_crtc_state *cstate = to_dpu_crtc_state(crtc->state);
+	struct drm_crtc_state *old_crtc_state = drm_atomic_get_old_crtc_state(state,
+									      crtc);
 	struct drm_encoder *encoder;
 	unsigned long flags;
 	bool release_bandwidth = false;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:776 @
 }
 
 static void dpu_crtc_enable(struct drm_crtc *crtc,
-		struct drm_crtc_state *old_crtc_state)
+		struct drm_atomic_state *state)
 {
 	struct dpu_crtc *dpu_crtc = to_dpu_crtc(crtc);
 	struct drm_encoder *encoder;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:818 @
 };
 
 static int dpu_crtc_atomic_check(struct drm_crtc *crtc,
-		struct drm_crtc_state *state)
+		struct drm_atomic_state *state)
 {
+	struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state,
+									  crtc);
 	struct dpu_crtc *dpu_crtc = to_dpu_crtc(crtc);
-	struct dpu_crtc_state *cstate = to_dpu_crtc_state(state);
+	struct dpu_crtc_state *cstate = to_dpu_crtc_state(crtc_state);
 	struct plane_state *pstates;
 
 	const struct drm_plane_state *pstate;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:840 @
 
 	pstates = kzalloc(sizeof(*pstates) * DPU_STAGE_MAX * 4, GFP_KERNEL);
 
-	if (!state->enable || !state->active) {
+	if (!crtc_state->enable || !crtc_state->active) {
 		DPU_DEBUG("crtc%d -> enable %d, active %d, skip atomic_check\n",
-				crtc->base.id, state->enable, state->active);
+				crtc->base.id, crtc_state->enable,
+				crtc_state->active);
 		goto end;
 	}
 
-	mode = &state->adjusted_mode;
+	mode = &crtc_state->adjusted_mode;
 	DPU_DEBUG("%s: check", dpu_crtc->name);
 
 	/* force a full mode set if active state changed */
-	if (state->active_changed)
-		state->mode_changed = true;
+	if (crtc_state->active_changed)
+		crtc_state->mode_changed = true;
 
 	memset(pipe_staged, 0, sizeof(pipe_staged));
 
 	if (cstate->num_mixers) {
 		mixer_width = mode->hdisplay / cstate->num_mixers;
 
-		_dpu_crtc_setup_lm_bounds(crtc, state);
+		_dpu_crtc_setup_lm_bounds(crtc, crtc_state);
 	}
 
 	crtc_rect.x2 = mode->hdisplay;
 	crtc_rect.y2 = mode->vdisplay;
 
 	 /* get plane state for all drm planes associated with crtc state */
-	drm_atomic_crtc_state_for_each_plane_state(plane, pstate, state) {
+	drm_atomic_crtc_state_for_each_plane_state(plane, pstate, crtc_state) {
 		struct drm_rect dst, clip = crtc_rect;
 
 		if (IS_ERR_OR_NULL(pstate)) {
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:972 @
 
 	atomic_inc(&_dpu_crtc_get_kms(crtc)->bandwidth_ref);
 
-	rc = dpu_core_perf_crtc_check(crtc, state);
+	rc = dpu_core_perf_crtc_check(crtc, crtc_state);
 	if (rc) {
 		DPU_ERROR("crtc%d failed performance check %d\n",
 				crtc->base.id, rc);
diff -Nur --no-dereference linux-5.10.52-orig/drivers/gpu/drm/msm/disp/mdp4/mdp4_crtc.c linux-5.10.52-v7l+/drivers/gpu/drm/msm/disp/mdp4/mdp4_crtc.c
--- linux-5.10.52-orig/drivers/gpu/drm/msm/disp/mdp4/mdp4_crtc.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/gpu/drm/msm/disp/mdp4/mdp4_crtc.c	2021-07-25 16:46:00.408384062 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:267 @
 }
 
 static void mdp4_crtc_atomic_disable(struct drm_crtc *crtc,
-				     struct drm_crtc_state *old_state)
+				     struct drm_atomic_state *state)
 {
 	struct mdp4_crtc *mdp4_crtc = to_mdp4_crtc(crtc);
 	struct mdp4_kms *mdp4_kms = get_kms(crtc);
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:287 @
 }
 
 static void mdp4_crtc_atomic_enable(struct drm_crtc *crtc,
-				    struct drm_crtc_state *old_state)
+				    struct drm_atomic_state *state)
 {
 	struct mdp4_crtc *mdp4_crtc = to_mdp4_crtc(crtc);
 	struct mdp4_kms *mdp4_kms = get_kms(crtc);
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:310 @
 }
 
 static int mdp4_crtc_atomic_check(struct drm_crtc *crtc,
-		struct drm_crtc_state *state)
+		struct drm_atomic_state *state)
 {
 	struct mdp4_crtc *mdp4_crtc = to_mdp4_crtc(crtc);
 	DBG("%s: check", mdp4_crtc->name);
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:319 @
 }
 
 static void mdp4_crtc_atomic_begin(struct drm_crtc *crtc,
-				   struct drm_crtc_state *old_crtc_state)
+				   struct drm_atomic_state *state)
 {
 	struct mdp4_crtc *mdp4_crtc = to_mdp4_crtc(crtc);
 	DBG("%s: begin", mdp4_crtc->name);
 }
 
 static void mdp4_crtc_atomic_flush(struct drm_crtc *crtc,
-				   struct drm_crtc_state *old_crtc_state)
+				   struct drm_atomic_state *state)
 {
 	struct mdp4_crtc *mdp4_crtc = to_mdp4_crtc(crtc);
 	struct drm_device *dev = crtc->dev;
diff -Nur --no-dereference linux-5.10.52-orig/drivers/gpu/drm/msm/disp/mdp5/mdp5_crtc.c linux-5.10.52-v7l+/drivers/gpu/drm/msm/disp/mdp5/mdp5_crtc.c
--- linux-5.10.52-orig/drivers/gpu/drm/msm/disp/mdp5/mdp5_crtc.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/gpu/drm/msm/disp/mdp5/mdp5_crtc.c	2021-07-25 16:46:00.408384062 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:10 @
 
 #include <linux/sort.h>
 
+#include <drm/drm_atomic.h>
 #include <drm/drm_mode.h>
 #include <drm/drm_crtc.h>
 #include <drm/drm_flip_work.h>
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:487 @
 }
 
 static void mdp5_crtc_atomic_disable(struct drm_crtc *crtc,
-				     struct drm_crtc_state *old_state)
+				     struct drm_atomic_state *state)
 {
 	struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc);
 	struct mdp5_crtc_state *mdp5_cstate = to_mdp5_crtc_state(crtc->state);
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:533 @
 }
 
 static void mdp5_crtc_atomic_enable(struct drm_crtc *crtc,
-				    struct drm_crtc_state *old_state)
+				    struct drm_atomic_state *state)
 {
 	struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc);
 	struct mdp5_crtc_state *mdp5_cstate = to_mdp5_crtc_state(crtc->state);
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:686 @
 }
 
 static int mdp5_crtc_atomic_check(struct drm_crtc *crtc,
-		struct drm_crtc_state *state)
+		struct drm_atomic_state *state)
 {
+	struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state,
+									  crtc);
 	struct mdp5_kms *mdp5_kms = get_kms(crtc);
 	struct drm_plane *plane;
 	struct drm_device *dev = crtc->dev;
 	struct plane_state pstates[STAGE_MAX + 1];
 	const struct mdp5_cfg_hw *hw_cfg;
 	const struct drm_plane_state *pstate;
-	const struct drm_display_mode *mode = &state->adjusted_mode;
+	const struct drm_display_mode *mode = &crtc_state->adjusted_mode;
 	bool cursor_plane = false;
 	bool need_right_mixer = false;
 	int cnt = 0, i;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:705 @
 
 	DBG("%s: check", crtc->name);
 
-	drm_atomic_crtc_state_for_each_plane_state(plane, pstate, state) {
+	drm_atomic_crtc_state_for_each_plane_state(plane, pstate, crtc_state) {
 		if (!pstate->visible)
 			continue;
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:737 @
 	if (mode->hdisplay > hw_cfg->lm.max_width)
 		need_right_mixer = true;
 
-	ret = mdp5_crtc_setup_pipeline(crtc, state, need_right_mixer);
+	ret = mdp5_crtc_setup_pipeline(crtc, crtc_state, need_right_mixer);
 	if (ret) {
 		DRM_DEV_ERROR(dev->dev, "couldn't assign mixers %d\n", ret);
 		return ret;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:750 @
 	WARN_ON(cursor_plane &&
 		(pstates[cnt - 1].plane->type != DRM_PLANE_TYPE_CURSOR));
 
-	start = get_start_stage(crtc, state, &pstates[0].state->base);
+	start = get_start_stage(crtc, crtc_state, &pstates[0].state->base);
 
 	/* verify that there are not too many planes attached to crtc
 	 * and that we don't have conflicting mixer stages:
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:775 @
 }
 
 static void mdp5_crtc_atomic_begin(struct drm_crtc *crtc,
-				   struct drm_crtc_state *old_crtc_state)
+				   struct drm_atomic_state *state)
 {
 	DBG("%s: begin", crtc->name);
 }
 
 static void mdp5_crtc_atomic_flush(struct drm_crtc *crtc,
-				   struct drm_crtc_state *old_crtc_state)
+				   struct drm_atomic_state *state)
 {
 	struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc);
 	struct mdp5_crtc_state *mdp5_cstate = to_mdp5_crtc_state(crtc->state);
diff -Nur --no-dereference linux-5.10.52-orig/drivers/gpu/drm/mxsfb/mxsfb_kms.c linux-5.10.52-v7l+/drivers/gpu/drm/mxsfb/mxsfb_kms.c
--- linux-5.10.52-orig/drivers/gpu/drm/mxsfb/mxsfb_kms.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/gpu/drm/mxsfb/mxsfb_kms.c	2021-07-25 16:46:00.468383056 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:273 @
 }
 
 static int mxsfb_crtc_atomic_check(struct drm_crtc *crtc,
-				   struct drm_crtc_state *state)
+				   struct drm_atomic_state *state)
 {
-	bool has_primary = state->plane_mask &
+	struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state,
+									  crtc);
+	bool has_primary = crtc_state->plane_mask &
 			   drm_plane_mask(crtc->primary);
 
 	/* The primary plane has to be enabled when the CRTC is active. */
-	if (state->active && !has_primary)
+	if (crtc_state->active && !has_primary)
 		return -EINVAL;
 
 	/* TODO: Is this needed ? */
-	return drm_atomic_add_affected_planes(state->state, crtc);
+	return drm_atomic_add_affected_planes(state, crtc);
 }
 
 static void mxsfb_crtc_atomic_flush(struct drm_crtc *crtc,
-				    struct drm_crtc_state *old_state)
+				    struct drm_atomic_state *state)
 {
 	struct drm_pending_vblank_event *event;
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:308 @
 }
 
 static void mxsfb_crtc_atomic_enable(struct drm_crtc *crtc,
-				     struct drm_crtc_state *old_state)
+				     struct drm_atomic_state *state)
 {
 	struct mxsfb_drm_private *mxsfb = to_mxsfb_drm_private(crtc->dev);
 	struct drm_device *drm = mxsfb->drm;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:332 @
 }
 
 static void mxsfb_crtc_atomic_disable(struct drm_crtc *crtc,
-				      struct drm_crtc_state *old_state)
+				      struct drm_atomic_state *state)
 {
 	struct mxsfb_drm_private *mxsfb = to_mxsfb_drm_private(crtc->dev);
 	struct drm_device *drm = mxsfb->drm;
diff -Nur --no-dereference linux-5.10.52-orig/drivers/gpu/drm/nouveau/dispnv50/disp.c linux-5.10.52-v7l+/drivers/gpu/drm/nouveau/dispnv50/disp.c
--- linux-5.10.52-orig/drivers/gpu/drm/nouveau/dispnv50/disp.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/gpu/drm/nouveau/dispnv50/disp.c	2021-07-25 16:46:00.498382553 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:35 @
 #include <linux/hdmi.h>
 #include <linux/component.h>
 
+#include <drm/drm_atomic.h>
 #include <drm/drm_atomic_helper.h>
 #include <drm/drm_dp_helper.h>
 #include <drm/drm_edid.h>
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1165 @
 
 static struct drm_encoder *
 nv50_mstc_atomic_best_encoder(struct drm_connector *connector,
-			      struct drm_connector_state *connector_state)
+			      struct drm_atomic_state *state)
 {
+	struct drm_connector_state *connector_state = drm_atomic_get_new_connector_state(state,
+											 connector);
 	struct nv50_mstc *mstc = nv50_mstc(connector);
 	struct drm_crtc *crtc = connector_state->crtc;
 
diff -Nur --no-dereference linux-5.10.52-orig/drivers/gpu/drm/nouveau/dispnv50/head.c linux-5.10.52-v7l+/drivers/gpu/drm/nouveau/dispnv50/head.c
--- linux-5.10.52-orig/drivers/gpu/drm/nouveau/dispnv50/head.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/gpu/drm/nouveau/dispnv50/head.c	2021-07-25 16:46:00.498382553 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:33 @
 #include <nvif/event.h>
 #include <nvif/cl0046.h>
 
+#include <drm/drm_atomic.h>
 #include <drm/drm_atomic_helper.h>
 #include <drm/drm_crtc_helper.h>
 #include <drm/drm_vblank.h>
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:314 @
 }
 
 static int
-nv50_head_atomic_check(struct drm_crtc *crtc, struct drm_crtc_state *state)
+nv50_head_atomic_check(struct drm_crtc *crtc, struct drm_atomic_state *state)
 {
+	struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state,
+									  crtc);
 	struct nouveau_drm *drm = nouveau_drm(crtc->dev);
 	struct nv50_head *head = nv50_head(crtc);
 	struct nv50_head_atom *armh = nv50_head_atom(crtc->state);
-	struct nv50_head_atom *asyh = nv50_head_atom(state);
+	struct nv50_head_atom *asyh = nv50_head_atom(crtc_state);
 	struct nouveau_conn_atom *asyc = NULL;
 	struct drm_connector_state *conns;
 	struct drm_connector *conn;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:504 @
 static const struct drm_crtc_funcs
 nv50_head_func = {
 	.reset = nv50_head_reset,
-	.gamma_set = drm_atomic_helper_legacy_gamma_set,
 	.destroy = nv50_head_destroy,
 	.set_config = drm_atomic_helper_set_config,
 	.page_flip = drm_atomic_helper_page_flip,
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:518 @
 static const struct drm_crtc_funcs
 nvd9_head_func = {
 	.reset = nv50_head_reset,
-	.gamma_set = drm_atomic_helper_legacy_gamma_set,
 	.destroy = nv50_head_destroy,
 	.set_config = drm_atomic_helper_set_config,
 	.page_flip = drm_atomic_helper_page_flip,
diff -Nur --no-dereference linux-5.10.52-orig/drivers/gpu/drm/omapdrm/omap_crtc.c linux-5.10.52-v7l+/drivers/gpu/drm/omapdrm/omap_crtc.c
--- linux-5.10.52-orig/drivers/gpu/drm/omapdrm/omap_crtc.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/gpu/drm/omapdrm/omap_crtc.c	2021-07-25 16:46:01.408367297 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:439 @
 }
 
 static void omap_crtc_atomic_enable(struct drm_crtc *crtc,
-				    struct drm_crtc_state *old_state)
+				    struct drm_atomic_state *state)
 {
 	struct omap_drm_private *priv = crtc->dev->dev_private;
 	struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:465 @
 }
 
 static void omap_crtc_atomic_disable(struct drm_crtc *crtc,
-				     struct drm_crtc_state *old_state)
+				     struct drm_atomic_state *state)
 {
 	struct omap_drm_private *priv = crtc->dev->dev_private;
 	struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:572 @
 }
 
 static int omap_crtc_atomic_check(struct drm_crtc *crtc,
-				struct drm_crtc_state *state)
+				struct drm_atomic_state *state)
 {
+	struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state,
+									  crtc);
 	struct drm_plane_state *pri_state;
 
-	if (state->color_mgmt_changed && state->gamma_lut) {
-		unsigned int length = state->gamma_lut->length /
+	if (crtc_state->color_mgmt_changed && crtc_state->gamma_lut) {
+		unsigned int length = crtc_state->gamma_lut->length /
 			sizeof(struct drm_color_lut);
 
 		if (length < 2)
 			return -EINVAL;
 	}
 
-	pri_state = drm_atomic_get_new_plane_state(state->state, crtc->primary);
+	pri_state = drm_atomic_get_new_plane_state(state,
+						   crtc->primary);
 	if (pri_state) {
 		struct omap_crtc_state *omap_crtc_state =
-			to_omap_crtc_state(state);
+			to_omap_crtc_state(crtc_state);
 
 		/* Mirror new values for zpos and rotation in omap_crtc_state */
 		omap_crtc_state->zpos = pri_state->zpos;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:604 @
 }
 
 static void omap_crtc_atomic_begin(struct drm_crtc *crtc,
-				   struct drm_crtc_state *old_crtc_state)
+				   struct drm_atomic_state *state)
 {
 }
 
 static void omap_crtc_atomic_flush(struct drm_crtc *crtc,
-				   struct drm_crtc_state *old_crtc_state)
+				   struct drm_atomic_state *state)
 {
 	struct omap_drm_private *priv = crtc->dev->dev_private;
 	struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:744 @
 	.set_config = drm_atomic_helper_set_config,
 	.destroy = omap_crtc_destroy,
 	.page_flip = drm_atomic_helper_page_flip,
-	.gamma_set = drm_atomic_helper_legacy_gamma_set,
 	.atomic_duplicate_state = omap_crtc_duplicate_state,
 	.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
 	.atomic_set_property = omap_crtc_atomic_set_property,
diff -Nur --no-dereference linux-5.10.52-orig/drivers/gpu/drm/panel/panel-jdi-lt070me05000.c linux-5.10.52-v7l+/drivers/gpu/drm/panel/panel-jdi-lt070me05000.c
--- linux-5.10.52-orig/drivers/gpu/drm/panel/panel-jdi-lt070me05000.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/gpu/drm/panel/panel-jdi-lt070me05000.c	2021-07-25 16:46:01.418367129 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:208 @
 	if (ret < 0)
 		dev_err(dev, "regulator disable failed, %d\n", ret);
 
-	gpiod_set_value(jdi->enable_gpio, 0);
+	gpiod_set_value_cansleep(jdi->enable_gpio, 0);
 
-	gpiod_set_value(jdi->reset_gpio, 1);
+	gpiod_set_value_cansleep(jdi->reset_gpio, 1);
 
-	gpiod_set_value(jdi->dcdc_en_gpio, 0);
+	gpiod_set_value_cansleep(jdi->dcdc_en_gpio, 0);
 
 	jdi->prepared = false;
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:236 @
 
 	msleep(20);
 
-	gpiod_set_value(jdi->dcdc_en_gpio, 1);
+	gpiod_set_value_cansleep(jdi->dcdc_en_gpio, 1);
 	usleep_range(10, 20);
 
-	gpiod_set_value(jdi->reset_gpio, 0);
+	gpiod_set_value_cansleep(jdi->reset_gpio, 0);
 	usleep_range(10, 20);
 
-	gpiod_set_value(jdi->enable_gpio, 1);
+	gpiod_set_value_cansleep(jdi->enable_gpio, 1);
 	usleep_range(10, 20);
 
 	ret = jdi_panel_init(jdi);
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:266 @
 	if (ret < 0)
 		dev_err(dev, "regulator disable failed, %d\n", ret);
 
-	gpiod_set_value(jdi->enable_gpio, 0);
+	gpiod_set_value_cansleep(jdi->enable_gpio, 0);
 
-	gpiod_set_value(jdi->reset_gpio, 1);
+	gpiod_set_value_cansleep(jdi->reset_gpio, 1);
 
-	gpiod_set_value(jdi->dcdc_en_gpio, 0);
+	gpiod_set_value_cansleep(jdi->dcdc_en_gpio, 0);
 
 	return ret;
 }
diff -Nur --no-dereference linux-5.10.52-orig/drivers/gpu/drm/panel/panel-raspberrypi-touchscreen.c linux-5.10.52-v7l+/drivers/gpu/drm/panel/panel-raspberrypi-touchscreen.c
--- linux-5.10.52-orig/drivers/gpu/drm/panel/panel-raspberrypi-touchscreen.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/gpu/drm/panel/panel-raspberrypi-touchscreen.c	2021-07-25 16:46:01.428366962 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:222 @
 
 static int rpi_touchscreen_i2c_read(struct rpi_touchscreen *ts, u8 reg)
 {
-	return i2c_smbus_read_byte_data(ts->i2c, reg);
+	struct i2c_client *client = ts->i2c;
+	struct i2c_msg msgs[1];
+	u8 addr_buf[1] = { reg };
+	u8 data_buf[1] = { 0, };
+	int ret;
+
+	/* Write register address */
+	msgs[0].addr = client->addr;
+	msgs[0].flags = 0;
+	msgs[0].len = ARRAY_SIZE(addr_buf);
+	msgs[0].buf = addr_buf;
+
+	ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
+	if (ret != ARRAY_SIZE(msgs))
+		return -EIO;
+
+	usleep_range(100, 300);
+
+	/* Read data from register */
+	msgs[0].addr = client->addr;
+	msgs[0].flags = I2C_M_RD;
+	msgs[0].len = 1;
+	msgs[0].buf = data_buf;
+
+	ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
+	if (ret != ARRAY_SIZE(msgs))
+		return -EIO;
+
+	return data_buf[0];
 }
 
 static void rpi_touchscreen_i2c_write(struct rpi_touchscreen *ts,
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:302 @
 	int i;
 
 	rpi_touchscreen_i2c_write(ts, REG_POWERON, 1);
+	usleep_range(20000, 25000);
 	/* Wait for nPWRDWN to go low to indicate poweron is done. */
 	for (i = 0; i < 100; i++) {
 		if (rpi_touchscreen_i2c_read(ts, REG_PORTB) & 1)
diff -Nur --no-dereference linux-5.10.52-orig/drivers/gpu/drm/panel/panel-simple.c linux-5.10.52-v7l+/drivers/gpu/drm/panel/panel-simple.c
--- linux-5.10.52-orig/drivers/gpu/drm/panel/panel-simple.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/gpu/drm/panel/panel-simple.c	2021-07-25 16:46:01.428366962 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:2096 @
 	.bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE,
 };
 
+static const struct display_timing innolux_at056tn53v1_timing = {
+	.pixelclock = { 39700000, 39700000, 39700000},
+	.hactive = { 640, 640, 640 },
+	.hfront_porch = { 16, 16, 16 },
+	.hback_porch = { 134, 134, 134 },
+	.hsync_len = { 10, 10, 10},
+	.vactive = { 480, 480, 480 },
+	.vfront_porch = { 32, 32, 32},
+	.vback_porch = { 11, 11, 11 },
+	.vsync_len = { 2, 2, 2 },
+	.flags = DRM_MODE_FLAG_PVSYNC | DRM_MODE_FLAG_PHSYNC,
+};
+
+static const struct panel_desc innolux_at056tn53v1 = {
+	.timings = &innolux_at056tn53v1_timing,
+	.num_timings = 1,
+	.bpc = 6,
+	.size = {
+		.width = 112,
+		.height = 84,
+	},
+	.delay = {
+		.prepare = 50,
+		.enable = 200,
+		.disable = 110,
+		.unprepare = 200,
+	},
+	.bus_format = MEDIA_BUS_FMT_BGR666_1X24_CPADHI,
+	.bus_flags = DRM_BUS_FLAG_PIXDATA_SAMPLE_POSEDGE,
+	.connector_type = DRM_MODE_CONNECTOR_DPI,
+};
+
 static const struct drm_display_mode innolux_at070tn92_mode = {
 	.clock = 33333,
 	.hdisplay = 800,
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4111 @
 		.compatible = "innolux,at043tn24",
 		.data = &innolux_at043tn24,
 	}, {
+		.compatible = "innolux,at056tn53v1",
+		.data = &innolux_at056tn53v1,
+	}, {
 		.compatible = "innolux,at070tn92",
 		.data = &innolux_at070tn92,
 	}, {
diff -Nur --no-dereference linux-5.10.52-orig/drivers/gpu/drm/qxl/qxl_display.c linux-5.10.52-v7l+/drivers/gpu/drm/qxl/qxl_display.c
--- linux-5.10.52-orig/drivers/gpu/drm/qxl/qxl_display.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/gpu/drm/qxl/qxl_display.c	2021-07-25 16:46:01.448366626 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:376 @
 }
 
 static void qxl_crtc_atomic_flush(struct drm_crtc *crtc,
-				  struct drm_crtc_state *old_crtc_state)
+				  struct drm_atomic_state *state)
 {
 	qxl_crtc_update_monitors_config(crtc, "flush");
 }
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:448 @
 };
 
 static void qxl_crtc_atomic_enable(struct drm_crtc *crtc,
-				   struct drm_crtc_state *old_state)
+				   struct drm_atomic_state *state)
 {
 	qxl_crtc_update_monitors_config(crtc, "enable");
 }
 
 static void qxl_crtc_atomic_disable(struct drm_crtc *crtc,
-				    struct drm_crtc_state *old_state)
+				    struct drm_atomic_state *state)
 {
 	qxl_crtc_update_monitors_config(crtc, "disable");
 }
diff -Nur --no-dereference linux-5.10.52-orig/drivers/gpu/drm/rcar-du/rcar_du_crtc.c linux-5.10.52-v7l+/drivers/gpu/drm/rcar-du/rcar_du_crtc.c
--- linux-5.10.52-orig/drivers/gpu/drm/rcar-du/rcar_du_crtc.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/gpu/drm/rcar-du/rcar_du_crtc.c	2021-07-25 16:46:01.568364614 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:685 @
  */
 
 static int rcar_du_crtc_atomic_check(struct drm_crtc *crtc,
-				     struct drm_crtc_state *state)
+				     struct drm_atomic_state *state)
 {
-	struct rcar_du_crtc_state *rstate = to_rcar_crtc_state(state);
+	struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state,
+									  crtc);
+	struct rcar_du_crtc_state *rstate = to_rcar_crtc_state(crtc_state);
 	struct drm_encoder *encoder;
 	int ret;
 
-	ret = rcar_du_cmm_check(crtc, state);
+	ret = rcar_du_cmm_check(crtc, crtc_state);
 	if (ret)
 		return ret;
 
 	/* Store the routes from the CRTC output to the DU outputs. */
 	rstate->outputs = 0;
 
-	drm_for_each_encoder_mask(encoder, crtc->dev, state->encoder_mask) {
+	drm_for_each_encoder_mask(encoder, crtc->dev,
+				  crtc_state->encoder_mask) {
 		struct rcar_du_encoder *renc;
 
 		/* Skip the writeback encoder. */
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:716 @
 }
 
 static void rcar_du_crtc_atomic_enable(struct drm_crtc *crtc,
-				       struct drm_crtc_state *old_state)
+				       struct drm_atomic_state *state)
 {
 	struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);
 	struct rcar_du_crtc_state *rstate = to_rcar_crtc_state(crtc->state);
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:751 @
 }
 
 static void rcar_du_crtc_atomic_disable(struct drm_crtc *crtc,
-					struct drm_crtc_state *old_state)
+					struct drm_atomic_state *state)
 {
+	struct drm_crtc_state *old_state = drm_atomic_get_old_crtc_state(state,
+									 crtc);
 	struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);
 	struct rcar_du_crtc_state *rstate = to_rcar_crtc_state(old_state);
 	struct rcar_du_device *rcdu = rcrtc->dev;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:782 @
 }
 
 static void rcar_du_crtc_atomic_begin(struct drm_crtc *crtc,
-				      struct drm_crtc_state *old_crtc_state)
+				      struct drm_atomic_state *state)
 {
 	struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:811 @
 }
 
 static void rcar_du_crtc_atomic_flush(struct drm_crtc *crtc,
-				      struct drm_crtc_state *old_crtc_state)
+				      struct drm_atomic_state *state)
 {
 	struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);
 	struct drm_device *dev = rcrtc->crtc.dev;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1141 @
 	.set_crc_source = rcar_du_crtc_set_crc_source,
 	.verify_crc_source = rcar_du_crtc_verify_crc_source,
 	.get_crc_sources = rcar_du_crtc_get_crc_sources,
-	.gamma_set = drm_atomic_helper_legacy_gamma_set,
 };
 
 /* -----------------------------------------------------------------------------
diff -Nur --no-dereference linux-5.10.52-orig/drivers/gpu/drm/rockchip/rockchip_drm_vop.c linux-5.10.52-v7l+/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
--- linux-5.10.52-orig/drivers/gpu/drm/rockchip/rockchip_drm_vop.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/gpu/drm/rockchip/rockchip_drm_vop.c	2021-07-25 16:46:01.578364447 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:696 @
 }
 
 static void vop_crtc_atomic_disable(struct drm_crtc *crtc,
-				    struct drm_crtc_state *old_state)
+				    struct drm_atomic_state *state)
 {
 	struct vop *vop = to_vop(crtc);
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1250 @
 }
 
 static void vop_crtc_atomic_begin(struct drm_crtc *crtc,
-				  struct drm_crtc_state *old_crtc_state)
+				  struct drm_atomic_state *state)
 {
+	struct drm_crtc_state *old_crtc_state = drm_atomic_get_old_crtc_state(state,
+									      crtc);
 	struct vop *vop = to_vop(crtc);
 
 	/*
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1266 @
 }
 
 static void vop_crtc_atomic_enable(struct drm_crtc *crtc,
-				   struct drm_crtc_state *old_state)
+				   struct drm_atomic_state *state)
 {
+	struct drm_crtc_state *old_state = drm_atomic_get_old_crtc_state(state,
+									 crtc);
 	struct vop *vop = to_vop(crtc);
 	const struct vop_data *vop_data = vop->data;
 	struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc->state);
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1421 @
 }
 
 static int vop_crtc_atomic_check(struct drm_crtc *crtc,
-				 struct drm_crtc_state *crtc_state)
+				 struct drm_atomic_state *state)
 {
+	struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state,
+									  crtc);
 	struct vop *vop = to_vop(crtc);
 	struct drm_plane *plane;
 	struct drm_plane_state *plane_state;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1468 @
 }
 
 static void vop_crtc_atomic_flush(struct drm_crtc *crtc,
-				  struct drm_crtc_state *old_crtc_state)
+				  struct drm_atomic_state *state)
 {
+	struct drm_crtc_state *old_crtc_state = drm_atomic_get_old_crtc_state(state,
+									      crtc);
 	struct drm_atomic_state *old_state = old_crtc_state->state;
 	struct drm_plane_state *old_plane_state, *new_plane_state;
 	struct vop *vop = to_vop(crtc);
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1645 @
 	.disable_vblank = vop_crtc_disable_vblank,
 	.set_crc_source = vop_crtc_set_crc_source,
 	.verify_crc_source = vop_crtc_verify_crc_source,
-	.gamma_set = drm_atomic_helper_legacy_gamma_set,
 };
 
 static void vop_fb_unref_worker(struct drm_flip_work *work, void *val)
diff -Nur --no-dereference linux-5.10.52-orig/drivers/gpu/drm/sti/sti_crtc.c linux-5.10.52-v7l+/drivers/gpu/drm/sti/sti_crtc.c
--- linux-5.10.52-orig/drivers/gpu/drm/sti/sti_crtc.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/gpu/drm/sti/sti_crtc.c	2021-07-25 16:46:01.588364279 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:26 @
 #include "sti_vtg.h"
 
 static void sti_crtc_atomic_enable(struct drm_crtc *crtc,
-				   struct drm_crtc_state *old_state)
+				   struct drm_atomic_state *state)
 {
 	struct sti_mixer *mixer = to_sti_mixer(crtc);
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:38 @
 }
 
 static void sti_crtc_atomic_disable(struct drm_crtc *crtc,
-				    struct drm_crtc_state *old_state)
+				    struct drm_atomic_state *state)
 {
 	struct sti_mixer *mixer = to_sti_mixer(crtc);
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:136 @
 }
 
 static void sti_crtc_atomic_flush(struct drm_crtc *crtc,
-				  struct drm_crtc_state *old_crtc_state)
+				  struct drm_atomic_state *state)
 {
 	struct drm_device *drm_dev = crtc->dev;
 	struct sti_mixer *mixer = to_sti_mixer(crtc);
diff -Nur --no-dereference linux-5.10.52-orig/drivers/gpu/drm/stm/ltdc.c linux-5.10.52-v7l+/drivers/gpu/drm/stm/ltdc.c
--- linux-5.10.52-orig/drivers/gpu/drm/stm/ltdc.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/gpu/drm/stm/ltdc.c	2021-07-25 16:46:01.598364111 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:423 @
 }
 
 static void ltdc_crtc_atomic_enable(struct drm_crtc *crtc,
-				    struct drm_crtc_state *old_state)
+				    struct drm_atomic_state *state)
 {
 	struct ltdc_device *ldev = crtc_to_ltdc(crtc);
 	struct drm_device *ddev = crtc->dev;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:445 @
 }
 
 static void ltdc_crtc_atomic_disable(struct drm_crtc *crtc,
-				     struct drm_crtc_state *old_state)
+				     struct drm_atomic_state *state)
 {
 	struct ltdc_device *ldev = crtc_to_ltdc(crtc);
 	struct drm_device *ddev = crtc->dev;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:628 @
 }
 
 static void ltdc_crtc_atomic_flush(struct drm_crtc *crtc,
-				   struct drm_crtc_state *old_crtc_state)
+				   struct drm_atomic_state *state)
 {
 	struct ltdc_device *ldev = crtc_to_ltdc(crtc);
 	struct drm_device *ddev = crtc->dev;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:745 @
 	.enable_vblank = ltdc_crtc_enable_vblank,
 	.disable_vblank = ltdc_crtc_disable_vblank,
 	.get_vblank_timestamp = drm_crtc_vblank_helper_get_vblank_timestamp,
-	.gamma_set = drm_atomic_helper_legacy_gamma_set,
 };
 
 /*
diff -Nur --no-dereference linux-5.10.52-orig/drivers/gpu/drm/sun4i/sun4i_crtc.c linux-5.10.52-v7l+/drivers/gpu/drm/sun4i/sun4i_crtc.c
--- linux-5.10.52-orig/drivers/gpu/drm/sun4i/sun4i_crtc.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/gpu/drm/sun4i/sun4i_crtc.c	2021-07-25 16:46:01.598364111 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:18 @
 
 #include <video/videomode.h>
 
+#include <drm/drm_atomic.h>
 #include <drm/drm_atomic_helper.h>
 #include <drm/drm_crtc.h>
 #include <drm/drm_modes.h>
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:49 @
 }
 
 static int sun4i_crtc_atomic_check(struct drm_crtc *crtc,
-				    struct drm_crtc_state *state)
+				    struct drm_atomic_state *state)
 {
+	struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state,
+									  crtc);
 	struct sun4i_crtc *scrtc = drm_crtc_to_sun4i_crtc(crtc);
 	struct sunxi_engine *engine = scrtc->engine;
 	int ret = 0;
 
 	if (engine && engine->ops && engine->ops->atomic_check)
-		ret = engine->ops->atomic_check(engine, state);
+		ret = engine->ops->atomic_check(engine, crtc_state);
 
 	return ret;
 }
 
 static void sun4i_crtc_atomic_begin(struct drm_crtc *crtc,
-				    struct drm_crtc_state *old_state)
+				    struct drm_atomic_state *state)
 {
+	struct drm_crtc_state *old_state = drm_atomic_get_old_crtc_state(state,
+									 crtc);
 	struct sun4i_crtc *scrtc = drm_crtc_to_sun4i_crtc(crtc);
 	struct drm_device *dev = crtc->dev;
 	struct sunxi_engine *engine = scrtc->engine;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:87 @
 }
 
 static void sun4i_crtc_atomic_flush(struct drm_crtc *crtc,
-				    struct drm_crtc_state *old_state)
+				    struct drm_atomic_state *state)
 {
 	struct sun4i_crtc *scrtc = drm_crtc_to_sun4i_crtc(crtc);
 	struct drm_pending_vblank_event *event = crtc->state->event;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:109 @
 }
 
 static void sun4i_crtc_atomic_disable(struct drm_crtc *crtc,
-				      struct drm_crtc_state *old_state)
+				      struct drm_atomic_state *state)
 {
 	struct drm_encoder *encoder = sun4i_crtc_get_encoder(crtc);
 	struct sun4i_crtc *scrtc = drm_crtc_to_sun4i_crtc(crtc);
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:130 @
 }
 
 static void sun4i_crtc_atomic_enable(struct drm_crtc *crtc,
-				     struct drm_crtc_state *old_state)
+				     struct drm_atomic_state *state)
 {
 	struct drm_encoder *encoder = sun4i_crtc_get_encoder(crtc);
 	struct sun4i_crtc *scrtc = drm_crtc_to_sun4i_crtc(crtc);
diff -Nur --no-dereference linux-5.10.52-orig/drivers/gpu/drm/tegra/dc.c linux-5.10.52-v7l+/drivers/gpu/drm/tegra/dc.c
--- linux-5.10.52-orig/drivers/gpu/drm/tegra/dc.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/gpu/drm/tegra/dc.c	2021-07-25 16:46:01.628363609 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1751 @
 }
 
 static void tegra_crtc_atomic_disable(struct drm_crtc *crtc,
-				      struct drm_crtc_state *old_state)
+				      struct drm_atomic_state *state)
 {
 	struct tegra_dc *dc = to_tegra_dc(crtc);
 	u32 value;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1808 @
 }
 
 static void tegra_crtc_atomic_enable(struct drm_crtc *crtc,
-				     struct drm_crtc_state *old_state)
+				     struct drm_atomic_state *state)
 {
 	struct drm_display_mode *mode = &crtc->state->adjusted_mode;
-	struct tegra_dc_state *state = to_dc_state(crtc->state);
+	struct tegra_dc_state *crtc_state = to_dc_state(crtc->state);
 	struct tegra_dc *dc = to_tegra_dc(crtc);
 	u32 value;
 	int err;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1891 @
 		tegra_dc_writel(dc, 0, DC_DISP_BORDER_COLOR);
 
 	/* apply PLL and pixel clock changes */
-	tegra_dc_commit_state(dc, state);
+	tegra_dc_commit_state(dc, crtc_state);
 
 	/* program display mode */
 	tegra_dc_set_timings(dc, mode);
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1927 @
 }
 
 static void tegra_crtc_atomic_begin(struct drm_crtc *crtc,
-				    struct drm_crtc_state *old_crtc_state)
+				    struct drm_atomic_state *state)
 {
 	unsigned long flags;
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1946 @
 }
 
 static void tegra_crtc_atomic_flush(struct drm_crtc *crtc,
-				    struct drm_crtc_state *old_crtc_state)
+				    struct drm_atomic_state *state)
 {
-	struct tegra_dc_state *state = to_dc_state(crtc->state);
+	struct tegra_dc_state *crtc_state = to_dc_state(crtc->state);
 	struct tegra_dc *dc = to_tegra_dc(crtc);
 	u32 value;
 
-	value = state->planes << 8 | GENERAL_UPDATE;
+	value = crtc_state->planes << 8 | GENERAL_UPDATE;
 	tegra_dc_writel(dc, value, DC_CMD_STATE_CONTROL);
 	value = tegra_dc_readl(dc, DC_CMD_STATE_CONTROL);
 
-	value = state->planes | GENERAL_ACT_REQ;
+	value = crtc_state->planes | GENERAL_ACT_REQ;
 	tegra_dc_writel(dc, value, DC_CMD_STATE_CONTROL);
 	value = tegra_dc_readl(dc, DC_CMD_STATE_CONTROL);
 }
diff -Nur --no-dereference linux-5.10.52-orig/drivers/gpu/drm/tidss/tidss_crtc.c linux-5.10.52-v7l+/drivers/gpu/drm/tidss/tidss_crtc.c
--- linux-5.10.52-orig/drivers/gpu/drm/tidss/tidss_crtc.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/gpu/drm/tidss/tidss_crtc.c	2021-07-25 16:46:01.648363273 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:88 @
 /* drm_crtc_helper_funcs */
 
 static int tidss_crtc_atomic_check(struct drm_crtc *crtc,
-				   struct drm_crtc_state *state)
+				   struct drm_atomic_state *state)
 {
+	struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state,
+									  crtc);
 	struct drm_device *ddev = crtc->dev;
 	struct tidss_device *tidss = to_tidss(ddev);
 	struct dispc_device *dispc = tidss->dispc;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:102 @
 
 	dev_dbg(ddev->dev, "%s\n", __func__);
 
-	if (!state->enable)
+	if (!crtc_state->enable)
 		return 0;
 
-	mode = &state->adjusted_mode;
+	mode = &crtc_state->adjusted_mode;
 
 	ok = dispc_vp_mode_valid(dispc, hw_videoport, mode);
 	if (ok != MODE_OK) {
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:114 @
 		return -EINVAL;
 	}
 
-	return dispc_vp_bus_check(dispc, hw_videoport, state);
+	return dispc_vp_bus_check(dispc, hw_videoport, crtc_state);
 }
 
 /*
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:166 @
 }
 
 static void tidss_crtc_atomic_flush(struct drm_crtc *crtc,
-				    struct drm_crtc_state *old_crtc_state)
+				    struct drm_atomic_state *state)
 {
+	struct drm_crtc_state *old_crtc_state = drm_atomic_get_old_crtc_state(state,
+									      crtc);
 	struct tidss_crtc *tcrtc = to_tidss_crtc(crtc);
 	struct drm_device *ddev = crtc->dev;
 	struct tidss_device *tidss = to_tidss(ddev);
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:219 @
 }
 
 static void tidss_crtc_atomic_enable(struct drm_crtc *crtc,
-				     struct drm_crtc_state *old_state)
+				     struct drm_atomic_state *state)
 {
+	struct drm_crtc_state *old_state = drm_atomic_get_old_crtc_state(state,
+									 crtc);
 	struct tidss_crtc *tcrtc = to_tidss_crtc(crtc);
 	struct drm_device *ddev = crtc->dev;
 	struct tidss_device *tidss = to_tidss(ddev);
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:264 @
 }
 
 static void tidss_crtc_atomic_disable(struct drm_crtc *crtc,
-				      struct drm_crtc_state *old_state)
+				      struct drm_atomic_state *state)
 {
 	struct tidss_crtc *tcrtc = to_tidss_crtc(crtc);
 	struct drm_device *ddev = crtc->dev;
diff -Nur --no-dereference linux-5.10.52-orig/drivers/gpu/drm/tilcdc/tilcdc_crtc.c linux-5.10.52-v7l+/drivers/gpu/drm/tilcdc/tilcdc_crtc.c
--- linux-5.10.52-orig/drivers/gpu/drm/tilcdc/tilcdc_crtc.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/gpu/drm/tilcdc/tilcdc_crtc.c	2021-07-25 16:46:01.658363106 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:487 @
 }
 
 static void tilcdc_crtc_atomic_enable(struct drm_crtc *crtc,
-				      struct drm_crtc_state *old_state)
+				      struct drm_atomic_state *state)
 {
 	tilcdc_crtc_enable(crtc);
 }
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:544 @
 }
 
 static void tilcdc_crtc_atomic_disable(struct drm_crtc *crtc,
-				       struct drm_crtc_state *old_state)
+				       struct drm_atomic_state *state)
 {
 	tilcdc_crtc_disable(crtc);
 }
 
 static void tilcdc_crtc_atomic_flush(struct drm_crtc *crtc,
-				     struct drm_crtc_state *old_state)
+				     struct drm_atomic_state *state)
 {
 	if (!crtc->state->event)
 		return;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:672 @
 }
 
 static int tilcdc_crtc_atomic_check(struct drm_crtc *crtc,
-				    struct drm_crtc_state *state)
+				    struct drm_atomic_state *state)
 {
+	struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state,
+									  crtc);
 	/* If we are not active we don't care */
-	if (!state->active)
+	if (!crtc_state->active)
 		return 0;
 
-	if (state->state->planes[0].ptr != crtc->primary ||
-	    state->state->planes[0].state == NULL ||
-	    state->state->planes[0].state->crtc != crtc) {
+	if (state->planes[0].ptr != crtc->primary ||
+	    state->planes[0].state == NULL ||
+	    state->planes[0].state->crtc != crtc) {
 		dev_dbg(crtc->dev->dev, "CRTC primary plane must be present");
 		return -EINVAL;
 	}
diff -Nur --no-dereference linux-5.10.52-orig/drivers/gpu/drm/v3d/Kconfig linux-5.10.52-v7l+/drivers/gpu/drm/v3d/Kconfig
--- linux-5.10.52-orig/drivers/gpu/drm/v3d/Kconfig	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/gpu/drm/v3d/Kconfig	2021-07-25 16:46:01.678362770 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
 # SPDX-License-Identifier: GPL-2.0-only
 config DRM_V3D
 	tristate "Broadcom V3D 3.x and newer"
-	depends on ARCH_BCM || ARCH_BCMSTB || COMPILE_TEST
+	depends on ARCH_BCM || ARCH_BCMSTB || ARCH_BCM2835 || COMPILE_TEST
 	depends on DRM
 	depends on COMMON_CLK
 	depends on MMU
diff -Nur --no-dereference linux-5.10.52-orig/drivers/gpu/drm/v3d/v3d_debugfs.c linux-5.10.52-v7l+/drivers/gpu/drm/v3d/v3d_debugfs.c
--- linux-5.10.52-orig/drivers/gpu/drm/v3d/v3d_debugfs.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/gpu/drm/v3d/v3d_debugfs.c	2021-07-25 16:46:01.678362770 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:7 @
 #include <linux/circ_buf.h>
 #include <linux/ctype.h>
 #include <linux/debugfs.h>
-#include <linux/pm_runtime.h>
 #include <linux/seq_file.h>
 
 #include <drm/drm_debugfs.h>
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:132 @
 	struct drm_device *dev = node->minor->dev;
 	struct v3d_dev *v3d = to_v3d_dev(dev);
 	u32 ident0, ident1, ident2, ident3, cores;
-	int ret, core;
+	int core;
 
-	ret = pm_runtime_get_sync(v3d->drm.dev);
-	if (ret < 0)
-		return ret;
 
 	ident0 = V3D_READ(V3D_HUB_IDENT0);
 	ident1 = V3D_READ(V3D_HUB_IDENT1);
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:186 @
 			   (misccfg & V3D_MISCCFG_OVRTMUOUT) != 0);
 	}
 
-	pm_runtime_mark_last_busy(v3d->drm.dev);
-	pm_runtime_put_autosuspend(v3d->drm.dev);
-
 	return 0;
 }
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:213 @
 	uint32_t cycles;
 	int core = 0;
 	int measure_ms = 1000;
-	int ret;
-
-	ret = pm_runtime_get_sync(v3d->drm.dev);
-	if (ret < 0)
-		return ret;
 
 	if (v3d->ver >= 40) {
 		V3D_CORE_WRITE(core, V3D_V4_PCTR_0_SRC_0_3,
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:236 @
 		   cycles / (measure_ms * 1000),
 		   (cycles / (measure_ms * 100)) % 10);
 
-	pm_runtime_mark_last_busy(v3d->drm.dev);
-	pm_runtime_put_autosuspend(v3d->drm.dev);
 
 	return 0;
 }
diff -Nur --no-dereference linux-5.10.52-orig/drivers/gpu/drm/v3d/v3d_drv.c linux-5.10.52-v7l+/drivers/gpu/drm/v3d/v3d_drv.c
--- linux-5.10.52-orig/drivers/gpu/drm/v3d/v3d_drv.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/gpu/drm/v3d/v3d_drv.c	2021-07-25 16:46:01.678362770 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:73 @
 }
 #endif
 
-static const struct dev_pm_ops v3d_v3d_pm_ops = {
+static const struct dev_pm_ops v3d_pm_ops = {
 	SET_RUNTIME_PM_OPS(v3d_runtime_suspend, v3d_runtime_resume, NULL)
 };
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:82 @
 {
 	struct v3d_dev *v3d = to_v3d_dev(dev);
 	struct drm_v3d_get_param *args = data;
-	int ret;
 	static const u32 reg_map[] = {
 		[DRM_V3D_PARAM_V3D_UIFCFG] = V3D_HUB_UIFCFG,
 		[DRM_V3D_PARAM_V3D_HUB_IDENT1] = V3D_HUB_IDENT1,
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:107 @
 		if (args->value != 0)
 			return -EINVAL;
 
-		ret = pm_runtime_get_sync(v3d->drm.dev);
-		if (ret < 0)
-			return ret;
 		if (args->param >= DRM_V3D_PARAM_V3D_CORE0_IDENT0 &&
 		    args->param <= DRM_V3D_PARAM_V3D_CORE0_IDENT2) {
 			args->value = V3D_CORE_READ(0, offset);
 		} else {
 			args->value = V3D_READ(offset);
 		}
-		pm_runtime_mark_last_busy(v3d->drm.dev);
-		pm_runtime_put_autosuspend(v3d->drm.dev);
 		return 0;
 	}
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:224 @
 static const struct of_device_id v3d_of_match[] = {
 	{ .compatible = "brcm,7268-v3d" },
 	{ .compatible = "brcm,7278-v3d" },
+	{ .compatible = "brcm,2711-v3d" },
 	{},
 };
 MODULE_DEVICE_TABLE(of, v3d_of_match);
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:266 @
 		return ret;
 
 	mmu_debug = V3D_READ(V3D_MMU_DEBUG_INFO);
-	dev->coherent_dma_mask =
-		DMA_BIT_MASK(30 + V3D_GET_FIELD(mmu_debug, V3D_MMU_PA_WIDTH));
+	dma_set_mask_and_coherent(dev,
+		DMA_BIT_MASK(30 + V3D_GET_FIELD(mmu_debug, V3D_MMU_PA_WIDTH)));
 	v3d->va_width = 30 + V3D_GET_FIELD(mmu_debug, V3D_MMU_VA_WIDTH);
 
 	ident1 = V3D_READ(V3D_HUB_IDENT1);
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:292 @
 		}
 	}
 
+	v3d->clk = devm_clk_get(dev, NULL);
+	if (IS_ERR_OR_NULL(v3d->clk)) {
+		if (PTR_ERR(v3d->clk) != -EPROBE_DEFER)
+			dev_err(dev, "Failed to get clock (%ld)\n", PTR_ERR(v3d->clk));
+		return PTR_ERR(v3d->clk);
+	}
+	v3d->clk_up_rate = clk_get_rate(v3d->clk);
+	/* For downclocking, drop it to the minimum frequency we can get from
+	 * the CPRMAN clock generator dividing off our parent.  The divider is
+	 * 4 bits, but ask for just higher than that so that rounding doesn't
+	 * make cprman reject our rate.
+	 */
+	v3d->clk_down_rate =
+		(clk_get_rate(clk_get_parent(v3d->clk)) / (1 << 4)) + 10000;
+
 	if (v3d->ver < 41) {
 		ret = map_regs(v3d, &v3d->gca_regs, "gca");
 		if (ret)
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:320 @
 		return -ENOMEM;
 	}
 
-	pm_runtime_use_autosuspend(dev);
-	pm_runtime_set_autosuspend_delay(dev, 50);
-	pm_runtime_enable(dev);
 
 	ret = v3d_gem_init(drm);
 	if (ret)
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:333 @
 	if (ret)
 		goto irq_disable;
 
+	ret = clk_set_rate(v3d->clk, v3d->clk_down_rate);
+	WARN_ON_ONCE(ret != 0);
+
 	return 0;
 
 irq_disable:
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:368 @
 	.driver		= {
 		.name	= "v3d",
 		.of_match_table = v3d_of_match,
+		.pm = &v3d_pm_ops,
 	},
 };
 
diff -Nur --no-dereference linux-5.10.52-orig/drivers/gpu/drm/v3d/v3d_drv.h linux-5.10.52-v7l+/drivers/gpu/drm/v3d/v3d_drv.h
--- linux-5.10.52-orig/drivers/gpu/drm/v3d/v3d_drv.h	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/gpu/drm/v3d/v3d_drv.h	2021-07-25 16:46:01.678362770 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:54 @
 	void __iomem *bridge_regs;
 	void __iomem *gca_regs;
 	struct clk *clk;
+	struct delayed_work clk_down_work;
+	unsigned long clk_up_rate, clk_down_rate;
+	struct mutex clk_lock;
+	u32 clk_refcount;
+	bool clk_up;
+
 	struct reset_control *reset;
 
 	/* Virtual and DMA addresses of the single shared page table. */
diff -Nur --no-dereference linux-5.10.52-orig/drivers/gpu/drm/v3d/v3d_gem.c linux-5.10.52-v7l+/drivers/gpu/drm/v3d/v3d_gem.c
--- linux-5.10.52-orig/drivers/gpu/drm/v3d/v3d_gem.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/gpu/drm/v3d/v3d_gem.c	2021-07-25 16:46:01.678362770 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:7 @
 #include <linux/device.h>
 #include <linux/dma-mapping.h>
 #include <linux/io.h>
+#include <linux/clk.h>
 #include <linux/module.h>
 #include <linux/platform_device.h>
 #include <linux/pm_runtime.h>
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:23 @
 #include "v3d_trace.h"
 
 static void
+v3d_clock_down_work(struct work_struct *work)
+{
+	struct v3d_dev *v3d =
+		container_of(work, struct v3d_dev, clk_down_work.work);
+	int ret;
+
+	ret = clk_set_rate(v3d->clk, v3d->clk_down_rate);
+	v3d->clk_up = false;
+	WARN_ON_ONCE(ret != 0);
+}
+
+static void
+v3d_clock_up_get(struct v3d_dev *v3d)
+{
+	mutex_lock(&v3d->clk_lock);
+	if (v3d->clk_refcount++ == 0) {
+		cancel_delayed_work_sync(&v3d->clk_down_work);
+		if (!v3d->clk_up)  {
+			int ret;
+
+			ret = clk_set_rate(v3d->clk, v3d->clk_up_rate);
+			WARN_ON_ONCE(ret != 0);
+			v3d->clk_up = true;
+		}
+	}
+	mutex_unlock(&v3d->clk_lock);
+}
+
+static void
+v3d_clock_up_put(struct v3d_dev *v3d)
+{
+	mutex_lock(&v3d->clk_lock);
+	if (--v3d->clk_refcount == 0) {
+		schedule_delayed_work(&v3d->clk_down_work,
+				      msecs_to_jiffies(100));
+	}
+	mutex_unlock(&v3d->clk_lock);
+}
+
+
+static void
 v3d_init_core(struct v3d_dev *v3d, int core)
 {
 	/* Set OVRTMUOUT, which means that the texture sampler uniform
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:399 @
 	struct v3d_job *job = container_of(ref, struct v3d_job, refcount);
 	unsigned long index;
 	struct dma_fence *fence;
+	struct v3d_dev *v3d = job->v3d;
 	int i;
 
 	for (i = 0; i < job->bo_count; i++) {
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:416 @
 	dma_fence_put(job->irq_fence);
 	dma_fence_put(job->done_fence);
 
-	pm_runtime_mark_last_busy(job->v3d->drm.dev);
-	pm_runtime_put_autosuspend(job->v3d->drm.dev);
+	v3d_clock_up_put(v3d);
 
 	kfree(job);
 }
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:484 @
 	job->v3d = v3d;
 	job->free = free;
 
-	ret = pm_runtime_get_sync(v3d->drm.dev);
-	if (ret < 0)
-		return ret;
-
 	xa_init_flags(&job->deps, XA_FLAGS_ALLOC);
 
 	ret = drm_syncobj_find_fence(file_priv, in_sync, 0, 0, &in_fence);
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:494 @
 	if (ret)
 		goto fail;
 
+	v3d_clock_up_get(v3d);
 	kref_init(&job->refcount);
 
 	return 0;
 fail:
 	xa_destroy(&job->deps);
-	pm_runtime_put_autosuspend(v3d->drm.dev);
 	return ret;
 }
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:920 @
 	mutex_init(&v3d->sched_lock);
 	mutex_init(&v3d->cache_clean_lock);
 
+	mutex_init(&v3d->clk_lock);
+	INIT_DELAYED_WORK(&v3d->clk_down_work, v3d_clock_down_work);
+
+	/* kick the clock so firmware knows we are using firmware clock interface */
+	v3d_clock_up_get(v3d);
+	v3d_clock_up_put(v3d);
+
 	/* Note: We don't allocate address 0.  Various bits of HW
 	 * treat 0 as special, such as the occlusion query counters
 	 * where 0 means "disabled".
diff -Nur --no-dereference linux-5.10.52-orig/drivers/gpu/drm/v3d/v3d_irq.c linux-5.10.52-v7l+/drivers/gpu/drm/v3d/v3d_irq.c
--- linux-5.10.52-orig/drivers/gpu/drm/v3d/v3d_irq.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/gpu/drm/v3d/v3d_irq.c	2021-07-25 16:46:01.678362770 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:180 @
 			"GMP",
 		};
 		const char *client = "?";
+		static int logged_error;
 
-		V3D_WRITE(V3D_MMU_CTL,
-			  V3D_READ(V3D_MMU_CTL) & (V3D_MMU_CTL_CAP_EXCEEDED |
-						   V3D_MMU_CTL_PT_INVALID |
-						   V3D_MMU_CTL_WRITE_VIOLATION));
+		V3D_WRITE(V3D_MMU_CTL, V3D_READ(V3D_MMU_CTL));
 
 		if (v3d->ver >= 41) {
 			axi_id = axi_id >> 5;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:190 @
 				client = v3d41_axi_ids[axi_id];
 		}
 
+		if (!logged_error)
 		dev_err(v3d->drm.dev, "MMU error from client %s (%d) at 0x%llx%s%s%s\n",
 			client, axi_id, (long long)vio_addr,
 			((intsts & V3D_HUB_INT_MMU_WRV) ?
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:199 @
 			 ", pte invalid" : ""),
 			((intsts & V3D_HUB_INT_MMU_CAP) ?
 			 ", cap exceeded" : ""));
+		logged_error = 1;
 		status = IRQ_HANDLED;
 	}
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:220 @
 		V3D_CORE_WRITE(core, V3D_CTL_INT_CLR, V3D_CORE_IRQS);
 	V3D_WRITE(V3D_HUB_INT_CLR, V3D_HUB_IRQS);
 
-	irq1 = platform_get_irq(v3d_to_pdev(v3d), 1);
+	irq1 = platform_get_irq_optional(v3d_to_pdev(v3d), 1);
 	if (irq1 == -EPROBE_DEFER)
 		return irq1;
 	if (irq1 > 0) {
diff -Nur --no-dereference linux-5.10.52-orig/drivers/gpu/drm/v3d/v3d_mmu.c linux-5.10.52-v7l+/drivers/gpu/drm/v3d/v3d_mmu.c
--- linux-5.10.52-orig/drivers/gpu/drm/v3d/v3d_mmu.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/gpu/drm/v3d/v3d_mmu.c	2021-07-25 16:46:01.678362770 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:21 @
  * each client.  This is not yet implemented.
  */
 
+#include <linux/pm_runtime.h>
+
 #include "v3d_drv.h"
 #include "v3d_regs.h"
 
diff -Nur --no-dereference linux-5.10.52-orig/drivers/gpu/drm/vboxvideo/vbox_mode.c linux-5.10.52-v7l+/drivers/gpu/drm/vboxvideo/vbox_mode.c
--- linux-5.10.52-orig/drivers/gpu/drm/vboxvideo/vbox_mode.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/gpu/drm/vboxvideo/vbox_mode.c	2021-07-25 16:46:01.688362603 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:216 @
 }
 
 static void vbox_crtc_atomic_enable(struct drm_crtc *crtc,
-				    struct drm_crtc_state *old_crtc_state)
+				    struct drm_atomic_state *state)
 {
 }
 
 static void vbox_crtc_atomic_disable(struct drm_crtc *crtc,
-				     struct drm_crtc_state *old_crtc_state)
+				     struct drm_atomic_state *state)
 {
 }
 
 static void vbox_crtc_atomic_flush(struct drm_crtc *crtc,
-				   struct drm_crtc_state *old_crtc_state)
+				   struct drm_atomic_state *state)
 {
 }
 
diff -Nur --no-dereference linux-5.10.52-orig/drivers/gpu/drm/vc4/Kconfig linux-5.10.52-v7l+/drivers/gpu/drm/vc4/Kconfig
--- linux-5.10.52-orig/drivers/gpu/drm/vc4/Kconfig	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/gpu/drm/vc4/Kconfig	2021-07-25 16:46:01.688362603 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:15 @
 	select SND_PCM
 	select SND_PCM_ELD
 	select SND_SOC_GENERIC_DMAENGINE_PCM
+	select SND_SOC_HDMI_CODEC
 	select DRM_MIPI_DSI
 	help
 	  Choose this option if you have a system that has a Broadcom
diff -Nur --no-dereference linux-5.10.52-orig/drivers/gpu/drm/vc4/Makefile linux-5.10.52-v7l+/drivers/gpu/drm/vc4/Makefile
--- linux-5.10.52-orig/drivers/gpu/drm/vc4/Makefile	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/gpu/drm/vc4/Makefile	2021-07-25 16:46:01.688362603 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:12 @
 	vc4_dpi.o \
 	vc4_dsi.o \
 	vc4_fence.o \
+	vc4_firmware_kms.o \
 	vc4_kms.o \
 	vc4_gem.o \
 	vc4_hdmi.o \
diff -Nur --no-dereference linux-5.10.52-orig/drivers/gpu/drm/vc4/vc4_crtc.c linux-5.10.52-v7l+/drivers/gpu/drm/vc4/vc4_crtc.c
--- linux-5.10.52-orig/drivers/gpu/drm/vc4/vc4_crtc.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/gpu/drm/vc4/vc4_crtc.c	2021-07-25 16:46:01.698362435 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:282 @
  * allows drivers to push pixels to more than one encoder from the
  * same CRTC.
  */
-static struct drm_encoder *vc4_get_crtc_encoder(struct drm_crtc *crtc)
+struct drm_encoder *vc4_get_crtc_encoder(struct drm_crtc *crtc,
+					 struct drm_crtc_state *state)
 {
-	struct drm_connector *connector;
-	struct drm_connector_list_iter conn_iter;
+	struct drm_encoder *encoder;
 
-	drm_connector_list_iter_begin(crtc->dev, &conn_iter);
-	drm_for_each_connector_iter(connector, &conn_iter) {
-		if (connector->state->crtc == crtc) {
-			drm_connector_list_iter_end(&conn_iter);
-			return connector->encoder;
-		}
-	}
-	drm_connector_list_iter_end(&conn_iter);
+	WARN_ON(hweight32(state->encoder_mask) > 1);
+
+	drm_for_each_encoder_mask(encoder, crtc->dev, state->encoder_mask)
+		return encoder;
 
 	return NULL;
 }
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:304 @
 	CRTC_WRITE(PV_CONTROL, CRTC_READ(PV_CONTROL) | PV_CONTROL_FIFO_CLR);
 }
 
-static void vc4_crtc_config_pv(struct drm_crtc *crtc)
+static void vc4_crtc_config_pv(struct drm_crtc *crtc, struct drm_encoder *encoder,
+			       struct drm_atomic_state *state)
 {
 	struct drm_device *dev = crtc->dev;
 	struct vc4_dev *vc4 = to_vc4_dev(dev);
-	struct drm_encoder *encoder = vc4_get_crtc_encoder(crtc);
 	struct vc4_encoder *vc4_encoder = to_vc4_encoder(encoder);
 	struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
 	const struct vc4_pv_data *pv_data = vc4_crtc_to_vc4_pv_data(vc4_crtc);
-	struct drm_crtc_state *state = crtc->state;
-	struct drm_display_mode *mode = &state->adjusted_mode;
+	struct drm_crtc_state *crtc_state = crtc->state;
+	struct drm_display_mode *mode = &crtc_state->adjusted_mode;
 	bool interlace = mode->flags & DRM_MODE_FLAG_INTERLACE;
 	u32 pixel_rep = (mode->flags & DRM_MODE_FLAG_DBLCLK) ? 2 : 1;
 	bool is_dsi = (vc4_encoder->type == VC4_ENCODER_TYPE_DSI0 ||
 		       vc4_encoder->type == VC4_ENCODER_TYPE_DSI1);
-	u32 format = is_dsi ? PV_CONTROL_FORMAT_DSIV_24 : PV_CONTROL_FORMAT_24;
+	bool is_dsi1 = vc4_encoder->type == VC4_ENCODER_TYPE_DSI1;
+	bool is_vec = vc4_encoder->type == VC4_ENCODER_TYPE_VEC;
+	u32 format = is_dsi1 ? PV_CONTROL_FORMAT_DSIV_24 : PV_CONTROL_FORMAT_24;
 	u8 ppc = pv_data->pixels_per_clock;
+
+	u16 vert_bp = mode->crtc_vtotal - mode->crtc_vsync_end;
+	u16 vert_sync = mode->crtc_vsync_end - mode->crtc_vsync_start;
+	u16 vert_fp = mode->crtc_vsync_start - mode->crtc_vdisplay;
+
 	bool debug_dump_regs = false;
 
 	if (debug_dump_regs) {
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:350 @
 		   VC4_SET_FIELD(mode->hdisplay * pixel_rep / ppc,
 				 PV_HORZB_HACTIVE));
 
-	CRTC_WRITE(PV_VERTA,
-		   VC4_SET_FIELD(mode->crtc_vtotal - mode->crtc_vsync_end,
-				 PV_VERTA_VBP) |
-		   VC4_SET_FIELD(mode->crtc_vsync_end - mode->crtc_vsync_start,
-				 PV_VERTA_VSYNC));
-	CRTC_WRITE(PV_VERTB,
-		   VC4_SET_FIELD(mode->crtc_vsync_start - mode->crtc_vdisplay,
-				 PV_VERTB_VFP) |
-		   VC4_SET_FIELD(mode->crtc_vdisplay, PV_VERTB_VACTIVE));
-
 	if (interlace) {
+		bool odd_field_first = false;
+		u32 field_delay = mode->htotal * pixel_rep / (2 * ppc);
+		u16 vert_bp_even = vert_bp;
+		u16 vert_fp_even = vert_fp;
+
+		if (is_vec) {
+			/* VEC (composite output) */
+			++field_delay;
+			if (mode->htotal == 858) {
+				/* 525-line mode (NTSC or PAL-M) */
+				odd_field_first = true;
+			}
+		}
+
+		if (odd_field_first)
+			++vert_fp_even;
+		else
+			++vert_bp;
+
 		CRTC_WRITE(PV_VERTA_EVEN,
-			   VC4_SET_FIELD(mode->crtc_vtotal -
-					 mode->crtc_vsync_end - 1,
-					 PV_VERTA_VBP) |
-			   VC4_SET_FIELD(mode->crtc_vsync_end -
-					 mode->crtc_vsync_start,
-					 PV_VERTA_VSYNC));
+			   VC4_SET_FIELD(vert_bp_even, PV_VERTA_VBP) |
+			   VC4_SET_FIELD(vert_sync, PV_VERTA_VSYNC));
 		CRTC_WRITE(PV_VERTB_EVEN,
-			   VC4_SET_FIELD(mode->crtc_vsync_start -
-					 mode->crtc_vdisplay,
-					 PV_VERTB_VFP) |
+			   VC4_SET_FIELD(vert_fp_even, PV_VERTB_VFP) |
 			   VC4_SET_FIELD(mode->crtc_vdisplay, PV_VERTB_VACTIVE));
 
-		/* We set up first field even mode for HDMI.  VEC's
-		 * NTSC mode would want first field odd instead, once
-		 * we support it (to do so, set ODD_FIRST and put the
-		 * delay in VSYNCD_EVEN instead).
+		/* We set up first field even mode for HDMI and VEC's PAL.
+		 * For NTSC, we need first field odd.
 		 */
 		CRTC_WRITE(PV_V_CONTROL,
 			   PV_VCONTROL_CONTINUOUS |
 			   (is_dsi ? PV_VCONTROL_DSI : 0) |
 			   PV_VCONTROL_INTERLACE |
-			   VC4_SET_FIELD(mode->htotal * pixel_rep / 2,
-					 PV_VCONTROL_ODD_DELAY));
-		CRTC_WRITE(PV_VSYNCD_EVEN, 0);
+			   (odd_field_first
+				   ? PV_VCONTROL_ODD_FIRST
+				   : VC4_SET_FIELD(field_delay,
+						   PV_VCONTROL_ODD_DELAY)));
+		CRTC_WRITE(PV_VSYNCD_EVEN,
+			   (odd_field_first ? field_delay : 0));
 	} else {
 		CRTC_WRITE(PV_V_CONTROL,
 			   PV_VCONTROL_CONTINUOUS |
 			   (is_dsi ? PV_VCONTROL_DSI : 0));
 	}
 
+	CRTC_WRITE(PV_VERTA,
+		   VC4_SET_FIELD(vert_bp, PV_VERTA_VBP) |
+		   VC4_SET_FIELD(vert_sync, PV_VERTA_VSYNC));
+	CRTC_WRITE(PV_VERTB,
+		   VC4_SET_FIELD(vert_fp, PV_VERTB_VFP) |
+		   VC4_SET_FIELD(mode->crtc_vdisplay, PV_VERTB_VACTIVE));
+
 	if (is_dsi)
 		CRTC_WRITE(PV_HACT_ACT, mode->hdisplay * pixel_rep);
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:437 @
 		     SCALER_DISPCTRL_ENABLE);
 }
 
-static int vc4_crtc_disable(struct drm_crtc *crtc, unsigned int channel)
+static int vc4_crtc_disable(struct drm_crtc *crtc,
+			    struct drm_encoder *encoder,
+			    struct drm_atomic_state *state,
+			    unsigned int channel)
 {
-	struct drm_encoder *encoder = vc4_get_crtc_encoder(crtc);
 	struct vc4_encoder *vc4_encoder = to_vc4_encoder(encoder);
 	struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
 	struct drm_device *dev = crtc->dev;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:471 @
 	mdelay(20);
 
 	if (vc4_encoder && vc4_encoder->post_crtc_disable)
-		vc4_encoder->post_crtc_disable(encoder);
+		vc4_encoder->post_crtc_disable(encoder, state);
 
 	vc4_crtc_pixelvalve_reset(crtc);
 	vc4_hvs_stop_channel(dev, channel);
 
 	if (vc4_encoder && vc4_encoder->post_crtc_powerdown)
-		vc4_encoder->post_crtc_powerdown(encoder);
+		vc4_encoder->post_crtc_powerdown(encoder, state);
 
 	return 0;
 }
 
+static struct drm_encoder *vc4_crtc_get_encoder_by_type(struct drm_crtc *crtc,
+							enum vc4_encoder_type type)
+{
+	struct drm_encoder *encoder;
+
+	drm_for_each_encoder(encoder, crtc->dev) {
+		struct vc4_encoder *vc4_encoder = to_vc4_encoder(encoder);
+
+		if (vc4_encoder->type == type)
+			return encoder;
+	}
+
+	return NULL;
+}
+
 int vc4_crtc_disable_at_boot(struct drm_crtc *crtc)
 {
 	struct drm_device *drm = crtc->dev;
 	struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
+	enum vc4_encoder_type encoder_type;
+	const struct vc4_pv_data *pv_data;
+	struct drm_encoder *encoder;
+	unsigned encoder_sel;
 	int channel;
 
 	if (!(of_device_is_compatible(vc4_crtc->pdev->dev.of_node,
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:523 @
 	if (channel < 0)
 		return 0;
 
-	return vc4_crtc_disable(crtc, channel);
+	encoder_sel = VC4_GET_FIELD(CRTC_READ(PV_CONTROL), PV_CONTROL_CLK_SELECT);
+	if (WARN_ON(encoder_sel != 0))
+		return 0;
+
+	pv_data = vc4_crtc_to_vc4_pv_data(vc4_crtc);
+	encoder_type = pv_data->encoder_types[encoder_sel];
+	encoder = vc4_crtc_get_encoder_by_type(crtc, encoder_type);
+	if (WARN_ON(!encoder))
+		return 0;
+
+	return vc4_crtc_disable(crtc, encoder, NULL, channel);
 }
 
 static void vc4_crtc_atomic_disable(struct drm_crtc *crtc,
-				    struct drm_crtc_state *old_state)
+				    struct drm_atomic_state *state)
 {
+	struct drm_crtc_state *old_state = drm_atomic_get_old_crtc_state(state,
+									 crtc);
 	struct vc4_crtc_state *old_vc4_state = to_vc4_crtc_state(old_state);
+	struct drm_encoder *encoder = vc4_get_crtc_encoder(crtc, old_state);
 	struct drm_device *dev = crtc->dev;
 
+	drm_dbg(dev, "Disabling CRTC %s (%u) connected to Encoder %s (%u)",
+		crtc->name, crtc->base.id, encoder->name, encoder->base.id);
+
 	require_hvs_enabled(dev);
 
 	/* Disable vblank irq handling before crtc is disabled. */
 	drm_crtc_vblank_off(crtc);
 
-	vc4_crtc_disable(crtc, old_vc4_state->assigned_channel);
+	vc4_crtc_disable(crtc, encoder, state, old_vc4_state->assigned_channel);
 
 	/*
 	 * Make sure we issue a vblank event after disabling the CRTC if
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:570 @
 }
 
 static void vc4_crtc_atomic_enable(struct drm_crtc *crtc,
-				   struct drm_crtc_state *old_state)
+				   struct drm_atomic_state *state)
 {
+	struct drm_crtc_state *new_state = drm_atomic_get_new_crtc_state(state,
+									 crtc);
 	struct drm_device *dev = crtc->dev;
 	struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
-	struct drm_encoder *encoder = vc4_get_crtc_encoder(crtc);
+	struct drm_encoder *encoder = vc4_get_crtc_encoder(crtc, new_state);
 	struct vc4_encoder *vc4_encoder = to_vc4_encoder(encoder);
 
+	drm_dbg(dev, "Enabling CRTC %s (%u) connected to Encoder %s (%u)",
+		crtc->name, crtc->base.id, encoder->name, encoder->base.id);
+
 	require_hvs_enabled(dev);
 
 	/* Enable vblank irq handling before crtc is started otherwise
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:589 @
 	 */
 	drm_crtc_vblank_on(crtc);
 
-	vc4_hvs_atomic_enable(crtc, old_state);
+	vc4_hvs_atomic_enable(crtc, state);
 
 	if (vc4_encoder->pre_crtc_configure)
-		vc4_encoder->pre_crtc_configure(encoder);
+		vc4_encoder->pre_crtc_configure(encoder, state);
 
-	vc4_crtc_config_pv(crtc);
+	vc4_crtc_config_pv(crtc, encoder, state);
 
 	CRTC_WRITE(PV_CONTROL, CRTC_READ(PV_CONTROL) | PV_CONTROL_EN);
 
 	if (vc4_encoder->pre_crtc_enable)
-		vc4_encoder->pre_crtc_enable(encoder);
+		vc4_encoder->pre_crtc_enable(encoder, state);
 
 	/* When feeding the transposer block the pixelvalve is unneeded and
 	 * should not be enabled.
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:608 @
 		   CRTC_READ(PV_V_CONTROL) | PV_VCONTROL_VIDEN);
 
 	if (vc4_encoder->post_crtc_enable)
-		vc4_encoder->post_crtc_enable(encoder);
+		vc4_encoder->post_crtc_enable(encoder, state);
 }
 
 static enum drm_mode_status vc4_crtc_mode_valid(struct drm_crtc *crtc,
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:656 @
 }
 
 static int vc4_crtc_atomic_check(struct drm_crtc *crtc,
-				 struct drm_crtc_state *state)
+				 struct drm_atomic_state *state)
 {
-	struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(state);
+	struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state,
+									  crtc);
+	struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(crtc_state);
 	struct drm_connector *conn;
 	struct drm_connector_state *conn_state;
+	struct drm_encoder *encoder;
 	int ret, i;
 
 	ret = vc4_hvs_atomic_check(crtc, state);
 	if (ret)
 		return ret;
 
-	for_each_new_connector_in_state(state->state, conn, conn_state, i) {
+	encoder = vc4_get_crtc_encoder(crtc, crtc_state);
+	if (encoder) {
+		const struct drm_display_mode *mode = &crtc_state->adjusted_mode;
+		struct vc4_encoder *vc4_encoder = to_vc4_encoder(encoder);
+
+		mode = &crtc_state->adjusted_mode;
+		if (vc4_encoder->type == VC4_ENCODER_TYPE_HDMI0) {
+			vc4_state->hvs_load = max(mode->clock * mode->hdisplay / mode->htotal + 1000,
+						  mode->clock * 9 / 10) * 1000;
+		} else {
+			vc4_state->hvs_load = mode->clock * 1000;
+		}
+	}
+
+	for_each_new_connector_in_state(state, conn, conn_state,
+					i) {
 		if (conn_state->crtc != crtc)
 			continue;
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:784 @
 		container_of(cb, struct vc4_async_flip_state, cb);
 	struct drm_crtc *crtc = flip_state->crtc;
 	struct drm_device *dev = crtc->dev;
-	struct vc4_dev *vc4 = to_vc4_dev(dev);
 	struct drm_plane *plane = crtc->primary;
 
 	vc4_plane_async_set_fb(plane, flip_state->fb);
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:815 @
 	}
 
 	kfree(flip_state);
-
-	up(&vc4->async_modeset);
 }
 
 /* Implements async (non-vblank-synced) page flips.
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:829 @
 			       uint32_t flags)
 {
 	struct drm_device *dev = crtc->dev;
-	struct vc4_dev *vc4 = to_vc4_dev(dev);
 	struct drm_plane *plane = crtc->primary;
 	int ret = 0;
 	struct vc4_async_flip_state *flip_state;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:857 @
 	flip_state->crtc = crtc;
 	flip_state->event = event;
 
-	/* Make sure all other async modesetes have landed. */
-	ret = down_interruptible(&vc4->async_modeset);
-	if (ret) {
-		drm_framebuffer_put(fb);
-		vc4_bo_dec_usecnt(bo);
-		kfree(flip_state);
-		return ret;
-	}
-
 	/* Save the current FB before it's replaced by the new one in
 	 * drm_atomic_set_fb_for_plane(). We'll need the old FB in
 	 * vc4_async_page_flip_complete() to decrement the BO usecnt and keep
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:958 @
 	.reset = vc4_crtc_reset,
 	.atomic_duplicate_state = vc4_crtc_duplicate_state,
 	.atomic_destroy_state = vc4_crtc_destroy_state,
-	.gamma_set = drm_atomic_helper_legacy_gamma_set,
 	.enable_vblank = vc4_enable_vblank,
 	.disable_vblank = vc4_disable_vblank,
 	.get_vblank_timestamp = drm_crtc_vblank_helper_get_vblank_timestamp,
diff -Nur --no-dereference linux-5.10.52-orig/drivers/gpu/drm/vc4/vc4_debugfs.c linux-5.10.52-v7l+/drivers/gpu/drm/vc4/vc4_debugfs.c
--- linux-5.10.52-orig/drivers/gpu/drm/vc4/vc4_debugfs.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/gpu/drm/vc4/vc4_debugfs.c	2021-07-25 16:46:01.698362435 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:10 @
 #include <linux/circ_buf.h>
 #include <linux/ctype.h>
 #include <linux/debugfs.h>
+#include <linux/platform_device.h>
 
 #include "vc4_drv.h"
 #include "vc4_regs.h"
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:30 @
 	struct vc4_dev *vc4 = to_vc4_dev(minor->dev);
 	struct vc4_debugfs_info_entry *entry;
 
-	debugfs_create_bool("hvs_load_tracker", S_IRUGO | S_IWUSR,
-			    minor->debugfs_root, &vc4->load_tracker_enabled);
+	if (vc4->hvs && !of_device_is_compatible(vc4->hvs->pdev->dev.of_node,
+				     "brcm,bcm2711-vc5"))
+		debugfs_create_bool("hvs_load_tracker", S_IRUGO | S_IWUSR,
+				    minor->debugfs_root, &vc4->load_tracker_enabled);
 
 	list_for_each_entry(entry, &vc4->debugfs_list, link) {
 		drm_debugfs_create_files(&entry->info, 1,
diff -Nur --no-dereference linux-5.10.52-orig/drivers/gpu/drm/vc4/vc4_dpi.c linux-5.10.52-v7l+/drivers/gpu/drm/vc4/vc4_dpi.c
--- linux-5.10.52-orig/drivers/gpu/drm/vc4/vc4_dpi.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/gpu/drm/vc4/vc4_dpi.c	2021-07-25 16:46:01.698362435 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:168 @
 			dpi_c |= VC4_SET_FIELD(DPI_FORMAT_18BIT_666_RGB_2,
 					       DPI_FORMAT);
 			break;
+		case MEDIA_BUS_FMT_BGR666_1X24_CPADHI:
+			dpi_c |= VC4_SET_FIELD(DPI_FORMAT_18BIT_666_RGB_2,
+					       DPI_FORMAT);
+			dpi_c |= VC4_SET_FIELD(DPI_ORDER_BGR, DPI_ORDER);
+			break;
 		case MEDIA_BUS_FMT_RGB666_1X18:
 			dpi_c |= VC4_SET_FIELD(DPI_FORMAT_18BIT_666_RGB_1,
 					       DPI_FORMAT);
 			break;
+		case MEDIA_BUS_FMT_BGR666_1X18:
+			dpi_c |= VC4_SET_FIELD(DPI_FORMAT_18BIT_666_RGB_1,
+					       DPI_FORMAT);
+			dpi_c |= VC4_SET_FIELD(DPI_ORDER_BGR, DPI_ORDER);
+			break;
 		case MEDIA_BUS_FMT_RGB565_1X16:
 			dpi_c |= VC4_SET_FIELD(DPI_FORMAT_16BIT_565_RGB_3,
 					       DPI_FORMAT);
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:191 @
 			break;
 		}
 	} else {
-		/* Default to 24bit if no connector found. */
-		dpi_c |= VC4_SET_FIELD(DPI_FORMAT_24BIT_888_RGB, DPI_FORMAT);
+		/* Default to 18bit if no connector found. */
+		dpi_c |= VC4_SET_FIELD(DPI_FORMAT_18BIT_666_RGB_1, DPI_FORMAT);
 	}
 
 	if (mode->flags & DRM_MODE_FLAG_NHSYNC)
diff -Nur --no-dereference linux-5.10.52-orig/drivers/gpu/drm/vc4/vc4_drv.c linux-5.10.52-v7l+/drivers/gpu/drm/vc4/vc4_drv.c
--- linux-5.10.52-orig/drivers/gpu/drm/vc4/vc4_drv.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/gpu/drm/vc4/vc4_drv.c	2021-07-25 16:46:01.698362435 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:39 @
 #include <drm/drm_fb_helper.h>
 #include <drm/drm_vblank.h>
 
+#include <soc/bcm2835/raspberrypi-firmware.h>
+
 #include "uapi/drm/vc4_drm.h"
 
 #include "vc4_drv.h"
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:251 @
 	}
 }
 
+const struct of_device_id vc4_dma_range_matches[] = {
+	{ .compatible = "brcm,bcm2835-hvs" },
+	{ .compatible = "brcm,bcm2711-hvs" },
+	{ .compatible = "raspberrypi,rpi-firmware-kms" },
+	{ .compatible = "brcm,bcm2835-v3d" },
+	{ .compatible = "brcm,cygnus-v3d" },
+	{ .compatible = "brcm,vc4-v3d" },
+	{}
+};
+
+/*
+ * we need this helper function for determining presence of fkms
+ * before it's been bound
+ */
+static bool firmware_kms(void)
+{
+	return of_device_is_available(of_find_compatible_node(NULL, NULL,
+	       "raspberrypi,rpi-firmware-kms")) ||
+	       of_device_is_available(of_find_compatible_node(NULL, NULL,
+	       "raspberrypi,rpi-firmware-kms-2711"));
+}
+
 static int vc4_drm_bind(struct device *dev)
 {
 	struct platform_device *pdev = to_platform_device(dev);
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:290 @
 		vc4_drm_driver.driver_features &= ~DRIVER_RENDER;
 	of_node_put(node);
 
+	node = of_find_matching_node_and_match(NULL, vc4_dma_range_matches,
+					       NULL);
+	if (node) {
+		ret = of_dma_configure(dev, node, true);
+		of_node_put(node);
+
+		if (ret)
+			return ret;
+	}
+
 	vc4 = devm_drm_dev_alloc(dev, &vc4_drm_driver, struct vc4_dev, base);
 	if (IS_ERR(vc4))
 		return PTR_ERR(vc4);
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:322 @
 	if (ret)
 		return ret;
 
+	node = of_parse_phandle(dev->of_node, "raspberrypi,firmware", 0);
+	if (node) {
+		vc4->firmware = rpi_firmware_get(node);
+		of_node_put(node);
+
+		if (!vc4->firmware)
+			return -EPROBE_DEFER;
+	}
+
+	drm_fb_helper_remove_conflicting_framebuffers(NULL, "vc4drmfb", false);
+
+	if (vc4->firmware && !firmware_kms()) {
+		ret = rpi_firmware_property(vc4->firmware,
+					    RPI_FIRMWARE_NOTIFY_DISPLAY_DONE,
+					    NULL, 0);
+		if (ret)
+			drm_warn(drm, "Couldn't stop firmware display driver: %d\n", ret);
+	}
+
 	ret = component_bind_all(dev, drm);
 	if (ret)
 		return ret;
 
-	ret = vc4_plane_create_additional_planes(drm);
-	if (ret)
-		goto unbind_all;
-
-	drm_fb_helper_remove_conflicting_framebuffers(NULL, "vc4drmfb", false);
+	if (!vc4->firmware_kms) {
+		ret = vc4_plane_create_additional_planes(drm);
+		if (ret)
+			goto unbind_all;
+	}
 
 	ret = vc4_kms_load(drm);
 	if (ret < 0)
 		goto unbind_all;
 
-	drm_for_each_crtc(crtc, drm)
-		vc4_crtc_disable_at_boot(crtc);
+	if (!vc4->firmware_kms) {
+		drm_for_each_crtc(crtc, drm)
+			vc4_crtc_disable_at_boot(crtc);
+	}
 
 	ret = drm_dev_register(drm, 0);
 	if (ret < 0)
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:388 @
 	.unbind = vc4_drm_unbind,
 };
 
+/*
+ * This list determines the binding order of our components, and we have
+ * a few constraints:
+ *   - The TXP driver needs to be bound before the PixelValves (CRTC)
+ *     but after the HVS to set the possible_crtc field properly
+ *   - The HDMI driver needs to be bound after the HVS so that we can
+ *     lookup the HVS maximum core clock rate and figure out if we
+ *     support 4kp60 or not.
+ */
 static struct platform_driver *const component_drivers[] = {
+	&vc4_hvs_driver,
 	&vc4_hdmi_driver,
 	&vc4_vec_driver,
 	&vc4_dpi_driver,
 	&vc4_dsi_driver,
-	&vc4_hvs_driver,
 	&vc4_txp_driver,
 	&vc4_crtc_driver,
+	&vc4_firmware_kms_driver,
 	&vc4_v3d_driver,
 };
 
diff -Nur --no-dereference linux-5.10.52-orig/drivers/gpu/drm/vc4/vc4_drv.h linux-5.10.52-v7l+/drivers/gpu/drm/vc4/vc4_drv.h
--- linux-5.10.52-orig/drivers/gpu/drm/vc4/vc4_drv.h	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/gpu/drm/vc4/vc4_drv.h	2021-07-25 16:46:01.698362435 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:77 @
 struct vc4_dev {
 	struct drm_device base;
 
+	bool firmware_kms;
+	struct rpi_firmware *firmware;
+
+	struct vc4_hdmi *hdmi;
 	struct vc4_hvs *hvs;
 	struct vc4_v3d *v3d;
 	struct vc4_dpi *dpi;
-	struct vc4_dsi *dsi1;
 	struct vc4_vec *vec;
 	struct vc4_txp *txp;
+	struct vc4_fkms *fkms;
 
 	struct vc4_hang_state *hang_state;
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:208 @
 
 	int power_refcount;
 
-	/* Set to true when the load tracker is supported. */
-	bool load_tracker_available;
-
 	/* Set to true when the load tracker is active. */
 	bool load_tracker_enabled;
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:219 @
 		struct work_struct reset_work;
 	} hangcheck;
 
-	struct semaphore async_modeset;
-
 	struct drm_modeset_lock ctm_state_lock;
 	struct drm_private_obj ctm_manager;
 	struct drm_private_obj hvs_channels;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:327 @
 	u32 __iomem *dlist;
 
 	struct clk *core_clk;
+	struct clk_request *core_req;
 
 	/* Memory manager for CRTCs to allocate space in the display
 	 * list.  Units are dwords.
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:447 @
 	enum vc4_encoder_type type;
 	u32 clock_select;
 
-	void (*pre_crtc_configure)(struct drm_encoder *encoder);
-	void (*pre_crtc_enable)(struct drm_encoder *encoder);
-	void (*post_crtc_enable)(struct drm_encoder *encoder);
+	void (*pre_crtc_configure)(struct drm_encoder *encoder, struct drm_atomic_state *state);
+	void (*pre_crtc_enable)(struct drm_encoder *encoder, struct drm_atomic_state *state);
+	void (*post_crtc_enable)(struct drm_encoder *encoder, struct drm_atomic_state *state);
 
-	void (*post_crtc_disable)(struct drm_encoder *encoder);
-	void (*post_crtc_powerdown)(struct drm_encoder *encoder);
+	void (*post_crtc_disable)(struct drm_encoder *encoder, struct drm_atomic_state *state);
+	void (*post_crtc_powerdown)(struct drm_encoder *encoder, struct drm_atomic_state *state);
 };
 
 static inline struct vc4_encoder *
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:521 @
 	return container_of(data, struct vc4_pv_data, base);
 }
 
+struct drm_encoder *vc4_get_crtc_encoder(struct drm_crtc *crtc,
+					 struct drm_crtc_state *state);
+
 struct vc4_crtc_state {
 	struct drm_crtc_state base;
 	/* Dlist area for this CRTC configuration. */
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:539 @
 		unsigned int bottom;
 	} margins;
 
+	unsigned long hvs_load;
+
 	/* Transitional state below, only valid during atomic commits */
 	bool update_muxing;
 };
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:885 @
 /* vc4_fence.c */
 extern const struct dma_fence_ops vc4_fence_ops;
 
+/* vc4_firmware_kms.c */
+extern struct platform_driver vc4_firmware_kms_driver;
+
 /* vc4_gem.c */
 int vc4_gem_init(struct drm_device *dev);
 int vc4_submit_cl_ioctl(struct drm_device *dev, void *data,
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:928 @
 extern struct platform_driver vc4_hvs_driver;
 void vc4_hvs_stop_channel(struct drm_device *dev, unsigned int output);
 int vc4_hvs_get_fifo_from_output(struct drm_device *dev, unsigned int output);
-int vc4_hvs_atomic_check(struct drm_crtc *crtc, struct drm_crtc_state *state);
-void vc4_hvs_atomic_enable(struct drm_crtc *crtc, struct drm_crtc_state *old_state);
-void vc4_hvs_atomic_disable(struct drm_crtc *crtc, struct drm_crtc_state *old_state);
-void vc4_hvs_atomic_flush(struct drm_crtc *crtc, struct drm_crtc_state *state);
+int vc4_hvs_atomic_check(struct drm_crtc *crtc, struct drm_atomic_state *state);
+void vc4_hvs_atomic_enable(struct drm_crtc *crtc, struct drm_atomic_state *state);
+void vc4_hvs_atomic_disable(struct drm_crtc *crtc, struct drm_atomic_state *state);
+void vc4_hvs_atomic_flush(struct drm_crtc *crtc, struct drm_atomic_state *state);
 void vc4_hvs_dump_state(struct drm_device *dev);
 void vc4_hvs_unmask_underrun(struct drm_device *dev, int channel);
 void vc4_hvs_mask_underrun(struct drm_device *dev, int channel);
diff -Nur --no-dereference linux-5.10.52-orig/drivers/gpu/drm/vc4/vc4_dsi.c linux-5.10.52-v7l+/drivers/gpu/drm/vc4/vc4_dsi.c
--- linux-5.10.52-orig/drivers/gpu/drm/vc4/vc4_dsi.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/gpu/drm/vc4/vc4_dsi.c	2021-07-25 16:46:01.698362435 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:184 @
 
 #define DSI0_TXPKT_PIX_FIFO		0x20 /* AKA PIX_FIFO */
 
-#define DSI0_INT_STAT		0x24
-#define DSI0_INT_EN		0x28
+#define DSI0_INT_STAT			0x24
+#define DSI0_INT_EN			0x28
+# define DSI0_INT_FIFO_ERR		BIT(25)
+# define DSI0_INT_CMDC_DONE_MASK	VC4_MASK(24, 23)
+# define DSI0_INT_CMDC_DONE_SHIFT	23
+#  define DSI0_INT_CMDC_DONE_NO_REPEAT		1
+#  define DSI0_INT_CMDC_DONE_REPEAT		3
+# define DSI0_INT_PHY_DIR_RTF		BIT(22)
+# define DSI0_INT_PHY_D1_ULPS		BIT(21)
+# define DSI0_INT_PHY_D1_STOP		BIT(20)
+# define DSI0_INT_PHY_RXLPDT		BIT(19)
+# define DSI0_INT_PHY_RXTRIG		BIT(18)
+# define DSI0_INT_PHY_D0_ULPS		BIT(17)
+# define DSI0_INT_PHY_D0_LPDT		BIT(16)
+# define DSI0_INT_PHY_D0_FTR		BIT(15)
+# define DSI0_INT_PHY_D0_STOP		BIT(14)
+/* Signaled when the clock lane enters the given state. */
+# define DSI0_INT_PHY_CLK_ULPS		BIT(13)
+# define DSI0_INT_PHY_CLK_HS		BIT(12)
+# define DSI0_INT_PHY_CLK_FTR		BIT(11)
+/* Signaled on timeouts */
+# define DSI0_INT_PR_TO			BIT(10)
+# define DSI0_INT_TA_TO			BIT(9)
+# define DSI0_INT_LPRX_TO		BIT(8)
+# define DSI0_INT_HSTX_TO		BIT(7)
+/* Contention on a line when trying to drive the line low */
+# define DSI0_INT_ERR_CONT_LP1		BIT(6)
+# define DSI0_INT_ERR_CONT_LP0		BIT(5)
+/* Control error: incorrect line state sequence on data lane 0. */
+# define DSI0_INT_ERR_CONTROL		BIT(4)
+# define DSI0_INT_ERR_SYNC_ESC		BIT(3)
+# define DSI0_INT_RX2_PKT		BIT(2)
+# define DSI0_INT_RX1_PKT		BIT(1)
+# define DSI0_INT_CMD_PKT		BIT(0)
+
+#define DSI0_INTERRUPTS_ALWAYS_ENABLED	(DSI0_INT_ERR_SYNC_ESC | \
+					 DSI0_INT_ERR_CONTROL |	 \
+					 DSI0_INT_ERR_CONT_LP0 | \
+					 DSI0_INT_ERR_CONT_LP1 | \
+					 DSI0_INT_HSTX_TO |	 \
+					 DSI0_INT_LPRX_TO |	 \
+					 DSI0_INT_TA_TO |	 \
+					 DSI0_INT_PR_TO)
+
 # define DSI1_INT_PHY_D3_ULPS		BIT(30)
 # define DSI1_INT_PHY_D3_STOP		BIT(29)
 # define DSI1_INT_PHY_D2_ULPS		BIT(28)
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:351 @
 # define DSI0_PHY_AFEC0_RESET			BIT(11)
 # define DSI1_PHY_AFEC0_PD_BG			BIT(11)
 # define DSI0_PHY_AFEC0_PD			BIT(10)
-# define DSI1_PHY_AFEC0_PD_DLANE3		BIT(10)
+# define DSI1_PHY_AFEC0_PD_DLANE1		BIT(10)
 # define DSI0_PHY_AFEC0_PD_BG			BIT(9)
 # define DSI1_PHY_AFEC0_PD_DLANE2		BIT(9)
 # define DSI0_PHY_AFEC0_PD_DLANE1		BIT(8)
-# define DSI1_PHY_AFEC0_PD_DLANE1		BIT(8)
+# define DSI1_PHY_AFEC0_PD_DLANE3		BIT(8)
 # define DSI_PHY_AFEC0_PTATADJ_MASK		VC4_MASK(7, 4)
 # define DSI_PHY_AFEC0_PTATADJ_SHIFT		4
 # define DSI_PHY_AFEC0_CTATADJ_MASK		VC4_MASK(3, 0)
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:538 @
  */
 #define DSI1_ID			0x8c
 
+struct vc4_dsi_variant {
+	/* Whether we're on bcm2835's DSI0 or DSI1. */
+	unsigned int port;
+
+	bool broken_axi_workaround;
+
+	const char *debugfs_name;
+	const struct debugfs_reg32 *regs;
+	size_t nregs;
+
+};
+
 /* General DSI hardware state. */
 struct vc4_dsi {
 	struct platform_device *pdev;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:566 @
 	u32 *reg_dma_mem;
 	dma_addr_t reg_paddr;
 
-	/* Whether we're on bcm2835's DSI0 or DSI1. */
-	int port;
+	const struct vc4_dsi_variant *variant;
 
 	/* DSI channel for the panel we're connected to. */
 	u32 channel;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:642 @
 #define DSI_READ(offset) readl(dsi->regs + (offset))
 #define DSI_WRITE(offset, val) dsi_dma_workaround_write(dsi, offset, val)
 #define DSI_PORT_READ(offset) \
-	DSI_READ(dsi->port ? DSI1_##offset : DSI0_##offset)
+	DSI_READ(dsi->variant->port ? DSI1_##offset : DSI0_##offset)
 #define DSI_PORT_WRITE(offset, val) \
-	DSI_WRITE(dsi->port ? DSI1_##offset : DSI0_##offset, val)
-#define DSI_PORT_BIT(bit) (dsi->port ? DSI1_##bit : DSI0_##bit)
+	DSI_WRITE(dsi->variant->port ? DSI1_##offset : DSI0_##offset, val)
+#define DSI_PORT_BIT(bit) (dsi->variant->port ? DSI1_##bit : DSI0_##bit)
 
 /* VC4 DSI encoder KMS struct */
 struct vc4_dsi_encoder {
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:806 @
 	list_for_each_entry_reverse(iter, &dsi->bridge_chain, chain_node) {
 		if (iter->funcs->disable)
 			iter->funcs->disable(iter);
+
+		if (iter == dsi->bridge)
+			break;
 	}
 
 	vc4_dsi_ulps(dsi, true);
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:896 @
 
 	ret = pm_runtime_get_sync(dev);
 	if (ret) {
-		DRM_ERROR("Failed to runtime PM enable on DSI%d\n", dsi->port);
+		DRM_ERROR("Failed to runtime PM enable on DSI%d\n", dsi->variant->port);
 		return;
 	}
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:930 @
 	DSI_PORT_WRITE(STAT, DSI_PORT_READ(STAT));
 
 	/* Set AFE CTR00/CTR1 to release powerdown of analog. */
-	if (dsi->port == 0) {
+	if (dsi->variant->port == 0) {
 		u32 afec0 = (VC4_SET_FIELD(7, DSI_PHY_AFEC0_PTATADJ) |
 			     VC4_SET_FIELD(7, DSI_PHY_AFEC0_CTATADJ));
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:942 @
 
 		DSI_PORT_WRITE(PHY_AFEC0, afec0);
 
+		/* AFEC reset hold time */
+		mdelay(1);
+
 		DSI_PORT_WRITE(PHY_AFEC1,
 			       VC4_SET_FIELD(6,  DSI0_PHY_AFEC1_IDR_DLANE1) |
 			       VC4_SET_FIELD(6,  DSI0_PHY_AFEC1_IDR_DLANE0) |
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1079 @
 		       DSI_PORT_BIT(PHYC_CLANE_ENABLE) |
 		       ((dsi->mode_flags & MIPI_DSI_CLOCK_NON_CONTINUOUS) ?
 			0 : DSI_PORT_BIT(PHYC_HS_CLK_CONTINUOUS)) |
-		       (dsi->port == 0 ?
+		       (dsi->variant->port == 0 ?
 			VC4_SET_FIELD(lpx - 1, DSI0_PHYC_ESC_CLK_LPDT) :
 			VC4_SET_FIELD(lpx - 1, DSI1_PHYC_ESC_CLK_LPDT)));
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1105 @
 		       DSI_DISP1_ENABLE);
 
 	/* Ungate the block. */
-	if (dsi->port == 0)
+	if (dsi->variant->port == 0)
 		DSI_PORT_WRITE(CTRL, DSI_PORT_READ(CTRL) | DSI0_CTRL_CTRL0);
 	else
 		DSI_PORT_WRITE(CTRL, DSI_PORT_READ(CTRL) | DSI1_CTRL_EN);
 
 	/* Bring AFE out of reset. */
-	if (dsi->port == 0) {
-	} else {
-		DSI_PORT_WRITE(PHY_AFEC0,
-			       DSI_PORT_READ(PHY_AFEC0) &
-			       ~DSI1_PHY_AFEC0_RESET);
-	}
+	DSI_PORT_WRITE(PHY_AFEC0,
+		       DSI_PORT_READ(PHY_AFEC0) &
+		       ~DSI_PORT_BIT(PHY_AFEC0_RESET));
 
 	vc4_dsi_ulps(dsi, false);
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1232 @
 	/* Enable the appropriate interrupt for the transfer completion. */
 	dsi->xfer_result = 0;
 	reinit_completion(&dsi->xfer_completion);
-	DSI_PORT_WRITE(INT_STAT, DSI1_INT_TXPKT1_DONE | DSI1_INT_PHY_DIR_RTF);
-	if (msg->rx_len) {
-		DSI_PORT_WRITE(INT_EN, (DSI1_INTERRUPTS_ALWAYS_ENABLED |
-					DSI1_INT_PHY_DIR_RTF));
+	if (dsi->variant->port == 0) {
+		DSI_PORT_WRITE(INT_STAT,
+			       DSI0_INT_CMDC_DONE_MASK | DSI1_INT_PHY_DIR_RTF);
+		if (msg->rx_len) {
+			DSI_PORT_WRITE(INT_EN, (DSI0_INTERRUPTS_ALWAYS_ENABLED |
+						DSI0_INT_PHY_DIR_RTF));
+		} else {
+			DSI_PORT_WRITE(INT_EN,
+				       (DSI0_INTERRUPTS_ALWAYS_ENABLED |
+					VC4_SET_FIELD(DSI0_INT_CMDC_DONE_NO_REPEAT,
+						      DSI0_INT_CMDC_DONE)));
+		}
 	} else {
-		DSI_PORT_WRITE(INT_EN, (DSI1_INTERRUPTS_ALWAYS_ENABLED |
-					DSI1_INT_TXPKT1_DONE));
+		DSI_PORT_WRITE(INT_STAT,
+			       DSI1_INT_TXPKT1_DONE | DSI1_INT_PHY_DIR_RTF);
+		if (msg->rx_len) {
+			DSI_PORT_WRITE(INT_EN, (DSI1_INTERRUPTS_ALWAYS_ENABLED |
+						DSI1_INT_PHY_DIR_RTF));
+		} else {
+			DSI_PORT_WRITE(INT_EN, (DSI1_INTERRUPTS_ALWAYS_ENABLED |
+						DSI1_INT_TXPKT1_DONE));
+		}
 	}
 
 	/* Send the packet. */
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1270 @
 		ret = dsi->xfer_result;
 	}
 
-	DSI_PORT_WRITE(INT_EN, DSI1_INTERRUPTS_ALWAYS_ENABLED);
+	DSI_PORT_WRITE(INT_EN, DSI_PORT_BIT(INTERRUPTS_ALWAYS_ENABLED));
 
 	if (ret)
 		goto reset_fifo_and_return;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1316 @
 		       DSI_PORT_BIT(CTRL_RESET_FIFOS));
 
 	DSI_PORT_WRITE(TXPKT1C, 0);
-	DSI_PORT_WRITE(INT_EN, DSI1_INTERRUPTS_ALWAYS_ENABLED);
+	DSI_PORT_WRITE(INT_EN, DSI_PORT_BIT(INTERRUPTS_ALWAYS_ENABLED));
 	return ret;
 }
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1379 @
 	.mode_fixup = vc4_dsi_encoder_mode_fixup,
 };
 
+static const struct vc4_dsi_variant bcm2711_dsi1_variant = {
+	.port			= 1,
+	.debugfs_name		= "dsi1_regs",
+	.regs			= dsi1_regs,
+	.nregs			= ARRAY_SIZE(dsi1_regs),
+};
+
+static const struct vc4_dsi_variant bcm2835_dsi0_variant = {
+	.port			= 0,
+	.debugfs_name		= "dsi0_regs",
+	.regs			= dsi0_regs,
+	.nregs			= ARRAY_SIZE(dsi0_regs),
+};
+
+static const struct vc4_dsi_variant bcm2835_dsi1_variant = {
+	.port			= 1,
+	.broken_axi_workaround	= true,
+	.debugfs_name		= "dsi1_regs",
+	.regs			= dsi1_regs,
+	.nregs			= ARRAY_SIZE(dsi1_regs),
+};
+
 static const struct of_device_id vc4_dsi_dt_match[] = {
-	{ .compatible = "brcm,bcm2835-dsi1", (void *)(uintptr_t)1 },
+	{ .compatible = "brcm,bcm2711-dsi1", &bcm2711_dsi1_variant },
+	{ .compatible = "brcm,bcm2835-dsi0", &bcm2835_dsi0_variant },
+	{ .compatible = "brcm,bcm2835-dsi1", &bcm2835_dsi1_variant },
 	{}
 };
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1415 @
 	if (!(stat & bit))
 		return;
 
-	DRM_ERROR("DSI%d: %s error\n", dsi->port, type);
+	DRM_ERROR("DSI%d: %s error\n", dsi->variant->port, type);
 	*ret = IRQ_HANDLED;
 }
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1449 @
 	DSI_PORT_WRITE(INT_STAT, stat);
 
 	dsi_handle_error(dsi, &ret, stat,
-			 DSI1_INT_ERR_SYNC_ESC, "LPDT sync");
+			 DSI_PORT_BIT(INT_ERR_SYNC_ESC), "LPDT sync");
 	dsi_handle_error(dsi, &ret, stat,
-			 DSI1_INT_ERR_CONTROL, "data lane 0 sequence");
+			 DSI_PORT_BIT(INT_ERR_CONTROL), "data lane 0 sequence");
 	dsi_handle_error(dsi, &ret, stat,
-			 DSI1_INT_ERR_CONT_LP0, "LP0 contention");
+			 DSI_PORT_BIT(INT_ERR_CONT_LP0), "LP0 contention");
 	dsi_handle_error(dsi, &ret, stat,
-			 DSI1_INT_ERR_CONT_LP1, "LP1 contention");
+			 DSI_PORT_BIT(INT_ERR_CONT_LP1), "LP1 contention");
 	dsi_handle_error(dsi, &ret, stat,
-			 DSI1_INT_HSTX_TO, "HSTX timeout");
+			 DSI_PORT_BIT(INT_HSTX_TO), "HSTX timeout");
 	dsi_handle_error(dsi, &ret, stat,
-			 DSI1_INT_LPRX_TO, "LPRX timeout");
+			 DSI_PORT_BIT(INT_LPRX_TO), "LPRX timeout");
 	dsi_handle_error(dsi, &ret, stat,
-			 DSI1_INT_TA_TO, "turnaround timeout");
+			 DSI_PORT_BIT(INT_TA_TO), "turnaround timeout");
 	dsi_handle_error(dsi, &ret, stat,
-			 DSI1_INT_PR_TO, "peripheral reset timeout");
+			 DSI_PORT_BIT(INT_PR_TO), "peripheral reset timeout");
 
-	if (stat & (DSI1_INT_TXPKT1_DONE | DSI1_INT_PHY_DIR_RTF)) {
+	if (stat & ((dsi->variant->port ? DSI1_INT_TXPKT1_DONE :
+					  DSI0_INT_CMDC_DONE_MASK) |
+		    DSI_PORT_BIT(INT_PHY_DIR_RTF))) {
 		complete(&dsi->xfer_completion);
 		ret = IRQ_HANDLED;
-	} else if (stat & DSI1_INT_HSTX_TO) {
+	} else if (stat & DSI_PORT_BIT(INT_HSTX_TO)) {
 		complete(&dsi->xfer_completion);
 		dsi->xfer_result = -ETIMEDOUT;
 		ret = IRQ_HANDLED;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1490 @
 	struct device *dev = &dsi->pdev->dev;
 	const char *parent_name = __clk_get_name(dsi->pll_phy_clock);
 	static const struct {
-		const char *dsi0_name, *dsi1_name;
+		const char *name;
 		int div;
 	} phy_clocks[] = {
-		{ "dsi0_byte", "dsi1_byte", 8 },
-		{ "dsi0_ddr2", "dsi1_ddr2", 4 },
-		{ "dsi0_ddr", "dsi1_ddr", 2 },
+		{ "byte", 8 },
+		{ "ddr2", 4 },
+		{ "ddr", 2 },
 	};
 	int i;
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1511 @
 	for (i = 0; i < ARRAY_SIZE(phy_clocks); i++) {
 		struct clk_fixed_factor *fix = &dsi->phy_clocks[i];
 		struct clk_init_data init;
+		char clk_name[16];
 		int ret;
 
+		snprintf(clk_name, sizeof(clk_name),
+			 "dsi%u_%s", dsi->variant->port, phy_clocks[i].name);
+
 		/* We just use core fixed factor clock ops for the PHY
 		 * clocks.  The clocks are actually gated by the
 		 * PHY_AFEC0_DDRCLK_EN bits, which we should be
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1533 @
 		memset(&init, 0, sizeof(init));
 		init.parent_names = &parent_name;
 		init.num_parents = 1;
-		if (dsi->port == 1)
-			init.name = phy_clocks[i].dsi1_name;
-		else
-			init.name = phy_clocks[i].dsi0_name;
+		init.name = clk_name;
 		init.ops = &clk_fixed_factor_ops;
 
 		ret = devm_clk_hw_register(dev, &fix->hw);
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1552 @
 {
 	struct platform_device *pdev = to_platform_device(dev);
 	struct drm_device *drm = dev_get_drvdata(master);
-	struct vc4_dev *vc4 = to_vc4_dev(drm);
 	struct vc4_dsi *dsi = dev_get_drvdata(dev);
 	struct vc4_dsi_encoder *vc4_dsi_encoder;
 	struct drm_panel *panel;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1563 @
 	if (!match)
 		return -ENODEV;
 
-	dsi->port = (uintptr_t)match->data;
+	dsi->variant = match->data;
 
 	vc4_dsi_encoder = devm_kzalloc(dev, sizeof(*vc4_dsi_encoder),
 				       GFP_KERNEL);
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1571 @
 		return -ENOMEM;
 
 	INIT_LIST_HEAD(&dsi->bridge_chain);
-	vc4_dsi_encoder->base.type = VC4_ENCODER_TYPE_DSI1;
+	vc4_dsi_encoder->base.type = dsi->variant->port ?
+			VC4_ENCODER_TYPE_DSI1 : VC4_ENCODER_TYPE_DSI0;
 	vc4_dsi_encoder->dsi = dsi;
 	dsi->encoder = &vc4_dsi_encoder->base.base;
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1581 @
 		return PTR_ERR(dsi->regs);
 
 	dsi->regset.base = dsi->regs;
-	if (dsi->port == 0) {
-		dsi->regset.regs = dsi0_regs;
-		dsi->regset.nregs = ARRAY_SIZE(dsi0_regs);
-	} else {
-		dsi->regset.regs = dsi1_regs;
-		dsi->regset.nregs = ARRAY_SIZE(dsi1_regs);
-	}
+	dsi->regset.regs = dsi->variant->regs;
+	dsi->regset.nregs = dsi->variant->nregs;
 
 	if (DSI_PORT_READ(ID) != DSI_ID_VALUE) {
 		dev_err(dev, "Port returned 0x%08x for ID instead of 0x%08x\n",
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1590 @
 		return -ENODEV;
 	}
 
-	/* DSI1 has a broken AXI slave that doesn't respond to writes
-	 * from the ARM.  It does handle writes from the DMA engine,
+	/* DSI1 on BCM2835/6/7 has a broken AXI slave that doesn't respond to
+	 * writes from the ARM.  It does handle writes from the DMA engine,
 	 * so set up a channel for talking to it.
 	 */
-	if (dsi->port == 1) {
+	if (dsi->variant->broken_axi_workaround) {
 		dsi->reg_dma_mem = dma_alloc_coherent(dev, 4,
 						      &dsi->reg_dma_paddr,
 						      GFP_KERNEL);
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1700 @
 	if (ret)
 		return ret;
 
-	if (dsi->port == 1)
-		vc4->dsi1 = dsi;
-
 	drm_simple_encoder_init(drm, dsi->encoder, DRM_MODE_ENCODER_DSI);
 	drm_encoder_helper_add(dsi->encoder, &vc4_dsi_encoder_helper_funcs);
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1715 @
 	 */
 	list_splice_init(&dsi->encoder->bridge_chain, &dsi->bridge_chain);
 
-	if (dsi->port == 0)
-		vc4_debugfs_add_regset32(drm, "dsi0_regs", &dsi->regset);
-	else
-		vc4_debugfs_add_regset32(drm, "dsi1_regs", &dsi->regset);
+	vc4_debugfs_add_regset32(drm, dsi->variant->debugfs_name, &dsi->regset);
 
 	pm_runtime_enable(dev);
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1725 @
 static void vc4_dsi_unbind(struct device *dev, struct device *master,
 			   void *data)
 {
-	struct drm_device *drm = dev_get_drvdata(master);
-	struct vc4_dev *vc4 = to_vc4_dev(drm);
 	struct vc4_dsi *dsi = dev_get_drvdata(dev);
 
 	if (dsi->bridge)
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1736 @
 	 */
 	list_splice_init(&dsi->bridge_chain, &dsi->encoder->bridge_chain);
 	drm_encoder_cleanup(dsi->encoder);
-
-	if (dsi->port == 1)
-		vc4->dsi1 = NULL;
 }
 
 static const struct component_ops vc4_dsi_ops = {
diff -Nur --no-dereference linux-5.10.52-orig/drivers/gpu/drm/vc4/vc4_firmware_kms.c linux-5.10.52-v7l+/drivers/gpu/drm/vc4/vc4_firmware_kms.c
--- linux-5.10.52-orig/drivers/gpu/drm/vc4/vc4_firmware_kms.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/gpu/drm/vc4/vc4_firmware_kms.c	2021-07-25 16:46:01.698362435 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2016 Broadcom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+/**
+ * DOC: VC4 firmware KMS module.
+ *
+ * As a hack to get us from the current closed source driver world
+ * toward a totally open stack, implement KMS on top of the Raspberry
+ * Pi's firmware display stack.
+ */
+
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_drv.h>
+#include <drm/drm_fb_cma_helper.h>
+#include <drm/drm_fourcc.h>
+#include <drm/drm_gem_framebuffer_helper.h>
+#include <drm/drm_plane_helper.h>
+#include <drm/drm_probe_helper.h>
+#include <drm/drm_vblank.h>
+
+#include <linux/component.h>
+#include <linux/clk.h>
+#include <linux/debugfs.h>
+#include <linux/module.h>
+
+#include <soc/bcm2835/raspberrypi-firmware.h>
+
+#include "vc4_drv.h"
+#include "vc4_regs.h"
+#include "vc_image_types.h"
+
+int fkms_max_refresh_rate = 85;
+module_param(fkms_max_refresh_rate, int, 0644);
+MODULE_PARM_DESC(fkms_max_refresh_rate, "Max supported refresh rate");
+
+struct get_display_cfg {
+	u32  max_pixel_clock[2];  //Max pixel clock for each display
+};
+
+struct vc4_fkms {
+	struct get_display_cfg cfg;
+	bool bcm2711;
+};
+
+#define PLANES_PER_CRTC		8
+
+struct set_plane {
+	u8 display;
+	u8 plane_id;
+	u8 vc_image_type;
+	s8 layer;
+
+	u16 width;
+	u16 height;
+
+	u16 pitch;
+	u16 vpitch;
+
+	u32 src_x;	/* 16p16 */
+	u32 src_y;	/* 16p16 */
+
+	u32 src_w;	/* 16p16 */
+	u32 src_h;	/* 16p16 */
+
+	s16 dst_x;
+	s16 dst_y;
+
+	u16 dst_w;
+	u16 dst_h;
+
+	u8 alpha;
+	u8 num_planes;
+	u8 is_vu;
+	u8 color_encoding;
+
+	u32 planes[4];  /* DMA address of each plane */
+
+	u32 transform;
+};
+
+/* Values for the transform field */
+#define TRANSFORM_NO_ROTATE	0
+#define TRANSFORM_ROTATE_180	BIT(1)
+#define TRANSFORM_FLIP_HRIZ	BIT(16)
+#define TRANSFORM_FLIP_VERT	BIT(17)
+
+struct mailbox_set_plane {
+	struct rpi_firmware_property_tag_header tag;
+	struct set_plane plane;
+};
+
+struct mailbox_blank_display {
+	struct rpi_firmware_property_tag_header tag1;
+	u32 display;
+	struct rpi_firmware_property_tag_header tag2;
+	u32 blank;
+};
+
+struct mailbox_display_pwr {
+	struct rpi_firmware_property_tag_header tag1;
+	u32 display;
+	u32 state;
+};
+
+struct mailbox_get_edid {
+	struct rpi_firmware_property_tag_header tag1;
+	u32 block;
+	u32 display_number;
+	u8 edid[128];
+};
+
+struct set_timings {
+	u8 display;
+	u8 padding;
+	u16 video_id_code;
+
+	u32 clock;		/* in kHz */
+
+	u16 hdisplay;
+	u16 hsync_start;
+
+	u16 hsync_end;
+	u16 htotal;
+
+	u16 hskew;
+	u16 vdisplay;
+
+	u16 vsync_start;
+	u16 vsync_end;
+
+	u16 vtotal;
+	u16 vscan;
+
+	u16 vrefresh;
+	u16 padding2;
+
+	u32 flags;
+#define  TIMINGS_FLAGS_H_SYNC_POS	BIT(0)
+#define  TIMINGS_FLAGS_H_SYNC_NEG	0
+#define  TIMINGS_FLAGS_V_SYNC_POS	BIT(1)
+#define  TIMINGS_FLAGS_V_SYNC_NEG	0
+#define  TIMINGS_FLAGS_INTERLACE	BIT(2)
+
+#define TIMINGS_FLAGS_ASPECT_MASK	GENMASK(7, 4)
+#define TIMINGS_FLAGS_ASPECT_NONE	(0 << 4)
+#define TIMINGS_FLAGS_ASPECT_4_3	(1 << 4)
+#define TIMINGS_FLAGS_ASPECT_16_9	(2 << 4)
+#define TIMINGS_FLAGS_ASPECT_64_27	(3 << 4)
+#define TIMINGS_FLAGS_ASPECT_256_135	(4 << 4)
+
+/* Limited range RGB flag. Not set corresponds to full range. */
+#define TIMINGS_FLAGS_RGB_LIMITED	BIT(8)
+/* DVI monitor, therefore disable infoframes. Not set corresponds to HDMI. */
+#define TIMINGS_FLAGS_DVI		BIT(9)
+/* Double clock */
+#define TIMINGS_FLAGS_DBL_CLK		BIT(10)
+};
+
+struct mailbox_set_mode {
+	struct rpi_firmware_property_tag_header tag1;
+	struct set_timings timings;
+};
+
+static const struct vc_image_format {
+	u32 drm;	/* DRM_FORMAT_* */
+	u32 vc_image;	/* VC_IMAGE_* */
+	u32 is_vu;
+} vc_image_formats[] = {
+	{
+		.drm = DRM_FORMAT_XRGB8888,
+		.vc_image = VC_IMAGE_XRGB8888,
+	},
+	{
+		.drm = DRM_FORMAT_ARGB8888,
+		.vc_image = VC_IMAGE_ARGB8888,
+	},
+/*
+ *	FIXME: Need to resolve which DRM format goes to which vc_image format
+ *	for the remaining RGBA and RGBX formats.
+ *	{
+ *		.drm = DRM_FORMAT_ABGR8888,
+ *		.vc_image = VC_IMAGE_RGBA8888,
+ *	},
+ *	{
+ *		.drm = DRM_FORMAT_XBGR8888,
+ *		.vc_image = VC_IMAGE_RGBA8888,
+ *	},
+ */
+	{
+		.drm = DRM_FORMAT_RGB565,
+		.vc_image = VC_IMAGE_RGB565,
+	},
+	{
+		.drm = DRM_FORMAT_RGB888,
+		.vc_image = VC_IMAGE_BGR888,
+	},
+	{
+		.drm = DRM_FORMAT_BGR888,
+		.vc_image = VC_IMAGE_RGB888,
+	},
+	{
+		.drm = DRM_FORMAT_YUV422,
+		.vc_image = VC_IMAGE_YUV422PLANAR,
+	},
+	{
+		.drm = DRM_FORMAT_YUV420,
+		.vc_image = VC_IMAGE_YUV420,
+	},
+	{
+		.drm = DRM_FORMAT_YVU420,
+		.vc_image = VC_IMAGE_YUV420,
+		.is_vu = 1,
+	},
+	{
+		.drm = DRM_FORMAT_NV12,
+		.vc_image = VC_IMAGE_YUV420SP,
+	},
+	{
+		.drm = DRM_FORMAT_NV21,
+		.vc_image = VC_IMAGE_YUV420SP,
+		.is_vu = 1,
+	},
+	{
+		.drm = DRM_FORMAT_P030,
+		.vc_image = VC_IMAGE_YUV10COL,
+	},
+};
+
+static const struct vc_image_format *vc4_get_vc_image_fmt(u32 drm_format)
+{
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(vc_image_formats); i++) {
+		if (vc_image_formats[i].drm == drm_format)
+			return &vc_image_formats[i];
+	}
+
+	return NULL;
+}
+
+/* The firmware delivers a vblank interrupt to us through the SMI
+ * hardware, which has only this one register.
+ */
+#define SMICS 0x0
+#define SMIDSW0 0x14
+#define SMIDSW1 0x1C
+#define SMICS_INTERRUPTS (BIT(9) | BIT(10) | BIT(11))
+
+/* Flag to denote that the firmware is giving multiple display callbacks */
+#define SMI_NEW 0xabcd0000
+
+#define vc4_crtc vc4_kms_crtc
+#define to_vc4_crtc to_vc4_kms_crtc
+struct vc4_crtc {
+	struct drm_crtc base;
+	struct drm_encoder *encoder;
+	struct drm_connector *connector;
+	void __iomem *regs;
+
+	struct drm_pending_vblank_event *event;
+	bool vblank_enabled;
+	u32 display_number;
+	u32 display_type;
+};
+
+static inline struct vc4_crtc *to_vc4_crtc(struct drm_crtc *crtc)
+{
+	return container_of(crtc, struct vc4_crtc, base);
+}
+
+struct vc4_fkms_encoder {
+	struct drm_encoder base;
+	bool hdmi_monitor;
+	bool rgb_range_selectable;
+	int display_num;
+};
+
+static inline struct vc4_fkms_encoder *
+to_vc4_fkms_encoder(struct drm_encoder *encoder)
+{
+	return container_of(encoder, struct vc4_fkms_encoder, base);
+}
+
+/* "Broadcast RGB" property.
+ * Allows overriding of HDMI full or limited range RGB
+ */
+#define VC4_BROADCAST_RGB_AUTO 0
+#define VC4_BROADCAST_RGB_FULL 1
+#define VC4_BROADCAST_RGB_LIMITED 2
+
+/* VC4 FKMS connector KMS struct */
+struct vc4_fkms_connector {
+	struct drm_connector base;
+
+	/* Since the connector is attached to just the one encoder,
+	 * this is the reference to it so we can do the best_encoder()
+	 * hook.
+	 */
+	struct drm_encoder *encoder;
+	struct vc4_dev *vc4_dev;
+	u32 display_number;
+	u32 display_type;
+
+	struct drm_property *broadcast_rgb_property;
+};
+
+static inline struct vc4_fkms_connector *
+to_vc4_fkms_connector(struct drm_connector *connector)
+{
+	return container_of(connector, struct vc4_fkms_connector, base);
+}
+
+/* VC4 FKMS connector state */
+struct vc4_fkms_connector_state {
+	struct drm_connector_state base;
+
+	int broadcast_rgb;
+};
+
+#define to_vc4_fkms_connector_state(x) \
+			container_of(x, struct vc4_fkms_connector_state, base)
+
+static u32 vc4_get_display_type(u32 display_number)
+{
+	const u32 display_types[] = {
+		/* The firmware display (DispmanX) IDs map to specific types in
+		 * a fixed manner.
+		 */
+		DRM_MODE_ENCODER_DSI,	/* MAIN_LCD - DSI or DPI */
+		DRM_MODE_ENCODER_DSI,	/* AUX_LCD */
+		DRM_MODE_ENCODER_TMDS,	/* HDMI0 */
+		DRM_MODE_ENCODER_TVDAC,	/* VEC */
+		DRM_MODE_ENCODER_NONE,	/* FORCE_LCD */
+		DRM_MODE_ENCODER_NONE,	/* FORCE_TV */
+		DRM_MODE_ENCODER_NONE,	/* FORCE_OTHER */
+		DRM_MODE_ENCODER_TMDS,	/* HDMI1 */
+		DRM_MODE_ENCODER_NONE,	/* FORCE_TV2 */
+	};
+	return display_number > ARRAY_SIZE(display_types) - 1 ?
+			DRM_MODE_ENCODER_NONE : display_types[display_number];
+}
+
+/* Firmware's structure for making an FB mbox call. */
+struct fbinfo_s {
+	u32 xres, yres, xres_virtual, yres_virtual;
+	u32 pitch, bpp;
+	u32 xoffset, yoffset;
+	u32 base;
+	u32 screen_size;
+	u16 cmap[256];
+};
+
+struct vc4_fkms_plane {
+	struct drm_plane base;
+	struct fbinfo_s *fbinfo;
+	dma_addr_t fbinfo_bus_addr;
+	u32 pitch;
+	struct mailbox_set_plane mb;
+};
+
+static inline struct vc4_fkms_plane *to_vc4_fkms_plane(struct drm_plane *plane)
+{
+	return (struct vc4_fkms_plane *)plane;
+}
+
+static int vc4_plane_set_blank(struct drm_plane *plane, bool blank)
+{
+	struct vc4_dev *vc4 = to_vc4_dev(plane->dev);
+	struct vc4_fkms_plane *vc4_plane = to_vc4_fkms_plane(plane);
+	struct mailbox_set_plane blank_mb = {
+		.tag = { RPI_FIRMWARE_SET_PLANE, sizeof(struct set_plane), 0 },
+		.plane = {
+			.display = vc4_plane->mb.plane.display,
+			.plane_id = vc4_plane->mb.plane.plane_id,
+		}
+	};
+	static const char * const plane_types[] = {
+							"overlay",
+							"primary",
+							"cursor"
+						  };
+	int ret;
+
+	DRM_DEBUG_ATOMIC("[PLANE:%d:%s] %s plane %s",
+			 plane->base.id, plane->name, plane_types[plane->type],
+			 blank ? "blank" : "unblank");
+
+	if (blank)
+		ret = rpi_firmware_property_list(vc4->firmware, &blank_mb,
+						 sizeof(blank_mb));
+	else
+		ret = rpi_firmware_property_list(vc4->firmware, &vc4_plane->mb,
+						 sizeof(vc4_plane->mb));
+
+	WARN_ONCE(ret, "%s: firmware call failed. Please update your firmware",
+		  __func__);
+	return ret;
+}
+
+static void vc4_fkms_crtc_get_margins(struct drm_crtc_state *state,
+				      unsigned int *left, unsigned int *right,
+				      unsigned int *top, unsigned int *bottom)
+{
+	struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(state);
+	struct drm_connector_state *conn_state;
+	struct drm_connector *conn;
+	int i;
+
+	*left = vc4_state->margins.left;
+	*right = vc4_state->margins.right;
+	*top = vc4_state->margins.top;
+	*bottom = vc4_state->margins.bottom;
+
+	/* We have to interate over all new connector states because
+	 * vc4_fkms_crtc_get_margins() might be called before
+	 * vc4_fkms_crtc_atomic_check() which means margins info in
+	 * vc4_crtc_state might be outdated.
+	 */
+	for_each_new_connector_in_state(state->state, conn, conn_state, i) {
+		if (conn_state->crtc != state->crtc)
+			continue;
+
+		*left = conn_state->tv.margins.left;
+		*right = conn_state->tv.margins.right;
+		*top = conn_state->tv.margins.top;
+		*bottom = conn_state->tv.margins.bottom;
+		break;
+	}
+}
+
+static int vc4_fkms_margins_adj(struct drm_plane_state *pstate,
+				struct set_plane *plane)
+{
+	unsigned int left, right, top, bottom;
+	int adjhdisplay, adjvdisplay;
+	struct drm_crtc_state *crtc_state;
+
+	crtc_state = drm_atomic_get_new_crtc_state(pstate->state,
+						   pstate->crtc);
+
+	vc4_fkms_crtc_get_margins(crtc_state, &left, &right, &top, &bottom);
+
+	if (!left && !right && !top && !bottom)
+		return 0;
+
+	if (left + right >= crtc_state->mode.hdisplay ||
+	    top + bottom >= crtc_state->mode.vdisplay)
+		return -EINVAL;
+
+	adjhdisplay = crtc_state->mode.hdisplay - (left + right);
+	plane->dst_x = DIV_ROUND_CLOSEST(plane->dst_x * adjhdisplay,
+					 (int)crtc_state->mode.hdisplay);
+	plane->dst_x += left;
+	if (plane->dst_x > (int)(crtc_state->mode.hdisplay - right))
+		plane->dst_x = crtc_state->mode.hdisplay - right;
+
+	adjvdisplay = crtc_state->mode.vdisplay - (top + bottom);
+	plane->dst_y = DIV_ROUND_CLOSEST(plane->dst_y * adjvdisplay,
+					 (int)crtc_state->mode.vdisplay);
+	plane->dst_y += top;
+	if (plane->dst_y > (int)(crtc_state->mode.vdisplay - bottom))
+		plane->dst_y = crtc_state->mode.vdisplay - bottom;
+
+	plane->dst_w = DIV_ROUND_CLOSEST(plane->dst_w * adjhdisplay,
+					 crtc_state->mode.hdisplay);
+	plane->dst_h = DIV_ROUND_CLOSEST(plane->dst_h * adjvdisplay,
+					 crtc_state->mode.vdisplay);
+
+	if (!plane->dst_w || !plane->dst_h)
+		return -EINVAL;
+
+	return 0;
+}
+
+static void vc4_plane_atomic_update(struct drm_plane *plane,
+				    struct drm_plane_state *old_state)
+{
+	struct drm_plane_state *state = plane->state;
+
+	/*
+	 * Do NOT set now, as we haven't checked if the crtc is active or not.
+	 * Set from vc4_plane_set_blank instead.
+	 *
+	 * If the CRTC is on (or going to be on) and we're enabled,
+	 * then unblank.  Otherwise, stay blank until CRTC enable.
+	 */
+	if (state->crtc->state->active)
+		vc4_plane_set_blank(plane, false);
+}
+
+static void vc4_plane_atomic_disable(struct drm_plane *plane,
+				     struct drm_plane_state *old_state)
+{
+	struct drm_plane_state *state = plane->state;
+	struct vc4_fkms_plane *vc4_plane = to_vc4_fkms_plane(plane);
+
+	DRM_DEBUG_ATOMIC("[PLANE:%d:%s] plane disable %dx%d@%d +%d,%d\n",
+			 plane->base.id, plane->name,
+			 state->crtc_w,
+			 state->crtc_h,
+			 vc4_plane->mb.plane.vc_image_type,
+			 state->crtc_x,
+			 state->crtc_y);
+	vc4_plane_set_blank(plane, true);
+}
+
+static bool plane_enabled(struct drm_plane_state *state)
+{
+	return state->fb && state->crtc;
+}
+
+static int vc4_plane_to_mb(struct drm_plane *plane,
+			   struct mailbox_set_plane *mb,
+			   struct drm_plane_state *state)
+{
+	struct drm_framebuffer *fb = state->fb;
+	struct drm_gem_cma_object *bo = drm_fb_cma_get_gem_obj(fb, 0);
+	const struct drm_format_info *drm_fmt = fb->format;
+	const struct vc_image_format *vc_fmt =
+					vc4_get_vc_image_fmt(drm_fmt->format);
+	int num_planes = fb->format->num_planes;
+	unsigned int rotation;
+
+	mb->plane.vc_image_type = vc_fmt->vc_image;
+	mb->plane.width = fb->width;
+	mb->plane.height = fb->height;
+	mb->plane.pitch = fb->pitches[0];
+	mb->plane.src_w = state->src_w;
+	mb->plane.src_h = state->src_h;
+	mb->plane.src_x = state->src_x;
+	mb->plane.src_y = state->src_y;
+	mb->plane.dst_w = state->crtc_w;
+	mb->plane.dst_h = state->crtc_h;
+	mb->plane.dst_x = state->crtc_x;
+	mb->plane.dst_y = state->crtc_y;
+	mb->plane.alpha = state->alpha >> 8;
+	mb->plane.layer = state->normalized_zpos ?
+					state->normalized_zpos : -127;
+	mb->plane.num_planes = num_planes;
+	mb->plane.is_vu = vc_fmt->is_vu;
+	mb->plane.planes[0] = bo->paddr + fb->offsets[0];
+
+	rotation = drm_rotation_simplify(state->rotation,
+					 DRM_MODE_ROTATE_0 |
+					 DRM_MODE_REFLECT_X |
+					 DRM_MODE_REFLECT_Y);
+
+	mb->plane.transform = TRANSFORM_NO_ROTATE;
+	if (rotation & DRM_MODE_REFLECT_X)
+		mb->plane.transform |= TRANSFORM_FLIP_HRIZ;
+	if (rotation & DRM_MODE_REFLECT_Y)
+		mb->plane.transform |= TRANSFORM_FLIP_VERT;
+
+	vc4_fkms_margins_adj(state, &mb->plane);
+
+	if (num_planes > 1) {
+		/* Assume this must be YUV */
+		/* Makes assumptions on the stride for the chroma planes as we
+		 * can't easily plumb in non-standard pitches.
+		 */
+		mb->plane.planes[1] = bo->paddr + fb->offsets[1];
+		if (num_planes > 2)
+			mb->plane.planes[2] = bo->paddr + fb->offsets[2];
+		else
+			mb->plane.planes[2] = 0;
+
+		/* Special case the YUV420 with U and V as line interleaved
+		 * planes as we have special handling for that case.
+		 */
+		if (num_planes == 3 &&
+		    (fb->offsets[2] - fb->offsets[1]) == fb->pitches[1])
+			mb->plane.vc_image_type = VC_IMAGE_YUV420_S;
+
+		switch (state->color_encoding) {
+		default:
+		case DRM_COLOR_YCBCR_BT601:
+			if (state->color_range == DRM_COLOR_YCBCR_LIMITED_RANGE)
+				mb->plane.color_encoding =
+						VC_IMAGE_YUVINFO_CSC_ITUR_BT601;
+			else
+				mb->plane.color_encoding =
+						VC_IMAGE_YUVINFO_CSC_JPEG_JFIF;
+			break;
+		case DRM_COLOR_YCBCR_BT709:
+			/* Currently no support for a full range BT709 */
+			mb->plane.color_encoding =
+						VC_IMAGE_YUVINFO_CSC_ITUR_BT709;
+			break;
+		case DRM_COLOR_YCBCR_BT2020:
+			/* Currently no support for a full range BT2020 */
+			mb->plane.color_encoding =
+					VC_IMAGE_YUVINFO_CSC_REC_2020;
+			break;
+		}
+	} else {
+		mb->plane.planes[1] = 0;
+		mb->plane.planes[2] = 0;
+	}
+	mb->plane.planes[3] = 0;
+
+	switch (fourcc_mod_broadcom_mod(fb->modifier)) {
+	case DRM_FORMAT_MOD_BROADCOM_VC4_T_TILED:
+		switch (mb->plane.vc_image_type) {
+		case VC_IMAGE_XRGB8888:
+			mb->plane.vc_image_type = VC_IMAGE_TF_RGBX32;
+			break;
+		case VC_IMAGE_ARGB8888:
+			mb->plane.vc_image_type = VC_IMAGE_TF_RGBA32;
+			break;
+		case VC_IMAGE_RGB565:
+			mb->plane.vc_image_type = VC_IMAGE_TF_RGB565;
+			break;
+		}
+		break;
+	case DRM_FORMAT_MOD_BROADCOM_SAND128:
+		switch (mb->plane.vc_image_type) {
+		case VC_IMAGE_YUV420SP:
+			mb->plane.vc_image_type = VC_IMAGE_YUV_UV;
+			break;
+		/* VC_IMAGE_YUV10COL could be included in here, but it is only
+		 * valid as a SAND128 format, so the table at the top will have
+		 * already set the correct format.
+		 */
+		}
+		/* Note that the column pitch is passed across in lines, not
+		 * bytes.
+		 */
+		mb->plane.pitch = fourcc_mod_broadcom_param(fb->modifier);
+		break;
+	}
+
+	DRM_DEBUG_ATOMIC("[PLANE:%d:%s] plane update %dx%d@%d +dst(%d,%d, %d,%d) +src(%d,%d, %d,%d) 0x%08x/%08x/%08x/%d, alpha %u zpos %u\n",
+			 plane->base.id, plane->name,
+			 mb->plane.width,
+			 mb->plane.height,
+			 mb->plane.vc_image_type,
+			 state->crtc_x,
+			 state->crtc_y,
+			 state->crtc_w,
+			 state->crtc_h,
+			 mb->plane.src_x,
+			 mb->plane.src_y,
+			 mb->plane.src_w,
+			 mb->plane.src_h,
+			 mb->plane.planes[0],
+			 mb->plane.planes[1],
+			 mb->plane.planes[2],
+			 fb->pitches[0],
+			 state->alpha,
+			 state->normalized_zpos);
+
+	return 0;
+}
+
+static int vc4_plane_atomic_check(struct drm_plane *plane,
+				  struct drm_plane_state *state)
+{
+	struct vc4_fkms_plane *vc4_plane = to_vc4_fkms_plane(plane);
+
+	if (!plane_enabled(state))
+		return 0;
+
+	return vc4_plane_to_mb(plane, &vc4_plane->mb, state);
+}
+
+/* Called during init to allocate the plane's atomic state. */
+static void vc4_plane_reset(struct drm_plane *plane)
+{
+	struct vc4_plane_state *vc4_state;
+
+	WARN_ON(plane->state);
+
+	vc4_state = kzalloc(sizeof(*vc4_state), GFP_KERNEL);
+	if (!vc4_state)
+		return;
+
+	__drm_atomic_helper_plane_reset(plane, &vc4_state->base);
+}
+
+static void vc4_plane_destroy(struct drm_plane *plane)
+{
+	drm_plane_cleanup(plane);
+}
+
+static bool vc4_fkms_format_mod_supported(struct drm_plane *plane,
+					  uint32_t format,
+					  uint64_t modifier)
+{
+	/* Support T_TILING for RGB formats only. */
+	switch (format) {
+	case DRM_FORMAT_XRGB8888:
+	case DRM_FORMAT_ARGB8888:
+	case DRM_FORMAT_RGB565:
+		switch (modifier) {
+		case DRM_FORMAT_MOD_BROADCOM_VC4_T_TILED:
+		case DRM_FORMAT_MOD_LINEAR:
+			return true;
+		default:
+			return false;
+		}
+	case DRM_FORMAT_NV12:
+		switch (fourcc_mod_broadcom_mod(modifier)) {
+		case DRM_FORMAT_MOD_LINEAR:
+		case DRM_FORMAT_MOD_BROADCOM_SAND128:
+			return true;
+		default:
+			return false;
+		}
+	case DRM_FORMAT_P030:
+		switch (fourcc_mod_broadcom_mod(modifier)) {
+		case DRM_FORMAT_MOD_BROADCOM_SAND128:
+			return true;
+		default:
+			return false;
+		}
+	case DRM_FORMAT_NV21:
+	case DRM_FORMAT_RGB888:
+	case DRM_FORMAT_BGR888:
+	case DRM_FORMAT_YUV422:
+	case DRM_FORMAT_YUV420:
+	case DRM_FORMAT_YVU420:
+	default:
+		return (modifier == DRM_FORMAT_MOD_LINEAR);
+	}
+}
+
+static struct drm_plane_state *vc4_plane_duplicate_state(struct drm_plane *plane)
+{
+	struct vc4_plane_state *vc4_state;
+
+	if (WARN_ON(!plane->state))
+		return NULL;
+
+	vc4_state = kzalloc(sizeof(*vc4_state), GFP_KERNEL);
+	if (!vc4_state)
+		return NULL;
+
+	__drm_atomic_helper_plane_duplicate_state(plane, &vc4_state->base);
+
+	return &vc4_state->base;
+}
+
+static const struct drm_plane_funcs vc4_plane_funcs = {
+	.update_plane = drm_atomic_helper_update_plane,
+	.disable_plane = drm_atomic_helper_disable_plane,
+	.destroy = vc4_plane_destroy,
+	.set_property = NULL,
+	.reset = vc4_plane_reset,
+	.atomic_duplicate_state = vc4_plane_duplicate_state,
+	.atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
+	.format_mod_supported = vc4_fkms_format_mod_supported,
+};
+
+static const struct drm_plane_helper_funcs vc4_plane_helper_funcs = {
+	.prepare_fb = drm_gem_fb_prepare_fb,
+	.cleanup_fb = NULL,
+	.atomic_check = vc4_plane_atomic_check,
+	.atomic_update = vc4_plane_atomic_update,
+	.atomic_disable = vc4_plane_atomic_disable,
+};
+
+static struct drm_plane *vc4_fkms_plane_init(struct drm_device *dev,
+					     enum drm_plane_type type,
+					     u8 display_num,
+					     u8 plane_id)
+{
+	struct drm_plane *plane = NULL;
+	struct vc4_fkms_plane *vc4_plane;
+	u32 formats[ARRAY_SIZE(vc_image_formats)];
+	unsigned int default_zpos = 0;
+	u32 num_formats = 0;
+	int ret = 0;
+	static const uint64_t modifiers[] = {
+		DRM_FORMAT_MOD_LINEAR,
+		/* VC4_T_TILED should come after linear, because we
+		 * would prefer to scan out linear (less bus traffic).
+		 */
+		DRM_FORMAT_MOD_BROADCOM_VC4_T_TILED,
+		DRM_FORMAT_MOD_BROADCOM_SAND128,
+		DRM_FORMAT_MOD_INVALID,
+	};
+	int i;
+
+	vc4_plane = devm_kzalloc(dev->dev, sizeof(*vc4_plane),
+				 GFP_KERNEL);
+	if (!vc4_plane) {
+		ret = -ENOMEM;
+		goto fail;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(vc_image_formats); i++)
+		formats[num_formats++] = vc_image_formats[i].drm;
+
+	plane = &vc4_plane->base;
+	ret = drm_universal_plane_init(dev, plane, 0,
+				       &vc4_plane_funcs,
+				       formats, num_formats, modifiers,
+				       type, NULL);
+
+	/* FIXME: Do we need to be checking return values from all these calls?
+	 */
+	drm_plane_helper_add(plane, &vc4_plane_helper_funcs);
+
+	drm_plane_create_alpha_property(plane);
+	drm_plane_create_rotation_property(plane, DRM_MODE_ROTATE_0,
+					   DRM_MODE_ROTATE_0 |
+					   DRM_MODE_ROTATE_180 |
+					   DRM_MODE_REFLECT_X |
+					   DRM_MODE_REFLECT_Y);
+	drm_plane_create_color_properties(plane,
+					  BIT(DRM_COLOR_YCBCR_BT601) |
+					  BIT(DRM_COLOR_YCBCR_BT709) |
+					  BIT(DRM_COLOR_YCBCR_BT2020),
+					  BIT(DRM_COLOR_YCBCR_LIMITED_RANGE) |
+					  BIT(DRM_COLOR_YCBCR_FULL_RANGE),
+					  DRM_COLOR_YCBCR_BT709,
+					  DRM_COLOR_YCBCR_LIMITED_RANGE);
+
+	/*
+	 * Default frame buffer setup is with FB on -127, and raspistill etc
+	 * tend to drop overlays on layer 2. Cursor plane was on layer +127.
+	 *
+	 * For F-KMS the mailbox call allows for a s8.
+	 * Remap zpos 0 to -127 for the background layer, but leave all the
+	 * other layers as requested by KMS.
+	 */
+	switch (type) {
+	default:
+	case DRM_PLANE_TYPE_PRIMARY:
+		default_zpos = 0;
+		break;
+	case DRM_PLANE_TYPE_OVERLAY:
+		default_zpos = 1;
+		break;
+	case DRM_PLANE_TYPE_CURSOR:
+		default_zpos = 2;
+		break;
+	}
+	drm_plane_create_zpos_property(plane, default_zpos, 0, 127);
+
+	/* Prepare the static elements of the mailbox structure */
+	vc4_plane->mb.tag.tag = RPI_FIRMWARE_SET_PLANE;
+	vc4_plane->mb.tag.buf_size = sizeof(struct set_plane);
+	vc4_plane->mb.tag.req_resp_size = 0;
+	vc4_plane->mb.plane.display = display_num;
+	vc4_plane->mb.plane.plane_id = plane_id;
+	vc4_plane->mb.plane.layer = default_zpos ? default_zpos : -127;
+
+	return plane;
+fail:
+	if (plane)
+		vc4_plane_destroy(plane);
+
+	return ERR_PTR(ret);
+}
+
+static void vc4_crtc_mode_set_nofb(struct drm_crtc *crtc)
+{
+	struct drm_device *dev = crtc->dev;
+	struct vc4_dev *vc4 = to_vc4_dev(dev);
+	struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
+	struct drm_display_mode *mode = &crtc->state->adjusted_mode;
+	struct vc4_fkms_encoder *vc4_encoder =
+					to_vc4_fkms_encoder(vc4_crtc->encoder);
+	struct mailbox_set_mode mb = {
+		.tag1 = { RPI_FIRMWARE_SET_TIMING,
+			  sizeof(struct set_timings), 0},
+	};
+	union hdmi_infoframe frame;
+	int ret;
+
+	ret = drm_hdmi_avi_infoframe_from_display_mode(&frame.avi, vc4_crtc->connector, mode);
+	if (ret < 0) {
+		DRM_ERROR("couldn't fill AVI infoframe\n");
+		return;
+	}
+
+	DRM_DEBUG_KMS("Setting mode for display num %u mode name %s, clk %d, h(disp %d, start %d, end %d, total %d, skew %d) v(disp %d, start %d, end %d, total %d, scan %d), vrefresh %d, par %u, flags 0x%04x\n",
+		      vc4_crtc->display_number, mode->name, mode->clock,
+		      mode->hdisplay, mode->hsync_start, mode->hsync_end,
+		      mode->htotal, mode->hskew, mode->vdisplay,
+		      mode->vsync_start, mode->vsync_end, mode->vtotal,
+		      mode->vscan, drm_mode_vrefresh(mode),
+		      mode->picture_aspect_ratio, mode->flags);
+	mb.timings.display = vc4_crtc->display_number;
+
+	mb.timings.clock = mode->clock;
+	mb.timings.hdisplay = mode->hdisplay;
+	mb.timings.hsync_start = mode->hsync_start;
+	mb.timings.hsync_end = mode->hsync_end;
+	mb.timings.htotal = mode->htotal;
+	mb.timings.hskew = mode->hskew;
+	mb.timings.vdisplay = mode->vdisplay;
+	mb.timings.vsync_start = mode->vsync_start;
+	mb.timings.vsync_end = mode->vsync_end;
+	mb.timings.vtotal = mode->vtotal;
+	mb.timings.vscan = mode->vscan;
+	mb.timings.vrefresh = drm_mode_vrefresh(mode);
+	mb.timings.flags = 0;
+	if (mode->flags & DRM_MODE_FLAG_PHSYNC)
+		mb.timings.flags |= TIMINGS_FLAGS_H_SYNC_POS;
+	if (mode->flags & DRM_MODE_FLAG_PVSYNC)
+		mb.timings.flags |= TIMINGS_FLAGS_V_SYNC_POS;
+
+	switch (frame.avi.picture_aspect) {
+	default:
+	case HDMI_PICTURE_ASPECT_NONE:
+		mb.timings.flags |= TIMINGS_FLAGS_ASPECT_NONE;
+		break;
+	case HDMI_PICTURE_ASPECT_4_3:
+		mb.timings.flags |= TIMINGS_FLAGS_ASPECT_4_3;
+		break;
+	case HDMI_PICTURE_ASPECT_16_9:
+		mb.timings.flags |= TIMINGS_FLAGS_ASPECT_16_9;
+		break;
+	case HDMI_PICTURE_ASPECT_64_27:
+		mb.timings.flags |= TIMINGS_FLAGS_ASPECT_64_27;
+		break;
+	case HDMI_PICTURE_ASPECT_256_135:
+		mb.timings.flags |= TIMINGS_FLAGS_ASPECT_256_135;
+		break;
+	}
+
+	if (mode->flags & DRM_MODE_FLAG_INTERLACE)
+		mb.timings.flags |= TIMINGS_FLAGS_INTERLACE;
+	if (mode->flags & DRM_MODE_FLAG_DBLCLK)
+		mb.timings.flags |= TIMINGS_FLAGS_DBL_CLK;
+
+	mb.timings.video_id_code = frame.avi.video_code;
+
+	if (!vc4_encoder->hdmi_monitor) {
+		mb.timings.flags |= TIMINGS_FLAGS_DVI;
+	} else {
+		struct vc4_fkms_connector_state *conn_state =
+			to_vc4_fkms_connector_state(vc4_crtc->connector->state);
+
+		if (conn_state->broadcast_rgb == VC4_BROADCAST_RGB_AUTO) {
+			/* See CEA-861-E - 5.1 Default Encoding Parameters */
+			if (drm_default_rgb_quant_range(mode) ==
+					HDMI_QUANTIZATION_RANGE_LIMITED)
+				mb.timings.flags |= TIMINGS_FLAGS_RGB_LIMITED;
+		} else {
+			if (conn_state->broadcast_rgb ==
+						VC4_BROADCAST_RGB_LIMITED)
+				mb.timings.flags |= TIMINGS_FLAGS_RGB_LIMITED;
+
+			/* If not using the default range, then do not provide
+			 * a VIC as the HDMI spec requires that we do not
+			 * signal the opposite of the defined range in the AVI
+			 * infoframe.
+			 */
+			if (!!(mb.timings.flags & TIMINGS_FLAGS_RGB_LIMITED) !=
+			    (drm_default_rgb_quant_range(mode) ==
+					HDMI_QUANTIZATION_RANGE_LIMITED))
+				mb.timings.video_id_code = 0;
+		}
+	}
+
+	/*
+	 * FIXME: To implement
+	 * switch(mode->flag & DRM_MODE_FLAG_3D_MASK) {
+	 * case DRM_MODE_FLAG_3D_NONE:
+	 * case DRM_MODE_FLAG_3D_FRAME_PACKING:
+	 * case DRM_MODE_FLAG_3D_FIELD_ALTERNATIVE:
+	 * case DRM_MODE_FLAG_3D_LINE_ALTERNATIVE:
+	 * case DRM_MODE_FLAG_3D_SIDE_BY_SIDE_FULL:
+	 * case DRM_MODE_FLAG_3D_L_DEPTH:
+	 * case DRM_MODE_FLAG_3D_L_DEPTH_GFX_GFX_DEPTH:
+	 * case DRM_MODE_FLAG_3D_TOP_AND_BOTTOM:
+	 * case DRM_MODE_FLAG_3D_SIDE_BY_SIDE_HALF:
+	 * }
+	 */
+
+	ret = rpi_firmware_property_list(vc4->firmware, &mb, sizeof(mb));
+}
+
+static void vc4_crtc_disable(struct drm_crtc *crtc,
+			     struct drm_atomic_state *state)
+{
+	struct drm_device *dev = crtc->dev;
+	struct drm_plane *plane;
+
+	DRM_DEBUG_KMS("[CRTC:%d] vblanks off.\n",
+		      crtc->base.id);
+	drm_crtc_vblank_off(crtc);
+
+	/* Always turn the planes off on CRTC disable. In DRM, planes
+	 * are enabled/disabled through the update/disable hooks
+	 * above, and the CRTC enable/disable independently controls
+	 * whether anything scans out at all, but the firmware doesn't
+	 * give us a CRTC-level control for that.
+	 */
+
+	drm_atomic_crtc_for_each_plane(plane, crtc)
+		vc4_plane_atomic_disable(plane, plane->state);
+
+	/*
+	 * Make sure we issue a vblank event after disabling the CRTC if
+	 * someone was waiting it.
+	 */
+	if (crtc->state->event) {
+		unsigned long flags;
+
+		spin_lock_irqsave(&dev->event_lock, flags);
+		drm_crtc_send_vblank_event(crtc, crtc->state->event);
+		crtc->state->event = NULL;
+		spin_unlock_irqrestore(&dev->event_lock, flags);
+	}
+}
+
+static void vc4_crtc_consume_event(struct drm_crtc *crtc)
+{
+	struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
+	struct drm_device *dev = crtc->dev;
+	unsigned long flags;
+
+	if (!crtc->state->event)
+		return;
+
+	crtc->state->event->pipe = drm_crtc_index(crtc);
+
+	WARN_ON(drm_crtc_vblank_get(crtc) != 0);
+
+	spin_lock_irqsave(&dev->event_lock, flags);
+	vc4_crtc->event = crtc->state->event;
+	crtc->state->event = NULL;
+	spin_unlock_irqrestore(&dev->event_lock, flags);
+}
+
+static void vc4_crtc_enable(struct drm_crtc *crtc,
+			    struct drm_atomic_state *state)
+{
+	struct drm_plane *plane;
+
+	DRM_DEBUG_KMS("[CRTC:%d] vblanks on.\n",
+		      crtc->base.id);
+	drm_crtc_vblank_on(crtc);
+	vc4_crtc_consume_event(crtc);
+
+	/* Unblank the planes (if they're supposed to be displayed). */
+	drm_atomic_crtc_for_each_plane(plane, crtc)
+		if (plane->state->fb)
+			vc4_plane_set_blank(plane, plane->state->visible);
+}
+
+static enum drm_mode_status
+vc4_crtc_mode_valid(struct drm_crtc *crtc, const struct drm_display_mode *mode)
+{
+	struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
+	struct drm_device *dev = crtc->dev;
+	struct vc4_dev *vc4 = to_vc4_dev(dev);
+	struct vc4_fkms *fkms = vc4->fkms;
+
+	/* Do not allow doublescan modes from user space */
+	if (mode->flags & DRM_MODE_FLAG_DBLSCAN) {
+		DRM_DEBUG_KMS("[CRTC:%d] Doublescan mode rejected.\n",
+			      crtc->base.id);
+		return MODE_NO_DBLESCAN;
+	}
+
+	/* Disable refresh rates > defined threshold (default 85Hz) as limited
+	 * gain from them
+	 */
+	if (drm_mode_vrefresh(mode) > fkms_max_refresh_rate)
+		return MODE_BAD_VVALUE;
+
+	/* Limit the pixel clock based on the HDMI clock limits from the
+	 * firmware
+	 */
+	switch (vc4_crtc->display_number) {
+	case 2:	/* HDMI0 */
+		if (fkms->cfg.max_pixel_clock[0] &&
+		    mode->clock > fkms->cfg.max_pixel_clock[0])
+			return MODE_CLOCK_HIGH;
+		break;
+	case 7:	/* HDMI1 */
+		if (fkms->cfg.max_pixel_clock[1] &&
+		    mode->clock > fkms->cfg.max_pixel_clock[1])
+			return MODE_CLOCK_HIGH;
+		break;
+	}
+
+	/* Pi4 can't generate odd horizontal timings on HDMI, so reject modes
+	 * that would set them.
+	 */
+	if (fkms->bcm2711 &&
+	    (vc4_crtc->display_number == 2 || vc4_crtc->display_number == 7) &&
+	    !(mode->flags & DRM_MODE_FLAG_DBLCLK) &&
+	    ((mode->hdisplay |				/* active */
+	      (mode->hsync_start - mode->hdisplay) |	/* front porch */
+	      (mode->hsync_end - mode->hsync_start) |	/* sync pulse */
+	      (mode->htotal - mode->hsync_end)) & 1))	/* back porch */ {
+		DRM_DEBUG_KMS("[CRTC:%d] Odd timing rejected %u %u %u %u.\n",
+			      crtc->base.id, mode->hdisplay, mode->hsync_start,
+			      mode->hsync_end, mode->htotal);
+		return MODE_H_ILLEGAL;
+	}
+
+	return MODE_OK;
+}
+
+static int vc4_crtc_atomic_check(struct drm_crtc *crtc,
+				 struct drm_atomic_state *state)
+{
+	struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state,
+									  crtc);
+	struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(crtc_state);
+	struct drm_connector *conn;
+	struct drm_connector_state *conn_state;
+	int i;
+
+	DRM_DEBUG_KMS("[CRTC:%d] crtc_atomic_check.\n", crtc->base.id);
+
+	for_each_new_connector_in_state(crtc_state->state, conn, conn_state, i) {
+		if (conn_state->crtc != crtc)
+			continue;
+
+		vc4_state->margins.left = conn_state->tv.margins.left;
+		vc4_state->margins.right = conn_state->tv.margins.right;
+		vc4_state->margins.top = conn_state->tv.margins.top;
+		vc4_state->margins.bottom = conn_state->tv.margins.bottom;
+		break;
+	}
+	return 0;
+}
+
+static void vc4_crtc_atomic_flush(struct drm_crtc *crtc,
+				  struct drm_atomic_state *state)
+{
+	struct drm_crtc_state *old_state = drm_atomic_get_old_crtc_state(state,
+									 crtc);
+
+	DRM_DEBUG_KMS("[CRTC:%d] crtc_atomic_flush.\n",
+		      crtc->base.id);
+	if (crtc->state->active && old_state->active && crtc->state->event)
+		vc4_crtc_consume_event(crtc);
+}
+
+static void vc4_crtc_handle_page_flip(struct vc4_crtc *vc4_crtc)
+{
+	struct drm_crtc *crtc = &vc4_crtc->base;
+	struct drm_device *dev = crtc->dev;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->event_lock, flags);
+	if (vc4_crtc->event) {
+		drm_crtc_send_vblank_event(crtc, vc4_crtc->event);
+		vc4_crtc->event = NULL;
+		drm_crtc_vblank_put(crtc);
+	}
+	spin_unlock_irqrestore(&dev->event_lock, flags);
+}
+
+static irqreturn_t vc4_crtc_irq_handler(int irq, void *data)
+{
+	struct vc4_crtc **crtc_list = data;
+	int i;
+	u32 stat = readl(crtc_list[0]->regs + SMICS);
+	irqreturn_t ret = IRQ_NONE;
+	u32 chan;
+
+	if (stat & SMICS_INTERRUPTS) {
+		writel(0, crtc_list[0]->regs + SMICS);
+
+		chan = readl(crtc_list[0]->regs + SMIDSW0);
+
+		if ((chan & 0xFFFF0000) != SMI_NEW) {
+			/* Older firmware. Treat the one interrupt as vblank/
+			 * complete for all crtcs.
+			 */
+			for (i = 0; crtc_list[i]; i++) {
+				if (crtc_list[i]->vblank_enabled)
+					drm_crtc_handle_vblank(&crtc_list[i]->base);
+				vc4_crtc_handle_page_flip(crtc_list[i]);
+			}
+		} else {
+			if (chan & 1) {
+				writel(SMI_NEW, crtc_list[0]->regs + SMIDSW0);
+				if (crtc_list[0]->vblank_enabled)
+					drm_crtc_handle_vblank(&crtc_list[0]->base);
+				vc4_crtc_handle_page_flip(crtc_list[0]);
+			}
+
+			if (crtc_list[1]) {
+				/* Check for the secondary display too */
+				chan = readl(crtc_list[0]->regs + SMIDSW1);
+
+				if (chan & 1) {
+					writel(SMI_NEW, crtc_list[0]->regs + SMIDSW1);
+
+					if (crtc_list[1]->vblank_enabled)
+						drm_crtc_handle_vblank(&crtc_list[1]->base);
+					vc4_crtc_handle_page_flip(crtc_list[1]);
+				}
+			}
+		}
+
+		ret = IRQ_HANDLED;
+	}
+
+	return ret;
+}
+
+static int vc4_fkms_page_flip(struct drm_crtc *crtc,
+			      struct drm_framebuffer *fb,
+			      struct drm_pending_vblank_event *event,
+			      uint32_t flags,
+			      struct drm_modeset_acquire_ctx *ctx)
+{
+	if (flags & DRM_MODE_PAGE_FLIP_ASYNC) {
+		DRM_ERROR("Async flips aren't allowed\n");
+		return -EINVAL;
+	}
+
+	return drm_atomic_helper_page_flip(crtc, fb, event, flags, ctx);
+}
+
+static struct drm_crtc_state *
+vc4_fkms_crtc_duplicate_state(struct drm_crtc *crtc)
+{
+	struct vc4_crtc_state *vc4_state, *old_vc4_state;
+
+	vc4_state = kzalloc(sizeof(*vc4_state), GFP_KERNEL);
+	if (!vc4_state)
+		return NULL;
+
+	old_vc4_state = to_vc4_crtc_state(crtc->state);
+	vc4_state->margins = old_vc4_state->margins;
+
+	__drm_atomic_helper_crtc_duplicate_state(crtc, &vc4_state->base);
+	return &vc4_state->base;
+}
+
+static void
+vc4_fkms_crtc_reset(struct drm_crtc *crtc)
+{
+	if (crtc->state)
+		__drm_atomic_helper_crtc_destroy_state(crtc->state);
+
+	crtc->state = kzalloc(sizeof(*crtc->state), GFP_KERNEL);
+	if (crtc->state)
+		crtc->state->crtc = crtc;
+}
+
+static int vc4_fkms_enable_vblank(struct drm_crtc *crtc)
+{
+	struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
+
+	DRM_DEBUG_KMS("[CRTC:%d] enable_vblank.\n",
+		      crtc->base.id);
+	vc4_crtc->vblank_enabled = true;
+
+	return 0;
+}
+
+static void vc4_fkms_disable_vblank(struct drm_crtc *crtc)
+{
+	struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
+
+	DRM_DEBUG_KMS("[CRTC:%d] disable_vblank.\n",
+		      crtc->base.id);
+	vc4_crtc->vblank_enabled = false;
+}
+
+static const struct drm_crtc_funcs vc4_crtc_funcs = {
+	.set_config = drm_atomic_helper_set_config,
+	.destroy = drm_crtc_cleanup,
+	.page_flip = vc4_fkms_page_flip,
+	.set_property = NULL,
+	.cursor_set = NULL, /* handled by drm_mode_cursor_universal */
+	.cursor_move = NULL, /* handled by drm_mode_cursor_universal */
+	.reset = vc4_fkms_crtc_reset,
+	.atomic_duplicate_state = vc4_fkms_crtc_duplicate_state,
+	.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
+	.enable_vblank = vc4_fkms_enable_vblank,
+	.disable_vblank = vc4_fkms_disable_vblank,
+};
+
+static const struct drm_crtc_helper_funcs vc4_crtc_helper_funcs = {
+	.mode_set_nofb = vc4_crtc_mode_set_nofb,
+	.mode_valid = vc4_crtc_mode_valid,
+	.atomic_check = vc4_crtc_atomic_check,
+	.atomic_flush = vc4_crtc_atomic_flush,
+	.atomic_enable = vc4_crtc_enable,
+	.atomic_disable = vc4_crtc_disable,
+};
+
+static const struct of_device_id vc4_firmware_kms_dt_match[] = {
+	{ .compatible = "raspberrypi,rpi-firmware-kms" },
+	{ .compatible = "raspberrypi,rpi-firmware-kms-2711",
+	  .data = (void *)1 },
+	{}
+};
+
+static enum drm_connector_status
+vc4_fkms_connector_detect(struct drm_connector *connector, bool force)
+{
+	DRM_DEBUG_KMS("connector detect.\n");
+	return connector_status_connected;
+}
+
+/* Queries the firmware to populate a drm_mode structure for this display */
+static int vc4_fkms_get_fw_mode(struct vc4_fkms_connector *fkms_connector,
+				struct drm_display_mode *mode)
+{
+	struct vc4_dev *vc4 = fkms_connector->vc4_dev;
+	struct set_timings timings = { 0 };
+	int ret;
+
+	timings.display = fkms_connector->display_number;
+
+	ret = rpi_firmware_property(vc4->firmware,
+				    RPI_FIRMWARE_GET_DISPLAY_TIMING, &timings,
+				    sizeof(timings));
+	if (ret || !timings.clock)
+		/* No mode returned - abort */
+		return -1;
+
+	/* Equivalent to DRM_MODE macro. */
+	memset(mode, 0, sizeof(*mode));
+	strncpy(mode->name, "FIXED_MODE", sizeof(mode->name));
+	mode->status = 0;
+	mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
+	mode->clock = timings.clock;
+	mode->hdisplay = timings.hdisplay;
+	mode->hsync_start = timings.hsync_start;
+	mode->hsync_end = timings.hsync_end;
+	mode->htotal = timings.htotal;
+	mode->hskew = 0;
+	mode->vdisplay = timings.vdisplay;
+	mode->vsync_start = timings.vsync_start;
+	mode->vsync_end = timings.vsync_end;
+	mode->vtotal = timings.vtotal;
+	mode->vscan = timings.vscan;
+
+	if (timings.flags & TIMINGS_FLAGS_H_SYNC_POS)
+		mode->flags |= DRM_MODE_FLAG_PHSYNC;
+	else
+		mode->flags |= DRM_MODE_FLAG_NHSYNC;
+
+	if (timings.flags & TIMINGS_FLAGS_V_SYNC_POS)
+		mode->flags |= DRM_MODE_FLAG_PVSYNC;
+	else
+		mode->flags |= DRM_MODE_FLAG_NVSYNC;
+
+	if (timings.flags & TIMINGS_FLAGS_INTERLACE)
+		mode->flags |= DRM_MODE_FLAG_INTERLACE;
+
+	return 0;
+}
+
+static int vc4_fkms_get_edid_block(void *data, u8 *buf, unsigned int block,
+				   size_t len)
+{
+	struct vc4_fkms_connector *fkms_connector =
+					(struct vc4_fkms_connector *)data;
+	struct vc4_dev *vc4 = fkms_connector->vc4_dev;
+	struct mailbox_get_edid mb = {
+		.tag1 = { RPI_FIRMWARE_GET_EDID_BLOCK_DISPLAY,
+			  128 + 8, 0 },
+		.block = block,
+		.display_number = fkms_connector->display_number,
+	};
+	int ret = 0;
+
+	ret = rpi_firmware_property_list(vc4->firmware, &mb, sizeof(mb));
+
+	if (!ret)
+		memcpy(buf, mb.edid, len);
+
+	return ret;
+}
+
+static int vc4_fkms_connector_get_modes(struct drm_connector *connector)
+{
+	struct vc4_fkms_connector *fkms_connector =
+					to_vc4_fkms_connector(connector);
+	struct drm_encoder *encoder = fkms_connector->encoder;
+	struct vc4_fkms_encoder *vc4_encoder = to_vc4_fkms_encoder(encoder);
+	struct drm_display_mode fw_mode;
+	struct drm_display_mode *mode;
+	struct edid *edid;
+	int num_modes;
+
+	if (!vc4_fkms_get_fw_mode(fkms_connector, &fw_mode)) {
+		drm_mode_debug_printmodeline(&fw_mode);
+		mode = drm_mode_duplicate(connector->dev,
+					  &fw_mode);
+		drm_mode_probed_add(connector, mode);
+		num_modes = 1;	/* 1 mode */
+	} else {
+		edid = drm_do_get_edid(connector, vc4_fkms_get_edid_block,
+				       fkms_connector);
+
+		/* FIXME: Can we do CEC?
+		 * cec_s_phys_addr_from_edid(vc4->hdmi->cec_adap, edid);
+		 * if (!edid)
+		 *	return -ENODEV;
+		 */
+
+		vc4_encoder->hdmi_monitor = drm_detect_hdmi_monitor(edid);
+
+		drm_connector_update_edid_property(connector, edid);
+		num_modes = drm_add_edid_modes(connector, edid);
+		kfree(edid);
+	}
+
+	return num_modes;
+}
+
+/* This is the DSI panel resolution. Use this as a default should the firmware
+ * not respond to our request for the timings.
+ */
+static const struct drm_display_mode lcd_mode = {
+	DRM_MODE("800x480", DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED,
+		 25979400 / 1000,
+		 800, 800 + 1, 800 + 1 + 2, 800 + 1 + 2 + 46, 0,
+		 480, 480 + 7, 480 + 7 + 2, 480 + 7 + 2 + 21, 0,
+		 0)
+};
+
+static int vc4_fkms_lcd_connector_get_modes(struct drm_connector *connector)
+{
+	struct vc4_fkms_connector *fkms_connector =
+					to_vc4_fkms_connector(connector);
+	struct drm_display_mode *mode;
+	struct drm_display_mode fw_mode;
+
+	if (!vc4_fkms_get_fw_mode(fkms_connector, &fw_mode) && fw_mode.clock)
+		mode = drm_mode_duplicate(connector->dev,
+					  &fw_mode);
+	else
+		mode = drm_mode_duplicate(connector->dev,
+					  &lcd_mode);
+
+	if (!mode) {
+		DRM_ERROR("Failed to create a new display mode\n");
+		return -ENOMEM;
+	}
+
+	drm_mode_probed_add(connector, mode);
+
+	/* We have one mode */
+	return 1;
+}
+
+static struct drm_encoder *
+vc4_fkms_connector_best_encoder(struct drm_connector *connector)
+{
+	struct vc4_fkms_connector *fkms_connector =
+		to_vc4_fkms_connector(connector);
+	DRM_DEBUG_KMS("best_connector.\n");
+	return fkms_connector->encoder;
+}
+
+static void vc4_fkms_connector_destroy(struct drm_connector *connector)
+{
+	DRM_DEBUG_KMS("[CONNECTOR:%d] destroy.\n",
+		      connector->base.id);
+	drm_connector_unregister(connector);
+	drm_connector_cleanup(connector);
+}
+
+/**
+ * vc4_connector_duplicate_state - duplicate connector state
+ * @connector: digital connector
+ *
+ * Allocates and returns a copy of the connector state (both common and
+ * digital connector specific) for the specified connector.
+ *
+ * Returns: The newly allocated connector state, or NULL on failure.
+ */
+struct drm_connector_state *
+vc4_connector_duplicate_state(struct drm_connector *connector)
+{
+	struct vc4_fkms_connector_state *state;
+
+	state = kmemdup(connector->state, sizeof(*state), GFP_KERNEL);
+	if (!state)
+		return NULL;
+
+	__drm_atomic_helper_connector_duplicate_state(connector, &state->base);
+	return &state->base;
+}
+
+/**
+ * vc4_connector_atomic_get_property - hook for connector->atomic_get_property.
+ * @connector: Connector to get the property for.
+ * @state: Connector state to retrieve the property from.
+ * @property: Property to retrieve.
+ * @val: Return value for the property.
+ *
+ * Returns the atomic property value for a digital connector.
+ */
+int vc4_connector_atomic_get_property(struct drm_connector *connector,
+				      const struct drm_connector_state *state,
+				      struct drm_property *property,
+				      uint64_t *val)
+{
+	struct vc4_fkms_connector *fkms_connector =
+					to_vc4_fkms_connector(connector);
+	struct vc4_fkms_connector_state *vc4_conn_state =
+					to_vc4_fkms_connector_state(state);
+
+	if (property == fkms_connector->broadcast_rgb_property) {
+		*val = vc4_conn_state->broadcast_rgb;
+	} else {
+		DRM_DEBUG_ATOMIC("Unknown property [PROP:%d:%s]\n",
+				 property->base.id, property->name);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/**
+ * vc4_connector_atomic_set_property - hook for connector->atomic_set_property.
+ * @connector: Connector to set the property for.
+ * @state: Connector state to set the property on.
+ * @property: Property to set.
+ * @val: New value for the property.
+ *
+ * Sets the atomic property value for a digital connector.
+ */
+int vc4_connector_atomic_set_property(struct drm_connector *connector,
+				      struct drm_connector_state *state,
+				      struct drm_property *property,
+				      uint64_t val)
+{
+	struct vc4_fkms_connector *fkms_connector =
+					to_vc4_fkms_connector(connector);
+	struct vc4_fkms_connector_state *vc4_conn_state =
+					to_vc4_fkms_connector_state(state);
+
+	if (property == fkms_connector->broadcast_rgb_property) {
+		vc4_conn_state->broadcast_rgb = val;
+		return 0;
+	}
+
+	DRM_DEBUG_ATOMIC("Unknown property [PROP:%d:%s]\n",
+			 property->base.id, property->name);
+	return -EINVAL;
+}
+
+int vc4_connector_atomic_check(struct drm_connector *connector,
+			       struct drm_atomic_state *state)
+{
+	struct drm_connector_state *old_state =
+		drm_atomic_get_old_connector_state(state, connector);
+	struct vc4_fkms_connector_state *vc4_old_state =
+					to_vc4_fkms_connector_state(old_state);
+	struct drm_connector_state *new_state =
+		drm_atomic_get_new_connector_state(state, connector);
+	struct vc4_fkms_connector_state *vc4_new_state =
+					to_vc4_fkms_connector_state(new_state);
+	struct drm_crtc *crtc = new_state->crtc;
+
+	if (!crtc)
+		return 0;
+
+	if (vc4_old_state->broadcast_rgb != vc4_new_state->broadcast_rgb) {
+		struct drm_crtc_state *crtc_state;
+
+		crtc_state = drm_atomic_get_crtc_state(state, crtc);
+		if (IS_ERR(crtc_state))
+			return PTR_ERR(crtc_state);
+
+		crtc_state->mode_changed = true;
+	}
+	return 0;
+}
+
+static void vc4_hdmi_connector_reset(struct drm_connector *connector)
+{
+	drm_atomic_helper_connector_reset(connector);
+	drm_atomic_helper_connector_tv_reset(connector);
+}
+
+static const struct drm_connector_funcs vc4_fkms_connector_funcs = {
+	.detect = vc4_fkms_connector_detect,
+	.fill_modes = drm_helper_probe_single_connector_modes,
+	.destroy = vc4_fkms_connector_destroy,
+	.reset = vc4_hdmi_connector_reset,
+	.atomic_duplicate_state = vc4_connector_duplicate_state,
+	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+	.atomic_get_property = vc4_connector_atomic_get_property,
+	.atomic_set_property = vc4_connector_atomic_set_property,
+};
+
+static const struct drm_connector_helper_funcs vc4_fkms_connector_helper_funcs = {
+	.get_modes = vc4_fkms_connector_get_modes,
+	.best_encoder = vc4_fkms_connector_best_encoder,
+	.atomic_check = vc4_connector_atomic_check,
+};
+
+static const struct drm_connector_helper_funcs vc4_fkms_lcd_conn_helper_funcs = {
+	.get_modes = vc4_fkms_lcd_connector_get_modes,
+	.best_encoder = vc4_fkms_connector_best_encoder,
+};
+
+static const struct drm_prop_enum_list broadcast_rgb_names[] = {
+	{ VC4_BROADCAST_RGB_AUTO, "Automatic" },
+	{ VC4_BROADCAST_RGB_FULL, "Full" },
+	{ VC4_BROADCAST_RGB_LIMITED, "Limited 16:235" },
+};
+
+static void
+vc4_attach_broadcast_rgb_property(struct vc4_fkms_connector *fkms_connector)
+{
+	struct drm_device *dev = fkms_connector->base.dev;
+	struct drm_property *prop;
+
+	prop = fkms_connector->broadcast_rgb_property;
+	if (!prop) {
+		prop = drm_property_create_enum(dev, DRM_MODE_PROP_ENUM,
+						"Broadcast RGB",
+						broadcast_rgb_names,
+						ARRAY_SIZE(broadcast_rgb_names));
+		if (!prop)
+			return;
+
+		fkms_connector->broadcast_rgb_property = prop;
+	}
+
+	drm_object_attach_property(&fkms_connector->base.base, prop, 0);
+}
+
+static struct drm_connector *
+vc4_fkms_connector_init(struct drm_device *dev, struct drm_encoder *encoder,
+			u32 display_num)
+{
+	struct drm_connector *connector = NULL;
+	struct vc4_fkms_connector *fkms_connector;
+	struct vc4_fkms_connector_state *conn_state = NULL;
+	struct vc4_dev *vc4_dev = to_vc4_dev(dev);
+	int ret = 0;
+
+	DRM_DEBUG_KMS("connector_init, display_num %u\n", display_num);
+
+	fkms_connector = devm_kzalloc(dev->dev, sizeof(*fkms_connector),
+				      GFP_KERNEL);
+	if (!fkms_connector)
+		return ERR_PTR(-ENOMEM);
+
+	/*
+	 * Allocate enough memory to hold vc4_fkms_connector_state,
+	 */
+	conn_state = kzalloc(sizeof(*conn_state), GFP_KERNEL);
+	if (!conn_state) {
+		kfree(fkms_connector);
+		return ERR_PTR(-ENOMEM);
+	}
+
+	connector = &fkms_connector->base;
+
+	fkms_connector->encoder = encoder;
+	fkms_connector->display_number = display_num;
+	fkms_connector->display_type = vc4_get_display_type(display_num);
+	fkms_connector->vc4_dev = vc4_dev;
+
+	__drm_atomic_helper_connector_reset(connector,
+					    &conn_state->base);
+
+	if (fkms_connector->display_type == DRM_MODE_ENCODER_DSI) {
+		drm_connector_init(dev, connector, &vc4_fkms_connector_funcs,
+				   DRM_MODE_CONNECTOR_DSI);
+		drm_connector_helper_add(connector,
+					 &vc4_fkms_lcd_conn_helper_funcs);
+		connector->interlace_allowed = 0;
+	} else if (fkms_connector->display_type == DRM_MODE_ENCODER_TVDAC) {
+		drm_connector_init(dev, connector, &vc4_fkms_connector_funcs,
+				   DRM_MODE_CONNECTOR_Composite);
+		drm_connector_helper_add(connector,
+					 &vc4_fkms_lcd_conn_helper_funcs);
+		connector->interlace_allowed = 1;
+	} else {
+		drm_connector_init(dev, connector, &vc4_fkms_connector_funcs,
+				   DRM_MODE_CONNECTOR_HDMIA);
+		drm_connector_helper_add(connector,
+					 &vc4_fkms_connector_helper_funcs);
+		connector->interlace_allowed = 1;
+	}
+
+	ret = drm_mode_create_tv_margin_properties(dev);
+	if (ret)
+		goto fail;
+
+	drm_connector_attach_tv_margin_properties(connector);
+
+	connector->polled = (DRM_CONNECTOR_POLL_CONNECT |
+			     DRM_CONNECTOR_POLL_DISCONNECT);
+
+	connector->doublescan_allowed = 0;
+
+	vc4_attach_broadcast_rgb_property(fkms_connector);
+
+	drm_connector_attach_encoder(connector, encoder);
+
+	return connector;
+
+ fail:
+	if (connector)
+		vc4_fkms_connector_destroy(connector);
+
+	return ERR_PTR(ret);
+}
+
+static void vc4_fkms_encoder_destroy(struct drm_encoder *encoder)
+{
+	DRM_DEBUG_KMS("Encoder_destroy\n");
+	drm_encoder_cleanup(encoder);
+}
+
+static const struct drm_encoder_funcs vc4_fkms_encoder_funcs = {
+	.destroy = vc4_fkms_encoder_destroy,
+};
+
+static void vc4_fkms_display_power(struct drm_encoder *encoder, bool power)
+{
+	struct vc4_fkms_encoder *vc4_encoder = to_vc4_fkms_encoder(encoder);
+	struct vc4_dev *vc4 = to_vc4_dev(encoder->dev);
+
+	struct mailbox_display_pwr pwr = {
+		.tag1 = {RPI_FIRMWARE_SET_DISPLAY_POWER, 8, 0, },
+		.display = vc4_encoder->display_num,
+		.state = power ? 1 : 0,
+	};
+
+	rpi_firmware_property_list(vc4->firmware, &pwr, sizeof(pwr));
+}
+
+static void vc4_fkms_encoder_enable(struct drm_encoder *encoder)
+{
+	vc4_fkms_display_power(encoder, true);
+	DRM_DEBUG_KMS("Encoder_enable\n");
+}
+
+static void vc4_fkms_encoder_disable(struct drm_encoder *encoder)
+{
+	vc4_fkms_display_power(encoder, false);
+	DRM_DEBUG_KMS("Encoder_disable\n");
+}
+
+static const struct drm_encoder_helper_funcs vc4_fkms_encoder_helper_funcs = {
+	.enable = vc4_fkms_encoder_enable,
+	.disable = vc4_fkms_encoder_disable,
+};
+
+static int vc4_fkms_create_screen(struct device *dev, struct drm_device *drm,
+				  int display_idx, int display_ref,
+				  struct vc4_crtc **ret_crtc)
+{
+	struct vc4_dev *vc4 = to_vc4_dev(drm);
+	struct vc4_crtc *vc4_crtc;
+	struct vc4_fkms_encoder *vc4_encoder;
+	struct drm_crtc *crtc;
+	struct drm_plane *destroy_plane, *temp;
+	struct mailbox_blank_display blank = {
+		.tag1 = {RPI_FIRMWARE_FRAMEBUFFER_SET_DISPLAY_NUM, 4, 0, },
+		.display = display_idx,
+		.tag2 = { RPI_FIRMWARE_FRAMEBUFFER_BLANK, 4, 0, },
+		.blank = 1,
+	};
+	struct drm_plane *planes[PLANES_PER_CRTC];
+	int ret, i;
+
+	vc4_crtc = devm_kzalloc(dev, sizeof(*vc4_crtc), GFP_KERNEL);
+	if (!vc4_crtc)
+		return -ENOMEM;
+	crtc = &vc4_crtc->base;
+
+	vc4_crtc->display_number = display_ref;
+	vc4_crtc->display_type = vc4_get_display_type(display_ref);
+
+	/* Blank the firmware provided framebuffer */
+	rpi_firmware_property_list(vc4->firmware, &blank, sizeof(blank));
+
+	for (i = 0; i < PLANES_PER_CRTC; i++) {
+		planes[i] = vc4_fkms_plane_init(drm,
+						(i == 0) ?
+						  DRM_PLANE_TYPE_PRIMARY :
+						  (i == PLANES_PER_CRTC - 1) ?
+							DRM_PLANE_TYPE_CURSOR :
+							DRM_PLANE_TYPE_OVERLAY,
+						display_ref,
+						i + (display_idx * PLANES_PER_CRTC)
+					       );
+		if (IS_ERR(planes[i])) {
+			dev_err(dev, "failed to construct plane %u\n", i);
+			ret = PTR_ERR(planes[i]);
+			goto err;
+		}
+	}
+
+	drm_crtc_init_with_planes(drm, crtc, planes[0],
+				  planes[PLANES_PER_CRTC - 1], &vc4_crtc_funcs,
+				  NULL);
+	drm_crtc_helper_add(crtc, &vc4_crtc_helper_funcs);
+
+	/* Update the possible_crtcs mask for the overlay plane(s) */
+	for (i = 1; i < (PLANES_PER_CRTC - 1); i++)
+		planes[i]->possible_crtcs = drm_crtc_mask(crtc);
+
+	vc4_encoder = devm_kzalloc(dev, sizeof(*vc4_encoder), GFP_KERNEL);
+	if (!vc4_encoder)
+		return -ENOMEM;
+	vc4_crtc->encoder = &vc4_encoder->base;
+
+	vc4_encoder->display_num = display_ref;
+	vc4_encoder->base.possible_crtcs |= drm_crtc_mask(crtc);
+
+	drm_encoder_init(drm, &vc4_encoder->base, &vc4_fkms_encoder_funcs,
+			 vc4_crtc->display_type, NULL);
+	drm_encoder_helper_add(&vc4_encoder->base,
+			       &vc4_fkms_encoder_helper_funcs);
+
+	vc4_crtc->connector = vc4_fkms_connector_init(drm, &vc4_encoder->base,
+						      display_ref);
+	if (IS_ERR(vc4_crtc->connector)) {
+		ret = PTR_ERR(vc4_crtc->connector);
+		goto err_destroy_encoder;
+	}
+
+	*ret_crtc = vc4_crtc;
+
+	return 0;
+
+err_destroy_encoder:
+	vc4_fkms_encoder_destroy(vc4_crtc->encoder);
+	list_for_each_entry_safe(destroy_plane, temp,
+				 &drm->mode_config.plane_list, head) {
+		if (destroy_plane->possible_crtcs == 1 << drm_crtc_index(crtc))
+			destroy_plane->funcs->destroy(destroy_plane);
+	}
+err:
+	return ret;
+}
+
+static int vc4_fkms_bind(struct device *dev, struct device *master, void *data)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct drm_device *drm = dev_get_drvdata(master);
+	struct vc4_dev *vc4 = to_vc4_dev(drm);
+	struct device_node *firmware_node;
+	const struct of_device_id *match;
+	struct vc4_crtc **crtc_list;
+	u32 num_displays, display_num;
+	struct vc4_fkms *fkms;
+	int ret;
+	u32 display_id;
+
+	vc4->firmware_kms = true;
+
+	fkms = devm_kzalloc(dev, sizeof(*fkms), GFP_KERNEL);
+	if (!fkms)
+		return -ENOMEM;
+
+	match = of_match_device(vc4_firmware_kms_dt_match, dev);
+	if (!match)
+		return -ENODEV;
+	if (match->data)
+		fkms->bcm2711 = true;
+
+	firmware_node = of_parse_phandle(dev->of_node, "brcm,firmware", 0);
+	vc4->firmware = rpi_firmware_get(firmware_node);
+	if (!vc4->firmware) {
+		DRM_DEBUG("Failed to get Raspberry Pi firmware reference.\n");
+		return -EPROBE_DEFER;
+	}
+	of_node_put(firmware_node);
+
+	ret = rpi_firmware_property(vc4->firmware,
+				    RPI_FIRMWARE_FRAMEBUFFER_GET_NUM_DISPLAYS,
+				    &num_displays, sizeof(u32));
+
+	/* If we fail to get the number of displays, then
+	 * assume old firmware that doesn't have the mailbox call, so just
+	 * set one display
+	 */
+	if (ret) {
+		num_displays = 1;
+		DRM_WARN("Unable to determine number of displays - assuming 1\n");
+		ret = 0;
+	}
+
+	ret = rpi_firmware_property(vc4->firmware,
+				    RPI_FIRMWARE_GET_DISPLAY_CFG,
+				    &fkms->cfg, sizeof(fkms->cfg));
+
+	if (ret)
+		return -EINVAL;
+	/* The firmware works in Hz. This will be compared against kHz, so div
+	 * 1000 now rather than multiple times later.
+	 */
+	fkms->cfg.max_pixel_clock[0] /= 1000;
+	fkms->cfg.max_pixel_clock[1] /= 1000;
+
+	/* Allocate a list, with space for a NULL on the end */
+	crtc_list = devm_kzalloc(dev, sizeof(crtc_list) * (num_displays + 1),
+				 GFP_KERNEL);
+	if (!crtc_list)
+		return -ENOMEM;
+
+	for (display_num = 0; display_num < num_displays; display_num++) {
+		display_id = display_num;
+		ret = rpi_firmware_property(vc4->firmware,
+					    RPI_FIRMWARE_FRAMEBUFFER_GET_DISPLAY_ID,
+					    &display_id, sizeof(display_id));
+		/* FIXME: Determine the correct error handling here.
+		 * Should we fail to create the one "screen" but keep the
+		 * others, or fail the whole thing?
+		 */
+		if (ret)
+			DRM_ERROR("Failed to get display id %u\n", display_num);
+
+		ret = vc4_fkms_create_screen(dev, drm, display_num, display_id,
+					     &crtc_list[display_num]);
+		if (ret)
+			DRM_ERROR("Oh dear, failed to create display %u\n",
+				  display_num);
+	}
+
+	if (num_displays > 0) {
+		/* Map the SMI interrupt reg */
+		crtc_list[0]->regs = vc4_ioremap_regs(pdev, 0);
+		if (IS_ERR(crtc_list[0]->regs))
+			DRM_ERROR("Oh dear, failed to map registers\n");
+
+		writel(0, crtc_list[0]->regs + SMICS);
+		ret = devm_request_irq(dev, platform_get_irq(pdev, 0),
+				       vc4_crtc_irq_handler, 0,
+				       "vc4 firmware kms", crtc_list);
+		if (ret)
+			DRM_ERROR("Oh dear, failed to register IRQ\n");
+	} else {
+		DRM_WARN("No displays found. Consider forcing hotplug if HDMI is attached\n");
+	}
+
+	vc4->fkms = fkms;
+
+	platform_set_drvdata(pdev, crtc_list);
+
+	return 0;
+}
+
+static void vc4_fkms_unbind(struct device *dev, struct device *master,
+			    void *data)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct vc4_crtc **crtc_list = dev_get_drvdata(dev);
+	int i;
+
+	for (i = 0; crtc_list[i]; i++) {
+		vc4_fkms_connector_destroy(crtc_list[i]->connector);
+		vc4_fkms_encoder_destroy(crtc_list[i]->encoder);
+		drm_crtc_cleanup(&crtc_list[i]->base);
+	}
+
+	platform_set_drvdata(pdev, NULL);
+}
+
+static const struct component_ops vc4_fkms_ops = {
+	.bind   = vc4_fkms_bind,
+	.unbind = vc4_fkms_unbind,
+};
+
+static int vc4_fkms_probe(struct platform_device *pdev)
+{
+	return component_add(&pdev->dev, &vc4_fkms_ops);
+}
+
+static int vc4_fkms_remove(struct platform_device *pdev)
+{
+	component_del(&pdev->dev, &vc4_fkms_ops);
+	return 0;
+}
+
+struct platform_driver vc4_firmware_kms_driver = {
+	.probe = vc4_fkms_probe,
+	.remove = vc4_fkms_remove,
+	.driver = {
+		.name = "vc4_firmware_kms",
+		.of_match_table = vc4_firmware_kms_dt_match,
+	},
+};
diff -Nur --no-dereference linux-5.10.52-orig/drivers/gpu/drm/vc4/vc4_gem.c linux-5.10.52-v7l+/drivers/gpu/drm/vc4/vc4_gem.c
--- linux-5.10.52-orig/drivers/gpu/drm/vc4/vc4_gem.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/gpu/drm/vc4/vc4_gem.c	2021-07-25 16:46:01.698362435 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1029 @
 		       void (*func)(struct vc4_seqno_cb *cb))
 {
 	struct vc4_dev *vc4 = to_vc4_dev(dev);
-	int ret = 0;
 	unsigned long irqflags;
 
 	cb->func = func;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1043 @
 	}
 	spin_unlock_irqrestore(&vc4->job_lock, irqflags);
 
-	return ret;
+	return 0;
 }
 
 /* Scheduled when any job has been completed, this walks the list of
diff -Nur --no-dereference linux-5.10.52-orig/drivers/gpu/drm/vc4/vc4_hdmi.c linux-5.10.52-v7l+/drivers/gpu/drm/vc4/vc4_hdmi.c
--- linux-5.10.52-orig/drivers/gpu/drm/vc4/vc4_hdmi.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/gpu/drm/vc4/vc4_hdmi.c	2021-07-25 16:46:01.698362435 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:38 @
 #include <drm/drm_edid.h>
 #include <drm/drm_probe_helper.h>
 #include <drm/drm_simple_kms_helper.h>
+#include <drm/drm_scdc_helper.h>
 #include <linux/clk.h>
 #include <linux/component.h>
 #include <linux/i2c.h>
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:48 @
 #include <linux/pm_runtime.h>
 #include <linux/rational.h>
 #include <linux/reset.h>
+#include <sound/asoundef.h>
 #include <sound/dmaengine_pcm.h>
+#include <sound/hdmi-codec.h>
 #include <sound/pcm_drm_eld.h>
 #include <sound/pcm_params.h>
 #include <sound/soc.h>
+#include <sound/tlv.h>
 #include "media/cec.h"
 #include "vc4_drv.h"
 #include "vc4_hdmi.h"
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:83 @
 #define VC5_HDMI_VERTB_VSPO_SHIFT		16
 #define VC5_HDMI_VERTB_VSPO_MASK		VC4_MASK(29, 16)
 
+#define VC5_HDMI_SCRAMBLER_CTL_ENABLE		BIT(0)
+
+#define VC5_HDMI_DEEP_COLOR_CONFIG_1_INIT_PACK_PHASE_SHIFT	8
+#define VC5_HDMI_DEEP_COLOR_CONFIG_1_INIT_PACK_PHASE_MASK	VC4_MASK(10, 8)
+
+#define VC5_HDMI_DEEP_COLOR_CONFIG_1_COLOR_DEPTH_SHIFT		0
+#define VC5_HDMI_DEEP_COLOR_CONFIG_1_COLOR_DEPTH_MASK		VC4_MASK(3, 0)
+
+#define VC5_HDMI_GCP_CONFIG_GCP_ENABLE		BIT(31)
+
+#define VC5_HDMI_GCP_WORD_1_GCP_SUBPACKET_BYTE_1_SHIFT	8
+#define VC5_HDMI_GCP_WORD_1_GCP_SUBPACKET_BYTE_1_MASK	VC4_MASK(15, 8)
+
 # define VC4_HD_M_SW_RST			BIT(2)
 # define VC4_HD_M_ENABLE			BIT(0)
 
 #define CEC_CLOCK_FREQ 40000
-#define VC4_HSM_MID_CLOCK 149985000
+#define HDMI_14_MAX_TMDS_CLK   (340 * 1000 * 1000)
+
+static bool vc4_hdmi_mode_needs_scrambling(const struct drm_display_mode *mode)
+{
+	return (mode->clock * 1000) > HDMI_14_MAX_TMDS_CLK;
+}
 
 static int vc4_hdmi_debugfs_regs(struct seq_file *m, void *unused)
 {
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:115 @
 
 	drm_print_regset32(&p, &vc4_hdmi->hdmi_regset);
 	drm_print_regset32(&p, &vc4_hdmi->hd_regset);
+	drm_print_regset32(&p, &vc4_hdmi->cec_regset);
+	drm_print_regset32(&p, &vc4_hdmi->csc_regset);
+	drm_print_regset32(&p, &vc4_hdmi->dvp_regset);
+	drm_print_regset32(&p, &vc4_hdmi->phy_regset);
+	drm_print_regset32(&p, &vc4_hdmi->ram_regset);
+	drm_print_regset32(&p, &vc4_hdmi->rm_regset);
 
 	return 0;
 }
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:163 @
 	 * Set the clock divider: the hsm_clock rate and this divider
 	 * setting will give a 40 kHz CEC clock.
 	 */
-	clk_cnt = clk_get_rate(vc4_hdmi->hsm_clock) / CEC_CLOCK_FREQ;
+	clk_cnt = clk_get_rate(vc4_hdmi->cec_clock) / CEC_CLOCK_FREQ;
 	value |= clk_cnt << VC4_HDMI_CEC_DIV_CLK_CNT_SHIFT;
 	HDMI_WRITE(HDMI_CEC_CNTRL_1, value);
 }
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:171 @
 static void vc4_hdmi_cec_update_clk_div(struct vc4_hdmi *vc4_hdmi) {}
 #endif
 
+static void vc4_hdmi_enable_scrambling(struct drm_encoder *encoder);
+
 static enum drm_connector_status
 vc4_hdmi_connector_detect(struct drm_connector *connector, bool force)
 {
 	struct vc4_hdmi *vc4_hdmi = connector_to_vc4_hdmi(connector);
+	enum drm_connector_status ret = connector_status_disconnected;
 	bool connected = false;
 
 	WARN_ON(pm_runtime_resume_and_get(&vc4_hdmi->pdev->dev));
+	WARN_ON(clk_prepare_enable(vc4_hdmi->hsm_clock));
 
 	if (vc4_hdmi->hpd_gpio) {
 		if (gpio_get_value_cansleep(vc4_hdmi->hpd_gpio) ^
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:204 @
 			}
 		}
 
-		pm_runtime_put(&vc4_hdmi->pdev->dev);
-		return connector_status_connected;
+		vc4_hdmi_enable_scrambling(&vc4_hdmi->encoder.base.base);
+
+		ret = connector_status_connected;
+		goto out;
 	}
 
 	cec_phys_addr_invalidate(vc4_hdmi->cec_adap);
+
+out:
+	clk_disable_unprepare(vc4_hdmi->hsm_clock);
 	pm_runtime_put(&vc4_hdmi->pdev->dev);
-	return connector_status_disconnected;
+	return ret;
 }
 
 static void vc4_hdmi_connector_destroy(struct drm_connector *connector)
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:242 @
 	ret = drm_add_edid_modes(connector, edid);
 	kfree(edid);
 
+	if (vc4_hdmi->disable_4kp60) {
+		struct drm_device *drm = connector->dev;
+		struct drm_display_mode *mode;
+
+		list_for_each_entry(mode, &connector->probed_modes, head) {
+			if (vc4_hdmi_mode_needs_scrambling(mode)) {
+				drm_warn_once(drm, "The core clock cannot reach frequencies high enough to support 4k @ 60Hz.");
+				drm_warn_once(drm, "Please change your config.txt file to add hdmi_enable_4kp60.");
+			}
+		}
+	}
+
 	return ret;
 }
 
+static int vc4_hdmi_connector_atomic_check(struct drm_connector *connector,
+					  struct drm_atomic_state *state)
+{
+	struct drm_connector_state *old_state =
+		drm_atomic_get_old_connector_state(state, connector);
+	struct drm_connector_state *new_state =
+		drm_atomic_get_new_connector_state(state, connector);
+	struct drm_crtc *crtc = new_state->crtc;
+
+	if (!crtc)
+		return 0;
+
+	if (old_state->colorspace != new_state->colorspace ||
+	    !drm_connector_atomic_hdr_metadata_equal(old_state, new_state)) {
+		struct drm_crtc_state *crtc_state;
+
+		crtc_state = drm_atomic_get_crtc_state(state, crtc);
+		if (IS_ERR(crtc_state))
+			return PTR_ERR(crtc_state);
+
+		crtc_state->mode_changed = true;
+	}
+
+	return 0;
+}
+
 static void vc4_hdmi_connector_reset(struct drm_connector *connector)
 {
-	drm_atomic_helper_connector_reset(connector);
+	struct vc4_hdmi_connector_state *old_state =
+		conn_state_to_vc4_hdmi_conn_state(connector->state);
+	struct vc4_hdmi_connector_state *new_state =
+		kzalloc(sizeof(*new_state), GFP_KERNEL);
+
+	if (connector->state)
+		__drm_atomic_helper_connector_destroy_state(connector->state);
+
+	kfree(old_state);
+	__drm_atomic_helper_connector_reset(connector, &new_state->base);
+
+	if (!new_state)
+		return;
+
+	new_state->base.max_bpc = 8;
+	new_state->base.max_requested_bpc = 8;
 	drm_atomic_helper_connector_tv_reset(connector);
 }
 
+static struct drm_connector_state *
+vc4_hdmi_connector_duplicate_state(struct drm_connector *connector)
+{
+	struct drm_connector_state *conn_state = connector->state;
+	struct vc4_hdmi_connector_state *vc4_state = conn_state_to_vc4_hdmi_conn_state(conn_state);
+	struct vc4_hdmi_connector_state *new_state;
+
+	new_state = kzalloc(sizeof(*new_state), GFP_KERNEL);
+	if (!new_state)
+		return NULL;
+
+	new_state->pixel_rate = vc4_state->pixel_rate;
+	__drm_atomic_helper_connector_duplicate_state(connector, &new_state->base);
+
+	return &new_state->base;
+}
+
 static const struct drm_connector_funcs vc4_hdmi_connector_funcs = {
 	.detect = vc4_hdmi_connector_detect,
 	.fill_modes = drm_helper_probe_single_connector_modes,
 	.destroy = vc4_hdmi_connector_destroy,
 	.reset = vc4_hdmi_connector_reset,
-	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+	.atomic_duplicate_state = vc4_hdmi_connector_duplicate_state,
 	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
 };
 
 static const struct drm_connector_helper_funcs vc4_hdmi_connector_helper_funcs = {
 	.get_modes = vc4_hdmi_connector_get_modes,
+	.atomic_check = vc4_hdmi_connector_atomic_check,
 };
 
 static int vc4_hdmi_connector_init(struct drm_device *dev,
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:348 @
 				    vc4_hdmi->ddc);
 	drm_connector_helper_add(connector, &vc4_hdmi_connector_helper_funcs);
 
+	/*
+	 * Some of the properties below require access to state, like bpc.
+	 * Allocate some default initial connector state with our reset helper.
+	 */
+	if (connector->funcs->reset)
+		connector->funcs->reset(connector);
+
 	/* Create and attach TV margin props to this connector. */
 	ret = drm_mode_create_tv_margin_properties(dev);
 	if (ret)
 		return ret;
 
+	ret = drm_mode_create_hdmi_colorspace_property(connector);
+	if (ret)
+		return ret;
+
+	drm_connector_attach_colorspace_property(connector);
 	drm_connector_attach_tv_margin_properties(connector);
+	drm_connector_attach_max_bpc_property(connector, 8, 12);
 
 	connector->polled = (DRM_CONNECTOR_POLL_CONNECT |
 			     DRM_CONNECTOR_POLL_DISCONNECT);
 
 	connector->interlace_allowed = 1;
 	connector->doublescan_allowed = 0;
+	connector->stereo_allowed = 1;
+
+	if (vc4_hdmi->variant->supports_hdr)
+		drm_connector_attach_hdr_output_metadata_property(connector);
 
 	drm_connector_attach_encoder(connector, encoder);
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:384 @
 }
 
 static int vc4_hdmi_stop_packet(struct drm_encoder *encoder,
-				enum hdmi_infoframe_type type)
+				enum hdmi_infoframe_type type,
+				bool poll)
 {
 	struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder);
 	u32 packet_id = type - 0x80;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:393 @
 	HDMI_WRITE(HDMI_RAM_PACKET_CONFIG,
 		   HDMI_READ(HDMI_RAM_PACKET_CONFIG) & ~BIT(packet_id));
 
+	if (!poll)
+		return 0;
+
 	return wait_for(!(HDMI_READ(HDMI_RAM_PACKET_STATUS) &
 			  BIT(packet_id)), 100);
 }
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:408 @
 	const struct vc4_hdmi_register *ram_packet_start =
 		&vc4_hdmi->variant->registers[HDMI_RAM_PACKET_START];
 	u32 packet_reg = ram_packet_start->offset + VC4_HDMI_PACKET_STRIDE * packet_id;
+	u32 packet_reg_next = ram_packet_start->offset +
+		VC4_HDMI_PACKET_STRIDE * (packet_id + 1);
 	void __iomem *base = __vc4_hdmi_get_field_base(vc4_hdmi,
 						       ram_packet_start->reg);
-	uint8_t buffer[VC4_HDMI_PACKET_STRIDE];
+	uint8_t buffer[VC4_HDMI_PACKET_STRIDE] = {};
 	ssize_t len, i;
 	int ret;
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:424 @
 	if (len < 0)
 		return;
 
-	ret = vc4_hdmi_stop_packet(encoder, frame->any.type);
+	ret = vc4_hdmi_stop_packet(encoder, frame->any.type, true);
 	if (ret) {
 		DRM_ERROR("Failed to wait for infoframe to go idle: %d\n", ret);
 		return;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:445 @
 		packet_reg += 4;
 	}
 
+	/*
+	 * clear remainder of packet ram as it's included in the
+	 * infoframe and triggers a checksum error on hdmi analyser
+	 */
+	for (; packet_reg < packet_reg_next; packet_reg += 4)
+		writel(0, base + packet_reg);
+
 	HDMI_WRITE(HDMI_RAM_PACKET_CONFIG,
 		   HDMI_READ(HDMI_RAM_PACKET_CONFIG) | BIT(packet_id));
 	ret = wait_for((HDMI_READ(HDMI_RAM_PACKET_STATUS) &
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:483 @
 					   vc4_encoder->limited_rgb_range ?
 					   HDMI_QUANTIZATION_RANGE_LIMITED :
 					   HDMI_QUANTIZATION_RANGE_FULL);
-
+	drm_hdmi_avi_infoframe_colorspace(&frame.avi, cstate);
 	drm_hdmi_avi_infoframe_bars(&frame.avi, cstate);
 
 	vc4_hdmi_write_infoframe(encoder, &frame);
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:508 @
 static void vc4_hdmi_set_audio_infoframe(struct drm_encoder *encoder)
 {
 	struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder);
+	struct hdmi_audio_infoframe *audio = &vc4_hdmi->audio.infoframe;
 	union hdmi_infoframe frame;
-	int ret;
 
-	ret = hdmi_audio_infoframe_init(&frame.audio);
+	memcpy(&frame.audio, audio, sizeof(*audio));
+	vc4_hdmi_write_infoframe(encoder, &frame);
+}
 
-	frame.audio.coding_type = HDMI_AUDIO_CODING_TYPE_STREAM;
-	frame.audio.sample_frequency = HDMI_AUDIO_SAMPLE_FREQUENCY_STREAM;
-	frame.audio.sample_size = HDMI_AUDIO_SAMPLE_SIZE_STREAM;
-	frame.audio.channels = vc4_hdmi->audio.channels;
+static void vc4_hdmi_set_hdr_infoframe(struct drm_encoder *encoder)
+{
+	struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder);
+	struct drm_connector *connector = &vc4_hdmi->connector;
+	struct drm_connector_state *conn_state = connector->state;
+	union hdmi_infoframe frame;
+
+	if (!vc4_hdmi->variant->supports_hdr)
+		return;
+
+	if (!conn_state->hdr_output_metadata)
+		return;
+
+	if (drm_hdmi_infoframe_set_hdr_metadata(&frame.drm, conn_state))
+		return;
 
 	vc4_hdmi_write_infoframe(encoder, &frame);
 }
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:546 @
 	 */
 	if (vc4_hdmi->audio.streaming)
 		vc4_hdmi_set_audio_infoframe(encoder);
+
+	vc4_hdmi_set_hdr_infoframe(encoder);
 }
 
-static void vc4_hdmi_encoder_post_crtc_disable(struct drm_encoder *encoder)
+static bool vc4_hdmi_supports_scrambling(struct drm_encoder *encoder,
+					 struct drm_display_mode *mode)
+{
+	struct vc4_hdmi_encoder *vc4_encoder = to_vc4_hdmi_encoder(encoder);
+	struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder);
+	struct drm_display_info *display = &vc4_hdmi->connector.display_info;
+
+	if (!vc4_encoder->hdmi_monitor)
+		return false;
+
+	if (!display->hdmi.scdc.supported ||
+	    !display->hdmi.scdc.scrambling.supported)
+		return false;
+
+	return true;
+}
+
+#define SCRAMBLING_POLLING_DELAY_MS	1000
+
+static void vc4_hdmi_enable_scrambling(struct drm_encoder *encoder)
+{
+	struct drm_display_mode *mode;
+	struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder);
+
+	if (!encoder->crtc || !encoder->crtc->state)
+		return;
+
+	mode = &encoder->crtc->state->adjusted_mode;
+	if (!vc4_hdmi_supports_scrambling(encoder, mode))
+		return;
+
+	if (!vc4_hdmi_mode_needs_scrambling(mode))
+		return;
+
+	drm_scdc_set_high_tmds_clock_ratio(vc4_hdmi->ddc, true);
+	drm_scdc_set_scrambling(vc4_hdmi->ddc, true);
+
+	HDMI_WRITE(HDMI_SCRAMBLER_CTL, HDMI_READ(HDMI_SCRAMBLER_CTL) |
+		   VC5_HDMI_SCRAMBLER_CTL_ENABLE);
+
+	queue_delayed_work(system_wq, &vc4_hdmi->scrambling_work,
+			   msecs_to_jiffies(SCRAMBLING_POLLING_DELAY_MS));
+}
+
+static void vc4_hdmi_disable_scrambling(struct drm_encoder *encoder)
+{
+	struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder);
+	struct drm_crtc *crtc = encoder->crtc;
+
+	/*
+	 * At boot, encoder->crtc will be NULL. Since we don't know the
+	 * state of the scrambler and in order to avoid any
+	 * inconsistency, let's disable it all the time.
+	 */
+	if (crtc && !vc4_hdmi_supports_scrambling(encoder, &crtc->mode))
+		return;
+
+	if (crtc && !vc4_hdmi_mode_needs_scrambling(&crtc->mode))
+		return;
+
+	if (delayed_work_pending(&vc4_hdmi->scrambling_work))
+		cancel_delayed_work_sync(&vc4_hdmi->scrambling_work);
+
+	HDMI_WRITE(HDMI_SCRAMBLER_CTL, HDMI_READ(HDMI_SCRAMBLER_CTL) &
+		   ~VC5_HDMI_SCRAMBLER_CTL_ENABLE);
+
+	drm_scdc_set_scrambling(vc4_hdmi->ddc, false);
+	drm_scdc_set_high_tmds_clock_ratio(vc4_hdmi->ddc, false);
+}
+
+static void vc4_hdmi_scrambling_wq(struct work_struct *work)
+{
+	struct vc4_hdmi *vc4_hdmi = container_of(to_delayed_work(work),
+						 struct vc4_hdmi,
+						 scrambling_work);
+
+	if (drm_scdc_get_scrambling_status(vc4_hdmi->ddc))
+		return;
+
+	drm_scdc_set_high_tmds_clock_ratio(vc4_hdmi->ddc, true);
+	drm_scdc_set_scrambling(vc4_hdmi->ddc, true);
+
+	queue_delayed_work(system_wq, &vc4_hdmi->scrambling_work,
+			   msecs_to_jiffies(SCRAMBLING_POLLING_DELAY_MS));
+}
+
+static void vc4_hdmi_encoder_post_crtc_disable(struct drm_encoder *encoder,
+					       struct drm_atomic_state *state)
 {
 	struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder);
 
 	HDMI_WRITE(HDMI_RAM_PACKET_CONFIG, 0);
 
-	HDMI_WRITE(HDMI_VID_CTL, HDMI_READ(HDMI_VID_CTL) |
-		   VC4_HD_VID_CTL_CLRRGB | VC4_HD_VID_CTL_CLRSYNC);
+	HDMI_WRITE(HDMI_VID_CTL, HDMI_READ(HDMI_VID_CTL) | VC4_HD_VID_CTL_CLRRGB);
+
+	mdelay(1);
 
 	HDMI_WRITE(HDMI_VID_CTL,
-		   HDMI_READ(HDMI_VID_CTL) | VC4_HD_VID_CTL_BLANKPIX);
+		   HDMI_READ(HDMI_VID_CTL) & ~VC4_HD_VID_CTL_ENABLE);
+	vc4_hdmi_disable_scrambling(encoder);
 }
 
-static void vc4_hdmi_encoder_post_crtc_powerdown(struct drm_encoder *encoder)
+static void vc4_hdmi_encoder_post_crtc_powerdown(struct drm_encoder *encoder,
+						 struct drm_atomic_state *state)
 {
 	struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder);
 	int ret;
 
+	HDMI_WRITE(HDMI_VID_CTL,
+		   HDMI_READ(HDMI_VID_CTL) | VC4_HD_VID_CTL_BLANKPIX);
+
 	if (vc4_hdmi->variant->phy_disable)
 		vc4_hdmi->variant->phy_disable(vc4_hdmi);
 
-	HDMI_WRITE(HDMI_VID_CTL,
-		   HDMI_READ(HDMI_VID_CTL) & ~VC4_HD_VID_CTL_ENABLE);
-
 	clk_disable_unprepare(vc4_hdmi->pixel_bvb_clock);
+	if (vc4_hdmi->bvb_req)
+		clk_request_done(vc4_hdmi->bvb_req);
+	clk_request_done(vc4_hdmi->hsm_req);
 	clk_disable_unprepare(vc4_hdmi->pixel_clock);
 
 	ret = pm_runtime_put(&vc4_hdmi->pdev->dev);
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:754 @
 }
 
 static void vc4_hdmi_set_timings(struct vc4_hdmi *vc4_hdmi,
+				 struct drm_connector_state *state,
 				 struct drm_display_mode *mode)
 {
 	bool hsync_pos = mode->flags & DRM_MODE_FLAG_PHSYNC;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:767 @
 				   VC4_HDMI_VERTA_VFP) |
 		     VC4_SET_FIELD(mode->crtc_vdisplay, VC4_HDMI_VERTA_VAL));
 	u32 vertb = (VC4_SET_FIELD(0, VC4_HDMI_VERTB_VSPO) |
-		     VC4_SET_FIELD(mode->crtc_vtotal - mode->crtc_vsync_end,
+		     VC4_SET_FIELD(mode->crtc_vtotal - mode->crtc_vsync_end +
+				   interlaced,
 				   VC4_HDMI_VERTB_VBP));
 	u32 vertb_even = (VC4_SET_FIELD(0, VC4_HDMI_VERTB_VSPO) |
 			  VC4_SET_FIELD(mode->crtc_vtotal -
-					mode->crtc_vsync_end -
-					interlaced,
+					mode->crtc_vsync_end,
 					VC4_HDMI_VERTB_VBP));
 
 	HDMI_WRITE(HDMI_HORZA,
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:798 @
 	HDMI_WRITE(HDMI_VERTB0, vertb_even);
 	HDMI_WRITE(HDMI_VERTB1, vertb);
 }
+
 static void vc5_hdmi_set_timings(struct vc4_hdmi *vc4_hdmi,
+				 struct drm_connector_state *state,
 				 struct drm_display_mode *mode)
 {
 	bool hsync_pos = mode->flags & DRM_MODE_FLAG_PHSYNC;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:813 @
 				   VC5_HDMI_VERTA_VFP) |
 		     VC4_SET_FIELD(mode->crtc_vdisplay, VC5_HDMI_VERTA_VAL));
 	u32 vertb = (VC4_SET_FIELD(0, VC5_HDMI_VERTB_VSPO) |
-		     VC4_SET_FIELD(mode->crtc_vtotal - mode->crtc_vsync_end,
+		     VC4_SET_FIELD(mode->crtc_vtotal - mode->crtc_vsync_end +
+				   interlaced,
 				   VC4_HDMI_VERTB_VBP));
 	u32 vertb_even = (VC4_SET_FIELD(0, VC5_HDMI_VERTB_VSPO) |
 			  VC4_SET_FIELD(mode->crtc_vtotal -
-					mode->crtc_vsync_end -
-					interlaced,
+					mode->crtc_vsync_end,
 					VC4_HDMI_VERTB_VBP));
+	unsigned char gcp;
+	bool gcp_en;
+	u32 reg;
 
 	HDMI_WRITE(HDMI_VEC_INTERFACE_XBAR, 0x354021);
 	HDMI_WRITE(HDMI_HORZA,
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:848 @
 	HDMI_WRITE(HDMI_VERTB0, vertb_even);
 	HDMI_WRITE(HDMI_VERTB1, vertb);
 
+	switch (state->max_bpc) {
+	case 12:
+		gcp = 6;
+		gcp_en = true;
+		break;
+	case 10:
+		gcp = 5;
+		gcp_en = true;
+		break;
+	case 8:
+	default:
+		gcp = 4;
+		gcp_en = false;
+		break;
+	}
+
+	reg = HDMI_READ(HDMI_DEEP_COLOR_CONFIG_1);
+	reg &= ~(VC5_HDMI_DEEP_COLOR_CONFIG_1_INIT_PACK_PHASE_MASK |
+		 VC5_HDMI_DEEP_COLOR_CONFIG_1_COLOR_DEPTH_MASK);
+	reg |= VC4_SET_FIELD(2, VC5_HDMI_DEEP_COLOR_CONFIG_1_INIT_PACK_PHASE) |
+	       VC4_SET_FIELD(gcp, VC5_HDMI_DEEP_COLOR_CONFIG_1_COLOR_DEPTH);
+	HDMI_WRITE(HDMI_DEEP_COLOR_CONFIG_1, reg);
+
+	reg = HDMI_READ(HDMI_GCP_WORD_1);
+	reg &= ~VC5_HDMI_GCP_WORD_1_GCP_SUBPACKET_BYTE_1_MASK;
+	reg |= VC4_SET_FIELD(gcp, VC5_HDMI_GCP_WORD_1_GCP_SUBPACKET_BYTE_1);
+	HDMI_WRITE(HDMI_GCP_WORD_1, reg);
+
+	reg = HDMI_READ(HDMI_GCP_CONFIG);
+	reg &= ~VC5_HDMI_GCP_CONFIG_GCP_ENABLE;
+	reg |= gcp_en ? VC5_HDMI_GCP_CONFIG_GCP_ENABLE : 0;
+	HDMI_WRITE(HDMI_GCP_CONFIG, reg);
+
 	HDMI_WRITE(HDMI_CLOCK_STOP, 0);
 }
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:908 @
 		  "VC4_HDMI_FIFO_CTL_RECENTER_DONE");
 }
 
-static void vc4_hdmi_encoder_pre_crtc_configure(struct drm_encoder *encoder)
+static void vc4_hdmi_encoder_pre_crtc_configure(struct drm_encoder *encoder,
+						struct drm_atomic_state *state)
 {
-	struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode;
 	struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder);
-	unsigned long pixel_rate, hsm_rate;
+	struct drm_connector *connector = &vc4_hdmi->connector;
+	struct drm_connector_state *conn_state =
+		drm_atomic_get_new_connector_state(state, connector);
+	struct vc4_hdmi_connector_state *vc4_conn_state =
+		conn_state_to_vc4_hdmi_conn_state(conn_state);
+	struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode;
+	unsigned long bvb_rate, pixel_rate, hsm_rate;
 	int ret;
 
 	ret = pm_runtime_resume_and_get(&vc4_hdmi->pdev->dev);
 	if (ret < 0) {
 		DRM_ERROR("Failed to retain power domain: %d\n", ret);
+		pm_runtime_put(&vc4_hdmi->pdev->dev);
 		return;
 	}
 
-	pixel_rate = mode->clock * 1000 * ((mode->flags & DRM_MODE_FLAG_DBLCLK) ? 2 : 1);
+	pixel_rate = vc4_conn_state->pixel_rate;
 	ret = clk_set_rate(vc4_hdmi->pixel_clock, pixel_rate);
 	if (ret) {
 		DRM_ERROR("Failed to set pixel clock rate: %d\n", ret);
+		pm_runtime_put(&vc4_hdmi->pdev->dev);
 		return;
 	}
 
 	ret = clk_prepare_enable(vc4_hdmi->pixel_clock);
 	if (ret) {
 		DRM_ERROR("Failed to turn on pixel clock: %d\n", ret);
+		pm_runtime_put(&vc4_hdmi->pdev->dev);
 		return;
 	}
 
-	/*
-	 * As stated in RPi's vc4 firmware "HDMI state machine (HSM) clock must
-	 * be faster than pixel clock, infinitesimally faster, tested in
-	 * simulation. Otherwise, exact value is unimportant for HDMI
-	 * operation." This conflicts with bcm2835's vc4 documentation, which
-	 * states HSM's clock has to be at least 108% of the pixel clock.
-	 *
-	 * Real life tests reveal that vc4's firmware statement holds up, and
-	 * users are able to use pixel clocks closer to HSM's, namely for
-	 * 1920x1200@60Hz. So it was decided to have leave a 1% margin between
-	 * both clocks. Which, for RPi0-3 implies a maximum pixel clock of
-	 * 162MHz.
-	 *
-	 * Additionally, the AXI clock needs to be at least 25% of
-	 * pixel clock, but HSM ends up being the limiting factor.
-	 */
-	hsm_rate = max_t(unsigned long, 120000000, (pixel_rate / 100) * 101);
-	ret = clk_set_min_rate(vc4_hdmi->hsm_clock, hsm_rate);
-	if (ret) {
-		DRM_ERROR("Failed to set HSM clock rate: %d\n", ret);
+	hsm_rate = vc4_hdmi->variant->calc_hsm_clock(vc4_hdmi, pixel_rate);
+	vc4_hdmi->hsm_req = clk_request_start(vc4_hdmi->hsm_clock, hsm_rate);
+	if (IS_ERR(vc4_hdmi->hsm_req)) {
+		DRM_ERROR("Failed to set HSM clock rate: %ld\n", PTR_ERR(vc4_hdmi->hsm_req));
+		clk_disable_unprepare(vc4_hdmi->pixel_clock);
+		pm_runtime_put(&vc4_hdmi->pdev->dev);
 		return;
 	}
 
 	vc4_hdmi_cec_update_clk_div(vc4_hdmi);
 
-	/*
-	 * FIXME: When the pixel freq is 594MHz (4k60), this needs to be setup
-	 * at 300MHz.
-	 */
-	ret = clk_set_min_rate(vc4_hdmi->pixel_bvb_clock,
-			       (hsm_rate > VC4_HSM_MID_CLOCK ? 150000000 : 75000000));
-	if (ret) {
-		DRM_ERROR("Failed to set pixel bvb clock rate: %d\n", ret);
+	if (pixel_rate > 297000000)
+		bvb_rate = 300000000;
+	else if (pixel_rate > 148500000)
+		bvb_rate = 150000000;
+	else
+		bvb_rate = 75000000;
+
+	if (vc4_hdmi->pixel_bvb_clock)
+		vc4_hdmi->bvb_req = clk_request_start(vc4_hdmi->pixel_bvb_clock, bvb_rate);
+	if (IS_ERR(vc4_hdmi->bvb_req)) {
+		DRM_ERROR("Failed to set pixel bvb clock rate: %ld\n", PTR_ERR(vc4_hdmi->bvb_req));
+		clk_request_done(vc4_hdmi->hsm_req);
 		clk_disable_unprepare(vc4_hdmi->pixel_clock);
+		pm_runtime_put(&vc4_hdmi->pdev->dev);
 		return;
 	}
 
 	ret = clk_prepare_enable(vc4_hdmi->pixel_bvb_clock);
 	if (ret) {
 		DRM_ERROR("Failed to turn on pixel bvb clock: %d\n", ret);
+		if (vc4_hdmi->bvb_req)
+			clk_request_done(vc4_hdmi->bvb_req);
+		clk_request_done(vc4_hdmi->hsm_req);
 		clk_disable_unprepare(vc4_hdmi->pixel_clock);
+		pm_runtime_put(&vc4_hdmi->pdev->dev);
 		return;
 	}
 
 	if (vc4_hdmi->variant->phy_init)
-		vc4_hdmi->variant->phy_init(vc4_hdmi, mode);
+		vc4_hdmi->variant->phy_init(vc4_hdmi, vc4_conn_state);
 
 	HDMI_WRITE(HDMI_SCHEDULER_CONTROL,
 		   HDMI_READ(HDMI_SCHEDULER_CONTROL) |
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:991 @
 		   VC4_HDMI_SCHEDULER_CONTROL_IGNORE_VSYNC_PREDICTS);
 
 	if (vc4_hdmi->variant->set_timings)
-		vc4_hdmi->variant->set_timings(vc4_hdmi, mode);
+		vc4_hdmi->variant->set_timings(vc4_hdmi, conn_state, mode);
 }
 
-static void vc4_hdmi_encoder_pre_crtc_enable(struct drm_encoder *encoder)
+static void vc4_hdmi_encoder_pre_crtc_enable(struct drm_encoder *encoder,
+					     struct drm_atomic_state *state)
 {
 	struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode;
 	struct vc4_hdmi_encoder *vc4_encoder = to_vc4_hdmi_encoder(encoder);
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1017 @
 	HDMI_WRITE(HDMI_FIFO_CTL, VC4_HDMI_FIFO_CTL_MASTER_SLAVE_N);
 }
 
-static void vc4_hdmi_encoder_post_crtc_enable(struct drm_encoder *encoder)
+static void vc4_hdmi_encoder_post_crtc_enable(struct drm_encoder *encoder,
+					      struct drm_atomic_state *state)
 {
 	struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode;
 	struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder);
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1029 @
 
 	HDMI_WRITE(HDMI_VID_CTL,
 		   VC4_HD_VID_CTL_ENABLE |
+		   VC4_HD_VID_CTL_CLRRGB |
 		   VC4_HD_VID_CTL_UNDERFLOW_ENABLE |
 		   VC4_HD_VID_CTL_FRAME_COUNTER_RESET |
 		   (vsync_pos ? 0 : VC4_HD_VID_CTL_VSYNC_LOW) |
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1075 @
 	}
 
 	vc4_hdmi_recenter_fifo(vc4_hdmi);
+	vc4_hdmi_enable_scrambling(encoder);
 }
 
 static void vc4_hdmi_encoder_enable(struct drm_encoder *encoder)
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1089 @
 					 struct drm_crtc_state *crtc_state,
 					 struct drm_connector_state *conn_state)
 {
+	struct vc4_hdmi_connector_state *vc4_state = conn_state_to_vc4_hdmi_conn_state(conn_state);
 	struct drm_display_mode *mode = &crtc_state->adjusted_mode;
 	struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder);
 	unsigned long long pixel_rate = mode->clock * 1000;
 	unsigned long long tmds_rate;
 
 	if (vc4_hdmi->variant->unsupported_odd_h_timings &&
+	    !(mode->flags & DRM_MODE_FLAG_DBLCLK) &&
 	    ((mode->hdisplay % 2) || (mode->hsync_start % 2) ||
 	     (mode->hsync_end % 2) || (mode->htotal % 2)))
 		return -EINVAL;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1115 @
 		pixel_rate = mode->clock * 1000;
 	}
 
+	if (conn_state->max_bpc == 12) {
+		pixel_rate = pixel_rate * 150;
+		do_div(pixel_rate, 100);
+	} else if (conn_state->max_bpc == 10) {
+		pixel_rate = pixel_rate * 125;
+		do_div(pixel_rate, 100);
+	}
+
 	if (mode->flags & DRM_MODE_FLAG_DBLCLK)
 		pixel_rate = pixel_rate * 2;
 
 	if (pixel_rate > vc4_hdmi->variant->max_pixel_clock)
 		return -EINVAL;
 
+	if (vc4_hdmi->disable_4kp60 && (pixel_rate > HDMI_14_MAX_TMDS_CLK))
+		return -EINVAL;
+
+	vc4_state->pixel_rate = pixel_rate;
+
 	return 0;
 }
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1144 @
 	struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder);
 
 	if (vc4_hdmi->variant->unsupported_odd_h_timings &&
+	    !(mode->flags & DRM_MODE_FLAG_DBLCLK) &&
 	    ((mode->hdisplay % 2) || (mode->hsync_start % 2) ||
 	     (mode->hsync_end % 2) || (mode->htotal % 2)))
 		return MODE_H_ILLEGAL;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1152 @
 	if ((mode->clock * 1000) > vc4_hdmi->variant->max_pixel_clock)
 		return MODE_CLOCK_HIGH;
 
+	if (vc4_hdmi->disable_4kp60 && vc4_hdmi_mode_needs_scrambling(mode))
+		return MODE_CLOCK_HIGH;
+
 	return MODE_OK;
 }
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1165 @
 	.enable = vc4_hdmi_encoder_enable,
 };
 
+static u32 vc4_hdmi_calc_hsm_clock(struct vc4_hdmi *vc4_hdmi, unsigned long pixel_rate)
+{
+	/*
+	 * Whilst this can vary, all the CEC timings are derived from this
+	 * clock, so make it constant to avoid having to reconfigure CEC on
+	 * every mode change.
+	 */
+
+	return 163682864;
+}
+
+static u32 vc5_hdmi_calc_hsm_clock(struct vc4_hdmi *vc4_hdmi, unsigned long pixel_rate)
+{
+	/*
+	 * As stated in RPi's vc4 firmware "HDMI state machine (HSM) clock must
+	 * be faster than pixel clock, infinitesimally faster, tested in
+	 * simulation. Otherwise, exact value is unimportant for HDMI
+	 * operation." This conflicts with bcm2835's vc4 documentation, which
+	 * states HSM's clock has to be at least 108% of the pixel clock.
+	 *
+	 * Real life tests reveal that vc4's firmware statement holds up, and
+	 * users are able to use pixel clocks closer to HSM's, namely for
+	 * 1920x1200@60Hz. So it was decided to have leave a 1% margin between
+	 * both clocks. Which, for RPi0-3 implies a maximum pixel clock of
+	 * 162MHz.
+	 *
+	 * Additionally, the AXI clock needs to be at least 25% of
+	 * pixel clock, but HSM ends up being the limiting factor.
+	 */
+
+	return max_t(unsigned long, 120000000, (pixel_rate / 100) * 101);
+}
+
 static u32 vc4_hdmi_channel_map(struct vc4_hdmi *vc4_hdmi, u32 channel_mask)
 {
 	int i;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1223 @
 }
 
 /* HDMI audio codec callbacks */
-static void vc4_hdmi_audio_set_mai_clock(struct vc4_hdmi *vc4_hdmi)
+static void vc4_hdmi_audio_set_mai_clock(struct vc4_hdmi *vc4_hdmi,
+					 unsigned int samplerate)
 {
 	u32 hsm_clock = clk_get_rate(vc4_hdmi->audio_clock);
 	unsigned long n, m;
 
-	rational_best_approximation(hsm_clock, vc4_hdmi->audio.samplerate,
+	rational_best_approximation(hsm_clock, samplerate,
 				    VC4_HD_MAI_SMP_N_MASK >>
 				    VC4_HD_MAI_SMP_N_SHIFT,
 				    (VC4_HD_MAI_SMP_M_MASK >>
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1241 @
 		   VC4_SET_FIELD(m - 1, VC4_HD_MAI_SMP_M));
 }
 
-static void vc4_hdmi_set_n_cts(struct vc4_hdmi *vc4_hdmi)
+static void vc4_hdmi_set_n_cts(struct vc4_hdmi *vc4_hdmi, unsigned int samplerate)
 {
 	struct drm_encoder *encoder = &vc4_hdmi->encoder.base.base;
 	struct drm_crtc *crtc = encoder->crtc;
 	const struct drm_display_mode *mode = &crtc->state->adjusted_mode;
-	u32 samplerate = vc4_hdmi->audio.samplerate;
 	u32 n, cts;
 	u64 tmp;
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1274 @
 	return snd_soc_card_get_drvdata(card);
 }
 
-static int vc4_hdmi_audio_startup(struct snd_pcm_substream *substream,
-				  struct snd_soc_dai *dai)
+static int vc4_hdmi_audio_startup(struct device *dev, void *data)
 {
-	struct vc4_hdmi *vc4_hdmi = dai_to_hdmi(dai);
+	struct vc4_hdmi *vc4_hdmi = dev_get_drvdata(dev);
 	struct drm_encoder *encoder = &vc4_hdmi->encoder.base.base;
-	struct drm_connector *connector = &vc4_hdmi->connector;
-	int ret;
-
-	if (vc4_hdmi->audio.substream && vc4_hdmi->audio.substream != substream)
-		return -EINVAL;
-
-	vc4_hdmi->audio.substream = substream;
 
 	/*
 	 * If the HDMI encoder hasn't probed, or the encoder is
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1287 @
 				VC4_HDMI_RAM_PACKET_ENABLE))
 		return -ENODEV;
 
-	ret = snd_pcm_hw_constraint_eld(substream->runtime, connector->eld);
-	if (ret)
-		return ret;
+	vc4_hdmi->audio.streaming = true;
 
-	return 0;
-}
+	HDMI_WRITE(HDMI_MAI_CTL,
+		   VC4_HD_MAI_CTL_RESET |
+		   VC4_HD_MAI_CTL_FLUSH |
+		   VC4_HD_MAI_CTL_DLATE |
+		   VC4_HD_MAI_CTL_ERRORE |
+		   VC4_HD_MAI_CTL_ERRORF);
+
+	if (vc4_hdmi->variant->phy_rng_enable)
+		vc4_hdmi->variant->phy_rng_enable(vc4_hdmi);
 
-static int vc4_hdmi_audio_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
-{
 	return 0;
 }
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1309 @
 	int ret;
 
 	vc4_hdmi->audio.streaming = false;
-	ret = vc4_hdmi_stop_packet(encoder, HDMI_INFOFRAME_TYPE_AUDIO);
+	ret = vc4_hdmi_stop_packet(encoder, HDMI_INFOFRAME_TYPE_AUDIO, false);
 	if (ret)
 		dev_err(dev, "Failed to stop audio infoframe: %d\n", ret);
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1318 @
 	HDMI_WRITE(HDMI_MAI_CTL, VC4_HD_MAI_CTL_FLUSH);
 }
 
-static void vc4_hdmi_audio_shutdown(struct snd_pcm_substream *substream,
-				    struct snd_soc_dai *dai)
+static void vc4_hdmi_audio_shutdown(struct device *dev, void *data)
 {
-	struct vc4_hdmi *vc4_hdmi = dai_to_hdmi(dai);
+	struct vc4_hdmi *vc4_hdmi = dev_get_drvdata(dev);
 
-	if (substream != vc4_hdmi->audio.substream)
-		return;
+	HDMI_WRITE(HDMI_MAI_CTL,
+		   VC4_HD_MAI_CTL_DLATE |
+		   VC4_HD_MAI_CTL_ERRORE |
+		   VC4_HD_MAI_CTL_ERRORF);
 
+	if (vc4_hdmi->variant->phy_rng_disable)
+		vc4_hdmi->variant->phy_rng_disable(vc4_hdmi);
+
+	vc4_hdmi->audio.streaming = false;
 	vc4_hdmi_audio_reset(vc4_hdmi);
+}
 
-	vc4_hdmi->audio.substream = NULL;
+static int sample_rate_to_mai_fmt(int samplerate)
+{
+	switch (samplerate) {
+	case 8000:
+		return VC4_HDMI_MAI_SAMPLE_RATE_8000;
+	case 11025:
+		return VC4_HDMI_MAI_SAMPLE_RATE_11025;
+	case 12000:
+		return VC4_HDMI_MAI_SAMPLE_RATE_12000;
+	case 16000:
+		return VC4_HDMI_MAI_SAMPLE_RATE_16000;
+	case 22050:
+		return VC4_HDMI_MAI_SAMPLE_RATE_22050;
+	case 24000:
+		return VC4_HDMI_MAI_SAMPLE_RATE_24000;
+	case 32000:
+		return VC4_HDMI_MAI_SAMPLE_RATE_32000;
+	case 44100:
+		return VC4_HDMI_MAI_SAMPLE_RATE_44100;
+	case 48000:
+		return VC4_HDMI_MAI_SAMPLE_RATE_48000;
+	case 64000:
+		return VC4_HDMI_MAI_SAMPLE_RATE_64000;
+	case 88200:
+		return VC4_HDMI_MAI_SAMPLE_RATE_88200;
+	case 96000:
+		return VC4_HDMI_MAI_SAMPLE_RATE_96000;
+	case 128000:
+		return VC4_HDMI_MAI_SAMPLE_RATE_128000;
+	case 176400:
+		return VC4_HDMI_MAI_SAMPLE_RATE_176400;
+	case 192000:
+		return VC4_HDMI_MAI_SAMPLE_RATE_192000;
+	default:
+		return VC4_HDMI_MAI_SAMPLE_RATE_NOT_INDICATED;
+	}
 }
 
 /* HDMI audio codec callbacks */
-static int vc4_hdmi_audio_hw_params(struct snd_pcm_substream *substream,
-				    struct snd_pcm_hw_params *params,
-				    struct snd_soc_dai *dai)
+static int vc4_hdmi_audio_prepare(struct device *dev, void *data,
+				  struct hdmi_codec_daifmt *daifmt,
+				  struct hdmi_codec_params *params)
 {
-	struct vc4_hdmi *vc4_hdmi = dai_to_hdmi(dai);
+	struct vc4_hdmi *vc4_hdmi = dev_get_drvdata(dev);
 	struct drm_encoder *encoder = &vc4_hdmi->encoder.base.base;
-	struct device *dev = &vc4_hdmi->pdev->dev;
+	unsigned int sample_rate = params->sample_rate;
+	unsigned int channels = params->channels;
 	u32 audio_packet_config, channel_mask;
 	u32 channel_map;
-
-	if (substream != vc4_hdmi->audio.substream)
-		return -EINVAL;
+	u32 mai_audio_format;
+	u32 mai_sample_rate;
 
 	dev_dbg(dev, "%s: %u Hz, %d bit, %d channels\n", __func__,
-		params_rate(params), params_width(params),
-		params_channels(params));
-
-	vc4_hdmi->audio.channels = params_channels(params);
-	vc4_hdmi->audio.samplerate = params_rate(params);
+		sample_rate, params->sample_width, channels);
 
 	HDMI_WRITE(HDMI_MAI_CTL,
-		   VC4_HD_MAI_CTL_RESET |
-		   VC4_HD_MAI_CTL_FLUSH |
-		   VC4_HD_MAI_CTL_DLATE |
-		   VC4_HD_MAI_CTL_ERRORE |
-		   VC4_HD_MAI_CTL_ERRORF);
-
-	vc4_hdmi_audio_set_mai_clock(vc4_hdmi);
+		   VC4_SET_FIELD(channels, VC4_HD_MAI_CTL_CHNUM) |
+		   VC4_HD_MAI_CTL_WHOLSMP |
+		   VC4_HD_MAI_CTL_CHALIGN |
+		   VC4_HD_MAI_CTL_ENABLE);
+
+	vc4_hdmi_audio_set_mai_clock(vc4_hdmi, sample_rate);
+
+	mai_sample_rate = sample_rate_to_mai_fmt(sample_rate);
+	if (params->iec.status[0] & IEC958_AES0_NONAUDIO &&
+	    params->channels == 8)
+		mai_audio_format = VC4_HDMI_MAI_FORMAT_HBR;
+	else
+		mai_audio_format = VC4_HDMI_MAI_FORMAT_PCM;
+	HDMI_WRITE(HDMI_MAI_FMT,
+		   VC4_SET_FIELD(mai_sample_rate,
+				 VC4_HDMI_MAI_FORMAT_SAMPLE_RATE) |
+		   VC4_SET_FIELD(mai_audio_format,
+				 VC4_HDMI_MAI_FORMAT_AUDIO_FORMAT));
 
 	/* The B frame identifier should match the value used by alsa-lib (8) */
 	audio_packet_config =
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1415 @
 		VC4_HDMI_AUDIO_PACKET_ZERO_DATA_ON_INACTIVE_CHANNELS |
 		VC4_SET_FIELD(0x8, VC4_HDMI_AUDIO_PACKET_B_FRAME_IDENTIFIER);
 
-	channel_mask = GENMASK(vc4_hdmi->audio.channels - 1, 0);
+	channel_mask = GENMASK(channels - 1, 0);
 	audio_packet_config |= VC4_SET_FIELD(channel_mask,
 					     VC4_HDMI_AUDIO_PACKET_CEA_MASK);
 
-	/* Set the MAI threshold.  This logic mimics the firmware's. */
-	if (vc4_hdmi->audio.samplerate > 96000) {
-		HDMI_WRITE(HDMI_MAI_THR,
-			   VC4_SET_FIELD(0x12, VC4_HD_MAI_THR_DREQHIGH) |
-			   VC4_SET_FIELD(0x12, VC4_HD_MAI_THR_DREQLOW));
-	} else if (vc4_hdmi->audio.samplerate > 48000) {
-		HDMI_WRITE(HDMI_MAI_THR,
-			   VC4_SET_FIELD(0x14, VC4_HD_MAI_THR_DREQHIGH) |
-			   VC4_SET_FIELD(0x12, VC4_HD_MAI_THR_DREQLOW));
-	} else {
-		HDMI_WRITE(HDMI_MAI_THR,
-			   VC4_SET_FIELD(0x10, VC4_HD_MAI_THR_PANICHIGH) |
-			   VC4_SET_FIELD(0x10, VC4_HD_MAI_THR_PANICLOW) |
-			   VC4_SET_FIELD(0x10, VC4_HD_MAI_THR_DREQHIGH) |
-			   VC4_SET_FIELD(0x10, VC4_HD_MAI_THR_DREQLOW));
-	}
+	/* Set the MAI threshold */
+	HDMI_WRITE(HDMI_MAI_THR,
+		   VC4_SET_FIELD(0x08, VC4_HD_MAI_THR_PANICHIGH) |
+		   VC4_SET_FIELD(0x08, VC4_HD_MAI_THR_PANICLOW) |
+		   VC4_SET_FIELD(0x06, VC4_HD_MAI_THR_DREQHIGH) |
+		   VC4_SET_FIELD(0x08, VC4_HD_MAI_THR_DREQLOW));
 
 	HDMI_WRITE(HDMI_MAI_CONFIG,
 		   VC4_HDMI_MAI_CONFIG_BIT_REVERSE |
+		   VC4_HDMI_MAI_CONFIG_FORMAT_REVERSE |
 		   VC4_SET_FIELD(channel_mask, VC4_HDMI_MAI_CHANNEL_MASK));
 
 	channel_map = vc4_hdmi->variant->channel_map(vc4_hdmi, channel_mask);
 	HDMI_WRITE(HDMI_MAI_CHANNEL_MAP, channel_map);
 	HDMI_WRITE(HDMI_AUDIO_PACKET_CONFIG, audio_packet_config);
-	vc4_hdmi_set_n_cts(vc4_hdmi);
+	vc4_hdmi_set_n_cts(vc4_hdmi, sample_rate);
 
+	memcpy(&vc4_hdmi->audio.infoframe, &params->cea, sizeof(params->cea));
 	vc4_hdmi_set_audio_infoframe(encoder);
 
 	return 0;
 }
 
-static int vc4_hdmi_audio_trigger(struct snd_pcm_substream *substream, int cmd,
-				  struct snd_soc_dai *dai)
-{
-	struct vc4_hdmi *vc4_hdmi = dai_to_hdmi(dai);
-
-	switch (cmd) {
-	case SNDRV_PCM_TRIGGER_START:
-		vc4_hdmi->audio.streaming = true;
-
-		if (vc4_hdmi->variant->phy_rng_enable)
-			vc4_hdmi->variant->phy_rng_enable(vc4_hdmi);
-
-		HDMI_WRITE(HDMI_MAI_CTL,
-			   VC4_SET_FIELD(vc4_hdmi->audio.channels,
-					 VC4_HD_MAI_CTL_CHNUM) |
-			   VC4_HD_MAI_CTL_ENABLE);
-		break;
-	case SNDRV_PCM_TRIGGER_STOP:
-		HDMI_WRITE(HDMI_MAI_CTL,
-			   VC4_HD_MAI_CTL_DLATE |
-			   VC4_HD_MAI_CTL_ERRORE |
-			   VC4_HD_MAI_CTL_ERRORF);
-
-		if (vc4_hdmi->variant->phy_rng_disable)
-			vc4_hdmi->variant->phy_rng_disable(vc4_hdmi);
-
-		vc4_hdmi->audio.streaming = false;
-
-		break;
-	default:
-		break;
-	}
-
-	return 0;
-}
-
-static inline struct vc4_hdmi *
-snd_component_to_hdmi(struct snd_soc_component *component)
-{
-	struct snd_soc_card *card = snd_soc_component_get_drvdata(component);
-
-	return snd_soc_card_get_drvdata(card);
-}
-
-static int vc4_hdmi_audio_eld_ctl_info(struct snd_kcontrol *kcontrol,
-				       struct snd_ctl_elem_info *uinfo)
-{
-	struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
-	struct vc4_hdmi *vc4_hdmi = snd_component_to_hdmi(component);
-	struct drm_connector *connector = &vc4_hdmi->connector;
-
-	uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
-	uinfo->count = sizeof(connector->eld);
-
-	return 0;
-}
-
-static int vc4_hdmi_audio_eld_ctl_get(struct snd_kcontrol *kcontrol,
-				      struct snd_ctl_elem_value *ucontrol)
-{
-	struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
-	struct vc4_hdmi *vc4_hdmi = snd_component_to_hdmi(component);
-	struct drm_connector *connector = &vc4_hdmi->connector;
-
-	memcpy(ucontrol->value.bytes.data, connector->eld,
-	       sizeof(connector->eld));
-
-	return 0;
-}
-
-static const struct snd_kcontrol_new vc4_hdmi_audio_controls[] = {
-	{
-		.access = SNDRV_CTL_ELEM_ACCESS_READ |
-			  SNDRV_CTL_ELEM_ACCESS_VOLATILE,
-		.iface = SNDRV_CTL_ELEM_IFACE_PCM,
-		.name = "ELD",
-		.info = vc4_hdmi_audio_eld_ctl_info,
-		.get = vc4_hdmi_audio_eld_ctl_get,
-	},
-};
-
 static const struct snd_soc_dapm_widget vc4_hdmi_audio_widgets[] = {
 	SND_SOC_DAPM_OUTPUT("TX"),
 };
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1452 @
 
 static const struct snd_soc_component_driver vc4_hdmi_audio_component_drv = {
 	.name			= "vc4-hdmi-codec-dai-component",
-	.controls		= vc4_hdmi_audio_controls,
-	.num_controls		= ARRAY_SIZE(vc4_hdmi_audio_controls),
 	.dapm_widgets		= vc4_hdmi_audio_widgets,
 	.num_dapm_widgets	= ARRAY_SIZE(vc4_hdmi_audio_widgets),
 	.dapm_routes		= vc4_hdmi_audio_routes,
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1462 @
 	.non_legacy_dai_naming	= 1,
 };
 
-static const struct snd_soc_dai_ops vc4_hdmi_audio_dai_ops = {
-	.startup = vc4_hdmi_audio_startup,
-	.shutdown = vc4_hdmi_audio_shutdown,
-	.hw_params = vc4_hdmi_audio_hw_params,
-	.set_fmt = vc4_hdmi_audio_set_fmt,
-	.trigger = vc4_hdmi_audio_trigger,
-};
-
-static struct snd_soc_dai_driver vc4_hdmi_audio_codec_dai_drv = {
-	.name = "vc4-hdmi-hifi",
-	.playback = {
-		.stream_name = "Playback",
-		.channels_min = 2,
-		.channels_max = 8,
-		.rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
-			 SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |
-			 SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 |
-			 SNDRV_PCM_RATE_192000,
-		.formats = SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE,
-	},
-};
-
 static const struct snd_soc_component_driver vc4_hdmi_audio_cpu_dai_comp = {
 	.name = "vc4-hdmi-cpu-dai-component",
 };
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1488 @
 			 SNDRV_PCM_RATE_192000,
 		.formats = SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE,
 	},
-	.ops = &vc4_hdmi_audio_dai_ops,
 };
 
 static const struct snd_dmaengine_pcm_config pcm_conf = {
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1495 @
 	.prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config,
 };
 
+
+static int vc4_hdmi_audio_get_eld(struct device *dev, void *data,
+				  uint8_t *buf, size_t len)
+{
+	struct vc4_hdmi *vc4_hdmi = dev_get_drvdata(dev);
+	struct drm_connector *connector = &vc4_hdmi->connector;
+
+	memcpy(buf, connector->eld, min(sizeof(connector->eld), len));
+
+	return 0;
+}
+
+static const struct hdmi_codec_ops vc4_hdmi_codec_ops = {
+	.get_eld = vc4_hdmi_audio_get_eld,
+	.prepare = vc4_hdmi_audio_prepare,
+	.audio_shutdown = vc4_hdmi_audio_shutdown,
+	.audio_startup = vc4_hdmi_audio_startup,
+};
+
+struct hdmi_codec_pdata vc4_hdmi_codec_pdata = {
+	.ops = &vc4_hdmi_codec_ops,
+	.max_i2s_channels = 8,
+	.i2s = 1,
+};
+
 static int vc4_hdmi_audio_init(struct vc4_hdmi *vc4_hdmi)
 {
 	const struct vc4_hdmi_register *mai_data =
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1527 @
 	struct snd_soc_dai_link *dai_link = &vc4_hdmi->audio.link;
 	struct snd_soc_card *card = &vc4_hdmi->audio.card;
 	struct device *dev = &vc4_hdmi->pdev->dev;
+	struct platform_device *codec_pdev;
 	const __be32 *addr;
 	int index;
 	int ret;
+	int len;
 
-	if (!of_find_property(dev->of_node, "dmas", NULL)) {
+	if (!of_find_property(dev->of_node, "dmas", &len) ||
+	    len == 0) {
 		dev_warn(dev,
-			 "'dmas' DT property is missing, no HDMI audio\n");
+			 "'dmas' DT property is missing or empty, no HDMI audio\n");
 		return 0;
 	}
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1576 @
 		return ret;
 	}
 
-	/* register component and codec dai */
-	ret = devm_snd_soc_register_component(dev, &vc4_hdmi_audio_component_drv,
-				     &vc4_hdmi_audio_codec_dai_drv, 1);
-	if (ret) {
-		dev_err(dev, "Could not register component: %d\n", ret);
-		return ret;
+	codec_pdev = platform_device_register_data(dev, HDMI_CODEC_DRV_NAME,
+						   PLATFORM_DEVID_AUTO,
+						   &vc4_hdmi_codec_pdata,
+						   sizeof(vc4_hdmi_codec_pdata));
+	if (IS_ERR(codec_pdev)) {
+		dev_err(dev, "Couldn't register the HDMI codec: %ld\n", PTR_ERR(codec_pdev));
+		return PTR_ERR(codec_pdev);
 	}
 
 	dai_link->cpus		= &vc4_hdmi->audio.cpu;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1595 @
 
 	dai_link->name = "MAI";
 	dai_link->stream_name = "MAI PCM";
-	dai_link->codecs->dai_name = vc4_hdmi_audio_codec_dai_drv.name;
+	dai_link->codecs->dai_name = "i2s-hifi";
 	dai_link->cpus->dai_name = dev_name(dev);
-	dai_link->codecs->name = dev_name(dev);
+	dai_link->codecs->name = dev_name(&codec_pdev->dev);
 	dai_link->platforms->name = dev_name(dev);
 
 	card->dai_link = dai_link;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1623 @
 
 }
 
+static irqreturn_t vc4_hdmi_hpd_irq_thread(int irq, void *priv)
+{
+	struct vc4_hdmi *vc4_hdmi = priv;
+	struct drm_device *dev = vc4_hdmi->connector.dev;
+
+	if (dev && dev->registered)
+		drm_kms_helper_hotplug_event(dev);
+
+	return IRQ_HANDLED;
+}
+
+static int vc4_hdmi_hotplug_init(struct vc4_hdmi *vc4_hdmi)
+{
+	struct platform_device *pdev = vc4_hdmi->pdev;
+	struct drm_connector *connector = &vc4_hdmi->connector;
+	int ret;
+
+	if (vc4_hdmi->variant->external_irq_controller) {
+		unsigned int hpd_con = platform_get_irq_byname(pdev, "hpd-connected");
+		unsigned int hpd_rm = platform_get_irq_byname(pdev, "hpd-removed");
+
+		ret = request_threaded_irq(hpd_con,
+					   NULL,
+					   vc4_hdmi_hpd_irq_thread, IRQF_ONESHOT,
+					   "vc4 hdmi hpd connected", vc4_hdmi);
+		if (ret)
+			return ret;
+
+		ret = request_threaded_irq(hpd_rm,
+					   NULL,
+					   vc4_hdmi_hpd_irq_thread, IRQF_ONESHOT,
+					   "vc4 hdmi hpd disconnected", vc4_hdmi);
+		if (ret) {
+			free_irq(hpd_con, vc4_hdmi);
+			return ret;
+		}
+
+		connector->polled = DRM_CONNECTOR_POLL_HPD;
+	}
+
+	return 0;
+}
+
+static void vc4_hdmi_hotplug_exit(struct vc4_hdmi *vc4_hdmi)
+{
+	struct platform_device *pdev = vc4_hdmi->pdev;
+
+	if (vc4_hdmi->variant->external_irq_controller) {
+		free_irq(platform_get_irq_byname(pdev, "hpd-connected"), vc4_hdmi);
+		free_irq(platform_get_irq_byname(pdev, "hpd-removed"), vc4_hdmi);
+	}
+}
+
 #ifdef CONFIG_DRM_VC4_HDMI_CEC
-static irqreturn_t vc4_cec_irq_handler_thread(int irq, void *priv)
+static irqreturn_t vc4_cec_irq_handler_rx_thread(int irq, void *priv)
+{
+	struct vc4_hdmi *vc4_hdmi = priv;
+
+	if (vc4_hdmi->cec_rx_msg.len)
+		cec_received_msg(vc4_hdmi->cec_adap,
+				 &vc4_hdmi->cec_rx_msg);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t vc4_cec_irq_handler_tx_thread(int irq, void *priv)
 {
 	struct vc4_hdmi *vc4_hdmi = priv;
 
-	if (vc4_hdmi->cec_irq_was_rx) {
-		if (vc4_hdmi->cec_rx_msg.len)
-			cec_received_msg(vc4_hdmi->cec_adap,
-					 &vc4_hdmi->cec_rx_msg);
-	} else if (vc4_hdmi->cec_tx_ok) {
+	if (vc4_hdmi->cec_tx_ok) {
 		cec_transmit_done(vc4_hdmi->cec_adap, CEC_TX_STATUS_OK,
 				  0, 0, 0, 0);
 	} else {
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1706 @
 	return IRQ_HANDLED;
 }
 
+static irqreturn_t vc4_cec_irq_handler_thread(int irq, void *priv)
+{
+	struct vc4_hdmi *vc4_hdmi = priv;
+	irqreturn_t ret;
+
+	if (vc4_hdmi->cec_irq_was_rx)
+		ret = vc4_cec_irq_handler_rx_thread(irq, priv);
+	else
+		ret = vc4_cec_irq_handler_tx_thread(irq, priv);
+
+	return ret;
+}
+
 static void vc4_cec_read_msg(struct vc4_hdmi *vc4_hdmi, u32 cntrl1)
 {
 	struct drm_device *dev = vc4_hdmi->connector.dev;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1743 @
 	}
 }
 
+static irqreturn_t vc4_cec_irq_handler_tx_bare(int irq, void *priv)
+{
+	struct vc4_hdmi *vc4_hdmi = priv;
+	u32 cntrl1;
+
+	cntrl1 = HDMI_READ(HDMI_CEC_CNTRL_1);
+	vc4_hdmi->cec_tx_ok = cntrl1 & VC4_HDMI_CEC_TX_STATUS_GOOD;
+	cntrl1 &= ~VC4_HDMI_CEC_START_XMIT_BEGIN;
+	HDMI_WRITE(HDMI_CEC_CNTRL_1, cntrl1);
+
+	return IRQ_WAKE_THREAD;
+}
+
+static irqreturn_t vc4_cec_irq_handler_rx_bare(int irq, void *priv)
+{
+	struct vc4_hdmi *vc4_hdmi = priv;
+	u32 cntrl1;
+
+	vc4_hdmi->cec_rx_msg.len = 0;
+	cntrl1 = HDMI_READ(HDMI_CEC_CNTRL_1);
+	vc4_cec_read_msg(vc4_hdmi, cntrl1);
+	cntrl1 |= VC4_HDMI_CEC_CLEAR_RECEIVE_OFF;
+	HDMI_WRITE(HDMI_CEC_CNTRL_1, cntrl1);
+	cntrl1 &= ~VC4_HDMI_CEC_CLEAR_RECEIVE_OFF;
+
+	HDMI_WRITE(HDMI_CEC_CNTRL_1, cntrl1);
+
+	return IRQ_WAKE_THREAD;
+}
+
 static irqreturn_t vc4_cec_irq_handler(int irq, void *priv)
 {
 	struct vc4_hdmi *vc4_hdmi = priv;
 	u32 stat = HDMI_READ(HDMI_CEC_CPU_STATUS);
-	u32 cntrl1, cntrl5;
+	irqreturn_t ret;
+	u32 cntrl5;
 
 	if (!(stat & VC4_HDMI_CPU_CEC))
 		return IRQ_NONE;
-	vc4_hdmi->cec_rx_msg.len = 0;
-	cntrl1 = HDMI_READ(HDMI_CEC_CNTRL_1);
+
 	cntrl5 = HDMI_READ(HDMI_CEC_CNTRL_5);
 	vc4_hdmi->cec_irq_was_rx = cntrl5 & VC4_HDMI_CEC_RX_CEC_INT;
-	if (vc4_hdmi->cec_irq_was_rx) {
-		vc4_cec_read_msg(vc4_hdmi, cntrl1);
-		cntrl1 |= VC4_HDMI_CEC_CLEAR_RECEIVE_OFF;
-		HDMI_WRITE(HDMI_CEC_CNTRL_1, cntrl1);
-		cntrl1 &= ~VC4_HDMI_CEC_CLEAR_RECEIVE_OFF;
-	} else {
-		vc4_hdmi->cec_tx_ok = cntrl1 & VC4_HDMI_CEC_TX_STATUS_GOOD;
-		cntrl1 &= ~VC4_HDMI_CEC_START_XMIT_BEGIN;
-	}
-	HDMI_WRITE(HDMI_CEC_CNTRL_1, cntrl1);
-	HDMI_WRITE(HDMI_CEC_CPU_CLEAR, VC4_HDMI_CPU_CEC);
+	if (vc4_hdmi->cec_irq_was_rx)
+		ret = vc4_cec_irq_handler_rx_bare(irq, priv);
+	else
+		ret = vc4_cec_irq_handler_tx_bare(irq, priv);
 
-	return IRQ_WAKE_THREAD;
+	HDMI_WRITE(HDMI_CEC_CPU_CLEAR, VC4_HDMI_CPU_CEC);
+	return ret;
 }
 
-static int vc4_hdmi_cec_adap_enable(struct cec_adapter *adap, bool enable)
+static int vc4_hdmi_cec_enable(struct cec_adapter *adap)
 {
 	struct vc4_hdmi *vc4_hdmi = cec_get_drvdata(adap);
 	/* clock period in microseconds */
 	const u32 usecs = 1000000 / CEC_CLOCK_FREQ;
-	u32 val = HDMI_READ(HDMI_CEC_CNTRL_5);
+	u32 val;
+	int ret;
+
+	ret = pm_runtime_resume_and_get(&vc4_hdmi->pdev->dev);
+	if (ret)
+		return ret;
 
+	val = HDMI_READ(HDMI_CEC_CNTRL_5);
 	val &= ~(VC4_HDMI_CEC_TX_SW_RESET | VC4_HDMI_CEC_RX_SW_RESET |
 		 VC4_HDMI_CEC_CNT_TO_4700_US_MASK |
 		 VC4_HDMI_CEC_CNT_TO_4500_US_MASK);
 	val |= ((4700 / usecs) << VC4_HDMI_CEC_CNT_TO_4700_US_SHIFT) |
 	       ((4500 / usecs) << VC4_HDMI_CEC_CNT_TO_4500_US_SHIFT);
 
-	if (enable) {
-		HDMI_WRITE(HDMI_CEC_CNTRL_5, val |
-			   VC4_HDMI_CEC_TX_SW_RESET | VC4_HDMI_CEC_RX_SW_RESET);
-		HDMI_WRITE(HDMI_CEC_CNTRL_5, val);
-		HDMI_WRITE(HDMI_CEC_CNTRL_2,
-			   ((1500 / usecs) << VC4_HDMI_CEC_CNT_TO_1500_US_SHIFT) |
-			   ((1300 / usecs) << VC4_HDMI_CEC_CNT_TO_1300_US_SHIFT) |
-			   ((800 / usecs) << VC4_HDMI_CEC_CNT_TO_800_US_SHIFT) |
-			   ((600 / usecs) << VC4_HDMI_CEC_CNT_TO_600_US_SHIFT) |
-			   ((400 / usecs) << VC4_HDMI_CEC_CNT_TO_400_US_SHIFT));
-		HDMI_WRITE(HDMI_CEC_CNTRL_3,
-			   ((2750 / usecs) << VC4_HDMI_CEC_CNT_TO_2750_US_SHIFT) |
-			   ((2400 / usecs) << VC4_HDMI_CEC_CNT_TO_2400_US_SHIFT) |
-			   ((2050 / usecs) << VC4_HDMI_CEC_CNT_TO_2050_US_SHIFT) |
-			   ((1700 / usecs) << VC4_HDMI_CEC_CNT_TO_1700_US_SHIFT));
-		HDMI_WRITE(HDMI_CEC_CNTRL_4,
-			   ((4300 / usecs) << VC4_HDMI_CEC_CNT_TO_4300_US_SHIFT) |
-			   ((3900 / usecs) << VC4_HDMI_CEC_CNT_TO_3900_US_SHIFT) |
-			   ((3600 / usecs) << VC4_HDMI_CEC_CNT_TO_3600_US_SHIFT) |
-			   ((3500 / usecs) << VC4_HDMI_CEC_CNT_TO_3500_US_SHIFT));
+	HDMI_WRITE(HDMI_CEC_CNTRL_5, val |
+		   VC4_HDMI_CEC_TX_SW_RESET | VC4_HDMI_CEC_RX_SW_RESET);
+	HDMI_WRITE(HDMI_CEC_CNTRL_5, val);
+	HDMI_WRITE(HDMI_CEC_CNTRL_2,
+		   ((1500 / usecs) << VC4_HDMI_CEC_CNT_TO_1500_US_SHIFT) |
+		   ((1300 / usecs) << VC4_HDMI_CEC_CNT_TO_1300_US_SHIFT) |
+		   ((800 / usecs) << VC4_HDMI_CEC_CNT_TO_800_US_SHIFT) |
+		   ((600 / usecs) << VC4_HDMI_CEC_CNT_TO_600_US_SHIFT) |
+		   ((400 / usecs) << VC4_HDMI_CEC_CNT_TO_400_US_SHIFT));
+	HDMI_WRITE(HDMI_CEC_CNTRL_3,
+		   ((2750 / usecs) << VC4_HDMI_CEC_CNT_TO_2750_US_SHIFT) |
+		   ((2400 / usecs) << VC4_HDMI_CEC_CNT_TO_2400_US_SHIFT) |
+		   ((2050 / usecs) << VC4_HDMI_CEC_CNT_TO_2050_US_SHIFT) |
+		   ((1700 / usecs) << VC4_HDMI_CEC_CNT_TO_1700_US_SHIFT));
+	HDMI_WRITE(HDMI_CEC_CNTRL_4,
+		   ((4300 / usecs) << VC4_HDMI_CEC_CNT_TO_4300_US_SHIFT) |
+		   ((3900 / usecs) << VC4_HDMI_CEC_CNT_TO_3900_US_SHIFT) |
+		   ((3600 / usecs) << VC4_HDMI_CEC_CNT_TO_3600_US_SHIFT) |
+		   ((3500 / usecs) << VC4_HDMI_CEC_CNT_TO_3500_US_SHIFT));
 
+	if (!vc4_hdmi->variant->external_irq_controller)
 		HDMI_WRITE(HDMI_CEC_CPU_MASK_CLEAR, VC4_HDMI_CPU_CEC);
-	} else {
+
+	return 0;
+}
+
+static int vc4_hdmi_cec_disable(struct cec_adapter *adap)
+{
+	struct vc4_hdmi *vc4_hdmi = cec_get_drvdata(adap);
+
+	if (!vc4_hdmi->variant->external_irq_controller)
 		HDMI_WRITE(HDMI_CEC_CPU_MASK_SET, VC4_HDMI_CPU_CEC);
-		HDMI_WRITE(HDMI_CEC_CNTRL_5, val |
-			   VC4_HDMI_CEC_TX_SW_RESET | VC4_HDMI_CEC_RX_SW_RESET);
-	}
+
+	HDMI_WRITE(HDMI_CEC_CNTRL_5, HDMI_READ(HDMI_CEC_CNTRL_5) |
+		   VC4_HDMI_CEC_TX_SW_RESET | VC4_HDMI_CEC_RX_SW_RESET);
+
+	pm_runtime_put(&vc4_hdmi->pdev->dev);
+
 	return 0;
 }
 
+static int vc4_hdmi_cec_adap_enable(struct cec_adapter *adap, bool enable)
+{
+	if (enable)
+		return vc4_hdmi_cec_enable(adap);
+	else
+		return vc4_hdmi_cec_disable(adap);
+}
+
 static int vc4_hdmi_cec_adap_log_addr(struct cec_adapter *adap, u8 log_addr)
 {
 	struct vc4_hdmi *vc4_hdmi = cec_get_drvdata(adap);
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1913 @
 {
 	struct cec_connector_info conn_info;
 	struct platform_device *pdev = vc4_hdmi->pdev;
+	struct device *dev = &pdev->dev;
 	u32 value;
 	int ret;
 
-	if (!vc4_hdmi->variant->cec_available)
+	if (!of_find_property(dev->of_node, "interrupts", NULL)) {
+		dev_warn(dev, "'interrupts' DT property is missing, no CEC\n");
 		return 0;
+	}
 
 	vc4_hdmi->cec_adap = cec_allocate_adapter(&vc4_hdmi_cec_adap_ops,
 						  vc4_hdmi, "vc4",
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1933 @
 	cec_fill_conn_info_from_drm(&conn_info, &vc4_hdmi->connector);
 	cec_s_conn_info(vc4_hdmi->cec_adap, &conn_info);
 
-	HDMI_WRITE(HDMI_CEC_CPU_MASK_SET, 0xffffffff);
-
 	value = HDMI_READ(HDMI_CEC_CNTRL_1);
 	/* Set the logical address to Unregistered */
 	value |= VC4_HDMI_CEC_ADDR_MASK;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1940 @
 
 	vc4_hdmi_cec_update_clk_div(vc4_hdmi);
 
-	ret = devm_request_threaded_irq(&pdev->dev, platform_get_irq(pdev, 0),
-					vc4_cec_irq_handler,
-					vc4_cec_irq_handler_thread, 0,
-					"vc4 hdmi cec", vc4_hdmi);
-	if (ret)
-		goto err_delete_cec_adap;
+	if (vc4_hdmi->variant->external_irq_controller) {
+		ret = request_threaded_irq(platform_get_irq_byname(pdev, "cec-rx"),
+					   vc4_cec_irq_handler_rx_bare,
+					   vc4_cec_irq_handler_rx_thread, 0,
+					   "vc4 hdmi cec rx", vc4_hdmi);
+		if (ret)
+			goto err_delete_cec_adap;
+
+		ret = request_threaded_irq(platform_get_irq_byname(pdev, "cec-tx"),
+					   vc4_cec_irq_handler_tx_bare,
+					   vc4_cec_irq_handler_tx_thread, 0,
+					   "vc4 hdmi cec tx", vc4_hdmi);
+		if (ret)
+			goto err_remove_cec_rx_handler;
+	} else {
+		HDMI_WRITE(HDMI_CEC_CPU_MASK_SET, 0xffffffff);
+
+		ret = request_threaded_irq(platform_get_irq(pdev, 0),
+					   vc4_cec_irq_handler,
+					   vc4_cec_irq_handler_thread, 0,
+					   "vc4 hdmi cec", vc4_hdmi);
+		if (ret)
+			goto err_delete_cec_adap;
+	}
 
 	ret = cec_register_adapter(vc4_hdmi->cec_adap, &pdev->dev);
 	if (ret < 0)
-		goto err_delete_cec_adap;
+		goto err_remove_handlers;
 
 	return 0;
 
+err_remove_handlers:
+	if (vc4_hdmi->variant->external_irq_controller)
+		free_irq(platform_get_irq_byname(pdev, "cec-tx"), vc4_hdmi);
+	else
+		free_irq(platform_get_irq(pdev, 0), vc4_hdmi);
+
+err_remove_cec_rx_handler:
+	if (vc4_hdmi->variant->external_irq_controller)
+		free_irq(platform_get_irq_byname(pdev, "cec-rx"), vc4_hdmi);
+
 err_delete_cec_adap:
 	cec_delete_adapter(vc4_hdmi->cec_adap);
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1989 @
 
 static void vc4_hdmi_cec_exit(struct vc4_hdmi *vc4_hdmi)
 {
+	struct platform_device *pdev = vc4_hdmi->pdev;
+
+	if (vc4_hdmi->variant->external_irq_controller) {
+		free_irq(platform_get_irq_byname(pdev, "cec-rx"), vc4_hdmi);
+		free_irq(platform_get_irq_byname(pdev, "cec-tx"), vc4_hdmi);
+	} else {
+		free_irq(platform_get_irq(pdev, 0), vc4_hdmi);
+	}
+
 	cec_unregister_adapter(vc4_hdmi->cec_adap);
 }
 #else
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:2082 @
 		return PTR_ERR(vc4_hdmi->hsm_clock);
 	}
 	vc4_hdmi->audio_clock = vc4_hdmi->hsm_clock;
+	vc4_hdmi->cec_clock = vc4_hdmi->hsm_clock;
 
 	return 0;
 }
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:2092 @
 	struct platform_device *pdev = vc4_hdmi->pdev;
 	struct device *dev = &pdev->dev;
 	struct resource *res;
+	int ret;
 
 	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "hdmi");
 	if (!res)
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:2177 @
 		return PTR_ERR(vc4_hdmi->audio_clock);
 	}
 
+	vc4_hdmi->cec_clock = devm_clk_get(dev, "cec");
+	if (IS_ERR(vc4_hdmi->cec_clock)) {
+		DRM_ERROR("Failed to get CEC clock\n");
+		return PTR_ERR(vc4_hdmi->cec_clock);
+	}
+
 	vc4_hdmi->reset = devm_reset_control_get(dev, NULL);
 	if (IS_ERR(vc4_hdmi->reset)) {
 		DRM_ERROR("Failed to get HDMI reset line\n");
 		return PTR_ERR(vc4_hdmi->reset);
 	}
 
+	ret = vc4_hdmi_build_regset(vc4_hdmi, &vc4_hdmi->hdmi_regset, VC4_HDMI);
+	if (ret)
+		return ret;
+
+	ret = vc4_hdmi_build_regset(vc4_hdmi, &vc4_hdmi->hd_regset, VC4_HD);
+	if (ret)
+		return ret;
+
+	ret = vc4_hdmi_build_regset(vc4_hdmi, &vc4_hdmi->cec_regset, VC5_CEC);
+	if (ret)
+		return ret;
+
+	ret = vc4_hdmi_build_regset(vc4_hdmi, &vc4_hdmi->csc_regset, VC5_CSC);
+	if (ret)
+		return ret;
+
+	ret = vc4_hdmi_build_regset(vc4_hdmi, &vc4_hdmi->dvp_regset, VC5_DVP);
+	if (ret)
+		return ret;
+
+	ret = vc4_hdmi_build_regset(vc4_hdmi, &vc4_hdmi->phy_regset, VC5_PHY);
+	if (ret)
+		return ret;
+
+	ret = vc4_hdmi_build_regset(vc4_hdmi, &vc4_hdmi->ram_regset, VC5_RAM);
+	if (ret)
+		return ret;
+
+	ret = vc4_hdmi_build_regset(vc4_hdmi, &vc4_hdmi->rm_regset, VC5_RM);
+	if (ret)
+		return ret;
+
 	return 0;
 }
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:2261 @
 	vc4_hdmi = devm_kzalloc(dev, sizeof(*vc4_hdmi), GFP_KERNEL);
 	if (!vc4_hdmi)
 		return -ENOMEM;
+	INIT_DELAYED_WORK(&vc4_hdmi->scrambling_work, vc4_hdmi_scrambling_wq);
 
 	dev_set_drvdata(dev, vc4_hdmi);
 	encoder = &vc4_hdmi->encoder.base.base;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:2311 @
 	vc4_hdmi->disable_wifi_frequencies =
 		of_property_read_bool(dev->of_node, "wifi-2.4ghz-coexistence");
 
+	if (variant->max_pixel_clock == 600000000) {
+		struct vc4_dev *vc4 = to_vc4_dev(drm);
+		long max_rate = clk_round_rate(vc4->hvs->core_clk, 550000000);
+
+		if (max_rate < 550000000)
+			vc4_hdmi->disable_4kp60 = true;
+	}
+
+	/*
+	 * We need to have the device powered up at this point to call
+	 * our reset hook and for the CEC init.
+	 */
+	ret = vc4_hdmi_runtime_resume(dev);
+	if (ret)
+		goto err_put_ddc;
+
+	pm_runtime_get_noresume(dev);
+	pm_runtime_set_active(dev);
+	pm_runtime_enable(dev);
+
 	if (vc4_hdmi->variant->reset)
 		vc4_hdmi->variant->reset(vc4_hdmi);
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:2342 @
 		clk_prepare_enable(vc4_hdmi->pixel_bvb_clock);
 	}
 
-	pm_runtime_enable(dev);
-
 	drm_simple_encoder_init(drm, encoder, DRM_MODE_ENCODER_TMDS);
 	drm_encoder_helper_add(encoder, &vc4_hdmi_encoder_helper_funcs);
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:2349 @
 	if (ret)
 		goto err_destroy_encoder;
 
-	ret = vc4_hdmi_cec_init(vc4_hdmi);
+	ret = vc4_hdmi_hotplug_init(vc4_hdmi);
 	if (ret)
 		goto err_destroy_conn;
 
+	ret = vc4_hdmi_cec_init(vc4_hdmi);
+	if (ret)
+		goto err_free_hotplug;
+
 	ret = vc4_hdmi_audio_init(vc4_hdmi);
 	if (ret)
 		goto err_free_cec;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:2365 @
 			     vc4_hdmi_debugfs_regs,
 			     vc4_hdmi);
 
+	pm_runtime_put_sync(dev);
+
 	return 0;
 
 err_free_cec:
 	vc4_hdmi_cec_exit(vc4_hdmi);
+err_free_hotplug:
+	vc4_hdmi_hotplug_exit(vc4_hdmi);
 err_destroy_conn:
 	vc4_hdmi_connector_destroy(&vc4_hdmi->connector);
 err_destroy_encoder:
 	drm_encoder_cleanup(encoder);
+	pm_runtime_put_sync(dev);
 	pm_runtime_disable(dev);
 err_put_ddc:
 	put_device(&vc4_hdmi->ddc->dev);
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:2415 @
 	kfree(vc4_hdmi->hd_regset.regs);
 
 	vc4_hdmi_cec_exit(vc4_hdmi);
+	vc4_hdmi_hotplug_exit(vc4_hdmi);
 	vc4_hdmi_connector_destroy(&vc4_hdmi->connector);
 	drm_encoder_cleanup(&vc4_hdmi->encoder.base.base);
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:2445 @
 	.debugfs_name		= "hdmi_regs",
 	.card_name		= "vc4-hdmi",
 	.max_pixel_clock	= 162000000,
-	.cec_available		= true,
 	.registers		= vc4_hdmi_fields,
 	.num_registers		= ARRAY_SIZE(vc4_hdmi_fields),
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:2456 @
 	.phy_disable		= vc4_hdmi_phy_disable,
 	.phy_rng_enable		= vc4_hdmi_phy_rng_enable,
 	.phy_rng_disable	= vc4_hdmi_phy_rng_disable,
+	.calc_hsm_clock		= vc4_hdmi_calc_hsm_clock,
 	.channel_map		= vc4_hdmi_channel_map,
+	.supports_hdr		= false,
 };
 
 static const struct vc4_hdmi_variant bcm2711_hdmi0_variant = {
 	.encoder_type		= VC4_ENCODER_TYPE_HDMI0,
 	.debugfs_name		= "hdmi0_regs",
 	.card_name		= "vc4-hdmi-0",
-	.max_pixel_clock	= 297000000,
+	.max_pixel_clock	= 600000000,
 	.registers		= vc5_hdmi_hdmi0_fields,
 	.num_registers		= ARRAY_SIZE(vc5_hdmi_hdmi0_fields),
 	.phy_lane_mapping	= {
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:2475 @
 		PHY_LANE_CK,
 	},
 	.unsupported_odd_h_timings	= true,
+	.external_irq_controller	= true,
 
 	.init_resources		= vc5_hdmi_init_resources,
 	.csc_setup		= vc5_hdmi_csc_setup,
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:2485 @
 	.phy_disable		= vc5_hdmi_phy_disable,
 	.phy_rng_enable		= vc5_hdmi_phy_rng_enable,
 	.phy_rng_disable	= vc5_hdmi_phy_rng_disable,
+	.calc_hsm_clock		= vc5_hdmi_calc_hsm_clock,
 	.channel_map		= vc5_hdmi_channel_map,
+	.supports_hdr		= true,
 };
 
 static const struct vc4_hdmi_variant bcm2711_hdmi1_variant = {
 	.encoder_type		= VC4_ENCODER_TYPE_HDMI1,
 	.debugfs_name		= "hdmi1_regs",
 	.card_name		= "vc4-hdmi-1",
-	.max_pixel_clock	= 297000000,
+	.max_pixel_clock	= HDMI_14_MAX_TMDS_CLK,
 	.registers		= vc5_hdmi_hdmi1_fields,
 	.num_registers		= ARRAY_SIZE(vc5_hdmi_hdmi1_fields),
 	.phy_lane_mapping	= {
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:2504 @
 		PHY_LANE_2,
 	},
 	.unsupported_odd_h_timings	= true,
+	.external_irq_controller	= true,
 
 	.init_resources		= vc5_hdmi_init_resources,
 	.csc_setup		= vc5_hdmi_csc_setup,
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:2514 @
 	.phy_disable		= vc5_hdmi_phy_disable,
 	.phy_rng_enable		= vc5_hdmi_phy_rng_enable,
 	.phy_rng_disable	= vc5_hdmi_phy_rng_disable,
+	.calc_hsm_clock		= vc5_hdmi_calc_hsm_clock,
 	.channel_map		= vc5_hdmi_channel_map,
+	.supports_hdr		= true,
 };
 
 static const struct of_device_id vc4_hdmi_dt_match[] = {
diff -Nur --no-dereference linux-5.10.52-orig/drivers/gpu/drm/vc4/vc4_hdmi.h linux-5.10.52-v7l+/drivers/gpu/drm/vc4/vc4_hdmi.h
--- linux-5.10.52-orig/drivers/gpu/drm/vc4/vc4_hdmi.h	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/gpu/drm/vc4/vc4_hdmi.h	2021-07-25 16:46:01.698362435 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:24 @
 	return container_of(encoder, struct vc4_hdmi_encoder, base.base);
 }
 
-struct drm_display_mode;
-
 struct vc4_hdmi;
 struct vc4_hdmi_register;
+struct vc4_hdmi_connector_state;
 
 enum vc4_hdmi_phy_channel {
 	PHY_LANE_0 = 0,
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:45 @
 	/* Filename to expose the registers in debugfs */
 	const char *debugfs_name;
 
-	/* Set to true when the CEC support is available */
-	bool cec_available;
-
 	/* Maximum pixel clock supported by the controller (in Hz) */
 	unsigned long long max_pixel_clock;
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:64 @
 	/* The BCM2711 cannot deal with odd horizontal pixel timings */
 	bool unsupported_odd_h_timings;
 
+	/*
+	 * The BCM2711 CEC/hotplug IRQ controller is shared between the
+	 * two HDMI controllers, and we have a proper irqchip driver for
+	 * it.
+	 */
+	bool external_irq_controller;
+
 	/* Callback to get the resources (memory region, interrupts,
 	 * clocks, etc) for that variant.
 	 */
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:84 @
 
 	/* Callback to configure the video timings in the HDMI block */
 	void (*set_timings)(struct vc4_hdmi *vc4_hdmi,
+			    struct drm_connector_state *state,
 			    struct drm_display_mode *mode);
 
-	/* Callback to initialize the PHY according to the mode */
+	/* Callback to initialize the PHY according to the connector state */
 	void (*phy_init)(struct vc4_hdmi *vc4_hdmi,
-			 struct drm_display_mode *mode);
+			 struct vc4_hdmi_connector_state *vc4_conn_state);
 
 	/* Callback to disable the PHY */
 	void (*phy_disable)(struct vc4_hdmi *vc4_hdmi);
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:100 @
 	/* Callback to disable the RNG in the PHY */
 	void (*phy_rng_disable)(struct vc4_hdmi *vc4_hdmi);
 
+	/* Callback to calculate hsm clock */
+	u32 (*calc_hsm_clock)(struct vc4_hdmi *vc4_hdmi, unsigned long pixel_rate);
+
 	/* Callback to get channel map */
 	u32 (*channel_map)(struct vc4_hdmi *vc4_hdmi, u32 channel_mask);
+
+	/* Enables HDR metadata */
+	bool supports_hdr;
 };
 
 /* HDMI audio information */
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:117 @
 	struct snd_soc_dai_link_component cpu;
 	struct snd_soc_dai_link_component codec;
 	struct snd_soc_dai_link_component platform;
-	int samplerate;
-	int channels;
 	struct snd_dmaengine_dai_dma_data dma_data;
-	struct snd_pcm_substream *substream;
-
+	struct hdmi_audio_infoframe infoframe;
 	bool streaming;
 };
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:132 @
 	struct vc4_hdmi_encoder encoder;
 	struct drm_connector connector;
 
+	struct delayed_work scrambling_work;
+
 	struct i2c_adapter *ddc;
 	void __iomem *hdmicore_regs;
 	void __iomem *hd_regs;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:162 @
 	 */
 	bool disable_wifi_frequencies;
 
+	/*
+	 * Even if HDMI0 on the RPi4 can output modes requiring a pixel
+	 * rate higher than 297MHz, it needs some adjustments in the
+	 * config.txt file to be able to do so and thus won't always be
+	 * available.
+	 */
+	bool disable_4kp60;
+
 	struct cec_adapter *cec_adap;
 	struct cec_msg cec_rx_msg;
 	bool cec_tx_ok;
 	bool cec_irq_was_rx;
 
+	struct clk *cec_clock;
 	struct clk *pixel_clock;
 	struct clk *hsm_clock;
 	struct clk *audio_clock;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:183 @
 
 	struct reset_control *reset;
 
+	struct clk_request *bvb_req;
+	struct clk_request *hsm_req;
+
+	/* Common debugfs regset */
 	struct debugfs_regset32 hdmi_regset;
 	struct debugfs_regset32 hd_regset;
+	/* VC5 debugfs regset */
+	struct debugfs_regset32 cec_regset;
+	struct debugfs_regset32 csc_regset;
+	struct debugfs_regset32 dvp_regset;
+	struct debugfs_regset32 phy_regset;
+	struct debugfs_regset32 ram_regset;
+	struct debugfs_regset32 rm_regset;
 };
 
 static inline struct vc4_hdmi *
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:212 @
 	return container_of(_encoder, struct vc4_hdmi, encoder);
 }
 
+struct vc4_hdmi_connector_state {
+	struct drm_connector_state	base;
+	unsigned long long		pixel_rate;
+};
+
+static inline struct vc4_hdmi_connector_state *
+conn_state_to_vc4_hdmi_conn_state(struct drm_connector_state *conn_state)
+{
+	return container_of(conn_state, struct vc4_hdmi_connector_state, base);
+}
+
 void vc4_hdmi_phy_init(struct vc4_hdmi *vc4_hdmi,
-		       struct drm_display_mode *mode);
+		       struct vc4_hdmi_connector_state *vc4_conn_state);
 void vc4_hdmi_phy_disable(struct vc4_hdmi *vc4_hdmi);
 void vc4_hdmi_phy_rng_enable(struct vc4_hdmi *vc4_hdmi);
 void vc4_hdmi_phy_rng_disable(struct vc4_hdmi *vc4_hdmi);
 
 void vc5_hdmi_phy_init(struct vc4_hdmi *vc4_hdmi,
-		       struct drm_display_mode *mode);
+		       struct vc4_hdmi_connector_state *vc4_conn_state);
 void vc5_hdmi_phy_disable(struct vc4_hdmi *vc4_hdmi);
 void vc5_hdmi_phy_rng_enable(struct vc4_hdmi *vc4_hdmi);
 void vc5_hdmi_phy_rng_disable(struct vc4_hdmi *vc4_hdmi);
diff -Nur --no-dereference linux-5.10.52-orig/drivers/gpu/drm/vc4/vc4_hdmi_phy.c linux-5.10.52-v7l+/drivers/gpu/drm/vc4/vc4_hdmi_phy.c
--- linux-5.10.52-orig/drivers/gpu/drm/vc4/vc4_hdmi_phy.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/gpu/drm/vc4/vc4_hdmi_phy.c	2021-07-25 16:46:01.698362435 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:130 @
 
 #define OSCILLATOR_FREQUENCY	54000000
 
-void vc4_hdmi_phy_init(struct vc4_hdmi *vc4_hdmi, struct drm_display_mode *mode)
+void vc4_hdmi_phy_init(struct vc4_hdmi *vc4_hdmi,
+		       struct vc4_hdmi_connector_state *conn_state)
 {
 	/* PHY should be in reset, like
 	 * vc4_hdmi_encoder_disable() does.
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:343 @
 	HDMI_WRITE(HDMI_TX_PHY_POWERDOWN_CTL, BIT(10));
 }
 
-void vc5_hdmi_phy_init(struct vc4_hdmi *vc4_hdmi, struct drm_display_mode *mode)
+void vc5_hdmi_phy_init(struct vc4_hdmi *vc4_hdmi,
+		       struct vc4_hdmi_connector_state *conn_state)
 {
 	const struct phy_lane_settings *chan0_settings, *chan1_settings, *chan2_settings, *clock_settings;
 	const struct vc4_hdmi_variant *variant = vc4_hdmi->variant;
-	unsigned long long pixel_freq = mode->clock * 1000;
+	unsigned long long pixel_freq = conn_state->pixel_rate;
 	unsigned long long vco_freq;
 	unsigned char word_sel;
 	u8 vco_sel, vco_div;
diff -Nur --no-dereference linux-5.10.52-orig/drivers/gpu/drm/vc4/vc4_hdmi_regs.h linux-5.10.52-v7l+/drivers/gpu/drm/vc4/vc4_hdmi_regs.h
--- linux-5.10.52-orig/drivers/gpu/drm/vc4/vc4_hdmi_regs.h	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/gpu/drm/vc4/vc4_hdmi_regs.h	2021-07-25 16:46:01.708362267 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
 #ifndef _VC4_HDMI_REGS_H_
 #define _VC4_HDMI_REGS_H_
 
+#include <linux/pm_runtime.h>
+
 #include "vc4_hdmi.h"
 
 #define VC4_HDMI_PACKET_STRIDE			0x24
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:65 @
 	 */
 	HDMI_CTS_0,
 	HDMI_CTS_1,
+	HDMI_DEEP_COLOR_CONFIG_1,
 	HDMI_DVP_CTL,
 	HDMI_FIFO_CTL,
 	HDMI_FRAME_COUNT,
+	HDMI_GCP_CONFIG,
+	HDMI_GCP_WORD_1,
 	HDMI_HORZA,
 	HDMI_HORZB,
 	HDMI_HOTPLUG,
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:105 @
 	HDMI_RM_FORMAT,
 	HDMI_RM_OFFSET,
 	HDMI_SCHEDULER_CONTROL,
+	HDMI_SCRAMBLER_CTL,
 	HDMI_SW_RESET_CONTROL,
 	HDMI_TX_PHY_CHANNEL_SWAP,
 	HDMI_TX_PHY_CLK_DIV,
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:152 @
 #define VC5_RAM_REG(reg, offset)	_VC4_REG(VC5_RAM, reg, offset)
 #define VC5_RM_REG(reg, offset)		_VC4_REG(VC5_RM, reg, offset)
 
-static const struct vc4_hdmi_register vc4_hdmi_fields[] = {
+static const struct vc4_hdmi_register __maybe_unused vc4_hdmi_fields[] = {
 	VC4_HD_REG(HDMI_M_CTL, 0x000c),
 	VC4_HD_REG(HDMI_MAI_CTL, 0x0014),
 	VC4_HD_REG(HDMI_MAI_THR, 0x0018),
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:214 @
 	VC4_HDMI_REG(HDMI_RAM_PACKET_START, 0x0400),
 };
 
-static const struct vc4_hdmi_register vc5_hdmi_hdmi0_fields[] = {
+static const struct vc4_hdmi_register __maybe_unused vc5_hdmi_hdmi0_fields[] = {
 	VC4_HD_REG(HDMI_DVP_CTL, 0x0000),
 	VC4_HD_REG(HDMI_MAI_CTL, 0x0010),
 	VC4_HD_REG(HDMI_MAI_THR, 0x0014),
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:240 @
 	VC4_HDMI_REG(HDMI_VERTB1, 0x0f8),
 	VC4_HDMI_REG(HDMI_MAI_CHANNEL_MAP, 0x09c),
 	VC4_HDMI_REG(HDMI_MAI_CONFIG, 0x0a0),
+	VC4_HDMI_REG(HDMI_DEEP_COLOR_CONFIG_1, 0x170),
+	VC4_HDMI_REG(HDMI_GCP_CONFIG, 0x178),
+	VC4_HDMI_REG(HDMI_GCP_WORD_1, 0x17c),
 	VC4_HDMI_REG(HDMI_HOTPLUG, 0x1a8),
+	VC4_HDMI_REG(HDMI_SCRAMBLER_CTL, 0x1c4),
 
 	VC5_DVP_REG(HDMI_CLOCK_STOP, 0x0bc),
 	VC5_DVP_REG(HDMI_VEC_INTERFACE_XBAR, 0x0f0),
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:294 @
 	VC5_CSC_REG(HDMI_CSC_34_33, 0x018),
 };
 
-static const struct vc4_hdmi_register vc5_hdmi_hdmi1_fields[] = {
+static const struct vc4_hdmi_register __maybe_unused vc5_hdmi_hdmi1_fields[] = {
 	VC4_HD_REG(HDMI_DVP_CTL, 0x0000),
 	VC4_HD_REG(HDMI_MAI_CTL, 0x0030),
 	VC4_HD_REG(HDMI_MAI_THR, 0x0034),
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:320 @
 	VC4_HDMI_REG(HDMI_VERTB1, 0x0f8),
 	VC4_HDMI_REG(HDMI_MAI_CHANNEL_MAP, 0x09c),
 	VC4_HDMI_REG(HDMI_MAI_CONFIG, 0x0a0),
+	VC4_HDMI_REG(HDMI_DEEP_COLOR_CONFIG_1, 0x170),
+	VC4_HDMI_REG(HDMI_GCP_CONFIG, 0x178),
+	VC4_HDMI_REG(HDMI_GCP_WORD_1, 0x17c),
 	VC4_HDMI_REG(HDMI_HOTPLUG, 0x1a8),
+	VC4_HDMI_REG(HDMI_SCRAMBLER_CTL, 0x1c4),
 
 	VC5_DVP_REG(HDMI_CLOCK_STOP, 0x0bc),
 	VC5_DVP_REG(HDMI_VEC_INTERFACE_XBAR, 0x0f0),
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:417 @
 	const struct vc4_hdmi_variant *variant = hdmi->variant;
 	void __iomem *base;
 
+	WARN_ON(!pm_runtime_active(&hdmi->pdev->dev));
+
 	if (reg >= variant->num_registers) {
 		dev_warn(&hdmi->pdev->dev,
 			 "Invalid register ID %u\n", reg);
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:445 @
 	const struct vc4_hdmi_variant *variant = hdmi->variant;
 	void __iomem *base;
 
+	WARN_ON(!pm_runtime_active(&hdmi->pdev->dev));
+
 	if (reg >= variant->num_registers) {
 		dev_warn(&hdmi->pdev->dev,
 			 "Invalid register ID %u\n", reg);
diff -Nur --no-dereference linux-5.10.52-orig/drivers/gpu/drm/vc4/vc4_hvs.c linux-5.10.52-v7l+/drivers/gpu/drm/vc4/vc4_hvs.c
--- linux-5.10.52-orig/drivers/gpu/drm/vc4/vc4_hvs.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/gpu/drm/vc4/vc4_hvs.c	2021-07-25 16:46:01.708362267 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:98 @
 	return 0;
 }
 
+static int vc4_hvs_debugfs_dlist(struct seq_file *m, void *data)
+{
+	struct drm_info_node *node = m->private;
+	struct drm_device *dev = node->minor->dev;
+	struct vc4_dev *vc4 = to_vc4_dev(dev);
+	struct drm_printer p = drm_seq_file_printer(m);
+	unsigned int next_entry_start = 0;
+	unsigned int i, j;
+	u32 dlist_word, dispstat;
+
+	for (i = 0; i < SCALER_CHANNELS_COUNT; i++) {
+		dispstat = VC4_GET_FIELD(HVS_READ(SCALER_DISPSTATX(i)),
+					 SCALER_DISPSTATX_MODE);
+		if (dispstat == SCALER_DISPSTATX_MODE_DISABLED ||
+		    dispstat == SCALER_DISPSTATX_MODE_EOF) {
+			drm_printf(&p, "HVS chan %u disabled\n", i);
+			continue;
+		}
+
+		drm_printf(&p, "HVS chan %u:\n", i);
+
+		for (j = HVS_READ(SCALER_DISPLISTX(i)); j < 256; j++) {
+			dlist_word = readl((u32 __iomem *)vc4->hvs->dlist + j);
+			drm_printf(&p, "dlist: %02d: 0x%08x\n", j,
+				   dlist_word);
+			if (!next_entry_start ||
+			    next_entry_start == j) {
+				if (dlist_word & SCALER_CTL0_END)
+					break;
+				next_entry_start = j +
+					VC4_GET_FIELD(dlist_word,
+						      SCALER_CTL0_SIZE);
+			}
+		}
+	}
+
+	return 0;
+}
+
 /* The filter kernel is composed of dwords each containing 3 9-bit
  * signed integers packed next to each other.
  */
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:368 @
 		     SCALER_DISPSTATX_EMPTY);
 }
 
-int vc4_hvs_atomic_check(struct drm_crtc *crtc,
-			 struct drm_crtc_state *state)
+int vc4_hvs_atomic_check(struct drm_crtc *crtc, struct drm_atomic_state *state)
 {
-	struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(state);
+	struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
+	struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(crtc_state);
 	struct drm_device *dev = crtc->dev;
 	struct vc4_dev *vc4 = to_vc4_dev(dev);
 	struct drm_plane *plane;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:383 @
 	/* The pixelvalve can only feed one encoder (and encoders are
 	 * 1:1 with connectors.)
 	 */
-	if (hweight32(state->connector_mask) > 1)
+	if (hweight32(crtc_state->connector_mask) > 1)
 		return -EINVAL;
 
-	drm_atomic_crtc_state_for_each_plane_state(plane, plane_state, state)
+	drm_atomic_crtc_state_for_each_plane_state(plane, plane_state, crtc_state)
 		dlist_count += vc4_plane_dlist_size(plane_state);
 
 	dlist_count++; /* Account for SCALER_CTL0_END. */
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:433 @
 }
 
 void vc4_hvs_atomic_enable(struct drm_crtc *crtc,
-			   struct drm_crtc_state *old_state)
+			   struct drm_atomic_state *state)
 {
 	struct drm_device *dev = crtc->dev;
 	struct vc4_dev *vc4 = to_vc4_dev(dev);
-	struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(crtc->state);
+	struct drm_crtc_state *new_crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
+	struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(new_crtc_state);
 	struct drm_display_mode *mode = &crtc->state->adjusted_mode;
 	bool oneshot = vc4_state->feed_txp;
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:447 @
 }
 
 void vc4_hvs_atomic_disable(struct drm_crtc *crtc,
-			    struct drm_crtc_state *old_state)
+			    struct drm_atomic_state *state)
 {
 	struct drm_device *dev = crtc->dev;
+	struct drm_crtc_state *old_state = drm_atomic_get_old_crtc_state(state, crtc);
 	struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(old_state);
 	unsigned int chan = vc4_state->assigned_channel;
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:458 @
 }
 
 void vc4_hvs_atomic_flush(struct drm_crtc *crtc,
-			  struct drm_crtc_state *old_state)
+			  struct drm_atomic_state *state)
 {
+	struct drm_crtc_state *old_state = drm_atomic_get_old_crtc_state(state,
+									 crtc);
 	struct drm_device *dev = crtc->dev;
 	struct vc4_dev *vc4 = to_vc4_dev(dev);
 	struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(crtc->state);
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:717 @
 	vc4_debugfs_add_regset32(drm, "hvs_regs", &hvs->regset);
 	vc4_debugfs_add_file(drm, "hvs_underrun", vc4_hvs_debugfs_underrun,
 			     NULL);
+	vc4_debugfs_add_file(drm, "hvs_dlists", vc4_hvs_debugfs_dlist,
+			     NULL);
 
 	return 0;
 }
diff -Nur --no-dereference linux-5.10.52-orig/drivers/gpu/drm/vc4/vc4_kms.c linux-5.10.52-v7l+/drivers/gpu/drm/vc4/vc4_kms.c
--- linux-5.10.52-orig/drivers/gpu/drm/vc4/vc4_kms.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/gpu/drm/vc4/vc4_kms.c	2021-07-25 16:46:01.708362267 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:42 @
 
 struct vc4_hvs_state {
 	struct drm_private_state base;
-	unsigned int unassigned_channels;
+	unsigned long core_clock_rate;
+
+	struct {
+		unsigned in_use: 1;
+		unsigned long fifo_load;
+		struct drm_crtc_commit *pending_commit;
+	} fifo_state[HVS_NUM_CHANNELS];
 };
 
 static struct vc4_hvs_state *
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:163 @
 	struct vc4_ctm_state *ctm_state = to_vc4_ctm_state(vc4->ctm_manager.state);
 	struct drm_color_ctm *ctm = ctm_state->ctm;
 
+	if (vc4->firmware_kms)
+		return;
+
 	if (ctm_state->fifo) {
 		HVS_WRITE(SCALER_OLEDCOEF2,
 			  VC4_SET_FIELD(vc4_ctm_s31_32_to_s0_9(ctm->matrix[0]),
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:195 @
 }
 
 static struct vc4_hvs_state *
+vc4_hvs_get_new_global_state(struct drm_atomic_state *state)
+{
+	struct vc4_dev *vc4 = to_vc4_dev(state->dev);
+	struct drm_private_state *priv_state;
+
+	priv_state = drm_atomic_get_new_private_obj_state(state, &vc4->hvs_channels);
+	if (IS_ERR(priv_state))
+		return ERR_CAST(priv_state);
+
+	return to_vc4_hvs_state(priv_state);
+}
+
+static struct vc4_hvs_state *
+vc4_hvs_get_old_global_state(struct drm_atomic_state *state)
+{
+	struct vc4_dev *vc4 = to_vc4_dev(state->dev);
+	struct drm_private_state *priv_state;
+
+	priv_state = drm_atomic_get_old_private_obj_state(state, &vc4->hvs_channels);
+	if (IS_ERR(priv_state))
+		return ERR_CAST(priv_state);
+
+	return to_vc4_hvs_state(priv_state);
+}
+
+static struct vc4_hvs_state *
 vc4_hvs_get_global_state(struct drm_atomic_state *state)
 {
 	struct vc4_dev *vc4 = to_vc4_dev(state->dev);
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:340 @
 	}
 }
 
-static void
-vc4_atomic_complete_commit(struct drm_atomic_state *state)
+static void vc4_atomic_commit_tail(struct drm_atomic_state *state)
 {
 	struct drm_device *dev = state->dev;
 	struct vc4_dev *vc4 = to_vc4_dev(dev);
 	struct vc4_hvs *hvs = vc4->hvs;
+	struct drm_crtc_state *old_crtc_state;
 	struct drm_crtc_state *new_crtc_state;
+	struct vc4_hvs_state *new_hvs_state;
 	struct drm_crtc *crtc;
+	struct vc4_hvs_state *old_hvs_state;
+	struct clk_request *core_req;
 	int i;
 
+	old_hvs_state = vc4_hvs_get_old_global_state(state);
+	if (WARN_ON(!old_hvs_state))
+		return;
+
+	new_hvs_state = vc4_hvs_get_new_global_state(state);
+	if (WARN_ON(!new_hvs_state))
+		return;
+
 	for_each_new_crtc_in_state(state, crtc, new_crtc_state, i) {
 		struct vc4_crtc_state *vc4_crtc_state;
 
-		if (!new_crtc_state->commit)
+		if (!new_crtc_state->commit || vc4->firmware_kms)
 			continue;
 
 		vc4_crtc_state = to_vc4_crtc_state(new_crtc_state);
 		vc4_hvs_mask_underrun(dev, vc4_crtc_state->assigned_channel);
 	}
 
-	if (vc4->hvs->hvs5)
-		clk_set_min_rate(hvs->core_clk, 500000000);
+	if (vc4->hvs && vc4->hvs->hvs5) {
+		unsigned long core_rate = max_t(unsigned long,
+						500000000,
+						new_hvs_state->core_clock_rate);
 
-	drm_atomic_helper_wait_for_fences(dev, state, false);
+		drm_dbg(dev, "Raising the core clock at %lu Hz\n", core_rate);
 
-	drm_atomic_helper_wait_for_dependencies(state);
+		/*
+		 * Do a temporary request on the core clock during the
+		 * modeset.
+		 */
+		core_req = clk_request_start(hvs->core_clk, core_rate);
+
+		/*
+		 * And remove the previous one based on the HVS
+		 * requirements if any.
+		 */
+		clk_request_done(hvs->core_req);
+	}
+
+	for_each_old_crtc_in_state(state, crtc, old_crtc_state, i) {
+		struct vc4_crtc_state *vc4_crtc_state =
+			to_vc4_crtc_state(old_crtc_state);
+		struct drm_crtc_commit *commit;
+		unsigned int channel = vc4_crtc_state->assigned_channel;
+		unsigned long done;
+
+		if (channel == VC4_HVS_CHANNEL_DISABLED)
+			continue;
+
+		if (!old_hvs_state->fifo_state[channel].in_use)
+			continue;
+
+		commit = old_hvs_state->fifo_state[i].pending_commit;
+		if (!commit)
+			continue;
+
+		done = wait_for_completion_timeout(&commit->hw_done, 10 * HZ);
+		if (!done)
+			drm_err(dev, "Timed out waiting for hw_done\n");
+
+		done = wait_for_completion_timeout(&commit->flip_done, 10 * HZ);
+		if (!done)
+			drm_err(dev, "Timed out waiting for flip_done\n");
+	}
 
 	drm_atomic_helper_commit_modeset_disables(dev, state);
 
 	vc4_ctm_commit(vc4, state);
 
-	if (vc4->hvs->hvs5)
-		vc5_hvs_pv_muxing_commit(vc4, state);
-	else
-		vc4_hvs_pv_muxing_commit(vc4, state);
+	if (!vc4->firmware_kms) {
+		if (vc4->hvs->hvs5)
+			vc5_hvs_pv_muxing_commit(vc4, state);
+		else
+			vc4_hvs_pv_muxing_commit(vc4, state);
+	}
 
 	drm_atomic_helper_commit_planes(dev, state, 0);
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:440 @
 
 	drm_atomic_helper_cleanup_planes(dev, state);
 
-	drm_atomic_helper_commit_cleanup_done(state);
+	if (vc4->hvs && vc4->hvs->hvs5) {
+		drm_dbg(dev, "Running the core clock at %lu Hz\n",
+			new_hvs_state->core_clock_rate);
 
-	if (vc4->hvs->hvs5)
-		clk_set_min_rate(hvs->core_clk, 0);
-
-	drm_atomic_state_put(state);
-
-	up(&vc4->async_modeset);
-}
+		/*
+		 * Request a clock rate based on the current HVS
+		 * requirements.
+		 */
+		hvs->core_req = clk_request_start(hvs->core_clk,
+						  new_hvs_state->core_clock_rate);
 
-static void commit_work(struct work_struct *work)
-{
-	struct drm_atomic_state *state = container_of(work,
-						      struct drm_atomic_state,
-						      commit_work);
-	vc4_atomic_complete_commit(state);
+		/* And drop the temporary request */
+		clk_request_done(core_req);
+	}
 }
 
-/**
- * vc4_atomic_commit - commit validated state object
- * @dev: DRM device
- * @state: the driver state object
- * @nonblock: nonblocking commit
- *
- * This function commits a with drm_atomic_helper_check() pre-validated state
- * object. This can still fail when e.g. the framebuffer reservation fails. For
- * now this doesn't implement asynchronous commits.
- *
- * RETURNS
- * Zero for success or -errno.
- */
-static int vc4_atomic_commit(struct drm_device *dev,
-			     struct drm_atomic_state *state,
-			     bool nonblock)
+static int vc4_atomic_commit_setup(struct drm_atomic_state *state)
 {
+	struct drm_device *dev = state->dev;
 	struct vc4_dev *vc4 = to_vc4_dev(dev);
-	int ret;
-
-	if (state->async_update) {
-		ret = down_interruptible(&vc4->async_modeset);
-		if (ret)
-			return ret;
-
-		ret = drm_atomic_helper_prepare_planes(dev, state);
-		if (ret) {
-			up(&vc4->async_modeset);
-			return ret;
-		}
-
-		drm_atomic_helper_async_commit(dev, state);
-
-		drm_atomic_helper_cleanup_planes(dev, state);
-
-		up(&vc4->async_modeset);
-
-		return 0;
-	}
+	struct drm_crtc_state *crtc_state;
+	struct vc4_hvs_state *hvs_state;
+	struct drm_crtc *crtc;
+	unsigned int i;
 
 	/* We know for sure we don't want an async update here. Set
 	 * state->legacy_cursor_update to false to prevent
 	 * drm_atomic_helper_setup_commit() from auto-completing
 	 * commit->flip_done.
 	 */
-	state->legacy_cursor_update = false;
-	ret = drm_atomic_helper_setup_commit(state, nonblock);
-	if (ret)
-		return ret;
-
-	INIT_WORK(&state->commit_work, commit_work);
-
-	ret = down_interruptible(&vc4->async_modeset);
-	if (ret)
-		return ret;
+	if (!vc4->firmware_kms)
+		state->legacy_cursor_update = false;
 
-	ret = drm_atomic_helper_prepare_planes(dev, state);
-	if (ret) {
-		up(&vc4->async_modeset);
-		return ret;
-	}
-
-	if (!nonblock) {
-		ret = drm_atomic_helper_wait_for_fences(dev, state, true);
-		if (ret) {
-			drm_atomic_helper_cleanup_planes(dev, state);
-			up(&vc4->async_modeset);
-			return ret;
-		}
-	}
+	hvs_state = vc4_hvs_get_new_global_state(state);
+	if (!hvs_state)
+		return -EINVAL;
 
-	/*
-	 * This is the point of no return - everything below never fails except
-	 * when the hw goes bonghits. Which means we can commit the new state on
-	 * the software side now.
-	 */
+	for_each_new_crtc_in_state(state, crtc, crtc_state, i) {
+		struct vc4_crtc_state *vc4_crtc_state =
+			to_vc4_crtc_state(crtc_state);
+		unsigned int channel =
+			vc4_crtc_state->assigned_channel;
 
-	BUG_ON(drm_atomic_helper_swap_state(state, false) < 0);
+		if (channel == VC4_HVS_CHANNEL_DISABLED)
+			continue;
 
-	/*
-	 * Everything below can be run asynchronously without the need to grab
-	 * any modeset locks at all under one condition: It must be guaranteed
-	 * that the asynchronous work has either been cancelled (if the driver
-	 * supports it, which at least requires that the framebuffers get
-	 * cleaned up with drm_atomic_helper_cleanup_planes()) or completed
-	 * before the new state gets committed on the software side with
-	 * drm_atomic_helper_swap_state().
-	 *
-	 * This scheme allows new atomic state updates to be prepared and
-	 * checked in parallel to the asynchronous completion of the previous
-	 * update. Which is important since compositors need to figure out the
-	 * composition of the next frame right after having submitted the
-	 * current layout.
-	 */
+		if (!hvs_state->fifo_state[channel].in_use)
+			continue;
 
-	drm_atomic_state_get(state);
-	if (nonblock)
-		queue_work(system_unbound_wq, &state->commit_work);
-	else
-		vc4_atomic_complete_commit(state);
+		hvs_state->fifo_state[channel].pending_commit =
+			drm_crtc_commit_get(crtc_state->commit);
+	}
 
 	return 0;
 }
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:615 @
 	struct drm_plane *plane;
 	int i;
 
-	if (!vc4->load_tracker_available)
-		return 0;
-
 	priv_state = drm_atomic_get_private_obj_state(state,
 						      &vc4->load_tracker);
 	if (IS_ERR(priv_state))
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:689 @
 {
 	struct vc4_dev *vc4 = to_vc4_dev(dev);
 
-	if (!vc4->load_tracker_available)
-		return;
-
 	drm_atomic_private_obj_fini(&vc4->load_tracker);
 }
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:696 @
 {
 	struct vc4_load_tracker_state *load_state;
 
-	if (!vc4->load_tracker_available)
-		return 0;
-
 	load_state = kzalloc(sizeof(*load_state), GFP_KERNEL);
 	if (!load_state)
 		return -ENOMEM;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:712 @
 {
 	struct vc4_hvs_state *old_state = to_vc4_hvs_state(obj->state);
 	struct vc4_hvs_state *state;
+	unsigned int i;
 
 	state = kzalloc(sizeof(*state), GFP_KERNEL);
 	if (!state)
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:720 @
 
 	__drm_atomic_helper_private_obj_duplicate_state(obj, &state->base);
 
-	state->unassigned_channels = old_state->unassigned_channels;
+	for (i = 0; i < HVS_NUM_CHANNELS; i++) {
+		state->fifo_state[i].in_use = old_state->fifo_state[i].in_use;
+		state->fifo_state[i].fifo_load = old_state->fifo_state[i].fifo_load;
+
+		if (!old_state->fifo_state[i].pending_commit)
+			continue;
+
+		state->fifo_state[i].pending_commit =
+			drm_crtc_commit_get(old_state->fifo_state[i].pending_commit);
+	}
+
+	state->core_clock_rate = old_state->core_clock_rate;
+
 
 	return &state->base;
 }
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:741 @
 					   struct drm_private_state *state)
 {
 	struct vc4_hvs_state *hvs_state = to_vc4_hvs_state(state);
+	unsigned int i;
+
+	for (i = 0; i < HVS_NUM_CHANNELS; i++) {
+		if (!hvs_state->fifo_state[i].pending_commit)
+			continue;
+
+		drm_crtc_commit_put(hvs_state->fifo_state[i].pending_commit);
+	}
 
 	kfree(hvs_state);
 }
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:773 @
 	if (!state)
 		return -ENOMEM;
 
-	state->unassigned_channels = GENMASK(HVS_NUM_CHANNELS - 1, 0);
 	drm_atomic_private_obj_init(&vc4->base, &vc4->hvs_channels,
 				    &state->base,
 				    &vc4_hvs_state_funcs);
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:813 @
 static int vc4_pv_muxing_atomic_check(struct drm_device *dev,
 				      struct drm_atomic_state *state)
 {
+	struct vc4_dev *vc4 = to_vc4_dev(state->dev);
 	struct vc4_hvs_state *hvs_new_state;
 	struct drm_crtc_state *old_crtc_state, *new_crtc_state;
 	struct drm_crtc *crtc;
+	unsigned int unassigned_channels = 0;
 	unsigned int i;
 
 	hvs_new_state = vc4_hvs_get_global_state(state);
 	if (!hvs_new_state)
 		return -EINVAL;
 
+	for (i = 0; i < ARRAY_SIZE(hvs_new_state->fifo_state); i++)
+		if (!hvs_new_state->fifo_state[i].in_use)
+			unassigned_channels |= BIT(i);
+
 	for_each_oldnew_crtc_in_state(state, crtc, old_crtc_state, new_crtc_state, i) {
 		struct vc4_crtc_state *old_vc4_crtc_state =
 			to_vc4_crtc_state(old_crtc_state);
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:835 @
 			to_vc4_crtc_state(new_crtc_state);
 		struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
 		unsigned int matching_channels;
+		unsigned int channel;
+
+		if (vc4->firmware_kms)
+			continue;
 
 		/* Nothing to do here, let's skip it */
 		if (old_crtc_state->enable == new_crtc_state->enable)
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:849 @
 
 		/* If we're disabling our CRTC, we put back our channel */
 		if (!new_crtc_state->enable) {
-			hvs_new_state->unassigned_channels |= BIT(old_vc4_crtc_state->assigned_channel);
+			channel = old_vc4_crtc_state->assigned_channel;
+			hvs_new_state->fifo_state[channel].in_use = false;
 			new_vc4_crtc_state->assigned_channel = VC4_HVS_CHANNEL_DISABLED;
 			continue;
 		}
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:879 @
 		 * the future, we will need to have something smarter,
 		 * but it works so far.
 		 */
-		matching_channels = hvs_new_state->unassigned_channels & vc4_crtc->data->hvs_available_channels;
-		if (matching_channels) {
-			unsigned int channel = ffs(matching_channels) - 1;
-
-			new_vc4_crtc_state->assigned_channel = channel;
-			hvs_new_state->unassigned_channels &= ~BIT(channel);
-		} else {
+		matching_channels = unassigned_channels & vc4_crtc->data->hvs_available_channels;
+		if (!matching_channels)
 			return -EINVAL;
+
+		channel = ffs(matching_channels) - 1;
+		new_vc4_crtc_state->assigned_channel = channel;
+		unassigned_channels &= ~BIT(channel);
+		hvs_new_state->fifo_state[channel].in_use = true;
+	}
+
+	return 0;
+}
+
+static int
+vc4_core_clock_atomic_check(struct drm_atomic_state *state)
+{
+	struct vc4_dev *vc4 = to_vc4_dev(state->dev);
+	struct drm_private_state *priv_state;
+	struct vc4_hvs_state *hvs_new_state;
+	struct vc4_load_tracker_state *load_state;
+	struct drm_crtc_state *old_crtc_state, *new_crtc_state;
+	struct drm_crtc *crtc;
+	unsigned int num_outputs;
+	unsigned long pixel_rate;
+	unsigned long cob_rate;
+	unsigned int i;
+
+	priv_state = drm_atomic_get_private_obj_state(state,
+						      &vc4->load_tracker);
+	if (IS_ERR(priv_state))
+		return PTR_ERR(priv_state);
+
+	load_state = to_vc4_load_tracker_state(priv_state);
+
+	hvs_new_state = vc4_hvs_get_global_state(state);
+	if (!hvs_new_state)
+		return -EINVAL;
+
+	for_each_oldnew_crtc_in_state(state, crtc,
+				      old_crtc_state,
+				      new_crtc_state,
+				      i) {
+		if (old_crtc_state->active) {
+			struct vc4_crtc_state *old_vc4_state =
+				to_vc4_crtc_state(old_crtc_state);
+			unsigned int channel = old_vc4_state->assigned_channel;
+
+			hvs_new_state->fifo_state[channel].fifo_load = 0;
 		}
+
+		if (new_crtc_state->active) {
+			struct vc4_crtc_state *new_vc4_state =
+				to_vc4_crtc_state(new_crtc_state);
+			unsigned int channel = new_vc4_state->assigned_channel;
+
+			hvs_new_state->fifo_state[channel].fifo_load =
+				new_vc4_state->hvs_load;
+		}
+	}
+
+	cob_rate = 0;
+	num_outputs = 0;
+	for (i = 0; i < HVS_NUM_CHANNELS; i++) {
+		if (!hvs_new_state->fifo_state[i].in_use)
+			continue;
+
+		num_outputs++;
+		cob_rate += hvs_new_state->fifo_state[i].fifo_load;
 	}
 
+	pixel_rate = load_state->hvs_load;
+	if (num_outputs > 1) {
+		pixel_rate = (pixel_rate * 40) / 100;
+	} else {
+		pixel_rate = (pixel_rate * 60) / 100;
+	}
+
+	hvs_new_state->core_clock_rate = max(cob_rate, pixel_rate);
+
 	return 0;
 }
 
+
 static int
 vc4_atomic_check(struct drm_device *dev, struct drm_atomic_state *state)
 {
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:979 @
 	if (ret)
 		return ret;
 
-	return vc4_load_tracker_atomic_check(state);
+	ret = vc4_load_tracker_atomic_check(state);
+	if (ret)
+		return ret;
+
+	return vc4_core_clock_atomic_check(state);
 }
 
+static struct drm_mode_config_helper_funcs vc4_mode_config_helpers = {
+	.atomic_commit_setup	= vc4_atomic_commit_setup,
+	.atomic_commit_tail	= vc4_atomic_commit_tail,
+};
+
 static const struct drm_mode_config_funcs vc4_mode_funcs = {
 	.atomic_check = vc4_atomic_check,
-	.atomic_commit = vc4_atomic_commit,
+	.atomic_commit = drm_atomic_helper_commit,
 	.fb_create = vc4_fb_create,
 };
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1004 @
 					      "brcm,bcm2711-vc5");
 	int ret;
 
+	/*
+	 * The limits enforced by the load tracker aren't relevant for
+	 * the BCM2711, but the load tracker computations are used for
+	 * the core clock rate calculation.
+	 */
 	if (!is_vc5) {
-		vc4->load_tracker_available = true;
-
 		/* Start with the load tracker enabled. Can be
 		 * disabled through the debugfs load_tracker file.
 		 */
 		vc4->load_tracker_enabled = true;
 	}
 
-	sema_init(&vc4->async_modeset, 1);
-
 	/* Set support for vblank irq fast disable, before drm_vblank_init() */
 	dev->vblank_disable_immediate = true;
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1035 @
 	}
 
 	dev->mode_config.funcs = &vc4_mode_funcs;
+	dev->mode_config.helper_private = &vc4_mode_config_helpers;
 	dev->mode_config.preferred_depth = 24;
 	dev->mode_config.async_page_flip = true;
 	dev->mode_config.allow_fb_modifiers = true;
+	if (vc4->firmware_kms)
+		dev->mode_config.normalize_zpos = true;
 
 	ret = vc4_ctm_obj_init(vc4);
 	if (ret)
diff -Nur --no-dereference linux-5.10.52-orig/drivers/gpu/drm/vc4/vc4_perfmon.c linux-5.10.52-v7l+/drivers/gpu/drm/vc4/vc4_perfmon.c
--- linux-5.10.52-orig/drivers/gpu/drm/vc4/vc4_perfmon.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/gpu/drm/vc4/vc4_perfmon.c	2021-07-25 16:46:01.708362267 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:80 @
 void vc4_perfmon_open_file(struct vc4_file *vc4file)
 {
 	mutex_init(&vc4file->perfmon.lock);
-	idr_init(&vc4file->perfmon.idr);
+	idr_init_base(&vc4file->perfmon.idr, VC4_PERFMONID_MIN);
 }
 
 static int vc4_perfmon_idr_del(int id, void *elem, void *data)
diff -Nur --no-dereference linux-5.10.52-orig/drivers/gpu/drm/vc4/vc4_plane.c linux-5.10.52-v7l+/drivers/gpu/drm/vc4/vc4_plane.c
--- linux-5.10.52-orig/drivers/gpu/drm/vc4/vc4_plane.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/gpu/drm/vc4/vc4_plane.c	2021-07-25 16:46:01.708362267 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:36 @
 	u32 hvs; /* HVS_FORMAT_* */
 	u32 pixel_order;
 	u32 pixel_order_hvs5;
+	bool hvs5_only;
 } hvs_formats[] = {
 	{
 		.drm = DRM_FORMAT_XRGB8888,
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:132 @
 		.hvs = HVS_PIXEL_FORMAT_YCBCR_YUV422_2PLANE,
 		.pixel_order = HVS_PIXEL_ORDER_XYCRCB,
 	},
+	{
+		.drm = DRM_FORMAT_P030,
+		.hvs = HVS_PIXEL_FORMAT_YCBCR_10BIT,
+		.pixel_order = HVS_PIXEL_ORDER_XYCBCR,
+		.hvs5_only = true,
+	},
 };
 
 static const struct hvs_format *vc4_get_hvs_format(u32 drm_format)
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:313 @
 					       adjhdisplay,
 					       crtc_state->mode.hdisplay);
 	vc4_pstate->crtc_x += left;
-	if (vc4_pstate->crtc_x > crtc_state->mode.hdisplay - left)
-		vc4_pstate->crtc_x = crtc_state->mode.hdisplay - left;
+	if (vc4_pstate->crtc_x > crtc_state->mode.hdisplay - right)
+		vc4_pstate->crtc_x = crtc_state->mode.hdisplay - right;
 
 	adjvdisplay = crtc_state->mode.vdisplay - (top + bottom);
 	vc4_pstate->crtc_y = DIV_ROUND_CLOSEST(vc4_pstate->crtc_y *
 					       adjvdisplay,
 					       crtc_state->mode.vdisplay);
 	vc4_pstate->crtc_y += top;
-	if (vc4_pstate->crtc_y > crtc_state->mode.vdisplay - top)
-		vc4_pstate->crtc_y = crtc_state->mode.vdisplay - top;
+	if (vc4_pstate->crtc_y > crtc_state->mode.vdisplay - bottom)
+		vc4_pstate->crtc_y = crtc_state->mode.vdisplay - bottom;
 
 	vc4_pstate->crtc_w = DIV_ROUND_CLOSEST(vc4_pstate->crtc_w *
 					       adjhdisplay,
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:342 @
 	struct vc4_plane_state *vc4_state = to_vc4_plane_state(state);
 	struct drm_framebuffer *fb = state->fb;
 	struct drm_gem_cma_object *bo = drm_fb_cma_get_gem_obj(fb, 0);
-	u32 subpixel_src_mask = (1 << 16) - 1;
 	int num_planes = fb->format->num_planes;
 	struct drm_crtc_state *crtc_state;
 	u32 h_subsample = fb->format->hsub;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:363 @
 	for (i = 0; i < num_planes; i++)
 		vc4_state->offsets[i] = bo->paddr + fb->offsets[i];
 
-	/* We don't support subpixel source positioning for scaling. */
-	if ((state->src.x1 & subpixel_src_mask) ||
-	    (state->src.x2 & subpixel_src_mask) ||
-	    (state->src.y1 & subpixel_src_mask) ||
-	    (state->src.y2 & subpixel_src_mask)) {
-		return -EINVAL;
-	}
-
-	vc4_state->src_x = state->src.x1 >> 16;
-	vc4_state->src_y = state->src.y1 >> 16;
-	vc4_state->src_w[0] = (state->src.x2 - state->src.x1) >> 16;
-	vc4_state->src_h[0] = (state->src.y2 - state->src.y1) >> 16;
+	/* We don't support subpixel source positioning for scaling,
+	 * but fractional coordinates can be generated by clipping
+	 * so just round for now
+	 */
+	vc4_state->src_x = DIV_ROUND_CLOSEST(state->src.x1, 1<<16);
+	vc4_state->src_y = DIV_ROUND_CLOSEST(state->src.y1, 1<<16);
+	vc4_state->src_w[0] = DIV_ROUND_CLOSEST(state->src.x2, 1<<16) - vc4_state->src_x;
+	vc4_state->src_h[0] = DIV_ROUND_CLOSEST(state->src.y2, 1<<16) - vc4_state->src_y;
 
 	vc4_state->crtc_x = state->dst.x1;
 	vc4_state->crtc_y = state->dst.y1;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:537 @
 	struct vc4_dev *vc4;
 
 	vc4 = to_vc4_dev(state->plane->dev);
-	if (!vc4->load_tracker_available)
-		return;
-
 	vc4_state = to_vc4_plane_state(state);
 	crtc_state = drm_atomic_get_existing_crtc_state(state->state,
 							state->crtc);
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:623 @
 	return 0;
 }
 
+/* The colorspace conversion matrices are held in 3 entries in the dlist.
+ * Create an array of them, with entries for each full and limited mode, and
+ * each supported colorspace.
+ */
+#define VC4_LIMITED_RANGE	0
+#define VC4_FULL_RANGE		1
+
+static const u32 colorspace_coeffs[2][DRM_COLOR_ENCODING_MAX][3] = {
+	{
+		/* Limited range */
+		{
+			/* BT601 */
+			SCALER_CSC0_ITR_R_601_5,
+			SCALER_CSC1_ITR_R_601_5,
+			SCALER_CSC2_ITR_R_601_5,
+		}, {
+			/* BT709 */
+			SCALER_CSC0_ITR_R_709_3,
+			SCALER_CSC1_ITR_R_709_3,
+			SCALER_CSC2_ITR_R_709_3,
+		}, {
+			/* BT2020 */
+			SCALER_CSC0_ITR_R_2020,
+			SCALER_CSC1_ITR_R_2020,
+			SCALER_CSC2_ITR_R_2020,
+		}
+	}, {
+		/* Full range */
+		{
+			/* JFIF */
+			SCALER_CSC0_JPEG_JFIF,
+			SCALER_CSC1_JPEG_JFIF,
+			SCALER_CSC2_JPEG_JFIF,
+		}, {
+			/* BT709 */
+			SCALER_CSC0_ITR_R_709_3_FR,
+			SCALER_CSC1_ITR_R_709_3_FR,
+			SCALER_CSC2_ITR_R_709_3_FR,
+		}, {
+			/* BT2020 */
+			SCALER_CSC0_ITR_R_2020_FR,
+			SCALER_CSC1_ITR_R_2020_FR,
+			SCALER_CSC2_ITR_R_2020_FR,
+		}
+	}
+};
+
 /* Writes out a full display list for an active plane to the plane's
  * private dlist state.
  */
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:818 @
 		uint32_t param = fourcc_mod_broadcom_param(fb->modifier);
 		u32 tile_w, tile, x_off, pix_per_tile;
 
-		hvs_format = HVS_PIXEL_FORMAT_H264;
+		if (fb->format->format == DRM_FORMAT_P030) {
+			/*
+			 * Spec says: bits [31:4] of the given address should point to
+			 * the 128-bit word containing the desired starting pixel,
+			 * and bits[3:0] should be between 0 and 11, indicating which
+			 * of the 12-pixels in that 128-bit word is the first pixel to be used
+			 */
+	                u32 remaining_pixels = vc4_state->src_x % 96;
+			u32 aligned = remaining_pixels / 12;
+			u32 last_bits = remaining_pixels % 12;
 
-		switch (base_format_mod) {
-		case DRM_FORMAT_MOD_BROADCOM_SAND64:
-			tiling = SCALER_CTL0_TILING_64B;
-			tile_w = 64;
-			break;
-		case DRM_FORMAT_MOD_BROADCOM_SAND128:
+			x_off = aligned * 16 + last_bits;
+			hvs_format = HVS_PIXEL_FORMAT_YCBCR_10BIT;
 			tiling = SCALER_CTL0_TILING_128B;
 			tile_w = 128;
-			break;
-		case DRM_FORMAT_MOD_BROADCOM_SAND256:
-			tiling = SCALER_CTL0_TILING_256B_OR_T;
-			tile_w = 256;
-			break;
-		default:
-			break;
-		}
+			pix_per_tile = 96;
+		} else {
+			hvs_format = HVS_PIXEL_FORMAT_H264;
 
+			switch (base_format_mod) {
+			case DRM_FORMAT_MOD_BROADCOM_SAND64:
+				tiling = SCALER_CTL0_TILING_64B;
+				tile_w = 64;
+				break;
+			case DRM_FORMAT_MOD_BROADCOM_SAND128:
+				tiling = SCALER_CTL0_TILING_128B;
+				tile_w = 128;
+				break;
+			case DRM_FORMAT_MOD_BROADCOM_SAND256:
+				tiling = SCALER_CTL0_TILING_256B_OR_T;
+				tile_w = 256;
+				break;
+			default:
+				break;
+			}
+			pix_per_tile = tile_w / fb->format->cpp[0];
+			x_off = (vc4_state->src_x % pix_per_tile) /
+				(i ? h_subsample : 1) * fb->format->cpp[i];
+		}
 		if (param > SCALER_TILE_HEIGHT_MASK) {
-			DRM_DEBUG_KMS("SAND height too large (%d)\n", param);
+			DRM_DEBUG_KMS("SAND height too large (%d)\n",
+				      param);
 			return -EINVAL;
 		}
-
-		pix_per_tile = tile_w / fb->format->cpp[0];
 		tile = vc4_state->src_x / pix_per_tile;
-		x_off = vc4_state->src_x % pix_per_tile;
-
 		/* Adjust the base pointer to the first pixel to be scanned
 		 * out.
+		 *
+		 * For P030, y_ptr [31:4] is the 128bit word for the start pixel
+		 * y_ptr [3:0] is the pixel (0-11) contained within that 128bit
+		 * word that should be taken as the first pixel.
+		 * Ditto uv_ptr [31:4] vs [3:0], however [3:0] contains the
+		 * element within the 128bit word, eg for pixel 3 the value
+		 * should be 6.
 		 */
 		for (i = 0; i < num_planes; i++) {
 			vc4_state->offsets[i] += param * tile_w * tile;
 			vc4_state->offsets[i] += src_y /
 						 (i ? v_subsample : 1) *
 						 tile_w;
-			vc4_state->offsets[i] += x_off /
-						 (i ? h_subsample : 1) *
-						 fb->format->cpp[i];
+			vc4_state->offsets[i] += x_off & ~(i ? 1 : 0);
 		}
 
 		pitch0 = VC4_SET_FIELD(param, SCALER_TILE_HEIGHT);
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1031 @
 
 	/* Pitch word 1/2 */
 	for (i = 1; i < num_planes; i++) {
-		if (hvs_format != HVS_PIXEL_FORMAT_H264) {
+		if (hvs_format != HVS_PIXEL_FORMAT_H264 &&
+		    hvs_format != HVS_PIXEL_FORMAT_YCBCR_10BIT) {
 			vc4_dlist_write(vc4_state,
 					VC4_SET_FIELD(fb->pitches[i],
 						      SCALER_SRC_PITCH));
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1043 @
 
 	/* Colorspace conversion words */
 	if (vc4_state->is_yuv) {
-		vc4_dlist_write(vc4_state, SCALER_CSC0_ITR_R_601_5);
-		vc4_dlist_write(vc4_state, SCALER_CSC1_ITR_R_601_5);
-		vc4_dlist_write(vc4_state, SCALER_CSC2_ITR_R_601_5);
+		enum drm_color_encoding color_encoding = state->color_encoding;
+		enum drm_color_range color_range = state->color_range;
+		const u32 *ccm;
+
+		if (color_encoding >= DRM_COLOR_ENCODING_MAX)
+			color_encoding = DRM_COLOR_YCBCR_BT601;
+		if (color_range >= DRM_COLOR_RANGE_MAX)
+			color_range = DRM_COLOR_YCBCR_LIMITED_RANGE;
+
+		ccm = colorspace_coeffs[color_range][color_encoding];
+
+		vc4_dlist_write(vc4_state, ccm[0]);
+		vc4_dlist_write(vc4_state, ccm[1]);
+		vc4_dlist_write(vc4_state, ccm[2]);
 	}
 
 	vc4_state->lbm_offset = 0;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1229 @
 	plane->state->src_y = state->src_y;
 	plane->state->src_w = state->src_w;
 	plane->state->src_h = state->src_h;
-	plane->state->src_h = state->src_h;
 	plane->state->alpha = state->alpha;
 	plane->state->pixel_blend_mode = state->pixel_blend_mode;
 	plane->state->rotation = state->rotation;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1365 @
 	.atomic_async_update = vc4_plane_atomic_async_update,
 };
 
-static void vc4_plane_destroy(struct drm_plane *plane)
-{
-	drm_plane_cleanup(plane);
-}
-
 static bool vc4_format_mod_supported(struct drm_plane *plane,
 				     uint32_t format,
 				     uint64_t modifier)
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1397 @
 		default:
 			return false;
 		}
+	case DRM_FORMAT_P030:
+		switch (fourcc_mod_broadcom_mod(modifier)) {
+		case DRM_FORMAT_MOD_BROADCOM_SAND128:
+			return true;
+		default:
+			return false;
+		}
 	case DRM_FORMAT_RGBX1010102:
 	case DRM_FORMAT_BGRX1010102:
 	case DRM_FORMAT_RGBA1010102:
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1422 @
 static const struct drm_plane_funcs vc4_plane_funcs = {
 	.update_plane = drm_atomic_helper_update_plane,
 	.disable_plane = drm_atomic_helper_disable_plane,
-	.destroy = vc4_plane_destroy,
+	.destroy = drm_plane_cleanup,
 	.set_property = NULL,
 	.reset = vc4_plane_reset,
 	.atomic_duplicate_state = vc4_plane_duplicate_state,
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1436 @
 	struct drm_plane *plane = NULL;
 	struct vc4_plane *vc4_plane;
 	u32 formats[ARRAY_SIZE(hvs_formats)];
+	int num_formats = 0;
 	int ret = 0;
 	unsigned i;
+	bool hvs5 = of_device_is_compatible(dev->dev->of_node,
+					    "brcm,bcm2711-vc5");
 	static const uint64_t modifiers[] = {
 		DRM_FORMAT_MOD_BROADCOM_VC4_T_TILED,
 		DRM_FORMAT_MOD_BROADCOM_SAND128,
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1455 @
 	if (!vc4_plane)
 		return ERR_PTR(-ENOMEM);
 
-	for (i = 0; i < ARRAY_SIZE(hvs_formats); i++)
-		formats[i] = hvs_formats[i].drm;
+	for (i = 0; i < ARRAY_SIZE(hvs_formats); i++) {
+		if (!hvs_formats[i].hvs5_only || hvs5) {
+			formats[num_formats] = hvs_formats[i].drm;
+			num_formats++;
+		}
+	}
 
 	plane = &vc4_plane->base;
 	ret = drm_universal_plane_init(dev, plane, 0,
 				       &vc4_plane_funcs,
-				       formats, ARRAY_SIZE(formats),
+				       formats, num_formats,
 				       modifiers, type, NULL);
 	if (ret)
 		return ERR_PTR(ret);
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1479 @
 					   DRM_MODE_REFLECT_X |
 					   DRM_MODE_REFLECT_Y);
 
+	drm_plane_create_color_properties(plane,
+					  BIT(DRM_COLOR_YCBCR_BT601) |
+					  BIT(DRM_COLOR_YCBCR_BT709) |
+					  BIT(DRM_COLOR_YCBCR_BT2020),
+					  BIT(DRM_COLOR_YCBCR_LIMITED_RANGE) |
+					  BIT(DRM_COLOR_YCBCR_FULL_RANGE),
+					  DRM_COLOR_YCBCR_BT709,
+					  DRM_COLOR_YCBCR_LIMITED_RANGE);
+
 	return plane;
 }
 
diff -Nur --no-dereference linux-5.10.52-orig/drivers/gpu/drm/vc4/vc4_regs.h linux-5.10.52-v7l+/drivers/gpu/drm/vc4/vc4_regs.h
--- linux-5.10.52-orig/drivers/gpu/drm/vc4/vc4_regs.h	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/gpu/drm/vc4/vc4_regs.h	2021-07-25 16:46:01.708362267 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:519 @
 # define VC4_HDMI_AUDIO_PACKET_CEA_MASK_MASK			VC4_MASK(7, 0)
 # define VC4_HDMI_AUDIO_PACKET_CEA_MASK_SHIFT			0
 
+# define VC4_HDMI_MAI_FORMAT_AUDIO_FORMAT_MASK		VC4_MASK(23, 16)
+# define VC4_HDMI_MAI_FORMAT_AUDIO_FORMAT_SHIFT		16
+
+enum {
+	VC4_HDMI_MAI_FORMAT_PCM = 2,
+	VC4_HDMI_MAI_FORMAT_HBR = 200,
+};
+
+# define VC4_HDMI_MAI_FORMAT_SAMPLE_RATE_MASK		VC4_MASK(15, 8)
+# define VC4_HDMI_MAI_FORMAT_SAMPLE_RATE_SHIFT		8
+
+enum {
+	VC4_HDMI_MAI_SAMPLE_RATE_NOT_INDICATED = 0,
+	VC4_HDMI_MAI_SAMPLE_RATE_8000 = 1,
+	VC4_HDMI_MAI_SAMPLE_RATE_11025 = 2,
+	VC4_HDMI_MAI_SAMPLE_RATE_12000 = 3,
+	VC4_HDMI_MAI_SAMPLE_RATE_16000 = 4,
+	VC4_HDMI_MAI_SAMPLE_RATE_22050 = 5,
+	VC4_HDMI_MAI_SAMPLE_RATE_24000 = 6,
+	VC4_HDMI_MAI_SAMPLE_RATE_32000 = 7,
+	VC4_HDMI_MAI_SAMPLE_RATE_44100 = 8,
+	VC4_HDMI_MAI_SAMPLE_RATE_48000 = 9,
+	VC4_HDMI_MAI_SAMPLE_RATE_64000 = 10,
+	VC4_HDMI_MAI_SAMPLE_RATE_88200 = 11,
+	VC4_HDMI_MAI_SAMPLE_RATE_96000 = 12,
+	VC4_HDMI_MAI_SAMPLE_RATE_128000 = 13,
+	VC4_HDMI_MAI_SAMPLE_RATE_176400 = 14,
+	VC4_HDMI_MAI_SAMPLE_RATE_192000 = 15,
+};
+
 # define VC4_HDMI_RAM_PACKET_ENABLE		BIT(16)
 
 /* When set, the CTS_PERIOD counts based on MAI bus sync pulse instead
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:978 @
 #define SCALER_CSC0_COEF_CR_OFS_SHIFT		0
 #define SCALER_CSC0_ITR_R_601_5			0x00f00000
 #define SCALER_CSC0_ITR_R_709_3			0x00f00000
+#define SCALER_CSC0_ITR_R_2020			0x00f00000
 #define SCALER_CSC0_JPEG_JFIF			0x00000000
+#define SCALER_CSC0_ITR_R_709_3_FR		0x00000000
+#define SCALER_CSC0_ITR_R_2020_FR		0x00000000
 
 /* S2.8 contribution of Cb to Green */
 #define SCALER_CSC1_COEF_CB_GRN_MASK		VC4_MASK(31, 22)
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:996 @
 #define SCALER_CSC1_COEF_CR_BLU_MASK		VC4_MASK(1, 0)
 #define SCALER_CSC1_COEF_CR_BLU_SHIFT		0
 #define SCALER_CSC1_ITR_R_601_5			0xe73304a8
-#define SCALER_CSC1_ITR_R_709_3			0xf2b784a8
-#define SCALER_CSC1_JPEG_JFIF			0xea34a400
+#define SCALER_CSC1_ITR_R_709_3			0xf27784a8
+#define SCALER_CSC1_ITR_R_2020			0xf43594a8
+#define SCALER_CSC1_JPEG_JFIF			0xea349400
+#define SCALER_CSC1_ITR_R_709_3_FR		0xf4388400
+#define SCALER_CSC1_ITR_R_2020_FR		0xf5b6d400
 
 /* S2.8 contribution of Cb to Red */
 #define SCALER_CSC2_COEF_CB_RED_MASK		VC4_MASK(29, 20)
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1011 @
 /* S2.8 contribution of Cb to Blue */
 #define SCALER_CSC2_COEF_CB_BLU_MASK		VC4_MASK(19, 10)
 #define SCALER_CSC2_COEF_CB_BLU_SHIFT		10
-#define SCALER_CSC2_ITR_R_601_5			0x00066204
-#define SCALER_CSC2_ITR_R_709_3			0x00072a1c
-#define SCALER_CSC2_JPEG_JFIF			0x000599c5
+#define SCALER_CSC2_ITR_R_601_5			0x00066604
+#define SCALER_CSC2_ITR_R_709_3			0x00072e1d
+#define SCALER_CSC2_ITR_R_2020			0x0006b624
+#define SCALER_CSC2_JPEG_JFIF			0x00059dc6
+#define SCALER_CSC2_ITR_R_709_3_FR		0x00064ddb
+#define SCALER_CSC2_ITR_R_2020_FR		0x0005e5e2
 
 #define SCALER_TPZ0_VERT_RECALC			BIT(31)
 #define SCALER_TPZ0_SCALE_MASK			VC4_MASK(28, 8)
diff -Nur --no-dereference linux-5.10.52-orig/drivers/gpu/drm/vc4/vc4_txp.c linux-5.10.52-v7l+/drivers/gpu/drm/vc4/vc4_txp.c
--- linux-5.10.52-orig/drivers/gpu/drm/vc4/vc4_txp.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/gpu/drm/vc4/vc4_txp.c	2021-07-25 16:46:01.708362267 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:16 @
 #include <linux/of_platform.h>
 #include <linux/pm_runtime.h>
 
+#include <drm/drm_atomic.h>
 #include <drm/drm_atomic_helper.h>
 #include <drm/drm_edid.h>
 #include <drm/drm_fb_cma_helper.h>
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:276 @
 }
 
 static void vc4_txp_connector_atomic_commit(struct drm_connector *conn,
-					struct drm_connector_state *conn_state)
+					struct drm_atomic_state *state)
 {
+	struct drm_connector_state *conn_state = drm_atomic_get_new_connector_state(state,
+										    conn);
 	struct vc4_txp *txp = connector_to_vc4_txp(conn);
 	struct drm_gem_cma_object *gem;
 	struct drm_display_mode *mode;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:385 @
 	.reset			= vc4_crtc_reset,
 	.atomic_duplicate_state	= vc4_crtc_duplicate_state,
 	.atomic_destroy_state	= vc4_crtc_destroy_state,
-	.gamma_set		= drm_atomic_helper_legacy_gamma_set,
 	.enable_vblank		= vc4_txp_enable_vblank,
 	.disable_vblank		= vc4_txp_disable_vblank,
 };
 
 static int vc4_txp_atomic_check(struct drm_crtc *crtc,
-				struct drm_crtc_state *state)
+				struct drm_atomic_state *state)
 {
-	struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(state);
+	struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state,
+									  crtc);
+	struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(crtc_state);
 	int ret;
 
 	ret = vc4_hvs_atomic_check(crtc, state);
 	if (ret)
 		return ret;
 
-	state->no_vblank = true;
+	crtc_state->no_vblank = true;
 	vc4_state->feed_txp = true;
 
 	return 0;
 }
 
 static void vc4_txp_atomic_enable(struct drm_crtc *crtc,
-				  struct drm_crtc_state *old_state)
+				  struct drm_atomic_state *state)
 {
 	drm_crtc_vblank_on(crtc);
-	vc4_hvs_atomic_enable(crtc, old_state);
+	vc4_hvs_atomic_enable(crtc, state);
 }
 
 static void vc4_txp_atomic_disable(struct drm_crtc *crtc,
-				   struct drm_crtc_state *old_state)
+				   struct drm_atomic_state *state)
 {
 	struct drm_device *dev = crtc->dev;
 
 	/* Disable vblank irq handling before crtc is disabled. */
 	drm_crtc_vblank_off(crtc);
 
-	vc4_hvs_atomic_disable(crtc, old_state);
+	vc4_hvs_atomic_disable(crtc, state);
 
 	/*
 	 * Make sure we issue a vblank event after disabling the CRTC if
diff -Nur --no-dereference linux-5.10.52-orig/drivers/gpu/drm/vc4/vc4_vec.c linux-5.10.52-v7l+/drivers/gpu/drm/vc4/vc4_vec.c
--- linux-5.10.52-orig/drivers/gpu/drm/vc4/vc4_vec.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/gpu/drm/vc4/vc4_vec.c	2021-07-25 16:46:01.708362267 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:48 @
 #define VEC_CONFIG0_YDEL(x)		((x) << 26)
 #define VEC_CONFIG0_CDEL_MASK		GENMASK(25, 24)
 #define VEC_CONFIG0_CDEL(x)		((x) << 24)
+#define VEC_CONFIG0_SECAM_STD		BIT(21)
 #define VEC_CONFIG0_PBPR_FIL		BIT(18)
 #define VEC_CONFIG0_CHROMA_GAIN_MASK	GENMASK(17, 16)
 #define VEC_CONFIG0_CHROMA_GAIN_UNITY	(0 << 16)
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:69 @
 #define VEC_CONFIG0_YCDELAY		BIT(4)
 #define VEC_CONFIG0_RAMPEN		BIT(2)
 #define VEC_CONFIG0_YCDIS		BIT(2)
-#define VEC_CONFIG0_STD_MASK		GENMASK(1, 0)
+#define VEC_CONFIG0_STD_MASK		(VEC_CONFIG0_SECAM_STD | GENMASK(1, 0))
 #define VEC_CONFIG0_NTSC_STD		0
 #define VEC_CONFIG0_PAL_BDGHI_STD	1
+#define VEC_CONFIG0_PAL_M_STD		2
 #define VEC_CONFIG0_PAL_N_STD		3
 
 #define VEC_SCHPH			0x108
 #define VEC_SOFT_RESET			0x10c
 #define VEC_CLMP0_START			0x144
 #define VEC_CLMP0_END			0x148
+
+/*
+ * These set the color subcarrier frequency
+ * if VEC_CONFIG1_CUSTOM_FREQ is enabled.
+ *
+ * VEC_FREQ1_0 contains the most significant 16-bit half-word,
+ * VEC_FREQ3_2 contains the least significant 16-bit half-word.
+ * 0x80000000 seems to be equivalent to the pixel clock
+ * (which itself is the VEC clock divided by 8).
+ *
+ * Reference values (with the default pixel clock of 13.5 MHz):
+ *
+ * NTSC  (3579545.[45] Hz)     - 0x21F07C1F
+ * PAL   (4433618.75 Hz)       - 0x2A098ACB
+ * PAL-M (3575611.[888111] Hz) - 0x21E6EFE3
+ * PAL-N (3582056.25 Hz)       - 0x21F69446
+ *
+ * NOTE: For SECAM, it is used as the Dr center frequency,
+ * regardless of whether VEC_CONFIG1_CUSTOM_FREQ is enabled or not;
+ * that is specified as 4406250 Hz, which corresponds to 0x29C71C72.
+ */
 #define VEC_FREQ3_2			0x180
 #define VEC_FREQ1_0			0x184
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:142 @
 
 #define VEC_INTERRUPT_CONTROL		0x190
 #define VEC_INTERRUPT_STATUS		0x194
+
+/*
+ * Db center frequency for SECAM; the clock for this is the same as for
+ * VEC_FREQ3_2/VEC_FREQ1_0, which is used for Dr center frequency.
+ *
+ * This is specified as 4250000 Hz, which corresponds to 0x284BDA13.
+ * That is also the default value, so no need to set it explicitly.
+ */
 #define VEC_FCW_SECAM_B			0x198
 #define VEC_SECAM_GAIN_VAL		0x19c
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:188 @
 #define VEC_DAC_MISC_DAC_RST_N		BIT(0)
 
 
+static char *vc4_vec_tv_norm;
+
+struct vc4_vec_variant {
+	u32 dac_config;
+};
+
 /* General VEC hardware state. */
 struct vc4_vec {
 	struct platform_device *pdev;
+	const struct vc4_vec_variant *variant;
 
 	struct drm_encoder *encoder;
 	struct drm_connector *connector;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:206 @
 
 	struct clk *clock;
 
-	const struct vc4_vec_tv_mode *tv_mode;
-
 	struct debugfs_regset32 regset;
 };
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:245 @
 enum vc4_vec_tv_mode_id {
 	VC4_VEC_TV_MODE_NTSC,
 	VC4_VEC_TV_MODE_NTSC_J,
+	VC4_VEC_TV_MODE_NTSC_443,
 	VC4_VEC_TV_MODE_PAL,
 	VC4_VEC_TV_MODE_PAL_M,
+	VC4_VEC_TV_MODE_PAL_N,
+	VC4_VEC_TV_MODE_PAL60,
+	VC4_VEC_TV_MODE_SECAM,
 };
 
 struct vc4_vec_tv_mode {
 	const struct drm_display_mode *mode;
-	void (*mode_set)(struct vc4_vec *vec);
+	u32 config0;
+	u32 config1;
+	u32 custom_freq;
 };
 
 static const struct debugfs_reg32 vec_regs[] = {
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:287 @
 	VC4_REG32(VEC_DAC_MISC),
 };
 
-static void vc4_vec_ntsc_mode_set(struct vc4_vec *vec)
-{
-	VEC_WRITE(VEC_CONFIG0, VEC_CONFIG0_NTSC_STD | VEC_CONFIG0_PDEN);
-	VEC_WRITE(VEC_CONFIG1, VEC_CONFIG1_C_CVBS_CVBS);
-}
-
-static void vc4_vec_ntsc_j_mode_set(struct vc4_vec *vec)
-{
-	VEC_WRITE(VEC_CONFIG0, VEC_CONFIG0_NTSC_STD);
-	VEC_WRITE(VEC_CONFIG1, VEC_CONFIG1_C_CVBS_CVBS);
-}
-
-static const struct drm_display_mode ntsc_mode = {
+static const struct drm_display_mode drm_mode_480i = {
 	DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 13500,
 		 720, 720 + 14, 720 + 14 + 64, 720 + 14 + 64 + 60, 0,
-		 480, 480 + 3, 480 + 3 + 3, 480 + 3 + 3 + 16, 0,
+		 480, 480 + 7, 480 + 7 + 6, 525, 0,
 		 DRM_MODE_FLAG_INTERLACE)
 };
 
-static void vc4_vec_pal_mode_set(struct vc4_vec *vec)
-{
-	VEC_WRITE(VEC_CONFIG0, VEC_CONFIG0_PAL_BDGHI_STD);
-	VEC_WRITE(VEC_CONFIG1, VEC_CONFIG1_C_CVBS_CVBS);
-}
-
-static void vc4_vec_pal_m_mode_set(struct vc4_vec *vec)
-{
-	VEC_WRITE(VEC_CONFIG0, VEC_CONFIG0_PAL_BDGHI_STD);
-	VEC_WRITE(VEC_CONFIG1,
-		  VEC_CONFIG1_C_CVBS_CVBS | VEC_CONFIG1_CUSTOM_FREQ);
-	VEC_WRITE(VEC_FREQ3_2, 0x223b);
-	VEC_WRITE(VEC_FREQ1_0, 0x61d1);
-}
-
-static const struct drm_display_mode pal_mode = {
+static const struct drm_display_mode drm_mode_576i = {
 	DRM_MODE("720x576", DRM_MODE_TYPE_DRIVER, 13500,
 		 720, 720 + 20, 720 + 20 + 64, 720 + 20 + 64 + 60, 0,
-		 576, 576 + 2, 576 + 2 + 3, 576 + 2 + 3 + 20, 0,
+		 576, 576 + 4, 576 + 4 + 6, 625, 0,
 		 DRM_MODE_FLAG_INTERLACE)
 };
 
 static const struct vc4_vec_tv_mode vc4_vec_tv_modes[] = {
 	[VC4_VEC_TV_MODE_NTSC] = {
-		.mode = &ntsc_mode,
-		.mode_set = vc4_vec_ntsc_mode_set,
+		.mode = &drm_mode_480i,
+		.config0 = VEC_CONFIG0_NTSC_STD | VEC_CONFIG0_PDEN,
+		.config1 = VEC_CONFIG1_C_CVBS_CVBS,
 	},
 	[VC4_VEC_TV_MODE_NTSC_J] = {
-		.mode = &ntsc_mode,
-		.mode_set = vc4_vec_ntsc_j_mode_set,
+		.mode = &drm_mode_480i,
+		.config0 = VEC_CONFIG0_NTSC_STD,
+		.config1 = VEC_CONFIG1_C_CVBS_CVBS,
+	},
+	[VC4_VEC_TV_MODE_NTSC_443] = {
+		/* NTSC with PAL chroma frequency */
+		.mode = &drm_mode_480i,
+		.config0 = VEC_CONFIG0_NTSC_STD,
+		.config1 = VEC_CONFIG1_C_CVBS_CVBS | VEC_CONFIG1_CUSTOM_FREQ,
+		.custom_freq = 0x2a098acb,
 	},
 	[VC4_VEC_TV_MODE_PAL] = {
-		.mode = &pal_mode,
-		.mode_set = vc4_vec_pal_mode_set,
+		.mode = &drm_mode_576i,
+		.config0 = VEC_CONFIG0_PAL_BDGHI_STD,
+		.config1 = VEC_CONFIG1_C_CVBS_CVBS,
 	},
 	[VC4_VEC_TV_MODE_PAL_M] = {
-		.mode = &pal_mode,
-		.mode_set = vc4_vec_pal_m_mode_set,
+		.mode = &drm_mode_480i,
+		.config0 = VEC_CONFIG0_PAL_M_STD,
+		.config1 = VEC_CONFIG1_C_CVBS_CVBS,
+	},
+	[VC4_VEC_TV_MODE_PAL_N] = {
+		.mode = &drm_mode_576i,
+		.config0 = VEC_CONFIG0_PAL_N_STD,
+		.config1 = VEC_CONFIG1_C_CVBS_CVBS,
+	},
+	[VC4_VEC_TV_MODE_PAL60] = {
+		/* PAL-M with chroma frequency of regular PAL */
+		.mode = &drm_mode_480i,
+		.config0 = VEC_CONFIG0_PAL_M_STD,
+		.config1 = VEC_CONFIG1_C_CVBS_CVBS | VEC_CONFIG1_CUSTOM_FREQ,
+		.custom_freq = 0x2a098acb,
+	},
+	[VC4_VEC_TV_MODE_SECAM] = {
+		.mode = &drm_mode_576i,
+		.config0 = VEC_CONFIG0_SECAM_STD,
+		.config1 = VEC_CONFIG1_C_CVBS_CVBS,
+		.custom_freq = 0x29c71c72,
 	},
 };
 
+static const char * const tv_mode_names[] = {
+	[VC4_VEC_TV_MODE_NTSC] = "NTSC",
+	[VC4_VEC_TV_MODE_NTSC_J] = "NTSC-J",
+	[VC4_VEC_TV_MODE_NTSC_443] = "NTSC-443",
+	[VC4_VEC_TV_MODE_PAL] = "PAL",
+	[VC4_VEC_TV_MODE_PAL_M] = "PAL-M",
+	[VC4_VEC_TV_MODE_PAL_N] = "PAL-N",
+	[VC4_VEC_TV_MODE_PAL60] = "PAL60",
+	[VC4_VEC_TV_MODE_SECAM] = "SECAM",
+};
+
+enum vc4_vec_tv_mode_id
+vc4_vec_get_default_mode(struct drm_connector *connector)
+{
+	int i;
+
+	if (vc4_vec_tv_norm) {
+		for (i = 0; i < ARRAY_SIZE(tv_mode_names); i++)
+			if (strcmp(vc4_vec_tv_norm, tv_mode_names[i]) == 0)
+				return (enum vc4_vec_tv_mode_id) i;
+	} else if (connector->cmdline_mode.specified &&
+		   ((connector->cmdline_mode.refresh_specified &&
+		     (connector->cmdline_mode.refresh == 25 ||
+		      connector->cmdline_mode.refresh == 50)) ||
+		    (!connector->cmdline_mode.refresh_specified &&
+		     (connector->cmdline_mode.yres == 288 ||
+		      connector->cmdline_mode.yres == 576)))) {
+		/*
+		 * no explicitly specified TV norm; use PAL if a mode that
+		 * looks like PAL has been specified on the command line
+		 */
+		return VC4_VEC_TV_MODE_PAL;
+	}
+
+	/* in all other cases, default to NTSC */
+	return VC4_VEC_TV_MODE_NTSC;
+}
+
 static enum drm_connector_status
 vc4_vec_connector_detect(struct drm_connector *connector, bool force)
 {
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:416 @
 	return 1;
 }
 
+static void vc4_vec_connector_reset(struct drm_connector *connector)
+{
+	drm_atomic_helper_connector_reset(connector);
+	/* preserve TV standard */
+	if (connector->state)
+		connector->state->tv.mode = vc4_vec_get_default_mode(connector);
+}
+
+static int vc4_vec_connector_atomic_check(struct drm_connector *conn,
+					  struct drm_atomic_state *state)
+{
+	struct drm_connector_state *old_state =
+		drm_atomic_get_old_connector_state(state, conn);
+	struct drm_connector_state *new_state =
+		drm_atomic_get_new_connector_state(state, conn);
+
+	const struct vc4_vec_tv_mode *vec_mode =
+		&vc4_vec_tv_modes[new_state->tv.mode];
+
+	if (new_state->crtc) {
+		struct drm_crtc_state *crtc_state =
+			drm_atomic_get_new_crtc_state(state, new_state->crtc);
+
+		if (!drm_mode_equal(vec_mode->mode, &crtc_state->mode))
+			return -EINVAL;
+
+		if (old_state->tv.mode != new_state->tv.mode)
+			crtc_state->mode_changed = true;
+	}
+
+	return 0;
+}
+
 static const struct drm_connector_funcs vc4_vec_connector_funcs = {
 	.detect = vc4_vec_connector_detect,
 	.fill_modes = drm_helper_probe_single_connector_modes,
 	.destroy = vc4_vec_connector_destroy,
-	.reset = drm_atomic_helper_connector_reset,
+	.reset = vc4_vec_connector_reset,
 	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
 	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
 };
 
 static const struct drm_connector_helper_funcs vc4_vec_connector_helper_funcs = {
 	.get_modes = vc4_vec_connector_get_modes,
+	.atomic_check = vc4_vec_connector_atomic_check,
 };
 
 static struct drm_connector *vc4_vec_connector_init(struct drm_device *dev,
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:486 @
 
 	drm_object_attach_property(&connector->base,
 				   dev->mode_config.tv_mode_property,
-				   VC4_VEC_TV_MODE_NTSC);
-	vec->tv_mode = &vc4_vec_tv_modes[VC4_VEC_TV_MODE_NTSC];
+				   vc4_vec_get_default_mode(connector));
 
 	drm_connector_attach_encoder(connector, vec->encoder);
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:519 @
 {
 	struct vc4_vec_encoder *vc4_vec_encoder = to_vc4_vec_encoder(encoder);
 	struct vc4_vec *vec = vc4_vec_encoder->vec;
+	unsigned int tv_mode = vec->connector->state->tv.mode;
 	int ret;
 
 	ret = pm_runtime_get_sync(&vec->pdev->dev);
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:570 @
 	VEC_WRITE(VEC_CONFIG2,
 		  VEC_CONFIG2_UV_DIG_DIS | VEC_CONFIG2_RGB_DIG_DIS);
 	VEC_WRITE(VEC_CONFIG3, VEC_CONFIG3_HORIZ_LEN_STD);
-	VEC_WRITE(VEC_DAC_CONFIG,
-		  VEC_DAC_CONFIG_DAC_CTRL(0xc) |
-		  VEC_DAC_CONFIG_DRIVER_CTRL(0xc) |
-		  VEC_DAC_CONFIG_LDO_BIAS_CTRL(0x46));
+	VEC_WRITE(VEC_DAC_CONFIG, vec->variant->dac_config);
 
 	/* Mask all interrupts. */
 	VEC_WRITE(VEC_MASK0, 0);
 
-	vec->tv_mode->mode_set(vec);
+	VEC_WRITE(VEC_CONFIG0, vc4_vec_tv_modes[tv_mode].config0);
+	VEC_WRITE(VEC_CONFIG1, vc4_vec_tv_modes[tv_mode].config1);
+	if (vc4_vec_tv_modes[tv_mode].custom_freq != 0) {
+		VEC_WRITE(VEC_FREQ3_2,
+			  (vc4_vec_tv_modes[tv_mode].custom_freq >> 16) &
+			  0xffff);
+		VEC_WRITE(VEC_FREQ1_0,
+			  vc4_vec_tv_modes[tv_mode].custom_freq & 0xffff);
+	}
 
 	VEC_WRITE(VEC_DAC_MISC,
 		  VEC_DAC_MISC_VID_ACT | VEC_DAC_MISC_DAC_RST_N);
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:598 @
 	return true;
 }
 
-static void vc4_vec_encoder_atomic_mode_set(struct drm_encoder *encoder,
-					struct drm_crtc_state *crtc_state,
-					struct drm_connector_state *conn_state)
-{
-	struct vc4_vec_encoder *vc4_vec_encoder = to_vc4_vec_encoder(encoder);
-	struct vc4_vec *vec = vc4_vec_encoder->vec;
-
-	vec->tv_mode = &vc4_vec_tv_modes[conn_state->tv.mode];
-}
-
-static int vc4_vec_encoder_atomic_check(struct drm_encoder *encoder,
-					struct drm_crtc_state *crtc_state,
-					struct drm_connector_state *conn_state)
-{
-	const struct vc4_vec_tv_mode *vec_mode;
-
-	vec_mode = &vc4_vec_tv_modes[conn_state->tv.mode];
-
-	if (conn_state->crtc &&
-	    !drm_mode_equal(vec_mode->mode, &crtc_state->adjusted_mode))
-		return -EINVAL;
-
-	return 0;
-}
-
 static const struct drm_encoder_helper_funcs vc4_vec_encoder_helper_funcs = {
 	.disable = vc4_vec_encoder_disable,
 	.enable = vc4_vec_encoder_enable,
 	.mode_fixup = vc4_vec_encoder_mode_fixup,
-	.atomic_check = vc4_vec_encoder_atomic_check,
-	.atomic_mode_set = vc4_vec_encoder_atomic_mode_set,
 };
 
-static const struct of_device_id vc4_vec_dt_match[] = {
-	{ .compatible = "brcm,bcm2835-vec", .data = NULL },
-	{ /* sentinel */ },
+static const struct vc4_vec_variant bcm2835_vec_variant = {
+	.dac_config = VEC_DAC_CONFIG_DAC_CTRL(0xc) |
+		      VEC_DAC_CONFIG_DRIVER_CTRL(0xc) |
+		      VEC_DAC_CONFIG_LDO_BIAS_CTRL(0x46)
 };
 
-static const char * const tv_mode_names[] = {
-	[VC4_VEC_TV_MODE_NTSC] = "NTSC",
-	[VC4_VEC_TV_MODE_NTSC_J] = "NTSC-J",
-	[VC4_VEC_TV_MODE_PAL] = "PAL",
-	[VC4_VEC_TV_MODE_PAL_M] = "PAL-M",
+static const struct vc4_vec_variant bcm2711_vec_variant = {
+	.dac_config = VEC_DAC_CONFIG_DAC_CTRL(0x0) |
+		      VEC_DAC_CONFIG_DRIVER_CTRL(0x80) |
+		      VEC_DAC_CONFIG_LDO_BIAS_CTRL(0x61)
+};
+
+static const struct of_device_id vc4_vec_dt_match[] = {
+	{ .compatible = "brcm,bcm2835-vec", .data = &bcm2835_vec_variant },
+	{ .compatible = "brcm,bcm2711-vec", .data = &bcm2711_vec_variant },
+	{ /* sentinel */ },
 };
 
 static int vc4_vec_bind(struct device *dev, struct device *master, void *data)
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:649 @
 	vec->encoder = &vc4_vec_encoder->base.base;
 
 	vec->pdev = pdev;
+	vec->variant = (const struct vc4_vec_variant *)
+		of_device_get_match_data(dev);
 	vec->regs = vc4_ioremap_regs(pdev, 0);
 	if (IS_ERR(vec->regs))
 		return PTR_ERR(vec->regs);
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:730 @
 		.of_match_table = vc4_vec_dt_match,
 	},
 };
+
+module_param_named(tv_norm, vc4_vec_tv_norm, charp, 0600);
+MODULE_PARM_DESC(tv_norm, "Default TV norm.\n"
+		 "\t\tSupported: NTSC, NTSC-J, NTSC-443, PAL, PAL-M, PAL-N,\n"
+		 "\t\t\tPAL60, SECAM.\n"
+		 "\t\tDefault: PAL if a 50 Hz mode has been set via video=,\n"
+		 "\t\t\tNTSC otherwise");
diff -Nur --no-dereference linux-5.10.52-orig/drivers/gpu/drm/vc4/vc_image_types.h linux-5.10.52-v7l+/drivers/gpu/drm/vc4/vc_image_types.h
--- linux-5.10.52-orig/drivers/gpu/drm/vc4/vc_image_types.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/gpu/drm/vc4/vc_image_types.h	2021-07-25 16:46:01.708362267 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+
+/*
+ * Copyright (c) 2012, Broadcom Europe Ltd
+ *
+ * Values taken from vc_image_types.h released by Broadcom at
+ * https://github.com/raspberrypi/userland/blob/master/interface/vctypes/vc_image_types.h
+ * and vc_image_structs.h at
+ * https://github.com/raspberrypi/userland/blob/master/interface/vctypes/vc_image_structs.h
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+enum {
+	VC_IMAGE_MIN = 0, //bounds for error checking
+
+	VC_IMAGE_RGB565 = 1,
+	VC_IMAGE_1BPP,
+	VC_IMAGE_YUV420,
+	VC_IMAGE_48BPP,
+	VC_IMAGE_RGB888,
+	VC_IMAGE_8BPP,
+	/* 4bpp palettised image */
+	VC_IMAGE_4BPP,
+	/* A separated format of 16 colour/light shorts followed by 16 z
+	 * values
+	 */
+	VC_IMAGE_3D32,
+	/* 16 colours followed by 16 z values */
+	VC_IMAGE_3D32B,
+	/* A separated format of 16 material/colour/light shorts followed by
+	 * 16 z values
+	 */
+	VC_IMAGE_3D32MAT,
+	/* 32 bit format containing 18 bits of 6.6.6 RGB, 9 bits per short */
+	VC_IMAGE_RGB2X9,
+	/* 32-bit format holding 18 bits of 6.6.6 RGB */
+	VC_IMAGE_RGB666,
+	/* 4bpp palettised image with embedded palette */
+	VC_IMAGE_PAL4_OBSOLETE,
+	/* 8bpp palettised image with embedded palette */
+	VC_IMAGE_PAL8_OBSOLETE,
+	/* RGB888 with an alpha byte after each pixel */
+	VC_IMAGE_RGBA32,
+	/* a line of Y (32-byte padded), a line of U (16-byte padded), and a
+	 * line of V (16-byte padded)
+	 */
+	VC_IMAGE_YUV422,
+	/* RGB565 with a transparent patch */
+	VC_IMAGE_RGBA565,
+	/* Compressed (4444) version of RGBA32 */
+	VC_IMAGE_RGBA16,
+	/* VCIII codec format */
+	VC_IMAGE_YUV_UV,
+	/* VCIII T-format RGBA8888 */
+	VC_IMAGE_TF_RGBA32,
+	/* VCIII T-format RGBx8888 */
+	VC_IMAGE_TF_RGBX32,
+	/* VCIII T-format float */
+	VC_IMAGE_TF_FLOAT,
+	/* VCIII T-format RGBA4444 */
+	VC_IMAGE_TF_RGBA16,
+	/* VCIII T-format RGB5551 */
+	VC_IMAGE_TF_RGBA5551,
+	/* VCIII T-format RGB565 */
+	VC_IMAGE_TF_RGB565,
+	/* VCIII T-format 8-bit luma and 8-bit alpha */
+	VC_IMAGE_TF_YA88,
+	/* VCIII T-format 8 bit generic sample */
+	VC_IMAGE_TF_BYTE,
+	/* VCIII T-format 8-bit palette */
+	VC_IMAGE_TF_PAL8,
+	/* VCIII T-format 4-bit palette */
+	VC_IMAGE_TF_PAL4,
+	/* VCIII T-format Ericsson Texture Compressed */
+	VC_IMAGE_TF_ETC1,
+	/* RGB888 with R & B swapped */
+	VC_IMAGE_BGR888,
+	/* RGB888 with R & B swapped, but with no pitch, i.e. no padding after
+	 * each row of pixels
+	 */
+	VC_IMAGE_BGR888_NP,
+	/* Bayer image, extra defines which variant is being used */
+	VC_IMAGE_BAYER,
+	/* General wrapper for codec images e.g. JPEG from camera */
+	VC_IMAGE_CODEC,
+	/* VCIII codec format */
+	VC_IMAGE_YUV_UV32,
+	/* VCIII T-format 8-bit luma */
+	VC_IMAGE_TF_Y8,
+	/* VCIII T-format 8-bit alpha */
+	VC_IMAGE_TF_A8,
+	/* VCIII T-format 16-bit generic sample */
+	VC_IMAGE_TF_SHORT,
+	/* VCIII T-format 1bpp black/white */
+	VC_IMAGE_TF_1BPP,
+	VC_IMAGE_OPENGL,
+	/* VCIII-B0 HVS YUV 4:4:4 interleaved samples */
+	VC_IMAGE_YUV444I,
+	/* Y, U, & V planes separately (VC_IMAGE_YUV422 has them interleaved on
+	 * a per line basis)
+	 */
+	VC_IMAGE_YUV422PLANAR,
+	/* 32bpp with 8bit alpha at MS byte, with R, G, B (LS byte) */
+	VC_IMAGE_ARGB8888,
+	/* 32bpp with 8bit unused at MS byte, with R, G, B (LS byte) */
+	VC_IMAGE_XRGB8888,
+
+	/* interleaved 8 bit samples of Y, U, Y, V (4 flavours) */
+	VC_IMAGE_YUV422YUYV,
+	VC_IMAGE_YUV422YVYU,
+	VC_IMAGE_YUV422UYVY,
+	VC_IMAGE_YUV422VYUY,
+
+	/* 32bpp like RGBA32 but with unused alpha */
+	VC_IMAGE_RGBX32,
+	/* 32bpp, corresponding to RGBA with unused alpha */
+	VC_IMAGE_RGBX8888,
+	/* 32bpp, corresponding to BGRA with unused alpha */
+	VC_IMAGE_BGRX8888,
+
+	/* Y as a plane, then UV byte interleaved in plane with same pitch,
+	 * half height
+	 */
+	VC_IMAGE_YUV420SP,
+
+	/* Y, U, & V planes separately 4:4:4 */
+	VC_IMAGE_YUV444PLANAR,
+
+	/* T-format 8-bit U - same as TF_Y8 buf from U plane */
+	VC_IMAGE_TF_U8,
+	/* T-format 8-bit U - same as TF_Y8 buf from V plane */
+	VC_IMAGE_TF_V8,
+
+	/* YUV4:2:0 planar, 16bit values */
+	VC_IMAGE_YUV420_16,
+	/* YUV4:2:0 codec format, 16bit values */
+	VC_IMAGE_YUV_UV_16,
+	/* YUV4:2:0 with U,V in side-by-side format */
+	VC_IMAGE_YUV420_S,
+	/* 10-bit YUV 420 column image format */
+	VC_IMAGE_YUV10COL,
+	/* 32-bpp, 10-bit R/G/B, 2-bit Alpha */
+	VC_IMAGE_RGBA1010102,
+
+	VC_IMAGE_MAX,     /* bounds for error checking */
+	VC_IMAGE_FORCE_ENUM_16BIT = 0xffff,
+};
+
+enum {
+	/* Unknown or unset - defaults to BT601 interstitial */
+	VC_IMAGE_YUVINFO_UNSPECIFIED    = 0,
+
+	/* colour-space conversions data [4 bits] */
+
+	/* ITU-R BT.601-5 [SDTV] (compatible with VideoCore-II) */
+	VC_IMAGE_YUVINFO_CSC_ITUR_BT601      = 1,
+	/* ITU-R BT.709-3 [HDTV] */
+	VC_IMAGE_YUVINFO_CSC_ITUR_BT709      = 2,
+	/* JPEG JFIF */
+	VC_IMAGE_YUVINFO_CSC_JPEG_JFIF       = 3,
+	/* Title 47 Code of Federal Regulations (2003) 73.682 (a) (20) */
+	VC_IMAGE_YUVINFO_CSC_FCC             = 4,
+	/* Society of Motion Picture and Television Engineers 240M (1999) */
+	VC_IMAGE_YUVINFO_CSC_SMPTE_240M      = 5,
+	/* ITU-R BT.470-2 System M */
+	VC_IMAGE_YUVINFO_CSC_ITUR_BT470_2_M  = 6,
+	/* ITU-R BT.470-2 System B,G */
+	VC_IMAGE_YUVINFO_CSC_ITUR_BT470_2_BG = 7,
+	/* JPEG JFIF, but with 16..255 luma */
+	VC_IMAGE_YUVINFO_CSC_JPEG_JFIF_Y16_255 = 8,
+	/* Rec 2020 */
+	VC_IMAGE_YUVINFO_CSC_REC_2020        = 9,
+};
diff -Nur --no-dereference linux-5.10.52-orig/drivers/gpu/drm/virtio/virtgpu_display.c linux-5.10.52-v7l+/drivers/gpu/drm/virtio/virtgpu_display.c
--- linux-5.10.52-orig/drivers/gpu/drm/virtio/virtgpu_display.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/gpu/drm/virtio/virtgpu_display.c	2021-07-25 16:46:01.718362100 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:98 @
 }
 
 static void virtio_gpu_crtc_atomic_enable(struct drm_crtc *crtc,
-					  struct drm_crtc_state *old_state)
+					  struct drm_atomic_state *state)
 {
 }
 
 static void virtio_gpu_crtc_atomic_disable(struct drm_crtc *crtc,
-					   struct drm_crtc_state *old_state)
+					   struct drm_atomic_state *state)
 {
 	struct drm_device *dev = crtc->dev;
 	struct virtio_gpu_device *vgdev = dev->dev_private;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:114 @
 }
 
 static int virtio_gpu_crtc_atomic_check(struct drm_crtc *crtc,
-					struct drm_crtc_state *state)
+					struct drm_atomic_state *state)
 {
 	return 0;
 }
 
 static void virtio_gpu_crtc_atomic_flush(struct drm_crtc *crtc,
-					 struct drm_crtc_state *old_state)
+					 struct drm_atomic_state *state)
 {
 	struct virtio_gpu_output *output = drm_crtc_to_virtio_gpu_output(crtc);
 
diff -Nur --no-dereference linux-5.10.52-orig/drivers/gpu/drm/vkms/vkms_crtc.c linux-5.10.52-v7l+/drivers/gpu/drm/vkms/vkms_crtc.c
--- linux-5.10.52-orig/drivers/gpu/drm/vkms/vkms_crtc.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/gpu/drm/vkms/vkms_crtc.c	2021-07-25 16:46:01.718362100 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:172 @
 };
 
 static int vkms_crtc_atomic_check(struct drm_crtc *crtc,
-				  struct drm_crtc_state *state)
+				  struct drm_atomic_state *state)
 {
-	struct vkms_crtc_state *vkms_state = to_vkms_crtc_state(state);
+	struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state,
+									  crtc);
+	struct vkms_crtc_state *vkms_state = to_vkms_crtc_state(crtc_state);
 	struct drm_plane *plane;
 	struct drm_plane_state *plane_state;
 	int i = 0, ret;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:184 @
 	if (vkms_state->active_planes)
 		return 0;
 
-	ret = drm_atomic_add_affected_planes(state->state, crtc);
+	ret = drm_atomic_add_affected_planes(crtc_state->state, crtc);
 	if (ret < 0)
 		return ret;
 
-	drm_for_each_plane_mask(plane, crtc->dev, state->plane_mask) {
-		plane_state = drm_atomic_get_existing_plane_state(state->state,
+	drm_for_each_plane_mask(plane, crtc->dev, crtc_state->plane_mask) {
+		plane_state = drm_atomic_get_existing_plane_state(crtc_state->state,
 								  plane);
 		WARN_ON(!plane_state);
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:205 @
 	vkms_state->num_active_planes = i;
 
 	i = 0;
-	drm_for_each_plane_mask(plane, crtc->dev, state->plane_mask) {
-		plane_state = drm_atomic_get_existing_plane_state(state->state,
+	drm_for_each_plane_mask(plane, crtc->dev, crtc_state->plane_mask) {
+		plane_state = drm_atomic_get_existing_plane_state(crtc_state->state,
 								  plane);
 
 		if (!plane_state->visible)
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:220 @
 }
 
 static void vkms_crtc_atomic_enable(struct drm_crtc *crtc,
-				    struct drm_crtc_state *old_state)
+				    struct drm_atomic_state *state)
 {
 	drm_crtc_vblank_on(crtc);
 }
 
 static void vkms_crtc_atomic_disable(struct drm_crtc *crtc,
-				     struct drm_crtc_state *old_state)
+				     struct drm_atomic_state *state)
 {
 	drm_crtc_vblank_off(crtc);
 }
 
 static void vkms_crtc_atomic_begin(struct drm_crtc *crtc,
-				   struct drm_crtc_state *old_crtc_state)
+				   struct drm_atomic_state *state)
 {
 	struct vkms_output *vkms_output = drm_crtc_to_vkms_output(crtc);
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:243 @
 }
 
 static void vkms_crtc_atomic_flush(struct drm_crtc *crtc,
-				   struct drm_crtc_state *old_crtc_state)
+				   struct drm_atomic_state *state)
 {
 	struct vkms_output *vkms_output = drm_crtc_to_vkms_output(crtc);
 
diff -Nur --no-dereference linux-5.10.52-orig/drivers/gpu/drm/vkms/vkms_writeback.c linux-5.10.52-v7l+/drivers/gpu/drm/vkms/vkms_writeback.c
--- linux-5.10.52-orig/drivers/gpu/drm/vkms/vkms_writeback.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/gpu/drm/vkms/vkms_writeback.c	2021-07-25 16:46:01.718362100 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
 // SPDX-License-Identifier: GPL-2.0+
 
 #include "vkms_drv.h"
+
+#include <drm/drm_atomic.h>
 #include <drm/drm_fourcc.h>
 #include <drm/drm_writeback.h>
 #include <drm/drm_probe_helper.h>
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:105 @
 }
 
 static void vkms_wb_atomic_commit(struct drm_connector *conn,
-				  struct drm_connector_state *state)
+				  struct drm_atomic_state *state)
 {
+	struct drm_connector_state *connector_state = drm_atomic_get_new_connector_state(state,
+											 conn);
 	struct vkms_device *vkmsdev = drm_device_to_vkms_device(conn->dev);
 	struct vkms_output *output = &vkmsdev->output;
 	struct drm_writeback_connector *wb_conn = &output->wb_connector;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:124 @
 	crtc_state->active_writeback = conn_state->writeback_job->priv;
 	crtc_state->wb_pending = true;
 	spin_unlock_irq(&output->composer_lock);
-	drm_writeback_queue_job(wb_conn, state);
+	drm_writeback_queue_job(wb_conn, connector_state);
 }
 
 static const struct drm_connector_helper_funcs vkms_wb_conn_helper_funcs = {
diff -Nur --no-dereference linux-5.10.52-orig/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c linux-5.10.52-v7l+/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
--- linux-5.10.52-orig/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c	2021-07-25 16:46:01.748361597 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:525 @
 
 
 int vmw_du_crtc_atomic_check(struct drm_crtc *crtc,
-			     struct drm_crtc_state *new_state)
+			     struct drm_atomic_state *state)
 {
+	struct drm_crtc_state *new_state = drm_atomic_get_new_crtc_state(state,
+									 crtc);
 	struct vmw_display_unit *du = vmw_crtc_to_du(new_state->crtc);
 	int connector_mask = drm_connector_mask(&du->connector);
 	bool has_primary = new_state->plane_mask &
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:557 @
 
 
 void vmw_du_crtc_atomic_begin(struct drm_crtc *crtc,
-			      struct drm_crtc_state *old_crtc_state)
+			      struct drm_atomic_state *state)
 {
 }
 
 
 void vmw_du_crtc_atomic_flush(struct drm_crtc *crtc,
-			      struct drm_crtc_state *old_crtc_state)
+			      struct drm_atomic_state *state)
 {
 	struct drm_pending_vblank_event *event = crtc->state->event;
 
diff -Nur --no-dereference linux-5.10.52-orig/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h linux-5.10.52-v7l+/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h
--- linux-5.10.52-orig/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h	2021-07-25 16:46:01.748361597 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:476 @
 			     bool unreference);
 
 int vmw_du_crtc_atomic_check(struct drm_crtc *crtc,
-			     struct drm_crtc_state *state);
+			     struct drm_atomic_state *state);
 void vmw_du_crtc_atomic_begin(struct drm_crtc *crtc,
-			      struct drm_crtc_state *old_crtc_state);
+			      struct drm_atomic_state *state);
 void vmw_du_crtc_atomic_flush(struct drm_crtc *crtc,
-			      struct drm_crtc_state *old_crtc_state);
+			      struct drm_atomic_state *state);
 void vmw_du_crtc_reset(struct drm_crtc *crtc);
 struct drm_crtc_state *vmw_du_crtc_duplicate_state(struct drm_crtc *crtc);
 void vmw_du_crtc_destroy_state(struct drm_crtc *crtc,
diff -Nur --no-dereference linux-5.10.52-orig/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c linux-5.10.52-v7l+/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c
--- linux-5.10.52-orig/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c	2021-07-25 16:46:01.748361597 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:217 @
  * CRTC, it makes more sense to do those at plane update time.
  */
 static void vmw_ldu_crtc_atomic_enable(struct drm_crtc *crtc,
-				       struct drm_crtc_state *old_state)
+				       struct drm_atomic_state *state)
 {
 }
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:227 @
  * @crtc: CRTC to be turned off
  */
 static void vmw_ldu_crtc_atomic_disable(struct drm_crtc *crtc,
-					struct drm_crtc_state *old_state)
+					struct drm_atomic_state *state)
 {
 }
 
diff -Nur --no-dereference linux-5.10.52-orig/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c linux-5.10.52-v7l+/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c
--- linux-5.10.52-orig/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c	2021-07-25 16:46:01.748361597 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:282 @
  * This is called after a mode set has been completed.
  */
 static void vmw_sou_crtc_atomic_enable(struct drm_crtc *crtc,
-				       struct drm_crtc_state *old_state)
+				       struct drm_atomic_state *state)
 {
 }
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:292 @
  * @crtc: CRTC to be turned off
  */
 static void vmw_sou_crtc_atomic_disable(struct drm_crtc *crtc,
-					struct drm_crtc_state *old_state)
+					struct drm_atomic_state *state)
 {
 	struct vmw_private *dev_priv;
 	struct vmw_screen_object_unit *sou;
diff -Nur --no-dereference linux-5.10.52-orig/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c linux-5.10.52-v7l+/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c
--- linux-5.10.52-orig/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c	2021-07-25 16:46:01.758361429 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:411 @
 }
 
 static void vmw_stdu_crtc_atomic_enable(struct drm_crtc *crtc,
-					struct drm_crtc_state *old_state)
+					struct drm_atomic_state *state)
 {
 }
 
 static void vmw_stdu_crtc_atomic_disable(struct drm_crtc *crtc,
-					 struct drm_crtc_state *old_state)
+					 struct drm_atomic_state *state)
 {
 	struct vmw_private *dev_priv;
 	struct vmw_screen_target_display_unit *stdu;
diff -Nur --no-dereference linux-5.10.52-orig/drivers/gpu/drm/xlnx/zynqmp_disp.c linux-5.10.52-v7l+/drivers/gpu/drm/xlnx/zynqmp_disp.c
--- linux-5.10.52-orig/drivers/gpu/drm/xlnx/zynqmp_disp.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/gpu/drm/xlnx/zynqmp_disp.c	2021-07-25 16:46:01.758361429 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1444 @
 
 static void
 zynqmp_disp_crtc_atomic_enable(struct drm_crtc *crtc,
-			       struct drm_crtc_state *old_crtc_state)
+			       struct drm_atomic_state *state)
 {
 	struct zynqmp_disp *disp = crtc_to_disp(crtc);
 	struct drm_display_mode *adjusted_mode = &crtc->state->adjusted_mode;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1475 @
 
 static void
 zynqmp_disp_crtc_atomic_disable(struct drm_crtc *crtc,
-				struct drm_crtc_state *old_crtc_state)
+				struct drm_atomic_state *state)
 {
+	struct drm_crtc_state *old_crtc_state = drm_atomic_get_old_crtc_state(state,
+									      crtc);
 	struct zynqmp_disp *disp = crtc_to_disp(crtc);
 	struct drm_plane_state *old_plane_state;
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1508 @
 }
 
 static int zynqmp_disp_crtc_atomic_check(struct drm_crtc *crtc,
-					 struct drm_crtc_state *state)
+					 struct drm_atomic_state *state)
 {
-	return drm_atomic_add_affected_planes(state->state, crtc);
+	return drm_atomic_add_affected_planes(state, crtc);
 }
 
 static void
 zynqmp_disp_crtc_atomic_begin(struct drm_crtc *crtc,
-			      struct drm_crtc_state *old_crtc_state)
+			      struct drm_atomic_state *state)
 {
 	drm_crtc_vblank_on(crtc);
 }
 
 static void
 zynqmp_disp_crtc_atomic_flush(struct drm_crtc *crtc,
-			      struct drm_crtc_state *old_crtc_state)
+			      struct drm_atomic_state *state)
 {
 	if (crtc->state->event) {
 		struct drm_pending_vblank_event *event;
diff -Nur --no-dereference linux-5.10.52-orig/drivers/gpu/drm/zte/zx_vou.c linux-5.10.52-v7l+/drivers/gpu/drm/zte/zx_vou.c
--- linux-5.10.52-orig/drivers/gpu/drm/zte/zx_vou.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/gpu/drm/zte/zx_vou.c	2021-07-25 16:46:01.768361261 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:353 @
 }
 
 static void zx_crtc_atomic_enable(struct drm_crtc *crtc,
-				  struct drm_crtc_state *old_state)
+				  struct drm_atomic_state *state)
 {
 	struct drm_display_mode *mode = &crtc->state->adjusted_mode;
 	bool interlaced = mode->flags & DRM_MODE_FLAG_INTERLACE;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:458 @
 }
 
 static void zx_crtc_atomic_disable(struct drm_crtc *crtc,
-				   struct drm_crtc_state *old_state)
+				   struct drm_atomic_state *state)
 {
 	struct zx_crtc *zcrtc = to_zx_crtc(crtc);
 	const struct zx_crtc_bits *bits = zcrtc->bits;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:476 @
 }
 
 static void zx_crtc_atomic_flush(struct drm_crtc *crtc,
-				  struct drm_crtc_state *old_state)
+				  struct drm_atomic_state *state)
 {
 	struct drm_pending_vblank_event *event = crtc->state->event;
 
diff -Nur --no-dereference linux-5.10.52-orig/drivers/hid/hid-ids.h linux-5.10.52-v7l+/drivers/hid/hid-ids.h
--- linux-5.10.52-orig/drivers/hid/hid-ids.h	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/hid/hid-ids.h	2021-07-25 16:46:01.808360591 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:224 @
 #define USB_VENDOR_ID_BAANTO		0x2453
 #define USB_DEVICE_ID_BAANTO_MT_190W2	0x0100
 
+#define USB_VENDOR_ID_BEKEN		0x25a7
+#define USB_DEVICE_ID_AIRMOUSE_T3	0x2402
+
 #define USB_VENDOR_ID_BELKIN		0x050d
 #define USB_DEVICE_ID_FLIP_KVM		0x3201
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1274 @
 #define USB_VENDOR_ID_XAT	0x2505
 #define USB_DEVICE_ID_XAT_CSR	0x0220
 
+#define USB_VENDOR_ID_XENTA			0x1d57
+#define USB_DEVICE_ID_AIRMOUSE_MX3		0xad03
+
 #define USB_VENDOR_ID_XIN_MO			0x16c0
 #define USB_DEVICE_ID_XIN_MO_DUAL_ARCADE	0x05e1
 #define USB_DEVICE_ID_THT_2P_ARCADE		0x75e1
diff -Nur --no-dereference linux-5.10.52-orig/drivers/hid/hid-quirks.c linux-5.10.52-v7l+/drivers/hid/hid-quirks.c
--- linux-5.10.52-orig/drivers/hid/hid-quirks.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/hid/hid-quirks.c	2021-07-25 16:46:01.818360423 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:44 @
 	{ HID_USB_DEVICE(USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_CS682), HID_QUIRK_NOGET },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_CS692), HID_QUIRK_NOGET },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_UC100KM), HID_QUIRK_NOGET },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_BEKEN, USB_DEVICE_ID_AIRMOUSE_T3), HID_QUIRK_INCREMENT_USAGE_ON_DUPLICATE },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_MULTI_TOUCH), HID_QUIRK_MULTI_INPUT },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_PIXART_USB_OPTICAL_MOUSE), HID_QUIRK_ALWAYS_POLL },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_PIXART_USB_OPTICAL_MOUSE2), HID_QUIRK_ALWAYS_POLL },
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:197 @
 	{ HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_QUAD_USB_JOYPAD), HID_QUIRK_NOGET | HID_QUIRK_MULTI_INPUT },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_XIN_MO, USB_DEVICE_ID_XIN_MO_DUAL_ARCADE), HID_QUIRK_MULTI_INPUT },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_GROUP_AUDIO), HID_QUIRK_NOGET },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_XENTA, USB_DEVICE_ID_AIRMOUSE_MX3), HID_QUIRK_INCREMENT_USAGE_ON_DUPLICATE },
 
 	{ 0 }
 };
diff -Nur --no-dereference linux-5.10.52-orig/drivers/hid/usbhid/hid-core.c linux-5.10.52-v7l+/drivers/hid/usbhid/hid-core.c
--- linux-5.10.52-orig/drivers/hid/usbhid/hid-core.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/hid/usbhid/hid-core.c	2021-07-25 16:46:01.848359920 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:48 @
  * Module parameters.
  */
 
-static unsigned int hid_mousepoll_interval;
+static unsigned int hid_mousepoll_interval = ~0;
 module_param_named(mousepoll, hid_mousepoll_interval, uint, 0644);
 MODULE_PARM_DESC(mousepoll, "Polling interval of mice");
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1117 @
 		 */
 		switch (hid->collection->usage) {
 		case HID_GD_MOUSE:
-			if (hid_mousepoll_interval > 0)
+			if (hid_mousepoll_interval == ~0 && interval < 16)
+				interval = 16;
+			else if (hid_mousepoll_interval != ~0 && hid_mousepoll_interval != 0)
 				interval = hid_mousepoll_interval;
 			break;
 		case HID_GD_JOYSTICK:
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1131 @
 				interval = hid_kbpoll_interval;
 			break;
 		}
+		usb_fixup_endpoint(dev, endpoint->bEndpointAddress, interval);
 
 		ret = -ENOMEM;
 		if (usb_endpoint_dir_in(endpoint)) {
diff -Nur --no-dereference linux-5.10.52-orig/drivers/hwmon/Kconfig linux-5.10.52-v7l+/drivers/hwmon/Kconfig
--- linux-5.10.52-orig/drivers/hwmon/Kconfig	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/hwmon/Kconfig	2021-07-25 16:46:01.868359585 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1492 @
 	  This driver can also be built as a module. If so, the module
 	  will be called raspberrypi-hwmon.
 
+config SENSORS_RPI_POE_FAN
+	tristate "Raspberry Pi PoE HAT fan"
+	depends on RASPBERRYPI_FIRMWARE
+	depends on THERMAL || THERMAL=n
+	help
+	  If you say yes here you get support for Raspberry Pi PoE (Power over
+	  Ethernet) HAT fan.
+
+	  This driver can also be built as a module.  If so, the module
+	  will be called rpi-poe-fan.
+
 config SENSORS_SL28CPLD
 	tristate "Kontron sl28cpld hardware monitoring driver"
 	depends on MFD_SL28CPLD || COMPILE_TEST
diff -Nur --no-dereference linux-5.10.52-orig/drivers/hwmon/Makefile linux-5.10.52-v7l+/drivers/hwmon/Makefile
--- linux-5.10.52-orig/drivers/hwmon/Makefile	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/hwmon/Makefile	2021-07-25 16:46:01.868359585 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:160 @
 obj-$(CONFIG_SENSORS_POWR1220)  += powr1220.o
 obj-$(CONFIG_SENSORS_PWM_FAN)	+= pwm-fan.o
 obj-$(CONFIG_SENSORS_RASPBERRYPI_HWMON)	+= raspberrypi-hwmon.o
+obj-$(CONFIG_SENSORS_RPI_POE_FAN)	+= rpi-poe-fan.o
 obj-$(CONFIG_SENSORS_S3C)	+= s3c-hwmon.o
 obj-$(CONFIG_SENSORS_SCH56XX_COMMON)+= sch56xx-common.o
 obj-$(CONFIG_SENSORS_SCH5627)	+= sch5627.o
diff -Nur --no-dereference linux-5.10.52-orig/drivers/hwmon/raspberrypi-hwmon.c linux-5.10.52-v7l+/drivers/hwmon/raspberrypi-hwmon.c
--- linux-5.10.52-orig/drivers/hwmon/raspberrypi-hwmon.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/hwmon/raspberrypi-hwmon.c	2021-07-25 16:46:02.028356902 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:18 @
 #include <linux/workqueue.h>
 #include <soc/bcm2835/raspberrypi-firmware.h>
 
+/*
+ * This section defines some rate limited logging that prevent
+ * repeated messages at much lower Hz than the default kernel settings.
+ * It's usually 5s, this is 5 minutes.
+ * Burst 3 means you may get three messages 'quickly', before
+ * the ratelimiting kicks in.
+ */
+#define LOCAL_RATELIMIT_INTERVAL (5 * 60 * HZ)
+#define LOCAL_RATELIMIT_BURST 3
+
+#ifdef CONFIG_PRINTK
+#define printk_ratelimited_local(fmt, ...)	\
+({						\
+	static DEFINE_RATELIMIT_STATE(_rs,	\
+		LOCAL_RATELIMIT_INTERVAL,	\
+		LOCAL_RATELIMIT_BURST);		\
+						\
+	if (__ratelimit(&_rs))			\
+		printk(fmt, ##__VA_ARGS__);	\
+})
+#else
+#define printk_ratelimited_local(fmt, ...)	\
+	no_printk(fmt, ##__VA_ARGS__)
+#endif
+
+#define pr_crit_ratelimited_local(fmt, ...)              \
+	printk_ratelimited_local(KERN_CRIT pr_fmt(fmt), ##__VA_ARGS__)
+#define pr_info_ratelimited_local(fmt, ...)              \
+printk_ratelimited_local(KERN_INFO pr_fmt(fmt), ##__VA_ARGS__)
+
 #define UNDERVOLTAGE_STICKY_BIT	BIT(16)
 
 struct rpi_hwmon_data {
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:80 @
 	if (new_uv == old_uv)
 		return;
 
-	if (new_uv)
-		dev_crit(data->hwmon_dev, "Undervoltage detected!\n");
-	else
-		dev_info(data->hwmon_dev, "Voltage normalised\n");
+	if (new_uv) {
+		pr_crit_ratelimited_local("Under-voltage detected! (0x%08x)\n",
+					  value);
+	} else {
+		pr_info_ratelimited_local("Voltage normalised (0x%08x)\n",
+					  value);
+	}
 
 	sysfs_notify(&data->hwmon_dev->kobj, NULL, "in0_lcrit_alarm");
 }
diff -Nur --no-dereference linux-5.10.52-orig/drivers/hwmon/rpi-poe-fan.c linux-5.10.52-v7l+/drivers/hwmon/rpi-poe-fan.c
--- linux-5.10.52-orig/drivers/hwmon/rpi-poe-fan.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/hwmon/rpi-poe-fan.c	2021-07-25 16:46:02.028356902 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * rpi-poe-fan.c - Hwmon driver for Raspberry Pi PoE HAT fan.
+ *
+ * Copyright (C) 2018 Raspberry Pi (Trading) Ltd.
+ * Based on pwm-fan.c by Kamil Debski <k.debski@samsung.com>
+ *
+ * Author: Serge Schneider <serge@raspberrypi.org>
+ */
+
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/notifier.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/reboot.h>
+#include <linux/sysfs.h>
+#include <linux/thermal.h>
+#include <soc/bcm2835/raspberrypi-firmware.h>
+
+#define MAX_PWM 255
+
+#define POE_CUR_PWM 0x0
+#define POE_DEF_PWM 0x1
+
+struct rpi_poe_fan_ctx {
+	struct mutex lock;
+	struct rpi_firmware *fw;
+	u32 set_tag;
+	unsigned int pwm_value;
+	unsigned int def_pwm_value;
+	unsigned int rpi_poe_fan_state;
+	unsigned int rpi_poe_fan_max_state;
+	unsigned int *rpi_poe_fan_cooling_levels;
+	struct thermal_cooling_device *cdev;
+	struct notifier_block nb;
+};
+
+struct fw_tag_data_s{
+	u32 reg;
+	u32 val;
+	u32 ret;
+};
+
+static int write_reg(struct rpi_firmware *fw, u32 reg, u32 *val, u32 set_tag)
+{
+	struct fw_tag_data_s fw_tag_data = {
+		.reg = reg,
+		.val = *val
+	};
+	int ret;
+
+	ret = rpi_firmware_property(fw, set_tag,
+				    &fw_tag_data, sizeof(fw_tag_data));
+	if (ret) {
+		return ret;
+	} else if (fw_tag_data.ret) {
+		return -EIO;
+	}
+	return 0;
+}
+
+static int read_reg(struct rpi_firmware *fw, u32 reg, u32 *val){
+	struct fw_tag_data_s fw_tag_data = {
+		.reg = reg,
+	};
+	int ret;
+	ret = rpi_firmware_property(fw, RPI_FIRMWARE_GET_POE_HAT_VAL,
+				    &fw_tag_data, sizeof(fw_tag_data));
+	if (ret) {
+		return ret;
+	} else if (fw_tag_data.ret) {
+		return -EIO;
+	}
+	*val = fw_tag_data.val;
+	return 0;
+}
+
+static int rpi_poe_reboot(struct notifier_block *nb, unsigned long code,
+			  void *unused)
+{
+	struct rpi_poe_fan_ctx *ctx = container_of(nb, struct rpi_poe_fan_ctx,
+						   nb);
+
+	if (ctx->pwm_value != ctx->def_pwm_value)
+		write_reg(ctx->fw, POE_CUR_PWM, &ctx->def_pwm_value, ctx->set_tag);
+
+	return NOTIFY_DONE;
+}
+
+static int  __set_pwm(struct rpi_poe_fan_ctx *ctx, u32 pwm)
+{
+	int ret = 0;
+
+	mutex_lock(&ctx->lock);
+	if (ctx->pwm_value == pwm)
+		goto exit_set_pwm_err;
+
+	ret = write_reg(ctx->fw, POE_CUR_PWM, &pwm, ctx->set_tag);
+	if (!ret)
+		ctx->pwm_value = pwm;
+exit_set_pwm_err:
+	mutex_unlock(&ctx->lock);
+	return ret;
+}
+
+static int  __set_def_pwm(struct rpi_poe_fan_ctx *ctx, u32 def_pwm)
+{
+	int ret = 0;
+	mutex_lock(&ctx->lock);
+	if (ctx->def_pwm_value == def_pwm)
+		goto exit_set_def_pwm_err;
+
+	ret = write_reg(ctx->fw, POE_DEF_PWM, &def_pwm, ctx->set_tag);
+	if (!ret)
+		ctx->def_pwm_value = def_pwm;
+exit_set_def_pwm_err:
+	mutex_unlock(&ctx->lock);
+	return ret;
+}
+
+static void rpi_poe_fan_update_state(struct rpi_poe_fan_ctx *ctx,
+				     unsigned long pwm)
+{
+	int i;
+
+	for (i = 0; i < ctx->rpi_poe_fan_max_state; ++i)
+		if (pwm < ctx->rpi_poe_fan_cooling_levels[i + 1])
+			break;
+
+	ctx->rpi_poe_fan_state = i;
+}
+
+static ssize_t set_pwm(struct device *dev, struct device_attribute *attr,
+		       const char *buf, size_t count)
+{
+	struct rpi_poe_fan_ctx *ctx = dev_get_drvdata(dev);
+	unsigned long pwm;
+	int ret;
+
+	if (kstrtoul(buf, 10, &pwm) || pwm > MAX_PWM)
+		return -EINVAL;
+
+	ret = __set_pwm(ctx, pwm);
+	if (ret)
+		return ret;
+
+	rpi_poe_fan_update_state(ctx, pwm);
+	return count;
+}
+
+static ssize_t set_def_pwm(struct device *dev, struct device_attribute *attr,
+			   const char *buf, size_t count)
+{
+	struct rpi_poe_fan_ctx *ctx = dev_get_drvdata(dev);
+	unsigned long def_pwm;
+	int ret;
+
+	if (kstrtoul(buf, 10, &def_pwm) || def_pwm > MAX_PWM)
+		return -EINVAL;
+
+	ret = __set_def_pwm(ctx, def_pwm);
+	if (ret)
+		return ret;
+	return count;
+}
+
+static ssize_t show_pwm(struct device *dev,
+			struct device_attribute *attr, char *buf)
+{
+	struct rpi_poe_fan_ctx *ctx = dev_get_drvdata(dev);
+
+	return sprintf(buf, "%u\n", ctx->pwm_value);
+}
+
+static ssize_t show_def_pwm(struct device *dev,
+			struct device_attribute *attr, char *buf)
+{
+	struct rpi_poe_fan_ctx *ctx = dev_get_drvdata(dev);
+
+	return sprintf(buf, "%u\n", ctx->def_pwm_value);
+}
+
+
+static SENSOR_DEVICE_ATTR(pwm1, 0644, show_pwm, set_pwm, 0);
+static SENSOR_DEVICE_ATTR(def_pwm1, 0644, show_def_pwm, set_def_pwm, 1);
+
+static struct attribute *rpi_poe_fan_attrs[] = {
+	&sensor_dev_attr_pwm1.dev_attr.attr,
+	&sensor_dev_attr_def_pwm1.dev_attr.attr,
+	NULL,
+};
+
+ATTRIBUTE_GROUPS(rpi_poe_fan);
+
+/* thermal cooling device callbacks */
+static int rpi_poe_fan_get_max_state(struct thermal_cooling_device *cdev,
+				     unsigned long *state)
+{
+	struct rpi_poe_fan_ctx *ctx = cdev->devdata;
+
+	if (!ctx)
+		return -EINVAL;
+
+	*state = ctx->rpi_poe_fan_max_state;
+
+	return 0;
+}
+
+static int rpi_poe_fan_get_cur_state(struct thermal_cooling_device *cdev,
+				     unsigned long *state)
+{
+	struct rpi_poe_fan_ctx *ctx = cdev->devdata;
+
+	if (!ctx)
+		return -EINVAL;
+
+	*state = ctx->rpi_poe_fan_state;
+
+	return 0;
+}
+
+static int rpi_poe_fan_set_cur_state(struct thermal_cooling_device *cdev,
+				     unsigned long state)
+{
+	struct rpi_poe_fan_ctx *ctx = cdev->devdata;
+	int ret;
+
+	if (!ctx || (state > ctx->rpi_poe_fan_max_state))
+		return -EINVAL;
+
+	if (state == ctx->rpi_poe_fan_state)
+		return 0;
+
+	ret = __set_pwm(ctx, ctx->rpi_poe_fan_cooling_levels[state]);
+	if (ret) {
+		dev_err(&cdev->device, "Cannot set pwm!\n");
+		return ret;
+	}
+
+	ctx->rpi_poe_fan_state = state;
+
+	return ret;
+}
+
+static const struct thermal_cooling_device_ops rpi_poe_fan_cooling_ops = {
+	.get_max_state = rpi_poe_fan_get_max_state,
+	.get_cur_state = rpi_poe_fan_get_cur_state,
+	.set_cur_state = rpi_poe_fan_set_cur_state,
+};
+
+static int rpi_poe_fan_of_get_cooling_data(struct device *dev,
+				       struct rpi_poe_fan_ctx *ctx)
+{
+	struct device_node *np = dev->of_node;
+	int num, i, ret;
+
+	if (!of_find_property(np, "cooling-levels", NULL))
+		return 0;
+
+	ret = of_property_count_u32_elems(np, "cooling-levels");
+	if (ret <= 0) {
+		dev_err(dev, "cooling-levels property missing or invalid: %d\n",
+			ret);
+		return ret ? : -EINVAL;
+	}
+
+	num = ret;
+	ctx->rpi_poe_fan_cooling_levels = devm_kzalloc(dev, num * sizeof(u32),
+						   GFP_KERNEL);
+	if (!ctx->rpi_poe_fan_cooling_levels)
+		return -ENOMEM;
+
+	ret = of_property_read_u32_array(np, "cooling-levels",
+					 ctx->rpi_poe_fan_cooling_levels, num);
+	if (ret) {
+		dev_err(dev, "Property 'cooling-levels' cannot be read!\n");
+		return ret;
+	}
+
+	for (i = 0; i < num; i++) {
+		if (ctx->rpi_poe_fan_cooling_levels[i] > MAX_PWM) {
+			dev_err(dev, "PWM fan state[%d]:%d > %d\n", i,
+				ctx->rpi_poe_fan_cooling_levels[i], MAX_PWM);
+			return -EINVAL;
+		}
+	}
+
+	ctx->rpi_poe_fan_max_state = num - 1;
+
+	return 0;
+}
+
+static int rpi_poe_fan_probe(struct platform_device *pdev)
+{
+	struct thermal_cooling_device *cdev;
+	struct rpi_poe_fan_ctx *ctx;
+	struct device *hwmon;
+	struct device_node *np = pdev->dev.of_node;
+	struct device_node *fw_node;
+	u32 revision;
+	int ret;
+
+	fw_node = of_parse_phandle(np, "firmware", 0);
+	if (!fw_node) {
+		dev_err(&pdev->dev, "Missing firmware node\n");
+		return -ENOENT;
+	}
+
+	ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
+	if (!ctx)
+		return -ENOMEM;
+
+	mutex_init(&ctx->lock);
+
+	ctx->fw = rpi_firmware_get(fw_node);
+	if (!ctx->fw)
+		return -EPROBE_DEFER;
+	ret = rpi_firmware_property(ctx->fw,
+		RPI_FIRMWARE_GET_FIRMWARE_REVISION,
+		&revision, sizeof(revision));
+	if (ret) {
+		dev_err(&pdev->dev, "Failed to get firmware revision: %i\n", ret);
+		return ret;
+	}
+	if (revision < 0x60af72e8)
+		ctx->set_tag = RPI_FIRMWARE_SET_POE_HAT_VAL_OLD;
+	else
+		ctx->set_tag = RPI_FIRMWARE_SET_POE_HAT_VAL;
+
+	platform_set_drvdata(pdev, ctx);
+
+	ctx->nb.notifier_call = rpi_poe_reboot;
+	ret = register_reboot_notifier(&ctx->nb);
+	if (ret) {
+		dev_err(&pdev->dev, "Failed to register reboot notifier: %i\n",
+			ret);
+		return ret;
+	}
+	ret = read_reg(ctx->fw, POE_DEF_PWM, &ctx->def_pwm_value);
+	if (ret) {
+		dev_err(&pdev->dev, "Failed to get default PWM value: %i\n",
+			ret);
+		goto err;
+	}
+	ret = read_reg(ctx->fw, POE_CUR_PWM, &ctx->pwm_value);
+	if (ret) {
+		dev_err(&pdev->dev, "Failed to get current PWM value: %i\n",
+			ret);
+		goto err;
+	}
+
+	hwmon = devm_hwmon_device_register_with_groups(&pdev->dev, "rpipoefan",
+						       ctx, rpi_poe_fan_groups);
+	if (IS_ERR(hwmon)) {
+		dev_err(&pdev->dev, "Failed to register hwmon device\n");
+		ret = PTR_ERR(hwmon);
+		goto err;
+	}
+
+	ret = rpi_poe_fan_of_get_cooling_data(&pdev->dev, ctx);
+	if (ret)
+		return ret;
+
+	rpi_poe_fan_update_state(ctx, ctx->pwm_value);
+	if (!IS_ENABLED(CONFIG_THERMAL))
+		return 0;
+
+	cdev = thermal_of_cooling_device_register(np,
+						  "rpi-poe-fan", ctx,
+						  &rpi_poe_fan_cooling_ops);
+	if (IS_ERR(cdev)) {
+		dev_err(&pdev->dev,
+			"Failed to register rpi-poe-fan as cooling device");
+		ret = PTR_ERR(cdev);
+		goto err;
+	}
+	ctx->cdev = cdev;
+	thermal_cdev_update(cdev);
+
+	return 0;
+err:
+	unregister_reboot_notifier(&ctx->nb);
+	return ret;
+}
+
+static int rpi_poe_fan_remove(struct platform_device *pdev)
+{
+	struct rpi_poe_fan_ctx *ctx = platform_get_drvdata(pdev);
+	u32 value = ctx->def_pwm_value;
+
+	unregister_reboot_notifier(&ctx->nb);
+	thermal_cooling_device_unregister(ctx->cdev);
+	if (ctx->pwm_value != value)
+		write_reg(ctx->fw, POE_CUR_PWM, &value, ctx->set_tag);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int rpi_poe_fan_suspend(struct device *dev)
+{
+	struct rpi_poe_fan_ctx *ctx = dev_get_drvdata(dev);
+	u32 value = 0;
+	int ret = 0;
+
+	if (ctx->pwm_value != value)
+		ret = write_reg(ctx->fw, POE_CUR_PWM, &value, ctx->set_tag);
+	return ret;
+}
+
+static int rpi_poe_fan_resume(struct device *dev)
+{
+	struct rpi_poe_fan_ctx *ctx = dev_get_drvdata(dev);
+	u32 value = ctx->pwm_value;
+	int ret = 0;
+
+	if (value != 0)
+		ret = write_reg(ctx->fw, POE_CUR_PWM, &value, ctx->set_tag);
+
+	return ret;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(rpi_poe_fan_pm, rpi_poe_fan_suspend,
+			 rpi_poe_fan_resume);
+
+static const struct of_device_id of_rpi_poe_fan_match[] = {
+	{ .compatible = "raspberrypi,rpi-poe-fan", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, of_rpi_poe_fan_match);
+
+static struct platform_driver rpi_poe_fan_driver = {
+	.probe		= rpi_poe_fan_probe,
+	.remove		= rpi_poe_fan_remove,
+	.driver	= {
+		.name		= "rpi-poe-fan",
+		.pm		= &rpi_poe_fan_pm,
+		.of_match_table	= of_rpi_poe_fan_match,
+	},
+};
+
+module_platform_driver(rpi_poe_fan_driver);
+
+MODULE_AUTHOR("Serge Schneider <serge@raspberrypi.org>");
+MODULE_ALIAS("platform:rpi-poe-fan");
+MODULE_DESCRIPTION("Raspberry Pi PoE HAT fan driver");
+MODULE_LICENSE("GPL");
diff -Nur --no-dereference linux-5.10.52-orig/drivers/i2c/busses/i2c-bcm2708.c linux-5.10.52-v7l+/drivers/i2c/busses/i2c-bcm2708.c
--- linux-5.10.52-orig/drivers/i2c/busses/i2c-bcm2708.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/i2c/busses/i2c-bcm2708.c	2021-07-25 16:46:02.118355394 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/*
+ * Driver for Broadcom BCM2708 BSC Controllers
+ *
+ * Copyright (C) 2012 Chris Boot & Frank Buss
+ *
+ * This driver is inspired by:
+ * i2c-ocores.c, by Peter Korsgaard <jacmet@sunsite.dk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+
+/* BSC register offsets */
+#define BSC_C			0x00
+#define BSC_S			0x04
+#define BSC_DLEN		0x08
+#define BSC_A			0x0c
+#define BSC_FIFO		0x10
+#define BSC_DIV			0x14
+#define BSC_DEL			0x18
+#define BSC_CLKT		0x1c
+
+/* Bitfields in BSC_C */
+#define BSC_C_I2CEN		0x00008000
+#define BSC_C_INTR		0x00000400
+#define BSC_C_INTT		0x00000200
+#define BSC_C_INTD		0x00000100
+#define BSC_C_ST		0x00000080
+#define BSC_C_CLEAR_1		0x00000020
+#define BSC_C_CLEAR_2		0x00000010
+#define BSC_C_READ		0x00000001
+
+/* Bitfields in BSC_S */
+#define BSC_S_CLKT		0x00000200
+#define BSC_S_ERR		0x00000100
+#define BSC_S_RXF		0x00000080
+#define BSC_S_TXE		0x00000040
+#define BSC_S_RXD		0x00000020
+#define BSC_S_TXD		0x00000010
+#define BSC_S_RXR		0x00000008
+#define BSC_S_TXW		0x00000004
+#define BSC_S_DONE		0x00000002
+#define BSC_S_TA		0x00000001
+
+#define I2C_WAIT_LOOP_COUNT	200
+
+#define DRV_NAME		"bcm2708_i2c"
+
+static unsigned int baudrate;
+module_param(baudrate, uint, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
+MODULE_PARM_DESC(baudrate, "The I2C baudrate");
+
+static bool combined = false;
+module_param(combined, bool, 0644);
+MODULE_PARM_DESC(combined, "Use combined transactions");
+
+struct bcm2708_i2c {
+	struct i2c_adapter adapter;
+
+	spinlock_t lock;
+	void __iomem *base;
+	int irq;
+	struct clk *clk;
+	u32 cdiv;
+	u32 clk_tout;
+
+	struct completion done;
+
+	struct i2c_msg *msg;
+	int pos;
+	int nmsgs;
+	bool error;
+};
+
+static inline u32 bcm2708_rd(struct bcm2708_i2c *bi, unsigned reg)
+{
+	return readl(bi->base + reg);
+}
+
+static inline void bcm2708_wr(struct bcm2708_i2c *bi, unsigned reg, u32 val)
+{
+	writel(val, bi->base + reg);
+}
+
+static inline void bcm2708_bsc_reset(struct bcm2708_i2c *bi)
+{
+	bcm2708_wr(bi, BSC_C, 0);
+	bcm2708_wr(bi, BSC_S, BSC_S_CLKT | BSC_S_ERR | BSC_S_DONE);
+}
+
+static inline void bcm2708_bsc_fifo_drain(struct bcm2708_i2c *bi)
+{
+	while ((bi->pos < bi->msg->len) && (bcm2708_rd(bi, BSC_S) & BSC_S_RXD))
+		bi->msg->buf[bi->pos++] = bcm2708_rd(bi, BSC_FIFO);
+}
+
+static inline void bcm2708_bsc_fifo_fill(struct bcm2708_i2c *bi)
+{
+	while ((bi->pos < bi->msg->len) && (bcm2708_rd(bi, BSC_S) & BSC_S_TXD))
+		bcm2708_wr(bi, BSC_FIFO, bi->msg->buf[bi->pos++]);
+}
+
+static inline int bcm2708_bsc_setup(struct bcm2708_i2c *bi)
+{
+	u32 cdiv, s, clk_tout;
+	u32 c = BSC_C_I2CEN | BSC_C_INTD | BSC_C_ST | BSC_C_CLEAR_1;
+	int wait_loops = I2C_WAIT_LOOP_COUNT;
+
+	/* Can't call clk_get_rate as it locks a mutex and here we are spinlocked.
+	 * Use the value that we cached in the probe.
+	 */
+	cdiv = bi->cdiv;
+	clk_tout = bi->clk_tout;
+
+	if (bi->msg->flags & I2C_M_RD)
+		c |= BSC_C_INTR | BSC_C_READ;
+	else
+		c |= BSC_C_INTT;
+
+	bcm2708_wr(bi, BSC_CLKT, clk_tout);
+	bcm2708_wr(bi, BSC_DIV, cdiv);
+	bcm2708_wr(bi, BSC_A, bi->msg->addr);
+	bcm2708_wr(bi, BSC_DLEN, bi->msg->len);
+	if (combined)
+	{
+		/* Do the next two messages meet combined transaction criteria?
+		   - Current message is a write, next message is a read
+		   - Both messages to same slave address
+		   - Write message can fit inside FIFO (16 bytes or less) */
+		if ( (bi->nmsgs > 1) &&
+			!(bi->msg[0].flags & I2C_M_RD) && (bi->msg[1].flags & I2C_M_RD) &&
+			 (bi->msg[0].addr == bi->msg[1].addr) && (bi->msg[0].len <= 16)) {
+
+			/* Clear FIFO */
+			bcm2708_wr(bi, BSC_C, BSC_C_CLEAR_1);
+
+			/* Fill FIFO with entire write message (16 byte FIFO) */
+			while (bi->pos < bi->msg->len) {
+				bcm2708_wr(bi, BSC_FIFO, bi->msg->buf[bi->pos++]);
+			}
+			/* Start write transfer (no interrupts, don't clear FIFO) */
+			bcm2708_wr(bi, BSC_C, BSC_C_I2CEN | BSC_C_ST);
+
+			/* poll for transfer start bit (should only take 1-20 polls) */
+			do {
+				s = bcm2708_rd(bi, BSC_S);
+			} while (!(s & (BSC_S_TA | BSC_S_ERR | BSC_S_CLKT | BSC_S_DONE)) && --wait_loops >= 0);
+
+			/* did we time out or some error occured? */
+			if (wait_loops < 0 || (s & (BSC_S_ERR | BSC_S_CLKT))) {
+				return -1;
+			}
+
+			/* Send next read message before the write transfer finishes. */
+			bi->nmsgs--;
+			bi->msg++;
+			bi->pos = 0;
+			bcm2708_wr(bi, BSC_DLEN, bi->msg->len);
+			c = BSC_C_I2CEN | BSC_C_INTD | BSC_C_INTR | BSC_C_ST | BSC_C_READ;
+		}
+	}
+	bcm2708_wr(bi, BSC_C, c);
+
+	return 0;
+}
+
+static irqreturn_t bcm2708_i2c_interrupt(int irq, void *dev_id)
+{
+	struct bcm2708_i2c *bi = dev_id;
+	bool handled = true;
+	u32 s;
+	int ret;
+
+	spin_lock(&bi->lock);
+
+	/* we may see camera interrupts on the "other" I2C channel
+		   Just return if we've not sent anything */
+	if (!bi->nmsgs || !bi->msg) {
+		goto early_exit;
+	}
+
+	s = bcm2708_rd(bi, BSC_S);
+
+	if (s & (BSC_S_CLKT | BSC_S_ERR)) {
+		bcm2708_bsc_reset(bi);
+		bi->error = true;
+
+		bi->msg = 0; /* to inform the that all work is done */
+		bi->nmsgs = 0;
+		/* wake up our bh */
+		complete(&bi->done);
+	} else if (s & BSC_S_DONE) {
+		bi->nmsgs--;
+
+		if (bi->msg->flags & I2C_M_RD) {
+			bcm2708_bsc_fifo_drain(bi);
+		}
+
+		bcm2708_bsc_reset(bi);
+
+		if (bi->nmsgs) {
+			/* advance to next message */
+			bi->msg++;
+			bi->pos = 0;
+			ret = bcm2708_bsc_setup(bi);
+			if (ret < 0) {
+				bcm2708_bsc_reset(bi);
+				bi->error = true;
+				bi->msg = 0; /* to inform the that all work is done */
+				bi->nmsgs = 0;
+				/* wake up our bh */
+				complete(&bi->done);
+				goto early_exit;
+			}
+		} else {
+			bi->msg = 0; /* to inform the that all work is done */
+			bi->nmsgs = 0;
+			/* wake up our bh */
+			complete(&bi->done);
+		}
+	} else if (s & BSC_S_TXW) {
+		bcm2708_bsc_fifo_fill(bi);
+	} else if (s & BSC_S_RXR) {
+		bcm2708_bsc_fifo_drain(bi);
+	} else {
+		handled = false;
+	}
+
+early_exit:
+	spin_unlock(&bi->lock);
+
+	return handled ? IRQ_HANDLED : IRQ_NONE;
+}
+
+static int bcm2708_i2c_master_xfer(struct i2c_adapter *adap,
+	struct i2c_msg *msgs, int num)
+{
+	struct bcm2708_i2c *bi = adap->algo_data;
+	unsigned long flags;
+	int ret;
+
+	spin_lock_irqsave(&bi->lock, flags);
+
+	reinit_completion(&bi->done);
+	bi->msg = msgs;
+	bi->pos = 0;
+	bi->nmsgs = num;
+	bi->error = false;
+
+	ret = bcm2708_bsc_setup(bi);
+
+	spin_unlock_irqrestore(&bi->lock, flags);
+
+	/* check the result of the setup */
+	if (ret < 0)
+	{
+		dev_err(&adap->dev, "transfer setup timed out\n");
+		goto error_timeout;
+	}
+
+	ret = wait_for_completion_timeout(&bi->done, adap->timeout);
+	if (ret == 0) {
+		dev_err(&adap->dev, "transfer timed out\n");
+		goto error_timeout;
+	}
+
+	ret = bi->error ? -EIO : num;
+	return ret;
+
+error_timeout:
+	spin_lock_irqsave(&bi->lock, flags);
+	bcm2708_bsc_reset(bi);
+	bi->msg = 0; /* to inform the interrupt handler that there's nothing else to be done */
+	bi->nmsgs = 0;
+	spin_unlock_irqrestore(&bi->lock, flags);
+	return -ETIMEDOUT;
+}
+
+static u32 bcm2708_i2c_functionality(struct i2c_adapter *adap)
+{
+	return I2C_FUNC_I2C | /*I2C_FUNC_10BIT_ADDR |*/ I2C_FUNC_SMBUS_EMUL;
+}
+
+static struct i2c_algorithm bcm2708_i2c_algorithm = {
+	.master_xfer = bcm2708_i2c_master_xfer,
+	.functionality = bcm2708_i2c_functionality,
+};
+
+static int bcm2708_i2c_probe(struct platform_device *pdev)
+{
+	struct resource *regs;
+	int irq, err = -ENOMEM;
+	struct clk *clk;
+	struct bcm2708_i2c *bi;
+	struct i2c_adapter *adap;
+	unsigned long bus_hz;
+	u32 cdiv, clk_tout;
+	u32 baud;
+
+	baud = CONFIG_I2C_BCM2708_BAUDRATE;
+
+	if (pdev->dev.of_node) {
+		u32 bus_clk_rate;
+		pdev->id = of_alias_get_id(pdev->dev.of_node, "i2c");
+		if (pdev->id < 0) {
+			dev_err(&pdev->dev, "alias is missing\n");
+			return -EINVAL;
+		}
+		if (!of_property_read_u32(pdev->dev.of_node,
+					"clock-frequency", &bus_clk_rate))
+			baud = bus_clk_rate;
+		else
+			dev_warn(&pdev->dev,
+				"Could not read clock-frequency property\n");
+	}
+
+	if (baudrate)
+		baud = baudrate;
+
+	regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!regs) {
+		dev_err(&pdev->dev, "could not get IO memory\n");
+		return -ENXIO;
+	}
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0) {
+		dev_err(&pdev->dev, "could not get IRQ\n");
+		return irq;
+	}
+
+	clk = clk_get(&pdev->dev, NULL);
+	if (IS_ERR(clk)) {
+		dev_err(&pdev->dev, "could not find clk: %ld\n", PTR_ERR(clk));
+		return PTR_ERR(clk);
+	}
+
+	err = clk_prepare_enable(clk);
+	if (err) {
+		dev_err(&pdev->dev, "could not enable clk: %d\n", err);
+		goto out_clk_put;
+	}
+
+	bi = kzalloc(sizeof(*bi), GFP_KERNEL);
+	if (!bi)
+		goto out_clk_disable;
+
+	platform_set_drvdata(pdev, bi);
+
+	adap = &bi->adapter;
+	adap->class = I2C_CLASS_HWMON | I2C_CLASS_DDC;
+	adap->algo = &bcm2708_i2c_algorithm;
+	adap->algo_data = bi;
+	adap->dev.parent = &pdev->dev;
+	adap->nr = pdev->id;
+	strlcpy(adap->name, dev_name(&pdev->dev), sizeof(adap->name));
+	adap->dev.of_node = pdev->dev.of_node;
+
+	switch (pdev->id) {
+	case 0:
+		adap->class = I2C_CLASS_HWMON;
+		break;
+	case 1:
+		adap->class = I2C_CLASS_DDC;
+		break;
+	case 2:
+		adap->class = I2C_CLASS_DDC;
+		break;
+	default:
+		dev_err(&pdev->dev, "can only bind to BSC 0, 1 or 2\n");
+		err = -ENXIO;
+		goto out_free_bi;
+	}
+
+	spin_lock_init(&bi->lock);
+	init_completion(&bi->done);
+
+	bi->base = ioremap(regs->start, resource_size(regs));
+	if (!bi->base) {
+		dev_err(&pdev->dev, "could not remap memory\n");
+		goto out_free_bi;
+	}
+
+	bi->irq = irq;
+	bi->clk = clk;
+
+	err = request_irq(irq, bcm2708_i2c_interrupt, IRQF_SHARED,
+			dev_name(&pdev->dev), bi);
+	if (err) {
+		dev_err(&pdev->dev, "could not request IRQ: %d\n", err);
+		goto out_iounmap;
+	}
+
+	bcm2708_bsc_reset(bi);
+
+	err = i2c_add_numbered_adapter(adap);
+	if (err < 0) {
+		dev_err(&pdev->dev, "could not add I2C adapter: %d\n", err);
+		goto out_free_irq;
+	}
+
+	bus_hz = clk_get_rate(bi->clk);
+	cdiv = bus_hz / baud;
+	if (cdiv > 0xffff) {
+		cdiv = 0xffff;
+		baud = bus_hz / cdiv;
+	}
+
+	clk_tout = 35/1000*baud; //35ms timeout as per SMBus specs.
+	if (clk_tout > 0xffff)
+		clk_tout = 0xffff;
+	
+	bi->cdiv = cdiv;
+	bi->clk_tout = clk_tout;
+
+	dev_info(&pdev->dev, "BSC%d Controller at 0x%08lx (irq %d) (baudrate %d)\n",
+		pdev->id, (unsigned long)regs->start, irq, baud);
+
+	return 0;
+
+out_free_irq:
+	free_irq(bi->irq, bi);
+out_iounmap:
+	iounmap(bi->base);
+out_free_bi:
+	kfree(bi);
+out_clk_disable:
+	clk_disable_unprepare(clk);
+out_clk_put:
+	clk_put(clk);
+	return err;
+}
+
+static int bcm2708_i2c_remove(struct platform_device *pdev)
+{
+	struct bcm2708_i2c *bi = platform_get_drvdata(pdev);
+
+	platform_set_drvdata(pdev, NULL);
+
+	i2c_del_adapter(&bi->adapter);
+	free_irq(bi->irq, bi);
+	iounmap(bi->base);
+	clk_disable_unprepare(bi->clk);
+	clk_put(bi->clk);
+	kfree(bi);
+
+	return 0;
+}
+
+static const struct of_device_id bcm2708_i2c_of_match[] = {
+        { .compatible = "brcm,bcm2708-i2c" },
+        {},
+};
+MODULE_DEVICE_TABLE(of, bcm2708_i2c_of_match);
+
+static struct platform_driver bcm2708_i2c_driver = {
+	.driver		= {
+		.name	= DRV_NAME,
+		.owner	= THIS_MODULE,
+		.of_match_table = bcm2708_i2c_of_match,
+	},
+	.probe		= bcm2708_i2c_probe,
+	.remove		= bcm2708_i2c_remove,
+};
+
+// module_platform_driver(bcm2708_i2c_driver);
+
+
+static int __init bcm2708_i2c_init(void)
+{
+	return platform_driver_register(&bcm2708_i2c_driver);
+}
+
+static void __exit bcm2708_i2c_exit(void)
+{
+	platform_driver_unregister(&bcm2708_i2c_driver);
+}
+
+module_init(bcm2708_i2c_init);
+module_exit(bcm2708_i2c_exit);
+
+
+
+MODULE_DESCRIPTION("BSC controller driver for Broadcom BCM2708");
+MODULE_AUTHOR("Chris Boot <bootc@bootc.net>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRV_NAME);
diff -Nur --no-dereference linux-5.10.52-orig/drivers/i2c/busses/i2c-bcm2835.c linux-5.10.52-v7l+/drivers/i2c/busses/i2c-bcm2835.c
--- linux-5.10.52-orig/drivers/i2c/busses/i2c-bcm2835.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/i2c/busses/i2c-bcm2835.c	2021-07-25 16:46:02.118355394 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:54 @
 #define BCM2835_I2C_CDIV_MIN	0x0002
 #define BCM2835_I2C_CDIV_MAX	0xFFFE
 
+static unsigned int debug;
+module_param(debug, uint, 0644);
+MODULE_PARM_DESC(debug, "1=err, 2=isr, 3=xfer");
+
+#define BCM2835_DEBUG_MAX	512
+struct bcm2835_debug {
+	struct i2c_msg *msg;
+	int msg_idx;
+	size_t remain;
+	u32 status;
+};
+
 struct bcm2835_i2c_dev {
 	struct device *dev;
 	void __iomem *regs;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:78 @
 	u32 msg_err;
 	u8 *msg_buf;
 	size_t msg_buf_remaining;
+	struct bcm2835_debug debug[BCM2835_DEBUG_MAX];
+	unsigned int debug_num;
+	unsigned int debug_num_msgs;
 };
 
+static inline void bcm2835_debug_add(struct bcm2835_i2c_dev *i2c_dev, u32 s)
+{
+	if (!i2c_dev->debug_num_msgs || i2c_dev->debug_num >= BCM2835_DEBUG_MAX)
+		return;
+
+	i2c_dev->debug[i2c_dev->debug_num].msg = i2c_dev->curr_msg;
+	i2c_dev->debug[i2c_dev->debug_num].msg_idx =
+				i2c_dev->debug_num_msgs - i2c_dev->num_msgs;
+	i2c_dev->debug[i2c_dev->debug_num].remain = i2c_dev->msg_buf_remaining;
+	i2c_dev->debug[i2c_dev->debug_num].status = s;
+	i2c_dev->debug_num++;
+}
+
+static void bcm2835_debug_print_status(struct bcm2835_i2c_dev *i2c_dev,
+				       struct bcm2835_debug *d)
+{
+	u32 s = d->status;
+
+	pr_info("isr: remain=%zu, status=0x%x : %s%s%s%s%s%s%s%s%s%s [i2c%d]\n",
+		d->remain, s,
+		s & BCM2835_I2C_S_TA ? "TA " : "",
+		s & BCM2835_I2C_S_DONE ? "DONE " : "",
+		s & BCM2835_I2C_S_TXW ? "TXW " : "",
+		s & BCM2835_I2C_S_RXR ? "RXR " : "",
+		s & BCM2835_I2C_S_TXD ? "TXD " : "",
+		s & BCM2835_I2C_S_RXD ? "RXD " : "",
+		s & BCM2835_I2C_S_TXE ? "TXE " : "",
+		s & BCM2835_I2C_S_RXF ? "RXF " : "",
+		s & BCM2835_I2C_S_ERR ? "ERR " : "",
+		s & BCM2835_I2C_S_CLKT ? "CLKT " : "",
+		i2c_dev->adapter.nr);
+}
+
+static void bcm2835_debug_print_msg(struct bcm2835_i2c_dev *i2c_dev,
+				    struct i2c_msg *msg, int i, int total,
+				    const char *fname)
+{
+	pr_info("%s: msg(%d/%d) %s addr=0x%02x, len=%u flags=%s%s%s%s%s%s%s [i2c%d]\n",
+		fname, i, total,
+		msg->flags & I2C_M_RD ? "read" : "write", msg->addr, msg->len,
+		msg->flags & I2C_M_TEN ? "TEN" : "",
+		msg->flags & I2C_M_RECV_LEN ? "RECV_LEN" : "",
+		msg->flags & I2C_M_NO_RD_ACK ? "NO_RD_ACK" : "",
+		msg->flags & I2C_M_IGNORE_NAK ? "IGNORE_NAK" : "",
+		msg->flags & I2C_M_REV_DIR_ADDR ? "REV_DIR_ADDR" : "",
+		msg->flags & I2C_M_NOSTART ? "NOSTART" : "",
+		msg->flags & I2C_M_STOP ? "STOP" : "",
+		i2c_dev->adapter.nr);
+}
+
+static void bcm2835_debug_print(struct bcm2835_i2c_dev *i2c_dev)
+{
+	struct bcm2835_debug *d;
+	unsigned int i;
+
+	for (i = 0; i < i2c_dev->debug_num; i++) {
+		d = &i2c_dev->debug[i];
+		if (d->status == ~0)
+			bcm2835_debug_print_msg(i2c_dev, d->msg, d->msg_idx,
+				i2c_dev->debug_num_msgs, "start_transfer");
+		else
+			bcm2835_debug_print_status(i2c_dev, d);
+	}
+	if (i2c_dev->debug_num >= BCM2835_DEBUG_MAX)
+		pr_info("BCM2835_DEBUG_MAX reached\n");
+}
+
 static inline void bcm2835_i2c_writel(struct bcm2835_i2c_dev *i2c_dev,
 				      u32 reg, u32 val)
 {
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:191 @
 {
 	struct clk_bcm2835_i2c *div = to_clk_bcm2835_i2c(hw);
 	u32 redl, fedl;
+	u32 clk_tout;
 	u32 divider = clk_bcm2835_i2c_calc_divider(rate, parent_rate);
 
 	if (divider == -EINVAL)
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:215 @
 	bcm2835_i2c_writel(div->i2c_dev, BCM2835_I2C_DEL,
 			   (fedl << BCM2835_I2C_FEDL_SHIFT) |
 			   (redl << BCM2835_I2C_REDL_SHIFT));
+
+	/*
+	 * Set the clock stretch timeout to the SMBUs-recommended 35ms.
+	 */
+	if (rate > 0xffff*1000/35)
+	    clk_tout = 0xffff;
+	else
+	    clk_tout = 35*rate/1000;
+
+	bcm2835_i2c_writel(div->i2c_dev, BCM2835_I2C_CLKT, clk_tout);
+
 	return 0;
 }
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:349 @
 	bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_A, msg->addr);
 	bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_DLEN, msg->len);
 	bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_C, c);
+	bcm2835_debug_add(i2c_dev, ~0);
 }
 
 static void bcm2835_i2c_finish_transfer(struct bcm2835_i2c_dev *i2c_dev)
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:376 @
 	u32 val, err;
 
 	val = bcm2835_i2c_readl(i2c_dev, BCM2835_I2C_S);
+	bcm2835_debug_add(i2c_dev, val);
 
 	err = val & (BCM2835_I2C_S_CLKT | BCM2835_I2C_S_ERR);
 	if (err) {
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:443 @
 	unsigned long time_left;
 	int i;
 
+	if (debug)
+		i2c_dev->debug_num_msgs = num;
+
+	if (debug > 2)
+		for (i = 0; i < num; i++)
+			bcm2835_debug_print_msg(i2c_dev, &msgs[i], i + 1, num, __func__);
+
 	for (i = 0; i < (num - 1); i++)
 		if (msgs[i].flags & I2C_M_RD) {
 			dev_warn_once(i2c_dev->dev,
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:468 @
 
 	bcm2835_i2c_finish_transfer(i2c_dev);
 
+	if (debug > 1 || (debug && (!time_left || i2c_dev->msg_err)))
+		bcm2835_debug_print(i2c_dev);
+	i2c_dev->debug_num_msgs = 0;
+	i2c_dev->debug_num = 0;
 	if (!time_left) {
 		bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_C,
 				   BCM2835_I2C_C_CLEAR);
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:482 @
 	if (!i2c_dev->msg_err)
 		return num;
 
-	dev_dbg(i2c_dev->dev, "i2c transfer failed: %x\n", i2c_dev->msg_err);
+	if (debug)
+		dev_err(i2c_dev->dev, "i2c transfer failed: %x\n",
+			i2c_dev->msg_err);
 
 	if (i2c_dev->msg_err & BCM2835_I2C_S_ERR)
 		return -EREMOTEIO;
diff -Nur --no-dereference linux-5.10.52-orig/drivers/i2c/busses/i2c-gpio.c linux-5.10.52-v7l+/drivers/i2c/busses/i2c-gpio.c
--- linux-5.10.52-orig/drivers/i2c/busses/i2c-gpio.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/i2c/busses/i2c-gpio.c	2021-07-25 16:46:02.128355226 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:448 @
 	adap->dev.parent = dev;
 	adap->dev.of_node = np;
 
-	adap->nr = pdev->id;
+	if (pdev->id != PLATFORM_DEVID_NONE || !pdev->dev.of_node ||
+	    of_property_read_u32(pdev->dev.of_node, "reg", &adap->nr))
+		adap->nr = pdev->id;
 	ret = i2c_bit_add_numbered_bus(adap);
 	if (ret)
 		return ret;
diff -Nur --no-dereference linux-5.10.52-orig/drivers/i2c/busses/Kconfig linux-5.10.52-v7l+/drivers/i2c/busses/Kconfig
--- linux-5.10.52-orig/drivers/i2c/busses/Kconfig	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/i2c/busses/Kconfig	2021-07-25 16:46:02.098355729 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:12 @
 comment "PC SMBus host controller drivers"
 	depends on PCI
 
+config I2C_BCM2708
+	tristate "BCM2708 BSC"
+	depends on ARCH_BCM2835
+	help
+	  Enabling this option will add BSC (Broadcom Serial Controller)
+	  support for the BCM2708. BSC is a Broadcom proprietary bus compatible
+	  with I2C/TWI/SMBus.
+
+config I2C_BCM2708_BAUDRATE
+	prompt "BCM2708 I2C baudrate"
+	depends on I2C_BCM2708
+	int
+	default 100000
+	help
+	  Set the I2C baudrate. This will alter the default value. A
+	  different baudrate can be set by using a module parameter as well. If
+	  no parameter is provided when loading, this is the value that will be
+	  used.
+
 config I2C_ALI1535
 	tristate "ALI 1535"
 	depends on PCI
diff -Nur --no-dereference linux-5.10.52-orig/drivers/i2c/busses/Makefile linux-5.10.52-v7l+/drivers/i2c/busses/Makefile
--- linux-5.10.52-orig/drivers/i2c/busses/Makefile	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/i2c/busses/Makefile	2021-07-25 16:46:02.098355729 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:6 @
 # Makefile for the i2c bus drivers.
 #
 
+obj-$(CONFIG_I2C_BCM2708)	+= i2c-bcm2708.o
+
 # ACPI drivers
 obj-$(CONFIG_I2C_SCMI)		+= i2c-scmi.o
 
diff -Nur --no-dereference linux-5.10.52-orig/drivers/input/joystick/Kconfig linux-5.10.52-v7l+/drivers/input/joystick/Kconfig
--- linux-5.10.52-orig/drivers/input/joystick/Kconfig	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/input/joystick/Kconfig	2021-07-25 16:46:03.218336952 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:385 @
 	  To compile this driver as a module, choose M here: the
 	  module will be called fsia6b.
 
+config JOYSTICK_RPISENSE
+	tristate "Raspberry Pi Sense HAT joystick"
+	depends on GPIOLIB && INPUT
+	select MFD_RPISENSE_CORE
+
+	help
+	  This is the joystick driver for the Raspberry Pi Sense HAT
+
 endif
diff -Nur --no-dereference linux-5.10.52-orig/drivers/input/joystick/Makefile linux-5.10.52-v7l+/drivers/input/joystick/Makefile
--- linux-5.10.52-orig/drivers/input/joystick/Makefile	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/input/joystick/Makefile	2021-07-25 16:46:03.218336952 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:40 @
 obj-$(CONFIG_JOYSTICK_WALKERA0701)	+= walkera0701.o
 obj-$(CONFIG_JOYSTICK_XPAD)		+= xpad.o
 obj-$(CONFIG_JOYSTICK_ZHENHUA)		+= zhenhua.o
-
+obj-$(CONFIG_JOYSTICK_RPISENSE)		+= rpisense-js.o
diff -Nur --no-dereference linux-5.10.52-orig/drivers/input/joystick/rpisense-js.c linux-5.10.52-v7l+/drivers/input/joystick/rpisense-js.c
--- linux-5.10.52-orig/drivers/input/joystick/rpisense-js.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/input/joystick/rpisense-js.c	2021-07-25 16:46:03.248336449 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/*
+ * Raspberry Pi Sense HAT joystick driver
+ * http://raspberrypi.org
+ *
+ * Copyright (C) 2015 Raspberry Pi
+ *
+ * Author: Serge Schneider
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ */
+
+#include <linux/module.h>
+
+#include <linux/mfd/rpisense/joystick.h>
+#include <linux/mfd/rpisense/core.h>
+
+static struct rpisense *rpisense;
+static unsigned char keymap[5] = {KEY_DOWN, KEY_RIGHT, KEY_UP, KEY_ENTER, KEY_LEFT,};
+
+static void keys_work_fn(struct work_struct *work)
+{
+	int i;
+	static s32 prev_keys;
+	struct rpisense_js *rpisense_js = &rpisense->joystick;
+	s32 keys = rpisense_reg_read(rpisense, RPISENSE_KEYS);
+	s32 changes = keys ^ prev_keys;
+
+	prev_keys = keys;
+	for (i = 0; i < 5; i++) {
+		if (changes & 1) {
+			input_report_key(rpisense_js->keys_dev,
+					 keymap[i], keys & 1);
+		}
+		changes >>= 1;
+		keys >>= 1;
+	}
+	input_sync(rpisense_js->keys_dev);
+}
+
+static irqreturn_t keys_irq_handler(int irq, void *pdev)
+{
+	struct rpisense_js *rpisense_js = &rpisense->joystick;
+
+	schedule_work(&rpisense_js->keys_work_s);
+	return IRQ_HANDLED;
+}
+
+static int rpisense_js_probe(struct platform_device *pdev)
+{
+	int ret;
+	int i;
+	struct rpisense_js *rpisense_js;
+
+	rpisense = rpisense_get_dev();
+	rpisense_js = &rpisense->joystick;
+
+	INIT_WORK(&rpisense_js->keys_work_s, keys_work_fn);
+
+	rpisense_js->keys_dev = input_allocate_device();
+	if (!rpisense_js->keys_dev) {
+		dev_err(&pdev->dev, "Could not allocate input device.\n");
+		return -ENOMEM;
+	}
+
+	rpisense_js->keys_dev->evbit[0] = BIT_MASK(EV_KEY);
+	for (i = 0; i < ARRAY_SIZE(keymap); i++) {
+		set_bit(keymap[i],
+			rpisense_js->keys_dev->keybit);
+	}
+
+	rpisense_js->keys_dev->name = "Raspberry Pi Sense HAT Joystick";
+	rpisense_js->keys_dev->phys = "rpi-sense-joy/input0";
+	rpisense_js->keys_dev->id.bustype = BUS_I2C;
+	rpisense_js->keys_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP);
+	rpisense_js->keys_dev->keycode = keymap;
+	rpisense_js->keys_dev->keycodesize = sizeof(unsigned char);
+	rpisense_js->keys_dev->keycodemax = ARRAY_SIZE(keymap);
+
+	ret = input_register_device(rpisense_js->keys_dev);
+	if (ret) {
+		dev_err(&pdev->dev, "Could not register input device.\n");
+		goto err_keys_alloc;
+	}
+
+	ret = gpiod_direction_input(rpisense_js->keys_desc);
+	if (ret) {
+		dev_err(&pdev->dev, "Could not set keys-int direction.\n");
+		goto err_keys_reg;
+	}
+
+	rpisense_js->keys_irq = gpiod_to_irq(rpisense_js->keys_desc);
+	if (rpisense_js->keys_irq < 0) {
+		dev_err(&pdev->dev, "Could not determine keys-int IRQ.\n");
+		ret = rpisense_js->keys_irq;
+		goto err_keys_reg;
+	}
+
+	ret = devm_request_irq(&pdev->dev, rpisense_js->keys_irq,
+			       keys_irq_handler, IRQF_TRIGGER_RISING,
+			       "keys", &pdev->dev);
+	if (ret) {
+		dev_err(&pdev->dev, "IRQ request failed.\n");
+		goto err_keys_reg;
+	}
+	return 0;
+err_keys_reg:
+	input_unregister_device(rpisense_js->keys_dev);
+err_keys_alloc:
+	input_free_device(rpisense_js->keys_dev);
+	return ret;
+}
+
+static int rpisense_js_remove(struct platform_device *pdev)
+{
+	struct rpisense_js *rpisense_js = &rpisense->joystick;
+
+	input_unregister_device(rpisense_js->keys_dev);
+	input_free_device(rpisense_js->keys_dev);
+	return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id rpisense_js_id[] = {
+	{ .compatible = "rpi,rpi-sense-js" },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, rpisense_js_id);
+#endif
+
+static struct platform_device_id rpisense_js_device_id[] = {
+	{ .name = "rpi-sense-js" },
+	{ },
+};
+MODULE_DEVICE_TABLE(platform, rpisense_js_device_id);
+
+static struct platform_driver rpisense_js_driver = {
+	.probe = rpisense_js_probe,
+	.remove = rpisense_js_remove,
+	.driver = {
+		.name = "rpi-sense-js",
+		.owner = THIS_MODULE,
+	},
+};
+
+module_platform_driver(rpisense_js_driver);
+
+MODULE_DESCRIPTION("Raspberry Pi Sense HAT joystick driver");
+MODULE_AUTHOR("Serge Schneider <serge@raspberrypi.org>");
+MODULE_LICENSE("GPL");
diff -Nur --no-dereference linux-5.10.52-orig/drivers/input/touchscreen/edt-ft5x06.c linux-5.10.52-v7l+/drivers/input/touchscreen/edt-ft5x06.c
--- linux-5.10.52-orig/drivers/input/touchscreen/edt-ft5x06.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/input/touchscreen/edt-ft5x06.c	2021-07-25 16:46:03.588330749 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:72 @
 #define EDT_RAW_DATA_RETRIES		100
 #define EDT_RAW_DATA_DELAY		1000 /* usec */
 
+#define POLL_INTERVAL_MS		17	/* 17ms = 60fps */
+
 enum edt_pmode {
 	EDT_PMODE_NOT_SUPPORTED,
 	EDT_PMODE_HIBERNATE,
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:131 @
 
 	struct edt_reg_addr reg_addr;
 	enum edt_ver version;
+
+	struct timer_list timer;
+	struct work_struct work_i2c_poll;
 };
 
 struct edt_i2c_chip_data {
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:283 @
 	return IRQ_HANDLED;
 }
 
+static void edt_ft5x06_ts_irq_poll_timer(struct timer_list *t)
+{
+	struct edt_ft5x06_ts_data *tsdata = from_timer(tsdata, t, timer);
+
+	schedule_work(&tsdata->work_i2c_poll);
+	mod_timer(&tsdata->timer, jiffies + msecs_to_jiffies(POLL_INTERVAL_MS));
+}
+
+static void edt_ft5x06_ts_work_i2c_poll(struct work_struct *work)
+{
+	struct edt_ft5x06_ts_data *tsdata = container_of(work,
+			struct edt_ft5x06_ts_data, work_i2c_poll);
+
+	edt_ft5x06_ts_isr(0, tsdata);
+}
+
 static int edt_ft5x06_register_write(struct edt_ft5x06_ts_data *tsdata,
 				     u8 addr, u8 value)
 {
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1245 @
 
 	i2c_set_clientdata(client, tsdata);
 
-	irq_flags = irq_get_trigger_type(client->irq);
-	if (irq_flags == IRQF_TRIGGER_NONE)
-		irq_flags = IRQF_TRIGGER_FALLING;
-	irq_flags |= IRQF_ONESHOT;
-
-	error = devm_request_threaded_irq(&client->dev, client->irq,
-					NULL, edt_ft5x06_ts_isr, irq_flags,
-					client->name, tsdata);
-	if (error) {
-		dev_err(&client->dev, "Unable to request touchscreen IRQ.\n");
-		return error;
+	if (client->irq) {
+		irq_flags = irq_get_trigger_type(client->irq);
+		if (irq_flags == IRQF_TRIGGER_NONE)
+			irq_flags = IRQF_TRIGGER_FALLING;
+		irq_flags |= IRQF_ONESHOT;
+
+		error = devm_request_threaded_irq(&client->dev, client->irq,
+						  NULL, edt_ft5x06_ts_isr,
+						  irq_flags, client->name,
+						  tsdata);
+		if (error) {
+			dev_err(&client->dev, "Unable to request touchscreen IRQ.\n");
+			return error;
+		}
+	} else {
+		INIT_WORK(&tsdata->work_i2c_poll,
+			  edt_ft5x06_ts_work_i2c_poll);
+		timer_setup(&tsdata->timer, edt_ft5x06_ts_irq_poll_timer, 0);
+		tsdata->timer.expires = jiffies +
+					msecs_to_jiffies(POLL_INTERVAL_MS);
+		add_timer(&tsdata->timer);
 	}
 
 	error = devm_device_add_group(&client->dev, &edt_ft5x06_attr_group);
diff -Nur --no-dereference linux-5.10.52-orig/drivers/irqchip/irq-bcm2835.c linux-5.10.52-v7l+/drivers/irqchip/irq-bcm2835.c
--- linux-5.10.52-orig/drivers/irqchip/irq-bcm2835.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/irqchip/irq-bcm2835.c	2021-07-25 16:46:03.838326557 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:46 @
 #include <linux/irqdomain.h>
 
 #include <asm/exception.h>
+#ifndef CONFIG_ARM64
+#include <asm/mach/irq.h>
+#endif
 
 /* Put the bank and irq (32 bits) into the hwirq */
-#define MAKE_HWIRQ(b, n)	((b << 5) | (n))
+#define MAKE_HWIRQ(b, n)	(((b) << 5) | (n))
 #define HWIRQ_BANK(i)		(i >> 5)
 #define HWIRQ_BIT(i)		BIT(i & 0x1f)
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:66 @
 #define BANK0_VALID_MASK	(BANK0_HWIRQ_MASK | BANK1_HWIRQ | BANK2_HWIRQ \
 					| SHORTCUT1_MASK | SHORTCUT2_MASK)
 
+#undef ARM_LOCAL_GPU_INT_ROUTING
+#define ARM_LOCAL_GPU_INT_ROUTING 0x0c
+
 #define REG_FIQ_CONTROL		0x0c
 #define FIQ_CONTROL_ENABLE	BIT(7)
+#define REG_FIQ_ENABLE		FIQ_CONTROL_ENABLE
+#define REG_FIQ_DISABLE	0
 
 #define NR_BANKS		3
 #define IRQS_PER_BANK		32
+#define NUMBER_IRQS		MAKE_HWIRQ(NR_BANKS, 0)
 
 static const int reg_pending[] __initconst = { 0x00, 0x04, 0x08 };
 static const int reg_enable[] __initconst = { 0x18, 0x10, 0x14 };
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:94 @
 	void __iomem *enable[NR_BANKS];
 	void __iomem *disable[NR_BANKS];
 	struct irq_domain *domain;
+	void __iomem *local_base;
 };
 
 static struct armctrl_ic intc __read_mostly;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:102 @
 	struct pt_regs *regs);
 static void bcm2836_chained_handle_irq(struct irq_desc *desc);
 
+static inline unsigned int hwirq_to_fiq(unsigned long hwirq)
+{
+	hwirq -= NUMBER_IRQS;
+	/*
+	 * The hwirq numbering used in this driver is:
+	 *   BASE (0-7) GPU1 (32-63) GPU2 (64-95).
+	 * This differ from the one used in the FIQ register:
+	 *   GPU1 (0-31) GPU2 (32-63) BASE (64-71)
+	 */
+	if (hwirq >= 32)
+		return hwirq - 32;
+
+	return hwirq + 64;
+}
+
 static void armctrl_mask_irq(struct irq_data *d)
 {
-	writel_relaxed(HWIRQ_BIT(d->hwirq), intc.disable[HWIRQ_BANK(d->hwirq)]);
+	if (d->hwirq >= NUMBER_IRQS)
+		writel_relaxed(REG_FIQ_DISABLE, intc.base + REG_FIQ_CONTROL);
+	else
+		writel_relaxed(HWIRQ_BIT(d->hwirq),
+			       intc.disable[HWIRQ_BANK(d->hwirq)]);
 }
 
 static void armctrl_unmask_irq(struct irq_data *d)
 {
-	writel_relaxed(HWIRQ_BIT(d->hwirq), intc.enable[HWIRQ_BANK(d->hwirq)]);
+	if (d->hwirq >= NUMBER_IRQS) {
+		if (num_online_cpus() > 1) {
+			unsigned int data;
+
+			if (!intc.local_base) {
+				pr_err("FIQ is disabled due to missing arm_local_intc\n");
+				return;
+			}
+
+			data = readl_relaxed(intc.local_base +
+					     ARM_LOCAL_GPU_INT_ROUTING);
+
+			data &= ~0xc;
+			data |= (1 << 2);
+			writel_relaxed(data,
+				       intc.local_base +
+				       ARM_LOCAL_GPU_INT_ROUTING);
+		}
+
+		writel_relaxed(REG_FIQ_ENABLE | hwirq_to_fiq(d->hwirq),
+			       intc.base + REG_FIQ_CONTROL);
+	} else {
+		writel_relaxed(HWIRQ_BIT(d->hwirq),
+			       intc.enable[HWIRQ_BANK(d->hwirq)]);
+	}
+}
+
+#ifdef CONFIG_ARM64
+void bcm2836_arm_irqchip_spin_gpu_irq(void);
+
+static void armctrl_ack_irq(struct irq_data *d)
+{
+	bcm2836_arm_irqchip_spin_gpu_irq();
 }
 
+#endif
+
 static struct irq_chip armctrl_chip = {
 	.name = "ARMCTRL-level",
 	.irq_mask = armctrl_mask_irq,
-	.irq_unmask = armctrl_unmask_irq
+	.irq_unmask = armctrl_unmask_irq,
+#ifdef CONFIG_ARM64
+	.irq_ack    = armctrl_ack_irq
+#endif
 };
 
 static int armctrl_xlate(struct irq_domain *d, struct device_node *ctrlr,
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:204 @
 				  bool is_2836)
 {
 	void __iomem *base;
-	int irq, b, i;
+	int irq = 0, last_irq, b, i;
 	u32 reg;
 
 	base = of_iomap(node, 0);
 	if (!base)
 		panic("%pOF: unable to map IC registers\n", node);
 
-	intc.domain = irq_domain_add_linear(node, MAKE_HWIRQ(NR_BANKS, 0),
-			&armctrl_ops, NULL);
+	intc.base = base;
+	intc.domain = irq_domain_add_linear(node, NUMBER_IRQS * 2,
+					    &armctrl_ops, NULL);
 	if (!intc.domain)
 		panic("%pOF: unable to create IRQ domain\n", node);
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:244 @
 		pr_err(FW_BUG "Bootloader left fiq enabled\n");
 	}
 
+	last_irq = irq;
+
 	if (is_2836) {
 		int parent_irq = irq_of_parse_and_map(node, 0);
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:258 @
 		set_handle_irq(bcm2835_handle_irq);
 	}
 
+	if (is_2836) {
+		extern void __iomem * __attribute__((weak)) arm_local_intc;
+		intc.local_base = arm_local_intc;
+		if (!intc.local_base)
+			pr_err("Failed to get local intc base. FIQ is disabled for cpus > 1\n");
+	}
+
+	/* Make a duplicate irq range which is used to enable FIQ */
+	for (b = 0; b < NR_BANKS; b++) {
+		for (i = 0; i < bank_irqs[b]; i++) {
+			irq = irq_create_mapping(intc.domain,
+					MAKE_HWIRQ(b, i) + NUMBER_IRQS);
+			BUG_ON(irq <= 0);
+			irq_set_chip(irq, &armctrl_chip);
+			irq_set_probe(irq);
+		}
+	}
+#ifndef CONFIG_ARM64
+	init_FIQ(irq - last_irq);
+#endif
+
 	return 0;
 }
 
diff -Nur --no-dereference linux-5.10.52-orig/drivers/irqchip/irq-bcm2836.c linux-5.10.52-v7l+/drivers/irqchip/irq-bcm2836.c
--- linux-5.10.52-orig/drivers/irqchip/irq-bcm2836.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/irqchip/irq-bcm2836.c	2021-07-25 16:46:03.838326557 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:25 @
 
 static struct bcm2836_arm_irqchip_intc intc  __read_mostly;
 
+void __iomem *arm_local_intc;
+EXPORT_SYMBOL_GPL(arm_local_intc);
+
 static void bcm2836_arm_irqchip_mask_per_cpu_irq(unsigned int reg_offset,
 						 unsigned int bit,
 						 int cpu)
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:90 @
 {
 }
 
+#ifdef CONFIG_ARM64
+
+void bcm2836_arm_irqchip_spin_gpu_irq(void)
+{
+	u32 i;
+	void __iomem *gpurouting = (intc.base + LOCAL_GPU_ROUTING);
+	u32 routing_val = readl(gpurouting);
+
+	for (i = 1; i <= 3; i++) {
+		u32 new_routing_val = (routing_val + i) & 3;
+
+		if (cpu_active(new_routing_val)) {
+			writel(new_routing_val, gpurouting);
+			return;
+		}
+	}
+}
+EXPORT_SYMBOL(bcm2836_arm_irqchip_spin_gpu_irq);
+
+#endif
+
 static struct irq_chip bcm2836_arm_irqchip_gpu = {
 	.name		= "bcm2836-gpu",
 	.irq_mask	= bcm2836_arm_irqchip_mask_gpu_irq,
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:155 @
 	irq_set_percpu_devid(irq);
 	irq_domain_set_info(d, irq, hw, chip, d->host_data,
 			    handle_percpu_devid_irq, NULL, NULL);
-	irq_set_status_flags(irq, IRQ_NOAUTOEN);
+	irq_set_status_flags(irq, IRQ_NOAUTOEN | IRQ_TYPE_LEVEL_LOW);
 
 	return 0;
 }
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:350 @
 		panic("%pOF: unable to map local interrupt registers\n", node);
 	}
 
+	arm_local_intc = intc.base;
+
 	bcm2835_init_local_timer_frequency();
 
 	intc.domain = irq_domain_add_linear(node, LAST_IRQ + 1,
diff -Nur --no-dereference linux-5.10.52-orig/drivers/leds/leds-gpio.c linux-5.10.52-v7l+/drivers/leds/leds-gpio.c
--- linux-5.10.52-orig/drivers/leds/leds-gpio.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/leds/leds-gpio.c	2021-07-25 16:46:04.058322869 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:49 @
 		led_dat->platform_gpio_blink_set(led_dat->gpiod, level,
 						 NULL, NULL);
 		led_dat->blinking = 0;
+	} else if (led_dat->cdev.flags & SET_GPIO_INPUT) {
+		gpiod_direction_input(led_dat->gpiod);
+		led_dat->cdev.flags &= ~SET_GPIO_INPUT;
+	} else if (led_dat->cdev.flags & SET_GPIO_OUTPUT) {
+		gpiod_direction_output(led_dat->gpiod, level);
+		led_dat->cdev.flags &= ~SET_GPIO_OUTPUT;
 	} else {
-		if (led_dat->can_sleep)
+		if (led_dat->can_sleep ||
+			(led_dat->cdev.flags & (SET_GPIO_INPUT | SET_GPIO_OUTPUT) ))
 			gpiod_set_value_cansleep(led_dat->gpiod, level);
 		else
 			gpiod_set_value(led_dat->gpiod, level);
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:71 @
 	return 0;
 }
 
+static enum led_brightness gpio_led_get(struct led_classdev *led_cdev)
+{
+	struct gpio_led_data *led_dat =
+		container_of(led_cdev, struct gpio_led_data, cdev);
+	return gpiod_get_value_cansleep(led_dat->gpiod) ? LED_FULL : LED_OFF;
+}
+
 static int gpio_blink_set(struct led_classdev *led_cdev,
 	unsigned long *delay_on, unsigned long *delay_off)
 {
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:106 @
 		led_dat->platform_gpio_blink_set = blink_set;
 		led_dat->cdev.blink_set = gpio_blink_set;
 	}
+	led_dat->cdev.brightness_get = gpio_led_get;
 	if (template->default_state == LEDS_GPIO_DEFSTATE_KEEP) {
 		state = gpiod_get_value_cansleep(led_dat->gpiod);
 		if (state < 0)
diff -Nur --no-dereference linux-5.10.52-orig/drivers/leds/trigger/Kconfig linux-5.10.52-v7l+/drivers/leds/trigger/Kconfig
--- linux-5.10.52-orig/drivers/leds/trigger/Kconfig	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/leds/trigger/Kconfig	2021-07-25 16:46:04.118321863 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:117 @
 	  This enables direct flash/torch on/off by the driver, kernel space.
 	  If unsure, say Y.
 
+config LEDS_TRIGGER_INPUT
+	tristate "LED Input Trigger"
+	depends on LEDS_TRIGGERS
+	help
+	  This allows the GPIOs assigned to be LEDs to be initialised to inputs.
+	  If unsure, say Y.
+
 config LEDS_TRIGGER_PANIC
 	bool "LED Panic Trigger"
 	help
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:154 @
 	  the audio mute and mic-mute changes.
 	  If unsure, say N
 
+config LEDS_TRIGGER_ACTPWR
+	tristate "ACT/PWR Input Trigger"
+	depends on LEDS_TRIGGERS
+	help
+	  This trigger is intended for platforms that have one software-
+	  controllable LED and no dedicated activity or power LEDs, hence the
+	  need to make the one LED perform both functions. It cycles between
+	  default-on and an inverted mmc0 every 500ms, guaranteeing that it is
+	  on for at least half of the time.
+	  If unsure, say N.
+
 endif # LEDS_TRIGGERS
diff -Nur --no-dereference linux-5.10.52-orig/drivers/leds/trigger/ledtrig-actpwr.c linux-5.10.52-v7l+/drivers/leds/trigger/ledtrig-actpwr.c
--- linux-5.10.52-orig/drivers/leds/trigger/ledtrig-actpwr.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/leds/trigger/ledtrig-actpwr.c	2021-07-25 16:46:04.118321863 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Activity/power trigger
+ *
+ * Copyright (C) 2020 Raspberry Pi (Trading) Ltd.
+ *
+ * Based on Atsushi Nemoto's ledtrig-heartbeat.c, although there may be
+ * nothing left of the original now.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/timer.h>
+#include <linux/leds.h>
+#include "../leds.h"
+
+enum {
+	TRIG_ACT,
+	TRIG_PWR,
+
+	TRIG_COUNT
+};
+
+struct actpwr_trig_src {
+	const char *name;
+	int interval;
+	bool invert;
+};
+
+struct actpwr_vled {
+	struct led_classdev cdev;
+	struct actpwr_trig_data *parent;
+	enum led_brightness value;
+	unsigned int interval;
+	bool invert;
+};
+
+struct actpwr_trig_data {
+	struct led_trigger trig;
+	struct actpwr_vled virt_leds[TRIG_COUNT];
+	struct actpwr_vled *active;
+	struct timer_list timer;
+	int next_active;
+};
+
+static int actpwr_trig_activate(struct led_classdev *led_cdev);
+static void actpwr_trig_deactivate(struct led_classdev *led_cdev);
+
+static const struct actpwr_trig_src actpwr_trig_sources[TRIG_COUNT] = {
+	[TRIG_ACT] = { "mmc0", 500, true },
+	[TRIG_PWR] = { "default-on", 500, false },
+};
+
+static struct actpwr_trig_data actpwr_data = {
+	{
+		.name     = "actpwr",
+		.activate = actpwr_trig_activate,
+		.deactivate = actpwr_trig_deactivate,
+	}
+};
+
+static void actpwr_brightness_set(struct led_classdev *led_cdev,
+				  enum led_brightness value)
+{
+	struct actpwr_vled *vled = container_of(led_cdev, struct actpwr_vled,
+					       cdev);
+	struct actpwr_trig_data *trig = vled->parent;
+
+	if (vled->invert)
+		value = !value;
+	vled->value = value;
+
+	if (vled == trig->active)
+		led_trigger_event(&trig->trig, value);
+}
+
+static int actpwr_brightness_set_blocking(struct led_classdev *led_cdev,
+					  enum led_brightness value)
+{
+	actpwr_brightness_set(led_cdev, value);
+	return 0;
+}
+
+static enum led_brightness actpwr_brightness_get(struct led_classdev *led_cdev)
+{
+	struct actpwr_vled *vled = container_of(led_cdev, struct actpwr_vled,
+					      cdev);
+
+	return vled->value;
+}
+
+static void actpwr_trig_cycle(struct timer_list *t)
+{
+	struct actpwr_trig_data *trig  = &actpwr_data;
+	struct actpwr_vled *active;
+
+	active = &trig->virt_leds[trig->next_active];
+	trig->active = active;
+	trig->next_active = (trig->next_active + 1) % TRIG_COUNT;
+
+	led_trigger_event(&trig->trig, active->value);
+
+	mod_timer(&trig->timer, jiffies + msecs_to_jiffies(active->interval));
+}
+
+static int actpwr_trig_activate(struct led_classdev *led_cdev)
+{
+	struct actpwr_trig_data *trig  = &actpwr_data;
+
+	/* Start the timer if this is the first LED */
+	if (!trig->active)
+		actpwr_trig_cycle(&trig->timer);
+	else
+		led_set_brightness_nosleep(led_cdev, trig->active->value);
+
+	return 0;
+}
+
+static void actpwr_trig_deactivate(struct led_classdev *led_cdev)
+{
+	struct actpwr_trig_data *trig  = &actpwr_data;
+
+	if (list_empty(&trig->trig.led_cdevs)) {
+		del_timer_sync(&trig->timer);
+		trig->active = NULL;
+	}
+}
+
+static int __init actpwr_trig_init(void)
+{
+	struct actpwr_trig_data *trig  = &actpwr_data;
+	int ret = 0;
+	int i;
+
+	timer_setup(&trig->timer, actpwr_trig_cycle, 0);
+
+	/* Register one "LED" for each source trigger */
+	for (i = 0; i < TRIG_COUNT; i++)
+	{
+		struct actpwr_vled *vled = &trig->virt_leds[i];
+		struct led_classdev *cdev = &vled->cdev;
+		const struct actpwr_trig_src *src = &actpwr_trig_sources[i];
+
+		vled->parent = trig;
+		vled->interval = src->interval;
+		vled->invert = src->invert;
+		cdev->name = src->name;
+		cdev->brightness_set = actpwr_brightness_set;
+		cdev->brightness_set_blocking = actpwr_brightness_set_blocking;
+		cdev->brightness_get = actpwr_brightness_get;
+		cdev->default_trigger = src->name;
+		ret = led_classdev_register(NULL, cdev);
+		if (ret)
+			goto error_classdev;
+	}
+
+	ret = led_trigger_register(&trig->trig);
+	if (ret)
+		goto error_classdev;
+
+	return 0;
+
+error_classdev:
+	while (i > 0)
+	{
+		i--;
+		led_classdev_unregister(&trig->virt_leds[i].cdev);
+	}
+
+	return ret;
+}
+
+static void __exit actpwr_trig_exit(void)
+{
+	int i;
+
+	led_trigger_unregister(&actpwr_data.trig);
+	for (i = 0; i < TRIG_COUNT; i++)
+	{
+		led_classdev_unregister(&actpwr_data.virt_leds[i].cdev);
+	}
+}
+
+module_init(actpwr_trig_init);
+module_exit(actpwr_trig_exit);
+
+MODULE_AUTHOR("Phil Elwell <phil@raspberrypi.com>");
+MODULE_DESCRIPTION("ACT/PWR LED trigger");
+MODULE_LICENSE("GPL v2");
diff -Nur --no-dereference linux-5.10.52-orig/drivers/leds/trigger/ledtrig-input.c linux-5.10.52-v7l+/drivers/leds/trigger/ledtrig-input.c
--- linux-5.10.52-orig/drivers/leds/trigger/ledtrig-input.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/leds/trigger/ledtrig-input.c	2021-07-25 16:46:04.128321695 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/*
+ * Set LED GPIO to Input "Trigger"
+ *
+ * Copyright 2015 Phil Elwell <phil@raspberrypi.org>
+ *
+ * Based on Nick Forbes's ledtrig-default-on.c.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/leds.h>
+#include <linux/gpio.h>
+#include "../leds.h"
+
+static int input_trig_activate(struct led_classdev *led_cdev)
+{
+	led_cdev->flags |= SET_GPIO_INPUT;
+	led_set_brightness(led_cdev, 0);
+	return 0;
+}
+
+static void input_trig_deactivate(struct led_classdev *led_cdev)
+{
+	led_cdev->flags |= SET_GPIO_OUTPUT;
+	led_set_brightness(led_cdev, 0);
+}
+
+static struct led_trigger input_led_trigger = {
+	.name     = "input",
+	.activate = input_trig_activate,
+	.deactivate = input_trig_deactivate,
+};
+
+static int __init input_trig_init(void)
+{
+	return led_trigger_register(&input_led_trigger);
+}
+
+static void __exit input_trig_exit(void)
+{
+	led_trigger_unregister(&input_led_trigger);
+}
+
+module_init(input_trig_init);
+module_exit(input_trig_exit);
+
+MODULE_AUTHOR("Phil Elwell <phil@raspberrypi.org>");
+MODULE_DESCRIPTION("Set LED GPIO to Input \"trigger\"");
+MODULE_LICENSE("GPL");
diff -Nur --no-dereference linux-5.10.52-orig/drivers/leds/trigger/Makefile linux-5.10.52-v7l+/drivers/leds/trigger/Makefile
--- linux-5.10.52-orig/drivers/leds/trigger/Makefile	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/leds/trigger/Makefile	2021-07-25 16:46:04.118321863 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:14 @
 obj-$(CONFIG_LEDS_TRIGGER_DEFAULT_ON)	+= ledtrig-default-on.o
 obj-$(CONFIG_LEDS_TRIGGER_TRANSIENT)	+= ledtrig-transient.o
 obj-$(CONFIG_LEDS_TRIGGER_CAMERA)	+= ledtrig-camera.o
+obj-$(CONFIG_LEDS_TRIGGER_INPUT)	+= ledtrig-input.o
 obj-$(CONFIG_LEDS_TRIGGER_PANIC)	+= ledtrig-panic.o
 obj-$(CONFIG_LEDS_TRIGGER_NETDEV)	+= ledtrig-netdev.o
 obj-$(CONFIG_LEDS_TRIGGER_PATTERN)	+= ledtrig-pattern.o
 obj-$(CONFIG_LEDS_TRIGGER_AUDIO)	+= ledtrig-audio.o
+obj-$(CONFIG_LEDS_TRIGGER_ACTPWR)	+= ledtrig-actpwr.o
diff -Nur --no-dereference linux-5.10.52-orig/drivers/mailbox/bcm2835-mailbox.c linux-5.10.52-v7l+/drivers/mailbox/bcm2835-mailbox.c
--- linux-5.10.52-orig/drivers/mailbox/bcm2835-mailbox.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/mailbox/bcm2835-mailbox.c	2021-07-25 16:46:04.218320186 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:48 @
 #define MAIL1_WRT	(ARM_0_MAIL1 + 0x00)
 #define MAIL1_STA	(ARM_0_MAIL1 + 0x18)
 
+/* On ARCH_BCM270x these come through <linux/interrupt.h> (arm_control.h ) */
+#ifndef ARM_MS_FULL
 /* Status register: FIFO state. */
 #define ARM_MS_FULL		BIT(31)
 #define ARM_MS_EMPTY		BIT(30)
 
 /* Configuration register: Enable interrupts. */
 #define ARM_MC_IHAVEDATAIRQEN	BIT(0)
+#endif
 
 struct bcm2835_mbox {
 	void __iomem *regs;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:151 @
 		return -ENOMEM;
 	spin_lock_init(&mbox->lock);
 
-	ret = devm_request_irq(dev, irq_of_parse_and_map(dev->of_node, 0),
+	ret = devm_request_irq(dev, platform_get_irq(pdev, 0),
 			       bcm2835_mbox_irq, 0, dev_name(dev), mbox);
 	if (ret) {
 		dev_err(dev, "Failed to register a mailbox IRQ handler: %d\n",
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:201 @
 	},
 	.probe		= bcm2835_mbox_probe,
 };
-module_platform_driver(bcm2835_mbox_driver);
+
+static int __init bcm2835_mbox_init(void)
+{
+	return platform_driver_register(&bcm2835_mbox_driver);
+}
+arch_initcall(bcm2835_mbox_init);
+
+static void __init bcm2835_mbox_exit(void)
+{
+	platform_driver_unregister(&bcm2835_mbox_driver);
+}
+module_exit(bcm2835_mbox_exit);
 
 MODULE_AUTHOR("Lubomir Rintel <lkundrak@v3.sk>");
 MODULE_DESCRIPTION("BCM2835 mailbox IPC driver");
diff -Nur --no-dereference linux-5.10.52-orig/drivers/mailbox/mailbox.c linux-5.10.52-v7l+/drivers/mailbox/mailbox.c
--- linux-5.10.52-orig/drivers/mailbox/mailbox.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/mailbox/mailbox.c	2021-07-25 16:46:04.238319851 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:85 @
 exit:
 	spin_unlock_irqrestore(&chan->lock, flags);
 
-	/* kick start the timer immediately to avoid delays */
-	if (!err && (chan->txdone_method & TXDONE_BY_POLL)) {
-		/* but only if not already active */
-		if (!hrtimer_active(&chan->mbox->poll_hrt))
-			hrtimer_start(&chan->mbox->poll_hrt, 0, HRTIMER_MODE_REL);
-	}
+	if (!err && (chan->txdone_method & TXDONE_BY_POLL))
+		/* kick start the timer immediately to avoid delays */
+		hrtimer_start(&chan->mbox->poll_hrt, 0, HRTIMER_MODE_REL);
 }
 
 static void tx_tick(struct mbox_chan *chan, int r)
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:125 @
 		struct mbox_chan *chan = &mbox->chans[i];
 
 		if (chan->active_req && chan->cl) {
-			resched = true;
 			txdone = chan->mbox->ops->last_tx_done(chan);
 			if (txdone)
 				tx_tick(chan, 0);
+			else
+				resched = true;
 		}
 	}
 
diff -Nur --no-dereference linux-5.10.52-orig/drivers/media/common/videobuf2/videobuf2-core.c linux-5.10.52-v7l+/drivers/media/common/videobuf2/videobuf2-core.c
--- linux-5.10.52-orig/drivers/media/common/videobuf2/videobuf2-core.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/media/common/videobuf2/videobuf2-core.c	2021-07-25 16:46:04.798310463 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:2132 @
 	return -EINVAL;
 }
 
-int vb2_core_expbuf(struct vb2_queue *q, int *fd, unsigned int type,
-		unsigned int index, unsigned int plane, unsigned int flags)
+int vb2_core_expbuf_dmabuf(struct vb2_queue *q, unsigned int type,
+			   unsigned int index, unsigned int plane,
+			   unsigned int flags, struct dma_buf **dmabuf)
 {
 	struct vb2_buffer *vb = NULL;
 	struct vb2_plane *vb_plane;
-	int ret;
 	struct dma_buf *dbuf;
 
 	if (q->memory != VB2_MEMORY_MMAP) {
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:2187 @
 		return -EINVAL;
 	}
 
+	*dmabuf = dbuf;
+	return 0;
+}
+EXPORT_SYMBOL_GPL(vb2_core_expbuf_dmabuf);
+
+int vb2_core_expbuf(struct vb2_queue *q, int *fd, unsigned int type,
+		    unsigned int index, unsigned int plane, unsigned int flags)
+{
+	struct dma_buf *dbuf;
+	int ret;
+
+	ret = vb2_core_expbuf_dmabuf(q, type, index, plane, flags, &dbuf);
+	if (ret)
+		return ret;
+
 	ret = dma_buf_fd(dbuf, flags & ~O_ACCMODE);
 	if (ret < 0) {
 		dprintk(q, 3, "buffer %d, plane %d failed to export (%d)\n",
diff -Nur --no-dereference linux-5.10.52-orig/drivers/media/i2c/adv7180.c linux-5.10.52-v7l+/drivers/media/i2c/adv7180.c
--- linux-5.10.52-orig/drivers/media/i2c/adv7180.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/media/i2c/adv7180.c	2021-07-25 16:46:05.648296212 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1251 @
 		BIT(ADV7182_INPUT_SVIDEO_AIN1_AIN2) |
 		BIT(ADV7182_INPUT_SVIDEO_AIN3_AIN4) |
 		BIT(ADV7182_INPUT_SVIDEO_AIN7_AIN8) |
+		BIT(ADV7182_INPUT_YPRPB_AIN1_AIN2_AIN3) |
 		BIT(ADV7182_INPUT_DIFF_CVBS_AIN1_AIN2) |
 		BIT(ADV7182_INPUT_DIFF_CVBS_AIN3_AIN4) |
 		BIT(ADV7182_INPUT_DIFF_CVBS_AIN7_AIN8),
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1263 @
 static int init_device(struct adv7180_state *state)
 {
 	int ret;
+	int i;
 
 	mutex_lock(&state->mutex);
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1310 @
 			goto out_unlock;
 	}
 
+	/* Select first valid input */
+	for (i = 0; i < 32; i++) {
+		if (BIT(i) & state->chip_info->valid_input_mask) {
+			ret = state->chip_info->select_input(state, i);
+
+			if (ret == 0) {
+				state->input = i;
+				break;
+			}
+		}
+	}
+
 out_unlock:
 	mutex_unlock(&state->mutex);
 
diff -Nur --no-dereference linux-5.10.52-orig/drivers/media/i2c/imx219.c linux-5.10.52-v7l+/drivers/media/i2c/imx219.c
--- linux-5.10.52-orig/drivers/media/i2c/imx219.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/media/i2c/imx219.c	2021-07-25 16:46:05.698295374 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:121 @
 #define IMX219_PIXEL_ARRAY_WIDTH	3280U
 #define IMX219_PIXEL_ARRAY_HEIGHT	2464U
 
+/* Embedded metadata stream structure */
+#define IMX219_EMBEDDED_LINE_WIDTH 16384
+#define IMX219_NUM_EMBEDDED_LINES 1
+
+enum pad_types {
+	IMAGE_PAD,
+	METADATA_PAD,
+	NUM_PADS
+};
+
 struct imx219_reg {
 	u16 address;
 	u8 val;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:549 @
 
 struct imx219 {
 	struct v4l2_subdev sd;
-	struct media_pad pad;
+	struct media_pad pad[NUM_PADS];
 
 	struct v4l2_mbus_framefmt fmt;
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:698 @
 static int imx219_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
 {
 	struct imx219 *imx219 = to_imx219(sd);
-	struct v4l2_mbus_framefmt *try_fmt =
-		v4l2_subdev_get_try_format(sd, fh->pad, 0);
+	struct v4l2_mbus_framefmt *try_fmt_img =
+		v4l2_subdev_get_try_format(sd, fh->pad, IMAGE_PAD);
+	struct v4l2_mbus_framefmt *try_fmt_meta =
+		v4l2_subdev_get_try_format(sd, fh->pad, METADATA_PAD);
 	struct v4l2_rect *try_crop;
 
 	mutex_lock(&imx219->mutex);
 
-	/* Initialize try_fmt */
-	try_fmt->width = supported_modes[0].width;
-	try_fmt->height = supported_modes[0].height;
-	try_fmt->code = imx219_get_format_code(imx219,
-					       MEDIA_BUS_FMT_SRGGB10_1X10);
-	try_fmt->field = V4L2_FIELD_NONE;
+	/* Initialize try_fmt for the image pad */
+	try_fmt_img->width = supported_modes[0].width;
+	try_fmt_img->height = supported_modes[0].height;
+	try_fmt_img->code = imx219_get_format_code(imx219,
+						   MEDIA_BUS_FMT_SRGGB10_1X10);
+	try_fmt_img->field = V4L2_FIELD_NONE;
+
+	/* Initialize try_fmt for the embedded metadata pad */
+	try_fmt_meta->width = IMX219_EMBEDDED_LINE_WIDTH;
+	try_fmt_meta->height = IMX219_NUM_EMBEDDED_LINES;
+	try_fmt_meta->code = MEDIA_BUS_FMT_SENSOR_DATA;
+	try_fmt_meta->field = V4L2_FIELD_NONE;
 
 	/* Initialize try_crop rectangle. */
 	try_crop = v4l2_subdev_get_try_crop(sd, fh->pad, 0);
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:826 @
 {
 	struct imx219 *imx219 = to_imx219(sd);
 
-	if (code->index >= (ARRAY_SIZE(codes) / 4))
+	if (code->pad >= NUM_PADS)
 		return -EINVAL;
 
-	code->code = imx219_get_format_code(imx219, codes[code->index * 4]);
+	if (code->pad == IMAGE_PAD) {
+		if (code->index >= (ARRAY_SIZE(codes) / 4))
+			return -EINVAL;
+
+		code->code = imx219_get_format_code(imx219,
+						    codes[code->index * 4]);
+	} else {
+		if (code->index > 0)
+			return -EINVAL;
+
+		code->code = MEDIA_BUS_FMT_SENSOR_DATA;
+	}
 
 	return 0;
 }
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:851 @
 {
 	struct imx219 *imx219 = to_imx219(sd);
 
-	if (fse->index >= ARRAY_SIZE(supported_modes))
+	if (fse->pad >= NUM_PADS)
 		return -EINVAL;
 
-	if (fse->code != imx219_get_format_code(imx219, fse->code))
-		return -EINVAL;
+	if (fse->pad == IMAGE_PAD) {
+		if (fse->index >= ARRAY_SIZE(supported_modes))
+			return -EINVAL;
+
+		if (fse->code != imx219_get_format_code(imx219, fse->code))
+			return -EINVAL;
+
+		fse->min_width = supported_modes[fse->index].width;
+		fse->max_width = fse->min_width;
+		fse->min_height = supported_modes[fse->index].height;
+		fse->max_height = fse->min_height;
+	} else {
+		if (fse->code != MEDIA_BUS_FMT_SENSOR_DATA || fse->index > 0)
+			return -EINVAL;
 
-	fse->min_width = supported_modes[fse->index].width;
-	fse->max_width = fse->min_width;
-	fse->min_height = supported_modes[fse->index].height;
-	fse->max_height = fse->min_height;
+		fse->min_width = IMX219_EMBEDDED_LINE_WIDTH;
+		fse->max_width = fse->min_width;
+		fse->min_height = IMX219_NUM_EMBEDDED_LINES;
+		fse->max_height = fse->min_height;
+	}
 
 	return 0;
 }
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:888 @
 	fmt->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(fmt->colorspace);
 }
 
-static void imx219_update_pad_format(struct imx219 *imx219,
-				     const struct imx219_mode *mode,
-				     struct v4l2_subdev_format *fmt)
+static void imx219_update_image_pad_format(struct imx219 *imx219,
+					   const struct imx219_mode *mode,
+					   struct v4l2_subdev_format *fmt)
 {
 	fmt->format.width = mode->width;
 	fmt->format.height = mode->height;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:898 @
 	imx219_reset_colorspace(&fmt->format);
 }
 
+static void imx219_update_metadata_pad_format(struct v4l2_subdev_format *fmt)
+{
+	fmt->format.width = IMX219_EMBEDDED_LINE_WIDTH;
+	fmt->format.height = IMX219_NUM_EMBEDDED_LINES;
+	fmt->format.code = MEDIA_BUS_FMT_SENSOR_DATA;
+	fmt->format.field = V4L2_FIELD_NONE;
+}
+
 static int __imx219_get_pad_format(struct imx219 *imx219,
 				   struct v4l2_subdev_pad_config *cfg,
 				   struct v4l2_subdev_format *fmt)
 {
+	if (fmt->pad >= NUM_PADS)
+		return -EINVAL;
+
 	if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
 		struct v4l2_mbus_framefmt *try_fmt =
 			v4l2_subdev_get_try_format(&imx219->sd, cfg, fmt->pad);
 		/* update the code which could change due to vflip or hflip: */
-		try_fmt->code = imx219_get_format_code(imx219, try_fmt->code);
+		try_fmt->code = fmt->pad == IMAGE_PAD ?
+				imx219_get_format_code(imx219, try_fmt->code) :
+				MEDIA_BUS_FMT_SENSOR_DATA;
 		fmt->format = *try_fmt;
 	} else {
-		imx219_update_pad_format(imx219, imx219->mode, fmt);
-		fmt->format.code = imx219_get_format_code(imx219,
-							  imx219->fmt.code);
+		if (fmt->pad == IMAGE_PAD) {
+			imx219_update_image_pad_format(imx219, imx219->mode,
+						       fmt);
+			fmt->format.code = imx219_get_format_code(imx219,
+							      imx219->fmt.code);
+		} else {
+			imx219_update_metadata_pad_format(fmt);
+		}
 	}
 
 	return 0;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:959 @
 	int exposure_max, exposure_def, hblank;
 	unsigned int i;
 
-	mutex_lock(&imx219->mutex);
-
-	for (i = 0; i < ARRAY_SIZE(codes); i++)
-		if (codes[i] == fmt->format.code)
-			break;
-	if (i >= ARRAY_SIZE(codes))
-		i = 0;
+	if (fmt->pad >= NUM_PADS)
+		return -EINVAL;
 
-	/* Bayer order varies with flips */
-	fmt->format.code = imx219_get_format_code(imx219, codes[i]);
+	mutex_lock(&imx219->mutex);
 
-	mode = v4l2_find_nearest_size(supported_modes,
-				      ARRAY_SIZE(supported_modes),
-				      width, height,
-				      fmt->format.width, fmt->format.height);
-	imx219_update_pad_format(imx219, mode, fmt);
-	if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
-		framefmt = v4l2_subdev_get_try_format(sd, cfg, fmt->pad);
-		*framefmt = fmt->format;
-	} else if (imx219->mode != mode ||
-		   imx219->fmt.code != fmt->format.code) {
-		imx219->fmt = fmt->format;
-		imx219->mode = mode;
-		/* Update limits and set FPS to default */
-		__v4l2_ctrl_modify_range(imx219->vblank, IMX219_VBLANK_MIN,
-					 IMX219_VTS_MAX - mode->height, 1,
-					 mode->vts_def - mode->height);
-		__v4l2_ctrl_s_ctrl(imx219->vblank,
-				   mode->vts_def - mode->height);
-		/* Update max exposure while meeting expected vblanking */
-		exposure_max = mode->vts_def - 4;
-		exposure_def = (exposure_max < IMX219_EXPOSURE_DEFAULT) ?
-			exposure_max : IMX219_EXPOSURE_DEFAULT;
-		__v4l2_ctrl_modify_range(imx219->exposure,
-					 imx219->exposure->minimum,
-					 exposure_max, imx219->exposure->step,
-					 exposure_def);
-		/*
-		 * Currently PPL is fixed to IMX219_PPL_DEFAULT, so hblank
-		 * depends on mode->width only, and is not changeble in any
-		 * way other than changing the mode.
-		 */
-		hblank = IMX219_PPL_DEFAULT - mode->width;
-		__v4l2_ctrl_modify_range(imx219->hblank, hblank, hblank, 1,
-					 hblank);
+	if (fmt->pad == IMAGE_PAD) {
+		for (i = 0; i < ARRAY_SIZE(codes); i++)
+			if (codes[i] == fmt->format.code)
+				break;
+		if (i >= ARRAY_SIZE(codes))
+			i = 0;
+
+		/* Bayer order varies with flips */
+		fmt->format.code = imx219_get_format_code(imx219, codes[i]);
+
+		mode = v4l2_find_nearest_size(supported_modes,
+					      ARRAY_SIZE(supported_modes),
+					      width, height,
+					      fmt->format.width,
+					      fmt->format.height);
+		imx219_update_image_pad_format(imx219, mode, fmt);
+		if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
+			framefmt = v4l2_subdev_get_try_format(sd, cfg,
+							      fmt->pad);
+			*framefmt = fmt->format;
+		} else if (imx219->mode != mode ||
+			imx219->fmt.code != fmt->format.code) {
+			imx219->fmt = fmt->format;
+			imx219->mode = mode;
+			/* Update limits and set FPS to default */
+			__v4l2_ctrl_modify_range(imx219->vblank,
+						 IMX219_VBLANK_MIN,
+						 IMX219_VTS_MAX - mode->height,
+						 1,
+						 mode->vts_def - mode->height);
+			__v4l2_ctrl_s_ctrl(imx219->vblank,
+					   mode->vts_def - mode->height);
+			/*
+			 * Update max exposure while meeting
+			 * expected vblanking
+			 */
+			exposure_max = mode->vts_def - 4;
+			exposure_def =
+				(exposure_max < IMX219_EXPOSURE_DEFAULT) ?
+					exposure_max : IMX219_EXPOSURE_DEFAULT;
+			__v4l2_ctrl_modify_range(imx219->exposure,
+						 imx219->exposure->minimum,
+						 exposure_max,
+						 imx219->exposure->step,
+						 exposure_def);
+			/*
+			 * Currently PPL is fixed to IMX219_PPL_DEFAULT, so
+			 * hblank depends on mode->width only, and is not
+			 * changeble in any way other than changing the mode.
+			 */
+			hblank = IMX219_PPL_DEFAULT - mode->width;
+			__v4l2_ctrl_modify_range(imx219->hblank, hblank, hblank,
+						 1, hblank);
+		}
+	} else {
+		if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
+			framefmt = v4l2_subdev_get_try_format(sd, cfg,
+							      fmt->pad);
+			*framefmt = fmt->format;
+		} else {
+			/* Only one embedded data mode is supported */
+			imx219_update_metadata_pad_format(fmt);
+		}
 	}
 
 	mutex_unlock(&imx219->mutex);
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1594 @
 
 	/* Initialize subdev */
 	imx219->sd.internal_ops = &imx219_internal_ops;
-	imx219->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+	imx219->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE |
+			    V4L2_SUBDEV_FL_HAS_EVENTS;
 	imx219->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
 
-	/* Initialize source pad */
-	imx219->pad.flags = MEDIA_PAD_FL_SOURCE;
+	/* Initialize source pads */
+	imx219->pad[IMAGE_PAD].flags = MEDIA_PAD_FL_SOURCE;
+	imx219->pad[METADATA_PAD].flags = MEDIA_PAD_FL_SOURCE;
 
 	/* Initialize default format */
 	imx219_set_default_format(imx219);
 
-	ret = media_entity_pads_init(&imx219->sd.entity, 1, &imx219->pad);
+	ret = media_entity_pads_init(&imx219->sd.entity, NUM_PADS, imx219->pad);
 	if (ret) {
 		dev_err(dev, "failed to init entity pads: %d\n", ret);
 		goto error_handler_free;
diff -Nur --no-dereference linux-5.10.52-orig/drivers/media/i2c/imx290.c linux-5.10.52-v7l+/drivers/media/i2c/imx290.c
--- linux-5.10.52-orig/drivers/media/i2c/imx290.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/media/i2c/imx290.c	2021-07-25 16:46:05.698295374 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
 // SPDX-License-Identifier: GPL-2.0
 /*
- * Sony IMX290 CMOS Image Sensor Driver
+ * Sony IMX290 & IMX327 CMOS Image Sensor Driver
+ *
+ * The IMX290 and IMX327 are very similar 1920x1080 1/2.8 CMOS image sensors.
+ * IMX327 can support up to 60fps, whilst IMX290 can support up to 120fps, but
+ * only 10bit and when connected over 4 CSI-2 lanes.
+ * The modules don't appear to have a mechanism to identify whether the mono or
+ * colour variant is connected, therefore it is done via compatible string.
  *
  * Copyright (C) 2019 FRAMOS GmbH.
  *
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:22 @
 #include <linux/gpio/consumer.h>
 #include <linux/i2c.h>
 #include <linux/module.h>
+#include <linux/of_device.h>
 #include <linux/pm_runtime.h>
 #include <linux/regmap.h>
 #include <linux/regulator/consumer.h>
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:32 @
 #include <media/v4l2-fwnode.h>
 #include <media/v4l2-subdev.h>
 
+enum imx290_clk_index {
+	CLK_37_125,
+	CLK_74_25,
+};
+
 #define IMX290_STANDBY 0x3000
 #define IMX290_REGHOLD 0x3001
 #define IMX290_XMSTA 0x3002
+#define IMX290_FLIP_WINMODE 0x3007
 #define IMX290_FR_FDG_SEL 0x3009
 #define IMX290_BLKLEVEL_LOW 0x300a
 #define IMX290_BLKLEVEL_HIGH 0x300b
 #define IMX290_GAIN 0x3014
+#define IMX290_VMAX_LOW 0x3018
+#define IMX290_VMAX_MAX 0x3fff
 #define IMX290_HMAX_LOW 0x301c
 #define IMX290_HMAX_HIGH 0x301d
+#define IMX290_HMAX_MIN 2200 /* Min of 2200 pixels = 60fps */
+#define IMX290_HMAX_MAX 0xffff
+
+#define IMX290_EXPOSURE_MIN 1
+#define IMX290_EXPOSURE_STEP 1
+#define IMX290_EXPOSURE_LOW 0x3020
 #define IMX290_PGCTRL 0x308c
 #define IMX290_PHY_LANE_NUM 0x3407
 #define IMX290_CSI_LANE_MODE 0x3443
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:63 @
 #define IMX290_PGCTRL_THRU BIT(1)
 #define IMX290_PGCTRL_MODE(n) ((n) << 4)
 
+#define IMX290_NATIVE_WIDTH		1945U
+#define IMX290_NATIVE_HEIGHT		1109U
+#define IMX290_PIXEL_ARRAY_LEFT		4U
+#define IMX290_PIXEL_ARRAY_TOP		12U
+#define IMX290_PIXEL_ARRAY_WIDTH	1937U
+#define IMX290_PIXEL_ARRAY_HEIGHT	1097U
+
 static const char * const imx290_supply_name[] = {
 	"vdda",
 	"vddd",
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:87 @
 	u32 width;
 	u32 height;
 	u32 hmax;
+	u32 vmax;
 	u8 link_freq_index;
+	struct v4l2_rect crop;
+
+	const struct imx290_regval *mode_data;
+	u32 mode_data_size;
+	const struct imx290_regval *lane_data;
+	u32 lane_data_size;
+
 
-	const struct imx290_regval *data;
-	u32 data_size;
+	/* Clock setup can vary. Index as enum imx290_clk_index */
+	const struct imx290_regval *clk_data[2];
+	u32 clk_size;
 };
 
 struct imx290 {
 	struct device *dev;
 	struct clk *xclk;
+	u32 xclk_freq;
 	struct regmap *regmap;
 	u8 nlanes;
 	u8 bpp;
+	u16 hmax_min;
+
+	const struct imx290_pixfmt *formats;
 
 	struct v4l2_subdev sd;
 	struct media_pad pad;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:124 @
 	struct v4l2_ctrl_handler ctrls;
 	struct v4l2_ctrl *link_freq;
 	struct v4l2_ctrl *pixel_rate;
+	struct v4l2_ctrl *hblank;
+	struct v4l2_ctrl *vblank;
+	struct v4l2_ctrl *hflip;
+	struct v4l2_ctrl *vflip;
+	struct v4l2_ctrl *exposure;
 
 	struct mutex lock;
 };
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:138 @
 	u8 bpp;
 };
 
-static const struct imx290_pixfmt imx290_formats[] = {
+#define IMX290_NUM_FORMATS 2
+
+static const struct imx290_pixfmt imx290_colour_formats[IMX290_NUM_FORMATS] = {
 	{ MEDIA_BUS_FMT_SRGGB10_1X10, 10 },
 	{ MEDIA_BUS_FMT_SRGGB12_1X12, 12 },
 };
 
+static const struct imx290_pixfmt imx290_mono_formats[IMX290_NUM_FORMATS] = {
+	{ MEDIA_BUS_FMT_Y10_1X10, 10 },
+	{ MEDIA_BUS_FMT_Y12_1X12, 12 },
+};
+
 static const struct regmap_config imx290_regmap_config = {
 	.reg_bits = 16,
 	.val_bits = 8,
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:169 @
 
 static const struct imx290_regval imx290_global_init_settings[] = {
 	{ 0x3007, 0x00 },
-	{ 0x3018, 0x65 },
-	{ 0x3019, 0x04 },
 	{ 0x301a, 0x00 },
-	{ 0x3444, 0x20 },
-	{ 0x3445, 0x25 },
 	{ 0x303a, 0x0c },
 	{ 0x3040, 0x00 },
 	{ 0x3041, 0x00 },
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:223 @
 	{ 0x33b3, 0x04 },
 };
 
-static const struct imx290_regval imx290_1080p_settings[] = {
+static const struct imx290_regval imx290_37_125mhz_clock_1080p[] = {
+	{ 0x305c, 0x18 },
+	{ 0x305d, 0x03 },
+	{ 0x305e, 0x20 },
+	{ 0x305f, 0x01 },
+	{ 0x315e, 0x1a },
+	{ 0x3164, 0x1a },
+	{ 0x3444, 0x20 },
+	{ 0x3445, 0x25 },
+	{ 0x3480, 0x49 },
+};
+
+static const struct imx290_regval imx290_74_250mhz_clock_1080p[] = {
+	{ 0x305c, 0x0c },
+	{ 0x305d, 0x03 },
+	{ 0x305e, 0x10 },
+	{ 0x305f, 0x01 },
+	{ 0x315e, 0x1b },
+	{ 0x3164, 0x1b },
+	{ 0x3444, 0x40 },
+	{ 0x3445, 0x4a },
+	{ 0x3480, 0x92 },
+};
+
+static const struct imx290_regval imx290_1080p_common_settings[] = {
 	/* mode settings */
+	{ IMX290_FR_FDG_SEL, 0x01 },
 	{ 0x3007, 0x00 },
 	{ 0x303a, 0x0c },
 	{ 0x3414, 0x0a },
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:259 @
 	{ 0x3419, 0x04 },
 	{ 0x3012, 0x64 },
 	{ 0x3013, 0x00 },
-	{ 0x305c, 0x18 },
-	{ 0x305d, 0x03 },
-	{ 0x305e, 0x20 },
-	{ 0x305f, 0x01 },
-	{ 0x315e, 0x1a },
-	{ 0x3164, 0x1a },
-	{ 0x3480, 0x49 },
+};
+
+static const struct imx290_regval imx290_1080p_2lane_settings[] = {
+	{ 0x3405, 0x00 },
 	/* data rate settings */
+	{ IMX290_PHY_LANE_NUM, 0x01 },
+	{ IMX290_CSI_LANE_MODE, 0x01 },
+	{ 0x3446, 0x77 },
+	{ 0x3447, 0x00 },
+	{ 0x3448, 0x67 },
+	{ 0x3449, 0x00 },
+	{ 0x344a, 0x47 },
+	{ 0x344b, 0x00 },
+	{ 0x344c, 0x37 },
+	{ 0x344d, 0x00 },
+	{ 0x344e, 0x3f },
+	{ 0x344f, 0x00 },
+	{ 0x3450, 0xff },
+	{ 0x3451, 0x00 },
+	{ 0x3452, 0x3f },
+	{ 0x3453, 0x00 },
+	{ 0x3454, 0x37 },
+	{ 0x3455, 0x00 },
+};
+
+static const struct imx290_regval imx290_1080p_4lane_settings[] = {
 	{ 0x3405, 0x10 },
+	/* data rate settings */
+	{ IMX290_PHY_LANE_NUM, 0x03 },
+	{ IMX290_CSI_LANE_MODE, 0x03 },
 	{ 0x3446, 0x57 },
 	{ 0x3447, 0x00 },
 	{ 0x3448, 0x37 },
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:307 @
 	{ 0x3455, 0x00 },
 };
 
-static const struct imx290_regval imx290_720p_settings[] = {
+static const struct imx290_regval imx290_37_125mhz_clock_720p[] = {
+	{ 0x305c, 0x20 },
+	{ 0x305d, 0x00 },
+	{ 0x305e, 0x20 },
+	{ 0x305f, 0x01 },
+	{ 0x315e, 0x1a },
+	{ 0x3164, 0x1a },
+	{ 0x3444, 0x20 },
+	{ 0x3445, 0x25 },
+	{ 0x3480, 0x49 },
+};
+
+static const struct imx290_regval imx290_74_250mhz_clock_720p[] = {
+	{ 0x305c, 0x10 },
+	{ 0x305d, 0x00 },
+	{ 0x305e, 0x10 },
+	{ 0x305f, 0x01 },
+	{ 0x315e, 0x1b },
+	{ 0x3164, 0x1b },
+	{ 0x3444, 0x40 },
+	{ 0x3445, 0x4a },
+	{ 0x3480, 0x92 },
+};
+
+static const struct imx290_regval imx290_720p_common_settings[] = {
 	/* mode settings */
+	{ IMX290_FR_FDG_SEL, 0x01 },
 	{ 0x3007, 0x10 },
 	{ 0x303a, 0x06 },
 	{ 0x3414, 0x04 },
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:343 @
 	{ 0x3419, 0x02 },
 	{ 0x3012, 0x64 },
 	{ 0x3013, 0x00 },
-	{ 0x305c, 0x20 },
-	{ 0x305d, 0x00 },
-	{ 0x305e, 0x20 },
-	{ 0x305f, 0x01 },
-	{ 0x315e, 0x1a },
-	{ 0x3164, 0x1a },
-	{ 0x3480, 0x49 },
+};
+
+static const struct imx290_regval imx290_720p_2lane_settings[] = {
+	{ 0x3405, 0x00 },
+	{ IMX290_PHY_LANE_NUM, 0x01 },
+	{ IMX290_CSI_LANE_MODE, 0x01 },
 	/* data rate settings */
+	{ 0x3446, 0x67 },
+	{ 0x3447, 0x00 },
+	{ 0x3448, 0x57 },
+	{ 0x3449, 0x00 },
+	{ 0x344a, 0x2f },
+	{ 0x344b, 0x00 },
+	{ 0x344c, 0x27 },
+	{ 0x344d, 0x00 },
+	{ 0x344e, 0x2f },
+	{ 0x344f, 0x00 },
+	{ 0x3450, 0xbf },
+	{ 0x3451, 0x00 },
+	{ 0x3452, 0x2f },
+	{ 0x3453, 0x00 },
+	{ 0x3454, 0x27 },
+	{ 0x3455, 0x00 },
+};
+
+static const struct imx290_regval imx290_720p_4lane_settings[] = {
 	{ 0x3405, 0x10 },
+	{ IMX290_PHY_LANE_NUM, 0x03 },
+	{ IMX290_CSI_LANE_MODE, 0x03 },
+	/* data rate settings */
 	{ 0x3446, 0x4f },
 	{ 0x3447, 0x00 },
 	{ 0x3448, 0x2f },
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:452 @
 	{
 		.width = 1920,
 		.height = 1080,
-		.hmax = 0x1130,
+		.hmax = 0x0898,
+		.vmax = 0x0465,
 		.link_freq_index = FREQ_INDEX_1080P,
-		.data = imx290_1080p_settings,
-		.data_size = ARRAY_SIZE(imx290_1080p_settings),
+		.crop = {
+			.left = 4 + 8,
+			.top = 12 + 8,
+			.width = 1920,
+			.height = 1080,
+		},
+		.mode_data = imx290_1080p_common_settings,
+		.mode_data_size = ARRAY_SIZE(imx290_1080p_common_settings),
+		.lane_data = imx290_1080p_2lane_settings,
+		.lane_data_size = ARRAY_SIZE(imx290_1080p_2lane_settings),
+		.clk_data = {
+			[CLK_37_125] = imx290_37_125mhz_clock_1080p,
+			[CLK_74_25] = imx290_74_250mhz_clock_1080p,
+		},
+		.clk_size = ARRAY_SIZE(imx290_37_125mhz_clock_1080p),
 	},
 	{
 		.width = 1280,
 		.height = 720,
-		.hmax = 0x19c8,
+		.hmax = 0x0ce4,
+		.vmax = 0x02ee,
 		.link_freq_index = FREQ_INDEX_720P,
-		.data = imx290_720p_settings,
-		.data_size = ARRAY_SIZE(imx290_720p_settings),
+		.crop = {
+			.left = 4 + 8 + 320,
+			.top = 12 + 8 + 180,
+			.width = 1280,
+			.height = 720,
+		},
+		.mode_data = imx290_720p_common_settings,
+		.mode_data_size = ARRAY_SIZE(imx290_720p_common_settings),
+		.lane_data = imx290_720p_2lane_settings,
+		.lane_data_size = ARRAY_SIZE(imx290_720p_2lane_settings),
+		.clk_data = {
+			[CLK_37_125] = imx290_37_125mhz_clock_720p,
+			[CLK_74_25] = imx290_74_250mhz_clock_720p,
+		},
+		.clk_size = ARRAY_SIZE(imx290_37_125mhz_clock_720p),
 	},
 };
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:500 @
 		.width = 1920,
 		.height = 1080,
 		.hmax = 0x0898,
+		.vmax = 0x0465,
 		.link_freq_index = FREQ_INDEX_1080P,
-		.data = imx290_1080p_settings,
-		.data_size = ARRAY_SIZE(imx290_1080p_settings),
+		.crop = {
+			.left = 4 + 8,
+			.top = 12 + 8,
+			.width = 1920,
+			.height = 1080,
+		},
+		.mode_data = imx290_1080p_common_settings,
+		.mode_data_size = ARRAY_SIZE(imx290_1080p_common_settings),
+		.lane_data = imx290_1080p_4lane_settings,
+		.lane_data_size = ARRAY_SIZE(imx290_1080p_4lane_settings),
+		.clk_data = {
+			[CLK_37_125] = imx290_37_125mhz_clock_1080p,
+			[CLK_74_25] = imx290_74_250mhz_clock_1080p,
+		},
+		.clk_size = ARRAY_SIZE(imx290_37_125mhz_clock_1080p),
 	},
 	{
 		.width = 1280,
 		.height = 720,
 		.hmax = 0x0ce4,
+		.vmax = 0x02ee,
 		.link_freq_index = FREQ_INDEX_720P,
-		.data = imx290_720p_settings,
-		.data_size = ARRAY_SIZE(imx290_720p_settings),
+		.crop = {
+			.left = 4 + 8 + 320,
+			.top = 12 + 8 + 180,
+			.width = 1280,
+			.height = 720,
+		},
+		.mode_data = imx290_720p_common_settings,
+		.mode_data_size = ARRAY_SIZE(imx290_720p_common_settings),
+		.lane_data = imx290_720p_4lane_settings,
+		.lane_data_size = ARRAY_SIZE(imx290_720p_4lane_settings),
+		.clk_data = {
+			[CLK_37_125] = imx290_37_125mhz_clock_720p,
+			[CLK_74_25] = imx290_74_250mhz_clock_720p,
+		},
+		.clk_size = ARRAY_SIZE(imx290_37_125mhz_clock_720p),
 	},
 };
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:652 @
 	return ret;
 }
 
+static int imx290_set_exposure(struct imx290 *imx290, u32 value)
+{
+	u32 exposure = (imx290->current_mode->height + imx290->vblank->val) -
+						value - 1;
+	int ret;
+
+	ret = imx290_write_buffered_reg(imx290, IMX290_EXPOSURE_LOW, 3,
+					exposure);
+	if (ret)
+		dev_err(imx290->dev, "Unable to write exposure\n");
+
+	return ret;
+}
+
+static int imx290_set_hmax(struct imx290 *imx290, u32 val)
+{
+	u32 hmax = val + imx290->current_mode->width;
+	int ret;
+
+	ret = imx290_write_buffered_reg(imx290, IMX290_HMAX_LOW, 2,
+					hmax);
+	if (ret)
+		dev_err(imx290->dev, "Error setting HMAX register\n");
+
+	return ret;
+}
+
+static int imx290_set_vmax(struct imx290 *imx290, u32 val)
+{
+	u32 vmax = val + imx290->current_mode->height;
+	int ret;
+
+	ret = imx290_write_buffered_reg(imx290, IMX290_VMAX_LOW, 3,
+					vmax);
+	if (ret)
+		dev_err(imx290->dev, "Unable to write vmax\n");
+
+	/*
+	 * Changing vblank changes the allowed range for exposure.
+	 * We don't supply the current exposure as default here as it
+	 * may lie outside the new range. We will reset it just below.
+	 */
+	__v4l2_ctrl_modify_range(imx290->exposure,
+				 IMX290_EXPOSURE_MIN,
+				 vmax - 2,
+				 IMX290_EXPOSURE_STEP,
+				 vmax - 2);
+
+	/*
+	 * Becuse of the way exposure works for this sensor, updating
+	 * vblank causes the effective exposure to change, so we must
+	 * set it back to the "new" correct value.
+	 */
+	imx290_set_exposure(imx290, imx290->exposure->val);
+
+	return ret;
+}
+
 /* Stop streaming */
 static int imx290_stop_streaming(struct imx290 *imx290)
 {
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:729 @
 	struct imx290 *imx290 = container_of(ctrl->handler,
 					     struct imx290, ctrls);
 	int ret = 0;
+	u8 val;
 
 	/* V4L2 controls values will be applied only when power is already up */
 	if (!pm_runtime_get_if_in_use(imx290->dev))
 		return 0;
 
 	switch (ctrl->id) {
-	case V4L2_CID_GAIN:
+	case V4L2_CID_ANALOGUE_GAIN:
 		ret = imx290_set_gain(imx290, ctrl->val);
 		break;
+	case V4L2_CID_EXPOSURE:
+		ret = imx290_set_exposure(imx290, ctrl->val);
+		break;
+	case V4L2_CID_HBLANK:
+		ret = imx290_set_hmax(imx290, ctrl->val);
+		break;
+	case V4L2_CID_VBLANK:
+		ret = imx290_set_vmax(imx290, ctrl->val);
+		break;
+	case V4L2_CID_HFLIP:
+	case V4L2_CID_VFLIP:
+		/* WINMODE is in bits [6:4], so need to read-modify-write */
+		ret = imx290_read_reg(imx290, IMX290_FLIP_WINMODE, &val);
+		if (ret)
+			break;
+		val &= ~0x03;
+		val |= imx290->vflip->val | (imx290->hflip->val << 1);
+		ret = imx290_write_reg(imx290, IMX290_FLIP_WINMODE, val);
+		break;
 	case V4L2_CID_TEST_PATTERN:
 		if (ctrl->val) {
 			imx290_write_reg(imx290, IMX290_BLKLEVEL_LOW, 0x00);
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:797 @
 				 struct v4l2_subdev_pad_config *cfg,
 				 struct v4l2_subdev_mbus_code_enum *code)
 {
-	if (code->index >= ARRAY_SIZE(imx290_formats))
+	const struct imx290 *imx290 = to_imx290(sd);
+
+	if (code->index >= IMX290_NUM_FORMATS)
 		return -EINVAL;
 
-	code->code = imx290_formats[code->index].code;
+	code->code = imx290->formats[code->index].code;
 
 	return 0;
 }
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:814 @
 	const struct imx290 *imx290 = to_imx290(sd);
 	const struct imx290_mode *imx290_modes = imx290_modes_ptr(imx290);
 
-	if ((fse->code != imx290_formats[0].code) &&
-	    (fse->code != imx290_formats[1].code))
+	if (fse->code != imx290->formats[0].code &&
+	    fse->code != imx290->formats[1].code)
 		return -EINVAL;
 
 	if (fse->index >= imx290_modes_num(imx290))
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:856 @
 	return imx290->current_mode->link_freq_index;
 }
 
-static s64 imx290_get_link_freq(struct imx290 *imx290)
-{
-	u8 index = imx290_get_link_freq_index(imx290);
-
-	return *(imx290_link_freqs_ptr(imx290) + index);
-}
-
 static u64 imx290_calc_pixel_rate(struct imx290 *imx290)
 {
-	s64 link_freq = imx290_get_link_freq(imx290);
-	u8 nlanes = imx290->nlanes;
-	u64 pixel_rate;
-
-	/* pixel rate = link_freq * 2 * nr_of_lanes / bits_per_sample */
-	pixel_rate = link_freq * 2 * nlanes;
-	do_div(pixel_rate, imx290->bpp);
-	return pixel_rate;
+	return 148500000;
 }
 
 static int imx290_set_fmt(struct v4l2_subdev *sd,
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:879 @
 	fmt->format.width = mode->width;
 	fmt->format.height = mode->height;
 
-	for (i = 0; i < ARRAY_SIZE(imx290_formats); i++)
-		if (imx290_formats[i].code == fmt->format.code)
+	for (i = 0; i < IMX290_NUM_FORMATS; i++)
+		if (imx290->formats[i].code == fmt->format.code)
 			break;
 
-	if (i >= ARRAY_SIZE(imx290_formats))
+	if (i >= IMX290_NUM_FORMATS)
 		i = 0;
 
-	fmt->format.code = imx290_formats[i].code;
+	fmt->format.code = imx290->formats[i].code;
 	fmt->format.field = V4L2_FIELD_NONE;
+	fmt->format.colorspace = V4L2_COLORSPACE_SRGB;
+	fmt->format.ycbcr_enc =
+			V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->format.colorspace);
+	fmt->format.quantization =
+		V4L2_MAP_QUANTIZATION_DEFAULT(true, fmt->format.colorspace,
+					      fmt->format.ycbcr_enc);
+	fmt->format.xfer_func =
+		V4L2_MAP_XFER_FUNC_DEFAULT(fmt->format.colorspace);
 
 	if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
 		format = v4l2_subdev_get_try_format(sd, cfg, fmt->pad);
 	} else {
 		format = &imx290->current_format;
 		imx290->current_mode = mode;
-		imx290->bpp = imx290_formats[i].bpp;
+		imx290->bpp = imx290->formats[i].bpp;
 
 		if (imx290->link_freq)
 			__v4l2_ctrl_s_ctrl(imx290->link_freq,
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:910 @
 		if (imx290->pixel_rate)
 			__v4l2_ctrl_s_ctrl_int64(imx290->pixel_rate,
 						 imx290_calc_pixel_rate(imx290));
+
+		if (imx290->hblank) {
+			__v4l2_ctrl_modify_range(imx290->hblank,
+						 imx290->hmax_min - mode->width,
+						 IMX290_HMAX_MAX - mode->width,
+						 1, mode->hmax - mode->width);
+			__v4l2_ctrl_s_ctrl(imx290->hblank,
+					   mode->hmax - mode->width);
+		}
+		if (imx290->vblank) {
+			__v4l2_ctrl_modify_range(imx290->vblank,
+						 mode->vmax - mode->height,
+						 IMX290_VMAX_MAX - mode->height,
+						 1,
+						 mode->vmax - mode->height);
+			__v4l2_ctrl_s_ctrl(imx290->vblank,
+					   mode->vmax - mode->height);
+		}
+		if (imx290->exposure)
+			__v4l2_ctrl_modify_range(imx290->exposure,
+						 IMX290_EXPOSURE_MIN,
+						 mode->vmax - 2,
+						 IMX290_EXPOSURE_STEP,
+						 mode->vmax - 2);
 	}
 
 	*format = fmt->format;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:963 @
 
 	switch (imx290->current_format.code) {
 	case MEDIA_BUS_FMT_SRGGB10_1X10:
+	case MEDIA_BUS_FMT_Y10_1X10:
 		ret = imx290_set_register_array(imx290, imx290_10bit_settings,
 						ARRAY_SIZE(
 							imx290_10bit_settings));
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:973 @
 		}
 		break;
 	case MEDIA_BUS_FMT_SRGGB12_1X12:
+	case MEDIA_BUS_FMT_Y12_1X12:
 		ret = imx290_set_register_array(imx290, imx290_12bit_settings,
 						ARRAY_SIZE(
 							imx290_12bit_settings));
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:990 @
 	return 0;
 }
 
-static int imx290_set_hmax(struct imx290 *imx290, u32 val)
+static const struct v4l2_rect *
+__imx290_get_pad_crop(struct imx290 *imx290, struct v4l2_subdev_pad_config *cfg,
+		      unsigned int pad, enum v4l2_subdev_format_whence which)
 {
-	int ret;
+	switch (which) {
+	case V4L2_SUBDEV_FORMAT_TRY:
+		return v4l2_subdev_get_try_crop(&imx290->sd, cfg, pad);
+	case V4L2_SUBDEV_FORMAT_ACTIVE:
+		return &imx290->current_mode->crop;
+	}
 
-	ret = imx290_write_reg(imx290, IMX290_HMAX_LOW, (val & 0xff));
-	if (ret) {
-		dev_err(imx290->dev, "Error setting HMAX register\n");
-		return ret;
+	return NULL;
+}
+
+static int imx290_get_selection(struct v4l2_subdev *sd,
+				struct v4l2_subdev_pad_config *cfg,
+				struct v4l2_subdev_selection *sel)
+{
+	switch (sel->target) {
+	case V4L2_SEL_TGT_CROP: {
+		struct imx290 *imx290 = to_imx290(sd);
+
+		mutex_lock(&imx290->lock);
+		sel->r = *__imx290_get_pad_crop(imx290, cfg, sel->pad,
+						sel->which);
+		mutex_unlock(&imx290->lock);
+
+		return 0;
 	}
 
-	ret = imx290_write_reg(imx290, IMX290_HMAX_HIGH, ((val >> 8) & 0xff));
-	if (ret) {
-		dev_err(imx290->dev, "Error setting HMAX register\n");
-		return ret;
+	case V4L2_SEL_TGT_NATIVE_SIZE:
+		sel->r.top = 0;
+		sel->r.left = 0;
+		sel->r.width = IMX290_NATIVE_WIDTH;
+		sel->r.height = IMX290_NATIVE_HEIGHT;
+
+		return 0;
+
+	case V4L2_SEL_TGT_CROP_DEFAULT:
+	case V4L2_SEL_TGT_CROP_BOUNDS:
+		sel->r.top = IMX290_PIXEL_ARRAY_TOP;
+		sel->r.left = IMX290_PIXEL_ARRAY_LEFT;
+		sel->r.width = IMX290_PIXEL_ARRAY_WIDTH;
+		sel->r.height = IMX290_PIXEL_ARRAY_HEIGHT;
+
+		return 0;
 	}
 
-	return 0;
+	return -EINVAL;
 }
 
 /* Start streaming */
 static int imx290_start_streaming(struct imx290 *imx290)
 {
+	enum imx290_clk_index clk_idx = imx290->xclk_freq == 37125000 ?
+					CLK_37_125 : CLK_74_25;
 	int ret;
 
 	/* Set init register settings */
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1057 @
 		return ret;
 	}
 
+	ret = imx290_set_register_array(imx290,
+					imx290->current_mode->clk_data[clk_idx],
+					imx290->current_mode->clk_size);
+	if (ret < 0) {
+		dev_err(imx290->dev, "Could not set clock registers\n");
+		return ret;
+	}
+
 	/* Apply the register values related to current frame format */
 	ret = imx290_write_current_format(imx290);
 	if (ret < 0) {
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1073 @
 	}
 
 	/* Apply default values of current mode */
-	ret = imx290_set_register_array(imx290, imx290->current_mode->data,
-					imx290->current_mode->data_size);
+	ret = imx290_set_register_array(imx290,
+					imx290->current_mode->mode_data,
+					imx290->current_mode->mode_data_size);
 	if (ret < 0) {
 		dev_err(imx290->dev, "Could not set current mode\n");
 		return ret;
 	}
-	ret = imx290_set_hmax(imx290, imx290->current_mode->hmax);
-	if (ret < 0)
+
+	/* Apply lane config registers of current mode */
+	ret = imx290_set_register_array(imx290,
+					imx290->current_mode->lane_data,
+					imx290->current_mode->lane_data_size);
+	if (ret < 0) {
+		dev_err(imx290->dev, "Could not set current mode\n");
 		return ret;
+	}
 
 	/* Apply customized values from user */
 	ret = v4l2_ctrl_handler_setup(imx290->sd.ctrl_handler);
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1129 @
 		imx290_stop_streaming(imx290);
 		pm_runtime_put(imx290->dev);
 	}
+	/* vflip and hflip cannot change during streaming */
+	__v4l2_ctrl_grab(imx290->vflip, enable);
+	__v4l2_ctrl_grab(imx290->hflip, enable);
 
 unlock_and_return:
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1149 @
 				       imx290->supplies);
 }
 
-static int imx290_set_data_lanes(struct imx290 *imx290)
-{
-	int ret = 0, laneval, frsel;
-
-	switch (imx290->nlanes) {
-	case 2:
-		laneval = 0x01;
-		frsel = 0x02;
-		break;
-	case 4:
-		laneval = 0x03;
-		frsel = 0x01;
-		break;
-	default:
-		/*
-		 * We should never hit this since the data lane count is
-		 * validated in probe itself
-		 */
-		dev_err(imx290->dev, "Lane configuration not supported\n");
-		ret = -EINVAL;
-		goto exit;
-	}
-
-	ret = imx290_write_reg(imx290, IMX290_PHY_LANE_NUM, laneval);
-	if (ret) {
-		dev_err(imx290->dev, "Error setting Physical Lane number register\n");
-		goto exit;
-	}
-
-	ret = imx290_write_reg(imx290, IMX290_CSI_LANE_MODE, laneval);
-	if (ret) {
-		dev_err(imx290->dev, "Error setting CSI Lane mode register\n");
-		goto exit;
-	}
-
-	ret = imx290_write_reg(imx290, IMX290_FR_FDG_SEL, frsel);
-	if (ret)
-		dev_err(imx290->dev, "Error setting FR/FDG SEL register\n");
-
-exit:
-	return ret;
-}
-
 static int imx290_power_on(struct device *dev)
 {
 	struct i2c_client *client = to_i2c_client(dev);
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1173 @
 	gpiod_set_value_cansleep(imx290->rst_gpio, 0);
 	usleep_range(30000, 31000);
 
-	/* Set data lane count */
-	imx290_set_data_lanes(imx290);
-
 	return 0;
 }
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1203 @
 	.enum_frame_size = imx290_enum_frame_size,
 	.get_fmt = imx290_get_fmt,
 	.set_fmt = imx290_set_fmt,
+	.get_selection = imx290_get_selection,
 };
 
 static const struct v4l2_subdev_ops imx290_subdev_ops = {
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1237 @
 	return 0;
 }
 
+static const struct of_device_id imx290_of_match[] = {
+	{ .compatible = "sony,imx290", .data = imx290_colour_formats },
+	{ .compatible = "sony,imx290-mono", .data = imx290_mono_formats },
+	{ /* sentinel */ }
+};
+
 static int imx290_probe(struct i2c_client *client)
 {
 	struct device *dev = &client->dev;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1251 @
 	struct v4l2_fwnode_endpoint ep = {
 		.bus_type = V4L2_MBUS_CSI2_DPHY
 	};
+	const struct of_device_id *match;
+	const struct imx290_mode *mode;
 	struct imx290 *imx290;
-	u32 xclk_freq;
 	s64 fq;
 	int ret;
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1268 @
 		return -ENODEV;
 	}
 
+	match = of_match_device(imx290_of_match, dev);
+	if (!match)
+		return -ENODEV;
+	imx290->formats = (const struct imx290_pixfmt *)match->data;
+
 	endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(dev), NULL);
 	if (!endpoint) {
 		dev_err(dev, "Endpoint node not found\n");
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1296 @
 		ret = -EINVAL;
 		goto free_err;
 	}
+	imx290->hmax_min = IMX290_HMAX_MIN;
 
 	dev_dbg(dev, "Using %u data lanes\n", imx290->nlanes);
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1323 @
 	}
 
 	ret = fwnode_property_read_u32(dev_fwnode(dev), "clock-frequency",
-				       &xclk_freq);
+				       &imx290->xclk_freq);
 	if (ret) {
 		dev_err(dev, "Could not get xclk frequency\n");
 		goto free_err;
 	}
 
 	/* external clock must be 37.125 MHz */
-	if (xclk_freq != 37125000) {
+	if (imx290->xclk_freq != 37125000 && imx290->xclk_freq != 74250000) {
 		dev_err(dev, "External clock frequency %u is not supported\n",
-			xclk_freq);
+			imx290->xclk_freq);
 		ret = -EINVAL;
 		goto free_err;
 	}
 
-	ret = clk_set_rate(imx290->xclk, xclk_freq);
+	ret = clk_set_rate(imx290->xclk, imx290->xclk_freq);
 	if (ret) {
 		dev_err(dev, "Could not set xclk frequency\n");
 		goto free_err;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1366 @
 	 */
 	imx290_entity_init_cfg(&imx290->sd, NULL);
 
-	v4l2_ctrl_handler_init(&imx290->ctrls, 4);
+	v4l2_ctrl_handler_init(&imx290->ctrls, 9);
 
 	v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops,
-			  V4L2_CID_GAIN, 0, 72, 1, 0);
+			  V4L2_CID_ANALOGUE_GAIN, 0, 100, 1, 0);
+
+	mode = imx290->current_mode;
+	imx290->hblank = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops,
+					   V4L2_CID_HBLANK,
+					   imx290->hmax_min - mode->width,
+					   IMX290_HMAX_MAX - mode->width, 1,
+					   mode->hmax - mode->width);
+
+	imx290->vblank = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops,
+					   V4L2_CID_VBLANK,
+					   mode->vmax - mode->height,
+					   IMX290_VMAX_MAX - mode->height, 1,
+					   mode->vmax - mode->height);
+
+	imx290->exposure = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops,
+					     V4L2_CID_EXPOSURE,
+					     IMX290_EXPOSURE_MIN,
+					     mode->vmax - 2,
+					     IMX290_EXPOSURE_STEP,
+					     mode->vmax - 2);
+
+	imx290->hflip = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops,
+					  V4L2_CID_HFLIP, 0, 1, 1, 0);
+	imx290->vflip = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops,
+					  V4L2_CID_VFLIP, 0, 1, 1, 0);
 
 	imx290->link_freq =
 		v4l2_ctrl_new_int_menu(&imx290->ctrls, &imx290_ctrl_ops,
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1436 @
 		goto free_ctrl;
 	}
 
+	/* Initialize the frame format (this also sets imx290->current_mode) */
+	imx290_entity_init_cfg(&imx290->sd, NULL);
+
 	ret = v4l2_async_register_subdev(&imx290->sd);
 	if (ret < 0) {
 		dev_err(dev, "Could not register v4l2 device\n");
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1490 @
 	return 0;
 }
 
-static const struct of_device_id imx290_of_match[] = {
-	{ .compatible = "sony,imx290" },
-	{ /* sentinel */ }
-};
 MODULE_DEVICE_TABLE(of, imx290_of_match);
 
 static struct i2c_driver imx290_i2c_driver = {
diff -Nur --no-dereference linux-5.10.52-orig/drivers/media/i2c/imx477.c linux-5.10.52-v7l+/drivers/media/i2c/imx477.c
--- linux-5.10.52-orig/drivers/media/i2c/imx477.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/media/i2c/imx477.c	2021-07-25 16:46:05.698295374 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * A V4L2 driver for Sony IMX477 cameras.
+ * Copyright (C) 2020, Raspberry Pi (Trading) Ltd
+ *
+ * Based on Sony imx219 camera driver
+ * Copyright (C) 2019-2020 Raspberry Pi (Trading) Ltd
+ */
+#include <asm/unaligned.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regulator/consumer.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-fwnode.h>
+#include <media/v4l2-mediabus.h>
+
+#define IMX477_REG_VALUE_08BIT		1
+#define IMX477_REG_VALUE_16BIT		2
+
+/* Chip ID */
+#define IMX477_REG_CHIP_ID		0x0016
+#define IMX477_CHIP_ID			0x0477
+#define IMX378_CHIP_ID			0x0378
+
+#define IMX477_REG_MODE_SELECT		0x0100
+#define IMX477_MODE_STANDBY		0x00
+#define IMX477_MODE_STREAMING		0x01
+
+#define IMX477_REG_ORIENTATION		0x101
+
+#define IMX477_XCLK_FREQ		24000000
+
+#define IMX477_DEFAULT_LINK_FREQ	450000000
+
+/* Pixel rate is fixed at 840MHz for all the modes */
+#define IMX477_PIXEL_RATE		840000000
+
+/* V_TIMING internal */
+#define IMX477_REG_FRAME_LENGTH		0x0340
+#define IMX477_FRAME_LENGTH_MAX		0xffdc
+
+/* Long exposure multiplier */
+#define IMX477_LONG_EXP_SHIFT_MAX	7
+#define IMX477_LONG_EXP_SHIFT_REG	0x3100
+
+/* Exposure control */
+#define IMX477_REG_EXPOSURE		0x0202
+#define IMX477_EXPOSURE_OFFSET		22
+#define IMX477_EXPOSURE_MIN		20
+#define IMX477_EXPOSURE_STEP		1
+#define IMX477_EXPOSURE_DEFAULT		0x640
+#define IMX477_EXPOSURE_MAX		(IMX477_FRAME_LENGTH_MAX - \
+					 IMX477_EXPOSURE_OFFSET)
+
+/* Analog gain control */
+#define IMX477_REG_ANALOG_GAIN		0x0204
+#define IMX477_ANA_GAIN_MIN		0
+#define IMX477_ANA_GAIN_MAX		978
+#define IMX477_ANA_GAIN_STEP		1
+#define IMX477_ANA_GAIN_DEFAULT		0x0
+
+/* Digital gain control */
+#define IMX477_REG_DIGITAL_GAIN		0x020e
+#define IMX477_DGTL_GAIN_MIN		0x0100
+#define IMX477_DGTL_GAIN_MAX		0xffff
+#define IMX477_DGTL_GAIN_DEFAULT	0x0100
+#define IMX477_DGTL_GAIN_STEP		1
+
+/* Test Pattern Control */
+#define IMX477_REG_TEST_PATTERN		0x0600
+#define IMX477_TEST_PATTERN_DISABLE	0
+#define IMX477_TEST_PATTERN_SOLID_COLOR	1
+#define IMX477_TEST_PATTERN_COLOR_BARS	2
+#define IMX477_TEST_PATTERN_GREY_COLOR	3
+#define IMX477_TEST_PATTERN_PN9		4
+
+/* Test pattern colour components */
+#define IMX477_REG_TEST_PATTERN_R	0x0602
+#define IMX477_REG_TEST_PATTERN_GR	0x0604
+#define IMX477_REG_TEST_PATTERN_B	0x0606
+#define IMX477_REG_TEST_PATTERN_GB	0x0608
+#define IMX477_TEST_PATTERN_COLOUR_MIN	0
+#define IMX477_TEST_PATTERN_COLOUR_MAX	0x0fff
+#define IMX477_TEST_PATTERN_COLOUR_STEP	1
+#define IMX477_TEST_PATTERN_R_DEFAULT	IMX477_TEST_PATTERN_COLOUR_MAX
+#define IMX477_TEST_PATTERN_GR_DEFAULT	0
+#define IMX477_TEST_PATTERN_B_DEFAULT	0
+#define IMX477_TEST_PATTERN_GB_DEFAULT	0
+
+/* Embedded metadata stream structure */
+#define IMX477_EMBEDDED_LINE_WIDTH 16384
+#define IMX477_NUM_EMBEDDED_LINES 1
+
+enum pad_types {
+	IMAGE_PAD,
+	METADATA_PAD,
+	NUM_PADS
+};
+
+/* IMX477 native and active pixel array size. */
+#define IMX477_NATIVE_WIDTH		4072U
+#define IMX477_NATIVE_HEIGHT		3176U
+#define IMX477_PIXEL_ARRAY_LEFT		8U
+#define IMX477_PIXEL_ARRAY_TOP		16U
+#define IMX477_PIXEL_ARRAY_WIDTH	4056U
+#define IMX477_PIXEL_ARRAY_HEIGHT	3040U
+
+struct imx477_reg {
+	u16 address;
+	u8 val;
+};
+
+struct imx477_reg_list {
+	unsigned int num_of_regs;
+	const struct imx477_reg *regs;
+};
+
+/* Mode : resolution and related config&values */
+struct imx477_mode {
+	/* Frame width */
+	unsigned int width;
+
+	/* Frame height */
+	unsigned int height;
+
+	/* H-timing in pixels */
+	unsigned int line_length_pix;
+
+	/* Analog crop rectangle. */
+	struct v4l2_rect crop;
+
+	/* Highest possible framerate. */
+	struct v4l2_fract timeperframe_min;
+
+	/* Default framerate. */
+	struct v4l2_fract timeperframe_default;
+
+	/* Default register values */
+	struct imx477_reg_list reg_list;
+};
+
+static const struct imx477_reg mode_common_regs[] = {
+	{0x0136, 0x18},
+	{0x0137, 0x00},
+	{0xe000, 0x00},
+	{0xe07a, 0x01},
+	{0x0808, 0x02},
+	{0x4ae9, 0x18},
+	{0x4aea, 0x08},
+	{0xf61c, 0x04},
+	{0xf61e, 0x04},
+	{0x4ae9, 0x21},
+	{0x4aea, 0x80},
+	{0x38a8, 0x1f},
+	{0x38a9, 0xff},
+	{0x38aa, 0x1f},
+	{0x38ab, 0xff},
+	{0x55d4, 0x00},
+	{0x55d5, 0x00},
+	{0x55d6, 0x07},
+	{0x55d7, 0xff},
+	{0x55e8, 0x07},
+	{0x55e9, 0xff},
+	{0x55ea, 0x00},
+	{0x55eb, 0x00},
+	{0x574c, 0x07},
+	{0x574d, 0xff},
+	{0x574e, 0x00},
+	{0x574f, 0x00},
+	{0x5754, 0x00},
+	{0x5755, 0x00},
+	{0x5756, 0x07},
+	{0x5757, 0xff},
+	{0x5973, 0x04},
+	{0x5974, 0x01},
+	{0x5d13, 0xc3},
+	{0x5d14, 0x58},
+	{0x5d15, 0xa3},
+	{0x5d16, 0x1d},
+	{0x5d17, 0x65},
+	{0x5d18, 0x8c},
+	{0x5d1a, 0x06},
+	{0x5d1b, 0xa9},
+	{0x5d1c, 0x45},
+	{0x5d1d, 0x3a},
+	{0x5d1e, 0xab},
+	{0x5d1f, 0x15},
+	{0x5d21, 0x0e},
+	{0x5d22, 0x52},
+	{0x5d23, 0xaa},
+	{0x5d24, 0x7d},
+	{0x5d25, 0x57},
+	{0x5d26, 0xa8},
+	{0x5d37, 0x5a},
+	{0x5d38, 0x5a},
+	{0x5d77, 0x7f},
+	{0x7b75, 0x0e},
+	{0x7b76, 0x0b},
+	{0x7b77, 0x08},
+	{0x7b78, 0x0a},
+	{0x7b79, 0x47},
+	{0x7b7c, 0x00},
+	{0x7b7d, 0x00},
+	{0x8d1f, 0x00},
+	{0x8d27, 0x00},
+	{0x9004, 0x03},
+	{0x9200, 0x50},
+	{0x9201, 0x6c},
+	{0x9202, 0x71},
+	{0x9203, 0x00},
+	{0x9204, 0x71},
+	{0x9205, 0x01},
+	{0x9371, 0x6a},
+	{0x9373, 0x6a},
+	{0x9375, 0x64},
+	{0x991a, 0x00},
+	{0x996b, 0x8c},
+	{0x996c, 0x64},
+	{0x996d, 0x50},
+	{0x9a4c, 0x0d},
+	{0x9a4d, 0x0d},
+	{0xa001, 0x0a},
+	{0xa003, 0x0a},
+	{0xa005, 0x0a},
+	{0xa006, 0x01},
+	{0xa007, 0xc0},
+	{0xa009, 0xc0},
+	{0x3d8a, 0x01},
+	{0x4421, 0x04},
+	{0x7b3b, 0x01},
+	{0x7b4c, 0x00},
+	{0x9905, 0x00},
+	{0x9907, 0x00},
+	{0x9909, 0x00},
+	{0x990b, 0x00},
+	{0x9944, 0x3c},
+	{0x9947, 0x3c},
+	{0x994a, 0x8c},
+	{0x994b, 0x50},
+	{0x994c, 0x1b},
+	{0x994d, 0x8c},
+	{0x994e, 0x50},
+	{0x994f, 0x1b},
+	{0x9950, 0x8c},
+	{0x9951, 0x1b},
+	{0x9952, 0x0a},
+	{0x9953, 0x8c},
+	{0x9954, 0x1b},
+	{0x9955, 0x0a},
+	{0x9a13, 0x04},
+	{0x9a14, 0x04},
+	{0x9a19, 0x00},
+	{0x9a1c, 0x04},
+	{0x9a1d, 0x04},
+	{0x9a26, 0x05},
+	{0x9a27, 0x05},
+	{0x9a2c, 0x01},
+	{0x9a2d, 0x03},
+	{0x9a2f, 0x05},
+	{0x9a30, 0x05},
+	{0x9a41, 0x00},
+	{0x9a46, 0x00},
+	{0x9a47, 0x00},
+	{0x9c17, 0x35},
+	{0x9c1d, 0x31},
+	{0x9c29, 0x50},
+	{0x9c3b, 0x2f},
+	{0x9c41, 0x6b},
+	{0x9c47, 0x2d},
+	{0x9c4d, 0x40},
+	{0x9c6b, 0x00},
+	{0x9c71, 0xc8},
+	{0x9c73, 0x32},
+	{0x9c75, 0x04},
+	{0x9c7d, 0x2d},
+	{0x9c83, 0x40},
+	{0x9c94, 0x3f},
+	{0x9c95, 0x3f},
+	{0x9c96, 0x3f},
+	{0x9c97, 0x00},
+	{0x9c98, 0x00},
+	{0x9c99, 0x00},
+	{0x9c9a, 0x3f},
+	{0x9c9b, 0x3f},
+	{0x9c9c, 0x3f},
+	{0x9ca0, 0x0f},
+	{0x9ca1, 0x0f},
+	{0x9ca2, 0x0f},
+	{0x9ca3, 0x00},
+	{0x9ca4, 0x00},
+	{0x9ca5, 0x00},
+	{0x9ca6, 0x1e},
+	{0x9ca7, 0x1e},
+	{0x9ca8, 0x1e},
+	{0x9ca9, 0x00},
+	{0x9caa, 0x00},
+	{0x9cab, 0x00},
+	{0x9cac, 0x09},
+	{0x9cad, 0x09},
+	{0x9cae, 0x09},
+	{0x9cbd, 0x50},
+	{0x9cbf, 0x50},
+	{0x9cc1, 0x50},
+	{0x9cc3, 0x40},
+	{0x9cc5, 0x40},
+	{0x9cc7, 0x40},
+	{0x9cc9, 0x0a},
+	{0x9ccb, 0x0a},
+	{0x9ccd, 0x0a},
+	{0x9d17, 0x35},
+	{0x9d1d, 0x31},
+	{0x9d29, 0x50},
+	{0x9d3b, 0x2f},
+	{0x9d41, 0x6b},
+	{0x9d47, 0x42},
+	{0x9d4d, 0x5a},
+	{0x9d6b, 0x00},
+	{0x9d71, 0xc8},
+	{0x9d73, 0x32},
+	{0x9d75, 0x04},
+	{0x9d7d, 0x42},
+	{0x9d83, 0x5a},
+	{0x9d94, 0x3f},
+	{0x9d95, 0x3f},
+	{0x9d96, 0x3f},
+	{0x9d97, 0x00},
+	{0x9d98, 0x00},
+	{0x9d99, 0x00},
+	{0x9d9a, 0x3f},
+	{0x9d9b, 0x3f},
+	{0x9d9c, 0x3f},
+	{0x9d9d, 0x1f},
+	{0x9d9e, 0x1f},
+	{0x9d9f, 0x1f},
+	{0x9da0, 0x0f},
+	{0x9da1, 0x0f},
+	{0x9da2, 0x0f},
+	{0x9da3, 0x00},
+	{0x9da4, 0x00},
+	{0x9da5, 0x00},
+	{0x9da6, 0x1e},
+	{0x9da7, 0x1e},
+	{0x9da8, 0x1e},
+	{0x9da9, 0x00},
+	{0x9daa, 0x00},
+	{0x9dab, 0x00},
+	{0x9dac, 0x09},
+	{0x9dad, 0x09},
+	{0x9dae, 0x09},
+	{0x9dc9, 0x0a},
+	{0x9dcb, 0x0a},
+	{0x9dcd, 0x0a},
+	{0x9e17, 0x35},
+	{0x9e1d, 0x31},
+	{0x9e29, 0x50},
+	{0x9e3b, 0x2f},
+	{0x9e41, 0x6b},
+	{0x9e47, 0x2d},
+	{0x9e4d, 0x40},
+	{0x9e6b, 0x00},
+	{0x9e71, 0xc8},
+	{0x9e73, 0x32},
+	{0x9e75, 0x04},
+	{0x9e94, 0x0f},
+	{0x9e95, 0x0f},
+	{0x9e96, 0x0f},
+	{0x9e97, 0x00},
+	{0x9e98, 0x00},
+	{0x9e99, 0x00},
+	{0x9ea0, 0x0f},
+	{0x9ea1, 0x0f},
+	{0x9ea2, 0x0f},
+	{0x9ea3, 0x00},
+	{0x9ea4, 0x00},
+	{0x9ea5, 0x00},
+	{0x9ea6, 0x3f},
+	{0x9ea7, 0x3f},
+	{0x9ea8, 0x3f},
+	{0x9ea9, 0x00},
+	{0x9eaa, 0x00},
+	{0x9eab, 0x00},
+	{0x9eac, 0x09},
+	{0x9ead, 0x09},
+	{0x9eae, 0x09},
+	{0x9ec9, 0x0a},
+	{0x9ecb, 0x0a},
+	{0x9ecd, 0x0a},
+	{0x9f17, 0x35},
+	{0x9f1d, 0x31},
+	{0x9f29, 0x50},
+	{0x9f3b, 0x2f},
+	{0x9f41, 0x6b},
+	{0x9f47, 0x42},
+	{0x9f4d, 0x5a},
+	{0x9f6b, 0x00},
+	{0x9f71, 0xc8},
+	{0x9f73, 0x32},
+	{0x9f75, 0x04},
+	{0x9f94, 0x0f},
+	{0x9f95, 0x0f},
+	{0x9f96, 0x0f},
+	{0x9f97, 0x00},
+	{0x9f98, 0x00},
+	{0x9f99, 0x00},
+	{0x9f9a, 0x2f},
+	{0x9f9b, 0x2f},
+	{0x9f9c, 0x2f},
+	{0x9f9d, 0x00},
+	{0x9f9e, 0x00},
+	{0x9f9f, 0x00},
+	{0x9fa0, 0x0f},
+	{0x9fa1, 0x0f},
+	{0x9fa2, 0x0f},
+	{0x9fa3, 0x00},
+	{0x9fa4, 0x00},
+	{0x9fa5, 0x00},
+	{0x9fa6, 0x1e},
+	{0x9fa7, 0x1e},
+	{0x9fa8, 0x1e},
+	{0x9fa9, 0x00},
+	{0x9faa, 0x00},
+	{0x9fab, 0x00},
+	{0x9fac, 0x09},
+	{0x9fad, 0x09},
+	{0x9fae, 0x09},
+	{0x9fc9, 0x0a},
+	{0x9fcb, 0x0a},
+	{0x9fcd, 0x0a},
+	{0xa14b, 0xff},
+	{0xa151, 0x0c},
+	{0xa153, 0x50},
+	{0xa155, 0x02},
+	{0xa157, 0x00},
+	{0xa1ad, 0xff},
+	{0xa1b3, 0x0c},
+	{0xa1b5, 0x50},
+	{0xa1b9, 0x00},
+	{0xa24b, 0xff},
+	{0xa257, 0x00},
+	{0xa2ad, 0xff},
+	{0xa2b9, 0x00},
+	{0xb21f, 0x04},
+	{0xb35c, 0x00},
+	{0xb35e, 0x08},
+	{0x0112, 0x0c},
+	{0x0113, 0x0c},
+	{0x0114, 0x01},
+	{0x0350, 0x00},
+	{0xbcf1, 0x02},
+	{0x3ff9, 0x01},
+};
+
+/* 12 mpix 10fps */
+static const struct imx477_reg mode_4056x3040_regs[] = {
+	{0x0342, 0x5d},
+	{0x0343, 0xc0},
+	{0x0344, 0x00},
+	{0x0345, 0x00},
+	{0x0346, 0x00},
+	{0x0347, 0x00},
+	{0x0348, 0x0f},
+	{0x0349, 0xd7},
+	{0x034a, 0x0b},
+	{0x034b, 0xdf},
+	{0x00e3, 0x00},
+	{0x00e4, 0x00},
+	{0x00fc, 0x0a},
+	{0x00fd, 0x0a},
+	{0x00fe, 0x0a},
+	{0x00ff, 0x0a},
+	{0x0220, 0x00},
+	{0x0221, 0x11},
+	{0x0381, 0x01},
+	{0x0383, 0x01},
+	{0x0385, 0x01},
+	{0x0387, 0x01},
+	{0x0900, 0x00},
+	{0x0901, 0x11},
+	{0x0902, 0x02},
+	{0x3140, 0x02},
+	{0x3c00, 0x00},
+	{0x3c01, 0x03},
+	{0x3c02, 0xa2},
+	{0x3f0d, 0x01},
+	{0x5748, 0x07},
+	{0x5749, 0xff},
+	{0x574a, 0x00},
+	{0x574b, 0x00},
+	{0x7b75, 0x0a},
+	{0x7b76, 0x0c},
+	{0x7b77, 0x07},
+	{0x7b78, 0x06},
+	{0x7b79, 0x3c},
+	{0x7b53, 0x01},
+	{0x9369, 0x5a},
+	{0x936b, 0x55},
+	{0x936d, 0x28},
+	{0x9304, 0x00},
+	{0x9305, 0x00},
+	{0x9e9a, 0x2f},
+	{0x9e9b, 0x2f},
+	{0x9e9c, 0x2f},
+	{0x9e9d, 0x00},
+	{0x9e9e, 0x00},
+	{0x9e9f, 0x00},
+	{0xa2a9, 0x60},
+	{0xa2b7, 0x00},
+	{0x0401, 0x00},
+	{0x0404, 0x00},
+	{0x0405, 0x10},
+	{0x0408, 0x00},
+	{0x0409, 0x00},
+	{0x040a, 0x00},
+	{0x040b, 0x00},
+	{0x040c, 0x0f},
+	{0x040d, 0xd8},
+	{0x040e, 0x0b},
+	{0x040f, 0xe0},
+	{0x034c, 0x0f},
+	{0x034d, 0xd8},
+	{0x034e, 0x0b},
+	{0x034f, 0xe0},
+	{0x0301, 0x05},
+	{0x0303, 0x02},
+	{0x0305, 0x04},
+	{0x0306, 0x01},
+	{0x0307, 0x5e},
+	{0x0309, 0x0c},
+	{0x030b, 0x02},
+	{0x030d, 0x02},
+	{0x030e, 0x00},
+	{0x030f, 0x96},
+	{0x0310, 0x01},
+	{0x0820, 0x07},
+	{0x0821, 0x08},
+	{0x0822, 0x00},
+	{0x0823, 0x00},
+	{0x080a, 0x00},
+	{0x080b, 0x7f},
+	{0x080c, 0x00},
+	{0x080d, 0x4f},
+	{0x080e, 0x00},
+	{0x080f, 0x77},
+	{0x0810, 0x00},
+	{0x0811, 0x5f},
+	{0x0812, 0x00},
+	{0x0813, 0x57},
+	{0x0814, 0x00},
+	{0x0815, 0x4f},
+	{0x0816, 0x01},
+	{0x0817, 0x27},
+	{0x0818, 0x00},
+	{0x0819, 0x3f},
+	{0xe04c, 0x00},
+	{0xe04d, 0x7f},
+	{0xe04e, 0x00},
+	{0xe04f, 0x1f},
+	{0x3e20, 0x01},
+	{0x3e37, 0x00},
+	{0x3f50, 0x00},
+	{0x3f56, 0x02},
+	{0x3f57, 0xae},
+};
+
+/* 2x2 binned. 40fps */
+static const struct imx477_reg mode_2028x1520_regs[] = {
+	{0x0342, 0x31},
+	{0x0343, 0xc4},
+	{0x0344, 0x00},
+	{0x0345, 0x00},
+	{0x0346, 0x00},
+	{0x0347, 0x00},
+	{0x0348, 0x0f},
+	{0x0349, 0xd7},
+	{0x034a, 0x0b},
+	{0x034b, 0xdf},
+	{0x0220, 0x00},
+	{0x0221, 0x11},
+	{0x0381, 0x01},
+	{0x0383, 0x01},
+	{0x0385, 0x01},
+	{0x0387, 0x01},
+	{0x0900, 0x01},
+	{0x0901, 0x12},
+	{0x0902, 0x02},
+	{0x3140, 0x02},
+	{0x3c00, 0x00},
+	{0x3c01, 0x03},
+	{0x3c02, 0xa2},
+	{0x3f0d, 0x01},
+	{0x5748, 0x07},
+	{0x5749, 0xff},
+	{0x574a, 0x00},
+	{0x574b, 0x00},
+	{0x7b53, 0x01},
+	{0x9369, 0x73},
+	{0x936b, 0x64},
+	{0x936d, 0x5f},
+	{0x9304, 0x00},
+	{0x9305, 0x00},
+	{0x9e9a, 0x2f},
+	{0x9e9b, 0x2f},
+	{0x9e9c, 0x2f},
+	{0x9e9d, 0x00},
+	{0x9e9e, 0x00},
+	{0x9e9f, 0x00},
+	{0xa2a9, 0x60},
+	{0xa2b7, 0x00},
+	{0x0401, 0x01},
+	{0x0404, 0x00},
+	{0x0405, 0x20},
+	{0x0408, 0x00},
+	{0x0409, 0x00},
+	{0x040a, 0x00},
+	{0x040b, 0x00},
+	{0x040c, 0x0f},
+	{0x040d, 0xd8},
+	{0x040e, 0x0b},
+	{0x040f, 0xe0},
+	{0x034c, 0x07},
+	{0x034d, 0xec},
+	{0x034e, 0x05},
+	{0x034f, 0xf0},
+	{0x0301, 0x05},
+	{0x0303, 0x02},
+	{0x0305, 0x04},
+	{0x0306, 0x01},
+	{0x0307, 0x5e},
+	{0x0309, 0x0c},
+	{0x030b, 0x02},
+	{0x030d, 0x02},
+	{0x030e, 0x00},
+	{0x030f, 0x96},
+	{0x0310, 0x01},
+	{0x0820, 0x07},
+	{0x0821, 0x08},
+	{0x0822, 0x00},
+	{0x0823, 0x00},
+	{0x080a, 0x00},
+	{0x080b, 0x7f},
+	{0x080c, 0x00},
+	{0x080d, 0x4f},
+	{0x080e, 0x00},
+	{0x080f, 0x77},
+	{0x0810, 0x00},
+	{0x0811, 0x5f},
+	{0x0812, 0x00},
+	{0x0813, 0x57},
+	{0x0814, 0x00},
+	{0x0815, 0x4f},
+	{0x0816, 0x01},
+	{0x0817, 0x27},
+	{0x0818, 0x00},
+	{0x0819, 0x3f},
+	{0xe04c, 0x00},
+	{0xe04d, 0x7f},
+	{0xe04e, 0x00},
+	{0xe04f, 0x1f},
+	{0x3e20, 0x01},
+	{0x3e37, 0x00},
+	{0x3f50, 0x00},
+	{0x3f56, 0x01},
+	{0x3f57, 0x6c},
+};
+
+/* 1080p cropped mode */
+static const struct imx477_reg mode_2028x1080_regs[] = {
+	{0x0342, 0x31},
+	{0x0343, 0xc4},
+	{0x0344, 0x00},
+	{0x0345, 0x00},
+	{0x0346, 0x01},
+	{0x0347, 0xb8},
+	{0x0348, 0x0f},
+	{0x0349, 0xd7},
+	{0x034a, 0x0a},
+	{0x034b, 0x27},
+	{0x0220, 0x00},
+	{0x0221, 0x11},
+	{0x0381, 0x01},
+	{0x0383, 0x01},
+	{0x0385, 0x01},
+	{0x0387, 0x01},
+	{0x0900, 0x01},
+	{0x0901, 0x12},
+	{0x0902, 0x02},
+	{0x3140, 0x02},
+	{0x3c00, 0x00},
+	{0x3c01, 0x03},
+	{0x3c02, 0xa2},
+	{0x3f0d, 0x01},
+	{0x5748, 0x07},
+	{0x5749, 0xff},
+	{0x574a, 0x00},
+	{0x574b, 0x00},
+	{0x7b53, 0x01},
+	{0x9369, 0x73},
+	{0x936b, 0x64},
+	{0x936d, 0x5f},
+	{0x9304, 0x00},
+	{0x9305, 0x00},
+	{0x9e9a, 0x2f},
+	{0x9e9b, 0x2f},
+	{0x9e9c, 0x2f},
+	{0x9e9d, 0x00},
+	{0x9e9e, 0x00},
+	{0x9e9f, 0x00},
+	{0xa2a9, 0x60},
+	{0xa2b7, 0x00},
+	{0x0401, 0x01},
+	{0x0404, 0x00},
+	{0x0405, 0x20},
+	{0x0408, 0x00},
+	{0x0409, 0x00},
+	{0x040a, 0x00},
+	{0x040b, 0x00},
+	{0x040c, 0x0f},
+	{0x040d, 0xd8},
+	{0x040e, 0x04},
+	{0x040f, 0x38},
+	{0x034c, 0x07},
+	{0x034d, 0xec},
+	{0x034e, 0x04},
+	{0x034f, 0x38},
+	{0x0301, 0x05},
+	{0x0303, 0x02},
+	{0x0305, 0x04},
+	{0x0306, 0x01},
+	{0x0307, 0x5e},
+	{0x0309, 0x0c},
+	{0x030b, 0x02},
+	{0x030d, 0x02},
+	{0x030e, 0x00},
+	{0x030f, 0x96},
+	{0x0310, 0x01},
+	{0x0820, 0x07},
+	{0x0821, 0x08},
+	{0x0822, 0x00},
+	{0x0823, 0x00},
+	{0x080a, 0x00},
+	{0x080b, 0x7f},
+	{0x080c, 0x00},
+	{0x080d, 0x4f},
+	{0x080e, 0x00},
+	{0x080f, 0x77},
+	{0x0810, 0x00},
+	{0x0811, 0x5f},
+	{0x0812, 0x00},
+	{0x0813, 0x57},
+	{0x0814, 0x00},
+	{0x0815, 0x4f},
+	{0x0816, 0x01},
+	{0x0817, 0x27},
+	{0x0818, 0x00},
+	{0x0819, 0x3f},
+	{0xe04c, 0x00},
+	{0xe04d, 0x7f},
+	{0xe04e, 0x00},
+	{0xe04f, 0x1f},
+	{0x3e20, 0x01},
+	{0x3e37, 0x00},
+	{0x3f50, 0x00},
+	{0x3f56, 0x01},
+	{0x3f57, 0x6c},
+};
+
+/* 4x4 binned. 120fps */
+static const struct imx477_reg mode_1332x990_regs[] = {
+	{0x420b, 0x01},
+	{0x990c, 0x00},
+	{0x990d, 0x08},
+	{0x9956, 0x8c},
+	{0x9957, 0x64},
+	{0x9958, 0x50},
+	{0x9a48, 0x06},
+	{0x9a49, 0x06},
+	{0x9a4a, 0x06},
+	{0x9a4b, 0x06},
+	{0x9a4c, 0x06},
+	{0x9a4d, 0x06},
+	{0x0112, 0x0a},
+	{0x0113, 0x0a},
+	{0x0114, 0x01},
+	{0x0342, 0x1a},
+	{0x0343, 0x08},
+	{0x0340, 0x04},
+	{0x0341, 0x1a},
+	{0x0344, 0x00},
+	{0x0345, 0x00},
+	{0x0346, 0x02},
+	{0x0347, 0x10},
+	{0x0348, 0x0f},
+	{0x0349, 0xd7},
+	{0x034a, 0x09},
+	{0x034b, 0xcf},
+	{0x00e3, 0x00},
+	{0x00e4, 0x00},
+	{0x00fc, 0x0a},
+	{0x00fd, 0x0a},
+	{0x00fe, 0x0a},
+	{0x00ff, 0x0a},
+	{0xe013, 0x00},
+	{0x0220, 0x00},
+	{0x0221, 0x11},
+	{0x0381, 0x01},
+	{0x0383, 0x01},
+	{0x0385, 0x01},
+	{0x0387, 0x01},
+	{0x0900, 0x01},
+	{0x0901, 0x22},
+	{0x0902, 0x02},
+	{0x3140, 0x02},
+	{0x3c00, 0x00},
+	{0x3c01, 0x01},
+	{0x3c02, 0x9c},
+	{0x3f0d, 0x00},
+	{0x5748, 0x00},
+	{0x5749, 0x00},
+	{0x574a, 0x00},
+	{0x574b, 0xa4},
+	{0x7b75, 0x0e},
+	{0x7b76, 0x09},
+	{0x7b77, 0x08},
+	{0x7b78, 0x06},
+	{0x7b79, 0x34},
+	{0x7b53, 0x00},
+	{0x9369, 0x73},
+	{0x936b, 0x64},
+	{0x936d, 0x5f},
+	{0x9304, 0x03},
+	{0x9305, 0x80},
+	{0x9e9a, 0x2f},
+	{0x9e9b, 0x2f},
+	{0x9e9c, 0x2f},
+	{0x9e9d, 0x00},
+	{0x9e9e, 0x00},
+	{0x9e9f, 0x00},
+	{0xa2a9, 0x27},
+	{0xa2b7, 0x03},
+	{0x0401, 0x00},
+	{0x0404, 0x00},
+	{0x0405, 0x10},
+	{0x0408, 0x01},
+	{0x0409, 0x5c},
+	{0x040a, 0x00},
+	{0x040b, 0x00},
+	{0x040c, 0x05},
+	{0x040d, 0x34},
+	{0x040e, 0x03},
+	{0x040f, 0xde},
+	{0x034c, 0x05},
+	{0x034d, 0x34},
+	{0x034e, 0x03},
+	{0x034f, 0xde},
+	{0x0301, 0x05},
+	{0x0303, 0x02},
+	{0x0305, 0x02},
+	{0x0306, 0x00},
+	{0x0307, 0xaf},
+	{0x0309, 0x0a},
+	{0x030b, 0x02},
+	{0x030d, 0x02},
+	{0x030e, 0x00},
+	{0x030f, 0x96},
+	{0x0310, 0x01},
+	{0x0820, 0x07},
+	{0x0821, 0x08},
+	{0x0822, 0x00},
+	{0x0823, 0x00},
+	{0x080a, 0x00},
+	{0x080b, 0x7f},
+	{0x080c, 0x00},
+	{0x080d, 0x4f},
+	{0x080e, 0x00},
+	{0x080f, 0x77},
+	{0x0810, 0x00},
+	{0x0811, 0x5f},
+	{0x0812, 0x00},
+	{0x0813, 0x57},
+	{0x0814, 0x00},
+	{0x0815, 0x4f},
+	{0x0816, 0x01},
+	{0x0817, 0x27},
+	{0x0818, 0x00},
+	{0x0819, 0x3f},
+	{0xe04c, 0x00},
+	{0xe04d, 0x5f},
+	{0xe04e, 0x00},
+	{0xe04f, 0x1f},
+	{0x3e20, 0x01},
+	{0x3e37, 0x00},
+	{0x3f50, 0x00},
+	{0x3f56, 0x00},
+	{0x3f57, 0xbf},
+};
+
+/* Mode configs */
+static const struct imx477_mode supported_modes_12bit[] = {
+	{
+		/* 12MPix 10fps mode */
+		.width = 4056,
+		.height = 3040,
+		.line_length_pix = 0x5dc0,
+		.crop = {
+			.left = IMX477_PIXEL_ARRAY_LEFT,
+			.top = IMX477_PIXEL_ARRAY_TOP,
+			.width = 4056,
+			.height = 3040,
+		},
+		.timeperframe_min = {
+			.numerator = 100,
+			.denominator = 1000
+		},
+		.timeperframe_default = {
+			.numerator = 100,
+			.denominator = 1000
+		},
+		.reg_list = {
+			.num_of_regs = ARRAY_SIZE(mode_4056x3040_regs),
+			.regs = mode_4056x3040_regs,
+		},
+	},
+	{
+		/* 2x2 binned 40fps mode */
+		.width = 2028,
+		.height = 1520,
+		.line_length_pix = 0x31c4,
+		.crop = {
+			.left = IMX477_PIXEL_ARRAY_LEFT,
+			.top = IMX477_PIXEL_ARRAY_TOP,
+			.width = 4056,
+			.height = 3040,
+		},
+		.timeperframe_min = {
+			.numerator = 100,
+			.denominator = 4000
+		},
+		.timeperframe_default = {
+			.numerator = 100,
+			.denominator = 3000
+		},
+		.reg_list = {
+			.num_of_regs = ARRAY_SIZE(mode_2028x1520_regs),
+			.regs = mode_2028x1520_regs,
+		},
+	},
+	{
+		/* 1080p 50fps cropped mode */
+		.width = 2028,
+		.height = 1080,
+		.line_length_pix = 0x31c4,
+		.crop = {
+			.left = IMX477_PIXEL_ARRAY_LEFT,
+			.top = IMX477_PIXEL_ARRAY_TOP + 440,
+			.width = 4056,
+			.height = 2160,
+		},
+		.timeperframe_min = {
+			.numerator = 100,
+			.denominator = 5000
+		},
+		.timeperframe_default = {
+			.numerator = 100,
+			.denominator = 3000
+		},
+		.reg_list = {
+			.num_of_regs = ARRAY_SIZE(mode_2028x1080_regs),
+			.regs = mode_2028x1080_regs,
+		},
+	}
+};
+
+static const struct imx477_mode supported_modes_10bit[] = {
+	{
+		/* 120fps. 2x2 binned and cropped */
+		.width = 1332,
+		.height = 990,
+		.line_length_pix = 6664,
+		.crop = {
+			/*
+			 * FIXME: the analog crop rectangle is actually
+			 * programmed with a horizontal displacement of 0
+			 * pixels, not 4. It gets shrunk after going through
+			 * the scaler. Move this information to the compose
+			 * rectangle once the driver is expanded to represent
+			 * its processing blocks with multiple subdevs.
+			 */
+			.left = IMX477_PIXEL_ARRAY_LEFT + 696,
+			.top = IMX477_PIXEL_ARRAY_TOP + 528,
+			.width = 2664,
+			.height = 1980,
+		},
+		.timeperframe_min = {
+			.numerator = 100,
+			.denominator = 12000
+		},
+		.timeperframe_default = {
+			.numerator = 100,
+			.denominator = 12000
+		},
+		.reg_list = {
+			.num_of_regs = ARRAY_SIZE(mode_1332x990_regs),
+			.regs = mode_1332x990_regs,
+		}
+	}
+};
+
+/*
+ * The supported formats.
+ * This table MUST contain 4 entries per format, to cover the various flip
+ * combinations in the order
+ * - no flip
+ * - h flip
+ * - v flip
+ * - h&v flips
+ */
+static const u32 codes[] = {
+	/* 12-bit modes. */
+	MEDIA_BUS_FMT_SRGGB12_1X12,
+	MEDIA_BUS_FMT_SGRBG12_1X12,
+	MEDIA_BUS_FMT_SGBRG12_1X12,
+	MEDIA_BUS_FMT_SBGGR12_1X12,
+	/* 10-bit modes. */
+	MEDIA_BUS_FMT_SRGGB10_1X10,
+	MEDIA_BUS_FMT_SGRBG10_1X10,
+	MEDIA_BUS_FMT_SGBRG10_1X10,
+	MEDIA_BUS_FMT_SBGGR10_1X10,
+};
+
+static const char * const imx477_test_pattern_menu[] = {
+	"Disabled",
+	"Color Bars",
+	"Solid Color",
+	"Grey Color Bars",
+	"PN9"
+};
+
+static const int imx477_test_pattern_val[] = {
+	IMX477_TEST_PATTERN_DISABLE,
+	IMX477_TEST_PATTERN_COLOR_BARS,
+	IMX477_TEST_PATTERN_SOLID_COLOR,
+	IMX477_TEST_PATTERN_GREY_COLOR,
+	IMX477_TEST_PATTERN_PN9,
+};
+
+/* regulator supplies */
+static const char * const imx477_supply_name[] = {
+	/* Supplies can be enabled in any order */
+	"VANA",  /* Analog (2.8V) supply */
+	"VDIG",  /* Digital Core (1.05V) supply */
+	"VDDL",  /* IF (1.8V) supply */
+};
+
+#define IMX477_NUM_SUPPLIES ARRAY_SIZE(imx477_supply_name)
+
+/*
+ * Initialisation delay between XCLR low->high and the moment when the sensor
+ * can start capture (i.e. can leave software standby), given by T7 in the
+ * datasheet is 8ms.  This does include I2C setup time as well.
+ *
+ * Note, that delay between XCLR low->high and reading the CCI ID register (T6
+ * in the datasheet) is much smaller - 600us.
+ */
+#define IMX477_XCLR_MIN_DELAY_US	8000
+#define IMX477_XCLR_DELAY_RANGE_US	1000
+
+struct imx477_compatible_data {
+	unsigned int chip_id;
+	struct imx477_reg_list extra_regs;
+};
+
+struct imx477 {
+	struct v4l2_subdev sd;
+	struct media_pad pad[NUM_PADS];
+
+	unsigned int fmt_code;
+
+	struct clk *xclk;
+	u32 xclk_freq;
+
+	struct gpio_desc *reset_gpio;
+	struct regulator_bulk_data supplies[IMX477_NUM_SUPPLIES];
+
+	struct v4l2_ctrl_handler ctrl_handler;
+	/* V4L2 Controls */
+	struct v4l2_ctrl *pixel_rate;
+	struct v4l2_ctrl *exposure;
+	struct v4l2_ctrl *vflip;
+	struct v4l2_ctrl *hflip;
+	struct v4l2_ctrl *vblank;
+	struct v4l2_ctrl *hblank;
+
+	/* Current mode */
+	const struct imx477_mode *mode;
+
+	/*
+	 * Mutex for serialized access:
+	 * Protect sensor module set pad format and start/stop streaming safely.
+	 */
+	struct mutex mutex;
+
+	/* Streaming on/off */
+	bool streaming;
+
+	/* Rewrite common registers on stream on? */
+	bool common_regs_written;
+
+	/* Current long exposure factor in use. Set through V4L2_CID_VBLANK */
+	unsigned int long_exp_shift;
+
+	/* Any extra information related to different compatible sensors */
+	const struct imx477_compatible_data *compatible_data;
+};
+
+static inline struct imx477 *to_imx477(struct v4l2_subdev *_sd)
+{
+	return container_of(_sd, struct imx477, sd);
+}
+
+static inline void get_mode_table(unsigned int code,
+				  const struct imx477_mode **mode_list,
+				  unsigned int *num_modes)
+{
+	switch (code) {
+	/* 12-bit */
+	case MEDIA_BUS_FMT_SRGGB12_1X12:
+	case MEDIA_BUS_FMT_SGRBG12_1X12:
+	case MEDIA_BUS_FMT_SGBRG12_1X12:
+	case MEDIA_BUS_FMT_SBGGR12_1X12:
+		*mode_list = supported_modes_12bit;
+		*num_modes = ARRAY_SIZE(supported_modes_12bit);
+		break;
+	/* 10-bit */
+	case MEDIA_BUS_FMT_SRGGB10_1X10:
+	case MEDIA_BUS_FMT_SGRBG10_1X10:
+	case MEDIA_BUS_FMT_SGBRG10_1X10:
+	case MEDIA_BUS_FMT_SBGGR10_1X10:
+		*mode_list = supported_modes_10bit;
+		*num_modes = ARRAY_SIZE(supported_modes_10bit);
+		break;
+	default:
+		*mode_list = NULL;
+		*num_modes = 0;
+	}
+}
+
+/* Read registers up to 2 at a time */
+static int imx477_read_reg(struct imx477 *imx477, u16 reg, u32 len, u32 *val)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(&imx477->sd);
+	struct i2c_msg msgs[2];
+	u8 addr_buf[2] = { reg >> 8, reg & 0xff };
+	u8 data_buf[4] = { 0, };
+	int ret;
+
+	if (len > 4)
+		return -EINVAL;
+
+	/* Write register address */
+	msgs[0].addr = client->addr;
+	msgs[0].flags = 0;
+	msgs[0].len = ARRAY_SIZE(addr_buf);
+	msgs[0].buf = addr_buf;
+
+	/* Read data from register */
+	msgs[1].addr = client->addr;
+	msgs[1].flags = I2C_M_RD;
+	msgs[1].len = len;
+	msgs[1].buf = &data_buf[4 - len];
+
+	ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
+	if (ret != ARRAY_SIZE(msgs))
+		return -EIO;
+
+	*val = get_unaligned_be32(data_buf);
+
+	return 0;
+}
+
+/* Write registers up to 2 at a time */
+static int imx477_write_reg(struct imx477 *imx477, u16 reg, u32 len, u32 val)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(&imx477->sd);
+	u8 buf[6];
+
+	if (len > 4)
+		return -EINVAL;
+
+	put_unaligned_be16(reg, buf);
+	put_unaligned_be32(val << (8 * (4 - len)), buf + 2);
+	if (i2c_master_send(client, buf, len + 2) != len + 2)
+		return -EIO;
+
+	return 0;
+}
+
+/* Write a list of registers */
+static int imx477_write_regs(struct imx477 *imx477,
+			     const struct imx477_reg *regs, u32 len)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(&imx477->sd);
+	unsigned int i;
+	int ret;
+
+	for (i = 0; i < len; i++) {
+		ret = imx477_write_reg(imx477, regs[i].address, 1, regs[i].val);
+		if (ret) {
+			dev_err_ratelimited(&client->dev,
+					    "Failed to write reg 0x%4.4x. error = %d\n",
+					    regs[i].address, ret);
+
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+/* Get bayer order based on flip setting. */
+static u32 imx477_get_format_code(struct imx477 *imx477, u32 code)
+{
+	unsigned int i;
+
+	lockdep_assert_held(&imx477->mutex);
+
+	for (i = 0; i < ARRAY_SIZE(codes); i++)
+		if (codes[i] == code)
+			break;
+
+	if (i >= ARRAY_SIZE(codes))
+		i = 0;
+
+	i = (i & ~3) | (imx477->vflip->val ? 2 : 0) |
+	    (imx477->hflip->val ? 1 : 0);
+
+	return codes[i];
+}
+
+static void imx477_set_default_format(struct imx477 *imx477)
+{
+	/* Set default mode to max resolution */
+	imx477->mode = &supported_modes_12bit[0];
+	imx477->fmt_code = MEDIA_BUS_FMT_SRGGB12_1X12;
+}
+
+static int imx477_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+	struct imx477 *imx477 = to_imx477(sd);
+	struct v4l2_mbus_framefmt *try_fmt_img =
+		v4l2_subdev_get_try_format(sd, fh->pad, IMAGE_PAD);
+	struct v4l2_mbus_framefmt *try_fmt_meta =
+		v4l2_subdev_get_try_format(sd, fh->pad, METADATA_PAD);
+	struct v4l2_rect *try_crop;
+
+	mutex_lock(&imx477->mutex);
+
+	/* Initialize try_fmt for the image pad */
+	try_fmt_img->width = supported_modes_12bit[0].width;
+	try_fmt_img->height = supported_modes_12bit[0].height;
+	try_fmt_img->code = imx477_get_format_code(imx477,
+						   MEDIA_BUS_FMT_SRGGB12_1X12);
+	try_fmt_img->field = V4L2_FIELD_NONE;
+
+	/* Initialize try_fmt for the embedded metadata pad */
+	try_fmt_meta->width = IMX477_EMBEDDED_LINE_WIDTH;
+	try_fmt_meta->height = IMX477_NUM_EMBEDDED_LINES;
+	try_fmt_meta->code = MEDIA_BUS_FMT_SENSOR_DATA;
+	try_fmt_meta->field = V4L2_FIELD_NONE;
+
+	/* Initialize try_crop */
+	try_crop = v4l2_subdev_get_try_crop(sd, fh->pad, IMAGE_PAD);
+	try_crop->left = IMX477_PIXEL_ARRAY_LEFT;
+	try_crop->top = IMX477_PIXEL_ARRAY_TOP;
+	try_crop->width = IMX477_PIXEL_ARRAY_WIDTH;
+	try_crop->height = IMX477_PIXEL_ARRAY_HEIGHT;
+
+	mutex_unlock(&imx477->mutex);
+
+	return 0;
+}
+
+static void imx477_adjust_exposure_range(struct imx477 *imx477)
+{
+	int exposure_max, exposure_def;
+
+	/* Honour the VBLANK limits when setting exposure. */
+	exposure_max = imx477->mode->height + imx477->vblank->val -
+		       IMX477_EXPOSURE_OFFSET;
+	exposure_def = min(exposure_max, imx477->exposure->val);
+	__v4l2_ctrl_modify_range(imx477->exposure, imx477->exposure->minimum,
+				 exposure_max, imx477->exposure->step,
+				 exposure_def);
+}
+
+static int imx477_set_frame_length(struct imx477 *imx477, unsigned int val)
+{
+	int ret = 0;
+
+	imx477->long_exp_shift = 0;
+
+	while (val > IMX477_FRAME_LENGTH_MAX) {
+		imx477->long_exp_shift++;
+		val >>= 1;
+	}
+
+	ret = imx477_write_reg(imx477, IMX477_REG_FRAME_LENGTH,
+			       IMX477_REG_VALUE_16BIT, val);
+	if (ret)
+		return ret;
+
+	return imx477_write_reg(imx477, IMX477_LONG_EXP_SHIFT_REG,
+				IMX477_REG_VALUE_08BIT, imx477->long_exp_shift);
+}
+
+static int imx477_set_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct imx477 *imx477 =
+		container_of(ctrl->handler, struct imx477, ctrl_handler);
+	struct i2c_client *client = v4l2_get_subdevdata(&imx477->sd);
+	int ret = 0;
+
+	/*
+	 * The VBLANK control may change the limits of usable exposure, so check
+	 * and adjust if necessary.
+	 */
+	if (ctrl->id == V4L2_CID_VBLANK)
+		imx477_adjust_exposure_range(imx477);
+
+	/*
+	 * Applying V4L2 control value only happens
+	 * when power is up for streaming
+	 */
+	if (pm_runtime_get_if_in_use(&client->dev) == 0)
+		return 0;
+
+	switch (ctrl->id) {
+	case V4L2_CID_ANALOGUE_GAIN:
+		ret = imx477_write_reg(imx477, IMX477_REG_ANALOG_GAIN,
+				       IMX477_REG_VALUE_16BIT, ctrl->val);
+		break;
+	case V4L2_CID_EXPOSURE:
+		ret = imx477_write_reg(imx477, IMX477_REG_EXPOSURE,
+				       IMX477_REG_VALUE_16BIT, ctrl->val >>
+							imx477->long_exp_shift);
+		break;
+	case V4L2_CID_DIGITAL_GAIN:
+		ret = imx477_write_reg(imx477, IMX477_REG_DIGITAL_GAIN,
+				       IMX477_REG_VALUE_16BIT, ctrl->val);
+		break;
+	case V4L2_CID_TEST_PATTERN:
+		ret = imx477_write_reg(imx477, IMX477_REG_TEST_PATTERN,
+				       IMX477_REG_VALUE_16BIT,
+				       imx477_test_pattern_val[ctrl->val]);
+		break;
+	case V4L2_CID_TEST_PATTERN_RED:
+		ret = imx477_write_reg(imx477, IMX477_REG_TEST_PATTERN_R,
+				       IMX477_REG_VALUE_16BIT, ctrl->val);
+		break;
+	case V4L2_CID_TEST_PATTERN_GREENR:
+		ret = imx477_write_reg(imx477, IMX477_REG_TEST_PATTERN_GR,
+				       IMX477_REG_VALUE_16BIT, ctrl->val);
+		break;
+	case V4L2_CID_TEST_PATTERN_BLUE:
+		ret = imx477_write_reg(imx477, IMX477_REG_TEST_PATTERN_B,
+				       IMX477_REG_VALUE_16BIT, ctrl->val);
+		break;
+	case V4L2_CID_TEST_PATTERN_GREENB:
+		ret = imx477_write_reg(imx477, IMX477_REG_TEST_PATTERN_GB,
+				       IMX477_REG_VALUE_16BIT, ctrl->val);
+		break;
+	case V4L2_CID_HFLIP:
+	case V4L2_CID_VFLIP:
+		ret = imx477_write_reg(imx477, IMX477_REG_ORIENTATION, 1,
+				       imx477->hflip->val |
+				       imx477->vflip->val << 1);
+		break;
+	case V4L2_CID_VBLANK:
+		ret = imx477_set_frame_length(imx477,
+					      imx477->mode->height + ctrl->val);
+		break;
+	default:
+		dev_info(&client->dev,
+			 "ctrl(id:0x%x,val:0x%x) is not handled\n",
+			 ctrl->id, ctrl->val);
+		ret = -EINVAL;
+		break;
+	}
+
+	pm_runtime_put(&client->dev);
+
+	return ret;
+}
+
+static const struct v4l2_ctrl_ops imx477_ctrl_ops = {
+	.s_ctrl = imx477_set_ctrl,
+};
+
+static int imx477_enum_mbus_code(struct v4l2_subdev *sd,
+				 struct v4l2_subdev_pad_config *cfg,
+				 struct v4l2_subdev_mbus_code_enum *code)
+{
+	struct imx477 *imx477 = to_imx477(sd);
+
+	if (code->pad >= NUM_PADS)
+		return -EINVAL;
+
+	if (code->pad == IMAGE_PAD) {
+		if (code->index >= (ARRAY_SIZE(codes) / 4))
+			return -EINVAL;
+
+		code->code = imx477_get_format_code(imx477,
+						    codes[code->index * 4]);
+	} else {
+		if (code->index > 0)
+			return -EINVAL;
+
+		code->code = MEDIA_BUS_FMT_SENSOR_DATA;
+	}
+
+	return 0;
+}
+
+static int imx477_enum_frame_size(struct v4l2_subdev *sd,
+				  struct v4l2_subdev_pad_config *cfg,
+				  struct v4l2_subdev_frame_size_enum *fse)
+{
+	struct imx477 *imx477 = to_imx477(sd);
+
+	if (fse->pad >= NUM_PADS)
+		return -EINVAL;
+
+	if (fse->pad == IMAGE_PAD) {
+		const struct imx477_mode *mode_list;
+		unsigned int num_modes;
+
+		get_mode_table(fse->code, &mode_list, &num_modes);
+
+		if (fse->index >= num_modes)
+			return -EINVAL;
+
+		if (fse->code != imx477_get_format_code(imx477, fse->code))
+			return -EINVAL;
+
+		fse->min_width = mode_list[fse->index].width;
+		fse->max_width = fse->min_width;
+		fse->min_height = mode_list[fse->index].height;
+		fse->max_height = fse->min_height;
+	} else {
+		if (fse->code != MEDIA_BUS_FMT_SENSOR_DATA || fse->index > 0)
+			return -EINVAL;
+
+		fse->min_width = IMX477_EMBEDDED_LINE_WIDTH;
+		fse->max_width = fse->min_width;
+		fse->min_height = IMX477_NUM_EMBEDDED_LINES;
+		fse->max_height = fse->min_height;
+	}
+
+	return 0;
+}
+
+static void imx477_reset_colorspace(struct v4l2_mbus_framefmt *fmt)
+{
+	fmt->colorspace = V4L2_COLORSPACE_SRGB;
+	fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->colorspace);
+	fmt->quantization = V4L2_MAP_QUANTIZATION_DEFAULT(true,
+							  fmt->colorspace,
+							  fmt->ycbcr_enc);
+	fmt->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(fmt->colorspace);
+}
+
+static void imx477_update_image_pad_format(struct imx477 *imx477,
+					   const struct imx477_mode *mode,
+					   struct v4l2_subdev_format *fmt)
+{
+	fmt->format.width = mode->width;
+	fmt->format.height = mode->height;
+	fmt->format.field = V4L2_FIELD_NONE;
+	imx477_reset_colorspace(&fmt->format);
+}
+
+static void imx477_update_metadata_pad_format(struct v4l2_subdev_format *fmt)
+{
+	fmt->format.width = IMX477_EMBEDDED_LINE_WIDTH;
+	fmt->format.height = IMX477_NUM_EMBEDDED_LINES;
+	fmt->format.code = MEDIA_BUS_FMT_SENSOR_DATA;
+	fmt->format.field = V4L2_FIELD_NONE;
+}
+
+static int imx477_get_pad_format(struct v4l2_subdev *sd,
+				 struct v4l2_subdev_pad_config *cfg,
+				 struct v4l2_subdev_format *fmt)
+{
+	struct imx477 *imx477 = to_imx477(sd);
+
+	if (fmt->pad >= NUM_PADS)
+		return -EINVAL;
+
+	mutex_lock(&imx477->mutex);
+
+	if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
+		struct v4l2_mbus_framefmt *try_fmt =
+			v4l2_subdev_get_try_format(&imx477->sd, cfg, fmt->pad);
+		/* update the code which could change due to vflip or hflip: */
+		try_fmt->code = fmt->pad == IMAGE_PAD ?
+				imx477_get_format_code(imx477, try_fmt->code) :
+				MEDIA_BUS_FMT_SENSOR_DATA;
+		fmt->format = *try_fmt;
+	} else {
+		if (fmt->pad == IMAGE_PAD) {
+			imx477_update_image_pad_format(imx477, imx477->mode,
+						       fmt);
+			fmt->format.code =
+			       imx477_get_format_code(imx477, imx477->fmt_code);
+		} else {
+			imx477_update_metadata_pad_format(fmt);
+		}
+	}
+
+	mutex_unlock(&imx477->mutex);
+	return 0;
+}
+
+static
+unsigned int imx477_get_frame_length(const struct imx477_mode *mode,
+				     const struct v4l2_fract *timeperframe)
+{
+	u64 frame_length;
+
+	frame_length = (u64)timeperframe->numerator * IMX477_PIXEL_RATE;
+	do_div(frame_length,
+	       (u64)timeperframe->denominator * mode->line_length_pix);
+
+	if (WARN_ON(frame_length > IMX477_FRAME_LENGTH_MAX))
+		frame_length = IMX477_FRAME_LENGTH_MAX;
+
+	return max_t(unsigned int, frame_length, mode->height);
+}
+
+static void imx477_set_framing_limits(struct imx477 *imx477)
+{
+	unsigned int frm_length_min, frm_length_default, hblank;
+	const struct imx477_mode *mode = imx477->mode;
+
+	frm_length_min = imx477_get_frame_length(mode, &mode->timeperframe_min);
+	frm_length_default =
+		     imx477_get_frame_length(mode, &mode->timeperframe_default);
+
+	/* Default to no long exposure multiplier. */
+	imx477->long_exp_shift = 0;
+
+	/* Update limits and set FPS to default */
+	__v4l2_ctrl_modify_range(imx477->vblank, frm_length_min - mode->height,
+				 ((1 << IMX477_LONG_EXP_SHIFT_MAX) *
+					IMX477_FRAME_LENGTH_MAX) - mode->height,
+				 1, frm_length_default - mode->height);
+
+	/* Setting this will adjust the exposure limits as well. */
+	__v4l2_ctrl_s_ctrl(imx477->vblank, frm_length_default - mode->height);
+
+	/*
+	 * Currently PPL is fixed to the mode specified value, so hblank
+	 * depends on mode->width only, and is not changeable in any
+	 * way other than changing the mode.
+	 */
+	hblank = mode->line_length_pix - mode->width;
+	__v4l2_ctrl_modify_range(imx477->hblank, hblank, hblank, 1, hblank);
+}
+
+static int imx477_set_pad_format(struct v4l2_subdev *sd,
+				 struct v4l2_subdev_pad_config *cfg,
+				 struct v4l2_subdev_format *fmt)
+{
+	struct v4l2_mbus_framefmt *framefmt;
+	const struct imx477_mode *mode;
+	struct imx477 *imx477 = to_imx477(sd);
+
+	if (fmt->pad >= NUM_PADS)
+		return -EINVAL;
+
+	mutex_lock(&imx477->mutex);
+
+	if (fmt->pad == IMAGE_PAD) {
+		const struct imx477_mode *mode_list;
+		unsigned int num_modes;
+
+		/* Bayer order varies with flips */
+		fmt->format.code = imx477_get_format_code(imx477,
+							  fmt->format.code);
+
+		get_mode_table(fmt->format.code, &mode_list, &num_modes);
+
+		mode = v4l2_find_nearest_size(mode_list,
+					      num_modes,
+					      width, height,
+					      fmt->format.width,
+					      fmt->format.height);
+		imx477_update_image_pad_format(imx477, mode, fmt);
+		if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
+			framefmt = v4l2_subdev_get_try_format(sd, cfg,
+							      fmt->pad);
+			*framefmt = fmt->format;
+		} else {
+			imx477->mode = mode;
+			imx477->fmt_code = fmt->format.code;
+			imx477_set_framing_limits(imx477);
+		}
+	} else {
+		if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
+			framefmt = v4l2_subdev_get_try_format(sd, cfg,
+							      fmt->pad);
+			*framefmt = fmt->format;
+		} else {
+			/* Only one embedded data mode is supported */
+			imx477_update_metadata_pad_format(fmt);
+		}
+	}
+
+	mutex_unlock(&imx477->mutex);
+
+	return 0;
+}
+
+static const struct v4l2_rect *
+__imx477_get_pad_crop(struct imx477 *imx477, struct v4l2_subdev_pad_config *cfg,
+		      unsigned int pad, enum v4l2_subdev_format_whence which)
+{
+	switch (which) {
+	case V4L2_SUBDEV_FORMAT_TRY:
+		return v4l2_subdev_get_try_crop(&imx477->sd, cfg, pad);
+	case V4L2_SUBDEV_FORMAT_ACTIVE:
+		return &imx477->mode->crop;
+	}
+
+	return NULL;
+}
+
+static int imx477_get_selection(struct v4l2_subdev *sd,
+				struct v4l2_subdev_pad_config *cfg,
+				struct v4l2_subdev_selection *sel)
+{
+	switch (sel->target) {
+	case V4L2_SEL_TGT_CROP: {
+		struct imx477 *imx477 = to_imx477(sd);
+
+		mutex_lock(&imx477->mutex);
+		sel->r = *__imx477_get_pad_crop(imx477, cfg, sel->pad,
+						sel->which);
+		mutex_unlock(&imx477->mutex);
+
+		return 0;
+	}
+
+	case V4L2_SEL_TGT_NATIVE_SIZE:
+		sel->r.left = 0;
+		sel->r.top = 0;
+		sel->r.width = IMX477_NATIVE_WIDTH;
+		sel->r.height = IMX477_NATIVE_HEIGHT;
+
+		return 0;
+
+	case V4L2_SEL_TGT_CROP_DEFAULT:
+	case V4L2_SEL_TGT_CROP_BOUNDS:
+		sel->r.left = IMX477_PIXEL_ARRAY_LEFT;
+		sel->r.top = IMX477_PIXEL_ARRAY_TOP;
+		sel->r.width = IMX477_PIXEL_ARRAY_WIDTH;
+		sel->r.height = IMX477_PIXEL_ARRAY_HEIGHT;
+
+		return 0;
+	}
+
+	return -EINVAL;
+}
+
+/* Start streaming */
+static int imx477_start_streaming(struct imx477 *imx477)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(&imx477->sd);
+	const struct imx477_reg_list *reg_list;
+	const struct imx477_reg_list *extra_regs;
+	int ret;
+
+	if (!imx477->common_regs_written) {
+		ret = imx477_write_regs(imx477, mode_common_regs,
+					ARRAY_SIZE(mode_common_regs));
+		if (!ret) {
+			extra_regs = &imx477->compatible_data->extra_regs;
+			ret = imx477_write_regs(imx477,	extra_regs->regs,
+						extra_regs->num_of_regs);
+		}
+
+		if (ret) {
+			dev_err(&client->dev, "%s failed to set common settings\n",
+				__func__);
+			return ret;
+		}
+		imx477->common_regs_written = true;
+	}
+
+	/* Apply default values of current mode */
+	reg_list = &imx477->mode->reg_list;
+	ret = imx477_write_regs(imx477, reg_list->regs, reg_list->num_of_regs);
+	if (ret) {
+		dev_err(&client->dev, "%s failed to set mode\n", __func__);
+		return ret;
+	}
+
+	/* Apply customized values from user */
+	ret =  __v4l2_ctrl_handler_setup(imx477->sd.ctrl_handler);
+	if (ret)
+		return ret;
+
+	/* set stream on register */
+	return imx477_write_reg(imx477, IMX477_REG_MODE_SELECT,
+				IMX477_REG_VALUE_08BIT, IMX477_MODE_STREAMING);
+}
+
+/* Stop streaming */
+static void imx477_stop_streaming(struct imx477 *imx477)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(&imx477->sd);
+	int ret;
+
+	/* set stream off register */
+	ret = imx477_write_reg(imx477, IMX477_REG_MODE_SELECT,
+			       IMX477_REG_VALUE_08BIT, IMX477_MODE_STANDBY);
+	if (ret)
+		dev_err(&client->dev, "%s failed to set stream\n", __func__);
+}
+
+static int imx477_set_stream(struct v4l2_subdev *sd, int enable)
+{
+	struct imx477 *imx477 = to_imx477(sd);
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	int ret = 0;
+
+	mutex_lock(&imx477->mutex);
+	if (imx477->streaming == enable) {
+		mutex_unlock(&imx477->mutex);
+		return 0;
+	}
+
+	if (enable) {
+		ret = pm_runtime_get_sync(&client->dev);
+		if (ret < 0) {
+			pm_runtime_put_noidle(&client->dev);
+			goto err_unlock;
+		}
+
+		/*
+		 * Apply default & customized values
+		 * and then start streaming.
+		 */
+		ret = imx477_start_streaming(imx477);
+		if (ret)
+			goto err_rpm_put;
+	} else {
+		imx477_stop_streaming(imx477);
+		pm_runtime_put(&client->dev);
+	}
+
+	imx477->streaming = enable;
+
+	/* vflip and hflip cannot change during streaming */
+	__v4l2_ctrl_grab(imx477->vflip, enable);
+	__v4l2_ctrl_grab(imx477->hflip, enable);
+
+	mutex_unlock(&imx477->mutex);
+
+	return ret;
+
+err_rpm_put:
+	pm_runtime_put(&client->dev);
+err_unlock:
+	mutex_unlock(&imx477->mutex);
+
+	return ret;
+}
+
+/* Power/clock management functions */
+static int imx477_power_on(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct v4l2_subdev *sd = i2c_get_clientdata(client);
+	struct imx477 *imx477 = to_imx477(sd);
+	int ret;
+
+	ret = regulator_bulk_enable(IMX477_NUM_SUPPLIES,
+				    imx477->supplies);
+	if (ret) {
+		dev_err(&client->dev, "%s: failed to enable regulators\n",
+			__func__);
+		return ret;
+	}
+
+	ret = clk_prepare_enable(imx477->xclk);
+	if (ret) {
+		dev_err(&client->dev, "%s: failed to enable clock\n",
+			__func__);
+		goto reg_off;
+	}
+
+	gpiod_set_value_cansleep(imx477->reset_gpio, 1);
+	usleep_range(IMX477_XCLR_MIN_DELAY_US,
+		     IMX477_XCLR_MIN_DELAY_US + IMX477_XCLR_DELAY_RANGE_US);
+
+	return 0;
+
+reg_off:
+	regulator_bulk_disable(IMX477_NUM_SUPPLIES, imx477->supplies);
+	return ret;
+}
+
+static int imx477_power_off(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct v4l2_subdev *sd = i2c_get_clientdata(client);
+	struct imx477 *imx477 = to_imx477(sd);
+
+	gpiod_set_value_cansleep(imx477->reset_gpio, 0);
+	regulator_bulk_disable(IMX477_NUM_SUPPLIES, imx477->supplies);
+	clk_disable_unprepare(imx477->xclk);
+
+	/* Force reprogramming of the common registers when powered up again. */
+	imx477->common_regs_written = false;
+
+	return 0;
+}
+
+static int __maybe_unused imx477_suspend(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct v4l2_subdev *sd = i2c_get_clientdata(client);
+	struct imx477 *imx477 = to_imx477(sd);
+
+	if (imx477->streaming)
+		imx477_stop_streaming(imx477);
+
+	return 0;
+}
+
+static int __maybe_unused imx477_resume(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct v4l2_subdev *sd = i2c_get_clientdata(client);
+	struct imx477 *imx477 = to_imx477(sd);
+	int ret;
+
+	if (imx477->streaming) {
+		ret = imx477_start_streaming(imx477);
+		if (ret)
+			goto error;
+	}
+
+	return 0;
+
+error:
+	imx477_stop_streaming(imx477);
+	imx477->streaming = 0;
+	return ret;
+}
+
+static int imx477_get_regulators(struct imx477 *imx477)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(&imx477->sd);
+	unsigned int i;
+
+	for (i = 0; i < IMX477_NUM_SUPPLIES; i++)
+		imx477->supplies[i].supply = imx477_supply_name[i];
+
+	return devm_regulator_bulk_get(&client->dev,
+				       IMX477_NUM_SUPPLIES,
+				       imx477->supplies);
+}
+
+/* Verify chip ID */
+static int imx477_identify_module(struct imx477 *imx477, u32 expected_id)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(&imx477->sd);
+	int ret;
+	u32 val;
+
+	ret = imx477_read_reg(imx477, IMX477_REG_CHIP_ID,
+			      IMX477_REG_VALUE_16BIT, &val);
+	if (ret) {
+		dev_err(&client->dev, "failed to read chip id %x, with error %d\n",
+			expected_id, ret);
+		return ret;
+	}
+
+	if (val != expected_id) {
+		dev_err(&client->dev, "chip id mismatch: %x!=%x\n",
+			expected_id, val);
+		return -EIO;
+	}
+
+	dev_info(&client->dev, "Device found is imx%x\n", val);
+
+	return 0;
+}
+
+static const struct v4l2_subdev_core_ops imx477_core_ops = {
+	.subscribe_event = v4l2_ctrl_subdev_subscribe_event,
+	.unsubscribe_event = v4l2_event_subdev_unsubscribe,
+};
+
+static const struct v4l2_subdev_video_ops imx477_video_ops = {
+	.s_stream = imx477_set_stream,
+};
+
+static const struct v4l2_subdev_pad_ops imx477_pad_ops = {
+	.enum_mbus_code = imx477_enum_mbus_code,
+	.get_fmt = imx477_get_pad_format,
+	.set_fmt = imx477_set_pad_format,
+	.get_selection = imx477_get_selection,
+	.enum_frame_size = imx477_enum_frame_size,
+};
+
+static const struct v4l2_subdev_ops imx477_subdev_ops = {
+	.core = &imx477_core_ops,
+	.video = &imx477_video_ops,
+	.pad = &imx477_pad_ops,
+};
+
+static const struct v4l2_subdev_internal_ops imx477_internal_ops = {
+	.open = imx477_open,
+};
+
+/* Initialize control handlers */
+static int imx477_init_controls(struct imx477 *imx477)
+{
+	struct v4l2_ctrl_handler *ctrl_hdlr;
+	struct i2c_client *client = v4l2_get_subdevdata(&imx477->sd);
+	struct v4l2_fwnode_device_properties props;
+	unsigned int i;
+	int ret;
+
+	ctrl_hdlr = &imx477->ctrl_handler;
+	ret = v4l2_ctrl_handler_init(ctrl_hdlr, 16);
+	if (ret)
+		return ret;
+
+	mutex_init(&imx477->mutex);
+	ctrl_hdlr->lock = &imx477->mutex;
+
+	/* By default, PIXEL_RATE is read only */
+	imx477->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, &imx477_ctrl_ops,
+					       V4L2_CID_PIXEL_RATE,
+					       IMX477_PIXEL_RATE,
+					       IMX477_PIXEL_RATE, 1,
+					       IMX477_PIXEL_RATE);
+
+	/*
+	 * Create the controls here, but mode specific limits are setup
+	 * in the imx477_set_framing_limits() call below.
+	 */
+	imx477->vblank = v4l2_ctrl_new_std(ctrl_hdlr, &imx477_ctrl_ops,
+					   V4L2_CID_VBLANK, 0, 0xffff, 1, 0);
+	imx477->hblank = v4l2_ctrl_new_std(ctrl_hdlr, &imx477_ctrl_ops,
+					   V4L2_CID_HBLANK, 0, 0xffff, 1, 0);
+
+	/* HBLANK is read-only for now, but does change with mode. */
+	if (imx477->hblank)
+		imx477->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+
+	imx477->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &imx477_ctrl_ops,
+					     V4L2_CID_EXPOSURE,
+					     IMX477_EXPOSURE_MIN,
+					     IMX477_EXPOSURE_MAX,
+					     IMX477_EXPOSURE_STEP,
+					     IMX477_EXPOSURE_DEFAULT);
+
+	v4l2_ctrl_new_std(ctrl_hdlr, &imx477_ctrl_ops, V4L2_CID_ANALOGUE_GAIN,
+			  IMX477_ANA_GAIN_MIN, IMX477_ANA_GAIN_MAX,
+			  IMX477_ANA_GAIN_STEP, IMX477_ANA_GAIN_DEFAULT);
+
+	v4l2_ctrl_new_std(ctrl_hdlr, &imx477_ctrl_ops, V4L2_CID_DIGITAL_GAIN,
+			  IMX477_DGTL_GAIN_MIN, IMX477_DGTL_GAIN_MAX,
+			  IMX477_DGTL_GAIN_STEP, IMX477_DGTL_GAIN_DEFAULT);
+
+	imx477->hflip = v4l2_ctrl_new_std(ctrl_hdlr, &imx477_ctrl_ops,
+					  V4L2_CID_HFLIP, 0, 1, 1, 0);
+	if (imx477->hflip)
+		imx477->hflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT;
+
+	imx477->vflip = v4l2_ctrl_new_std(ctrl_hdlr, &imx477_ctrl_ops,
+					  V4L2_CID_VFLIP, 0, 1, 1, 0);
+	if (imx477->vflip)
+		imx477->vflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT;
+
+	v4l2_ctrl_new_std_menu_items(ctrl_hdlr, &imx477_ctrl_ops,
+				     V4L2_CID_TEST_PATTERN,
+				     ARRAY_SIZE(imx477_test_pattern_menu) - 1,
+				     0, 0, imx477_test_pattern_menu);
+	for (i = 0; i < 4; i++) {
+		/*
+		 * The assumption is that
+		 * V4L2_CID_TEST_PATTERN_GREENR == V4L2_CID_TEST_PATTERN_RED + 1
+		 * V4L2_CID_TEST_PATTERN_BLUE   == V4L2_CID_TEST_PATTERN_RED + 2
+		 * V4L2_CID_TEST_PATTERN_GREENB == V4L2_CID_TEST_PATTERN_RED + 3
+		 */
+		v4l2_ctrl_new_std(ctrl_hdlr, &imx477_ctrl_ops,
+				  V4L2_CID_TEST_PATTERN_RED + i,
+				  IMX477_TEST_PATTERN_COLOUR_MIN,
+				  IMX477_TEST_PATTERN_COLOUR_MAX,
+				  IMX477_TEST_PATTERN_COLOUR_STEP,
+				  IMX477_TEST_PATTERN_COLOUR_MAX);
+		/* The "Solid color" pattern is white by default */
+	}
+
+	if (ctrl_hdlr->error) {
+		ret = ctrl_hdlr->error;
+		dev_err(&client->dev, "%s control init failed (%d)\n",
+			__func__, ret);
+		goto error;
+	}
+
+	ret = v4l2_fwnode_device_parse(&client->dev, &props);
+	if (ret)
+		goto error;
+
+	ret = v4l2_ctrl_new_fwnode_properties(ctrl_hdlr, &imx477_ctrl_ops,
+					      &props);
+	if (ret)
+		goto error;
+
+	imx477->sd.ctrl_handler = ctrl_hdlr;
+
+	/* Setup exposure and frame/line length limits. */
+	imx477_set_framing_limits(imx477);
+
+	return 0;
+
+error:
+	v4l2_ctrl_handler_free(ctrl_hdlr);
+	mutex_destroy(&imx477->mutex);
+
+	return ret;
+}
+
+static void imx477_free_controls(struct imx477 *imx477)
+{
+	v4l2_ctrl_handler_free(imx477->sd.ctrl_handler);
+	mutex_destroy(&imx477->mutex);
+}
+
+static int imx477_check_hwcfg(struct device *dev)
+{
+	struct fwnode_handle *endpoint;
+	struct v4l2_fwnode_endpoint ep_cfg = {
+		.bus_type = V4L2_MBUS_CSI2_DPHY
+	};
+	int ret = -EINVAL;
+
+	endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(dev), NULL);
+	if (!endpoint) {
+		dev_err(dev, "endpoint node not found\n");
+		return -EINVAL;
+	}
+
+	if (v4l2_fwnode_endpoint_alloc_parse(endpoint, &ep_cfg)) {
+		dev_err(dev, "could not parse endpoint\n");
+		goto error_out;
+	}
+
+	/* Check the number of MIPI CSI2 data lanes */
+	if (ep_cfg.bus.mipi_csi2.num_data_lanes != 2) {
+		dev_err(dev, "only 2 data lanes are currently supported\n");
+		goto error_out;
+	}
+
+	/* Check the link frequency set in device tree */
+	if (!ep_cfg.nr_of_link_frequencies) {
+		dev_err(dev, "link-frequency property not found in DT\n");
+		goto error_out;
+	}
+
+	if (ep_cfg.nr_of_link_frequencies != 1 ||
+	    ep_cfg.link_frequencies[0] != IMX477_DEFAULT_LINK_FREQ) {
+		dev_err(dev, "Link frequency not supported: %lld\n",
+			ep_cfg.link_frequencies[0]);
+		goto error_out;
+	}
+
+	ret = 0;
+
+error_out:
+	v4l2_fwnode_endpoint_free(&ep_cfg);
+	fwnode_handle_put(endpoint);
+
+	return ret;
+}
+
+static const struct imx477_compatible_data imx477_compatible = {
+	.chip_id = IMX477_CHIP_ID,
+	.extra_regs = {
+		.num_of_regs = 0,
+		.regs = NULL
+	}
+};
+
+static const struct imx477_reg imx378_regs[] = {
+	{0x3e35, 0x01},
+	{0x4421, 0x08},
+	{0x3ff9, 0x00},
+};
+
+static const struct imx477_compatible_data imx378_compatible = {
+	.chip_id = IMX378_CHIP_ID,
+	.extra_regs = {
+		.num_of_regs = ARRAY_SIZE(imx378_regs),
+		.regs = imx378_regs
+	}
+};
+
+static const struct of_device_id imx477_dt_ids[] = {
+	{ .compatible = "sony,imx477", .data = &imx477_compatible },
+	{ .compatible = "sony,imx378", .data = &imx378_compatible },
+	{ /* sentinel */ }
+};
+
+static int imx477_probe(struct i2c_client *client)
+{
+	struct device *dev = &client->dev;
+	struct imx477 *imx477;
+	const struct of_device_id *match;
+	int ret;
+
+	imx477 = devm_kzalloc(&client->dev, sizeof(*imx477), GFP_KERNEL);
+	if (!imx477)
+		return -ENOMEM;
+
+	v4l2_i2c_subdev_init(&imx477->sd, client, &imx477_subdev_ops);
+
+	match = of_match_device(imx477_dt_ids, dev);
+	if (!match)
+		return -ENODEV;
+	imx477->compatible_data =
+		(const struct imx477_compatible_data *)match->data;
+
+	/* Check the hardware configuration in device tree */
+	if (imx477_check_hwcfg(dev))
+		return -EINVAL;
+
+	/* Get system clock (xclk) */
+	imx477->xclk = devm_clk_get(dev, NULL);
+	if (IS_ERR(imx477->xclk)) {
+		dev_err(dev, "failed to get xclk\n");
+		return PTR_ERR(imx477->xclk);
+	}
+
+	imx477->xclk_freq = clk_get_rate(imx477->xclk);
+	if (imx477->xclk_freq != IMX477_XCLK_FREQ) {
+		dev_err(dev, "xclk frequency not supported: %d Hz\n",
+			imx477->xclk_freq);
+		return -EINVAL;
+	}
+
+	ret = imx477_get_regulators(imx477);
+	if (ret) {
+		dev_err(dev, "failed to get regulators\n");
+		return ret;
+	}
+
+	/* Request optional enable pin */
+	imx477->reset_gpio = devm_gpiod_get_optional(dev, "reset",
+						     GPIOD_OUT_HIGH);
+
+	/*
+	 * The sensor must be powered for imx477_identify_module()
+	 * to be able to read the CHIP_ID register
+	 */
+	ret = imx477_power_on(dev);
+	if (ret)
+		return ret;
+
+	ret = imx477_identify_module(imx477, imx477->compatible_data->chip_id);
+	if (ret)
+		goto error_power_off;
+
+	/* Initialize default format */
+	imx477_set_default_format(imx477);
+
+	/* Enable runtime PM and turn off the device */
+	pm_runtime_set_active(dev);
+	pm_runtime_enable(dev);
+	pm_runtime_idle(dev);
+
+	/* This needs the pm runtime to be registered. */
+	ret = imx477_init_controls(imx477);
+	if (ret)
+		goto error_power_off;
+
+	/* Initialize subdev */
+	imx477->sd.internal_ops = &imx477_internal_ops;
+	imx477->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE |
+			    V4L2_SUBDEV_FL_HAS_EVENTS;
+	imx477->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
+
+	/* Initialize source pads */
+	imx477->pad[IMAGE_PAD].flags = MEDIA_PAD_FL_SOURCE;
+	imx477->pad[METADATA_PAD].flags = MEDIA_PAD_FL_SOURCE;
+
+	ret = media_entity_pads_init(&imx477->sd.entity, NUM_PADS, imx477->pad);
+	if (ret) {
+		dev_err(dev, "failed to init entity pads: %d\n", ret);
+		goto error_handler_free;
+	}
+
+	ret = v4l2_async_register_subdev_sensor_common(&imx477->sd);
+	if (ret < 0) {
+		dev_err(dev, "failed to register sensor sub-device: %d\n", ret);
+		goto error_media_entity;
+	}
+
+	return 0;
+
+error_media_entity:
+	media_entity_cleanup(&imx477->sd.entity);
+
+error_handler_free:
+	imx477_free_controls(imx477);
+
+error_power_off:
+	pm_runtime_disable(&client->dev);
+	pm_runtime_set_suspended(&client->dev);
+	imx477_power_off(&client->dev);
+
+	return ret;
+}
+
+static int imx477_remove(struct i2c_client *client)
+{
+	struct v4l2_subdev *sd = i2c_get_clientdata(client);
+	struct imx477 *imx477 = to_imx477(sd);
+
+	v4l2_async_unregister_subdev(sd);
+	media_entity_cleanup(&sd->entity);
+	imx477_free_controls(imx477);
+
+	pm_runtime_disable(&client->dev);
+	if (!pm_runtime_status_suspended(&client->dev))
+		imx477_power_off(&client->dev);
+	pm_runtime_set_suspended(&client->dev);
+
+	return 0;
+}
+
+MODULE_DEVICE_TABLE(of, imx477_dt_ids);
+
+static const struct dev_pm_ops imx477_pm_ops = {
+	SET_SYSTEM_SLEEP_PM_OPS(imx477_suspend, imx477_resume)
+	SET_RUNTIME_PM_OPS(imx477_power_off, imx477_power_on, NULL)
+};
+
+static struct i2c_driver imx477_i2c_driver = {
+	.driver = {
+		.name = "imx477",
+		.of_match_table	= imx477_dt_ids,
+		.pm = &imx477_pm_ops,
+	},
+	.probe_new = imx477_probe,
+	.remove = imx477_remove,
+};
+
+module_i2c_driver(imx477_i2c_driver);
+
+MODULE_AUTHOR("Naushir Patuck <naush@raspberrypi.com>");
+MODULE_DESCRIPTION("Sony IMX477 sensor driver");
+MODULE_LICENSE("GPL v2");
diff -Nur --no-dereference linux-5.10.52-orig/drivers/media/i2c/irs1125.c linux-5.10.52-v7l+/drivers/media/i2c/irs1125.c
--- linux-5.10.52-orig/drivers/media/i2c/irs1125.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/media/i2c/irs1125.c	2021-07-25 16:46:05.698295374 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * A V4L2 driver for Infineon IRS1125 TOF cameras.
+ * Copyright (C) 2018, pieye GmbH
+ *
+ * Based on V4L2 OmniVision OV5647 Image Sensor driver
+ * Copyright (C) 2016 Ramiro Oliveira <roliveir@synopsys.com>
+ *
+ * DT / fwnode changes, and GPIO control taken from ov5640.c
+ * Copyright (C) 2011-2013 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright (C) 2014-2017 Mentor Graphics Inc.
+ *
+ */
+
+#include "irs1125.h"
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/firmware.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of_graph.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-fwnode.h>
+#include <media/v4l2-image-sizes.h>
+#include <media/v4l2-mediabus.h>
+
+#define CHECK_BIT(val, pos) ((val) & BIT(pos))
+
+#define SENSOR_NAME "irs1125"
+
+#define RESET_ACTIVE_DELAY_MS	 20
+
+#define IRS1125_ALTERNATE_FW "irs1125_af.bin"
+
+#define IRS1125_REG_SAFE_RECONFIG	0xA850
+#define IRS1125_REG_CSICFG		0xA882
+#define IRS1125_REG_DESIGN_STEP		0xB0AD
+#define IRS1125_REG_EFUSEVAL2		0xB09F
+#define IRS1125_REG_EFUSEVAL3		0xB0A0
+#define IRS1125_REG_EFUSEVAL4		0xB0A1
+#define IRS1125_REG_DMEM_SHADOW		0xC320
+
+#define IRS1125_DESIGN_STEP_EXPECTED	0x0a12
+
+#define IRS1125_ROW_START_DEF		0
+#define IRS1125_COLUMN_START_DEF	0
+#define IRS1125_WINDOW_HEIGHT_DEF	288
+#define IRS1125_WINDOW_WIDTH_DEF	352
+
+struct regval_list {
+	u16 addr;
+	u16 data;
+};
+
+struct irs1125 {
+	struct v4l2_subdev sd;
+	struct media_pad pad;
+	/* the parsed DT endpoint info */
+	struct v4l2_fwnode_endpoint ep;
+
+	struct clk *xclk;
+	struct v4l2_ctrl_handler ctrl_handler;
+
+	/* To serialize asynchronus callbacks */
+	struct mutex lock;
+
+	/* image data layout */
+	unsigned int num_seq;
+
+	/* reset pin */
+	struct gpio_desc *reset;
+
+	/* V4l2 Controls to grab */
+	struct v4l2_ctrl *ctrl_modplls;
+	struct v4l2_ctrl *ctrl_numseq;
+
+	int power_count;
+	bool mod_pll_init;
+};
+
+static inline struct irs1125 *to_state(struct v4l2_subdev *sd)
+{
+	return container_of(sd, struct irs1125, sd);
+}
+
+static const char *expo_ctrl_names[IRS1125_NUM_SEQ_ENTRIES] = {
+	"safe reconfiguration of exposure of sequence 0",
+	"safe reconfiguration of exposure of sequence 1",
+	"safe reconfiguration of exposure of sequence 2",
+	"safe reconfiguration of exposure of sequence 3",
+	"safe reconfiguration of exposure of sequence 4",
+	"safe reconfiguration of exposure of sequence 5",
+	"safe reconfiguration of exposure of sequence 6",
+	"safe reconfiguration of exposure of sequence 7",
+	"safe reconfiguration of exposure of sequence 8",
+	"safe reconfiguration of exposure of sequence 9",
+	"safe reconfiguration of exposure of sequence 10",
+	"safe reconfiguration of exposure of sequence 11",
+	"safe reconfiguration of exposure of sequence 12",
+	"safe reconfiguration of exposure of sequence 13",
+	"safe reconfiguration of exposure of sequence 14",
+	"safe reconfiguration of exposure of sequence 15",
+	"safe reconfiguration of exposure of sequence 16",
+	"safe reconfiguration of exposure of sequence 17",
+	"safe reconfiguration of exposure of sequence 18",
+	"safe reconfiguration of exposure of sequence 19",
+};
+
+static const char *frame_ctrl_names[IRS1125_NUM_SEQ_ENTRIES] = {
+	"safe reconfiguration of framerate of sequence 0",
+	"safe reconfiguration of framerate of sequence 1",
+	"safe reconfiguration of framerate of sequence 2",
+	"safe reconfiguration of framerate of sequence 3",
+	"safe reconfiguration of framerate of sequence 4",
+	"safe reconfiguration of framerate of sequence 5",
+	"safe reconfiguration of framerate of sequence 6",
+	"safe reconfiguration of framerate of sequence 7",
+	"safe reconfiguration of framerate of sequence 8",
+	"safe reconfiguration of framerate of sequence 9",
+	"safe reconfiguration of framerate of sequence 10",
+	"safe reconfiguration of framerate of sequence 11",
+	"safe reconfiguration of framerate of sequence 12",
+	"safe reconfiguration of framerate of sequence 13",
+	"safe reconfiguration of framerate of sequence 14",
+	"safe reconfiguration of framerate of sequence 15",
+	"safe reconfiguration of framerate of sequence 16",
+	"safe reconfiguration of framerate of sequence 17",
+	"safe reconfiguration of framerate of sequence 18",
+	"safe reconfiguration of framerate of sequence 19",
+};
+
+static struct regval_list irs1125_26mhz[] = {
+	{0xB017, 0x0413},
+	{0xB086, 0x3535},
+	{0xB0AE, 0xEF02},
+	{0xA000, 0x0004},
+	{0xFFFF, 100},
+
+	{0xB062, 0x6383},
+	{0xB063, 0x55A8},
+	{0xB068, 0x7628},
+	{0xB069, 0x03E2},
+
+	{0xFFFF, 100},
+	{0xB05A, 0x01C5},
+	{0xB05C, 0x0206},
+	{0xB05D, 0x01C5},
+	{0xB05F, 0x0206},
+	{0xB016, 0x1335},
+	{0xFFFF, 100},
+	{0xA893, 0x8261},
+	{0xA894, 0x89d8},
+	{0xA895, 0x131d},
+	{0xA896, 0x4251},
+	{0xA897, 0x9D8A},
+	{0xA898, 0x0BD8},
+	{0xA899, 0x2245},
+	{0xA89A, 0xAB9B},
+	{0xA89B, 0x03B9},
+	{0xA89C, 0x8041},
+	{0xA89D, 0xE07E},
+	{0xA89E, 0x0307},
+	{0xFFFF, 100},
+	{0xA88D, 0x0004},
+	{0xA800, 0x0E68},
+	{0xA801, 0x0000},
+	{0xA802, 0x000C},
+	{0xA803, 0x0000},
+	{0xA804, 0x0E68},
+	{0xA805, 0x0000},
+	{0xA806, 0x0440},
+	{0xA807, 0x0000},
+	{0xA808, 0x0E68},
+	{0xA809, 0x0000},
+	{0xA80A, 0x0884},
+	{0xA80B, 0x0000},
+	{0xA80C, 0x0E68},
+	{0xA80D, 0x0000},
+	{0xA80E, 0x0CC8},
+	{0xA80F, 0x0000},
+	{0xA810, 0x0E68},
+	{0xA811, 0x0000},
+	{0xA812, 0x2000},
+	{0xA813, 0x0000},
+	{0xA882, 0x0081},
+	{0xA88C, 0x403A},
+	{0xA88F, 0x031E},
+	{0xA892, 0x0351},
+	{0x9813, 0x13FF},
+	{0x981B, 0x7608},
+
+	{0xB008, 0x0000},
+	{0xB015, 0x1513},
+
+	{0xFFFF, 100}
+};
+
+static struct regval_list irs1125_seq_cfg_init[] = {
+	{0xC3A0, 0x823D},
+	{0xC3A1, 0xB13B},
+	{0xC3A2, 0x0313},
+	{0xC3A3, 0x4659},
+	{0xC3A4, 0xC4EC},
+	{0xC3A5, 0x03CE},
+	{0xC3A6, 0x4259},
+	{0xC3A7, 0xC4EC},
+	{0xC3A8, 0x03CE},
+	{0xC3A9, 0x8839},
+	{0xC3AA, 0x89D8},
+	{0xC3AB, 0x031D},
+
+	{0xC24C, 0x5529},
+	{0xC24D, 0x0000},
+	{0xC24E, 0x1200},
+	{0xC24F, 0x6CB2},
+	{0xC250, 0x0000},
+	{0xC251, 0x5529},
+	{0xC252, 0x42F4},
+	{0xC253, 0xD1AF},
+	{0xC254, 0x8A18},
+	{0xC255, 0x0002},
+	{0xC256, 0x5529},
+	{0xC257, 0x6276},
+	{0xC258, 0x11A7},
+	{0xC259, 0xD907},
+	{0xC25A, 0x0000},
+	{0xC25B, 0x5529},
+	{0xC25C, 0x07E0},
+	{0xC25D, 0x7BFE},
+	{0xC25E, 0x6402},
+	{0xC25F, 0x0019},
+
+	{0xC3AC, 0x0007},
+	{0xC3AD, 0xED88},
+	{0xC320, 0x003E},
+	{0xC321, 0x0000},
+	{0xC322, 0x2000},
+	{0xC323, 0x0000},
+	{0xC324, 0x0271},
+	{0xC325, 0x0000},
+	{0xC326, 0x000C},
+	{0xC327, 0x0000},
+	{0xC328, 0x0271},
+	{0xC329, 0x0000},
+	{0xC32A, 0x0440},
+	{0xC32B, 0x0000},
+	{0xC32C, 0x0271},
+	{0xC32D, 0x0000},
+	{0xC32E, 0x0884},
+	{0xC32F, 0x0000},
+	{0xC330, 0x0271},
+	{0xC331, 0x0000},
+	{0xC332, 0x0CC8},
+	{0xC333, 0x0000},
+	{0xA88D, 0x0004},
+
+	{0xA890, 0x0000},
+	{0xC219, 0x0002},
+	{0xC21A, 0x0000},
+	{0xC21B, 0x0000},
+	{0xC21C, 0x00CD},
+	{0xC21D, 0x0009},
+	{0xC21E, 0x00CD},
+	{0xC21F, 0x0009},
+
+	{0xA87C, 0x0000},
+	{0xC032, 0x0001},
+	{0xC034, 0x0000},
+	{0xC035, 0x0001},
+	{0xC039, 0x0000},
+	{0xC401, 0x0002},
+
+	{0xFFFF, 1}
+};
+
+static int irs1125_write(struct v4l2_subdev *sd, u16 reg, u16 val)
+{
+	int ret;
+	unsigned char data[4] = { reg >> 8, reg & 0xff, val >> 8, val & 0xff};
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+	ret = i2c_master_send(client, data, 4);
+	if (ret < 0)
+		dev_err(&client->dev, "%s: i2c write error, reg: %x\n",
+			__func__, reg);
+
+	dev_dbg(&client->dev, "write addr 0x%04x, val 0x%04x\n", reg, val);
+	return ret;
+}
+
+static int irs1125_read(struct v4l2_subdev *sd, u16 reg, u16 *val)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct i2c_msg msgs[2];
+	u8 addr_buf[2] = { reg >> 8, reg & 0xff };
+	u8 data_buf[2] = { 0, };
+	int ret;
+
+	/* Write register address */
+	msgs[0].addr = client->addr;
+	msgs[0].flags = 0;
+	msgs[0].len = ARRAY_SIZE(addr_buf);
+	msgs[0].buf = addr_buf;
+
+	/* Read data from register */
+	msgs[1].addr = client->addr;
+	msgs[1].flags = I2C_M_RD;
+	msgs[1].len = 2;
+	msgs[1].buf = data_buf;
+
+	ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
+	if (ret != ARRAY_SIZE(msgs)) {
+		if (ret >= 0)
+			ret = -EIO;
+		return ret;
+	}
+
+	*val = data_buf[1] | (data_buf[0] << 8);
+
+	return 0;
+}
+
+static int irs1125_write_array(struct v4l2_subdev *sd,
+			       struct regval_list *regs, int array_size)
+{
+	int i, ret;
+
+	for (i = 0; i < array_size; i++) {
+		if (regs[i].addr == 0xFFFF) {
+			msleep(regs[i].data);
+		} else {
+			ret = irs1125_write(sd, regs[i].addr, regs[i].data);
+			if (ret < 0)
+				return ret;
+		}
+	}
+
+	return 0;
+}
+
+static int irs1125_stream_on(struct v4l2_subdev *sd)
+{
+	int ret;
+	struct irs1125 *irs1125 = to_state(sd);
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+	v4l2_ctrl_grab(irs1125->ctrl_numseq, 1);
+	v4l2_ctrl_grab(irs1125->ctrl_modplls, 1);
+
+	ret = irs1125_write(sd, 0xC400, 0x0001);
+	if (ret < 0) {
+		dev_err(&client->dev, "error enabling firmware: %d", ret);
+		return ret;
+	}
+
+	msleep(100);
+
+	return irs1125_write(sd, 0xA87C, 0x0001);
+}
+
+static int irs1125_stream_off(struct v4l2_subdev *sd)
+{
+	int ret;
+	struct irs1125 *irs1125 = to_state(sd);
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+	v4l2_ctrl_grab(irs1125->ctrl_numseq, 0);
+	v4l2_ctrl_grab(irs1125->ctrl_modplls, 0);
+
+	ret = irs1125_write(sd, 0xA87C, 0x0000);
+	if (ret < 0) {
+		dev_err(&client->dev, "error disabling trigger: %d", ret);
+		return ret;
+	}
+
+	msleep(100);
+
+	return irs1125_write(sd, 0xC400, 0x0002);
+}
+
+static int __sensor_init(struct v4l2_subdev *sd)
+{
+	unsigned int cnt, idx;
+	int ret;
+	u16 val;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct irs1125 *irs1125 = to_state(sd);
+	const struct firmware *fw;
+	struct regval_list *reg_data;
+
+	cnt = 0;
+	while (1) {
+		ret = irs1125_read(sd, 0xC40F, &val);
+		if (ret < 0) {
+			dev_err(&client->dev, "read register 0xC40F failed\n");
+			return ret;
+		}
+		if (CHECK_BIT(val, 14) == 0)
+			break;
+
+		if (cnt >= 5) {
+			dev_err(&client->dev, "timeout waiting for 0xC40F\n");
+			return -EAGAIN;
+		}
+
+		cnt++;
+	}
+
+	ret = irs1125_write_array(sd, irs1125_26mhz,
+				  ARRAY_SIZE(irs1125_26mhz));
+	if (ret < 0) {
+		dev_err(&client->dev, "write sensor default regs error\n");
+		return ret;
+	}
+
+	/* set CSI-2 number of data lanes */
+	if (irs1125->ep.bus.mipi_csi2.num_data_lanes == 1) {
+		val = 0x0001;
+	} else if (irs1125->ep.bus.mipi_csi2.num_data_lanes == 2) {
+		val = 0x0081;
+	} else {
+		dev_err(&client->dev, "invalid number of data lanes %d\n",
+			irs1125->ep.bus.mipi_csi2.num_data_lanes);
+		return -EINVAL;
+	}
+
+	ret = irs1125_write(sd, IRS1125_REG_CSICFG, val);
+	if (ret < 0) {
+		dev_err(&client->dev, "write sensor csi2 config error\n");
+		return ret;
+	}
+
+	/* request the firmware, this will block and timeout */
+	ret = request_firmware(&fw, IRS1125_ALTERNATE_FW, &client->dev);
+	if (ret) {
+		dev_err(&client->dev,
+			"did not find the firmware file '%s' (status %d)\n",
+			IRS1125_ALTERNATE_FW, ret);
+		return ret;
+	}
+
+	if (fw->size % 4) {
+		dev_err(&client->dev, "firmware file '%s' invalid\n",
+			IRS1125_ALTERNATE_FW);
+		release_firmware(fw);
+		return -EINVAL;
+	}
+
+	for (idx = 0; idx < fw->size; idx += 4)	{
+		reg_data = (struct regval_list *)&fw->data[idx];
+		ret = irs1125_write(sd, reg_data->addr, reg_data->data);
+		if (ret < 0) {
+			dev_err(&client->dev, "firmware write error\n");
+			release_firmware(fw);
+			return ret;
+		}
+	}
+	release_firmware(fw);
+
+	ret = irs1125_write_array(sd, irs1125_seq_cfg_init,
+				  ARRAY_SIZE(irs1125_seq_cfg_init));
+	if (ret < 0) {
+		dev_err(&client->dev, "write default sequence failed\n");
+		return ret;
+	}
+
+	irs1125->mod_pll_init = true;
+	v4l2_ctrl_handler_setup(&irs1125->ctrl_handler);
+	irs1125->mod_pll_init = false;
+
+	return irs1125_write(sd, 0xA87C, 0x0001);
+}
+
+static int irs1125_sensor_power(struct v4l2_subdev *sd, int on)
+{
+	int ret = 0;
+	struct irs1125 *irs1125 = to_state(sd);
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+	mutex_lock(&irs1125->lock);
+
+	if (on && !irs1125->power_count) {
+		gpiod_set_value_cansleep(irs1125->reset, 1);
+		msleep(RESET_ACTIVE_DELAY_MS);
+
+		ret = clk_prepare_enable(irs1125->xclk);
+		if (ret < 0) {
+			dev_err(&client->dev, "clk prepare enable failed\n");
+			goto out;
+		}
+
+		ret = __sensor_init(sd);
+		if (ret < 0) {
+			clk_disable_unprepare(irs1125->xclk);
+			dev_err(&client->dev,
+				"Camera not available, check Power\n");
+			goto out;
+		}
+	} else if (!on && irs1125->power_count == 1) {
+		gpiod_set_value_cansleep(irs1125->reset, 0);
+	}
+
+	/* Update the power count. */
+	irs1125->power_count += on ? 1 : -1;
+	WARN_ON(irs1125->power_count < 0);
+
+out:
+	mutex_unlock(&irs1125->lock);
+
+	return ret;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int irs1125_sensor_get_register(struct v4l2_subdev *sd,
+				       struct v4l2_dbg_register *reg)
+{
+	u16 val;
+	int ret;
+
+	ret = irs1125_read(sd, reg->reg & 0xffff, &val);
+	if (ret < 0)
+		return ret;
+
+	reg->val = val;
+	reg->size = 1;
+
+	return 0;
+}
+
+static int irs1125_sensor_set_register(struct v4l2_subdev *sd,
+				       const struct v4l2_dbg_register *reg)
+{
+	return irs1125_write(sd, reg->reg & 0xffff, reg->val & 0xffff);
+}
+#endif
+
+static const struct v4l2_subdev_core_ops irs1125_subdev_core_ops = {
+	.s_power = irs1125_sensor_power,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+	.g_register = irs1125_sensor_get_register,
+	.s_register = irs1125_sensor_set_register,
+#endif
+};
+
+static int irs1125_s_stream(struct v4l2_subdev *sd, int enable)
+{
+	if (enable)
+		return irs1125_stream_on(sd);
+	else
+		return irs1125_stream_off(sd);
+}
+
+static const struct v4l2_subdev_video_ops irs1125_subdev_video_ops = {
+	.s_stream = irs1125_s_stream,
+};
+
+static int irs1125_enum_mbus_code(struct v4l2_subdev *sd,
+				  struct v4l2_subdev_pad_config *cfg,
+	struct v4l2_subdev_mbus_code_enum *code)
+{
+	if (code->index > 0)
+		return -EINVAL;
+
+	code->code = MEDIA_BUS_FMT_Y12_1X12;
+
+	return 0;
+}
+
+static int irs1125_set_get_fmt(struct v4l2_subdev *sd,
+			       struct v4l2_subdev_pad_config *cfg,
+			       struct v4l2_subdev_format *format)
+{
+	struct v4l2_mbus_framefmt *fmt = &format->format;
+	struct irs1125 *irs1125 = to_state(sd);
+
+	if (format->pad != 0)
+		return -EINVAL;
+
+	/* Only one format is supported, so return that */
+	memset(fmt, 0, sizeof(*fmt));
+	fmt->code = MEDIA_BUS_FMT_Y12_1X12;
+	fmt->colorspace = V4L2_COLORSPACE_RAW;
+	fmt->field = V4L2_FIELD_NONE;
+	fmt->width = IRS1125_WINDOW_WIDTH_DEF;
+	fmt->height = IRS1125_WINDOW_HEIGHT_DEF * irs1125->num_seq;
+
+	return 0;
+}
+
+static const struct v4l2_subdev_pad_ops irs1125_subdev_pad_ops = {
+	.enum_mbus_code = irs1125_enum_mbus_code,
+	.set_fmt = irs1125_set_get_fmt,
+	.get_fmt = irs1125_set_get_fmt,
+};
+
+static const struct v4l2_subdev_ops irs1125_subdev_ops = {
+	.core = &irs1125_subdev_core_ops,
+	.video = &irs1125_subdev_video_ops,
+	.pad = &irs1125_subdev_pad_ops,
+};
+
+static int irs1125_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct irs1125 *dev = container_of(ctrl->handler,
+					struct irs1125, ctrl_handler);
+	struct i2c_client *client = v4l2_get_subdevdata(&dev->sd);
+	int err = 0, i;
+
+	switch (ctrl->id) {
+	case IRS1125_CID_SAFE_RECONFIG_S0_EXPO:
+	case IRS1125_CID_SAFE_RECONFIG_S0_FRAME:
+	case IRS1125_CID_SAFE_RECONFIG_S1_EXPO:
+	case IRS1125_CID_SAFE_RECONFIG_S1_FRAME:
+	case IRS1125_CID_SAFE_RECONFIG_S2_EXPO:
+	case IRS1125_CID_SAFE_RECONFIG_S2_FRAME:
+	case IRS1125_CID_SAFE_RECONFIG_S3_EXPO:
+	case IRS1125_CID_SAFE_RECONFIG_S3_FRAME:
+	case IRS1125_CID_SAFE_RECONFIG_S4_EXPO:
+	case IRS1125_CID_SAFE_RECONFIG_S4_FRAME:
+	case IRS1125_CID_SAFE_RECONFIG_S5_EXPO:
+	case IRS1125_CID_SAFE_RECONFIG_S5_FRAME:
+	case IRS1125_CID_SAFE_RECONFIG_S6_EXPO:
+	case IRS1125_CID_SAFE_RECONFIG_S6_FRAME:
+	case IRS1125_CID_SAFE_RECONFIG_S7_EXPO:
+	case IRS1125_CID_SAFE_RECONFIG_S7_FRAME:
+	case IRS1125_CID_SAFE_RECONFIG_S8_EXPO:
+	case IRS1125_CID_SAFE_RECONFIG_S8_FRAME:
+	case IRS1125_CID_SAFE_RECONFIG_S9_EXPO:
+	case IRS1125_CID_SAFE_RECONFIG_S9_FRAME:
+	case IRS1125_CID_SAFE_RECONFIG_S10_EXPO:
+	case IRS1125_CID_SAFE_RECONFIG_S10_FRAME:
+	case IRS1125_CID_SAFE_RECONFIG_S11_EXPO:
+	case IRS1125_CID_SAFE_RECONFIG_S11_FRAME:
+	case IRS1125_CID_SAFE_RECONFIG_S12_EXPO:
+	case IRS1125_CID_SAFE_RECONFIG_S12_FRAME:
+	case IRS1125_CID_SAFE_RECONFIG_S13_EXPO:
+	case IRS1125_CID_SAFE_RECONFIG_S13_FRAME:
+	case IRS1125_CID_SAFE_RECONFIG_S14_EXPO:
+	case IRS1125_CID_SAFE_RECONFIG_S14_FRAME:
+	case IRS1125_CID_SAFE_RECONFIG_S15_EXPO:
+	case IRS1125_CID_SAFE_RECONFIG_S15_FRAME:
+	case IRS1125_CID_SAFE_RECONFIG_S16_EXPO:
+	case IRS1125_CID_SAFE_RECONFIG_S16_FRAME:
+	case IRS1125_CID_SAFE_RECONFIG_S17_EXPO:
+	case IRS1125_CID_SAFE_RECONFIG_S17_FRAME:
+	case IRS1125_CID_SAFE_RECONFIG_S18_EXPO:
+	case IRS1125_CID_SAFE_RECONFIG_S18_FRAME:
+	case IRS1125_CID_SAFE_RECONFIG_S19_EXPO:
+	case IRS1125_CID_SAFE_RECONFIG_S19_FRAME: {
+		unsigned int offset = ctrl->id -
+			IRS1125_CID_SAFE_RECONFIG_S0_EXPO;
+
+		err = irs1125_write(&dev->sd,
+				    IRS1125_REG_SAFE_RECONFIG + offset,
+				    ctrl->val);
+		break;
+	}
+	case IRS1125_CID_MOD_PLL: {
+		struct irs1125_mod_pll *mod_new;
+
+		if (dev->mod_pll_init)
+			break;
+
+		mod_new = (struct irs1125_mod_pll *)ctrl->p_new.p;
+		for (i = 0; i < IRS1125_NUM_MOD_PLLS; i++) {
+			unsigned int pll_offset, ssc_offset;
+
+			pll_offset = i * 3;
+			ssc_offset = i * 5;
+
+			err = irs1125_write(&dev->sd, 0xC3A0 + pll_offset,
+					    mod_new[i].pllcfg1);
+			if (err < 0)
+				break;
+
+			err = irs1125_write(&dev->sd, 0xC3A1 + pll_offset,
+					    mod_new[i].pllcfg2);
+			if (err < 0)
+				break;
+
+			err = irs1125_write(&dev->sd, 0xC3A2 + pll_offset,
+					    mod_new[i].pllcfg3);
+			if (err < 0)
+				break;
+
+			err = irs1125_write(&dev->sd, 0xC24C + ssc_offset,
+					    mod_new[i].pllcfg4);
+			if (err < 0)
+				break;
+
+			err = irs1125_write(&dev->sd, 0xC24D + ssc_offset,
+					    mod_new[i].pllcfg5);
+			if (err < 0)
+				break;
+
+			err = irs1125_write(&dev->sd, 0xC24E + ssc_offset,
+					    mod_new[i].pllcfg6);
+			if (err < 0)
+				break;
+
+			err = irs1125_write(&dev->sd, 0xC24F + ssc_offset,
+					    mod_new[i].pllcfg7);
+			if (err < 0)
+				break;
+
+			err = irs1125_write(&dev->sd, 0xC250 + ssc_offset,
+					    mod_new[i].pllcfg8);
+			if (err < 0)
+				break;
+		}
+		break;
+	}
+	case IRS1125_CID_SEQ_CONFIG: {
+		struct irs1125_seq_cfg *cfg_new;
+
+		cfg_new = (struct irs1125_seq_cfg *)ctrl->p_new.p;
+		for (i = 0; i < IRS1125_NUM_SEQ_ENTRIES; i++) {
+			unsigned int seq_offset = i * 4;
+			u16 addr, val;
+
+			addr = IRS1125_REG_DMEM_SHADOW + seq_offset;
+			val = cfg_new[i].exposure;
+			err = irs1125_write(&dev->sd, addr, val);
+			if (err < 0)
+				break;
+
+			addr = IRS1125_REG_DMEM_SHADOW + 1 + seq_offset;
+			val = cfg_new[i].framerate;
+			err = irs1125_write(&dev->sd, addr, val);
+			if (err < 0)
+				break;
+
+			addr = IRS1125_REG_DMEM_SHADOW + 2 + seq_offset;
+			val = cfg_new[i].ps;
+			err = irs1125_write(&dev->sd, addr, val);
+			if (err < 0)
+				break;
+
+			addr = IRS1125_REG_DMEM_SHADOW + 3 + seq_offset;
+			val = cfg_new[i].pll;
+			err = irs1125_write(&dev->sd, addr, val);
+			if (err < 0)
+				break;
+		}
+		break;
+	}
+	case IRS1125_CID_NUM_SEQS:
+		err = irs1125_write(&dev->sd, 0xA88D, ctrl->val - 1);
+		if (err >= 0)
+			dev->num_seq = ctrl->val;
+		break;
+	case IRS1125_CID_CONTINUOUS_TRIG:
+		if (ctrl->val == 0)
+			err = irs1125_write(&dev->sd, 0xA87C, 0);
+		else
+			err = irs1125_write(&dev->sd, 0xA87C, 1);
+		break;
+	case IRS1125_CID_TRIGGER:
+		if (ctrl->val != 0) {
+			err = irs1125_write(&dev->sd, 0xA87C, 1);
+			if (err >= 0)
+				err = irs1125_write(&dev->sd, 0xA87C, 0);
+		}
+		break;
+	case IRS1125_CID_RECONFIG:
+		if (ctrl->val != 0)
+			err = irs1125_write(&dev->sd, 0xA87A, 1);
+		break;
+	case IRS1125_CID_ILLU_ON:
+		if (ctrl->val == 0)
+			err = irs1125_write(&dev->sd, 0xA892, 0x377);
+		else
+			err = irs1125_write(&dev->sd, 0xA892, 0x355);
+		break;
+	default:
+		break;
+	}
+
+	if (err < 0)
+		dev_err(&client->dev, "Error executing control ID: %d, val %d, err %d",
+			ctrl->id, ctrl->val, err);
+	else
+		err = 0;
+
+	return err;
+}
+
+static const struct v4l2_ctrl_ops irs1125_ctrl_ops = {
+	.s_ctrl = irs1125_s_ctrl,
+};
+
+static const struct v4l2_ctrl_config irs1125_custom_ctrls[] = {
+	{
+		.ops = &irs1125_ctrl_ops,
+		.id = IRS1125_CID_NUM_SEQS,
+		.name = "Change number of sequences",
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.flags = V4L2_CTRL_FLAG_MODIFY_LAYOUT,
+		.min = 1,
+		.max = 20,
+		.step = 1,
+		.def = 5,
+	}, {
+		.ops = &irs1125_ctrl_ops,
+		.id = IRS1125_CID_MOD_PLL,
+		.name = "Reconfigure modulation PLLs",
+		.type = V4L2_CTRL_TYPE_U16,
+		.flags = V4L2_CTRL_FLAG_HAS_PAYLOAD,
+		.min = 0,
+		.max = U16_MAX,
+		.step = 1,
+		.def = 0,
+		.elem_size = sizeof(u16),
+		.dims = {sizeof(struct irs1125_mod_pll) / sizeof(u16),
+			IRS1125_NUM_MOD_PLLS}
+	}, {
+		.ops = &irs1125_ctrl_ops,
+		.id = IRS1125_CID_SEQ_CONFIG,
+		.name = "Change sequence settings",
+		.type = V4L2_CTRL_TYPE_U16,
+		.flags = V4L2_CTRL_FLAG_HAS_PAYLOAD,
+		.min = 0,
+		.max = U16_MAX,
+		.step = 1,
+		.def = 0,
+		.elem_size = sizeof(u16),
+		.dims = {sizeof(struct irs1125_seq_cfg) / sizeof(u16),
+			IRS1125_NUM_SEQ_ENTRIES}
+	}, {
+		.ops = &irs1125_ctrl_ops,
+		.id = IRS1125_CID_CONTINUOUS_TRIG,
+		.name = "Enable/disable continuous trigger",
+		.type = V4L2_CTRL_TYPE_BOOLEAN,
+		.flags = V4L2_CTRL_FLAG_EXECUTE_ON_WRITE,
+		.min = 0,
+		.max = 1,
+		.step = 1,
+		.def = 0
+	}, {
+		.ops = &irs1125_ctrl_ops,
+		.id = IRS1125_CID_TRIGGER,
+		.name = "Capture a single sequence",
+		.type = V4L2_CTRL_TYPE_BOOLEAN,
+		.flags = V4L2_CTRL_FLAG_EXECUTE_ON_WRITE,
+		.min = 0,
+		.max = 1,
+		.step = 1,
+		.def = 0
+	}, {
+		.ops = &irs1125_ctrl_ops,
+		.id = IRS1125_CID_RECONFIG,
+		.name = "Trigger imager reconfiguration",
+		.type = V4L2_CTRL_TYPE_BOOLEAN,
+		.flags = V4L2_CTRL_FLAG_EXECUTE_ON_WRITE,
+		.min = 0,
+		.max = 1,
+		.step = 1,
+		.def = 0
+	}, {
+		.ops = &irs1125_ctrl_ops,
+		.id = IRS1125_CID_ILLU_ON,
+		.name = "Turn illu on or off",
+		.type = V4L2_CTRL_TYPE_BOOLEAN,
+		.flags = V4L2_CTRL_FLAG_EXECUTE_ON_WRITE,
+		.min = 0,
+		.max = 1,
+		.step = 1,
+		.def = 1
+	}, {
+		.ops = &irs1125_ctrl_ops,
+		.id = IRS1125_CID_IDENT0,
+		.name = "Get ident 0 information",
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.flags = V4L2_CTRL_FLAG_READ_ONLY,
+		.min = S32_MIN,
+		.max = S32_MAX,
+		.step = 1,
+		.def = 0
+	}, {
+		.ops = &irs1125_ctrl_ops,
+		.id = IRS1125_CID_IDENT1,
+		.name = "Get ident 1 information",
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.flags = V4L2_CTRL_FLAG_READ_ONLY,
+		.min = S32_MIN,
+		.max = S32_MAX,
+		.step = 1,
+		.def = 0
+	}, {
+		.ops = &irs1125_ctrl_ops,
+		.id = IRS1125_CID_IDENT2,
+		.name = "Get ident 2 information",
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.flags = V4L2_CTRL_FLAG_READ_ONLY,
+		.min = S32_MIN,
+		.max = S32_MAX,
+		.step = 1,
+		.def = 0
+	}
+};
+
+static int irs1125_detect(struct v4l2_subdev *sd)
+{
+	u16 read;
+	int ret;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+	ret = irs1125_read(sd, IRS1125_REG_DESIGN_STEP, &read);
+	if (ret < 0) {
+		dev_err(&client->dev, "error reading from i2c\n");
+		return ret;
+	}
+
+	if (read != IRS1125_DESIGN_STEP_EXPECTED) {
+		dev_err(&client->dev, "Design step expected 0x%x got 0x%x",
+			IRS1125_DESIGN_STEP_EXPECTED, read);
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+static int irs1125_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+	struct v4l2_mbus_framefmt *format =
+	v4l2_subdev_get_try_format(sd, fh->pad, 0);
+
+	format->code = MEDIA_BUS_FMT_Y12_1X12;
+	format->width = IRS1125_WINDOW_WIDTH_DEF;
+	format->height = IRS1125_WINDOW_HEIGHT_DEF;
+	format->field = V4L2_FIELD_NONE;
+	format->colorspace = V4L2_COLORSPACE_RAW;
+
+	return 0;
+}
+
+static const struct v4l2_subdev_internal_ops irs1125_subdev_internal_ops = {
+	.open = irs1125_open,
+};
+
+static int irs1125_ctrls_init(struct irs1125 *sensor, struct device *dev)
+{
+	struct v4l2_ctrl *ctrl;
+	int err, i;
+	struct v4l2_ctrl_handler *hdl = &sensor->ctrl_handler;
+	struct v4l2_ctrl_config ctrl_cfg = {
+		.ops = &irs1125_ctrl_ops,
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.min = 0,
+		.max = U16_MAX,
+		.step = 1,
+		.def = 0x1000
+	};
+
+	v4l2_ctrl_handler_init(hdl, ARRAY_SIZE(irs1125_custom_ctrls));
+
+	for (i = 0; i < ARRAY_SIZE(irs1125_custom_ctrls); i++)	{
+		ctrl = v4l2_ctrl_new_custom(hdl, &irs1125_custom_ctrls[i],
+					    NULL);
+		if (!ctrl)
+			dev_err(dev, "Failed to init custom control %s\n",
+				irs1125_custom_ctrls[i].name);
+		else if (irs1125_custom_ctrls[i].id == IRS1125_CID_NUM_SEQS)
+			sensor->ctrl_numseq = ctrl;
+		else if (irs1125_custom_ctrls[i].id == IRS1125_CID_MOD_PLL)
+			sensor->ctrl_modplls = ctrl;
+	}
+
+	if (hdl->error)	{
+		dev_err(dev, "Error %d adding controls\n", hdl->error);
+		err = hdl->error;
+		goto error_ctrls;
+	}
+
+	for (i = 0; i < IRS1125_NUM_SEQ_ENTRIES; i++) {
+		ctrl_cfg.name = expo_ctrl_names[i];
+		ctrl_cfg.id = IRS1125_CID_SAFE_RECONFIG_S0_EXPO + i * 2;
+		ctrl = v4l2_ctrl_new_custom(hdl, &ctrl_cfg,
+					    NULL);
+		if (!ctrl)
+			dev_err(dev, "Failed to init exposure control %s\n",
+				ctrl_cfg.name);
+	}
+
+	ctrl_cfg.def = 0;
+	for (i = 0; i < IRS1125_NUM_SEQ_ENTRIES; i++) {
+		ctrl_cfg.name = frame_ctrl_names[i];
+		ctrl_cfg.id = IRS1125_CID_SAFE_RECONFIG_S0_FRAME + i * 2;
+		ctrl = v4l2_ctrl_new_custom(hdl, &ctrl_cfg,
+					    NULL);
+		if (!ctrl)
+			dev_err(dev, "Failed to init framerate control %s\n",
+				ctrl_cfg.name);
+	}
+
+	sensor->sd.ctrl_handler = hdl;
+	return 0;
+
+error_ctrls:
+	v4l2_ctrl_handler_free(&sensor->ctrl_handler);
+	return -err;
+}
+
+static int irs1125_ident_setup(struct irs1125 *sensor, struct device *dev)
+{
+	int ret;
+	struct v4l2_ctrl *ctrl;
+	struct v4l2_subdev *sd;
+	u16 read;
+
+	sd = &sensor->sd;
+
+	ctrl = v4l2_ctrl_find(&sensor->ctrl_handler, IRS1125_CID_IDENT0);
+	if (!ctrl) {
+		dev_err(dev, "could not find device ctrl.\n");
+		return -EINVAL;
+	}
+
+	ret = irs1125_read(sd, IRS1125_REG_EFUSEVAL2, &read);
+	if (ret < 0) {
+		dev_err(dev, "error reading from i2c\n");
+		return -EIO;
+	}
+
+	v4l2_ctrl_s_ctrl(ctrl, read);
+
+	ctrl = v4l2_ctrl_find(&sensor->ctrl_handler, IRS1125_CID_IDENT1);
+	if (!ctrl) {
+		dev_err(dev, "could not find device ctrl.\n");
+		return -EINVAL;
+	}
+
+	ret = irs1125_read(sd, IRS1125_REG_EFUSEVAL3, &read);
+	if (ret < 0) {
+		dev_err(dev, "error reading from i2c\n");
+		return -EIO;
+	}
+
+	v4l2_ctrl_s_ctrl(ctrl, read);
+
+	ctrl = v4l2_ctrl_find(&sensor->ctrl_handler, IRS1125_CID_IDENT2);
+	if (!ctrl) {
+		dev_err(dev, "could not find device ctrl.\n");
+		return -EINVAL;
+	}
+
+	ret = irs1125_read(sd, IRS1125_REG_EFUSEVAL4, &read);
+	if (ret < 0) {
+		dev_err(dev, "error reading from i2c\n");
+		return -EIO;
+	}
+	v4l2_ctrl_s_ctrl(ctrl, read & 0xFFFC);
+
+	return 0;
+}
+
+static int irs1125_probe(struct i2c_client *client,
+			 const struct i2c_device_id *id)
+{
+	struct device *dev = &client->dev;
+	struct irs1125 *sensor;
+	int ret;
+	struct fwnode_handle *endpoint;
+	u32 xclk_freq;
+	int gpio_num;
+
+	sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL);
+	if (!sensor)
+		return -ENOMEM;
+
+	v4l2_i2c_subdev_init(&sensor->sd, client, &irs1125_subdev_ops);
+
+	/* Get CSI2 bus config */
+	endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(dev),
+						  NULL);
+	if (!endpoint) {
+		dev_err(dev, "endpoint node not found\n");
+		return -EINVAL;
+	}
+
+	ret = v4l2_fwnode_endpoint_parse(endpoint, &sensor->ep);
+	fwnode_handle_put(endpoint);
+	if (ret) {
+		dev_err(dev, "Could not parse endpoint\n");
+		return ret;
+	}
+
+	/* get system clock (xclk) */
+	sensor->xclk = devm_clk_get(dev, NULL);
+	if (IS_ERR(sensor->xclk)) {
+		dev_err(dev, "could not get xclk");
+		return PTR_ERR(sensor->xclk);
+	}
+
+	xclk_freq = clk_get_rate(sensor->xclk);
+	if (xclk_freq != 26000000) {
+		dev_err(dev, "Unsupported clock frequency: %u\n", xclk_freq);
+		return -EINVAL;
+	}
+
+	sensor->num_seq = 5;
+
+	/* Request the power down GPIO */
+	sensor->reset = devm_gpiod_get(&client->dev, "pwdn",
+				       GPIOD_OUT_LOW);
+
+	if (IS_ERR(sensor->reset)) {
+		dev_err(dev, "could not get reset");
+		return PTR_ERR(sensor->reset);
+	}
+
+	gpio_num = desc_to_gpio(sensor->reset);
+	dev_dbg(&client->dev, "reset on GPIO num %d\n", gpio_num);
+
+	mutex_init(&sensor->lock);
+
+	ret = irs1125_ctrls_init(sensor, dev);
+	if (ret < 0)
+		goto mutex_remove;
+
+	sensor->sd.internal_ops = &irs1125_subdev_internal_ops;
+	sensor->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+	sensor->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
+	sensor->pad.flags = MEDIA_PAD_FL_SOURCE;
+	ret = media_entity_pads_init(&sensor->sd.entity, 1, &sensor->pad);
+	if (ret < 0)
+		goto mutex_remove;
+
+	gpiod_set_value_cansleep(sensor->reset, 1);
+	msleep(RESET_ACTIVE_DELAY_MS);
+
+	ret = irs1125_detect(&sensor->sd);
+	if (ret < 0)
+		goto error;
+
+	ret = irs1125_ident_setup(sensor, dev);
+	if (ret < 0)
+		goto error;
+
+	gpiod_set_value_cansleep(sensor->reset, 0);
+
+	ret = v4l2_async_register_subdev(&sensor->sd);
+	if (ret < 0)
+		goto error;
+
+	dev_dbg(dev, "Infineon IRS1125 camera driver probed\n");
+
+	return 0;
+
+error:
+	media_entity_cleanup(&sensor->sd.entity);
+mutex_remove:
+	mutex_destroy(&sensor->lock);
+	return ret;
+}
+
+static int irs1125_remove(struct i2c_client *client)
+{
+	struct v4l2_subdev *sd = i2c_get_clientdata(client);
+	struct irs1125 *irs1125 = to_state(sd);
+
+	v4l2_async_unregister_subdev(&irs1125->sd);
+	media_entity_cleanup(&irs1125->sd.entity);
+	v4l2_device_unregister_subdev(sd);
+	mutex_destroy(&irs1125->lock);
+	v4l2_ctrl_handler_free(&irs1125->ctrl_handler);
+
+	return 0;
+}
+
+#if IS_ENABLED(CONFIG_OF)
+static const struct of_device_id irs1125_of_match[] = {
+	{ .compatible = "infineon,irs1125" },
+	{ /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, irs1125_of_match);
+#endif
+
+static struct i2c_driver irs1125_driver = {
+	.driver = {
+		.of_match_table = of_match_ptr(irs1125_of_match),
+		.name	 = SENSOR_NAME,
+	},
+	.probe		= irs1125_probe,
+	.remove		= irs1125_remove,
+};
+
+module_i2c_driver(irs1125_driver);
+
+MODULE_AUTHOR("Markus Proeller <markus.proeller@pieye.org>");
+MODULE_DESCRIPTION("Infineon irs1125 sensor driver");
+MODULE_LICENSE("GPL v2");
+
diff -Nur --no-dereference linux-5.10.52-orig/drivers/media/i2c/irs1125.h linux-5.10.52-v7l+/drivers/media/i2c/irs1125.h
--- linux-5.10.52-orig/drivers/media/i2c/irs1125.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/media/i2c/irs1125.h	2021-07-25 16:46:05.698295374 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * A V4L2 driver for Infineon IRS1125 TOF cameras.
+ * Copyright (C) 2018, pieye GmbH
+ *
+ * Based on V4L2 OmniVision OV5647 Image Sensor driver
+ * Copyright (C) 2016 Ramiro Oliveira <roliveir@synopsys.com>
+ *
+ * DT / fwnode changes, and GPIO control taken from ov5640.c
+ * Copyright (C) 2011-2013 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright (C) 2014-2017 Mentor Graphics Inc.
+ *
+ */
+
+#ifndef IRS1125_H
+#define IRS1125_H
+
+#include <linux/v4l2-controls.h>
+#include <linux/types.h>
+
+#define IRS1125_NUM_SEQ_ENTRIES 20
+#define IRS1125_NUM_MOD_PLLS 4
+
+#define IRS1125_CID_CUSTOM_BASE			(V4L2_CID_USER_BASE | 0xf000)
+#define IRS1125_CID_CONTINUOUS_TRIG		(IRS1125_CID_CUSTOM_BASE + 1)
+#define IRS1125_CID_TRIGGER			(IRS1125_CID_CUSTOM_BASE + 2)
+#define IRS1125_CID_RECONFIG			(IRS1125_CID_CUSTOM_BASE + 3)
+#define IRS1125_CID_ILLU_ON			(IRS1125_CID_CUSTOM_BASE + 4)
+#define IRS1125_CID_NUM_SEQS			(IRS1125_CID_CUSTOM_BASE + 5)
+#define IRS1125_CID_MOD_PLL			(IRS1125_CID_CUSTOM_BASE + 6)
+#define IRS1125_CID_SEQ_CONFIG			(IRS1125_CID_CUSTOM_BASE + 7)
+#define IRS1125_CID_IDENT0			(IRS1125_CID_CUSTOM_BASE + 8)
+#define IRS1125_CID_IDENT1			(IRS1125_CID_CUSTOM_BASE + 9)
+#define IRS1125_CID_IDENT2			(IRS1125_CID_CUSTOM_BASE + 10)
+#define IRS1125_CID_SAFE_RECONFIG_S0_EXPO	(IRS1125_CID_CUSTOM_BASE + 11)
+#define IRS1125_CID_SAFE_RECONFIG_S0_FRAME	(IRS1125_CID_CUSTOM_BASE + 12)
+#define IRS1125_CID_SAFE_RECONFIG_S1_EXPO	(IRS1125_CID_CUSTOM_BASE + 13)
+#define IRS1125_CID_SAFE_RECONFIG_S1_FRAME	(IRS1125_CID_CUSTOM_BASE + 14)
+#define IRS1125_CID_SAFE_RECONFIG_S2_EXPO	(IRS1125_CID_CUSTOM_BASE + 15)
+#define IRS1125_CID_SAFE_RECONFIG_S2_FRAME	(IRS1125_CID_CUSTOM_BASE + 16)
+#define IRS1125_CID_SAFE_RECONFIG_S3_EXPO	(IRS1125_CID_CUSTOM_BASE + 17)
+#define IRS1125_CID_SAFE_RECONFIG_S3_FRAME	(IRS1125_CID_CUSTOM_BASE + 18)
+#define IRS1125_CID_SAFE_RECONFIG_S4_EXPO	(IRS1125_CID_CUSTOM_BASE + 19)
+#define IRS1125_CID_SAFE_RECONFIG_S4_FRAME	(IRS1125_CID_CUSTOM_BASE + 20)
+#define IRS1125_CID_SAFE_RECONFIG_S5_EXPO	(IRS1125_CID_CUSTOM_BASE + 21)
+#define IRS1125_CID_SAFE_RECONFIG_S5_FRAME	(IRS1125_CID_CUSTOM_BASE + 22)
+#define IRS1125_CID_SAFE_RECONFIG_S6_EXPO	(IRS1125_CID_CUSTOM_BASE + 23)
+#define IRS1125_CID_SAFE_RECONFIG_S6_FRAME	(IRS1125_CID_CUSTOM_BASE + 24)
+#define IRS1125_CID_SAFE_RECONFIG_S7_EXPO	(IRS1125_CID_CUSTOM_BASE + 25)
+#define IRS1125_CID_SAFE_RECONFIG_S7_FRAME	(IRS1125_CID_CUSTOM_BASE + 26)
+#define IRS1125_CID_SAFE_RECONFIG_S8_EXPO	(IRS1125_CID_CUSTOM_BASE + 27)
+#define IRS1125_CID_SAFE_RECONFIG_S8_FRAME	(IRS1125_CID_CUSTOM_BASE + 28)
+#define IRS1125_CID_SAFE_RECONFIG_S9_EXPO	(IRS1125_CID_CUSTOM_BASE + 29)
+#define IRS1125_CID_SAFE_RECONFIG_S9_FRAME	(IRS1125_CID_CUSTOM_BASE + 30)
+#define IRS1125_CID_SAFE_RECONFIG_S10_EXPO	(IRS1125_CID_CUSTOM_BASE + 31)
+#define IRS1125_CID_SAFE_RECONFIG_S10_FRAME	(IRS1125_CID_CUSTOM_BASE + 32)
+#define IRS1125_CID_SAFE_RECONFIG_S11_EXPO	(IRS1125_CID_CUSTOM_BASE + 33)
+#define IRS1125_CID_SAFE_RECONFIG_S11_FRAME	(IRS1125_CID_CUSTOM_BASE + 34)
+#define IRS1125_CID_SAFE_RECONFIG_S12_EXPO	(IRS1125_CID_CUSTOM_BASE + 35)
+#define IRS1125_CID_SAFE_RECONFIG_S12_FRAME	(IRS1125_CID_CUSTOM_BASE + 36)
+#define IRS1125_CID_SAFE_RECONFIG_S13_EXPO	(IRS1125_CID_CUSTOM_BASE + 37)
+#define IRS1125_CID_SAFE_RECONFIG_S13_FRAME	(IRS1125_CID_CUSTOM_BASE + 38)
+#define IRS1125_CID_SAFE_RECONFIG_S14_EXPO	(IRS1125_CID_CUSTOM_BASE + 39)
+#define IRS1125_CID_SAFE_RECONFIG_S14_FRAME	(IRS1125_CID_CUSTOM_BASE + 40)
+#define IRS1125_CID_SAFE_RECONFIG_S15_EXPO	(IRS1125_CID_CUSTOM_BASE + 41)
+#define IRS1125_CID_SAFE_RECONFIG_S15_FRAME	(IRS1125_CID_CUSTOM_BASE + 42)
+#define IRS1125_CID_SAFE_RECONFIG_S16_EXPO	(IRS1125_CID_CUSTOM_BASE + 43)
+#define IRS1125_CID_SAFE_RECONFIG_S16_FRAME	(IRS1125_CID_CUSTOM_BASE + 44)
+#define IRS1125_CID_SAFE_RECONFIG_S17_EXPO	(IRS1125_CID_CUSTOM_BASE + 45)
+#define IRS1125_CID_SAFE_RECONFIG_S17_FRAME	(IRS1125_CID_CUSTOM_BASE + 46)
+#define IRS1125_CID_SAFE_RECONFIG_S18_EXPO	(IRS1125_CID_CUSTOM_BASE + 47)
+#define IRS1125_CID_SAFE_RECONFIG_S18_FRAME	(IRS1125_CID_CUSTOM_BASE + 48)
+#define IRS1125_CID_SAFE_RECONFIG_S19_EXPO	(IRS1125_CID_CUSTOM_BASE + 49)
+#define IRS1125_CID_SAFE_RECONFIG_S19_FRAME	(IRS1125_CID_CUSTOM_BASE + 50)
+
+struct irs1125_seq_cfg {
+	__u16 exposure;
+	__u16 framerate;
+	__u16 ps;
+	__u16 pll;
+};
+
+struct irs1125_mod_pll {
+	__u16 pllcfg1;
+	__u16 pllcfg2;
+	__u16 pllcfg3;
+	__u16 pllcfg4;
+	__u16 pllcfg5;
+	__u16 pllcfg6;
+	__u16 pllcfg7;
+	__u16 pllcfg8;
+};
+
+#endif /* IRS1125 */
+
diff -Nur --no-dereference linux-5.10.52-orig/drivers/media/i2c/Kconfig linux-5.10.52-v7l+/drivers/media/i2c/Kconfig
--- linux-5.10.52-orig/drivers/media/i2c/Kconfig	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/media/i2c/Kconfig	2021-07-25 16:46:05.618296715 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:804 @
 	  To compile this driver as a module, choose M here: the
 	  module will be called imx290.
 
+config VIDEO_IMX477
+	tristate "Sony IMX477 sensor support"
+	depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
+	depends on MEDIA_CAMERA_SUPPORT
+	help
+	  This is a Video4Linux2 sensor driver for the Sony
+	  IMX477 camera. Also supports the Sony IMX378.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called imx477.
+
 config VIDEO_IMX319
 	tristate "Sony IMX319 sensor support"
 	depends on I2C && VIDEO_V4L2
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1054 @
 	  This is a Video4Linux2 sensor driver for the OmniVision
 	  OV9640 camera sensor.
 
+config VIDEO_OV9281
+	tristate "OmniVision OV9281 sensor support"
+	depends on I2C && VIDEO_V4L2
+	depends on MEDIA_CAMERA_SUPPORT
+	help
+	  This is a Video4Linux2 sensor-level driver for the OmniVision
+	  OV9281 camera.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called ov9281.
+
 config VIDEO_OV9650
 	tristate "OmniVision OV9650/OV9652 sensor support"
 	depends on I2C && VIDEO_V4L2
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1085 @
 	  This is a Video4Linux2 sensor driver for the OmniVision
 	  OV13858 camera.
 
+config VIDEO_IRS1125
+	tristate "Infineon IRS1125 sensor support"
+	depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
+	depends on MEDIA_CAMERA_SUPPORT
+	select V4L2_FWNODE
+	help
+	  This is a Video4Linux2 sensor-level driver for the Infineon
+	  IRS1125 camera.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called irs1125.
+
 config VIDEO_VS6624
 	tristate "ST VS6624 sensor support"
 	depends on VIDEO_V4L2 && I2C
diff -Nur --no-dereference linux-5.10.52-orig/drivers/media/i2c/Makefile linux-5.10.52-v7l+/drivers/media/i2c/Makefile
--- linux-5.10.52-orig/drivers/media/i2c/Makefile	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/media/i2c/Makefile	2021-07-25 16:46:05.618296715 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:84 @
 obj-$(CONFIG_VIDEO_OV772X) += ov772x.o
 obj-$(CONFIG_VIDEO_OV7740) += ov7740.o
 obj-$(CONFIG_VIDEO_OV8856) += ov8856.o
+obj-$(CONFIG_VIDEO_OV9281) += ov9281.o
 obj-$(CONFIG_VIDEO_OV9640) += ov9640.o
 obj-$(CONFIG_VIDEO_OV9650) += ov9650.o
 obj-$(CONFIG_VIDEO_OV13858) += ov13858.o
+obj-$(CONFIG_VIDEO_IRS1125) += irs1125.o
 obj-$(CONFIG_VIDEO_MT9M001) += mt9m001.o
 obj-$(CONFIG_VIDEO_MT9M032) += mt9m032.o
 obj-$(CONFIG_VIDEO_MT9M111) += mt9m111.o
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:122 @
 obj-$(CONFIG_VIDEO_IMX258)	+= imx258.o
 obj-$(CONFIG_VIDEO_IMX274)	+= imx274.o
 obj-$(CONFIG_VIDEO_IMX290)	+= imx290.o
+obj-$(CONFIG_VIDEO_IMX477)	+= imx477.o
 obj-$(CONFIG_VIDEO_IMX319)	+= imx319.o
 obj-$(CONFIG_VIDEO_IMX355)	+= imx355.o
 obj-$(CONFIG_VIDEO_MAX9286)	+= max9286.o
diff -Nur --no-dereference linux-5.10.52-orig/drivers/media/i2c/ov5647.c linux-5.10.52-v7l+/drivers/media/i2c/ov5647.c
--- linux-5.10.52-orig/drivers/media/i2c/ov5647.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/media/i2c/ov5647.c	2021-07-25 16:46:05.758294368 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:24 @
 
 #include <linux/clk.h>
 #include <linux/delay.h>
+#include <linux/gpio/consumer.h>
 #include <linux/i2c.h>
 #include <linux/init.h>
 #include <linux/io.h>
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:32 @
 #include <linux/of_graph.h>
 #include <linux/slab.h>
 #include <linux/videodev2.h>
+#include <media/v4l2-ctrls.h>
 #include <media/v4l2-device.h>
+#include <media/v4l2-event.h>
 #include <media/v4l2-fwnode.h>
 #include <media/v4l2-image-sizes.h>
 #include <media/v4l2-mediabus.h>
 
+
 #define SENSOR_NAME "ov5647"
 
+/*
+ * From the datasheet, "20ms after PWDN goes low or 20ms after RESETB goes
+ * high if reset is inserted after PWDN goes high, host can access sensor's
+ * SCCB to initialize sensor."
+ */
+#define PWDN_ACTIVE_DELAY_MS	20
+
 #define MIPI_CTRL00_CLOCK_LANE_GATE		BIT(5)
+#define MIPI_CTRL00_LINE_SYNC_ENABLE		BIT(4)
 #define MIPI_CTRL00_BUS_IDLE			BIT(2)
 #define MIPI_CTRL00_CLOCK_LANE_DISABLE		BIT(0)
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:59 @
 #define OV5647_REG_CHIPID_H		0x300A
 #define OV5647_REG_CHIPID_L		0x300B
 #define OV5640_REG_PAD_OUT		0x300D
+#define OV5647_REG_EXP_HI		0x3500
+#define OV5647_REG_EXP_MID		0x3501
+#define OV5647_REG_EXP_LO		0x3502
+#define OV5647_REG_AEC_AGC		0x3503
+#define OV5647_REG_GAIN_HI		0x350A
+#define OV5647_REG_GAIN_LO		0x350B
+#define OV5647_REG_VTS_HI		0x380e
+#define OV5647_REG_VTS_LO		0x380f
 #define OV5647_REG_FRAME_OFF_NUMBER	0x4202
 #define OV5647_REG_MIPI_CTRL00		0x4800
 #define OV5647_REG_MIPI_CTRL14		0x4814
+#define OV5647_REG_AWB			0x5001
 
 #define REG_TERM 0xfffe
 #define VAL_TERM 0xfe
 #define REG_DLY  0xffff
 
-#define OV5647_ROW_START		0x01
-#define OV5647_ROW_START_MIN		0
-#define OV5647_ROW_START_MAX		2004
-#define OV5647_ROW_START_DEF		54
-
-#define OV5647_COLUMN_START		0x02
-#define OV5647_COLUMN_START_MIN		0
-#define OV5647_COLUMN_START_MAX		2750
-#define OV5647_COLUMN_START_DEF		16
-
-#define OV5647_WINDOW_HEIGHT		0x03
-#define OV5647_WINDOW_HEIGHT_MIN	2
-#define OV5647_WINDOW_HEIGHT_MAX	2006
-#define OV5647_WINDOW_HEIGHT_DEF	1944
-
-#define OV5647_WINDOW_WIDTH		0x04
-#define OV5647_WINDOW_WIDTH_MIN		2
-#define OV5647_WINDOW_WIDTH_MAX		2752
-#define OV5647_WINDOW_WIDTH_DEF		2592
+/* OV5647 native and active pixel array size */
+#define OV5647_NATIVE_WIDTH		2624U
+#define OV5647_NATIVE_HEIGHT		1956U
+
+#define OV5647_PIXEL_ARRAY_LEFT		16U
+#define OV5647_PIXEL_ARRAY_TOP		6U
+#define OV5647_PIXEL_ARRAY_WIDTH	2592U
+#define OV5647_PIXEL_ARRAY_HEIGHT	1944U
+
+#define OV5647_VBLANK_MIN		24
+#define OV5647_VTS_MAX			32767
+
+#define OV5647_EXPOSURE_MIN		4
+#define OV5647_EXPOSURE_STEP		1
+#define OV5647_EXPOSURE_DEFAULT		1000
+#define OV5647_EXPOSURE_MAX		65535
 
 struct regval_list {
 	u16 addr;
 	u8 data;
 };
 
+struct ov5647_mode {
+	struct v4l2_mbus_framefmt	format;
+	/* Analog crop rectangle. */
+	struct v4l2_rect crop;
+
+	u64 pixel_rate;
+	/* HTS as defined in the register set (0x380C/0x380D) */
+	int hts;
+	/* Default VTS value for this mode */
+	int vts_def;
+
+	struct regval_list		*reg_list;
+	unsigned int			num_regs;
+};
+
 struct ov5647 {
 	struct v4l2_subdev		sd;
 	struct media_pad		pad;
 	struct mutex			lock;
-	struct v4l2_mbus_framefmt	format;
-	unsigned int			width;
-	unsigned int			height;
+	const struct ov5647_mode	*mode;
 	int				power_count;
 	struct clk			*xclk;
+	struct gpio_desc		*pwdn;
+	unsigned int			flags;
+	struct v4l2_ctrl_handler	ctrls;
+	struct v4l2_ctrl		*pixel_rate;
+	struct v4l2_ctrl		*hblank;
+	struct v4l2_ctrl		*vblank;
+	struct v4l2_ctrl		*exposure;
+	bool				write_mode_regs;
 };
 
 static inline struct ov5647 *to_state(struct v4l2_subdev *sd)
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:147 @
 	{0x3002, 0xe4},
 };
 
-static struct regval_list ov5647_640x480[] = {
+static struct regval_list ov5647_640x480_8bit[] = {
 	{0x0100, 0x00},
 	{0x0103, 0x01},
 	{0x3034, 0x08},
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:162 @
 	{0x3612, 0x59},
 	{0x3618, 0x00},
 	{0x5000, 0x06},
-	{0x5001, 0x01},
 	{0x5002, 0x41},
 	{0x5003, 0x08},
 	{0x5a00, 0x08},
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:179 @
 	{0x3b07, 0x0c},
 	{0x380c, 0x07},
 	{0x380d, 0x68},
-	{0x380e, 0x03},
-	{0x380f, 0xd8},
 	{0x3814, 0x31},
 	{0x3815, 0x31},
 	{0x3708, 0x64},
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:235 @
 	{0x0100, 0x01},
 };
 
+static struct regval_list ov5647_2592x1944_10bit[] = {
+	{0x0100, 0x00},
+	{0x0103, 0x01},
+	{0x3034, 0x1a},
+	{0x3035, 0x21},
+	{0x3036, 0x69},
+	{0x303c, 0x11},
+	{0x3106, 0xf5},
+	{0x3821, 0x06},
+	{0x3820, 0x00},
+	{0x3827, 0xec},
+	{0x370c, 0x03},
+	{0x3612, 0x5b},
+	{0x3618, 0x04},
+	{0x5000, 0x06},
+	{0x5002, 0x41},
+	{0x5003, 0x08},
+	{0x5a00, 0x08},
+	{0x3000, 0x00},
+	{0x3001, 0x00},
+	{0x3002, 0x00},
+	{0x3016, 0x08},
+	{0x3017, 0xe0},
+	{0x3018, 0x44},
+	{0x301c, 0xf8},
+	{0x301d, 0xf0},
+	{0x3a18, 0x00},
+	{0x3a19, 0xf8},
+	{0x3c01, 0x80},
+	{0x3b07, 0x0c},
+	{0x380c, 0x0b},
+	{0x380d, 0x1c},
+	{0x3814, 0x11},
+	{0x3815, 0x11},
+	{0x3708, 0x64},
+	{0x3709, 0x12},
+	{0x3808, 0x0a},
+	{0x3809, 0x20},
+	{0x380a, 0x07},
+	{0x380b, 0x98},
+	{0x3800, 0x00},
+	{0x3801, 0x00},
+	{0x3802, 0x00},
+	{0x3803, 0x00},
+	{0x3804, 0x0a},
+	{0x3805, 0x3f},
+	{0x3806, 0x07},
+	{0x3807, 0xa3},
+	{0x3811, 0x10},
+	{0x3813, 0x06},
+	{0x3630, 0x2e},
+	{0x3632, 0xe2},
+	{0x3633, 0x23},
+	{0x3634, 0x44},
+	{0x3636, 0x06},
+	{0x3620, 0x64},
+	{0x3621, 0xe0},
+	{0x3600, 0x37},
+	{0x3704, 0xa0},
+	{0x3703, 0x5a},
+	{0x3715, 0x78},
+	{0x3717, 0x01},
+	{0x3731, 0x02},
+	{0x370b, 0x60},
+	{0x3705, 0x1a},
+	{0x3f05, 0x02},
+	{0x3f06, 0x10},
+	{0x3f01, 0x0a},
+	{0x3a08, 0x01},
+	{0x3a09, 0x28},
+	{0x3a0a, 0x00},
+	{0x3a0b, 0xf6},
+	{0x3a0d, 0x08},
+	{0x3a0e, 0x06},
+	{0x3a0f, 0x58},
+	{0x3a10, 0x50},
+	{0x3a1b, 0x58},
+	{0x3a1e, 0x50},
+	{0x3a11, 0x60},
+	{0x3a1f, 0x28},
+	{0x4001, 0x02},
+	{0x4004, 0x04},
+	{0x4000, 0x09},
+	{0x4837, 0x19},
+	{0x4800, 0x24},
+	{0x3503, 0x03},
+	{0x0100, 0x01},
+};
+
+static struct regval_list ov5647_1080p30_10bit[] = {
+	{0x0100, 0x00},
+	{0x0103, 0x01},
+	{0x3034, 0x1a},
+	{0x3035, 0x21},
+	{0x3036, 0x62},
+	{0x303c, 0x11},
+	{0x3106, 0xf5},
+	{0x3821, 0x06},
+	{0x3820, 0x00},
+	{0x3827, 0xec},
+	{0x370c, 0x03},
+	{0x3612, 0x5b},
+	{0x3618, 0x04},
+	{0x5000, 0x06},
+	{0x5002, 0x41},
+	{0x5003, 0x08},
+	{0x5a00, 0x08},
+	{0x3000, 0x00},
+	{0x3001, 0x00},
+	{0x3002, 0x00},
+	{0x3016, 0x08},
+	{0x3017, 0xe0},
+	{0x3018, 0x44},
+	{0x301c, 0xf8},
+	{0x301d, 0xf0},
+	{0x3a18, 0x00},
+	{0x3a19, 0xf8},
+	{0x3c01, 0x80},
+	{0x3b07, 0x0c},
+	{0x380c, 0x09},
+	{0x380d, 0x70},
+	{0x3814, 0x11},
+	{0x3815, 0x11},
+	{0x3708, 0x64},
+	{0x3709, 0x12},
+	{0x3808, 0x07},
+	{0x3809, 0x80},
+	{0x380a, 0x04},
+	{0x380b, 0x38},
+	{0x3800, 0x01},
+	{0x3801, 0x5c},
+	{0x3802, 0x01},
+	{0x3803, 0xb2},
+	{0x3804, 0x08},
+	{0x3805, 0xe3},
+	{0x3806, 0x05},
+	{0x3807, 0xf1},
+	{0x3811, 0x04},
+	{0x3813, 0x02},
+	{0x3630, 0x2e},
+	{0x3632, 0xe2},
+	{0x3633, 0x23},
+	{0x3634, 0x44},
+	{0x3636, 0x06},
+	{0x3620, 0x64},
+	{0x3621, 0xe0},
+	{0x3600, 0x37},
+	{0x3704, 0xa0},
+	{0x3703, 0x5a},
+	{0x3715, 0x78},
+	{0x3717, 0x01},
+	{0x3731, 0x02},
+	{0x370b, 0x60},
+	{0x3705, 0x1a},
+	{0x3f05, 0x02},
+	{0x3f06, 0x10},
+	{0x3f01, 0x0a},
+	{0x3a08, 0x01},
+	{0x3a09, 0x4b},
+	{0x3a0a, 0x01},
+	{0x3a0b, 0x13},
+	{0x3a0d, 0x04},
+	{0x3a0e, 0x03},
+	{0x3a0f, 0x58},
+	{0x3a10, 0x50},
+	{0x3a1b, 0x58},
+	{0x3a1e, 0x50},
+	{0x3a11, 0x60},
+	{0x3a1f, 0x28},
+	{0x4001, 0x02},
+	{0x4004, 0x04},
+	{0x4000, 0x09},
+	{0x4837, 0x19},
+	{0x4800, 0x34},
+	{0x3503, 0x03},
+	{0x0100, 0x01},
+};
+
+static struct regval_list ov5647_2x2binned_10bit[] = {
+	{0x0100, 0x00},
+	{0x0103, 0x01},
+	{0x3034, 0x1A},
+	{0x3035, 0x21},
+	{0x3036, 0x62},
+	{0x303C, 0x11},
+	{0x3106, 0xF5},
+	{0x3827, 0xEC},
+	{0x370C, 0x03},
+	{0x3612, 0x59},
+	{0x3618, 0x00},
+	{0x5000, 0x06},
+	{0x5002, 0x41},
+	{0x5003, 0x08},
+	{0x5A00, 0x08},
+	{0x3000, 0x00},
+	{0x3001, 0x00},
+	{0x3002, 0x00},
+	{0x3016, 0x08},
+	{0x3017, 0xE0},
+	{0x3018, 0x44},
+	{0x301C, 0xF8},
+	{0x301D, 0xF0},
+	{0x3A18, 0x00},
+	{0x3A19, 0xF8},
+	{0x3C01, 0x80},
+	{0x3B07, 0x0C},
+	{0x3800, 0x00},
+	{0x3801, 0x00},
+	{0x3802, 0x00},
+	{0x3803, 0x00},
+	{0x3804, 0x0A},
+	{0x3805, 0x3F},
+	{0x3806, 0x07},
+	{0x3807, 0xA3},
+	{0x3808, 0x05},
+	{0x3809, 0x10},
+	{0x380A, 0x03},
+	{0x380B, 0xCC},
+	{0x380C, 0x07},
+	{0x380D, 0x68},
+	{0x3811, 0x0c},
+	{0x3813, 0x06},
+	{0x3814, 0x31},
+	{0x3815, 0x31},
+	{0x3630, 0x2E},
+	{0x3632, 0xE2},
+	{0x3633, 0x23},
+	{0x3634, 0x44},
+	{0x3636, 0x06},
+	{0x3620, 0x64},
+	{0x3621, 0xE0},
+	{0x3600, 0x37},
+	{0x3704, 0xA0},
+	{0x3703, 0x5A},
+	{0x3715, 0x78},
+	{0x3717, 0x01},
+	{0x3731, 0x02},
+	{0x370B, 0x60},
+	{0x3705, 0x1A},
+	{0x3F05, 0x02},
+	{0x3F06, 0x10},
+	{0x3F01, 0x0A},
+	{0x3A08, 0x01},
+	{0x3A09, 0x28},
+	{0x3A0A, 0x00},
+	{0x3A0B, 0xF6},
+	{0x3A0D, 0x08},
+	{0x3A0E, 0x06},
+	{0x3A0F, 0x58},
+	{0x3A10, 0x50},
+	{0x3A1B, 0x58},
+	{0x3A1E, 0x50},
+	{0x3A11, 0x60},
+	{0x3A1F, 0x28},
+	{0x4001, 0x02},
+	{0x4004, 0x04},
+	{0x4000, 0x09},
+	{0x4837, 0x16},
+	{0x4800, 0x24},
+	{0x3503, 0x03},
+	{0x3820, 0x41},
+	{0x3821, 0x07},
+	{0x350A, 0x00},
+	{0x350B, 0x10},
+	{0x3500, 0x00},
+	{0x3501, 0x1A},
+	{0x3502, 0xF0},
+	{0x3212, 0xA0},
+	{0x0100, 0x01},
+};
+
+static struct regval_list ov5647_640x480_10bit[] = {
+	{0x0100, 0x00},
+	{0x0103, 0x01},
+	{0x3035, 0x11},
+	{0x3036, 0x46},
+	{0x303c, 0x11},
+	{0x3821, 0x07},
+	{0x3820, 0x41},
+	{0x370c, 0x03},
+	{0x3612, 0x59},
+	{0x3618, 0x00},
+	{0x5000, 0x06},
+	{0x5003, 0x08},
+	{0x5a00, 0x08},
+	{0x3000, 0xff},
+	{0x3001, 0xff},
+	{0x3002, 0xff},
+	{0x301d, 0xf0},
+	{0x3a18, 0x00},
+	{0x3a19, 0xf8},
+	{0x3c01, 0x80},
+	{0x3b07, 0x0c},
+	{0x380c, 0x07},
+	{0x380d, 0x3c},
+	{0x3814, 0x35},
+	{0x3815, 0x35},
+	{0x3708, 0x64},
+	{0x3709, 0x52},
+	{0x3808, 0x02},
+	{0x3809, 0x80},
+	{0x380a, 0x01},
+	{0x380b, 0xe0},
+	{0x3800, 0x00},
+	{0x3801, 0x10},
+	{0x3802, 0x00},
+	{0x3803, 0x00},
+	{0x3804, 0x0a},
+	{0x3805, 0x2f},
+	{0x3806, 0x07},
+	{0x3807, 0x9f},
+	{0x3630, 0x2e},
+	{0x3632, 0xe2},
+	{0x3633, 0x23},
+	{0x3634, 0x44},
+	{0x3620, 0x64},
+	{0x3621, 0xe0},
+	{0x3600, 0x37},
+	{0x3704, 0xa0},
+	{0x3703, 0x5a},
+	{0x3715, 0x78},
+	{0x3717, 0x01},
+	{0x3731, 0x02},
+	{0x370b, 0x60},
+	{0x3705, 0x1a},
+	{0x3f05, 0x02},
+	{0x3f06, 0x10},
+	{0x3f01, 0x0a},
+	{0x3a08, 0x01},
+	{0x3a09, 0x2e},
+	{0x3a0a, 0x00},
+	{0x3a0b, 0xfb},
+	{0x3a0d, 0x02},
+	{0x3a0e, 0x01},
+	{0x3a0f, 0x58},
+	{0x3a10, 0x50},
+	{0x3a1b, 0x58},
+	{0x3a1e, 0x50},
+	{0x3a11, 0x60},
+	{0x3a1f, 0x28},
+	{0x4001, 0x02},
+	{0x4004, 0x02},
+	{0x4000, 0x09},
+	{0x3000, 0x00},
+	{0x3001, 0x00},
+	{0x3002, 0x00},
+	{0x3017, 0xe0},
+	{0x301c, 0xfc},
+	{0x3636, 0x06},
+	{0x3016, 0x08},
+	{0x3827, 0xec},
+	{0x3018, 0x44},
+	{0x3035, 0x21},
+	{0x3106, 0xf5},
+	{0x3034, 0x1a},
+	{0x301c, 0xf8},
+	{0x4800, 0x34},
+	{0x3503, 0x03},
+	{0x0100, 0x01},
+};
+
+static struct ov5647_mode supported_modes_8bit[] = {
+	/*
+	 * MODE 0: Original 8-bit VGA mode.
+	 * Uncentred crop (top left quarter) from 2x2 binned 1296x972 image.
+	 */
+	{
+		.format = {
+			.code = MEDIA_BUS_FMT_SBGGR8_1X8,
+			.colorspace = V4L2_COLORSPACE_SRGB,
+			.field = V4L2_FIELD_NONE,
+			.width = 640,
+			.height = 480
+		},
+		.crop = {
+			.left = OV5647_PIXEL_ARRAY_LEFT,
+			.top = OV5647_PIXEL_ARRAY_TOP,
+			.width = 1280,
+			.height = 960,
+		},
+		.pixel_rate = 77291670,
+		.hts = 1896,
+		.vts_def = 0x3d8,
+		.reg_list = ov5647_640x480_8bit,
+		.num_regs = ARRAY_SIZE(ov5647_640x480_8bit)
+	},
+};
+
+static struct ov5647_mode supported_modes_10bit[] = {
+	/*
+	 * MODE 0: 2592x1944 full resolution full FOV 10-bit mode.
+	 */
+	{
+		.format = {
+			.code = MEDIA_BUS_FMT_SBGGR10_1X10,
+			.colorspace = V4L2_COLORSPACE_SRGB,
+			.field = V4L2_FIELD_NONE,
+			.width = 2592,
+			.height = 1944
+		},
+		.crop = {
+			.left = OV5647_PIXEL_ARRAY_LEFT,
+			.top = OV5647_PIXEL_ARRAY_TOP,
+			.width = 2592,
+			.height = 1944
+		},
+		.pixel_rate = 87500000,
+		.hts = 2844,
+		.vts_def = 0x7b0,
+		.reg_list = ov5647_2592x1944_10bit,
+		.num_regs = ARRAY_SIZE(ov5647_2592x1944_10bit)
+	},
+	/*
+	 * MODE 1: 1080p30 10-bit mode.
+	 * Full resolution centre-cropped down to 1080p.
+	 */
+	{
+		.format = {
+			.code = MEDIA_BUS_FMT_SBGGR10_1X10,
+			.colorspace = V4L2_COLORSPACE_SRGB,
+			.field = V4L2_FIELD_NONE,
+			.width = 1920,
+			.height = 1080
+		},
+		.crop = {
+			.left = 364,
+			.top = 450,
+			.width = 1928,
+			.height = 1080,
+		},
+		.pixel_rate = 81666700,
+		.hts = 2416,
+		.vts_def = 0x450,
+		.reg_list = ov5647_1080p30_10bit,
+		.num_regs = ARRAY_SIZE(ov5647_1080p30_10bit)
+	},
+	/*
+	 * MODE 2: 2x2 binned full FOV 10-bit mode.
+	 */
+	{
+		.format = {
+			.code = MEDIA_BUS_FMT_SBGGR10_1X10,
+			.colorspace = V4L2_COLORSPACE_SRGB,
+			.field = V4L2_FIELD_NONE,
+			.width = 1296,
+			.height = 972
+		},
+		.crop = {
+			.left = OV5647_PIXEL_ARRAY_LEFT,
+			.top = OV5647_PIXEL_ARRAY_TOP,
+			.width = 2592,
+			.height = 1944,
+		},
+		.pixel_rate = 81666700,
+		.hts = 1896,
+		.vts_def = 0x59b,
+		.reg_list = ov5647_2x2binned_10bit,
+		.num_regs = ARRAY_SIZE(ov5647_2x2binned_10bit)
+	},
+	/*
+	 * MODE 3: 10-bit VGA full FOV mode 60fps.
+	 * 2x2 binned and subsampled down to VGA.
+	 */
+	{
+		.format = {
+			.code = MEDIA_BUS_FMT_SBGGR10_1X10,
+			.colorspace = V4L2_COLORSPACE_SRGB,
+			.field = V4L2_FIELD_NONE,
+			.width = 640,
+			.height = 480
+		},
+		.crop = {
+			.left = OV5647_PIXEL_ARRAY_LEFT,
+			.top = OV5647_PIXEL_ARRAY_TOP,
+			.width = 2560,
+			.height = 1920,
+		},
+		.pixel_rate = 55000000,
+		.hts = 1852,
+		.vts_def = 0x1f8,
+		.reg_list = ov5647_640x480_10bit,
+		.num_regs = ARRAY_SIZE(ov5647_640x480_10bit)
+	},
+};
+
+/* Use 2x2 binned 10-bit mode as default. */
+#define OV5647_DEFAULT_MODE (&supported_modes_10bit[2])
+
+static int ov5647_write16(struct v4l2_subdev *sd, u16 reg, u16 val)
+{
+	int ret;
+	unsigned char data[4] = { reg >> 8, reg & 0xff, val >> 8, val & 0xff};
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+	ret = i2c_master_send(client, data, 4);
+	/*
+	 * Writing the wrong number of bytes also needs to be flagged as an
+	 * error. Success needs to produce a 0 return code.
+	 */
+	if (ret == 4) {
+		ret = 0;
+	} else {
+		dev_dbg(&client->dev, "%s: i2c write error, reg: %x\n",
+			__func__, reg);
+		if (ret >= 0)
+			ret = -EINVAL;
+	}
+
+	return ret;
+}
+
 static int ov5647_write(struct v4l2_subdev *sd, u16 reg, u8 val)
 {
 	int ret;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:753 @
 	struct i2c_client *client = v4l2_get_subdevdata(sd);
 
 	ret = i2c_master_send(client, data, 3);
-	if (ret < 0)
+	/*
+	 * Writing the wrong number of bytes also needs to be flagged as an
+	 * error. Success needs to produce a 0 return code.
+	 */
+	if (ret == 3) {
+		ret = 0;
+	} else {
 		dev_dbg(&client->dev, "%s: i2c write error, reg: %x\n",
 				__func__, reg);
+		if (ret >= 0)
+			ret = -EINVAL;
+	}
 
 	return ret;
 }
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:776 @
 	struct i2c_client *client = v4l2_get_subdevdata(sd);
 
 	ret = i2c_master_send(client, data_w, 2);
-	if (ret < 0) {
+	/*
+	 * A negative return code, or sending the wrong number of bytes, both
+	 * count as an error.
+	 */
+	if (ret != 2) {
 		dev_dbg(&client->dev, "%s: i2c write error, reg: %x\n",
 			__func__, reg);
+		if (ret >= 0)
+			ret = -EINVAL;
 		return ret;
 	}
 
 	ret = i2c_master_recv(client, val, 1);
-	if (ret < 0)
+	/*
+	 * The only return value indicating success is 1. Anything else, even
+	 * a non-negative value, indicates something went wrong.
+	 */
+	if (ret == 1) {
+		ret = 0;
+	} else {
 		dev_dbg(&client->dev, "%s: i2c read error, reg: %x\n",
 				__func__, reg);
+		if (ret >= 0)
+			ret = -EINVAL;
+	}
 
 	return ret;
 }
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:832 @
 	return ov5647_write(sd, OV5647_REG_MIPI_CTRL14, channel_id | (channel << 6));
 }
 
+static int __sensor_init(struct v4l2_subdev *sd)
+{
+	int ret;
+	u8 resetval, rdval;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct ov5647 *state = to_state(sd);
+
+	ret = ov5647_read(sd, OV5647_SW_STANDBY, &rdval);
+	if (ret < 0)
+		return ret;
+
+	if (state->write_mode_regs) {
+		ret = ov5647_write_array(sd, state->mode->reg_list,
+					 state->mode->num_regs);
+		if (ret < 0) {
+			dev_err(&client->dev, "write sensor default regs error\n");
+			return ret;
+		}
+		state->write_mode_regs = false;
+	}
+
+	ret = ov5647_set_virtual_channel(sd, 0);
+	if (ret < 0)
+		return ret;
+
+	ret = ov5647_read(sd, OV5647_SW_STANDBY, &resetval);
+	if (ret < 0)
+		return ret;
+
+	if (!(resetval & 0x01)) {
+		dev_err(&client->dev, "Device was in SW standby");
+		ret = ov5647_write(sd, OV5647_SW_STANDBY, 0x01);
+		if (ret < 0)
+			return ret;
+	}
+
+	return 0;
+}
+
 static int ov5647_stream_on(struct v4l2_subdev *sd)
 {
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct ov5647 *ov5647 = to_state(sd);
+	u8 val = MIPI_CTRL00_BUS_IDLE;
 	int ret;
 
-	ret = ov5647_write(sd, OV5647_REG_MIPI_CTRL00, MIPI_CTRL00_BUS_IDLE);
+	ret = __sensor_init(sd);
+	if (ret < 0) {
+		dev_err(&client->dev, "sensor_init failed\n");
+		return ret;
+	}
+
+	/* Apply customized values from user when stream starts */
+	ret =  __v4l2_ctrl_handler_setup(sd->ctrl_handler);
+	if (ret)
+		return ret;
+
+	if (ov5647->flags & V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK)
+		val |= MIPI_CTRL00_CLOCK_LANE_GATE |
+		       MIPI_CTRL00_LINE_SYNC_ENABLE;
+
+	ret = ov5647_write(sd, OV5647_REG_MIPI_CTRL00, val);
 	if (ret < 0)
 		return ret;
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:937 @
 	return ov5647_write(sd, OV5647_SW_STANDBY, rdval);
 }
 
-static int __sensor_init(struct v4l2_subdev *sd)
-{
-	int ret;
-	u8 resetval, rdval;
-	struct i2c_client *client = v4l2_get_subdevdata(sd);
-
-	ret = ov5647_read(sd, OV5647_SW_STANDBY, &rdval);
-	if (ret < 0)
-		return ret;
-
-	ret = ov5647_write_array(sd, ov5647_640x480,
-					ARRAY_SIZE(ov5647_640x480));
-	if (ret < 0) {
-		dev_err(&client->dev, "write sensor default regs error\n");
-		return ret;
-	}
-
-	ret = ov5647_set_virtual_channel(sd, 0);
-	if (ret < 0)
-		return ret;
-
-	ret = ov5647_read(sd, OV5647_SW_STANDBY, &resetval);
-	if (ret < 0)
-		return ret;
-
-	if (!(resetval & 0x01)) {
-		dev_err(&client->dev, "Device was in SW standby");
-		ret = ov5647_write(sd, OV5647_SW_STANDBY, 0x01);
-		if (ret < 0)
-			return ret;
-	}
-
-	/*
-	 * stream off to make the clock lane into LP-11 state.
-	 */
-	return ov5647_stream_off(sd);
-}
-
 static int ov5647_sensor_power(struct v4l2_subdev *sd, int on)
 {
 	int ret = 0;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:948 @
 	if (on && !ov5647->power_count)	{
 		dev_dbg(&client->dev, "OV5647 power on\n");
 
+		if (ov5647->pwdn) {
+			gpiod_set_value_cansleep(ov5647->pwdn, 0);
+			msleep(PWDN_ACTIVE_DELAY_MS);
+		}
+
 		ret = clk_prepare_enable(ov5647->xclk);
 		if (ret < 0) {
 			dev_err(&client->dev, "clk prepare enable failed\n");
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:960 @
 		}
 
 		ret = ov5647_write_array(sd, sensor_oe_enable_regs,
-				ARRAY_SIZE(sensor_oe_enable_regs));
+					 ARRAY_SIZE(sensor_oe_enable_regs));
 		if (ret < 0) {
 			clk_disable_unprepare(ov5647->xclk);
 			dev_err(&client->dev,
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:968 @
 			goto out;
 		}
 
-		ret = __sensor_init(sd);
+		/*
+		 * Ensure streaming off to make clock lane go into LP-11 state.
+		 */
+		ret = ov5647_stream_off(sd);
 		if (ret < 0) {
 			clk_disable_unprepare(ov5647->xclk);
 			dev_err(&client->dev,
 				"Camera not available, check Power\n");
 			goto out;
 		}
+
+		/* Write out the register set over I2C on stream-on. */
+		ov5647->write_mode_regs = true;
 	} else if (!on && ov5647->power_count == 1) {
 		dev_dbg(&client->dev, "OV5647 power off\n");
 
 		ret = ov5647_write_array(sd, sensor_oe_disable_regs,
-				ARRAY_SIZE(sensor_oe_disable_regs));
+					 ARRAY_SIZE(sensor_oe_disable_regs));
 
 		if (ret < 0)
 			dev_dbg(&client->dev, "disable oe failed\n");
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:996 @
 			dev_dbg(&client->dev, "soft stby failed\n");
 
 		clk_disable_unprepare(ov5647->xclk);
+
+		gpiod_set_value_cansleep(ov5647->pwdn, 1);
 	}
 
 	/* Update the power count. */
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1043 @
 	.g_register		= ov5647_sensor_get_register,
 	.s_register		= ov5647_sensor_set_register,
 #endif
+	.subscribe_event = v4l2_ctrl_subdev_subscribe_event,
+	.unsubscribe_event = v4l2_event_subdev_unsubscribe,
 };
 
+static const struct v4l2_rect *
+__ov5647_get_pad_crop(struct ov5647 *ov5647, struct v4l2_subdev_pad_config *cfg,
+		      unsigned int pad, enum v4l2_subdev_format_whence which)
+{
+	switch (which) {
+	case V4L2_SUBDEV_FORMAT_TRY:
+		return v4l2_subdev_get_try_crop(&ov5647->sd, cfg, pad);
+	case V4L2_SUBDEV_FORMAT_ACTIVE:
+		return &ov5647->mode->crop;
+	}
+
+	return NULL;
+}
+
+static int ov5647_get_selection(struct v4l2_subdev *sd,
+				struct v4l2_subdev_pad_config *cfg,
+				struct v4l2_subdev_selection *sel)
+{
+	switch (sel->target) {
+	case V4L2_SEL_TGT_CROP: {
+		struct ov5647 *state = to_state(sd);
+
+		mutex_lock(&state->lock);
+		sel->r = *__ov5647_get_pad_crop(state, cfg, sel->pad,
+						sel->which);
+		mutex_unlock(&state->lock);
+
+		return 0;
+	}
+
+	case V4L2_SEL_TGT_NATIVE_SIZE:
+		sel->r.top = 0;
+		sel->r.left = 0;
+		sel->r.width = OV5647_NATIVE_WIDTH;
+		sel->r.height = OV5647_NATIVE_HEIGHT;
+
+		return 0;
+
+	case V4L2_SEL_TGT_CROP_DEFAULT:
+	case V4L2_SEL_TGT_CROP_BOUNDS:
+		sel->r.top = OV5647_PIXEL_ARRAY_TOP;
+		sel->r.left = OV5647_PIXEL_ARRAY_LEFT;
+		sel->r.width = OV5647_PIXEL_ARRAY_WIDTH;
+		sel->r.height = OV5647_PIXEL_ARRAY_HEIGHT;
+
+		return 0;
+	}
+
+	return -EINVAL;
+}
+
 static int ov5647_s_stream(struct v4l2_subdev *sd, int enable)
 {
+	struct ov5647 *state = to_state(sd);
+	int ret = 0;
+
+	mutex_lock(&state->lock);
+
 	if (enable)
-		return ov5647_stream_on(sd);
+		ret = ov5647_stream_on(sd);
 	else
-		return ov5647_stream_off(sd);
+		ret = ov5647_stream_off(sd);
+
+	mutex_unlock(&state->lock);
+
+	return ret;
 }
 
 static const struct v4l2_subdev_video_ops ov5647_subdev_video_ops = {
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1123 @
 				struct v4l2_subdev_pad_config *cfg,
 				struct v4l2_subdev_mbus_code_enum *code)
 {
-	if (code->index > 0)
+	if (code->index == 0 && ARRAY_SIZE(supported_modes_8bit))
+		code->code = MEDIA_BUS_FMT_SBGGR8_1X8;
+	else if (code->index == 0 && ARRAY_SIZE(supported_modes_8bit) == 0 &&
+		 ARRAY_SIZE(supported_modes_10bit))
+		code->code = MEDIA_BUS_FMT_SBGGR10_1X10;
+	else if (code->index == 1 && ARRAY_SIZE(supported_modes_8bit) &&
+		 ARRAY_SIZE(supported_modes_10bit))
+		code->code = MEDIA_BUS_FMT_SBGGR10_1X10;
+	else
+		return -EINVAL;
+
+	return 0;
+}
+
+static int ov5647_enum_frame_size(struct v4l2_subdev *sd,
+				  struct v4l2_subdev_pad_config *cfg,
+				  struct v4l2_subdev_frame_size_enum *fse)
+{
+	struct ov5647_mode *mode = NULL;
+
+	if (fse->code == MEDIA_BUS_FMT_SBGGR8_1X8) {
+		if (fse->index >= ARRAY_SIZE(supported_modes_8bit))
+			return -EINVAL;
+		mode = &supported_modes_8bit[fse->index];
+	} else if (fse->code == MEDIA_BUS_FMT_SBGGR10_1X10) {
+		if (fse->index >= ARRAY_SIZE(supported_modes_10bit))
+			return -EINVAL;
+		mode = &supported_modes_10bit[fse->index];
+	} else {
 		return -EINVAL;
+	}
 
-	code->code = MEDIA_BUS_FMT_SBGGR8_1X8;
+	fse->min_width = mode->format.width;
+	fse->max_width = fse->min_width;
+	fse->min_height = mode->format.height;
+	fse->max_height = fse->min_height;
+
+	return 0;
+}
+
+static int ov5647_set_fmt(struct v4l2_subdev *sd,
+			  struct v4l2_subdev_pad_config *cfg,
+			  struct v4l2_subdev_format *format)
+{
+	struct v4l2_mbus_framefmt *fmt = &format->format;
+	struct ov5647 *state = to_state(sd);
+	struct v4l2_mbus_framefmt *framefmt;
+	const struct ov5647_mode *mode_8bit, *mode_10bit, *mode = NULL;
+
+	if (format->pad != 0)
+		return -EINVAL;
+
+	mutex_lock(&state->lock);
+
+	/*
+	 * Try to respect any given pixel format, otherwise try for a 10-bit
+	 * mode.
+	 */
+	mode_8bit = v4l2_find_nearest_size(supported_modes_8bit,
+					   ARRAY_SIZE(supported_modes_8bit),
+					   format.width, format.height,
+					   format->format.width,
+					   format->format.height);
+	mode_10bit = v4l2_find_nearest_size(supported_modes_10bit,
+					    ARRAY_SIZE(supported_modes_10bit),
+					    format.width, format.height,
+					    format->format.width,
+					    format->format.height);
+	if (format->format.code == MEDIA_BUS_FMT_SBGGR8_1X8 && mode_8bit)
+		mode = mode_8bit;
+	else if (format->format.code == MEDIA_BUS_FMT_SBGGR10_1X10 &&
+		 mode_10bit)
+		mode = mode_10bit;
+	else if (mode_10bit)
+		mode = mode_10bit;
+	else
+		mode = mode_8bit;
+
+	if (!mode) {
+		mutex_unlock(&state->lock);
+		return -EINVAL;
+	}
+
+	*fmt = mode->format;
+	if (format->which == V4L2_SUBDEV_FORMAT_TRY) {
+		framefmt = v4l2_subdev_get_try_format(sd, cfg, format->pad);
+		*framefmt = format->format;
+	} else {
+		/*
+		 * If we have changed modes, write the I2C register list on
+		 * a stream_on().
+		 */
+		int exposure_max, exposure_def, hblank;
+
+		if (state->mode != mode)
+			state->write_mode_regs = true;
+		state->mode = mode;
+
+		__v4l2_ctrl_modify_range(state->pixel_rate,
+					 mode->pixel_rate,
+					 mode->pixel_rate, 1,
+					 mode->pixel_rate);
+		hblank = mode->hts - mode->format.width;
+		__v4l2_ctrl_modify_range(state->hblank, hblank, hblank, 1,
+					 hblank);
+
+		__v4l2_ctrl_modify_range(state->vblank,
+					 OV5647_VBLANK_MIN,
+					 OV5647_VTS_MAX - mode->format.height,
+					 1,
+					 mode->vts_def - mode->format.height);
+		__v4l2_ctrl_s_ctrl(state->vblank,
+				   mode->vts_def - mode->format.height);
+
+		exposure_max = mode->vts_def - 4;
+		exposure_def = (exposure_max < OV5647_EXPOSURE_DEFAULT) ?
+					exposure_max : OV5647_EXPOSURE_DEFAULT;
+		__v4l2_ctrl_modify_range(state->exposure,
+					 state->exposure->minimum,
+					 exposure_max,
+					 state->exposure->step,
+					 exposure_def);
+	}
+
+	mutex_unlock(&state->lock);
+
+	return 0;
+}
+
+static int ov5647_get_fmt(struct v4l2_subdev *sd,
+			  struct v4l2_subdev_pad_config *cfg,
+			  struct v4l2_subdev_format *format)
+{
+	struct v4l2_mbus_framefmt *fmt = &format->format;
+	struct ov5647 *state = to_state(sd);
+
+	if (format->pad != 0)
+		return -EINVAL;
+
+	mutex_lock(&state->lock);
+
+	if (format->which == V4L2_SUBDEV_FORMAT_TRY)
+		*fmt = *v4l2_subdev_get_try_format(sd, cfg, format->pad);
+	else
+		*fmt = state->mode->format;
+
+	mutex_unlock(&state->lock);
 
 	return 0;
 }
 
 static const struct v4l2_subdev_pad_ops ov5647_subdev_pad_ops = {
 	.enum_mbus_code = ov5647_enum_mbus_code,
+	.set_fmt =	  ov5647_set_fmt,
+	.get_fmt =	  ov5647_get_fmt,
+	.get_selection =  ov5647_get_selection,
+	.enum_frame_size = ov5647_enum_frame_size,
 };
 
 static const struct v4l2_subdev_ops ov5647_subdev_ops = {
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1325 @
 				v4l2_subdev_get_try_format(sd, fh->pad, 0);
 	struct v4l2_rect *crop =
 				v4l2_subdev_get_try_crop(sd, fh->pad, 0);
+	struct ov5647 *state = to_state(sd);
+
+	crop->left = OV5647_PIXEL_ARRAY_LEFT;
+	crop->top = OV5647_PIXEL_ARRAY_TOP;
+	crop->width = OV5647_PIXEL_ARRAY_WIDTH;
+	crop->height = OV5647_PIXEL_ARRAY_HEIGHT;
 
-	crop->left = OV5647_COLUMN_START_DEF;
-	crop->top = OV5647_ROW_START_DEF;
-	crop->width = OV5647_WINDOW_WIDTH_DEF;
-	crop->height = OV5647_WINDOW_HEIGHT_DEF;
-
-	format->code = MEDIA_BUS_FMT_SBGGR8_1X8;
-
-	format->width = OV5647_WINDOW_WIDTH_DEF;
-	format->height = OV5647_WINDOW_HEIGHT_DEF;
-	format->field = V4L2_FIELD_NONE;
-	format->colorspace = V4L2_COLORSPACE_SRGB;
+	/* Set the default format to the same as the sensor. */
+	*format = state->mode->format;
 
 	return 0;
 }
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1342 @
 	.open = ov5647_open,
 };
 
-static int ov5647_parse_dt(struct device_node *np)
+static int ov5647_parse_dt(struct device_node *np, struct ov5647 *sensor)
 {
 	struct v4l2_fwnode_endpoint bus_cfg = { .bus_type = 0 };
 	struct device_node *ep;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1355 @
 
 	ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(ep), &bus_cfg);
 
+	if (!ret)
+		sensor->flags = bus_cfg.bus.mipi_csi2.flags;
+
 	of_node_put(ep);
 	return ret;
 }
 
+static int ov5647_s_auto_white_balance(struct v4l2_subdev *sd, u32 val)
+{
+	/* non-zero turns on AWB */
+	return ov5647_write(sd, OV5647_REG_AWB, val ? 1 : 0);
+}
+
+static int ov5647_s_autogain(struct v4l2_subdev *sd, u32 val)
+{
+	int ret;
+	u8 reg;
+
+	/* non-zero turns on AGC by clearing bit 1 */
+	ret = ov5647_read(sd, OV5647_REG_AEC_AGC, &reg);
+	if (ret == 0)
+		ret = ov5647_write(sd, OV5647_REG_AEC_AGC,
+				   val ? reg & ~2 : reg | 2);
+
+	return ret;
+}
+
+static int ov5647_s_exposure_auto(struct v4l2_subdev *sd, u32 val)
+{
+	int ret;
+	u8 reg;
+
+	/* Everything except V4L2_EXPOSURE_MANUAL turns on AEC by
+	 * clearing bit 0
+	 */
+	ret = ov5647_read(sd, OV5647_REG_AEC_AGC, &reg);
+	if (ret == 0)
+		ret = ov5647_write(sd, OV5647_REG_AEC_AGC,
+				   val == V4L2_EXPOSURE_MANUAL ?
+				   reg | 1 : reg & ~1);
+
+	return ret;
+}
+
+static int ov5647_s_analogue_gain(struct v4l2_subdev *sd, u32 val)
+{
+	int ret;
+
+	/* 10 bits of gain, 2 in the high register */
+	ret = ov5647_write(sd, OV5647_REG_GAIN_HI, (val >> 8) & 3);
+	if (ret == 0)
+		ret = ov5647_write(sd, OV5647_REG_GAIN_LO, val & 0xff);
+
+	return ret;
+}
+
+static int ov5647_s_exposure(struct v4l2_subdev *sd, u32 val)
+{
+	int ret;
+
+	/* Sensor has 20 bits, but the bottom 4 bits are fractions of a line
+	 * which we leave as zero (and don't receive in "val").
+	 */
+	ret = ov5647_write(sd, OV5647_REG_EXP_HI, (val >> 12) & 0xf);
+	if (ret == 0)
+		ov5647_write(sd, OV5647_REG_EXP_MID, (val >> 4) & 0xff);
+	if (ret == 0)
+		ov5647_write(sd, OV5647_REG_EXP_LO, (val & 0xf) << 4);
+
+	return ret;
+}
+
+static int ov5647_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct ov5647 *state = container_of(ctrl->handler,
+					     struct ov5647, ctrls);
+	struct v4l2_subdev *sd = &state->sd;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	int ret = 0;
+
+	/* v4l2_ctrl_lock() locks our own mutex */
+
+	if (ctrl->id == V4L2_CID_VBLANK) {
+		int exposure_max, exposure_def;
+
+		/* Update max exposure while meeting expected vblanking */
+		exposure_max = state->mode->format.height + ctrl->val - 4;
+		exposure_def = (exposure_max < OV5647_EXPOSURE_DEFAULT) ?
+			exposure_max : OV5647_EXPOSURE_DEFAULT;
+		__v4l2_ctrl_modify_range(state->exposure,
+					 state->exposure->minimum,
+					 exposure_max, state->exposure->step,
+					 exposure_def);
+	}
+
+	/*
+	 * If the device is not powered up by the host driver do
+	 * not apply any controls to H/W at this time. Instead
+	 * the controls will be restored right after power-up.
+	 */
+	if (state->power_count == 0)
+		return 0;
+
+	switch (ctrl->id) {
+	case V4L2_CID_AUTO_WHITE_BALANCE:
+		ret = ov5647_s_auto_white_balance(sd, ctrl->val);
+		break;
+	case V4L2_CID_AUTOGAIN:
+		ret = ov5647_s_autogain(sd, ctrl->val);
+		break;
+	case V4L2_CID_EXPOSURE_AUTO:
+		ret = ov5647_s_exposure_auto(sd, ctrl->val);
+		break;
+	case V4L2_CID_ANALOGUE_GAIN:
+		ret = ov5647_s_analogue_gain(sd, ctrl->val);
+		break;
+	case V4L2_CID_EXPOSURE:
+		ret = ov5647_s_exposure(sd, ctrl->val);
+		break;
+	case V4L2_CID_PIXEL_RATE:
+		/* Read-only, but we adjust it based on mode. */
+		break;
+	case V4L2_CID_HBLANK:
+		/* Read-only, but we adjust it based on mode. */
+		break;
+	case V4L2_CID_VBLANK:
+		ret = ov5647_write16(sd, OV5647_REG_VTS_HI,
+				     state->mode->format.height + ctrl->val);
+		break;
+	default:
+		dev_info(&client->dev,
+			 "ctrl(id:0x%x,val:0x%x) is not handled\n",
+			 ctrl->id, ctrl->val);
+		ret = -EINVAL;
+		break;
+	}
+
+	return ret;
+}
+
+static const struct v4l2_ctrl_ops ov5647_ctrl_ops = {
+	.s_ctrl = ov5647_s_ctrl,
+};
+
 static int ov5647_probe(struct i2c_client *client)
 {
 	struct device *dev = &client->dev;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1507 @
 	struct v4l2_subdev *sd;
 	struct device_node *np = client->dev.of_node;
 	u32 xclk_freq;
+	int hblank, exposure_max, exposure_def;
+	struct v4l2_fwnode_device_properties props;
 
 	sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL);
 	if (!sensor)
 		return -ENOMEM;
 
 	if (IS_ENABLED(CONFIG_OF) && np) {
-		ret = ov5647_parse_dt(np);
+		ret = ov5647_parse_dt(np, sensor);
 		if (ret) {
 			dev_err(dev, "DT parsing error: %d\n", ret);
 			return ret;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1535 @
 		return -EINVAL;
 	}
 
+	/* Request the power down GPIO asserted */
+	sensor->pwdn = devm_gpiod_get_optional(&client->dev, "pwdn",
+					       GPIOD_OUT_HIGH);
+
 	mutex_init(&sensor->lock);
 
+	/* Initialise controls. */
+	v4l2_ctrl_handler_init(&sensor->ctrls, 9);
+	v4l2_ctrl_new_std(&sensor->ctrls, &ov5647_ctrl_ops,
+			  V4L2_CID_AUTOGAIN,
+			  0,  /* min */
+			  1,  /* max */
+			  1,  /* step */
+			  0); /* default */
+	v4l2_ctrl_new_std(&sensor->ctrls, &ov5647_ctrl_ops,
+			  V4L2_CID_AUTO_WHITE_BALANCE,
+			  0,  /* min */
+			  1,  /* max */
+			  1,  /* step */
+			  0); /* default */
+	v4l2_ctrl_new_std_menu(&sensor->ctrls, &ov5647_ctrl_ops,
+			       V4L2_CID_EXPOSURE_AUTO,
+			       V4L2_EXPOSURE_MANUAL,  /* max */
+			       0,                     /* skip_mask */
+			       V4L2_EXPOSURE_MANUAL); /* default */
+	v4l2_ctrl_new_std(&sensor->ctrls, &ov5647_ctrl_ops,
+			  V4L2_CID_ANALOGUE_GAIN,
+			  16,   /* min, 16 = 1.0x */
+			  1023, /* max (10 bits) */
+			  1,    /* step */
+			  32);  /* default, 32 = 2.0x */
+
+	/* Set the default mode before we init the subdev */
+	sensor->mode = OV5647_DEFAULT_MODE;
+
+	exposure_max = sensor->mode->vts_def - 4;
+	exposure_def = (exposure_max < OV5647_EXPOSURE_DEFAULT) ?
+		exposure_max : OV5647_EXPOSURE_DEFAULT;
+	sensor->exposure = v4l2_ctrl_new_std(&sensor->ctrls, &ov5647_ctrl_ops,
+					     V4L2_CID_EXPOSURE,
+					     OV5647_EXPOSURE_MIN, exposure_max,
+					     OV5647_EXPOSURE_STEP,
+					     exposure_def);
+
+	/* By default, PIXEL_RATE is read only, but it does change per mode */
+	sensor->pixel_rate = v4l2_ctrl_new_std(&sensor->ctrls, &ov5647_ctrl_ops,
+					       V4L2_CID_PIXEL_RATE,
+					       sensor->mode->pixel_rate,
+					       sensor->mode->pixel_rate, 1,
+					       sensor->mode->pixel_rate);
+
+	/* By default, HBLANK is read only, but it does change per mode */
+	hblank = sensor->mode->hts - sensor->mode->format.width;
+	sensor->hblank = v4l2_ctrl_new_std(&sensor->ctrls, &ov5647_ctrl_ops,
+					   V4L2_CID_HBLANK, hblank, hblank, 1,
+					   hblank);
+	sensor->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+
+	sensor->vblank = v4l2_ctrl_new_std(&sensor->ctrls, &ov5647_ctrl_ops,
+					   V4L2_CID_VBLANK, OV5647_VBLANK_MIN,
+					   OV5647_VTS_MAX -
+						sensor->mode->format.height, 1,
+					   sensor->mode->vts_def -
+						sensor->mode->format.height);
+
+	if (sensor->ctrls.error) {
+		ret = sensor->ctrls.error;
+		dev_err(&client->dev, "%s control init failed (%d)\n",
+			__func__, ret);
+		goto error;
+	}
+
+	ret = v4l2_fwnode_device_parse(&client->dev, &props);
+	if (ret)
+		goto error;
+
+	ret = v4l2_ctrl_new_fwnode_properties(&sensor->ctrls, &ov5647_ctrl_ops,
+					      &props);
+	if (ret)
+		goto error;
+
+	sensor->sd.ctrl_handler = &sensor->ctrls;
+
+	/* Write out the register set over I2C on stream-on. */
+	sensor->write_mode_regs = true;
+
 	sd = &sensor->sd;
 	v4l2_i2c_subdev_init(sd, client, &ov5647_subdev_ops);
 	sensor->sd.internal_ops = &ov5647_subdev_internal_ops;
-	sensor->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+	sensor->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE |
+			    V4L2_SUBDEV_FL_HAS_EVENTS;
 
 	sensor->pad.flags = MEDIA_PAD_FL_SOURCE;
 	sd->entity.function = MEDIA_ENT_F_CAM_SENSOR;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1633 @
 	if (ret < 0)
 		goto mutex_remove;
 
+	if (sensor->pwdn) {
+		gpiod_set_value_cansleep(sensor->pwdn, 0);
+		msleep(PWDN_ACTIVE_DELAY_MS);
+	}
+
 	ret = ov5647_detect(sd);
+
+	gpiod_set_value_cansleep(sensor->pwdn, 1);
+
 	if (ret < 0)
 		goto error;
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1654 @
 error:
 	media_entity_cleanup(&sd->entity);
 mutex_remove:
+	v4l2_ctrl_handler_free(&sensor->ctrls);
 	mutex_destroy(&sensor->lock);
 	return ret;
 }
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1666 @
 
 	v4l2_async_unregister_subdev(&ov5647->sd);
 	media_entity_cleanup(&ov5647->sd.entity);
+	v4l2_ctrl_handler_free(&ov5647->ctrls);
 	v4l2_device_unregister_subdev(sd);
 	mutex_destroy(&ov5647->lock);
 
diff -Nur --no-dereference linux-5.10.52-orig/drivers/media/i2c/ov9281.c linux-5.10.52-v7l+/drivers/media/i2c/ov9281.c
--- linux-5.10.52-orig/drivers/media/i2c/ov9281.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/media/i2c/ov9281.c	2021-07-25 16:46:05.788293865 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Omnivision OV9281 1280x800 global shutter image sensor driver
+ *
+ * This driver has been taken from
+ * https://github.com/rockchip-linux/kernel/blob/develop-4.4/drivers/media/i2c/ov9281.c
+ * cleaned up, made to compile against mainline kernels instead of the Rockchip
+ * vendor kernel, and the relevant controls added to work with libcamera.
+ *
+ * Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd.
+ * V0.0X01.0X02 fix mclk issue when probe multiple camera.
+ * V0.0X01.0X03 add enum_frame_interval function.
+ */
+
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/regulator/consumer.h>
+#include <linux/sysfs.h>
+#include <linux/slab.h>
+#include <media/media-entity.h>
+#include <media/v4l2-async.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-subdev.h>
+
+#define OV9281_LINK_FREQ_400MHZ		400000000
+#define OV9281_LANES			2
+
+/* pixel rate = link frequency * 2 * lanes / BITS_PER_SAMPLE */
+#define OV9281_PIXEL_RATE_10BIT		(OV9281_LINK_FREQ_400MHZ * 2 * \
+					 OV9281_LANES / 10)
+#define OV9281_PIXEL_RATE_8BIT		(OV9281_LINK_FREQ_400MHZ * 2 * \
+					 OV9281_LANES / 8)
+#define OV9281_XVCLK_FREQ		24000000
+
+#define CHIP_ID				0x9281
+#define OV9281_REG_CHIP_ID		0x300a
+
+#define OV9281_REG_TIMING_FORMAT_1		0x3820
+#define OV9281_REG_TIMING_FORMAT_2		0x3821
+#define OV9281_FLIP_BIT				BIT(2)
+
+#define OV9281_REG_CTRL_MODE		0x0100
+#define OV9281_MODE_SW_STANDBY		0x0
+#define OV9281_MODE_STREAMING		BIT(0)
+
+#define OV9281_REG_EXPOSURE		0x3500
+#define	OV9281_EXPOSURE_MIN		4
+#define	OV9281_EXPOSURE_STEP		1
+#define OV9281_VTS_MAX			0x7fff
+
+#define OV9281_REG_GAIN_H		0x3508
+#define OV9281_REG_GAIN_L		0x3509
+#define OV9281_GAIN_H_MASK		0x07
+#define OV9281_GAIN_H_SHIFT		8
+#define OV9281_GAIN_L_MASK		0xff
+#define OV9281_GAIN_MIN			0x10
+#define OV9281_GAIN_MAX			0xf8
+#define OV9281_GAIN_STEP		1
+#define OV9281_GAIN_DEFAULT		0x10
+
+#define OV9281_REG_TEST_PATTERN		0x5e00
+#define OV9281_TEST_PATTERN_ENABLE	0x80
+#define OV9281_TEST_PATTERN_DISABLE	0x0
+
+#define OV9281_REG_VTS			0x380e
+
+/*
+ * OV9281 native and active pixel array size.
+ * Datasheet not available to confirm these values, so assume there are no
+ * border pixels.
+ */
+#define OV9281_NATIVE_WIDTH		1280U
+#define OV9281_NATIVE_HEIGHT		800U
+#define OV9281_PIXEL_ARRAY_LEFT		0U
+#define OV9281_PIXEL_ARRAY_TOP		0U
+#define OV9281_PIXEL_ARRAY_WIDTH	1280U
+#define OV9281_PIXEL_ARRAY_HEIGHT	800U
+
+#define REG_NULL			0xFFFF
+
+#define OV9281_REG_VALUE_08BIT		1
+#define OV9281_REG_VALUE_16BIT		2
+#define OV9281_REG_VALUE_24BIT		3
+
+#define OV9281_NAME			"ov9281"
+
+static const char * const ov9281_supply_names[] = {
+	"avdd",		/* Analog power */
+	"dovdd",	/* Digital I/O power */
+	"dvdd",		/* Digital core power */
+};
+
+#define OV9281_NUM_SUPPLIES ARRAY_SIZE(ov9281_supply_names)
+
+struct regval {
+	u16 addr;
+	u8 val;
+};
+
+struct ov9281_mode {
+	u32 width;
+	u32 height;
+	u32 hts_def;
+	u32 vts_def;
+	u32 exp_def;
+	struct v4l2_rect crop;
+	const struct regval *reg_list;
+};
+
+struct ov9281 {
+	struct i2c_client	*client;
+	struct clk		*xvclk;
+	struct gpio_desc	*reset_gpio;
+	struct gpio_desc	*pwdn_gpio;
+	struct regulator_bulk_data supplies[OV9281_NUM_SUPPLIES];
+
+	struct v4l2_subdev	subdev;
+	struct media_pad	pad;
+	struct v4l2_ctrl_handler ctrl_handler;
+	struct v4l2_ctrl	*exposure;
+	struct v4l2_ctrl	*anal_gain;
+	struct v4l2_ctrl	*digi_gain;
+	struct v4l2_ctrl	*hblank;
+	struct v4l2_ctrl	*vblank;
+	struct v4l2_ctrl	*hflip;
+	struct v4l2_ctrl	*vflip;
+	struct v4l2_ctrl	*pixel_rate;
+	struct v4l2_ctrl	*test_pattern;
+	struct mutex		mutex;
+	bool			streaming;
+	bool			power_on;
+	const struct ov9281_mode *cur_mode;
+	u32			code;
+};
+
+#define to_ov9281(sd) container_of(sd, struct ov9281, subdev)
+
+/*
+ * Xclk 24Mhz
+ * max_framerate 120fps for 10 bit, 144fps for 8 bit.
+ * mipi_datarate per lane 800Mbps
+ */
+static const struct regval ov9281_common_regs[] = {
+	{0x0103, 0x01},
+	{0x0302, 0x32},
+	{0x030e, 0x02},
+	{0x3001, 0x00},
+	{0x3004, 0x00},
+	{0x3005, 0x00},
+	{0x3006, 0x04},
+	{0x3011, 0x0a},
+	{0x3013, 0x18},
+	{0x3022, 0x01},
+	{0x3023, 0x00},
+	{0x302c, 0x00},
+	{0x302f, 0x00},
+	{0x3030, 0x04},
+	{0x3039, 0x32},
+	{0x303a, 0x00},
+	{0x303f, 0x01},
+	{0x3500, 0x00},
+	{0x3501, 0x2a},
+	{0x3502, 0x90},
+	{0x3503, 0x08},
+	{0x3505, 0x8c},
+	{0x3507, 0x03},
+	{0x3508, 0x00},
+	{0x3509, 0x10},
+	{0x3610, 0x80},
+	{0x3611, 0xa0},
+	{0x3620, 0x6f},
+	{0x3632, 0x56},
+	{0x3633, 0x78},
+	{0x3666, 0x00},
+	{0x366f, 0x5a},
+	{0x3680, 0x84},
+	{0x3712, 0x80},
+	{0x372d, 0x22},
+	{0x3731, 0x80},
+	{0x3732, 0x30},
+	{0x377d, 0x22},
+	{0x3788, 0x02},
+	{0x3789, 0xa4},
+	{0x378a, 0x00},
+	{0x378b, 0x4a},
+	{0x3799, 0x20},
+	{0x3881, 0x42},
+	{0x38b1, 0x00},
+	{0x3920, 0xff},
+	{0x4010, 0x40},
+	{0x4043, 0x40},
+	{0x4307, 0x30},
+	{0x4317, 0x00},
+	{0x4501, 0x00},
+	{0x450a, 0x08},
+	{0x4601, 0x04},
+	{0x470f, 0x00},
+	{0x4f07, 0x00},
+	{0x4800, 0x00},
+	{0x5000, 0x9f},
+	{0x5001, 0x00},
+	{0x5e00, 0x00},
+	{0x5d00, 0x07},
+	{0x5d01, 0x00},
+	{REG_NULL, 0x00},
+};
+
+static const struct regval ov9281_1280x800_regs[] = {
+	{0x3778, 0x00},
+	{0x3800, 0x00},
+	{0x3801, 0x00},
+	{0x3802, 0x00},
+	{0x3803, 0x00},
+	{0x3804, 0x05},
+	{0x3805, 0x0f},
+	{0x3806, 0x03},
+	{0x3807, 0x2f},
+	{0x3808, 0x05},
+	{0x3809, 0x00},
+	{0x380a, 0x03},
+	{0x380b, 0x20},
+	{0x380c, 0x02},
+	{0x380d, 0xd8},
+	{0x380e, 0x03},
+	{0x380f, 0x8e},
+	{0x3810, 0x00},
+	{0x3811, 0x08},
+	{0x3812, 0x00},
+	{0x3813, 0x08},
+	{0x3814, 0x11},
+	{0x3815, 0x11},
+	{0x3820, 0x40},
+	{0x3821, 0x00},
+	{0x4003, 0x40},
+	{0x4008, 0x04},
+	{0x4009, 0x0b},
+	{0x400c, 0x00},
+	{0x400d, 0x07},
+	{0x4507, 0x00},
+	{0x4509, 0x00},
+	{REG_NULL, 0x00},
+};
+
+static const struct regval ov9281_1280x720_regs[] = {
+	{0x3778, 0x00},
+	{0x3800, 0x00},
+	{0x3801, 0x00},
+	{0x3802, 0x00},
+	{0x3803, 0x28},
+	{0x3804, 0x05},
+	{0x3805, 0x0f},
+	{0x3806, 0x03},
+	{0x3807, 0x07},
+	{0x3808, 0x05},
+	{0x3809, 0x00},
+	{0x380a, 0x02},
+	{0x380b, 0xd0},
+	{0x380c, 0x02},
+	{0x380d, 0xd8},
+	{0x380e, 0x03},
+	{0x380f, 0x8e},
+	{0x3810, 0x00},
+	{0x3811, 0x08},
+	{0x3812, 0x00},
+	{0x3813, 0x08},
+	{0x3814, 0x11},
+	{0x3815, 0x11},
+	{0x3820, 0x40},
+	{0x3821, 0x00},
+	{0x4003, 0x40},
+	{0x4008, 0x04},
+	{0x4009, 0x0b},
+	{0x400c, 0x00},
+	{0x400d, 0x07},
+	{0x4507, 0x00},
+	{0x4509, 0x00},
+	{REG_NULL, 0x00},
+};
+
+static const struct regval ov9281_640x400_regs[] = {
+	{0x3778, 0x10},
+	{0x3800, 0x00},
+	{0x3801, 0x00},
+	{0x3802, 0x00},
+	{0x3803, 0x00},
+	{0x3804, 0x05},
+	{0x3805, 0x0f},
+	{0x3806, 0x03},
+	{0x3807, 0x2f},
+	{0x3808, 0x02},
+	{0x3809, 0x80},
+	{0x380a, 0x01},
+	{0x380b, 0x90},
+	{0x380c, 0x02},
+	{0x380d, 0xd8},
+	{0x380e, 0x02},
+	{0x380f, 0x08},
+	{0x3810, 0x00},
+	{0x3811, 0x04},
+	{0x3812, 0x00},
+	{0x3813, 0x04},
+	{0x3814, 0x31},
+	{0x3815, 0x22},
+	{0x3820, 0x60},
+	{0x3821, 0x01},
+	{0x4008, 0x02},
+	{0x4009, 0x05},
+	{0x400c, 0x00},
+	{0x400d, 0x03},
+	{0x4507, 0x03},
+	{0x4509, 0x80},
+	{REG_NULL, 0x00},
+};
+
+static const struct regval op_10bit[] = {
+	{0x030d, 0x50},
+	{0x3662, 0x05},
+	{REG_NULL, 0x00},
+};
+
+static const struct regval op_8bit[] = {
+	{0x030d, 0x60},
+	{0x3662, 0x07},
+	{REG_NULL, 0x00},
+};
+
+static const struct ov9281_mode supported_modes[] = {
+	{
+		.width = 1280,
+		.height = 800,
+		.exp_def = 0x0320,
+		.hts_def = 0x05b0,	/* 0x2d8*2 */
+		.vts_def = 0x038e,
+		.crop = {
+			.left = 0,
+			.top = 0,
+			.width = 1280,
+			.height = 800
+		},
+		.reg_list = ov9281_1280x800_regs,
+	},
+	{
+		.width = 1280,
+		.height = 720,
+		.exp_def = 0x0320,
+		.hts_def = 0x05b0,
+		.vts_def = 761,
+		.crop = {
+			.left = 0,
+			.top = 40,
+			.width = 1280,
+			.height = 720
+		},
+		.reg_list = ov9281_1280x720_regs,
+	},
+	{
+		.width = 640,
+		.height = 400,
+		.exp_def = 0x0320,
+		.hts_def = 0x05b0,
+		.vts_def = 421,
+		.crop = {
+			.left = 0,
+			.top = 0,
+			.width = 1280,
+			.height = 800
+		},
+		.reg_list = ov9281_640x400_regs,
+	},
+};
+
+static const s64 link_freq_menu_items[] = {
+	OV9281_LINK_FREQ_400MHZ
+};
+
+static const char * const ov9281_test_pattern_menu[] = {
+	"Disabled",
+	"Vertical Color Bar Type 1",
+	"Vertical Color Bar Type 2",
+	"Vertical Color Bar Type 3",
+	"Vertical Color Bar Type 4"
+};
+
+/* Write registers up to 4 at a time */
+static int ov9281_write_reg(struct i2c_client *client, u16 reg,
+			    u32 len, u32 val)
+{
+	u32 buf_i, val_i;
+	u8 buf[6];
+	u8 *val_p;
+	__be32 val_be;
+
+	if (len > 4)
+		return -EINVAL;
+
+	buf[0] = reg >> 8;
+	buf[1] = reg & 0xff;
+
+	val_be = cpu_to_be32(val);
+	val_p = (u8 *)&val_be;
+	buf_i = 2;
+	val_i = 4 - len;
+
+	while (val_i < 4)
+		buf[buf_i++] = val_p[val_i++];
+
+	if (i2c_master_send(client, buf, len + 2) != len + 2)
+		return -EIO;
+
+	return 0;
+}
+
+static int ov9281_write_array(struct i2c_client *client,
+			      const struct regval *regs)
+{
+	u32 i;
+	int ret = 0;
+
+	for (i = 0; ret == 0 && regs[i].addr != REG_NULL; i++)
+		ret = ov9281_write_reg(client, regs[i].addr,
+				       OV9281_REG_VALUE_08BIT, regs[i].val);
+
+	return ret;
+}
+
+/* Read registers up to 4 at a time */
+static int ov9281_read_reg(struct i2c_client *client, u16 reg, unsigned int len,
+			   u32 *val)
+{
+	struct i2c_msg msgs[2];
+	u8 *data_be_p;
+	__be32 data_be = 0;
+	__be16 reg_addr_be = cpu_to_be16(reg);
+	int ret;
+
+	if (len > 4 || !len)
+		return -EINVAL;
+
+	data_be_p = (u8 *)&data_be;
+	/* Write register address */
+	msgs[0].addr = client->addr;
+	msgs[0].flags = 0;
+	msgs[0].len = 2;
+	msgs[0].buf = (u8 *)&reg_addr_be;
+
+	/* Read data from register */
+	msgs[1].addr = client->addr;
+	msgs[1].flags = I2C_M_RD;
+	msgs[1].len = len;
+	msgs[1].buf = &data_be_p[4 - len];
+
+	ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
+	if (ret != ARRAY_SIZE(msgs))
+		return -EIO;
+
+	*val = be32_to_cpu(data_be);
+
+	return 0;
+}
+
+static int ov9281_get_reso_dist(const struct ov9281_mode *mode,
+				struct v4l2_mbus_framefmt *framefmt)
+{
+	return abs(mode->width - framefmt->width) +
+	       abs(mode->height - framefmt->height);
+}
+
+static const struct ov9281_mode *
+ov9281_find_best_fit(struct v4l2_subdev_format *fmt)
+{
+	struct v4l2_mbus_framefmt *framefmt = &fmt->format;
+	int dist;
+	int cur_best_fit = 0;
+	int cur_best_fit_dist = -1;
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(supported_modes); i++) {
+		dist = ov9281_get_reso_dist(&supported_modes[i], framefmt);
+		if (cur_best_fit_dist == -1 || dist < cur_best_fit_dist) {
+			cur_best_fit_dist = dist;
+			cur_best_fit = i;
+		}
+	}
+
+	return &supported_modes[cur_best_fit];
+}
+
+static int ov9281_set_fmt(struct v4l2_subdev *sd,
+			  struct v4l2_subdev_pad_config *cfg,
+			  struct v4l2_subdev_format *fmt)
+{
+	struct ov9281 *ov9281 = to_ov9281(sd);
+	const struct ov9281_mode *mode;
+	s64 h_blank, vblank_def, pixel_rate;
+
+	mutex_lock(&ov9281->mutex);
+
+	mode = ov9281_find_best_fit(fmt);
+	if (fmt->format.code != MEDIA_BUS_FMT_Y8_1X8)
+		fmt->format.code = MEDIA_BUS_FMT_Y10_1X10;
+	fmt->format.width = mode->width;
+	fmt->format.height = mode->height;
+	fmt->format.field = V4L2_FIELD_NONE;
+	fmt->format.colorspace = V4L2_COLORSPACE_SRGB;
+	fmt->format.ycbcr_enc =
+			V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->format.colorspace);
+	fmt->format.quantization =
+		V4L2_MAP_QUANTIZATION_DEFAULT(true, fmt->format.colorspace,
+					      fmt->format.ycbcr_enc);
+	fmt->format.xfer_func =
+		V4L2_MAP_XFER_FUNC_DEFAULT(fmt->format.colorspace);
+
+	if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
+		*v4l2_subdev_get_try_format(sd, cfg, fmt->pad) = fmt->format;
+	} else {
+		ov9281->cur_mode = mode;
+		ov9281->code = fmt->format.code;
+		h_blank = mode->hts_def - mode->width;
+		__v4l2_ctrl_modify_range(ov9281->hblank, h_blank,
+					 h_blank, 1, h_blank);
+		__v4l2_ctrl_s_ctrl(ov9281->hblank, h_blank);
+		vblank_def = mode->vts_def - mode->height;
+		__v4l2_ctrl_modify_range(ov9281->vblank, vblank_def,
+					 OV9281_VTS_MAX - mode->height,
+					 1, vblank_def);
+		__v4l2_ctrl_s_ctrl(ov9281->vblank, vblank_def);
+
+		pixel_rate = (fmt->format.code == MEDIA_BUS_FMT_Y10_1X10) ?
+			OV9281_PIXEL_RATE_10BIT : OV9281_PIXEL_RATE_8BIT;
+		__v4l2_ctrl_modify_range(ov9281->pixel_rate, pixel_rate,
+					 pixel_rate, 1, pixel_rate);
+	}
+
+	mutex_unlock(&ov9281->mutex);
+
+	return 0;
+}
+
+static int ov9281_get_fmt(struct v4l2_subdev *sd,
+			  struct v4l2_subdev_pad_config *cfg,
+			  struct v4l2_subdev_format *fmt)
+{
+	struct ov9281 *ov9281 = to_ov9281(sd);
+	const struct ov9281_mode *mode = ov9281->cur_mode;
+
+	mutex_lock(&ov9281->mutex);
+	if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
+		fmt->format = *v4l2_subdev_get_try_format(sd, cfg, fmt->pad);
+	} else {
+		fmt->format.width = mode->width;
+		fmt->format.height = mode->height;
+		fmt->format.code = ov9281->code;
+		fmt->format.field = V4L2_FIELD_NONE;
+		fmt->format.colorspace = V4L2_COLORSPACE_SRGB;
+		fmt->format.ycbcr_enc =
+			V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->format.colorspace);
+		fmt->format.quantization =
+			V4L2_MAP_QUANTIZATION_DEFAULT(true,
+						      fmt->format.colorspace,
+						      fmt->format.ycbcr_enc);
+		fmt->format.xfer_func =
+			V4L2_MAP_XFER_FUNC_DEFAULT(fmt->format.colorspace);
+	}
+	mutex_unlock(&ov9281->mutex);
+
+	return 0;
+}
+
+static int ov9281_enum_mbus_code(struct v4l2_subdev *sd,
+				 struct v4l2_subdev_pad_config *cfg,
+				 struct v4l2_subdev_mbus_code_enum *code)
+{
+	switch (code->index) {
+	default:
+		return -EINVAL;
+	case 0:
+		code->code = MEDIA_BUS_FMT_Y10_1X10;
+		break;
+	case 1:
+		code->code = MEDIA_BUS_FMT_Y8_1X8;
+		break;
+	}
+
+	return 0;
+}
+
+static int ov9281_enum_frame_sizes(struct v4l2_subdev *sd,
+				   struct v4l2_subdev_pad_config *cfg,
+				   struct v4l2_subdev_frame_size_enum *fse)
+{
+	if (fse->index >= ARRAY_SIZE(supported_modes))
+		return -EINVAL;
+
+	if (fse->code != MEDIA_BUS_FMT_Y10_1X10 &&
+	    fse->code != MEDIA_BUS_FMT_Y8_1X8)
+		return -EINVAL;
+
+	fse->min_width  = supported_modes[fse->index].width;
+	fse->max_width  = supported_modes[fse->index].width;
+	fse->max_height = supported_modes[fse->index].height;
+	fse->min_height = supported_modes[fse->index].height;
+
+	return 0;
+}
+
+static int ov9281_enable_test_pattern(struct ov9281 *ov9281, u32 pattern)
+{
+	u32 val;
+
+	if (pattern)
+		val = (pattern - 1) | OV9281_TEST_PATTERN_ENABLE;
+	else
+		val = OV9281_TEST_PATTERN_DISABLE;
+
+	return ov9281_write_reg(ov9281->client, OV9281_REG_TEST_PATTERN,
+				OV9281_REG_VALUE_08BIT, val);
+}
+
+static int ov9281_set_ctrl_hflip(struct ov9281 *ov9281, int value)
+{
+	u32 current_val;
+	int ret = ov9281_read_reg(ov9281->client, OV9281_REG_TIMING_FORMAT_2,
+					OV9281_REG_VALUE_08BIT, &current_val);
+	if (!ret) {
+		if (value)
+			current_val |= OV9281_FLIP_BIT;
+		else
+			current_val &= ~OV9281_FLIP_BIT;
+		return ov9281_write_reg(ov9281->client,
+						OV9281_REG_TIMING_FORMAT_2,
+						OV9281_REG_VALUE_08BIT,
+						current_val);
+	}
+	return ret;
+}
+
+static int ov9281_set_ctrl_vflip(struct ov9281 *ov9281, int value)
+{
+	u32 current_val;
+	int ret = ov9281_read_reg(ov9281->client, OV9281_REG_TIMING_FORMAT_1,
+					OV9281_REG_VALUE_08BIT, &current_val);
+	if (!ret) {
+		if (value)
+			current_val |= OV9281_FLIP_BIT;
+		else
+			current_val &= ~OV9281_FLIP_BIT;
+		return ov9281_write_reg(ov9281->client,
+						OV9281_REG_TIMING_FORMAT_1,
+						OV9281_REG_VALUE_08BIT,
+						current_val);
+	}
+	return ret;
+}
+
+static const struct v4l2_rect *
+__ov9281_get_pad_crop(struct ov9281 *ov9281, struct v4l2_subdev_pad_config *cfg,
+		      unsigned int pad, enum v4l2_subdev_format_whence which)
+{
+	switch (which) {
+	case V4L2_SUBDEV_FORMAT_TRY:
+		return v4l2_subdev_get_try_crop(&ov9281->subdev, cfg, pad);
+	case V4L2_SUBDEV_FORMAT_ACTIVE:
+		return &ov9281->cur_mode->crop;
+	}
+
+	return NULL;
+}
+
+static int ov9281_get_selection(struct v4l2_subdev *sd,
+				struct v4l2_subdev_pad_config *cfg,
+				struct v4l2_subdev_selection *sel)
+{
+	switch (sel->target) {
+	case V4L2_SEL_TGT_CROP: {
+		struct ov9281 *ov9281 = to_ov9281(sd);
+
+		mutex_lock(&ov9281->mutex);
+		sel->r = *__ov9281_get_pad_crop(ov9281, cfg, sel->pad,
+						sel->which);
+		mutex_unlock(&ov9281->mutex);
+
+		return 0;
+	}
+
+	case V4L2_SEL_TGT_NATIVE_SIZE:
+		sel->r.top = 0;
+		sel->r.left = 0;
+		sel->r.width = OV9281_NATIVE_WIDTH;
+		sel->r.height = OV9281_NATIVE_HEIGHT;
+
+		return 0;
+
+	case V4L2_SEL_TGT_CROP_DEFAULT:
+	case V4L2_SEL_TGT_CROP_BOUNDS:
+		sel->r.top = OV9281_PIXEL_ARRAY_TOP;
+		sel->r.left = OV9281_PIXEL_ARRAY_LEFT;
+		sel->r.width = OV9281_PIXEL_ARRAY_WIDTH;
+		sel->r.height = OV9281_PIXEL_ARRAY_HEIGHT;
+
+		return 0;
+	}
+
+	return -EINVAL;
+}
+
+static int __ov9281_start_stream(struct ov9281 *ov9281)
+{
+	int ret;
+
+	ret = ov9281_write_array(ov9281->client, ov9281_common_regs);
+	if (ret)
+		return ret;
+
+	ret = ov9281_write_array(ov9281->client, ov9281->cur_mode->reg_list);
+	if (ret)
+		return ret;
+
+	if (ov9281->code == MEDIA_BUS_FMT_Y10_1X10)
+		ret = ov9281_write_array(ov9281->client, op_10bit);
+	else
+		ret = ov9281_write_array(ov9281->client, op_8bit);
+	if (ret)
+		return ret;
+
+	/* In case these controls are set before streaming */
+	mutex_unlock(&ov9281->mutex);
+	ret = v4l2_ctrl_handler_setup(&ov9281->ctrl_handler);
+	mutex_lock(&ov9281->mutex);
+	if (ret)
+		return ret;
+
+	return ov9281_write_reg(ov9281->client, OV9281_REG_CTRL_MODE,
+				OV9281_REG_VALUE_08BIT, OV9281_MODE_STREAMING);
+}
+
+static int __ov9281_stop_stream(struct ov9281 *ov9281)
+{
+	return ov9281_write_reg(ov9281->client, OV9281_REG_CTRL_MODE,
+				OV9281_REG_VALUE_08BIT, OV9281_MODE_SW_STANDBY);
+}
+
+static int ov9281_s_stream(struct v4l2_subdev *sd, int on)
+{
+	struct ov9281 *ov9281 = to_ov9281(sd);
+	struct i2c_client *client = ov9281->client;
+	int ret = 0;
+
+	mutex_lock(&ov9281->mutex);
+	on = !!on;
+	if (on == ov9281->streaming)
+		goto unlock_and_return;
+
+	if (on) {
+		ret = pm_runtime_get_sync(&client->dev);
+		if (ret < 0) {
+			pm_runtime_put_noidle(&client->dev);
+			goto unlock_and_return;
+		}
+
+		ret = __ov9281_start_stream(ov9281);
+		if (ret) {
+			v4l2_err(sd, "start stream failed while write regs\n");
+			pm_runtime_put(&client->dev);
+			goto unlock_and_return;
+		}
+	} else {
+		__ov9281_stop_stream(ov9281);
+		pm_runtime_put(&client->dev);
+	}
+
+	ov9281->streaming = on;
+
+unlock_and_return:
+	mutex_unlock(&ov9281->mutex);
+
+	return ret;
+}
+
+static int ov9281_s_power(struct v4l2_subdev *sd, int on)
+{
+	struct ov9281 *ov9281 = to_ov9281(sd);
+	struct i2c_client *client = ov9281->client;
+	int ret = 0;
+
+	mutex_lock(&ov9281->mutex);
+
+	/* If the power state is not modified - no work to do. */
+	if (ov9281->power_on == !!on)
+		goto unlock_and_return;
+
+	if (on) {
+		ret = pm_runtime_get_sync(&client->dev);
+		if (ret < 0) {
+			pm_runtime_put_noidle(&client->dev);
+			goto unlock_and_return;
+		}
+		ov9281->power_on = true;
+	} else {
+		pm_runtime_put(&client->dev);
+		ov9281->power_on = false;
+	}
+
+unlock_and_return:
+	mutex_unlock(&ov9281->mutex);
+
+	return ret;
+}
+
+/* Calculate the delay in us by clock rate and clock cycles */
+static inline u32 ov9281_cal_delay(u32 cycles)
+{
+	return DIV_ROUND_UP(cycles, OV9281_XVCLK_FREQ / 1000 / 1000);
+}
+
+static int __ov9281_power_on(struct ov9281 *ov9281)
+{
+	int ret;
+	u32 delay_us;
+	struct device *dev = &ov9281->client->dev;
+
+	ret = clk_set_rate(ov9281->xvclk, OV9281_XVCLK_FREQ);
+	if (ret < 0)
+		dev_warn(dev, "Failed to set xvclk rate (24MHz)\n");
+	if (clk_get_rate(ov9281->xvclk) != OV9281_XVCLK_FREQ)
+		dev_warn(dev, "xvclk mismatched, modes are based on 24MHz - rate is %lu\n",
+			 clk_get_rate(ov9281->xvclk));
+
+	ret = clk_prepare_enable(ov9281->xvclk);
+	if (ret < 0) {
+		dev_err(dev, "Failed to enable xvclk\n");
+		return ret;
+	}
+
+	if (!IS_ERR(ov9281->reset_gpio))
+		gpiod_set_value_cansleep(ov9281->reset_gpio, 0);
+
+	ret = regulator_bulk_enable(OV9281_NUM_SUPPLIES, ov9281->supplies);
+	if (ret < 0) {
+		dev_err(dev, "Failed to enable regulators\n");
+		goto disable_clk;
+	}
+
+	if (!IS_ERR(ov9281->reset_gpio))
+		gpiod_set_value_cansleep(ov9281->reset_gpio, 1);
+
+	usleep_range(500, 1000);
+	if (!IS_ERR(ov9281->pwdn_gpio))
+		gpiod_set_value_cansleep(ov9281->pwdn_gpio, 1);
+
+	/* 8192 cycles prior to first SCCB transaction */
+	delay_us = ov9281_cal_delay(8192);
+	usleep_range(delay_us, delay_us * 2);
+
+	return 0;
+
+disable_clk:
+	clk_disable_unprepare(ov9281->xvclk);
+
+	return ret;
+}
+
+static void __ov9281_power_off(struct ov9281 *ov9281)
+{
+	if (!IS_ERR(ov9281->pwdn_gpio))
+		gpiod_set_value_cansleep(ov9281->pwdn_gpio, 0);
+	clk_disable_unprepare(ov9281->xvclk);
+	if (!IS_ERR(ov9281->reset_gpio))
+		gpiod_set_value_cansleep(ov9281->reset_gpio, 0);
+	regulator_bulk_disable(OV9281_NUM_SUPPLIES, ov9281->supplies);
+}
+
+static int ov9281_runtime_resume(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct v4l2_subdev *sd = i2c_get_clientdata(client);
+	struct ov9281 *ov9281 = to_ov9281(sd);
+
+	return __ov9281_power_on(ov9281);
+}
+
+static int ov9281_runtime_suspend(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct v4l2_subdev *sd = i2c_get_clientdata(client);
+	struct ov9281 *ov9281 = to_ov9281(sd);
+
+	__ov9281_power_off(ov9281);
+
+	return 0;
+}
+
+static int ov9281_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+	struct ov9281 *ov9281 = to_ov9281(sd);
+	struct v4l2_mbus_framefmt *try_fmt =
+				v4l2_subdev_get_try_format(sd, fh->pad, 0);
+	const struct ov9281_mode *def_mode = &supported_modes[0];
+
+	mutex_lock(&ov9281->mutex);
+	/* Initialize try_fmt */
+	try_fmt->width = def_mode->width;
+	try_fmt->height = def_mode->height;
+	try_fmt->code = MEDIA_BUS_FMT_Y10_1X10;
+	try_fmt->field = V4L2_FIELD_NONE;
+	try_fmt->colorspace = V4L2_COLORSPACE_SRGB;
+	try_fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(try_fmt->colorspace);
+	try_fmt->quantization =
+		V4L2_MAP_QUANTIZATION_DEFAULT(true, try_fmt->colorspace,
+					      try_fmt->ycbcr_enc);
+	try_fmt->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(try_fmt->colorspace);
+
+	mutex_unlock(&ov9281->mutex);
+	/* No crop or compose */
+
+	return 0;
+}
+
+static const struct dev_pm_ops ov9281_pm_ops = {
+	SET_RUNTIME_PM_OPS(ov9281_runtime_suspend,
+			   ov9281_runtime_resume, NULL)
+};
+
+static const struct v4l2_subdev_internal_ops ov9281_internal_ops = {
+	.open = ov9281_open,
+};
+
+static const struct v4l2_subdev_core_ops ov9281_core_ops = {
+	.s_power = ov9281_s_power,
+};
+
+static const struct v4l2_subdev_video_ops ov9281_video_ops = {
+	.s_stream = ov9281_s_stream,
+};
+
+static const struct v4l2_subdev_pad_ops ov9281_pad_ops = {
+	.enum_mbus_code = ov9281_enum_mbus_code,
+	.enum_frame_size = ov9281_enum_frame_sizes,
+	.get_fmt = ov9281_get_fmt,
+	.set_fmt = ov9281_set_fmt,
+	.get_selection = ov9281_get_selection,
+};
+
+static const struct v4l2_subdev_ops ov9281_subdev_ops = {
+	.core	= &ov9281_core_ops,
+	.video	= &ov9281_video_ops,
+	.pad	= &ov9281_pad_ops,
+};
+
+static int ov9281_set_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct ov9281 *ov9281 = container_of(ctrl->handler,
+					     struct ov9281, ctrl_handler);
+	struct i2c_client *client = ov9281->client;
+	s64 max;
+	int ret = 0;
+
+	/* Propagate change of current control to all related controls */
+	switch (ctrl->id) {
+	case V4L2_CID_VBLANK:
+		/* Update max exposure while meeting expected vblanking */
+		max = ov9281->cur_mode->height + ctrl->val - 4;
+		__v4l2_ctrl_modify_range(ov9281->exposure,
+					 ov9281->exposure->minimum, max,
+					 ov9281->exposure->step,
+					 ov9281->exposure->default_value);
+		break;
+	}
+
+	if (pm_runtime_get(&client->dev) <= 0)
+		return 0;
+
+	switch (ctrl->id) {
+	case V4L2_CID_HFLIP:
+		ret = ov9281_set_ctrl_hflip(ov9281, ctrl->val);
+		break;
+	case V4L2_CID_VFLIP:
+		ret = ov9281_set_ctrl_vflip(ov9281, ctrl->val);
+		break;
+	case V4L2_CID_EXPOSURE:
+		/* 4 least significant bits of expsoure are fractional part */
+		ret = ov9281_write_reg(ov9281->client, OV9281_REG_EXPOSURE,
+				       OV9281_REG_VALUE_24BIT, ctrl->val << 4);
+		break;
+	case V4L2_CID_ANALOGUE_GAIN:
+		ret = ov9281_write_reg(ov9281->client, OV9281_REG_GAIN_H,
+				       OV9281_REG_VALUE_08BIT,
+				       (ctrl->val >> OV9281_GAIN_H_SHIFT) &
+							OV9281_GAIN_H_MASK);
+		ret |= ov9281_write_reg(ov9281->client, OV9281_REG_GAIN_L,
+				       OV9281_REG_VALUE_08BIT,
+				       ctrl->val & OV9281_GAIN_L_MASK);
+		break;
+	case V4L2_CID_VBLANK:
+		ret = ov9281_write_reg(ov9281->client, OV9281_REG_VTS,
+				       OV9281_REG_VALUE_16BIT,
+				       ctrl->val + ov9281->cur_mode->height);
+		break;
+	case V4L2_CID_TEST_PATTERN:
+		ret = ov9281_enable_test_pattern(ov9281, ctrl->val);
+		break;
+	default:
+		dev_warn(&client->dev, "%s Unhandled id:0x%x, val:0x%x\n",
+			 __func__, ctrl->id, ctrl->val);
+		break;
+	}
+
+	pm_runtime_put(&client->dev);
+
+	return ret;
+}
+
+static const struct v4l2_ctrl_ops ov9281_ctrl_ops = {
+	.s_ctrl = ov9281_set_ctrl,
+};
+
+static int ov9281_initialize_controls(struct ov9281 *ov9281)
+{
+	const struct ov9281_mode *mode;
+	struct v4l2_ctrl_handler *handler;
+	struct v4l2_ctrl *ctrl;
+	s64 exposure_max, vblank_def;
+	u32 h_blank;
+	int ret;
+
+	handler = &ov9281->ctrl_handler;
+	mode = ov9281->cur_mode;
+	ret = v4l2_ctrl_handler_init(handler, 9);
+	if (ret)
+		return ret;
+	handler->lock = &ov9281->mutex;
+
+	ctrl = v4l2_ctrl_new_int_menu(handler, NULL, V4L2_CID_LINK_FREQ,
+				      0, 0, link_freq_menu_items);
+	if (ctrl)
+		ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+
+	ov9281->pixel_rate = v4l2_ctrl_new_std(handler, NULL,
+					       V4L2_CID_PIXEL_RATE,
+					       OV9281_PIXEL_RATE_10BIT,
+					       OV9281_PIXEL_RATE_10BIT, 1,
+					       OV9281_PIXEL_RATE_10BIT);
+
+	h_blank = mode->hts_def - mode->width;
+	ov9281->hblank = v4l2_ctrl_new_std(handler, NULL, V4L2_CID_HBLANK,
+					   h_blank, h_blank, 1, h_blank);
+	if (ov9281->hblank)
+		ov9281->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+
+	vblank_def = mode->vts_def - mode->height;
+	ov9281->vblank = v4l2_ctrl_new_std(handler, &ov9281_ctrl_ops,
+					   V4L2_CID_VBLANK, vblank_def,
+					   OV9281_VTS_MAX - mode->height, 1,
+					   vblank_def);
+
+	exposure_max = mode->vts_def - 4;
+	ov9281->exposure = v4l2_ctrl_new_std(handler, &ov9281_ctrl_ops,
+					     V4L2_CID_EXPOSURE,
+					     OV9281_EXPOSURE_MIN, exposure_max,
+					     OV9281_EXPOSURE_STEP,
+					     mode->exp_def);
+
+	ov9281->anal_gain = v4l2_ctrl_new_std(handler, &ov9281_ctrl_ops,
+					      V4L2_CID_ANALOGUE_GAIN,
+					      OV9281_GAIN_MIN, OV9281_GAIN_MAX,
+					      OV9281_GAIN_STEP,
+					      OV9281_GAIN_DEFAULT);
+
+	ov9281->vflip = v4l2_ctrl_new_std(handler, &ov9281_ctrl_ops,
+					  V4L2_CID_VFLIP,
+						0, 1, 1, 0);
+
+	ov9281->hflip = v4l2_ctrl_new_std(handler, &ov9281_ctrl_ops,
+					  V4L2_CID_HFLIP,
+						0, 1, 1, 0);
+
+	ov9281->test_pattern =
+		v4l2_ctrl_new_std_menu_items(handler, &ov9281_ctrl_ops,
+					     V4L2_CID_TEST_PATTERN,
+					     ARRAY_SIZE(ov9281_test_pattern_menu) - 1,
+					     0, 0, ov9281_test_pattern_menu);
+
+	if (handler->error) {
+		ret = handler->error;
+		dev_err(&ov9281->client->dev,
+			"Failed to init controls(%d)\n", ret);
+		goto err_free_handler;
+	}
+
+	ov9281->subdev.ctrl_handler = handler;
+
+	return 0;
+
+err_free_handler:
+	v4l2_ctrl_handler_free(handler);
+
+	return ret;
+}
+
+static int ov9281_check_sensor_id(struct ov9281 *ov9281,
+				  struct i2c_client *client)
+{
+	struct device *dev = &ov9281->client->dev;
+	u32 id = 0, id_msb;
+	int ret;
+
+	ret = ov9281_read_reg(client, OV9281_REG_CHIP_ID + 1,
+			      OV9281_REG_VALUE_08BIT, &id);
+	if (!ret)
+		ret = ov9281_read_reg(client, OV9281_REG_CHIP_ID,
+				      OV9281_REG_VALUE_08BIT, &id_msb);
+	id |= (id_msb << 8);
+	if (ret || id != CHIP_ID) {
+		dev_err(dev, "Unexpected sensor id(%04x), ret(%d)\n", id, ret);
+		return -ENODEV;
+	}
+
+	dev_info(dev, "Detected OV%06x sensor\n", CHIP_ID);
+
+	return 0;
+}
+
+static int ov9281_configure_regulators(struct ov9281 *ov9281)
+{
+	unsigned int i;
+
+	for (i = 0; i < OV9281_NUM_SUPPLIES; i++)
+		ov9281->supplies[i].supply = ov9281_supply_names[i];
+
+	return devm_regulator_bulk_get(&ov9281->client->dev,
+				       OV9281_NUM_SUPPLIES,
+				       ov9281->supplies);
+}
+
+static int ov9281_probe(struct i2c_client *client,
+			const struct i2c_device_id *id)
+{
+	struct device *dev = &client->dev;
+	struct ov9281 *ov9281;
+	struct v4l2_subdev *sd;
+	int ret;
+
+	ov9281 = devm_kzalloc(dev, sizeof(*ov9281), GFP_KERNEL);
+	if (!ov9281)
+		return -ENOMEM;
+
+	ov9281->client = client;
+	ov9281->cur_mode = &supported_modes[0];
+
+	ov9281->xvclk = devm_clk_get(dev, "xvclk");
+	if (IS_ERR(ov9281->xvclk)) {
+		dev_err(dev, "Failed to get xvclk\n");
+		return -EINVAL;
+	}
+
+	ov9281->reset_gpio = devm_gpiod_get_optional(dev, "reset",
+						     GPIOD_OUT_LOW);
+	if (IS_ERR(ov9281->reset_gpio))
+		dev_warn(dev, "Failed to get reset-gpios\n");
+
+	ov9281->pwdn_gpio = devm_gpiod_get_optional(dev, "pwdn", GPIOD_OUT_LOW);
+	if (IS_ERR(ov9281->pwdn_gpio))
+		dev_warn(dev, "Failed to get pwdn-gpios\n");
+
+	ret = ov9281_configure_regulators(ov9281);
+	if (ret) {
+		dev_err(dev, "Failed to get power regulators\n");
+		return ret;
+	}
+
+	mutex_init(&ov9281->mutex);
+
+	sd = &ov9281->subdev;
+	v4l2_i2c_subdev_init(sd, client, &ov9281_subdev_ops);
+	ret = ov9281_initialize_controls(ov9281);
+	if (ret)
+		goto err_destroy_mutex;
+
+	ret = __ov9281_power_on(ov9281);
+	if (ret)
+		goto err_free_handler;
+
+	ret = ov9281_check_sensor_id(ov9281, client);
+	if (ret)
+		goto err_power_off;
+
+	sd->internal_ops = &ov9281_internal_ops;
+	sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+
+	ov9281->pad.flags = MEDIA_PAD_FL_SOURCE;
+	sd->entity.function = MEDIA_ENT_F_CAM_SENSOR;
+	ret = media_entity_pads_init(&sd->entity, 1, &ov9281->pad);
+	if (ret < 0)
+		goto err_power_off;
+
+	ret = v4l2_async_register_subdev_sensor_common(sd);
+	if (ret) {
+		dev_err(dev, "v4l2 async register subdev failed\n");
+		goto err_clean_entity;
+	}
+
+	pm_runtime_set_active(dev);
+	pm_runtime_enable(dev);
+	pm_runtime_idle(dev);
+
+	return 0;
+
+err_clean_entity:
+	media_entity_cleanup(&sd->entity);
+err_power_off:
+	__ov9281_power_off(ov9281);
+err_free_handler:
+	v4l2_ctrl_handler_free(&ov9281->ctrl_handler);
+err_destroy_mutex:
+	mutex_destroy(&ov9281->mutex);
+
+	return ret;
+}
+
+static int ov9281_remove(struct i2c_client *client)
+{
+	struct v4l2_subdev *sd = i2c_get_clientdata(client);
+	struct ov9281 *ov9281 = to_ov9281(sd);
+
+	v4l2_async_unregister_subdev(sd);
+	media_entity_cleanup(&sd->entity);
+	v4l2_ctrl_handler_free(&ov9281->ctrl_handler);
+	mutex_destroy(&ov9281->mutex);
+
+	pm_runtime_disable(&client->dev);
+	if (!pm_runtime_status_suspended(&client->dev))
+		__ov9281_power_off(ov9281);
+	pm_runtime_set_suspended(&client->dev);
+
+	return 0;
+}
+
+static const struct of_device_id ov9281_of_match[] = {
+	{ .compatible = "ovti,ov9281" },
+	{},
+};
+MODULE_DEVICE_TABLE(of, ov9281_of_match);
+
+static const struct i2c_device_id ov9281_match_id[] = {
+	{ "ovti,ov9281", 0 },
+	{ },
+};
+
+static struct i2c_driver ov9281_i2c_driver = {
+	.driver = {
+		.name = OV9281_NAME,
+		.pm = &ov9281_pm_ops,
+		.of_match_table = of_match_ptr(ov9281_of_match),
+	},
+	.probe		= &ov9281_probe,
+	.remove		= &ov9281_remove,
+	.id_table	= ov9281_match_id,
+};
+
+static int __init sensor_mod_init(void)
+{
+	return i2c_add_driver(&ov9281_i2c_driver);
+}
+
+static void __exit sensor_mod_exit(void)
+{
+	i2c_del_driver(&ov9281_i2c_driver);
+}
+
+device_initcall_sync(sensor_mod_init);
+module_exit(sensor_mod_exit);
+
+MODULE_DESCRIPTION("OmniVision ov9281 sensor driver");
+MODULE_LICENSE("GPL v2");
diff -Nur --no-dereference linux-5.10.52-orig/drivers/media/i2c/tc358743.c linux-5.10.52-v7l+/drivers/media/i2c/tc358743.c
--- linux-5.10.52-orig/drivers/media/i2c/tc358743.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/media/i2c/tc358743.c	2021-07-25 16:46:05.818293362 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:113 @
 
 /* --------------- I2C --------------- */
 
-static void i2c_rd(struct v4l2_subdev *sd, u16 reg, u8 *values, u32 n)
+static int i2c_rd(struct v4l2_subdev *sd, u16 reg, u8 *values, u32 n)
 {
 	struct tc358743_state *state = to_state(sd);
 	struct i2c_client *client = state->i2c_client;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:139 @
 		v4l2_err(sd, "%s: reading register 0x%x from 0x%x failed\n",
 				__func__, reg, client->addr);
 	}
+	return err != ARRAY_SIZE(msgs);
 }
 
 static void i2c_wr(struct v4l2_subdev *sd, u16 reg, u8 *values, u32 n)
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:196 @
 	}
 }
 
-static noinline u32 i2c_rdreg(struct v4l2_subdev *sd, u16 reg, u32 n)
+static noinline u32 i2c_rdreg_err(struct v4l2_subdev *sd, u16 reg, u32 n,
+				  int *err)
 {
+	int error;
 	__le32 val = 0;
 
-	i2c_rd(sd, reg, (u8 __force *)&val, n);
+	error = i2c_rd(sd, reg, (u8 __force *)&val, n);
+	if (err)
+		*err = error;
 
 	return le32_to_cpu(val);
 }
 
+static inline u32 i2c_rdreg(struct v4l2_subdev *sd, u16 reg, u32 n)
+{
+	return i2c_rdreg_err(sd, reg, n, NULL);
+}
+
 static noinline void i2c_wrreg(struct v4l2_subdev *sd, u16 reg, u32 val, u32 n)
 {
 	__le32 raw = cpu_to_le32(val);
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:242 @
 	return i2c_rdreg(sd, reg, 2);
 }
 
+static int i2c_rd16_err(struct v4l2_subdev *sd, u16 reg, u16 *value)
+{
+	int err;
+	*value = i2c_rdreg_err(sd, reg, 2, &err);
+	return err;
+}
+
 static void i2c_wr16(struct v4l2_subdev *sd, u16 reg, u16 val)
 {
 	i2c_wrreg(sd, reg, val, 2);
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1629 @
 				    struct v4l2_mbus_config *cfg)
 {
 	struct tc358743_state *state = to_state(sd);
+	const u32 mask = V4L2_MBUS_CSI2_LANE_MASK;
+
+	if (state->csi_lanes_in_use > state->bus.num_data_lanes)
+		return -EINVAL;
 
 	cfg->type = V4L2_MBUS_CSI2_DPHY;
+	cfg->flags = (state->csi_lanes_in_use << __ffs(mask)) & mask;
 
-	/* Support for non-continuous CSI-2 clock is missing in the driver */
-	cfg->flags = V4L2_MBUS_CSI2_CONTINUOUS_CLOCK;
+	/* In DT mode, only report the number of active lanes */
+	if (sd->dev->of_node)
+		return 0;
+
+	/* Support for non-continuous CSI-2 clock is missing in pdate mode */
+	cfg->flags |= V4L2_MBUS_CSI2_CONTINUOUS_CLOCK;
 
 	switch (state->csi_lanes_in_use) {
 	case 1:
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1694 @
 	return 0;
 }
 
+static u32 tc358743_g_colorspace(u32 code)
+{
+	switch (code) {
+	case MEDIA_BUS_FMT_RGB888_1X24:
+		return V4L2_COLORSPACE_SRGB;
+	case MEDIA_BUS_FMT_UYVY8_1X16:
+		return V4L2_COLORSPACE_SMPTE170M;
+	default:
+		return 0;
+	}
+}
+
 static int tc358743_get_fmt(struct v4l2_subdev *sd,
 		struct v4l2_subdev_pad_config *cfg,
 		struct v4l2_subdev_format *format)
 {
 	struct tc358743_state *state = to_state(sd);
-	u8 vi_rep = i2c_rd8(sd, VI_REP);
 
 	if (format->pad != 0)
 		return -EINVAL;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1720 @
 	format->format.height = state->timings.bt.height;
 	format->format.field = V4L2_FIELD_NONE;
 
-	switch (vi_rep & MASK_VOUT_COLOR_SEL) {
-	case MASK_VOUT_COLOR_RGB_FULL:
-	case MASK_VOUT_COLOR_RGB_LIMITED:
-		format->format.colorspace = V4L2_COLORSPACE_SRGB;
-		break;
-	case MASK_VOUT_COLOR_601_YCBCR_LIMITED:
-	case MASK_VOUT_COLOR_601_YCBCR_FULL:
-		format->format.colorspace = V4L2_COLORSPACE_SMPTE170M;
-		break;
-	case MASK_VOUT_COLOR_709_YCBCR_FULL:
-	case MASK_VOUT_COLOR_709_YCBCR_LIMITED:
-		format->format.colorspace = V4L2_COLORSPACE_REC709;
-		break;
-	default:
-		format->format.colorspace = 0;
-		break;
-	}
+	format->format.colorspace = tc358743_g_colorspace(format->format.code);
 
 	return 0;
 }
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1734 @
 	u32 code = format->format.code; /* is overwritten by get_fmt */
 	int ret = tc358743_get_fmt(sd, cfg, format);
 
-	format->format.code = code;
+	if (code == MEDIA_BUS_FMT_RGB888_1X24 ||
+	    code == MEDIA_BUS_FMT_UYVY8_1X16)
+		format->format.code = code;
+	format->format.colorspace = tc358743_g_colorspace(format->format.code);
 
 	if (ret)
 		return ret;
 
-	switch (code) {
-	case MEDIA_BUS_FMT_RGB888_1X24:
-	case MEDIA_BUS_FMT_UYVY8_1X16:
-		break;
-	default:
-		return -EINVAL;
-	}
-
 	if (format->which == V4L2_SUBDEV_FORMAT_TRY)
 		return 0;
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1969 @
 	state->pdata.ddc5v_delay = DDC5V_DELAY_100_MS;
 	state->pdata.enable_hdcp = false;
 	/* A FIFO level of 16 should be enough for 2-lane 720p60 at 594 MHz. */
-	state->pdata.fifo_level = 16;
+	state->pdata.fifo_level = 374;
 	/*
 	 * The PLL input clock is obtained by dividing refclk by pll_prd.
 	 * It must be between 6 MHz and 40 MHz, lower frequency is better.
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1989 @
 	/*
 	 * The CSI bps per lane must be between 62.5 Mbps and 1 Gbps.
 	 * The default is 594 Mbps for 4-lane 1080p60 or 2-lane 720p60.
+	 * 972 Mbps allows 1080P50 UYVY over 2-lane.
 	 */
 	bps_pr_lane = 2 * endpoint.link_frequencies[0];
 	if (bps_pr_lane < 62500000U || bps_pr_lane > 1000000000U) {
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:2003 @
 			       state->pdata.refclk_hz * state->pdata.pll_prd;
 
 	/*
-	 * FIXME: These timings are from REF_02 for 594 Mbps per lane (297 MHz
-	 * link frequency). In principle it should be possible to calculate
+	 * FIXME: These timings are from REF_02 for 594 or 972 Mbps per lane
+	 * (297 MHz or 486 MHz link frequency).
+	 * In principle it should be possible to calculate
 	 * them based on link frequency and resolution.
 	 */
-	if (bps_pr_lane != 594000000U)
+	switch (bps_pr_lane) {
+	default:
 		dev_warn(dev, "untested bps per lane: %u bps\n", bps_pr_lane);
-	state->pdata.lineinitcnt = 0xe80;
-	state->pdata.lptxtimecnt = 0x003;
-	/* tclk-preparecnt: 3, tclk-zerocnt: 20 */
-	state->pdata.tclk_headercnt = 0x1403;
-	state->pdata.tclk_trailcnt = 0x00;
-	/* ths-preparecnt: 3, ths-zerocnt: 1 */
-	state->pdata.ths_headercnt = 0x0103;
-	state->pdata.twakeup = 0x4882;
-	state->pdata.tclk_postcnt = 0x008;
-	state->pdata.ths_trailcnt = 0x2;
-	state->pdata.hstxvregcnt = 0;
+		/* fall through */
+	case 594000000U:
+		state->pdata.lineinitcnt = 0xe80;
+		state->pdata.lptxtimecnt = 0x003;
+		/* tclk-preparecnt: 3, tclk-zerocnt: 20 */
+		state->pdata.tclk_headercnt = 0x1403;
+		state->pdata.tclk_trailcnt = 0x00;
+		/* ths-preparecnt: 3, ths-zerocnt: 1 */
+		state->pdata.ths_headercnt = 0x0103;
+		state->pdata.twakeup = 0x4882;
+		state->pdata.tclk_postcnt = 0x008;
+		state->pdata.ths_trailcnt = 0x2;
+		state->pdata.hstxvregcnt = 0;
+		break;
+	case 972000000U:
+		state->pdata.lineinitcnt = 0x1b58;
+		state->pdata.lptxtimecnt = 0x007;
+		/* tclk-preparecnt: 6, tclk-zerocnt: 40 */
+		state->pdata.tclk_headercnt = 0x2806;
+		state->pdata.tclk_trailcnt = 0x00;
+		/* ths-preparecnt: 6, ths-zerocnt: 8 */
+		state->pdata.ths_headercnt = 0x0806;
+		state->pdata.twakeup = 0x4268;
+		state->pdata.tclk_postcnt = 0x008;
+		state->pdata.ths_trailcnt = 0x5;
+		state->pdata.hstxvregcnt = 0;
+		break;
+	}
 
 	state->reset_gpio = devm_gpiod_get_optional(dev, "reset",
 						    GPIOD_OUT_LOW);
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:2077 @
 	struct tc358743_platform_data *pdata = client->dev.platform_data;
 	struct v4l2_subdev *sd;
 	u16 irq_mask = MASK_HDMI_MSK | MASK_CSI_MSK;
+	u16 chipid;
 	int err;
 
 	if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:2096 @
 	if (pdata) {
 		state->pdata = *pdata;
 		state->bus.flags = V4L2_MBUS_CSI2_CONTINUOUS_CLOCK;
+		state->bus.num_data_lanes = 4;
 	} else {
 		err = tc358743_probe_of(state);
 		if (err == -ENODEV)
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:2110 @
 	sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS;
 
 	/* i2c access */
-	if ((i2c_rd16(sd, CHIPID) & MASK_CHIPID) != 0) {
+	if (i2c_rd16_err(sd, CHIPID, &chipid) ||
+	    (chipid & MASK_CHIPID) != 0) {
 		v4l2_info(sd, "not a TC358743 on address 0x%x\n",
 			  client->addr << 1);
 		return -ENODEV;
diff -Nur --no-dereference linux-5.10.52-orig/drivers/media/mc/mc-request.c linux-5.10.52-v7l+/drivers/media/mc/mc-request.c
--- linux-5.10.52-orig/drivers/media/mc/mc-request.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/media/mc/mc-request.c	2021-07-25 16:46:05.888292189 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:507 @
 		media_request_put(req);
 }
 EXPORT_SYMBOL_GPL(media_request_object_complete);
+
+void media_request_pin(struct media_request *req)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&req->lock, flags);
+	if (WARN_ON(req->state != MEDIA_REQUEST_STATE_QUEUED))
+		goto unlock;
+	req->num_incomplete_objects++;
+unlock:
+	spin_unlock_irqrestore(&req->lock, flags);
+}
+EXPORT_SYMBOL_GPL(media_request_pin);
+
+void media_request_unpin(struct media_request *req)
+{
+	unsigned long flags;
+	bool completed = false;
+
+	spin_lock_irqsave(&req->lock, flags);
+	if (WARN_ON(!req->num_incomplete_objects) ||
+	    WARN_ON(req->state != MEDIA_REQUEST_STATE_QUEUED))
+		goto unlock;
+
+	if (!--req->num_incomplete_objects) {
+		req->state = MEDIA_REQUEST_STATE_COMPLETE;
+		wake_up_interruptible_all(&req->poll_wait);
+		completed = true;
+	}
+unlock:
+	spin_unlock_irqrestore(&req->lock, flags);
+	if (completed)
+		media_request_put(req);
+}
+EXPORT_SYMBOL_GPL(media_request_unpin);
diff -Nur --no-dereference linux-5.10.52-orig/drivers/media/platform/bcm2835/bcm2835-unicam.c linux-5.10.52-v7l+/drivers/media/platform/bcm2835/bcm2835-unicam.c
--- linux-5.10.52-orig/drivers/media/platform/bcm2835/bcm2835-unicam.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/media/platform/bcm2835/bcm2835-unicam.c	2021-07-25 16:46:06.388283806 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * BCM283x / BCM271x Unicam Capture Driver
+ *
+ * Copyright (C) 2017-2020 - Raspberry Pi (Trading) Ltd.
+ *
+ * Dave Stevenson <dave.stevenson@raspberrypi.com>
+ *
+ * Based on TI am437x driver by
+ *   Benoit Parrot <bparrot@ti.com>
+ *   Lad, Prabhakar <prabhakar.csengg@gmail.com>
+ *
+ * and TI CAL camera interface driver by
+ *    Benoit Parrot <bparrot@ti.com>
+ *
+ *
+ * There are two camera drivers in the kernel for BCM283x - this one
+ * and bcm2835-camera (currently in staging).
+ *
+ * This driver directly controls the Unicam peripheral - there is no
+ * involvement with the VideoCore firmware. Unicam receives CSI-2 or
+ * CCP2 data and writes it into SDRAM.
+ * The only potential processing options are to repack Bayer data into an
+ * alternate format, and applying windowing.
+ * The repacking does not shift the data, so can repack V4L2_PIX_FMT_Sxxxx10P
+ * to V4L2_PIX_FMT_Sxxxx10, or V4L2_PIX_FMT_Sxxxx12P to V4L2_PIX_FMT_Sxxxx12,
+ * but not generically up to V4L2_PIX_FMT_Sxxxx16. The driver will add both
+ * formats where the relevant formats are defined, and will automatically
+ * configure the repacking as required.
+ * Support for windowing may be added later.
+ *
+ * It should be possible to connect this driver to any sensor with a
+ * suitable output interface and V4L2 subdevice driver.
+ *
+ * bcm2835-camera uses the VideoCore firmware to control the sensor,
+ * Unicam, ISP, and all tuner control loops. Fully processed frames are
+ * delivered to the driver by the firmware. It only has sensor drivers
+ * for Omnivision OV5647, and Sony IMX219 sensors.
+ *
+ * The two drivers are mutually exclusive for the same Unicam instance.
+ * The VideoCore firmware checks the device tree configuration during boot.
+ * If it finds device tree nodes called csi0 or csi1 it will block the
+ * firmware from accessing the peripheral, and bcm2835-camera will
+ * not be able to stream data.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/of_graph.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/videodev2.h>
+
+#include <media/v4l2-common.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-dv-timings.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-fwnode.h>
+#include <media/videobuf2-dma-contig.h>
+
+#include "vc4-regs-unicam.h"
+
+#define UNICAM_MODULE_NAME	"unicam"
+#define UNICAM_VERSION		"0.1.0"
+
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Debug level 0-3");
+
+#define unicam_dbg(level, dev, fmt, arg...)	\
+		v4l2_dbg(level, debug, &(dev)->v4l2_dev, fmt, ##arg)
+#define unicam_info(dev, fmt, arg...)	\
+		v4l2_info(&(dev)->v4l2_dev, fmt, ##arg)
+#define unicam_err(dev, fmt, arg...)	\
+		v4l2_err(&(dev)->v4l2_dev, fmt, ##arg)
+
+/*
+ * Unicam must request a minimum of 250Mhz from the VPU clock.
+ * Otherwise the input FIFOs overrun and cause image corruption.
+ */
+#define MIN_VPU_CLOCK_RATE (250 * 1000 * 1000)
+/*
+ * To protect against a dodgy sensor driver never returning an error from
+ * enum_mbus_code, set a maximum index value to be used.
+ */
+#define MAX_ENUM_MBUS_CODE	128
+
+/*
+ * Stride is a 16 bit register, but also has to be a multiple of 32.
+ */
+#define BPL_ALIGNMENT		32
+#define MAX_BYTESPERLINE	((1 << 16) - BPL_ALIGNMENT)
+/*
+ * Max width is therefore determined by the max stride divided by
+ * the number of bits per pixel. Take 32bpp as a
+ * worst case.
+ * No imposed limit on the height, so adopt a square image for want
+ * of anything better.
+ */
+#define MAX_WIDTH		(MAX_BYTESPERLINE / 4)
+#define MAX_HEIGHT		MAX_WIDTH
+/* Define a nominal minimum image size */
+#define MIN_WIDTH		16
+#define MIN_HEIGHT		16
+/* Default size of the embedded buffer */
+#define UNICAM_EMBEDDED_SIZE	8192
+
+/*
+ * Size of the dummy buffer. Can be any size really, but the DMA
+ * allocation works in units of page sizes.
+ */
+#define DUMMY_BUF_SIZE		(PAGE_SIZE)
+
+enum pad_types {
+	IMAGE_PAD,
+	METADATA_PAD,
+	MAX_NODES
+};
+
+/*
+ * struct unicam_fmt - Unicam media bus format information
+ * @pixelformat: V4L2 pixel format FCC identifier. 0 if n/a.
+ * @repacked_fourcc: V4L2 pixel format FCC identifier if the data is expanded
+ * out to 16bpp. 0 if n/a.
+ * @code: V4L2 media bus format code.
+ * @depth: Bits per pixel as delivered from the source.
+ * @csi_dt: CSI data type.
+ * @check_variants: Flag to denote that there are multiple mediabus formats
+ *		still in the list that could match this V4L2 format.
+ */
+struct unicam_fmt {
+	u32	fourcc;
+	u32	repacked_fourcc;
+	u32	code;
+	u8	depth;
+	u8	csi_dt;
+	u8	check_variants;
+};
+
+static const struct unicam_fmt formats[] = {
+	/* YUV Formats */
+	{
+		.fourcc		= V4L2_PIX_FMT_YUYV,
+		.code		= MEDIA_BUS_FMT_YUYV8_2X8,
+		.depth		= 16,
+		.csi_dt		= 0x1e,
+		.check_variants = 1,
+	}, {
+		.fourcc		= V4L2_PIX_FMT_UYVY,
+		.code		= MEDIA_BUS_FMT_UYVY8_2X8,
+		.depth		= 16,
+		.csi_dt		= 0x1e,
+		.check_variants = 1,
+	}, {
+		.fourcc		= V4L2_PIX_FMT_YVYU,
+		.code		= MEDIA_BUS_FMT_YVYU8_2X8,
+		.depth		= 16,
+		.csi_dt		= 0x1e,
+		.check_variants = 1,
+	}, {
+		.fourcc		= V4L2_PIX_FMT_VYUY,
+		.code		= MEDIA_BUS_FMT_VYUY8_2X8,
+		.depth		= 16,
+		.csi_dt		= 0x1e,
+		.check_variants = 1,
+	}, {
+		.fourcc		= V4L2_PIX_FMT_YUYV,
+		.code		= MEDIA_BUS_FMT_YUYV8_1X16,
+		.depth		= 16,
+		.csi_dt		= 0x1e,
+	}, {
+		.fourcc		= V4L2_PIX_FMT_UYVY,
+		.code		= MEDIA_BUS_FMT_UYVY8_1X16,
+		.depth		= 16,
+		.csi_dt		= 0x1e,
+	}, {
+		.fourcc		= V4L2_PIX_FMT_YVYU,
+		.code		= MEDIA_BUS_FMT_YVYU8_1X16,
+		.depth		= 16,
+		.csi_dt		= 0x1e,
+	}, {
+		.fourcc		= V4L2_PIX_FMT_VYUY,
+		.code		= MEDIA_BUS_FMT_VYUY8_1X16,
+		.depth		= 16,
+		.csi_dt		= 0x1e,
+	}, {
+	/* RGB Formats */
+		.fourcc		= V4L2_PIX_FMT_RGB565, /* gggbbbbb rrrrrggg */
+		.code		= MEDIA_BUS_FMT_RGB565_2X8_LE,
+		.depth		= 16,
+		.csi_dt		= 0x22,
+	}, {
+		.fourcc		= V4L2_PIX_FMT_RGB565X, /* rrrrrggg gggbbbbb */
+		.code		= MEDIA_BUS_FMT_RGB565_2X8_BE,
+		.depth		= 16,
+		.csi_dt		= 0x22
+	}, {
+		.fourcc		= V4L2_PIX_FMT_RGB555, /* gggbbbbb arrrrrgg */
+		.code		= MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE,
+		.depth		= 16,
+		.csi_dt		= 0x21,
+	}, {
+		.fourcc		= V4L2_PIX_FMT_RGB555X, /* arrrrrgg gggbbbbb */
+		.code		= MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE,
+		.depth		= 16,
+		.csi_dt		= 0x21,
+	}, {
+		.fourcc		= V4L2_PIX_FMT_RGB24, /* rgb */
+		.code		= MEDIA_BUS_FMT_RGB888_1X24,
+		.depth		= 24,
+		.csi_dt		= 0x24,
+	}, {
+		.fourcc		= V4L2_PIX_FMT_BGR24, /* bgr */
+		.code		= MEDIA_BUS_FMT_BGR888_1X24,
+		.depth		= 24,
+		.csi_dt		= 0x24,
+	}, {
+		.fourcc		= V4L2_PIX_FMT_RGB32, /* argb */
+		.code		= MEDIA_BUS_FMT_ARGB8888_1X32,
+		.depth		= 32,
+		.csi_dt		= 0x0,
+	}, {
+	/* Bayer Formats */
+		.fourcc		= V4L2_PIX_FMT_SBGGR8,
+		.code		= MEDIA_BUS_FMT_SBGGR8_1X8,
+		.depth		= 8,
+		.csi_dt		= 0x2a,
+	}, {
+		.fourcc		= V4L2_PIX_FMT_SGBRG8,
+		.code		= MEDIA_BUS_FMT_SGBRG8_1X8,
+		.depth		= 8,
+		.csi_dt		= 0x2a,
+	}, {
+		.fourcc		= V4L2_PIX_FMT_SGRBG8,
+		.code		= MEDIA_BUS_FMT_SGRBG8_1X8,
+		.depth		= 8,
+		.csi_dt		= 0x2a,
+	}, {
+		.fourcc		= V4L2_PIX_FMT_SRGGB8,
+		.code		= MEDIA_BUS_FMT_SRGGB8_1X8,
+		.depth		= 8,
+		.csi_dt		= 0x2a,
+	}, {
+		.fourcc		= V4L2_PIX_FMT_SBGGR10P,
+		.repacked_fourcc = V4L2_PIX_FMT_SBGGR10,
+		.code		= MEDIA_BUS_FMT_SBGGR10_1X10,
+		.depth		= 10,
+		.csi_dt		= 0x2b,
+	}, {
+		.fourcc		= V4L2_PIX_FMT_SGBRG10P,
+		.repacked_fourcc = V4L2_PIX_FMT_SGBRG10,
+		.code		= MEDIA_BUS_FMT_SGBRG10_1X10,
+		.depth		= 10,
+		.csi_dt		= 0x2b,
+	}, {
+		.fourcc		= V4L2_PIX_FMT_SGRBG10P,
+		.repacked_fourcc = V4L2_PIX_FMT_SGRBG10,
+		.code		= MEDIA_BUS_FMT_SGRBG10_1X10,
+		.depth		= 10,
+		.csi_dt		= 0x2b,
+	}, {
+		.fourcc		= V4L2_PIX_FMT_SRGGB10P,
+		.repacked_fourcc = V4L2_PIX_FMT_SRGGB10,
+		.code		= MEDIA_BUS_FMT_SRGGB10_1X10,
+		.depth		= 10,
+		.csi_dt		= 0x2b,
+	}, {
+		.fourcc		= V4L2_PIX_FMT_SBGGR12P,
+		.repacked_fourcc = V4L2_PIX_FMT_SBGGR12,
+		.code		= MEDIA_BUS_FMT_SBGGR12_1X12,
+		.depth		= 12,
+		.csi_dt		= 0x2c,
+	}, {
+		.fourcc		= V4L2_PIX_FMT_SGBRG12P,
+		.repacked_fourcc = V4L2_PIX_FMT_SGBRG12,
+		.code		= MEDIA_BUS_FMT_SGBRG12_1X12,
+		.depth		= 12,
+		.csi_dt		= 0x2c,
+	}, {
+		.fourcc		= V4L2_PIX_FMT_SGRBG12P,
+		.repacked_fourcc = V4L2_PIX_FMT_SGRBG12,
+		.code		= MEDIA_BUS_FMT_SGRBG12_1X12,
+		.depth		= 12,
+		.csi_dt		= 0x2c,
+	}, {
+		.fourcc		= V4L2_PIX_FMT_SRGGB12P,
+		.repacked_fourcc = V4L2_PIX_FMT_SRGGB12,
+		.code		= MEDIA_BUS_FMT_SRGGB12_1X12,
+		.depth		= 12,
+		.csi_dt		= 0x2c,
+	}, {
+		.fourcc		= V4L2_PIX_FMT_SBGGR14P,
+		.repacked_fourcc = V4L2_PIX_FMT_SBGGR14,
+		.code		= MEDIA_BUS_FMT_SBGGR14_1X14,
+		.depth		= 14,
+		.csi_dt		= 0x2d,
+	}, {
+		.fourcc		= V4L2_PIX_FMT_SGBRG14P,
+		.repacked_fourcc = V4L2_PIX_FMT_SGBRG14,
+		.code		= MEDIA_BUS_FMT_SGBRG14_1X14,
+		.depth		= 14,
+		.csi_dt		= 0x2d,
+	}, {
+		.fourcc		= V4L2_PIX_FMT_SGRBG14P,
+		.repacked_fourcc = V4L2_PIX_FMT_SGRBG14,
+		.code		= MEDIA_BUS_FMT_SGRBG14_1X14,
+		.depth		= 14,
+		.csi_dt		= 0x2d,
+	}, {
+		.fourcc		= V4L2_PIX_FMT_SRGGB14P,
+		.repacked_fourcc = V4L2_PIX_FMT_SRGGB14,
+		.code		= MEDIA_BUS_FMT_SRGGB14_1X14,
+		.depth		= 14,
+		.csi_dt		= 0x2d,
+	}, {
+	/*
+	 * 16 bit Bayer formats could be supported, but there is no CSI2
+	 * data_type defined for raw 16, and no sensors that produce it at
+	 * present.
+	 */
+
+	/* Greyscale formats */
+		.fourcc		= V4L2_PIX_FMT_GREY,
+		.code		= MEDIA_BUS_FMT_Y8_1X8,
+		.depth		= 8,
+		.csi_dt		= 0x2a,
+	}, {
+		.fourcc		= V4L2_PIX_FMT_Y10P,
+		.repacked_fourcc = V4L2_PIX_FMT_Y10,
+		.code		= MEDIA_BUS_FMT_Y10_1X10,
+		.depth		= 10,
+		.csi_dt		= 0x2b,
+	}, {
+		.fourcc		= V4L2_PIX_FMT_Y12P,
+		.repacked_fourcc = V4L2_PIX_FMT_Y12,
+		.code		= MEDIA_BUS_FMT_Y12_1X12,
+		.depth		= 12,
+		.csi_dt		= 0x2c,
+	}, {
+		.fourcc		= V4L2_PIX_FMT_Y14P,
+		.repacked_fourcc = V4L2_PIX_FMT_Y14,
+		.code		= MEDIA_BUS_FMT_Y14_1X14,
+		.depth		= 14,
+		.csi_dt		= 0x2d,
+	},
+	/* Embedded data format */
+	{
+		.fourcc		= V4L2_META_FMT_SENSOR_DATA,
+		.code		= MEDIA_BUS_FMT_SENSOR_DATA,
+		.depth		= 8,
+	}
+};
+
+struct unicam_buffer {
+	struct vb2_v4l2_buffer vb;
+	struct list_head list;
+};
+
+static inline struct unicam_buffer *to_unicam_buffer(struct vb2_buffer *vb)
+{
+	return container_of(vb, struct unicam_buffer, vb.vb2_buf);
+}
+
+struct unicam_node {
+	bool registered;
+	int open;
+	bool streaming;
+	unsigned int pad_id;
+	/* Pointer pointing to current v4l2_buffer */
+	struct unicam_buffer *cur_frm;
+	/* Pointer pointing to next v4l2_buffer */
+	struct unicam_buffer *next_frm;
+	/* video capture */
+	const struct unicam_fmt *fmt;
+	/* Used to store current pixel format */
+	struct v4l2_format v_fmt;
+	/* Used to store current mbus frame format */
+	struct v4l2_mbus_framefmt m_fmt;
+	/* Buffer queue used in video-buf */
+	struct vb2_queue buffer_queue;
+	/* Queue of filled frames */
+	struct list_head dma_queue;
+	/* IRQ lock for DMA queue */
+	spinlock_t dma_queue_lock;
+	/* lock used to access this structure */
+	struct mutex lock;
+	/* Identifies video device for this channel */
+	struct video_device video_dev;
+	/* Pointer to the parent handle */
+	struct unicam_device *dev;
+	struct media_pad pad;
+	unsigned int embedded_lines;
+	/*
+	 * Dummy buffer intended to be used by unicam
+	 * if we have no other queued buffers to swap to.
+	 */
+	void *dummy_buf_cpu_addr;
+	dma_addr_t dummy_buf_dma_addr;
+};
+
+struct unicam_device {
+	struct kref kref;
+
+	/* V4l2 specific parameters */
+	struct v4l2_async_subdev asd;
+
+	/* peripheral base address */
+	void __iomem *base;
+	/* clock gating base address */
+	void __iomem *clk_gate_base;
+	/* lp clock handle */
+	struct clk *clock;
+	/* vpu clock handle */
+	struct clk *vpu_clock;
+	/* vpu clock request */
+	struct clk_request *vpu_req;
+	/* clock status for error handling */
+	bool clocks_enabled;
+	/* V4l2 device */
+	struct v4l2_device v4l2_dev;
+	struct media_device mdev;
+
+	/* parent device */
+	struct platform_device *pdev;
+	/* subdevice async Notifier */
+	struct v4l2_async_notifier notifier;
+	unsigned int sequence;
+
+	/* ptr to  sub device */
+	struct v4l2_subdev *sensor;
+	/* Pad config for the sensor */
+	struct v4l2_subdev_pad_config *sensor_config;
+
+	enum v4l2_mbus_type bus_type;
+	/*
+	 * Stores bus.mipi_csi2.flags for CSI2 sensors, or
+	 * bus.mipi_csi1.strobe for CCP2.
+	 */
+	unsigned int bus_flags;
+	unsigned int max_data_lanes;
+	unsigned int active_data_lanes;
+	bool sensor_embedded_data;
+
+	struct unicam_node node[MAX_NODES];
+	struct v4l2_ctrl_handler ctrl_handler;
+};
+
+static inline struct unicam_device *
+to_unicam_device(struct v4l2_device *v4l2_dev)
+{
+	return container_of(v4l2_dev, struct unicam_device, v4l2_dev);
+}
+
+/* Hardware access */
+static inline void clk_write(struct unicam_device *dev, u32 val)
+{
+	writel(val | 0x5a000000, dev->clk_gate_base);
+}
+
+static inline u32 reg_read(struct unicam_device *dev, u32 offset)
+{
+	return readl(dev->base + offset);
+}
+
+static inline void reg_write(struct unicam_device *dev, u32 offset, u32 val)
+{
+	writel(val, dev->base + offset);
+}
+
+static inline int get_field(u32 value, u32 mask)
+{
+	return (value & mask) >> __ffs(mask);
+}
+
+static inline void set_field(u32 *valp, u32 field, u32 mask)
+{
+	u32 val = *valp;
+
+	val &= ~mask;
+	val |= (field << __ffs(mask)) & mask;
+	*valp = val;
+}
+
+static inline u32 reg_read_field(struct unicam_device *dev, u32 offset,
+				 u32 mask)
+{
+	return get_field(reg_read(dev, offset), mask);
+}
+
+static inline void reg_write_field(struct unicam_device *dev, u32 offset,
+				   u32 field, u32 mask)
+{
+	u32 val = reg_read(dev, offset);
+
+	set_field(&val, field, mask);
+	reg_write(dev, offset, val);
+}
+
+/* Power management functions */
+static inline int unicam_runtime_get(struct unicam_device *dev)
+{
+	return pm_runtime_get_sync(&dev->pdev->dev);
+}
+
+static inline void unicam_runtime_put(struct unicam_device *dev)
+{
+	pm_runtime_put_sync(&dev->pdev->dev);
+}
+
+/* Format setup functions */
+static const struct unicam_fmt *find_format_by_code(u32 code)
+{
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(formats); i++) {
+		if (formats[i].code == code)
+			return &formats[i];
+	}
+
+	return NULL;
+}
+
+static int check_mbus_format(struct unicam_device *dev,
+			     const struct unicam_fmt *format)
+{
+	unsigned int i;
+	int ret = 0;
+
+	for (i = 0; !ret && i < MAX_ENUM_MBUS_CODE; i++) {
+		struct v4l2_subdev_mbus_code_enum mbus_code = {
+			.index = i,
+			.pad = IMAGE_PAD,
+			.which = V4L2_SUBDEV_FORMAT_ACTIVE,
+		};
+
+		ret = v4l2_subdev_call(dev->sensor, pad, enum_mbus_code,
+				       NULL, &mbus_code);
+
+		if (!ret && mbus_code.code == format->code)
+			return 1;
+	}
+
+	return 0;
+}
+
+static const struct unicam_fmt *find_format_by_pix(struct unicam_device *dev,
+						   u32 pixelformat)
+{
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(formats); i++) {
+		if (formats[i].fourcc == pixelformat ||
+		    formats[i].repacked_fourcc == pixelformat) {
+			if (formats[i].check_variants &&
+			    !check_mbus_format(dev, &formats[i]))
+				continue;
+			return &formats[i];
+		}
+	}
+
+	return NULL;
+}
+
+static unsigned int bytes_per_line(u32 width, const struct unicam_fmt *fmt,
+				   u32 v4l2_fourcc)
+{
+	if (v4l2_fourcc == fmt->repacked_fourcc)
+		/* Repacking always goes to 16bpp */
+		return ALIGN(width << 1, BPL_ALIGNMENT);
+	else
+		return ALIGN((width * fmt->depth) >> 3, BPL_ALIGNMENT);
+}
+
+static int __subdev_get_format(struct unicam_device *dev,
+			       struct v4l2_mbus_framefmt *fmt, int pad_id)
+{
+	struct v4l2_subdev_format sd_fmt = {
+		.which = V4L2_SUBDEV_FORMAT_ACTIVE,
+		.pad = pad_id
+	};
+	int ret;
+
+	ret = v4l2_subdev_call(dev->sensor, pad, get_fmt, dev->sensor_config,
+			       &sd_fmt);
+	if (ret < 0)
+		return ret;
+
+	*fmt = sd_fmt.format;
+
+	unicam_dbg(1, dev, "%s %dx%d code:%04x\n", __func__,
+		   fmt->width, fmt->height, fmt->code);
+
+	return 0;
+}
+
+static int __subdev_set_format(struct unicam_device *dev,
+			       struct v4l2_mbus_framefmt *fmt, int pad_id)
+{
+	struct v4l2_subdev_format sd_fmt = {
+		.which = V4L2_SUBDEV_FORMAT_ACTIVE,
+		.pad = pad_id
+	};
+	int ret;
+
+	sd_fmt.format = *fmt;
+
+	ret = v4l2_subdev_call(dev->sensor, pad, set_fmt, dev->sensor_config,
+			       &sd_fmt);
+	if (ret < 0)
+		return ret;
+
+	*fmt = sd_fmt.format;
+
+	if (pad_id == IMAGE_PAD)
+		unicam_dbg(1, dev, "%s %dx%d code:%04x\n", __func__, fmt->width,
+			   fmt->height, fmt->code);
+	else
+		unicam_dbg(1, dev, "%s Embedded data code:%04x\n", __func__,
+			   sd_fmt.format.code);
+
+	return 0;
+}
+
+static int unicam_calc_format_size_bpl(struct unicam_device *dev,
+				       const struct unicam_fmt *fmt,
+				       struct v4l2_format *f)
+{
+	unsigned int min_bytesperline;
+
+	v4l_bound_align_image(&f->fmt.pix.width, MIN_WIDTH, MAX_WIDTH, 2,
+			      &f->fmt.pix.height, MIN_HEIGHT, MAX_HEIGHT, 0,
+			      0);
+
+	min_bytesperline = bytes_per_line(f->fmt.pix.width, fmt,
+					  f->fmt.pix.pixelformat);
+
+	if (f->fmt.pix.bytesperline > min_bytesperline &&
+	    f->fmt.pix.bytesperline <= MAX_BYTESPERLINE)
+		f->fmt.pix.bytesperline = ALIGN(f->fmt.pix.bytesperline,
+						BPL_ALIGNMENT);
+	else
+		f->fmt.pix.bytesperline = min_bytesperline;
+
+	f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline;
+
+	unicam_dbg(3, dev, "%s: fourcc: %08X size: %dx%d bpl:%d img_size:%d\n",
+		   __func__,
+		   f->fmt.pix.pixelformat,
+		   f->fmt.pix.width, f->fmt.pix.height,
+		   f->fmt.pix.bytesperline, f->fmt.pix.sizeimage);
+
+	return 0;
+}
+
+static int unicam_reset_format(struct unicam_node *node)
+{
+	struct unicam_device *dev = node->dev;
+	struct v4l2_mbus_framefmt mbus_fmt;
+	int ret;
+
+	if (dev->sensor_embedded_data || node->pad_id != METADATA_PAD) {
+		ret = __subdev_get_format(dev, &mbus_fmt, node->pad_id);
+		if (ret) {
+			unicam_err(dev, "Failed to get_format - ret %d\n", ret);
+			return ret;
+		}
+
+		if (mbus_fmt.code != node->fmt->code) {
+			unicam_err(dev, "code mismatch - fmt->code %08x, mbus_fmt.code %08x\n",
+				   node->fmt->code, mbus_fmt.code);
+			return ret;
+		}
+	}
+
+	if (node->pad_id == IMAGE_PAD) {
+		v4l2_fill_pix_format(&node->v_fmt.fmt.pix, &mbus_fmt);
+		node->v_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+		unicam_calc_format_size_bpl(dev, node->fmt, &node->v_fmt);
+	} else {
+		node->v_fmt.type = V4L2_BUF_TYPE_META_CAPTURE;
+		node->v_fmt.fmt.meta.dataformat = V4L2_META_FMT_SENSOR_DATA;
+		if (dev->sensor_embedded_data) {
+			node->v_fmt.fmt.meta.buffersize =
+					mbus_fmt.width * mbus_fmt.height;
+			node->embedded_lines = mbus_fmt.height;
+		} else {
+			node->v_fmt.fmt.meta.buffersize = UNICAM_EMBEDDED_SIZE;
+			node->embedded_lines = 1;
+		}
+	}
+
+	node->m_fmt = mbus_fmt;
+	return 0;
+}
+
+static void unicam_wr_dma_addr(struct unicam_device *dev, dma_addr_t dmaaddr,
+			       unsigned int buffer_size, int pad_id)
+{
+	dma_addr_t endaddr = dmaaddr + buffer_size;
+
+	if (pad_id == IMAGE_PAD) {
+		reg_write(dev, UNICAM_IBSA0, dmaaddr);
+		reg_write(dev, UNICAM_IBEA0, endaddr);
+	} else {
+		reg_write(dev, UNICAM_DBSA0, dmaaddr);
+		reg_write(dev, UNICAM_DBEA0, endaddr);
+	}
+}
+
+static unsigned int unicam_get_lines_done(struct unicam_device *dev)
+{
+	dma_addr_t start_addr, cur_addr;
+	unsigned int stride = dev->node[IMAGE_PAD].v_fmt.fmt.pix.bytesperline;
+	struct unicam_buffer *frm = dev->node[IMAGE_PAD].cur_frm;
+
+	if (!frm)
+		return 0;
+
+	start_addr = vb2_dma_contig_plane_dma_addr(&frm->vb.vb2_buf, 0);
+	cur_addr = reg_read(dev, UNICAM_IBWP);
+	return (unsigned int)(cur_addr - start_addr) / stride;
+}
+
+static void unicam_schedule_next_buffer(struct unicam_node *node)
+{
+	struct unicam_device *dev = node->dev;
+	struct unicam_buffer *buf;
+	unsigned int size;
+	dma_addr_t addr;
+
+	buf = list_first_entry(&node->dma_queue, struct unicam_buffer, list);
+	node->next_frm = buf;
+	list_del(&buf->list);
+
+	addr = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0);
+	size = (node->pad_id == IMAGE_PAD) ?
+			node->v_fmt.fmt.pix.sizeimage :
+			node->v_fmt.fmt.meta.buffersize;
+
+	unicam_wr_dma_addr(dev, addr, size, node->pad_id);
+}
+
+static void unicam_schedule_dummy_buffer(struct unicam_node *node)
+{
+	struct unicam_device *dev = node->dev;
+
+	unicam_dbg(3, dev, "Scheduling dummy buffer for node %d\n",
+		   node->pad_id);
+
+	unicam_wr_dma_addr(dev, node->dummy_buf_dma_addr, DUMMY_BUF_SIZE,
+			   node->pad_id);
+	node->next_frm = NULL;
+}
+
+static void unicam_process_buffer_complete(struct unicam_node *node,
+					   unsigned int sequence)
+{
+	node->cur_frm->vb.field = node->m_fmt.field;
+	node->cur_frm->vb.sequence = sequence;
+
+	vb2_buffer_done(&node->cur_frm->vb.vb2_buf, VB2_BUF_STATE_DONE);
+}
+
+static void unicam_queue_event_sof(struct unicam_device *unicam)
+{
+	struct v4l2_event event = {
+		.type = V4L2_EVENT_FRAME_SYNC,
+		.u.frame_sync.frame_sequence = unicam->sequence,
+	};
+
+	v4l2_event_queue(&unicam->node[IMAGE_PAD].video_dev, &event);
+}
+
+/*
+ * unicam_isr : ISR handler for unicam capture
+ * @irq: irq number
+ * @dev_id: dev_id ptr
+ *
+ * It changes status of the captured buffer, takes next buffer from the queue
+ * and sets its address in unicam registers
+ */
+static irqreturn_t unicam_isr(int irq, void *dev)
+{
+	struct unicam_device *unicam = dev;
+	unsigned int lines_done = unicam_get_lines_done(dev);
+	unsigned int sequence = unicam->sequence;
+	unsigned int i;
+	u32 ista, sta;
+	bool fe;
+	u64 ts;
+
+	sta = reg_read(unicam, UNICAM_STA);
+	/* Write value back to clear the interrupts */
+	reg_write(unicam, UNICAM_STA, sta);
+
+	ista = reg_read(unicam, UNICAM_ISTA);
+	/* Write value back to clear the interrupts */
+	reg_write(unicam, UNICAM_ISTA, ista);
+
+	unicam_dbg(3, unicam, "ISR: ISTA: 0x%X, STA: 0x%X, sequence %d, lines done %d",
+		   ista, sta, sequence, lines_done);
+
+	if (!(sta & (UNICAM_IS | UNICAM_PI0)))
+		return IRQ_HANDLED;
+
+	/*
+	 * Look for either the Frame End interrupt or the Packet Capture status
+	 * to signal a frame end.
+	 */
+	fe = (ista & UNICAM_FEI || sta & UNICAM_PI0);
+
+	/*
+	 * We must run the frame end handler first. If we have a valid next_frm
+	 * and we get a simultaneout FE + FS interrupt, running the FS handler
+	 * first would null out the next_frm ptr and we would have lost the
+	 * buffer forever.
+	 */
+	if (fe) {
+		/*
+		 * Ensure we have swapped buffers already as we can't
+		 * stop the peripheral. If no buffer is available, use a
+		 * dummy buffer to dump out frames until we get a new buffer
+		 * to use.
+		 */
+		for (i = 0; i < ARRAY_SIZE(unicam->node); i++) {
+			if (!unicam->node[i].streaming)
+				continue;
+
+			/*
+			 * If cur_frm == next_frm, it means we have not had
+			 * a chance to swap buffers, likely due to having
+			 * multiple interrupts occurring simultaneously (like FE
+			 * + FS + LS). In this case, we cannot signal the buffer
+			 * as complete, as the HW will reuse that buffer.
+			 */
+			if (unicam->node[i].cur_frm &&
+			    unicam->node[i].cur_frm != unicam->node[i].next_frm)
+				unicam_process_buffer_complete(&unicam->node[i],
+							       sequence);
+			unicam->node[i].cur_frm = unicam->node[i].next_frm;
+		}
+		unicam->sequence++;
+	}
+
+	if (ista & UNICAM_FSI) {
+		/*
+		 * Timestamp is to be when the first data byte was captured,
+		 * aka frame start.
+		 */
+		ts = ktime_get_ns();
+		for (i = 0; i < ARRAY_SIZE(unicam->node); i++) {
+			if (!unicam->node[i].streaming)
+				continue;
+
+			if (unicam->node[i].cur_frm)
+				unicam->node[i].cur_frm->vb.vb2_buf.timestamp =
+								ts;
+			/*
+			 * Set the next frame output to go to a dummy frame
+			 * if we have not managed to obtain another frame
+			 * from the queue.
+			 */
+			unicam_schedule_dummy_buffer(&unicam->node[i]);
+		}
+
+		unicam_queue_event_sof(unicam);
+	}
+
+	/*
+	 * Cannot swap buffer at frame end, there may be a race condition
+	 * where the HW does not actually swap it if the new frame has
+	 * already started.
+	 */
+	if (ista & (UNICAM_FSI | UNICAM_LCI) && !fe) {
+		for (i = 0; i < ARRAY_SIZE(unicam->node); i++) {
+			if (!unicam->node[i].streaming)
+				continue;
+
+			spin_lock(&unicam->node[i].dma_queue_lock);
+			if (!list_empty(&unicam->node[i].dma_queue) &&
+			    !unicam->node[i].next_frm)
+				unicam_schedule_next_buffer(&unicam->node[i]);
+			spin_unlock(&unicam->node[i].dma_queue_lock);
+		}
+	}
+
+	if (reg_read(unicam, UNICAM_ICTL) & UNICAM_FCM) {
+		/* Switch out of trigger mode if selected */
+		reg_write_field(unicam, UNICAM_ICTL, 1, UNICAM_TFC);
+		reg_write_field(unicam, UNICAM_ICTL, 0, UNICAM_FCM);
+	}
+	return IRQ_HANDLED;
+}
+
+static int unicam_querycap(struct file *file, void *priv,
+			   struct v4l2_capability *cap)
+{
+	struct unicam_node *node = video_drvdata(file);
+	struct unicam_device *dev = node->dev;
+
+	strscpy(cap->driver, UNICAM_MODULE_NAME, sizeof(cap->driver));
+	strscpy(cap->card, UNICAM_MODULE_NAME, sizeof(cap->card));
+
+	snprintf(cap->bus_info, sizeof(cap->bus_info),
+		 "platform:%s", dev_name(&dev->pdev->dev));
+
+	cap->capabilities |= V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_META_CAPTURE;
+
+	return 0;
+}
+
+static int unicam_enum_fmt_vid_cap(struct file *file, void  *priv,
+				   struct v4l2_fmtdesc *f)
+{
+	struct unicam_node *node = video_drvdata(file);
+	struct unicam_device *dev = node->dev;
+	unsigned int index = 0;
+	unsigned int i;
+	int ret = 0;
+
+	if (node->pad_id != IMAGE_PAD)
+		return -EINVAL;
+
+	for (i = 0; !ret && i < MAX_ENUM_MBUS_CODE; i++) {
+		struct v4l2_subdev_mbus_code_enum mbus_code = {
+			.index = i,
+			.pad = IMAGE_PAD,
+			.which = V4L2_SUBDEV_FORMAT_ACTIVE,
+		};
+		const struct unicam_fmt *fmt;
+
+		ret = v4l2_subdev_call(dev->sensor, pad, enum_mbus_code,
+				       NULL, &mbus_code);
+		if (ret < 0) {
+			unicam_dbg(2, dev,
+				   "subdev->enum_mbus_code idx %d returned %d - index invalid\n",
+				   i, ret);
+			return -EINVAL;
+		}
+
+		fmt = find_format_by_code(mbus_code.code);
+		if (fmt) {
+			if (fmt->fourcc) {
+				if (index == f->index) {
+					f->pixelformat = fmt->fourcc;
+					break;
+				}
+				index++;
+			}
+			if (fmt->repacked_fourcc) {
+				if (index == f->index) {
+					f->pixelformat = fmt->repacked_fourcc;
+					break;
+				}
+				index++;
+			}
+		}
+	}
+
+	return 0;
+}
+
+static int unicam_g_fmt_vid_cap(struct file *file, void *priv,
+				struct v4l2_format *f)
+{
+	struct v4l2_mbus_framefmt mbus_fmt = {0};
+	struct unicam_node *node = video_drvdata(file);
+	struct unicam_device *dev = node->dev;
+	const struct unicam_fmt *fmt = NULL;
+	int ret;
+
+	if (node->pad_id != IMAGE_PAD)
+		return -EINVAL;
+
+	/*
+	 * If a flip has occurred in the sensor, the fmt code might have
+	 * changed. So we will need to re-fetch the format from the subdevice.
+	 */
+	ret = __subdev_get_format(dev, &mbus_fmt, node->pad_id);
+	if (ret)
+		return -EINVAL;
+
+	/* Find the V4L2 format from mbus code. We must match a known format. */
+	fmt = find_format_by_code(mbus_fmt.code);
+	if (!fmt)
+		return -EINVAL;
+
+	if (node->fmt != fmt) {
+		/*
+		 * The sensor format has changed so the pixelformat needs to
+		 * be updated. Try and retain the packed/unpacked choice if
+		 * at all possible.
+		 */
+		if (node->fmt->repacked_fourcc ==
+						node->v_fmt.fmt.pix.pixelformat)
+			/* Using the repacked format */
+			node->v_fmt.fmt.pix.pixelformat = fmt->repacked_fourcc;
+		else
+			/* Using the native format */
+			node->v_fmt.fmt.pix.pixelformat = fmt->fourcc;
+
+		node->fmt = fmt;
+	}
+
+	*f = node->v_fmt;
+
+	return 0;
+}
+
+static const struct unicam_fmt *
+get_first_supported_format(struct unicam_device *dev)
+{
+	struct v4l2_subdev_mbus_code_enum mbus_code;
+	const struct unicam_fmt *fmt = NULL;
+	unsigned int i;
+	int ret = 0;
+
+	for (i = 0; ret != -EINVAL && ret != -ENOIOCTLCMD; ++i) {
+		memset(&mbus_code, 0, sizeof(mbus_code));
+		mbus_code.index = i;
+		mbus_code.pad = IMAGE_PAD;
+		mbus_code.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+
+		ret = v4l2_subdev_call(dev->sensor, pad, enum_mbus_code, NULL,
+				       &mbus_code);
+		if (ret < 0) {
+			unicam_dbg(2, dev,
+				   "subdev->enum_mbus_code idx %u returned %d - continue\n",
+				   i, ret);
+			continue;
+		}
+
+		unicam_dbg(2, dev, "subdev %s: code: 0x%08x idx: %u\n",
+			   dev->sensor->name, mbus_code.code, i);
+
+		fmt = find_format_by_code(mbus_code.code);
+		unicam_dbg(2, dev, "fmt 0x%08x returned as %p, V4L2 FOURCC 0x%08x, csi_dt 0x%02x\n",
+			   mbus_code.code, fmt, fmt ? fmt->fourcc : 0,
+			   fmt ? fmt->csi_dt : 0);
+		if (fmt)
+			return fmt;
+	}
+
+	return NULL;
+}
+
+static int unicam_try_fmt_vid_cap(struct file *file, void *priv,
+				  struct v4l2_format *f)
+{
+	struct unicam_node *node = video_drvdata(file);
+	struct unicam_device *dev = node->dev;
+	struct v4l2_subdev_format sd_fmt = {
+		.which = V4L2_SUBDEV_FORMAT_TRY,
+		.pad = IMAGE_PAD
+	};
+	struct v4l2_mbus_framefmt *mbus_fmt = &sd_fmt.format;
+	const struct unicam_fmt *fmt;
+	int ret;
+
+	if (node->pad_id != IMAGE_PAD)
+		return -EINVAL;
+
+	fmt = find_format_by_pix(dev, f->fmt.pix.pixelformat);
+	if (!fmt) {
+		/*
+		 * Pixel format not supported by unicam. Choose the first
+		 * supported format, and let the sensor choose something else.
+		 */
+		unicam_dbg(3, dev, "Fourcc format (0x%08x) not found. Use first format.\n",
+			   f->fmt.pix.pixelformat);
+
+		fmt = &formats[0];
+		f->fmt.pix.pixelformat = fmt->fourcc;
+	}
+
+	v4l2_fill_mbus_format(mbus_fmt, &f->fmt.pix, fmt->code);
+	/*
+	 * No support for receiving interlaced video, so never
+	 * request it from the sensor subdev.
+	 */
+	mbus_fmt->field = V4L2_FIELD_NONE;
+
+	ret = v4l2_subdev_call(dev->sensor, pad, set_fmt, dev->sensor_config,
+			       &sd_fmt);
+	if (ret && ret != -ENOIOCTLCMD && ret != -ENODEV)
+		return ret;
+
+	if (mbus_fmt->field != V4L2_FIELD_NONE)
+		unicam_info(dev, "Sensor trying to send interlaced video - results may be unpredictable\n");
+
+	v4l2_fill_pix_format(&f->fmt.pix, &sd_fmt.format);
+	if (mbus_fmt->code != fmt->code) {
+		/* Sensor has returned an alternate format */
+		fmt = find_format_by_code(mbus_fmt->code);
+		if (!fmt) {
+			/*
+			 * The alternate format is one unicam can't support.
+			 * Find the first format that is supported by both, and
+			 * then set that.
+			 */
+			fmt = get_first_supported_format(dev);
+			mbus_fmt->code = fmt->code;
+
+			ret = v4l2_subdev_call(dev->sensor, pad, set_fmt,
+					       dev->sensor_config, &sd_fmt);
+			if (ret && ret != -ENOIOCTLCMD && ret != -ENODEV)
+				return ret;
+
+			if (mbus_fmt->field != V4L2_FIELD_NONE)
+				unicam_info(dev, "Sensor trying to send interlaced video - results may be unpredictable\n");
+
+			v4l2_fill_pix_format(&f->fmt.pix, &sd_fmt.format);
+
+			if (mbus_fmt->code != fmt->code) {
+				/*
+				 * We've set a format that the sensor reports
+				 * as being supported, but it refuses to set it.
+				 * Not much else we can do.
+				 * Assume that the sensor driver may accept the
+				 * format when it is set (rather than tried).
+				 */
+				unicam_err(dev, "Sensor won't accept default format, and Unicam can't support sensor default\n");
+			}
+		}
+
+		if (fmt->fourcc)
+			f->fmt.pix.pixelformat = fmt->fourcc;
+		else
+			f->fmt.pix.pixelformat = fmt->repacked_fourcc;
+	}
+
+	return unicam_calc_format_size_bpl(dev, fmt, f);
+}
+
+static int unicam_s_fmt_vid_cap(struct file *file, void *priv,
+				struct v4l2_format *f)
+{
+	struct unicam_node *node = video_drvdata(file);
+	struct unicam_device *dev = node->dev;
+	struct vb2_queue *q = &node->buffer_queue;
+	struct v4l2_mbus_framefmt mbus_fmt = {0};
+	const struct unicam_fmt *fmt;
+	int ret;
+
+	if (vb2_is_busy(q))
+		return -EBUSY;
+
+	ret = unicam_try_fmt_vid_cap(file, priv, f);
+	if (ret < 0)
+		return ret;
+
+	fmt = find_format_by_pix(dev, f->fmt.pix.pixelformat);
+	if (!fmt) {
+		/*
+		 * Unknown pixel format - adopt a default.
+		 * This shouldn't happen as try_fmt should have resolved any
+		 * issues first.
+		 */
+		fmt = get_first_supported_format(dev);
+		if (!fmt)
+			/*
+			 * It shouldn't be possible to get here with no
+			 * supported formats
+			 */
+			return -EINVAL;
+		f->fmt.pix.pixelformat = fmt->fourcc;
+		return -EINVAL;
+	}
+
+	v4l2_fill_mbus_format(&mbus_fmt, &f->fmt.pix, fmt->code);
+
+	ret = __subdev_set_format(dev, &mbus_fmt, node->pad_id);
+	if (ret) {
+		unicam_dbg(3, dev, "%s __subdev_set_format failed %d\n",
+			   __func__, ret);
+		return ret;
+	}
+
+	/* Just double check nothing has gone wrong */
+	if (mbus_fmt.code != fmt->code) {
+		unicam_dbg(3, dev,
+			   "%s subdev changed format on us, this should not happen\n",
+			   __func__);
+		return -EINVAL;
+	}
+
+	node->fmt = fmt;
+	node->v_fmt.fmt.pix.pixelformat = f->fmt.pix.pixelformat;
+	node->v_fmt.fmt.pix.bytesperline = f->fmt.pix.bytesperline;
+	unicam_reset_format(node);
+
+	unicam_dbg(3, dev,
+		   "%s %dx%d, mbus_fmt 0x%08X, V4L2 pix 0x%08X.\n",
+		   __func__, node->v_fmt.fmt.pix.width,
+		   node->v_fmt.fmt.pix.height, mbus_fmt.code,
+		   node->v_fmt.fmt.pix.pixelformat);
+
+	*f = node->v_fmt;
+
+	return 0;
+}
+
+static int unicam_enum_fmt_meta_cap(struct file *file, void *priv,
+				    struct v4l2_fmtdesc *f)
+{
+	struct unicam_node *node = video_drvdata(file);
+	struct unicam_device *dev = node->dev;
+	const struct unicam_fmt *fmt;
+	u32 code;
+	int ret = 0;
+
+	if (node->pad_id != METADATA_PAD || f->index != 0)
+		return -EINVAL;
+
+	if (dev->sensor_embedded_data) {
+		struct v4l2_subdev_mbus_code_enum mbus_code = {
+			.index = f->index,
+			.which = V4L2_SUBDEV_FORMAT_ACTIVE,
+			.pad = METADATA_PAD,
+		};
+
+		ret = v4l2_subdev_call(dev->sensor, pad, enum_mbus_code, NULL,
+				       &mbus_code);
+		if (ret < 0) {
+			unicam_dbg(2, dev,
+				   "subdev->enum_mbus_code idx 0 returned %d - index invalid\n",
+				   ret);
+			return -EINVAL;
+		}
+
+		code = mbus_code.code;
+	} else {
+		code = MEDIA_BUS_FMT_SENSOR_DATA;
+	}
+
+	fmt = find_format_by_code(code);
+	if (fmt)
+		f->pixelformat = fmt->fourcc;
+
+	return 0;
+}
+
+static int unicam_g_fmt_meta_cap(struct file *file, void *priv,
+				 struct v4l2_format *f)
+{
+	struct unicam_node *node = video_drvdata(file);
+
+	if (node->pad_id != METADATA_PAD)
+		return -EINVAL;
+
+	*f = node->v_fmt;
+
+	return 0;
+}
+
+static int unicam_queue_setup(struct vb2_queue *vq,
+			      unsigned int *nbuffers,
+			      unsigned int *nplanes,
+			      unsigned int sizes[],
+			      struct device *alloc_devs[])
+{
+	struct unicam_node *node = vb2_get_drv_priv(vq);
+	struct unicam_device *dev = node->dev;
+	unsigned int size = node->pad_id == IMAGE_PAD ?
+				    node->v_fmt.fmt.pix.sizeimage :
+				    node->v_fmt.fmt.meta.buffersize;
+
+	if (vq->num_buffers + *nbuffers < 3)
+		*nbuffers = 3 - vq->num_buffers;
+
+	if (*nplanes) {
+		if (sizes[0] < size) {
+			unicam_err(dev, "sizes[0] %i < size %u\n", sizes[0],
+				   size);
+			return -EINVAL;
+		}
+		size = sizes[0];
+	}
+
+	*nplanes = 1;
+	sizes[0] = size;
+
+	return 0;
+}
+
+static int unicam_buffer_prepare(struct vb2_buffer *vb)
+{
+	struct unicam_node *node = vb2_get_drv_priv(vb->vb2_queue);
+	struct unicam_device *dev = node->dev;
+	struct unicam_buffer *buf = to_unicam_buffer(vb);
+	unsigned long size;
+
+	if (WARN_ON(!node->fmt))
+		return -EINVAL;
+
+	size = node->pad_id == IMAGE_PAD ? node->v_fmt.fmt.pix.sizeimage :
+					   node->v_fmt.fmt.meta.buffersize;
+	if (vb2_plane_size(vb, 0) < size) {
+		unicam_err(dev, "data will not fit into plane (%lu < %lu)\n",
+			   vb2_plane_size(vb, 0), size);
+		return -EINVAL;
+	}
+
+	vb2_set_plane_payload(&buf->vb.vb2_buf, 0, size);
+	return 0;
+}
+
+static void unicam_buffer_queue(struct vb2_buffer *vb)
+{
+	struct unicam_node *node = vb2_get_drv_priv(vb->vb2_queue);
+	struct unicam_buffer *buf = to_unicam_buffer(vb);
+	unsigned long flags;
+
+	spin_lock_irqsave(&node->dma_queue_lock, flags);
+	list_add_tail(&buf->list, &node->dma_queue);
+	spin_unlock_irqrestore(&node->dma_queue_lock, flags);
+}
+
+static void unicam_set_packing_config(struct unicam_device *dev)
+{
+	u32 pack, unpack;
+	u32 val;
+
+	if (dev->node[IMAGE_PAD].v_fmt.fmt.pix.pixelformat ==
+	    dev->node[IMAGE_PAD].fmt->fourcc) {
+		unpack = UNICAM_PUM_NONE;
+		pack = UNICAM_PPM_NONE;
+	} else {
+		switch (dev->node[IMAGE_PAD].fmt->depth) {
+		case 8:
+			unpack = UNICAM_PUM_UNPACK8;
+			break;
+		case 10:
+			unpack = UNICAM_PUM_UNPACK10;
+			break;
+		case 12:
+			unpack = UNICAM_PUM_UNPACK12;
+			break;
+		case 14:
+			unpack = UNICAM_PUM_UNPACK14;
+			break;
+		case 16:
+			unpack = UNICAM_PUM_UNPACK16;
+			break;
+		default:
+			unpack = UNICAM_PUM_NONE;
+			break;
+		}
+
+		/* Repacking is always to 16bpp */
+		pack = UNICAM_PPM_PACK16;
+	}
+
+	val = 0;
+	set_field(&val, unpack, UNICAM_PUM_MASK);
+	set_field(&val, pack, UNICAM_PPM_MASK);
+	reg_write(dev, UNICAM_IPIPE, val);
+}
+
+static void unicam_cfg_image_id(struct unicam_device *dev)
+{
+	if (dev->bus_type == V4L2_MBUS_CSI2_DPHY) {
+		/* CSI2 mode, hardcode VC 0 for now. */
+		reg_write(dev, UNICAM_IDI0,
+			  (0 << 6) | dev->node[IMAGE_PAD].fmt->csi_dt);
+	} else {
+		/* CCP2 mode */
+		reg_write(dev, UNICAM_IDI0,
+			  0x80 | dev->node[IMAGE_PAD].fmt->csi_dt);
+	}
+}
+
+static void unicam_enable_ed(struct unicam_device *dev)
+{
+	u32 val = reg_read(dev, UNICAM_DCS);
+
+	set_field(&val, 2, UNICAM_EDL_MASK);
+	/* Do not wrap at the end of the embedded data buffer */
+	set_field(&val, 0, UNICAM_DBOB);
+
+	reg_write(dev, UNICAM_DCS, val);
+}
+
+static void unicam_start_rx(struct unicam_device *dev, dma_addr_t *addr)
+{
+	int line_int_freq = dev->node[IMAGE_PAD].v_fmt.fmt.pix.height >> 2;
+	unsigned int size, i;
+	u32 val;
+
+	if (line_int_freq < 128)
+		line_int_freq = 128;
+
+	/* Enable lane clocks */
+	val = 1;
+	for (i = 0; i < dev->active_data_lanes; i++)
+		val = val << 2 | 1;
+	clk_write(dev, val);
+
+	/* Basic init */
+	reg_write(dev, UNICAM_CTRL, UNICAM_MEM);
+
+	/* Enable analogue control, and leave in reset. */
+	val = UNICAM_AR;
+	set_field(&val, 7, UNICAM_CTATADJ_MASK);
+	set_field(&val, 7, UNICAM_PTATADJ_MASK);
+	reg_write(dev, UNICAM_ANA, val);
+	usleep_range(1000, 2000);
+
+	/* Come out of reset */
+	reg_write_field(dev, UNICAM_ANA, 0, UNICAM_AR);
+
+	/* Peripheral reset */
+	reg_write_field(dev, UNICAM_CTRL, 1, UNICAM_CPR);
+	reg_write_field(dev, UNICAM_CTRL, 0, UNICAM_CPR);
+
+	reg_write_field(dev, UNICAM_CTRL, 0, UNICAM_CPE);
+
+	/* Enable Rx control. */
+	val = reg_read(dev, UNICAM_CTRL);
+	if (dev->bus_type == V4L2_MBUS_CSI2_DPHY) {
+		set_field(&val, UNICAM_CPM_CSI2, UNICAM_CPM_MASK);
+		set_field(&val, UNICAM_DCM_STROBE, UNICAM_DCM_MASK);
+	} else {
+		set_field(&val, UNICAM_CPM_CCP2, UNICAM_CPM_MASK);
+		set_field(&val, dev->bus_flags, UNICAM_DCM_MASK);
+	}
+	/* Packet framer timeout */
+	set_field(&val, 0xf, UNICAM_PFT_MASK);
+	set_field(&val, 128, UNICAM_OET_MASK);
+	reg_write(dev, UNICAM_CTRL, val);
+
+	reg_write(dev, UNICAM_IHWIN, 0);
+	reg_write(dev, UNICAM_IVWIN, 0);
+
+	/* AXI bus access QoS setup */
+	val = reg_read(dev, UNICAM_PRI);
+	set_field(&val, 0, UNICAM_BL_MASK);
+	set_field(&val, 0, UNICAM_BS_MASK);
+	set_field(&val, 0xe, UNICAM_PP_MASK);
+	set_field(&val, 8, UNICAM_NP_MASK);
+	set_field(&val, 2, UNICAM_PT_MASK);
+	set_field(&val, 1, UNICAM_PE);
+	reg_write(dev, UNICAM_PRI, val);
+
+	reg_write_field(dev, UNICAM_ANA, 0, UNICAM_DDL);
+
+	/* Always start in trigger frame capture mode (UNICAM_FCM set) */
+	val = UNICAM_FSIE | UNICAM_FEIE | UNICAM_FCM | UNICAM_IBOB;
+	set_field(&val, line_int_freq, UNICAM_LCIE_MASK);
+	reg_write(dev, UNICAM_ICTL, val);
+	reg_write(dev, UNICAM_STA, UNICAM_STA_MASK_ALL);
+	reg_write(dev, UNICAM_ISTA, UNICAM_ISTA_MASK_ALL);
+
+	/* tclk_term_en */
+	reg_write_field(dev, UNICAM_CLT, 2, UNICAM_CLT1_MASK);
+	/* tclk_settle */
+	reg_write_field(dev, UNICAM_CLT, 6, UNICAM_CLT2_MASK);
+	/* td_term_en */
+	reg_write_field(dev, UNICAM_DLT, 2, UNICAM_DLT1_MASK);
+	/* ths_settle */
+	reg_write_field(dev, UNICAM_DLT, 6, UNICAM_DLT2_MASK);
+	/* trx_enable */
+	reg_write_field(dev, UNICAM_DLT, 0, UNICAM_DLT3_MASK);
+
+	reg_write_field(dev, UNICAM_CTRL, 0, UNICAM_SOE);
+
+	/* Packet compare setup - required to avoid missing frame ends */
+	val = 0;
+	set_field(&val, 1, UNICAM_PCE);
+	set_field(&val, 1, UNICAM_GI);
+	set_field(&val, 1, UNICAM_CPH);
+	set_field(&val, 0, UNICAM_PCVC_MASK);
+	set_field(&val, 1, UNICAM_PCDT_MASK);
+	reg_write(dev, UNICAM_CMP0, val);
+
+	/* Enable clock lane and set up terminations */
+	val = 0;
+	if (dev->bus_type == V4L2_MBUS_CSI2_DPHY) {
+		/* CSI2 */
+		set_field(&val, 1, UNICAM_CLE);
+		set_field(&val, 1, UNICAM_CLLPE);
+		if (dev->bus_flags & V4L2_MBUS_CSI2_CONTINUOUS_CLOCK) {
+			set_field(&val, 1, UNICAM_CLTRE);
+			set_field(&val, 1, UNICAM_CLHSE);
+		}
+	} else {
+		/* CCP2 */
+		set_field(&val, 1, UNICAM_CLE);
+		set_field(&val, 1, UNICAM_CLHSE);
+		set_field(&val, 1, UNICAM_CLTRE);
+	}
+	reg_write(dev, UNICAM_CLK, val);
+
+	/*
+	 * Enable required data lanes with appropriate terminations.
+	 * The same value needs to be written to UNICAM_DATn registers for
+	 * the active lanes, and 0 for inactive ones.
+	 */
+	val = 0;
+	if (dev->bus_type == V4L2_MBUS_CSI2_DPHY) {
+		/* CSI2 */
+		set_field(&val, 1, UNICAM_DLE);
+		set_field(&val, 1, UNICAM_DLLPE);
+		if (dev->bus_flags & V4L2_MBUS_CSI2_CONTINUOUS_CLOCK) {
+			set_field(&val, 1, UNICAM_DLTRE);
+			set_field(&val, 1, UNICAM_DLHSE);
+		}
+	} else {
+		/* CCP2 */
+		set_field(&val, 1, UNICAM_DLE);
+		set_field(&val, 1, UNICAM_DLHSE);
+		set_field(&val, 1, UNICAM_DLTRE);
+	}
+	reg_write(dev, UNICAM_DAT0, val);
+
+	if (dev->active_data_lanes == 1)
+		val = 0;
+	reg_write(dev, UNICAM_DAT1, val);
+
+	if (dev->max_data_lanes > 2) {
+		/*
+		 * Registers UNICAM_DAT2 and UNICAM_DAT3 only valid if the
+		 * instance supports more than 2 data lanes.
+		 */
+		if (dev->active_data_lanes == 2)
+			val = 0;
+		reg_write(dev, UNICAM_DAT2, val);
+
+		if (dev->active_data_lanes == 3)
+			val = 0;
+		reg_write(dev, UNICAM_DAT3, val);
+	}
+
+	reg_write(dev, UNICAM_IBLS,
+		  dev->node[IMAGE_PAD].v_fmt.fmt.pix.bytesperline);
+	size = dev->node[IMAGE_PAD].v_fmt.fmt.pix.sizeimage;
+	unicam_wr_dma_addr(dev, addr[IMAGE_PAD], size, IMAGE_PAD);
+	unicam_set_packing_config(dev);
+	unicam_cfg_image_id(dev);
+
+	val = reg_read(dev, UNICAM_MISC);
+	set_field(&val, 1, UNICAM_FL0);
+	set_field(&val, 1, UNICAM_FL1);
+	reg_write(dev, UNICAM_MISC, val);
+
+	if (dev->node[METADATA_PAD].streaming && dev->sensor_embedded_data) {
+		size = dev->node[METADATA_PAD].v_fmt.fmt.meta.buffersize;
+		unicam_enable_ed(dev);
+		unicam_wr_dma_addr(dev, addr[METADATA_PAD], size, METADATA_PAD);
+	}
+
+	/* Enable peripheral */
+	reg_write_field(dev, UNICAM_CTRL, 1, UNICAM_CPE);
+
+	/* Load image pointers */
+	reg_write_field(dev, UNICAM_ICTL, 1, UNICAM_LIP_MASK);
+
+	/* Load embedded data buffer pointers if needed */
+	if (dev->node[METADATA_PAD].streaming && dev->sensor_embedded_data)
+		reg_write_field(dev, UNICAM_DCS, 1, UNICAM_LDP);
+
+	/*
+	 * Enable trigger only for the first frame to
+	 * sync correctly to the FS from the source.
+	 */
+	reg_write_field(dev, UNICAM_ICTL, 1, UNICAM_TFC);
+}
+
+static void unicam_disable(struct unicam_device *dev)
+{
+	/* Analogue lane control disable */
+	reg_write_field(dev, UNICAM_ANA, 1, UNICAM_DDL);
+
+	/* Stop the output engine */
+	reg_write_field(dev, UNICAM_CTRL, 1, UNICAM_SOE);
+
+	/* Disable the data lanes. */
+	reg_write(dev, UNICAM_DAT0, 0);
+	reg_write(dev, UNICAM_DAT1, 0);
+
+	if (dev->max_data_lanes > 2) {
+		reg_write(dev, UNICAM_DAT2, 0);
+		reg_write(dev, UNICAM_DAT3, 0);
+	}
+
+	/* Peripheral reset */
+	reg_write_field(dev, UNICAM_CTRL, 1, UNICAM_CPR);
+	usleep_range(50, 100);
+	reg_write_field(dev, UNICAM_CTRL, 0, UNICAM_CPR);
+
+	/* Disable peripheral */
+	reg_write_field(dev, UNICAM_CTRL, 0, UNICAM_CPE);
+
+	/* Clear ED setup */
+	reg_write(dev, UNICAM_DCS, 0);
+
+	/* Disable all lane clocks */
+	clk_write(dev, 0);
+}
+
+static void unicam_return_buffers(struct unicam_node *node,
+				  enum vb2_buffer_state state)
+{
+	struct unicam_buffer *buf, *tmp;
+	unsigned long flags;
+
+	spin_lock_irqsave(&node->dma_queue_lock, flags);
+	list_for_each_entry_safe(buf, tmp, &node->dma_queue, list) {
+		list_del(&buf->list);
+		vb2_buffer_done(&buf->vb.vb2_buf, state);
+	}
+
+	if (node->cur_frm)
+		vb2_buffer_done(&node->cur_frm->vb.vb2_buf,
+				state);
+	if (node->next_frm && node->cur_frm != node->next_frm)
+		vb2_buffer_done(&node->next_frm->vb.vb2_buf,
+				state);
+
+	node->cur_frm = NULL;
+	node->next_frm = NULL;
+	spin_unlock_irqrestore(&node->dma_queue_lock, flags);
+}
+
+static int unicam_start_streaming(struct vb2_queue *vq, unsigned int count)
+{
+	struct unicam_node *node = vb2_get_drv_priv(vq);
+	struct unicam_device *dev = node->dev;
+	dma_addr_t buffer_addr[MAX_NODES] = { 0 };
+	unsigned long flags;
+	unsigned int i;
+	int ret;
+
+	node->streaming = true;
+	if (!(dev->node[IMAGE_PAD].open && dev->node[IMAGE_PAD].streaming &&
+	      (!dev->node[METADATA_PAD].open ||
+	       dev->node[METADATA_PAD].streaming))) {
+		/*
+		 * Metadata pad must be enabled before image pad if it is
+		 * wanted.
+		 */
+		unicam_dbg(3, dev, "Not all nodes are streaming yet.");
+		return 0;
+	}
+
+	dev->sequence = 0;
+	ret = unicam_runtime_get(dev);
+	if (ret < 0) {
+		unicam_dbg(3, dev, "unicam_runtime_get failed\n");
+		goto err_streaming;
+	}
+
+	dev->active_data_lanes = dev->max_data_lanes;
+
+	if (dev->bus_type == V4L2_MBUS_CSI2_DPHY) {
+		struct v4l2_mbus_config mbus_config = { 0 };
+
+		ret = v4l2_subdev_call(dev->sensor, pad, get_mbus_config,
+				       0, &mbus_config);
+		if (ret < 0 && ret != -ENOIOCTLCMD) {
+			unicam_dbg(3, dev, "g_mbus_config failed\n");
+			goto err_pm_put;
+		}
+
+		dev->active_data_lanes =
+			(mbus_config.flags & V4L2_MBUS_CSI2_LANE_MASK) >>
+					__ffs(V4L2_MBUS_CSI2_LANE_MASK);
+		if (!dev->active_data_lanes)
+			dev->active_data_lanes = dev->max_data_lanes;
+		if (dev->active_data_lanes > dev->max_data_lanes) {
+			unicam_err(dev, "Device has requested %u data lanes, which is >%u configured in DT\n",
+				   dev->active_data_lanes,
+				   dev->max_data_lanes);
+			ret = -EINVAL;
+			goto err_pm_put;
+		}
+	}
+
+	unicam_dbg(1, dev, "Running with %u data lanes\n",
+		   dev->active_data_lanes);
+
+	dev->vpu_req = clk_request_start(dev->vpu_clock, MIN_VPU_CLOCK_RATE);
+	if (!dev->vpu_req) {
+		unicam_err(dev, "failed to set up VPU clock\n");
+		goto err_pm_put;
+	}
+
+	ret = clk_prepare_enable(dev->vpu_clock);
+	if (ret) {
+		unicam_err(dev, "Failed to enable VPU clock: %d\n", ret);
+		goto err_pm_put;
+	}
+
+	ret = clk_set_rate(dev->clock, 100 * 1000 * 1000);
+	if (ret) {
+		unicam_err(dev, "failed to set up CSI clock\n");
+		goto err_vpu_clock;
+	}
+
+	ret = clk_prepare_enable(dev->clock);
+	if (ret) {
+		unicam_err(dev, "Failed to enable CSI clock: %d\n", ret);
+		goto err_vpu_clock;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(dev->node); i++) {
+		struct unicam_buffer *buf;
+
+		if (!dev->node[i].streaming)
+			continue;
+
+		spin_lock_irqsave(&dev->node[i].dma_queue_lock, flags);
+		buf = list_first_entry(&dev->node[i].dma_queue,
+				       struct unicam_buffer, list);
+		dev->node[i].cur_frm = buf;
+		dev->node[i].next_frm = buf;
+		list_del(&buf->list);
+		spin_unlock_irqrestore(&dev->node[i].dma_queue_lock, flags);
+
+		buffer_addr[i] =
+			vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0);
+	}
+
+	unicam_start_rx(dev, buffer_addr);
+
+	ret = v4l2_subdev_call(dev->sensor, video, s_stream, 1);
+	if (ret < 0) {
+		unicam_err(dev, "stream on failed in subdev\n");
+		goto err_disable_unicam;
+	}
+
+	dev->clocks_enabled = true;
+	return 0;
+
+err_disable_unicam:
+	unicam_disable(dev);
+	clk_disable_unprepare(dev->clock);
+err_vpu_clock:
+	clk_request_done(dev->vpu_req);
+	clk_disable_unprepare(dev->vpu_clock);
+err_pm_put:
+	unicam_runtime_put(dev);
+err_streaming:
+	unicam_return_buffers(node, VB2_BUF_STATE_QUEUED);
+	node->streaming = false;
+
+	return ret;
+}
+
+static void unicam_stop_streaming(struct vb2_queue *vq)
+{
+	struct unicam_node *node = vb2_get_drv_priv(vq);
+	struct unicam_device *dev = node->dev;
+
+	node->streaming = false;
+
+	if (node->pad_id == IMAGE_PAD) {
+		/*
+		 * Stop streaming the sensor and disable the peripheral.
+		 * We cannot continue streaming embedded data with the
+		 * image pad disabled.
+		 */
+		if (v4l2_subdev_call(dev->sensor, video, s_stream, 0) < 0)
+			unicam_err(dev, "stream off failed in subdev\n");
+
+		unicam_disable(dev);
+
+		if (dev->clocks_enabled) {
+			clk_request_done(dev->vpu_req);
+			clk_disable_unprepare(dev->vpu_clock);
+			clk_disable_unprepare(dev->clock);
+			dev->clocks_enabled = false;
+		}
+		unicam_runtime_put(dev);
+
+	} else if (node->pad_id == METADATA_PAD) {
+		/*
+		 * Allow the hardware to spin in the dummy buffer.
+		 * This is only really needed if the embedded data pad is
+		 * disabled before the image pad.
+		 */
+		unicam_wr_dma_addr(dev, node->dummy_buf_dma_addr,
+				   DUMMY_BUF_SIZE, METADATA_PAD);
+	}
+
+	/* Clear all queued buffers for the node */
+	unicam_return_buffers(node, VB2_BUF_STATE_ERROR);
+}
+
+static int unicam_enum_input(struct file *file, void *priv,
+			     struct v4l2_input *inp)
+{
+	struct unicam_node *node = video_drvdata(file);
+	struct unicam_device *dev = node->dev;
+	int ret;
+
+	if (inp->index != 0)
+		return -EINVAL;
+
+	inp->type = V4L2_INPUT_TYPE_CAMERA;
+	if (v4l2_subdev_has_op(dev->sensor, video, s_dv_timings)) {
+		inp->capabilities = V4L2_IN_CAP_DV_TIMINGS;
+		inp->std = 0;
+	} else if (v4l2_subdev_has_op(dev->sensor, video, s_std)) {
+		inp->capabilities = V4L2_IN_CAP_STD;
+		if (v4l2_subdev_call(dev->sensor, video, g_tvnorms, &inp->std) < 0)
+			inp->std = V4L2_STD_ALL;
+	} else {
+		inp->capabilities = 0;
+		inp->std = 0;
+	}
+
+	if (v4l2_subdev_has_op(dev->sensor, video, g_input_status)) {
+		ret = v4l2_subdev_call(dev->sensor, video, g_input_status,
+				       &inp->status);
+		if (ret < 0)
+			return ret;
+	}
+
+	snprintf(inp->name, sizeof(inp->name), "Camera 0");
+	return 0;
+}
+
+static int unicam_g_input(struct file *file, void *priv, unsigned int *i)
+{
+	*i = 0;
+
+	return 0;
+}
+
+static int unicam_s_input(struct file *file, void *priv, unsigned int i)
+{
+	/*
+	 * FIXME: Ideally we would like to be able to query the source
+	 * subdevice for information over the input connectors it supports,
+	 * and map that through in to a call to video_ops->s_routing.
+	 * There is no infrastructure support for defining that within
+	 * devicetree at present. Until that is implemented we can't
+	 * map a user physical connector number to s_routing input number.
+	 */
+	if (i > 0)
+		return -EINVAL;
+
+	return 0;
+}
+
+static int unicam_querystd(struct file *file, void *priv,
+			   v4l2_std_id *std)
+{
+	struct unicam_node *node = video_drvdata(file);
+	struct unicam_device *dev = node->dev;
+
+	return v4l2_subdev_call(dev->sensor, video, querystd, std);
+}
+
+static int unicam_g_std(struct file *file, void *priv, v4l2_std_id *std)
+{
+	struct unicam_node *node = video_drvdata(file);
+	struct unicam_device *dev = node->dev;
+
+	return v4l2_subdev_call(dev->sensor, video, g_std, std);
+}
+
+static int unicam_s_std(struct file *file, void *priv, v4l2_std_id std)
+{
+	struct unicam_node *node = video_drvdata(file);
+	struct unicam_device *dev = node->dev;
+	int ret;
+	v4l2_std_id current_std;
+
+	ret = v4l2_subdev_call(dev->sensor, video, g_std, &current_std);
+	if (ret)
+		return ret;
+
+	if (std == current_std)
+		return 0;
+
+	if (vb2_is_busy(&node->buffer_queue))
+		return -EBUSY;
+
+	ret = v4l2_subdev_call(dev->sensor, video, s_std, std);
+
+	/* Force recomputation of bytesperline */
+	node->v_fmt.fmt.pix.bytesperline = 0;
+
+	unicam_reset_format(node);
+
+	return ret;
+}
+
+static int unicam_s_edid(struct file *file, void *priv, struct v4l2_edid *edid)
+{
+	struct unicam_node *node = video_drvdata(file);
+	struct unicam_device *dev = node->dev;
+
+	return v4l2_subdev_call(dev->sensor, pad, set_edid, edid);
+}
+
+static int unicam_g_edid(struct file *file, void *priv, struct v4l2_edid *edid)
+{
+	struct unicam_node *node = video_drvdata(file);
+	struct unicam_device *dev = node->dev;
+
+	return v4l2_subdev_call(dev->sensor, pad, get_edid, edid);
+}
+
+static int unicam_s_selection(struct file *file, void *priv,
+			      struct v4l2_selection *sel)
+{
+	struct unicam_node *node = video_drvdata(file);
+	struct unicam_device *dev = node->dev;
+	struct v4l2_subdev_selection sdsel = {
+		.which = V4L2_SUBDEV_FORMAT_ACTIVE,
+		.target = sel->target,
+		.flags = sel->flags,
+		.r = sel->r,
+	};
+
+	if (sel->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		return -EINVAL;
+
+	return v4l2_subdev_call(dev->sensor, pad, set_selection, NULL, &sdsel);
+}
+
+static int unicam_g_selection(struct file *file, void *priv,
+			      struct v4l2_selection *sel)
+{
+	struct unicam_node *node = video_drvdata(file);
+	struct unicam_device *dev = node->dev;
+	struct v4l2_subdev_selection sdsel = {
+		.which = V4L2_SUBDEV_FORMAT_ACTIVE,
+		.target = sel->target,
+	};
+	int ret;
+
+	if (sel->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		return -EINVAL;
+
+	ret = v4l2_subdev_call(dev->sensor, pad, get_selection, NULL, &sdsel);
+	if (!ret)
+		sel->r = sdsel.r;
+
+	return ret;
+}
+
+static int unicam_enum_framesizes(struct file *file, void *priv,
+				  struct v4l2_frmsizeenum *fsize)
+{
+	struct unicam_node *node = video_drvdata(file);
+	struct unicam_device *dev = node->dev;
+	const struct unicam_fmt *fmt;
+	struct v4l2_subdev_frame_size_enum fse;
+	int ret;
+
+	/* check for valid format */
+	fmt = find_format_by_pix(dev, fsize->pixel_format);
+	if (!fmt) {
+		unicam_dbg(3, dev, "Invalid pixel code: %x\n",
+			   fsize->pixel_format);
+		return -EINVAL;
+	}
+	fse.code = fmt->code;
+
+	fse.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+	fse.index = fsize->index;
+	fse.pad = node->pad_id;
+
+	ret = v4l2_subdev_call(dev->sensor, pad, enum_frame_size, NULL, &fse);
+	if (ret)
+		return ret;
+
+	unicam_dbg(1, dev, "%s: index: %d code: %x W:[%d,%d] H:[%d,%d]\n",
+		   __func__, fse.index, fse.code, fse.min_width, fse.max_width,
+		   fse.min_height, fse.max_height);
+
+	fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
+	fsize->discrete.width = fse.max_width;
+	fsize->discrete.height = fse.max_height;
+
+	return 0;
+}
+
+static int unicam_enum_frameintervals(struct file *file, void *priv,
+				      struct v4l2_frmivalenum *fival)
+{
+	struct unicam_node *node = video_drvdata(file);
+	struct unicam_device *dev = node->dev;
+	const struct unicam_fmt *fmt;
+	struct v4l2_subdev_frame_interval_enum fie = {
+		.index = fival->index,
+		.width = fival->width,
+		.height = fival->height,
+		.which = V4L2_SUBDEV_FORMAT_ACTIVE,
+	};
+	int ret;
+
+	fmt = find_format_by_pix(dev, fival->pixel_format);
+	if (!fmt)
+		return -EINVAL;
+
+	fie.code = fmt->code;
+	ret = v4l2_subdev_call(dev->sensor, pad, enum_frame_interval,
+			       NULL, &fie);
+	if (ret)
+		return ret;
+
+	fival->type = V4L2_FRMIVAL_TYPE_DISCRETE;
+	fival->discrete = fie.interval;
+
+	return 0;
+}
+
+static int unicam_g_parm(struct file *file, void *fh, struct v4l2_streamparm *a)
+{
+	struct unicam_node *node = video_drvdata(file);
+	struct unicam_device *dev = node->dev;
+
+	return v4l2_g_parm_cap(video_devdata(file), dev->sensor, a);
+}
+
+static int unicam_s_parm(struct file *file, void *fh, struct v4l2_streamparm *a)
+{
+	struct unicam_node *node = video_drvdata(file);
+	struct unicam_device *dev = node->dev;
+
+	return v4l2_s_parm_cap(video_devdata(file), dev->sensor, a);
+}
+
+static int unicam_g_dv_timings(struct file *file, void *priv,
+			       struct v4l2_dv_timings *timings)
+{
+	struct unicam_node *node = video_drvdata(file);
+	struct unicam_device *dev = node->dev;
+
+	return v4l2_subdev_call(dev->sensor, video, g_dv_timings, timings);
+}
+
+static int unicam_s_dv_timings(struct file *file, void *priv,
+			       struct v4l2_dv_timings *timings)
+{
+	struct unicam_node *node = video_drvdata(file);
+	struct unicam_device *dev = node->dev;
+	struct v4l2_dv_timings current_timings;
+	int ret;
+
+	ret = v4l2_subdev_call(dev->sensor, video, g_dv_timings,
+			       &current_timings);
+
+	if (ret < 0)
+		return ret;
+
+	if (v4l2_match_dv_timings(timings, &current_timings, 0, false))
+		return 0;
+
+	if (vb2_is_busy(&node->buffer_queue))
+		return -EBUSY;
+
+	ret = v4l2_subdev_call(dev->sensor, video, s_dv_timings, timings);
+
+	/* Force recomputation of bytesperline */
+	node->v_fmt.fmt.pix.bytesperline = 0;
+
+	unicam_reset_format(node);
+
+	return ret;
+}
+
+static int unicam_query_dv_timings(struct file *file, void *priv,
+				   struct v4l2_dv_timings *timings)
+{
+	struct unicam_node *node = video_drvdata(file);
+	struct unicam_device *dev = node->dev;
+
+	return v4l2_subdev_call(dev->sensor, video, query_dv_timings, timings);
+}
+
+static int unicam_enum_dv_timings(struct file *file, void *priv,
+				  struct v4l2_enum_dv_timings *timings)
+{
+	struct unicam_node *node = video_drvdata(file);
+	struct unicam_device *dev = node->dev;
+
+	return v4l2_subdev_call(dev->sensor, pad, enum_dv_timings, timings);
+}
+
+static int unicam_dv_timings_cap(struct file *file, void *priv,
+				 struct v4l2_dv_timings_cap *cap)
+{
+	struct unicam_node *node = video_drvdata(file);
+	struct unicam_device *dev = node->dev;
+
+	return v4l2_subdev_call(dev->sensor, pad, dv_timings_cap, cap);
+}
+
+static int unicam_subscribe_event(struct v4l2_fh *fh,
+				  const struct v4l2_event_subscription *sub)
+{
+	switch (sub->type) {
+	case V4L2_EVENT_FRAME_SYNC:
+		return v4l2_event_subscribe(fh, sub, 2, NULL);
+	case V4L2_EVENT_SOURCE_CHANGE:
+		return v4l2_event_subscribe(fh, sub, 4, NULL);
+	}
+
+	return v4l2_ctrl_subscribe_event(fh, sub);
+}
+
+static int unicam_log_status(struct file *file, void *fh)
+{
+	struct unicam_node *node = video_drvdata(file);
+	struct unicam_device *dev = node->dev;
+	u32 reg;
+
+	/* status for sub devices */
+	v4l2_device_call_all(&dev->v4l2_dev, 0, core, log_status);
+
+	unicam_info(dev, "-----Receiver status-----\n");
+	unicam_info(dev, "V4L2 width/height:   %ux%u\n",
+		    node->v_fmt.fmt.pix.width, node->v_fmt.fmt.pix.height);
+	unicam_info(dev, "Mediabus format:     %08x\n", node->fmt->code);
+	unicam_info(dev, "V4L2 format:         %08x\n",
+		    node->v_fmt.fmt.pix.pixelformat);
+	reg = reg_read(dev, UNICAM_IPIPE);
+	unicam_info(dev, "Unpacking/packing:   %u / %u\n",
+		    get_field(reg, UNICAM_PUM_MASK),
+		    get_field(reg, UNICAM_PPM_MASK));
+	unicam_info(dev, "----Live data----\n");
+	unicam_info(dev, "Programmed stride:   %4u\n",
+		    reg_read(dev, UNICAM_IBLS));
+	unicam_info(dev, "Detected resolution: %ux%u\n",
+		    reg_read(dev, UNICAM_IHSTA),
+		    reg_read(dev, UNICAM_IVSTA));
+	unicam_info(dev, "Write pointer:       %08x\n",
+		    reg_read(dev, UNICAM_IBWP));
+
+	return 0;
+}
+
+static void unicam_notify(struct v4l2_subdev *sd,
+			  unsigned int notification, void *arg)
+{
+	struct unicam_device *dev = to_unicam_device(sd->v4l2_dev);
+
+	switch (notification) {
+	case V4L2_DEVICE_NOTIFY_EVENT:
+		v4l2_event_queue(&dev->node[IMAGE_PAD].video_dev, arg);
+		break;
+	default:
+		break;
+	}
+}
+
+static const struct vb2_ops unicam_video_qops = {
+	.wait_prepare		= vb2_ops_wait_prepare,
+	.wait_finish		= vb2_ops_wait_finish,
+	.queue_setup		= unicam_queue_setup,
+	.buf_prepare		= unicam_buffer_prepare,
+	.buf_queue		= unicam_buffer_queue,
+	.start_streaming	= unicam_start_streaming,
+	.stop_streaming		= unicam_stop_streaming,
+};
+
+/*
+ * unicam_v4l2_open : This function is based on the v4l2_fh_open helper
+ * function. It has been augmented to handle sensor subdevice power management,
+ */
+static int unicam_v4l2_open(struct file *file)
+{
+	struct unicam_node *node = video_drvdata(file);
+	struct unicam_device *dev = node->dev;
+	int ret;
+
+	mutex_lock(&node->lock);
+
+	ret = v4l2_fh_open(file);
+	if (ret) {
+		unicam_err(dev, "v4l2_fh_open failed\n");
+		goto unlock;
+	}
+
+	node->open++;
+
+	if (!v4l2_fh_is_singular_file(file))
+		goto unlock;
+
+	ret = v4l2_subdev_call(dev->sensor, core, s_power, 1);
+	if (ret < 0 && ret != -ENOIOCTLCMD) {
+		v4l2_fh_release(file);
+		node->open--;
+		goto unlock;
+	}
+
+	ret = 0;
+
+unlock:
+	mutex_unlock(&node->lock);
+	return ret;
+}
+
+static int unicam_v4l2_release(struct file *file)
+{
+	struct unicam_node *node = video_drvdata(file);
+	struct unicam_device *dev = node->dev;
+	struct v4l2_subdev *sd = dev->sensor;
+	bool fh_singular;
+	int ret;
+
+	mutex_lock(&node->lock);
+
+	fh_singular = v4l2_fh_is_singular_file(file);
+
+	ret = _vb2_fop_release(file, NULL);
+
+	if (fh_singular)
+		v4l2_subdev_call(sd, core, s_power, 0);
+
+	node->open--;
+	mutex_unlock(&node->lock);
+
+	return ret;
+}
+
+/* unicam capture driver file operations */
+static const struct v4l2_file_operations unicam_fops = {
+	.owner		= THIS_MODULE,
+	.open		= unicam_v4l2_open,
+	.release	= unicam_v4l2_release,
+	.read		= vb2_fop_read,
+	.poll		= vb2_fop_poll,
+	.unlocked_ioctl	= video_ioctl2,
+	.mmap		= vb2_fop_mmap,
+};
+
+/* unicam capture ioctl operations */
+static const struct v4l2_ioctl_ops unicam_ioctl_ops = {
+	.vidioc_querycap		= unicam_querycap,
+	.vidioc_enum_fmt_vid_cap	= unicam_enum_fmt_vid_cap,
+	.vidioc_g_fmt_vid_cap		= unicam_g_fmt_vid_cap,
+	.vidioc_s_fmt_vid_cap		= unicam_s_fmt_vid_cap,
+	.vidioc_try_fmt_vid_cap		= unicam_try_fmt_vid_cap,
+
+	.vidioc_enum_fmt_meta_cap	= unicam_enum_fmt_meta_cap,
+	.vidioc_g_fmt_meta_cap		= unicam_g_fmt_meta_cap,
+	.vidioc_s_fmt_meta_cap		= unicam_g_fmt_meta_cap,
+	.vidioc_try_fmt_meta_cap	= unicam_g_fmt_meta_cap,
+
+	.vidioc_enum_input		= unicam_enum_input,
+	.vidioc_g_input			= unicam_g_input,
+	.vidioc_s_input			= unicam_s_input,
+
+	.vidioc_querystd		= unicam_querystd,
+	.vidioc_s_std			= unicam_s_std,
+	.vidioc_g_std			= unicam_g_std,
+
+	.vidioc_g_edid			= unicam_g_edid,
+	.vidioc_s_edid			= unicam_s_edid,
+
+	.vidioc_enum_framesizes		= unicam_enum_framesizes,
+	.vidioc_enum_frameintervals	= unicam_enum_frameintervals,
+
+	.vidioc_g_selection		= unicam_g_selection,
+	.vidioc_s_selection		= unicam_s_selection,
+
+	.vidioc_g_parm			= unicam_g_parm,
+	.vidioc_s_parm			= unicam_s_parm,
+
+	.vidioc_s_dv_timings		= unicam_s_dv_timings,
+	.vidioc_g_dv_timings		= unicam_g_dv_timings,
+	.vidioc_query_dv_timings	= unicam_query_dv_timings,
+	.vidioc_enum_dv_timings		= unicam_enum_dv_timings,
+	.vidioc_dv_timings_cap		= unicam_dv_timings_cap,
+
+	.vidioc_reqbufs			= vb2_ioctl_reqbufs,
+	.vidioc_create_bufs		= vb2_ioctl_create_bufs,
+	.vidioc_prepare_buf		= vb2_ioctl_prepare_buf,
+	.vidioc_querybuf		= vb2_ioctl_querybuf,
+	.vidioc_qbuf			= vb2_ioctl_qbuf,
+	.vidioc_dqbuf			= vb2_ioctl_dqbuf,
+	.vidioc_expbuf			= vb2_ioctl_expbuf,
+	.vidioc_streamon		= vb2_ioctl_streamon,
+	.vidioc_streamoff		= vb2_ioctl_streamoff,
+
+	.vidioc_log_status		= unicam_log_status,
+	.vidioc_subscribe_event		= unicam_subscribe_event,
+	.vidioc_unsubscribe_event	= v4l2_event_unsubscribe,
+};
+
+static int
+unicam_async_bound(struct v4l2_async_notifier *notifier,
+		   struct v4l2_subdev *subdev,
+		   struct v4l2_async_subdev *asd)
+{
+	struct unicam_device *unicam = to_unicam_device(notifier->v4l2_dev);
+
+	if (unicam->sensor) {
+		unicam_info(unicam, "Rejecting subdev %s (Already set!!)",
+			    subdev->name);
+		return 0;
+	}
+
+	unicam->sensor = subdev;
+	unicam_dbg(1, unicam, "Using sensor %s for capture\n", subdev->name);
+
+	return 0;
+}
+
+static void unicam_release(struct kref *kref)
+{
+	struct unicam_device *unicam =
+		container_of(kref, struct unicam_device, kref);
+
+	v4l2_ctrl_handler_free(&unicam->ctrl_handler);
+	media_device_cleanup(&unicam->mdev);
+
+	if (unicam->sensor_config)
+		v4l2_subdev_free_pad_config(unicam->sensor_config);
+
+	kfree(unicam);
+}
+
+static void unicam_put(struct unicam_device *unicam)
+{
+	kref_put(&unicam->kref, unicam_release);
+}
+
+static void unicam_get(struct unicam_device *unicam)
+{
+	kref_get(&unicam->kref);
+}
+
+static void unicam_node_release(struct video_device *vdev)
+{
+	struct unicam_node *node = video_get_drvdata(vdev);
+
+	unicam_put(node->dev);
+}
+
+static int register_node(struct unicam_device *unicam, struct unicam_node *node,
+			 enum v4l2_buf_type type, int pad_id)
+{
+	struct video_device *vdev;
+	struct vb2_queue *q;
+	struct v4l2_mbus_framefmt mbus_fmt = {0};
+	const struct unicam_fmt *fmt;
+	int ret;
+
+	if (pad_id == IMAGE_PAD) {
+		ret = __subdev_get_format(unicam, &mbus_fmt, pad_id);
+		if (ret) {
+			unicam_err(unicam, "Failed to get_format - ret %d\n",
+				   ret);
+			return ret;
+		}
+
+		fmt = find_format_by_code(mbus_fmt.code);
+		if (!fmt) {
+			/*
+			 * Find the first format that the sensor and unicam both
+			 * support
+			 */
+			fmt = get_first_supported_format(unicam);
+
+			if (!fmt)
+				/* No compatible formats */
+				return -EINVAL;
+
+			mbus_fmt.code = fmt->code;
+			ret = __subdev_set_format(unicam, &mbus_fmt, pad_id);
+			if (ret)
+				return -EINVAL;
+		}
+		if (mbus_fmt.field != V4L2_FIELD_NONE) {
+			/* Interlaced not supported - disable it now. */
+			mbus_fmt.field = V4L2_FIELD_NONE;
+			ret = __subdev_set_format(unicam, &mbus_fmt, pad_id);
+			if (ret)
+				return -EINVAL;
+		}
+
+		node->v_fmt.fmt.pix.pixelformat = fmt->fourcc ? fmt->fourcc
+						: fmt->repacked_fourcc;
+	} else {
+		/* Fix this node format as embedded data. */
+		fmt = find_format_by_code(MEDIA_BUS_FMT_SENSOR_DATA);
+		node->v_fmt.fmt.meta.dataformat = fmt->fourcc;
+	}
+
+	node->dev = unicam;
+	node->pad_id = pad_id;
+	node->fmt = fmt;
+
+	/* Read current subdev format */
+	unicam_reset_format(node);
+
+	if (v4l2_subdev_has_op(unicam->sensor, video, s_std)) {
+		v4l2_std_id tvnorms;
+
+		if (WARN_ON(!v4l2_subdev_has_op(unicam->sensor, video,
+						g_tvnorms)))
+			/*
+			 * Subdevice should not advertise s_std but not
+			 * g_tvnorms
+			 */
+			return -EINVAL;
+
+		ret = v4l2_subdev_call(unicam->sensor, video,
+				       g_tvnorms, &tvnorms);
+		if (WARN_ON(ret))
+			return -EINVAL;
+		node->video_dev.tvnorms |= tvnorms;
+	}
+
+	spin_lock_init(&node->dma_queue_lock);
+	mutex_init(&node->lock);
+
+	vdev = &node->video_dev;
+	if (pad_id == IMAGE_PAD) {
+		/* Add controls from the subdevice */
+		ret = v4l2_ctrl_add_handler(&unicam->ctrl_handler,
+					    unicam->sensor->ctrl_handler, NULL,
+					    true);
+		if (ret < 0)
+			return ret;
+
+		/*
+		 * If the sensor subdevice has any controls, associate the node
+		 *  with the ctrl handler to allow access from userland.
+		 */
+		if (!list_empty(&unicam->ctrl_handler.ctrls))
+			vdev->ctrl_handler = &unicam->ctrl_handler;
+	}
+
+	q = &node->buffer_queue;
+	q->type = type;
+	q->io_modes = VB2_MMAP | VB2_DMABUF | VB2_READ;
+	q->drv_priv = node;
+	q->ops = &unicam_video_qops;
+	q->mem_ops = &vb2_dma_contig_memops;
+	q->buf_struct_size = sizeof(struct unicam_buffer);
+	q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+	q->lock = &node->lock;
+	q->min_buffers_needed = 1;
+	q->dev = &unicam->pdev->dev;
+
+	ret = vb2_queue_init(q);
+	if (ret) {
+		unicam_err(unicam, "vb2_queue_init() failed\n");
+		return ret;
+	}
+
+	INIT_LIST_HEAD(&node->dma_queue);
+
+	vdev->release = unicam_node_release;
+	vdev->fops = &unicam_fops;
+	vdev->ioctl_ops = &unicam_ioctl_ops;
+	vdev->v4l2_dev = &unicam->v4l2_dev;
+	vdev->vfl_dir = VFL_DIR_RX;
+	vdev->queue = q;
+	vdev->lock = &node->lock;
+	vdev->device_caps = (pad_id == IMAGE_PAD) ?
+				V4L2_CAP_VIDEO_CAPTURE : V4L2_CAP_META_CAPTURE;
+	vdev->device_caps |= V4L2_CAP_READWRITE | V4L2_CAP_STREAMING;
+
+	/* Define the device names */
+	snprintf(vdev->name, sizeof(vdev->name), "%s-%s", UNICAM_MODULE_NAME,
+		 pad_id == IMAGE_PAD ? "image" : "embedded");
+
+	video_set_drvdata(vdev, node);
+	if (pad_id == IMAGE_PAD)
+		vdev->entity.flags |= MEDIA_ENT_FL_DEFAULT;
+	node->pad.flags = MEDIA_PAD_FL_SINK;
+	media_entity_pads_init(&vdev->entity, 1, &node->pad);
+
+	node->dummy_buf_cpu_addr = dma_alloc_coherent(&unicam->pdev->dev,
+						      DUMMY_BUF_SIZE,
+						      &node->dummy_buf_dma_addr,
+						      GFP_KERNEL);
+	if (!node->dummy_buf_cpu_addr) {
+		unicam_err(unicam, "Unable to allocate dummy buffer.\n");
+		return -ENOMEM;
+	}
+	if (pad_id == METADATA_PAD ||
+	    !v4l2_subdev_has_op(unicam->sensor, video, s_std)) {
+		v4l2_disable_ioctl(&node->video_dev, VIDIOC_S_STD);
+		v4l2_disable_ioctl(&node->video_dev, VIDIOC_G_STD);
+		v4l2_disable_ioctl(&node->video_dev, VIDIOC_ENUMSTD);
+	}
+	if (pad_id == METADATA_PAD ||
+	    !v4l2_subdev_has_op(unicam->sensor, video, querystd))
+		v4l2_disable_ioctl(&node->video_dev, VIDIOC_QUERYSTD);
+	if (pad_id == METADATA_PAD ||
+	    !v4l2_subdev_has_op(unicam->sensor, video, s_dv_timings)) {
+		v4l2_disable_ioctl(&node->video_dev, VIDIOC_S_EDID);
+		v4l2_disable_ioctl(&node->video_dev, VIDIOC_G_EDID);
+		v4l2_disable_ioctl(&node->video_dev, VIDIOC_DV_TIMINGS_CAP);
+		v4l2_disable_ioctl(&node->video_dev, VIDIOC_G_DV_TIMINGS);
+		v4l2_disable_ioctl(&node->video_dev, VIDIOC_S_DV_TIMINGS);
+		v4l2_disable_ioctl(&node->video_dev, VIDIOC_ENUM_DV_TIMINGS);
+		v4l2_disable_ioctl(&node->video_dev, VIDIOC_QUERY_DV_TIMINGS);
+	}
+	if (pad_id == METADATA_PAD ||
+	    !v4l2_subdev_has_op(unicam->sensor, pad, enum_frame_interval))
+		v4l2_disable_ioctl(&node->video_dev,
+				   VIDIOC_ENUM_FRAMEINTERVALS);
+	if (pad_id == METADATA_PAD ||
+	    !v4l2_subdev_has_op(unicam->sensor, video, g_frame_interval))
+		v4l2_disable_ioctl(&node->video_dev, VIDIOC_G_PARM);
+	if (pad_id == METADATA_PAD ||
+	    !v4l2_subdev_has_op(unicam->sensor, video, s_frame_interval))
+		v4l2_disable_ioctl(&node->video_dev, VIDIOC_S_PARM);
+
+	if (pad_id == METADATA_PAD ||
+	    !v4l2_subdev_has_op(unicam->sensor, pad, enum_frame_size))
+		v4l2_disable_ioctl(&node->video_dev, VIDIOC_ENUM_FRAMESIZES);
+
+	if (node->pad_id == METADATA_PAD ||
+	    !v4l2_subdev_has_op(unicam->sensor, pad, set_selection))
+		v4l2_disable_ioctl(&node->video_dev, VIDIOC_S_SELECTION);
+
+	if (node->pad_id == METADATA_PAD ||
+	    !v4l2_subdev_has_op(unicam->sensor, pad, get_selection))
+		v4l2_disable_ioctl(&node->video_dev, VIDIOC_G_SELECTION);
+
+	ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1);
+	if (ret) {
+		unicam_err(unicam, "Unable to register video device %s\n",
+			   vdev->name);
+		return ret;
+	}
+
+	/*
+	 * Acquire a reference to unicam, which will be released when the video
+	 * device will be unregistered and userspace will have closed all open
+	 * file handles.
+	 */
+	unicam_get(unicam);
+	node->registered = true;
+
+	if (pad_id != METADATA_PAD || unicam->sensor_embedded_data) {
+		ret = media_create_pad_link(&unicam->sensor->entity, pad_id,
+					    &node->video_dev.entity, 0,
+					    MEDIA_LNK_FL_ENABLED |
+					    MEDIA_LNK_FL_IMMUTABLE);
+		if (ret)
+			unicam_err(unicam, "Unable to create pad link for %s\n",
+				   vdev->name);
+	}
+
+	return ret;
+}
+
+static void unregister_nodes(struct unicam_device *unicam)
+{
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(unicam->node); i++) {
+		struct unicam_node *node = &unicam->node[i];
+
+		if (node->dummy_buf_cpu_addr) {
+			dma_free_coherent(&unicam->pdev->dev, DUMMY_BUF_SIZE,
+					  node->dummy_buf_cpu_addr,
+					  node->dummy_buf_dma_addr);
+		}
+
+		if (node->registered) {
+			node->registered = false;
+			video_unregister_device(&node->video_dev);
+		}
+	}
+}
+
+static int unicam_probe_complete(struct unicam_device *unicam)
+{
+	int ret;
+
+	unicam->v4l2_dev.notify = unicam_notify;
+
+	unicam->sensor_config = v4l2_subdev_alloc_pad_config(unicam->sensor);
+	if (!unicam->sensor_config)
+		return -ENOMEM;
+
+	unicam->sensor_embedded_data = (unicam->sensor->entity.num_pads >= 2);
+
+	ret = register_node(unicam, &unicam->node[IMAGE_PAD],
+			    V4L2_BUF_TYPE_VIDEO_CAPTURE, IMAGE_PAD);
+	if (ret) {
+		unicam_err(unicam, "Unable to register image video device.\n");
+		goto unregister;
+	}
+
+	ret = register_node(unicam, &unicam->node[METADATA_PAD],
+			    V4L2_BUF_TYPE_META_CAPTURE, METADATA_PAD);
+	if (ret) {
+		unicam_err(unicam, "Unable to register metadata video device.\n");
+		goto unregister;
+	}
+
+	ret = v4l2_device_register_ro_subdev_nodes(&unicam->v4l2_dev);
+	if (ret) {
+		unicam_err(unicam, "Unable to register subdev nodes.\n");
+		goto unregister;
+	}
+
+	/*
+	 * Release the initial reference, all references are now owned by the
+	 * video devices.
+	 */
+	unicam_put(unicam);
+	return 0;
+
+unregister:
+	unregister_nodes(unicam);
+	unicam_put(unicam);
+
+	return ret;
+}
+
+static int unicam_async_complete(struct v4l2_async_notifier *notifier)
+{
+	struct unicam_device *unicam = to_unicam_device(notifier->v4l2_dev);
+
+	return unicam_probe_complete(unicam);
+}
+
+static const struct v4l2_async_notifier_operations unicam_async_ops = {
+	.bound = unicam_async_bound,
+	.complete = unicam_async_complete,
+};
+
+static int of_unicam_connect_subdevs(struct unicam_device *dev)
+{
+	struct platform_device *pdev = dev->pdev;
+	struct v4l2_fwnode_endpoint ep = { };
+	struct device_node *ep_node;
+	struct device_node *sensor_node;
+	unsigned int lane;
+	int ret = -EINVAL;
+
+	if (of_property_read_u32(pdev->dev.of_node, "brcm,num-data-lanes",
+				 &dev->max_data_lanes) < 0) {
+		unicam_err(dev, "number of data lanes not set\n");
+		return -EINVAL;
+	}
+
+	/* Get the local endpoint and remote device. */
+	ep_node = of_graph_get_next_endpoint(pdev->dev.of_node, NULL);
+	if (!ep_node) {
+		unicam_dbg(3, dev, "can't get next endpoint\n");
+		return -EINVAL;
+	}
+
+	unicam_dbg(3, dev, "ep_node is %pOF\n", ep_node);
+
+	sensor_node = of_graph_get_remote_port_parent(ep_node);
+	if (!sensor_node) {
+		unicam_dbg(3, dev, "can't get remote parent\n");
+		goto cleanup_exit;
+	}
+
+	unicam_dbg(1, dev, "found subdevice %pOF\n", sensor_node);
+
+	/* Parse the local endpoint and validate its configuration. */
+	v4l2_fwnode_endpoint_parse(of_fwnode_handle(ep_node), &ep);
+
+	unicam_dbg(3, dev, "parsed local endpoint, bus_type %u\n",
+		   ep.bus_type);
+
+	dev->bus_type = ep.bus_type;
+
+	switch (ep.bus_type) {
+	case V4L2_MBUS_CSI2_DPHY:
+		switch (ep.bus.mipi_csi2.num_data_lanes) {
+		case 1:
+		case 2:
+		case 4:
+			break;
+
+		default:
+			unicam_err(dev, "subdevice %pOF: %u data lanes not supported\n",
+				   sensor_node,
+				   ep.bus.mipi_csi2.num_data_lanes);
+			goto cleanup_exit;
+		}
+
+		for (lane = 0; lane < ep.bus.mipi_csi2.num_data_lanes; lane++) {
+			if (ep.bus.mipi_csi2.data_lanes[lane] != lane + 1) {
+				unicam_err(dev, "subdevice %pOF: data lanes reordering not supported\n",
+					   sensor_node);
+				goto cleanup_exit;
+			}
+		}
+
+		if (ep.bus.mipi_csi2.num_data_lanes > dev->max_data_lanes) {
+			unicam_err(dev, "subdevice requires %u data lanes when %u are supported\n",
+				   ep.bus.mipi_csi2.num_data_lanes,
+				   dev->max_data_lanes);
+		}
+
+		dev->max_data_lanes = ep.bus.mipi_csi2.num_data_lanes;
+		dev->bus_flags = ep.bus.mipi_csi2.flags;
+
+		break;
+
+	case V4L2_MBUS_CCP2:
+		if (ep.bus.mipi_csi1.clock_lane != 0 ||
+		    ep.bus.mipi_csi1.data_lane != 1) {
+			unicam_err(dev, "subdevice %pOF: unsupported lanes configuration\n",
+				   sensor_node);
+			goto cleanup_exit;
+		}
+
+		dev->max_data_lanes = 1;
+		dev->bus_flags = ep.bus.mipi_csi1.strobe;
+		break;
+
+	default:
+		/* Unsupported bus type */
+		unicam_err(dev, "subdevice %pOF: unsupported bus type %u\n",
+			   sensor_node, ep.bus_type);
+		goto cleanup_exit;
+	}
+
+	unicam_dbg(3, dev, "subdevice %pOF: %s bus, %u data lanes, flags=0x%08x\n",
+		   sensor_node,
+		   dev->bus_type == V4L2_MBUS_CSI2_DPHY ? "CSI-2" : "CCP2",
+		   dev->max_data_lanes, dev->bus_flags);
+
+	/* Initialize and register the async notifier. */
+	v4l2_async_notifier_init(&dev->notifier);
+	dev->notifier.ops = &unicam_async_ops;
+
+	dev->asd.match_type = V4L2_ASYNC_MATCH_FWNODE;
+	dev->asd.match.fwnode = of_fwnode_handle(sensor_node);
+	ret = v4l2_async_notifier_add_subdev(&dev->notifier, &dev->asd);
+	if (ret) {
+		unicam_err(dev, "Error adding subdevice: %d\n", ret);
+		goto cleanup_exit;
+	}
+
+	ret = v4l2_async_notifier_register(&dev->v4l2_dev, &dev->notifier);
+	if (ret) {
+		unicam_err(dev, "Error registering async notifier: %d\n", ret);
+		ret = -EINVAL;
+	}
+
+cleanup_exit:
+	of_node_put(sensor_node);
+	of_node_put(ep_node);
+
+	return ret;
+}
+
+static int unicam_probe(struct platform_device *pdev)
+{
+	struct unicam_device *unicam;
+	int ret;
+
+	unicam = kzalloc(sizeof(*unicam), GFP_KERNEL);
+	if (!unicam)
+		return -ENOMEM;
+
+	kref_init(&unicam->kref);
+	unicam->pdev = pdev;
+
+	unicam->base = devm_platform_ioremap_resource(pdev, 0);
+	if (IS_ERR(unicam->base)) {
+		unicam_err(unicam, "Failed to get main io block\n");
+		ret = PTR_ERR(unicam->base);
+		goto err_unicam_put;
+	}
+
+	unicam->clk_gate_base = devm_platform_ioremap_resource(pdev, 1);
+	if (IS_ERR(unicam->clk_gate_base)) {
+		unicam_err(unicam, "Failed to get 2nd io block\n");
+		ret = PTR_ERR(unicam->clk_gate_base);
+		goto err_unicam_put;
+	}
+
+	unicam->clock = devm_clk_get(&pdev->dev, "lp");
+	if (IS_ERR(unicam->clock)) {
+		unicam_err(unicam, "Failed to get lp clock\n");
+		ret = PTR_ERR(unicam->clock);
+		goto err_unicam_put;
+	}
+
+	unicam->vpu_clock = devm_clk_get(&pdev->dev, "vpu");
+	if (IS_ERR(unicam->vpu_clock)) {
+		unicam_err(unicam, "Failed to get vpu clock\n");
+		ret = PTR_ERR(unicam->vpu_clock);
+		goto err_unicam_put;
+	}
+
+	ret = platform_get_irq(pdev, 0);
+	if (ret <= 0) {
+		dev_err(&pdev->dev, "No IRQ resource\n");
+		ret = -EINVAL;
+		goto err_unicam_put;
+	}
+
+	ret = devm_request_irq(&pdev->dev, ret, unicam_isr, 0,
+			       "unicam_capture0", unicam);
+	if (ret) {
+		dev_err(&pdev->dev, "Unable to request interrupt\n");
+		ret = -EINVAL;
+		goto err_unicam_put;
+	}
+
+	unicam->mdev.dev = &pdev->dev;
+	strscpy(unicam->mdev.model, UNICAM_MODULE_NAME,
+		sizeof(unicam->mdev.model));
+	strscpy(unicam->mdev.serial, "", sizeof(unicam->mdev.serial));
+	snprintf(unicam->mdev.bus_info, sizeof(unicam->mdev.bus_info),
+		 "platform:%s", dev_name(&pdev->dev));
+	unicam->mdev.hw_revision = 0;
+
+	media_device_init(&unicam->mdev);
+
+	unicam->v4l2_dev.mdev = &unicam->mdev;
+
+	ret = v4l2_device_register(&pdev->dev, &unicam->v4l2_dev);
+	if (ret) {
+		unicam_err(unicam,
+			   "Unable to register v4l2 device.\n");
+		goto err_unicam_put;
+	}
+
+	ret = media_device_register(&unicam->mdev);
+	if (ret < 0) {
+		unicam_err(unicam,
+			   "Unable to register media-controller device.\n");
+		goto err_v4l2_unregister;
+	}
+
+	/* Reserve space for the controls */
+	ret = v4l2_ctrl_handler_init(&unicam->ctrl_handler, 16);
+	if (ret < 0)
+		goto err_media_unregister;
+
+	/* set the driver data in platform device */
+	platform_set_drvdata(pdev, unicam);
+
+	ret = of_unicam_connect_subdevs(unicam);
+	if (ret) {
+		dev_err(&pdev->dev, "Failed to connect subdevs\n");
+		goto err_media_unregister;
+	}
+
+	/* Enable the block power domain */
+	pm_runtime_enable(&pdev->dev);
+
+	return 0;
+
+err_media_unregister:
+	media_device_unregister(&unicam->mdev);
+err_v4l2_unregister:
+	v4l2_device_unregister(&unicam->v4l2_dev);
+err_unicam_put:
+	unicam_put(unicam);
+
+	return ret;
+}
+
+static int unicam_remove(struct platform_device *pdev)
+{
+	struct unicam_device *unicam = platform_get_drvdata(pdev);
+
+	unicam_dbg(2, unicam, "%s\n", __func__);
+
+	v4l2_async_notifier_unregister(&unicam->notifier);
+	v4l2_device_unregister(&unicam->v4l2_dev);
+	media_device_unregister(&unicam->mdev);
+	unregister_nodes(unicam);
+
+	pm_runtime_disable(&pdev->dev);
+
+	return 0;
+}
+
+static const struct of_device_id unicam_of_match[] = {
+	{ .compatible = "brcm,bcm2835-unicam", },
+	{ /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, unicam_of_match);
+
+static struct platform_driver unicam_driver = {
+	.probe		= unicam_probe,
+	.remove		= unicam_remove,
+	.driver = {
+		.name	= UNICAM_MODULE_NAME,
+		.of_match_table = of_match_ptr(unicam_of_match),
+	},
+};
+
+module_platform_driver(unicam_driver);
+
+MODULE_AUTHOR("Dave Stevenson <dave.stevenson@raspberrypi.com>");
+MODULE_DESCRIPTION("BCM2835 Unicam driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(UNICAM_VERSION);
diff -Nur --no-dereference linux-5.10.52-orig/drivers/media/platform/bcm2835/Kconfig linux-5.10.52-v7l+/drivers/media/platform/bcm2835/Kconfig
--- linux-5.10.52-orig/drivers/media/platform/bcm2835/Kconfig	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/media/platform/bcm2835/Kconfig	2021-07-25 16:46:06.388283806 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+# Broadcom VideoCore4 V4L2 camera support
+
+config VIDEO_BCM2835_UNICAM
+	tristate "Broadcom BCM283x/BCM271x Unicam video capture driver"
+	depends on VIDEO_V4L2
+	depends on ARCH_BCM2835 || COMPILE_TEST
+	select VIDEO_V4L2_SUBDEV_API
+	select MEDIA_CONTROLLER
+	select VIDEOBUF2_DMA_CONTIG
+	select V4L2_FWNODE
+	help
+	  Say Y here to enable support for the BCM283x/BCM271x CSI-2 receiver.
+	  This is a V4L2 driver that controls the CSI-2 receiver directly,
+	  independently from the VC4 firmware.
+	  This driver is mutually exclusive with the use of bcm2835-camera. The
+	  firmware will disable all access to the peripheral from within the
+	  firmware if it finds a DT node using it, and bcm2835-camera will
+	  therefore fail to probe.
+
+	  To compile this driver as a module, choose M here. The module will be
+	  called bcm2835-unicam.
diff -Nur --no-dereference linux-5.10.52-orig/drivers/media/platform/bcm2835/Makefile linux-5.10.52-v7l+/drivers/media/platform/bcm2835/Makefile
--- linux-5.10.52-orig/drivers/media/platform/bcm2835/Makefile	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/media/platform/bcm2835/Makefile	2021-07-25 16:46:06.388283806 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1 @
+# Makefile for BCM2835 Unicam driver
+
+obj-$(CONFIG_VIDEO_BCM2835_UNICAM) += bcm2835-unicam.o
diff -Nur --no-dereference linux-5.10.52-orig/drivers/media/platform/bcm2835/vc4-regs-unicam.h linux-5.10.52-v7l+/drivers/media/platform/bcm2835/vc4-regs-unicam.h
--- linux-5.10.52-orig/drivers/media/platform/bcm2835/vc4-regs-unicam.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/media/platform/bcm2835/vc4-regs-unicam.h	2021-07-25 16:46:06.388283806 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+/*
+ * Copyright (C) 2017-2020 Raspberry Pi Trading.
+ * Dave Stevenson <dave.stevenson@raspberrypi.com>
+ */
+
+#ifndef VC4_REGS_UNICAM_H
+#define VC4_REGS_UNICAM_H
+
+/*
+ * The following values are taken from files found within the code drop
+ * made by Broadcom for the BCM21553 Graphics Driver, predominantly in
+ * brcm_usrlib/dag/vmcsx/vcinclude/hardware_vc4.h.
+ * They have been modified to be only the register offset.
+ */
+#define UNICAM_CTRL	0x000
+#define UNICAM_STA	0x004
+#define UNICAM_ANA	0x008
+#define UNICAM_PRI	0x00c
+#define UNICAM_CLK	0x010
+#define UNICAM_CLT	0x014
+#define UNICAM_DAT0	0x018
+#define UNICAM_DAT1	0x01c
+#define UNICAM_DAT2	0x020
+#define UNICAM_DAT3	0x024
+#define UNICAM_DLT	0x028
+#define UNICAM_CMP0	0x02c
+#define UNICAM_CMP1	0x030
+#define UNICAM_CAP0	0x034
+#define UNICAM_CAP1	0x038
+#define UNICAM_ICTL	0x100
+#define UNICAM_ISTA	0x104
+#define UNICAM_IDI0	0x108
+#define UNICAM_IPIPE	0x10c
+#define UNICAM_IBSA0	0x110
+#define UNICAM_IBEA0	0x114
+#define UNICAM_IBLS	0x118
+#define UNICAM_IBWP	0x11c
+#define UNICAM_IHWIN	0x120
+#define UNICAM_IHSTA	0x124
+#define UNICAM_IVWIN	0x128
+#define UNICAM_IVSTA	0x12c
+#define UNICAM_ICC	0x130
+#define UNICAM_ICS	0x134
+#define UNICAM_IDC	0x138
+#define UNICAM_IDPO	0x13c
+#define UNICAM_IDCA	0x140
+#define UNICAM_IDCD	0x144
+#define UNICAM_IDS	0x148
+#define UNICAM_DCS	0x200
+#define UNICAM_DBSA0	0x204
+#define UNICAM_DBEA0	0x208
+#define UNICAM_DBWP	0x20c
+#define UNICAM_DBCTL	0x300
+#define UNICAM_IBSA1	0x304
+#define UNICAM_IBEA1	0x308
+#define UNICAM_IDI1	0x30c
+#define UNICAM_DBSA1	0x310
+#define UNICAM_DBEA1	0x314
+#define UNICAM_MISC	0x400
+
+/*
+ * The following bitmasks are from the kernel released by Broadcom
+ * for Android - https://android.googlesource.com/kernel/bcm/
+ * The Rhea, Hawaii, and Java chips all contain the same VideoCore4
+ * Unicam block as BCM2835, as defined in eg
+ * arch/arm/mach-rhea/include/mach/rdb_A0/brcm_rdb_cam.h and similar.
+ * Values reworked to use the kernel BIT and GENMASK macros.
+ *
+ * Some of the bit mnenomics have been amended to match the datasheet.
+ */
+/* UNICAM_CTRL Register */
+#define UNICAM_CPE		BIT(0)
+#define UNICAM_MEM		BIT(1)
+#define UNICAM_CPR		BIT(2)
+#define UNICAM_CPM_MASK		GENMASK(3, 3)
+#define UNICAM_CPM_CSI2		0
+#define UNICAM_CPM_CCP2		1
+#define UNICAM_SOE		BIT(4)
+#define UNICAM_DCM_MASK		GENMASK(5, 5)
+#define UNICAM_DCM_STROBE	0
+#define UNICAM_DCM_DATA		1
+#define UNICAM_SLS		BIT(6)
+#define UNICAM_PFT_MASK		GENMASK(11, 8)
+#define UNICAM_OET_MASK		GENMASK(20, 12)
+
+/* UNICAM_STA Register */
+#define UNICAM_SYN		BIT(0)
+#define UNICAM_CS		BIT(1)
+#define UNICAM_SBE		BIT(2)
+#define UNICAM_PBE		BIT(3)
+#define UNICAM_HOE		BIT(4)
+#define UNICAM_PLE		BIT(5)
+#define UNICAM_SSC		BIT(6)
+#define UNICAM_CRCE		BIT(7)
+#define UNICAM_OES		BIT(8)
+#define UNICAM_IFO		BIT(9)
+#define UNICAM_OFO		BIT(10)
+#define UNICAM_BFO		BIT(11)
+#define UNICAM_DL		BIT(12)
+#define UNICAM_PS		BIT(13)
+#define UNICAM_IS		BIT(14)
+#define UNICAM_PI0		BIT(15)
+#define UNICAM_PI1		BIT(16)
+#define UNICAM_FSI_S		BIT(17)
+#define UNICAM_FEI_S		BIT(18)
+#define UNICAM_LCI_S		BIT(19)
+#define UNICAM_BUF0_RDY		BIT(20)
+#define UNICAM_BUF0_NO		BIT(21)
+#define UNICAM_BUF1_RDY		BIT(22)
+#define UNICAM_BUF1_NO		BIT(23)
+#define UNICAM_DI		BIT(24)
+
+#define UNICAM_STA_MASK_ALL \
+		(UNICAM_DL + \
+		UNICAM_SBE + \
+		UNICAM_PBE + \
+		UNICAM_HOE + \
+		UNICAM_PLE + \
+		UNICAM_SSC + \
+		UNICAM_CRCE + \
+		UNICAM_IFO + \
+		UNICAM_OFO + \
+		UNICAM_PS + \
+		UNICAM_PI0 + \
+		UNICAM_PI1)
+
+/* UNICAM_ANA Register */
+#define UNICAM_APD		BIT(0)
+#define UNICAM_BPD		BIT(1)
+#define UNICAM_AR		BIT(2)
+#define UNICAM_DDL		BIT(3)
+#define UNICAM_CTATADJ_MASK	GENMASK(7, 4)
+#define UNICAM_PTATADJ_MASK	GENMASK(11, 8)
+
+/* UNICAM_PRI Register */
+#define UNICAM_PE		BIT(0)
+#define UNICAM_PT_MASK		GENMASK(2, 1)
+#define UNICAM_NP_MASK		GENMASK(7, 4)
+#define UNICAM_PP_MASK		GENMASK(11, 8)
+#define UNICAM_BS_MASK		GENMASK(15, 12)
+#define UNICAM_BL_MASK		GENMASK(17, 16)
+
+/* UNICAM_CLK Register */
+#define UNICAM_CLE		BIT(0)
+#define UNICAM_CLPD		BIT(1)
+#define UNICAM_CLLPE		BIT(2)
+#define UNICAM_CLHSE		BIT(3)
+#define UNICAM_CLTRE		BIT(4)
+#define UNICAM_CLAC_MASK	GENMASK(8, 5)
+#define UNICAM_CLSTE		BIT(29)
+
+/* UNICAM_CLT Register */
+#define UNICAM_CLT1_MASK	GENMASK(7, 0)
+#define UNICAM_CLT2_MASK	GENMASK(15, 8)
+
+/* UNICAM_DATn Registers */
+#define UNICAM_DLE		BIT(0)
+#define UNICAM_DLPD		BIT(1)
+#define UNICAM_DLLPE		BIT(2)
+#define UNICAM_DLHSE		BIT(3)
+#define UNICAM_DLTRE		BIT(4)
+#define UNICAM_DLSM		BIT(5)
+#define UNICAM_DLFO		BIT(28)
+#define UNICAM_DLSTE		BIT(29)
+
+#define UNICAM_DAT_MASK_ALL (UNICAM_DLSTE + UNICAM_DLFO)
+
+/* UNICAM_DLT Register */
+#define UNICAM_DLT1_MASK	GENMASK(7, 0)
+#define UNICAM_DLT2_MASK	GENMASK(15, 8)
+#define UNICAM_DLT3_MASK	GENMASK(23, 16)
+
+/* UNICAM_ICTL Register */
+#define UNICAM_FSIE		BIT(0)
+#define UNICAM_FEIE		BIT(1)
+#define UNICAM_IBOB		BIT(2)
+#define UNICAM_FCM		BIT(3)
+#define UNICAM_TFC		BIT(4)
+#define UNICAM_LIP_MASK		GENMASK(6, 5)
+#define UNICAM_LCIE_MASK	GENMASK(28, 16)
+
+/* UNICAM_IDI0/1 Register */
+#define UNICAM_ID0_MASK		GENMASK(7, 0)
+#define UNICAM_ID1_MASK		GENMASK(15, 8)
+#define UNICAM_ID2_MASK		GENMASK(23, 16)
+#define UNICAM_ID3_MASK		GENMASK(31, 24)
+
+/* UNICAM_ISTA Register */
+#define UNICAM_FSI		BIT(0)
+#define UNICAM_FEI		BIT(1)
+#define UNICAM_LCI		BIT(2)
+
+#define UNICAM_ISTA_MASK_ALL (UNICAM_FSI + UNICAM_FEI + UNICAM_LCI)
+
+/* UNICAM_IPIPE Register */
+#define UNICAM_PUM_MASK		GENMASK(2, 0)
+		/* Unpacking modes */
+		#define UNICAM_PUM_NONE		0
+		#define UNICAM_PUM_UNPACK6	1
+		#define UNICAM_PUM_UNPACK7	2
+		#define UNICAM_PUM_UNPACK8	3
+		#define UNICAM_PUM_UNPACK10	4
+		#define UNICAM_PUM_UNPACK12	5
+		#define UNICAM_PUM_UNPACK14	6
+		#define UNICAM_PUM_UNPACK16	7
+#define UNICAM_DDM_MASK		GENMASK(6, 3)
+#define UNICAM_PPM_MASK		GENMASK(9, 7)
+		/* Packing modes */
+		#define UNICAM_PPM_NONE		0
+		#define UNICAM_PPM_PACK8	1
+		#define UNICAM_PPM_PACK10	2
+		#define UNICAM_PPM_PACK12	3
+		#define UNICAM_PPM_PACK14	4
+		#define UNICAM_PPM_PACK16	5
+#define UNICAM_DEM_MASK		GENMASK(11, 10)
+#define UNICAM_DEBL_MASK	GENMASK(14, 12)
+#define UNICAM_ICM_MASK		GENMASK(16, 15)
+#define UNICAM_IDM_MASK		GENMASK(17, 17)
+
+/* UNICAM_ICC Register */
+#define UNICAM_ICFL_MASK	GENMASK(4, 0)
+#define UNICAM_ICFH_MASK	GENMASK(9, 5)
+#define UNICAM_ICST_MASK	GENMASK(12, 10)
+#define UNICAM_ICLT_MASK	GENMASK(15, 13)
+#define UNICAM_ICLL_MASK	GENMASK(31, 16)
+
+/* UNICAM_DCS Register */
+#define UNICAM_DIE		BIT(0)
+#define UNICAM_DIM		BIT(1)
+#define UNICAM_DBOB		BIT(3)
+#define UNICAM_FDE		BIT(4)
+#define UNICAM_LDP		BIT(5)
+#define UNICAM_EDL_MASK		GENMASK(15, 8)
+
+/* UNICAM_DBCTL Register */
+#define UNICAM_DBEN		BIT(0)
+#define UNICAM_BUF0_IE		BIT(1)
+#define UNICAM_BUF1_IE		BIT(2)
+
+/* UNICAM_CMP[0,1] register */
+#define UNICAM_PCE		BIT(31)
+#define UNICAM_GI		BIT(9)
+#define UNICAM_CPH		BIT(8)
+#define UNICAM_PCVC_MASK	GENMASK(7, 6)
+#define UNICAM_PCDT_MASK	GENMASK(5, 0)
+
+/* UNICAM_MISC register */
+#define UNICAM_FL0		BIT(6)
+#define UNICAM_FL1		BIT(9)
+
+#endif
diff -Nur --no-dereference linux-5.10.52-orig/drivers/media/platform/Kconfig linux-5.10.52-v7l+/drivers/media/platform/Kconfig
--- linux-5.10.52-orig/drivers/media/platform/Kconfig	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/media/platform/Kconfig	2021-07-25 16:46:06.368284141 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:155 @
 source "drivers/media/platform/xilinx/Kconfig"
 source "drivers/media/platform/rcar-vin/Kconfig"
 source "drivers/media/platform/atmel/Kconfig"
+source "drivers/media/platform/bcm2835/Kconfig"
 source "drivers/media/platform/sunxi/Kconfig"
 
 config VIDEO_TI_CAL
diff -Nur --no-dereference linux-5.10.52-orig/drivers/media/platform/Makefile linux-5.10.52-v7l+/drivers/media/platform/Makefile
--- linux-5.10.52-orig/drivers/media/platform/Makefile	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/media/platform/Makefile	2021-07-25 16:46:06.368284141 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:82 @
 
 obj-$(CONFIG_VIDEO_QCOM_VENUS)		+= qcom/venus/
 
+obj-y					+= bcm2835/
+
 obj-y					+= sunxi/
diff -Nur --no-dereference linux-5.10.52-orig/drivers/media/spi/Kconfig linux-5.10.52-v7l+/drivers/media/spi/Kconfig
--- linux-5.10.52-orig/drivers/media/spi/Kconfig	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/media/spi/Kconfig	2021-07-25 16:46:07.098271903 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:28 @
 config CXD2880_SPI_DRV
 	tristate "Sony CXD2880 SPI support"
 	depends on DVB_CORE && SPI
+	select DVB_CXD2880 if MEDIA_SUBDRV_AUTOSELECT
 	default m if !MEDIA_SUBDRV_AUTOSELECT
 	help
 	  Choose if you would like to have SPI interface support for Sony CXD2880.
diff -Nur --no-dereference linux-5.10.52-orig/drivers/media/usb/dvb-usb-v2/rtl28xxu.c linux-5.10.52-v7l+/drivers/media/usb/dvb-usb-v2/rtl28xxu.c
--- linux-5.10.52-orig/drivers/media/usb/dvb-usb-v2/rtl28xxu.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/media/usb/dvb-usb-v2/rtl28xxu.c	2021-07-25 16:46:07.678262179 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1947 @
 		&rtl28xxu_props, "Compro VideoMate U650F", NULL) },
 	{ DVB_USB_DEVICE(USB_VID_KWORLD_2, 0xd394,
 		&rtl28xxu_props, "MaxMedia HU394-T", NULL) },
+	{ DVB_USB_DEVICE(USB_VID_GTEK, 0xb803 /*USB_PID_AUGUST_DVBT205*/,
+		&rtl28xxu_props, "August DVB-T 205", NULL) },
+	{ DVB_USB_DEVICE(USB_VID_GTEK, 0xa803 /*USB_PID_AUGUST_DVBT205*/,
+		&rtl28xxu_props, "August DVB-T 205", NULL) },
 	{ DVB_USB_DEVICE(USB_VID_LEADTEK, 0x6a03,
 		&rtl28xxu_props, "Leadtek WinFast DTV Dongle mini", NULL) },
 	{ DVB_USB_DEVICE(USB_VID_GTEK, USB_PID_CPYTO_REDI_PC50A,
diff -Nur --no-dereference linux-5.10.52-orig/drivers/media/v4l2-core/v4l2-ctrls.c linux-5.10.52-v7l+/drivers/media/v4l2-core/v4l2-ctrls.c
--- linux-5.10.52-orig/drivers/media/v4l2-core/v4l2-ctrls.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/media/v4l2-core/v4l2-ctrls.c	2021-07-25 16:46:08.048255976 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:278 @
 		"Flash",
 		"Cloudy",
 		"Shade",
+		"Greyworld",
 		NULL,
 	};
 	static const char * const camera_iso_sensitivity_auto[] = {
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1025 @
 	case V4L2_CID_MPEG_VIDEO_HEVC_SPS:			return "HEVC Sequence Parameter Set";
 	case V4L2_CID_MPEG_VIDEO_HEVC_PPS:			return "HEVC Picture Parameter Set";
 	case V4L2_CID_MPEG_VIDEO_HEVC_SLICE_PARAMS:		return "HEVC Slice Parameters";
+	case V4L2_CID_MPEG_VIDEO_HEVC_SCALING_MATRIX:		return "HEVC Scaling Matrix";
 	case V4L2_CID_MPEG_VIDEO_HEVC_DECODE_MODE:		return "HEVC Decode Mode";
 	case V4L2_CID_MPEG_VIDEO_HEVC_START_CODE:		return "HEVC Start Code";
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1466 @
 	case V4L2_CID_MPEG_VIDEO_HEVC_SLICE_PARAMS:
 		*type = V4L2_CTRL_TYPE_HEVC_SLICE_PARAMS;
 		break;
+	case V4L2_CID_MPEG_VIDEO_HEVC_SCALING_MATRIX:
+		*type = V4L2_CTRL_TYPE_HEVC_SCALING_MATRIX;
+		break;
 	case V4L2_CID_UNIT_CELL_SIZE:
 		*type = V4L2_CTRL_TYPE_AREA;
 		*flags |= V4L2_CTRL_FLAG_READ_ONLY;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1942 @
 		zero_padding(*p_hevc_slice_params);
 		break;
 
+	case V4L2_CTRL_TYPE_HEVC_SCALING_MATRIX:
+		break;
+
 	case V4L2_CTRL_TYPE_AREA:
 		area = p;
 		if (!area->width || !area->height)
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:2655 @
 	case V4L2_CTRL_TYPE_HEVC_SLICE_PARAMS:
 		elem_size = sizeof(struct v4l2_ctrl_hevc_slice_params);
 		break;
+	case V4L2_CTRL_TYPE_HEVC_SCALING_MATRIX:
+		elem_size = sizeof(struct v4l2_ctrl_hevc_scaling_matrix);
+		break;
 	case V4L2_CTRL_TYPE_AREA:
 		elem_size = sizeof(struct v4l2_area);
 		break;
diff -Nur --no-dereference linux-5.10.52-orig/drivers/media/v4l2-core/v4l2-ioctl.c linux-5.10.52-v7l+/drivers/media/v4l2-core/v4l2-ioctl.c
--- linux-5.10.52-orig/drivers/media/v4l2-core/v4l2-ioctl.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/media/v4l2-core/v4l2-ioctl.c	2021-07-25 16:46:08.108254970 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1286 @
 	case V4L2_PIX_FMT_Y16_BE:	descr = "16-bit Greyscale BE"; break;
 	case V4L2_PIX_FMT_Y10BPACK:	descr = "10-bit Greyscale (Packed)"; break;
 	case V4L2_PIX_FMT_Y10P:		descr = "10-bit Greyscale (MIPI Packed)"; break;
+	case V4L2_PIX_FMT_Y12P:		descr = "12-bit Greyscale (MIPI Packed)"; break;
+	case V4L2_PIX_FMT_Y14P:		descr = "14-bit Greyscale (MIPI Packed)"; break;
 	case V4L2_PIX_FMT_Y8I:		descr = "Interleaved 8-bit Greyscale"; break;
 	case V4L2_PIX_FMT_Y12I:		descr = "Interleaved 12-bit Greyscale"; break;
 	case V4L2_PIX_FMT_Z16:		descr = "16-bit Depth"; break;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1330 @
 	case V4L2_PIX_FMT_NV61M:	descr = "Y/CrCb 4:2:2 (N-C)"; break;
 	case V4L2_PIX_FMT_NV12MT:	descr = "Y/CbCr 4:2:0 (64x32 MB, N-C)"; break;
 	case V4L2_PIX_FMT_NV12MT_16X16:	descr = "Y/CbCr 4:2:0 (16x16 MB, N-C)"; break;
+	case V4L2_PIX_FMT_NV12_COL128:	descr = "Y/CbCr 4:2:0 (128b cols)"; break;
+	case V4L2_PIX_FMT_NV12_10_COL128: descr = "10-bit Y/CbCr 4:2:0 (128b cols)"; break;
 	case V4L2_PIX_FMT_YUV420M:	descr = "Planar YUV 4:2:0 (N-C)"; break;
 	case V4L2_PIX_FMT_YVU420M:	descr = "Planar YVU 4:2:0 (N-C)"; break;
 	case V4L2_PIX_FMT_YUV422M:	descr = "Planar YUV 4:2:2 (N-C)"; break;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1409 @
 	case V4L2_META_FMT_UVC:		descr = "UVC Payload Header Metadata"; break;
 	case V4L2_META_FMT_D4XX:	descr = "Intel D4xx UVC Metadata"; break;
 	case V4L2_META_FMT_VIVID:       descr = "Vivid Metadata"; break;
+	case V4L2_META_FMT_SENSOR_DATA:	descr = "Sensor Ancillary Metadata"; break;
+	case V4L2_META_FMT_BCM2835_ISP_STATS: descr = "BCM2835 ISP Image Statistics"; break;
 
 	default:
 		/* Compressed formats */
diff -Nur --no-dereference linux-5.10.52-orig/drivers/media/v4l2-core/v4l2-mem2mem.c linux-5.10.52-v7l+/drivers/media/v4l2-core/v4l2-mem2mem.c
--- linux-5.10.52-orig/drivers/media/v4l2-core/v4l2-mem2mem.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/media/v4l2-core/v4l2-mem2mem.c	2021-07-25 16:46:08.108254970 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:304 @
 
 	dprintk("Trying to schedule a job for m2m_ctx: %p\n", m2m_ctx);
 
-	if (!m2m_ctx->out_q_ctx.q.streaming
-	    || !m2m_ctx->cap_q_ctx.q.streaming) {
-		dprintk("Streaming needs to be on for both queues\n");
+	if (!(m2m_ctx->out_q_ctx.q.streaming &&
+	      m2m_ctx->cap_q_ctx.q.streaming) &&
+	    !(m2m_ctx->out_q_ctx.buffered && m2m_ctx->out_q_ctx.q.streaming)) {
+		dprintk("Streaming needs to be on for both queues, or buffered and OUTPUT streaming\n");
 		return;
 	}
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:495 @
 	 * holding capture buffers. Those should use
 	 * v4l2_m2m_buf_done_and_job_finish() instead.
 	 */
-	WARN_ON(m2m_ctx->out_q_ctx.q.subsystem_flags &
-		VB2_V4L2_FL_SUPPORTS_M2M_HOLD_CAPTURE_BUF);
 	spin_lock_irqsave(&m2m_dev->job_spinlock, flags);
 	schedule_next = _v4l2_m2m_job_finish(m2m_dev, m2m_ctx);
 	spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags);
diff -Nur --no-dereference linux-5.10.52-orig/drivers/mfd/bcm2835-pm.c linux-5.10.52-v7l+/drivers/mfd/bcm2835-pm.c
--- linux-5.10.52-orig/drivers/mfd/bcm2835-pm.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/mfd/bcm2835-pm.c	2021-07-25 16:46:08.398250108 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:53 @
 	if (ret)
 		return ret;
 
+	/* Map the RPiVid ASB regs if present. */
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
+	if (res) {
+		pm->rpivid_asb = devm_ioremap_resource(dev, res);
+		if (IS_ERR(pm->rpivid_asb)) {
+			dev_err(dev, "Failed to map RPiVid ASB: %ld\n",
+				PTR_ERR(pm->rpivid_asb));
+			return PTR_ERR(pm->rpivid_asb);
+		}
+	}
+
 	/* We'll use the presence of the AXI ASB regs in the
 	 * bcm2835-pm binding as the key for whether we can reference
 	 * the full PM register range and support power domains.
diff -Nur --no-dereference linux-5.10.52-orig/drivers/mfd/Kconfig linux-5.10.52-v7l+/drivers/mfd/Kconfig
--- linux-5.10.52-orig/drivers/mfd/Kconfig	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/mfd/Kconfig	2021-07-25 16:46:08.368250611 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:14 @
 	select IRQ_DOMAIN
 	default n
 
+config MFD_RPISENSE_CORE
+	tristate "Raspberry Pi Sense HAT core functions"
+	depends on I2C
+	select MFD_CORE
+	help
+	  This is the core driver for the Raspberry Pi Sense HAT. This provides
+	  the necessary functions to communicate with the hardware.
+
 config MFD_CS5535
 	tristate "AMD CS5535 and CS5536 southbridge core functions"
 	select MFD_CORE
diff -Nur --no-dereference linux-5.10.52-orig/drivers/mfd/Makefile linux-5.10.52-v7l+/drivers/mfd/Makefile
--- linux-5.10.52-orig/drivers/mfd/Makefile	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/mfd/Makefile	2021-07-25 16:46:08.368250611 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:266 @
 obj-$(CONFIG_MFD_ROHM_BD718XX)	+= rohm-bd718x7.o
 obj-$(CONFIG_MFD_STMFX) 	+= stmfx.o
 obj-$(CONFIG_MFD_KHADAS_MCU) 	+= khadas-mcu.o
+obj-$(CONFIG_MFD_RPISENSE_CORE)	+= rpisense-core.o
 
 obj-$(CONFIG_SGI_MFD_IOC3)	+= ioc3.o
 obj-$(CONFIG_MFD_SIMPLE_MFD_I2C)	+= simple-mfd-i2c.o
diff -Nur --no-dereference linux-5.10.52-orig/drivers/mfd/rpisense-core.c linux-5.10.52-v7l+/drivers/mfd/rpisense-core.c
--- linux-5.10.52-orig/drivers/mfd/rpisense-core.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/mfd/rpisense-core.c	2021-07-25 16:46:08.548247593 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/*
+ * Raspberry Pi Sense HAT core driver
+ * http://raspberrypi.org
+ *
+ * Copyright (C) 2015 Raspberry Pi
+ *
+ * Author: Serge Schneider
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ *  This driver is based on wm8350 implementation.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/rpisense/core.h>
+#include <linux/slab.h>
+
+static struct rpisense *rpisense;
+
+static void rpisense_client_dev_register(struct rpisense *rpisense,
+					 const char *name,
+					 struct platform_device **pdev)
+{
+	int ret;
+
+	*pdev = platform_device_alloc(name, -1);
+	if (*pdev == NULL) {
+		dev_err(rpisense->dev, "Failed to allocate %s\n", name);
+		return;
+	}
+
+	(*pdev)->dev.parent = rpisense->dev;
+	platform_set_drvdata(*pdev, rpisense);
+	ret = platform_device_add(*pdev);
+	if (ret != 0) {
+		dev_err(rpisense->dev, "Failed to register %s: %d\n",
+			name, ret);
+		platform_device_put(*pdev);
+		*pdev = NULL;
+	}
+}
+
+static int rpisense_probe(struct i2c_client *i2c,
+			       const struct i2c_device_id *id)
+{
+	int ret;
+	struct rpisense_js *rpisense_js;
+
+	rpisense = devm_kzalloc(&i2c->dev, sizeof(struct rpisense), GFP_KERNEL);
+	if (rpisense == NULL)
+		return -ENOMEM;
+
+	i2c_set_clientdata(i2c, rpisense);
+	rpisense->dev = &i2c->dev;
+	rpisense->i2c_client = i2c;
+
+	ret = rpisense_reg_read(rpisense, RPISENSE_WAI);
+	if (ret > 0) {
+		if (ret != 's')
+			return -EINVAL;
+	} else {
+		return ret;
+	}
+	ret = rpisense_reg_read(rpisense, RPISENSE_VER);
+	if (ret < 0)
+		return ret;
+
+	dev_info(rpisense->dev,
+		 "Raspberry Pi Sense HAT firmware version %i\n", ret);
+
+	rpisense_js = &rpisense->joystick;
+	rpisense_js->keys_desc = devm_gpiod_get(&i2c->dev,
+						"keys-int", GPIOD_IN);
+	if (IS_ERR(rpisense_js->keys_desc)) {
+		dev_warn(&i2c->dev, "Failed to get keys-int descriptor.\n");
+		rpisense_js->keys_desc = gpio_to_desc(23);
+		if (rpisense_js->keys_desc == NULL) {
+			dev_err(&i2c->dev, "GPIO23 fallback failed.\n");
+			return PTR_ERR(rpisense_js->keys_desc);
+		}
+	}
+	rpisense_client_dev_register(rpisense, "rpi-sense-js",
+				     &(rpisense->joystick.pdev));
+	rpisense_client_dev_register(rpisense, "rpi-sense-fb",
+				     &(rpisense->framebuffer.pdev));
+
+	return 0;
+}
+
+static int rpisense_remove(struct i2c_client *i2c)
+{
+	struct rpisense *rpisense = i2c_get_clientdata(i2c);
+
+	platform_device_unregister(rpisense->joystick.pdev);
+	return 0;
+}
+
+struct rpisense *rpisense_get_dev(void)
+{
+	return rpisense;
+}
+EXPORT_SYMBOL_GPL(rpisense_get_dev);
+
+s32 rpisense_reg_read(struct rpisense *rpisense, int reg)
+{
+	int ret = i2c_smbus_read_byte_data(rpisense->i2c_client, reg);
+
+	if (ret < 0)
+		dev_err(rpisense->dev, "Read from reg %d failed\n", reg);
+	/* Due to the BCM270x I2C clock stretching bug, some values
+	 * may have MSB set. Clear it to avoid incorrect values.
+	 * */
+	return ret & 0x7F;
+}
+EXPORT_SYMBOL_GPL(rpisense_reg_read);
+
+int rpisense_block_write(struct rpisense *rpisense, const char *buf, int count)
+{
+	int ret = i2c_master_send(rpisense->i2c_client, buf, count);
+
+	if (ret < 0)
+		dev_err(rpisense->dev, "Block write failed\n");
+	return ret;
+}
+EXPORT_SYMBOL_GPL(rpisense_block_write);
+
+static const struct i2c_device_id rpisense_i2c_id[] = {
+	{ "rpi-sense", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, rpisense_i2c_id);
+
+#ifdef CONFIG_OF
+static const struct of_device_id rpisense_core_id[] = {
+	{ .compatible = "rpi,rpi-sense" },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, rpisense_core_id);
+#endif
+
+
+static struct i2c_driver rpisense_driver = {
+	.driver = {
+		   .name = "rpi-sense",
+		   .owner = THIS_MODULE,
+	},
+	.probe = rpisense_probe,
+	.remove = rpisense_remove,
+	.id_table = rpisense_i2c_id,
+};
+
+module_i2c_driver(rpisense_driver);
+
+MODULE_DESCRIPTION("Raspberry Pi Sense HAT core driver");
+MODULE_AUTHOR("Serge Schneider <serge@raspberrypi.org>");
+MODULE_LICENSE("GPL");
+
diff -Nur --no-dereference linux-5.10.52-orig/drivers/misc/bcm2835_smi.c linux-5.10.52-v7l+/drivers/misc/bcm2835_smi.c
--- linux-5.10.52-orig/drivers/misc/bcm2835_smi.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/misc/bcm2835_smi.c	2021-07-25 16:46:08.688245246 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/**
+ * Broadcom Secondary Memory Interface driver
+ *
+ * Written by Luke Wren <luke@raspberrypi.org>
+ * Copyright (c) 2015, Raspberry Pi (Trading) Ltd.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The names of the above-listed copyright holders may not be used
+ *    to endorse or promote products derived from this software without
+ *    specific prior written permission.
+ *
+ * ALTERNATIVELY, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2, as published by the Free
+ * Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <linux/clk.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/pagemap.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmaengine.h>
+#include <linux/semaphore.h>
+#include <linux/spinlock.h>
+#include <linux/io.h>
+
+#define BCM2835_SMI_IMPLEMENTATION
+#include <linux/broadcom/bcm2835_smi.h>
+
+#define DRIVER_NAME "smi-bcm2835"
+
+#define N_PAGES_FROM_BYTES(n) ((n + PAGE_SIZE-1) / PAGE_SIZE)
+
+#define DMA_WRITE_TO_MEM true
+#define DMA_READ_FROM_MEM false
+
+struct bcm2835_smi_instance {
+	struct device *dev;
+	struct smi_settings settings;
+	__iomem void *smi_regs_ptr;
+	dma_addr_t smi_regs_busaddr;
+
+	struct dma_chan *dma_chan;
+	struct dma_slave_config dma_config;
+
+	struct bcm2835_smi_bounce_info bounce;
+
+	struct scatterlist buffer_sgl;
+
+	struct clk *clk;
+
+	/* Sometimes we are called into in an atomic context (e.g. by
+	   JFFS2 + MTD) so we can't use a mutex */
+	spinlock_t transaction_lock;
+};
+
+/****************************************************************************
+*
+*   SMI peripheral setup
+*
+***************************************************************************/
+
+static inline void write_smi_reg(struct bcm2835_smi_instance *inst,
+	u32 val, unsigned reg)
+{
+	writel(val, inst->smi_regs_ptr + reg);
+}
+
+static inline u32 read_smi_reg(struct bcm2835_smi_instance *inst, unsigned reg)
+{
+	return readl(inst->smi_regs_ptr + reg);
+}
+
+/* Token-paste macro for e.g SMIDSR_RSTROBE ->  value of SMIDSR_RSTROBE_MASK */
+#define _CONCAT(x, y) x##y
+#define CONCAT(x, y) _CONCAT(x, y)
+
+#define SET_BIT_FIELD(dest, field, bits) ((dest) = \
+	((dest) & ~CONCAT(field, _MASK)) | (((bits) << CONCAT(field, _OFFS))& \
+	 CONCAT(field, _MASK)))
+#define GET_BIT_FIELD(src, field) (((src) & \
+	CONCAT(field, _MASK)) >> CONCAT(field, _OFFS))
+
+static void smi_dump_context_labelled(struct bcm2835_smi_instance *inst,
+	const char *label)
+{
+	dev_err(inst->dev, "SMI context dump: %s", label);
+	dev_err(inst->dev, "SMICS:  0x%08x", read_smi_reg(inst, SMICS));
+	dev_err(inst->dev, "SMIL:   0x%08x", read_smi_reg(inst, SMIL));
+	dev_err(inst->dev, "SMIDSR: 0x%08x", read_smi_reg(inst, SMIDSR0));
+	dev_err(inst->dev, "SMIDSW: 0x%08x", read_smi_reg(inst, SMIDSW0));
+	dev_err(inst->dev, "SMIDC:  0x%08x", read_smi_reg(inst, SMIDC));
+	dev_err(inst->dev, "SMIFD:  0x%08x", read_smi_reg(inst, SMIFD));
+	dev_err(inst->dev, " ");
+}
+
+static inline void smi_dump_context(struct bcm2835_smi_instance *inst)
+{
+	smi_dump_context_labelled(inst, "");
+}
+
+static void smi_get_default_settings(struct bcm2835_smi_instance *inst)
+{
+	struct smi_settings *settings = &inst->settings;
+
+	settings->data_width = SMI_WIDTH_16BIT;
+	settings->pack_data = true;
+
+	settings->read_setup_time = 1;
+	settings->read_hold_time = 1;
+	settings->read_pace_time = 1;
+	settings->read_strobe_time = 3;
+
+	settings->write_setup_time = settings->read_setup_time;
+	settings->write_hold_time = settings->read_hold_time;
+	settings->write_pace_time = settings->read_pace_time;
+	settings->write_strobe_time = settings->read_strobe_time;
+
+	settings->dma_enable = true;
+	settings->dma_passthrough_enable = false;
+	settings->dma_read_thresh = 0x01;
+	settings->dma_write_thresh = 0x3f;
+	settings->dma_panic_read_thresh = 0x20;
+	settings->dma_panic_write_thresh = 0x20;
+}
+
+void bcm2835_smi_set_regs_from_settings(struct bcm2835_smi_instance *inst)
+{
+	struct smi_settings *settings = &inst->settings;
+	int smidsr_temp = 0, smidsw_temp = 0, smics_temp,
+	    smidcs_temp, smidc_temp = 0;
+
+	spin_lock(&inst->transaction_lock);
+
+	/* temporarily disable the peripheral: */
+	smics_temp = read_smi_reg(inst, SMICS);
+	write_smi_reg(inst, 0, SMICS);
+	smidcs_temp = read_smi_reg(inst, SMIDCS);
+	write_smi_reg(inst, 0, SMIDCS);
+
+	if (settings->pack_data)
+		smics_temp |= SMICS_PXLDAT;
+	else
+		smics_temp &= ~SMICS_PXLDAT;
+
+	SET_BIT_FIELD(smidsr_temp, SMIDSR_RWIDTH, settings->data_width);
+	SET_BIT_FIELD(smidsr_temp, SMIDSR_RSETUP, settings->read_setup_time);
+	SET_BIT_FIELD(smidsr_temp, SMIDSR_RHOLD, settings->read_hold_time);
+	SET_BIT_FIELD(smidsr_temp, SMIDSR_RPACE, settings->read_pace_time);
+	SET_BIT_FIELD(smidsr_temp, SMIDSR_RSTROBE, settings->read_strobe_time);
+	write_smi_reg(inst, smidsr_temp, SMIDSR0);
+
+	SET_BIT_FIELD(smidsw_temp, SMIDSW_WWIDTH, settings->data_width);
+	if (settings->data_width == SMI_WIDTH_8BIT)
+		smidsw_temp |= SMIDSW_WSWAP;
+	else
+		smidsw_temp &= ~SMIDSW_WSWAP;
+	SET_BIT_FIELD(smidsw_temp, SMIDSW_WSETUP, settings->write_setup_time);
+	SET_BIT_FIELD(smidsw_temp, SMIDSW_WHOLD, settings->write_hold_time);
+	SET_BIT_FIELD(smidsw_temp, SMIDSW_WPACE, settings->write_pace_time);
+	SET_BIT_FIELD(smidsw_temp, SMIDSW_WSTROBE,
+			settings->write_strobe_time);
+	write_smi_reg(inst, smidsw_temp, SMIDSW0);
+
+	SET_BIT_FIELD(smidc_temp, SMIDC_REQR, settings->dma_read_thresh);
+	SET_BIT_FIELD(smidc_temp, SMIDC_REQW, settings->dma_write_thresh);
+	SET_BIT_FIELD(smidc_temp, SMIDC_PANICR,
+		      settings->dma_panic_read_thresh);
+	SET_BIT_FIELD(smidc_temp, SMIDC_PANICW,
+		      settings->dma_panic_write_thresh);
+	if (settings->dma_passthrough_enable) {
+		smidc_temp |= SMIDC_DMAP;
+		smidsr_temp |= SMIDSR_RDREQ;
+		write_smi_reg(inst, smidsr_temp, SMIDSR0);
+		smidsw_temp |= SMIDSW_WDREQ;
+		write_smi_reg(inst, smidsw_temp, SMIDSW0);
+	} else
+		smidc_temp &= ~SMIDC_DMAP;
+	if (settings->dma_enable)
+		smidc_temp |= SMIDC_DMAEN;
+	else
+		smidc_temp &= ~SMIDC_DMAEN;
+
+	write_smi_reg(inst, smidc_temp, SMIDC);
+
+	/* re-enable (if was previously enabled) */
+	write_smi_reg(inst, smics_temp, SMICS);
+	write_smi_reg(inst, smidcs_temp, SMIDCS);
+
+	spin_unlock(&inst->transaction_lock);
+}
+EXPORT_SYMBOL(bcm2835_smi_set_regs_from_settings);
+
+struct smi_settings *bcm2835_smi_get_settings_from_regs
+	(struct bcm2835_smi_instance *inst)
+{
+	struct smi_settings *settings = &inst->settings;
+	int smidsr, smidsw, smidc;
+
+	spin_lock(&inst->transaction_lock);
+
+	smidsr = read_smi_reg(inst, SMIDSR0);
+	smidsw = read_smi_reg(inst, SMIDSW0);
+	smidc = read_smi_reg(inst, SMIDC);
+
+	settings->pack_data = (read_smi_reg(inst, SMICS) & SMICS_PXLDAT) ?
+	    true : false;
+
+	settings->data_width = GET_BIT_FIELD(smidsr, SMIDSR_RWIDTH);
+	settings->read_setup_time = GET_BIT_FIELD(smidsr, SMIDSR_RSETUP);
+	settings->read_hold_time = GET_BIT_FIELD(smidsr, SMIDSR_RHOLD);
+	settings->read_pace_time = GET_BIT_FIELD(smidsr, SMIDSR_RPACE);
+	settings->read_strobe_time = GET_BIT_FIELD(smidsr, SMIDSR_RSTROBE);
+
+	settings->write_setup_time = GET_BIT_FIELD(smidsw, SMIDSW_WSETUP);
+	settings->write_hold_time = GET_BIT_FIELD(smidsw, SMIDSW_WHOLD);
+	settings->write_pace_time = GET_BIT_FIELD(smidsw, SMIDSW_WPACE);
+	settings->write_strobe_time = GET_BIT_FIELD(smidsw, SMIDSW_WSTROBE);
+
+	settings->dma_read_thresh = GET_BIT_FIELD(smidc, SMIDC_REQR);
+	settings->dma_write_thresh = GET_BIT_FIELD(smidc, SMIDC_REQW);
+	settings->dma_panic_read_thresh = GET_BIT_FIELD(smidc, SMIDC_PANICR);
+	settings->dma_panic_write_thresh = GET_BIT_FIELD(smidc, SMIDC_PANICW);
+	settings->dma_passthrough_enable = (smidc & SMIDC_DMAP) ? true : false;
+	settings->dma_enable = (smidc & SMIDC_DMAEN) ? true : false;
+
+	spin_unlock(&inst->transaction_lock);
+
+	return settings;
+}
+EXPORT_SYMBOL(bcm2835_smi_get_settings_from_regs);
+
+static inline void smi_set_address(struct bcm2835_smi_instance *inst,
+	unsigned int address)
+{
+	int smia_temp = 0, smida_temp = 0;
+
+	SET_BIT_FIELD(smia_temp, SMIA_ADDR, address);
+	SET_BIT_FIELD(smida_temp, SMIDA_ADDR, address);
+
+	/* Write to both address registers - user doesn't care whether we're
+	   doing programmed or direct transfers. */
+	write_smi_reg(inst, smia_temp, SMIA);
+	write_smi_reg(inst, smida_temp, SMIDA);
+}
+
+static void smi_setup_regs(struct bcm2835_smi_instance *inst)
+{
+
+	dev_dbg(inst->dev, "Initialising SMI registers...");
+	/* Disable the peripheral if already enabled */
+	write_smi_reg(inst, 0, SMICS);
+	write_smi_reg(inst, 0, SMIDCS);
+
+	smi_get_default_settings(inst);
+	bcm2835_smi_set_regs_from_settings(inst);
+	smi_set_address(inst, 0);
+
+	write_smi_reg(inst, read_smi_reg(inst, SMICS) | SMICS_ENABLE, SMICS);
+	write_smi_reg(inst, read_smi_reg(inst, SMIDCS) | SMIDCS_ENABLE,
+		SMIDCS);
+}
+
+/****************************************************************************
+*
+*   Low-level SMI access functions
+*   Other modules should use the exported higher-level functions e.g.
+*   bcm2835_smi_write_buf() unless they have a good reason to use these
+*
+***************************************************************************/
+
+static inline uint32_t smi_read_single_word(struct bcm2835_smi_instance *inst)
+{
+	int timeout = 0;
+
+	write_smi_reg(inst, SMIDCS_ENABLE, SMIDCS);
+	write_smi_reg(inst, SMIDCS_ENABLE | SMIDCS_START, SMIDCS);
+	/* Make sure things happen in the right order...*/
+	mb();
+	while (!(read_smi_reg(inst, SMIDCS) & SMIDCS_DONE) &&
+		++timeout < 10000)
+		;
+	if (timeout < 10000)
+		return read_smi_reg(inst, SMIDD);
+
+	dev_err(inst->dev,
+		"SMI direct read timed out (is the clock set up correctly?)");
+	return 0;
+}
+
+static inline void smi_write_single_word(struct bcm2835_smi_instance *inst,
+	uint32_t data)
+{
+	int timeout = 0;
+
+	write_smi_reg(inst, SMIDCS_ENABLE | SMIDCS_WRITE, SMIDCS);
+	write_smi_reg(inst, data, SMIDD);
+	write_smi_reg(inst, SMIDCS_ENABLE | SMIDCS_WRITE | SMIDCS_START,
+		SMIDCS);
+
+	while (!(read_smi_reg(inst, SMIDCS) & SMIDCS_DONE) &&
+		++timeout < 10000)
+		;
+	if (timeout >= 10000)
+		dev_err(inst->dev,
+		"SMI direct write timed out (is the clock set up correctly?)");
+}
+
+/* Initiates a programmed read into the read FIFO. It is up to the caller to
+ * read data from the FIFO -  either via paced DMA transfer,
+ * or polling SMICS_RXD to check whether data is available.
+ * SMICS_ACTIVE will go low upon completion. */
+static void smi_init_programmed_read(struct bcm2835_smi_instance *inst,
+	int num_transfers)
+{
+	int smics_temp;
+
+	/* Disable the peripheral: */
+	smics_temp = read_smi_reg(inst, SMICS) & ~(SMICS_ENABLE | SMICS_WRITE);
+	write_smi_reg(inst, smics_temp, SMICS);
+	while (read_smi_reg(inst, SMICS) & SMICS_ENABLE)
+		;
+
+	/* Program the transfer count: */
+	write_smi_reg(inst, num_transfers, SMIL);
+
+	/* re-enable and start: */
+	smics_temp |= SMICS_ENABLE;
+	write_smi_reg(inst, smics_temp, SMICS);
+	smics_temp |= SMICS_CLEAR;
+	/* Just to be certain: */
+	mb();
+	while (read_smi_reg(inst, SMICS) & SMICS_ACTIVE)
+		;
+	write_smi_reg(inst, smics_temp, SMICS);
+	smics_temp |= SMICS_START;
+	write_smi_reg(inst, smics_temp, SMICS);
+}
+
+/* Initiates a programmed write sequence, using data from the write FIFO.
+ * It is up to the caller to initiate a DMA transfer before calling,
+ * or use another method to keep the write FIFO topped up.
+ * SMICS_ACTIVE will go low upon completion.
+ */
+static void smi_init_programmed_write(struct bcm2835_smi_instance *inst,
+	int num_transfers)
+{
+	int smics_temp;
+
+	/* Disable the peripheral: */
+	smics_temp = read_smi_reg(inst, SMICS) & ~SMICS_ENABLE;
+	write_smi_reg(inst, smics_temp, SMICS);
+	while (read_smi_reg(inst, SMICS) & SMICS_ENABLE)
+		;
+
+	/* Program the transfer count: */
+	write_smi_reg(inst, num_transfers, SMIL);
+
+	/* setup, re-enable and start: */
+	smics_temp |= SMICS_WRITE | SMICS_ENABLE;
+	write_smi_reg(inst, smics_temp, SMICS);
+	smics_temp |= SMICS_START;
+	write_smi_reg(inst, smics_temp, SMICS);
+}
+
+/* Initiate a read and then poll FIFO for data, reading out as it appears. */
+static void smi_read_fifo(struct bcm2835_smi_instance *inst,
+	uint32_t *dest, int n_bytes)
+{
+	if (read_smi_reg(inst, SMICS) & SMICS_RXD) {
+		smi_dump_context_labelled(inst,
+			"WARNING: read FIFO not empty at start of read call.");
+		while (read_smi_reg(inst, SMICS))
+			;
+	}
+
+	/* Dispatch the read: */
+	if (inst->settings.data_width == SMI_WIDTH_8BIT)
+		smi_init_programmed_read(inst, n_bytes);
+	else if (inst->settings.data_width == SMI_WIDTH_16BIT)
+		smi_init_programmed_read(inst, n_bytes / 2);
+	else {
+		dev_err(inst->dev, "Unsupported data width for read.");
+		return;
+	}
+
+	/* Poll FIFO to keep it empty */
+	while (!(read_smi_reg(inst, SMICS) & SMICS_DONE))
+		if (read_smi_reg(inst, SMICS) & SMICS_RXD)
+			*dest++ = read_smi_reg(inst, SMID);
+
+	/* Ensure that the FIFO is emptied */
+	if (read_smi_reg(inst, SMICS) & SMICS_RXD) {
+		int fifo_count;
+
+		fifo_count = GET_BIT_FIELD(read_smi_reg(inst, SMIFD),
+			SMIFD_FCNT);
+		while (fifo_count--)
+			*dest++ = read_smi_reg(inst, SMID);
+	}
+
+	if (!(read_smi_reg(inst, SMICS) & SMICS_DONE))
+		smi_dump_context_labelled(inst,
+			"WARNING: transaction finished but done bit not set.");
+
+	if (read_smi_reg(inst, SMICS) & SMICS_RXD)
+		smi_dump_context_labelled(inst,
+			"WARNING: read FIFO not empty at end of read call.");
+
+}
+
+/* Initiate a write, and then keep the FIFO topped up. */
+static void smi_write_fifo(struct bcm2835_smi_instance *inst,
+	uint32_t *src, int n_bytes)
+{
+	int i, timeout = 0;
+
+	/* Empty FIFOs if not already so */
+	if (!(read_smi_reg(inst, SMICS) & SMICS_TXE)) {
+		smi_dump_context_labelled(inst,
+		    "WARNING: write fifo not empty at start of write call.");
+		write_smi_reg(inst, read_smi_reg(inst, SMICS) | SMICS_CLEAR,
+			SMICS);
+	}
+
+	/* Initiate the transfer */
+	if (inst->settings.data_width == SMI_WIDTH_8BIT)
+		smi_init_programmed_write(inst, n_bytes);
+	else if (inst->settings.data_width == SMI_WIDTH_16BIT)
+		smi_init_programmed_write(inst, n_bytes / 2);
+	else {
+		dev_err(inst->dev, "Unsupported data width for write.");
+		return;
+	}
+	/* Fill the FIFO: */
+	for (i = 0; i < (n_bytes - 1) / 4 + 1; ++i) {
+		while (!(read_smi_reg(inst, SMICS) & SMICS_TXD))
+			;
+		write_smi_reg(inst, *src++, SMID);
+	}
+	/* Busy wait... */
+	while (!(read_smi_reg(inst, SMICS) & SMICS_DONE) && ++timeout <
+		1000000)
+		;
+	if (timeout >= 1000000)
+		smi_dump_context_labelled(inst,
+			"Timed out on write operation!");
+	if (!(read_smi_reg(inst, SMICS) & SMICS_TXE))
+		smi_dump_context_labelled(inst,
+			"WARNING: FIFO not empty at end of write operation.");
+}
+
+/****************************************************************************
+*
+*   SMI DMA operations
+*
+***************************************************************************/
+
+/* Disable SMI and put it into the correct direction before doing DMA setup.
+   Stops spurious DREQs during setup. Peripheral is re-enabled by init_*() */
+static void smi_disable(struct bcm2835_smi_instance *inst,
+	enum dma_transfer_direction direction)
+{
+	int smics_temp = read_smi_reg(inst, SMICS) & ~SMICS_ENABLE;
+
+	if (direction == DMA_DEV_TO_MEM)
+		smics_temp &= ~SMICS_WRITE;
+	else
+		smics_temp |= SMICS_WRITE;
+	write_smi_reg(inst, smics_temp, SMICS);
+	while (read_smi_reg(inst, SMICS) & SMICS_ACTIVE)
+		;
+}
+
+static struct scatterlist *smi_scatterlist_from_buffer(
+	struct bcm2835_smi_instance *inst,
+	dma_addr_t buf,
+	size_t len,
+	struct scatterlist *sg)
+{
+	sg_init_table(sg, 1);
+	sg_dma_address(sg) = buf;
+	sg_dma_len(sg) = len;
+	return sg;
+}
+
+static void smi_dma_callback_user_copy(void *param)
+{
+	/* Notify the bottom half that a chunk is ready for user copy */
+	struct bcm2835_smi_instance *inst =
+		(struct bcm2835_smi_instance *)param;
+
+	up(&inst->bounce.callback_sem);
+}
+
+/* Creates a descriptor, assigns the given callback, and submits the
+   descriptor to dmaengine. Does not block - can queue up multiple
+   descriptors and then wait for them all to complete.
+   sg_len is the number of control blocks, NOT the number of bytes.
+   dir can be DMA_MEM_TO_DEV or DMA_DEV_TO_MEM.
+   callback can be NULL - in this case it is not called. */
+static inline struct dma_async_tx_descriptor *smi_dma_submit_sgl(
+	struct bcm2835_smi_instance *inst,
+	struct scatterlist *sgl,
+	size_t sg_len,
+	enum dma_transfer_direction dir,
+	dma_async_tx_callback callback)
+{
+	struct dma_async_tx_descriptor *desc;
+
+	desc = dmaengine_prep_slave_sg(inst->dma_chan,
+				       sgl,
+				       sg_len,
+				       dir,
+				       DMA_PREP_INTERRUPT | DMA_CTRL_ACK |
+				       DMA_PREP_FENCE);
+	if (!desc) {
+		dev_err(inst->dev, "read_sgl: dma slave preparation failed!");
+		write_smi_reg(inst, read_smi_reg(inst, SMICS) & ~SMICS_ACTIVE,
+			SMICS);
+		while (read_smi_reg(inst, SMICS) & SMICS_ACTIVE)
+			cpu_relax();
+		write_smi_reg(inst, read_smi_reg(inst, SMICS) | SMICS_ACTIVE,
+			SMICS);
+		return NULL;
+	}
+	desc->callback = callback;
+	desc->callback_param = inst;
+	if (dmaengine_submit(desc) < 0)
+		return NULL;
+	return desc;
+}
+
+/* NB this function blocks until the transfer is complete */
+static void
+smi_dma_read_sgl(struct bcm2835_smi_instance *inst,
+	struct scatterlist *sgl, size_t sg_len, size_t n_bytes)
+{
+	struct dma_async_tx_descriptor *desc;
+
+	/* Disable SMI and set to read before dispatching DMA - if SMI is in
+	 * write mode and TX fifo is empty, it will generate a DREQ which may
+	 * cause the read DMA to complete before the SMI read command is even
+	 * dispatched! We want to dispatch DMA before SMI read so that reading
+	 * is gapless, for logic analyser.
+	 */
+
+	smi_disable(inst, DMA_DEV_TO_MEM);
+
+	desc = smi_dma_submit_sgl(inst, sgl, sg_len, DMA_DEV_TO_MEM, NULL);
+	dma_async_issue_pending(inst->dma_chan);
+
+	if (inst->settings.data_width == SMI_WIDTH_8BIT)
+		smi_init_programmed_read(inst, n_bytes);
+	else
+		smi_init_programmed_read(inst, n_bytes / 2);
+
+	if (dma_wait_for_async_tx(desc) == DMA_ERROR)
+		smi_dump_context_labelled(inst, "DMA timeout!");
+}
+
+static void
+smi_dma_write_sgl(struct bcm2835_smi_instance *inst,
+	struct scatterlist *sgl, size_t sg_len, size_t n_bytes)
+{
+	struct dma_async_tx_descriptor *desc;
+
+	if (inst->settings.data_width == SMI_WIDTH_8BIT)
+		smi_init_programmed_write(inst, n_bytes);
+	else
+		smi_init_programmed_write(inst, n_bytes / 2);
+
+	desc = smi_dma_submit_sgl(inst, sgl, sg_len, DMA_MEM_TO_DEV, NULL);
+	dma_async_issue_pending(inst->dma_chan);
+
+	if (dma_wait_for_async_tx(desc) == DMA_ERROR)
+		smi_dump_context_labelled(inst, "DMA timeout!");
+	else
+		/* Wait for SMI to finish our writes */
+		while (!(read_smi_reg(inst, SMICS) & SMICS_DONE))
+			cpu_relax();
+}
+
+ssize_t bcm2835_smi_user_dma(
+	struct bcm2835_smi_instance *inst,
+	enum dma_transfer_direction dma_dir,
+	char __user *user_ptr, size_t count,
+	struct bcm2835_smi_bounce_info **bounce)
+{
+	int chunk_no = 0, chunk_size, count_left = count;
+	struct scatterlist *sgl;
+	void (*init_trans_func)(struct bcm2835_smi_instance *, int);
+
+	spin_lock(&inst->transaction_lock);
+
+	if (dma_dir == DMA_DEV_TO_MEM)
+		init_trans_func = smi_init_programmed_read;
+	else
+		init_trans_func = smi_init_programmed_write;
+
+	smi_disable(inst, dma_dir);
+
+	sema_init(&inst->bounce.callback_sem, 0);
+	if (bounce)
+		*bounce = &inst->bounce;
+	while (count_left) {
+		chunk_size = count_left > DMA_BOUNCE_BUFFER_SIZE ?
+			DMA_BOUNCE_BUFFER_SIZE : count_left;
+		if (chunk_size == DMA_BOUNCE_BUFFER_SIZE) {
+			sgl =
+			&inst->bounce.sgl[chunk_no % DMA_BOUNCE_BUFFER_COUNT];
+		} else {
+			sgl = smi_scatterlist_from_buffer(
+				inst,
+				inst->bounce.phys[
+					chunk_no % DMA_BOUNCE_BUFFER_COUNT],
+				chunk_size,
+				&inst->buffer_sgl);
+		}
+
+		if (!smi_dma_submit_sgl(inst, sgl, 1, dma_dir,
+			smi_dma_callback_user_copy
+		)) {
+			dev_err(inst->dev, "sgl submit failed");
+			count = 0;
+			goto out;
+		}
+		count_left -= chunk_size;
+		chunk_no++;
+	}
+	dma_async_issue_pending(inst->dma_chan);
+
+	if (inst->settings.data_width == SMI_WIDTH_8BIT)
+		init_trans_func(inst, count);
+	else if (inst->settings.data_width == SMI_WIDTH_16BIT)
+		init_trans_func(inst, count / 2);
+out:
+	spin_unlock(&inst->transaction_lock);
+	return count;
+}
+EXPORT_SYMBOL(bcm2835_smi_user_dma);
+
+
+/****************************************************************************
+*
+*   High level buffer transfer functions - for use by other drivers
+*
+***************************************************************************/
+
+/* Buffer must be physically contiguous - i.e. kmalloc, not vmalloc! */
+void bcm2835_smi_write_buf(
+	struct bcm2835_smi_instance *inst,
+	const void *buf, size_t n_bytes)
+{
+	int odd_bytes = n_bytes & 0x3;
+
+	n_bytes -= odd_bytes;
+
+	spin_lock(&inst->transaction_lock);
+
+	if (n_bytes > DMA_THRESHOLD_BYTES) {
+		dma_addr_t phy_addr = dma_map_single(
+			inst->dev,
+			(void *)buf,
+			n_bytes,
+			DMA_MEM_TO_DEV);
+		struct scatterlist *sgl =
+			smi_scatterlist_from_buffer(inst, phy_addr, n_bytes,
+				&inst->buffer_sgl);
+
+		if (!sgl) {
+			smi_dump_context_labelled(inst,
+			"Error: could not create scatterlist for write!");
+			goto out;
+		}
+		smi_dma_write_sgl(inst, sgl, 1, n_bytes);
+
+		dma_unmap_single
+			(inst->dev, phy_addr, n_bytes, DMA_MEM_TO_DEV);
+	} else if (n_bytes) {
+		smi_write_fifo(inst, (uint32_t *) buf, n_bytes);
+	}
+	buf += n_bytes;
+
+	if (inst->settings.data_width == SMI_WIDTH_8BIT) {
+		while (odd_bytes--)
+			smi_write_single_word(inst, *(uint8_t *) (buf++));
+	} else {
+		while (odd_bytes >= 2) {
+			smi_write_single_word(inst, *(uint16_t *)buf);
+			buf += 2;
+			odd_bytes -= 2;
+		}
+		if (odd_bytes) {
+			/* Reading an odd number of bytes on a 16 bit bus is
+			   a user bug. It's kinder to fail early and tell them
+			   than to e.g. transparently give them the bottom byte
+			   of a 16 bit transfer. */
+			dev_err(inst->dev,
+		"WARNING: odd number of bytes specified for wide transfer.");
+			dev_err(inst->dev,
+		"At least one byte dropped as a result.");
+			dump_stack();
+		}
+	}
+out:
+	spin_unlock(&inst->transaction_lock);
+}
+EXPORT_SYMBOL(bcm2835_smi_write_buf);
+
+void bcm2835_smi_read_buf(struct bcm2835_smi_instance *inst,
+	void *buf, size_t n_bytes)
+{
+
+	/* SMI is inherently 32-bit, which causes surprising amounts of mess
+	   for bytes % 4 != 0. Easiest to avoid this mess altogether
+	   by handling remainder separately. */
+	int odd_bytes = n_bytes & 0x3;
+
+	spin_lock(&inst->transaction_lock);
+	n_bytes -= odd_bytes;
+	if (n_bytes > DMA_THRESHOLD_BYTES) {
+		dma_addr_t phy_addr = dma_map_single(inst->dev,
+						     buf, n_bytes,
+						     DMA_DEV_TO_MEM);
+		struct scatterlist *sgl = smi_scatterlist_from_buffer(
+			inst, phy_addr, n_bytes,
+			&inst->buffer_sgl);
+		if (!sgl) {
+			smi_dump_context_labelled(inst,
+			"Error: could not create scatterlist for read!");
+			goto out;
+		}
+		smi_dma_read_sgl(inst, sgl, 1, n_bytes);
+		dma_unmap_single(inst->dev, phy_addr, n_bytes, DMA_DEV_TO_MEM);
+	} else if (n_bytes) {
+		smi_read_fifo(inst, (uint32_t *)buf, n_bytes);
+	}
+	buf += n_bytes;
+
+	if (inst->settings.data_width == SMI_WIDTH_8BIT) {
+		while (odd_bytes--)
+			*((uint8_t *) (buf++)) = smi_read_single_word(inst);
+	} else {
+		while (odd_bytes >= 2) {
+			*(uint16_t *) buf = smi_read_single_word(inst);
+			buf += 2;
+			odd_bytes -= 2;
+		}
+		if (odd_bytes) {
+			dev_err(inst->dev,
+		"WARNING: odd number of bytes specified for wide transfer.");
+			dev_err(inst->dev,
+		"At least one byte dropped as a result.");
+			dump_stack();
+		}
+	}
+out:
+	spin_unlock(&inst->transaction_lock);
+}
+EXPORT_SYMBOL(bcm2835_smi_read_buf);
+
+void bcm2835_smi_set_address(struct bcm2835_smi_instance *inst,
+	unsigned int address)
+{
+	spin_lock(&inst->transaction_lock);
+	smi_set_address(inst, address);
+	spin_unlock(&inst->transaction_lock);
+}
+EXPORT_SYMBOL(bcm2835_smi_set_address);
+
+struct bcm2835_smi_instance *bcm2835_smi_get(struct device_node *node)
+{
+	struct platform_device *pdev;
+
+	if (!node)
+		return NULL;
+
+	pdev = of_find_device_by_node(node);
+	if (!pdev)
+		return NULL;
+
+	return platform_get_drvdata(pdev);
+}
+EXPORT_SYMBOL(bcm2835_smi_get);
+
+/****************************************************************************
+*
+*   bcm2835_smi_probe - called when the driver is loaded.
+*
+***************************************************************************/
+
+static int bcm2835_smi_dma_setup(struct bcm2835_smi_instance *inst)
+{
+	int i, rv = 0;
+
+	inst->dma_chan = dma_request_slave_channel(inst->dev, "rx-tx");
+
+	inst->dma_config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+	inst->dma_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+	inst->dma_config.src_addr = inst->smi_regs_busaddr + SMID;
+	inst->dma_config.dst_addr = inst->dma_config.src_addr;
+	/* Direction unimportant - always overridden by prep_slave_sg */
+	inst->dma_config.direction = DMA_DEV_TO_MEM;
+	dmaengine_slave_config(inst->dma_chan, &inst->dma_config);
+	/* Alloc and map bounce buffers */
+	for (i = 0; i < DMA_BOUNCE_BUFFER_COUNT; ++i) {
+		inst->bounce.buffer[i] =
+		dmam_alloc_coherent(inst->dev, DMA_BOUNCE_BUFFER_SIZE,
+				&inst->bounce.phys[i],
+				GFP_KERNEL);
+		if (!inst->bounce.buffer[i]) {
+			dev_err(inst->dev, "Could not allocate buffer!");
+			rv = -ENOMEM;
+			break;
+		}
+		smi_scatterlist_from_buffer(
+			inst,
+			inst->bounce.phys[i],
+			DMA_BOUNCE_BUFFER_SIZE,
+			&inst->bounce.sgl[i]
+		);
+	}
+
+	return rv;
+}
+
+static int bcm2835_smi_probe(struct platform_device *pdev)
+{
+	int err;
+	struct device *dev = &pdev->dev;
+	struct device_node *node = dev->of_node;
+	struct resource *ioresource;
+	struct bcm2835_smi_instance *inst;
+	const __be32 *addr;
+
+	/* We require device tree support */
+	if (!node)
+		return -EINVAL;
+	/* Allocate buffers and instance data */
+	inst = devm_kzalloc(dev, sizeof(struct bcm2835_smi_instance),
+		GFP_KERNEL);
+	if (!inst)
+		return -ENOMEM;
+
+	inst->dev = dev;
+	spin_lock_init(&inst->transaction_lock);
+
+	ioresource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	inst->smi_regs_ptr = devm_ioremap_resource(dev, ioresource);
+	if (IS_ERR(inst->smi_regs_ptr)) {
+		err = PTR_ERR(inst->smi_regs_ptr);
+		goto err;
+	}
+	addr = of_get_address(node, 0, NULL, NULL);
+	inst->smi_regs_busaddr = be32_to_cpu(*addr);
+
+	err = bcm2835_smi_dma_setup(inst);
+	if (err)
+		goto err;
+
+	/* request clock */
+	inst->clk = devm_clk_get(dev, NULL);
+	if (!inst->clk)
+		goto err;
+	clk_prepare_enable(inst->clk);
+
+	/* Finally, do peripheral setup */
+	smi_setup_regs(inst);
+
+	platform_set_drvdata(pdev, inst);
+
+	dev_info(inst->dev, "initialised");
+
+	return 0;
+err:
+	kfree(inst);
+	return err;
+}
+
+/****************************************************************************
+*
+*   bcm2835_smi_remove - called when the driver is unloaded.
+*
+***************************************************************************/
+
+static int bcm2835_smi_remove(struct platform_device *pdev)
+{
+	struct bcm2835_smi_instance *inst = platform_get_drvdata(pdev);
+	struct device *dev = inst->dev;
+
+	dmaengine_terminate_all(inst->dma_chan);
+	dma_release_channel(inst->dma_chan);
+
+	clk_disable_unprepare(inst->clk);
+
+	dev_info(dev, "SMI device removed - OK");
+	return 0;
+}
+
+/****************************************************************************
+*
+*   Register the driver with device tree
+*
+***************************************************************************/
+
+static const struct of_device_id bcm2835_smi_of_match[] = {
+	{.compatible = "brcm,bcm2835-smi",},
+	{ /* sentinel */ },
+};
+
+MODULE_DEVICE_TABLE(of, bcm2835_smi_of_match);
+
+static struct platform_driver bcm2835_smi_driver = {
+	.probe = bcm2835_smi_probe,
+	.remove = bcm2835_smi_remove,
+	.driver = {
+		   .name = DRIVER_NAME,
+		   .owner = THIS_MODULE,
+		   .of_match_table = bcm2835_smi_of_match,
+		   },
+};
+
+module_platform_driver(bcm2835_smi_driver);
+
+MODULE_ALIAS("platform:smi-bcm2835");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Device driver for BCM2835's secondary memory interface");
+MODULE_AUTHOR("Luke Wren <luke@raspberrypi.org>");
diff -Nur --no-dereference linux-5.10.52-orig/drivers/misc/Kconfig linux-5.10.52-v7l+/drivers/misc/Kconfig
--- linux-5.10.52-orig/drivers/misc/Kconfig	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/misc/Kconfig	2021-07-25 16:46:08.668245581 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:12 @
 	tristate
 	depends on INPUT
 
+config BCM2835_SMI
+	tristate "Broadcom 283x Secondary Memory Interface driver"
+	depends on ARCH_BCM2835
+	default m
+	help
+		Driver for enabling and using Broadcom's Secondary/Slow Memory Interface.
+		Appears as /dev/bcm2835_smi. For ioctl interface see drivers/misc/bcm2835_smi.h
+
 config AD525X_DPOT
 	tristate "Analog Devices Digital Potentiometers"
 	depends on (I2C || SPI) && SYSFS
diff -Nur --no-dereference linux-5.10.52-orig/drivers/misc/Makefile linux-5.10.52-v7l+/drivers/misc/Makefile
--- linux-5.10.52-orig/drivers/misc/Makefile	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/misc/Makefile	2021-07-25 16:46:08.668245581 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:14 @
 obj-$(CONFIG_INTEL_MID_PTI)	+= pti.o
 obj-$(CONFIG_ATMEL_SSC)		+= atmel-ssc.o
 obj-$(CONFIG_ATMEL_TCLIB)	+= atmel_tclib.o
+obj-$(CONFIG_BCM2835_SMI)	+= bcm2835_smi.o
 obj-$(CONFIG_DUMMY_IRQ)		+= dummy-irq.o
 obj-$(CONFIG_ICS932S401)	+= ics932s401.o
 obj-$(CONFIG_LKDTM)		+= lkdtm/
diff -Nur --no-dereference linux-5.10.52-orig/drivers/mmc/core/block.c linux-5.10.52-v7l+/drivers/mmc/core/block.c
--- linux-5.10.52-orig/drivers/mmc/core/block.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/mmc/core/block.c	2021-07-25 16:46:09.438232672 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:168 @
 module_param(perdev_minors, int, 0444);
 MODULE_PARM_DESC(perdev_minors, "Minors numbers to allocate per device");
 
+/*
+ * Allow quirks to be overridden for the current card
+ */
+static char *card_quirks;
+module_param(card_quirks, charp, 0644);
+MODULE_PARM_DESC(card_quirks, "Force the use of the indicated quirks (a bitfield)");
+
 static inline int mmc_blk_part_switch(struct mmc_card *card,
 				      unsigned int part_type);
 static void mmc_blk_rw_rq_prep(struct mmc_queue_req *mqrq,
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:2908 @
 {
 	struct mmc_blk_data *md, *part_md;
 	char cap_str[10];
+	char quirk_str[24];
 
 	/*
 	 * Check that the card supports the command class(es) we need.
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:2916 @
 	if (!(card->csd.cmdclass & CCC_BLOCK_READ))
 		return -ENODEV;
 
-	mmc_fixup_device(card, mmc_blk_fixups);
+	if (card_quirks) {
+		unsigned long quirks;
+		if (kstrtoul(card_quirks, 0, &quirks) == 0)
+			card->quirks = (unsigned int)quirks;
+		else
+			pr_err("mmc_block: Invalid card_quirks parameter '%s'\n",
+			       card_quirks);
+	}
+	else
+		mmc_fixup_device(card, mmc_blk_fixups);
 
 	card->complete_wq = alloc_workqueue("mmc_complete",
 					WQ_MEM_RECLAIM | WQ_HIGHPRI, 0);
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:2940 @
 
 	string_get_size((u64)get_capacity(md->disk), 512, STRING_UNITS_2,
 			cap_str, sizeof(cap_str));
-	pr_info("%s: %s %s %s %s\n",
+	if (card->quirks)
+		snprintf(quirk_str, sizeof(quirk_str),
+			 " (quirks 0x%08x)", card->quirks);
+	else
+		quirk_str[0] = '\0';
+	pr_info("%s: %s %s %s%s%s\n",
 		md->disk->disk_name, mmc_card_id(card), mmc_card_name(card),
-		cap_str, md->read_only ? "(ro)" : "");
+		cap_str, md->read_only ? " (ro)" : "", quirk_str);
 
 	if (mmc_blk_alloc_parts(card, md))
 		goto out;
diff -Nur --no-dereference linux-5.10.52-orig/drivers/mmc/core/core.c linux-5.10.52-v7l+/drivers/mmc/core/core.c
--- linux-5.10.52-orig/drivers/mmc/core/core.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/mmc/core/core.c	2021-07-25 16:46:09.458232337 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1877 @
 
 int mmc_can_erase(struct mmc_card *card)
 {
-	if (card->csd.cmdclass & CCC_ERASE && card->erase_size)
+	if (card->csd.cmdclass & CCC_ERASE && card->erase_size &&
+	    !(card->quirks & MMC_QUIRK_ERASE_BROKEN))
 		return 1;
 	return 0;
 }
diff -Nur --no-dereference linux-5.10.52-orig/drivers/mmc/core/quirks.h linux-5.10.52-v7l+/drivers/mmc/core/quirks.h
--- linux-5.10.52-orig/drivers/mmc/core/quirks.h	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/mmc/core/quirks.h	2021-07-25 16:46:09.478232001 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:102 @
 	MMC_FIXUP("V10016", CID_MANFID_KINGSTON, CID_OEMID_ANY, add_quirk_mmc,
 		  MMC_QUIRK_TRIM_BROKEN),
 
+	/*
+	 *  On some Kingston SD cards, multiple erases of less than 64
+	 *  sectors can cause corruption.
+	 */
+	MMC_FIXUP("SD16G", 0x41, 0x3432, add_quirk, MMC_QUIRK_ERASE_BROKEN),
+	MMC_FIXUP("SD32G", 0x41, 0x3432, add_quirk, MMC_QUIRK_ERASE_BROKEN),
+	MMC_FIXUP("SD64G", 0x41, 0x3432, add_quirk, MMC_QUIRK_ERASE_BROKEN),
+
 	END_FIXUP
 };
 
diff -Nur --no-dereference linux-5.10.52-orig/drivers/mmc/host/bcm2835-mmc.c linux-5.10.52-v7l+/drivers/mmc/host/bcm2835-mmc.c
--- linux-5.10.52-orig/drivers/mmc/host/bcm2835-mmc.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/mmc/host/bcm2835-mmc.c	2021-07-25 16:46:09.518231331 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/*
+ * BCM2835 MMC host driver.
+ *
+ * Author:      Gellert Weisz <gellert@raspberrypi.org>
+ *              Copyright 2014
+ *
+ * Based on
+ *  sdhci-bcm2708.c by Broadcom
+ *  sdhci-bcm2835.c by Stephen Warren and Oleksandr Tymoshenko
+ *  sdhci.c and sdhci-pci.c by Pierre Ossman
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/mmc/mmc.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/sd.h>
+#include <linux/scatterlist.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/blkdev.h>
+#include <linux/dmaengine.h>
+#include <linux/dma-mapping.h>
+#include <linux/of_dma.h>
+#include <linux/swiotlb.h>
+
+#include "sdhci.h"
+
+
+#define DRIVER_NAME "mmc-bcm2835"
+
+#define DBG(f, x...) \
+pr_debug(DRIVER_NAME " [%s()]: " f, __func__, ## x)
+
+#ifndef CONFIG_MMC_BCM2835_DMA
+ #define FORCE_PIO
+#endif
+
+
+/* the inclusive limit in bytes under which PIO will be used instead of DMA */
+#ifdef CONFIG_MMC_BCM2835_PIO_DMA_BARRIER
+#define PIO_DMA_BARRIER CONFIG_MMC_BCM2835_PIO_DMA_BARRIER
+#else
+#define PIO_DMA_BARRIER 00
+#endif
+
+#define MIN_FREQ 400000
+#define TIMEOUT_VAL 0xE
+#define BCM2835_SDHCI_WRITE_DELAY(f)	(((2 * 1000000) / f) + 1)
+
+
+unsigned mmc_debug;
+unsigned mmc_debug2;
+
+struct bcm2835_host {
+	spinlock_t				lock;
+
+	void __iomem			*ioaddr;
+	u32						bus_addr;
+
+	struct mmc_host			*mmc;
+
+	u32						timeout;
+
+	int						clock;	/* Current clock speed */
+	u8						pwr;	/* Current voltage */
+
+	unsigned int			max_clk;		/* Max possible freq */
+	unsigned int			timeout_clk;	/* Timeout freq (KHz) */
+	unsigned int			clk_mul;		/* Clock Muliplier value */
+
+	struct tasklet_struct	finish_tasklet;		/* Tasklet structures */
+
+	struct timer_list		timer;			/* Timer for timeouts */
+
+	struct sg_mapping_iter	sg_miter;		/* SG state for PIO */
+	unsigned int			blocks;			/* remaining PIO blocks */
+
+	int						irq;			/* Device IRQ */
+
+
+	u32						ier;			/* cached registers */
+
+	struct mmc_request		*mrq;			/* Current request */
+	struct mmc_command		*cmd;			/* Current command */
+	struct mmc_data			*data;			/* Current data request */
+	unsigned int			data_early:1;		/* Data finished before cmd */
+
+	wait_queue_head_t		buf_ready_int;		/* Waitqueue for Buffer Read Ready interrupt */
+
+	u32						shadow;
+
+	/*DMA part*/
+	struct dma_chan			*dma_chan_rxtx;		/* DMA channel for reads and writes */
+	struct dma_slave_config		dma_cfg_rx;
+	struct dma_slave_config		dma_cfg_tx;
+	struct dma_async_tx_descriptor	*tx_desc;	/* descriptor */
+
+	bool					have_dma;
+	bool					use_dma;
+	bool					wait_for_dma;
+	/*end of DMA part*/
+
+	int						max_delay;	/* maximum length of time spent waiting */
+
+	int						flags;				/* Host attributes */
+#define SDHCI_REQ_USE_DMA	(1<<2)	/* Use DMA for this req. */
+#define SDHCI_DEVICE_DEAD	(1<<3)	/* Device unresponsive */
+#define SDHCI_AUTO_CMD12	(1<<6)	/* Auto CMD12 support */
+#define SDHCI_AUTO_CMD23	(1<<7)	/* Auto CMD23 support */
+#define SDHCI_SDIO_IRQ_ENABLED	(1<<9)	/* SDIO irq enabled */
+
+	u32				overclock_50;	/* frequency to use when 50MHz is requested (in MHz) */
+	u32				max_overclock;	/* Highest reported */
+};
+
+
+static inline void bcm2835_mmc_writel(struct bcm2835_host *host, u32 val, int reg, int from)
+{
+	unsigned delay;
+	lockdep_assert_held_once(&host->lock);
+	writel(val, host->ioaddr + reg);
+	udelay(BCM2835_SDHCI_WRITE_DELAY(max(host->clock, MIN_FREQ)));
+
+	delay = ((mmc_debug >> 16) & 0xf) << ((mmc_debug >> 20) & 0xf);
+	if (delay && !((1<<from) & mmc_debug2))
+		udelay(delay);
+}
+
+static inline void mmc_raw_writel(struct bcm2835_host *host, u32 val, int reg)
+{
+	unsigned delay;
+	lockdep_assert_held_once(&host->lock);
+	writel(val, host->ioaddr + reg);
+
+	delay = ((mmc_debug >> 24) & 0xf) << ((mmc_debug >> 28) & 0xf);
+	if (delay)
+		udelay(delay);
+}
+
+static inline u32 bcm2835_mmc_readl(struct bcm2835_host *host, int reg)
+{
+	lockdep_assert_held_once(&host->lock);
+	return readl(host->ioaddr + reg);
+}
+
+static inline void bcm2835_mmc_writew(struct bcm2835_host *host, u16 val, int reg)
+{
+	u32 oldval = (reg == SDHCI_COMMAND) ? host->shadow :
+		bcm2835_mmc_readl(host, reg & ~3);
+	u32 word_num = (reg >> 1) & 1;
+	u32 word_shift = word_num * 16;
+	u32 mask = 0xffff << word_shift;
+	u32 newval = (oldval & ~mask) | (val << word_shift);
+
+	if (reg == SDHCI_TRANSFER_MODE)
+		host->shadow = newval;
+	else
+		bcm2835_mmc_writel(host, newval, reg & ~3, 0);
+
+}
+
+static inline void bcm2835_mmc_writeb(struct bcm2835_host *host, u8 val, int reg)
+{
+	u32 oldval = bcm2835_mmc_readl(host, reg & ~3);
+	u32 byte_num = reg & 3;
+	u32 byte_shift = byte_num * 8;
+	u32 mask = 0xff << byte_shift;
+	u32 newval = (oldval & ~mask) | (val << byte_shift);
+
+	bcm2835_mmc_writel(host, newval, reg & ~3, 1);
+}
+
+
+static inline u16 bcm2835_mmc_readw(struct bcm2835_host *host, int reg)
+{
+	u32 val = bcm2835_mmc_readl(host, (reg & ~3));
+	u32 word_num = (reg >> 1) & 1;
+	u32 word_shift = word_num * 16;
+	u32 word = (val >> word_shift) & 0xffff;
+
+	return word;
+}
+
+static inline u8 bcm2835_mmc_readb(struct bcm2835_host *host, int reg)
+{
+	u32 val = bcm2835_mmc_readl(host, (reg & ~3));
+	u32 byte_num = reg & 3;
+	u32 byte_shift = byte_num * 8;
+	u32 byte = (val >> byte_shift) & 0xff;
+
+	return byte;
+}
+
+static void bcm2835_mmc_unsignal_irqs(struct bcm2835_host *host, u32 clear)
+{
+	u32 ier;
+
+	ier = bcm2835_mmc_readl(host, SDHCI_SIGNAL_ENABLE);
+	ier &= ~clear;
+	/* change which requests generate IRQs - makes no difference to
+	   the content of SDHCI_INT_STATUS, or the need to acknowledge IRQs */
+	bcm2835_mmc_writel(host, ier, SDHCI_SIGNAL_ENABLE, 2);
+}
+
+
+static void bcm2835_mmc_dumpregs(struct bcm2835_host *host)
+{
+	pr_debug(DRIVER_NAME ": =========== REGISTER DUMP (%s)===========\n",
+		mmc_hostname(host->mmc));
+
+	pr_debug(DRIVER_NAME ": Sys addr: 0x%08x | Version:  0x%08x\n",
+		bcm2835_mmc_readl(host, SDHCI_DMA_ADDRESS),
+		bcm2835_mmc_readw(host, SDHCI_HOST_VERSION));
+	pr_debug(DRIVER_NAME ": Blk size: 0x%08x | Blk cnt:  0x%08x\n",
+		bcm2835_mmc_readw(host, SDHCI_BLOCK_SIZE),
+		bcm2835_mmc_readw(host, SDHCI_BLOCK_COUNT));
+	pr_debug(DRIVER_NAME ": Argument: 0x%08x | Trn mode: 0x%08x\n",
+		bcm2835_mmc_readl(host, SDHCI_ARGUMENT),
+		bcm2835_mmc_readw(host, SDHCI_TRANSFER_MODE));
+	pr_debug(DRIVER_NAME ": Present:  0x%08x | Host ctl: 0x%08x\n",
+		bcm2835_mmc_readl(host, SDHCI_PRESENT_STATE),
+		bcm2835_mmc_readb(host, SDHCI_HOST_CONTROL));
+	pr_debug(DRIVER_NAME ": Power:    0x%08x | Blk gap:  0x%08x\n",
+		bcm2835_mmc_readb(host, SDHCI_POWER_CONTROL),
+		bcm2835_mmc_readb(host, SDHCI_BLOCK_GAP_CONTROL));
+	pr_debug(DRIVER_NAME ": Wake-up:  0x%08x | Clock:    0x%08x\n",
+		bcm2835_mmc_readb(host, SDHCI_WAKE_UP_CONTROL),
+		bcm2835_mmc_readw(host, SDHCI_CLOCK_CONTROL));
+	pr_debug(DRIVER_NAME ": Timeout:  0x%08x | Int stat: 0x%08x\n",
+		bcm2835_mmc_readb(host, SDHCI_TIMEOUT_CONTROL),
+		bcm2835_mmc_readl(host, SDHCI_INT_STATUS));
+	pr_debug(DRIVER_NAME ": Int enab: 0x%08x | Sig enab: 0x%08x\n",
+		bcm2835_mmc_readl(host, SDHCI_INT_ENABLE),
+		bcm2835_mmc_readl(host, SDHCI_SIGNAL_ENABLE));
+	pr_debug(DRIVER_NAME ": AC12 err: 0x%08x | Slot int: 0x%08x\n",
+		bcm2835_mmc_readw(host, SDHCI_AUTO_CMD_STATUS),
+		bcm2835_mmc_readw(host, SDHCI_SLOT_INT_STATUS));
+	pr_debug(DRIVER_NAME ": Caps:     0x%08x | Caps_1:   0x%08x\n",
+		bcm2835_mmc_readl(host, SDHCI_CAPABILITIES),
+		bcm2835_mmc_readl(host, SDHCI_CAPABILITIES_1));
+	pr_debug(DRIVER_NAME ": Cmd:      0x%08x | Max curr: 0x%08x\n",
+		bcm2835_mmc_readw(host, SDHCI_COMMAND),
+		bcm2835_mmc_readl(host, SDHCI_MAX_CURRENT));
+	pr_debug(DRIVER_NAME ": Host ctl2: 0x%08x\n",
+		bcm2835_mmc_readw(host, SDHCI_HOST_CONTROL2));
+
+	pr_debug(DRIVER_NAME ": ===========================================\n");
+}
+
+
+static void bcm2835_mmc_reset(struct bcm2835_host *host, u8 mask)
+{
+	unsigned long timeout;
+	unsigned long flags;
+
+	spin_lock_irqsave(&host->lock, flags);
+	bcm2835_mmc_writeb(host, mask, SDHCI_SOFTWARE_RESET);
+
+	if (mask & SDHCI_RESET_ALL)
+		host->clock = 0;
+
+	/* Wait max 100 ms */
+	timeout = 100;
+
+	/* hw clears the bit when it's done */
+	while (bcm2835_mmc_readb(host, SDHCI_SOFTWARE_RESET) & mask) {
+		if (timeout == 0) {
+			pr_err("%s: Reset 0x%x never completed.\n",
+				mmc_hostname(host->mmc), (int)mask);
+			bcm2835_mmc_dumpregs(host);
+			return;
+		}
+		timeout--;
+		spin_unlock_irqrestore(&host->lock, flags);
+		mdelay(1);
+		spin_lock_irqsave(&host->lock, flags);
+	}
+
+	if (100-timeout > 10 && 100-timeout > host->max_delay) {
+		host->max_delay = 100-timeout;
+		pr_warn("Warning: MMC controller hung for %d ms\n", host->max_delay);
+	}
+	spin_unlock_irqrestore(&host->lock, flags);
+}
+
+static void bcm2835_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios);
+
+static void bcm2835_mmc_init(struct bcm2835_host *host, int soft)
+{
+	unsigned long flags;
+	if (soft)
+		bcm2835_mmc_reset(host, SDHCI_RESET_CMD|SDHCI_RESET_DATA);
+	else
+		bcm2835_mmc_reset(host, SDHCI_RESET_ALL);
+
+	host->ier = SDHCI_INT_BUS_POWER | SDHCI_INT_DATA_END_BIT |
+		    SDHCI_INT_DATA_CRC | SDHCI_INT_DATA_TIMEOUT |
+		    SDHCI_INT_INDEX | SDHCI_INT_END_BIT | SDHCI_INT_CRC |
+		    SDHCI_INT_TIMEOUT | SDHCI_INT_DATA_END |
+		    SDHCI_INT_RESPONSE;
+
+	spin_lock_irqsave(&host->lock, flags);
+	bcm2835_mmc_writel(host, host->ier, SDHCI_INT_ENABLE, 3);
+	bcm2835_mmc_writel(host, host->ier, SDHCI_SIGNAL_ENABLE, 3);
+	spin_unlock_irqrestore(&host->lock, flags);
+
+	if (soft) {
+		/* force clock reconfiguration */
+		host->clock = 0;
+		bcm2835_mmc_set_ios(host->mmc, &host->mmc->ios);
+	}
+}
+
+
+
+static void bcm2835_mmc_finish_data(struct bcm2835_host *host);
+
+static void bcm2835_mmc_dma_complete(void *param)
+{
+	struct bcm2835_host *host = param;
+	struct dma_chan *dma_chan;
+	unsigned long flags;
+	u32 dir_data;
+
+	spin_lock_irqsave(&host->lock, flags);
+
+	host->use_dma = false;
+
+	if (host->data) {
+		dma_chan = host->dma_chan_rxtx;
+		if (host->data->flags & MMC_DATA_WRITE)
+			dir_data = DMA_TO_DEVICE;
+		else
+			dir_data = DMA_FROM_DEVICE;
+		dma_unmap_sg(dma_chan->device->dev,
+		     host->data->sg, host->data->sg_len,
+		     dir_data);
+		if (! (host->data->flags & MMC_DATA_WRITE))
+			bcm2835_mmc_finish_data(host);
+	} else if (host->wait_for_dma) {
+		host->wait_for_dma = false;
+		tasklet_schedule(&host->finish_tasklet);
+	}
+
+	spin_unlock_irqrestore(&host->lock, flags);
+}
+
+static void bcm2835_bcm2835_mmc_read_block_pio(struct bcm2835_host *host)
+{
+	unsigned long flags;
+	size_t blksize, len, chunk;
+
+	u32 scratch = 0;
+	u8 *buf;
+
+	blksize = host->data->blksz;
+	chunk = 0;
+
+	local_irq_save(flags);
+
+	while (blksize) {
+		if (!sg_miter_next(&host->sg_miter))
+			BUG();
+
+		len = min(host->sg_miter.length, blksize);
+
+		blksize -= len;
+		host->sg_miter.consumed = len;
+
+		buf = host->sg_miter.addr;
+
+		while (len) {
+			if (chunk == 0) {
+				scratch = bcm2835_mmc_readl(host, SDHCI_BUFFER);
+				chunk = 4;
+			}
+
+			*buf = scratch & 0xFF;
+
+			buf++;
+			scratch >>= 8;
+			chunk--;
+			len--;
+		}
+	}
+
+	sg_miter_stop(&host->sg_miter);
+
+	local_irq_restore(flags);
+}
+
+static void bcm2835_bcm2835_mmc_write_block_pio(struct bcm2835_host *host)
+{
+	unsigned long flags;
+	size_t blksize, len, chunk;
+	u32 scratch;
+	u8 *buf;
+
+	blksize = host->data->blksz;
+	chunk = 0;
+	chunk = 0;
+	scratch = 0;
+
+	local_irq_save(flags);
+
+	while (blksize) {
+		if (!sg_miter_next(&host->sg_miter))
+			BUG();
+
+		len = min(host->sg_miter.length, blksize);
+
+		blksize -= len;
+		host->sg_miter.consumed = len;
+
+		buf = host->sg_miter.addr;
+
+		while (len) {
+			scratch |= (u32)*buf << (chunk * 8);
+
+			buf++;
+			chunk++;
+			len--;
+
+			if ((chunk == 4) || ((len == 0) && (blksize == 0))) {
+				mmc_raw_writel(host, scratch, SDHCI_BUFFER);
+				chunk = 0;
+				scratch = 0;
+			}
+		}
+	}
+
+	sg_miter_stop(&host->sg_miter);
+
+	local_irq_restore(flags);
+}
+
+
+static void bcm2835_mmc_transfer_pio(struct bcm2835_host *host)
+{
+	u32 mask;
+
+	BUG_ON(!host->data);
+
+	if (host->blocks == 0)
+		return;
+
+	if (host->data->flags & MMC_DATA_READ)
+		mask = SDHCI_DATA_AVAILABLE;
+	else
+		mask = SDHCI_SPACE_AVAILABLE;
+
+	while (bcm2835_mmc_readl(host, SDHCI_PRESENT_STATE) & mask) {
+
+		if (host->data->flags & MMC_DATA_READ)
+			bcm2835_bcm2835_mmc_read_block_pio(host);
+		else
+			bcm2835_bcm2835_mmc_write_block_pio(host);
+
+		host->blocks--;
+
+		/* QUIRK used in sdhci.c removes the 'if' */
+		/* but it seems this is unnecessary */
+		if (host->blocks == 0)
+			break;
+
+
+	}
+}
+
+
+static void bcm2835_mmc_transfer_dma(struct bcm2835_host *host)
+{
+	u32 len, dir_data, dir_slave;
+	struct dma_async_tx_descriptor *desc = NULL;
+	struct dma_chan *dma_chan;
+
+
+	WARN_ON(!host->data);
+
+	if (!host->data)
+		return;
+
+	if (host->blocks == 0)
+		return;
+
+	dma_chan = host->dma_chan_rxtx;
+	if (host->data->flags & MMC_DATA_READ) {
+		dir_data = DMA_FROM_DEVICE;
+		dir_slave = DMA_DEV_TO_MEM;
+	} else {
+		dir_data = DMA_TO_DEVICE;
+		dir_slave = DMA_MEM_TO_DEV;
+	}
+
+	/* The parameters have already been validated, so this will not fail */
+	(void)dmaengine_slave_config(dma_chan,
+				     (dir_data == DMA_FROM_DEVICE) ?
+				     &host->dma_cfg_rx :
+				     &host->dma_cfg_tx);
+
+	BUG_ON(!dma_chan->device);
+	BUG_ON(!dma_chan->device->dev);
+	BUG_ON(!host->data->sg);
+
+	len = dma_map_sg(dma_chan->device->dev, host->data->sg,
+			 host->data->sg_len, dir_data);
+	if (len > 0) {
+		desc = dmaengine_prep_slave_sg(dma_chan, host->data->sg,
+					       len, dir_slave,
+					       DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+	} else {
+		dev_err(mmc_dev(host->mmc), "dma_map_sg returned zero length\n");
+	}
+	if (desc) {
+		unsigned long flags;
+		spin_lock_irqsave(&host->lock, flags);
+		bcm2835_mmc_unsignal_irqs(host, SDHCI_INT_DATA_AVAIL |
+						    SDHCI_INT_SPACE_AVAIL);
+		host->tx_desc = desc;
+		desc->callback = bcm2835_mmc_dma_complete;
+		desc->callback_param = host;
+		spin_unlock_irqrestore(&host->lock, flags);
+		dmaengine_submit(desc);
+		dma_async_issue_pending(dma_chan);
+	} else {
+		dma_unmap_sg(dma_chan->device->dev, host->data->sg, len, dir_data);
+	}
+
+}
+
+
+
+static void bcm2835_mmc_set_transfer_irqs(struct bcm2835_host *host)
+{
+	u32 pio_irqs = SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL;
+	u32 dma_irqs = SDHCI_INT_DMA_END | SDHCI_INT_ADMA_ERROR;
+
+	if (host->use_dma)
+		host->ier = (host->ier & ~pio_irqs) | dma_irqs;
+	else
+		host->ier = (host->ier & ~dma_irqs) | pio_irqs;
+
+	bcm2835_mmc_writel(host, host->ier, SDHCI_INT_ENABLE, 4);
+	bcm2835_mmc_writel(host, host->ier, SDHCI_SIGNAL_ENABLE, 4);
+}
+
+
+static void bcm2835_mmc_prepare_data(struct bcm2835_host *host, struct mmc_command *cmd)
+{
+	u8 count;
+	struct mmc_data *data = cmd->data;
+
+	WARN_ON(host->data);
+
+	if (data || (cmd->flags & MMC_RSP_BUSY)) {
+		count = TIMEOUT_VAL;
+		bcm2835_mmc_writeb(host, count, SDHCI_TIMEOUT_CONTROL);
+	}
+
+	if (!data)
+		return;
+
+	/* Sanity checks */
+	BUG_ON(data->blksz * data->blocks > 524288);
+	BUG_ON(data->blksz > host->mmc->max_blk_size);
+	BUG_ON(data->blocks > 65535);
+
+	host->data = data;
+	host->data_early = 0;
+	host->data->bytes_xfered = 0;
+
+
+	if (!(host->flags & SDHCI_REQ_USE_DMA)) {
+		int flags;
+
+		flags = SG_MITER_ATOMIC;
+		if (host->data->flags & MMC_DATA_READ)
+			flags |= SG_MITER_TO_SG;
+		else
+			flags |= SG_MITER_FROM_SG;
+		sg_miter_start(&host->sg_miter, data->sg, data->sg_len, flags);
+		host->blocks = data->blocks;
+	}
+
+	host->use_dma = host->have_dma && data->blocks > PIO_DMA_BARRIER;
+
+	bcm2835_mmc_set_transfer_irqs(host);
+
+	/* Set the DMA boundary value and block size */
+	bcm2835_mmc_writew(host, SDHCI_MAKE_BLKSZ(SDHCI_DEFAULT_BOUNDARY_ARG,
+		data->blksz), SDHCI_BLOCK_SIZE);
+	bcm2835_mmc_writew(host, data->blocks, SDHCI_BLOCK_COUNT);
+
+	BUG_ON(!host->data);
+}
+
+static void bcm2835_mmc_set_transfer_mode(struct bcm2835_host *host,
+	struct mmc_command *cmd)
+{
+	u16 mode;
+	struct mmc_data *data = cmd->data;
+
+	if (data == NULL) {
+		/* clear Auto CMD settings for no data CMDs */
+		mode = bcm2835_mmc_readw(host, SDHCI_TRANSFER_MODE);
+		bcm2835_mmc_writew(host, mode & ~(SDHCI_TRNS_AUTO_CMD12 |
+				SDHCI_TRNS_AUTO_CMD23), SDHCI_TRANSFER_MODE);
+		return;
+	}
+
+	WARN_ON(!host->data);
+
+	mode = SDHCI_TRNS_BLK_CNT_EN;
+
+	if ((mmc_op_multi(cmd->opcode) || data->blocks > 1)) {
+		mode |= SDHCI_TRNS_MULTI;
+
+		/*
+		 * If we are sending CMD23, CMD12 never gets sent
+		 * on successful completion (so no Auto-CMD12).
+		 */
+		if (!host->mrq->sbc && (host->flags & SDHCI_AUTO_CMD12))
+			mode |= SDHCI_TRNS_AUTO_CMD12;
+		else if (host->mrq->sbc && (host->flags & SDHCI_AUTO_CMD23)) {
+			mode |= SDHCI_TRNS_AUTO_CMD23;
+			bcm2835_mmc_writel(host, host->mrq->sbc->arg, SDHCI_ARGUMENT2, 5);
+		}
+	}
+
+	if (data->flags & MMC_DATA_READ)
+		mode |= SDHCI_TRNS_READ;
+	if (host->flags & SDHCI_REQ_USE_DMA)
+		mode |= SDHCI_TRNS_DMA;
+
+	bcm2835_mmc_writew(host, mode, SDHCI_TRANSFER_MODE);
+}
+
+void bcm2835_mmc_send_command(struct bcm2835_host *host, struct mmc_command *cmd)
+{
+	int flags;
+	u32 mask;
+	unsigned long timeout;
+
+	WARN_ON(host->cmd);
+
+	/* Wait max 10 ms */
+	timeout = 1000;
+
+	mask = SDHCI_CMD_INHIBIT;
+	if ((cmd->data != NULL) || (cmd->flags & MMC_RSP_BUSY))
+		mask |= SDHCI_DATA_INHIBIT;
+
+	/* We shouldn't wait for data inihibit for stop commands, even
+	   though they might use busy signaling */
+	if (host->mrq->data && (cmd == host->mrq->data->stop))
+		mask &= ~SDHCI_DATA_INHIBIT;
+
+	while (bcm2835_mmc_readl(host, SDHCI_PRESENT_STATE) & mask) {
+		if (timeout == 0) {
+			pr_err("%s: Controller never released inhibit bit(s).\n",
+				mmc_hostname(host->mmc));
+			bcm2835_mmc_dumpregs(host);
+			cmd->error = -EIO;
+			tasklet_schedule(&host->finish_tasklet);
+			return;
+		}
+		timeout--;
+		udelay(10);
+	}
+
+	if ((1000-timeout)/100 > 1 && (1000-timeout)/100 > host->max_delay) {
+		host->max_delay = (1000-timeout)/100;
+		pr_warn("Warning: MMC controller hung for %d ms\n", host->max_delay);
+	}
+
+	timeout = jiffies;
+	if (!cmd->data && cmd->busy_timeout > 9000)
+		timeout += DIV_ROUND_UP(cmd->busy_timeout, 1000) * HZ + HZ;
+	else
+		timeout += 10 * HZ;
+	mod_timer(&host->timer, timeout);
+
+	host->cmd = cmd;
+	host->use_dma = false;
+
+	bcm2835_mmc_prepare_data(host, cmd);
+
+	bcm2835_mmc_writel(host, cmd->arg, SDHCI_ARGUMENT, 6);
+
+	bcm2835_mmc_set_transfer_mode(host, cmd);
+
+	if ((cmd->flags & MMC_RSP_136) && (cmd->flags & MMC_RSP_BUSY)) {
+		pr_err("%s: Unsupported response type!\n",
+			mmc_hostname(host->mmc));
+		cmd->error = -EINVAL;
+		tasklet_schedule(&host->finish_tasklet);
+		return;
+	}
+
+	if (!(cmd->flags & MMC_RSP_PRESENT))
+		flags = SDHCI_CMD_RESP_NONE;
+	else if (cmd->flags & MMC_RSP_136)
+		flags = SDHCI_CMD_RESP_LONG;
+	else if (cmd->flags & MMC_RSP_BUSY)
+		flags = SDHCI_CMD_RESP_SHORT_BUSY;
+	else
+		flags = SDHCI_CMD_RESP_SHORT;
+
+	if (cmd->flags & MMC_RSP_CRC)
+		flags |= SDHCI_CMD_CRC;
+	if (cmd->flags & MMC_RSP_OPCODE)
+		flags |= SDHCI_CMD_INDEX;
+
+	if (cmd->data)
+		flags |= SDHCI_CMD_DATA;
+
+	bcm2835_mmc_writew(host, SDHCI_MAKE_CMD(cmd->opcode, flags), SDHCI_COMMAND);
+}
+
+
+static void bcm2835_mmc_finish_data(struct bcm2835_host *host)
+{
+	struct mmc_data *data;
+
+	BUG_ON(!host->data);
+
+	data = host->data;
+	host->data = NULL;
+
+	if (data->error)
+		data->bytes_xfered = 0;
+	else
+		data->bytes_xfered = data->blksz * data->blocks;
+
+	/*
+	 * Need to send CMD12 if -
+	 * a) open-ended multiblock transfer (no CMD23)
+	 * b) error in multiblock transfer
+	 */
+	if (data->stop &&
+	    (data->error ||
+	     !host->mrq->sbc)) {
+
+		/*
+		 * The controller needs a reset of internal state machines
+		 * upon error conditions.
+		 */
+		if (data->error) {
+			bcm2835_mmc_reset(host, SDHCI_RESET_CMD);
+			bcm2835_mmc_reset(host, SDHCI_RESET_DATA);
+		}
+
+		bcm2835_mmc_send_command(host, data->stop);
+	} else if (host->use_dma) {
+		host->wait_for_dma = true;
+	} else {
+		tasklet_schedule(&host->finish_tasklet);
+	}
+}
+
+static void bcm2835_mmc_finish_command(struct bcm2835_host *host)
+{
+	int i;
+
+	BUG_ON(host->cmd == NULL);
+
+	if (host->cmd->flags & MMC_RSP_PRESENT) {
+		if (host->cmd->flags & MMC_RSP_136) {
+			/* CRC is stripped so we need to do some shifting. */
+			for (i = 0; i < 4; i++) {
+				host->cmd->resp[i] = bcm2835_mmc_readl(host,
+					SDHCI_RESPONSE + (3-i)*4) << 8;
+				if (i != 3)
+					host->cmd->resp[i] |=
+						bcm2835_mmc_readb(host,
+						SDHCI_RESPONSE + (3-i)*4-1);
+			}
+		} else {
+			host->cmd->resp[0] = bcm2835_mmc_readl(host, SDHCI_RESPONSE);
+		}
+	}
+
+	host->cmd->error = 0;
+
+	/* Finished CMD23, now send actual command. */
+	if (host->cmd == host->mrq->sbc) {
+		host->cmd = NULL;
+		bcm2835_mmc_send_command(host, host->mrq->cmd);
+
+		if (host->mrq->cmd->data && host->use_dma) {
+			/* DMA transfer starts now, PIO starts after interrupt */
+			bcm2835_mmc_transfer_dma(host);
+		}
+	} else {
+
+		/* Processed actual command. */
+		if (host->data && host->data_early)
+			bcm2835_mmc_finish_data(host);
+
+		if (!host->cmd->data)
+			tasklet_schedule(&host->finish_tasklet);
+
+		host->cmd = NULL;
+	}
+}
+
+
+static void bcm2835_mmc_timeout_timer(struct timer_list *t)
+{
+	struct bcm2835_host *host = from_timer(host, t, timer);
+	unsigned long flags;
+
+	spin_lock_irqsave(&host->lock, flags);
+
+	if (host->mrq) {
+		pr_err("%s: Timeout waiting for hardware interrupt.\n",
+			mmc_hostname(host->mmc));
+		bcm2835_mmc_dumpregs(host);
+
+		if (host->data) {
+			host->data->error = -ETIMEDOUT;
+			bcm2835_mmc_finish_data(host);
+		} else {
+			if (host->cmd)
+				host->cmd->error = -ETIMEDOUT;
+			else
+				host->mrq->cmd->error = -ETIMEDOUT;
+
+			tasklet_schedule(&host->finish_tasklet);
+		}
+	}
+
+	spin_unlock_irqrestore(&host->lock, flags);
+}
+
+
+static void bcm2835_mmc_enable_sdio_irq_nolock(struct bcm2835_host *host, int enable)
+{
+	if (!(host->flags & SDHCI_DEVICE_DEAD)) {
+		if (enable)
+			host->ier |= SDHCI_INT_CARD_INT;
+		else
+			host->ier &= ~SDHCI_INT_CARD_INT;
+
+		bcm2835_mmc_writel(host, host->ier, SDHCI_INT_ENABLE, 7);
+		bcm2835_mmc_writel(host, host->ier, SDHCI_SIGNAL_ENABLE, 7);
+	}
+}
+
+static void bcm2835_mmc_enable_sdio_irq(struct mmc_host *mmc, int enable)
+{
+	struct bcm2835_host *host = mmc_priv(mmc);
+	unsigned long flags;
+
+	spin_lock_irqsave(&host->lock, flags);
+	if (enable)
+		host->flags |= SDHCI_SDIO_IRQ_ENABLED;
+	else
+		host->flags &= ~SDHCI_SDIO_IRQ_ENABLED;
+
+	bcm2835_mmc_enable_sdio_irq_nolock(host, enable);
+	spin_unlock_irqrestore(&host->lock, flags);
+}
+
+static void bcm2835_mmc_cmd_irq(struct bcm2835_host *host, u32 intmask)
+{
+
+	BUG_ON(intmask == 0);
+
+	if (!host->cmd) {
+		pr_err("%s: Got command interrupt 0x%08x even "
+			"though no command operation was in progress.\n",
+			mmc_hostname(host->mmc), (unsigned)intmask);
+		bcm2835_mmc_dumpregs(host);
+		return;
+	}
+
+	if (intmask & SDHCI_INT_TIMEOUT)
+		host->cmd->error = -ETIMEDOUT;
+	else if (intmask & (SDHCI_INT_CRC | SDHCI_INT_END_BIT |
+			SDHCI_INT_INDEX)) {
+			host->cmd->error = -EILSEQ;
+	}
+
+	if (host->cmd->error) {
+		tasklet_schedule(&host->finish_tasklet);
+		return;
+	}
+
+	if (intmask & SDHCI_INT_RESPONSE)
+		bcm2835_mmc_finish_command(host);
+
+}
+
+static void bcm2835_mmc_data_irq(struct bcm2835_host *host, u32 intmask)
+{
+	struct dma_chan *dma_chan;
+	u32 dir_data;
+
+	BUG_ON(intmask == 0);
+
+	if (!host->data) {
+		/*
+		 * The "data complete" interrupt is also used to
+		 * indicate that a busy state has ended. See comment
+		 * above in sdhci_cmd_irq().
+		 */
+		if (host->cmd && (host->cmd->flags & MMC_RSP_BUSY)) {
+			if (intmask & SDHCI_INT_DATA_END) {
+				bcm2835_mmc_finish_command(host);
+				return;
+			}
+		}
+
+		pr_debug("%s: Got data interrupt 0x%08x even "
+			"though no data operation was in progress.\n",
+			mmc_hostname(host->mmc), (unsigned)intmask);
+		bcm2835_mmc_dumpregs(host);
+
+		return;
+	}
+
+	if (intmask & SDHCI_INT_DATA_TIMEOUT)
+		host->data->error = -ETIMEDOUT;
+	else if (intmask & SDHCI_INT_DATA_END_BIT)
+		host->data->error = -EILSEQ;
+	else if ((intmask & SDHCI_INT_DATA_CRC) &&
+		SDHCI_GET_CMD(bcm2835_mmc_readw(host, SDHCI_COMMAND))
+			!= MMC_BUS_TEST_R)
+		host->data->error = -EILSEQ;
+
+	if (host->use_dma) {
+		if  (host->data->flags & MMC_DATA_WRITE) {
+			/* IRQ handled here */
+
+			dma_chan = host->dma_chan_rxtx;
+			dir_data = DMA_TO_DEVICE;
+			dma_unmap_sg(dma_chan->device->dev,
+				 host->data->sg, host->data->sg_len,
+				 dir_data);
+
+			bcm2835_mmc_finish_data(host);
+		}
+
+	} else {
+		if (host->data->error)
+			bcm2835_mmc_finish_data(host);
+		else {
+			if (intmask & (SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL))
+				bcm2835_mmc_transfer_pio(host);
+
+			if (intmask & SDHCI_INT_DATA_END) {
+				if (host->cmd) {
+					/*
+					 * Data managed to finish before the
+					 * command completed. Make sure we do
+					 * things in the proper order.
+					 */
+					host->data_early = 1;
+				} else {
+					bcm2835_mmc_finish_data(host);
+				}
+			}
+		}
+	}
+}
+
+
+static irqreturn_t bcm2835_mmc_irq(int irq, void *dev_id)
+{
+	irqreturn_t result = IRQ_NONE;
+	struct bcm2835_host *host = dev_id;
+	u32 intmask, mask, unexpected = 0;
+	int max_loops = 16;
+
+	spin_lock(&host->lock);
+
+	intmask = bcm2835_mmc_readl(host, SDHCI_INT_STATUS);
+
+	if (!intmask || intmask == 0xffffffff) {
+		result = IRQ_NONE;
+		goto out;
+	}
+
+	do {
+		/* Clear selected interrupts. */
+		mask = intmask & (SDHCI_INT_CMD_MASK | SDHCI_INT_DATA_MASK |
+				  SDHCI_INT_BUS_POWER);
+		bcm2835_mmc_writel(host, mask, SDHCI_INT_STATUS, 8);
+
+
+		if (intmask & SDHCI_INT_CMD_MASK)
+			bcm2835_mmc_cmd_irq(host, intmask & SDHCI_INT_CMD_MASK);
+
+		if (intmask & SDHCI_INT_DATA_MASK)
+			bcm2835_mmc_data_irq(host, intmask & SDHCI_INT_DATA_MASK);
+
+		if (intmask & SDHCI_INT_BUS_POWER)
+			pr_err("%s: Card is consuming too much power!\n",
+				mmc_hostname(host->mmc));
+
+		if (intmask & SDHCI_INT_CARD_INT) {
+			bcm2835_mmc_enable_sdio_irq_nolock(host, false);
+			sdio_signal_irq(host->mmc);
+		}
+
+		intmask &= ~(SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE |
+			     SDHCI_INT_CMD_MASK | SDHCI_INT_DATA_MASK |
+			     SDHCI_INT_ERROR | SDHCI_INT_BUS_POWER |
+			     SDHCI_INT_CARD_INT);
+
+		if (intmask) {
+			unexpected |= intmask;
+			bcm2835_mmc_writel(host, intmask, SDHCI_INT_STATUS, 9);
+		}
+
+		if (result == IRQ_NONE)
+			result = IRQ_HANDLED;
+
+		intmask = bcm2835_mmc_readl(host, SDHCI_INT_STATUS);
+	} while (intmask && --max_loops);
+out:
+	spin_unlock(&host->lock);
+
+	if (unexpected) {
+		pr_err("%s: Unexpected interrupt 0x%08x.\n",
+			   mmc_hostname(host->mmc), unexpected);
+		bcm2835_mmc_dumpregs(host);
+	}
+
+	return result;
+}
+
+
+static void bcm2835_mmc_ack_sdio_irq(struct mmc_host *mmc)
+{
+	struct bcm2835_host *host = mmc_priv(mmc);
+	unsigned long flags;
+
+	spin_lock_irqsave(&host->lock, flags);
+	if (host->flags & SDHCI_SDIO_IRQ_ENABLED)
+		bcm2835_mmc_enable_sdio_irq_nolock(host, true);
+	spin_unlock_irqrestore(&host->lock, flags);
+}
+
+void bcm2835_mmc_set_clock(struct bcm2835_host *host, unsigned int clock)
+{
+	int div = 0; /* Initialized for compiler warning */
+	int real_div = div, clk_mul = 1;
+	u16 clk = 0;
+	unsigned long timeout;
+	unsigned int input_clock = clock;
+
+	if (host->overclock_50 && (clock == 50000000))
+		clock = host->overclock_50 * 1000000 + 999999;
+
+	host->mmc->actual_clock = 0;
+
+	bcm2835_mmc_writew(host, 0, SDHCI_CLOCK_CONTROL);
+
+	if (clock == 0)
+		return;
+
+	/* Version 3.00 divisors must be a multiple of 2. */
+	if (host->max_clk <= clock)
+		div = 1;
+	else {
+		for (div = 2; div < SDHCI_MAX_DIV_SPEC_300;
+			 div += 2) {
+			if ((host->max_clk / div) <= clock)
+				break;
+		}
+	}
+
+	real_div = div;
+	div >>= 1;
+
+	if (real_div)
+		clock = (host->max_clk * clk_mul) / real_div;
+	host->mmc->actual_clock = clock;
+
+	if ((clock > input_clock) && (clock > host->max_overclock)) {
+		pr_warn("%s: Overclocking to %dHz\n",
+			mmc_hostname(host->mmc), clock);
+		host->max_overclock = clock;
+	}
+
+	clk |= (div & SDHCI_DIV_MASK) << SDHCI_DIVIDER_SHIFT;
+	clk |= ((div & SDHCI_DIV_HI_MASK) >> SDHCI_DIV_MASK_LEN)
+		<< SDHCI_DIVIDER_HI_SHIFT;
+	clk |= SDHCI_CLOCK_INT_EN;
+	bcm2835_mmc_writew(host, clk, SDHCI_CLOCK_CONTROL);
+
+	/* Wait max 20 ms */
+	timeout = 20;
+	while (!((clk = bcm2835_mmc_readw(host, SDHCI_CLOCK_CONTROL))
+		& SDHCI_CLOCK_INT_STABLE)) {
+		if (timeout == 0) {
+			pr_err("%s: Internal clock never "
+				"stabilised.\n", mmc_hostname(host->mmc));
+			bcm2835_mmc_dumpregs(host);
+			return;
+		}
+		timeout--;
+		mdelay(1);
+	}
+
+	if (20-timeout > 10 && 20-timeout > host->max_delay) {
+		host->max_delay = 20-timeout;
+		pr_warn("Warning: MMC controller hung for %d ms\n", host->max_delay);
+	}
+
+	clk |= SDHCI_CLOCK_CARD_EN;
+	bcm2835_mmc_writew(host, clk, SDHCI_CLOCK_CONTROL);
+}
+
+static void bcm2835_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq)
+{
+	struct bcm2835_host *host;
+	unsigned long flags;
+
+	host = mmc_priv(mmc);
+
+	spin_lock_irqsave(&host->lock, flags);
+
+	WARN_ON(host->mrq != NULL);
+
+	host->mrq = mrq;
+
+	if (mrq->sbc && !(host->flags & SDHCI_AUTO_CMD23))
+		bcm2835_mmc_send_command(host, mrq->sbc);
+	else
+		bcm2835_mmc_send_command(host, mrq->cmd);
+
+	spin_unlock_irqrestore(&host->lock, flags);
+
+	if (!(mrq->sbc && !(host->flags & SDHCI_AUTO_CMD23)) && mrq->cmd->data && host->use_dma) {
+		/* DMA transfer starts now, PIO starts after interrupt */
+		bcm2835_mmc_transfer_dma(host);
+	}
+}
+
+
+static void bcm2835_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+
+	struct bcm2835_host *host = mmc_priv(mmc);
+	unsigned long flags;
+	u8 ctrl;
+	u16 clk, ctrl_2;
+
+	pr_debug("bcm2835_mmc_set_ios: clock %d, pwr %d, bus_width %d, timing %d, vdd %d, drv_type %d\n",
+		 ios->clock, ios->power_mode, ios->bus_width,
+		 ios->timing, ios->signal_voltage, ios->drv_type);
+
+	spin_lock_irqsave(&host->lock, flags);
+
+	if (!ios->clock || ios->clock != host->clock) {
+		bcm2835_mmc_set_clock(host, ios->clock);
+		host->clock = ios->clock;
+	}
+
+	if (host->pwr != SDHCI_POWER_330) {
+		host->pwr = SDHCI_POWER_330;
+		bcm2835_mmc_writeb(host, SDHCI_POWER_330 | SDHCI_POWER_ON, SDHCI_POWER_CONTROL);
+	}
+
+	ctrl = bcm2835_mmc_readb(host, SDHCI_HOST_CONTROL);
+
+	/* set bus width */
+	ctrl &= ~SDHCI_CTRL_8BITBUS;
+	if (ios->bus_width == MMC_BUS_WIDTH_4)
+		ctrl |= SDHCI_CTRL_4BITBUS;
+	else
+		ctrl &= ~SDHCI_CTRL_4BITBUS;
+
+	ctrl &= ~SDHCI_CTRL_HISPD; /* NO_HISPD_BIT */
+
+
+	bcm2835_mmc_writeb(host, ctrl, SDHCI_HOST_CONTROL);
+	/*
+	 * We only need to set Driver Strength if the
+	 * preset value enable is not set.
+	 */
+	ctrl_2 = bcm2835_mmc_readw(host, SDHCI_HOST_CONTROL2);
+	ctrl_2 &= ~SDHCI_CTRL_DRV_TYPE_MASK;
+	if (ios->drv_type == MMC_SET_DRIVER_TYPE_A)
+		ctrl_2 |= SDHCI_CTRL_DRV_TYPE_A;
+	else if (ios->drv_type == MMC_SET_DRIVER_TYPE_C)
+		ctrl_2 |= SDHCI_CTRL_DRV_TYPE_C;
+
+	bcm2835_mmc_writew(host, ctrl_2, SDHCI_HOST_CONTROL2);
+
+	/* Reset SD Clock Enable */
+	clk = bcm2835_mmc_readw(host, SDHCI_CLOCK_CONTROL);
+	clk &= ~SDHCI_CLOCK_CARD_EN;
+	bcm2835_mmc_writew(host, clk, SDHCI_CLOCK_CONTROL);
+
+	/* Re-enable SD Clock */
+	bcm2835_mmc_set_clock(host, host->clock);
+	bcm2835_mmc_writeb(host, ctrl, SDHCI_HOST_CONTROL);
+
+	spin_unlock_irqrestore(&host->lock, flags);
+}
+
+
+static struct mmc_host_ops bcm2835_ops = {
+	.request = bcm2835_mmc_request,
+	.set_ios = bcm2835_mmc_set_ios,
+	.enable_sdio_irq = bcm2835_mmc_enable_sdio_irq,
+	.ack_sdio_irq = bcm2835_mmc_ack_sdio_irq,
+};
+
+
+static void bcm2835_mmc_tasklet_finish(unsigned long param)
+{
+	struct bcm2835_host *host;
+	unsigned long flags;
+	struct mmc_request *mrq;
+
+	host = (struct bcm2835_host *)param;
+
+	spin_lock_irqsave(&host->lock, flags);
+
+	/*
+	 * If this tasklet gets rescheduled while running, it will
+	 * be run again afterwards but without any active request.
+	 */
+	if (!host->mrq) {
+		spin_unlock_irqrestore(&host->lock, flags);
+		return;
+	}
+
+	del_timer(&host->timer);
+
+	mrq = host->mrq;
+
+	/*
+	 * The controller needs a reset of internal state machines
+	 * upon error conditions.
+	 */
+	if (!(host->flags & SDHCI_DEVICE_DEAD) &&
+	    ((mrq->cmd && mrq->cmd->error) ||
+		 (mrq->data && (mrq->data->error ||
+		  (mrq->data->stop && mrq->data->stop->error))))) {
+
+		spin_unlock_irqrestore(&host->lock, flags);
+		bcm2835_mmc_reset(host, SDHCI_RESET_CMD);
+		bcm2835_mmc_reset(host, SDHCI_RESET_DATA);
+		spin_lock_irqsave(&host->lock, flags);
+	}
+
+	host->mrq = NULL;
+	host->cmd = NULL;
+	host->data = NULL;
+
+	spin_unlock_irqrestore(&host->lock, flags);
+	mmc_request_done(host->mmc, mrq);
+}
+
+
+
+static int bcm2835_mmc_add_host(struct bcm2835_host *host)
+{
+	struct mmc_host *mmc = host->mmc;
+	struct device *dev = mmc->parent;
+#ifndef FORCE_PIO
+	struct dma_slave_config cfg;
+#endif
+	int ret;
+
+	bcm2835_mmc_reset(host, SDHCI_RESET_ALL);
+
+	host->clk_mul = 0;
+
+	if (!mmc->f_max || mmc->f_max > host->max_clk)
+		mmc->f_max = host->max_clk;
+	mmc->f_min = host->max_clk / SDHCI_MAX_DIV_SPEC_300;
+
+	/* SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK */
+	host->timeout_clk = mmc->f_max / 1000;
+	mmc->max_busy_timeout = (1 << 27) / host->timeout_clk;
+
+	/* host controller capabilities */
+	mmc->caps |= MMC_CAP_CMD23 | MMC_CAP_NEEDS_POLL |
+		MMC_CAP_SDIO_IRQ | MMC_CAP_SD_HIGHSPEED |
+		MMC_CAP_MMC_HIGHSPEED;
+
+	mmc->caps2 |= MMC_CAP2_SDIO_IRQ_NOTHREAD;
+
+	host->flags = SDHCI_AUTO_CMD23;
+
+	dev_info(dev, "mmc_debug:%x mmc_debug2:%x\n", mmc_debug, mmc_debug2);
+#ifdef FORCE_PIO
+	dev_info(dev, "Forcing PIO mode\n");
+	host->have_dma = false;
+#else
+	if (IS_ERR_OR_NULL(host->dma_chan_rxtx)) {
+		dev_err(dev, "%s: Unable to initialise DMA channel. Falling back to PIO\n",
+			DRIVER_NAME);
+		host->have_dma = false;
+	} else {
+		dev_info(dev, "DMA channel allocated");
+
+		cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+		cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+		cfg.slave_id = 11;		/* DREQ channel */
+
+		/* Validate the slave configurations */
+
+		cfg.direction = DMA_MEM_TO_DEV;
+		cfg.src_addr = 0;
+		cfg.dst_addr = host->bus_addr + SDHCI_BUFFER;
+
+		ret = dmaengine_slave_config(host->dma_chan_rxtx, &cfg);
+
+		if (ret == 0) {
+			host->dma_cfg_tx = cfg;
+
+			cfg.direction = DMA_DEV_TO_MEM;
+			cfg.src_addr = host->bus_addr + SDHCI_BUFFER;
+			cfg.dst_addr = 0;
+
+			ret = dmaengine_slave_config(host->dma_chan_rxtx, &cfg);
+		}
+
+		if (ret == 0) {
+			host->dma_cfg_rx = cfg;
+
+			host->have_dma = true;
+		} else {
+			pr_err("%s: unable to configure DMA channel. "
+			       "Falling back to PIO\n",
+			       mmc_hostname(mmc));
+			dma_release_channel(host->dma_chan_rxtx);
+			host->dma_chan_rxtx = NULL;
+			host->have_dma = false;
+		}
+	}
+#endif
+	mmc->max_segs = 128;
+	if (swiotlb_max_segment())
+		mmc->max_req_size = (1 << IO_TLB_SHIFT) * IO_TLB_SEGSIZE;
+	else
+		mmc->max_req_size = 524288;
+	mmc->max_seg_size = mmc->max_req_size;
+	mmc->max_blk_size = 512;
+	mmc->max_blk_count =  65535;
+
+	/* report supported voltage ranges */
+	mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
+
+	tasklet_init(&host->finish_tasklet,
+		bcm2835_mmc_tasklet_finish, (unsigned long)host);
+
+	timer_setup(&host->timer, bcm2835_mmc_timeout_timer, 0);
+	init_waitqueue_head(&host->buf_ready_int);
+
+	bcm2835_mmc_init(host, 0);
+	ret = request_irq(host->irq, bcm2835_mmc_irq, IRQF_SHARED,
+				   mmc_hostname(mmc), host);
+	if (ret) {
+		dev_err(dev, "Failed to request IRQ %d: %d\n", host->irq, ret);
+		goto untasklet;
+	}
+
+	ret = mmc_add_host(mmc);
+	if (ret) {
+		dev_err(dev, "could not add MMC host\n");
+		goto free_irq;
+	}
+
+	return 0;
+
+free_irq:
+	free_irq(host->irq, host);
+untasklet:
+	tasklet_kill(&host->finish_tasklet);
+
+	return ret;
+}
+
+static int bcm2835_mmc_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct device_node *node = dev->of_node;
+	struct clk *clk;
+	struct resource *iomem;
+	struct bcm2835_host *host;
+	struct mmc_host *mmc;
+	const __be32 *addr;
+	int ret;
+
+	mmc = mmc_alloc_host(sizeof(*host), dev);
+	if (!mmc)
+		return -ENOMEM;
+
+	mmc->ops = &bcm2835_ops;
+	host = mmc_priv(mmc);
+	host->mmc = mmc;
+	host->timeout = msecs_to_jiffies(1000);
+	spin_lock_init(&host->lock);
+
+	iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	host->ioaddr = devm_ioremap_resource(dev, iomem);
+	if (IS_ERR(host->ioaddr)) {
+		ret = PTR_ERR(host->ioaddr);
+		goto err;
+	}
+
+	addr = of_get_address(node, 0, NULL, NULL);
+	if (!addr) {
+		dev_err(dev, "could not get DMA-register address\n");
+		ret = -ENODEV;
+		goto err;
+	}
+	host->bus_addr = be32_to_cpup(addr);
+	pr_debug(" - ioaddr %lx, iomem->start %lx, bus_addr %lx\n",
+		 (unsigned long)host->ioaddr,
+		 (unsigned long)iomem->start,
+		 (unsigned long)host->bus_addr);
+
+#ifndef FORCE_PIO
+	if (node) {
+		host->dma_chan_rxtx = dma_request_slave_channel(dev, "rx-tx");
+		if (!host->dma_chan_rxtx)
+			host->dma_chan_rxtx =
+				dma_request_slave_channel(dev, "tx");
+		if (!host->dma_chan_rxtx)
+			host->dma_chan_rxtx =
+				dma_request_slave_channel(dev, "rx");
+	} else {
+		dma_cap_mask_t mask;
+
+		dma_cap_zero(mask);
+		/* we don't care about the channel, any would work */
+		dma_cap_set(DMA_SLAVE, mask);
+		host->dma_chan_rxtx = dma_request_channel(mask, NULL, NULL);
+	}
+#endif
+	clk = devm_clk_get(dev, NULL);
+	if (IS_ERR(clk)) {
+		ret = PTR_ERR(clk);
+		if (ret == -EPROBE_DEFER)
+			dev_info(dev, "could not get clk, deferring probe\n");
+		else
+			dev_err(dev, "could not get clk\n");
+		goto err;
+	}
+
+	host->max_clk = clk_get_rate(clk);
+
+	host->irq = platform_get_irq(pdev, 0);
+	if (host->irq <= 0) {
+		dev_err(dev, "get IRQ failed\n");
+		ret = -EINVAL;
+		goto err;
+	}
+
+	if (node) {
+		mmc_of_parse(mmc);
+
+		/* Read any custom properties */
+		of_property_read_u32(node,
+				     "brcm,overclock-50",
+				     &host->overclock_50);
+	} else {
+		mmc->caps |= MMC_CAP_4_BIT_DATA;
+	}
+
+	ret = bcm2835_mmc_add_host(host);
+	if (ret)
+		goto err;
+
+	platform_set_drvdata(pdev, host);
+
+	return 0;
+err:
+	if (host->dma_chan_rxtx)
+		dma_release_channel(host->dma_chan_rxtx);
+	mmc_free_host(mmc);
+
+	return ret;
+}
+
+static int bcm2835_mmc_remove(struct platform_device *pdev)
+{
+	struct bcm2835_host *host = platform_get_drvdata(pdev);
+	unsigned long flags;
+	int dead;
+	u32 scratch;
+
+	dead = 0;
+	scratch = bcm2835_mmc_readl(host, SDHCI_INT_STATUS);
+	if (scratch == (u32)-1)
+		dead = 1;
+
+
+	if (dead) {
+		spin_lock_irqsave(&host->lock, flags);
+
+		host->flags |= SDHCI_DEVICE_DEAD;
+
+		if (host->mrq) {
+			pr_err("%s: Controller removed during "
+				" transfer!\n", mmc_hostname(host->mmc));
+
+			host->mrq->cmd->error = -ENOMEDIUM;
+			tasklet_schedule(&host->finish_tasklet);
+		}
+
+		spin_unlock_irqrestore(&host->lock, flags);
+	}
+
+	mmc_remove_host(host->mmc);
+
+	if (!dead)
+		bcm2835_mmc_reset(host, SDHCI_RESET_ALL);
+
+	free_irq(host->irq, host);
+
+	del_timer_sync(&host->timer);
+
+	tasklet_kill(&host->finish_tasklet);
+
+	if (host->dma_chan_rxtx)
+		dma_release_channel(host->dma_chan_rxtx);
+
+	mmc_free_host(host->mmc);
+
+	return 0;
+}
+
+
+static const struct of_device_id bcm2835_mmc_match[] = {
+	{ .compatible = "brcm,bcm2835-mmc" },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, bcm2835_mmc_match);
+
+
+
+static struct platform_driver bcm2835_mmc_driver = {
+	.probe      = bcm2835_mmc_probe,
+	.remove     = bcm2835_mmc_remove,
+	.driver     = {
+		.name		= DRIVER_NAME,
+		.owner		= THIS_MODULE,
+		.of_match_table	= bcm2835_mmc_match,
+	},
+};
+module_platform_driver(bcm2835_mmc_driver);
+
+module_param(mmc_debug, uint, 0644);
+module_param(mmc_debug2, uint, 0644);
+MODULE_ALIAS("platform:mmc-bcm2835");
+MODULE_DESCRIPTION("BCM2835 SDHCI driver");
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Gellert Weisz");
diff -Nur --no-dereference linux-5.10.52-orig/drivers/mmc/host/bcm2835-sdhost.c linux-5.10.52-v7l+/drivers/mmc/host/bcm2835-sdhost.c
--- linux-5.10.52-orig/drivers/mmc/host/bcm2835-sdhost.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/mmc/host/bcm2835-sdhost.c	2021-07-25 16:46:09.518231331 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/*
+ * BCM2835 SD host driver.
+ *
+ * Author:      Phil Elwell <phil@raspberrypi.org>
+ *              Copyright (C) 2015-2016 Raspberry Pi (Trading) Ltd.
+ *
+ * Based on
+ *  mmc-bcm2835.c by Gellert Weisz
+ * which is, in turn, based on
+ *  sdhci-bcm2708.c by Broadcom
+ *  sdhci-bcm2835.c by Stephen Warren and Oleksandr Tymoshenko
+ *  sdhci.c and sdhci-pci.c by Pierre Ossman
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define FIFO_READ_THRESHOLD     4
+#define FIFO_WRITE_THRESHOLD    4
+#define ALLOW_CMD23_READ        1
+#define ALLOW_CMD23_WRITE       0
+#define ENABLE_LOG              1
+#define SDDATA_FIFO_PIO_BURST   8
+#define CMD_DALLY_US            1
+
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/mmc/mmc.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/sd.h>
+#include <linux/mmc/sdio.h>
+#include <linux/scatterlist.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/blkdev.h>
+#include <linux/dmaengine.h>
+#include <linux/dma-mapping.h>
+#include <linux/of_dma.h>
+#include <linux/time.h>
+#include <linux/workqueue.h>
+#include <linux/interrupt.h>
+#include <linux/highmem.h>
+#include <soc/bcm2835/raspberrypi-firmware.h>
+
+/* For mmc_card_blockaddr */
+#include "../core/card.h"
+
+#define DRIVER_NAME "sdhost-bcm2835"
+
+#define SDCMD  0x00 /* Command to SD card              - 16 R/W */
+#define SDARG  0x04 /* Argument to SD card             - 32 R/W */
+#define SDTOUT 0x08 /* Start value for timeout counter - 32 R/W */
+#define SDCDIV 0x0c /* Start value for clock divider   - 11 R/W */
+#define SDRSP0 0x10 /* SD card response (31:0)         - 32 R   */
+#define SDRSP1 0x14 /* SD card response (63:32)        - 32 R   */
+#define SDRSP2 0x18 /* SD card response (95:64)        - 32 R   */
+#define SDRSP3 0x1c /* SD card response (127:96)       - 32 R   */
+#define SDHSTS 0x20 /* SD host status                  - 11 R   */
+#define SDVDD  0x30 /* SD card power control           -  1 R/W */
+#define SDEDM  0x34 /* Emergency Debug Mode            - 13 R/W */
+#define SDHCFG 0x38 /* Host configuration              -  2 R/W */
+#define SDHBCT 0x3c /* Host byte count (debug)         - 32 R/W */
+#define SDDATA 0x40 /* Data to/from SD card            - 32 R/W */
+#define SDHBLC 0x50 /* Host block count (SDIO/SDHC)    -  9 R/W */
+
+#define SDCMD_NEW_FLAG                  0x8000
+#define SDCMD_FAIL_FLAG                 0x4000
+#define SDCMD_BUSYWAIT                  0x800
+#define SDCMD_NO_RESPONSE               0x400
+#define SDCMD_LONG_RESPONSE             0x200
+#define SDCMD_WRITE_CMD                 0x80
+#define SDCMD_READ_CMD                  0x40
+#define SDCMD_CMD_MASK                  0x3f
+
+#define SDCDIV_MAX_CDIV                 0x7ff
+
+#define SDHSTS_BUSY_IRPT                0x400
+#define SDHSTS_BLOCK_IRPT               0x200
+#define SDHSTS_SDIO_IRPT                0x100
+#define SDHSTS_REW_TIME_OUT             0x80
+#define SDHSTS_CMD_TIME_OUT             0x40
+#define SDHSTS_CRC16_ERROR              0x20
+#define SDHSTS_CRC7_ERROR               0x10
+#define SDHSTS_FIFO_ERROR               0x08
+/* Reserved */
+/* Reserved */
+#define SDHSTS_DATA_FLAG                0x01
+
+#define SDHSTS_TRANSFER_ERROR_MASK      (SDHSTS_CRC7_ERROR|SDHSTS_CRC16_ERROR|SDHSTS_REW_TIME_OUT|SDHSTS_FIFO_ERROR)
+#define SDHSTS_ERROR_MASK               (SDHSTS_CMD_TIME_OUT|SDHSTS_TRANSFER_ERROR_MASK)
+
+#define SDHCFG_BUSY_IRPT_EN     (1<<10)
+#define SDHCFG_BLOCK_IRPT_EN    (1<<8)
+#define SDHCFG_SDIO_IRPT_EN     (1<<5)
+#define SDHCFG_DATA_IRPT_EN     (1<<4)
+#define SDHCFG_SLOW_CARD        (1<<3)
+#define SDHCFG_WIDE_EXT_BUS     (1<<2)
+#define SDHCFG_WIDE_INT_BUS     (1<<1)
+#define SDHCFG_REL_CMD_LINE     (1<<0)
+
+#define SDEDM_FORCE_DATA_MODE   (1<<19)
+#define SDEDM_CLOCK_PULSE       (1<<20)
+#define SDEDM_BYPASS            (1<<21)
+
+#define SDEDM_WRITE_THRESHOLD_SHIFT 9
+#define SDEDM_READ_THRESHOLD_SHIFT 14
+#define SDEDM_THRESHOLD_MASK     0x1f
+
+#define SDEDM_FSM_MASK           0xf
+#define SDEDM_FSM_IDENTMODE      0x0
+#define SDEDM_FSM_DATAMODE       0x1
+#define SDEDM_FSM_READDATA       0x2
+#define SDEDM_FSM_WRITEDATA      0x3
+#define SDEDM_FSM_READWAIT       0x4
+#define SDEDM_FSM_READCRC        0x5
+#define SDEDM_FSM_WRITECRC       0x6
+#define SDEDM_FSM_WRITEWAIT1     0x7
+#define SDEDM_FSM_POWERDOWN      0x8
+#define SDEDM_FSM_POWERUP        0x9
+#define SDEDM_FSM_WRITESTART1    0xa
+#define SDEDM_FSM_WRITESTART2    0xb
+#define SDEDM_FSM_GENPULSES      0xc
+#define SDEDM_FSM_WRITEWAIT2     0xd
+#define SDEDM_FSM_STARTPOWDOWN   0xf
+
+#define SDDATA_FIFO_WORDS        16
+
+#define USE_CMD23_FLAGS          ((ALLOW_CMD23_READ * MMC_DATA_READ) | \
+				  (ALLOW_CMD23_WRITE * MMC_DATA_WRITE))
+
+#define MHZ 1000000
+
+
+struct bcm2835_host {
+	spinlock_t		lock;
+
+	void __iomem		*ioaddr;
+	phys_addr_t		bus_addr;
+
+	struct mmc_host		*mmc;
+
+	u32			pio_timeout;	/* In jiffies */
+
+	int			clock;		/* Current clock speed */
+
+	bool			slow_card;	/* Force 11-bit divisor */
+
+	unsigned int		max_clk;	/* Max possible freq */
+
+	struct tasklet_struct	finish_tasklet;	/* Tasklet structures */
+
+	struct work_struct	cmd_wait_wq;	/* Workqueue function */
+
+	struct timer_list	timer;		/* Timer for timeouts */
+
+	struct sg_mapping_iter	sg_miter;	/* SG state for PIO */
+	unsigned int		blocks;		/* remaining PIO blocks */
+
+	int			irq;		/* Device IRQ */
+
+	u32			cmd_quick_poll_retries;
+	u32			ns_per_fifo_word;
+
+	/* cached registers */
+	u32			hcfg;
+	u32			cdiv;
+
+	struct mmc_request		*mrq;			/* Current request */
+	struct mmc_command		*cmd;			/* Current command */
+	struct mmc_data			*data;			/* Current data request */
+	unsigned int			data_complete:1;	/* Data finished before cmd */
+
+	unsigned int			flush_fifo:1;		/* Drain the fifo when finishing */
+
+	unsigned int			use_busy:1;		/* Wait for busy interrupt */
+
+	unsigned int			use_sbc:1;		/* Send CMD23 */
+
+	unsigned int			debug:1;		/* Enable debug output */
+	unsigned int			firmware_sets_cdiv:1;	/* Let the firmware manage the clock */
+	unsigned int			reset_clock:1;		/* Reset the clock fore the next request */
+
+	/*DMA part*/
+	struct dma_chan			*dma_chan_rxtx;		/* DMA channel for reads and writes */
+	struct dma_chan			*dma_chan;		/* Channel in use */
+	struct dma_slave_config		dma_cfg_rx;
+	struct dma_slave_config		dma_cfg_tx;
+	struct dma_async_tx_descriptor	*dma_desc;
+	u32				dma_dir;
+	u32				drain_words;
+	struct page 			*drain_page;
+	u32				drain_offset;
+
+	bool				allow_dma;
+	bool				use_dma;
+	/*end of DMA part*/
+
+	int				max_delay;	/* maximum length of time spent waiting */
+	struct timespec64		stop_time;	/* when the last stop was issued */
+	u32				delay_after_stop; /* minimum time between stop and subsequent data transfer */
+	u32				delay_after_this_stop; /* minimum time between this stop and subsequent data transfer */
+	u32				user_overclock_50; /* User's preferred frequency to use when 50MHz is requested (in MHz) */
+	u32				overclock_50;	/* frequency to use when 50MHz is requested (in MHz) */
+	u32				overclock;	/* Current frequency if overclocked, else zero */
+	u32				pio_limit;	/* Maximum block count for PIO (0 = always DMA) */
+
+	u32				sectors;	/* Cached card size in sectors */
+};
+
+#if ENABLE_LOG
+
+struct log_entry_struct {
+	char event[4];
+	u32 timestamp;
+	u32 param1;
+	u32 param2;
+};
+
+typedef struct log_entry_struct LOG_ENTRY_T;
+
+LOG_ENTRY_T *sdhost_log_buf;
+dma_addr_t sdhost_log_addr;
+static u32 sdhost_log_idx;
+static spinlock_t log_lock;
+static void __iomem *timer_base;
+
+#define LOG_ENTRIES (256*1)
+#define LOG_SIZE (sizeof(LOG_ENTRY_T)*LOG_ENTRIES)
+
+static void log_init(struct device *dev, u32 bus_to_phys)
+{
+	spin_lock_init(&log_lock);
+	sdhost_log_buf = dma_alloc_coherent(dev, LOG_SIZE, &sdhost_log_addr,
+					     GFP_KERNEL);
+	if (sdhost_log_buf) {
+		pr_info("sdhost: log_buf @ %p (%llx)\n",
+			sdhost_log_buf, (u64)sdhost_log_addr);
+		timer_base = ioremap(bus_to_phys + 0x7e003000, SZ_4K);
+		if (!timer_base)
+			pr_err("sdhost: failed to remap timer\n");
+	}
+	else
+		pr_err("sdhost: failed to allocate log buf\n");
+}
+
+static void log_event_impl(const char *event, u32 param1, u32 param2)
+{
+	if (sdhost_log_buf) {
+		LOG_ENTRY_T *entry;
+		unsigned long flags;
+
+		spin_lock_irqsave(&log_lock, flags);
+
+		entry = sdhost_log_buf + sdhost_log_idx;
+		memcpy(entry->event, event, 4);
+		entry->timestamp = (readl(timer_base + 4) & 0x3fffffff) +
+			(smp_processor_id()<<30);
+		entry->param1 = param1;
+		entry->param2 = param2;
+		sdhost_log_idx = (sdhost_log_idx + 1) % LOG_ENTRIES;
+
+		spin_unlock_irqrestore(&log_lock, flags);
+	}
+}
+
+static void log_dump(void)
+{
+	if (sdhost_log_buf) {
+		LOG_ENTRY_T *entry;
+		unsigned long flags;
+		int idx;
+
+		spin_lock_irqsave(&log_lock, flags);
+
+		idx = sdhost_log_idx;
+		do {
+			entry = sdhost_log_buf + idx;
+			if (entry->event[0] != '\0')
+				pr_info("[%08x] %.4s %x %x\n",
+				       entry->timestamp,
+				       entry->event,
+				       entry->param1,
+				       entry->param2);
+			idx = (idx + 1) % LOG_ENTRIES;
+		} while (idx != sdhost_log_idx);
+
+		spin_unlock_irqrestore(&log_lock, flags);
+	}
+}
+
+#define log_event(event, param1, param2) log_event_impl(event, (u32)(uintptr_t)param1, (u32)(uintptr_t)param2)
+
+#else
+
+#define log_init(x) (void)0
+#define log_event(event, param1, param2) (void)0
+#define log_dump() (void)0
+
+#endif
+
+static inline void bcm2835_sdhost_write(struct bcm2835_host *host, u32 val, int reg)
+{
+	writel(val, host->ioaddr + reg);
+}
+
+static inline u32 bcm2835_sdhost_read(struct bcm2835_host *host, int reg)
+{
+	return readl(host->ioaddr + reg);
+}
+
+static inline u32 bcm2835_sdhost_read_relaxed(struct bcm2835_host *host, int reg)
+{
+	return readl_relaxed(host->ioaddr + reg);
+}
+
+static void bcm2835_sdhost_dumpcmd(struct bcm2835_host *host,
+				   struct mmc_command *cmd,
+				   const char *label)
+{
+	if (cmd)
+		pr_info("%s:%c%s op %d arg 0x%x flags 0x%x - resp %08x %08x %08x %08x, err %d\n",
+			mmc_hostname(host->mmc),
+			(cmd == host->cmd) ? '>' : ' ',
+			label, cmd->opcode, cmd->arg, cmd->flags,
+			cmd->resp[0], cmd->resp[1], cmd->resp[2], cmd->resp[3],
+			cmd->error);
+}
+
+static void bcm2835_sdhost_dumpregs(struct bcm2835_host *host)
+{
+	if (host->mrq)
+	{
+		bcm2835_sdhost_dumpcmd(host, host->mrq->sbc, "sbc");
+		bcm2835_sdhost_dumpcmd(host, host->mrq->cmd, "cmd");
+		if (host->mrq->data)
+			pr_info("%s: data blocks %x blksz %x - err %d\n",
+			       mmc_hostname(host->mmc),
+			       host->mrq->data->blocks,
+			       host->mrq->data->blksz,
+			       host->mrq->data->error);
+		bcm2835_sdhost_dumpcmd(host, host->mrq->stop, "stop");
+	}
+
+	pr_info("%s: =========== REGISTER DUMP ===========\n",
+		mmc_hostname(host->mmc));
+
+	pr_info("%s: SDCMD  0x%08x\n",
+		mmc_hostname(host->mmc),
+		bcm2835_sdhost_read(host, SDCMD));
+	pr_info("%s: SDARG  0x%08x\n",
+		mmc_hostname(host->mmc),
+		bcm2835_sdhost_read(host, SDARG));
+	pr_info("%s: SDTOUT 0x%08x\n",
+		mmc_hostname(host->mmc),
+		bcm2835_sdhost_read(host, SDTOUT));
+	pr_info("%s: SDCDIV 0x%08x\n",
+		mmc_hostname(host->mmc),
+		bcm2835_sdhost_read(host, SDCDIV));
+	pr_info("%s: SDRSP0 0x%08x\n",
+		mmc_hostname(host->mmc),
+		bcm2835_sdhost_read(host, SDRSP0));
+	pr_info("%s: SDRSP1 0x%08x\n",
+		mmc_hostname(host->mmc),
+		bcm2835_sdhost_read(host, SDRSP1));
+	pr_info("%s: SDRSP2 0x%08x\n",
+		mmc_hostname(host->mmc),
+		bcm2835_sdhost_read(host, SDRSP2));
+	pr_info("%s: SDRSP3 0x%08x\n",
+		mmc_hostname(host->mmc),
+		bcm2835_sdhost_read(host, SDRSP3));
+	pr_info("%s: SDHSTS 0x%08x\n",
+		mmc_hostname(host->mmc),
+		bcm2835_sdhost_read(host, SDHSTS));
+	pr_info("%s: SDVDD  0x%08x\n",
+		mmc_hostname(host->mmc),
+		bcm2835_sdhost_read(host, SDVDD));
+	pr_info("%s: SDEDM  0x%08x\n",
+		mmc_hostname(host->mmc),
+		bcm2835_sdhost_read(host, SDEDM));
+	pr_info("%s: SDHCFG 0x%08x\n",
+		mmc_hostname(host->mmc),
+		bcm2835_sdhost_read(host, SDHCFG));
+	pr_info("%s: SDHBCT 0x%08x\n",
+		mmc_hostname(host->mmc),
+		bcm2835_sdhost_read(host, SDHBCT));
+	pr_info("%s: SDHBLC 0x%08x\n",
+		mmc_hostname(host->mmc),
+		bcm2835_sdhost_read(host, SDHBLC));
+
+	pr_info("%s: ===========================================\n",
+		mmc_hostname(host->mmc));
+}
+
+static void bcm2835_sdhost_set_power(struct bcm2835_host *host, bool on)
+{
+	bcm2835_sdhost_write(host, on ? 1 : 0, SDVDD);
+}
+
+static void bcm2835_sdhost_reset_internal(struct bcm2835_host *host)
+{
+	u32 temp;
+
+	if (host->debug)
+		pr_info("%s: reset\n", mmc_hostname(host->mmc));
+
+	bcm2835_sdhost_set_power(host, false);
+
+	bcm2835_sdhost_write(host, 0, SDCMD);
+	bcm2835_sdhost_write(host, 0, SDARG);
+	bcm2835_sdhost_write(host, 0xf00000, SDTOUT);
+	bcm2835_sdhost_write(host, 0, SDCDIV);
+	bcm2835_sdhost_write(host, 0x7f8, SDHSTS); /* Write 1s to clear */
+	bcm2835_sdhost_write(host, 0, SDHCFG);
+	bcm2835_sdhost_write(host, 0, SDHBCT);
+	bcm2835_sdhost_write(host, 0, SDHBLC);
+
+	/* Limit fifo usage due to silicon bug */
+	temp = bcm2835_sdhost_read(host, SDEDM);
+	temp &= ~((SDEDM_THRESHOLD_MASK<<SDEDM_READ_THRESHOLD_SHIFT) |
+		  (SDEDM_THRESHOLD_MASK<<SDEDM_WRITE_THRESHOLD_SHIFT));
+	temp |= (FIFO_READ_THRESHOLD << SDEDM_READ_THRESHOLD_SHIFT) |
+		(FIFO_WRITE_THRESHOLD << SDEDM_WRITE_THRESHOLD_SHIFT);
+	bcm2835_sdhost_write(host, temp, SDEDM);
+	mdelay(10);
+	bcm2835_sdhost_set_power(host, true);
+	mdelay(10);
+	host->clock = 0;
+	host->sectors = 0;
+	bcm2835_sdhost_write(host, host->hcfg, SDHCFG);
+	bcm2835_sdhost_write(host, SDCDIV_MAX_CDIV, SDCDIV);
+}
+
+static void bcm2835_sdhost_reset(struct mmc_host *mmc)
+{
+	struct bcm2835_host *host = mmc_priv(mmc);
+	unsigned long flags;
+	spin_lock_irqsave(&host->lock, flags);
+	log_event("RST<", 0, 0);
+
+	bcm2835_sdhost_reset_internal(host);
+
+	spin_unlock_irqrestore(&host->lock, flags);
+}
+
+static void bcm2835_sdhost_set_ios(struct mmc_host *mmc, struct mmc_ios *ios);
+
+static void bcm2835_sdhost_init(struct bcm2835_host *host, int soft)
+{
+	pr_debug("bcm2835_sdhost_init(%d)\n", soft);
+
+	/* Set interrupt enables */
+	host->hcfg = SDHCFG_BUSY_IRPT_EN;
+
+	bcm2835_sdhost_reset_internal(host);
+
+	if (soft) {
+		/* force clock reconfiguration */
+		host->clock = 0;
+		bcm2835_sdhost_set_ios(host->mmc, &host->mmc->ios);
+	}
+}
+
+static void bcm2835_sdhost_wait_transfer_complete(struct bcm2835_host *host)
+{
+	int timediff;
+	u32 alternate_idle;
+	u32 edm;
+
+	alternate_idle = (host->mrq->data->flags & MMC_DATA_READ) ?
+		SDEDM_FSM_READWAIT : SDEDM_FSM_WRITESTART1;
+
+	edm = bcm2835_sdhost_read(host, SDEDM);
+
+	log_event("WTC<", edm, 0);
+
+	timediff = 0;
+
+	while (1) {
+		u32 fsm = edm & SDEDM_FSM_MASK;
+		if ((fsm == SDEDM_FSM_IDENTMODE) ||
+		    (fsm == SDEDM_FSM_DATAMODE))
+			break;
+		if (fsm == alternate_idle) {
+			bcm2835_sdhost_write(host,
+					     edm | SDEDM_FORCE_DATA_MODE,
+					     SDEDM);
+			break;
+		}
+
+		timediff++;
+		if (timediff == 100000) {
+			pr_err("%s: wait_transfer_complete - still waiting after %d retries\n",
+			       mmc_hostname(host->mmc),
+			       timediff);
+			log_dump();
+			bcm2835_sdhost_dumpregs(host);
+			host->mrq->data->error = -ETIMEDOUT;
+			log_event("WTC!", edm, 0);
+			return;
+		}
+		cpu_relax();
+		edm = bcm2835_sdhost_read(host, SDEDM);
+	}
+	log_event("WTC>", edm, 0);
+}
+
+static void bcm2835_sdhost_finish_data(struct bcm2835_host *host);
+
+static void bcm2835_sdhost_dma_complete(void *param)
+{
+	struct bcm2835_host *host = param;
+	struct mmc_data *data = host->data;
+	unsigned long flags;
+
+	spin_lock_irqsave(&host->lock, flags);
+	log_event("DMA<", host->data, bcm2835_sdhost_read(host, SDHSTS));
+	log_event("DMA ", bcm2835_sdhost_read(host, SDCMD),
+		  bcm2835_sdhost_read(host, SDEDM));
+
+	if (host->dma_chan) {
+		dma_unmap_sg(host->dma_chan->device->dev,
+			     data->sg, data->sg_len,
+			     host->dma_dir);
+
+		host->dma_chan = NULL;
+	}
+
+	if (host->drain_words) {
+		void *page;
+		u32 *buf;
+
+		if (host->drain_offset & PAGE_MASK) {
+			host->drain_page += host->drain_offset >> PAGE_SHIFT;
+			host->drain_offset &= ~PAGE_MASK;
+		}
+
+		page = kmap_atomic(host->drain_page);
+		buf = page + host->drain_offset;
+
+		while (host->drain_words) {
+			u32 edm = bcm2835_sdhost_read(host, SDEDM);
+			if ((edm >> 4) & 0x1f)
+				*(buf++) = bcm2835_sdhost_read(host,
+							       SDDATA);
+			host->drain_words--;
+		}
+
+		kunmap_atomic(page);
+	}
+
+	bcm2835_sdhost_finish_data(host);
+
+	log_event("DMA>", host->data, 0);
+	spin_unlock_irqrestore(&host->lock, flags);
+}
+
+static void bcm2835_sdhost_read_block_pio(struct bcm2835_host *host)
+{
+	unsigned long flags;
+	size_t blksize, len;
+	u32 *buf;
+	unsigned long wait_max;
+
+	blksize = host->data->blksz;
+
+	wait_max = jiffies + msecs_to_jiffies(host->pio_timeout);
+
+	local_irq_save(flags);
+
+	while (blksize) {
+		int copy_words;
+		u32 hsts = 0;
+
+		if (!sg_miter_next(&host->sg_miter)) {
+			host->data->error = -EINVAL;
+			break;
+		}
+
+		len = min(host->sg_miter.length, blksize);
+		if (len % 4) {
+			host->data->error = -EINVAL;
+			break;
+		}
+
+		blksize -= len;
+		host->sg_miter.consumed = len;
+
+		buf = (u32 *)host->sg_miter.addr;
+
+		copy_words = len/4;
+
+		while (copy_words) {
+			int burst_words, words;
+			u32 edm;
+
+			burst_words = SDDATA_FIFO_PIO_BURST;
+			if (burst_words > copy_words)
+				burst_words = copy_words;
+			edm = bcm2835_sdhost_read(host, SDEDM);
+			words = ((edm >> 4) & 0x1f);
+
+			if (words < burst_words) {
+				int fsm_state = (edm & SDEDM_FSM_MASK);
+				if ((fsm_state != SDEDM_FSM_READDATA) &&
+				    (fsm_state != SDEDM_FSM_READWAIT) &&
+				    (fsm_state != SDEDM_FSM_READCRC)) {
+					hsts = bcm2835_sdhost_read(host,
+								   SDHSTS);
+					pr_info("%s: fsm %x, hsts %x\n",
+					       mmc_hostname(host->mmc),
+					       fsm_state, hsts);
+					if (hsts & SDHSTS_ERROR_MASK)
+						break;
+				}
+
+				if (time_after(jiffies, wait_max)) {
+					pr_err("%s: PIO read timeout - EDM %x\n",
+					       mmc_hostname(host->mmc),
+					       edm);
+					hsts = SDHSTS_REW_TIME_OUT;
+					break;
+				}
+				ndelay((burst_words - words) *
+				       host->ns_per_fifo_word);
+				continue;
+			} else if (words > copy_words) {
+				words = copy_words;
+			}
+
+			copy_words -= words;
+
+			while (words) {
+				*(buf++) = bcm2835_sdhost_read(host, SDDATA);
+				words--;
+			}
+		}
+
+		if (hsts & SDHSTS_ERROR_MASK)
+			break;
+	}
+
+	sg_miter_stop(&host->sg_miter);
+
+	local_irq_restore(flags);
+}
+
+static void bcm2835_sdhost_write_block_pio(struct bcm2835_host *host)
+{
+	unsigned long flags;
+	size_t blksize, len;
+	u32 *buf;
+	unsigned long wait_max;
+
+	blksize = host->data->blksz;
+
+	wait_max = jiffies + msecs_to_jiffies(host->pio_timeout);
+
+	local_irq_save(flags);
+
+	while (blksize) {
+		int copy_words;
+		u32 hsts = 0;
+
+		if (!sg_miter_next(&host->sg_miter)) {
+			host->data->error = -EINVAL;
+			break;
+		}
+
+		len = min(host->sg_miter.length, blksize);
+		if (len % 4) {
+			host->data->error = -EINVAL;
+			break;
+		}
+
+		blksize -= len;
+		host->sg_miter.consumed = len;
+
+		buf = (u32 *)host->sg_miter.addr;
+
+		copy_words = len/4;
+
+		while (copy_words) {
+			int burst_words, words;
+			u32 edm;
+
+			burst_words = SDDATA_FIFO_PIO_BURST;
+			if (burst_words > copy_words)
+				burst_words = copy_words;
+			edm = bcm2835_sdhost_read(host, SDEDM);
+			words = SDDATA_FIFO_WORDS - ((edm >> 4) & 0x1f);
+
+			if (words < burst_words) {
+				int fsm_state = (edm & SDEDM_FSM_MASK);
+				if ((fsm_state != SDEDM_FSM_WRITEDATA) &&
+				    (fsm_state != SDEDM_FSM_WRITESTART1) &&
+				    (fsm_state != SDEDM_FSM_WRITESTART2)) {
+					hsts = bcm2835_sdhost_read(host,
+								   SDHSTS);
+					pr_info("%s: fsm %x, hsts %x\n",
+					       mmc_hostname(host->mmc),
+					       fsm_state, hsts);
+					if (hsts & SDHSTS_ERROR_MASK)
+						break;
+				}
+
+				if (time_after(jiffies, wait_max)) {
+					pr_err("%s: PIO write timeout - EDM %x\n",
+					       mmc_hostname(host->mmc),
+					       edm);
+					hsts = SDHSTS_REW_TIME_OUT;
+					break;
+				}
+				ndelay((burst_words - words) *
+				       host->ns_per_fifo_word);
+				continue;
+			} else if (words > copy_words) {
+				words = copy_words;
+			}
+
+			copy_words -= words;
+
+			while (words) {
+				bcm2835_sdhost_write(host, *(buf++), SDDATA);
+				words--;
+			}
+		}
+
+		if (hsts & SDHSTS_ERROR_MASK)
+			break;
+	}
+
+	sg_miter_stop(&host->sg_miter);
+
+	local_irq_restore(flags);
+}
+
+static void bcm2835_sdhost_transfer_pio(struct bcm2835_host *host)
+{
+	u32 sdhsts;
+	bool is_read;
+	BUG_ON(!host->data);
+	log_event("XFP<", host->data, host->blocks);
+
+	is_read = (host->data->flags & MMC_DATA_READ) != 0;
+	if (is_read)
+		bcm2835_sdhost_read_block_pio(host);
+	else
+		bcm2835_sdhost_write_block_pio(host);
+
+	sdhsts = bcm2835_sdhost_read(host, SDHSTS);
+	if (sdhsts & (SDHSTS_CRC16_ERROR |
+		      SDHSTS_CRC7_ERROR |
+		      SDHSTS_FIFO_ERROR)) {
+		pr_err("%s: %s transfer error - HSTS %x\n",
+		       mmc_hostname(host->mmc),
+		       is_read ? "read" : "write",
+		       sdhsts);
+		host->data->error = -EILSEQ;
+	} else if ((sdhsts & (SDHSTS_CMD_TIME_OUT |
+			      SDHSTS_REW_TIME_OUT))) {
+		pr_err("%s: %s timeout error - HSTS %x\n",
+		       mmc_hostname(host->mmc),
+		       is_read ? "read" : "write",
+		       sdhsts);
+		host->data->error = -ETIMEDOUT;
+	}
+	log_event("XFP>", host->data, host->blocks);
+}
+
+static void bcm2835_sdhost_prepare_dma(struct bcm2835_host *host,
+	struct mmc_data *data)
+{
+	int len, dir_data, dir_slave;
+	struct dma_async_tx_descriptor *desc = NULL;
+	struct dma_chan *dma_chan;
+
+	log_event("PRD<", data, 0);
+	pr_debug("bcm2835_sdhost_prepare_dma()\n");
+
+	dma_chan = host->dma_chan_rxtx;
+	if (data->flags & MMC_DATA_READ) {
+		dir_data = DMA_FROM_DEVICE;
+		dir_slave = DMA_DEV_TO_MEM;
+	} else {
+		dir_data = DMA_TO_DEVICE;
+		dir_slave = DMA_MEM_TO_DEV;
+	}
+	log_event("PRD1", dma_chan, 0);
+
+	BUG_ON(!dma_chan->device);
+	BUG_ON(!dma_chan->device->dev);
+	BUG_ON(!data->sg);
+
+	/* The block doesn't manage the FIFO DREQs properly for multi-block
+	   transfers, so don't attempt to DMA the final few words.
+	   Unfortunately this requires the final sg entry to be trimmed.
+	   N.B. This code demands that the overspill is contained in
+	   a single sg entry.
+	*/
+
+	host->drain_words = 0;
+	if ((data->blocks > 1) && (dir_data == DMA_FROM_DEVICE)) {
+		struct scatterlist *sg;
+		u32 len;
+		int i;
+
+		len = min((u32)(FIFO_READ_THRESHOLD - 1) * 4,
+			  (u32)data->blocks * data->blksz);
+
+		for_each_sg(data->sg, sg, data->sg_len, i) {
+			if (sg_is_last(sg)) {
+				BUG_ON(sg->length < len);
+				sg->length -= len;
+				host->drain_page = sg_page(sg);
+				host->drain_offset = sg->offset + sg->length;
+			}
+		}
+		host->drain_words = len/4;
+	}
+
+	/* The parameters have already been validated, so this will not fail */
+	(void)dmaengine_slave_config(dma_chan,
+				     (dir_data == DMA_FROM_DEVICE) ?
+				     &host->dma_cfg_rx :
+				     &host->dma_cfg_tx);
+
+	len = dma_map_sg(dma_chan->device->dev, data->sg, data->sg_len,
+			 dir_data);
+
+	log_event("PRD2", len, 0);
+	if (len > 0)
+		desc = dmaengine_prep_slave_sg(dma_chan, data->sg,
+					       len, dir_slave,
+					       DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+	log_event("PRD3", desc, 0);
+
+	if (desc) {
+		desc->callback = bcm2835_sdhost_dma_complete;
+		desc->callback_param = host;
+		host->dma_desc = desc;
+		host->dma_chan = dma_chan;
+		host->dma_dir = dir_data;
+	}
+	log_event("PDM>", data, 0);
+}
+
+static void bcm2835_sdhost_start_dma(struct bcm2835_host *host)
+{
+	log_event("SDMA", host->data, host->dma_chan);
+	dmaengine_submit(host->dma_desc);
+	dma_async_issue_pending(host->dma_chan);
+}
+
+static void bcm2835_sdhost_set_transfer_irqs(struct bcm2835_host *host)
+{
+	u32 all_irqs = SDHCFG_DATA_IRPT_EN | SDHCFG_BLOCK_IRPT_EN |
+		SDHCFG_BUSY_IRPT_EN;
+	if (host->dma_desc)
+		host->hcfg = (host->hcfg & ~all_irqs) |
+			SDHCFG_BUSY_IRPT_EN;
+	else
+		host->hcfg = (host->hcfg & ~all_irqs) |
+			SDHCFG_DATA_IRPT_EN |
+			SDHCFG_BUSY_IRPT_EN;
+
+	bcm2835_sdhost_write(host, host->hcfg, SDHCFG);
+}
+
+static void bcm2835_sdhost_prepare_data(struct bcm2835_host *host, struct mmc_command *cmd)
+{
+	struct mmc_data *data = cmd->data;
+
+	WARN_ON(host->data);
+
+	host->data = data;
+	if (!data)
+		return;
+
+	/* Sanity checks */
+	BUG_ON(data->blksz * data->blocks > 524288);
+	BUG_ON(data->blksz > host->mmc->max_blk_size);
+	BUG_ON(data->blocks > 65535);
+
+	host->data_complete = 0;
+	host->flush_fifo = 0;
+	host->data->bytes_xfered = 0;
+
+	if (!host->sectors && host->mmc->card) {
+		struct mmc_card *card = host->mmc->card;
+		if (!mmc_card_sd(card) && mmc_card_blockaddr(card)) {
+			/*
+			 * The EXT_CSD sector count is in number of 512 byte
+			 * sectors.
+			 */
+			host->sectors = card->ext_csd.sectors;
+		} else {
+			/*
+			 * The CSD capacity field is in units of read_blkbits.
+			 * set_capacity takes units of 512 bytes.
+			 */
+			host->sectors = card->csd.capacity <<
+				(card->csd.read_blkbits - 9);
+		}
+	}
+
+	if (!host->dma_desc) {
+		/* Use PIO */
+		int flags = SG_MITER_ATOMIC;
+
+		if (data->flags & MMC_DATA_READ)
+			flags |= SG_MITER_TO_SG;
+		else
+			flags |= SG_MITER_FROM_SG;
+		sg_miter_start(&host->sg_miter, data->sg, data->sg_len, flags);
+		host->blocks = data->blocks;
+	}
+
+	bcm2835_sdhost_set_transfer_irqs(host);
+
+	bcm2835_sdhost_write(host, data->blksz, SDHBCT);
+	bcm2835_sdhost_write(host, data->blocks, SDHBLC);
+
+	BUG_ON(!host->data);
+}
+
+bool bcm2835_sdhost_send_command(struct bcm2835_host *host,
+				 struct mmc_command *cmd)
+{
+	u32 sdcmd, sdhsts;
+	unsigned long timeout;
+	int delay;
+
+	WARN_ON(host->cmd);
+	log_event("CMD<", cmd->opcode, cmd->arg);
+
+	if (cmd->data)
+		pr_debug("%s: send_command %d 0x%x "
+			 "(flags 0x%x) - %s %d*%d\n",
+			 mmc_hostname(host->mmc),
+			 cmd->opcode, cmd->arg, cmd->flags,
+			 (cmd->data->flags & MMC_DATA_READ) ?
+			 "read" : "write", cmd->data->blocks,
+			 cmd->data->blksz);
+	else
+		pr_debug("%s: send_command %d 0x%x (flags 0x%x)\n",
+			 mmc_hostname(host->mmc),
+			 cmd->opcode, cmd->arg, cmd->flags);
+
+	/* Wait max 100 ms */
+	timeout = 10000;
+
+	while (bcm2835_sdhost_read(host, SDCMD) & SDCMD_NEW_FLAG) {
+		if (timeout == 0) {
+			pr_warn("%s: previous command never completed.\n",
+				mmc_hostname(host->mmc));
+			if (host->debug)
+				bcm2835_sdhost_dumpregs(host);
+			cmd->error = -EILSEQ;
+			tasklet_schedule(&host->finish_tasklet);
+			return false;
+		}
+		timeout--;
+		udelay(10);
+	}
+
+	delay = (10000 - timeout)/100;
+	if (delay > host->max_delay) {
+		host->max_delay = delay;
+		pr_warn("%s: controller hung for %d ms\n",
+			   mmc_hostname(host->mmc),
+			   host->max_delay);
+	}
+
+	timeout = jiffies;
+	if (!cmd->data && cmd->busy_timeout > 9000)
+		timeout += DIV_ROUND_UP(cmd->busy_timeout, 1000) * HZ + HZ;
+	else
+		timeout += 10 * HZ;
+	mod_timer(&host->timer, timeout);
+
+	host->cmd = cmd;
+
+	/* Clear any error flags */
+	sdhsts = bcm2835_sdhost_read(host, SDHSTS);
+	if (sdhsts & SDHSTS_ERROR_MASK)
+		bcm2835_sdhost_write(host, sdhsts, SDHSTS);
+
+	if ((cmd->flags & MMC_RSP_136) && (cmd->flags & MMC_RSP_BUSY)) {
+		pr_err("%s: unsupported response type!\n",
+			mmc_hostname(host->mmc));
+		cmd->error = -EINVAL;
+		tasklet_schedule(&host->finish_tasklet);
+		return false;
+	}
+
+	bcm2835_sdhost_prepare_data(host, cmd);
+
+	bcm2835_sdhost_write(host, cmd->arg, SDARG);
+
+	sdcmd = cmd->opcode & SDCMD_CMD_MASK;
+
+	host->use_busy = 0;
+	if (!(cmd->flags & MMC_RSP_PRESENT)) {
+		sdcmd |= SDCMD_NO_RESPONSE;
+	} else {
+		if (cmd->flags & MMC_RSP_136)
+			sdcmd |= SDCMD_LONG_RESPONSE;
+		if (cmd->flags & MMC_RSP_BUSY) {
+			sdcmd |= SDCMD_BUSYWAIT;
+			host->use_busy = 1;
+		}
+	}
+
+	if (cmd->data) {
+		log_event("CMDD", cmd->data->blocks, cmd->data->blksz);
+		if (host->delay_after_this_stop) {
+			struct timespec64 now;
+			int time_since_stop;
+
+			ktime_get_real_ts64(&now);
+			time_since_stop = now.tv_sec - host->stop_time.tv_sec;
+			if (time_since_stop < 2) {
+				/* Possibly less than one second */
+				time_since_stop = time_since_stop * 1000000 +
+					(now.tv_nsec - host->stop_time.tv_nsec)/1000;
+				if (time_since_stop <
+				    host->delay_after_this_stop)
+					udelay(host->delay_after_this_stop -
+					       time_since_stop);
+			}
+		}
+
+		host->delay_after_this_stop = host->delay_after_stop;
+		if ((cmd->data->flags & MMC_DATA_READ) && !host->use_sbc) {
+			/* See if read crosses one of the hazardous sectors */
+			u32 first_blk, last_blk;
+
+			/* Intentionally include the following sector because
+			   without CMD23/SBC the read may run on. */
+			first_blk = host->mrq->cmd->arg;
+			last_blk = first_blk + cmd->data->blocks;
+
+			if (((last_blk >= (host->sectors - 64)) &&
+			     (first_blk <= (host->sectors - 64))) ||
+			    ((last_blk >= (host->sectors - 32)) &&
+			     (first_blk <= (host->sectors - 32)))) {
+				host->delay_after_this_stop =
+					max(250u, host->delay_after_stop);
+			}
+		}
+
+		if (cmd->data->flags & MMC_DATA_WRITE)
+			sdcmd |= SDCMD_WRITE_CMD;
+		if (cmd->data->flags & MMC_DATA_READ)
+			sdcmd |= SDCMD_READ_CMD;
+	}
+
+	bcm2835_sdhost_write(host, sdcmd | SDCMD_NEW_FLAG, SDCMD);
+
+	return true;
+}
+
+static void bcm2835_sdhost_finish_command(struct bcm2835_host *host,
+					  unsigned long *irq_flags);
+static void bcm2835_sdhost_transfer_complete(struct bcm2835_host *host);
+
+static void bcm2835_sdhost_finish_data(struct bcm2835_host *host)
+{
+	struct mmc_data *data;
+
+	data = host->data;
+	BUG_ON(!data);
+
+	log_event("FDA<", host->mrq, host->cmd);
+	pr_debug("finish_data(error %d, stop %d, sbc %d)\n",
+	       data->error, data->stop ? 1 : 0,
+	       host->mrq->sbc ? 1 : 0);
+
+	host->hcfg &= ~(SDHCFG_DATA_IRPT_EN | SDHCFG_BLOCK_IRPT_EN);
+	bcm2835_sdhost_write(host, host->hcfg, SDHCFG);
+
+	data->bytes_xfered = data->error ? 0 : (data->blksz * data->blocks);
+
+	host->data_complete = 1;
+
+	if (host->cmd) {
+		/*
+		 * Data managed to finish before the
+		 * command completed. Make sure we do
+		 * things in the proper order.
+		 */
+		pr_debug("Finished early - HSTS %x\n",
+			 bcm2835_sdhost_read(host, SDHSTS));
+	}
+	else
+		bcm2835_sdhost_transfer_complete(host);
+	log_event("FDA>", host->mrq, host->cmd);
+}
+
+static void bcm2835_sdhost_transfer_complete(struct bcm2835_host *host)
+{
+	struct mmc_data *data;
+
+	BUG_ON(host->cmd);
+	BUG_ON(!host->data);
+	BUG_ON(!host->data_complete);
+
+	data = host->data;
+	host->data = NULL;
+
+	log_event("TCM<", data, data->error);
+	pr_debug("transfer_complete(error %d, stop %d)\n",
+	       data->error, data->stop ? 1 : 0);
+
+	/*
+	 * Need to send CMD12 if -
+	 * a) open-ended multiblock transfer (no CMD23)
+	 * b) error in multiblock transfer
+	 */
+	if (host->mrq->stop && (data->error || !host->use_sbc)) {
+		if (bcm2835_sdhost_send_command(host, host->mrq->stop)) {
+			/* No busy, so poll for completion */
+			if (!host->use_busy)
+				bcm2835_sdhost_finish_command(host, NULL);
+
+			if (host->delay_after_this_stop)
+				ktime_get_real_ts64(&host->stop_time);
+		}
+	} else {
+		bcm2835_sdhost_wait_transfer_complete(host);
+		tasklet_schedule(&host->finish_tasklet);
+	}
+	log_event("TCM>", data, 0);
+}
+
+/* If irq_flags is valid, the caller is in a thread context and is allowed
+   to sleep */
+static void bcm2835_sdhost_finish_command(struct bcm2835_host *host,
+					  unsigned long *irq_flags)
+{
+	u32 sdcmd;
+	u32 retries;
+#ifdef DEBUG
+	struct timespec64 before, after;
+	int timediff = 0;
+#endif
+
+	log_event("FCM<", host->mrq, host->cmd);
+	pr_debug("finish_command(%x)\n", bcm2835_sdhost_read(host, SDCMD));
+
+	BUG_ON(!host->cmd || !host->mrq);
+
+	/* Poll quickly at first */
+
+	retries = host->cmd_quick_poll_retries;
+	if (!retries) {
+		/* Work out how many polls take 1us by timing 10us */
+		struct timespec64 start, now;
+		int us_diff;
+
+		retries = 1;
+		do {
+			int i;
+
+			retries *= 2;
+
+			ktime_get_real_ts64(&start);
+
+			for (i = 0; i < retries; i++) {
+				cpu_relax();
+				sdcmd = bcm2835_sdhost_read(host, SDCMD);
+			}
+
+			ktime_get_real_ts64(&now);
+			us_diff = (now.tv_sec - start.tv_sec) * 1000000 +
+				(now.tv_nsec - start.tv_nsec)/1000;
+		} while (us_diff < 10);
+
+		host->cmd_quick_poll_retries = ((retries * us_diff + 9)*CMD_DALLY_US)/10 + 1;
+		retries = 1; // We've already waited long enough this time
+	}
+
+	for (sdcmd = bcm2835_sdhost_read(host, SDCMD);
+	     (sdcmd & SDCMD_NEW_FLAG) && retries;
+	     retries--) {
+		cpu_relax();
+		sdcmd = bcm2835_sdhost_read(host, SDCMD);
+	}
+
+	if (!retries) {
+		unsigned long wait_max;
+
+		if (!irq_flags) {
+			/* Schedule the work */
+			log_event("CWWQ", 0, 0);
+			schedule_work(&host->cmd_wait_wq);
+			return;
+		}
+
+		/* Wait max 100 ms */
+		wait_max = jiffies + msecs_to_jiffies(100);
+		while (time_before(jiffies, wait_max)) {
+			spin_unlock_irqrestore(&host->lock, *irq_flags);
+			usleep_range(1, 10);
+			spin_lock_irqsave(&host->lock, *irq_flags);
+			sdcmd = bcm2835_sdhost_read(host, SDCMD);
+			if (!(sdcmd & SDCMD_NEW_FLAG))
+				break;
+		}
+	}
+
+	/* Check for errors */
+	if (sdcmd & SDCMD_NEW_FLAG) {
+		if (host->debug) {
+			pr_err("%s: command %d never completed.\n",
+			       mmc_hostname(host->mmc), host->cmd->opcode);
+			bcm2835_sdhost_dumpregs(host);
+		}
+		host->cmd->error = -EILSEQ;
+		tasklet_schedule(&host->finish_tasklet);
+		return;
+	} else if (sdcmd & SDCMD_FAIL_FLAG) {
+		u32 sdhsts = bcm2835_sdhost_read(host, SDHSTS);
+
+		/* Clear the errors */
+		bcm2835_sdhost_write(host, SDHSTS_ERROR_MASK, SDHSTS);
+
+		if (host->debug)
+			pr_info("%s: error detected - CMD %x, HSTS %03x, EDM %x\n",
+				mmc_hostname(host->mmc), sdcmd, sdhsts,
+				bcm2835_sdhost_read(host, SDEDM));
+
+		if ((sdhsts & SDHSTS_CRC7_ERROR) &&
+		    (host->cmd->opcode == 1)) {
+			if (host->debug)
+				pr_info("%s: ignoring CRC7 error for CMD1\n",
+					mmc_hostname(host->mmc));
+		} else {
+			u32 edm, fsm;
+
+			if (sdhsts & SDHSTS_CMD_TIME_OUT) {
+				if (host->debug)
+					pr_warn("%s: command %d timeout\n",
+					       mmc_hostname(host->mmc),
+					       host->cmd->opcode);
+				host->cmd->error = -ETIMEDOUT;
+			} else {
+				pr_warn("%s: unexpected command %d error\n",
+				       mmc_hostname(host->mmc),
+				       host->cmd->opcode);
+				host->cmd->error = -EILSEQ;
+			}
+
+			edm = readl(host->ioaddr + SDEDM);
+			fsm = edm & SDEDM_FSM_MASK;
+			if (fsm == SDEDM_FSM_READWAIT ||
+			    fsm == SDEDM_FSM_WRITESTART1)
+				writel(edm | SDEDM_FORCE_DATA_MODE,
+				       host->ioaddr + SDEDM);
+			tasklet_schedule(&host->finish_tasklet);
+			return;
+		}
+	}
+
+	if (host->cmd->flags & MMC_RSP_PRESENT) {
+		if (host->cmd->flags & MMC_RSP_136) {
+			int i;
+			for (i = 0; i < 4; i++)
+				host->cmd->resp[3 - i] = bcm2835_sdhost_read(host, SDRSP0 + i*4);
+			pr_debug("%s: finish_command %08x %08x %08x %08x\n",
+				 mmc_hostname(host->mmc),
+				 host->cmd->resp[0], host->cmd->resp[1], host->cmd->resp[2], host->cmd->resp[3]);
+			log_event("RSP ", host->cmd->resp[0], host->cmd->resp[1]);
+		} else {
+			host->cmd->resp[0] = bcm2835_sdhost_read(host, SDRSP0);
+			pr_debug("%s: finish_command %08x\n",
+				 mmc_hostname(host->mmc),
+				 host->cmd->resp[0]);
+			log_event("RSP ", host->cmd->resp[0], 0);
+		}
+	}
+
+	if (host->cmd == host->mrq->sbc) {
+		/* Finished CMD23, now send actual command. */
+		host->cmd = NULL;
+		if (bcm2835_sdhost_send_command(host, host->mrq->cmd)) {
+			if (host->data && host->dma_desc)
+				/* DMA transfer starts now, PIO starts after irq */
+				bcm2835_sdhost_start_dma(host);
+
+			if (!host->use_busy)
+				bcm2835_sdhost_finish_command(host, NULL);
+		}
+	} else if (host->cmd == host->mrq->stop) {
+		/* Finished CMD12 */
+		tasklet_schedule(&host->finish_tasklet);
+	} else {
+		/* Processed actual command. */
+		host->cmd = NULL;
+		if (!host->data)
+			tasklet_schedule(&host->finish_tasklet);
+		else if (host->data_complete)
+			bcm2835_sdhost_transfer_complete(host);
+	}
+	log_event("FCM>", host->mrq, host->cmd);
+}
+
+static void bcm2835_sdhost_timeout(struct timer_list *t)
+{
+	struct bcm2835_host *host = from_timer(host, t, timer);
+	unsigned long flags;
+
+	spin_lock_irqsave(&host->lock, flags);
+	log_event("TIM<", 0, 0);
+
+	if (host->mrq) {
+		pr_err("%s: timeout waiting for hardware interrupt.\n",
+			mmc_hostname(host->mmc));
+		log_dump();
+		bcm2835_sdhost_dumpregs(host);
+
+		if (host->data) {
+			host->data->error = -ETIMEDOUT;
+			bcm2835_sdhost_finish_data(host);
+		} else {
+			if (host->cmd)
+				host->cmd->error = -ETIMEDOUT;
+			else
+				host->mrq->cmd->error = -ETIMEDOUT;
+
+			pr_debug("timeout_timer tasklet_schedule\n");
+			tasklet_schedule(&host->finish_tasklet);
+		}
+	}
+
+	spin_unlock_irqrestore(&host->lock, flags);
+}
+
+static void bcm2835_sdhost_busy_irq(struct bcm2835_host *host, u32 intmask)
+{
+	log_event("IRQB", host->cmd, intmask);
+	if (!host->cmd) {
+		pr_err("%s: got command busy interrupt 0x%08x even "
+			"though no command operation was in progress.\n",
+			mmc_hostname(host->mmc), (unsigned)intmask);
+		bcm2835_sdhost_dumpregs(host);
+		return;
+	}
+
+	if (!host->use_busy) {
+		pr_err("%s: got command busy interrupt 0x%08x even "
+			"though not expecting one.\n",
+			mmc_hostname(host->mmc), (unsigned)intmask);
+		bcm2835_sdhost_dumpregs(host);
+		return;
+	}
+	host->use_busy = 0;
+
+	if (intmask & SDHSTS_ERROR_MASK)
+	{
+		pr_err("sdhost_busy_irq: intmask %x, data %p\n", intmask, host->mrq->data);
+		if (intmask & SDHSTS_CRC7_ERROR)
+			host->cmd->error = -EILSEQ;
+		else if (intmask & (SDHSTS_CRC16_ERROR |
+				    SDHSTS_FIFO_ERROR)) {
+			if (host->mrq->data)
+				host->mrq->data->error = -EILSEQ;
+			else
+				host->cmd->error = -EILSEQ;
+		} else if (intmask & SDHSTS_REW_TIME_OUT) {
+			if (host->mrq->data)
+				host->mrq->data->error = -ETIMEDOUT;
+			else
+				host->cmd->error = -ETIMEDOUT;
+		} else if (intmask & SDHSTS_CMD_TIME_OUT)
+			host->cmd->error = -ETIMEDOUT;
+
+		if (host->debug) {
+			log_dump();
+			bcm2835_sdhost_dumpregs(host);
+		}
+	}
+	else
+		bcm2835_sdhost_finish_command(host, NULL);
+}
+
+static void bcm2835_sdhost_data_irq(struct bcm2835_host *host, u32 intmask)
+{
+	/* There are no dedicated data/space available interrupt
+	   status bits, so it is necessary to use the single shared
+	   data/space available FIFO status bits. It is therefore not
+	   an error to get here when there is no data transfer in
+	   progress. */
+	log_event("IRQD", host->data, intmask);
+	if (!host->data)
+		return;
+
+	if (intmask & (SDHSTS_CRC16_ERROR |
+		       SDHSTS_FIFO_ERROR |
+		       SDHSTS_REW_TIME_OUT)) {
+		if (intmask & (SDHSTS_CRC16_ERROR |
+			       SDHSTS_FIFO_ERROR))
+			host->data->error = -EILSEQ;
+		else
+			host->data->error = -ETIMEDOUT;
+
+		if (host->debug) {
+			log_dump();
+			bcm2835_sdhost_dumpregs(host);
+		}
+	}
+
+	if (host->data->error) {
+		bcm2835_sdhost_finish_data(host);
+	} else if (host->data->flags & MMC_DATA_WRITE) {
+		/* Use the block interrupt for writes after the first block */
+		host->hcfg &= ~(SDHCFG_DATA_IRPT_EN);
+		host->hcfg |= SDHCFG_BLOCK_IRPT_EN;
+		bcm2835_sdhost_write(host, host->hcfg, SDHCFG);
+		bcm2835_sdhost_transfer_pio(host);
+	} else {
+		bcm2835_sdhost_transfer_pio(host);
+		host->blocks--;
+		if ((host->blocks == 0) || host->data->error)
+			bcm2835_sdhost_finish_data(host);
+	}
+}
+
+static void bcm2835_sdhost_block_irq(struct bcm2835_host *host, u32 intmask)
+{
+	log_event("IRQK", host->data, intmask);
+	if (!host->data) {
+		pr_err("%s: got block interrupt 0x%08x even "
+			"though no data operation was in progress.\n",
+			mmc_hostname(host->mmc), (unsigned)intmask);
+		bcm2835_sdhost_dumpregs(host);
+		return;
+	}
+
+	if (intmask & (SDHSTS_CRC16_ERROR |
+		       SDHSTS_FIFO_ERROR |
+		       SDHSTS_REW_TIME_OUT)) {
+		if (intmask & (SDHSTS_CRC16_ERROR |
+			       SDHSTS_FIFO_ERROR))
+			host->data->error = -EILSEQ;
+		else
+			host->data->error = -ETIMEDOUT;
+
+		if (host->debug) {
+			log_dump();
+			bcm2835_sdhost_dumpregs(host);
+		}
+	}
+
+	if (!host->dma_desc) {
+		BUG_ON(!host->blocks);
+		if (host->data->error || (--host->blocks == 0)) {
+			bcm2835_sdhost_finish_data(host);
+		} else {
+			bcm2835_sdhost_transfer_pio(host);
+		}
+	} else if (host->data->flags & MMC_DATA_WRITE) {
+		bcm2835_sdhost_finish_data(host);
+	}
+}
+
+static irqreturn_t bcm2835_sdhost_irq(int irq, void *dev_id)
+{
+	irqreturn_t result = IRQ_NONE;
+	struct bcm2835_host *host = dev_id;
+	u32 intmask;
+
+	spin_lock(&host->lock);
+
+	intmask = bcm2835_sdhost_read(host, SDHSTS);
+	log_event("IRQ<", intmask, 0);
+
+	bcm2835_sdhost_write(host,
+			     SDHSTS_BUSY_IRPT |
+			     SDHSTS_BLOCK_IRPT |
+			     SDHSTS_SDIO_IRPT |
+			     SDHSTS_DATA_FLAG,
+			     SDHSTS);
+
+	if (intmask & SDHSTS_BLOCK_IRPT) {
+		bcm2835_sdhost_block_irq(host, intmask);
+		result = IRQ_HANDLED;
+	}
+
+	if (intmask & SDHSTS_BUSY_IRPT) {
+		bcm2835_sdhost_busy_irq(host, intmask);
+		result = IRQ_HANDLED;
+	}
+
+	/* There is no true data interrupt status bit, so it is
+	   necessary to qualify the data flag with the interrupt
+	   enable bit */
+	if ((intmask & SDHSTS_DATA_FLAG) &&
+	    (host->hcfg & SDHCFG_DATA_IRPT_EN)) {
+		bcm2835_sdhost_data_irq(host, intmask);
+		result = IRQ_HANDLED;
+	}
+
+	log_event("IRQ>", bcm2835_sdhost_read(host, SDHSTS), 0);
+	spin_unlock(&host->lock);
+
+	return result;
+}
+
+void bcm2835_sdhost_set_clock(struct bcm2835_host *host, unsigned int clock)
+{
+	int div = 0; /* Initialized for compiler warning */
+	unsigned int input_clock = clock;
+	unsigned long flags;
+
+	if (host->debug)
+		pr_info("%s: set_clock(%d)\n", mmc_hostname(host->mmc), clock);
+
+	if (host->overclock_50 && (clock == 50*MHZ))
+		clock = host->overclock_50 * MHZ + (MHZ - 1);
+
+	/* The SDCDIV register has 11 bits, and holds (div - 2).
+	   But in data mode the max is 50MHz wihout a minimum, and only the
+	   bottom 3 bits are used. Since the switch over is automatic (unless
+	   we have marked the card as slow...), chosen values have to make
+	   sense in both modes.
+	   Ident mode must be 100-400KHz, so can range check the requested
+	   clock. CMD15 must be used to return to data mode, so this can be
+	   monitored.
+
+	   clock 250MHz -> 0->125MHz, 1->83.3MHz, 2->62.5MHz, 3->50.0MHz
+                           4->41.7MHz, 5->35.7MHz, 6->31.3MHz, 7->27.8MHz
+
+			 623->400KHz/27.8MHz
+			 reset value (507)->491159/50MHz
+
+	   BUT, the 3-bit clock divisor in data mode is too small if the
+	   core clock is higher than 250MHz, so instead use the SLOW_CARD
+	   configuration bit to force the use of the ident clock divisor
+	   at all times.
+	*/
+
+	host->mmc->actual_clock = 0;
+
+	if (host->firmware_sets_cdiv) {
+		u32 msg[3] = { clock, 0, 0 };
+
+		rpi_firmware_property(rpi_firmware_get(NULL),
+				      RPI_FIRMWARE_SET_SDHOST_CLOCK,
+				      &msg, sizeof(msg));
+
+		clock = max(msg[1], msg[2]);
+		spin_lock_irqsave(&host->lock, flags);
+	} else {
+		spin_lock_irqsave(&host->lock, flags);
+		if (clock < 100000) {
+			/* Can't stop the clock, but make it as slow as
+			 * possible to show willing
+			 */
+			host->cdiv = SDCDIV_MAX_CDIV;
+			bcm2835_sdhost_write(host, host->cdiv, SDCDIV);
+			spin_unlock_irqrestore(&host->lock, flags);
+			return;
+		}
+
+		div = host->max_clk / clock;
+		if (div < 2)
+			div = 2;
+		if ((host->max_clk / div) > clock)
+			div++;
+		div -= 2;
+
+		if (div > SDCDIV_MAX_CDIV)
+			div = SDCDIV_MAX_CDIV;
+
+		clock = host->max_clk / (div + 2);
+
+		host->cdiv = div;
+		bcm2835_sdhost_write(host, host->cdiv, SDCDIV);
+
+		if (host->debug)
+			pr_info("%s: clock=%d -> max_clk=%d, cdiv=%x "
+				"(actual clock %d)\n",
+				mmc_hostname(host->mmc), input_clock,
+				host->max_clk, host->cdiv,
+				clock);
+	}
+
+	/* Calibrate some delays */
+
+	host->ns_per_fifo_word = (1000000000/clock) *
+		((host->mmc->caps & MMC_CAP_4_BIT_DATA) ? 8 : 32);
+
+	if (input_clock == 50 * MHZ) {
+		if (clock > input_clock) {
+			/* Save the closest value, to make it easier
+			   to reduce in the event of error */
+			host->overclock_50 = (clock/MHZ);
+
+			if (clock != host->overclock) {
+				pr_info("%s: overclocking to %dHz\n",
+					mmc_hostname(host->mmc), clock);
+				host->overclock = clock;
+			}
+		} else if (host->overclock) {
+			host->overclock = 0;
+			if (clock == 50 * MHZ)
+				pr_warn("%s: cancelling overclock\n",
+					mmc_hostname(host->mmc));
+		}
+	} else if (input_clock == 0) {
+		/* Reset the preferred overclock when the clock is stopped.
+		 * This always happens during initialisation. */
+		host->overclock_50 = host->user_overclock_50;
+		host->overclock = 0;
+	}
+
+	/* Set the timeout to 500ms */
+	bcm2835_sdhost_write(host, clock/2, SDTOUT);
+
+	host->mmc->actual_clock = clock;
+	host->clock = input_clock;
+	host->reset_clock = 0;
+
+	spin_unlock_irqrestore(&host->lock, flags);
+}
+
+static void bcm2835_sdhost_request(struct mmc_host *mmc, struct mmc_request *mrq)
+{
+	struct bcm2835_host *host;
+	unsigned long flags;
+	u32 edm, fsm;
+
+	host = mmc_priv(mmc);
+
+	if (host->debug) {
+		struct mmc_command *cmd = mrq->cmd;
+		BUG_ON(!cmd);
+		if (cmd->data)
+			pr_info("%s: cmd %d 0x%x (flags 0x%x) - %s %d*%d\n",
+				mmc_hostname(mmc),
+				cmd->opcode, cmd->arg, cmd->flags,
+				(cmd->data->flags & MMC_DATA_READ) ?
+				"read" : "write", cmd->data->blocks,
+				cmd->data->blksz);
+		else
+			pr_info("%s: cmd %d 0x%x (flags 0x%x)\n",
+				mmc_hostname(mmc),
+				cmd->opcode, cmd->arg, cmd->flags);
+	}
+
+	/* Reset the error statuses in case this is a retry */
+	if (mrq->sbc)
+		mrq->sbc->error = 0;
+	if (mrq->cmd)
+		mrq->cmd->error = 0;
+	if (mrq->data)
+		mrq->data->error = 0;
+	if (mrq->stop)
+		mrq->stop->error = 0;
+
+	if (mrq->data && !is_power_of_2(mrq->data->blksz)) {
+		pr_err("%s: unsupported block size (%d bytes)\n",
+		       mmc_hostname(mmc), mrq->data->blksz);
+		mrq->cmd->error = -EINVAL;
+		mmc_request_done(mmc, mrq);
+		return;
+	}
+
+	if (host->use_dma && mrq->data &&
+	    (mrq->data->blocks > host->pio_limit))
+		bcm2835_sdhost_prepare_dma(host, mrq->data);
+
+	if (host->reset_clock)
+	    bcm2835_sdhost_set_clock(host, host->clock);
+
+	spin_lock_irqsave(&host->lock, flags);
+
+	WARN_ON(host->mrq != NULL);
+	host->mrq = mrq;
+
+	edm = bcm2835_sdhost_read(host, SDEDM);
+	fsm = edm & SDEDM_FSM_MASK;
+
+	log_event("REQ<", mrq, edm);
+	if ((fsm != SDEDM_FSM_IDENTMODE) &&
+	    (fsm != SDEDM_FSM_DATAMODE)) {
+		log_event("REQ!", mrq, edm);
+		if (host->debug) {
+			pr_warn("%s: previous command (%d) not complete (EDM %x)\n",
+			       mmc_hostname(host->mmc),
+			       bcm2835_sdhost_read(host, SDCMD) & SDCMD_CMD_MASK,
+			       edm);
+			log_dump();
+			bcm2835_sdhost_dumpregs(host);
+		}
+		mrq->cmd->error = -EILSEQ;
+		tasklet_schedule(&host->finish_tasklet);
+		spin_unlock_irqrestore(&host->lock, flags);
+		return;
+	}
+
+	host->use_sbc = !!mrq->sbc &&
+		(host->mrq->data->flags & USE_CMD23_FLAGS);
+	if (host->use_sbc) {
+		if (bcm2835_sdhost_send_command(host, mrq->sbc)) {
+			if (!host->use_busy)
+				bcm2835_sdhost_finish_command(host, &flags);
+		}
+	} else if (bcm2835_sdhost_send_command(host, mrq->cmd)) {
+		if (host->data && host->dma_desc)
+			/* DMA transfer starts now, PIO starts after irq */
+			bcm2835_sdhost_start_dma(host);
+
+		if (!host->use_busy)
+			bcm2835_sdhost_finish_command(host, &flags);
+	}
+
+	log_event("CMD ", mrq->cmd->opcode,
+		   mrq->data ? (u32)mrq->data->blksz : 0);
+
+	log_event("REQ>", mrq, 0);
+	spin_unlock_irqrestore(&host->lock, flags);
+}
+
+static void bcm2835_sdhost_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+
+	struct bcm2835_host *host = mmc_priv(mmc);
+	unsigned long flags;
+
+	if (host->debug)
+		pr_info("%s: ios clock %d, pwr %d, bus_width %d, "
+			"timing %d, vdd %d, drv_type %d\n",
+			mmc_hostname(mmc),
+			ios->clock, ios->power_mode, ios->bus_width,
+			ios->timing, ios->signal_voltage, ios->drv_type);
+
+	spin_lock_irqsave(&host->lock, flags);
+
+	log_event("IOS<", ios->clock, 0);
+
+	/* set bus width */
+	host->hcfg &= ~SDHCFG_WIDE_EXT_BUS;
+	if (ios->bus_width == MMC_BUS_WIDTH_4)
+		host->hcfg |= SDHCFG_WIDE_EXT_BUS;
+
+	host->hcfg |= SDHCFG_WIDE_INT_BUS;
+
+	/* Disable clever clock switching, to cope with fast core clocks */
+	host->hcfg |= SDHCFG_SLOW_CARD;
+
+	bcm2835_sdhost_write(host, host->hcfg, SDHCFG);
+
+	spin_unlock_irqrestore(&host->lock, flags);
+
+	if (!ios->clock || ios->clock != host->clock)
+		bcm2835_sdhost_set_clock(host, ios->clock);
+}
+
+static struct mmc_host_ops bcm2835_sdhost_ops = {
+	.request = bcm2835_sdhost_request,
+	.set_ios = bcm2835_sdhost_set_ios,
+	.hw_reset = bcm2835_sdhost_reset,
+};
+
+static void bcm2835_sdhost_cmd_wait_work(struct work_struct *work)
+{
+	struct bcm2835_host *host;
+	unsigned long flags;
+
+	host = container_of(work, struct bcm2835_host, cmd_wait_wq);
+
+	spin_lock_irqsave(&host->lock, flags);
+
+	log_event("CWK<", host->cmd, host->mrq);
+
+	/*
+	 * If this tasklet gets rescheduled while running, it will
+	 * be run again afterwards but without any active request.
+	 */
+	if (!host->mrq) {
+		spin_unlock_irqrestore(&host->lock, flags);
+		return;
+	}
+
+	bcm2835_sdhost_finish_command(host, &flags);
+
+	log_event("CWK>", host->cmd, 0);
+
+	spin_unlock_irqrestore(&host->lock, flags);
+}
+
+static void bcm2835_sdhost_tasklet_finish(unsigned long param)
+{
+	struct bcm2835_host *host;
+	unsigned long flags;
+	struct mmc_request *mrq;
+	struct dma_chan *terminate_chan = NULL;
+
+	host = (struct bcm2835_host *)param;
+
+	spin_lock_irqsave(&host->lock, flags);
+
+	log_event("TSK<", host->mrq, 0);
+	/*
+	 * If this tasklet gets rescheduled while running, it will
+	 * be run again afterwards but without any active request.
+	 */
+	if (!host->mrq) {
+		spin_unlock_irqrestore(&host->lock, flags);
+		return;
+	}
+
+	del_timer(&host->timer);
+
+	mrq = host->mrq;
+
+	/* Drop the overclock after any data corruption, or after any
+	 * error while overclocked. Ignore errors for status commands,
+	 * as they are likely when a card is ejected. */
+	if (host->overclock) {
+		if ((mrq->cmd && mrq->cmd->error &&
+		     (mrq->cmd->opcode != MMC_SEND_STATUS)) ||
+		    (mrq->data && mrq->data->error) ||
+		    (mrq->stop && mrq->stop->error) ||
+		    (mrq->sbc && mrq->sbc->error)) {
+			host->overclock_50--;
+			pr_warn("%s: reducing overclock due to errors\n",
+				mmc_hostname(host->mmc));
+			host->reset_clock = 1;
+			mrq->cmd->error = -ETIMEDOUT;
+			mrq->cmd->retries = 1;
+		}
+	}
+
+	host->mrq = NULL;
+	host->cmd = NULL;
+	host->data = NULL;
+
+	host->dma_desc = NULL;
+	terminate_chan = host->dma_chan;
+	host->dma_chan = NULL;
+
+	spin_unlock_irqrestore(&host->lock, flags);
+
+	if (terminate_chan)
+	{
+		int err = dmaengine_terminate_all(terminate_chan);
+		if (err)
+			pr_err("%s: failed to terminate DMA (%d)\n",
+			       mmc_hostname(host->mmc), err);
+	}
+
+	/* The SDHOST block doesn't report any errors for a disconnected
+	   interface. All cards and SDIO devices should report some supported
+	   voltage range, so a zero response to SEND_OP_COND, IO_SEND_OP_COND
+	   or APP_SEND_OP_COND can be treated as an error. */
+	if (((mrq->cmd->opcode == MMC_SEND_OP_COND) ||
+	     (mrq->cmd->opcode == SD_IO_SEND_OP_COND) ||
+	     (mrq->cmd->opcode == SD_APP_OP_COND)) &&
+	    (mrq->cmd->error == 0) &&
+	    (mrq->cmd->resp[0] == 0)) {
+		mrq->cmd->error = -ETIMEDOUT;
+		if (host->debug)
+			pr_info("%s: faking timeout due to zero OCR\n",
+				mmc_hostname(host->mmc));
+	}
+
+	mmc_request_done(host->mmc, mrq);
+	log_event("TSK>", mrq, 0);
+}
+
+int bcm2835_sdhost_add_host(struct bcm2835_host *host)
+{
+	struct mmc_host *mmc;
+	struct dma_slave_config cfg;
+	char pio_limit_string[20];
+	int ret;
+
+	mmc = host->mmc;
+
+	if (!mmc->f_max || mmc->f_max > host->max_clk)
+		mmc->f_max = host->max_clk;
+	mmc->f_min = host->max_clk / SDCDIV_MAX_CDIV;
+
+	mmc->max_busy_timeout =  (~(unsigned int)0)/(mmc->f_max/1000);
+
+	pr_debug("f_max %d, f_min %d, max_busy_timeout %d\n",
+		 mmc->f_max, mmc->f_min, mmc->max_busy_timeout);
+
+	/* host controller capabilities */
+	mmc->caps |=
+		MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED |
+		MMC_CAP_NEEDS_POLL | MMC_CAP_HW_RESET |
+		((ALLOW_CMD23_READ|ALLOW_CMD23_WRITE) * MMC_CAP_CMD23);
+
+	spin_lock_init(&host->lock);
+
+	if (host->allow_dma) {
+		if (IS_ERR_OR_NULL(host->dma_chan_rxtx)) {
+			pr_err("%s: unable to initialise DMA channel. "
+			       "Falling back to PIO\n",
+			       mmc_hostname(mmc));
+			host->use_dma = false;
+		} else {
+			cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+			cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+			cfg.slave_id = 13;		/* DREQ channel */
+
+			/* Validate the slave configurations */
+
+			cfg.direction = DMA_MEM_TO_DEV;
+			cfg.src_addr = 0;
+			cfg.dst_addr = host->bus_addr + SDDATA;
+
+			ret = dmaengine_slave_config(host->dma_chan_rxtx, &cfg);
+
+			if (ret == 0) {
+				host->dma_cfg_tx = cfg;
+
+				cfg.direction = DMA_DEV_TO_MEM;
+				cfg.src_addr = host->bus_addr + SDDATA;
+				cfg.dst_addr = 0;
+
+				ret = dmaengine_slave_config(host->dma_chan_rxtx, &cfg);
+			}
+
+			if (ret == 0) {
+				host->dma_cfg_rx = cfg;
+
+				host->use_dma = true;
+			} else {
+				pr_err("%s: unable to configure DMA channel. "
+				       "Falling back to PIO\n",
+				       mmc_hostname(mmc));
+				dma_release_channel(host->dma_chan_rxtx);
+				host->dma_chan_rxtx = NULL;
+				host->use_dma = false;
+			}
+		}
+	} else {
+		host->use_dma = false;
+	}
+
+	mmc->max_segs = 128;
+	mmc->max_req_size = 524288;
+	mmc->max_seg_size = mmc->max_req_size;
+	mmc->max_blk_size = 512;
+	mmc->max_blk_count =  65535;
+
+	/* report supported voltage ranges */
+	mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
+
+	tasklet_init(&host->finish_tasklet,
+		bcm2835_sdhost_tasklet_finish, (unsigned long)host);
+
+	INIT_WORK(&host->cmd_wait_wq, bcm2835_sdhost_cmd_wait_work);
+
+	timer_setup(&host->timer, bcm2835_sdhost_timeout, 0);
+
+	bcm2835_sdhost_init(host, 0);
+
+	ret = request_irq(host->irq, bcm2835_sdhost_irq, 0 /*IRQF_SHARED*/,
+				  mmc_hostname(mmc), host);
+	if (ret) {
+		pr_err("%s: failed to request IRQ %d: %d\n",
+		       mmc_hostname(mmc), host->irq, ret);
+		goto untasklet;
+	}
+
+	mmc_add_host(mmc);
+
+	pio_limit_string[0] = '\0';
+	if (host->use_dma && (host->pio_limit > 0))
+		sprintf(pio_limit_string, " (>%d)", host->pio_limit);
+	pr_info("%s: %s loaded - DMA %s%s\n",
+		mmc_hostname(mmc), DRIVER_NAME,
+		host->use_dma ? "enabled" : "disabled",
+		pio_limit_string);
+
+	return 0;
+
+untasklet:
+	tasklet_kill(&host->finish_tasklet);
+
+	return ret;
+}
+
+static int bcm2835_sdhost_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct device_node *node = dev->of_node;
+	struct clk *clk;
+	struct resource *iomem;
+	struct bcm2835_host *host;
+	struct mmc_host *mmc;
+	const __be32 *addr;
+	u32 msg[3];
+	int na;
+	int ret;
+
+	pr_debug("bcm2835_sdhost_probe\n");
+	mmc = mmc_alloc_host(sizeof(*host), dev);
+	if (!mmc)
+		return -ENOMEM;
+
+	mmc->ops = &bcm2835_sdhost_ops;
+	host = mmc_priv(mmc);
+	host->mmc = mmc;
+	host->pio_timeout = msecs_to_jiffies(500);
+	host->pio_limit = 1;
+	host->max_delay = 1; /* Warn if over 1ms */
+	host->allow_dma = 1;
+	spin_lock_init(&host->lock);
+
+	iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	host->ioaddr = devm_ioremap_resource(dev, iomem);
+	if (IS_ERR(host->ioaddr)) {
+		ret = PTR_ERR(host->ioaddr);
+		goto err;
+	}
+
+	na = of_n_addr_cells(node);
+	addr = of_get_address(node, 0, NULL, NULL);
+	if (!addr) {
+		dev_err(dev, "could not get DMA-register address\n");
+		return -ENODEV;
+	}
+	host->bus_addr = (phys_addr_t)of_read_number(addr, na);
+	pr_debug(" - ioaddr %lx, iomem->start %lx, bus_addr %lx\n",
+		 (unsigned long)host->ioaddr,
+		 (unsigned long)iomem->start,
+		 (unsigned long)host->bus_addr);
+
+	if (node) {
+		/* Read any custom properties */
+		of_property_read_u32(node,
+				     "brcm,delay-after-stop",
+				     &host->delay_after_stop);
+		of_property_read_u32(node,
+				     "brcm,overclock-50",
+				     &host->user_overclock_50);
+		of_property_read_u32(node,
+				     "brcm,pio-limit",
+				     &host->pio_limit);
+		host->allow_dma =
+			!of_property_read_bool(node, "brcm,force-pio");
+		host->debug = of_property_read_bool(node, "brcm,debug");
+	}
+
+	host->dma_chan = NULL;
+	host->dma_desc = NULL;
+
+	/* Formally recognise the other way of disabling DMA */
+	if (host->pio_limit == 0x7fffffff)
+		host->allow_dma = false;
+
+	if (host->allow_dma) {
+		if (node) {
+			host->dma_chan_rxtx =
+				dma_request_slave_channel(dev, "rx-tx");
+			if (!host->dma_chan_rxtx)
+				host->dma_chan_rxtx =
+					dma_request_slave_channel(dev, "tx");
+			if (!host->dma_chan_rxtx)
+				host->dma_chan_rxtx =
+					dma_request_slave_channel(dev, "rx");
+		} else {
+			dma_cap_mask_t mask;
+
+			dma_cap_zero(mask);
+			/* we don't care about the channel, any would work */
+			dma_cap_set(DMA_SLAVE, mask);
+			host->dma_chan_rxtx =
+				dma_request_channel(mask, NULL, NULL);
+		}
+	}
+
+	clk = devm_clk_get(dev, NULL);
+	if (IS_ERR(clk)) {
+		ret = PTR_ERR(clk);
+		if (ret == -EPROBE_DEFER)
+			dev_info(dev, "could not get clk, deferring probe\n");
+		else
+			dev_err(dev, "could not get clk\n");
+		goto err;
+	}
+
+	host->max_clk = clk_get_rate(clk);
+
+	host->irq = platform_get_irq(pdev, 0);
+	if (host->irq <= 0) {
+		dev_err(dev, "get IRQ failed\n");
+		ret = -EINVAL;
+		goto err;
+	}
+
+	pr_debug(" - max_clk %lx, irq %d\n",
+		 (unsigned long)host->max_clk,
+		 (int)host->irq);
+
+	log_init(dev, iomem->start - host->bus_addr);
+
+	if (node)
+		mmc_of_parse(mmc);
+	else
+		mmc->caps |= MMC_CAP_4_BIT_DATA;
+
+	msg[0] = 0;
+	msg[1] = ~0;
+	msg[2] = ~0;
+
+	rpi_firmware_property(rpi_firmware_get(NULL),
+			      RPI_FIRMWARE_SET_SDHOST_CLOCK,
+			      &msg, sizeof(msg));
+
+	host->firmware_sets_cdiv = (msg[1] != ~0);
+
+	ret = bcm2835_sdhost_add_host(host);
+	if (ret)
+		goto err;
+
+	platform_set_drvdata(pdev, host);
+
+	pr_debug("bcm2835_sdhost_probe -> OK\n");
+
+	return 0;
+
+err:
+	pr_debug("bcm2835_sdhost_probe -> err %d\n", ret);
+	if (host->dma_chan_rxtx)
+		dma_release_channel(host->dma_chan_rxtx);
+	mmc_free_host(mmc);
+
+	return ret;
+}
+
+static int bcm2835_sdhost_remove(struct platform_device *pdev)
+{
+	struct bcm2835_host *host = platform_get_drvdata(pdev);
+
+	pr_debug("bcm2835_sdhost_remove\n");
+
+	mmc_remove_host(host->mmc);
+
+	bcm2835_sdhost_set_power(host, false);
+
+	free_irq(host->irq, host);
+
+	del_timer_sync(&host->timer);
+
+	tasklet_kill(&host->finish_tasklet);
+	if (host->dma_chan_rxtx)
+		dma_release_channel(host->dma_chan_rxtx);
+	mmc_free_host(host->mmc);
+	platform_set_drvdata(pdev, NULL);
+
+	pr_debug("bcm2835_sdhost_remove - OK\n");
+	return 0;
+}
+
+static const struct of_device_id bcm2835_sdhost_match[] = {
+	{ .compatible = "brcm,bcm2835-sdhost" },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, bcm2835_sdhost_match);
+
+static struct platform_driver bcm2835_sdhost_driver = {
+	.probe      = bcm2835_sdhost_probe,
+	.remove     = bcm2835_sdhost_remove,
+	.driver     = {
+		.name		= DRIVER_NAME,
+		.owner		= THIS_MODULE,
+		.of_match_table	= bcm2835_sdhost_match,
+	},
+};
+module_platform_driver(bcm2835_sdhost_driver);
+
+MODULE_ALIAS("platform:sdhost-bcm2835");
+MODULE_DESCRIPTION("BCM2835 SDHost driver");
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Phil Elwell");
diff -Nur --no-dereference linux-5.10.52-orig/drivers/mmc/host/Kconfig linux-5.10.52-v7l+/drivers/mmc/host/Kconfig
--- linux-5.10.52-orig/drivers/mmc/host/Kconfig	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/mmc/host/Kconfig	2021-07-25 16:46:09.498231666 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:8 @
 
 comment "MMC/SD/SDIO Host Controller Drivers"
 
+config MMC_BCM2835_MMC
+	tristate "MMC support on BCM2835"
+	depends on MACH_BCM2708 || MACH_BCM2709 || ARCH_BCM2835
+	help
+	  This selects the MMC Interface on BCM2835.
+
+	  If you have a controller with this interface, say Y or M here.
+
+	  If unsure, say N.
+
+config MMC_BCM2835_DMA
+	bool "DMA support on BCM2835 Arasan controller"
+	depends on MMC_BCM2835_MMC
+	help
+	  Enable DMA support on the Arasan SDHCI controller in Broadcom 2708
+	  based chips.
+
+	  If unsure, say N.
+
+config MMC_BCM2835_PIO_DMA_BARRIER
+	int "Block count limit for PIO transfers"
+	depends on MMC_BCM2835_MMC && MMC_BCM2835_DMA
+	range 0 256
+	default 2
+	help
+	  The inclusive limit in bytes under which PIO will be used instead of DMA
+
+	  If unsure, say 2 here.
+
+config MMC_BCM2835_SDHOST
+	tristate "Support for the SDHost controller on BCM2708/9"
+	depends on ARCH_BCM2835
+	help
+	  This selects the SDHost controller on BCM2835/6.
+
+	  If you have a controller with this interface, say Y or M here.
+
+	  If unsure, say N.
+
 config MMC_DEBUG
 	bool "MMC host drivers debugging"
 	depends on MMC != n
diff -Nur --no-dereference linux-5.10.52-orig/drivers/mmc/host/Makefile linux-5.10.52-v7l+/drivers/mmc/host/Makefile
--- linux-5.10.52-orig/drivers/mmc/host/Makefile	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/mmc/host/Makefile	2021-07-25 16:46:09.498231666 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:27 @
 obj-$(CONFIG_MMC_SDHCI_MILBEAUT)	+= sdhci-milbeaut.o
 obj-$(CONFIG_MMC_SDHCI_SPEAR)	+= sdhci-spear.o
 obj-$(CONFIG_MMC_SDHCI_AM654)	+= sdhci_am654.o
+obj-$(CONFIG_MMC_BCM2835_MMC)	+= bcm2835-mmc.o
+obj-$(CONFIG_MMC_BCM2835_SDHOST)	+= bcm2835-sdhost.o
 obj-$(CONFIG_MMC_WBSD)		+= wbsd.o
 obj-$(CONFIG_MMC_AU1X)		+= au1xmmc.o
 obj-$(CONFIG_MMC_ALCOR)	+= alcor.o
diff -Nur --no-dereference linux-5.10.52-orig/drivers/mmc/host/sdhci.c linux-5.10.52-v7l+/drivers/mmc/host/sdhci.c
--- linux-5.10.52-orig/drivers/mmc/host/sdhci.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/mmc/host/sdhci.c	2021-07-25 16:46:09.678228648 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:44 @
 	pr_debug("%s: " DRIVER_NAME ": " f, mmc_hostname(host->mmc), ## x)
 
 #define SDHCI_DUMP(f, x...) \
-	pr_err("%s: " DRIVER_NAME ": " f, mmc_hostname(host->mmc), ## x)
+	pr_debug("%s: " DRIVER_NAME ": " f, mmc_hostname(host->mmc), ## x)
 
 #define MAX_TUNING_LOOP 40
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:3129 @
 	spin_lock_irqsave(&host->lock, flags);
 
 	if (host->cmd && !sdhci_data_line_cmd(host->cmd)) {
-		pr_err("%s: Timeout waiting for hardware cmd interrupt.\n",
+		pr_debug("%s: Timeout waiting for hardware cmd interrupt.\n",
 		       mmc_hostname(host->mmc));
 		sdhci_dumpregs(host);
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:3151 @
 
 	if (host->data || host->data_cmd ||
 	    (host->cmd && sdhci_data_line_cmd(host->cmd))) {
-		pr_err("%s: Timeout waiting for hardware interrupt.\n",
+		pr_debug("%s: Timeout waiting for hardware interrupt.\n",
 		       mmc_hostname(host->mmc));
 		sdhci_dumpregs(host);
 
diff -Nur --no-dereference linux-5.10.52-orig/drivers/mmc/host/sdhci-iproc.c linux-5.10.52-v7l+/drivers/mmc/host/sdhci-iproc.c
--- linux-5.10.52-orig/drivers/mmc/host/sdhci-iproc.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/mmc/host/sdhci-iproc.c	2021-07-25 16:46:09.618229654 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:176 @
 		return pltfm_host->clock;
 }
 
+static void sdhci_iproc_set_power(struct sdhci_host *host, unsigned char mode,
+				  unsigned short vdd)
+{
+	if (!IS_ERR(host->mmc->supply.vmmc)) {
+		struct mmc_host *mmc = host->mmc;
+
+		mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, vdd);
+	}
+	sdhci_set_power_noreg(host, mode, vdd);
+}
+
 static const struct sdhci_ops sdhci_iproc_ops = {
 	.set_clock = sdhci_set_clock,
 	.get_max_clock = sdhci_iproc_get_max_clock,
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:204 @
 	.write_b = sdhci_iproc_writeb,
 	.set_clock = sdhci_set_clock,
 	.get_max_clock = sdhci_iproc_get_max_clock,
+	.set_power = sdhci_iproc_set_power,
 	.set_bus_width = sdhci_set_bus_width,
 	.reset = sdhci_reset,
 	.set_uhs_signaling = sdhci_set_uhs_signaling,
diff -Nur --no-dereference linux-5.10.52-orig/drivers/net/ethernet/broadcom/genet/bcmgenet.c linux-5.10.52-v7l+/drivers/net/ethernet/broadcom/genet/bcmgenet.c
--- linux-5.10.52-orig/drivers/net/ethernet/broadcom/genet/bcmgenet.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/net/ethernet/broadcom/genet/bcmgenet.c	2021-07-25 16:46:11.398199812 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:70 @
 
 /* Forward declarations */
 static void bcmgenet_set_rx_mode(struct net_device *dev);
+static bool skip_umac_reset = false;
+module_param(skip_umac_reset, bool, 0444);
+MODULE_PARM_DESC(skip_umac_reset, "Skip UMAC reset step");
 
 static inline void bcmgenet_writel(u32 value, void __iomem *offset)
 {
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:2431 @
 	bcmgenet_rbuf_ctrl_set(priv, 0);
 	udelay(10);
 
+	if (skip_umac_reset) {
+		pr_warn("Skipping UMAC reset\n");
+		return;
+	}
+
 	/* issue soft reset and disable MAC while updating its registers */
 	bcmgenet_umac_writel(priv, CMD_SW_RESET, UMAC_CMD);
 	udelay(2);
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:2605 @
 
 	bcmgenet_tdma_ring_writel(priv, index, 0, TDMA_PROD_INDEX);
 	bcmgenet_tdma_ring_writel(priv, index, 0, TDMA_CONS_INDEX);
-	bcmgenet_tdma_ring_writel(priv, index, 1, DMA_MBUF_DONE_THRESH);
+	bcmgenet_tdma_ring_writel(priv, index, 10, DMA_MBUF_DONE_THRESH);
 	/* Disable rate control for now */
 	bcmgenet_tdma_ring_writel(priv, index, flow_period_val,
 				  TDMA_FLOW_PERIOD);
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:3246 @
 }
 
 /* Returns a reusable dma control register value */
-static u32 bcmgenet_dma_disable(struct bcmgenet_priv *priv)
+static u32 bcmgenet_dma_disable(struct bcmgenet_priv *priv, bool flush_rx)
 {
 	u32 reg;
 	u32 dma_ctrl;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:3265 @
 	udelay(10);
 	bcmgenet_umac_writel(priv, 0, UMAC_TX_FLUSH);
 
+	if (flush_rx) {
+	    reg = bcmgenet_rbuf_ctrl_get(priv);
+	    bcmgenet_rbuf_ctrl_set(priv, reg | BIT(0));
+	    udelay(10);
+	    bcmgenet_rbuf_ctrl_set(priv, reg);
+	    udelay(10);
+	}
+
 	return dma_ctrl;
 }
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:3343 @
 		bcmgenet_ext_writel(priv, reg, EXT_EXT_PWR_MGMT);
 	}
 
-	/* Disable RX/TX DMA and flush TX queues */
-	dma_ctrl = bcmgenet_dma_disable(priv);
+	/* Disable RX/TX DMA and flush TX and RX queues */
+	dma_ctrl = bcmgenet_dma_disable(priv, true);
 
 	/* Reinitialize TDMA and RDMA and SW housekeeping */
 	ret = bcmgenet_init_dma(priv);
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4077 @
 	netif_set_real_num_rx_queues(priv->dev, priv->hw_params->rx_queues + 1);
 
 	/* Set default coalescing parameters */
-	for (i = 0; i < priv->hw_params->rx_queues; i++)
+	for (i = 0; i < priv->hw_params->rx_queues; i++) {
 		priv->rx_rings[i].rx_max_coalesced_frames = 1;
+		priv->rx_rings[i].rx_coalesce_usecs = 50;
+	}
 	priv->rx_rings[DESC_INDEX].rx_max_coalesced_frames = 1;
+	priv->rx_rings[DESC_INDEX].rx_coalesce_usecs = 50;
 
 	/* libphy will determine the link state */
 	netif_carrier_off(dev);
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4205 @
 	}
 
 	/* Disable RX/TX DMA and flush TX queues */
-	dma_ctrl = bcmgenet_dma_disable(priv);
+	dma_ctrl = bcmgenet_dma_disable(priv, false);
 
 	/* Reinitialize TDMA and RDMA and SW housekeeping */
 	ret = bcmgenet_init_dma(priv);
diff -Nur --no-dereference linux-5.10.52-orig/drivers/net/ethernet/broadcom/genet/bcmgenet.h linux-5.10.52-v7l+/drivers/net/ethernet/broadcom/genet/bcmgenet.h
--- linux-5.10.52-orig/drivers/net/ethernet/broadcom/genet/bcmgenet.h	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/net/ethernet/broadcom/genet/bcmgenet.h	2021-07-25 16:46:11.398199812 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:32 @
 #define ENET_PAD		8
 #define ENET_MAX_MTU_SIZE	(ETH_DATA_LEN + ETH_HLEN + VLAN_HLEN + \
 				 ENET_BRCM_TAG_LEN + ETH_FCS_LEN + ENET_PAD)
-#define DMA_MAX_BURST_LENGTH    0x10
+#define DMA_MAX_BURST_LENGTH    0x08
 
 /* misc. configuration */
 #define MAX_NUM_OF_FS_RULES		16
diff -Nur --no-dereference linux-5.10.52-orig/drivers/net/ethernet/broadcom/genet/bcmmii.c linux-5.10.52-v7l+/drivers/net/ethernet/broadcom/genet/bcmmii.c
--- linux-5.10.52-orig/drivers/net/ethernet/broadcom/genet/bcmmii.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/net/ethernet/broadcom/genet/bcmmii.c	2021-07-25 16:46:11.398199812 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:296 @
 	/* Communicate the integrated PHY revision */
 	if (priv->internal_phy)
 		phy_flags = priv->gphy_rev;
+	else
+		phy_flags = PHY_BRCM_AUTO_PWRDWN_ENABLE;
 
 	/* Initialize link state variables that bcmgenet_mii_setup() uses */
 	priv->old_link = -1;
diff -Nur --no-dereference linux-5.10.52-orig/drivers/net/phy/broadcom.c linux-5.10.52-v7l+/drivers/net/phy/broadcom.c
--- linux-5.10.52-orig/drivers/net/phy/broadcom.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/net/phy/broadcom.c	2021-07-25 16:46:15.808125878 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:85 @
 	return 0;
 }
 
+static int bcm54213pe_config_init(struct phy_device *phydev)
+{
+	return bcm54210e_config_init(phydev);
+}
+
 static int bcm54612e_config_init(struct phy_device *phydev)
 {
 	int reg;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:260 @
 	    BRCM_PHY_MODEL(phydev) != PHY_ID_BCM50610 &&
 	    BRCM_PHY_MODEL(phydev) != PHY_ID_BCM50610M &&
 	    BRCM_PHY_MODEL(phydev) != PHY_ID_BCM54810 &&
-	    BRCM_PHY_MODEL(phydev) != PHY_ID_BCM54811)
+	    BRCM_PHY_MODEL(phydev) != PHY_ID_BCM54811 &&
+	    BRCM_PHY_MODEL(phydev) != PHY_ID_BCM54213PE)
 		return;
 
 	val = bcm_phy_read_shadow(phydev, BCM54XX_SHD_SCR3);
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:322 @
 static int bcm54xx_config_init(struct phy_device *phydev)
 {
 	int reg, err, val;
+	u32 led_modes[] = {BCM_LED_MULTICOLOR_LINK_ACT,
+			   BCM_LED_MULTICOLOR_LINK};
+	struct device_node *np = phydev->mdio.dev.of_node;
 
 	reg = phy_read(phydev, MII_BCM54XX_ECR);
 	if (reg < 0)
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:362 @
 	case PHY_ID_BCM54612E:
 		err = bcm54612e_config_init(phydev);
 		break;
+	case PHY_ID_BCM54213PE:
+		err = bcm54213pe_config_init(phydev);
+		break;
 	case PHY_ID_BCM54616S:
 		err = bcm54616s_config_init(phydev);
 		break;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:383 @
 
 	bcm54xx_phydsp_config(phydev);
 
-	/* Encode link speed into LED1 and LED3 pair (green/amber).
-	 * Also flash these two LEDs on activity. This means configuring
-	 * them for MULTICOLOR and encoding link/activity into them.
-	 */
+	of_property_read_u32_array(np, "led-modes", led_modes, 2);
+
 	val = BCM5482_SHD_LEDS1_LED1(BCM_LED_SRC_MULTICOLOR1) |
 		BCM5482_SHD_LEDS1_LED3(BCM_LED_SRC_MULTICOLOR1);
 	bcm_phy_write_shadow(phydev, BCM5482_SHD_LEDS1, val);
 
 	val = BCM_LED_MULTICOLOR_IN_PHASE |
-		BCM5482_SHD_LEDS1_LED1(BCM_LED_MULTICOLOR_LINK_ACT) |
-		BCM5482_SHD_LEDS1_LED3(BCM_LED_MULTICOLOR_LINK_ACT);
+		BCM5482_SHD_LEDS1_LED1(led_modes[0]) |
+		BCM5482_SHD_LEDS1_LED3(led_modes[1]);
 	bcm_phy_write_exp(phydev, BCM_EXP_MULTICOLOR, val);
 
 	return 0;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:775 @
 	.config_intr	= bcm_phy_config_intr,
 }, {
 	.phy_id		= PHY_ID_BCM54210E,
-	.phy_id_mask	= 0xfffffff0,
+	.phy_id_mask	= 0xffffffff,
 	.name		= "Broadcom BCM54210E",
 	/* PHY_GBIT_FEATURES */
 	.config_init	= bcm54xx_config_init,
 	.ack_interrupt	= bcm_phy_ack_intr,
 	.config_intr	= bcm_phy_config_intr,
 }, {
+	.phy_id		= PHY_ID_BCM54213PE,
+	.phy_id_mask	= 0xffffffff,
+	.name		= "Broadcom BCM54213PE",
+	/* PHY_GBIT_FEATURES */
+	.config_init	= bcm54xx_config_init,
+	.ack_interrupt	= bcm_phy_ack_intr,
+	.config_intr	= bcm_phy_config_intr,
+}, {
 	.phy_id		= PHY_ID_BCM5461,
 	.phy_id_mask	= 0xfffffff0,
 	.name		= "Broadcom BCM5461",
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:944 @
 static struct mdio_device_id __maybe_unused broadcom_tbl[] = {
 	{ PHY_ID_BCM5411, 0xfffffff0 },
 	{ PHY_ID_BCM5421, 0xfffffff0 },
-	{ PHY_ID_BCM54210E, 0xfffffff0 },
+	{ PHY_ID_BCM54210E, 0xffffffff },
+	{ PHY_ID_BCM54213PE, 0xffffffff },
 	{ PHY_ID_BCM5461, 0xfffffff0 },
 	{ PHY_ID_BCM54612E, 0xfffffff0 },
 	{ PHY_ID_BCM54616S, 0xfffffff0 },
diff -Nur --no-dereference linux-5.10.52-orig/drivers/net/phy/microchip.c linux-5.10.52-v7l+/drivers/net/phy/microchip.c
--- linux-5.10.52-orig/drivers/net/phy/microchip.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/net/phy/microchip.c	2021-07-25 16:46:15.848125208 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:220 @
 	struct device *dev = &phydev->mdio.dev;
 	struct lan88xx_priv *priv;
 	u32 led_modes[4];
+	u32 downshift_after = 0;
 	int len;
 
 	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:250 @
 		return -EINVAL;
 	}
 
+	if (!of_property_read_u32(dev->of_node,
+				  "microchip,downshift-after",
+				  &downshift_after)) {
+		u32 mask = LAN78XX_PHY_CTRL3_DOWNSHIFT_CTRL_MASK;
+		u32 val= LAN78XX_PHY_CTRL3_AUTO_DOWNSHIFT;
+
+		switch (downshift_after) {
+		case 2:	val |= LAN78XX_PHY_CTRL3_DOWNSHIFT_CTRL_2;
+			break;
+		case 3:	val |= LAN78XX_PHY_CTRL3_DOWNSHIFT_CTRL_3;
+			break;
+		case 4:	val |= LAN78XX_PHY_CTRL3_DOWNSHIFT_CTRL_4;
+			break;
+		case 5:	val |= LAN78XX_PHY_CTRL3_DOWNSHIFT_CTRL_5;
+			break;
+		case 0: // Disable completely
+			mask = LAN78XX_PHY_CTRL3_AUTO_DOWNSHIFT;
+			val = 0;
+			break;
+		default:
+			return -EINVAL;
+		}
+		(void)phy_modify_paged(phydev, 1, LAN78XX_PHY_CTRL3,
+				       mask, val);
+	}
+
 	/* these values can be used to identify internal PHY */
 	priv->chip_id = phy_read_mmd(phydev, 3, LAN88XX_MMD3_CHIP_ID);
 	priv->chip_rev = phy_read_mmd(phydev, 3, LAN88XX_MMD3_CHIP_REV);
diff -Nur --no-dereference linux-5.10.52-orig/drivers/net/usb/lan78xx.c linux-5.10.52-v7l+/drivers/net/usb/lan78xx.c
--- linux-5.10.52-orig/drivers/net/usb/lan78xx.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/net/usb/lan78xx.c	2021-07-25 16:46:16.018122357 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:428 @
 module_param(msg_level, int, 0);
 MODULE_PARM_DESC(msg_level, "Override default message level");
 
+/* TSO seems to be having some issue with Selective Acknowledge (SACK) that
+ * results in lost data never being retransmitted.
+ * Disable it by default now, but adds a module parameter to enable it for
+ * debug purposes (the full cause is not currently understood).
+ */
+static bool enable_tso;
+module_param(enable_tso, bool, 0644);
+MODULE_PARM_DESC(enable_tso, "Enables TCP segmentation offload");
+
+#define INT_URB_MICROFRAMES_PER_MS	8
+static int int_urb_interval_ms = 8;
+module_param(int_urb_interval_ms, int, 0);
+MODULE_PARM_DESC(int_urb_interval_ms, "Override usb interrupt urb interval");
+
 static int lan78xx_read_reg(struct lan78xx_net *dev, u32 index, u32 *data)
 {
 	u32 *buf = kmalloc(sizeof(u32), GFP_KERNEL);
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1184 @
 	if (unlikely(ret < 0))
 		return -EIO;
 
+	/* Acknowledge any pending PHY interrupt, lest it be the last */
+	phy_read(phydev, LAN88XX_INT_STS);
+
 	phy_read_status(phydev);
 
 	if (!phydev->link && dev->link_on) {
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:2178 @
 	mii_adv_to_linkmode_adv_t(fc, mii_adv);
 	linkmode_or(phydev->advertising, fc, phydev->advertising);
 
+	if (of_property_read_bool(phydev->mdio.dev.of_node,
+				  "microchip,eee-enabled")) {
+		struct ethtool_eee edata;
+		memset(&edata, 0, sizeof(edata));
+		edata.cmd = ETHTOOL_SEEE;
+		edata.advertised = ADVERTISED_1000baseT_Full |
+				   ADVERTISED_100baseT_Full;
+		edata.eee_enabled = true;
+		edata.tx_lpi_enabled = true;
+		if (of_property_read_u32(dev->udev->dev.of_node,
+					 "microchip,tx-lpi-timer",
+					 &edata.tx_lpi_timer))
+			edata.tx_lpi_timer = 600; /* non-aggressive */
+		(void)lan78xx_set_eee(dev->net, &edata);
+	}
+
 	if (phydev->mdio.dev.of_node) {
 		u32 reg;
 		int len;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:2489 @
 	int ret = 0;
 	unsigned long timeout;
 	u8 sig;
+	bool has_eeprom;
+	bool has_otp;
+
+	has_eeprom = !lan78xx_read_eeprom(dev, 0, 0, NULL);
+	has_otp = !lan78xx_read_otp(dev, 0, 0, NULL);
 
 	ret = lan78xx_read_reg(dev, HW_CFG, &buf);
 	buf |= HW_CFG_LRST_;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:2547 @
 
 	ret = lan78xx_read_reg(dev, HW_CFG, &buf);
 	buf |= HW_CFG_MEF_;
+	/* If no valid EEPROM and no valid OTP, enable the LEDs by default */
+	if (!has_eeprom && !has_otp)
+	    buf |= HW_CFG_LED0_EN_ | HW_CFG_LED1_EN_;
 	ret = lan78xx_write_reg(dev, HW_CFG, buf);
 
 	ret = lan78xx_read_reg(dev, USB_CFG0, &buf);
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:2605 @
 			buf |= MAC_CR_AUTO_DUPLEX_ | MAC_CR_AUTO_SPEED_;
 		}
 	}
+	/* If no valid EEPROM and no valid OTP, enable AUTO negotiation */
+	if (!has_eeprom && !has_otp)
+	    buf |= MAC_CR_AUTO_DUPLEX_ | MAC_CR_AUTO_SPEED_;
 	ret = lan78xx_write_reg(dev, MAC_CR, buf);
 
 	ret = lan78xx_read_reg(dev, MAC_TX, &buf);
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:2937 @
 	if (DEFAULT_RX_CSUM_ENABLE)
 		dev->net->features |= NETIF_F_RXCSUM;
 
-	if (DEFAULT_TSO_CSUM_ENABLE)
-		dev->net->features |= NETIF_F_TSO | NETIF_F_TSO6 | NETIF_F_SG;
+	if (DEFAULT_TSO_CSUM_ENABLE) {
+		dev->net->features |= NETIF_F_SG;
+		/* Use module parameter to control TCP segmentation offload as
+		 * it appears to cause issues.
+		 */
+		if (enable_tso)
+			dev->net->features |= NETIF_F_TSO | NETIF_F_TSO6;
+	}
 
 	if (DEFAULT_VLAN_RX_OFFLOAD)
 		dev->net->features |= NETIF_F_HW_VLAN_CTAG_RX;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:3167 @
 	size_t size = dev->rx_urb_size;
 	int ret = 0;
 
-	skb = netdev_alloc_skb_ip_align(dev->net, size);
+	skb = netdev_alloc_skb(dev->net, size);
 	if (!skb) {
 		usb_free_urb(urb);
 		return -ENOMEM;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:3771 @
 	netdev->max_mtu = MAX_SINGLE_PACKET_SIZE;
 	netif_set_gso_max_size(netdev, MAX_SINGLE_PACKET_SIZE - MAX_HEADER);
 
-	period = ep_intr->desc.bInterval;
+	if (int_urb_interval_ms <= 0)
+		period = ep_intr->desc.bInterval;
+	else
+		period = int_urb_interval_ms * INT_URB_MICROFRAMES_PER_MS;
+
+	netif_notice(dev, probe, netdev, "int urb period %d\n", period);
+
 	maxp = usb_maxpacket(dev->udev, dev->pipe_intr, 0);
 	buf = kmalloc(maxp, GFP_KERNEL);
 	if (buf) {
diff -Nur --no-dereference linux-5.10.52-orig/drivers/net/usb/Makefile linux-5.10.52-v7l+/drivers/net/usb/Makefile
--- linux-5.10.52-orig/drivers/net/usb/Makefile	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/net/usb/Makefile	2021-07-25 16:46:15.958123363 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:16 @
 obj-$(CONFIG_USB_NET_AX8817X)	+= asix.o
 asix-y := asix_devices.o asix_common.o ax88172a.o
 obj-$(CONFIG_USB_NET_AX88179_178A)      += ax88179_178a.o
-obj-$(CONFIG_USB_NET_CDCETHER)	+= cdc_ether.o
+obj-$(CONFIG_USB_NET_CDCETHER)	+= cdc_ether.o r8153_ecm.o
 obj-$(CONFIG_USB_NET_CDC_EEM)	+= cdc_eem.o
 obj-$(CONFIG_USB_NET_DM9601)	+= dm9601.o
 obj-$(CONFIG_USB_NET_SR9700)	+= sr9700.o
diff -Nur --no-dereference linux-5.10.52-orig/drivers/net/usb/r8152.c linux-5.10.52-v7l+/drivers/net/usb/r8152.c
--- linux-5.10.52-orig/drivers/net/usb/r8152.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/net/usb/r8152.c	2021-07-25 16:46:16.038122022 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:29 @
 #include <linux/acpi.h>
 #include <linux/firmware.h>
 #include <crypto/hash.h>
+#include <linux/usb/r8152.h>
 
 /* Information for net-next */
-#define NETNEXT_VERSION		"11"
+#define NETNEXT_VERSION		"12"
 
 /* Information for net */
 #define NET_VERSION		"11"
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:46 @
 
 #define PLA_IDR			0xc000
 #define PLA_RCR			0xc010
+#define PLA_RCR1		0xc012
 #define PLA_RMS			0xc016
 #define PLA_RXFIFO_CTRL0	0xc0a0
+#define PLA_RXFIFO_FULL		0xc0a2
 #define PLA_RXFIFO_CTRL1	0xc0a4
+#define PLA_RX_FIFO_FULL	0xc0a6
 #define PLA_RXFIFO_CTRL2	0xc0a8
+#define PLA_RX_FIFO_EMPTY	0xc0aa
 #define PLA_DMY_REG0		0xc0b0
 #define PLA_FMC			0xc0b4
 #define PLA_CFG_WOL		0xc0b6
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:70 @
 #define PLA_MACDBG_PRE		0xd38c	/* RTL_VER_04 only */
 #define PLA_MACDBG_POST		0xd38e	/* RTL_VER_04 only */
 #define PLA_EXTRA_STATUS	0xd398
+#define PLA_GPHY_CTRL		0xd3ae
+#define PLA_POL_GPIO_CTRL	0xdc6a
 #define PLA_EFUSE_DATA		0xdd00
 #define PLA_EFUSE_CMD		0xdd02
 #define PLA_LEDSEL		0xdd90
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:81 @
 #define PLA_LWAKE_CTRL_REG	0xe007
 #define PLA_GPHY_INTR_IMR	0xe022
 #define PLA_EEE_CR		0xe040
+#define PLA_EEE_TXTWSYS		0xe04c
+#define PLA_EEE_TXTWSYS_2P5G	0xe058
 #define PLA_EEEP_CR		0xe080
 #define PLA_MAC_PWR_CTRL	0xe0c0
 #define PLA_MAC_PWR_CTRL2	0xe0ca
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:93 @
 #define PLA_TCR1		0xe612
 #define PLA_MTPS		0xe615
 #define PLA_TXFIFO_CTRL		0xe618
+#define PLA_TXFIFO_FULL		0xe61a
 #define PLA_RSTTALLY		0xe800
 #define PLA_CR			0xe813
 #define PLA_CRWECR		0xe81c
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:110 @
 #define PLA_SFF_STS_7		0xe8de
 #define PLA_PHYSTATUS		0xe908
 #define PLA_CONFIG6		0xe90a /* CONFIG6 */
+#define PLA_USB_CFG		0xe952
 #define PLA_BP_BA		0xfc26
 #define PLA_BP_0		0xfc28
 #define PLA_BP_1		0xfc2a
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:125 @
 #define USB_USB2PHY		0xb41e
 #define USB_SSPHYLINK1		0xb426
 #define USB_SSPHYLINK2		0xb428
+#define USB_L1_CTRL		0xb45e
 #define USB_U2P3_CTRL		0xb460
 #define USB_CSR_DUMMY1		0xb464
 #define USB_CSR_DUMMY2		0xb466
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:136 @
 #define USB_FW_FIX_EN0		0xcfca
 #define USB_FW_FIX_EN1		0xcfcc
 #define USB_LPM_CONFIG		0xcfd8
+#define USB_ECM_OPTION		0xcfee
 #define USB_CSTMR		0xcfef	/* RTL8153A */
+#define USB_MISC_2		0xcfff
+#define USB_ECM_OP		0xd26b
+#define USB_GPHY_CTRL		0xd284
+#define USB_SPEED_OPTION	0xd32a
 #define USB_FW_CTRL		0xd334	/* RTL8153B */
 #define USB_FC_TIMER		0xd340
 #define USB_USB_CTRL		0xd406
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:155 @
 #define USB_RX_EXTRA_AGGR_TMR	0xd432	/* RTL8153B */
 #define USB_TX_DMA		0xd434
 #define USB_UPT_RXDMA_OWN	0xd437
+#define USB_UPHY3_MDCMDIO	0xd480
 #define USB_TOLERANCE		0xd490
 #define USB_LPM_CTRL		0xd41a
 #define USB_BMU_RESET		0xd4b0
+#define USB_BMU_CONFIG		0xd4b4
 #define USB_U1U2_TIMER		0xd4da
 #define USB_FW_TASK		0xd4e8	/* RTL8153B */
+#define USB_RX_AGGR_NUM		0xd4ee
 #define USB_UPS_CTRL		0xd800
 #define USB_POWER_CUT		0xd80a
 #define USB_MISC_0		0xd81a
 #define USB_MISC_1		0xd81f
 #define USB_AFE_CTRL2		0xd824
+#define USB_UPHY_XTAL		0xd826
 #define USB_UPS_CFG		0xd842
 #define USB_UPS_FLAGS		0xd848
 #define USB_WDT1_CTRL		0xe404
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:211 @
 #define OCP_EEE_ABLE		0xa5c4
 #define OCP_EEE_ADV		0xa5d0
 #define OCP_EEE_LPABLE		0xa5d2
+#define OCP_10GBT_CTRL		0xa5d4
+#define OCP_10GBT_STAT		0xa5d6
+#define OCP_EEE_ADV2		0xa6d4
 #define OCP_PHY_STATE		0xa708		/* nway state for 8153 */
 #define OCP_PHY_PATCH_STAT	0xb800
 #define OCP_PHY_PATCH_CMD	0xb820
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:225 @
 /* SRAM Register */
 #define SRAM_GREEN_CFG		0x8011
 #define SRAM_LPF_CFG		0x8012
+#define SRAM_GPHY_FW_VER	0x801e
 #define SRAM_10M_AMP1		0x8080
 #define SRAM_10M_AMP2		0x8082
 #define SRAM_IMPEDANCE		0x8084
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:237 @
 #define RCR_AM			0x00000004
 #define RCR_AB			0x00000008
 #define RCR_ACPT_ALL		(RCR_AAP | RCR_APM | RCR_AM | RCR_AB)
+#define SLOT_EN			BIT(11)
+
+/* PLA_RCR1 */
+#define OUTER_VLAN		BIT(7)
+#define INNER_VLAN		BIT(6)
 
 /* PLA_RXFIFO_CTRL0 */
 #define RXFIFO_THR1_NORMAL	0x00080002
 #define RXFIFO_THR1_OOB		0x01800003
 
+/* PLA_RXFIFO_FULL */
+#define RXFIFO_FULL_MASK	0xfff
+
 /* PLA_RXFIFO_CTRL1 */
 #define RXFIFO_THR2_FULL	0x00000060
 #define RXFIFO_THR2_HIGH	0x00000038
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:284 @
 
 /* PLA_TCR1 */
 #define VERSION_MASK		0x7cf0
+#define IFG_MASK		(BIT(3) | BIT(9) | BIT(8))
+#define IFG_144NS		BIT(9)
+#define IFG_96NS		(BIT(9) | BIT(8))
 
 /* PLA_MTPS */
 #define MTPS_JUMBO		(12 * 1024 / 64)
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:320 @
 #define MCU_BORW_EN		0x4000
 
 /* PLA_CPCR */
+#define FLOW_CTRL_EN		BIT(0)
 #define CPCR_RX_VLAN		0x0040
 
 /* PLA_CFG_WOL */
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:346 @
 /* PLA_CONFIG6 */
 #define LANWAKE_CLR_EN		BIT(0)
 
+/* PLA_USB_CFG */
+#define EN_XG_LIP		BIT(1)
+#define EN_G_LIP		BIT(2)
+
 /* PLA_CONFIG5 */
 #define BWF_EN			0x0040
 #define MWF_EN			0x0020
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:373 @
 /* PLA_MAC_PWR_CTRL2 */
 #define EEE_SPDWN_RATIO		0x8007
 #define MAC_CLK_SPDWN_EN	BIT(15)
+#define EEE_SPDWN_RATIO_MASK	0xff
 
 /* PLA_MAC_PWR_CTRL3 */
 #define PLA_MCU_SPDWN_EN	BIT(14)
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:386 @
 #define PWRSAVE_SPDWN_EN	0x1000
 #define RXDV_SPDWN_EN		0x0800
 #define TX10MIDLE_EN		0x0100
+#define IDLE_SPDWN_EN		BIT(6)
 #define TP100_SPDWN_EN		0x0020
 #define TP500_SPDWN_EN		0x0010
 #define TP1000_SPDWN_EN		0x0008
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:427 @
 #define LINK_CHANGE_FLAG	BIT(8)
 #define POLL_LINK_CHG		BIT(0)
 
+/* PLA_GPHY_CTRL */
+#define GPHY_FLASH		BIT(1)
+
+/* PLA_POL_GPIO_CTRL */
+#define DACK_DET_EN		BIT(15)
+#define POL_GPHY_PATCH		BIT(4)
+
 /* USB_USB2PHY */
 #define USB2PHY_SUSPEND		0x0001
 #define USB2PHY_L1		0x0002
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:482 @
 #define BMU_RESET_EP_IN		0x01
 #define BMU_RESET_EP_OUT	0x02
 
+/* USB_BMU_CONFIG */
+#define ACT_ODMA		BIT(1)
+
 /* USB_UPT_RXDMA_OWN */
 #define OWN_UPDATE		BIT(0)
 #define OWN_CLEAR		BIT(1)
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:492 @
 /* USB_FW_TASK */
 #define FC_PATCH_TASK		BIT(1)
 
+/* USB_RX_AGGR_NUM */
+#define RX_AGGR_NUM_MASK	0x1ff
+
 /* USB_UPS_CTRL */
 #define POWER_CUT		0x0100
 
 /* USB_PM_CTRL_STATUS */
 #define RESUME_INDICATE		0x0001
 
+/* USB_ECM_OPTION */
+#define BYPASS_MAC_RESET	BIT(5)
+
 /* USB_CSTMR */
 #define FORCE_SUPER		BIT(0)
 
+/* USB_MISC_2 */
+#define UPS_FORCE_PWR_DOWN	BIT(0)
+
+/* USB_ECM_OP */
+#define	EN_ALL_SPEED		BIT(0)
+
+/* USB_GPHY_CTRL */
+#define GPHY_PATCH_DONE		BIT(2)
+#define BYPASS_FLASH		BIT(5)
+#define BACKUP_RESTRORE		BIT(6)
+
+/* USB_SPEED_OPTION */
+#define RG_PWRDN_EN		BIT(8)
+#define ALL_SPEED_OFF		BIT(9)
+
 /* USB_FW_CTRL */
 #define FLOW_CTRL_PATCH_OPT	BIT(1)
+#define AUTO_SPEEDUP		BIT(3)
+#define FLOW_CTRL_PATCH_2	BIT(8)
 
 /* USB_FC_TIMER */
 #define CTRL_TIMER_EN		BIT(15)
 
 /* USB_USB_CTRL */
+#define CDC_ECM_EN		BIT(3)
 #define RX_AGG_DISABLE		0x0010
 #define RX_ZERO_EN		0x0080
 
 /* USB_U2P3_CTRL */
 #define U2P3_ENABLE		0x0001
+#define RX_DETECT8		BIT(3)
 
 /* USB_POWER_CUT */
 #define PWR_EN			0x0001
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:573 @
 #define SEN_VAL_NORMAL		0xa000
 #define SEL_RXIDLE		0x0100
 
+/* USB_UPHY_XTAL */
+#define OOBS_POLLING		BIT(8)
+
 /* USB_UPS_CFG */
 #define SAW_CNT_1MS_MASK	0x0fff
+#define MID_REVERSE		BIT(5)	/* RTL8156A */
 
 /* USB_UPS_FLAGS */
 #define UPS_FLAGS_R_TUNE		BIT(0)
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:586 @
 #define UPS_FLAGS_250M_CKDIV		BIT(2)
 #define UPS_FLAGS_EN_ALDPS		BIT(3)
 #define UPS_FLAGS_CTAP_SHORT_DIS	BIT(4)
+#define UPS_FLAGS_SPEED_MASK		(0xf << 16)
 #define ups_flags_speed(x)		((x) << 16)
 #define UPS_FLAGS_EN_EEE		BIT(20)
 #define UPS_FLAGS_EN_500M_EEE		BIT(21)
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:607 @
 	FORCE_10M_FULL,
 	FORCE_100M_HALF,
 	FORCE_100M_FULL,
+	FORCE_1000M_FULL,
+	NWAY_2500M_FULL,
 };
 
 /* OCP_ALDPS_CONFIG */
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:673 @
 #define EN_10M_CLKDIV		BIT(11)
 #define EN_10M_BGOFF		0x0080
 
+/* OCP_10GBT_CTRL */
+#define RTL_ADV2_5G_F_R		BIT(5)	/* Advertise 2.5GBASE-T fast-retrain */
+
 /* OCP_PHY_STATE */
 #define TXDIS_STATE		0x01
 #define ABD_STATE		0x02
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:695 @
 #define EN_EMI_L		0x0040
 
 /* OCP_SYSCLK_CFG */
-#define clk_div_expo(x)		(min(x, 5) << 8)
+#define sysclk_div_expo(x)	(min(x, 5) << 8)
+#define clk_div_expo(x)		(min(x, 5) << 4)
 
 /* SRAM_GREEN_CFG */
 #define GREEN_ETH_EN		BIT(15)
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:727 @
 #define BP4_SUPER_ONLY		0x1578	/* RTL_VER_04 only */
 
 enum rtl_register_content {
+	_2500bps	= BIT(10),
+	_1250bps	= BIT(9),
+	_500bps		= BIT(8),
+	_tx_flow	= BIT(6),
+	_rx_flow	= BIT(5),
 	_1000bps	= 0x10,
 	_100bps		= 0x08,
 	_10bps		= 0x04,
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:739 @
 	FULL_DUP	= 0x01,
 };
 
+#define is_speed_2500(_speed)	(((_speed) & (_2500bps | LINK_STATUS)) == (_2500bps | LINK_STATUS))
+#define is_flow_control(_speed)	(((_speed) & (_tx_flow | _rx_flow)) == (_tx_flow | _rx_flow))
+
 #define RTL8152_MAX_TX		4
 #define RTL8152_MAX_RX		10
 #define INTBUFSIZE		2
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:753 @
 
 #define INTR_LINK		0x0004
 
-#define RTL8152_REQT_READ	0xc0
-#define RTL8152_REQT_WRITE	0x40
-#define RTL8152_REQ_GET_REGS	0x05
-#define RTL8152_REQ_SET_REGS	0x05
-
-#define BYTE_EN_DWORD		0xff
-#define BYTE_EN_WORD		0x33
-#define BYTE_EN_BYTE		0x11
-#define BYTE_EN_SIX_BYTES	0x3f
-#define BYTE_EN_START_MASK	0x0f
-#define BYTE_EN_END_MASK	0xf0
-
-#define RTL8153_MAX_PACKET	9216 /* 9K */
-#define RTL8153_MAX_MTU		(RTL8153_MAX_PACKET - VLAN_ETH_HLEN - \
-				 ETH_FCS_LEN)
 #define RTL8152_RMS		(VLAN_ETH_FRAME_LEN + ETH_FCS_LEN)
 #define RTL8153_RMS		RTL8153_MAX_PACKET
 #define RTL8152_TX_TIMEOUT	(5 * HZ)
-#define RTL8152_NAPI_WEIGHT	64
-#define rx_reserved_size(x)	((x) + VLAN_ETH_HLEN + ETH_FCS_LEN + \
-				 sizeof(struct rx_desc) + RX_ALIGN)
+#define mtu_to_size(m)		((m) + VLAN_ETH_HLEN + ETH_FCS_LEN)
+#define size_to_mtu(s)		((s) - VLAN_ETH_HLEN - ETH_FCS_LEN)
+#define rx_reserved_size(x)	(mtu_to_size(x) + sizeof(struct rx_desc) + RX_ALIGN)
 
 /* rtl8152 flags */
 enum rtl8152_flags {
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:774 @
 	LENOVO_MACPASSTHRU,
 };
 
-/* Define these values to match your device */
-#define VENDOR_ID_REALTEK		0x0bda
-#define VENDOR_ID_MICROSOFT		0x045e
-#define VENDOR_ID_SAMSUNG		0x04e8
-#define VENDOR_ID_LENOVO		0x17ef
-#define VENDOR_ID_LINKSYS		0x13b1
-#define VENDOR_ID_NVIDIA		0x0955
-#define VENDOR_ID_TPLINK		0x2357
-
 #define DEVICE_ID_THINKPAD_THUNDERBOLT3_DOCK_GEN2	0x3082
 #define DEVICE_ID_THINKPAD_USB_C_DOCK_GEN2		0xa387
 
-#define MCU_TYPE_PLA			0x0100
-#define MCU_TYPE_USB			0x0000
-
 struct tally_counter {
 	__le64	tx_packets;
 	__le64	rx_packets;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:888 @
 		bool (*in_nway)(struct r8152 *tp);
 		void (*hw_phy_cfg)(struct r8152 *tp);
 		void (*autosuspend_en)(struct r8152 *tp, bool enable);
+		void (*change_mtu)(struct r8152 *tp);
 	} rtl_ops;
 
 	struct ups_info {
+		u32 r_tune:1;
 		u32 _10m_ckdiv:1;
 		u32 _250m_ckdiv:1;
 		u32 aldps:1;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:934 @
 	u32 rx_buf_sz;
 	u32 rx_copybreak;
 	u32 rx_pending;
+	u32 fc_pause_on, fc_pause_off;
 
+	u32 support_2500full:1;
 	u16 ocp_base;
 	u16 speed;
 	u16 eee_adv;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:971 @
 	struct fw_block blocks[];
 } __packed;
 
+enum rtl8152_fw_flags {
+	FW_FLAGS_USB = 0,
+	FW_FLAGS_PLA,
+	FW_FLAGS_START,
+	FW_FLAGS_STOP,
+	FW_FLAGS_NC,
+	FW_FLAGS_NC1,
+	FW_FLAGS_NC2,
+	FW_FLAGS_UC2,
+	FW_FLAGS_UC,
+	FW_FLAGS_SPEED_UP,
+	FW_FLAGS_VER,
+};
+
+enum rtl8152_fw_fixup_cmd {
+	FW_FIXUP_AND = 0,
+	FW_FIXUP_OR,
+	FW_FIXUP_NOT,
+	FW_FIXUP_XOR,
+};
+
+struct fw_phy_set {
+	__le16 addr;
+	__le16 data;
+} __packed;
+
+struct fw_phy_speed_up {
+	struct fw_block blk_hdr;
+	__le16 fw_offset;
+	__le16 version;
+	__le16 fw_reg;
+	__le16 reserved;
+	char info[];
+} __packed;
+
+struct fw_phy_ver {
+	struct fw_block blk_hdr;
+	struct fw_phy_set ver;
+	__le32 reserved;
+} __packed;
+
+struct fw_phy_fixup {
+	struct fw_block blk_hdr;
+	struct fw_phy_set setting;
+	__le16 bit_cmd;
+	__le16 reserved;
+} __packed;
+
+struct fw_phy_union {
+	struct fw_block blk_hdr;
+	__le16 fw_offset;
+	__le16 fw_reg;
+	struct fw_phy_set pre_set[2];
+	struct fw_phy_set bp[8];
+	struct fw_phy_set bp_en;
+	u8 pre_num;
+	u8 bp_num;
+	char info[];
+} __packed;
+
 /**
  * struct fw_mac - a firmware block used by RTL_FW_PLA and RTL_FW_USB.
  *	The layout of the firmware block is:
  *	<struct fw_mac> + <info> + <firmware data>.
+ * @blk_hdr: firmware descriptor (type, length)
  * @fw_offset: offset of the firmware binary data. The start address of
  *	the data would be the address of struct fw_mac + @fw_offset.
  * @fw_reg: the register to load the firmware. Depends on chip.
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1049 @
  * @bp_num: the break point number which needs to be set for this firmware.
  *	Depends on the firmware.
  * @bp: break points. Depends on firmware.
+ * @reserved: reserved space (unused)
  * @fw_ver_reg: the register to store the fw version.
  * @fw_ver_data: the firmware version of the current type.
  * @info: additional information for debugging, and is followed by the
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1075 @
 /**
  * struct fw_phy_patch_key - a firmware block used by RTL_FW_PHY_START.
  *	This is used to set patch key when loading the firmware of PHY.
+ * @blk_hdr: firmware descriptor (type, length)
  * @key_reg: the register to write the patch key.
  * @key_data: patch key.
+ * @reserved: reserved space (unused)
  */
 struct fw_phy_patch_key {
 	struct fw_block blk_hdr;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1091 @
  * struct fw_phy_nc - a firmware block used by RTL_FW_PHY_NC.
  *	The layout of the firmware block is:
  *	<struct fw_phy_nc> + <info> + <firmware data>.
+ * @blk_hdr: firmware descriptor (type, length)
  * @fw_offset: offset of the firmware binary data. The start address of
  *	the data would be the address of struct fw_phy_nc + @fw_offset.
  * @fw_reg: the register to load the firmware. Depends on chip.
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1100 @
  * @patch_en_addr: the register of enabling patch mode. Depends on chip.
  * @patch_en_value: patch mode enabled mask. Depends on the firmware.
  * @mode_reg: the regitster of switching the mode.
- * @mod_pre: the mode needing to be set before loading the firmware.
- * @mod_post: the mode to be set when finishing to load the firmware.
+ * @mode_pre: the mode needing to be set before loading the firmware.
+ * @mode_post: the mode to be set when finishing to load the firmware.
+ * @reserved: reserved space (unused)
  * @bp_start: the start register of break points. Depends on chip.
  * @bp_num: the break point number which needs to be set for this firmware.
  *	Depends on the firmware.
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1135 @
 	RTL_FW_PHY_START,
 	RTL_FW_PHY_STOP,
 	RTL_FW_PHY_NC,
+	RTL_FW_PHY_FIXUP,
+	RTL_FW_PHY_UNION_NC,
+	RTL_FW_PHY_UNION_NC1,
+	RTL_FW_PHY_UNION_NC2,
+	RTL_FW_PHY_UNION_UC2,
+	RTL_FW_PHY_UNION_UC,
+	RTL_FW_PHY_UNION_MISC,
+	RTL_FW_PHY_SPEED_UP,
+	RTL_FW_PHY_VER,
 };
 
 enum rtl_version {
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1157 @
 	RTL_VER_07,
 	RTL_VER_08,
 	RTL_VER_09,
+
+	RTL_TEST_01,
+	RTL_VER_10,
+	RTL_VER_11,
+	RTL_VER_12,
+	RTL_VER_13,
+	RTL_VER_14,
+	RTL_VER_15,
+
 	RTL_VER_MAX
 };
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1181 @
 #define RTL_ADVERTISED_100_FULL			BIT(3)
 #define RTL_ADVERTISED_1000_HALF		BIT(4)
 #define RTL_ADVERTISED_1000_FULL		BIT(5)
+#define RTL_ADVERTISED_2500_FULL		BIT(6)
 
 /* Maximum number of multicast addresses to filter (vs. Rx-all-multicast).
  * The RTL chips use a 64 element hash table based on the Ethernet CRC.
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1189 @
 static const int multicast_filter_limit = 32;
 static unsigned int agg_buf_sz = 16384;
 
-#define RTL_LIMITED_TSO_SIZE	(agg_buf_sz - sizeof(struct tx_desc) - \
-				 VLAN_ETH_HLEN - ETH_FCS_LEN)
+#define RTL_LIMITED_TSO_SIZE	(size_to_mtu(agg_buf_sz) - sizeof(struct tx_desc))
 
 static
 int get_registers(struct r8152 *tp, u16 value, u16 index, u16 size, void *data)
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1549 @
 static int
 r8152_submit_rx(struct r8152 *tp, struct rx_agg *agg, gfp_t mem_flags);
 
+static int
+rtl8152_set_speed(struct r8152 *tp, u8 autoneg, u32 speed, u8 duplex,
+		  u32 advertising);
+
 static int rtl8152_set_mac_address(struct net_device *netdev, void *p)
 {
 	struct r8152 *tp = netdev_priv(netdev);
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:2575 @
 	} while (res == 0);
 }
 
-static void bottom_half(unsigned long data)
+static void bottom_half(struct tasklet_struct *t)
 {
-	struct r8152 *tp;
-
-	tp = (struct r8152 *)data;
+	struct r8152 *tp = from_tasklet(tp, t, tx_tl);
 
 	if (test_bit(RTL8152_UNPLUG, &tp->flags))
 		return;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:2775 @
 
 static void r8152b_reset_packet_filter(struct r8152 *tp)
 {
-	u32	ocp_data;
+	u32 ocp_data;
 
 	ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_FMC);
 	ocp_data &= ~FMC_FCR_MCU_EN;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:2786 @
 
 static void rtl8152_nic_reset(struct r8152 *tp)
 {
-	int	i;
+	u32 ocp_data;
+	int i;
 
-	ocp_write_byte(tp, MCU_TYPE_PLA, PLA_CR, CR_RST);
+	switch (tp->version) {
+	case RTL_TEST_01:
+	case RTL_VER_10:
+	case RTL_VER_11:
+		ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_CR);
+		ocp_data &= ~CR_TE;
+		ocp_write_byte(tp, MCU_TYPE_PLA, PLA_CR, ocp_data);
+
+		ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_BMU_RESET);
+		ocp_data &= ~BMU_RESET_EP_IN;
+		ocp_write_word(tp, MCU_TYPE_USB, USB_BMU_RESET, ocp_data);
+
+		ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_USB_CTRL);
+		ocp_data |= CDC_ECM_EN;
+		ocp_write_word(tp, MCU_TYPE_USB, USB_USB_CTRL, ocp_data);
+
+		ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_CR);
+		ocp_data &= ~CR_RE;
+		ocp_write_byte(tp, MCU_TYPE_PLA, PLA_CR, ocp_data);
+
+		ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_BMU_RESET);
+		ocp_data |= BMU_RESET_EP_IN;
+		ocp_write_word(tp, MCU_TYPE_USB, USB_BMU_RESET, ocp_data);
+
+		ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_USB_CTRL);
+		ocp_data &= ~CDC_ECM_EN;
+		ocp_write_word(tp, MCU_TYPE_USB, USB_USB_CTRL, ocp_data);
+		break;
 
-	for (i = 0; i < 1000; i++) {
-		if (!(ocp_read_byte(tp, MCU_TYPE_PLA, PLA_CR) & CR_RST))
-			break;
-		usleep_range(100, 400);
+	default:
+		ocp_write_byte(tp, MCU_TYPE_PLA, PLA_CR, CR_RST);
+
+		for (i = 0; i < 1000; i++) {
+			if (!(ocp_read_byte(tp, MCU_TYPE_PLA, PLA_CR) & CR_RST))
+				break;
+			usleep_range(100, 400);
+		}
+		break;
 	}
 }
 
 static void set_tx_qlen(struct r8152 *tp)
 {
-	struct net_device *netdev = tp->netdev;
-
-	tp->tx_qlen = agg_buf_sz / (netdev->mtu + VLAN_ETH_HLEN + ETH_FCS_LEN +
-				    sizeof(struct tx_desc));
+	tp->tx_qlen = agg_buf_sz / (mtu_to_size(tp->netdev->mtu) + sizeof(struct tx_desc));
 }
 
-static inline u8 rtl8152_get_speed(struct r8152 *tp)
+static inline u16 rtl8152_get_speed(struct r8152 *tp)
 {
-	return ocp_read_byte(tp, MCU_TYPE_PLA, PLA_PHYSTATUS);
+	return ocp_read_word(tp, MCU_TYPE_PLA, PLA_PHYSTATUS);
 }
 
-static void rtl_set_eee_plus(struct r8152 *tp)
+static void rtl_eee_plus_en(struct r8152 *tp, bool enable)
 {
 	u32 ocp_data;
-	u8 speed;
 
-	speed = rtl8152_get_speed(tp);
-	if (speed & _10bps) {
-		ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_EEEP_CR);
+	ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_EEEP_CR);
+	if (enable)
 		ocp_data |= EEEP_CR_EEEP_TX;
-		ocp_write_word(tp, MCU_TYPE_PLA, PLA_EEEP_CR, ocp_data);
-	} else {
-		ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_EEEP_CR);
+	else
 		ocp_data &= ~EEEP_CR_EEEP_TX;
-		ocp_write_word(tp, MCU_TYPE_PLA, PLA_EEEP_CR, ocp_data);
-	}
+	ocp_write_word(tp, MCU_TYPE_PLA, PLA_EEEP_CR, ocp_data);
+}
+
+static void rtl_set_eee_plus(struct r8152 *tp)
+{
+	if (rtl8152_get_speed(tp) & _10bps)
+		rtl_eee_plus_en(tp, true);
+	else
+		rtl_eee_plus_en(tp, false);
 }
 
 static void rxdy_gated_en(struct r8152 *tp, bool enable)
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:2955 @
 	return 0;
 }
 
+static void rtl_set_ifg(struct r8152 *tp, u16 speed)
+{
+	u32 ocp_data;
+
+	ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_TCR1);
+	ocp_data &= ~IFG_MASK;
+	if ((speed & (_10bps | _100bps)) && !(speed & FULL_DUP)) {
+		ocp_data |= IFG_144NS;
+		ocp_write_word(tp, MCU_TYPE_PLA, PLA_TCR1, ocp_data);
+
+		ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL4);
+		ocp_data &= ~TX10MIDLE_EN;
+		ocp_write_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL4, ocp_data);
+	} else {
+		ocp_data |= IFG_96NS;
+		ocp_write_word(tp, MCU_TYPE_PLA, PLA_TCR1, ocp_data);
+
+		ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL4);
+		ocp_data |= TX10MIDLE_EN;
+		ocp_write_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL4, ocp_data);
+	}
+}
+
 static inline void r8153b_rx_agg_chg_indicate(struct r8152 *tp)
 {
 	ocp_write_byte(tp, MCU_TYPE_USB, USB_UPT_RXDMA_OWN,
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:2997 @
 	switch (tp->version) {
 	case RTL_VER_08:
 	case RTL_VER_09:
+	case RTL_VER_14:
 		r8153b_rx_agg_chg_indicate(tp);
 		break;
 	default:
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:3035 @
 
 	case RTL_VER_08:
 	case RTL_VER_09:
+	case RTL_VER_14:
 		/* The RTL8153B uses USB_RX_EXTRA_AGGR_TMR for rx timeout
 		 * primarily. For USB_RX_EARLY_TIMEOUT, we fix it to 128ns.
 		 */
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:3045 @
 			       ocp_data);
 		break;
 
+	case RTL_VER_10:
+	case RTL_VER_11:
+	case RTL_VER_12:
+	case RTL_VER_13:
+	case RTL_VER_15:
+		ocp_write_word(tp, MCU_TYPE_USB, USB_RX_EARLY_TIMEOUT,
+			       640 / 8);
+		ocp_write_word(tp, MCU_TYPE_USB, USB_RX_EXTRA_AGGR_TMR,
+			       ocp_data);
+		r8153b_rx_agg_chg_indicate(tp);
+		break;
+
 	default:
 		break;
 	}
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:3076 @
 		break;
 	case RTL_VER_08:
 	case RTL_VER_09:
+	case RTL_VER_14:
+		ocp_write_word(tp, MCU_TYPE_USB, USB_RX_EARLY_SIZE,
+			       ocp_data / 8);
+		break;
+	case RTL_TEST_01:
+	case RTL_VER_10:
+	case RTL_VER_11:
+	case RTL_VER_12:
+	case RTL_VER_13:
+	case RTL_VER_15:
 		ocp_write_word(tp, MCU_TYPE_USB, USB_RX_EARLY_SIZE,
 			       ocp_data / 8);
+		r8153b_rx_agg_chg_indicate(tp);
 		break;
 	default:
 		WARN_ON_ONCE(1);
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:3098 @
 
 static int rtl8153_enable(struct r8152 *tp)
 {
+	u32 ocp_data;
+
 	if (test_bit(RTL8152_UNPLUG, &tp->flags))
 		return -ENODEV;
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:3108 @
 	r8153_set_rx_early_timeout(tp);
 	r8153_set_rx_early_size(tp);
 
-	if (tp->version == RTL_VER_09) {
-		u32 ocp_data;
+	rtl_set_ifg(tp, rtl8152_get_speed(tp));
 
+	switch (tp->version) {
+	case RTL_VER_09:
+	case RTL_VER_14:
 		ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_FW_TASK);
 		ocp_data &= ~FC_PATCH_TASK;
 		ocp_write_word(tp, MCU_TYPE_USB, USB_FW_TASK, ocp_data);
 		usleep_range(1000, 2000);
 		ocp_data |= FC_PATCH_TASK;
 		ocp_write_word(tp, MCU_TYPE_USB, USB_FW_TASK, ocp_data);
+		break;
+	default:
+		break;
 	}
 
 	return rtl_enable(tp);
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:3186 @
 {
 	u32 ocp_data;
 
-	ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_CPCR);
-	if (enable)
-		ocp_data |= CPCR_RX_VLAN;
-	else
-		ocp_data &= ~CPCR_RX_VLAN;
-	ocp_write_word(tp, MCU_TYPE_PLA, PLA_CPCR, ocp_data);
+	switch (tp->version) {
+	case RTL_VER_01:
+	case RTL_VER_02:
+	case RTL_VER_03:
+	case RTL_VER_04:
+	case RTL_VER_05:
+	case RTL_VER_06:
+	case RTL_VER_07:
+	case RTL_VER_08:
+	case RTL_VER_09:
+	case RTL_VER_14:
+		ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_CPCR);
+		if (enable)
+			ocp_data |= CPCR_RX_VLAN;
+		else
+			ocp_data &= ~CPCR_RX_VLAN;
+		ocp_write_word(tp, MCU_TYPE_PLA, PLA_CPCR, ocp_data);
+		break;
+
+	case RTL_TEST_01:
+	case RTL_VER_10:
+	case RTL_VER_11:
+	case RTL_VER_12:
+	case RTL_VER_13:
+	case RTL_VER_15:
+	default:
+		ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_RCR1);
+		if (enable)
+			ocp_data |= OUTER_VLAN | INNER_VLAN;
+		else
+			ocp_data &= ~(OUTER_VLAN | INNER_VLAN);
+		ocp_write_word(tp, MCU_TYPE_PLA, PLA_RCR1, ocp_data);
+		break;
+	}
 }
 
 static int rtl8152_set_features(struct net_device *dev,
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:3312 @
 		device_set_wakeup_enable(&tp->udev->dev, false);
 }
 
+static void r8153_mac_clk_speed_down(struct r8152 *tp, bool enable)
+{
+	u32 ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL2);
+
+	/* MAC clock speed down */
+	if (enable)
+		ocp_data |= MAC_CLK_SPDWN_EN;
+	else
+		ocp_data &= ~MAC_CLK_SPDWN_EN;
+
+	ocp_write_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL2, ocp_data);
+}
+
+static void r8156_mac_clk_spd(struct r8152 *tp, bool enable)
+{
+	u32 ocp_data;
+
+	/* MAC clock speed down */
+	if (enable) {
+		/* aldps_spdwn_ratio, tp10_spdwn_ratio */
+		ocp_write_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL,
+			       0x0403);
+
+		ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL2);
+		ocp_data &= ~EEE_SPDWN_RATIO_MASK;
+		ocp_data |= MAC_CLK_SPDWN_EN | 0x03; /* eee_spdwn_ratio */
+		ocp_write_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL2, ocp_data);
+	} else {
+		ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL2);
+		ocp_data &= ~MAC_CLK_SPDWN_EN;
+		ocp_write_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL2, ocp_data);
+	}
+}
+
 static void r8153_u1u2en(struct r8152 *tp, bool enable)
 {
 	u8 u1u2[8];
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:3405 @
 	if (tp->ups_info.eee_cmod_lv)
 		ups_flags |= UPS_FLAGS_EEE_CMOD_LV_EN;
 
+	if (tp->ups_info.r_tune)
+		ups_flags |= UPS_FLAGS_R_TUNE;
+
 	if (tp->ups_info._10m_ckdiv)
 		ups_flags |= UPS_FLAGS_EN_10M_CKDIV;
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:3458 @
 	ocp_write_dword(tp, MCU_TYPE_USB, USB_UPS_FLAGS, ups_flags);
 }
 
-static void r8153b_green_en(struct r8152 *tp, bool enable)
+static void r8156_ups_flags(struct r8152 *tp)
+{
+	u32 ups_flags = 0;
+
+	if (tp->ups_info.green)
+		ups_flags |= UPS_FLAGS_EN_GREEN;
+
+	if (tp->ups_info.aldps)
+		ups_flags |= UPS_FLAGS_EN_ALDPS;
+
+	if (tp->ups_info.eee)
+		ups_flags |= UPS_FLAGS_EN_EEE;
+
+	if (tp->ups_info.flow_control)
+		ups_flags |= UPS_FLAGS_EN_FLOW_CTR;
+
+	if (tp->ups_info.eee_ckdiv)
+		ups_flags |= UPS_FLAGS_EN_EEE_CKDIV;
+
+	if (tp->ups_info._10m_ckdiv)
+		ups_flags |= UPS_FLAGS_EN_10M_CKDIV;
+
+	if (tp->ups_info.eee_plloff_100)
+		ups_flags |= UPS_FLAGS_EEE_PLLOFF_100;
+
+	if (tp->ups_info.eee_plloff_giga)
+		ups_flags |= UPS_FLAGS_EEE_PLLOFF_GIGA;
+
+	if (tp->ups_info._250m_ckdiv)
+		ups_flags |= UPS_FLAGS_250M_CKDIV;
+
+	switch (tp->ups_info.speed_duplex) {
+	case FORCE_10M_HALF:
+		ups_flags |= ups_flags_speed(0);
+		break;
+	case FORCE_10M_FULL:
+		ups_flags |= ups_flags_speed(1);
+		break;
+	case FORCE_100M_HALF:
+		ups_flags |= ups_flags_speed(2);
+		break;
+	case FORCE_100M_FULL:
+		ups_flags |= ups_flags_speed(3);
+		break;
+	case NWAY_10M_HALF:
+		ups_flags |= ups_flags_speed(4);
+		break;
+	case NWAY_10M_FULL:
+		ups_flags |= ups_flags_speed(5);
+		break;
+	case NWAY_100M_HALF:
+		ups_flags |= ups_flags_speed(6);
+		break;
+	case NWAY_100M_FULL:
+		ups_flags |= ups_flags_speed(7);
+		break;
+	case NWAY_1000M_FULL:
+		ups_flags |= ups_flags_speed(8);
+		break;
+	case NWAY_2500M_FULL:
+		ups_flags |= ups_flags_speed(9);
+		break;
+	default:
+		break;
+	}
+
+	switch (tp->ups_info.lite_mode) {
+	case 1:
+		ups_flags |= 0 << 5;
+		break;
+	case 2:
+		ups_flags |= 2 << 5;
+		break;
+	case 0:
+	default:
+		ups_flags |= 1 << 5;
+		break;
+	}
+
+	ocp_write_dword(tp, MCU_TYPE_USB, USB_UPS_FLAGS, ups_flags);
+}
+
+static void rtl_green_en(struct r8152 *tp, bool enable)
 {
 	u16 data;
 
+	data = sram_read(tp, SRAM_GREEN_CFG);
+	if (enable)
+		data |= GREEN_ETH_EN;
+	else
+		data &= ~GREEN_ETH_EN;
+	sram_write(tp, SRAM_GREEN_CFG, data);
+
+	tp->ups_info.green = enable;
+}
+
+static void r8153b_green_en(struct r8152 *tp, bool enable)
+{
 	if (enable) {
 		sram_write(tp, 0x8045, 0);	/* 10M abiq&ldvbias */
 		sram_write(tp, 0x804d, 0x1222);	/* 100M short abiq&ldvbias */
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:3566 @
 		sram_write(tp, 0x805d, 0x2444);	/* 1000M short abiq&ldvbias */
 	}
 
-	data = sram_read(tp, SRAM_GREEN_CFG);
-	data |= GREEN_ETH_EN;
-	sram_write(tp, SRAM_GREEN_CFG, data);
-
-	tp->ups_info.green = enable;
+	rtl_green_en(tp, true);
 }
 
 static u16 r8153_phy_status(struct r8152 *tp, u16 desired)
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:3603 @
 		ocp_data |= UPS_EN | USP_PREWAKE | PHASE2_EN;
 		ocp_write_byte(tp, MCU_TYPE_USB, USB_POWER_CUT, ocp_data);
 
-		ocp_data = ocp_read_byte(tp, MCU_TYPE_USB, 0xcfff);
-		ocp_data |= BIT(0);
-		ocp_write_byte(tp, MCU_TYPE_USB, 0xcfff, ocp_data);
+		ocp_data = ocp_read_byte(tp, MCU_TYPE_USB, USB_MISC_2);
+		ocp_data |= UPS_FORCE_PWR_DOWN;
+		ocp_write_byte(tp, MCU_TYPE_USB, USB_MISC_2, ocp_data);
 	} else {
-		u16 data;
+		ocp_data &= ~(UPS_EN | USP_PREWAKE);
+		ocp_write_byte(tp, MCU_TYPE_USB, USB_POWER_CUT, ocp_data);
+
+		ocp_data = ocp_read_byte(tp, MCU_TYPE_USB, USB_MISC_2);
+		ocp_data &= ~UPS_FORCE_PWR_DOWN;
+		ocp_write_byte(tp, MCU_TYPE_USB, USB_MISC_2, ocp_data);
+
+		if (ocp_read_word(tp, MCU_TYPE_USB, USB_MISC_0) & PCUT_STATUS) {
+			int i;
+
+			for (i = 0; i < 500; i++) {
+				if (ocp_read_word(tp, MCU_TYPE_PLA, PLA_BOOT_CTRL) &
+				    AUTOLOAD_DONE)
+					break;
+				msleep(20);
+			}
+
+			tp->rtl_ops.hw_phy_cfg(tp);
+
+			rtl8152_set_speed(tp, tp->autoneg, tp->speed,
+					  tp->duplex, tp->advertising);
+		}
+	}
+}
 
+static void r8153c_ups_en(struct r8152 *tp, bool enable)
+{
+	u32 ocp_data = ocp_read_byte(tp, MCU_TYPE_USB, USB_POWER_CUT);
+
+	if (enable) {
+		r8153b_ups_flags(tp);
+
+		ocp_data |= UPS_EN | USP_PREWAKE | PHASE2_EN;
+		ocp_write_byte(tp, MCU_TYPE_USB, USB_POWER_CUT, ocp_data);
+
+		ocp_data = ocp_read_byte(tp, MCU_TYPE_USB, USB_MISC_2);
+		ocp_data |= UPS_FORCE_PWR_DOWN;
+		ocp_data &= ~BIT(7);
+		ocp_write_byte(tp, MCU_TYPE_USB, USB_MISC_2, ocp_data);
+	} else {
 		ocp_data &= ~(UPS_EN | USP_PREWAKE);
 		ocp_write_byte(tp, MCU_TYPE_USB, USB_POWER_CUT, ocp_data);
 
-		ocp_data = ocp_read_byte(tp, MCU_TYPE_USB, 0xcfff);
-		ocp_data &= ~BIT(0);
-		ocp_write_byte(tp, MCU_TYPE_USB, 0xcfff, ocp_data);
+		ocp_data = ocp_read_byte(tp, MCU_TYPE_USB, USB_MISC_2);
+		ocp_data &= ~UPS_FORCE_PWR_DOWN;
+		ocp_write_byte(tp, MCU_TYPE_USB, USB_MISC_2, ocp_data);
+
+		if (ocp_read_word(tp, MCU_TYPE_USB, USB_MISC_0) & PCUT_STATUS) {
+			int i;
+
+			for (i = 0; i < 500; i++) {
+				if (ocp_read_word(tp, MCU_TYPE_PLA, PLA_BOOT_CTRL) &
+				    AUTOLOAD_DONE)
+					break;
+				msleep(20);
+			}
 
-		ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_MISC_0);
-		ocp_data &= ~PCUT_STATUS;
-		ocp_write_word(tp, MCU_TYPE_USB, USB_MISC_0, ocp_data);
+			tp->rtl_ops.hw_phy_cfg(tp);
+
+			rtl8152_set_speed(tp, tp->autoneg, tp->speed,
+					  tp->duplex, tp->advertising);
+		}
 
-		data = r8153_phy_status(tp, 0);
+		ocp_write_byte(tp, MCU_TYPE_PLA, PLA_CRWECR, CRWECR_CONFIG);
 
-		switch (data) {
-		case PHY_STAT_PWRDN:
-		case PHY_STAT_EXT_INIT:
-			r8153b_green_en(tp,
-					test_bit(GREEN_ETHERNET, &tp->flags));
-
-			data = r8152_mdio_read(tp, MII_BMCR);
-			data &= ~BMCR_PDOWN;
-			data |= BMCR_RESET;
-			r8152_mdio_write(tp, MII_BMCR, data);
+		ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_CONFIG34);
+		ocp_data |= BIT(8);
+		ocp_write_word(tp, MCU_TYPE_PLA, PLA_CONFIG34, ocp_data);
 
-			data = r8153_phy_status(tp, PHY_STAT_LAN_ON);
-			fallthrough;
+		ocp_write_byte(tp, MCU_TYPE_PLA, PLA_CRWECR, CRWECR_NORAML);
+	}
+}
+
+static void r8156_ups_en(struct r8152 *tp, bool enable)
+{
+	u32 ocp_data = ocp_read_byte(tp, MCU_TYPE_USB, USB_POWER_CUT);
+
+	if (enable) {
+		r8156_ups_flags(tp);
+
+		ocp_data |= UPS_EN | USP_PREWAKE | PHASE2_EN;
+		ocp_write_byte(tp, MCU_TYPE_USB, USB_POWER_CUT, ocp_data);
+
+		ocp_data = ocp_read_byte(tp, MCU_TYPE_USB, USB_MISC_2);
+		ocp_data |= UPS_FORCE_PWR_DOWN;
+		ocp_write_byte(tp, MCU_TYPE_USB, USB_MISC_2, ocp_data);
 
+		switch (tp->version) {
+		case RTL_VER_13:
+		case RTL_VER_15:
+			ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_UPHY_XTAL);
+			ocp_data &= ~OOBS_POLLING;
+			ocp_write_byte(tp, MCU_TYPE_USB, USB_UPHY_XTAL, ocp_data);
+			break;
 		default:
-			if (data != PHY_STAT_LAN_ON)
-				netif_warn(tp, link, tp->netdev,
-					   "PHY not ready");
 			break;
 		}
+	} else {
+		ocp_data &= ~(UPS_EN | USP_PREWAKE);
+		ocp_write_byte(tp, MCU_TYPE_USB, USB_POWER_CUT, ocp_data);
+
+		ocp_data = ocp_read_byte(tp, MCU_TYPE_USB, USB_MISC_2);
+		ocp_data &= ~UPS_FORCE_PWR_DOWN;
+		ocp_write_byte(tp, MCU_TYPE_USB, USB_MISC_2, ocp_data);
+
+		if (ocp_read_word(tp, MCU_TYPE_USB, USB_MISC_0) & PCUT_STATUS) {
+			tp->rtl_ops.hw_phy_cfg(tp);
+
+			rtl8152_set_speed(tp, tp->autoneg, tp->speed,
+					  tp->duplex, tp->advertising);
+		}
 	}
 }
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:3845 @
 		r8153b_ups_en(tp, false);
 		r8153_queue_wake(tp, false);
 		rtl_runtime_suspend_enable(tp, false);
-		if (tp->udev->speed != USB_SPEED_HIGH)
+		if (tp->udev->speed >= USB_SPEED_SUPER)
+			r8153b_u1u2en(tp, true);
+	}
+}
+
+static void rtl8153c_runtime_enable(struct r8152 *tp, bool enable)
+{
+	if (enable) {
+		r8153_queue_wake(tp, true);
+		r8153b_u1u2en(tp, false);
+		r8153_u2p3en(tp, false);
+		rtl_runtime_suspend_enable(tp, true);
+		r8153c_ups_en(tp, true);
+	} else {
+		r8153c_ups_en(tp, false);
+		r8153_queue_wake(tp, false);
+		rtl_runtime_suspend_enable(tp, false);
+		r8153b_u1u2en(tp, true);
+	}
+}
+
+static void rtl8156_runtime_enable(struct r8152 *tp, bool enable)
+{
+	if (enable) {
+		r8153_queue_wake(tp, true);
+		r8153b_u1u2en(tp, false);
+		r8153_u2p3en(tp, false);
+		rtl_runtime_suspend_enable(tp, true);
+	} else {
+		r8153_queue_wake(tp, false);
+		rtl_runtime_suspend_enable(tp, false);
+		r8153_u2p3en(tp, true);
+		if (tp->udev->speed >= USB_SPEED_SUPER)
 			r8153b_u1u2en(tp, true);
 	}
 }
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:3902 @
 
 	case RTL_VER_08:
 	case RTL_VER_09:
+	case RTL_TEST_01:
+	case RTL_VER_10:
+	case RTL_VER_11:
+	case RTL_VER_12:
+	case RTL_VER_13:
+	case RTL_VER_14:
+	case RTL_VER_15:
+	default:
 		/* The bit 0 ~ 7 are relative with teredo settings. They are
 		 * W1C (write 1 to clear), so set all 1 to disable it.
 		 */
 		ocp_write_byte(tp, MCU_TYPE_PLA, PLA_TEREDO_CFG, 0xff);
 		break;
-
-	default:
-		break;
 	}
 
 	ocp_write_word(tp, MCU_TYPE_PLA, PLA_WDT6_CTRL, WDT6_SET_MODE);
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:3949 @
 		break;
 	case RTL_VER_08:
 	case RTL_VER_09:
+	case RTL_VER_10:
+	case RTL_VER_11:
+	case RTL_VER_12:
+	case RTL_VER_13:
+	case RTL_VER_14:
+	case RTL_VER_15:
 	default:
 		if (type == MCU_TYPE_USB) {
 			ocp_write_byte(tp, MCU_TYPE_USB, USB_BP2_EN, 0);
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:3987 @
 	ocp_write_word(tp, type, PLA_BP_BA, 0);
 }
 
-static int r8153_patch_request(struct r8152 *tp, bool request)
+static int rtl_phy_patch_request(struct r8152 *tp, bool request, bool wait)
 {
-	u16 data;
+	u16 data, check;
 	int i;
 
 	data = ocp_reg_read(tp, OCP_PHY_PATCH_CMD);
-	if (request)
+	if (request) {
 		data |= PATCH_REQUEST;
-	else
+		check = 0;
+	} else {
 		data &= ~PATCH_REQUEST;
+		check = PATCH_READY;
+	}
 	ocp_reg_write(tp, OCP_PHY_PATCH_CMD, data);
 
-	for (i = 0; request && i < 5000; i++) {
+	for (i = 0; wait && i < 5000; i++) {
+		u32 ocp_data;
+
 		usleep_range(1000, 2000);
-		if (ocp_reg_read(tp, OCP_PHY_PATCH_STAT) & PATCH_READY)
+		ocp_data = ocp_reg_read(tp, OCP_PHY_PATCH_STAT);
+		if ((ocp_data & PATCH_READY) ^ check)
 			break;
 	}
 
-	if (request && !(ocp_reg_read(tp, OCP_PHY_PATCH_STAT) & PATCH_READY)) {
-		netif_err(tp, drv, tp->netdev, "patch request fail\n");
-		r8153_patch_request(tp, false);
+	if (request && wait &&
+	    !(ocp_reg_read(tp, OCP_PHY_PATCH_STAT) & PATCH_READY)) {
+		dev_err(&tp->intf->dev, "PHY patch request fail\n");
+		rtl_phy_patch_request(tp, false, false);
 		return -ETIME;
 	} else {
 		return 0;
 	}
 }
 
-static int r8153_pre_ram_code(struct r8152 *tp, u16 key_addr, u16 patch_key)
+static void rtl_patch_key_set(struct r8152 *tp, u16 key_addr, u16 patch_key)
 {
-	if (r8153_patch_request(tp, true)) {
-		dev_err(&tp->intf->dev, "patch request fail\n");
-		return -ETIME;
-	}
+	if (patch_key && key_addr) {
+		sram_write(tp, key_addr, patch_key);
+		sram_write(tp, SRAM_PHY_LOCK, PHY_PATCH_LOCK);
+	} else if (key_addr) {
+		u16 data;
 
-	sram_write(tp, key_addr, patch_key);
-	sram_write(tp, SRAM_PHY_LOCK, PHY_PATCH_LOCK);
+		sram_write(tp, 0x0000, 0x0000);
 
-	return 0;
+		data = ocp_reg_read(tp, OCP_PHY_LOCK);
+		data &= ~PATCH_LOCK;
+		ocp_reg_write(tp, OCP_PHY_LOCK, data);
+
+		sram_write(tp, key_addr, 0x0000);
+	} else {
+		WARN_ON_ONCE(1);
+	}
 }
 
-static int r8153_post_ram_code(struct r8152 *tp, u16 key_addr)
+static int
+rtl_pre_ram_code(struct r8152 *tp, u16 key_addr, u16 patch_key, bool wait)
 {
-	u16 data;
+	if (rtl_phy_patch_request(tp, true, wait))
+		return -ETIME;
 
-	sram_write(tp, 0x0000, 0x0000);
+	rtl_patch_key_set(tp, key_addr, patch_key);
 
-	data = ocp_reg_read(tp, OCP_PHY_LOCK);
-	data &= ~PATCH_LOCK;
-	ocp_reg_write(tp, OCP_PHY_LOCK, data);
+	return 0;
+}
 
-	sram_write(tp, key_addr, 0x0000);
+static int rtl_post_ram_code(struct r8152 *tp, u16 key_addr, bool wait)
+{
+	rtl_patch_key_set(tp, key_addr, 0);
 
-	r8153_patch_request(tp, false);
+	rtl_phy_patch_request(tp, false, wait);
 
 	ocp_write_word(tp, MCU_TYPE_PLA, PLA_OCP_GPHY_BASE, tp->ocp_base);
 
 	return 0;
 }
 
+static bool rtl8152_is_fw_phy_speed_up_ok(struct r8152 *tp, struct fw_phy_speed_up *phy)
+{
+	u16 fw_offset;
+	u32 length;
+	bool rc = false;
+
+	switch (tp->version) {
+	case RTL_VER_01:
+	case RTL_VER_02:
+	case RTL_VER_03:
+	case RTL_VER_04:
+	case RTL_VER_05:
+	case RTL_VER_06:
+	case RTL_VER_07:
+	case RTL_VER_08:
+	case RTL_VER_09:
+	case RTL_VER_10:
+	case RTL_VER_11:
+	case RTL_VER_12:
+	case RTL_VER_14:
+		goto out;
+	case RTL_VER_13:
+	case RTL_VER_15:
+	default:
+		break;
+	}
+
+	fw_offset = __le16_to_cpu(phy->fw_offset);
+	length = __le32_to_cpu(phy->blk_hdr.length);
+	if (fw_offset < sizeof(*phy) || length <= fw_offset) {
+		dev_err(&tp->intf->dev, "invalid fw_offset\n");
+		goto out;
+	}
+
+	length -= fw_offset;
+	if (length & 3) {
+		dev_err(&tp->intf->dev, "invalid block length\n");
+		goto out;
+	}
+
+	if (__le16_to_cpu(phy->fw_reg) != 0x9A00) {
+		dev_err(&tp->intf->dev, "invalid register to load firmware\n");
+		goto out;
+	}
+
+	rc = true;
+out:
+	return rc;
+}
+
+static bool rtl8152_is_fw_phy_ver_ok(struct r8152 *tp, struct fw_phy_ver *ver)
+{
+	bool rc = false;
+
+	switch (tp->version) {
+	case RTL_VER_10:
+	case RTL_VER_11:
+	case RTL_VER_12:
+	case RTL_VER_13:
+	case RTL_VER_15:
+		break;
+	default:
+		goto out;
+	}
+
+	if (__le32_to_cpu(ver->blk_hdr.length) != sizeof(*ver)) {
+		dev_err(&tp->intf->dev, "invalid block length\n");
+		goto out;
+	}
+
+	if (__le16_to_cpu(ver->ver.addr) != SRAM_GPHY_FW_VER) {
+		dev_err(&tp->intf->dev, "invalid phy ver addr\n");
+		goto out;
+	}
+
+	rc = true;
+out:
+	return rc;
+}
+
+static bool rtl8152_is_fw_phy_fixup_ok(struct r8152 *tp, struct fw_phy_fixup *fix)
+{
+	bool rc = false;
+
+	switch (tp->version) {
+	case RTL_VER_10:
+	case RTL_VER_11:
+	case RTL_VER_12:
+	case RTL_VER_13:
+	case RTL_VER_15:
+		break;
+	default:
+		goto out;
+	}
+
+	if (__le32_to_cpu(fix->blk_hdr.length) != sizeof(*fix)) {
+		dev_err(&tp->intf->dev, "invalid block length\n");
+		goto out;
+	}
+
+	if (__le16_to_cpu(fix->setting.addr) != OCP_PHY_PATCH_CMD ||
+	    __le16_to_cpu(fix->setting.data) != BIT(7)) {
+		dev_err(&tp->intf->dev, "invalid phy fixup\n");
+		goto out;
+	}
+
+	rc = true;
+out:
+	return rc;
+}
+
+static bool rtl8152_is_fw_phy_union_ok(struct r8152 *tp, struct fw_phy_union *phy)
+{
+	u16 fw_offset;
+	u32 length;
+	bool rc = false;
+
+	switch (tp->version) {
+	case RTL_VER_10:
+	case RTL_VER_11:
+	case RTL_VER_12:
+	case RTL_VER_13:
+	case RTL_VER_15:
+		break;
+	default:
+		goto out;
+	}
+
+	fw_offset = __le16_to_cpu(phy->fw_offset);
+	length = __le32_to_cpu(phy->blk_hdr.length);
+	if (fw_offset < sizeof(*phy) || length <= fw_offset) {
+		dev_err(&tp->intf->dev, "invalid fw_offset\n");
+		goto out;
+	}
+
+	length -= fw_offset;
+	if (length & 1) {
+		dev_err(&tp->intf->dev, "invalid block length\n");
+		goto out;
+	}
+
+	if (phy->pre_num > 2) {
+		dev_err(&tp->intf->dev, "invalid pre_num %d\n", phy->pre_num);
+		goto out;
+	}
+
+	if (phy->bp_num > 8) {
+		dev_err(&tp->intf->dev, "invalid bp_num %d\n", phy->bp_num);
+		goto out;
+	}
+
+	rc = true;
+out:
+	return rc;
+}
+
 static bool rtl8152_is_fw_phy_nc_ok(struct r8152 *tp, struct fw_phy_nc *phy)
 {
 	u32 length;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4320 @
 		case RTL_VER_06:
 		case RTL_VER_08:
 		case RTL_VER_09:
+		case RTL_VER_11:
+		case RTL_VER_12:
+		case RTL_VER_13:
+		case RTL_VER_14:
+		case RTL_VER_15:
 			fw_reg = 0xf800;
 			bp_ba_addr = PLA_BP_BA;
 			bp_en_addr = PLA_BP_EN;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4348 @
 			break;
 		case RTL_VER_08:
 		case RTL_VER_09:
+		case RTL_VER_11:
+		case RTL_VER_12:
+		case RTL_VER_13:
+		case RTL_VER_14:
+		case RTL_VER_15:
 			fw_reg = 0xe600;
 			bp_ba_addr = USB_BP_BA;
 			bp_en_addr = USB_BP2_EN;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4480 @
 {
 	const struct firmware *fw = rtl_fw->fw;
 	struct fw_header *fw_hdr = (struct fw_header *)fw->data;
-	struct fw_mac *pla = NULL, *usb = NULL;
-	struct fw_phy_patch_key *start = NULL;
-	struct fw_phy_nc *phy_nc = NULL;
-	struct fw_block *stop = NULL;
+	unsigned long fw_flags = 0;
 	long ret = -EFAULT;
 	int i;
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4509 @
 				goto fail;
 			goto fw_end;
 		case RTL_FW_PLA:
-			if (pla) {
+			if (test_bit(FW_FLAGS_PLA, &fw_flags)) {
 				dev_err(&tp->intf->dev,
 					"multiple PLA firmware encountered");
 				goto fail;
 			}
 
-			pla = (struct fw_mac *)block;
-			if (!rtl8152_is_fw_mac_ok(tp, pla)) {
+			if (!rtl8152_is_fw_mac_ok(tp, (struct fw_mac *)block)) {
 				dev_err(&tp->intf->dev,
 					"check PLA firmware failed\n");
 				goto fail;
 			}
+			__set_bit(FW_FLAGS_PLA, &fw_flags);
 			break;
 		case RTL_FW_USB:
-			if (usb) {
+			if (test_bit(FW_FLAGS_USB, &fw_flags)) {
 				dev_err(&tp->intf->dev,
 					"multiple USB firmware encountered");
 				goto fail;
 			}
 
-			usb = (struct fw_mac *)block;
-			if (!rtl8152_is_fw_mac_ok(tp, usb)) {
+			if (!rtl8152_is_fw_mac_ok(tp, (struct fw_mac *)block)) {
 				dev_err(&tp->intf->dev,
 					"check USB firmware failed\n");
 				goto fail;
 			}
+			__set_bit(FW_FLAGS_USB, &fw_flags);
 			break;
 		case RTL_FW_PHY_START:
-			if (start || phy_nc || stop) {
+			if (test_bit(FW_FLAGS_START, &fw_flags) ||
+			    test_bit(FW_FLAGS_NC, &fw_flags) ||
+			    test_bit(FW_FLAGS_NC1, &fw_flags) ||
+			    test_bit(FW_FLAGS_NC2, &fw_flags) ||
+			    test_bit(FW_FLAGS_UC2, &fw_flags) ||
+			    test_bit(FW_FLAGS_UC, &fw_flags) ||
+			    test_bit(FW_FLAGS_STOP, &fw_flags)) {
 				dev_err(&tp->intf->dev,
 					"check PHY_START fail\n");
 				goto fail;
 			}
 
-			if (__le32_to_cpu(block->length) != sizeof(*start)) {
+			if (__le32_to_cpu(block->length) != sizeof(struct fw_phy_patch_key)) {
 				dev_err(&tp->intf->dev,
 					"Invalid length for PHY_START\n");
 				goto fail;
 			}
-
-			start = (struct fw_phy_patch_key *)block;
+			__set_bit(FW_FLAGS_START, &fw_flags);
 			break;
 		case RTL_FW_PHY_STOP:
-			if (stop || !start) {
+			if (test_bit(FW_FLAGS_STOP, &fw_flags) ||
+			    !test_bit(FW_FLAGS_START, &fw_flags)) {
 				dev_err(&tp->intf->dev,
 					"Check PHY_STOP fail\n");
 				goto fail;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4569 @
 					"Invalid length for PHY_STOP\n");
 				goto fail;
 			}
-
-			stop = block;
+			__set_bit(FW_FLAGS_STOP, &fw_flags);
 			break;
 		case RTL_FW_PHY_NC:
-			if (!start || stop) {
+			if (!test_bit(FW_FLAGS_START, &fw_flags) ||
+			    test_bit(FW_FLAGS_STOP, &fw_flags)) {
 				dev_err(&tp->intf->dev,
 					"check PHY_NC fail\n");
 				goto fail;
 			}
 
-			if (phy_nc) {
+			if (test_bit(FW_FLAGS_NC, &fw_flags)) {
 				dev_err(&tp->intf->dev,
 					"multiple PHY NC encountered\n");
 				goto fail;
 			}
 
-			phy_nc = (struct fw_phy_nc *)block;
-			if (!rtl8152_is_fw_phy_nc_ok(tp, phy_nc)) {
+			if (!rtl8152_is_fw_phy_nc_ok(tp, (struct fw_phy_nc *)block)) {
 				dev_err(&tp->intf->dev,
 					"check PHY NC firmware failed\n");
 				goto fail;
 			}
+			__set_bit(FW_FLAGS_NC, &fw_flags);
+			break;
+		case RTL_FW_PHY_UNION_NC:
+			if (!test_bit(FW_FLAGS_START, &fw_flags) ||
+			    test_bit(FW_FLAGS_NC1, &fw_flags) ||
+			    test_bit(FW_FLAGS_NC2, &fw_flags) ||
+			    test_bit(FW_FLAGS_UC2, &fw_flags) ||
+			    test_bit(FW_FLAGS_UC, &fw_flags) ||
+			    test_bit(FW_FLAGS_STOP, &fw_flags)) {
+				dev_err(&tp->intf->dev, "PHY_UNION_NC out of order\n");
+				goto fail;
+			}
+
+			if (test_bit(FW_FLAGS_NC, &fw_flags)) {
+				dev_err(&tp->intf->dev, "multiple PHY_UNION_NC encountered\n");
+				goto fail;
+			}
+
+			if (!rtl8152_is_fw_phy_union_ok(tp, (struct fw_phy_union *)block)) {
+				dev_err(&tp->intf->dev, "check PHY_UNION_NC failed\n");
+				goto fail;
+			}
+			__set_bit(FW_FLAGS_NC, &fw_flags);
+			break;
+		case RTL_FW_PHY_UNION_NC1:
+			if (!test_bit(FW_FLAGS_START, &fw_flags) ||
+			    test_bit(FW_FLAGS_NC2, &fw_flags) ||
+			    test_bit(FW_FLAGS_UC2, &fw_flags) ||
+			    test_bit(FW_FLAGS_UC, &fw_flags) ||
+			    test_bit(FW_FLAGS_STOP, &fw_flags)) {
+				dev_err(&tp->intf->dev, "PHY_UNION_NC1 out of order\n");
+				goto fail;
+			}
+
+			if (test_bit(FW_FLAGS_NC1, &fw_flags)) {
+				dev_err(&tp->intf->dev, "multiple PHY NC1 encountered\n");
+				goto fail;
+			}
+
+			if (!rtl8152_is_fw_phy_union_ok(tp, (struct fw_phy_union *)block)) {
+				dev_err(&tp->intf->dev, "check PHY_UNION_NC1 failed\n");
+				goto fail;
+			}
+			__set_bit(FW_FLAGS_NC1, &fw_flags);
+			break;
+		case RTL_FW_PHY_UNION_NC2:
+			if (!test_bit(FW_FLAGS_START, &fw_flags) ||
+			    test_bit(FW_FLAGS_UC2, &fw_flags) ||
+			    test_bit(FW_FLAGS_UC, &fw_flags) ||
+			    test_bit(FW_FLAGS_STOP, &fw_flags)) {
+				dev_err(&tp->intf->dev, "PHY_UNION_NC2 out of order\n");
+				goto fail;
+			}
 
+			if (test_bit(FW_FLAGS_NC2, &fw_flags)) {
+				dev_err(&tp->intf->dev, "multiple PHY NC2 encountered\n");
+				goto fail;
+			}
+
+			if (!rtl8152_is_fw_phy_union_ok(tp, (struct fw_phy_union *)block)) {
+				dev_err(&tp->intf->dev, "check PHY_UNION_NC2 failed\n");
+				goto fail;
+			}
+			__set_bit(FW_FLAGS_NC2, &fw_flags);
+			break;
+		case RTL_FW_PHY_UNION_UC2:
+			if (!test_bit(FW_FLAGS_START, &fw_flags) ||
+			    test_bit(FW_FLAGS_UC, &fw_flags) ||
+			    test_bit(FW_FLAGS_STOP, &fw_flags)) {
+				dev_err(&tp->intf->dev, "PHY_UNION_UC2 out of order\n");
+				goto fail;
+			}
+
+			if (test_bit(FW_FLAGS_UC2, &fw_flags)) {
+				dev_err(&tp->intf->dev, "multiple PHY UC2 encountered\n");
+				goto fail;
+			}
+
+			if (!rtl8152_is_fw_phy_union_ok(tp, (struct fw_phy_union *)block)) {
+				dev_err(&tp->intf->dev, "check PHY_UNION_UC2 failed\n");
+				goto fail;
+			}
+			__set_bit(FW_FLAGS_UC2, &fw_flags);
+			break;
+		case RTL_FW_PHY_UNION_UC:
+			if (!test_bit(FW_FLAGS_START, &fw_flags) ||
+			    test_bit(FW_FLAGS_STOP, &fw_flags)) {
+				dev_err(&tp->intf->dev, "PHY_UNION_UC out of order\n");
+				goto fail;
+			}
+
+			if (test_bit(FW_FLAGS_UC, &fw_flags)) {
+				dev_err(&tp->intf->dev, "multiple PHY UC encountered\n");
+				goto fail;
+			}
+
+			if (!rtl8152_is_fw_phy_union_ok(tp, (struct fw_phy_union *)block)) {
+				dev_err(&tp->intf->dev, "check PHY_UNION_UC failed\n");
+				goto fail;
+			}
+			__set_bit(FW_FLAGS_UC, &fw_flags);
+			break;
+		case RTL_FW_PHY_UNION_MISC:
+			if (!rtl8152_is_fw_phy_union_ok(tp, (struct fw_phy_union *)block)) {
+				dev_err(&tp->intf->dev, "check RTL_FW_PHY_UNION_MISC failed\n");
+				goto fail;
+			}
+			break;
+		case RTL_FW_PHY_FIXUP:
+			if (!rtl8152_is_fw_phy_fixup_ok(tp, (struct fw_phy_fixup *)block)) {
+				dev_err(&tp->intf->dev, "check PHY fixup failed\n");
+				goto fail;
+			}
+			break;
+		case RTL_FW_PHY_SPEED_UP:
+			if (test_bit(FW_FLAGS_SPEED_UP, &fw_flags)) {
+				dev_err(&tp->intf->dev, "multiple PHY firmware encountered");
+				goto fail;
+			}
+
+			if (!rtl8152_is_fw_phy_speed_up_ok(tp, (struct fw_phy_speed_up *)block)) {
+				dev_err(&tp->intf->dev, "check PHY speed up failed\n");
+				goto fail;
+			}
+			__set_bit(FW_FLAGS_SPEED_UP, &fw_flags);
+			break;
+		case RTL_FW_PHY_VER:
+			if (test_bit(FW_FLAGS_START, &fw_flags) ||
+			    test_bit(FW_FLAGS_NC, &fw_flags) ||
+			    test_bit(FW_FLAGS_NC1, &fw_flags) ||
+			    test_bit(FW_FLAGS_NC2, &fw_flags) ||
+			    test_bit(FW_FLAGS_UC2, &fw_flags) ||
+			    test_bit(FW_FLAGS_UC, &fw_flags) ||
+			    test_bit(FW_FLAGS_STOP, &fw_flags)) {
+				dev_err(&tp->intf->dev, "Invalid order to set PHY version\n");
+				goto fail;
+			}
+
+			if (test_bit(FW_FLAGS_VER, &fw_flags)) {
+				dev_err(&tp->intf->dev, "multiple PHY version encountered");
+				goto fail;
+			}
+
+			if (!rtl8152_is_fw_phy_ver_ok(tp, (struct fw_phy_ver *)block)) {
+				dev_err(&tp->intf->dev, "check PHY version failed\n");
+				goto fail;
+			}
+			__set_bit(FW_FLAGS_VER, &fw_flags);
 			break;
 		default:
 			dev_warn(&tp->intf->dev, "Unknown type %u is found\n",
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4750 @
 	}
 
 fw_end:
-	if ((phy_nc || start) && !stop) {
+	if (test_bit(FW_FLAGS_START, &fw_flags) && !test_bit(FW_FLAGS_STOP, &fw_flags)) {
 		dev_err(&tp->intf->dev, "without PHY_STOP\n");
 		goto fail;
 	}
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4760 @
 	return ret;
 }
 
+static void rtl_ram_code_speed_up(struct r8152 *tp, struct fw_phy_speed_up *phy, bool wait)
+{
+	u32 len;
+	u8 *data;
+
+	if (sram_read(tp, SRAM_GPHY_FW_VER) >= __le16_to_cpu(phy->version)) {
+		dev_dbg(&tp->intf->dev, "PHY firmware has been the newest\n");
+		return;
+	}
+
+	len = __le32_to_cpu(phy->blk_hdr.length);
+	len -= __le16_to_cpu(phy->fw_offset);
+	data = (u8 *)phy + __le16_to_cpu(phy->fw_offset);
+
+	if (rtl_phy_patch_request(tp, true, wait))
+		return;
+
+	while (len) {
+		u32 ocp_data, size;
+		int i;
+
+		if (len < 2048)
+			size = len;
+		else
+			size = 2048;
+
+		ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_GPHY_CTRL);
+		ocp_data |= GPHY_PATCH_DONE | BACKUP_RESTRORE;
+		ocp_write_word(tp, MCU_TYPE_USB, USB_GPHY_CTRL, ocp_data);
+
+		generic_ocp_write(tp, __le16_to_cpu(phy->fw_reg), 0xff, size, data, MCU_TYPE_USB);
+
+		data += size;
+		len -= size;
+
+		ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_POL_GPIO_CTRL);
+		ocp_data |= POL_GPHY_PATCH;
+		ocp_write_word(tp, MCU_TYPE_PLA, PLA_POL_GPIO_CTRL, ocp_data);
+
+		for (i = 0; i < 1000; i++) {
+			if (!(ocp_read_word(tp, MCU_TYPE_PLA, PLA_POL_GPIO_CTRL) & POL_GPHY_PATCH))
+				break;
+		}
+
+		if (i == 1000) {
+			dev_err(&tp->intf->dev, "ram code speedup mode timeout\n");
+			return;
+		}
+	}
+
+	ocp_write_word(tp, MCU_TYPE_PLA, PLA_OCP_GPHY_BASE, tp->ocp_base);
+	rtl_phy_patch_request(tp, false, wait);
+
+	if (sram_read(tp, SRAM_GPHY_FW_VER) == __le16_to_cpu(phy->version))
+		dev_dbg(&tp->intf->dev, "successfully applied %s\n", phy->info);
+	else
+		dev_err(&tp->intf->dev, "ram code speedup mode fail\n");
+}
+
+static int rtl8152_fw_phy_ver(struct r8152 *tp, struct fw_phy_ver *phy_ver)
+{
+	u16 ver_addr, ver;
+
+	ver_addr = __le16_to_cpu(phy_ver->ver.addr);
+	ver = __le16_to_cpu(phy_ver->ver.data);
+
+	if (sram_read(tp, ver_addr) >= ver) {
+		dev_dbg(&tp->intf->dev, "PHY firmware has been the newest\n");
+		return 0;
+	}
+
+	sram_write(tp, ver_addr, ver);
+
+	dev_dbg(&tp->intf->dev, "PHY firmware version %x\n", ver);
+
+	return ver;
+}
+
+static void rtl8152_fw_phy_fixup(struct r8152 *tp, struct fw_phy_fixup *fix)
+{
+	u16 addr, data;
+
+	addr = __le16_to_cpu(fix->setting.addr);
+	data = ocp_reg_read(tp, addr);
+
+	switch (__le16_to_cpu(fix->bit_cmd)) {
+	case FW_FIXUP_AND:
+		data &= __le16_to_cpu(fix->setting.data);
+		break;
+	case FW_FIXUP_OR:
+		data |= __le16_to_cpu(fix->setting.data);
+		break;
+	case FW_FIXUP_NOT:
+		data &= ~__le16_to_cpu(fix->setting.data);
+		break;
+	case FW_FIXUP_XOR:
+		data ^= __le16_to_cpu(fix->setting.data);
+		break;
+	default:
+		return;
+	}
+
+	ocp_reg_write(tp, addr, data);
+
+	dev_dbg(&tp->intf->dev, "applied ocp %x %x\n", addr, data);
+}
+
+static void rtl8152_fw_phy_union_apply(struct r8152 *tp, struct fw_phy_union *phy)
+{
+	__le16 *data;
+	u32 length;
+	int i, num;
+
+	num = phy->pre_num;
+	for (i = 0; i < num; i++)
+		sram_write(tp, __le16_to_cpu(phy->pre_set[i].addr),
+			   __le16_to_cpu(phy->pre_set[i].data));
+
+	length = __le32_to_cpu(phy->blk_hdr.length);
+	length -= __le16_to_cpu(phy->fw_offset);
+	num = length / 2;
+	data = (__le16 *)((u8 *)phy + __le16_to_cpu(phy->fw_offset));
+
+	ocp_reg_write(tp, OCP_SRAM_ADDR, __le16_to_cpu(phy->fw_reg));
+	for (i = 0; i < num; i++)
+		ocp_reg_write(tp, OCP_SRAM_DATA, __le16_to_cpu(data[i]));
+
+	num = phy->bp_num;
+	for (i = 0; i < num; i++)
+		sram_write(tp, __le16_to_cpu(phy->bp[i].addr), __le16_to_cpu(phy->bp[i].data));
+
+	if (phy->bp_num && phy->bp_en.addr)
+		sram_write(tp, __le16_to_cpu(phy->bp_en.addr), __le16_to_cpu(phy->bp_en.data));
+
+	dev_dbg(&tp->intf->dev, "successfully applied %s\n", phy->info);
+}
+
 static void rtl8152_fw_phy_nc_apply(struct r8152 *tp, struct fw_phy_nc *phy)
 {
 	u16 mode_reg, bp_index;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4950 @
 		return;
 	}
 
+	fw_ver_reg = __le16_to_cpu(mac->fw_ver_reg);
+	if (fw_ver_reg && ocp_read_byte(tp, MCU_TYPE_USB, fw_ver_reg) >= mac->fw_ver_data) {
+		dev_dbg(&tp->intf->dev, "%s firmware has been the newest\n", type ? "PLA" : "USB");
+		return;
+	}
+
 	rtl_clear_bp(tp, type);
 
 	/* Enable backup/restore of MACDBG. This is required after clearing PLA
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4991 @
 		ocp_write_word(tp, type, bp_en_addr,
 			       __le16_to_cpu(mac->bp_en_value));
 
-	fw_ver_reg = __le16_to_cpu(mac->fw_ver_reg);
 	if (fw_ver_reg)
 		ocp_write_byte(tp, MCU_TYPE_USB, fw_ver_reg,
 			       mac->fw_ver_data);
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4998 @
 	dev_dbg(&tp->intf->dev, "successfully applied %s\n", mac->info);
 }
 
-static void rtl8152_apply_firmware(struct r8152 *tp)
+static void rtl8152_apply_firmware(struct r8152 *tp, bool power_cut)
 {
 	struct rtl_fw *rtl_fw = &tp->rtl_fw;
 	const struct firmware *fw;
 	struct fw_header *fw_hdr;
 	struct fw_phy_patch_key *key;
 	u16 key_addr = 0;
-	int i;
+	int i, patch_phy = 1;
 
 	if (IS_ERR_OR_NULL(rtl_fw->fw))
 		return;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:5027 @
 			rtl8152_fw_mac_apply(tp, (struct fw_mac *)block);
 			break;
 		case RTL_FW_PHY_START:
+			if (!patch_phy)
+				break;
 			key = (struct fw_phy_patch_key *)block;
 			key_addr = __le16_to_cpu(key->key_reg);
-			r8153_pre_ram_code(tp, key_addr,
-					   __le16_to_cpu(key->key_data));
+			rtl_pre_ram_code(tp, key_addr, __le16_to_cpu(key->key_data), !power_cut);
 			break;
 		case RTL_FW_PHY_STOP:
+			if (!patch_phy)
+				break;
 			WARN_ON(!key_addr);
-			r8153_post_ram_code(tp, key_addr);
+			rtl_post_ram_code(tp, key_addr, !power_cut);
 			break;
 		case RTL_FW_PHY_NC:
 			rtl8152_fw_phy_nc_apply(tp, (struct fw_phy_nc *)block);
 			break;
+		case RTL_FW_PHY_VER:
+			patch_phy = rtl8152_fw_phy_ver(tp, (struct fw_phy_ver *)block);
+			break;
+		case RTL_FW_PHY_UNION_NC:
+		case RTL_FW_PHY_UNION_NC1:
+		case RTL_FW_PHY_UNION_NC2:
+		case RTL_FW_PHY_UNION_UC2:
+		case RTL_FW_PHY_UNION_UC:
+		case RTL_FW_PHY_UNION_MISC:
+			if (patch_phy)
+				rtl8152_fw_phy_union_apply(tp, (struct fw_phy_union *)block);
+			break;
+		case RTL_FW_PHY_FIXUP:
+			if (patch_phy)
+				rtl8152_fw_phy_fixup(tp, (struct fw_phy_fixup *)block);
+			break;
+		case RTL_FW_PHY_SPEED_UP:
+			rtl_ram_code_speed_up(tp, (struct fw_phy_speed_up *)block, !power_cut);
+			break;
 		default:
 			break;
 		}
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:5207 @
 	tp->ups_info.eee = enable;
 }
 
+static void r8156_eee_en(struct r8152 *tp, bool enable)
+{
+	u16 config;
+
+	r8153_eee_en(tp, enable);
+
+	config = ocp_reg_read(tp, OCP_EEE_ADV2);
+
+	if (enable)
+		config |= MDIO_EEE_2_5GT;
+	else
+		config &= ~MDIO_EEE_2_5GT;
+
+	ocp_reg_write(tp, OCP_EEE_ADV2, config);
+}
+
 static void rtl_eee_enable(struct r8152 *tp, bool enable)
 {
 	switch (tp->version) {
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:5244 @
 	case RTL_VER_06:
 	case RTL_VER_08:
 	case RTL_VER_09:
+	case RTL_VER_14:
 		if (enable) {
 			r8153_eee_en(tp, true);
 			ocp_reg_write(tp, OCP_EEE_ADV, tp->eee_adv);
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:5253 @
 			ocp_reg_write(tp, OCP_EEE_ADV, 0);
 		}
 		break;
+	case RTL_VER_10:
+	case RTL_VER_11:
+	case RTL_VER_12:
+	case RTL_VER_13:
+	case RTL_VER_15:
+		if (enable) {
+			r8156_eee_en(tp, true);
+			ocp_reg_write(tp, OCP_EEE_ADV, tp->eee_adv);
+		} else {
+			r8156_eee_en(tp, false);
+			ocp_reg_write(tp, OCP_EEE_ADV, 0);
+		}
+		break;
 	default:
 		break;
 	}
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:5291 @
 
 static void r8152b_hw_phy_cfg(struct r8152 *tp)
 {
-	rtl8152_apply_firmware(tp);
+	rtl8152_apply_firmware(tp, false);
 	rtl_eee_enable(tp, tp->eee_en);
 	r8152_aldps_en(tp, true);
 	r8152b_enable_fc(tp);
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:5312 @
 	}
 }
 
+static void r8156b_wait_loading_flash(struct r8152 *tp)
+{
+	if ((ocp_read_word(tp, MCU_TYPE_PLA, PLA_GPHY_CTRL) & GPHY_FLASH) &&
+	    !(ocp_read_word(tp, MCU_TYPE_USB, USB_GPHY_CTRL) & BYPASS_FLASH)) {
+		int i;
+
+		for (i = 0; i < 100; i++) {
+			if (ocp_read_word(tp, MCU_TYPE_USB, USB_GPHY_CTRL) & GPHY_PATCH_DONE)
+				break;
+			usleep_range(1000, 2000);
+		}
+	}
+}
+
 static void r8152b_exit_oob(struct r8152 *tp)
 {
 	u32 ocp_data;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:5376 @
 	}
 
 	/* TX share fifo free credit full threshold */
-	ocp_write_dword(tp, MCU_TYPE_PLA, PLA_TXFIFO_CTRL, TXFIFO_THR_NORMAL);
+	ocp_write_dword(tp, MCU_TYPE_PLA, PLA_TXFIFO_CTRL, TXFIFO_THR_NORMAL2);
 
 	ocp_write_byte(tp, MCU_TYPE_USB, USB_TX_AGG, TX_AGG_MAX_THRESHOLD);
 	ocp_write_dword(tp, MCU_TYPE_USB, USB_RX_BUF_TH, RX_THR_HIGH);
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:5553 @
 	return 0;
 }
 
+static int r8153c_post_firmware_1(struct r8152 *tp)
+{
+	u32 ocp_data;
+
+	ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_FW_CTRL);
+	ocp_data |= FLOW_CTRL_PATCH_2;
+	ocp_write_word(tp, MCU_TYPE_USB, USB_FW_CTRL, ocp_data);
+
+	ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_FW_TASK);
+	ocp_data |= FC_PATCH_TASK;
+	ocp_write_word(tp, MCU_TYPE_USB, USB_FW_TASK, ocp_data);
+
+	return 0;
+}
+
+static int r8156a_post_firmware_1(struct r8152 *tp)
+{
+	u32 ocp_data;
+
+	ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_FW_FIX_EN1);
+	ocp_data |= FW_IP_RESET_EN;
+	ocp_write_word(tp, MCU_TYPE_USB, USB_FW_FIX_EN1, ocp_data);
+
+	/* Modify U3PHY parameter for compatibility issue */
+	ocp_write_dword(tp, MCU_TYPE_USB, USB_UPHY3_MDCMDIO, 0x4026840e);
+	ocp_write_dword(tp, MCU_TYPE_USB, USB_UPHY3_MDCMDIO, 0x4001acc9);
+
+	return 0;
+}
+
 static void r8153_aldps_en(struct r8152 *tp, bool enable)
 {
 	u16 data;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:5617 @
 	/* disable EEE before updating the PHY parameters */
 	rtl_eee_enable(tp, false);
 
-	rtl8152_apply_firmware(tp);
+	rtl8152_apply_firmware(tp, false);
 
 	if (tp->version == RTL_VER_03) {
 		data = ocp_reg_read(tp, OCP_EEE_CFG);
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:5685 @
 	u32 ocp_data;
 	u16 data;
 
+	ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_MISC_0);
+	if (ocp_data & PCUT_STATUS) {
+		ocp_data &= ~PCUT_STATUS;
+		ocp_write_word(tp, MCU_TYPE_USB, USB_MISC_0, ocp_data);
+	}
+
 	/* disable ALDPS before updating the PHY parameters */
 	r8153_aldps_en(tp, false);
 
 	/* disable EEE before updating the PHY parameters */
 	rtl_eee_enable(tp, false);
 
-	rtl8152_apply_firmware(tp);
+	/* U1/U2/L1 idle timer. 500 us */
+	ocp_write_word(tp, MCU_TYPE_USB, USB_U1U2_TIMER, 500);
+
+	data = r8153_phy_status(tp, 0);
+
+	switch (data) {
+	case PHY_STAT_PWRDN:
+	case PHY_STAT_EXT_INIT:
+		rtl8152_apply_firmware(tp, true);
+
+		data = r8152_mdio_read(tp, MII_BMCR);
+		data &= ~BMCR_PDOWN;
+		r8152_mdio_write(tp, MII_BMCR, data);
+		break;
+	case PHY_STAT_LAN_ON:
+	default:
+		rtl8152_apply_firmware(tp, false);
+		break;
+	}
 
 	r8153b_green_en(tp, test_bit(GREEN_ETHERNET, &tp->flags));
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:5756 @
 	ocp_write_word(tp, MCU_TYPE_PLA, PLA_PHY_PWR, ocp_data);
 
 	/* Advnace EEE */
-	if (!r8153_patch_request(tp, true)) {
+	if (!rtl_phy_patch_request(tp, true, true)) {
 		data = ocp_reg_read(tp, OCP_POWER_CFG);
 		data |= EEE_CLKDIV_EN;
 		ocp_reg_write(tp, OCP_POWER_CFG, data);
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:5773 @
 		ocp_reg_write(tp, OCP_SYSCLK_CFG, clk_div_expo(5));
 		tp->ups_info._250m_ckdiv = true;
 
-		r8153_patch_request(tp, false);
+		rtl_phy_patch_request(tp, false, true);
 	}
 
 	if (tp->eee_en)
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:5785 @
 	set_bit(PHY_RESET, &tp->flags);
 }
 
+static void r8153c_hw_phy_cfg(struct r8152 *tp)
+{
+	r8153b_hw_phy_cfg(tp);
+
+	tp->ups_info.r_tune = true;
+}
+
+static void rtl8153_change_mtu(struct r8152 *tp)
+{
+	ocp_write_word(tp, MCU_TYPE_PLA, PLA_RMS, mtu_to_size(tp->netdev->mtu));
+	ocp_write_byte(tp, MCU_TYPE_PLA, PLA_MTPS, MTPS_JUMBO);
+}
+
 static void r8153_first_init(struct r8152 *tp)
 {
 	u32 ocp_data;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:5830 @
 
 	rtl_rx_vlan_en(tp, tp->netdev->features & NETIF_F_HW_VLAN_CTAG_RX);
 
-	ocp_data = tp->netdev->mtu + VLAN_ETH_HLEN + ETH_FCS_LEN;
-	ocp_write_word(tp, MCU_TYPE_PLA, PLA_RMS, ocp_data);
-	ocp_write_byte(tp, MCU_TYPE_PLA, PLA_MTPS, MTPS_JUMBO);
+	rtl8153_change_mtu(tp);
 
 	ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_TCR0);
 	ocp_data |= TCR0_AUTO_FIFO;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:5865 @
 
 	wait_oob_link_list_ready(tp);
 
-	ocp_data = tp->netdev->mtu + VLAN_ETH_HLEN + ETH_FCS_LEN;
-	ocp_write_word(tp, MCU_TYPE_PLA, PLA_RMS, ocp_data);
+	ocp_write_word(tp, MCU_TYPE_PLA, PLA_RMS, mtu_to_size(tp->netdev->mtu));
 
 	switch (tp->version) {
 	case RTL_VER_03:
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:5879 @
 
 	case RTL_VER_08:
 	case RTL_VER_09:
+	case RTL_VER_14:
 		/* Clear teredo wake event. bit[15:8] is the teredo wakeup
 		 * type. Set it to zero. bits[7:0] are the W1C bits about
 		 * the events. Set them to all 1 to clear them.
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:5916 @
 	r8153_aldps_en(tp, true);
 }
 
+static int rtl8156_enable(struct r8152 *tp)
+{
+	u32 ocp_data;
+	u16 speed;
+
+	if (test_bit(RTL8152_UNPLUG, &tp->flags))
+		return -ENODEV;
+
+	set_tx_qlen(tp);
+	rtl_set_eee_plus(tp);
+	r8153_set_rx_early_timeout(tp);
+	r8153_set_rx_early_size(tp);
+
+	speed = rtl8152_get_speed(tp);
+	rtl_set_ifg(tp, speed);
+
+	ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL4);
+	if (speed & _2500bps)
+		ocp_data &= ~IDLE_SPDWN_EN;
+	else
+		ocp_data |= IDLE_SPDWN_EN;
+	ocp_write_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL4, ocp_data);
+
+	if (speed & _1000bps)
+		ocp_write_word(tp, MCU_TYPE_PLA, PLA_EEE_TXTWSYS, 0x11);
+	else if (speed & _500bps)
+		ocp_write_word(tp, MCU_TYPE_PLA, PLA_EEE_TXTWSYS, 0x3d);
+
+	if (tp->udev->speed == USB_SPEED_HIGH) {
+		/* USB 0xb45e[3:0] l1_nyet_hird */
+		ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_L1_CTRL);
+		ocp_data &= ~0xf;
+		if (is_flow_control(speed))
+			ocp_data |= 0xf;
+		else
+			ocp_data |= 0x1;
+		ocp_write_word(tp, MCU_TYPE_USB, USB_L1_CTRL, ocp_data);
+	}
+
+	return rtl_enable(tp);
+}
+
+static int rtl8156b_enable(struct r8152 *tp)
+{
+	u32 ocp_data;
+	u16 speed;
+
+	if (test_bit(RTL8152_UNPLUG, &tp->flags))
+		return -ENODEV;
+
+	set_tx_qlen(tp);
+	rtl_set_eee_plus(tp);
+
+	ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_RX_AGGR_NUM);
+	ocp_data &= ~RX_AGGR_NUM_MASK;
+	ocp_write_word(tp, MCU_TYPE_USB, USB_RX_AGGR_NUM, ocp_data);
+
+	r8153_set_rx_early_timeout(tp);
+	r8153_set_rx_early_size(tp);
+
+	speed = rtl8152_get_speed(tp);
+	rtl_set_ifg(tp, speed);
+
+	ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL4);
+	if (speed & _2500bps)
+		ocp_data &= ~IDLE_SPDWN_EN;
+	else
+		ocp_data |= IDLE_SPDWN_EN;
+	ocp_write_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL4, ocp_data);
+
+	if (tp->udev->speed == USB_SPEED_HIGH) {
+		ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_L1_CTRL);
+		ocp_data &= ~0xf;
+		if (is_flow_control(speed))
+			ocp_data |= 0xf;
+		else
+			ocp_data |= 0x1;
+		ocp_write_word(tp, MCU_TYPE_USB, USB_L1_CTRL, ocp_data);
+	}
+
+	ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_FW_TASK);
+	ocp_data &= ~FC_PATCH_TASK;
+	ocp_write_word(tp, MCU_TYPE_USB, USB_FW_TASK, ocp_data);
+	usleep_range(1000, 2000);
+	ocp_data |= FC_PATCH_TASK;
+	ocp_write_word(tp, MCU_TYPE_USB, USB_FW_TASK, ocp_data);
+
+	return rtl_enable(tp);
+}
+
 static int rtl8152_set_speed(struct r8152 *tp, u8 autoneg, u32 speed, u8 duplex,
 			     u32 advertising)
 {
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:6054 @
 
 		tp->mii.force_media = 1;
 	} else {
-		u16 anar, tmp1;
+		u16 orig, new1;
 		u32 support;
 
 		support = RTL_ADVERTISED_10_HALF | RTL_ADVERTISED_10_FULL |
 			  RTL_ADVERTISED_100_HALF | RTL_ADVERTISED_100_FULL;
 
-		if (tp->mii.supports_gmii)
+		if (tp->mii.supports_gmii) {
 			support |= RTL_ADVERTISED_1000_FULL;
 
+			if (tp->support_2500full)
+				support |= RTL_ADVERTISED_2500_FULL;
+		}
+
 		if (!(advertising & support))
 			return -EINVAL;
 
-		anar = r8152_mdio_read(tp, MII_ADVERTISE);
-		tmp1 = anar & ~(ADVERTISE_10HALF | ADVERTISE_10FULL |
+		orig = r8152_mdio_read(tp, MII_ADVERTISE);
+		new1 = orig & ~(ADVERTISE_10HALF | ADVERTISE_10FULL |
 				ADVERTISE_100HALF | ADVERTISE_100FULL);
 		if (advertising & RTL_ADVERTISED_10_HALF) {
-			tmp1 |= ADVERTISE_10HALF;
+			new1 |= ADVERTISE_10HALF;
 			tp->ups_info.speed_duplex = NWAY_10M_HALF;
 		}
 		if (advertising & RTL_ADVERTISED_10_FULL) {
-			tmp1 |= ADVERTISE_10FULL;
+			new1 |= ADVERTISE_10FULL;
 			tp->ups_info.speed_duplex = NWAY_10M_FULL;
 		}
 
 		if (advertising & RTL_ADVERTISED_100_HALF) {
-			tmp1 |= ADVERTISE_100HALF;
+			new1 |= ADVERTISE_100HALF;
 			tp->ups_info.speed_duplex = NWAY_100M_HALF;
 		}
 		if (advertising & RTL_ADVERTISED_100_FULL) {
-			tmp1 |= ADVERTISE_100FULL;
+			new1 |= ADVERTISE_100FULL;
 			tp->ups_info.speed_duplex = NWAY_100M_FULL;
 		}
 
-		if (anar != tmp1) {
-			r8152_mdio_write(tp, MII_ADVERTISE, tmp1);
-			tp->mii.advertising = tmp1;
+		if (orig != new1) {
+			r8152_mdio_write(tp, MII_ADVERTISE, new1);
+			tp->mii.advertising = new1;
 		}
 
 		if (tp->mii.supports_gmii) {
-			u16 gbcr;
-
-			gbcr = r8152_mdio_read(tp, MII_CTRL1000);
-			tmp1 = gbcr & ~(ADVERTISE_1000FULL |
+			orig = r8152_mdio_read(tp, MII_CTRL1000);
+			new1 = orig & ~(ADVERTISE_1000FULL |
 					ADVERTISE_1000HALF);
 
 			if (advertising & RTL_ADVERTISED_1000_FULL) {
-				tmp1 |= ADVERTISE_1000FULL;
+				new1 |= ADVERTISE_1000FULL;
 				tp->ups_info.speed_duplex = NWAY_1000M_FULL;
 			}
 
-			if (gbcr != tmp1)
-				r8152_mdio_write(tp, MII_CTRL1000, tmp1);
+			if (orig != new1)
+				r8152_mdio_write(tp, MII_CTRL1000, new1);
+		}
+
+		if (tp->support_2500full) {
+			orig = ocp_reg_read(tp, OCP_10GBT_CTRL);
+			new1 = orig & ~MDIO_AN_10GBT_CTRL_ADV2_5G;
+
+			if (advertising & RTL_ADVERTISED_2500_FULL) {
+				new1 |= MDIO_AN_10GBT_CTRL_ADV2_5G;
+				tp->ups_info.speed_duplex = NWAY_2500M_FULL;
+			}
+
+			if (orig != new1)
+				ocp_reg_write(tp, OCP_10GBT_CTRL, new1);
 		}
 
 		bmcr = BMCR_ANENABLE | BMCR_ANRESTART;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:6251 @
 
 	r8153_aldps_en(tp, true);
 
-	if (tp->udev->speed != USB_SPEED_HIGH)
+	if (tp->udev->speed >= USB_SPEED_SUPER)
 		r8153b_u1u2en(tp, true);
 }
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:6276 @
 	r8153_aldps_en(tp, true);
 }
 
+static void rtl8153c_change_mtu(struct r8152 *tp)
+{
+	ocp_write_word(tp, MCU_TYPE_PLA, PLA_RMS, mtu_to_size(tp->netdev->mtu));
+	ocp_write_byte(tp, MCU_TYPE_PLA, PLA_MTPS, 10 * 1024 / 64);
+
+	ocp_write_word(tp, MCU_TYPE_PLA, PLA_TXFIFO_CTRL, 512 / 64);
+
+	/* Adjust the tx fifo free credit full threshold, otherwise
+	 * the fifo would be too small to send a jumbo frame packet.
+	 */
+	if (tp->netdev->mtu < 8000)
+		ocp_write_word(tp, MCU_TYPE_PLA, PLA_TXFIFO_FULL, 2048 / 8);
+	else
+		ocp_write_word(tp, MCU_TYPE_PLA, PLA_TXFIFO_FULL, 900 / 8);
+}
+
+static void rtl8153c_up(struct r8152 *tp)
+{
+	u32 ocp_data;
+
+	if (test_bit(RTL8152_UNPLUG, &tp->flags))
+		return;
+
+	r8153b_u1u2en(tp, false);
+	r8153_u2p3en(tp, false);
+	r8153_aldps_en(tp, false);
+
+	rxdy_gated_en(tp, true);
+	r8153_teredo_off(tp);
+
+	ocp_data = ocp_read_dword(tp, MCU_TYPE_PLA, PLA_RCR);
+	ocp_data &= ~RCR_ACPT_ALL;
+	ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RCR, ocp_data);
+
+	rtl8152_nic_reset(tp);
+	rtl_reset_bmu(tp);
+
+	ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL);
+	ocp_data &= ~NOW_IS_OOB;
+	ocp_write_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL, ocp_data);
+
+	ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_SFF_STS_7);
+	ocp_data &= ~MCU_BORW_EN;
+	ocp_write_word(tp, MCU_TYPE_PLA, PLA_SFF_STS_7, ocp_data);
+
+	wait_oob_link_list_ready(tp);
+
+	ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_SFF_STS_7);
+	ocp_data |= RE_INIT_LL;
+	ocp_write_word(tp, MCU_TYPE_PLA, PLA_SFF_STS_7, ocp_data);
+
+	wait_oob_link_list_ready(tp);
+
+	rtl_rx_vlan_en(tp, tp->netdev->features & NETIF_F_HW_VLAN_CTAG_RX);
+
+	rtl8153c_change_mtu(tp);
+
+	rtl8152_nic_reset(tp);
+
+	/* rx share fifo credit full threshold */
+	ocp_write_byte(tp, MCU_TYPE_PLA, PLA_RXFIFO_CTRL0, 0x02);
+	ocp_write_byte(tp, MCU_TYPE_PLA, PLA_RXFIFO_FULL, 0x08);
+	ocp_write_word(tp, MCU_TYPE_PLA, PLA_RXFIFO_CTRL1, RXFIFO_THR2_NORMAL);
+	ocp_write_word(tp, MCU_TYPE_PLA, PLA_RXFIFO_CTRL2, RXFIFO_THR3_NORMAL);
+
+	ocp_write_dword(tp, MCU_TYPE_USB, USB_RX_BUF_TH, RX_THR_B);
+
+	ocp_write_byte(tp, MCU_TYPE_PLA, PLA_CRWECR, CRWECR_CONFIG);
+
+	ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_CONFIG34);
+	ocp_data |= BIT(8);
+	ocp_write_word(tp, MCU_TYPE_PLA, PLA_CONFIG34, ocp_data);
+
+	ocp_write_byte(tp, MCU_TYPE_PLA, PLA_CRWECR, CRWECR_NORAML);
+
+	ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL3);
+	ocp_data &= ~PLA_MCU_SPDWN_EN;
+	ocp_write_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL3, ocp_data);
+
+	r8153_aldps_en(tp, true);
+	r8153b_u1u2en(tp, true);
+}
+
+static inline u32 fc_pause_on_auto(struct r8152 *tp)
+{
+	return (ALIGN(mtu_to_size(tp->netdev->mtu), 1024) + 6 * 1024);
+}
+
+static inline u32 fc_pause_off_auto(struct r8152 *tp)
+{
+	return (ALIGN(mtu_to_size(tp->netdev->mtu), 1024) + 14 * 1024);
+}
+
+static void r8156_fc_parameter(struct r8152 *tp)
+{
+	u32 pause_on = tp->fc_pause_on ? tp->fc_pause_on : fc_pause_on_auto(tp);
+	u32 pause_off = tp->fc_pause_off ? tp->fc_pause_off : fc_pause_off_auto(tp);
+
+	switch (tp->version) {
+	case RTL_VER_10:
+	case RTL_VER_11:
+		ocp_write_word(tp, MCU_TYPE_PLA, PLA_RX_FIFO_FULL, pause_on / 8);
+		ocp_write_word(tp, MCU_TYPE_PLA, PLA_RX_FIFO_EMPTY, pause_off / 8);
+		break;
+	case RTL_VER_12:
+	case RTL_VER_13:
+	case RTL_VER_15:
+		ocp_write_word(tp, MCU_TYPE_PLA, PLA_RX_FIFO_FULL, pause_on / 16);
+		ocp_write_word(tp, MCU_TYPE_PLA, PLA_RX_FIFO_EMPTY, pause_off / 16);
+		break;
+	default:
+		break;
+	}
+}
+
+static void rtl8156_change_mtu(struct r8152 *tp)
+{
+	u32 rx_max_size = mtu_to_size(tp->netdev->mtu);
+
+	ocp_write_word(tp, MCU_TYPE_PLA, PLA_RMS, rx_max_size);
+	ocp_write_byte(tp, MCU_TYPE_PLA, PLA_MTPS, MTPS_JUMBO);
+	r8156_fc_parameter(tp);
+
+	/* TX share fifo free credit full threshold */
+	ocp_write_word(tp, MCU_TYPE_PLA, PLA_TXFIFO_CTRL, 512 / 64);
+	ocp_write_word(tp, MCU_TYPE_PLA, PLA_TXFIFO_FULL,
+		       ALIGN(rx_max_size + sizeof(struct tx_desc), 1024) / 16);
+}
+
+static void rtl8156_up(struct r8152 *tp)
+{
+	u32 ocp_data;
+
+	if (test_bit(RTL8152_UNPLUG, &tp->flags))
+		return;
+
+	r8153b_u1u2en(tp, false);
+	r8153_u2p3en(tp, false);
+	r8153_aldps_en(tp, false);
+
+	rxdy_gated_en(tp, true);
+	r8153_teredo_off(tp);
+
+	ocp_data = ocp_read_dword(tp, MCU_TYPE_PLA, PLA_RCR);
+	ocp_data &= ~RCR_ACPT_ALL;
+	ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RCR, ocp_data);
+
+	rtl8152_nic_reset(tp);
+	rtl_reset_bmu(tp);
+
+	ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL);
+	ocp_data &= ~NOW_IS_OOB;
+	ocp_write_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL, ocp_data);
+
+	ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_SFF_STS_7);
+	ocp_data &= ~MCU_BORW_EN;
+	ocp_write_word(tp, MCU_TYPE_PLA, PLA_SFF_STS_7, ocp_data);
+
+	rtl_rx_vlan_en(tp, tp->netdev->features & NETIF_F_HW_VLAN_CTAG_RX);
+
+	rtl8156_change_mtu(tp);
+
+	switch (tp->version) {
+	case RTL_TEST_01:
+	case RTL_VER_10:
+	case RTL_VER_11:
+		ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_BMU_CONFIG);
+		ocp_data |= ACT_ODMA;
+		ocp_write_word(tp, MCU_TYPE_USB, USB_BMU_CONFIG, ocp_data);
+		break;
+	default:
+		break;
+	}
+
+	/* share FIFO settings */
+	ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_RXFIFO_FULL);
+	ocp_data &= ~RXFIFO_FULL_MASK;
+	ocp_data |= 0x08;
+	ocp_write_word(tp, MCU_TYPE_PLA, PLA_RXFIFO_FULL, ocp_data);
+
+	ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL3);
+	ocp_data &= ~PLA_MCU_SPDWN_EN;
+	ocp_write_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL3, ocp_data);
+
+	ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_SPEED_OPTION);
+	ocp_data &= ~(RG_PWRDN_EN | ALL_SPEED_OFF);
+	ocp_write_word(tp, MCU_TYPE_USB, USB_SPEED_OPTION, ocp_data);
+
+	ocp_write_dword(tp, MCU_TYPE_USB, USB_RX_BUF_TH, 0x00600400);
+
+	if (tp->saved_wolopts != __rtl_get_wol(tp)) {
+		netif_warn(tp, ifup, tp->netdev, "wol setting is changed\n");
+		__rtl_set_wol(tp, tp->saved_wolopts);
+	}
+
+	r8153_aldps_en(tp, true);
+	r8153_u2p3en(tp, true);
+
+	if (tp->udev->speed >= USB_SPEED_SUPER)
+		r8153b_u1u2en(tp, true);
+}
+
+static void rtl8156_down(struct r8152 *tp)
+{
+	u32 ocp_data;
+
+	if (test_bit(RTL8152_UNPLUG, &tp->flags)) {
+		rtl_drop_queued_tx(tp);
+		return;
+	}
+
+	ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL3);
+	ocp_data |= PLA_MCU_SPDWN_EN;
+	ocp_write_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL3, ocp_data);
+
+	r8153b_u1u2en(tp, false);
+	r8153_u2p3en(tp, false);
+	r8153b_power_cut_en(tp, false);
+	r8153_aldps_en(tp, false);
+
+	ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL);
+	ocp_data &= ~NOW_IS_OOB;
+	ocp_write_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL, ocp_data);
+
+	rtl_disable(tp);
+	rtl_reset_bmu(tp);
+
+	/* Clear teredo wake event. bit[15:8] is the teredo wakeup
+	 * type. Set it to zero. bits[7:0] are the W1C bits about
+	 * the events. Set them to all 1 to clear them.
+	 */
+	ocp_write_word(tp, MCU_TYPE_PLA, PLA_TEREDO_WAKE_BASE, 0x00ff);
+
+	ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL);
+	ocp_data |= NOW_IS_OOB;
+	ocp_write_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL, ocp_data);
+
+	rtl_rx_vlan_en(tp, true);
+	rxdy_gated_en(tp, false);
+
+	ocp_data = ocp_read_dword(tp, MCU_TYPE_PLA, PLA_RCR);
+	ocp_data |= RCR_APM | RCR_AM | RCR_AB;
+	ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RCR, ocp_data);
+
+	r8153_aldps_en(tp, true);
+}
+
 static bool rtl8152_in_nway(struct r8152 *tp)
 {
 	u16 nway_state;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:6553 @
 {
 	struct net_device *netdev = tp->netdev;
 	struct napi_struct *napi = &tp->napi;
-	u8 speed;
+	u16 speed;
 
 	speed = rtl8152_get_speed(tp);
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:6566 @
 			rtl_start_rx(tp);
 			clear_bit(RTL8152_SET_RX_MODE, &tp->flags);
 			_rtl8152_set_rx_mode(netdev);
-			napi_enable(&tp->napi);
+			napi_enable(napi);
 			netif_wake_queue(netdev);
 			netif_info(tp, link, netdev, "carrier on\n");
 		} else if (netif_queue_stopped(netdev) &&
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:6927 @
 
 	ocp_write_word(tp, MCU_TYPE_USB, USB_CONNECT_TIMER, 0x0001);
 
-	/* MAC clock speed down */
-	ocp_write_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL, 0);
-	ocp_write_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL2, 0);
-	ocp_write_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL3, 0);
-	ocp_write_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL4, 0);
-
 	r8153_power_cut_en(tp, false);
 	rtl_runtime_suspend_enable(tp, false);
+	r8153_mac_clk_speed_down(tp, false);
 	r8153_u1u2en(tp, true);
 	usb_enable_lpm(tp->udev);
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:7001 @
 	/* MSC timer = 0xfff * 8ms = 32760 ms */
 	ocp_write_word(tp, MCU_TYPE_USB, USB_MSC_TIMER, 0x0fff);
 
-	/* U1/U2/L1 idle timer. 500 us */
-	ocp_write_word(tp, MCU_TYPE_USB, USB_U1U2_TIMER, 500);
-
 	r8153b_power_cut_en(tp, false);
 	r8153b_ups_en(tp, false);
 	r8153_queue_wake(tp, false);
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:7014 @
 	ocp_data |= POLL_LINK_CHG;
 	ocp_write_word(tp, MCU_TYPE_PLA, PLA_EXTRA_STATUS, ocp_data);
 
-	if (tp->udev->speed != USB_SPEED_HIGH)
+	if (tp->udev->speed >= USB_SPEED_SUPER)
 		r8153b_u1u2en(tp, true);
+
 	usb_enable_lpm(tp->udev);
 
 	/* MAC clock speed down */
-	ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL2);
-	ocp_data |= MAC_CLK_SPDWN_EN;
-	ocp_write_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL2, ocp_data);
+	r8153_mac_clk_speed_down(tp, true);
 
 	ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL3);
 	ocp_data &= ~PLA_MCU_SPDWN_EN;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:7047 @
 	tp->coalesce = 15000;	/* 15 us */
 }
 
+static void r8153c_init(struct r8152 *tp)
+{
+	u32 ocp_data;
+	u16 data;
+	int i;
+
+	if (test_bit(RTL8152_UNPLUG, &tp->flags))
+		return;
+
+	r8153b_u1u2en(tp, false);
+
+	/* Disable spi_en */
+	ocp_write_byte(tp, MCU_TYPE_PLA, PLA_CRWECR, CRWECR_CONFIG);
+	ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_CONFIG5);
+	ocp_data &= ~BIT(3);
+	ocp_write_word(tp, MCU_TYPE_PLA, PLA_CONFIG5, ocp_data);
+	ocp_data = ocp_read_word(tp, MCU_TYPE_USB, 0xcbf0);
+	ocp_data |= BIT(1);
+	ocp_write_word(tp, MCU_TYPE_USB, 0xcbf0, ocp_data);
+
+	for (i = 0; i < 500; i++) {
+		if (ocp_read_word(tp, MCU_TYPE_PLA, PLA_BOOT_CTRL) &
+		    AUTOLOAD_DONE)
+			break;
+
+		msleep(20);
+		if (test_bit(RTL8152_UNPLUG, &tp->flags))
+			return;
+	}
+
+	data = r8153_phy_status(tp, 0);
+
+	data = r8152_mdio_read(tp, MII_BMCR);
+	if (data & BMCR_PDOWN) {
+		data &= ~BMCR_PDOWN;
+		r8152_mdio_write(tp, MII_BMCR, data);
+	}
+
+	data = r8153_phy_status(tp, PHY_STAT_LAN_ON);
+
+	r8153_u2p3en(tp, false);
+
+	/* MSC timer = 0xfff * 8ms = 32760 ms */
+	ocp_write_word(tp, MCU_TYPE_USB, USB_MSC_TIMER, 0x0fff);
+
+	r8153b_power_cut_en(tp, false);
+	r8153c_ups_en(tp, false);
+	r8153_queue_wake(tp, false);
+	rtl_runtime_suspend_enable(tp, false);
+
+	ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_EXTRA_STATUS);
+	if (rtl8152_get_speed(tp) & LINK_STATUS)
+		ocp_data |= CUR_LINK_OK;
+	else
+		ocp_data &= ~CUR_LINK_OK;
+
+	ocp_data |= POLL_LINK_CHG;
+	ocp_write_word(tp, MCU_TYPE_PLA, PLA_EXTRA_STATUS, ocp_data);
+
+	r8153b_u1u2en(tp, true);
+
+	usb_enable_lpm(tp->udev);
+
+	/* MAC clock speed down */
+	r8153_mac_clk_speed_down(tp, true);
+
+	ocp_data = ocp_read_byte(tp, MCU_TYPE_USB, USB_MISC_2);
+	ocp_data &= ~BIT(7);
+	ocp_write_byte(tp, MCU_TYPE_USB, USB_MISC_2, ocp_data);
+
+	set_bit(GREEN_ETHERNET, &tp->flags);
+
+	/* rx aggregation */
+	ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_USB_CTRL);
+	ocp_data &= ~(RX_AGG_DISABLE | RX_ZERO_EN);
+	ocp_write_word(tp, MCU_TYPE_USB, USB_USB_CTRL, ocp_data);
+
+	rtl_tally_reset(tp);
+
+	tp->coalesce = 15000;	/* 15 us */
+}
+
+static void r8156_hw_phy_cfg(struct r8152 *tp)
+{
+	u32 ocp_data;
+	u16 data;
+
+	ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_MISC_0);
+	if (ocp_data & PCUT_STATUS) {
+		ocp_data &= ~PCUT_STATUS;
+		ocp_write_word(tp, MCU_TYPE_USB, USB_MISC_0, ocp_data);
+	}
+
+	data = r8153_phy_status(tp, 0);
+	switch (data) {
+	case PHY_STAT_EXT_INIT:
+		rtl8152_apply_firmware(tp, true);
+
+		data = ocp_reg_read(tp, 0xa468);
+		data &= ~(BIT(3) | BIT(1));
+		ocp_reg_write(tp, 0xa468, data);
+		break;
+	case PHY_STAT_LAN_ON:
+	case PHY_STAT_PWRDN:
+	default:
+		rtl8152_apply_firmware(tp, false);
+		break;
+	}
+
+	/* disable ALDPS before updating the PHY parameters */
+	r8153_aldps_en(tp, false);
+
+	/* disable EEE before updating the PHY parameters */
+	rtl_eee_enable(tp, false);
+
+	data = r8153_phy_status(tp, PHY_STAT_LAN_ON);
+	WARN_ON_ONCE(data != PHY_STAT_LAN_ON);
+
+	ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_PHY_PWR);
+	ocp_data |= PFM_PWM_SWITCH;
+	ocp_write_word(tp, MCU_TYPE_PLA, PLA_PHY_PWR, ocp_data);
+
+	switch (tp->version) {
+	case RTL_VER_10:
+		data = ocp_reg_read(tp, 0xad40);
+		data &= ~0x3ff;
+		data |= BIT(7) | BIT(2);
+		ocp_reg_write(tp, 0xad40, data);
+
+		data = ocp_reg_read(tp, 0xad4e);
+		data |= BIT(4);
+		ocp_reg_write(tp, 0xad4e, data);
+		data = ocp_reg_read(tp, 0xad16);
+		data &= ~0x3ff;
+		data |= 0x6;
+		ocp_reg_write(tp, 0xad16, data);
+		data = ocp_reg_read(tp, 0xad32);
+		data &= ~0x3f;
+		data |= 6;
+		ocp_reg_write(tp, 0xad32, data);
+		data = ocp_reg_read(tp, 0xac08);
+		data &= ~(BIT(12) | BIT(8));
+		ocp_reg_write(tp, 0xac08, data);
+		data = ocp_reg_read(tp, 0xac8a);
+		data |= BIT(12) | BIT(13) | BIT(14);
+		data &= ~BIT(15);
+		ocp_reg_write(tp, 0xac8a, data);
+		data = ocp_reg_read(tp, 0xad18);
+		data |= BIT(10);
+		ocp_reg_write(tp, 0xad18, data);
+		data = ocp_reg_read(tp, 0xad1a);
+		data |= 0x3ff;
+		ocp_reg_write(tp, 0xad1a, data);
+		data = ocp_reg_read(tp, 0xad1c);
+		data |= 0x3ff;
+		ocp_reg_write(tp, 0xad1c, data);
+
+		data = sram_read(tp, 0x80ea);
+		data &= ~0xff00;
+		data |= 0xc400;
+		sram_write(tp, 0x80ea, data);
+		data = sram_read(tp, 0x80eb);
+		data &= ~0x0700;
+		data |= 0x0300;
+		sram_write(tp, 0x80eb, data);
+		data = sram_read(tp, 0x80f8);
+		data &= ~0xff00;
+		data |= 0x1c00;
+		sram_write(tp, 0x80f8, data);
+		data = sram_read(tp, 0x80f1);
+		data &= ~0xff00;
+		data |= 0x3000;
+		sram_write(tp, 0x80f1, data);
+
+		data = sram_read(tp, 0x80fe);
+		data &= ~0xff00;
+		data |= 0xa500;
+		sram_write(tp, 0x80fe, data);
+		data = sram_read(tp, 0x8102);
+		data &= ~0xff00;
+		data |= 0x5000;
+		sram_write(tp, 0x8102, data);
+		data = sram_read(tp, 0x8015);
+		data &= ~0xff00;
+		data |= 0x3300;
+		sram_write(tp, 0x8015, data);
+		data = sram_read(tp, 0x8100);
+		data &= ~0xff00;
+		data |= 0x7000;
+		sram_write(tp, 0x8100, data);
+		data = sram_read(tp, 0x8014);
+		data &= ~0xff00;
+		data |= 0xf000;
+		sram_write(tp, 0x8014, data);
+		data = sram_read(tp, 0x8016);
+		data &= ~0xff00;
+		data |= 0x6500;
+		sram_write(tp, 0x8016, data);
+		data = sram_read(tp, 0x80dc);
+		data &= ~0xff00;
+		data |= 0xed00;
+		sram_write(tp, 0x80dc, data);
+		data = sram_read(tp, 0x80df);
+		data |= BIT(8);
+		sram_write(tp, 0x80df, data);
+		data = sram_read(tp, 0x80e1);
+		data &= ~BIT(8);
+		sram_write(tp, 0x80e1, data);
+
+		data = ocp_reg_read(tp, 0xbf06);
+		data &= ~0x003f;
+		data |= 0x0038;
+		ocp_reg_write(tp, 0xbf06, data);
+
+		sram_write(tp, 0x819f, 0xddb6);
+
+		ocp_reg_write(tp, 0xbc34, 0x5555);
+		data = ocp_reg_read(tp, 0xbf0a);
+		data &= ~0x0e00;
+		data |= 0x0a00;
+		ocp_reg_write(tp, 0xbf0a, data);
+
+		data = ocp_reg_read(tp, 0xbd2c);
+		data &= ~BIT(13);
+		ocp_reg_write(tp, 0xbd2c, data);
+		break;
+	case RTL_VER_11:
+		data = ocp_reg_read(tp, 0xad16);
+		data |= 0x3ff;
+		ocp_reg_write(tp, 0xad16, data);
+		data = ocp_reg_read(tp, 0xad32);
+		data &= ~0x3f;
+		data |= 6;
+		ocp_reg_write(tp, 0xad32, data);
+		data = ocp_reg_read(tp, 0xac08);
+		data &= ~(BIT(12) | BIT(8));
+		ocp_reg_write(tp, 0xac08, data);
+		data = ocp_reg_read(tp, 0xacc0);
+		data &= ~0x3;
+		data |= BIT(1);
+		ocp_reg_write(tp, 0xacc0, data);
+		data = ocp_reg_read(tp, 0xad40);
+		data &= ~0xe7;
+		data |= BIT(6) | BIT(2);
+		ocp_reg_write(tp, 0xad40, data);
+		data = ocp_reg_read(tp, 0xac14);
+		data &= ~BIT(7);
+		ocp_reg_write(tp, 0xac14, data);
+		data = ocp_reg_read(tp, 0xac80);
+		data &= ~(BIT(8) | BIT(9));
+		ocp_reg_write(tp, 0xac80, data);
+		data = ocp_reg_read(tp, 0xac5e);
+		data &= ~0x7;
+		data |= BIT(1);
+		ocp_reg_write(tp, 0xac5e, data);
+		ocp_reg_write(tp, 0xad4c, 0x00a8);
+		ocp_reg_write(tp, 0xac5c, 0x01ff);
+		data = ocp_reg_read(tp, 0xac8a);
+		data &= ~0xf0;
+		data |= BIT(4) | BIT(5);
+		ocp_reg_write(tp, 0xac8a, data);
+		ocp_reg_write(tp, 0xb87c, 0x8157);
+		data = ocp_reg_read(tp, 0xb87e);
+		data &= ~0xff00;
+		data |= 0x0500;
+		ocp_reg_write(tp, 0xb87e, data);
+		ocp_reg_write(tp, 0xb87c, 0x8159);
+		data = ocp_reg_read(tp, 0xb87e);
+		data &= ~0xff00;
+		data |= 0x0700;
+		ocp_reg_write(tp, 0xb87e, data);
+
+		/* AAGC */
+		ocp_reg_write(tp, 0xb87c, 0x80a2);
+		ocp_reg_write(tp, 0xb87e, 0x0153);
+		ocp_reg_write(tp, 0xb87c, 0x809c);
+		ocp_reg_write(tp, 0xb87e, 0x0153);
+
+		/* EEE parameter */
+		ocp_write_word(tp, MCU_TYPE_PLA, PLA_EEE_TXTWSYS_2P5G, 0x0056);
+
+		ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_USB_CFG);
+		ocp_data |= EN_XG_LIP | EN_G_LIP;
+		ocp_write_word(tp, MCU_TYPE_PLA, PLA_USB_CFG, ocp_data);
+
+		sram_write(tp, 0x8257, 0x020f); /*  XG PLL */
+		sram_write(tp, 0x80ea, 0x7843); /* GIGA Master */
+
+		if (rtl_phy_patch_request(tp, true, true))
+			return;
+
+		/* Advance EEE */
+		ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL4);
+		ocp_data |= EEE_SPDWN_EN;
+		ocp_write_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL4, ocp_data);
+
+		data = ocp_reg_read(tp, OCP_DOWN_SPEED);
+		data &= ~(EN_EEE_100 | EN_EEE_1000);
+		data |= EN_10M_CLKDIV;
+		ocp_reg_write(tp, OCP_DOWN_SPEED, data);
+		tp->ups_info._10m_ckdiv = true;
+		tp->ups_info.eee_plloff_100 = false;
+		tp->ups_info.eee_plloff_giga = false;
+
+		data = ocp_reg_read(tp, OCP_POWER_CFG);
+		data &= ~EEE_CLKDIV_EN;
+		ocp_reg_write(tp, OCP_POWER_CFG, data);
+		tp->ups_info.eee_ckdiv = false;
+
+		ocp_reg_write(tp, OCP_SYSCLK_CFG, 0);
+		ocp_reg_write(tp, OCP_SYSCLK_CFG, sysclk_div_expo(5));
+		tp->ups_info._250m_ckdiv = false;
+
+		rtl_phy_patch_request(tp, false, true);
+
+		/* enable ADC Ibias Cal */
+		data = ocp_reg_read(tp, 0xd068);
+		data |= BIT(13);
+		ocp_reg_write(tp, 0xd068, data);
+
+		/* enable Thermal Sensor */
+		data = sram_read(tp, 0x81a2);
+		data &= ~BIT(8);
+		sram_write(tp, 0x81a2, data);
+		data = ocp_reg_read(tp, 0xb54c);
+		data &= ~0xff00;
+		data |= 0xdb00;
+		ocp_reg_write(tp, 0xb54c, data);
+
+		/* Nway 2.5G Lite */
+		data = ocp_reg_read(tp, 0xa454);
+		data &= ~BIT(0);
+		ocp_reg_write(tp, 0xa454, data);
+
+		/* CS DSP solution */
+		data = ocp_reg_read(tp, OCP_10GBT_CTRL);
+		data |= RTL_ADV2_5G_F_R;
+		ocp_reg_write(tp, OCP_10GBT_CTRL, data);
+		data = ocp_reg_read(tp, 0xad4e);
+		data &= ~BIT(4);
+		ocp_reg_write(tp, 0xad4e, data);
+		data = ocp_reg_read(tp, 0xa86a);
+		data &= ~BIT(0);
+		ocp_reg_write(tp, 0xa86a, data);
+
+		/* MDI SWAP */
+		if ((ocp_read_word(tp, MCU_TYPE_USB, USB_UPS_CFG) & MID_REVERSE) &&
+		    (ocp_reg_read(tp, 0xd068) & BIT(1))) {
+			u16 swap_a, swap_b;
+
+			data = ocp_reg_read(tp, 0xd068);
+			data &= ~0x1f;
+			data |= 0x1; /* p0 */
+			ocp_reg_write(tp, 0xd068, data);
+			swap_a = ocp_reg_read(tp, 0xd06a);
+			data &= ~0x18;
+			data |= 0x18; /* p3 */
+			ocp_reg_write(tp, 0xd068, data);
+			swap_b = ocp_reg_read(tp, 0xd06a);
+			data &= ~0x18; /* p0 */
+			ocp_reg_write(tp, 0xd068, data);
+			ocp_reg_write(tp, 0xd06a,
+				      (swap_a & ~0x7ff) | (swap_b & 0x7ff));
+			data |= 0x18; /* p3 */
+			ocp_reg_write(tp, 0xd068, data);
+			ocp_reg_write(tp, 0xd06a,
+				      (swap_b & ~0x7ff) | (swap_a & 0x7ff));
+			data &= ~0x18;
+			data |= 0x08; /* p1 */
+			ocp_reg_write(tp, 0xd068, data);
+			swap_a = ocp_reg_read(tp, 0xd06a);
+			data &= ~0x18;
+			data |= 0x10; /* p2 */
+			ocp_reg_write(tp, 0xd068, data);
+			swap_b = ocp_reg_read(tp, 0xd06a);
+			data &= ~0x18;
+			data |= 0x08; /* p1 */
+			ocp_reg_write(tp, 0xd068, data);
+			ocp_reg_write(tp, 0xd06a,
+				      (swap_a & ~0x7ff) | (swap_b & 0x7ff));
+			data &= ~0x18;
+			data |= 0x10; /* p2 */
+			ocp_reg_write(tp, 0xd068, data);
+			ocp_reg_write(tp, 0xd06a,
+				      (swap_b & ~0x7ff) | (swap_a & 0x7ff));
+			swap_a = ocp_reg_read(tp, 0xbd5a);
+			swap_b = ocp_reg_read(tp, 0xbd5c);
+			ocp_reg_write(tp, 0xbd5a, (swap_a & ~0x1f1f) |
+				      ((swap_b & 0x1f) << 8) |
+				      ((swap_b >> 8) & 0x1f));
+			ocp_reg_write(tp, 0xbd5c, (swap_b & ~0x1f1f) |
+				      ((swap_a & 0x1f) << 8) |
+				      ((swap_a >> 8) & 0x1f));
+			swap_a = ocp_reg_read(tp, 0xbc18);
+			swap_b = ocp_reg_read(tp, 0xbc1a);
+			ocp_reg_write(tp, 0xbc18, (swap_a & ~0x1f1f) |
+				      ((swap_b & 0x1f) << 8) |
+				      ((swap_b >> 8) & 0x1f));
+			ocp_reg_write(tp, 0xbc1a, (swap_b & ~0x1f1f) |
+				      ((swap_a & 0x1f) << 8) |
+				      ((swap_a >> 8) & 0x1f));
+		}
+		break;
+	default:
+		break;
+	}
+
+	rtl_green_en(tp, test_bit(GREEN_ETHERNET, &tp->flags));
+
+	data = ocp_reg_read(tp, 0xa428);
+	data &= ~BIT(9);
+	ocp_reg_write(tp, 0xa428, data);
+	data = ocp_reg_read(tp, 0xa5ea);
+	data &= ~BIT(0);
+	ocp_reg_write(tp, 0xa5ea, data);
+	tp->ups_info.lite_mode = 0;
+
+	if (tp->eee_en)
+		rtl_eee_enable(tp, true);
+
+	r8153_aldps_en(tp, true);
+	r8152b_enable_fc(tp);
+	r8153_u2p3en(tp, true);
+
+	set_bit(PHY_RESET, &tp->flags);
+}
+
+static void r8156b_hw_phy_cfg(struct r8152 *tp)
+{
+	u32 ocp_data;
+	u16 data;
+
+	switch (tp->version) {
+	case RTL_VER_12:
+		ocp_reg_write(tp, 0xbf86, 0x9000);
+		data = ocp_reg_read(tp, 0xc402);
+		data |= BIT(10);
+		ocp_reg_write(tp, 0xc402, data);
+		data &= ~BIT(10);
+		ocp_reg_write(tp, 0xc402, data);
+		ocp_reg_write(tp, 0xbd86, 0x1010);
+		ocp_reg_write(tp, 0xbd88, 0x1010);
+		data = ocp_reg_read(tp, 0xbd4e);
+		data &= ~(BIT(10) | BIT(11));
+		data |= BIT(11);
+		ocp_reg_write(tp, 0xbd4e, data);
+		data = ocp_reg_read(tp, 0xbf46);
+		data &= ~0xf00;
+		data |= 0x700;
+		ocp_reg_write(tp, 0xbf46, data);
+		break;
+	case RTL_VER_13:
+	case RTL_VER_15:
+		r8156b_wait_loading_flash(tp);
+		break;
+	default:
+		break;
+	}
+
+	ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_MISC_0);
+	if (ocp_data & PCUT_STATUS) {
+		ocp_data &= ~PCUT_STATUS;
+		ocp_write_word(tp, MCU_TYPE_USB, USB_MISC_0, ocp_data);
+	}
+
+	data = r8153_phy_status(tp, 0);
+	switch (data) {
+	case PHY_STAT_EXT_INIT:
+		rtl8152_apply_firmware(tp, true);
+
+		data = ocp_reg_read(tp, 0xa466);
+		data &= ~BIT(0);
+		ocp_reg_write(tp, 0xa466, data);
+
+		data = ocp_reg_read(tp, 0xa468);
+		data &= ~(BIT(3) | BIT(1));
+		ocp_reg_write(tp, 0xa468, data);
+		break;
+	case PHY_STAT_LAN_ON:
+	case PHY_STAT_PWRDN:
+	default:
+		rtl8152_apply_firmware(tp, false);
+		break;
+	}
+
+	data = r8152_mdio_read(tp, MII_BMCR);
+	if (data & BMCR_PDOWN) {
+		data &= ~BMCR_PDOWN;
+		r8152_mdio_write(tp, MII_BMCR, data);
+	}
+
+	/* disable ALDPS before updating the PHY parameters */
+	r8153_aldps_en(tp, false);
+
+	/* disable EEE before updating the PHY parameters */
+	rtl_eee_enable(tp, false);
+
+	data = r8153_phy_status(tp, PHY_STAT_LAN_ON);
+	WARN_ON_ONCE(data != PHY_STAT_LAN_ON);
+
+	ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_PHY_PWR);
+	ocp_data |= PFM_PWM_SWITCH;
+	ocp_write_word(tp, MCU_TYPE_PLA, PLA_PHY_PWR, ocp_data);
+
+	switch (tp->version) {
+	case RTL_VER_12:
+		data = ocp_reg_read(tp, 0xbc08);
+		data |= BIT(3) | BIT(2);
+		ocp_reg_write(tp, 0xbc08, data);
+
+		data = sram_read(tp, 0x8fff);
+		data &= ~0xff00;
+		data |= 0x0400;
+		sram_write(tp, 0x8fff, data);
+
+		data = ocp_reg_read(tp, 0xacda);
+		data |= 0xff00;
+		ocp_reg_write(tp, 0xacda, data);
+		data = ocp_reg_read(tp, 0xacde);
+		data |= 0xf000;
+		ocp_reg_write(tp, 0xacde, data);
+		ocp_reg_write(tp, 0xac8c, 0x0ffc);
+		ocp_reg_write(tp, 0xac46, 0xb7b4);
+		ocp_reg_write(tp, 0xac50, 0x0fbc);
+		ocp_reg_write(tp, 0xac3c, 0x9240);
+		ocp_reg_write(tp, 0xac4e, 0x0db4);
+		ocp_reg_write(tp, 0xacc6, 0x0707);
+		ocp_reg_write(tp, 0xacc8, 0xa0d3);
+		ocp_reg_write(tp, 0xad08, 0x0007);
+
+		ocp_reg_write(tp, 0xb87c, 0x8560);
+		ocp_reg_write(tp, 0xb87e, 0x19cc);
+		ocp_reg_write(tp, 0xb87c, 0x8562);
+		ocp_reg_write(tp, 0xb87e, 0x19cc);
+		ocp_reg_write(tp, 0xb87c, 0x8564);
+		ocp_reg_write(tp, 0xb87e, 0x19cc);
+		ocp_reg_write(tp, 0xb87c, 0x8566);
+		ocp_reg_write(tp, 0xb87e, 0x147d);
+		ocp_reg_write(tp, 0xb87c, 0x8568);
+		ocp_reg_write(tp, 0xb87e, 0x147d);
+		ocp_reg_write(tp, 0xb87c, 0x856a);
+		ocp_reg_write(tp, 0xb87e, 0x147d);
+		ocp_reg_write(tp, 0xb87c, 0x8ffe);
+		ocp_reg_write(tp, 0xb87e, 0x0907);
+		ocp_reg_write(tp, 0xb87c, 0x80d6);
+		ocp_reg_write(tp, 0xb87e, 0x2801);
+		ocp_reg_write(tp, 0xb87c, 0x80f2);
+		ocp_reg_write(tp, 0xb87e, 0x2801);
+		ocp_reg_write(tp, 0xb87c, 0x80f4);
+		ocp_reg_write(tp, 0xb87e, 0x6077);
+		ocp_reg_write(tp, 0xb506, 0x01e7);
+
+		ocp_reg_write(tp, 0xb87c, 0x8013);
+		ocp_reg_write(tp, 0xb87e, 0x0700);
+		ocp_reg_write(tp, 0xb87c, 0x8fb9);
+		ocp_reg_write(tp, 0xb87e, 0x2801);
+		ocp_reg_write(tp, 0xb87c, 0x8fba);
+		ocp_reg_write(tp, 0xb87e, 0x0100);
+		ocp_reg_write(tp, 0xb87c, 0x8fbc);
+		ocp_reg_write(tp, 0xb87e, 0x1900);
+		ocp_reg_write(tp, 0xb87c, 0x8fbe);
+		ocp_reg_write(tp, 0xb87e, 0xe100);
+		ocp_reg_write(tp, 0xb87c, 0x8fc0);
+		ocp_reg_write(tp, 0xb87e, 0x0800);
+		ocp_reg_write(tp, 0xb87c, 0x8fc2);
+		ocp_reg_write(tp, 0xb87e, 0xe500);
+		ocp_reg_write(tp, 0xb87c, 0x8fc4);
+		ocp_reg_write(tp, 0xb87e, 0x0f00);
+		ocp_reg_write(tp, 0xb87c, 0x8fc6);
+		ocp_reg_write(tp, 0xb87e, 0xf100);
+		ocp_reg_write(tp, 0xb87c, 0x8fc8);
+		ocp_reg_write(tp, 0xb87e, 0x0400);
+		ocp_reg_write(tp, 0xb87c, 0x8fca);
+		ocp_reg_write(tp, 0xb87e, 0xf300);
+		ocp_reg_write(tp, 0xb87c, 0x8fcc);
+		ocp_reg_write(tp, 0xb87e, 0xfd00);
+		ocp_reg_write(tp, 0xb87c, 0x8fce);
+		ocp_reg_write(tp, 0xb87e, 0xff00);
+		ocp_reg_write(tp, 0xb87c, 0x8fd0);
+		ocp_reg_write(tp, 0xb87e, 0xfb00);
+		ocp_reg_write(tp, 0xb87c, 0x8fd2);
+		ocp_reg_write(tp, 0xb87e, 0x0100);
+		ocp_reg_write(tp, 0xb87c, 0x8fd4);
+		ocp_reg_write(tp, 0xb87e, 0xf400);
+		ocp_reg_write(tp, 0xb87c, 0x8fd6);
+		ocp_reg_write(tp, 0xb87e, 0xff00);
+		ocp_reg_write(tp, 0xb87c, 0x8fd8);
+		ocp_reg_write(tp, 0xb87e, 0xf600);
+
+		ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_USB_CFG);
+		ocp_data |= EN_XG_LIP | EN_G_LIP;
+		ocp_write_byte(tp, MCU_TYPE_PLA, PLA_USB_CFG, ocp_data);
+		ocp_reg_write(tp, 0xb87c, 0x813d);
+		ocp_reg_write(tp, 0xb87e, 0x390e);
+		ocp_reg_write(tp, 0xb87c, 0x814f);
+		ocp_reg_write(tp, 0xb87e, 0x790e);
+		ocp_reg_write(tp, 0xb87c, 0x80b0);
+		ocp_reg_write(tp, 0xb87e, 0x0f31);
+		data = ocp_reg_read(tp, 0xbf4c);
+		data |= BIT(1);
+		ocp_reg_write(tp, 0xbf4c, data);
+		data = ocp_reg_read(tp, 0xbcca);
+		data |= BIT(9) | BIT(8);
+		ocp_reg_write(tp, 0xbcca, data);
+		ocp_reg_write(tp, 0xb87c, 0x8141);
+		ocp_reg_write(tp, 0xb87e, 0x320e);
+		ocp_reg_write(tp, 0xb87c, 0x8153);
+		ocp_reg_write(tp, 0xb87e, 0x720e);
+		ocp_reg_write(tp, 0xb87c, 0x8529);
+		ocp_reg_write(tp, 0xb87e, 0x050e);
+		data = ocp_reg_read(tp, OCP_EEE_CFG);
+		data &= ~CTAP_SHORT_EN;
+		ocp_reg_write(tp, OCP_EEE_CFG, data);
+
+		sram_write(tp, 0x816c, 0xc4a0);
+		sram_write(tp, 0x8170, 0xc4a0);
+		sram_write(tp, 0x8174, 0x04a0);
+		sram_write(tp, 0x8178, 0x04a0);
+		sram_write(tp, 0x817c, 0x0719);
+		sram_write(tp, 0x8ff4, 0x0400);
+		sram_write(tp, 0x8ff1, 0x0404);
+
+		ocp_reg_write(tp, 0xbf4a, 0x001b);
+		ocp_reg_write(tp, 0xb87c, 0x8033);
+		ocp_reg_write(tp, 0xb87e, 0x7c13);
+		ocp_reg_write(tp, 0xb87c, 0x8037);
+		ocp_reg_write(tp, 0xb87e, 0x7c13);
+		ocp_reg_write(tp, 0xb87c, 0x803b);
+		ocp_reg_write(tp, 0xb87e, 0xfc32);
+		ocp_reg_write(tp, 0xb87c, 0x803f);
+		ocp_reg_write(tp, 0xb87e, 0x7c13);
+		ocp_reg_write(tp, 0xb87c, 0x8043);
+		ocp_reg_write(tp, 0xb87e, 0x7c13);
+		ocp_reg_write(tp, 0xb87c, 0x8047);
+		ocp_reg_write(tp, 0xb87e, 0x7c13);
+
+		ocp_reg_write(tp, 0xb87c, 0x8145);
+		ocp_reg_write(tp, 0xb87e, 0x370e);
+		ocp_reg_write(tp, 0xb87c, 0x8157);
+		ocp_reg_write(tp, 0xb87e, 0x770e);
+		ocp_reg_write(tp, 0xb87c, 0x8169);
+		ocp_reg_write(tp, 0xb87e, 0x0d0a);
+		ocp_reg_write(tp, 0xb87c, 0x817b);
+		ocp_reg_write(tp, 0xb87e, 0x1d0a);
+
+		data = sram_read(tp, 0x8217);
+		data &= ~0xff00;
+		data |= 0x5000;
+		sram_write(tp, 0x8217, data);
+		data = sram_read(tp, 0x821a);
+		data &= ~0xff00;
+		data |= 0x5000;
+		sram_write(tp, 0x821a, data);
+		sram_write(tp, 0x80da, 0x0403);
+		data = sram_read(tp, 0x80dc);
+		data &= ~0xff00;
+		data |= 0x1000;
+		sram_write(tp, 0x80dc, data);
+		sram_write(tp, 0x80b3, 0x0384);
+		sram_write(tp, 0x80b7, 0x2007);
+		data = sram_read(tp, 0x80ba);
+		data &= ~0xff00;
+		data |= 0x6c00;
+		sram_write(tp, 0x80ba, data);
+		sram_write(tp, 0x80b5, 0xf009);
+		data = sram_read(tp, 0x80bd);
+		data &= ~0xff00;
+		data |= 0x9f00;
+		sram_write(tp, 0x80bd, data);
+		sram_write(tp, 0x80c7, 0xf083);
+		sram_write(tp, 0x80dd, 0x03f0);
+		data = sram_read(tp, 0x80df);
+		data &= ~0xff00;
+		data |= 0x1000;
+		sram_write(tp, 0x80df, data);
+		sram_write(tp, 0x80cb, 0x2007);
+		data = sram_read(tp, 0x80ce);
+		data &= ~0xff00;
+		data |= 0x6c00;
+		sram_write(tp, 0x80ce, data);
+		sram_write(tp, 0x80c9, 0x8009);
+		data = sram_read(tp, 0x80d1);
+		data &= ~0xff00;
+		data |= 0x8000;
+		sram_write(tp, 0x80d1, data);
+		sram_write(tp, 0x80a3, 0x200a);
+		sram_write(tp, 0x80a5, 0xf0ad);
+		sram_write(tp, 0x809f, 0x6073);
+		sram_write(tp, 0x80a1, 0x000b);
+		data = sram_read(tp, 0x80a9);
+		data &= ~0xff00;
+		data |= 0xc000;
+		sram_write(tp, 0x80a9, data);
+
+		if (rtl_phy_patch_request(tp, true, true))
+			return;
+
+		data = ocp_reg_read(tp, 0xb896);
+		data &= ~BIT(0);
+		ocp_reg_write(tp, 0xb896, data);
+		data = ocp_reg_read(tp, 0xb892);
+		data &= ~0xff00;
+		ocp_reg_write(tp, 0xb892, data);
+		ocp_reg_write(tp, 0xb88e, 0xc23e);
+		ocp_reg_write(tp, 0xb890, 0x0000);
+		ocp_reg_write(tp, 0xb88e, 0xc240);
+		ocp_reg_write(tp, 0xb890, 0x0103);
+		ocp_reg_write(tp, 0xb88e, 0xc242);
+		ocp_reg_write(tp, 0xb890, 0x0507);
+		ocp_reg_write(tp, 0xb88e, 0xc244);
+		ocp_reg_write(tp, 0xb890, 0x090b);
+		ocp_reg_write(tp, 0xb88e, 0xc246);
+		ocp_reg_write(tp, 0xb890, 0x0c0e);
+		ocp_reg_write(tp, 0xb88e, 0xc248);
+		ocp_reg_write(tp, 0xb890, 0x1012);
+		ocp_reg_write(tp, 0xb88e, 0xc24a);
+		ocp_reg_write(tp, 0xb890, 0x1416);
+		data = ocp_reg_read(tp, 0xb896);
+		data |= BIT(0);
+		ocp_reg_write(tp, 0xb896, data);
+
+		rtl_phy_patch_request(tp, false, true);
+
+		data = ocp_reg_read(tp, 0xa86a);
+		data |= BIT(0);
+		ocp_reg_write(tp, 0xa86a, data);
+		data = ocp_reg_read(tp, 0xa6f0);
+		data |= BIT(0);
+		ocp_reg_write(tp, 0xa6f0, data);
+
+		ocp_reg_write(tp, 0xbfa0, 0xd70d);
+		ocp_reg_write(tp, 0xbfa2, 0x4100);
+		ocp_reg_write(tp, 0xbfa4, 0xe868);
+		ocp_reg_write(tp, 0xbfa6, 0xdc59);
+		ocp_reg_write(tp, 0xb54c, 0x3c18);
+		data = ocp_reg_read(tp, 0xbfa4);
+		data &= ~BIT(5);
+		ocp_reg_write(tp, 0xbfa4, data);
+		data = sram_read(tp, 0x817d);
+		data |= BIT(12);
+		sram_write(tp, 0x817d, data);
+		break;
+	case RTL_VER_13:
+		/* 2.5G INRX */
+		data = ocp_reg_read(tp, 0xac46);
+		data &= ~0x00f0;
+		data |= 0x0090;
+		ocp_reg_write(tp, 0xac46, data);
+		data = ocp_reg_read(tp, 0xad30);
+		data &= ~0x0003;
+		data |= 0x0001;
+		ocp_reg_write(tp, 0xad30, data);
+		fallthrough;
+	case RTL_VER_15:
+		/* EEE parameter */
+		ocp_reg_write(tp, 0xb87c, 0x80f5);
+		ocp_reg_write(tp, 0xb87e, 0x760e);
+		ocp_reg_write(tp, 0xb87c, 0x8107);
+		ocp_reg_write(tp, 0xb87e, 0x360e);
+		ocp_reg_write(tp, 0xb87c, 0x8551);
+		data = ocp_reg_read(tp, 0xb87e);
+		data &= ~0xff00;
+		data |= 0x0800;
+		ocp_reg_write(tp, 0xb87e, data);
+
+		/* ADC_PGA parameter */
+		data = ocp_reg_read(tp, 0xbf00);
+		data &= ~0xe000;
+		data |= 0xa000;
+		ocp_reg_write(tp, 0xbf00, data);
+		data = ocp_reg_read(tp, 0xbf46);
+		data &= ~0x0f00;
+		data |= 0x0300;
+		ocp_reg_write(tp, 0xbf46, data);
+
+		/* Green Table-PGA, 1G full viterbi */
+		sram_write(tp, 0x8044, 0x2417);
+		sram_write(tp, 0x804a, 0x2417);
+		sram_write(tp, 0x8050, 0x2417);
+		sram_write(tp, 0x8056, 0x2417);
+		sram_write(tp, 0x805c, 0x2417);
+		sram_write(tp, 0x8062, 0x2417);
+		sram_write(tp, 0x8068, 0x2417);
+		sram_write(tp, 0x806e, 0x2417);
+		sram_write(tp, 0x8074, 0x2417);
+		sram_write(tp, 0x807a, 0x2417);
+
+		/* XG PLL */
+		data = ocp_reg_read(tp, 0xbf84);
+		data &= ~0xe000;
+		data |= 0xa000;
+		ocp_reg_write(tp, 0xbf84, data);
+		break;
+	default:
+		break;
+	}
+
+	if (rtl_phy_patch_request(tp, true, true))
+		return;
+
+	ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL4);
+	ocp_data |= EEE_SPDWN_EN;
+	ocp_write_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL4, ocp_data);
+
+	data = ocp_reg_read(tp, OCP_DOWN_SPEED);
+	data &= ~(EN_EEE_100 | EN_EEE_1000);
+	data |= EN_10M_CLKDIV;
+	ocp_reg_write(tp, OCP_DOWN_SPEED, data);
+	tp->ups_info._10m_ckdiv = true;
+	tp->ups_info.eee_plloff_100 = false;
+	tp->ups_info.eee_plloff_giga = false;
+
+	data = ocp_reg_read(tp, OCP_POWER_CFG);
+	data &= ~EEE_CLKDIV_EN;
+	ocp_reg_write(tp, OCP_POWER_CFG, data);
+	tp->ups_info.eee_ckdiv = false;
+
+	rtl_phy_patch_request(tp, false, true);
+
+	rtl_green_en(tp, test_bit(GREEN_ETHERNET, &tp->flags));
+
+	data = ocp_reg_read(tp, 0xa428);
+	data &= ~BIT(9);
+	ocp_reg_write(tp, 0xa428, data);
+	data = ocp_reg_read(tp, 0xa5ea);
+	data &= ~BIT(0);
+	ocp_reg_write(tp, 0xa5ea, data);
+	tp->ups_info.lite_mode = 0;
+
+	if (tp->eee_en)
+		rtl_eee_enable(tp, true);
+
+	r8153_aldps_en(tp, true);
+	r8152b_enable_fc(tp);
+	r8153_u2p3en(tp, true);
+
+	set_bit(PHY_RESET, &tp->flags);
+}
+
+static void r8156_init(struct r8152 *tp)
+{
+	u32 ocp_data;
+	u16 data;
+	int i;
+
+	if (test_bit(RTL8152_UNPLUG, &tp->flags))
+		return;
+
+	ocp_data = ocp_read_byte(tp, MCU_TYPE_USB, USB_ECM_OP);
+	ocp_data &= ~EN_ALL_SPEED;
+	ocp_write_byte(tp, MCU_TYPE_USB, USB_ECM_OP, ocp_data);
+
+	ocp_write_word(tp, MCU_TYPE_USB, USB_SPEED_OPTION, 0);
+
+	ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_ECM_OPTION);
+	ocp_data |= BYPASS_MAC_RESET;
+	ocp_write_word(tp, MCU_TYPE_USB, USB_ECM_OPTION, ocp_data);
+
+	r8153b_u1u2en(tp, false);
+
+	for (i = 0; i < 500; i++) {
+		if (ocp_read_word(tp, MCU_TYPE_PLA, PLA_BOOT_CTRL) &
+		    AUTOLOAD_DONE)
+			break;
+
+		msleep(20);
+		if (test_bit(RTL8152_UNPLUG, &tp->flags))
+			return;
+	}
+
+	data = r8153_phy_status(tp, 0);
+	if (data == PHY_STAT_EXT_INIT) {
+		data = ocp_reg_read(tp, 0xa468);
+		data &= ~(BIT(3) | BIT(1));
+		ocp_reg_write(tp, 0xa468, data);
+	}
+
+	data = r8152_mdio_read(tp, MII_BMCR);
+	if (data & BMCR_PDOWN) {
+		data &= ~BMCR_PDOWN;
+		r8152_mdio_write(tp, MII_BMCR, data);
+	}
+
+	data = r8153_phy_status(tp, PHY_STAT_LAN_ON);
+	WARN_ON_ONCE(data != PHY_STAT_LAN_ON);
+
+	r8153_u2p3en(tp, false);
+
+	/* MSC timer = 0xfff * 8ms = 32760 ms */
+	ocp_write_word(tp, MCU_TYPE_USB, USB_MSC_TIMER, 0x0fff);
+
+	/* U1/U2/L1 idle timer. 500 us */
+	ocp_write_word(tp, MCU_TYPE_USB, USB_U1U2_TIMER, 500);
+
+	r8153b_power_cut_en(tp, false);
+	r8156_ups_en(tp, false);
+	r8153_queue_wake(tp, false);
+	rtl_runtime_suspend_enable(tp, false);
+
+	if (tp->udev->speed >= USB_SPEED_SUPER)
+		r8153b_u1u2en(tp, true);
+
+	usb_enable_lpm(tp->udev);
+
+	r8156_mac_clk_spd(tp, true);
+
+	ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL3);
+	ocp_data &= ~PLA_MCU_SPDWN_EN;
+	ocp_write_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL3, ocp_data);
+
+	ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_EXTRA_STATUS);
+	if (rtl8152_get_speed(tp) & LINK_STATUS)
+		ocp_data |= CUR_LINK_OK;
+	else
+		ocp_data &= ~CUR_LINK_OK;
+	ocp_data |= POLL_LINK_CHG;
+	ocp_write_word(tp, MCU_TYPE_PLA, PLA_EXTRA_STATUS, ocp_data);
+
+	set_bit(GREEN_ETHERNET, &tp->flags);
+
+	/* rx aggregation */
+	ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_USB_CTRL);
+	ocp_data &= ~(RX_AGG_DISABLE | RX_ZERO_EN);
+	ocp_write_word(tp, MCU_TYPE_USB, USB_USB_CTRL, ocp_data);
+
+	ocp_data = ocp_read_byte(tp, MCU_TYPE_USB, USB_BMU_CONFIG);
+	ocp_data |= ACT_ODMA;
+	ocp_write_byte(tp, MCU_TYPE_USB, USB_BMU_CONFIG, ocp_data);
+
+	rtl_tally_reset(tp);
+
+	tp->coalesce = 15000;	/* 15 us */
+}
+
+static void r8156b_init(struct r8152 *tp)
+{
+	u32 ocp_data;
+	u16 data;
+	int i;
+
+	if (test_bit(RTL8152_UNPLUG, &tp->flags))
+		return;
+
+	ocp_data = ocp_read_byte(tp, MCU_TYPE_USB, USB_ECM_OP);
+	ocp_data &= ~EN_ALL_SPEED;
+	ocp_write_byte(tp, MCU_TYPE_USB, USB_ECM_OP, ocp_data);
+
+	ocp_write_word(tp, MCU_TYPE_USB, USB_SPEED_OPTION, 0);
+
+	ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_ECM_OPTION);
+	ocp_data |= BYPASS_MAC_RESET;
+	ocp_write_word(tp, MCU_TYPE_USB, USB_ECM_OPTION, ocp_data);
+
+	ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_U2P3_CTRL);
+	ocp_data |= RX_DETECT8;
+	ocp_write_word(tp, MCU_TYPE_USB, USB_U2P3_CTRL, ocp_data);
+
+	r8153b_u1u2en(tp, false);
+
+	switch (tp->version) {
+	case RTL_VER_13:
+	case RTL_VER_15:
+		r8156b_wait_loading_flash(tp);
+		break;
+	default:
+		break;
+	}
+
+	for (i = 0; i < 500; i++) {
+		if (ocp_read_word(tp, MCU_TYPE_PLA, PLA_BOOT_CTRL) &
+		    AUTOLOAD_DONE)
+			break;
+
+		msleep(20);
+		if (test_bit(RTL8152_UNPLUG, &tp->flags))
+			return;
+	}
+
+	data = r8153_phy_status(tp, 0);
+	if (data == PHY_STAT_EXT_INIT) {
+		data = ocp_reg_read(tp, 0xa468);
+		data &= ~(BIT(3) | BIT(1));
+		ocp_reg_write(tp, 0xa468, data);
+
+		data = ocp_reg_read(tp, 0xa466);
+		data &= ~BIT(0);
+		ocp_reg_write(tp, 0xa466, data);
+	}
+
+	data = r8152_mdio_read(tp, MII_BMCR);
+	if (data & BMCR_PDOWN) {
+		data &= ~BMCR_PDOWN;
+		r8152_mdio_write(tp, MII_BMCR, data);
+	}
+
+	data = r8153_phy_status(tp, PHY_STAT_LAN_ON);
+
+	r8153_u2p3en(tp, false);
+
+	/* MSC timer = 0xfff * 8ms = 32760 ms */
+	ocp_write_word(tp, MCU_TYPE_USB, USB_MSC_TIMER, 0x0fff);
+
+	/* U1/U2/L1 idle timer. 500 us */
+	ocp_write_word(tp, MCU_TYPE_USB, USB_U1U2_TIMER, 500);
+
+	r8153b_power_cut_en(tp, false);
+	r8156_ups_en(tp, false);
+	r8153_queue_wake(tp, false);
+	rtl_runtime_suspend_enable(tp, false);
+
+	if (tp->udev->speed >= USB_SPEED_SUPER)
+		r8153b_u1u2en(tp, true);
+
+	usb_enable_lpm(tp->udev);
+
+	ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_RCR);
+	ocp_data &= ~SLOT_EN;
+	ocp_write_word(tp, MCU_TYPE_PLA, PLA_RCR, ocp_data);
+
+	ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_CPCR);
+	ocp_data |= FLOW_CTRL_EN;
+	ocp_write_word(tp, MCU_TYPE_PLA, PLA_CPCR, ocp_data);
+
+	/* enable fc timer and set timer to 600 ms. */
+	ocp_write_word(tp, MCU_TYPE_USB, USB_FC_TIMER,
+		       CTRL_TIMER_EN | (600 / 8));
+
+	ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_FW_CTRL);
+	if (!(ocp_read_word(tp, MCU_TYPE_PLA, PLA_POL_GPIO_CTRL) & DACK_DET_EN))
+		ocp_data |= FLOW_CTRL_PATCH_2;
+	ocp_data &= ~AUTO_SPEEDUP;
+	ocp_write_word(tp, MCU_TYPE_USB, USB_FW_CTRL, ocp_data);
+
+	ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_FW_TASK);
+	ocp_data |= FC_PATCH_TASK;
+	ocp_write_word(tp, MCU_TYPE_USB, USB_FW_TASK, ocp_data);
+
+	r8156_mac_clk_spd(tp, true);
+
+	ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL3);
+	ocp_data &= ~PLA_MCU_SPDWN_EN;
+	ocp_write_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL3, ocp_data);
+
+	ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_EXTRA_STATUS);
+	if (rtl8152_get_speed(tp) & LINK_STATUS)
+		ocp_data |= CUR_LINK_OK;
+	else
+		ocp_data &= ~CUR_LINK_OK;
+	ocp_data |= POLL_LINK_CHG;
+	ocp_write_word(tp, MCU_TYPE_PLA, PLA_EXTRA_STATUS, ocp_data);
+
+	set_bit(GREEN_ETHERNET, &tp->flags);
+
+	/* rx aggregation */
+	ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_USB_CTRL);
+	ocp_data &= ~(RX_AGG_DISABLE | RX_ZERO_EN);
+	ocp_write_word(tp, MCU_TYPE_USB, USB_USB_CTRL, ocp_data);
+
+	rtl_tally_reset(tp);
+
+	tp->coalesce = 15000;	/* 15 us */
+}
+
+static bool rtl_vendor_mode(struct usb_interface *intf)
+{
+	struct usb_host_interface *alt = intf->cur_altsetting;
+	struct usb_device *udev;
+	struct usb_host_config *c;
+	int i, num_configs;
+
+	if (alt->desc.bInterfaceClass == USB_CLASS_VENDOR_SPEC)
+		return true;
+
+	/* The vendor mode is not always config #1, so to find it out. */
+	udev = interface_to_usbdev(intf);
+	c = udev->config;
+	num_configs = udev->descriptor.bNumConfigurations;
+	for (i = 0; i < num_configs; (i++, c++)) {
+		struct usb_interface_descriptor	*desc = NULL;
+
+		if (c->desc.bNumInterfaces > 0)
+			desc = &c->intf_cache[0]->altsetting->desc;
+		else
+			continue;
+
+		if (desc->bInterfaceClass == USB_CLASS_VENDOR_SPEC) {
+			usb_driver_set_configuration(udev, c->desc.bConfigurationValue);
+			break;
+		}
+	}
+
+	WARN_ON_ONCE(i == num_configs);
+
+	return false;
+}
+
 static int rtl8152_pre_reset(struct usb_interface *intf)
 {
 	struct r8152 *tp = usb_get_intfdata(intf);
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:8293 @
 	struct net_device *netdev = tp->netdev;
 	int ret = 0;
 
+	if (!tp->rtl_ops.autosuspend_en)
+		return -EBUSY;
+
 	set_bit(SELECTIVE_SUSPEND, &tp->flags);
 	smp_mb__after_atomic();
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:8506 @
 
 	mii_ethtool_get_link_ksettings(&tp->mii, cmd);
 
+	linkmode_mod_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT,
+			 cmd->link_modes.supported, tp->support_2500full);
+
+	if (tp->support_2500full) {
+		linkmode_mod_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT,
+				 cmd->link_modes.advertising,
+				 ocp_reg_read(tp, OCP_10GBT_CTRL) & MDIO_AN_10GBT_CTRL_ADV2_5G);
+
+		linkmode_mod_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT,
+				 cmd->link_modes.lp_advertising,
+				 ocp_reg_read(tp, OCP_10GBT_STAT) & MDIO_AN_10GBT_STAT_LP2_5G);
+
+		if (is_speed_2500(rtl8152_get_speed(tp)))
+			cmd->base.speed = SPEED_2500;
+	}
+
 	mutex_unlock(&tp->control);
 
 	usb_autopm_put_interface(tp->intf);
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:8565 @
 		     cmd->link_modes.advertising))
 		advertising |= RTL_ADVERTISED_1000_FULL;
 
+	if (test_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT,
+		     cmd->link_modes.advertising))
+		advertising |= RTL_ADVERTISED_2500_FULL;
+
 	mutex_lock(&tp->control);
 
 	ret = rtl8152_set_speed(tp, cmd->base.autoneg, cmd->base.speed,
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:8715 @
 	struct r8152 *tp = netdev_priv(net);
 	int ret;
 
+	if (!tp->rtl_ops.eee_get) {
+		ret = -EOPNOTSUPP;
+		goto out;
+	}
+
 	ret = usb_autopm_get_interface(tp->intf);
 	if (ret < 0)
 		goto out;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:8742 @
 	struct r8152 *tp = netdev_priv(net);
 	int ret;
 
+	if (!tp->rtl_ops.eee_set) {
+		ret = -EOPNOTSUPP;
+		goto out;
+	}
+
 	ret = usb_autopm_get_interface(tp->intf);
 	if (ret < 0)
 		goto out;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:9027 @
 	dev->mtu = new_mtu;
 
 	if (netif_running(dev)) {
-		u32 rms = new_mtu + VLAN_ETH_HLEN + ETH_FCS_LEN;
+		if (tp->rtl_ops.change_mtu)
+			tp->rtl_ops.change_mtu(tp);
 
-		ocp_write_word(tp, MCU_TYPE_PLA, PLA_RMS, rms);
-
-		if (netif_carrier_ok(dev))
-			r8153_set_rx_early_size(tp);
+		if (netif_carrier_ok(dev)) {
+			netif_stop_queue(dev);
+			napi_disable(&tp->napi);
+			tasklet_disable(&tp->tx_tl);
+			tp->rtl_ops.disable(tp);
+			tp->rtl_ops.enable(tp);
+			rtl_start_rx(tp);
+			tasklet_enable(&tp->tx_tl);
+			napi_enable(&tp->napi);
+			rtl8152_set_rx_mode(dev);
+			netif_wake_queue(dev);
+		}
 	}
 
 	mutex_unlock(&tp->control);
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:9130 @
 		ops->in_nway		= rtl8153_in_nway;
 		ops->hw_phy_cfg		= r8153_hw_phy_cfg;
 		ops->autosuspend_en	= rtl8153_runtime_enable;
+		ops->change_mtu		= rtl8153_change_mtu;
 		if (tp->udev->speed < USB_SPEED_SUPER)
 			tp->rx_buf_sz	= 16 * 1024;
 		else
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:9152 @
 		ops->in_nway		= rtl8153_in_nway;
 		ops->hw_phy_cfg		= r8153b_hw_phy_cfg;
 		ops->autosuspend_en	= rtl8153b_runtime_enable;
+		ops->change_mtu		= rtl8153_change_mtu;
+		tp->rx_buf_sz		= 32 * 1024;
+		tp->eee_en		= true;
+		tp->eee_adv		= MDIO_EEE_1000T | MDIO_EEE_100TX;
+		break;
+
+	case RTL_VER_11:
+		tp->eee_en		= true;
+		tp->eee_adv		= MDIO_EEE_1000T | MDIO_EEE_100TX;
+		fallthrough;
+	case RTL_VER_10:
+		ops->init		= r8156_init;
+		ops->enable		= rtl8156_enable;
+		ops->disable		= rtl8153_disable;
+		ops->up			= rtl8156_up;
+		ops->down		= rtl8156_down;
+		ops->unload		= rtl8153_unload;
+		ops->eee_get		= r8153_get_eee;
+		ops->eee_set		= r8152_set_eee;
+		ops->in_nway		= rtl8153_in_nway;
+		ops->hw_phy_cfg		= r8156_hw_phy_cfg;
+		ops->autosuspend_en	= rtl8156_runtime_enable;
+		ops->change_mtu		= rtl8156_change_mtu;
+		tp->rx_buf_sz		= 48 * 1024;
+		tp->support_2500full	= 1;
+		break;
+
+	case RTL_VER_12:
+	case RTL_VER_13:
+		tp->support_2500full	= 1;
+		fallthrough;
+	case RTL_VER_15:
+		tp->eee_en		= true;
+		tp->eee_adv		= MDIO_EEE_1000T | MDIO_EEE_100TX;
+		ops->init		= r8156b_init;
+		ops->enable		= rtl8156b_enable;
+		ops->disable		= rtl8153_disable;
+		ops->up			= rtl8156_up;
+		ops->down		= rtl8156_down;
+		ops->unload		= rtl8153_unload;
+		ops->eee_get		= r8153_get_eee;
+		ops->eee_set		= r8152_set_eee;
+		ops->in_nway		= rtl8153_in_nway;
+		ops->hw_phy_cfg		= r8156b_hw_phy_cfg;
+		ops->autosuspend_en	= rtl8156_runtime_enable;
+		ops->change_mtu		= rtl8156_change_mtu;
+		tp->rx_buf_sz		= 48 * 1024;
+		break;
+
+	case RTL_VER_14:
+		ops->init		= r8153c_init;
+		ops->enable		= rtl8153_enable;
+		ops->disable		= rtl8153_disable;
+		ops->up			= rtl8153c_up;
+		ops->down		= rtl8153b_down;
+		ops->unload		= rtl8153_unload;
+		ops->eee_get		= r8153_get_eee;
+		ops->eee_set		= r8152_set_eee;
+		ops->in_nway		= rtl8153_in_nway;
+		ops->hw_phy_cfg		= r8153c_hw_phy_cfg;
+		ops->autosuspend_en	= rtl8153c_runtime_enable;
+		ops->change_mtu		= rtl8153c_change_mtu;
 		tp->rx_buf_sz		= 32 * 1024;
 		tp->eee_en		= true;
 		tp->eee_adv		= MDIO_EEE_1000T | MDIO_EEE_100TX;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:9221 @
 
 	default:
 		ret = -ENODEV;
-		netif_err(tp, probe, tp->netdev, "Unknown Device\n");
+		dev_err(&tp->intf->dev, "Unknown Device\n");
 		break;
 	}
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:9232 @
 #define FIRMWARE_8153A_3	"rtl_nic/rtl8153a-3.fw"
 #define FIRMWARE_8153A_4	"rtl_nic/rtl8153a-4.fw"
 #define FIRMWARE_8153B_2	"rtl_nic/rtl8153b-2.fw"
+#define FIRMWARE_8153C_1	"rtl_nic/rtl8153c-1.fw"
+#define FIRMWARE_8156A_2	"rtl_nic/rtl8156a-2.fw"
+#define FIRMWARE_8156B_2	"rtl_nic/rtl8156b-2.fw"
 
 MODULE_FIRMWARE(FIRMWARE_8153A_2);
 MODULE_FIRMWARE(FIRMWARE_8153A_3);
 MODULE_FIRMWARE(FIRMWARE_8153A_4);
 MODULE_FIRMWARE(FIRMWARE_8153B_2);
+MODULE_FIRMWARE(FIRMWARE_8153C_1);
+MODULE_FIRMWARE(FIRMWARE_8156A_2);
+MODULE_FIRMWARE(FIRMWARE_8156B_2);
 
 static int rtl_fw_init(struct r8152 *tp)
 {
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:9268 @
 		rtl_fw->pre_fw		= r8153b_pre_firmware_1;
 		rtl_fw->post_fw		= r8153b_post_firmware_1;
 		break;
+	case RTL_VER_11:
+		rtl_fw->fw_name		= FIRMWARE_8156A_2;
+		rtl_fw->post_fw		= r8156a_post_firmware_1;
+		break;
+	case RTL_VER_13:
+	case RTL_VER_15:
+		rtl_fw->fw_name		= FIRMWARE_8156B_2;
+		break;
+	case RTL_VER_14:
+		rtl_fw->fw_name		= FIRMWARE_8153C_1;
+		rtl_fw->pre_fw		= r8153b_pre_firmware_1;
+		rtl_fw->post_fw		= r8153c_post_firmware_1;
+		break;
 	default:
 		break;
 	}
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:9288 @
 	return 0;
 }
 
-static u8 rtl_get_version(struct usb_interface *intf)
+u8 rtl8152_get_version(struct usb_interface *intf)
 {
 	struct usb_device *udev = interface_to_usbdev(intf);
 	u32 ocp_data = 0;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:9336 @
 	case 0x6010:
 		version = RTL_VER_09;
 		break;
+	case 0x7010:
+		version = RTL_TEST_01;
+		break;
+	case 0x7020:
+		version = RTL_VER_10;
+		break;
+	case 0x7030:
+		version = RTL_VER_11;
+		break;
+	case 0x7400:
+		version = RTL_VER_12;
+		break;
+	case 0x7410:
+		version = RTL_VER_13;
+		break;
+	case 0x6400:
+		version = RTL_VER_14;
+		break;
+	case 0x7420:
+		version = RTL_VER_15;
+		break;
 	default:
 		version = RTL_VER_UNKNOWN;
 		dev_info(&intf->dev, "Unknown version 0x%04x\n", ocp_data);
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:9367 @
 
 	return version;
 }
+EXPORT_SYMBOL_GPL(rtl8152_get_version);
 
 static int rtl8152_probe(struct usb_interface *intf,
 			 const struct usb_device_id *id)
 {
 	struct usb_device *udev = interface_to_usbdev(intf);
-	u8 version = rtl_get_version(intf);
+	u8 version = rtl8152_get_version(intf);
 	struct r8152 *tp;
 	struct net_device *netdev;
 	int ret;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:9381 @
 	if (version == RTL_VER_UNKNOWN)
 		return -ENODEV;
 
-	if (udev->actconfig->desc.bConfigurationValue != 1) {
-		usb_driver_set_configuration(udev, 1);
+	if (!rtl_vendor_mode(intf))
 		return -ENODEV;
-	}
 
 	if (intf->cur_altsetting->desc.bNumEndpoints < 3)
 		return -ENODEV;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:9423 @
 	mutex_init(&tp->control);
 	INIT_DELAYED_WORK(&tp->schedule, rtl_work_func_t);
 	INIT_DELAYED_WORK(&tp->hw_phy_work, rtl_hw_phy_work_func_t);
-	tasklet_init(&tp->tx_tl, bottom_half, (unsigned long)tp);
+	tasklet_setup(&tp->tx_tl, bottom_half);
 	tasklet_disable(&tp->tx_tl);
 
 	netdev->netdev_ops = &rtl8152_netdev_ops;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:9467 @
 	/* MTU range: 68 - 1500 or 9194 */
 	netdev->min_mtu = ETH_MIN_MTU;
 	switch (tp->version) {
+	case RTL_VER_03:
+	case RTL_VER_04:
+	case RTL_VER_05:
+	case RTL_VER_06:
+	case RTL_VER_08:
+	case RTL_VER_09:
+	case RTL_VER_14:
+		netdev->max_mtu = size_to_mtu(9 * 1024);
+		break;
+	case RTL_VER_10:
+	case RTL_VER_11:
+		netdev->max_mtu = size_to_mtu(15 * 1024);
+		break;
+	case RTL_VER_12:
+	case RTL_VER_13:
+	case RTL_VER_15:
+		netdev->max_mtu = size_to_mtu(16 * 1024);
+		break;
 	case RTL_VER_01:
 	case RTL_VER_02:
-		netdev->max_mtu = ETH_DATA_LEN;
-		break;
+	case RTL_VER_07:
 	default:
-		netdev->max_mtu = RTL8153_MAX_MTU;
+		netdev->max_mtu = ETH_DATA_LEN;
 		break;
 	}
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:9505 @
 	tp->advertising = RTL_ADVERTISED_10_HALF | RTL_ADVERTISED_10_FULL |
 			  RTL_ADVERTISED_100_HALF | RTL_ADVERTISED_100_FULL;
 	if (tp->mii.supports_gmii) {
-		tp->speed = SPEED_1000;
+		if (tp->support_2500full &&
+		    tp->udev->speed >= USB_SPEED_SUPER) {
+			tp->speed = SPEED_2500;
+			tp->advertising |= RTL_ADVERTISED_2500_FULL;
+		} else {
+			tp->speed = SPEED_1000;
+		}
 		tp->advertising |= RTL_ADVERTISED_1000_FULL;
 	}
 	tp->duplex = DUPLEX_FULL;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:9535 @
 	set_ethernet_addr(tp);
 
 	usb_set_intfdata(intf, tp);
-	netif_napi_add(netdev, &tp->napi, r8152_poll, RTL8152_NAPI_WEIGHT);
+
+	if (tp->support_2500full)
+		netif_napi_add(netdev, &tp->napi, r8152_poll, 256);
+	else
+		netif_napi_add(netdev, &tp->napi, r8152_poll, 64);
 
 	ret = register_netdev(netdev);
 	if (ret != 0) {
-		netif_err(tp, probe, netdev, "couldn't register the device\n");
+		dev_err(&intf->dev, "couldn't register the device\n");
 		goto out1;
 	}
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:9575 @
 		unregister_netdev(tp->netdev);
 		tasklet_kill(&tp->tx_tl);
 		cancel_delayed_work_sync(&tp->hw_phy_work);
-		tp->rtl_ops.unload(tp);
+		if (tp->rtl_ops.unload)
+			tp->rtl_ops.unload(tp);
 		rtl8152_release_firmware(tp);
 		free_netdev(tp->netdev);
 	}
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:9596 @
 	.idProduct = (prod), \
 	.bInterfaceClass = USB_CLASS_COMM, \
 	.bInterfaceSubClass = USB_CDC_SUBCLASS_ETHERNET, \
+	.bInterfaceProtocol = USB_CDC_PROTO_NONE \
+}, \
+{ \
+	.match_flags = USB_DEVICE_ID_MATCH_INT_INFO | \
+		       USB_DEVICE_ID_MATCH_DEVICE, \
+	.idVendor = (vend), \
+	.idProduct = (prod), \
+	.bInterfaceClass = USB_CLASS_COMM, \
+	.bInterfaceSubClass = USB_CDC_SUBCLASS_NCM, \
 	.bInterfaceProtocol = USB_CDC_PROTO_NONE
 
 /* table of devices that work with this driver */
 static const struct usb_device_id rtl8152_table[] = {
+	/* Realtek */
 	{REALTEK_USB_DEVICE(VENDOR_ID_REALTEK, 0x8050)},
+	{REALTEK_USB_DEVICE(VENDOR_ID_REALTEK, 0x8053)},
 	{REALTEK_USB_DEVICE(VENDOR_ID_REALTEK, 0x8152)},
 	{REALTEK_USB_DEVICE(VENDOR_ID_REALTEK, 0x8153)},
+	{REALTEK_USB_DEVICE(VENDOR_ID_REALTEK, 0x8155)},
+	{REALTEK_USB_DEVICE(VENDOR_ID_REALTEK, 0x8156)},
+
+	/* Microsoft */
 	{REALTEK_USB_DEVICE(VENDOR_ID_MICROSOFT, 0x07ab)},
 	{REALTEK_USB_DEVICE(VENDOR_ID_MICROSOFT, 0x07c6)},
 	{REALTEK_USB_DEVICE(VENDOR_ID_MICROSOFT, 0x0927)},
diff -Nur --no-dereference linux-5.10.52-orig/drivers/net/usb/r8153_ecm.c linux-5.10.52-v7l+/drivers/net/usb/r8153_ecm.c
--- linux-5.10.52-orig/drivers/net/usb/r8153_ecm.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/net/usb/r8153_ecm.c	2021-07-25 16:46:16.038122022 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// SPDX-License-Identifier: GPL-2.0-or-later
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/mii.h>
+#include <linux/usb.h>
+#include <linux/usb/cdc.h>
+#include <linux/usb/usbnet.h>
+#include <linux/usb/r8152.h>
+
+#define OCP_BASE		0xe86c
+
+static int pla_read_word(struct usbnet *dev, u16 index)
+{
+	u16 byen = BYTE_EN_WORD;
+	u8 shift = index & 2;
+	__le32 tmp;
+	int ret;
+
+	if (shift)
+		byen <<= shift;
+
+	index &= ~3;
+
+	ret = usbnet_read_cmd(dev, RTL8152_REQ_GET_REGS, RTL8152_REQT_READ, index,
+			      MCU_TYPE_PLA | byen, &tmp, sizeof(tmp));
+	if (ret < 0)
+		goto out;
+
+	ret = __le32_to_cpu(tmp);
+	ret >>= (shift * 8);
+	ret &= 0xffff;
+
+out:
+	return ret;
+}
+
+static int pla_write_word(struct usbnet *dev, u16 index, u32 data)
+{
+	u32 mask = 0xffff;
+	u16 byen = BYTE_EN_WORD;
+	u8 shift = index & 2;
+	__le32 tmp;
+	int ret;
+
+	data &= mask;
+
+	if (shift) {
+		byen <<= shift;
+		mask <<= (shift * 8);
+		data <<= (shift * 8);
+	}
+
+	index &= ~3;
+
+	ret = usbnet_read_cmd(dev, RTL8152_REQ_GET_REGS, RTL8152_REQT_READ, index,
+			      MCU_TYPE_PLA | byen, &tmp, sizeof(tmp));
+
+	if (ret < 0)
+		goto out;
+
+	data |= __le32_to_cpu(tmp) & ~mask;
+	tmp = __cpu_to_le32(data);
+
+	ret = usbnet_write_cmd(dev, RTL8152_REQ_SET_REGS, RTL8152_REQT_WRITE, index,
+			       MCU_TYPE_PLA | byen, &tmp, sizeof(tmp));
+
+out:
+	return ret;
+}
+
+static int r8153_ecm_mdio_read(struct net_device *netdev, int phy_id, int reg)
+{
+	struct usbnet *dev = netdev_priv(netdev);
+	int ret;
+
+	ret = pla_write_word(dev, OCP_BASE, 0xa000);
+	if (ret < 0)
+		goto out;
+
+	ret = pla_read_word(dev, 0xb400 + reg * 2);
+
+out:
+	return ret;
+}
+
+static void r8153_ecm_mdio_write(struct net_device *netdev, int phy_id, int reg, int val)
+{
+	struct usbnet *dev = netdev_priv(netdev);
+	int ret;
+
+	ret = pla_write_word(dev, OCP_BASE, 0xa000);
+	if (ret < 0)
+		return;
+
+	ret = pla_write_word(dev, 0xb400 + reg * 2, val);
+}
+
+static int r8153_bind(struct usbnet *dev, struct usb_interface *intf)
+{
+	int status;
+
+	status = usbnet_cdc_bind(dev, intf);
+	if (status < 0)
+		return status;
+
+	dev->mii.dev = dev->net;
+	dev->mii.mdio_read = r8153_ecm_mdio_read;
+	dev->mii.mdio_write = r8153_ecm_mdio_write;
+	dev->mii.reg_num_mask = 0x1f;
+	dev->mii.supports_gmii = 1;
+
+	return status;
+}
+
+static const struct driver_info r8153_info = {
+	.description =	"RTL8153 ECM Device",
+	.flags =	FLAG_ETHER,
+	.bind =		r8153_bind,
+	.unbind =	usbnet_cdc_unbind,
+	.status =	usbnet_cdc_status,
+	.manage_power =	usbnet_manage_power,
+};
+
+static const struct usb_device_id products[] = {
+{
+	USB_DEVICE_AND_INTERFACE_INFO(VENDOR_ID_REALTEK, 0x8153, USB_CLASS_COMM,
+				      USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE),
+	.driver_info = (unsigned long)&r8153_info,
+},
+
+	{ },		/* END */
+};
+MODULE_DEVICE_TABLE(usb, products);
+
+static int rtl8153_ecm_probe(struct usb_interface *intf,
+			     const struct usb_device_id *id)
+{
+#if IS_REACHABLE(CONFIG_USB_RTL8152)
+	if (rtl8152_get_version(intf))
+		return -ENODEV;
+#endif
+
+	return usbnet_probe(intf, id);
+}
+
+static struct usb_driver r8153_ecm_driver = {
+	.name =		"r8153_ecm",
+	.id_table =	products,
+	.probe =	rtl8153_ecm_probe,
+	.disconnect =	usbnet_disconnect,
+	.suspend =	usbnet_suspend,
+	.resume =	usbnet_resume,
+	.reset_resume =	usbnet_resume,
+	.supports_autosuspend = 1,
+	.disable_hub_initiated_lpm = 1,
+};
+
+module_usb_driver(r8153_ecm_driver);
+
+MODULE_AUTHOR("Hayes Wang");
+MODULE_DESCRIPTION("Realtek USB ECM device");
+MODULE_LICENSE("GPL");
diff -Nur --no-dereference linux-5.10.52-orig/drivers/net/usb/smsc95xx.c linux-5.10.52-v7l+/drivers/net/usb/smsc95xx.c
--- linux-5.10.52-orig/drivers/net/usb/smsc95xx.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/net/usb/smsc95xx.c	2021-07-25 16:46:16.058121687 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:53 @
 #define SUSPEND_SUSPEND3		(0x08)
 #define SUSPEND_ALLMODES		(SUSPEND_SUSPEND0 | SUSPEND_SUSPEND1 | \
 					 SUSPEND_SUSPEND2 | SUSPEND_SUSPEND3)
+#define MAC_ADDR_LEN                    (6)
 
 struct smsc95xx_priv {
 	u32 mac_cr;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:71 @
 module_param(turbo_mode, bool, 0644);
 MODULE_PARM_DESC(turbo_mode, "Enable multiple frames per Rx transaction");
 
+static bool truesize_mode = false;
+module_param(truesize_mode, bool, 0644);
+MODULE_PARM_DESC(truesize_mode, "Report larger truesize value");
+
+static int packetsize = 2560;
+module_param(packetsize, int, 0644);
+MODULE_PARM_DESC(packetsize, "Override the RX URB packet size");
+
+static char *macaddr = ":";
+module_param(macaddr, charp, 0);
+MODULE_PARM_DESC(macaddr, "MAC address");
+
 static int __must_check __smsc95xx_read_reg(struct usbnet *dev, u32 index,
 					    u32 *data, int in_pm)
 {
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:769 @
 	return phy_mii_ioctl(netdev->phydev, rq, cmd);
 }
 
+/* Check the macaddr module parameter for a MAC address */
+static int smsc95xx_is_macaddr_param(struct usbnet *dev, u8 *dev_mac)
+{
+       int i, j, got_num, num;
+       u8 mtbl[MAC_ADDR_LEN];
+
+       if (macaddr[0] == ':')
+               return 0;
+
+       i = 0;
+       j = 0;
+       num = 0;
+       got_num = 0;
+       while (j < MAC_ADDR_LEN) {
+               if (macaddr[i] && macaddr[i] != ':') {
+                       got_num++;
+                       if ('0' <= macaddr[i] && macaddr[i] <= '9')
+                               num = num * 16 + macaddr[i] - '0';
+                       else if ('A' <= macaddr[i] && macaddr[i] <= 'F')
+                               num = num * 16 + 10 + macaddr[i] - 'A';
+                       else if ('a' <= macaddr[i] && macaddr[i] <= 'f')
+                               num = num * 16 + 10 + macaddr[i] - 'a';
+                       else
+                               break;
+                       i++;
+               } else if (got_num == 2) {
+                       mtbl[j++] = (u8) num;
+                       num = 0;
+                       got_num = 0;
+                       i++;
+               } else {
+                       break;
+               }
+       }
+
+       if (j == MAC_ADDR_LEN) {
+               netif_dbg(dev, ifup, dev->net, "Overriding MAC address with: "
+               "%02x:%02x:%02x:%02x:%02x:%02x\n", mtbl[0], mtbl[1], mtbl[2],
+                                               mtbl[3], mtbl[4], mtbl[5]);
+               for (i = 0; i < MAC_ADDR_LEN; i++)
+                       dev_mac[i] = mtbl[i];
+               return 1;
+       } else {
+               return 0;
+       }
+}
+
 static void smsc95xx_init_mac_address(struct usbnet *dev)
 {
 	/* maybe the boot loader passed the MAC address in devicetree */
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:838 @
 		}
 	}
 
+	/* Check module parameters */
+	if (smsc95xx_is_macaddr_param(dev, dev->net->dev_addr))
+		return;
+
 	/* no useful static MAC address found. generate a random one */
 	eth_hw_addr_random(dev->net);
 	netif_dbg(dev, ifup, dev->net, "MAC address set to eth_random_addr\n");
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:968 @
 
 	if (!turbo_mode) {
 		burst_cap = 0;
-		dev->rx_urb_size = MAX_SINGLE_PACKET_SIZE;
+		dev->rx_urb_size = packetsize ? packetsize : MAX_SINGLE_PACKET_SIZE;
 	} else if (dev->udev->speed == USB_SPEED_HIGH) {
-		burst_cap = DEFAULT_HS_BURST_CAP_SIZE / HS_USB_PKT_SIZE;
-		dev->rx_urb_size = DEFAULT_HS_BURST_CAP_SIZE;
+		dev->rx_urb_size = packetsize ? packetsize : DEFAULT_HS_BURST_CAP_SIZE;
+		burst_cap = dev->rx_urb_size / HS_USB_PKT_SIZE;
 	} else {
-		burst_cap = DEFAULT_FS_BURST_CAP_SIZE / FS_USB_PKT_SIZE;
-		dev->rx_urb_size = DEFAULT_FS_BURST_CAP_SIZE;
+		dev->rx_urb_size = packetsize ? packetsize : DEFAULT_FS_BURST_CAP_SIZE;
+		burst_cap = dev->rx_urb_size / FS_USB_PKT_SIZE;
 	}
 
 	netif_dbg(dev, ifup, dev->net, "rx_urb_size=%ld\n",
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1894 @
 				if (dev->net->features & NETIF_F_RXCSUM)
 					smsc95xx_rx_csum_offload(skb);
 				skb_trim(skb, skb->len - 4); /* remove fcs */
-				skb->truesize = size + sizeof(struct sk_buff);
+				if (truesize_mode)
+					skb->truesize = size + sizeof(struct sk_buff);
 
 				return 1;
 			}
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1913 @
 			if (dev->net->features & NETIF_F_RXCSUM)
 				smsc95xx_rx_csum_offload(ax_skb);
 			skb_trim(ax_skb, ax_skb->len - 4); /* remove fcs */
-			ax_skb->truesize = size + sizeof(struct sk_buff);
+			if (truesize_mode)
+				ax_skb->truesize = size + sizeof(struct sk_buff);
 
 			usbnet_skb_return(dev, ax_skb);
 		}
diff -Nur --no-dereference linux-5.10.52-orig/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h linux-5.10.52-v7l+/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h
--- linux-5.10.52-orig/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h	2021-07-25 16:46:16.948106766 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:81 @
 	size_t (*get_ramsize)(struct device *dev);
 	int (*get_memdump)(struct device *dev, void *data, size_t len);
 	int (*get_fwname)(struct device *dev, const char *ext,
-			  unsigned char *fw_name);
+			  unsigned char *fw_name, bool board_specific);
 	void (*debugfs_create)(struct device *dev);
 	int (*reset)(struct device *dev);
 };
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:226 @
 int brcmf_bus_get_fwname(struct brcmf_bus *bus, const char *ext,
 			 unsigned char *fw_name)
 {
-	return bus->ops->get_fwname(bus->dev, ext, fw_name);
+	return bus->ops->get_fwname(bus->dev, ext, fw_name, false);
+}
+
+static inline
+int brcmf_bus_get_board_fwname(struct brcmf_bus *bus, const char *ext,
+			       unsigned char *fw_name)
+{
+	return bus->ops->get_fwname(bus->dev, ext, fw_name, true);
 }
 
 static inline
diff -Nur --no-dereference linux-5.10.52-orig/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c linux-5.10.52-v7l+/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
--- linux-5.10.52-orig/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c	2021-07-25 16:46:16.958106598 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:12 @
 #include <linux/etherdevice.h>
 #include <linux/module.h>
 #include <linux/vmalloc.h>
+#include <linux/ctype.h>
 #include <net/cfg80211.h>
 #include <net/netlink.h>
 #include <uapi/linux/if_arp.h>
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:2944 @
 		brcmf_dbg(INFO, "Do not enable power save for P2P clients\n");
 		pm = PM_OFF;
 	}
-	brcmf_dbg(INFO, "power save %s\n", (pm ? "enabled" : "disabled"));
+	brcmf_info("power save %s\n", (pm ? "enabled" : "disabled"));
 
 	err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, pm);
 	if (err) {
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:2954 @
 			bphy_err(drvr, "error (%d)\n", err);
 	}
 
+	timeout = 2000; /* 2000ms - the maximum */
 	err = brcmf_fil_iovar_int_set(ifp, "pm2_sleep_ret",
 				min_t(u32, timeout, BRCMF_PS_MAX_TIMEOUT_MS));
 	if (err)
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:7361 @
 	struct brcmfmac_pd_cc *country_codes;
 	struct brcmfmac_pd_cc_entry *cc;
 	s32 found_index;
+	char ccode[BRCMF_COUNTRY_BUF_SZ];
+	int rev;
 	int i;
 
+	memcpy(ccode, alpha2, sizeof(ccode));
+	rev = -1;
+
 	country_codes = drvr->settings->country_codes;
 	if (!country_codes) {
-		brcmf_dbg(TRACE, "No country codes configured for device\n");
-		return -EINVAL;
+		brcmf_dbg(TRACE, "No country codes configured for device"
+				 " - use requested value\n");
+		goto use_input_value;
 	}
 
 	if ((alpha2[0] == ccreq->country_abbrev[0]) &&
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:7396 @
 		brcmf_dbg(TRACE, "No country code match found\n");
 		return -EINVAL;
 	}
-	memset(ccreq, 0, sizeof(*ccreq));
-	ccreq->rev = cpu_to_le32(country_codes->table[found_index].rev);
-	memcpy(ccreq->ccode, country_codes->table[found_index].cc,
+	rev = country_codes->table[found_index].rev;
+	memcpy(ccode, country_codes->table[found_index].cc,
 	       BRCMF_COUNTRY_BUF_SZ);
+
+use_input_value:
+	memset(ccreq, 0, sizeof(*ccreq));
+	ccreq->rev = cpu_to_le32(rev);
+	memcpy(ccreq->ccode, ccode, sizeof(ccode));
 	ccreq->country_abbrev[0] = alpha2[0];
 	ccreq->country_abbrev[1] = alpha2[1];
 	ccreq->country_abbrev[2] = 0;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:7418 @
 	struct brcmf_if *ifp = brcmf_get_ifp(cfg->pub, 0);
 	struct brcmf_pub *drvr = cfg->pub;
 	struct brcmf_fil_country_le ccreq;
+	char *alpha2;
 	s32 err;
 	int i;
 
-	/* The country code gets set to "00" by default at boot, ignore */
-	if (req->alpha2[0] == '0' && req->alpha2[1] == '0')
+	err = brcmf_fil_iovar_data_get(ifp, "country", &ccreq, sizeof(ccreq));
+	if (err) {
+		bphy_err(drvr, "Country code iovar returned err = %d\n", err);
 		return;
+	}
+
+	/* The country code gets set to "00" by default at boot - substitute
+	 * any saved ccode from the nvram file unless there is a valid code
+	 * already set.
+	 */
+	alpha2 = req->alpha2;
+	if (alpha2[0] == '0' && alpha2[1] == '0') {
+		extern char saved_ccode[2];
+
+		if ((isupper(ccreq.country_abbrev[0]) &&
+		     isupper(ccreq.country_abbrev[1])) ||
+		    !saved_ccode[0])
+			return;
+		alpha2 = saved_ccode;
+		pr_debug("brcmfmac: substituting saved ccode %c%c\n",
+			 alpha2[0], alpha2[1]);
+	}
 
 	/* ignore non-ISO3166 country codes */
 	for (i = 0; i < 2; i++)
-		if (req->alpha2[i] < 'A' || req->alpha2[i] > 'Z') {
+		if (alpha2[i] < 'A' || alpha2[i] > 'Z') {
 			bphy_err(drvr, "not an ISO3166 code (0x%02x 0x%02x)\n",
-				 req->alpha2[0], req->alpha2[1]);
+				 alpha2[0], alpha2[1]);
 			return;
 		}
 
 	brcmf_dbg(TRACE, "Enter: initiator=%d, alpha=%c%c\n", req->initiator,
-		  req->alpha2[0], req->alpha2[1]);
-
-	err = brcmf_fil_iovar_data_get(ifp, "country", &ccreq, sizeof(ccreq));
-	if (err) {
-		bphy_err(drvr, "Country code iovar returned err = %d\n", err);
-		return;
-	}
+		  alpha2[0], alpha2[1]);
 
-	err = brcmf_translate_country_code(ifp->drvr, req->alpha2, &ccreq);
+	err = brcmf_translate_country_code(ifp->drvr, alpha2, &ccreq);
 	if (err)
 		return;
 
diff -Nur --no-dereference linux-5.10.52-orig/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c linux-5.10.52-v7l+/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c
--- linux-5.10.52-orig/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c	2021-07-25 16:46:16.978106263 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:137 @
 	brcmf_dbg(TRACE, "Enter\n");
 
 	memset(clm_name, 0, sizeof(clm_name));
-	err = brcmf_bus_get_fwname(bus, ".clm_blob", clm_name);
+	err = brcmf_bus_get_board_fwname(bus, ".clm_blob", clm_name);
 	if (err) {
 		bphy_err(drvr, "get CLM blob file name failed (%d)\n", err);
 		return err;
 	}
 
-	err = firmware_request_nowarn(&clm, clm_name, bus->dev);
+	if (clm_name[0])
+		err = firmware_request_nowarn(&clm, clm_name, bus->dev);
+	if (err || !clm_name[0]) {
+		err = brcmf_bus_get_fwname(bus, ".clm_blob", clm_name);
+		if (err) {
+			bphy_err(drvr, "get CLM blob file name failed (%d)\n", err);
+			return err;
+		}
+
+		err = firmware_request_nowarn(&clm, clm_name, bus->dev);
+	}
 	if (err) {
 		brcmf_info("no clm_blob available (err=%d), device may have limited channels available\n",
 			   err);
diff -Nur --no-dereference linux-5.10.52-orig/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.c linux-5.10.52-v7l+/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.c
--- linux-5.10.52-orig/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.c	2021-07-25 16:46:16.978106263 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:13 @
 #include <linux/firmware.h>
 #include <linux/module.h>
 #include <linux/bcm47xx_nvram.h>
+#include <linux/ctype.h>
 
 #include "debug.h"
 #include "firmware.h"
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:34 @
 	END
 };
 
+char saved_ccode[2] = {};
+
 /**
  * struct nvram_parser - internal info for parser.
  *
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:551 @
 			goto fail;
 	}
 
-	if (data)
+	if (data) {
+		char *ccode = strnstr((char *)data, "ccode=", data_len);
+		/* Ensure this is a whole token */
+		if (ccode && ((void *)ccode == (void *)data || isspace(ccode[-1]))) {
+			/* Comment out the line */
+			ccode[0] = '#';
+			ccode += 6;
+			if (isupper(ccode[0]) && isupper(ccode[1]) &&
+			    isspace(ccode[2])) {
+				pr_debug("brcmfmac: intercepting ccode=%c%c\n",
+					 ccode[0], ccode[1]);
+				saved_ccode[0] = ccode[0];
+				saved_ccode[1] = ccode[1];
+			}
+		};
+
 		nvram = brcmf_fw_nvram_strip(data, data_len, &nvram_length,
 					     fwctx->req->domain_nr,
 					     fwctx->req->bus_nr);
+	}
 
 	if (free_bcm47xx_nvram)
 		bcm47xx_nvram_release_contents(data);
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:635 @
 		strlcat(alt_path, fwctx->req->board_type, BRCMF_FW_NAME_LEN);
 		strlcat(alt_path, ".txt", BRCMF_FW_NAME_LEN);
 
-		ret = request_firmware(fw, alt_path, fwctx->dev);
+		ret = firmware_request_nowarn(fw, alt_path, fwctx->dev);
 		if (ret == 0)
 			return ret;
 	}
diff -Nur --no-dereference linux-5.10.52-orig/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c linux-5.10.52-v7l+/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c
--- linux-5.10.52-orig/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c	2021-07-25 16:46:16.998105928 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1403 @
 }
 
 static
-int brcmf_pcie_get_fwname(struct device *dev, const char *ext, u8 *fw_name)
+int brcmf_pcie_get_fwname(struct device *dev, const char *ext, u8 *fw_name,
+			  bool board_specific)
 {
 	struct brcmf_bus *bus_if = dev_get_drvdata(dev);
 	struct brcmf_fw_request *fwreq;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1412 @
 		{ ext, fw_name },
 	};
 
+	if (board_specific) {
+		fw_name[0] = 0;
+		return 0;
+	}
 	fwreq = brcmf_fw_alloc_request(bus_if->chip, bus_if->chiprev,
 				       brcmf_pcie_fwnames,
 				       ARRAY_SIZE(brcmf_pcie_fwnames),
diff -Nur --no-dereference linux-5.10.52-orig/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c linux-5.10.52-v7l+/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c
--- linux-5.10.52-orig/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c	2021-07-25 16:46:16.998105928 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:614 @
 BRCMF_FW_DEF(4330, "brcmfmac4330-sdio");
 BRCMF_FW_DEF(4334, "brcmfmac4334-sdio");
 BRCMF_FW_DEF(43340, "brcmfmac43340-sdio");
+BRCMF_FW_DEF(43341, "brcmfmac43341-sdio");
 BRCMF_FW_DEF(4335, "brcmfmac4335-sdio");
 BRCMF_FW_DEF(43362, "brcmfmac43362-sdio");
 BRCMF_FW_DEF(4339, "brcmfmac4339-sdio");
 BRCMF_FW_DEF(43430A0, "brcmfmac43430a0-sdio");
 /* Note the names are not postfixed with a1 for backward compatibility */
 BRCMF_FW_DEF(43430A1, "brcmfmac43430-sdio");
+BRCMF_FW_DEF(43436, "brcmfmac43436-sdio");
 BRCMF_FW_DEF(43455, "brcmfmac43455-sdio");
 BRCMF_FW_DEF(43456, "brcmfmac43456-sdio");
 BRCMF_FW_DEF(4354, "brcmfmac4354-sdio");
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:639 @
 	BRCMF_FW_ENTRY(BRCM_CC_4330_CHIP_ID, 0xFFFFFFFF, 4330),
 	BRCMF_FW_ENTRY(BRCM_CC_4334_CHIP_ID, 0xFFFFFFFF, 4334),
 	BRCMF_FW_ENTRY(BRCM_CC_43340_CHIP_ID, 0xFFFFFFFF, 43340),
-	BRCMF_FW_ENTRY(BRCM_CC_43341_CHIP_ID, 0xFFFFFFFF, 43340),
+	BRCMF_FW_ENTRY(BRCM_CC_43341_CHIP_ID, 0xFFFFFFFF, 43341),
 	BRCMF_FW_ENTRY(BRCM_CC_4335_CHIP_ID, 0xFFFFFFFF, 4335),
 	BRCMF_FW_ENTRY(BRCM_CC_43362_CHIP_ID, 0xFFFFFFFE, 43362),
 	BRCMF_FW_ENTRY(BRCM_CC_4339_CHIP_ID, 0xFFFFFFFF, 4339),
 	BRCMF_FW_ENTRY(BRCM_CC_43430_CHIP_ID, 0x00000001, 43430A0),
-	BRCMF_FW_ENTRY(BRCM_CC_43430_CHIP_ID, 0xFFFFFFFE, 43430A1),
+	BRCMF_FW_ENTRY(BRCM_CC_43430_CHIP_ID, 0xFFFFFFFA, 43430A1),
+	BRCMF_FW_ENTRY(BRCM_CC_43430_CHIP_ID, 0x00000004, 43436),
 	BRCMF_FW_ENTRY(BRCM_CC_4345_CHIP_ID, 0x00000200, 43456),
 	BRCMF_FW_ENTRY(BRCM_CC_4345_CHIP_ID, 0xFFFFFDC0, 43455),
 	BRCMF_FW_ENTRY(BRCM_CC_4354_CHIP_ID, 0xFFFFFFFF, 4354),
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4120 @
 }
 
 static
-int brcmf_sdio_get_fwname(struct device *dev, const char *ext, u8 *fw_name)
+int brcmf_sdio_get_fwname(struct device *dev, const char *ext, u8 *fw_name,
+			  bool board_specific)
 {
 	struct brcmf_bus *bus_if = dev_get_drvdata(dev);
+	struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
 	struct brcmf_fw_request *fwreq;
+	u8 board_ext[BRCMF_FW_NAME_LEN];
 	struct brcmf_fw_name fwnames[] = {
 		{ ext, fw_name },
 	};
 
+	if (board_specific) {
+		strlcpy(board_ext, ".", BRCMF_FW_NAME_LEN);
+		strlcat(board_ext, sdiodev->settings->board_type,
+			BRCMF_FW_NAME_LEN);
+		strlcat(board_ext, ext, BRCMF_FW_NAME_LEN);
+		fwnames[0].extension = board_ext;
+	}
 	fwreq = brcmf_fw_alloc_request(bus_if->chip, bus_if->chiprev,
 				       brcmf_sdio_fwnames,
 				       ARRAY_SIZE(brcmf_sdio_fwnames),
diff -Nur --no-dereference linux-5.10.52-orig/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c linux-5.10.52-v7l+/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c
--- linux-5.10.52-orig/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c	2021-07-25 16:46:16.998105928 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1158 @
 }
 
 static
-int brcmf_usb_get_fwname(struct device *dev, const char *ext, u8 *fw_name)
+int brcmf_usb_get_fwname(struct device *dev, const char *ext, u8 *fw_name,
+			 bool board_specific)
 {
 	struct brcmf_bus *bus = dev_get_drvdata(dev);
 	struct brcmf_fw_request *fwreq;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1167 @
 		{ ext, fw_name },
 	};
 
+	if (board_specific) {
+		fw_name[0] = 0;
+		return 0;
+	}
 	fwreq = brcmf_fw_alloc_request(bus->chip, bus->chiprev,
 				       brcmf_usb_fwnames,
 				       ARRAY_SIZE(brcmf_usb_fwnames),
diff -Nur --no-dereference linux-5.10.52-orig/drivers/nvmem/Kconfig linux-5.10.52-v7l+/drivers/nvmem/Kconfig
--- linux-5.10.52-orig/drivers/nvmem/Kconfig	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/nvmem/Kconfig	2021-07-25 16:46:19.268067871 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:273 @
 	  This driver can also be built as a module. If so, the module
 	  will be called nvmem-sprd-efuse.
 
+config NVMEM_RMEM
+	tristate "Reserved Memory Based Driver Support"
+	help
+	  This drivers maps reserved memory into an nvmem device. It might be
+	  useful to expose information left by firmware in memory.
+
+	  This driver can also be built as a module. If so, the module
+	  will be called nvmem-rmem.
 endif
diff -Nur --no-dereference linux-5.10.52-orig/drivers/nvmem/Makefile linux-5.10.52-v7l+/drivers/nvmem/Makefile
--- linux-5.10.52-orig/drivers/nvmem/Makefile	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/nvmem/Makefile	2021-07-25 16:46:19.268067871 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:58 @
 nvmem_zynqmp_nvmem-y		:= zynqmp_nvmem.o
 obj-$(CONFIG_SPRD_EFUSE)	+= nvmem_sprd_efuse.o
 nvmem_sprd_efuse-y		:= sprd-efuse.o
+obj-$(CONFIG_NVMEM_RMEM) 	+= nvmem-rmem.o
+nvmem-rmem-y			:= rmem.o
diff -Nur --no-dereference linux-5.10.52-orig/drivers/nvmem/rmem.c linux-5.10.52-v7l+/drivers/nvmem/rmem.c
--- linux-5.10.52-orig/drivers/nvmem/rmem.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/nvmem/rmem.c	2021-07-25 16:46:19.288067536 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2020 Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
+ */
+
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/nvmem-provider.h>
+#include <linux/of_reserved_mem.h>
+#include <linux/platform_device.h>
+
+struct rmem {
+	struct device *dev;
+	struct nvmem_device *nvmem;
+	struct reserved_mem *mem;
+
+	phys_addr_t size;
+};
+
+static int rmem_read(void *context, unsigned int offset,
+		     void *val, size_t bytes)
+{
+	struct rmem *priv = context;
+	size_t available = priv->mem->size;
+	loff_t off = offset;
+	void *addr;
+	int count;
+
+	/*
+	 * Only map the reserved memory at this point to avoid potential rogue
+	 * kernel threads inadvertently modifying it. Based on the current
+	 * uses-cases for this driver, the performance hit isn't a concern.
+	 * Nor is likely to be, given the nature of the subsystem. Most nvmem
+	 * devices operate over slow buses to begin with.
+	 *
+	 * An alternative would be setting the memory as RO, set_memory_ro(),
+	 * but as of Dec 2020 this isn't possible on arm64.
+	 */
+	addr = memremap(priv->mem->base, available, MEMREMAP_WB);
+	if (IS_ERR(addr)) {
+		dev_err(priv->dev, "Failed to remap memory region\n");
+		return PTR_ERR(addr);
+	}
+
+	count = memory_read_from_buffer(val, bytes, &off, addr, available);
+
+	memunmap(addr);
+
+	return count;
+}
+
+static int rmem_probe(struct platform_device *pdev)
+{
+	struct nvmem_config config = { };
+	struct device *dev = &pdev->dev;
+	struct reserved_mem *mem;
+	struct rmem *priv;
+
+	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+	priv->dev = dev;
+
+	mem = of_reserved_mem_lookup(dev->of_node);
+	if (!mem) {
+		dev_err(dev, "Failed to lookup reserved memory\n");
+		return -EINVAL;
+	}
+	priv->mem = mem;
+
+	config.dev = dev;
+	config.priv = priv;
+	config.name = "rmem";
+	config.size = mem->size;
+	config.reg_read = rmem_read;
+
+	return PTR_ERR_OR_ZERO(devm_nvmem_register(dev, &config));
+}
+
+static const struct of_device_id rmem_match[] = {
+	{ .compatible = "nvmem-rmem", },
+	{ /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, rmem_match);
+
+static struct platform_driver rmem_driver = {
+	.probe = rmem_probe,
+	.driver = {
+		.name = "rmem",
+		.of_match_table = rmem_match,
+	},
+};
+module_platform_driver(rmem_driver);
+
+MODULE_AUTHOR("Nicolas Saenz Julienne <nsaenzjulienne@suse.de>");
+MODULE_DESCRIPTION("Reserved Memory Based nvmem Driver");
+MODULE_LICENSE("GPL");
diff -Nur --no-dereference linux-5.10.52-orig/drivers/of/configfs.c linux-5.10.52-v7l+/drivers/of/configfs.c
--- linux-5.10.52-orig/drivers/of/configfs.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/of/configfs.c	2021-07-25 16:46:19.308067200 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/*
+ * Configfs entries for device-tree
+ *
+ * Copyright (C) 2013 - Pantelis Antoniou <panto@antoniou-consulting.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#include <linux/ctype.h>
+#include <linux/cpu.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_fdt.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/proc_fs.h>
+#include <linux/configfs.h>
+#include <linux/types.h>
+#include <linux/stat.h>
+#include <linux/limits.h>
+#include <linux/file.h>
+#include <linux/vmalloc.h>
+#include <linux/firmware.h>
+#include <linux/sizes.h>
+
+#include "of_private.h"
+
+struct cfs_overlay_item {
+	struct config_item	item;
+
+	char			path[PATH_MAX];
+
+	const struct firmware	*fw;
+	struct device_node	*overlay;
+	int			ov_id;
+
+	void			*dtbo;
+	int			dtbo_size;
+};
+
+static inline struct cfs_overlay_item *to_cfs_overlay_item(
+		struct config_item *item)
+{
+	return item ? container_of(item, struct cfs_overlay_item, item) : NULL;
+}
+
+static ssize_t cfs_overlay_item_path_show(struct config_item *item,
+		char *page)
+{
+	struct cfs_overlay_item *overlay = to_cfs_overlay_item(item);
+	return sprintf(page, "%s\n", overlay->path);
+}
+
+static ssize_t cfs_overlay_item_path_store(struct config_item *item,
+		const char *page, size_t count)
+{
+	struct cfs_overlay_item *overlay = to_cfs_overlay_item(item);
+	const char *p = page;
+	char *s;
+	int err;
+
+	/* if it's set do not allow changes */
+	if (overlay->path[0] != '\0' || overlay->dtbo_size > 0)
+		return -EPERM;
+
+	/* copy to path buffer (and make sure it's always zero terminated */
+	count = snprintf(overlay->path, sizeof(overlay->path) - 1, "%s", p);
+	overlay->path[sizeof(overlay->path) - 1] = '\0';
+
+	/* strip trailing newlines */
+	s = overlay->path + strlen(overlay->path);
+	while (s > overlay->path && *--s == '\n')
+		*s = '\0';
+
+	pr_debug("%s: path is '%s'\n", __func__, overlay->path);
+
+	err = request_firmware(&overlay->fw, overlay->path, NULL);
+	if (err != 0)
+		goto out_err;
+
+	err = of_overlay_fdt_apply((void *)overlay->fw->data,
+				   (u32)overlay->fw->size, &overlay->ov_id);
+	if (err != 0)
+		goto out_err;
+
+	return count;
+
+out_err:
+
+	release_firmware(overlay->fw);
+	overlay->fw = NULL;
+
+	overlay->path[0] = '\0';
+	return err;
+}
+
+static ssize_t cfs_overlay_item_status_show(struct config_item *item,
+		char *page)
+{
+	struct cfs_overlay_item *overlay = to_cfs_overlay_item(item);
+
+	return sprintf(page, "%s\n",
+			overlay->ov_id > 0 ? "applied" : "unapplied");
+}
+
+CONFIGFS_ATTR(cfs_overlay_item_, path);
+CONFIGFS_ATTR_RO(cfs_overlay_item_, status);
+
+static struct configfs_attribute *cfs_overlay_attrs[] = {
+	&cfs_overlay_item_attr_path,
+	&cfs_overlay_item_attr_status,
+	NULL,
+};
+
+ssize_t cfs_overlay_item_dtbo_read(struct config_item *item,
+		void *buf, size_t max_count)
+{
+	struct cfs_overlay_item *overlay = to_cfs_overlay_item(item);
+
+	pr_debug("%s: buf=%p max_count=%zu\n", __func__,
+			buf, max_count);
+
+	if (overlay->dtbo == NULL)
+		return 0;
+
+	/* copy if buffer provided */
+	if (buf != NULL) {
+		/* the buffer must be large enough */
+		if (overlay->dtbo_size > max_count)
+			return -ENOSPC;
+
+		memcpy(buf, overlay->dtbo, overlay->dtbo_size);
+	}
+
+	return overlay->dtbo_size;
+}
+
+ssize_t cfs_overlay_item_dtbo_write(struct config_item *item,
+		const void *buf, size_t count)
+{
+	struct cfs_overlay_item *overlay = to_cfs_overlay_item(item);
+	int err;
+
+	/* if it's set do not allow changes */
+	if (overlay->path[0] != '\0' || overlay->dtbo_size > 0)
+		return -EPERM;
+
+	/* copy the contents */
+	overlay->dtbo = kmemdup(buf, count, GFP_KERNEL);
+	if (overlay->dtbo == NULL)
+		return -ENOMEM;
+
+	overlay->dtbo_size = count;
+
+	err = of_overlay_fdt_apply(overlay->dtbo, overlay->dtbo_size,
+				   &overlay->ov_id);
+	if (err != 0)
+		goto out_err;
+
+	return count;
+
+out_err:
+	kfree(overlay->dtbo);
+	overlay->dtbo = NULL;
+	overlay->dtbo_size = 0;
+	overlay->ov_id = 0;
+
+	return err;
+}
+
+CONFIGFS_BIN_ATTR(cfs_overlay_item_, dtbo, NULL, SZ_1M);
+
+static struct configfs_bin_attribute *cfs_overlay_bin_attrs[] = {
+	&cfs_overlay_item_attr_dtbo,
+	NULL,
+};
+
+static void cfs_overlay_release(struct config_item *item)
+{
+	struct cfs_overlay_item *overlay = to_cfs_overlay_item(item);
+
+	if (overlay->ov_id > 0)
+		of_overlay_remove(&overlay->ov_id);
+	if (overlay->fw)
+		release_firmware(overlay->fw);
+	/* kfree with NULL is safe */
+	kfree(overlay->dtbo);
+	kfree(overlay);
+}
+
+static struct configfs_item_operations cfs_overlay_item_ops = {
+	.release	= cfs_overlay_release,
+};
+
+static struct config_item_type cfs_overlay_type = {
+	.ct_item_ops	= &cfs_overlay_item_ops,
+	.ct_attrs	= cfs_overlay_attrs,
+	.ct_bin_attrs	= cfs_overlay_bin_attrs,
+	.ct_owner	= THIS_MODULE,
+};
+
+static struct config_item *cfs_overlay_group_make_item(
+		struct config_group *group, const char *name)
+{
+	struct cfs_overlay_item *overlay;
+
+	overlay = kzalloc(sizeof(*overlay), GFP_KERNEL);
+	if (!overlay)
+		return ERR_PTR(-ENOMEM);
+
+	config_item_init_type_name(&overlay->item, name, &cfs_overlay_type);
+	return &overlay->item;
+}
+
+static void cfs_overlay_group_drop_item(struct config_group *group,
+		struct config_item *item)
+{
+	struct cfs_overlay_item *overlay = to_cfs_overlay_item(item);
+
+	config_item_put(&overlay->item);
+}
+
+static struct configfs_group_operations overlays_ops = {
+	.make_item	= cfs_overlay_group_make_item,
+	.drop_item	= cfs_overlay_group_drop_item,
+};
+
+static struct config_item_type overlays_type = {
+	.ct_group_ops   = &overlays_ops,
+	.ct_owner       = THIS_MODULE,
+};
+
+static struct configfs_group_operations of_cfs_ops = {
+	/* empty - we don't allow anything to be created */
+};
+
+static struct config_item_type of_cfs_type = {
+	.ct_group_ops   = &of_cfs_ops,
+	.ct_owner       = THIS_MODULE,
+};
+
+struct config_group of_cfs_overlay_group;
+
+static struct configfs_subsystem of_cfs_subsys = {
+	.su_group = {
+		.cg_item = {
+			.ci_namebuf = "device-tree",
+			.ci_type = &of_cfs_type,
+		},
+	},
+	.su_mutex = __MUTEX_INITIALIZER(of_cfs_subsys.su_mutex),
+};
+
+static int __init of_cfs_init(void)
+{
+	int ret;
+
+	pr_info("%s\n", __func__);
+
+	config_group_init(&of_cfs_subsys.su_group);
+	config_group_init_type_name(&of_cfs_overlay_group, "overlays",
+			&overlays_type);
+	configfs_add_default_group(&of_cfs_overlay_group,
+			&of_cfs_subsys.su_group);
+
+	ret = configfs_register_subsystem(&of_cfs_subsys);
+	if (ret != 0) {
+		pr_err("%s: failed to register subsys\n", __func__);
+		goto out;
+	}
+	pr_info("%s: OK\n", __func__);
+out:
+	return ret;
+}
+late_initcall(of_cfs_init);
diff -Nur --no-dereference linux-5.10.52-orig/drivers/of/Kconfig linux-5.10.52-v7l+/drivers/of/Kconfig
--- linux-5.10.52-orig/drivers/of/Kconfig	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/of/Kconfig	2021-07-25 16:46:19.288067536 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:103 @
 	# arches should select this if DMA is coherent by default for OF devices
 	bool
 
+config OF_CONFIGFS
+	bool "Device Tree Overlay ConfigFS interface"
+	select CONFIGFS_FS
+	select OF_OVERLAY
+	help
+	  Enable a simple user-space driven DT overlay interface.
+
 endif # OF
diff -Nur --no-dereference linux-5.10.52-orig/drivers/of/Makefile linux-5.10.52-v7l+/drivers/of/Makefile
--- linux-5.10.52-orig/drivers/of/Makefile	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/of/Makefile	2021-07-25 16:46:19.288067536 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
 # SPDX-License-Identifier: GPL-2.0
 obj-y = base.o device.o platform.o property.o
 obj-$(CONFIG_OF_KOBJ) += kobj.o
+obj-$(CONFIG_OF_CONFIGFS) += configfs.o
 obj-$(CONFIG_OF_DYNAMIC) += dynamic.o
 obj-$(CONFIG_OF_FLATTREE) += fdt.o
 obj-$(CONFIG_OF_EARLY_FLATTREE) += fdt_address.o
diff -Nur --no-dereference linux-5.10.52-orig/drivers/of/overlay.c linux-5.10.52-v7l+/drivers/of/overlay.c
--- linux-5.10.52-orig/drivers/of/overlay.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/of/overlay.c	2021-07-25 16:46:19.308067200 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:248 @
 	if (!target_path)
 		return NULL;
 	target_path_len = strlen(target_path);
+	if (!strcmp(target_path, "/"))
+		target_path_len = 0;
 
 	new_prop = kzalloc(sizeof(*new_prop), GFP_KERNEL);
 	if (!new_prop)
diff -Nur --no-dereference linux-5.10.52-orig/drivers/of/platform.c linux-5.10.52-v7l+/drivers/of/platform.c
--- linux-5.10.52-orig/drivers/of/platform.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/of/platform.c	2021-07-25 16:46:19.308067200 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:514 @
 	{ .compatible = "qcom,rmtfs-mem" },
 	{ .compatible = "qcom,cmd-db" },
 	{ .compatible = "ramoops" },
+	{ .compatible = "nvmem-rmem" },
 	{}
 };
 
diff -Nur --no-dereference linux-5.10.52-orig/drivers/pci/controller/pcie-brcmstb.c linux-5.10.52-v7l+/drivers/pci/controller/pcie-brcmstb.c
--- linux-5.10.52-orig/drivers/pci/controller/pcie-brcmstb.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/pci/controller/pcie-brcmstb.c	2021-07-25 16:46:19.448064853 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:117 @
 		PCIE_MISC_CPU_2_PCIE_MEM_WIN0_LIMIT_HI + ((win) * 8)
 
 #define PCIE_MISC_HARD_PCIE_HARD_DEBUG					0x4204
-#define  PCIE_MISC_HARD_PCIE_HARD_DEBUG_CLKREQ_DEBUG_ENABLE_MASK	0x2
-#define  PCIE_MISC_HARD_PCIE_HARD_DEBUG_SERDES_IDDQ_MASK		0x08000000
+#define  PCIE_MISC_HARD_PCIE_HARD_DEBUG_CLKREQ_DEBUG_ENABLE_MASK	BIT(1)
+#define  PCIE_MISC_HARD_PCIE_HARD_DEBUG_CLKREQ_L1SS_ENABLE_MASK		BIT(21)
+#define  PCIE_MISC_HARD_PCIE_HARD_DEBUG_SERDES_IDDQ_MASK		BIT(27)
 
 
 #define PCIE_INTR2_CPU_BASE		0x4300
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:280 @
 	struct clk		*clk;
 	struct device_node	*np;
 	bool			ssc;
+	bool			l1ss;
 	int			gen;
 	u64			msi_target_addr;
 	struct brcm_msi		*msi;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:466 @
 
 static struct msi_domain_info brcm_msi_domain_info = {
 	/* Multi MSI is supported by the controller, but not by this driver */
-	.flags	= (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS),
+	.flags	= (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
+		   MSI_FLAG_PCI_MSIX),
 	.chip	= &brcm_msi_irq_chip,
 };
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:875 @
 
 	/* Reset the bridge */
 	pcie->bridge_sw_init_set(pcie, 1);
+	pcie->perst_set(pcie, 1);
+
 	usleep_range(100, 200);
 
 	/* Take the bridge out of reset */
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1033 @
 		PCIE_RC_CFG_VENDOR_VENDOR_SPECIFIC_REG1_ENDIAN_MODE_BAR2_MASK);
 	writel(tmp, base + PCIE_RC_CFG_VENDOR_VENDOR_SPECIFIC_REG1);
 
-	/*
-	 * Refclk from RC should be gated with CLKREQ# input when ASPM L0s,L1
-	 * is enabled => setting the CLKREQ_DEBUG_ENABLE field to 1.
-	 */
 	tmp = readl(base + PCIE_MISC_HARD_PCIE_HARD_DEBUG);
-	tmp |= PCIE_MISC_HARD_PCIE_HARD_DEBUG_CLKREQ_DEBUG_ENABLE_MASK;
+	if (pcie->l1ss) {
+		/*
+		 * Enable CLKREQ# signalling include L1 Substate control of
+		 * the CLKREQ# signal and the external reference clock buffer.
+		 * meet requirement for Endpoints that require CLKREQ#
+		 * assertion to clock active within 400ns.
+		 */
+		tmp &= ~PCIE_MISC_HARD_PCIE_HARD_DEBUG_CLKREQ_DEBUG_ENABLE_MASK;
+		tmp |= PCIE_MISC_HARD_PCIE_HARD_DEBUG_CLKREQ_L1SS_ENABLE_MASK;
+	} else {
+		/*
+		 * Refclk from RC should be gated with CLKREQ# input when
+		 * ASPM L0s,L1 is enabled => setting the CLKREQ_DEBUG_ENABLE
+		 * field to 1.
+		 */
+		tmp &= ~PCIE_MISC_HARD_PCIE_HARD_DEBUG_CLKREQ_L1SS_ENABLE_MASK;
+		tmp |= PCIE_MISC_HARD_PCIE_HARD_DEBUG_CLKREQ_DEBUG_ENABLE_MASK;
+	}
 	writel(tmp, base + PCIE_MISC_HARD_PCIE_HARD_DEBUG);
 
 	return 0;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1272 @
 	pcie->gen = (ret < 0) ? 0 : ret;
 
 	pcie->ssc = of_property_read_bool(np, "brcm,enable-ssc");
+	pcie->l1ss = of_property_read_bool(np, "brcm,enable-l1ss");
 
 	ret = clk_prepare_enable(pcie->clk);
 	if (ret) {
diff -Nur --no-dereference linux-5.10.52-orig/drivers/perf/Kconfig linux-5.10.52-v7l+/drivers/perf/Kconfig
--- linux-5.10.52-orig/drivers/perf/Kconfig	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/perf/Kconfig	2021-07-25 16:46:19.668061165 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:133 @
 	  Extension, which provides periodic sampling of operations in
 	  the CPU pipeline and reports this via the perf AUX interface.
 
+config RPI_AXIPERF
+        depends on ARCH_BCM2835
+        tristate "RaspberryPi AXI Performance monitors"
+        default n
+        help
+          Say y if you want to use Raspberry Pi AXI performance monitors, m if
+          you want to build it as a module.
+
 source "drivers/perf/hisilicon/Kconfig"
 
 endmenu
diff -Nur --no-dereference linux-5.10.52-orig/drivers/perf/Makefile linux-5.10.52-v7l+/drivers/perf/Makefile
--- linux-5.10.52-orig/drivers/perf/Makefile	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/perf/Makefile	2021-07-25 16:46:19.668061165 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:16 @
 obj-$(CONFIG_THUNDERX2_PMU) += thunderx2_pmu.o
 obj-$(CONFIG_XGENE_PMU) += xgene_pmu.o
 obj-$(CONFIG_ARM_SPE_PMU) += arm_spe_pmu.o
+obj-$(CONFIG_RPI_AXIPERF) += raspberrypi_axi_monitor.o
diff -Nur --no-dereference linux-5.10.52-orig/drivers/perf/raspberrypi_axi_monitor.c linux-5.10.52-v7l+/drivers/perf/raspberrypi_axi_monitor.c
--- linux-5.10.52-orig/drivers/perf/raspberrypi_axi_monitor.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/perf/raspberrypi_axi_monitor.c	2021-07-25 16:46:19.698060662 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/*
+ * raspberrypi_axi_monitor.c
+ *
+ * Author: james.hughes@raspberrypi.org
+ *
+ * Raspberry Pi AXI performance counters.
+ *
+ * Copyright (C) 2017 Raspberry Pi Trading Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/debugfs.h>
+#include <linux/devcoredump.h>
+#include <linux/device.h>
+#include <linux/kthread.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+
+#include <soc/bcm2835/raspberrypi-firmware.h>
+
+#define NUM_MONITORS 2
+#define NUM_BUS_WATCHERS_PER_MONITOR 3
+
+#define SYSTEM_MONITOR 0
+#define VPU_MONITOR 1
+
+#define MAX_BUSES 16
+#define DEFAULT_SAMPLE_TIME 100
+
+#define NUM_BUS_WATCHER_RESULTS 9
+
+struct bus_watcher_data {
+	union	{
+		u32 results[NUM_BUS_WATCHER_RESULTS];
+		struct {
+			u32 atrans;
+			u32 atwait;
+			u32 amax;
+			u32 wtrans;
+			u32 wtwait;
+			u32 wmax;
+			u32 rtrans;
+			u32 rtwait;
+			u32 rmax;
+		};
+	};
+};
+
+
+struct rpi_axiperf {
+	struct platform_device *dev;
+	struct dentry *root_folder;
+
+	struct task_struct *monitor_thread;
+	struct mutex lock;
+
+	struct rpi_firmware *firmware;
+
+	/* Sample time spent on for each bus */
+	int sample_time;
+
+	/* Now storage for the per monitor settings and the resulting
+	 * performance figures
+	 */
+	struct {
+		/* Bit field of buses we want to monitor */
+		int bus_enabled;
+		/* Bit field of buses to filter by */
+		int bus_filter;
+		/* The current buses being monitored on this monitor */
+		int current_bus[NUM_BUS_WATCHERS_PER_MONITOR];
+		/* The last bus monitored on this monitor */
+		int last_monitored;
+
+		/* Set true if this mailbox must use the mailbox interface
+		 * rather than access registers directly.
+		 */
+		int use_mailbox_interface;
+
+		/* Current result values */
+		struct bus_watcher_data results[MAX_BUSES];
+
+		struct dentry *debugfs_entry;
+		void __iomem *base_address;
+
+	}  monitor[NUM_MONITORS];
+
+};
+
+static struct rpi_axiperf *state;
+
+/* Two monitors, System and VPU, each with the following register sets.
+ * Each monitor can only monitor one bus at a time, so we time share them,
+ * giving each bus 100ms (default, settable via debugfs) of time on its
+ * associated monitor
+ * Record results from the three Bus watchers per monitor and push to the sysfs
+ */
+
+/* general registers */
+const int GEN_CTRL;
+
+const int GEN_CTL_ENABLE_BIT	= BIT(0);
+const int GEN_CTL_RESET_BIT	= BIT(1);
+
+/* Bus watcher registers */
+const int BW_PITCH		= 0x40;
+
+const int BW0_CTRL		= 0x40;
+const int BW1_CTRL		= 0x80;
+const int BW2_CTRL		= 0xc0;
+
+const int BW_ATRANS_OFFSET	= 0x04;
+const int BW_ATWAIT_OFFSET	= 0x08;
+const int BW_AMAX_OFFSET	= 0x0c;
+const int BW_WTRANS_OFFSET	= 0x10;
+const int BW_WTWAIT_OFFSET	= 0x14;
+const int BW_WMAX_OFFSET	= 0x18;
+const int BW_RTRANS_OFFSET	= 0x1c;
+const int BW_RTWAIT_OFFSET	= 0x20;
+const int BW_RMAX_OFFSET	= 0x24;
+
+const int BW_CTRL_RESET_BIT	= BIT(31);
+const int BW_CTRL_ENABLE_BIT	= BIT(30);
+const int BW_CTRL_ENABLE_ID_FILTER_BIT	= BIT(29);
+const int BW_CTRL_LIMIT_HALT_BIT	= BIT(28);
+
+const int BW_CTRL_SOURCE_SHIFT	= 8;
+const int BW_CTRL_SOURCE_MASK	= GENMASK(12, 8); // 5 bits
+const int BW_CTRL_BUS_WATCH_SHIFT;
+const int BW_CTRL_BUS_WATCH_MASK = GENMASK(5, 0); // 6 bits
+const int BW_CTRL_BUS_FILTER_SHIFT = 8;
+
+const static char *bus_filter_strings[] = {
+	"",
+	"CORE0_V",
+	"ICACHE0",
+	"DCACHE0",
+	"CORE1_V",
+	"ICACHE1",
+	"DCACHE1",
+	"L2_MAIN",
+	"HOST_PORT",
+	"HOST_PORT2",
+	"HVS",
+	"ISP",
+	"VIDEO_DCT",
+	"VIDEO_SD2AXI",
+	"CAM0",
+	"CAM1",
+	"DMA0",
+	"DMA1",
+	"DMA2_VPU",
+	"JPEG",
+	"VIDEO_CME",
+	"TRANSPOSER",
+	"VIDEO_FME",
+	"CCP2TX",
+	"USB",
+	"V3D0",
+	"V3D1",
+	"V3D2",
+	"AVE",
+	"DEBUG",
+	"CPU",
+	"M30"
+};
+
+const int num_bus_filters = ARRAY_SIZE(bus_filter_strings);
+
+const static char *system_bus_string[] = {
+	"DMA_L2",
+	"TRANS",
+	"JPEG",
+	"SYSTEM_UC",
+	"DMA_UC",
+	"SYSTEM_L2",
+	"CCP2TX",
+	"MPHI_RX",
+	"MPHI_TX",
+	"HVS",
+	"H264",
+	"ISP",
+	"V3D",
+	"PERIPHERAL",
+	"CPU_UC",
+	"CPU_L2"
+};
+
+const int num_system_buses = ARRAY_SIZE(system_bus_string);
+
+const static char *vpu_bus_string[] = {
+	"VPU1_D_L2",
+	"VPU0_D_L2",
+	"VPU1_I_L2",
+	"VPU0_I_L2",
+	"SYSTEM_L2",
+	"L2_FLUSH",
+	"DMA_L2",
+	"VPU1_D_UC",
+	"VPU0_D_UC",
+	"VPU1_I_UC",
+	"VPU0_I_UC",
+	"SYSTEM_UC",
+	"L2_OUT",
+	"DMA_UC",
+	"SDRAM",
+	"L2_IN"
+};
+
+const int num_vpu_buses = ARRAY_SIZE(vpu_bus_string);
+
+const static char *monitor_name[] = {
+	"System",
+	"VPU"
+};
+
+static inline void write_reg(int monitor, int reg, u32 value)
+{
+	writel(value, state->monitor[monitor].base_address + reg);
+}
+
+static inline u32 read_reg(int monitor, u32 reg)
+{
+	return readl(state->monitor[monitor].base_address + reg);
+}
+
+static void read_bus_watcher(int monitor, int watcher, u32 *results)
+{
+	if (state->monitor[monitor].use_mailbox_interface) {
+		/* We have 9 results, plus the overheads of start address and
+		 * length So 11 u32 to define
+		 */
+		u32 tmp[11];
+		int err;
+
+		tmp[0] = (u32)(uintptr_t)(state->monitor[monitor].base_address + watcher
+				+ BW_ATRANS_OFFSET);
+		tmp[1] = NUM_BUS_WATCHER_RESULTS;
+
+		err = rpi_firmware_property(state->firmware,
+					    RPI_FIRMWARE_GET_PERIPH_REG,
+					    tmp, sizeof(tmp));
+
+		if (err < 0 || tmp[1] != NUM_BUS_WATCHER_RESULTS)
+			dev_err_once(&state->dev->dev,
+				     "Failed to read bus watcher");
+		else
+			memcpy(results, &tmp[2],
+			       NUM_BUS_WATCHER_RESULTS * sizeof(u32));
+	} else {
+		int i;
+		void __iomem *addr = state->monitor[monitor].base_address
+				+ watcher + BW_ATRANS_OFFSET;
+		for (i = 0; i < NUM_BUS_WATCHER_RESULTS; i++, addr += 4)
+			*results++ = readl(addr);
+	}
+}
+
+static void set_monitor_control(int monitor, u32 set)
+{
+	if (state->monitor[monitor].use_mailbox_interface) {
+		u32 tmp[3] = {(u32)(uintptr_t)(state->monitor[monitor].base_address +
+				GEN_CTRL), 1, set};
+		int err = rpi_firmware_property(state->firmware,
+						RPI_FIRMWARE_SET_PERIPH_REG,
+						tmp, sizeof(tmp));
+
+		if (err < 0 || tmp[1] != 1)
+			dev_err_once(&state->dev->dev,
+				"Failed to set monitor control");
+	} else
+		write_reg(monitor, GEN_CTRL, set);
+}
+
+static void set_bus_watcher_control(int monitor, int watcher, u32 set)
+{
+	if (state->monitor[monitor].use_mailbox_interface) {
+		u32 tmp[3] = {(u32)(uintptr_t)(state->monitor[monitor].base_address +
+				    watcher), 1, set};
+		int err = rpi_firmware_property(state->firmware,
+						RPI_FIRMWARE_SET_PERIPH_REG,
+						tmp, sizeof(tmp));
+		if (err < 0 || tmp[1] != 1)
+			dev_err_once(&state->dev->dev,
+				"Failed to set bus watcher control");
+	} else
+		write_reg(monitor, watcher, set);
+}
+
+static void monitor(struct rpi_axiperf *state)
+{
+	int monitor, num_buses[NUM_MONITORS];
+
+	mutex_lock(&state->lock);
+
+	for (monitor = 0; monitor < NUM_MONITORS; monitor++) {
+		typeof(state->monitor[0]) *mon = &(state->monitor[monitor]);
+
+		/* Anything enabled? */
+		if (mon->bus_enabled == 0) {
+			/* No, disable all monitoring for this monitor */
+			set_monitor_control(monitor, GEN_CTL_RESET_BIT);
+		} else {
+			int i;
+
+			/* Find out how many busses we want to monitor, and
+			 * spread our 3 actual monitors over them
+			 */
+			num_buses[monitor] = hweight32(mon->bus_enabled);
+			num_buses[monitor] = min(num_buses[monitor],
+						 NUM_BUS_WATCHERS_PER_MONITOR);
+
+			for (i = 0; i < num_buses[monitor]; i++) {
+				int bus_control;
+
+				do {
+					mon->last_monitored++;
+					mon->last_monitored &= 0xf;
+				} while ((mon->bus_enabled &
+					 (1 << mon->last_monitored)) == 0);
+
+				mon->current_bus[i] = mon->last_monitored;
+
+				/* Reset the counters */
+				set_bus_watcher_control(monitor,
+							BW0_CTRL +
+							i*BW_PITCH,
+							BW_CTRL_RESET_BIT);
+
+				bus_control = BW_CTRL_ENABLE_BIT |
+						mon->current_bus[i];
+
+				if (mon->bus_filter) {
+					bus_control |=
+						BW_CTRL_ENABLE_ID_FILTER_BIT;
+					bus_control |=
+						((mon->bus_filter & 0x1f)
+						<< BW_CTRL_BUS_FILTER_SHIFT);
+				}
+
+				// Start capture
+				set_bus_watcher_control(monitor,
+							BW0_CTRL + i*BW_PITCH,
+							bus_control);
+			}
+		}
+
+		/* start monitoring */
+		set_monitor_control(monitor, GEN_CTL_ENABLE_BIT);
+	}
+
+	mutex_unlock(&state->lock);
+
+	msleep(state->sample_time);
+
+	/* Now read the results */
+
+	mutex_lock(&state->lock);
+	for (monitor = 0; monitor < NUM_MONITORS; monitor++) {
+		typeof(state->monitor[0]) *mon = &(state->monitor[monitor]);
+
+		/* Anything enabled? */
+		if (mon->bus_enabled == 0) {
+			/* No, disable all monitoring for this monitor */
+			set_monitor_control(monitor, 0);
+		} else {
+			int i;
+
+			for (i = 0; i < num_buses[monitor]; i++) {
+				int bus = mon->current_bus[i];
+
+				read_bus_watcher(monitor,
+					BW0_CTRL + i*BW_PITCH,
+					(u32 *)&mon->results[bus].results);
+			}
+		}
+	}
+	mutex_unlock(&state->lock);
+}
+
+static int monitor_thread(void *data)
+{
+	struct rpi_axiperf *state  = data;
+
+	while (1) {
+		monitor(state);
+
+		if (kthread_should_stop())
+			return 0;
+	}
+	return 0;
+}
+
+static ssize_t myreader(struct file *fp, char __user *user_buffer,
+			size_t count, loff_t *position)
+{
+#define INIT_BUFF_SIZE 2048
+
+	int i;
+	int idx = (int)(uintptr_t)(fp->private_data);
+	int num_buses, cnt;
+	char *string_buffer;
+	int buff_size = INIT_BUFF_SIZE;
+	char *p;
+	typeof(state->monitor[0]) *mon = &(state->monitor[idx]);
+
+	if (idx < 0 || idx > NUM_MONITORS)
+		idx = 0;
+
+	num_buses = idx == SYSTEM_MONITOR ? num_system_buses : num_vpu_buses;
+
+	string_buffer = kmalloc(buff_size, GFP_KERNEL);
+
+	if (!string_buffer) {
+		dev_err(&state->dev->dev,
+				"Failed temporary string allocation\n");
+		return 0;
+	}
+
+	p = string_buffer;
+
+	mutex_lock(&state->lock);
+
+	if (mon->bus_filter) {
+		int filt = min(mon->bus_filter & 0x1f, num_bus_filters);
+
+		cnt = snprintf(p, buff_size,
+			       "\nMonitoring transactions from %s only\n",
+			       bus_filter_strings[filt]);
+		p += cnt;
+		buff_size -= cnt;
+	}
+
+	cnt = snprintf(p, buff_size, "     Bus   |    Atrans    Atwait      AMax    Wtrans    Wtwait      WMax    Rtrans    Rtwait      RMax\n"
+				     "======================================================================================================\n");
+
+	if (cnt >= buff_size)
+		goto done;
+
+	p += cnt;
+	buff_size -= cnt;
+
+	for (i = 0; i < num_buses; i++) {
+		if (mon->bus_enabled & (1 << i)) {
+#define DIVIDER (1024)
+			typeof(mon->results[0]) *res = &(mon->results[i]);
+
+			cnt = snprintf(p, buff_size,
+					"%10s | %8uK %8uK %8uK %8uK %8uK %8uK %8uK %8uK %8uK\n",
+					idx == SYSTEM_MONITOR ?
+						system_bus_string[i] :
+						vpu_bus_string[i],
+					res->atrans/DIVIDER,
+					res->atwait/DIVIDER,
+					res->amax/DIVIDER,
+					res->wtrans/DIVIDER,
+					res->wtwait/DIVIDER,
+					res->wmax/DIVIDER,
+					res->rtrans/DIVIDER,
+					res->rtwait/DIVIDER,
+					res->rmax/DIVIDER
+					);
+			if (cnt >= buff_size)
+				goto done;
+
+			p += cnt;
+			buff_size -= cnt;
+		}
+	}
+
+	mutex_unlock(&state->lock);
+
+done:
+
+	/* did the last string entry exceeed our buffer size? ie out of string
+	 * buffer space. Null terminate, use what we have.
+	 */
+	if (cnt >= buff_size) {
+		buff_size = 0;
+		string_buffer[INIT_BUFF_SIZE] = 0;
+	}
+
+	cnt = simple_read_from_buffer(user_buffer, count, position,
+				      string_buffer,
+				      INIT_BUFF_SIZE - buff_size);
+
+	kfree(string_buffer);
+
+	return cnt;
+}
+
+static ssize_t mywriter(struct file *fp, const char __user *user_buffer,
+			size_t count, loff_t *position)
+{
+	int idx = (int)(uintptr_t)(fp->private_data);
+
+	if (idx < 0 || idx > NUM_MONITORS)
+		idx = 0;
+
+	/* At the moment, this does nothing, but in the future it could be
+	 * used to reset counters etc
+	 */
+	return count;
+}
+
+static const struct file_operations fops_debug = {
+	.read = myreader,
+	.write = mywriter,
+	.open = simple_open
+};
+
+static int rpi_axiperf_probe(struct platform_device *pdev)
+{
+	int ret = 0, i;
+	struct device *dev = &pdev->dev;
+	struct device_node *np = dev->of_node;
+	struct device_node *fw_node;
+
+	state = kzalloc(sizeof(struct rpi_axiperf), GFP_KERNEL);
+	if (!state)
+		return -ENOMEM;
+
+	/* Get the firmware handle for future rpi-firmware-xxx calls */
+	fw_node = of_parse_phandle(np, "firmware", 0);
+	if (!fw_node) {
+		dev_err(dev, "Missing firmware node\n");
+		return -ENOENT;
+	}
+
+	state->firmware = rpi_firmware_get(fw_node);
+	if (!state->firmware)
+		return -EPROBE_DEFER;
+
+	/* Special case for the VPU monitor, we must use the mailbox interface
+	 * as it is not accessible from the ARM address space.
+	 */
+	state->monitor[VPU_MONITOR].use_mailbox_interface = 1;
+	state->monitor[SYSTEM_MONITOR].use_mailbox_interface = 0;
+
+	for (i = 0; i < NUM_MONITORS; i++) {
+		if (state->monitor[i].use_mailbox_interface) {
+			 of_property_read_u32_index(np, "reg", i*2,
+				(u32 *)(&state->monitor[i].base_address));
+		} else {
+			struct resource *resource =
+				platform_get_resource(pdev, IORESOURCE_MEM, i);
+
+			state->monitor[i].base_address =
+				devm_ioremap_resource(&pdev->dev, resource);
+		}
+
+		if (IS_ERR(state->monitor[i].base_address))
+			return PTR_ERR(state->monitor[i].base_address);
+
+		/* Enable all buses by default */
+		state->monitor[i].bus_enabled = 0xffff;
+	}
+
+	state->dev = pdev;
+	platform_set_drvdata(pdev, state);
+
+	state->sample_time = DEFAULT_SAMPLE_TIME;
+
+	/* Set up all the debugfs stuff */
+	state->root_folder = debugfs_create_dir(KBUILD_MODNAME, NULL);
+
+	for (i = 0; i < NUM_MONITORS; i++) {
+		state->monitor[i].debugfs_entry =
+			debugfs_create_dir(monitor_name[i], state->root_folder);
+		if (IS_ERR(state->monitor[i].debugfs_entry))
+			state->monitor[i].debugfs_entry = NULL;
+
+		debugfs_create_file("data", 0444,
+				    state->monitor[i].debugfs_entry,
+				    (void *)(uintptr_t)i, &fops_debug);
+		debugfs_create_u32("enable", 0644,
+				   state->monitor[i].debugfs_entry,
+				   &state->monitor[i].bus_enabled);
+		debugfs_create_u32("filter", 0644,
+				   state->monitor[i].debugfs_entry,
+				   &state->monitor[i].bus_filter);
+		debugfs_create_u32("sample_time", 0644,
+				   state->monitor[i].debugfs_entry,
+				   &state->sample_time);
+	}
+
+	mutex_init(&state->lock);
+
+	state->monitor_thread = kthread_run(monitor_thread, state,
+					    "rpi-axiperfmon");
+
+	return ret;
+
+}
+
+static int rpi_axiperf_remove(struct platform_device *dev)
+{
+	int ret = 0;
+
+	kthread_stop(state->monitor_thread);
+
+	debugfs_remove_recursive(state->root_folder);
+	state->root_folder = NULL;
+
+	return ret;
+}
+
+static const struct of_device_id rpi_axiperf_match[] = {
+	{
+		.compatible = "brcm,bcm2835-axiperf",
+	},
+	{},
+};
+MODULE_DEVICE_TABLE(of, rpi_axiperf_match);
+
+static struct platform_driver rpi_axiperf_driver  = {
+	.probe =	rpi_axiperf_probe,
+	.remove =	rpi_axiperf_remove,
+	.driver = {
+		.name   = "rpi-bcm2835-axiperf",
+		.of_match_table = of_match_ptr(rpi_axiperf_match),
+	},
+};
+
+module_platform_driver(rpi_axiperf_driver);
+
+/* Module information */
+MODULE_AUTHOR("James Hughes <james.hughes@raspberrypi.org>");
+MODULE_DESCRIPTION("RPI AXI Performance monitor driver");
+MODULE_LICENSE("GPL");
+
diff -Nur --no-dereference linux-5.10.52-orig/drivers/pinctrl/bcm/pinctrl-bcm2835.c linux-5.10.52-v7l+/drivers/pinctrl/bcm/pinctrl-bcm2835.c
--- linux-5.10.52-orig/drivers/pinctrl/bcm/pinctrl-bcm2835.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/pinctrl/bcm/pinctrl-bcm2835.c	2021-07-25 16:46:19.858057980 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:365 @
 	.get = bcm2835_gpio_get,
 	.set = bcm2835_gpio_set,
 	.set_config = gpiochip_generic_config,
-	.base = -1,
+	.base = 0,
 	.ngpio = BCM2835_NUM_GPIOS,
 	.can_sleep = false,
 };
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:381 @
 	.get = bcm2835_gpio_get,
 	.set = bcm2835_gpio_set,
 	.set_config = gpiochip_generic_config,
-	.base = -1,
+	.base = 0,
 	.ngpio = BCM2711_NUM_GPIOS,
 	.can_sleep = false,
 };
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1247 @
 		raw_spin_lock_init(&pc->irq_lock[i]);
 	}
 
+	pc->pctl_desc = *pdata->pctl_desc;
+	pc->pctl_dev = devm_pinctrl_register(dev, &pc->pctl_desc, pc);
+	if (IS_ERR(pc->pctl_dev)) {
+		gpiochip_remove(&pc->gpio_chip);
+		return PTR_ERR(pc->pctl_dev);
+	}
+
+	pc->gpio_range = *pdata->gpio_range;
+	pc->gpio_range.base = pc->gpio_chip.base;
+	pc->gpio_range.gc = &pc->gpio_chip;
+	pinctrl_add_gpio_range(pc->pctl_dev, &pc->gpio_range);
+
 	girq = &pc->gpio_chip.irq;
 	girq->chip = &bcm2835_gpio_irq_chip;
 	girq->parent_handler = bcm2835_gpio_irq_handler;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1266 @
 	girq->parents = devm_kcalloc(dev, BCM2835_NUM_IRQS,
 				     sizeof(*girq->parents),
 				     GFP_KERNEL);
-	if (!girq->parents)
+	if (!girq->parents) {
+		pinctrl_remove_gpio_range(pc->pctl_dev, &pc->gpio_range);
 		return -ENOMEM;
+	}
 
 	if (is_7211) {
 		pc->wake_irq = devm_kcalloc(dev, BCM2835_NUM_IRQS,
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1291 @
 		char *name;
 
 		girq->parents[i] = irq_of_parse_and_map(np, i);
-		if (!is_7211)
+		if (!is_7211) {
+			if (!girq->parents[i]) {
+				girq->num_parents = i;
+				break;
+			}
 			continue;
-
+		}
 		/* Skip over the all banks interrupts */
 		pc->wake_irq[i] = irq_of_parse_and_map(np, i +
 						       BCM2835_NUM_IRQS + 1);
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1321 @
 	girq->default_type = IRQ_TYPE_NONE;
 	girq->handler = handle_level_irq;
 
-	err = gpiochip_add_data(&pc->gpio_chip, pc);
+	err = devm_gpiochip_add_data(dev, &pc->gpio_chip, pc);
 	if (err) {
 		dev_err(dev, "could not add GPIO chip\n");
+		pinctrl_remove_gpio_range(pc->pctl_dev, &pc->gpio_range);
 		return err;
 	}
 
-	pc->pctl_desc = *pdata->pctl_desc;
-	pc->pctl_dev = devm_pinctrl_register(dev, &pc->pctl_desc, pc);
-	if (IS_ERR(pc->pctl_dev)) {
-		gpiochip_remove(&pc->gpio_chip);
-		return PTR_ERR(pc->pctl_dev);
-	}
-
-	pc->gpio_range = *pdata->gpio_range;
-	pc->gpio_range.base = pc->gpio_chip.base;
-	pc->gpio_range.gc = &pc->gpio_chip;
-	pinctrl_add_gpio_range(pc->pctl_dev, &pc->gpio_range);
-
 	return 0;
 }
 
diff -Nur --no-dereference linux-5.10.52-orig/drivers/power/reset/gpio-poweroff.c linux-5.10.52-v7l+/drivers/power/reset/gpio-poweroff.c
--- linux-5.10.52-orig/drivers/power/reset/gpio-poweroff.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/power/reset/gpio-poweroff.c	2021-07-25 16:46:20.758042891 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:27 @
 static u32 timeout = DEFAULT_TIMEOUT_MS;
 static u32 active_delay = 100;
 static u32 inactive_delay = 100;
+static void (*old_power_off)(void);
 
 static void gpio_poweroff_do_poweroff(void)
 {
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:47 @
 	/* give it some time */
 	mdelay(timeout);
 
+	if (old_power_off)
+		old_power_off();
+
 	WARN_ON(1);
 }
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:57 @
 {
 	bool input = false;
 	enum gpiod_flags flags;
+	bool force = false;
+	bool export = false;
 
 	/* If a pm_power_off function has already been added, leave it alone */
-	if (pm_power_off != NULL) {
+	force = of_property_read_bool(pdev->dev.of_node, "force");
+	if (!force && (pm_power_off != NULL)) {
 		dev_err(&pdev->dev,
 			"%s: pm_power_off function already registered\n",
 		       __func__);
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:84 @
 	if (IS_ERR(reset_gpio))
 		return PTR_ERR(reset_gpio);
 
+	export = of_property_read_bool(pdev->dev.of_node, "export");
+	if (export) {
+		gpiod_export(reset_gpio, false);
+		gpiod_export_link(&pdev->dev, "poweroff-gpio", reset_gpio);
+	}
+
+	old_power_off = pm_power_off;
 	pm_power_off = &gpio_poweroff_do_poweroff;
 	return 0;
 }
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:98 @
 static int gpio_poweroff_remove(struct platform_device *pdev)
 {
 	if (pm_power_off == &gpio_poweroff_do_poweroff)
-		pm_power_off = NULL;
+		pm_power_off = old_power_off;
+
+	gpiod_unexport(reset_gpio);
 
 	return 0;
 }
diff -Nur --no-dereference linux-5.10.52-orig/drivers/power/supply/Kconfig linux-5.10.52-v7l+/drivers/power/supply/Kconfig
--- linux-5.10.52-orig/drivers/power/supply/Kconfig	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/power/supply/Kconfig	2021-07-25 16:46:20.768042723 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:31 @
 	  Say 'Y' here if you want power supplies to
 	  have hwmon sysfs interface too.
 
+config RPI_POE_POWER
+	tristate "Raspberry Pi PoE+ HAT power supply driver"
+	depends on RASPBERRYPI_FIRMWARE
+	help
+	  Say Y here to enable support for Raspberry Pi PoE+ (Power over Ethernet
+	  Plus) HAT current measurement.
 
 config PDA_POWER
 	tristate "Generic PDA/phone power driver"
diff -Nur --no-dereference linux-5.10.52-orig/drivers/power/supply/Makefile linux-5.10.52-v7l+/drivers/power/supply/Makefile
--- linux-5.10.52-orig/drivers/power/supply/Makefile	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/power/supply/Makefile	2021-07-25 16:46:20.768042723 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:12 @
 obj-$(CONFIG_POWER_SUPPLY_HWMON) += power_supply_hwmon.o
 obj-$(CONFIG_GENERIC_ADC_BATTERY)	+= generic-adc-battery.o
 
+obj-$(CONFIG_RPI_POE_POWER)	+= rpi_poe_power.o
 obj-$(CONFIG_PDA_POWER)		+= pda_power.o
 obj-$(CONFIG_APM_POWER)		+= apm_power.o
 obj-$(CONFIG_AXP20X_POWER)	+= axp20x_usb_power.o
diff -Nur --no-dereference linux-5.10.52-orig/drivers/power/supply/rpi_poe_power.c linux-5.10.52-v7l+/drivers/power/supply/rpi_poe_power.c
--- linux-5.10.52-orig/drivers/power/supply/rpi_poe_power.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/power/supply/rpi_poe_power.c	2021-07-25 16:46:20.858041215 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * rpi-poe-power.c - Raspberry Pi PoE+ HAT power supply driver.
+ *
+ * Copyright (C) 2019 Raspberry Pi (Trading) Ltd.
+ * Based on axp20x_ac_power.c by Quentin Schulz <quentin.schulz@free-electrons.com>
+ *
+ * Author: Serge Schneider <serge@raspberrypi.org>
+ */
+
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+#include <soc/bcm2835/raspberrypi-firmware.h>
+
+#define RPI_POE_ADC_REG			0x2
+#define RPI_POE_FLAG_REG		0x4
+
+#define RPI_POE_FLAG_AT			BIT(0)
+#define RPI_POE_FLAG_OC			BIT(1)
+
+#define RPI_POE_CURRENT_AF_MAX	(2500 * 1000)
+#define RPI_POE_CURRENT_AT_MAX	(5000 * 1000)
+
+#define DRVNAME "rpi-poe-power-supply"
+
+struct rpi_poe_power_supply_ctx {
+	struct power_supply *supply;
+	struct rpi_firmware *fw;
+};
+
+struct fw_tag_data_s {
+	u32 reg;
+	u32 val;
+	u32 ret;
+};
+
+static int write_reg(struct rpi_firmware *fw, u32 reg, u32 *val)
+{
+	struct fw_tag_data_s fw_tag_data = {
+		.reg = reg,
+		.val = *val
+	};
+	int ret;
+
+	ret = rpi_firmware_property(fw, RPI_FIRMWARE_SET_POE_HAT_VAL,
+				    &fw_tag_data, sizeof(fw_tag_data));
+	if (ret)
+		return ret;
+	else if (fw_tag_data.ret)
+		return -EIO;
+	return 0;
+}
+
+static int read_reg(struct rpi_firmware *fw, u32 reg, u32 *val)
+{
+	struct fw_tag_data_s fw_tag_data = {
+		.reg = reg,
+		.val = *val
+	};
+	int ret;
+
+	ret = rpi_firmware_property(fw, RPI_FIRMWARE_GET_POE_HAT_VAL,
+				    &fw_tag_data, sizeof(fw_tag_data));
+	if (ret)
+		return ret;
+	else if (fw_tag_data.ret)
+		return -EIO;
+
+	*val = fw_tag_data.val;
+	return 0;
+}
+
+static int rpi_poe_power_supply_get_property(struct power_supply *psy,
+					enum power_supply_property psp,
+					union power_supply_propval *r_val)
+{
+	struct rpi_poe_power_supply_ctx *ctx = power_supply_get_drvdata(psy);
+	int ret;
+	unsigned int val = 0;
+
+	switch (psp) {
+	case POWER_SUPPLY_PROP_HEALTH:
+		ret = read_reg(ctx->fw, RPI_POE_FLAG_REG, &val);
+		if (ret)
+			return ret;
+
+		if (val & RPI_POE_FLAG_OC) {
+			r_val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
+			val = RPI_POE_FLAG_OC;
+			ret = write_reg(ctx->fw, RPI_POE_FLAG_REG, &val);
+			if (ret)
+				return ret;
+			return 0;
+		}
+
+		r_val->intval = POWER_SUPPLY_HEALTH_GOOD;
+		return 0;
+
+	case POWER_SUPPLY_PROP_ONLINE:
+		ret = read_reg(ctx->fw, RPI_POE_ADC_REG, &val);
+		if (ret)
+			return ret;
+
+		r_val->intval = (val > 5);
+		return 0;
+
+	case POWER_SUPPLY_PROP_CURRENT_AVG:
+		val = 50;
+		ret = read_reg(ctx->fw, RPI_POE_ADC_REG, &val);
+		if (ret)
+			return ret;
+		val = (val * 3300)/9821;
+		r_val->intval = val * 1000;
+		return 0;
+
+	case POWER_SUPPLY_PROP_CURRENT_NOW:
+		ret = read_reg(ctx->fw, RPI_POE_ADC_REG, &val);
+		if (ret)
+			return ret;
+		val = (val * 3300)/9821;
+		r_val->intval = val * 1000;
+		return 0;
+
+	case POWER_SUPPLY_PROP_CURRENT_MAX:
+		ret = read_reg(ctx->fw, RPI_POE_FLAG_REG, &val);
+		if (ret)
+			return ret;
+
+		if (val & RPI_POE_FLAG_AT) {
+			r_val->intval = RPI_POE_CURRENT_AT_MAX;
+			return 0;
+		}
+		r_val->intval = RPI_POE_CURRENT_AF_MAX;
+		return 0;
+
+	default:
+		return -EINVAL;
+	}
+
+	return -EINVAL;
+}
+
+static enum power_supply_property rpi_poe_power_supply_properties[] = {
+	POWER_SUPPLY_PROP_HEALTH,
+	POWER_SUPPLY_PROP_ONLINE,
+	POWER_SUPPLY_PROP_CURRENT_AVG,
+	POWER_SUPPLY_PROP_CURRENT_NOW,
+	POWER_SUPPLY_PROP_CURRENT_MAX,
+};
+
+static const struct power_supply_desc rpi_poe_power_supply_desc = {
+	.name = "rpi-poe",
+	.type = POWER_SUPPLY_TYPE_MAINS,
+	.properties = rpi_poe_power_supply_properties,
+	.num_properties = ARRAY_SIZE(rpi_poe_power_supply_properties),
+	.get_property = rpi_poe_power_supply_get_property,
+};
+
+static int rpi_poe_power_supply_probe(struct platform_device *pdev)
+{
+	struct power_supply_config psy_cfg = {};
+	struct rpi_poe_power_supply_ctx *ctx;
+	struct device_node *fw_node;
+	u32 revision;
+
+	if (!of_device_is_available(pdev->dev.of_node))
+		return -ENODEV;
+
+	fw_node = of_parse_phandle(pdev->dev.of_node, "firmware", 0);
+	if (!fw_node) {
+		dev_err(&pdev->dev, "Missing firmware node\n");
+		return -ENOENT;
+	}
+
+	ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
+	if (!ctx)
+		return -ENOMEM;
+
+	ctx->fw = rpi_firmware_get(fw_node);
+	if (!ctx->fw)
+		return -EPROBE_DEFER;
+	if (rpi_firmware_property(ctx->fw,
+			RPI_FIRMWARE_GET_FIRMWARE_REVISION,
+			&revision, sizeof(revision))) {
+		dev_err(&pdev->dev, "Failed to get firmware revision\n");
+		return -ENOENT;
+	}
+	if (revision < 0x60af72e8) {
+		dev_err(&pdev->dev, "Unsupported firmware\n");
+		return -ENOENT;
+	}
+	platform_set_drvdata(pdev, ctx);
+
+	psy_cfg.of_node = pdev->dev.of_node;
+	psy_cfg.drv_data = ctx;
+
+	ctx->supply = devm_power_supply_register(&pdev->dev,
+						   &rpi_poe_power_supply_desc,
+						   &psy_cfg);
+	if (IS_ERR(ctx->supply))
+		return PTR_ERR(ctx->supply);
+
+	return 0;
+}
+
+static const struct of_device_id of_rpi_poe_power_supply_match[] = {
+	{ .compatible = "raspberrypi,rpi-poe-power-supply", },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, of_rpi_poe_power_supply_match);
+
+static struct platform_driver rpi_poe_power_supply_driver = {
+	.probe = rpi_poe_power_supply_probe,
+	.driver = {
+		.name = DRVNAME,
+		.of_match_table = of_rpi_poe_power_supply_match
+	},
+};
+
+module_platform_driver(rpi_poe_power_supply_driver);
+
+MODULE_AUTHOR("Serge Schneider <serge@raspberrypi.org>");
+MODULE_ALIAS("platform:" DRVNAME);
+MODULE_DESCRIPTION("Raspberry Pi PoE+ HAT power supply driver");
+MODULE_LICENSE("GPL");
diff -Nur --no-dereference linux-5.10.52-orig/drivers/pps/clients/pps-gpio.c linux-5.10.52-v7l+/drivers/pps/clients/pps-gpio.c
--- linux-5.10.52-orig/drivers/pps/clients/pps-gpio.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/pps/clients/pps-gpio.c	2021-07-25 16:46:20.888040712 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:148 @
 
 	if (of_property_read_bool(np, "assert-falling-edge"))
 		data->assert_falling_edge = true;
+        if (of_property_read_bool(np, "capture-clear"))
+                data->capture_clear = true;
 	return 0;
 }
 
diff -Nur --no-dereference linux-5.10.52-orig/drivers/rtc/rtc-pcf2123.c linux-5.10.52-v7l+/drivers/rtc/rtc-pcf2123.c
--- linux-5.10.52-orig/drivers/rtc/rtc-pcf2123.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/rtc/rtc-pcf2123.c	2021-07-25 16:46:21.348033000 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:468 @
 MODULE_AUTHOR("Chris Verges <chrisv@cyberswitching.com>");
 MODULE_DESCRIPTION("NXP PCF2123 RTC driver");
 MODULE_LICENSE("GPL");
+MODULE_ALIAS("spi:rtc-pcf2123");
diff -Nur --no-dereference linux-5.10.52-orig/drivers/rtc/rtc-pcf8523.c linux-5.10.52-v7l+/drivers/rtc/rtc-pcf8523.c
--- linux-5.10.52-orig/drivers/rtc/rtc-pcf8523.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/rtc/rtc-pcf8523.c	2021-07-25 16:46:21.348033000 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:208 @
 	if (err < 0)
 		return err;
 
-	if (regs[0] & REG_SECONDS_OS)
-		return -EINVAL;
+	if (regs[0] & REG_SECONDS_OS) {
+		/*
+		 * If the oscillator was stopped, try to clear the flag. Upon
+		 * power-up the flag is always set, but if we cannot clear it
+		 * the oscillator isn't running properly for some reason. The
+		 * sensible thing therefore is to return an error, signalling
+		 * that the clock cannot be assumed to be correct.
+		 */
+
+		regs[0] &= ~REG_SECONDS_OS;
+
+		err = pcf8523_write(client, REG_SECONDS, regs[0]);
+		if (err < 0)
+			return err;
+
+		err = pcf8523_read(client, REG_SECONDS, &regs[0]);
+		if (err < 0)
+			return err;
+
+		if (regs[0] & REG_SECONDS_OS)
+			return -EAGAIN;
+	}
 
 	tm->tm_sec = bcd2bin(regs[0] & 0x7f);
 	tm->tm_min = bcd2bin(regs[1] & 0x7f);
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:265 @
 		return err;
 
 	regs[0] = REG_SECONDS;
-	/* This will purposely overwrite REG_SECONDS_OS */
 	regs[1] = bin2bcd(tm->tm_sec);
 	regs[2] = bin2bcd(tm->tm_min);
 	regs[3] = bin2bcd(tm->tm_hour);
diff -Nur --no-dereference linux-5.10.52-orig/drivers/rtc/rtc-rv3028.c linux-5.10.52-v7l+/drivers/rtc/rtc-rv3028.c
--- linux-5.10.52-orig/drivers/rtc/rtc-rv3028.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/rtc/rtc-rv3028.c	2021-07-25 16:46:21.378032497 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:83 @
 
 #define RV3028_BACKUP_TCE		BIT(5)
 #define RV3028_BACKUP_TCR_MASK		GENMASK(1,0)
+#define RV3028_BACKUP_BSM_MASK		0x0C
 
 #define OFFSET_STEP_PPT			953674
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:793 @
 	struct rv3028_data *rv3028;
 	int ret, status;
 	u32 ohms;
+	u8 bsm;
 	struct nvmem_config nvmem_cfg = {
 		.name = "rv3028_nvram",
 		.word_size = 1,
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:865 @
 	if (ret)
 		return ret;
 
+	/* setup backup switchover mode */
+	if (!device_property_read_u8(&client->dev, "backup-switchover-mode",
+				     &bsm))  {
+		if (bsm <= 3) {
+			ret = regmap_update_bits(rv3028->regmap, RV3028_BACKUP,
+				RV3028_BACKUP_BSM_MASK,
+				(bsm & 0x03) << 2);
+
+			if (ret)
+				return ret;
+		} else {
+			dev_warn(&client->dev, "invalid backup switchover mode value\n");
+		}
+	}
+
 	/* setup trickle charger */
 	if (!device_property_read_u32(&client->dev, "trickle-resistor-ohms",
 				      &ohms)) {
diff -Nur --no-dereference linux-5.10.52-orig/drivers/soc/bcm/bcm2835-power.c linux-5.10.52-v7l+/drivers/soc/bcm/bcm2835-power.c
--- linux-5.10.52-orig/drivers/soc/bcm/bcm2835-power.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/soc/bcm/bcm2835-power.c	2021-07-25 16:46:24.297983543 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:146 @
 	/* AXI Async bridge registers. */
 	void __iomem		*asb;
 
+	bool is_2711;
+
 	struct genpd_onecell_data pd_xlate;
 	struct bcm2835_power_domain domains[BCM2835_POWER_DOMAIN_COUNT];
 	struct reset_controller_dev reset;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:197 @
 {
 	struct bcm2835_power *power = pd->power;
 
+	/* 2711 has no power domains above the reset controller. */
+	if (power->is_2711)
+		return 0;
+
 	/* Enable functional isolation */
 	PM_WRITE(pm_reg, PM_READ(pm_reg) & ~PM_ISFUNC);
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:222 @
 	int inrush;
 	bool powok;
 
+	/* 2711 has no power domains above the reset controller. */
+	if (power->is_2711)
+		return 0;
+
 	/* If it was already powered on by the fw, leave it that way. */
 	if (PM_READ(pm_reg) & PM_POWUP)
 		return 0;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:640 @
 	power->base = pm->base;
 	power->asb = pm->asb;
 
+	/* 2711 hack: the new RPiVid ASB took over V3D, which is our
+	 * only consumer of this driver so far.  The old ASB seems to
+	 * still be present with ISP and H264 bits but no V3D, but I
+	 * don't know if that's real or not.  The V3D is in the same
+	 * place in the new ASB as the old one, so just poke the new
+	 * one for now.
+	 */
+	if (pm->rpivid_asb) {
+		power->asb = pm->rpivid_asb;
+		power->is_2711 = true;
+	}
+
 	id = ASB_READ(ASB_AXI_BRDG_ID);
 	if (id != 0x62726467 /* "BRDG" */) {
 		dev_err(dev, "ASB register ID returned 0x%08x\n", id);
diff -Nur --no-dereference linux-5.10.52-orig/drivers/soc/bcm/Kconfig linux-5.10.52-v7l+/drivers/soc/bcm/Kconfig
--- linux-5.10.52-orig/drivers/soc/bcm/Kconfig	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/soc/bcm/Kconfig	2021-07-25 16:46:24.297983543 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:20 @
 	bool "Raspberry Pi power domain driver"
 	depends on ARCH_BCM2835 || (COMPILE_TEST && OF)
 	depends on RASPBERRYPI_FIRMWARE=y
+	depends on PM
 	select PM_GENERIC_DOMAINS if PM
 	help
 	  This enables support for the RPi power domains which can be enabled
diff -Nur --no-dereference linux-5.10.52-orig/drivers/spi/spi-bcm2835.c linux-5.10.52-v7l+/drivers/spi/spi-bcm2835.c
--- linux-5.10.52-orig/drivers/spi/spi-bcm2835.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/spi/spi-bcm2835.c	2021-07-25 16:46:24.537979519 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:31 @
 #include <linux/gpio/consumer.h>
 #include <linux/gpio/machine.h> /* FIXME: using chip internals */
 #include <linux/gpio/driver.h> /* FIXME: using chip internals */
+#include <linux/of_gpio.h>
 #include <linux/of_irq.h>
 #include <linux/spi/spi.h>
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:381 @
 	if (bs->tx_len && cs & BCM2835_SPI_CS_DONE)
 		bcm2835_wr_fifo_blind(bs, BCM2835_SPI_FIFO_SIZE);
 
+	/* check if we got interrupt enabled */
+	if (!(bcm2835_rd(bs, BCM2835_SPI_CS) & BCM2835_SPI_CS_INTR))
+		return IRQ_NONE;
+
 	/* Read as many bytes as possible from FIFO */
 	bcm2835_rd_fifo(bs);
 	/* Write as many bytes as possible to FIFO */
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1094 @
 	unsigned long hz_per_byte, byte_limit;
 	u32 cs = bs->prepare_cs[spi->chip_select];
 
+	if (unlikely(!tfr->len)) {
+		static int warned;
+
+		if (!warned)
+			dev_warn(&spi->dev,
+				 "zero-length SPI transfer ignored\n");
+		warned = 1;
+		return 0;
+	}
+
 	/* set clock */
 	spi_hz = tfr->speed_hz;
 	clk_hz = clk_get_rate(bs->clk);
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1302 @
 	struct bcm2835_spi *bs;
 	int err;
 
+	if (of_gpio_named_count(pdev->dev.of_node, "cs-gpios") >
+	    BCM2835_SPI_NUM_CS)
+		return dev_err_probe(&pdev->dev, -EINVAL,
+				     "too many chip selects\n");
+
 	ctlr = devm_spi_alloc_master(&pdev->dev, ALIGN(sizeof(*bs),
 						  dma_get_cache_alignment()));
 	if (!ctlr)
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1350 @
 	bcm2835_wr(bs, BCM2835_SPI_CS,
 		   BCM2835_SPI_CS_CLEAR_RX | BCM2835_SPI_CS_CLEAR_TX);
 
-	err = devm_request_irq(&pdev->dev, bs->irq, bcm2835_spi_interrupt, 0,
+	err = devm_request_irq(&pdev->dev, bs->irq, bcm2835_spi_interrupt,
+			       IRQF_SHARED,
 			       dev_name(&pdev->dev), bs);
 	if (err) {
 		dev_err(&pdev->dev, "could not request IRQ: %d\n", err);
diff -Nur --no-dereference linux-5.10.52-orig/drivers/spi/spi.c linux-5.10.52-v7l+/drivers/spi/spi.c
--- linux-5.10.52-orig/drivers/spi/spi.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/spi/spi.c	2021-07-25 16:46:24.687977005 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:3341 @
  */
 int spi_setup(struct spi_device *spi)
 {
+	struct spi_controller *ctlr = spi->controller;
 	unsigned	bad_bits, ugly_bits;
 	int		status;
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:3359 @
 		(SPI_TX_DUAL | SPI_TX_QUAD | SPI_TX_OCTAL |
 		 SPI_RX_DUAL | SPI_RX_QUAD | SPI_RX_OCTAL)))
 		return -EINVAL;
+
+	if (ctlr->use_gpio_descriptors && ctlr->cs_gpiods &&
+	    ctlr->cs_gpiods[spi->chip_select] && !(spi->mode & SPI_CS_HIGH)) {
+		dev_dbg(&spi->dev,
+			"setup: forcing CS_HIGH (use_gpio_descriptors)\n");
+		spi->mode |= SPI_CS_HIGH;
+	}
+
 	/* help drivers fail *cleanly* when they need options
 	 * that aren't supported with their current controller
 	 * SPI_CS_WORD has a fallback software implementation,
diff -Nur --no-dereference linux-5.10.52-orig/drivers/spi/spidev.c linux-5.10.52-v7l+/drivers/spi/spidev.c
--- linux-5.10.52-orig/drivers/spi/spidev.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/spi/spidev.c	2021-07-25 16:46:24.687977005 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:405 @
 		else
 			retval = get_user(tmp, (u32 __user *)arg);
 		if (retval == 0) {
-			struct spi_controller *ctlr = spi->controller;
 			u32	save = spi->mode;
 
 			if (tmp & ~SPI_MODE_MASK) {
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:412 @
 				break;
 			}
 
-			if (ctlr->use_gpio_descriptors && ctlr->cs_gpiods &&
-			    ctlr->cs_gpiods[spi->chip_select])
-				tmp |= SPI_CS_HIGH;
-
 			tmp |= spi->mode & ~SPI_MODE_MASK;
 			spi->mode = (u16)tmp;
 			retval = spi_setup(spi);
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:735 @
 	 * compatible string, it is a Linux implementation thing
 	 * rather than a description of the hardware.
 	 */
-	WARN(spi->dev.of_node &&
+	WARN(0 && spi->dev.of_node &&
 	     of_device_is_compatible(spi->dev.of_node, "spidev"),
 	     "%pOF: buggy DT: spidev listed directly in DT\n", spi->dev.of_node);
 
diff -Nur --no-dereference linux-5.10.52-orig/drivers/staging/fbtft/fb_st7735r.c linux-5.10.52-v7l+/drivers/staging/fbtft/fb_st7735r.c
--- linux-5.10.52-orig/drivers/staging/fbtft/fb_st7735r.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/staging/fbtft/fb_st7735r.c	2021-07-25 16:46:24.937972813 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:19 @
 #define DEFAULT_GAMMA   "0F 1A 0F 18 2F 28 20 22 1F 1B 23 37 00 07 02 10\n" \
 			"0F 1B 0F 17 33 2C 29 2E 30 30 39 3F 00 07 03 10"
 
+#define ADAFRUIT18_GAMMA \
+			"02 1c 07 12 37 32 29 2d 29 25 2B 39 00 01 03 10\n" \
+			"03 1d 07 06 2E 2C 29 2D 2E 2E 37 3F 00 00 02 10"
+
 static const s16 default_init_sequence[] = {
 	-1, MIPI_DCS_SOFT_RESET,
 	-2, 150,                               /* delay */
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:101 @
 	write_reg(par, MIPI_DCS_WRITE_MEMORY_START);
 }
 
+static void adafruit18_green_tab_set_addr_win(struct fbtft_par *par,
+					      int xs, int ys, int xe, int ye)
+{
+	write_reg(par, 0x2A, 0, xs + 2, 0, xe + 2);
+	write_reg(par, 0x2B, 0, ys + 1, 0, ye + 1);
+	write_reg(par, 0x2C);
+}
+
 #define MY BIT(7)
 #define MX BIT(6)
 #define MV BIT(5)
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:189 @
 	},
 };
 
-FBTFT_REGISTER_DRIVER(DRVNAME, "sitronix,st7735r", &display);
+int variant_adafruit18(struct fbtft_display *display)
+{
+	display->gamma = ADAFRUIT18_GAMMA;
+	return 0;
+}
+
+int variant_adafruit18_green(struct fbtft_display *display)
+{
+	display->gamma = ADAFRUIT18_GAMMA;
+	display->fbtftops.set_addr_win = adafruit18_green_tab_set_addr_win;
+	return 0;
+}
+
+FBTFT_REGISTER_DRIVER_START(&display)
+FBTFT_COMPATIBLE("sitronix,st7735r")
+FBTFT_COMPATIBLE("fbtft,sainsmart18")
+FBTFT_VARIANT_COMPATIBLE("fbtft,adafruit18", variant_adafruit18)
+FBTFT_VARIANT_COMPATIBLE("fbtft,adafruit18_green", variant_adafruit18_green)
+FBTFT_REGISTER_DRIVER_END(DRVNAME, &display);
 
 MODULE_ALIAS("spi:" DRVNAME);
 MODULE_ALIAS("platform:" DRVNAME);
 MODULE_ALIAS("spi:st7735r");
 MODULE_ALIAS("platform:st7735r");
+MODULE_ALIAS("spi:sainsmart18");
+MODULE_ALIAS("platform:sainsmart");
+MODULE_ALIAS("spi:adafruit18");
+MODULE_ALIAS("platform:adafruit18");
+MODULE_ALIAS("spi:adafruit18_green");
+MODULE_ALIAS("platform:adafruit18_green");
 
 MODULE_DESCRIPTION("FB driver for the ST7735R LCD Controller");
 MODULE_AUTHOR("Noralf Tronnes");
diff -Nur --no-dereference linux-5.10.52-orig/drivers/staging/fbtft/fb_st7789v.c linux-5.10.52-v7l+/drivers/staging/fbtft/fb_st7789v.c
--- linux-5.10.52-orig/drivers/staging/fbtft/fb_st7789v.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/staging/fbtft/fb_st7789v.c	2021-07-25 16:46:24.937972813 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:69 @
 #define MADCTL_MX BIT(6) /* bitmask for column address order */
 #define MADCTL_MY BIT(7) /* bitmask for page address order */
 
+static u32 col_offset = 0;
+static u32 row_offset = 0;
+static u8 col_hack_fix_offset = 0;
+static short x_offset = 0;
+static short y_offset = 0;
+
 /**
  * init_display() - initialize the display controller
  *
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:154 @
 	return 0;
 }
 
+static void minipitft13_set_addr_win(struct fbtft_par *par, int xs, int ys,
+				     int xe, int ye)
+{
+	xs += x_offset;
+	xe += x_offset;
+	ys += y_offset;
+	ye += y_offset;
+	write_reg(par, MIPI_DCS_SET_COLUMN_ADDRESS,
+		  xs >> 8, xs & 0xFF, xe >> 8, xe & 0xFF);
+
+	write_reg(par, MIPI_DCS_SET_PAGE_ADDRESS,
+		  ys >> 8, ys & 0xFF, ye >> 8, ye & 0xFF);
+
+	write_reg(par, MIPI_DCS_WRITE_MEMORY_START);
+}
+
 /**
  * set_var() - apply LCD properties like rotation and BGR mode
  *
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:180 @
 static int set_var(struct fbtft_par *par)
 {
 	u8 madctl_par = 0;
+	struct fbtft_display *display = &par->pdata->display;
+	u32 width = display->width;
+	u32 height = display->height;
 
 	if (par->bgr)
 		madctl_par |= MADCTL_BGR;
 	switch (par->info->var.rotate) {
 	case 0:
+		x_offset = 0;
+		y_offset = 0;
 		break;
 	case 90:
 		madctl_par |= (MADCTL_MV | MADCTL_MY);
+		x_offset = (320 - height) - row_offset;
+		y_offset = (240 - width) - col_offset;
 		break;
 	case 180:
 		madctl_par |= (MADCTL_MX | MADCTL_MY);
+		x_offset = (240 - width) - col_offset + col_hack_fix_offset;
+		// hack tweak to account for extra pixel width to make even
+		y_offset = (320 - height) - row_offset;
 		break;
 	case 270:
 		madctl_par |= (MADCTL_MV | MADCTL_MX);
+		x_offset = row_offset;
+		y_offset = col_offset;
 		break;
 	default:
 		return -EINVAL;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:302 @
 	},
 };
 
-FBTFT_REGISTER_DRIVER(DRVNAME, "sitronix,st7789v", &display);
+int variant_minipitft13(struct fbtft_display *display)
+{
+	display->fbtftops.set_addr_win = minipitft13_set_addr_win;
+	return 0;
+}
+
+FBTFT_REGISTER_DRIVER_START(&display)
+FBTFT_COMPATIBLE("sitronix,st7789v")
+FBTFT_VARIANT_COMPATIBLE("fbtft,minipitft13", variant_minipitft13)
+FBTFT_REGISTER_DRIVER_END(DRVNAME, &display);
 
 MODULE_ALIAS("spi:" DRVNAME);
 MODULE_ALIAS("platform:" DRVNAME);
diff -Nur --no-dereference linux-5.10.52-orig/drivers/staging/fbtft/fbtft-core.c linux-5.10.52-v7l+/drivers/staging/fbtft/fbtft-core.c
--- linux-5.10.52-orig/drivers/staging/fbtft/fbtft-core.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/staging/fbtft/fbtft-core.c	2021-07-25 16:46:24.937972813 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:27 @
 #include <linux/platform_device.h>
 #include <linux/property.h>
 #include <linux/spinlock.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
 
 #include <video/mipi_display.h>
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1199 @
  * @display: Display properties
  * @sdev: SPI device
  * @pdev: Platform device
+ * @dt_ids: Compatible string table
  *
  * Allocates, initializes and registers a framebuffer
  *
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1209 @
  */
 int fbtft_probe_common(struct fbtft_display *display,
 		       struct spi_device *sdev,
-		       struct platform_device *pdev)
+		       struct platform_device *pdev,
+		       const struct of_device_id *dt_ids)
 {
 	struct device *dev;
 	struct fb_info *info;
 	struct fbtft_par *par;
 	struct fbtft_platform_data *pdata;
+	const struct of_device_id *match;
+	int (*variant)(struct fbtft_display *);
 	int ret;
 
 	if (sdev)
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1233 @
 		pdata = fbtft_properties_read(dev);
 		if (IS_ERR(pdata))
 			return PTR_ERR(pdata);
+		match = of_match_device(dt_ids, dev);
+		if (match && match->data) {
+			/* apply the variant */
+			variant = match->data;
+			ret = (*variant)(display);
+			if (ret)
+				return ret;
+		}
 	}
 
 	info = fbtft_framebuffer_alloc(display, dev, pdata);
diff -Nur --no-dereference linux-5.10.52-orig/drivers/staging/fbtft/fbtft.h linux-5.10.52-v7l+/drivers/staging/fbtft/fbtft.h
--- linux-5.10.52-orig/drivers/staging/fbtft/fbtft.h	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/staging/fbtft/fbtft.h	2021-07-25 16:46:24.937972813 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:254 @
 void fbtft_unregister_backlight(struct fbtft_par *par);
 int fbtft_init_display(struct fbtft_par *par);
 int fbtft_probe_common(struct fbtft_display *display, struct spi_device *sdev,
-		       struct platform_device *pdev);
+		       struct platform_device *pdev,
+		       const struct of_device_id *dt_ids);
 int fbtft_remove_common(struct device *dev, struct fb_info *info);
 
 /* fbtft-io.c */
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:276 @
 void fbtft_write_reg16_bus8(struct fbtft_par *par, int len, ...);
 void fbtft_write_reg16_bus16(struct fbtft_par *par, int len, ...);
 
-#define FBTFT_REGISTER_DRIVER(_name, _compatible, _display)                \
+#define FBTFT_REGISTER_DRIVER_START(_display)                              \
+									   \
+static const struct of_device_id dt_ids[];                                 \
 									   \
 static int fbtft_driver_probe_spi(struct spi_device *spi)                  \
 {                                                                          \
-	return fbtft_probe_common(_display, spi, NULL);                    \
+	return fbtft_probe_common(_display, spi, NULL, dt_ids);	           \
 }                                                                          \
 									   \
 static int fbtft_driver_remove_spi(struct spi_device *spi)                 \
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:294 @
 									   \
 static int fbtft_driver_probe_pdev(struct platform_device *pdev)           \
 {                                                                          \
-	return fbtft_probe_common(_display, NULL, pdev);                   \
+	return fbtft_probe_common(_display, NULL, pdev, dt_ids);           \
 }                                                                          \
 									   \
 static int fbtft_driver_remove_pdev(struct platform_device *pdev)          \
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:304 @
 	return fbtft_remove_common(&pdev->dev, info);                      \
 }                                                                          \
 									   \
-static const struct of_device_id dt_ids[] = {                              \
-	{ .compatible = _compatible },                                     \
+static const struct of_device_id dt_ids[] = {
+
+#define FBTFT_COMPATIBLE(_compatible)                                      \
+	{ .compatible = _compatible },
+
+#define FBTFT_VARIANT_COMPATIBLE(_compatible, _variant)                    \
+	{ .compatible = _compatible, .data = _variant },
+
+#define FBTFT_REGISTER_DRIVER_END(_name, _display)                         \
+									   \
 	{},                                                                \
 };                                                                         \
 									   \
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:358 @
 module_init(fbtft_driver_module_init);                                     \
 module_exit(fbtft_driver_module_exit);
 
+#define FBTFT_REGISTER_DRIVER(_name, _compatible, _display)                \
+	FBTFT_REGISTER_DRIVER_START(_display)                              \
+	FBTFT_COMPATIBLE(_compatible)                                      \
+	FBTFT_REGISTER_DRIVER_END(_name, _display)
+
 /* Debug macros */
 
 /* shorthand debug levels */
diff -Nur --no-dereference linux-5.10.52-orig/drivers/staging/media/Kconfig linux-5.10.52-v7l+/drivers/staging/media/Kconfig
--- linux-5.10.52-orig/drivers/staging/media/Kconfig	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/staging/media/Kconfig	2021-07-25 16:46:25.047970969 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:37 @
 
 source "drivers/staging/media/rkvdec/Kconfig"
 
+source "drivers/staging/media/rpivid/Kconfig"
+
 source "drivers/staging/media/sunxi/Kconfig"
 
 source "drivers/staging/media/tegra-vde/Kconfig"
diff -Nur --no-dereference linux-5.10.52-orig/drivers/staging/media/Makefile linux-5.10.52-v7l+/drivers/staging/media/Makefile
--- linux-5.10.52-orig/drivers/staging/media/Makefile	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/staging/media/Makefile	2021-07-25 16:46:25.047970969 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:8 @
 obj-$(CONFIG_VIDEO_MESON_VDEC)	+= meson/vdec/
 obj-$(CONFIG_VIDEO_OMAP4)	+= omap4iss/
 obj-$(CONFIG_VIDEO_ROCKCHIP_VDEC)	+= rkvdec/
+obj-$(CONFIG_VIDEO_RPIVID)	+= rpivid/
 obj-$(CONFIG_VIDEO_SUNXI)	+= sunxi/
 obj-$(CONFIG_VIDEO_TEGRA)	+= tegra-video/
 obj-$(CONFIG_TEGRA_VDE)		+= tegra-vde/
diff -Nur --no-dereference linux-5.10.52-orig/drivers/staging/media/rpivid/Kconfig linux-5.10.52-v7l+/drivers/staging/media/rpivid/Kconfig
--- linux-5.10.52-orig/drivers/staging/media/rpivid/Kconfig	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/staging/media/rpivid/Kconfig	2021-07-25 16:46:25.637961078 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+# SPDX-License-Identifier: GPL-2.0
+
+config VIDEO_RPIVID
+	tristate "Rpi H265 driver"
+	depends on VIDEO_DEV && VIDEO_V4L2
+	depends on OF
+	select MEDIA_CONTROLLER
+	select MEDIA_CONTROLLER_REQUEST_API
+	select VIDEOBUF2_DMA_CONTIG
+	select V4L2_MEM2MEM_DEV
+	help
+	  Support for the Rpi H265 h/w decoder.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called rpivid-hevc.
+
diff -Nur --no-dereference linux-5.10.52-orig/drivers/staging/media/rpivid/Makefile linux-5.10.52-v7l+/drivers/staging/media/rpivid/Makefile
--- linux-5.10.52-orig/drivers/staging/media/rpivid/Makefile	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/staging/media/rpivid/Makefile	2021-07-25 16:46:25.637961078 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:2 @
+# SPDX-License-Identifier: GPL-2.0
+obj-$(CONFIG_VIDEO_RPIVID) += rpivid-hevc.o
+
+rpivid-hevc-y = rpivid.o rpivid_video.o rpivid_dec.o \
+		 rpivid_hw.o rpivid_h265.o
diff -Nur --no-dereference linux-5.10.52-orig/drivers/staging/media/rpivid/rpivid.c linux-5.10.52-v7l+/drivers/staging/media/rpivid/rpivid.c
--- linux-5.10.52-orig/drivers/staging/media/rpivid/rpivid.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/staging/media/rpivid/rpivid.c	2021-07-25 16:46:25.637961078 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Raspberry Pi HEVC driver
+ *
+ * Copyright (C) 2020 Raspberry Pi (Trading) Ltd
+ *
+ * Based on the Cedrus VPU driver, that is:
+ *
+ * Copyright (C) 2016 Florent Revest <florent.revest@free-electrons.com>
+ * Copyright (C) 2018 Paul Kocialkowski <paul.kocialkowski@bootlin.com>
+ * Copyright (C) 2018 Bootlin
+ */
+
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/of.h>
+
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-mem2mem.h>
+
+#include "rpivid.h"
+#include "rpivid_video.h"
+#include "rpivid_hw.h"
+#include "rpivid_dec.h"
+
+/*
+ * Default /dev/videoN node number.
+ * Deliberately avoid the very low numbers as these are often taken by webcams
+ * etc, and simple apps tend to only go for /dev/video0.
+ */
+static int video_nr = 19;
+module_param(video_nr, int, 0644);
+MODULE_PARM_DESC(video_nr, "decoder video device number");
+
+static const struct rpivid_control rpivid_ctrls[] = {
+	{
+		.cfg = {
+			.id	= V4L2_CID_MPEG_VIDEO_HEVC_SPS,
+			.ops	= &rpivid_hevc_sps_ctrl_ops,
+		},
+		.required	= true,
+	},
+	{
+		.cfg = {
+			.id	= V4L2_CID_MPEG_VIDEO_HEVC_PPS,
+			.ops	= &rpivid_hevc_pps_ctrl_ops,
+		},
+		.required	= true,
+	},
+	{
+		.cfg = {
+			.id = V4L2_CID_MPEG_VIDEO_HEVC_SCALING_MATRIX,
+		},
+		.required	= false,
+	},
+	{
+		.cfg = {
+			.id	= V4L2_CID_MPEG_VIDEO_HEVC_SLICE_PARAMS,
+		},
+		.required	= true,
+	},
+	{
+		.cfg = {
+			.id	= V4L2_CID_MPEG_VIDEO_HEVC_DECODE_MODE,
+			.max	= V4L2_MPEG_VIDEO_HEVC_DECODE_MODE_SLICE_BASED,
+			.def	= V4L2_MPEG_VIDEO_HEVC_DECODE_MODE_SLICE_BASED,
+		},
+		.required	= false,
+	},
+	{
+		.cfg = {
+			.id	= V4L2_CID_MPEG_VIDEO_HEVC_START_CODE,
+			.max	= V4L2_MPEG_VIDEO_HEVC_START_CODE_NONE,
+			.def	= V4L2_MPEG_VIDEO_HEVC_START_CODE_NONE,
+		},
+		.required	= false,
+	},
+};
+
+#define rpivid_ctrls_COUNT	ARRAY_SIZE(rpivid_ctrls)
+
+struct v4l2_ctrl *rpivid_find_ctrl(struct rpivid_ctx *ctx, u32 id)
+{
+	unsigned int i;
+
+	for (i = 0; ctx->ctrls[i]; i++)
+		if (ctx->ctrls[i]->id == id)
+			return ctx->ctrls[i];
+
+	return NULL;
+}
+
+void *rpivid_find_control_data(struct rpivid_ctx *ctx, u32 id)
+{
+	struct v4l2_ctrl *const ctrl = rpivid_find_ctrl(ctx, id);
+
+	return !ctrl ? NULL : ctrl->p_cur.p;
+}
+
+static int rpivid_init_ctrls(struct rpivid_dev *dev, struct rpivid_ctx *ctx)
+{
+	struct v4l2_ctrl_handler *hdl = &ctx->hdl;
+	struct v4l2_ctrl *ctrl;
+	unsigned int ctrl_size;
+	unsigned int i;
+
+	v4l2_ctrl_handler_init(hdl, rpivid_ctrls_COUNT);
+	if (hdl->error) {
+		v4l2_err(&dev->v4l2_dev,
+			 "Failed to initialize control handler\n");
+		return hdl->error;
+	}
+
+	ctrl_size = sizeof(ctrl) * rpivid_ctrls_COUNT + 1;
+
+	ctx->ctrls = kzalloc(ctrl_size, GFP_KERNEL);
+	if (!ctx->ctrls)
+		return -ENOMEM;
+
+	for (i = 0; i < rpivid_ctrls_COUNT; i++) {
+		ctrl = v4l2_ctrl_new_custom(hdl, &rpivid_ctrls[i].cfg,
+					    ctx);
+		if (hdl->error) {
+			v4l2_err(&dev->v4l2_dev,
+				 "Failed to create new custom control id=%#x\n",
+				 rpivid_ctrls[i].cfg.id);
+
+			v4l2_ctrl_handler_free(hdl);
+			kfree(ctx->ctrls);
+			return hdl->error;
+		}
+
+		ctx->ctrls[i] = ctrl;
+	}
+
+	ctx->fh.ctrl_handler = hdl;
+	v4l2_ctrl_handler_setup(hdl);
+
+	return 0;
+}
+
+static int rpivid_request_validate(struct media_request *req)
+{
+	struct media_request_object *obj;
+	struct v4l2_ctrl_handler *parent_hdl, *hdl;
+	struct rpivid_ctx *ctx = NULL;
+	struct v4l2_ctrl *ctrl_test;
+	unsigned int count;
+	unsigned int i;
+
+	list_for_each_entry(obj, &req->objects, list) {
+		struct vb2_buffer *vb;
+
+		if (vb2_request_object_is_buffer(obj)) {
+			vb = container_of(obj, struct vb2_buffer, req_obj);
+			ctx = vb2_get_drv_priv(vb->vb2_queue);
+
+			break;
+		}
+	}
+
+	if (!ctx)
+		return -ENOENT;
+
+	count = vb2_request_buffer_cnt(req);
+	if (!count) {
+		v4l2_info(&ctx->dev->v4l2_dev,
+			  "No buffer was provided with the request\n");
+		return -ENOENT;
+	} else if (count > 1) {
+		v4l2_info(&ctx->dev->v4l2_dev,
+			  "More than one buffer was provided with the request\n");
+		return -EINVAL;
+	}
+
+	parent_hdl = &ctx->hdl;
+
+	hdl = v4l2_ctrl_request_hdl_find(req, parent_hdl);
+	if (!hdl) {
+		v4l2_info(&ctx->dev->v4l2_dev, "Missing codec control(s)\n");
+		return -ENOENT;
+	}
+
+	for (i = 0; i < rpivid_ctrls_COUNT; i++) {
+		if (!rpivid_ctrls[i].required)
+			continue;
+
+		ctrl_test =
+			v4l2_ctrl_request_hdl_ctrl_find(hdl,
+							rpivid_ctrls[i].cfg.id);
+		if (!ctrl_test) {
+			v4l2_info(&ctx->dev->v4l2_dev,
+				  "Missing required codec control\n");
+			v4l2_ctrl_request_hdl_put(hdl);
+			return -ENOENT;
+		}
+	}
+
+	v4l2_ctrl_request_hdl_put(hdl);
+
+	return vb2_request_validate(req);
+}
+
+static int rpivid_open(struct file *file)
+{
+	struct rpivid_dev *dev = video_drvdata(file);
+	struct rpivid_ctx *ctx = NULL;
+	int ret;
+
+	if (mutex_lock_interruptible(&dev->dev_mutex))
+		return -ERESTARTSYS;
+
+	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+	if (!ctx) {
+		mutex_unlock(&dev->dev_mutex);
+		ret = -ENOMEM;
+		goto err_unlock;
+	}
+
+	mutex_init(&ctx->ctx_mutex);
+
+	v4l2_fh_init(&ctx->fh, video_devdata(file));
+	file->private_data = &ctx->fh;
+	ctx->dev = dev;
+
+	ret = rpivid_init_ctrls(dev, ctx);
+	if (ret)
+		goto err_free;
+
+	ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev, ctx,
+					    &rpivid_queue_init);
+	if (IS_ERR(ctx->fh.m2m_ctx)) {
+		ret = PTR_ERR(ctx->fh.m2m_ctx);
+		goto err_ctrls;
+	}
+
+	/* The only bit of format info that we can guess now is H265 src
+	 * Everything else we need more info for
+	 */
+	ctx->src_fmt.pixelformat = RPIVID_SRC_PIXELFORMAT_DEFAULT;
+	rpivid_prepare_src_format(&ctx->src_fmt);
+
+	v4l2_fh_add(&ctx->fh);
+
+	mutex_unlock(&dev->dev_mutex);
+
+	return 0;
+
+err_ctrls:
+	v4l2_ctrl_handler_free(&ctx->hdl);
+err_free:
+	mutex_destroy(&ctx->ctx_mutex);
+	kfree(ctx);
+err_unlock:
+	mutex_unlock(&dev->dev_mutex);
+
+	return ret;
+}
+
+static int rpivid_release(struct file *file)
+{
+	struct rpivid_dev *dev = video_drvdata(file);
+	struct rpivid_ctx *ctx = container_of(file->private_data,
+					      struct rpivid_ctx, fh);
+
+	mutex_lock(&dev->dev_mutex);
+
+	v4l2_fh_del(&ctx->fh);
+	v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);
+
+	v4l2_ctrl_handler_free(&ctx->hdl);
+	kfree(ctx->ctrls);
+
+	v4l2_fh_exit(&ctx->fh);
+	mutex_destroy(&ctx->ctx_mutex);
+
+	kfree(ctx);
+
+	mutex_unlock(&dev->dev_mutex);
+
+	return 0;
+}
+
+static const struct v4l2_file_operations rpivid_fops = {
+	.owner		= THIS_MODULE,
+	.open		= rpivid_open,
+	.release	= rpivid_release,
+	.poll		= v4l2_m2m_fop_poll,
+	.unlocked_ioctl	= video_ioctl2,
+	.mmap		= v4l2_m2m_fop_mmap,
+};
+
+static const struct video_device rpivid_video_device = {
+	.name		= RPIVID_NAME,
+	.vfl_dir	= VFL_DIR_M2M,
+	.fops		= &rpivid_fops,
+	.ioctl_ops	= &rpivid_ioctl_ops,
+	.minor		= -1,
+	.release	= video_device_release_empty,
+	.device_caps	= V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING,
+};
+
+static const struct v4l2_m2m_ops rpivid_m2m_ops = {
+	.device_run	= rpivid_device_run,
+};
+
+static const struct media_device_ops rpivid_m2m_media_ops = {
+	.req_validate	= rpivid_request_validate,
+	.req_queue	= v4l2_m2m_request_queue,
+};
+
+static int rpivid_probe(struct platform_device *pdev)
+{
+	struct rpivid_dev *dev;
+	struct video_device *vfd;
+	int ret;
+
+	dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
+	if (!dev)
+		return -ENOMEM;
+
+	dev->vfd = rpivid_video_device;
+	dev->dev = &pdev->dev;
+	dev->pdev = pdev;
+
+	ret = 0;
+	ret = rpivid_hw_probe(dev);
+	if (ret) {
+		dev_err(&pdev->dev, "Failed to probe hardware\n");
+		return ret;
+	}
+
+	dev->dec_ops = &rpivid_dec_ops_h265;
+
+	mutex_init(&dev->dev_mutex);
+
+	ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev);
+	if (ret) {
+		dev_err(&pdev->dev, "Failed to register V4L2 device\n");
+		return ret;
+	}
+
+	vfd = &dev->vfd;
+	vfd->lock = &dev->dev_mutex;
+	vfd->v4l2_dev = &dev->v4l2_dev;
+
+	snprintf(vfd->name, sizeof(vfd->name), "%s", rpivid_video_device.name);
+	video_set_drvdata(vfd, dev);
+
+	dev->m2m_dev = v4l2_m2m_init(&rpivid_m2m_ops);
+	if (IS_ERR(dev->m2m_dev)) {
+		v4l2_err(&dev->v4l2_dev,
+			 "Failed to initialize V4L2 M2M device\n");
+		ret = PTR_ERR(dev->m2m_dev);
+
+		goto err_v4l2;
+	}
+
+	dev->mdev.dev = &pdev->dev;
+	strscpy(dev->mdev.model, RPIVID_NAME, sizeof(dev->mdev.model));
+	strscpy(dev->mdev.bus_info, "platform:" RPIVID_NAME,
+		sizeof(dev->mdev.bus_info));
+
+	media_device_init(&dev->mdev);
+	dev->mdev.ops = &rpivid_m2m_media_ops;
+	dev->v4l2_dev.mdev = &dev->mdev;
+
+	ret = video_register_device(vfd, VFL_TYPE_VIDEO, video_nr);
+	if (ret) {
+		v4l2_err(&dev->v4l2_dev, "Failed to register video device\n");
+		goto err_m2m;
+	}
+
+	v4l2_info(&dev->v4l2_dev,
+		  "Device registered as /dev/video%d\n", vfd->num);
+
+	ret = v4l2_m2m_register_media_controller(dev->m2m_dev, vfd,
+						 MEDIA_ENT_F_PROC_VIDEO_DECODER);
+	if (ret) {
+		v4l2_err(&dev->v4l2_dev,
+			 "Failed to initialize V4L2 M2M media controller\n");
+		goto err_video;
+	}
+
+	ret = media_device_register(&dev->mdev);
+	if (ret) {
+		v4l2_err(&dev->v4l2_dev, "Failed to register media device\n");
+		goto err_m2m_mc;
+	}
+
+	platform_set_drvdata(pdev, dev);
+
+	return 0;
+
+err_m2m_mc:
+	v4l2_m2m_unregister_media_controller(dev->m2m_dev);
+err_video:
+	video_unregister_device(&dev->vfd);
+err_m2m:
+	v4l2_m2m_release(dev->m2m_dev);
+err_v4l2:
+	v4l2_device_unregister(&dev->v4l2_dev);
+
+	return ret;
+}
+
+static int rpivid_remove(struct platform_device *pdev)
+{
+	struct rpivid_dev *dev = platform_get_drvdata(pdev);
+
+	if (media_devnode_is_registered(dev->mdev.devnode)) {
+		media_device_unregister(&dev->mdev);
+		v4l2_m2m_unregister_media_controller(dev->m2m_dev);
+		media_device_cleanup(&dev->mdev);
+	}
+
+	v4l2_m2m_release(dev->m2m_dev);
+	video_unregister_device(&dev->vfd);
+	v4l2_device_unregister(&dev->v4l2_dev);
+
+	rpivid_hw_remove(dev);
+
+	return 0;
+}
+
+static const struct of_device_id rpivid_dt_match[] = {
+	{
+		.compatible = "raspberrypi,rpivid-vid-decoder",
+	},
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, rpivid_dt_match);
+
+static struct platform_driver rpivid_driver = {
+	.probe		= rpivid_probe,
+	.remove		= rpivid_remove,
+	.driver		= {
+		.name = RPIVID_NAME,
+		.of_match_table	= of_match_ptr(rpivid_dt_match),
+	},
+};
+module_platform_driver(rpivid_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("John Cox <jc@kynesim.co.uk>");
+MODULE_DESCRIPTION("Raspberry Pi HEVC V4L2 driver");
diff -Nur --no-dereference linux-5.10.52-orig/drivers/staging/media/rpivid/rpivid_dec.c linux-5.10.52-v7l+/drivers/staging/media/rpivid/rpivid_dec.c
--- linux-5.10.52-orig/drivers/staging/media/rpivid/rpivid_dec.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/staging/media/rpivid/rpivid_dec.c	2021-07-25 16:46:25.637961078 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Raspberry Pi HEVC driver
+ *
+ * Copyright (C) 2020 Raspberry Pi (Trading) Ltd
+ *
+ * Based on the Cedrus VPU driver, that is:
+ *
+ * Copyright (C) 2016 Florent Revest <florent.revest@free-electrons.com>
+ * Copyright (C) 2018 Paul Kocialkowski <paul.kocialkowski@bootlin.com>
+ * Copyright (C) 2018 Bootlin
+ */
+
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-mem2mem.h>
+
+#include "rpivid.h"
+#include "rpivid_dec.h"
+
+void rpivid_device_run(void *priv)
+{
+	struct rpivid_ctx *const ctx = priv;
+	struct rpivid_dev *const dev = ctx->dev;
+	struct rpivid_run run = {};
+	struct media_request *src_req;
+
+	run.src = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
+	run.dst = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);
+
+	if (!run.src || !run.dst) {
+		v4l2_err(&dev->v4l2_dev, "%s: Missing buffer: src=%p, dst=%p\n",
+			 __func__, run.src, run.dst);
+		goto fail;
+	}
+
+	/* Apply request(s) controls */
+	src_req = run.src->vb2_buf.req_obj.req;
+	if (!src_req) {
+		v4l2_err(&dev->v4l2_dev, "%s: Missing request\n", __func__);
+		goto fail;
+	}
+
+	v4l2_ctrl_request_setup(src_req, &ctx->hdl);
+
+	switch (ctx->src_fmt.pixelformat) {
+	case V4L2_PIX_FMT_HEVC_SLICE:
+		run.h265.sps =
+			rpivid_find_control_data(ctx,
+						 V4L2_CID_MPEG_VIDEO_HEVC_SPS);
+		run.h265.pps =
+			rpivid_find_control_data(ctx,
+						 V4L2_CID_MPEG_VIDEO_HEVC_PPS);
+		run.h265.slice_params =
+			rpivid_find_control_data(ctx,
+						 V4L2_CID_MPEG_VIDEO_HEVC_SLICE_PARAMS);
+		run.h265.scaling_matrix =
+			rpivid_find_control_data(ctx,
+						 V4L2_CID_MPEG_VIDEO_HEVC_SCALING_MATRIX);
+		break;
+
+	default:
+		break;
+	}
+
+	v4l2_m2m_buf_copy_metadata(run.src, run.dst, true);
+
+	dev->dec_ops->setup(ctx, &run);
+
+	/* Complete request(s) controls */
+	v4l2_ctrl_request_complete(src_req, &ctx->hdl);
+
+	dev->dec_ops->trigger(ctx);
+	return;
+
+fail:
+	/* We really shouldn't get here but tidy up what we can */
+	v4l2_m2m_buf_done_and_job_finish(dev->m2m_dev, ctx->fh.m2m_ctx,
+					 VB2_BUF_STATE_ERROR);
+}
diff -Nur --no-dereference linux-5.10.52-orig/drivers/staging/media/rpivid/rpivid_dec.h linux-5.10.52-v7l+/drivers/staging/media/rpivid/rpivid_dec.h
--- linux-5.10.52-orig/drivers/staging/media/rpivid/rpivid_dec.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/staging/media/rpivid/rpivid_dec.h	2021-07-25 16:46:25.637961078 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Raspberry Pi HEVC driver
+ *
+ * Copyright (C) 2020 Raspberry Pi (Trading) Ltd
+ *
+ * Based on the Cedrus VPU driver, that is:
+ *
+ * Copyright (C) 2016 Florent Revest <florent.revest@free-electrons.com>
+ * Copyright (C) 2018 Paul Kocialkowski <paul.kocialkowski@bootlin.com>
+ * Copyright (C) 2018 Bootlin
+ */
+
+#ifndef _RPIVID_DEC_H_
+#define _RPIVID_DEC_H_
+
+void rpivid_device_run(void *priv);
+
+#endif
diff -Nur --no-dereference linux-5.10.52-orig/drivers/staging/media/rpivid/rpivid.h linux-5.10.52-v7l+/drivers/staging/media/rpivid/rpivid.h
--- linux-5.10.52-orig/drivers/staging/media/rpivid/rpivid.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/staging/media/rpivid/rpivid.h	2021-07-25 16:46:25.637961078 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Raspberry Pi HEVC driver
+ *
+ * Copyright (C) 2020 Raspberry Pi (Trading) Ltd
+ *
+ * Based on the Cedrus VPU driver, that is:
+ *
+ * Copyright (C) 2016 Florent Revest <florent.revest@free-electrons.com>
+ * Copyright (C) 2018 Paul Kocialkowski <paul.kocialkowski@bootlin.com>
+ * Copyright (C) 2018 Bootlin
+ */
+
+#ifndef _RPIVID_H_
+#define _RPIVID_H_
+
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-mem2mem.h>
+#include <media/videobuf2-v4l2.h>
+#include <media/videobuf2-dma-contig.h>
+
+#define OPT_DEBUG_POLL_IRQ  0
+
+#define RPIVID_DEC_ENV_COUNT 6
+#define RPIVID_P1BUF_COUNT 3
+#define RPIVID_P2BUF_COUNT 3
+
+#define RPIVID_NAME			"rpivid"
+
+#define RPIVID_CAPABILITY_UNTILED	BIT(0)
+#define RPIVID_CAPABILITY_H265_DEC	BIT(1)
+
+#define RPIVID_QUIRK_NO_DMA_OFFSET	BIT(0)
+
+#define RPIVID_SRC_PIXELFORMAT_DEFAULT	V4L2_PIX_FMT_HEVC_SLICE
+
+enum rpivid_irq_status {
+	RPIVID_IRQ_NONE,
+	RPIVID_IRQ_ERROR,
+	RPIVID_IRQ_OK,
+};
+
+struct rpivid_control {
+	struct v4l2_ctrl_config cfg;
+	unsigned char		required:1;
+};
+
+struct rpivid_h265_run {
+	u32 slice_ents;
+	const struct v4l2_ctrl_hevc_sps			*sps;
+	const struct v4l2_ctrl_hevc_pps			*pps;
+	const struct v4l2_ctrl_hevc_slice_params	*slice_params;
+	const struct v4l2_ctrl_hevc_scaling_matrix	*scaling_matrix;
+};
+
+struct rpivid_run {
+	struct vb2_v4l2_buffer	*src;
+	struct vb2_v4l2_buffer	*dst;
+
+	struct rpivid_h265_run	h265;
+};
+
+struct rpivid_buffer {
+	struct v4l2_m2m_buffer          m2m_buf;
+};
+
+struct rpivid_dec_state;
+struct rpivid_dec_env;
+
+struct rpivid_gptr {
+	size_t size;
+	__u8 *ptr;
+	dma_addr_t addr;
+	unsigned long attrs;
+};
+
+struct rpivid_dev;
+typedef void (*rpivid_irq_callback)(struct rpivid_dev *dev, void *ctx);
+
+struct rpivid_q_aux;
+#define RPIVID_AUX_ENT_COUNT VB2_MAX_FRAME
+
+
+#define RPIVID_CTX_STATE_STOPPED	0	/* stream_off */
+#define RPIVID_CTX_STATE_STREAM_ON	1	/* decoding */
+#define RPIVID_CTX_STATE_STREAM_STOP	2	/* in stream_off */
+#define RPIVID_CTX_STATE_STREAM_ERR	3	/* stream_on but broken */
+
+struct rpivid_ctx {
+	struct v4l2_fh			fh;
+	struct rpivid_dev		*dev;
+
+	struct v4l2_pix_format_mplane	src_fmt;
+	struct v4l2_pix_format_mplane	dst_fmt;
+	int dst_fmt_set;
+
+	atomic_t 			stream_state;
+	struct clk_request		*clk_req;
+	int 				src_stream_on;
+	int 				dst_stream_on;
+
+	// fatal_err is set if an error has occurred s.t. decode cannot
+	// continue (such as running out of CMA)
+	int fatal_err;
+
+	/* Lock for queue operations */
+	struct mutex			ctx_mutex;
+
+	struct v4l2_ctrl_handler	hdl;
+	struct v4l2_ctrl		**ctrls;
+
+	/* Decode state - stateless decoder my *** */
+	/* state contains stuff that is only needed in phase0
+	 * it could be held in dec_env but that would be wasteful
+	 */
+	struct rpivid_dec_state *state;
+	struct rpivid_dec_env *dec0;
+
+	/* Spinlock protecting dec_free */
+	spinlock_t dec_lock;
+	struct rpivid_dec_env *dec_free;
+
+	struct rpivid_dec_env *dec_pool;
+
+	unsigned int p1idx;
+	atomic_t p1out;
+	struct rpivid_gptr bitbufs[RPIVID_P1BUF_COUNT];
+
+	/* *** Should be in dev *** */
+	unsigned int p2idx;
+	struct rpivid_gptr pu_bufs[RPIVID_P2BUF_COUNT];
+	struct rpivid_gptr coeff_bufs[RPIVID_P2BUF_COUNT];
+
+	/* Spinlock protecting aux_free */
+	spinlock_t aux_lock;
+	struct rpivid_q_aux *aux_free;
+
+	struct rpivid_q_aux *aux_ents[RPIVID_AUX_ENT_COUNT];
+
+	unsigned int colmv_stride;
+	unsigned int colmv_picsize;
+};
+
+struct rpivid_dec_ops {
+	void (*setup)(struct rpivid_ctx *ctx, struct rpivid_run *run);
+	int (*start)(struct rpivid_ctx *ctx);
+	void (*stop)(struct rpivid_ctx *ctx);
+	void (*trigger)(struct rpivid_ctx *ctx);
+};
+
+struct rpivid_variant {
+	unsigned int	capabilities;
+	unsigned int	quirks;
+	unsigned int	mod_rate;
+};
+
+struct rpivid_hw_irq_ent;
+
+#define RPIVID_ICTL_ENABLE_UNLIMITED (-1)
+
+struct rpivid_hw_irq_ctrl {
+	/* Spinlock protecting claim and tail */
+	spinlock_t lock;
+	struct rpivid_hw_irq_ent *claim;
+	struct rpivid_hw_irq_ent *tail;
+
+	/* Ent for pending irq - also prevents sched */
+	struct rpivid_hw_irq_ent *irq;
+	/* Non-zero => do not start a new job - outer layer sched pending */
+	int no_sched;
+	/* Enable count. -1 always OK, 0 do not sched, +ve shed & count down */
+	int enable;
+	/* Thread CB requested */
+	bool thread_reqed;
+};
+
+struct rpivid_dev {
+	struct v4l2_device	v4l2_dev;
+	struct video_device	vfd;
+	struct media_device	mdev;
+	struct media_pad	pad[2];
+	struct platform_device	*pdev;
+	struct device		*dev;
+	struct v4l2_m2m_dev	*m2m_dev;
+	const struct rpivid_dec_ops *dec_ops;
+
+	/* Device file mutex */
+	struct mutex		dev_mutex;
+
+	void __iomem		*base_irq;
+	void __iomem		*base_h265;
+
+	struct clk		*clock;
+
+	int			cache_align;
+
+	struct rpivid_hw_irq_ctrl ic_active1;
+	struct rpivid_hw_irq_ctrl ic_active2;
+};
+
+extern const struct rpivid_dec_ops rpivid_dec_ops_h265;
+extern const struct v4l2_ctrl_ops rpivid_hevc_sps_ctrl_ops;
+extern const struct v4l2_ctrl_ops rpivid_hevc_pps_ctrl_ops;
+
+struct v4l2_ctrl *rpivid_find_ctrl(struct rpivid_ctx *ctx, u32 id);
+void *rpivid_find_control_data(struct rpivid_ctx *ctx, u32 id);
+
+#endif
diff -Nur --no-dereference linux-5.10.52-orig/drivers/staging/media/rpivid/rpivid_h265.c linux-5.10.52-v7l+/drivers/staging/media/rpivid/rpivid_h265.c
--- linux-5.10.52-orig/drivers/staging/media/rpivid/rpivid_h265.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/staging/media/rpivid/rpivid_h265.c	2021-07-25 16:46:25.637961078 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Raspberry Pi HEVC driver
+ *
+ * Copyright (C) 2020 Raspberry Pi (Trading) Ltd
+ *
+ * Based on the Cedrus VPU driver, that is:
+ *
+ * Copyright (C) 2016 Florent Revest <florent.revest@free-electrons.com>
+ * Copyright (C) 2018 Paul Kocialkowski <paul.kocialkowski@bootlin.com>
+ * Copyright (C) 2018 Bootlin
+ */
+
+#include <linux/delay.h>
+#include <linux/types.h>
+
+#include <media/videobuf2-dma-contig.h>
+
+#include "rpivid.h"
+#include "rpivid_hw.h"
+#include "rpivid_video.h"
+
+#define DEBUG_TRACE_P1_CMD 0
+#define DEBUG_TRACE_EXECUTION 0
+
+#define USE_REQUEST_PIN 1
+
+#if DEBUG_TRACE_EXECUTION
+#define xtrace_in(dev_, de_)\
+	v4l2_info(&(dev_)->v4l2_dev, "%s[%d]: in\n",   __func__,\
+		  (de_) == NULL ? -1 : (de_)->decode_order)
+#define xtrace_ok(dev_, de_)\
+	v4l2_info(&(dev_)->v4l2_dev, "%s[%d]: ok\n",   __func__,\
+		  (de_) == NULL ? -1 : (de_)->decode_order)
+#define xtrace_fin(dev_, de_)\
+	v4l2_info(&(dev_)->v4l2_dev, "%s[%d]: finish\n", __func__,\
+		  (de_) == NULL ? -1 : (de_)->decode_order)
+#define xtrace_fail(dev_, de_)\
+	v4l2_info(&(dev_)->v4l2_dev, "%s[%d]: FAIL\n", __func__,\
+		  (de_) == NULL ? -1 : (de_)->decode_order)
+#else
+#define xtrace_in(dev_, de_)
+#define xtrace_ok(dev_, de_)
+#define xtrace_fin(dev_, de_)
+#define xtrace_fail(dev_, de_)
+#endif
+
+enum hevc_slice_type {
+	HEVC_SLICE_B = 0,
+	HEVC_SLICE_P = 1,
+	HEVC_SLICE_I = 2,
+};
+
+enum hevc_layer { L0 = 0, L1 = 1 };
+
+static int gptr_alloc(struct rpivid_dev *const dev, struct rpivid_gptr *gptr,
+		      size_t size, unsigned long attrs)
+{
+	gptr->size = size;
+	gptr->attrs = attrs;
+	gptr->addr = 0;
+	gptr->ptr = dma_alloc_attrs(dev->dev, gptr->size, &gptr->addr,
+				    GFP_KERNEL, gptr->attrs);
+	return !gptr->ptr ? -ENOMEM : 0;
+}
+
+static void gptr_free(struct rpivid_dev *const dev,
+		      struct rpivid_gptr *const gptr)
+{
+	if (gptr->ptr)
+		dma_free_attrs(dev->dev, gptr->size, gptr->ptr, gptr->addr,
+			       gptr->attrs);
+	gptr->size = 0;
+	gptr->ptr = NULL;
+	gptr->addr = 0;
+	gptr->attrs = 0;
+}
+
+/* Realloc but do not copy
+ *
+ * Frees then allocs.
+ * If the alloc fails then it attempts to re-allocote the old size
+ * On error then check gptr->ptr to determine if anything is currently
+ * allocated.
+ */
+static int gptr_realloc_new(struct rpivid_dev * const dev,
+			    struct rpivid_gptr * const gptr, size_t size)
+{
+	const size_t old_size = gptr->size;
+
+	if (size == gptr->size)
+		return 0;
+
+	if (gptr->ptr)
+		dma_free_attrs(dev->dev, gptr->size, gptr->ptr,
+			       gptr->addr, gptr->attrs);
+
+	gptr->addr = 0;
+	gptr->size = size;
+	gptr->ptr = dma_alloc_attrs(dev->dev, gptr->size,
+				    &gptr->addr, GFP_KERNEL, gptr->attrs);
+
+	if (!gptr->ptr) {
+		gptr->addr = 0;
+		gptr->size = old_size;
+		gptr->ptr = dma_alloc_attrs(dev->dev, gptr->size,
+					    &gptr->addr, GFP_KERNEL, gptr->attrs);
+		if (!gptr->ptr) {
+			gptr->size = 0;
+			gptr->addr = 0;
+			gptr->attrs = 0;
+		}
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
+static size_t next_size(const size_t x)
+{
+	return rpivid_round_up_size(x + 1);
+}
+
+#define NUM_SCALING_FACTORS 4064 /* Not a typo = 0xbe0 + 0x400 */
+
+#define AXI_BASE64 0
+
+#define PROB_BACKUP ((20 << 12) + (20 << 6) + (0 << 0))
+#define PROB_RELOAD ((20 << 12) + (20 << 0) + (0 << 6))
+
+#define HEVC_MAX_REFS V4L2_HEVC_DPB_ENTRIES_NUM_MAX
+
+//////////////////////////////////////////////////////////////////////////////
+
+struct rpi_cmd {
+	u32 addr;
+	u32 data;
+} __packed;
+
+struct rpivid_q_aux {
+	unsigned int refcount;
+	unsigned int q_index;
+	struct rpivid_q_aux *next;
+	struct rpivid_gptr col;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+enum rpivid_decode_state {
+	RPIVID_DECODE_SLICE_START,
+	RPIVID_DECODE_SLICE_CONTINUE,
+	RPIVID_DECODE_ERROR_CONTINUE,
+	RPIVID_DECODE_ERROR_DONE,
+	RPIVID_DECODE_PHASE1,
+	RPIVID_DECODE_END,
+};
+
+struct rpivid_dec_env {
+	struct rpivid_ctx *ctx;
+	struct rpivid_dec_env *next;
+
+	enum rpivid_decode_state state;
+	unsigned int decode_order;
+	int p1_status;		/* P1 status - what to realloc */
+
+	struct rpi_cmd *cmd_fifo;
+	unsigned int cmd_len, cmd_max;
+	unsigned int num_slice_msgs;
+	unsigned int pic_width_in_ctbs_y;
+	unsigned int pic_height_in_ctbs_y;
+	unsigned int dpbno_col;
+	u32 reg_slicestart;
+	int collocated_from_l0_flag;
+	/*
+	 * Last CTB/Tile X,Y processed by (wpp_)entry_point
+	 * Could be in _state as P0 only but needs updating where _state
+	 * is const
+	 */
+	unsigned int entry_ctb_x;
+	unsigned int entry_ctb_y;
+	unsigned int entry_tile_x;
+	unsigned int entry_tile_y;
+	unsigned int entry_qp;
+	u32 entry_slice;
+
+	u32 rpi_config2;
+	u32 rpi_framesize;
+	u32 rpi_currpoc;
+
+	struct vb2_v4l2_buffer *frame_buf; // Detached dest buffer
+	struct vb2_v4l2_buffer *src_buf;   // Detached src buffer
+	unsigned int frame_c_offset;
+	unsigned int frame_stride;
+	dma_addr_t frame_addr;
+	dma_addr_t ref_addrs[16];
+	struct rpivid_q_aux *frame_aux;
+	struct rpivid_q_aux *col_aux;
+
+	dma_addr_t cmd_addr;
+	size_t cmd_size;
+
+	dma_addr_t pu_base_vc;
+	dma_addr_t coeff_base_vc;
+	u32 pu_stride;
+	u32 coeff_stride;
+
+	struct rpivid_gptr *bit_copy_gptr;
+	size_t bit_copy_len;
+
+#define SLICE_MSGS_MAX (2 * HEVC_MAX_REFS * 8 + 3)
+	u16 slice_msgs[SLICE_MSGS_MAX];
+	u8 scaling_factors[NUM_SCALING_FACTORS];
+
+#if USE_REQUEST_PIN
+	struct media_request *req_pin;
+#else
+	struct media_request_object *req_obj;
+#endif
+	struct rpivid_hw_irq_ent irq_ent;
+};
+
+#define member_size(type, member) sizeof(((type *)0)->member)
+
+struct rpivid_dec_state {
+	struct v4l2_ctrl_hevc_sps sps;
+	struct v4l2_ctrl_hevc_pps pps;
+
+	// Helper vars & tables derived from sps/pps
+	unsigned int log2_ctb_size;     /* log2 width of a CTB */
+	unsigned int ctb_width;         /* Width in CTBs */
+	unsigned int ctb_height;        /* Height in CTBs */
+	unsigned int ctb_size;          /* Pic area in CTBs */
+	unsigned int tile_width;        /* Width in tiles */
+	unsigned int tile_height;       /* Height in tiles */
+
+	int *col_bd;
+	int *row_bd;
+	int *ctb_addr_rs_to_ts;
+	int *ctb_addr_ts_to_rs;
+
+	// Aux starage for DPB
+	// Hold refs
+	struct rpivid_q_aux *ref_aux[HEVC_MAX_REFS];
+	struct rpivid_q_aux *frame_aux;
+
+	// Slice vars
+	unsigned int slice_idx;
+	bool frame_end;
+	bool slice_temporal_mvp;  /* Slice flag but constant for frame */
+
+	// Temp vars per run - don't actually need to persist
+	u8 *src_buf;
+	dma_addr_t src_addr;
+	const struct v4l2_ctrl_hevc_slice_params *sh;
+	unsigned int nb_refs[2];
+	unsigned int slice_qp;
+	unsigned int max_num_merge_cand; // 0 if I-slice
+	bool dependent_slice_segment_flag;
+
+	unsigned int start_ts;          /* slice_segment_addr -> ts */
+	unsigned int start_ctb_x;       /* CTB X,Y of start_ts */
+	unsigned int start_ctb_y;
+	unsigned int prev_ctb_x;        /* CTB X,Y of start_ts - 1 */
+	unsigned int prev_ctb_y;
+};
+
+#if !USE_REQUEST_PIN
+static void dst_req_obj_release(struct media_request_object *object)
+{
+	kfree(object);
+}
+
+static const struct media_request_object_ops dst_req_obj_ops = {
+	.release = dst_req_obj_release,
+};
+#endif
+
+static inline int clip_int(const int x, const int lo, const int hi)
+{
+	return x < lo ? lo : x > hi ? hi : x;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// Phase 1 command and bit FIFOs
+
+#if DEBUG_TRACE_P1_CMD
+static int p1_z;
+#endif
+
+static int cmds_check_space(struct rpivid_dec_env *const de, unsigned int n)
+{
+	struct rpi_cmd *a;
+	unsigned int newmax;
+
+	if (n > 0x100000) {
+		v4l2_err(&de->ctx->dev->v4l2_dev,
+			 "%s: n %u implausible\n", __func__, n);
+		return -ENOMEM;
+	}
+
+	if (de->cmd_len + n <= de->cmd_max)
+		return 0;
+
+	newmax = roundup_pow_of_two(de->cmd_len + n);
+
+	a = krealloc(de->cmd_fifo, newmax * sizeof(struct rpi_cmd),
+		     GFP_KERNEL);
+	if (!a) {
+		v4l2_err(&de->ctx->dev->v4l2_dev,
+			 "Failed cmd buffer realloc from %u to %u\n",
+			 de->cmd_max, newmax);
+		return -ENOMEM;
+	}
+	v4l2_info(&de->ctx->dev->v4l2_dev,
+		  "cmd buffer realloc from %u to %u\n", de->cmd_max, newmax);
+
+	de->cmd_fifo = a;
+	de->cmd_max = newmax;
+	return 0;
+}
+
+// ???? u16 addr - put in u32
+static void p1_apb_write(struct rpivid_dec_env *const de, const u16 addr,
+			 const u32 data)
+{
+	if (de->cmd_len >= de->cmd_max) {
+		v4l2_err(&de->ctx->dev->v4l2_dev,
+			 "%s: Overflow @ %d\n", __func__, de->cmd_len);
+		return;
+	}
+
+	de->cmd_fifo[de->cmd_len].addr = addr;
+	de->cmd_fifo[de->cmd_len].data = data;
+
+#if DEBUG_TRACE_P1_CMD
+	if (++p1_z < 256) {
+		v4l2_info(&de->ctx->dev->v4l2_dev, "[%02x] %x %x\n",
+			  de->cmd_len, addr, data);
+	}
+#endif
+	de->cmd_len++;
+}
+
+static int ctb_to_tile(unsigned int ctb, unsigned int *bd, int num)
+{
+	int i;
+
+	for (i = 1; ctb >= bd[i]; i++)
+		; // bd[] has num+1 elements; bd[0]=0;
+	return i - 1;
+}
+
+static unsigned int ctb_to_tile_x(const struct rpivid_dec_state *const s,
+				  const unsigned int ctb_x)
+{
+	return ctb_to_tile(ctb_x, s->col_bd, s->tile_width);
+}
+
+static unsigned int ctb_to_tile_y(const struct rpivid_dec_state *const s,
+				  const unsigned int ctb_y)
+{
+	return ctb_to_tile(ctb_y, s->row_bd, s->tile_height);
+}
+
+static void aux_q_free(struct rpivid_ctx *const ctx,
+		       struct rpivid_q_aux *const aq)
+{
+	struct rpivid_dev *const dev = ctx->dev;
+
+	gptr_free(dev, &aq->col);
+	kfree(aq);
+}
+
+static struct rpivid_q_aux *aux_q_alloc(struct rpivid_ctx *const ctx,
+					const unsigned int q_index)
+{
+	struct rpivid_dev *const dev = ctx->dev;
+	struct rpivid_q_aux *const aq = kzalloc(sizeof(*aq), GFP_KERNEL);
+
+	if (!aq)
+		return NULL;
+
+	if (gptr_alloc(dev, &aq->col, ctx->colmv_picsize,
+		       DMA_ATTR_FORCE_CONTIGUOUS | DMA_ATTR_NO_KERNEL_MAPPING))
+		goto fail;
+
+	/*
+	 * Spinlock not required as called in P0 only and
+	 * aux checks done by _new
+	 */
+	aq->refcount = 1;
+	aq->q_index = q_index;
+	ctx->aux_ents[q_index] = aq;
+	return aq;
+
+fail:
+	kfree(aq);
+	return NULL;
+}
+
+static struct rpivid_q_aux *aux_q_new(struct rpivid_ctx *const ctx,
+				      const unsigned int q_index)
+{
+	struct rpivid_q_aux *aq;
+	unsigned long lockflags;
+
+	spin_lock_irqsave(&ctx->aux_lock, lockflags);
+	/*
+	 * If we already have this allocated to a slot then use that
+	 * and assume that it will all work itself out in the pipeline
+	 */
+	if ((aq = ctx->aux_ents[q_index]) != NULL) {
+		++aq->refcount;
+	} else if ((aq = ctx->aux_free) != NULL) {
+		ctx->aux_free = aq->next;
+		aq->next = NULL;
+		aq->refcount = 1;
+		aq->q_index = q_index;
+		ctx->aux_ents[q_index] = aq;
+	}
+	spin_unlock_irqrestore(&ctx->aux_lock, lockflags);
+
+	if (!aq)
+		aq = aux_q_alloc(ctx, q_index);
+
+	return aq;
+}
+
+static struct rpivid_q_aux *aux_q_ref_idx(struct rpivid_ctx *const ctx,
+					  const int q_index)
+{
+	unsigned long lockflags;
+	struct rpivid_q_aux *aq;
+
+	spin_lock_irqsave(&ctx->aux_lock, lockflags);
+	if ((aq = ctx->aux_ents[q_index]) != NULL)
+		++aq->refcount;
+	spin_unlock_irqrestore(&ctx->aux_lock, lockflags);
+
+	return aq;
+}
+
+static struct rpivid_q_aux *aux_q_ref(struct rpivid_ctx *const ctx,
+				      struct rpivid_q_aux *const aq)
+{
+	if (aq) {
+		unsigned long lockflags;
+
+		spin_lock_irqsave(&ctx->aux_lock, lockflags);
+
+		++aq->refcount;
+
+		spin_unlock_irqrestore(&ctx->aux_lock, lockflags);
+	}
+	return aq;
+}
+
+static void aux_q_release(struct rpivid_ctx *const ctx,
+			  struct rpivid_q_aux **const paq)
+{
+	struct rpivid_q_aux *const aq = *paq;
+	unsigned long lockflags;
+
+	if (!aq)
+		return;
+
+	*paq = NULL;
+
+	spin_lock_irqsave(&ctx->aux_lock, lockflags);
+	if (--aq->refcount == 0) {
+		aq->next = ctx->aux_free;
+		ctx->aux_free = aq;
+		ctx->aux_ents[aq->q_index] = NULL;
+		aq->q_index = ~0U;
+	}
+	spin_unlock_irqrestore(&ctx->aux_lock, lockflags);
+}
+
+static void aux_q_init(struct rpivid_ctx *const ctx)
+{
+	spin_lock_init(&ctx->aux_lock);
+	ctx->aux_free = NULL;
+}
+
+static void aux_q_uninit(struct rpivid_ctx *const ctx)
+{
+	struct rpivid_q_aux *aq;
+
+	ctx->colmv_picsize = 0;
+	ctx->colmv_stride = 0;
+	while ((aq = ctx->aux_free) != NULL) {
+		ctx->aux_free = aq->next;
+		aux_q_free(ctx, aq);
+	}
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+/*
+ * Initialisation process for context variables (CABAC init)
+ * see H.265 9.3.2.2
+ *
+ * N.B. If comparing with FFmpeg note that this h/w uses slightly different
+ * offsets to FFmpegs array
+ */
+
+/* Actual number of values */
+#define RPI_PROB_VALS 154U
+/* Rounded up as we copy words */
+#define RPI_PROB_ARRAY_SIZE ((154 + 3) & ~3)
+
+/* Initialiser values - see tables H.265 9-4 through 9-42 */
+static const u8 prob_init[3][156] = {
+	{
+		153, 200, 139, 141, 157, 154, 154, 154, 154, 154, 184, 154, 154,
+		154, 184, 63,  154, 154, 154, 154, 154, 154, 154, 154, 154, 154,
+		154, 154, 154, 153, 138, 138, 111, 141, 94,  138, 182, 154, 154,
+		154, 140, 92,  137, 138, 140, 152, 138, 139, 153, 74,  149, 92,
+		139, 107, 122, 152, 140, 179, 166, 182, 140, 227, 122, 197, 110,
+		110, 124, 125, 140, 153, 125, 127, 140, 109, 111, 143, 127, 111,
+		79,  108, 123, 63,  110, 110, 124, 125, 140, 153, 125, 127, 140,
+		109, 111, 143, 127, 111, 79,  108, 123, 63,  91,  171, 134, 141,
+		138, 153, 136, 167, 152, 152, 139, 139, 111, 111, 125, 110, 110,
+		94,  124, 108, 124, 107, 125, 141, 179, 153, 125, 107, 125, 141,
+		179, 153, 125, 107, 125, 141, 179, 153, 125, 140, 139, 182, 182,
+		152, 136, 152, 136, 153, 136, 139, 111, 136, 139, 111, 0,   0,
+	},
+	{
+		153, 185, 107, 139, 126, 197, 185, 201, 154, 149, 154, 139, 154,
+		154, 154, 152, 110, 122, 95,  79,  63,  31,  31,  153, 153, 168,
+		140, 198, 79,  124, 138, 94,  153, 111, 149, 107, 167, 154, 154,
+		154, 154, 196, 196, 167, 154, 152, 167, 182, 182, 134, 149, 136,
+		153, 121, 136, 137, 169, 194, 166, 167, 154, 167, 137, 182, 125,
+		110, 94,  110, 95,  79,  125, 111, 110, 78,  110, 111, 111, 95,
+		94,  108, 123, 108, 125, 110, 94,  110, 95,  79,  125, 111, 110,
+		78,  110, 111, 111, 95,  94,  108, 123, 108, 121, 140, 61,  154,
+		107, 167, 91,  122, 107, 167, 139, 139, 155, 154, 139, 153, 139,
+		123, 123, 63,  153, 166, 183, 140, 136, 153, 154, 166, 183, 140,
+		136, 153, 154, 166, 183, 140, 136, 153, 154, 170, 153, 123, 123,
+		107, 121, 107, 121, 167, 151, 183, 140, 151, 183, 140, 0,   0,
+	},
+	{
+		153, 160, 107, 139, 126, 197, 185, 201, 154, 134, 154, 139, 154,
+		154, 183, 152, 154, 137, 95,  79,  63,  31,  31,  153, 153, 168,
+		169, 198, 79,  224, 167, 122, 153, 111, 149, 92,  167, 154, 154,
+		154, 154, 196, 167, 167, 154, 152, 167, 182, 182, 134, 149, 136,
+		153, 121, 136, 122, 169, 208, 166, 167, 154, 152, 167, 182, 125,
+		110, 124, 110, 95,  94,  125, 111, 111, 79,  125, 126, 111, 111,
+		79,  108, 123, 93,  125, 110, 124, 110, 95,  94,  125, 111, 111,
+		79,  125, 126, 111, 111, 79,  108, 123, 93,  121, 140, 61,  154,
+		107, 167, 91,  107, 107, 167, 139, 139, 170, 154, 139, 153, 139,
+		123, 123, 63,  124, 166, 183, 140, 136, 153, 154, 166, 183, 140,
+		136, 153, 154, 166, 183, 140, 136, 153, 154, 170, 153, 138, 138,
+		122, 121, 122, 121, 167, 151, 183, 140, 151, 183, 140, 0,   0,
+	},
+};
+
+#define CMDS_WRITE_PROB ((RPI_PROB_ARRAY_SIZE / 4) + 1)
+static void write_prob(struct rpivid_dec_env *const de,
+		       const struct rpivid_dec_state *const s)
+{
+	u8 dst[RPI_PROB_ARRAY_SIZE];
+
+	const unsigned int init_type =
+		((s->sh->flags & V4L2_HEVC_SLICE_PARAMS_FLAG_CABAC_INIT) != 0 &&
+		 s->sh->slice_type != HEVC_SLICE_I) ?
+			s->sh->slice_type + 1 :
+			2 - s->sh->slice_type;
+	const u8 *p = prob_init[init_type];
+	const int q = clip_int(s->slice_qp, 0, 51);
+	unsigned int i;
+
+	for (i = 0; i < RPI_PROB_VALS; i++) {
+		int init_value = p[i];
+		int m = (init_value >> 4) * 5 - 45;
+		int n = ((init_value & 15) << 3) - 16;
+		int pre = 2 * (((m * q) >> 4) + n) - 127;
+
+		pre ^= pre >> 31;
+		if (pre > 124)
+			pre = 124 + (pre & 1);
+		dst[i] = pre;
+	}
+	for (i = RPI_PROB_VALS; i != RPI_PROB_ARRAY_SIZE; ++i)
+		dst[i] = 0;
+
+	for (i = 0; i < RPI_PROB_ARRAY_SIZE; i += 4)
+		p1_apb_write(de, 0x1000 + i,
+			     dst[i] + (dst[i + 1] << 8) + (dst[i + 2] << 16) +
+				     (dst[i + 3] << 24));
+
+	/*
+	 * Having written the prob array back it up
+	 * This is not always needed but is a small overhead that simplifies
+	 * (and speeds up) some multi-tile & WPP scenarios
+	 * There are no scenarios where having written a prob we ever want
+	 * a previous (non-initial) state back
+	 */
+	p1_apb_write(de, RPI_TRANSFER, PROB_BACKUP);
+}
+
+#define CMDS_WRITE_SCALING_FACTORS NUM_SCALING_FACTORS
+static void write_scaling_factors(struct rpivid_dec_env *const de)
+{
+	int i;
+	const u8 *p = (u8 *)de->scaling_factors;
+
+	for (i = 0; i < NUM_SCALING_FACTORS; i += 4, p += 4)
+		p1_apb_write(de, 0x2000 + i,
+			     p[0] + (p[1] << 8) + (p[2] << 16) + (p[3] << 24));
+}
+
+static inline __u32 dma_to_axi_addr(dma_addr_t a)
+{
+	return (__u32)(a >> 6);
+}
+
+#define CMDS_WRITE_BITSTREAM 4
+static int write_bitstream(struct rpivid_dec_env *const de,
+			   const struct rpivid_dec_state *const s)
+{
+	// Note that FFmpeg V4L2 does not remove emulation prevention bytes,
+	// so this is matched in the configuration here.
+	// Whether that is the correct behaviour or not is not clear in the
+	// spec.
+	const int rpi_use_emu = 1;
+	unsigned int offset = s->sh->data_bit_offset / 8 + 1;
+	const unsigned int len = (s->sh->bit_size + 7) / 8 - offset;
+	dma_addr_t addr;
+
+	if (s->src_addr != 0) {
+		addr = s->src_addr + offset;
+	} else {
+		if (len + de->bit_copy_len > de->bit_copy_gptr->size) {
+			v4l2_warn(&de->ctx->dev->v4l2_dev,
+				  "Bit copy buffer overflow: size=%zu, offset=%zu, len=%u\n",
+				  de->bit_copy_gptr->size,
+				  de->bit_copy_len, len);
+			return -ENOMEM;
+		}
+		memcpy(de->bit_copy_gptr->ptr + de->bit_copy_len,
+		       s->src_buf + offset, len);
+		addr = de->bit_copy_gptr->addr + de->bit_copy_len;
+		de->bit_copy_len += (len + 63) & ~63;
+	}
+	offset = addr & 63;
+
+	p1_apb_write(de, RPI_BFBASE, dma_to_axi_addr(addr));
+	p1_apb_write(de, RPI_BFNUM, len);
+	p1_apb_write(de, RPI_BFCONTROL, offset + (1 << 7)); // Stop
+	p1_apb_write(de, RPI_BFCONTROL, offset + (rpi_use_emu << 6));
+	return 0;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+/*
+ * The slice constant part of the slice register - width and height need to
+ * be ORed in later as they are per-tile / WPP-row
+ */
+static u32 slice_reg_const(const struct rpivid_dec_state *const s)
+{
+	u32 x = (s->max_num_merge_cand << 0) |
+		(s->nb_refs[L0] << 4) |
+		(s->nb_refs[L1] << 8) |
+		(s->sh->slice_type << 12);
+
+	if (s->sh->flags & V4L2_HEVC_SLICE_PARAMS_FLAG_SLICE_SAO_LUMA)
+		x |= BIT(14);
+	if (s->sh->flags & V4L2_HEVC_SLICE_PARAMS_FLAG_SLICE_SAO_CHROMA)
+		x |= BIT(15);
+	if (s->sh->slice_type == HEVC_SLICE_B &&
+	    (s->sh->flags & V4L2_HEVC_SLICE_PARAMS_FLAG_MVD_L1_ZERO))
+		x |= BIT(16);
+
+	return x;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+#define CMDS_NEW_SLICE_SEGMENT (4 + CMDS_WRITE_SCALING_FACTORS)
+static void new_slice_segment(struct rpivid_dec_env *const de,
+			      const struct rpivid_dec_state *const s)
+{
+	const struct v4l2_ctrl_hevc_sps *const sps = &s->sps;
+	const struct v4l2_ctrl_hevc_pps *const pps = &s->pps;
+
+	p1_apb_write(de,
+		     RPI_SPS0,
+		     ((sps->log2_min_luma_coding_block_size_minus3 + 3) << 0) |
+		     (s->log2_ctb_size << 4) |
+		     ((sps->log2_min_luma_transform_block_size_minus2 + 2)
+							<< 8) |
+		     ((sps->log2_min_luma_transform_block_size_minus2 + 2 +
+		       sps->log2_diff_max_min_luma_transform_block_size)
+						<< 12) |
+		     ((sps->bit_depth_luma_minus8 + 8) << 16) |
+		     ((sps->bit_depth_chroma_minus8 + 8) << 20) |
+		     (sps->max_transform_hierarchy_depth_intra << 24) |
+		     (sps->max_transform_hierarchy_depth_inter << 28));
+
+	p1_apb_write(de,
+		     RPI_SPS1,
+		     ((sps->pcm_sample_bit_depth_luma_minus1 + 1) << 0) |
+		     ((sps->pcm_sample_bit_depth_chroma_minus1 + 1) << 4) |
+		     ((sps->log2_min_pcm_luma_coding_block_size_minus3 + 3)
+						<< 8) |
+		     ((sps->log2_min_pcm_luma_coding_block_size_minus3 + 3 +
+		       sps->log2_diff_max_min_pcm_luma_coding_block_size)
+						<< 12) |
+		     (((sps->flags & V4L2_HEVC_SPS_FLAG_SEPARATE_COLOUR_PLANE) ?
+				0 : sps->chroma_format_idc) << 16) |
+		     ((!!(sps->flags & V4L2_HEVC_SPS_FLAG_AMP_ENABLED)) << 18) |
+		     ((!!(sps->flags & V4L2_HEVC_SPS_FLAG_PCM_ENABLED)) << 19) |
+		     ((!!(sps->flags & V4L2_HEVC_SPS_FLAG_SCALING_LIST_ENABLED))
+						<< 20) |
+		     ((!!(sps->flags &
+			   V4L2_HEVC_SPS_FLAG_STRONG_INTRA_SMOOTHING_ENABLED))
+						<< 21));
+
+	p1_apb_write(de,
+		     RPI_PPS,
+		     ((s->log2_ctb_size - pps->diff_cu_qp_delta_depth) << 0) |
+		     ((!!(pps->flags & V4L2_HEVC_PPS_FLAG_CU_QP_DELTA_ENABLED))
+						 << 4) |
+		     ((!!(pps->flags &
+				V4L2_HEVC_PPS_FLAG_TRANSQUANT_BYPASS_ENABLED))
+						 << 5) |
+		     ((!!(pps->flags & V4L2_HEVC_PPS_FLAG_TRANSFORM_SKIP_ENABLED))
+						 << 6) |
+		     ((!!(pps->flags &
+				V4L2_HEVC_PPS_FLAG_SIGN_DATA_HIDING_ENABLED))
+						<< 7) |
+		     (((pps->pps_cb_qp_offset + s->sh->slice_cb_qp_offset) & 255)
+						<< 8) |
+		     (((pps->pps_cr_qp_offset + s->sh->slice_cr_qp_offset) & 255)
+						<< 16) |
+		     ((!!(pps->flags &
+				V4L2_HEVC_PPS_FLAG_CONSTRAINED_INTRA_PRED))
+						<< 24));
+
+	if ((sps->flags & V4L2_HEVC_SPS_FLAG_SCALING_LIST_ENABLED) != 0)
+		write_scaling_factors(de);
+
+	if (!s->dependent_slice_segment_flag) {
+		int ctb_col = s->sh->slice_segment_addr %
+							de->pic_width_in_ctbs_y;
+		int ctb_row = s->sh->slice_segment_addr /
+							de->pic_width_in_ctbs_y;
+
+		de->reg_slicestart = (ctb_col << 0) + (ctb_row << 16);
+	}
+
+	p1_apb_write(de, RPI_SLICESTART, de->reg_slicestart);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// Slice messages
+
+static void msg_slice(struct rpivid_dec_env *const de, const u16 msg)
+{
+	de->slice_msgs[de->num_slice_msgs++] = msg;
+}
+
+#define CMDS_PROGRAM_SLICECMDS (1 + SLICE_MSGS_MAX)
+static void program_slicecmds(struct rpivid_dec_env *const de,
+			      const int sliceid)
+{
+	int i;
+
+	p1_apb_write(de, RPI_SLICECMDS, de->num_slice_msgs + (sliceid << 8));
+
+	for (i = 0; i < de->num_slice_msgs; i++)
+		p1_apb_write(de, 0x4000 + 4 * i, de->slice_msgs[i] & 0xffff);
+}
+
+// NoBackwardPredictionFlag 8.3.5
+// Simply checks POCs
+static int has_backward(const struct v4l2_hevc_dpb_entry *const dpb,
+			const __u8 *const idx, const unsigned int n,
+			const unsigned int cur_poc)
+{
+	unsigned int i;
+
+	for (i = 0; i < n; ++i) {
+		// Compare mod 2^16
+		// We only get u16 pocs & 8.3.1 says
+		// "The bitstream shall not contain data that result in values
+		//  of DiffPicOrderCnt( picA, picB ) used in the decoding
+		//  process that are not in the range of −2^15 to 2^15 − 1,
+		//  inclusive."
+		if (((cur_poc - dpb[idx[i]].pic_order_cnt[0]) & 0x8000) != 0)
+			return 0;
+	}
+	return 1;
+}
+
+static void pre_slice_decode(struct rpivid_dec_env *const de,
+			     const struct rpivid_dec_state *const s)
+{
+	const struct v4l2_ctrl_hevc_slice_params *const sh = s->sh;
+	int weighted_pred_flag, idx;
+	u16 cmd_slice;
+	unsigned int collocated_from_l0_flag;
+
+	de->num_slice_msgs = 0;
+
+	cmd_slice = 0;
+	if (sh->slice_type == HEVC_SLICE_I)
+		cmd_slice = 1;
+	if (sh->slice_type == HEVC_SLICE_P)
+		cmd_slice = 2;
+	if (sh->slice_type == HEVC_SLICE_B)
+		cmd_slice = 3;
+
+	cmd_slice |= (s->nb_refs[L0] << 2) | (s->nb_refs[L1] << 6) |
+		     (s->max_num_merge_cand << 11);
+
+	collocated_from_l0_flag =
+		!s->slice_temporal_mvp ||
+		sh->slice_type != HEVC_SLICE_B ||
+		(sh->flags & V4L2_HEVC_SLICE_PARAMS_FLAG_COLLOCATED_FROM_L0);
+	cmd_slice |= collocated_from_l0_flag << 14;
+
+	if (sh->slice_type == HEVC_SLICE_P || sh->slice_type == HEVC_SLICE_B) {
+		// Flag to say all reference pictures are from the past
+		const int no_backward_pred_flag =
+			has_backward(sh->dpb, sh->ref_idx_l0, s->nb_refs[L0],
+				     sh->slice_pic_order_cnt) &&
+			has_backward(sh->dpb, sh->ref_idx_l1, s->nb_refs[L1],
+				     sh->slice_pic_order_cnt);
+		cmd_slice |= no_backward_pred_flag << 10;
+		msg_slice(de, cmd_slice);
+
+		if (s->slice_temporal_mvp) {
+			const __u8 *const rpl = collocated_from_l0_flag ?
+						sh->ref_idx_l0 : sh->ref_idx_l1;
+			de->dpbno_col = rpl[sh->collocated_ref_idx];
+			//v4l2_info(&de->ctx->dev->v4l2_dev,
+			//	    "L0=%d col_ref_idx=%d,
+			//          dpb_no=%d\n", collocated_from_l0_flag,
+			//          sh->collocated_ref_idx, de->dpbno_col);
+		}
+
+		// Write reference picture descriptions
+		weighted_pred_flag =
+			sh->slice_type == HEVC_SLICE_P ?
+				!!(s->pps.flags & V4L2_HEVC_PPS_FLAG_WEIGHTED_PRED) :
+				!!(s->pps.flags & V4L2_HEVC_PPS_FLAG_WEIGHTED_BIPRED);
+
+		for (idx = 0; idx < s->nb_refs[L0]; ++idx) {
+			unsigned int dpb_no = sh->ref_idx_l0[idx];
+			//v4l2_info(&de->ctx->dev->v4l2_dev,
+			//	  "L0[%d]=dpb[%d]\n", idx, dpb_no);
+
+			msg_slice(de,
+				  dpb_no |
+				  (sh->dpb[dpb_no].rps ==
+					V4L2_HEVC_DPB_ENTRY_RPS_LT_CURR ?
+						 (1 << 4) : 0) |
+				  (weighted_pred_flag ? (3 << 5) : 0));
+			msg_slice(de, sh->dpb[dpb_no].pic_order_cnt[0]);
+
+			if (weighted_pred_flag) {
+				const struct v4l2_hevc_pred_weight_table
+					*const w = &sh->pred_weight_table;
+				const int luma_weight_denom =
+					(1 << w->luma_log2_weight_denom);
+				const unsigned int chroma_log2_weight_denom =
+					(w->luma_log2_weight_denom +
+					 w->delta_chroma_log2_weight_denom);
+				const int chroma_weight_denom =
+					(1 << chroma_log2_weight_denom);
+
+				msg_slice(de,
+					  w->luma_log2_weight_denom |
+					  (((w->delta_luma_weight_l0[idx] +
+					     luma_weight_denom) & 0x1ff)
+						 << 3));
+				msg_slice(de, w->luma_offset_l0[idx] & 0xff);
+				msg_slice(de,
+					  chroma_log2_weight_denom |
+					  (((w->delta_chroma_weight_l0[idx][0] +
+					     chroma_weight_denom) & 0x1ff)
+						   << 3));
+				msg_slice(de,
+					  w->chroma_offset_l0[idx][0] & 0xff);
+				msg_slice(de,
+					  chroma_log2_weight_denom |
+					  (((w->delta_chroma_weight_l0[idx][1] +
+					     chroma_weight_denom) & 0x1ff)
+						   << 3));
+				msg_slice(de,
+					  w->chroma_offset_l0[idx][1] & 0xff);
+			}
+		}
+
+		for (idx = 0; idx < s->nb_refs[L1]; ++idx) {
+			unsigned int dpb_no = sh->ref_idx_l1[idx];
+			//v4l2_info(&de->ctx->dev->v4l2_dev,
+			//          "L1[%d]=dpb[%d]\n", idx, dpb_no);
+			msg_slice(de,
+				  dpb_no |
+				  (sh->dpb[dpb_no].rps ==
+					 V4L2_HEVC_DPB_ENTRY_RPS_LT_CURR ?
+						 (1 << 4) : 0) |
+					(weighted_pred_flag ? (3 << 5) : 0));
+			msg_slice(de, sh->dpb[dpb_no].pic_order_cnt[0]);
+			if (weighted_pred_flag) {
+				const struct v4l2_hevc_pred_weight_table
+					*const w = &sh->pred_weight_table;
+				const int luma_weight_denom =
+					(1 << w->luma_log2_weight_denom);
+				const unsigned int chroma_log2_weight_denom =
+					(w->luma_log2_weight_denom +
+					 w->delta_chroma_log2_weight_denom);
+				const int chroma_weight_denom =
+					(1 << chroma_log2_weight_denom);
+
+				msg_slice(de,
+					  w->luma_log2_weight_denom |
+					  (((w->delta_luma_weight_l1[idx] +
+					     luma_weight_denom) & 0x1ff) << 3));
+				msg_slice(de, w->luma_offset_l1[idx] & 0xff);
+				msg_slice(de,
+					  chroma_log2_weight_denom |
+					  (((w->delta_chroma_weight_l1[idx][0] +
+					     chroma_weight_denom) & 0x1ff)
+							<< 3));
+				msg_slice(de,
+					  w->chroma_offset_l1[idx][0] & 0xff);
+				msg_slice(de,
+					  chroma_log2_weight_denom |
+					  (((w->delta_chroma_weight_l1[idx][1] +
+					     chroma_weight_denom) & 0x1ff)
+						   << 3));
+				msg_slice(de,
+					  w->chroma_offset_l1[idx][1] & 0xff);
+			}
+		}
+	} else {
+		msg_slice(de, cmd_slice);
+	}
+
+	msg_slice(de,
+		  (sh->slice_beta_offset_div2 & 15) |
+		  ((sh->slice_tc_offset_div2 & 15) << 4) |
+		  ((sh->flags &
+		    V4L2_HEVC_SLICE_PARAMS_FLAG_SLICE_DEBLOCKING_FILTER_DISABLED) ?
+						1 << 8 : 0) |
+		  ((sh->flags &
+			  V4L2_HEVC_SLICE_PARAMS_FLAG_SLICE_LOOP_FILTER_ACROSS_SLICES_ENABLED) ?
+						1 << 9 : 0) |
+		  ((s->pps.flags &
+			  V4L2_HEVC_PPS_FLAG_LOOP_FILTER_ACROSS_TILES_ENABLED) ?
+						1 << 10 : 0));
+
+	msg_slice(de, ((sh->slice_cr_qp_offset & 31) << 5) +
+		       (sh->slice_cb_qp_offset & 31)); // CMD_QPOFF
+}
+
+#define CMDS_WRITE_SLICE 1
+static void write_slice(struct rpivid_dec_env *const de,
+			const struct rpivid_dec_state *const s,
+			const u32 slice_const,
+			const unsigned int ctb_col,
+			const unsigned int ctb_row)
+{
+	const unsigned int cs = (1 << s->log2_ctb_size);
+	const unsigned int w_last = s->sps.pic_width_in_luma_samples & (cs - 1);
+	const unsigned int h_last = s->sps.pic_height_in_luma_samples & (cs - 1);
+
+	p1_apb_write(de, RPI_SLICE,
+		     slice_const |
+		     ((ctb_col + 1 < s->ctb_width || !w_last ?
+				cs : w_last) << 17) |
+		     ((ctb_row + 1 < s->ctb_height || !h_last ?
+				cs : h_last) << 24));
+}
+
+#define PAUSE_MODE_WPP  1
+#define PAUSE_MODE_TILE 0xffff
+
+/*
+ * N.B. This can be called to fill in data from the previous slice so must not
+ * use any state data that may change from slice to slice (e.g. qp)
+ */
+#define CMDS_NEW_ENTRY_POINT (6 + CMDS_WRITE_SLICE)
+static void new_entry_point(struct rpivid_dec_env *const de,
+			    const struct rpivid_dec_state *const s,
+			    const bool do_bte,
+			    const bool reset_qp_y,
+			    const u32 pause_mode,
+			    const unsigned int tile_x,
+			    const unsigned int tile_y,
+			    const unsigned int ctb_col,
+			    const unsigned int ctb_row,
+			    const unsigned int slice_qp,
+			    const u32 slice_const)
+{
+	const unsigned int endx = s->col_bd[tile_x + 1] - 1;
+	const unsigned int endy = (pause_mode == PAUSE_MODE_WPP) ?
+		ctb_row : s->row_bd[tile_y + 1] - 1;
+
+	p1_apb_write(de, RPI_TILESTART,
+		     s->col_bd[tile_x] | (s->row_bd[tile_y] << 16));
+	p1_apb_write(de, RPI_TILEEND, endx | (endy << 16));
+
+	if (do_bte)
+		p1_apb_write(de, RPI_BEGINTILEEND, endx | (endy << 16));
+
+	write_slice(de, s, slice_const, endx, endy);
+
+	if (reset_qp_y) {
+		unsigned int sps_qp_bd_offset =
+			6 * s->sps.bit_depth_luma_minus8;
+
+		p1_apb_write(de, RPI_QP, sps_qp_bd_offset + slice_qp);
+	}
+
+	p1_apb_write(de, RPI_MODE,
+		     pause_mode |
+			((endx == s->ctb_width - 1) << 17) |
+			((endy == s->ctb_height - 1) << 18));
+
+	p1_apb_write(de, RPI_CONTROL, (ctb_col << 0) | (ctb_row << 16));
+
+	de->entry_tile_x = tile_x;
+	de->entry_tile_y = tile_y;
+	de->entry_ctb_x = ctb_col;
+	de->entry_ctb_y = ctb_row;
+	de->entry_qp = slice_qp;
+	de->entry_slice = slice_const;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// Wavefront mode
+
+#define CMDS_WPP_PAUSE 4
+static void wpp_pause(struct rpivid_dec_env *const de, int ctb_row)
+{
+	p1_apb_write(de, RPI_STATUS, (ctb_row << 18) | 0x25);
+	p1_apb_write(de, RPI_TRANSFER, PROB_BACKUP);
+	p1_apb_write(de, RPI_MODE,
+		     ctb_row == de->pic_height_in_ctbs_y - 1 ?
+							0x70000 : 0x30000);
+	p1_apb_write(de, RPI_CONTROL, (ctb_row << 16) + 2);
+}
+
+#define CMDS_WPP_ENTRY_FILL_1 (CMDS_WPP_PAUSE + 2 + CMDS_NEW_ENTRY_POINT)
+static int wpp_entry_fill(struct rpivid_dec_env *const de,
+			  const struct rpivid_dec_state *const s,
+			  const unsigned int last_y)
+{
+	int rv;
+	const unsigned int last_x = s->ctb_width - 1;
+
+	rv = cmds_check_space(de, CMDS_WPP_ENTRY_FILL_1 *
+				  (last_y - de->entry_ctb_y));
+	if (rv)
+		return rv;
+
+	while (de->entry_ctb_y < last_y) {
+		/* wpp_entry_x/y set by wpp_entry_point */
+		if (s->ctb_width > 2)
+			wpp_pause(de, de->entry_ctb_y);
+		p1_apb_write(de, RPI_STATUS,
+			     (de->entry_ctb_y << 18) | (last_x << 5) | 2);
+
+		/* if width == 1 then the saved state is the init one */
+		if (s->ctb_width == 2)
+			p1_apb_write(de, RPI_TRANSFER, PROB_BACKUP);
+		else
+			p1_apb_write(de, RPI_TRANSFER, PROB_RELOAD);
+
+		new_entry_point(de, s, false, true, PAUSE_MODE_WPP,
+				0, 0, 0, de->entry_ctb_y + 1,
+				de->entry_qp, de->entry_slice);
+	}
+	return 0;
+}
+
+static int wpp_end_previous_slice(struct rpivid_dec_env *const de,
+				  const struct rpivid_dec_state *const s)
+{
+	int rv;
+
+	rv = wpp_entry_fill(de, s, s->prev_ctb_y);
+	if (rv)
+		return rv;
+
+	rv = cmds_check_space(de, CMDS_WPP_PAUSE + 2);
+	if (rv)
+		return rv;
+
+	if (de->entry_ctb_x < 2 &&
+	    (de->entry_ctb_y < s->start_ctb_y || s->start_ctb_x > 2) &&
+	    s->ctb_width > 2)
+		wpp_pause(de, s->prev_ctb_y);
+	p1_apb_write(de, RPI_STATUS,
+		     1 | (s->prev_ctb_x << 5) | (s->prev_ctb_y << 18));
+	if (s->start_ctb_x == 2 ||
+	    (s->ctb_width == 2 && de->entry_ctb_y < s->start_ctb_y))
+		p1_apb_write(de, RPI_TRANSFER, PROB_BACKUP);
+	return 0;
+}
+
+/* Only main profile supported so WPP => !Tiles which makes some of the
+ * next chunk code simpler
+ */
+static int wpp_decode_slice(struct rpivid_dec_env *const de,
+			    const struct rpivid_dec_state *const s)
+{
+	bool reset_qp_y = true;
+	const bool indep = !s->dependent_slice_segment_flag;
+	int rv;
+
+	if (s->start_ts) {
+		rv = wpp_end_previous_slice(de, s);
+		if (rv)
+			return rv;
+	}
+	pre_slice_decode(de, s);
+
+	rv = cmds_check_space(de,
+			      CMDS_WRITE_BITSTREAM +
+				CMDS_WRITE_PROB +
+				CMDS_PROGRAM_SLICECMDS +
+				CMDS_NEW_SLICE_SEGMENT +
+				CMDS_NEW_ENTRY_POINT);
+	if (rv)
+		return rv;
+
+	rv = write_bitstream(de, s);
+	if (rv)
+		return rv;
+
+	if (!s->start_ts || indep || s->ctb_width == 1)
+		write_prob(de, s);
+	else if (!s->start_ctb_x)
+		p1_apb_write(de, RPI_TRANSFER, PROB_RELOAD);
+	else
+		reset_qp_y = false;
+
+	program_slicecmds(de, s->slice_idx);
+	new_slice_segment(de, s);
+	new_entry_point(de, s, indep, reset_qp_y, PAUSE_MODE_WPP,
+			0, 0, s->start_ctb_x, s->start_ctb_y,
+			s->slice_qp, slice_reg_const(s));
+
+	if (s->frame_end) {
+		rv = wpp_entry_fill(de, s, s->ctb_height - 1);
+		if (rv)
+			return rv;
+
+		rv = cmds_check_space(de, CMDS_WPP_PAUSE + 1);
+		if (rv)
+			return rv;
+
+		if (de->entry_ctb_x < 2 && s->ctb_width > 2)
+			wpp_pause(de, s->ctb_height - 1);
+
+		p1_apb_write(de, RPI_STATUS,
+			     1 | ((s->ctb_width - 1) << 5) |
+				((s->ctb_height - 1) << 18));
+	}
+	return 0;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// Tiles mode
+
+// Guarantees 1 cmd entry free on exit
+static int tile_entry_fill(struct rpivid_dec_env *const de,
+			   const struct rpivid_dec_state *const s,
+			   const unsigned int last_tile_x,
+			   const unsigned int last_tile_y)
+{
+	while (de->entry_tile_y < last_tile_y ||
+	       (de->entry_tile_y == last_tile_y &&
+		de->entry_tile_x < last_tile_x)) {
+		int rv;
+		unsigned int t_x = de->entry_tile_x;
+		unsigned int t_y = de->entry_tile_y;
+		const unsigned int last_x = s->col_bd[t_x + 1] - 1;
+		const unsigned int last_y = s->row_bd[t_y + 1] - 1;
+
+		// One more than needed here
+		rv = cmds_check_space(de, CMDS_NEW_ENTRY_POINT + 3);
+		if (rv)
+			return rv;
+
+		p1_apb_write(de, RPI_STATUS,
+			     2 | (last_x << 5) | (last_y << 18));
+		p1_apb_write(de, RPI_TRANSFER, PROB_RELOAD);
+
+		// Inc tile
+		if (++t_x >= s->tile_width) {
+			t_x = 0;
+			++t_y;
+		}
+
+		new_entry_point(de, s, false, true, PAUSE_MODE_TILE,
+				t_x, t_y, s->col_bd[t_x], s->row_bd[t_y],
+				de->entry_qp, de->entry_slice);
+	}
+	return 0;
+}
+
+/*
+ * Write STATUS register with expected end CTU address of previous slice
+ */
+static int end_previous_slice(struct rpivid_dec_env *const de,
+			      const struct rpivid_dec_state *const s)
+{
+	int rv;
+
+	rv = tile_entry_fill(de, s,
+			     ctb_to_tile_x(s, s->prev_ctb_x),
+			     ctb_to_tile_y(s, s->prev_ctb_y));
+	if (rv)
+		return rv;
+
+	p1_apb_write(de, RPI_STATUS,
+		     1 | (s->prev_ctb_x << 5) | (s->prev_ctb_y << 18));
+	return 0;
+}
+
+static int decode_slice(struct rpivid_dec_env *const de,
+			const struct rpivid_dec_state *const s)
+{
+	bool reset_qp_y;
+	unsigned int tile_x = ctb_to_tile_x(s, s->start_ctb_x);
+	unsigned int tile_y = ctb_to_tile_y(s, s->start_ctb_y);
+	int rv;
+
+	if (s->start_ts) {
+		rv = end_previous_slice(de, s);
+		if (rv)
+			return rv;
+	}
+
+	rv = cmds_check_space(de,
+			      CMDS_WRITE_BITSTREAM +
+				CMDS_WRITE_PROB +
+				CMDS_PROGRAM_SLICECMDS +
+				CMDS_NEW_SLICE_SEGMENT +
+				CMDS_NEW_ENTRY_POINT);
+	if (rv)
+		return rv;
+
+	pre_slice_decode(de, s);
+	rv = write_bitstream(de, s);
+	if (rv)
+		return rv;
+
+	reset_qp_y = !s->start_ts ||
+		!s->dependent_slice_segment_flag ||
+		tile_x != ctb_to_tile_x(s, s->prev_ctb_x) ||
+		tile_y != ctb_to_tile_y(s, s->prev_ctb_y);
+	if (reset_qp_y)
+		write_prob(de, s);
+
+	program_slicecmds(de, s->slice_idx);
+	new_slice_segment(de, s);
+	new_entry_point(de, s, !s->dependent_slice_segment_flag, reset_qp_y,
+			PAUSE_MODE_TILE,
+			tile_x, tile_y, s->start_ctb_x, s->start_ctb_y,
+			s->slice_qp, slice_reg_const(s));
+
+	/*
+	 * If this is the last slice then fill in the other tile entries
+	 * now, otherwise this will be done at the start of the next slice
+	 * when it will be known where this slice finishes
+	 */
+	if (s->frame_end) {
+		rv = tile_entry_fill(de, s,
+				     s->tile_width - 1,
+				     s->tile_height - 1);
+		if (rv)
+			return rv;
+		p1_apb_write(de, RPI_STATUS,
+			     1 | ((s->ctb_width - 1) << 5) |
+				((s->ctb_height - 1) << 18));
+	}
+	return 0;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// Scaling factors
+
+static void expand_scaling_list(const unsigned int size_id,
+				u8 *const dst0,
+				const u8 *const src0, uint8_t dc)
+{
+	u8 *d;
+	unsigned int x, y;
+
+	switch (size_id) {
+	case 0:
+		memcpy(dst0, src0, 16);
+		break;
+	case 1:
+		memcpy(dst0, src0, 64);
+		break;
+	case 2:
+		d = dst0;
+
+		for (y = 0; y != 16; y++) {
+			const u8 *s = src0 + (y >> 1) * 8;
+
+			for (x = 0; x != 8; ++x) {
+				*d++ = *s;
+				*d++ = *s++;
+			}
+		}
+		dst0[0] = dc;
+		break;
+	default:
+		d = dst0;
+
+		for (y = 0; y != 32; y++) {
+			const u8 *s = src0 + (y >> 2) * 8;
+
+			for (x = 0; x != 8; ++x) {
+				*d++ = *s;
+				*d++ = *s;
+				*d++ = *s;
+				*d++ = *s++;
+			}
+		}
+		dst0[0] = dc;
+		break;
+	}
+}
+
+static void populate_scaling_factors(const struct rpivid_run *const run,
+				     struct rpivid_dec_env *const de,
+				     const struct rpivid_dec_state *const s)
+{
+	const struct v4l2_ctrl_hevc_scaling_matrix *const sl =
+		run->h265.scaling_matrix;
+	// Array of constants for scaling factors
+	static const u32 scaling_factor_offsets[4][6] = {
+		// MID0    MID1    MID2    MID3    MID4    MID5
+		// SID0 (4x4)
+		{ 0x0000, 0x0010, 0x0020, 0x0030, 0x0040, 0x0050 },
+		// SID1 (8x8)
+		{ 0x0060, 0x00A0, 0x00E0, 0x0120, 0x0160, 0x01A0 },
+		// SID2 (16x16)
+		{ 0x01E0, 0x02E0, 0x03E0, 0x04E0, 0x05E0, 0x06E0 },
+		// SID3 (32x32)
+		{ 0x07E0, 0x0BE0, 0x0000, 0x0000, 0x0000, 0x0000 }
+	};
+
+	unsigned int mid;
+
+	for (mid = 0; mid < 6; mid++)
+		expand_scaling_list(0, de->scaling_factors +
+					    scaling_factor_offsets[0][mid],
+				    sl->scaling_list_4x4[mid], 0);
+	for (mid = 0; mid < 6; mid++)
+		expand_scaling_list(1, de->scaling_factors +
+					    scaling_factor_offsets[1][mid],
+				    sl->scaling_list_8x8[mid], 0);
+	for (mid = 0; mid < 6; mid++)
+		expand_scaling_list(2, de->scaling_factors +
+					    scaling_factor_offsets[2][mid],
+				    sl->scaling_list_16x16[mid],
+				    sl->scaling_list_dc_coef_16x16[mid]);
+	for (mid = 0; mid < 2; mid++)
+		expand_scaling_list(3, de->scaling_factors +
+					    scaling_factor_offsets[3][mid],
+				    sl->scaling_list_32x32[mid],
+				    sl->scaling_list_dc_coef_32x32[mid]);
+}
+
+static void free_ps_info(struct rpivid_dec_state *const s)
+{
+	kfree(s->ctb_addr_rs_to_ts);
+	s->ctb_addr_rs_to_ts = NULL;
+	kfree(s->ctb_addr_ts_to_rs);
+	s->ctb_addr_ts_to_rs = NULL;
+
+	kfree(s->col_bd);
+	s->col_bd = NULL;
+	kfree(s->row_bd);
+	s->row_bd = NULL;
+}
+
+static unsigned int tile_width(const struct rpivid_dec_state *const s,
+			       const unsigned int t_x)
+{
+	return s->col_bd[t_x + 1] - s->col_bd[t_x];
+}
+
+static unsigned int tile_height(const struct rpivid_dec_state *const s,
+				const unsigned int t_y)
+{
+	return s->row_bd[t_y + 1] - s->row_bd[t_y];
+}
+
+static void fill_rs_to_ts(struct rpivid_dec_state *const s)
+{
+	unsigned int ts = 0;
+	unsigned int t_y;
+	unsigned int tr_rs = 0;
+
+	for (t_y = 0; t_y != s->tile_height; ++t_y) {
+		const unsigned int t_h = tile_height(s, t_y);
+		unsigned int t_x;
+		unsigned int tc_rs = tr_rs;
+
+		for (t_x = 0; t_x != s->tile_width; ++t_x) {
+			const unsigned int t_w = tile_width(s, t_x);
+			unsigned int y;
+			unsigned int rs = tc_rs;
+
+			for (y = 0; y != t_h; ++y) {
+				unsigned int x;
+
+				for (x = 0; x != t_w; ++x) {
+					s->ctb_addr_rs_to_ts[rs + x] = ts;
+					s->ctb_addr_ts_to_rs[ts] = rs + x;
+					++ts;
+				}
+				rs += s->ctb_width;
+			}
+			tc_rs += t_w;
+		}
+		tr_rs += t_h * s->ctb_width;
+	}
+}
+
+static int updated_ps(struct rpivid_dec_state *const s)
+{
+	unsigned int i;
+
+	free_ps_info(s);
+
+	// Inferred parameters
+	s->log2_ctb_size = s->sps.log2_min_luma_coding_block_size_minus3 + 3 +
+			   s->sps.log2_diff_max_min_luma_coding_block_size;
+
+	s->ctb_width = (s->sps.pic_width_in_luma_samples +
+			(1 << s->log2_ctb_size) - 1) >>
+		       s->log2_ctb_size;
+	s->ctb_height = (s->sps.pic_height_in_luma_samples +
+			 (1 << s->log2_ctb_size) - 1) >>
+			s->log2_ctb_size;
+	s->ctb_size = s->ctb_width * s->ctb_height;
+
+	// Inferred parameters
+
+	s->ctb_addr_rs_to_ts = kmalloc_array(s->ctb_size,
+					     sizeof(*s->ctb_addr_rs_to_ts),
+					     GFP_KERNEL);
+	if (!s->ctb_addr_rs_to_ts)
+		goto fail;
+	s->ctb_addr_ts_to_rs = kmalloc_array(s->ctb_size,
+					     sizeof(*s->ctb_addr_ts_to_rs),
+					     GFP_KERNEL);
+	if (!s->ctb_addr_ts_to_rs)
+		goto fail;
+
+	if (!(s->pps.flags & V4L2_HEVC_PPS_FLAG_TILES_ENABLED)) {
+		s->tile_width = 1;
+		s->tile_height = 1;
+	} else {
+		s->tile_width = s->pps.num_tile_columns_minus1 + 1;
+		s->tile_height = s->pps.num_tile_rows_minus1 + 1;
+	}
+
+	s->col_bd = kmalloc((s->tile_width + 1) * sizeof(*s->col_bd),
+			    GFP_KERNEL);
+	if (!s->col_bd)
+		goto fail;
+	s->row_bd = kmalloc((s->tile_height + 1) * sizeof(*s->row_bd),
+			    GFP_KERNEL);
+	if (!s->row_bd)
+		goto fail;
+
+	s->col_bd[0] = 0;
+	for (i = 1; i < s->tile_width; i++)
+		s->col_bd[i] = s->col_bd[i - 1] +
+			s->pps.column_width_minus1[i - 1] + 1;
+	s->col_bd[s->tile_width] = s->ctb_width;
+
+	s->row_bd[0] = 0;
+	for (i = 1; i < s->tile_height; i++)
+		s->row_bd[i] = s->row_bd[i - 1] +
+			s->pps.row_height_minus1[i - 1] + 1;
+	s->row_bd[s->tile_height] = s->ctb_height;
+
+	fill_rs_to_ts(s);
+	return 0;
+
+fail:
+	free_ps_info(s);
+	/* Set invalid to force reload */
+	s->sps.pic_width_in_luma_samples = 0;
+	return -ENOMEM;
+}
+
+static int write_cmd_buffer(struct rpivid_dev *const dev,
+			    struct rpivid_dec_env *const de,
+			    const struct rpivid_dec_state *const s)
+{
+	const size_t cmd_size = ALIGN(de->cmd_len * sizeof(de->cmd_fifo[0]),
+				      dev->cache_align);
+
+	de->cmd_addr = dma_map_single(dev->dev, de->cmd_fifo,
+				      cmd_size, DMA_TO_DEVICE);
+	if (dma_mapping_error(dev->dev, de->cmd_addr)) {
+		v4l2_err(&dev->v4l2_dev,
+			 "Map cmd buffer (%zu): FAILED\n", cmd_size);
+		return -ENOMEM;
+	}
+	de->cmd_size = cmd_size;
+	return 0;
+}
+
+static void setup_colmv(struct rpivid_ctx *const ctx, struct rpivid_run *run,
+			struct rpivid_dec_state *const s)
+{
+	ctx->colmv_stride = ALIGN(s->sps.pic_width_in_luma_samples, 64);
+	ctx->colmv_picsize = ctx->colmv_stride *
+		(ALIGN(s->sps.pic_height_in_luma_samples, 64) >> 4);
+}
+
+// Can be called from irq context
+static struct rpivid_dec_env *dec_env_new(struct rpivid_ctx *const ctx)
+{
+	struct rpivid_dec_env *de;
+	unsigned long lock_flags;
+
+	spin_lock_irqsave(&ctx->dec_lock, lock_flags);
+
+	de = ctx->dec_free;
+	if (de) {
+		ctx->dec_free = de->next;
+		de->next = NULL;
+		de->state = RPIVID_DECODE_SLICE_START;
+	}
+
+	spin_unlock_irqrestore(&ctx->dec_lock, lock_flags);
+	return de;
+}
+
+// Can be called from irq context
+static void dec_env_delete(struct rpivid_dec_env *const de)
+{
+	struct rpivid_ctx * const ctx = de->ctx;
+	unsigned long lock_flags;
+
+	if (de->cmd_size) {
+		dma_unmap_single(ctx->dev->dev, de->cmd_addr, de->cmd_size,
+				 DMA_TO_DEVICE);
+		de->cmd_size = 0;
+	}
+
+	aux_q_release(ctx, &de->frame_aux);
+	aux_q_release(ctx, &de->col_aux);
+
+	spin_lock_irqsave(&ctx->dec_lock, lock_flags);
+
+	de->state = RPIVID_DECODE_END;
+	de->next = ctx->dec_free;
+	ctx->dec_free = de;
+
+	spin_unlock_irqrestore(&ctx->dec_lock, lock_flags);
+}
+
+static void dec_env_uninit(struct rpivid_ctx *const ctx)
+{
+	unsigned int i;
+
+	if (ctx->dec_pool) {
+		for (i = 0; i != RPIVID_DEC_ENV_COUNT; ++i) {
+			struct rpivid_dec_env *const de = ctx->dec_pool + i;
+
+			kfree(de->cmd_fifo);
+		}
+
+		kfree(ctx->dec_pool);
+	}
+
+	ctx->dec_pool = NULL;
+	ctx->dec_free = NULL;
+}
+
+static int dec_env_init(struct rpivid_ctx *const ctx)
+{
+	unsigned int i;
+
+	ctx->dec_pool = kzalloc(sizeof(*ctx->dec_pool) * RPIVID_DEC_ENV_COUNT,
+				GFP_KERNEL);
+	if (!ctx->dec_pool)
+		return -1;
+
+	spin_lock_init(&ctx->dec_lock);
+
+	// Build free chain
+	ctx->dec_free = ctx->dec_pool;
+	for (i = 0; i != RPIVID_DEC_ENV_COUNT - 1; ++i)
+		ctx->dec_pool[i].next = ctx->dec_pool + i + 1;
+
+	// Fill in other bits
+	for (i = 0; i != RPIVID_DEC_ENV_COUNT; ++i) {
+		struct rpivid_dec_env *const de = ctx->dec_pool + i;
+
+		de->ctx = ctx;
+		de->decode_order = i;
+//		de->cmd_max = 1024;
+		de->cmd_max = 8096;
+		de->cmd_fifo = kmalloc_array(de->cmd_max,
+					     sizeof(struct rpi_cmd),
+					     GFP_KERNEL);
+		if (!de->cmd_fifo)
+			goto fail;
+	}
+
+	return 0;
+
+fail:
+	dec_env_uninit(ctx);
+	return -1;
+}
+
+// Assume that we get exactly the same DPB for every slice
+// it makes no real sense otherwise
+#if V4L2_HEVC_DPB_ENTRIES_NUM_MAX > 16
+#error HEVC_DPB_ENTRIES > h/w slots
+#endif
+
+static u32 mk_config2(const struct rpivid_dec_state *const s)
+{
+	const struct v4l2_ctrl_hevc_sps *const sps = &s->sps;
+	const struct v4l2_ctrl_hevc_pps *const pps = &s->pps;
+	u32 c;
+	// BitDepthY
+	c = (sps->bit_depth_luma_minus8 + 8) << 0;
+	 // BitDepthC
+	c |= (sps->bit_depth_chroma_minus8 + 8) << 4;
+	 // BitDepthY
+	if (sps->bit_depth_luma_minus8)
+		c |= BIT(8);
+	// BitDepthC
+	if (sps->bit_depth_chroma_minus8)
+		c |= BIT(9);
+	c |= s->log2_ctb_size << 10;
+	if (pps->flags & V4L2_HEVC_PPS_FLAG_CONSTRAINED_INTRA_PRED)
+		c |= BIT(13);
+	if (sps->flags & V4L2_HEVC_SPS_FLAG_STRONG_INTRA_SMOOTHING_ENABLED)
+		c |= BIT(14);
+	if (sps->flags & V4L2_HEVC_SPS_FLAG_SPS_TEMPORAL_MVP_ENABLED)
+		c |= BIT(15); /* Write motion vectors to external memory */
+	c |= (pps->log2_parallel_merge_level_minus2 + 2) << 16;
+	if (s->slice_temporal_mvp)
+		c |= BIT(19);
+	if (sps->flags & V4L2_HEVC_SPS_FLAG_PCM_LOOP_FILTER_DISABLED)
+		c |= BIT(20);
+	c |= (pps->pps_cb_qp_offset & 31) << 21;
+	c |= (pps->pps_cr_qp_offset & 31) << 26;
+	return c;
+}
+
+static void rpivid_h265_setup(struct rpivid_ctx *ctx, struct rpivid_run *run)
+{
+	struct rpivid_dev *const dev = ctx->dev;
+	const struct v4l2_ctrl_hevc_slice_params *const sh =
+						run->h265.slice_params;
+//	const struct v4l2_hevc_pred_weight_table *pred_weight_table;
+	struct rpivid_q_aux *dpb_q_aux[V4L2_HEVC_DPB_ENTRIES_NUM_MAX];
+	struct rpivid_dec_state *const s = ctx->state;
+	struct vb2_queue *vq;
+	struct rpivid_dec_env *de = ctx->dec0;
+	unsigned int prev_rs;
+	unsigned int i;
+	int use_aux;
+	int rv;
+	bool slice_temporal_mvp;
+
+	xtrace_in(dev, de);
+
+//	pred_weight_table = &sh->pred_weight_table;
+
+	s->frame_end =
+		((run->src->flags & V4L2_BUF_FLAG_M2M_HOLD_CAPTURE_BUF) == 0);
+
+	slice_temporal_mvp = (sh->flags &
+		   V4L2_HEVC_SLICE_PARAMS_FLAG_SLICE_TEMPORAL_MVP_ENABLED);
+
+	if (de && de->state != RPIVID_DECODE_END) {
+		++s->slice_idx;
+
+		switch (de->state) {
+		case RPIVID_DECODE_SLICE_CONTINUE:
+			// Expected state
+			break;
+		default:
+			v4l2_err(&dev->v4l2_dev, "%s: Unexpected state: %d\n",
+				 __func__, de->state);
+		/* FALLTHRU */
+		case RPIVID_DECODE_ERROR_CONTINUE:
+			// Uncleared error - fail now
+			goto fail;
+		}
+
+		if (s->slice_temporal_mvp != slice_temporal_mvp) {
+			v4l2_warn(&dev->v4l2_dev,
+				  "Slice Temporal MVP non-constant\n");
+			goto fail;
+		}
+	} else {
+		/* Frame start */
+		unsigned int ctb_size_y;
+		bool sps_changed = false;
+
+		if (memcmp(&s->sps, run->h265.sps, sizeof(s->sps)) != 0) {
+			/* SPS changed */
+			v4l2_info(&dev->v4l2_dev, "SPS changed\n");
+			memcpy(&s->sps, run->h265.sps, sizeof(s->sps));
+			sps_changed = true;
+		}
+		if (sps_changed ||
+		    memcmp(&s->pps, run->h265.pps, sizeof(s->pps)) != 0) {
+			/* SPS changed */
+			v4l2_info(&dev->v4l2_dev, "PPS changed\n");
+			memcpy(&s->pps, run->h265.pps, sizeof(s->pps));
+
+			/* Recalc stuff as required */
+			rv = updated_ps(s);
+			if (rv)
+				goto fail;
+		}
+
+		de = dec_env_new(ctx);
+		if (!de) {
+			v4l2_err(&dev->v4l2_dev,
+				 "Failed to find free decode env\n");
+			goto fail;
+		}
+		ctx->dec0 = de;
+
+		ctb_size_y =
+			1U << (s->sps.log2_min_luma_coding_block_size_minus3 +
+			       3 +
+			       s->sps.log2_diff_max_min_luma_coding_block_size);
+
+		de->pic_width_in_ctbs_y =
+			(s->sps.pic_width_in_luma_samples + ctb_size_y - 1) /
+				ctb_size_y; // 7-15
+		de->pic_height_in_ctbs_y =
+			(s->sps.pic_height_in_luma_samples + ctb_size_y - 1) /
+				ctb_size_y; // 7-17
+		de->cmd_len = 0;
+		de->dpbno_col = ~0U;
+
+		de->bit_copy_gptr = ctx->bitbufs + ctx->p1idx;
+		de->bit_copy_len = 0;
+
+		de->frame_c_offset = ctx->dst_fmt.height * 128;
+		de->frame_stride = ctx->dst_fmt.plane_fmt[0].bytesperline * 128;
+		de->frame_addr =
+			vb2_dma_contig_plane_dma_addr(&run->dst->vb2_buf, 0);
+		de->frame_aux = NULL;
+
+		if (s->sps.bit_depth_luma_minus8 !=
+		    s->sps.bit_depth_chroma_minus8) {
+			v4l2_warn(&dev->v4l2_dev,
+				  "Chroma depth (%d) != Luma depth (%d)\n",
+				  s->sps.bit_depth_chroma_minus8 + 8,
+				  s->sps.bit_depth_luma_minus8 + 8);
+			goto fail;
+		}
+		if (s->sps.bit_depth_luma_minus8 == 0) {
+			if (ctx->dst_fmt.pixelformat !=
+						V4L2_PIX_FMT_NV12_COL128) {
+				v4l2_err(&dev->v4l2_dev,
+					 "Pixel format %#x != NV12_COL128 for 8-bit output",
+					 ctx->dst_fmt.pixelformat);
+				goto fail;
+			}
+		} else if (s->sps.bit_depth_luma_minus8 == 2) {
+			if (ctx->dst_fmt.pixelformat !=
+						V4L2_PIX_FMT_NV12_10_COL128) {
+				v4l2_err(&dev->v4l2_dev,
+					 "Pixel format %#x != NV12_10_COL128 for 10-bit output",
+					 ctx->dst_fmt.pixelformat);
+				goto fail;
+			}
+		} else {
+			v4l2_warn(&dev->v4l2_dev,
+				  "Luma depth (%d) unsupported\n",
+				  s->sps.bit_depth_luma_minus8 + 8);
+			goto fail;
+		}
+		if (run->dst->vb2_buf.num_planes != 1) {
+			v4l2_warn(&dev->v4l2_dev, "Capture planes (%d) != 1\n",
+				  run->dst->vb2_buf.num_planes);
+			goto fail;
+		}
+		if (run->dst->planes[0].length <
+		    ctx->dst_fmt.plane_fmt[0].sizeimage) {
+			v4l2_warn(&dev->v4l2_dev,
+				  "Capture plane[0] length (%d) < sizeimage (%d)\n",
+				  run->dst->planes[0].length,
+				  ctx->dst_fmt.plane_fmt[0].sizeimage);
+			goto fail;
+		}
+
+		// Fill in ref planes with our address s.t. if we mess
+		// up refs somehow then we still have a valid address
+		// entry
+		for (i = 0; i != 16; ++i)
+			de->ref_addrs[i] = de->frame_addr;
+
+		/*
+		 * Stash initial temporal_mvp flag
+		 * This must be the same for all pic slices (7.4.7.1)
+		 */
+		s->slice_temporal_mvp = slice_temporal_mvp;
+
+		// Phase 2 reg pre-calc
+		de->rpi_config2 = mk_config2(s);
+		de->rpi_framesize = (s->sps.pic_height_in_luma_samples << 16) |
+				    s->sps.pic_width_in_luma_samples;
+		de->rpi_currpoc = sh->slice_pic_order_cnt;
+
+		if (s->sps.flags &
+		    V4L2_HEVC_SPS_FLAG_SPS_TEMPORAL_MVP_ENABLED) {
+			setup_colmv(ctx, run, s);
+		}
+
+		s->slice_idx = 0;
+
+		if (sh->slice_segment_addr != 0) {
+			v4l2_warn(&dev->v4l2_dev,
+				  "New frame but segment_addr=%d\n",
+				  sh->slice_segment_addr);
+			goto fail;
+		}
+
+		/* Allocate a bitbuf if we need one - don't need one if single
+		 * slice as we can use the src buf directly
+		 */
+		if (!s->frame_end && !de->bit_copy_gptr->ptr) {
+			size_t bits_alloc;
+			bits_alloc = rpivid_bit_buf_size(s->sps.pic_width_in_luma_samples,
+							 s->sps.pic_height_in_luma_samples,
+							 s->sps.bit_depth_luma_minus8);
+
+			if (gptr_alloc(dev, de->bit_copy_gptr,
+				       bits_alloc,
+				       DMA_ATTR_FORCE_CONTIGUOUS) != 0) {
+				v4l2_err(&dev->v4l2_dev,
+					 "Unable to alloc buf (%zu) for bit copy\n",
+					 bits_alloc);
+				goto fail;
+			}
+			v4l2_info(&dev->v4l2_dev,
+				  "Alloc buf (%zu) for bit copy OK\n",
+				  bits_alloc);
+		}
+	}
+
+	// Either map src buffer or use directly
+	s->src_addr = 0;
+	s->src_buf = NULL;
+
+	if (run->src->planes[0].bytesused < (sh->bit_size + 7) / 8) {
+		v4l2_warn(&dev->v4l2_dev,
+			  "Bit size %d > bytesused %d\n",
+			  sh->bit_size, run->src->planes[0].bytesused);
+		goto fail;
+	}
+	if (sh->data_bit_offset >= sh->bit_size ||
+	    sh->bit_size - sh->data_bit_offset < 8) {
+		v4l2_warn(&dev->v4l2_dev,
+			  "Bit size %d < Bit offset %d + 8\n",
+			  sh->bit_size, sh->data_bit_offset);
+		goto fail;
+	}
+
+	if (s->frame_end)
+		s->src_addr = vb2_dma_contig_plane_dma_addr(&run->src->vb2_buf,
+							    0);
+	if (!s->src_addr)
+		s->src_buf = vb2_plane_vaddr(&run->src->vb2_buf, 0);
+	if (!s->src_addr && !s->src_buf) {
+		v4l2_err(&dev->v4l2_dev, "Failed to map src buffer\n");
+		goto fail;
+	}
+
+	// Pre calc a few things
+	s->sh = sh;
+	s->slice_qp = 26 + s->pps.init_qp_minus26 + s->sh->slice_qp_delta;
+	s->max_num_merge_cand = sh->slice_type == HEVC_SLICE_I ?
+					0 :
+					(5 - sh->five_minus_max_num_merge_cand);
+	// * SH DSS flag invented by me - but clearly needed
+	s->dependent_slice_segment_flag =
+		((sh->flags &
+		  V4L2_HEVC_SLICE_PARAMS_FLAG_DEPENDENT_SLICE_SEGMENT) != 0);
+
+	s->nb_refs[0] = (sh->slice_type == HEVC_SLICE_I) ?
+				0 :
+				sh->num_ref_idx_l0_active_minus1 + 1;
+	s->nb_refs[1] = (sh->slice_type != HEVC_SLICE_B) ?
+				0 :
+				sh->num_ref_idx_l1_active_minus1 + 1;
+
+	if (s->sps.flags & V4L2_HEVC_SPS_FLAG_SCALING_LIST_ENABLED)
+		populate_scaling_factors(run, de, s);
+
+	// Calc all the random coord info to avoid repeated conversion in/out
+	s->start_ts = s->ctb_addr_rs_to_ts[sh->slice_segment_addr];
+	s->start_ctb_x = sh->slice_segment_addr % de->pic_width_in_ctbs_y;
+	s->start_ctb_y = sh->slice_segment_addr / de->pic_width_in_ctbs_y;
+	// Last CTB of previous slice
+	prev_rs = !s->start_ts ? 0 : s->ctb_addr_ts_to_rs[s->start_ts - 1];
+	s->prev_ctb_x = prev_rs % de->pic_width_in_ctbs_y;
+	s->prev_ctb_y = prev_rs / de->pic_width_in_ctbs_y;
+
+	if ((s->pps.flags & V4L2_HEVC_PPS_FLAG_ENTROPY_CODING_SYNC_ENABLED))
+		rv = wpp_decode_slice(de, s);
+	else
+		rv = decode_slice(de, s);
+	if (rv)
+		goto fail;
+
+	if (!s->frame_end) {
+		xtrace_ok(dev, de);
+		return;
+	}
+
+	// Frame end
+	memset(dpb_q_aux, 0,
+	       sizeof(*dpb_q_aux) * V4L2_HEVC_DPB_ENTRIES_NUM_MAX);
+	/*
+	 * Need Aux ents for all (ref) DPB ents if temporal MV could
+	 * be enabled for any pic
+	 * ** At the moment we create aux ents for all pics whether or not
+	 *    they are ref - they should then be discarded by the DPB-aux
+	 *    garbage collection code
+	 */
+	use_aux = ((s->sps.flags &
+		  V4L2_HEVC_SPS_FLAG_SPS_TEMPORAL_MVP_ENABLED) != 0);
+
+	// Locate ref frames
+	// At least in the current implementation this is constant across all
+	// slices. If this changes we will need idx mapping code.
+	// Uses sh so here rather than trigger
+
+	vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx,
+			     V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
+
+	if (!vq) {
+		v4l2_err(&dev->v4l2_dev, "VQ gone!\n");
+		goto fail;
+	}
+
+	//        v4l2_info(&dev->v4l2_dev, "rpivid_h265_end of frame\n");
+	if (write_cmd_buffer(dev, de, s))
+		goto fail;
+
+	for (i = 0; i < sh->num_active_dpb_entries; ++i) {
+		int buffer_index =
+			vb2_find_timestamp(vq, sh->dpb[i].timestamp, 0);
+		struct vb2_buffer *buf = buffer_index < 0 ?
+					NULL :
+					vb2_get_buffer(vq, buffer_index);
+
+		if (!buf) {
+			v4l2_warn(&dev->v4l2_dev,
+				  "Missing DPB ent %d, timestamp=%lld, index=%d\n",
+				  i, (long long)sh->dpb[i].timestamp,
+				  buffer_index);
+			continue;
+		}
+
+		if (use_aux) {
+			dpb_q_aux[i] = aux_q_ref_idx(ctx, buffer_index);
+			if (!dpb_q_aux[i])
+				v4l2_warn(&dev->v4l2_dev,
+					  "Missing DPB AUX ent %d, timestamp=%lld, index=%d\n",
+					  i, (long long)sh->dpb[i].timestamp,
+					  buffer_index);
+		}
+
+		de->ref_addrs[i] =
+			vb2_dma_contig_plane_dma_addr(buf, 0);
+	}
+
+	// Move DPB from temp
+	for (i = 0; i != V4L2_HEVC_DPB_ENTRIES_NUM_MAX; ++i) {
+		aux_q_release(ctx, &s->ref_aux[i]);
+		s->ref_aux[i] = dpb_q_aux[i];
+	}
+	// Unref the old frame aux too - it is either in the DPB or not
+	// now
+	aux_q_release(ctx, &s->frame_aux);
+
+	if (use_aux) {
+		// New frame so new aux ent
+		// ??? Do we need this if non-ref ??? can we tell
+		s->frame_aux = aux_q_new(ctx, run->dst->vb2_buf.index);
+
+		if (!s->frame_aux) {
+			v4l2_err(&dev->v4l2_dev,
+				 "Failed to obtain aux storage for frame\n");
+			goto fail;
+		}
+
+		de->frame_aux = aux_q_ref(ctx, s->frame_aux);
+	}
+
+	if (de->dpbno_col != ~0U) {
+		if (de->dpbno_col >= sh->num_active_dpb_entries) {
+			v4l2_err(&dev->v4l2_dev,
+				 "Col ref index %d >= %d\n",
+				 de->dpbno_col,
+				 sh->num_active_dpb_entries);
+		} else {
+			// Standard requires that the col pic is
+			// constant for the duration of the pic
+			// (text of collocated_ref_idx in H265-2 2018
+			// 7.4.7.1)
+
+			// Spot the collocated ref in passing
+			de->col_aux = aux_q_ref(ctx,
+						dpb_q_aux[de->dpbno_col]);
+
+			if (!de->col_aux) {
+				v4l2_warn(&dev->v4l2_dev,
+					  "Missing DPB ent for col\n");
+				// Probably need to abort if this fails
+				// as P2 may explode on bad data
+				goto fail;
+			}
+		}
+	}
+
+	de->state = RPIVID_DECODE_PHASE1;
+	xtrace_ok(dev, de);
+	return;
+
+fail:
+	if (de)
+		// Actual error reporting happens in Trigger
+		de->state = s->frame_end ? RPIVID_DECODE_ERROR_DONE :
+					   RPIVID_DECODE_ERROR_CONTINUE;
+	xtrace_fail(dev, de);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// Handle PU and COEFF stream overflow
+
+// Returns:
+// -1  Phase 1 decode error
+//  0  OK
+// >0  Out of space (bitmask)
+
+#define STATUS_COEFF_EXHAUSTED	8
+#define STATUS_PU_EXHAUSTED	16
+
+static int check_status(const struct rpivid_dev *const dev)
+{
+	const u32 cfstatus = apb_read(dev, RPI_CFSTATUS);
+	const u32 cfnum = apb_read(dev, RPI_CFNUM);
+	u32 status = apb_read(dev, RPI_STATUS);
+
+	// Handle PU and COEFF stream overflow
+
+	// this is the definition of successful completion of phase 1
+	// it assures that status register is zero and all blocks in each tile
+	// have completed
+	if (cfstatus == cfnum)
+		return 0;	//No error
+
+	status &= (STATUS_PU_EXHAUSTED | STATUS_COEFF_EXHAUSTED);
+	if (status)
+		return status;
+
+	return -1;
+}
+
+static void phase2_cb(struct rpivid_dev *const dev, void *v)
+{
+	struct rpivid_dec_env *const de = v;
+
+	xtrace_in(dev, de);
+
+	/* Done with buffers - allow new P1 */
+	rpivid_hw_irq_active1_enable_claim(dev, 1);
+
+	v4l2_m2m_buf_done(de->frame_buf, VB2_BUF_STATE_DONE);
+	de->frame_buf = NULL;
+
+#if USE_REQUEST_PIN
+	media_request_unpin(de->req_pin);
+	de->req_pin = NULL;
+#else
+	media_request_object_complete(de->req_obj);
+	de->req_obj = NULL;
+#endif
+
+	xtrace_ok(dev, de);
+	dec_env_delete(de);
+}
+
+static void phase2_claimed(struct rpivid_dev *const dev, void *v)
+{
+	struct rpivid_dec_env *const de = v;
+	unsigned int i;
+
+	xtrace_in(dev, de);
+
+	apb_write_vc_addr(dev, RPI_PURBASE, de->pu_base_vc);
+	apb_write_vc_len(dev, RPI_PURSTRIDE, de->pu_stride);
+	apb_write_vc_addr(dev, RPI_COEFFRBASE, de->coeff_base_vc);
+	apb_write_vc_len(dev, RPI_COEFFRSTRIDE, de->coeff_stride);
+
+	apb_write_vc_addr(dev, RPI_OUTYBASE, de->frame_addr);
+	apb_write_vc_addr(dev, RPI_OUTCBASE,
+			  de->frame_addr + de->frame_c_offset);
+	apb_write_vc_len(dev, RPI_OUTYSTRIDE, de->frame_stride);
+	apb_write_vc_len(dev, RPI_OUTCSTRIDE, de->frame_stride);
+
+	//    v4l2_info(&dev->v4l2_dev, "Frame: Y=%llx, C=%llx, Stride=%x\n",
+	//              de->frame_addr, de->frame_addr + de->frame_c_offset,
+	//              de->frame_stride);
+
+	for (i = 0; i < 16; i++) {
+		// Strides are in fact unused but fill in anyway
+		apb_write_vc_addr(dev, 0x9000 + 16 * i, de->ref_addrs[i]);
+		apb_write_vc_len(dev, 0x9004 + 16 * i, de->frame_stride);
+		apb_write_vc_addr(dev, 0x9008 + 16 * i,
+				  de->ref_addrs[i] + de->frame_c_offset);
+		apb_write_vc_len(dev, 0x900C + 16 * i, de->frame_stride);
+	}
+
+	apb_write(dev, RPI_CONFIG2, de->rpi_config2);
+	apb_write(dev, RPI_FRAMESIZE, de->rpi_framesize);
+	apb_write(dev, RPI_CURRPOC, de->rpi_currpoc);
+	//    v4l2_info(&dev->v4l2_dev, "Config2=%#x, FrameSize=%#x, POC=%#x\n",
+	//    de->rpi_config2, de->rpi_framesize, de->rpi_currpoc);
+
+	// collocated reads/writes
+	apb_write_vc_len(dev, RPI_COLSTRIDE,
+			 de->ctx->colmv_stride); // Read vals
+	apb_write_vc_len(dev, RPI_MVSTRIDE,
+			 de->ctx->colmv_stride); // Write vals
+	apb_write_vc_addr(dev, RPI_MVBASE,
+			  !de->frame_aux ? 0 : de->frame_aux->col.addr);
+	apb_write_vc_addr(dev, RPI_COLBASE,
+			  !de->col_aux ? 0 : de->col_aux->col.addr);
+
+	//v4l2_info(&dev->v4l2_dev,
+	//	   "Mv=%llx, Col=%llx, Stride=%x, Buf=%llx->%llx\n",
+	//	   de->rpi_mvbase, de->rpi_colbase, de->ctx->colmv_stride,
+	//	   de->ctx->colmvbuf.addr, de->ctx->colmvbuf.addr +
+	//	   de->ctx->colmvbuf.size);
+
+	rpivid_hw_irq_active2_irq(dev, &de->irq_ent, phase2_cb, de);
+
+	apb_write_final(dev, RPI_NUMROWS, de->pic_height_in_ctbs_y);
+
+	xtrace_ok(dev, de);
+}
+
+static void phase1_claimed(struct rpivid_dev *const dev, void *v);
+
+// release any and all objects associated with de
+// and reenable phase 1 if required
+static void phase1_err_fin(struct rpivid_dev *const dev,
+			   struct rpivid_ctx *const ctx,
+			   struct rpivid_dec_env *const de)
+{
+	/* Return all detached buffers */
+	if (de->src_buf)
+		v4l2_m2m_buf_done(de->src_buf, VB2_BUF_STATE_ERROR);
+	de->src_buf = NULL;
+	if (de->frame_buf)
+		v4l2_m2m_buf_done(de->frame_buf, VB2_BUF_STATE_ERROR);
+	de->frame_buf = NULL;
+#if USE_REQUEST_PIN
+	if (de->req_pin)
+		media_request_unpin(de->req_pin);
+	de->req_pin = NULL;
+#else
+	if (de->req_obj)
+		media_request_object_complete(de->req_obj);
+	de->req_obj = NULL;
+#endif
+
+	dec_env_delete(de);
+
+	/* Reenable phase 0 if we were blocking */
+	if (atomic_add_return(-1, &ctx->p1out) >= RPIVID_P1BUF_COUNT - 1)
+		v4l2_m2m_job_finish(dev->m2m_dev, ctx->fh.m2m_ctx);
+
+	/* Done with P1-P2 buffers - allow new P1 */
+	rpivid_hw_irq_active1_enable_claim(dev, 1);
+}
+
+static void phase1_thread(struct rpivid_dev *const dev, void *v)
+{
+	struct rpivid_dec_env *const de = v;
+	struct rpivid_ctx *const ctx = de->ctx;
+
+	struct rpivid_gptr *const pu_gptr = ctx->pu_bufs + ctx->p2idx;
+	struct rpivid_gptr *const coeff_gptr = ctx->coeff_bufs + ctx->p2idx;
+
+	xtrace_in(dev, de);
+
+	if (de->p1_status & STATUS_PU_EXHAUSTED) {
+		if (gptr_realloc_new(dev, pu_gptr, next_size(pu_gptr->size))) {
+			v4l2_err(&dev->v4l2_dev,
+				 "%s: PU realloc (%zx) failed\n",
+				 __func__, pu_gptr->size);
+			goto fail;
+		}
+		v4l2_info(&dev->v4l2_dev, "%s: PU realloc (%zx) OK\n",
+			  __func__, pu_gptr->size);
+	}
+
+	if (de->p1_status & STATUS_COEFF_EXHAUSTED) {
+		if (gptr_realloc_new(dev, coeff_gptr,
+				     next_size(coeff_gptr->size))) {
+			v4l2_err(&dev->v4l2_dev,
+				 "%s: Coeff realloc (%zx) failed\n",
+				 __func__, coeff_gptr->size);
+			goto fail;
+		}
+		v4l2_info(&dev->v4l2_dev, "%s: Coeff realloc (%zx) OK\n",
+			  __func__, coeff_gptr->size);
+	}
+
+	phase1_claimed(dev, de);
+	xtrace_ok(dev, de);
+	return;
+
+fail:
+	if (!pu_gptr->addr || !coeff_gptr->addr) {
+		v4l2_err(&dev->v4l2_dev,
+			 "%s: Fatal: failed to reclaim old alloc\n",
+			 __func__);
+		ctx->fatal_err = 1;
+	}
+	xtrace_fail(dev, de);
+	phase1_err_fin(dev, ctx, de);
+}
+
+/* Always called in irq context (this is good) */
+static void phase1_cb(struct rpivid_dev *const dev, void *v)
+{
+	struct rpivid_dec_env *const de = v;
+	struct rpivid_ctx *const ctx = de->ctx;
+
+	xtrace_in(dev, de);
+
+	de->p1_status = check_status(dev);
+
+	if (de->p1_status != 0) {
+		v4l2_info(&dev->v4l2_dev, "%s: Post wait: %#x\n",
+			  __func__, de->p1_status);
+
+		if (de->p1_status < 0)
+			goto fail;
+
+		/* Need to realloc - push onto a thread rather than IRQ */
+		rpivid_hw_irq_active1_thread(dev, &de->irq_ent,
+					     phase1_thread, de);
+		return;
+	}
+
+	v4l2_m2m_buf_done(de->src_buf, VB2_BUF_STATE_DONE);
+	de->src_buf = NULL;
+
+	/* All phase1 error paths done - it is safe to inc p2idx */
+	ctx->p2idx =
+		(ctx->p2idx + 1 >= RPIVID_P2BUF_COUNT) ? 0 : ctx->p2idx + 1;
+
+	/* Renable the next setup if we were blocking */
+	if (atomic_add_return(-1, &ctx->p1out) >= RPIVID_P1BUF_COUNT - 1) {
+		xtrace_fin(dev, de);
+		v4l2_m2m_job_finish(dev->m2m_dev, ctx->fh.m2m_ctx);
+	}
+
+	rpivid_hw_irq_active2_claim(dev, &de->irq_ent, phase2_claimed, de);
+
+	xtrace_ok(dev, de);
+	return;
+
+fail:
+	xtrace_fail(dev, de);
+	phase1_err_fin(dev, ctx, de);
+}
+
+static void phase1_claimed(struct rpivid_dev *const dev, void *v)
+{
+	struct rpivid_dec_env *const de = v;
+	struct rpivid_ctx *const ctx = de->ctx;
+
+	const struct rpivid_gptr * const pu_gptr = ctx->pu_bufs + ctx->p2idx;
+	const struct rpivid_gptr * const coeff_gptr = ctx->coeff_bufs +
+						      ctx->p2idx;
+
+	xtrace_in(dev, de);
+
+	if (ctx->fatal_err)
+		goto fail;
+
+	de->pu_base_vc = pu_gptr->addr;
+	de->pu_stride =
+		ALIGN_DOWN(pu_gptr->size / de->pic_height_in_ctbs_y, 64);
+
+	de->coeff_base_vc = coeff_gptr->addr;
+	de->coeff_stride =
+		ALIGN_DOWN(coeff_gptr->size / de->pic_height_in_ctbs_y, 64);
+
+	/* phase1_claimed blocked until cb_phase1 completed so p2idx inc
+	 * in cb_phase1 after error detection
+	 */
+
+	apb_write_vc_addr(dev, RPI_PUWBASE, de->pu_base_vc);
+	apb_write_vc_len(dev, RPI_PUWSTRIDE, de->pu_stride);
+	apb_write_vc_addr(dev, RPI_COEFFWBASE, de->coeff_base_vc);
+	apb_write_vc_len(dev, RPI_COEFFWSTRIDE, de->coeff_stride);
+
+	// Trigger command FIFO
+	apb_write(dev, RPI_CFNUM, de->cmd_len);
+
+	// Claim irq
+	rpivid_hw_irq_active1_irq(dev, &de->irq_ent, phase1_cb, de);
+
+	// And start the h/w
+	apb_write_vc_addr_final(dev, RPI_CFBASE, de->cmd_addr);
+
+	xtrace_ok(dev, de);
+	return;
+
+fail:
+	xtrace_fail(dev, de);
+	phase1_err_fin(dev, ctx, de);
+}
+
+static void dec_state_delete(struct rpivid_ctx *const ctx)
+{
+	unsigned int i;
+	struct rpivid_dec_state *const s = ctx->state;
+
+	if (!s)
+		return;
+	ctx->state = NULL;
+
+	free_ps_info(s);
+
+	for (i = 0; i != HEVC_MAX_REFS; ++i)
+		aux_q_release(ctx, &s->ref_aux[i]);
+	aux_q_release(ctx, &s->frame_aux);
+
+	kfree(s);
+}
+
+static void rpivid_h265_stop(struct rpivid_ctx *ctx)
+{
+	struct rpivid_dev *const dev = ctx->dev;
+	unsigned int i;
+
+	v4l2_info(&dev->v4l2_dev, "%s\n", __func__);
+
+	dec_env_uninit(ctx);
+	dec_state_delete(ctx);
+
+	// dec_env & state must be killed before this to release the buffer to
+	// the free pool
+	aux_q_uninit(ctx);
+
+	for (i = 0; i != ARRAY_SIZE(ctx->bitbufs); ++i)
+		gptr_free(dev, ctx->bitbufs + i);
+	for (i = 0; i != ARRAY_SIZE(ctx->pu_bufs); ++i)
+		gptr_free(dev, ctx->pu_bufs + i);
+	for (i = 0; i != ARRAY_SIZE(ctx->coeff_bufs); ++i)
+		gptr_free(dev, ctx->coeff_bufs + i);
+}
+
+static int rpivid_h265_start(struct rpivid_ctx *ctx)
+{
+	struct rpivid_dev *const dev = ctx->dev;
+	unsigned int i;
+
+	unsigned int w = ctx->dst_fmt.width;
+	unsigned int h = ctx->dst_fmt.height;
+	unsigned int wxh;
+	size_t pu_alloc;
+	size_t coeff_alloc;
+
+#if DEBUG_TRACE_P1_CMD
+	p1_z = 0;
+#endif
+
+	// Generate a sanitised WxH for memory alloc
+	// Assume HD if unset
+	if (w == 0)
+		w = 1920;
+	if (w > 4096)
+		w = 4096;
+	if (h == 0)
+		h = 1088;
+	if (h > 4096)
+		h = 4096;
+	wxh = w * h;
+
+	v4l2_info(&dev->v4l2_dev, "%s: (%dx%d)\n", __func__,
+		  ctx->dst_fmt.width, ctx->dst_fmt.height);
+
+	ctx->fatal_err = 0;
+	ctx->dec0 = NULL;
+	ctx->state = kzalloc(sizeof(*ctx->state), GFP_KERNEL);
+	if (!ctx->state) {
+		v4l2_err(&dev->v4l2_dev, "Failed to allocate decode state\n");
+		goto fail;
+	}
+
+	if (dec_env_init(ctx) != 0) {
+		v4l2_err(&dev->v4l2_dev, "Failed to allocate decode envs\n");
+		goto fail;
+	}
+
+	// Finger in the air PU & Coeff alloc
+	// Will be realloced if too small
+	coeff_alloc = rpivid_round_up_size(wxh);
+	pu_alloc = rpivid_round_up_size(wxh / 4);
+	for (i = 0; i != ARRAY_SIZE(ctx->pu_bufs); ++i) {
+		// Don't actually need a kernel mapping here
+		if (gptr_alloc(dev, ctx->pu_bufs + i, pu_alloc,
+			       DMA_ATTR_NO_KERNEL_MAPPING))
+			goto fail;
+		if (gptr_alloc(dev, ctx->coeff_bufs + i, coeff_alloc,
+			       DMA_ATTR_NO_KERNEL_MAPPING))
+			goto fail;
+	}
+	aux_q_init(ctx);
+
+	return 0;
+
+fail:
+	rpivid_h265_stop(ctx);
+	return -ENOMEM;
+}
+
+static void rpivid_h265_trigger(struct rpivid_ctx *ctx)
+{
+	struct rpivid_dev *const dev = ctx->dev;
+	struct rpivid_dec_env *const de = ctx->dec0;
+
+	xtrace_in(dev, de);
+
+	switch (!de ? RPIVID_DECODE_ERROR_CONTINUE : de->state) {
+	case RPIVID_DECODE_SLICE_START:
+		de->state = RPIVID_DECODE_SLICE_CONTINUE;
+	/* FALLTHRU */
+	case RPIVID_DECODE_SLICE_CONTINUE:
+		v4l2_m2m_buf_done_and_job_finish(dev->m2m_dev, ctx->fh.m2m_ctx,
+						 VB2_BUF_STATE_DONE);
+		xtrace_ok(dev, de);
+		break;
+
+	default:
+		v4l2_err(&dev->v4l2_dev, "%s: Unexpected state: %d\n", __func__,
+			 de->state);
+	/* FALLTHRU */
+	case RPIVID_DECODE_ERROR_DONE:
+		ctx->dec0 = NULL;
+		dec_env_delete(de);
+	/* FALLTHRU */
+	case RPIVID_DECODE_ERROR_CONTINUE:
+		xtrace_fin(dev, de);
+		v4l2_m2m_buf_done_and_job_finish(dev->m2m_dev, ctx->fh.m2m_ctx,
+						 VB2_BUF_STATE_ERROR);
+		break;
+
+	case RPIVID_DECODE_PHASE1:
+		ctx->dec0 = NULL;
+
+#if !USE_REQUEST_PIN
+		/* Alloc a new request object - needs to be alloced dynamically
+		 * as the media request will release it some random time after
+		 * it is completed
+		 */
+		de->req_obj = kmalloc(sizeof(*de->req_obj), GFP_KERNEL);
+		if (!de->req_obj) {
+			xtrace_fail(dev, de);
+			dec_env_delete(de);
+			v4l2_m2m_buf_done_and_job_finish(dev->m2m_dev,
+							 ctx->fh.m2m_ctx,
+							 VB2_BUF_STATE_ERROR);
+			break;
+		}
+		media_request_object_init(de->req_obj);
+#warning probably needs to _get the req obj too
+#endif
+		ctx->p1idx = (ctx->p1idx + 1 >= RPIVID_P1BUF_COUNT) ?
+							0 : ctx->p1idx + 1;
+
+		/* We know we have src & dst so no need to test */
+		de->src_buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
+		de->frame_buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
+
+#if USE_REQUEST_PIN
+		de->req_pin = de->src_buf->vb2_buf.req_obj.req;
+		media_request_pin(de->req_pin);
+#else
+		media_request_object_bind(de->src_buf->vb2_buf.req_obj.req,
+					  &dst_req_obj_ops, de, false,
+					  de->req_obj);
+#endif
+
+		/* We could get rid of the src buffer here if we've already
+		 * copied it, but we don't copy the last buffer unless it
+		 * didn't return a contig dma addr and that shouldn't happen
+		 */
+
+		/* Enable the next setup if our Q isn't too big */
+		if (atomic_add_return(1, &ctx->p1out) < RPIVID_P1BUF_COUNT) {
+			xtrace_fin(dev, de);
+			v4l2_m2m_job_finish(dev->m2m_dev, ctx->fh.m2m_ctx);
+		}
+
+		rpivid_hw_irq_active1_claim(dev, &de->irq_ent, phase1_claimed,
+					    de);
+		xtrace_ok(dev, de);
+		break;
+	}
+}
+
+const struct rpivid_dec_ops rpivid_dec_ops_h265 = {
+	.setup = rpivid_h265_setup,
+	.start = rpivid_h265_start,
+	.stop = rpivid_h265_stop,
+	.trigger = rpivid_h265_trigger,
+};
+
+static int try_ctrl_sps(struct v4l2_ctrl *ctrl)
+{
+	const struct v4l2_ctrl_hevc_sps *const sps = ctrl->p_new.p_hevc_sps;
+	struct rpivid_ctx *const ctx = ctrl->priv;
+	struct rpivid_dev *const dev = ctx->dev;
+
+	if (sps->chroma_format_idc != 1) {
+		v4l2_warn(&dev->v4l2_dev,
+			  "Chroma format (%d) unsupported\n",
+			  sps->chroma_format_idc);
+		return -EINVAL;
+	}
+
+	if (sps->bit_depth_luma_minus8 != 0 &&
+	    sps->bit_depth_luma_minus8 != 2) {
+		v4l2_warn(&dev->v4l2_dev,
+			  "Luma depth (%d) unsupported\n",
+			  sps->bit_depth_luma_minus8 + 8);
+		return -EINVAL;
+	}
+
+	if (sps->bit_depth_luma_minus8 != sps->bit_depth_chroma_minus8) {
+		v4l2_warn(&dev->v4l2_dev,
+			  "Chroma depth (%d) != Luma depth (%d)\n",
+			  sps->bit_depth_chroma_minus8 + 8,
+			  sps->bit_depth_luma_minus8 + 8);
+		return -EINVAL;
+	}
+
+	if (!sps->pic_width_in_luma_samples ||
+	    !sps->pic_height_in_luma_samples ||
+	    sps->pic_width_in_luma_samples > 4096 ||
+	    sps->pic_height_in_luma_samples > 4096) {
+		v4l2_warn(&dev->v4l2_dev,
+			  "Bad sps width (%u) x height (%u)\n",
+			  sps->pic_width_in_luma_samples,
+			  sps->pic_height_in_luma_samples);
+		return -EINVAL;
+	}
+
+	if (!ctx->dst_fmt_set)
+		return 0;
+
+	if ((sps->bit_depth_luma_minus8 == 0 &&
+	     ctx->dst_fmt.pixelformat != V4L2_PIX_FMT_NV12_COL128) ||
+	    (sps->bit_depth_luma_minus8 == 2 &&
+	     ctx->dst_fmt.pixelformat != V4L2_PIX_FMT_NV12_10_COL128)) {
+		v4l2_warn(&dev->v4l2_dev,
+			  "SPS luma depth %d does not match capture format\n",
+			  sps->bit_depth_luma_minus8 + 8);
+		return -EINVAL;
+	}
+
+	if (sps->pic_width_in_luma_samples > ctx->dst_fmt.width ||
+	    sps->pic_height_in_luma_samples > ctx->dst_fmt.height) {
+		v4l2_warn(&dev->v4l2_dev,
+			  "SPS size (%dx%d) > capture size (%d,%d)\n",
+			  sps->pic_width_in_luma_samples,
+			  sps->pic_height_in_luma_samples,
+			  ctx->dst_fmt.width,
+			  ctx->dst_fmt.height);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+const struct v4l2_ctrl_ops rpivid_hevc_sps_ctrl_ops = {
+	.try_ctrl = try_ctrl_sps,
+};
+
+static int try_ctrl_pps(struct v4l2_ctrl *ctrl)
+{
+	const struct v4l2_ctrl_hevc_pps *const pps = ctrl->p_new.p_hevc_pps;
+	struct rpivid_ctx *const ctx = ctrl->priv;
+	struct rpivid_dev *const dev = ctx->dev;
+
+	if ((pps->flags &
+	     V4L2_HEVC_PPS_FLAG_ENTROPY_CODING_SYNC_ENABLED) &&
+	    (pps->flags &
+	     V4L2_HEVC_PPS_FLAG_TILES_ENABLED) &&
+	    (pps->num_tile_columns_minus1 || pps->num_tile_rows_minus1)) {
+		v4l2_warn(&dev->v4l2_dev,
+			  "WPP + Tiles not supported\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+const struct v4l2_ctrl_ops rpivid_hevc_pps_ctrl_ops = {
+	.try_ctrl = try_ctrl_pps,
+};
+
diff -Nur --no-dereference linux-5.10.52-orig/drivers/staging/media/rpivid/rpivid_hw.c linux-5.10.52-v7l+/drivers/staging/media/rpivid/rpivid_hw.c
--- linux-5.10.52-orig/drivers/staging/media/rpivid/rpivid_hw.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/staging/media/rpivid/rpivid_hw.c	2021-07-25 16:46:25.637961078 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Raspberry Pi HEVC driver
+ *
+ * Copyright (C) 2020 Raspberry Pi (Trading) Ltd
+ *
+ * Based on the Cedrus VPU driver, that is:
+ *
+ * Copyright (C) 2016 Florent Revest <florent.revest@free-electrons.com>
+ * Copyright (C) 2018 Paul Kocialkowski <paul.kocialkowski@bootlin.com>
+ * Copyright (C) 2018 Bootlin
+ */
+#include <linux/clk.h>
+#include <linux/component.h>
+#include <linux/dma-mapping.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/of_reserved_mem.h>
+#include <linux/of_device.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+
+#include <media/videobuf2-core.h>
+#include <media/v4l2-mem2mem.h>
+
+#include "rpivid.h"
+#include "rpivid_hw.h"
+
+static void pre_irq(struct rpivid_dev *dev, struct rpivid_hw_irq_ent *ient,
+		    rpivid_irq_callback cb, void *v,
+		    struct rpivid_hw_irq_ctrl *ictl)
+{
+	unsigned long flags;
+
+	if (ictl->irq) {
+		v4l2_err(&dev->v4l2_dev, "Attempt to claim IRQ when already claimed\n");
+		return;
+	}
+
+	ient->cb = cb;
+	ient->v = v;
+
+	spin_lock_irqsave(&ictl->lock, flags);
+	ictl->irq = ient;
+	ictl->no_sched++;
+	spin_unlock_irqrestore(&ictl->lock, flags);
+}
+
+/* Should be called from inside ictl->lock */
+static inline bool sched_enabled(const struct rpivid_hw_irq_ctrl * const ictl)
+{
+	return ictl->no_sched <= 0 && ictl->enable;
+}
+
+/* Should be called from inside ictl->lock & after checking sched_enabled() */
+static inline void set_claimed(struct rpivid_hw_irq_ctrl * const ictl)
+{
+	if (ictl->enable > 0)
+		--ictl->enable;
+	ictl->no_sched = 1;
+}
+
+/* Should be called from inside ictl->lock */
+static struct rpivid_hw_irq_ent *get_sched(struct rpivid_hw_irq_ctrl * const ictl)
+{
+	struct rpivid_hw_irq_ent *ient;
+
+	if (!sched_enabled(ictl))
+		return NULL;
+
+	ient = ictl->claim;
+	if (!ient)
+		return NULL;
+	ictl->claim = ient->next;
+
+	set_claimed(ictl);
+	return ient;
+}
+
+/* Run a callback & check to see if there is anything else to run */
+static void sched_cb(struct rpivid_dev * const dev,
+		     struct rpivid_hw_irq_ctrl * const ictl,
+		     struct rpivid_hw_irq_ent *ient)
+{
+	while (ient) {
+		unsigned long flags;
+
+		ient->cb(dev, ient->v);
+
+		spin_lock_irqsave(&ictl->lock, flags);
+
+		/* Always dec no_sched after cb exec - must have been set
+		 * on entry to cb
+		 */
+		--ictl->no_sched;
+		ient = get_sched(ictl);
+
+		spin_unlock_irqrestore(&ictl->lock, flags);
+	}
+}
+
+/* Should only ever be called from its own IRQ cb so no lock required */
+static void pre_thread(struct rpivid_dev *dev,
+		       struct rpivid_hw_irq_ent *ient,
+		       rpivid_irq_callback cb, void *v,
+		       struct rpivid_hw_irq_ctrl *ictl)
+{
+	ient->cb = cb;
+	ient->v = v;
+	ictl->irq = ient;
+	ictl->thread_reqed = true;
+	ictl->no_sched++;	/* This is unwound in do_thread */
+}
+
+// Called in irq context
+static void do_irq(struct rpivid_dev * const dev,
+		   struct rpivid_hw_irq_ctrl * const ictl)
+{
+	struct rpivid_hw_irq_ent *ient;
+	unsigned long flags;
+
+	spin_lock_irqsave(&ictl->lock, flags);
+	ient = ictl->irq;
+	ictl->irq = NULL;
+	spin_unlock_irqrestore(&ictl->lock, flags);
+
+	sched_cb(dev, ictl, ient);
+}
+
+static void do_claim(struct rpivid_dev * const dev,
+		     struct rpivid_hw_irq_ent *ient,
+		     const rpivid_irq_callback cb, void * const v,
+		     struct rpivid_hw_irq_ctrl * const ictl)
+{
+	unsigned long flags;
+
+	ient->next = NULL;
+	ient->cb = cb;
+	ient->v = v;
+
+	spin_lock_irqsave(&ictl->lock, flags);
+
+	if (ictl->claim) {
+		// If we have a Q then add to end
+		ictl->tail->next = ient;
+		ictl->tail = ient;
+		ient = NULL;
+	} else if (!sched_enabled(ictl)) {
+		// Empty Q but other activity in progress so Q
+		ictl->claim = ient;
+		ictl->tail = ient;
+		ient = NULL;
+	} else {
+		// Nothing else going on - schedule immediately and
+		// prevent anything else scheduling claims
+		set_claimed(ictl);
+	}
+
+	spin_unlock_irqrestore(&ictl->lock, flags);
+
+	sched_cb(dev, ictl, ient);
+}
+
+/* Enable n claims.
+ * n < 0   set to unlimited (default on init)
+ * n = 0   if previously unlimited then disable otherwise nop
+ * n > 0   if previously unlimited then set to n enables
+ *         otherwise add n enables
+ * The enable count is automatically decremented every time a claim is run
+ */
+static void do_enable_claim(struct rpivid_dev * const dev,
+			    int n,
+			    struct rpivid_hw_irq_ctrl * const ictl)
+{
+	unsigned long flags;
+	struct rpivid_hw_irq_ent *ient;
+
+	spin_lock_irqsave(&ictl->lock, flags);
+	ictl->enable = n < 0 ? -1 : ictl->enable <= 0 ? n : ictl->enable + n;
+	ient = get_sched(ictl);
+	spin_unlock_irqrestore(&ictl->lock, flags);
+
+	sched_cb(dev, ictl, ient);
+}
+
+static void ictl_init(struct rpivid_hw_irq_ctrl * const ictl, int enables)
+{
+	spin_lock_init(&ictl->lock);
+	ictl->claim = NULL;
+	ictl->tail = NULL;
+	ictl->irq = NULL;
+	ictl->no_sched = 0;
+	ictl->enable = enables;
+	ictl->thread_reqed = false;
+}
+
+static void ictl_uninit(struct rpivid_hw_irq_ctrl * const ictl)
+{
+	// Nothing to do
+}
+
+#if !OPT_DEBUG_POLL_IRQ
+static irqreturn_t rpivid_irq_irq(int irq, void *data)
+{
+	struct rpivid_dev * const dev = data;
+	__u32 ictrl;
+
+	ictrl = irq_read(dev, ARG_IC_ICTRL);
+	if (!(ictrl & ARG_IC_ICTRL_ALL_IRQ_MASK)) {
+		v4l2_warn(&dev->v4l2_dev, "IRQ but no IRQ bits set\n");
+		return IRQ_NONE;
+	}
+
+	// Cancel any/all irqs
+	irq_write(dev, ARG_IC_ICTRL, ictrl & ~ARG_IC_ICTRL_SET_ZERO_MASK);
+
+	// Service Active2 before Active1 so Phase 1 can transition to Phase 2
+	// without delay
+	if (ictrl & ARG_IC_ICTRL_ACTIVE2_INT_SET)
+		do_irq(dev, &dev->ic_active2);
+	if (ictrl & ARG_IC_ICTRL_ACTIVE1_INT_SET)
+		do_irq(dev, &dev->ic_active1);
+
+	return dev->ic_active1.thread_reqed || dev->ic_active2.thread_reqed ?
+		IRQ_WAKE_THREAD : IRQ_HANDLED;
+}
+
+static void do_thread(struct rpivid_dev * const dev,
+		      struct rpivid_hw_irq_ctrl *const ictl)
+{
+	unsigned long flags;
+	struct rpivid_hw_irq_ent *ient = NULL;
+
+	spin_lock_irqsave(&ictl->lock, flags);
+
+	if (ictl->thread_reqed) {
+		ient = ictl->irq;
+		ictl->thread_reqed = false;
+		ictl->irq = NULL;
+	}
+
+	spin_unlock_irqrestore(&ictl->lock, flags);
+
+	sched_cb(dev, ictl, ient);
+}
+
+static irqreturn_t rpivid_irq_thread(int irq, void *data)
+{
+	struct rpivid_dev * const dev = data;
+
+	do_thread(dev, &dev->ic_active1);
+	do_thread(dev, &dev->ic_active2);
+
+	return IRQ_HANDLED;
+}
+#endif
+
+/* May only be called from Active1 CB
+ * IRQs should not be expected until execution continues in the cb
+ */
+void rpivid_hw_irq_active1_thread(struct rpivid_dev *dev,
+				  struct rpivid_hw_irq_ent *ient,
+				  rpivid_irq_callback thread_cb, void *ctx)
+{
+	pre_thread(dev, ient, thread_cb, ctx, &dev->ic_active1);
+}
+
+void rpivid_hw_irq_active1_enable_claim(struct rpivid_dev *dev,
+					int n)
+{
+	do_enable_claim(dev, n, &dev->ic_active1);
+}
+
+void rpivid_hw_irq_active1_claim(struct rpivid_dev *dev,
+				 struct rpivid_hw_irq_ent *ient,
+				 rpivid_irq_callback ready_cb, void *ctx)
+{
+	do_claim(dev, ient, ready_cb, ctx, &dev->ic_active1);
+}
+
+void rpivid_hw_irq_active1_irq(struct rpivid_dev *dev,
+			       struct rpivid_hw_irq_ent *ient,
+			       rpivid_irq_callback irq_cb, void *ctx)
+{
+	pre_irq(dev, ient, irq_cb, ctx, &dev->ic_active1);
+}
+
+void rpivid_hw_irq_active2_claim(struct rpivid_dev *dev,
+				 struct rpivid_hw_irq_ent *ient,
+				 rpivid_irq_callback ready_cb, void *ctx)
+{
+	do_claim(dev, ient, ready_cb, ctx, &dev->ic_active2);
+}
+
+void rpivid_hw_irq_active2_irq(struct rpivid_dev *dev,
+			       struct rpivid_hw_irq_ent *ient,
+			       rpivid_irq_callback irq_cb, void *ctx)
+{
+	pre_irq(dev, ient, irq_cb, ctx, &dev->ic_active2);
+}
+
+int rpivid_hw_probe(struct rpivid_dev *dev)
+{
+	struct resource *res;
+	__u32 irq_stat;
+	int irq_dec;
+	int ret = 0;
+
+	ictl_init(&dev->ic_active1, RPIVID_P2BUF_COUNT);
+	ictl_init(&dev->ic_active2, RPIVID_ICTL_ENABLE_UNLIMITED);
+
+	res = platform_get_resource_byname(dev->pdev, IORESOURCE_MEM, "intc");
+	if (!res)
+		return -ENODEV;
+
+	dev->base_irq = devm_ioremap(dev->dev, res->start, resource_size(res));
+	if (IS_ERR(dev->base_irq))
+		return PTR_ERR(dev->base_irq);
+
+	res = platform_get_resource_byname(dev->pdev, IORESOURCE_MEM, "hevc");
+	if (!res)
+		return -ENODEV;
+
+	dev->base_h265 = devm_ioremap(dev->dev, res->start, resource_size(res));
+	if (IS_ERR(dev->base_h265))
+		return PTR_ERR(dev->base_h265);
+
+	dev->clock = devm_clk_get(&dev->pdev->dev, "hevc");
+	if (IS_ERR(dev->clock))
+		return PTR_ERR(dev->clock);
+
+	dev->cache_align = dma_get_cache_alignment();
+
+	// Disable IRQs & reset anything pending
+	irq_write(dev, 0,
+		  ARG_IC_ICTRL_ACTIVE1_EN_SET | ARG_IC_ICTRL_ACTIVE2_EN_SET);
+	irq_stat = irq_read(dev, 0);
+	irq_write(dev, 0, irq_stat);
+
+#if !OPT_DEBUG_POLL_IRQ
+	irq_dec = platform_get_irq(dev->pdev, 0);
+	if (irq_dec <= 0)
+		return irq_dec;
+	ret = devm_request_threaded_irq(dev->dev, irq_dec,
+					rpivid_irq_irq,
+					rpivid_irq_thread,
+					0, dev_name(dev->dev), dev);
+	if (ret) {
+		dev_err(dev->dev, "Failed to request IRQ - %d\n", ret);
+
+		return ret;
+	}
+#endif
+	return ret;
+}
+
+void rpivid_hw_remove(struct rpivid_dev *dev)
+{
+	// IRQ auto freed on unload so no need to do it here
+	// ioremap auto freed on unload
+	ictl_uninit(&dev->ic_active1);
+	ictl_uninit(&dev->ic_active2);
+}
+
diff -Nur --no-dereference linux-5.10.52-orig/drivers/staging/media/rpivid/rpivid_hw.h linux-5.10.52-v7l+/drivers/staging/media/rpivid/rpivid_hw.h
--- linux-5.10.52-orig/drivers/staging/media/rpivid/rpivid_hw.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/staging/media/rpivid/rpivid_hw.h	2021-07-25 16:46:25.637961078 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Raspberry Pi HEVC driver
+ *
+ * Copyright (C) 2020 Raspberry Pi (Trading) Ltd
+ *
+ * Based on the Cedrus VPU driver, that is:
+ *
+ * Copyright (C) 2016 Florent Revest <florent.revest@free-electrons.com>
+ * Copyright (C) 2018 Paul Kocialkowski <paul.kocialkowski@bootlin.com>
+ * Copyright (C) 2018 Bootlin
+ */
+
+#ifndef _RPIVID_HW_H_
+#define _RPIVID_HW_H_
+
+struct rpivid_hw_irq_ent {
+	struct rpivid_hw_irq_ent *next;
+	rpivid_irq_callback cb;
+	void *v;
+};
+
+/* Phase 1 Register offsets */
+
+#define RPI_SPS0 0
+#define RPI_SPS1 4
+#define RPI_PPS 8
+#define RPI_SLICE 12
+#define RPI_TILESTART 16
+#define RPI_TILEEND 20
+#define RPI_SLICESTART 24
+#define RPI_MODE 28
+#define RPI_LEFT0 32
+#define RPI_LEFT1 36
+#define RPI_LEFT2 40
+#define RPI_LEFT3 44
+#define RPI_QP 48
+#define RPI_CONTROL 52
+#define RPI_STATUS 56
+#define RPI_VERSION 60
+#define RPI_BFBASE 64
+#define RPI_BFNUM 68
+#define RPI_BFCONTROL 72
+#define RPI_BFSTATUS 76
+#define RPI_PUWBASE 80
+#define RPI_PUWSTRIDE 84
+#define RPI_COEFFWBASE 88
+#define RPI_COEFFWSTRIDE 92
+#define RPI_SLICECMDS 96
+#define RPI_BEGINTILEEND 100
+#define RPI_TRANSFER 104
+#define RPI_CFBASE 108
+#define RPI_CFNUM 112
+#define RPI_CFSTATUS 116
+
+/* Phase 2 Register offsets */
+
+#define RPI_PURBASE 0x8000
+#define RPI_PURSTRIDE 0x8004
+#define RPI_COEFFRBASE 0x8008
+#define RPI_COEFFRSTRIDE 0x800C
+#define RPI_NUMROWS 0x8010
+#define RPI_CONFIG2 0x8014
+#define RPI_OUTYBASE 0x8018
+#define RPI_OUTYSTRIDE 0x801C
+#define RPI_OUTCBASE 0x8020
+#define RPI_OUTCSTRIDE 0x8024
+#define RPI_STATUS2 0x8028
+#define RPI_FRAMESIZE 0x802C
+#define RPI_MVBASE 0x8030
+#define RPI_MVSTRIDE 0x8034
+#define RPI_COLBASE 0x8038
+#define RPI_COLSTRIDE 0x803C
+#define RPI_CURRPOC 0x8040
+
+/*
+ * Write a general register value
+ * Order is unimportant
+ */
+static inline void apb_write(const struct rpivid_dev * const dev,
+			     const unsigned int offset, const u32 val)
+{
+	writel_relaxed(val, dev->base_h265 + offset);
+}
+
+/* Write the final register value that actually starts the phase */
+static inline void apb_write_final(const struct rpivid_dev * const dev,
+				   const unsigned int offset, const u32 val)
+{
+	writel(val, dev->base_h265 + offset);
+}
+
+static inline u32 apb_read(const struct rpivid_dev * const dev,
+			   const unsigned int offset)
+{
+	return readl(dev->base_h265 + offset);
+}
+
+static inline void irq_write(const struct rpivid_dev * const dev,
+			     const unsigned int offset, const u32 val)
+{
+	writel(val, dev->base_irq + offset);
+}
+
+static inline u32 irq_read(const struct rpivid_dev * const dev,
+			   const unsigned int offset)
+{
+	return readl(dev->base_irq + offset);
+}
+
+static inline void apb_write_vc_addr(const struct rpivid_dev * const dev,
+				     const unsigned int offset,
+				     const dma_addr_t a)
+{
+	apb_write(dev, offset, (u32)(a >> 6));
+}
+
+static inline void apb_write_vc_addr_final(const struct rpivid_dev * const dev,
+					   const unsigned int offset,
+					   const dma_addr_t a)
+{
+	apb_write_final(dev, offset, (u32)(a >> 6));
+}
+
+static inline void apb_write_vc_len(const struct rpivid_dev * const dev,
+				    const unsigned int offset,
+				    const unsigned int x)
+{
+	apb_write(dev, offset, (x + 63) >> 6);
+}
+
+/* *ARG_IC_ICTRL - Interrupt control for ARGON Core*
+ * Offset (byte space) = 40'h2b10000
+ * Physical Address (byte space) = 40'h7eb10000
+ * Verilog Macro Address = `ARG_IC_REG_START + `ARGON_INTCTRL_ICTRL
+ * Reset Value = 32'b100x100x_100xxxxx_xxxxxxx0_x100x100
+ * Access = RW (32-bit only)
+ * Interrupt control logic for ARGON Core.
+ */
+#define ARG_IC_ICTRL 0
+
+/* acc=LWC ACTIVE1_INT FIELD ACCESS: LWC
+ *
+ * Interrupt 1
+ * This is set and held when an hevc_active1 interrupt edge is detected
+ * The polarity of the edge is set by the ACTIVE1_EDGE field
+ * Write a 1 to this bit to clear down the latched interrupt
+ * The latched interrupt is only enabled out onto the interrupt line if
+ * ACTIVE1_EN is set
+ * Reset value is *0* decimal.
+ */
+#define ARG_IC_ICTRL_ACTIVE1_INT_SET		BIT(0)
+
+/* ACTIVE1_EDGE Sets the polarity of the interrupt edge detection logic
+ * This logic detects edges of the hevc_active1 line from the argon core
+ * 0 = negedge, 1 = posedge
+ * Reset value is *0* decimal.
+ */
+#define ARG_IC_ICTRL_ACTIVE1_EDGE_SET		BIT(1)
+
+/* ACTIVE1_EN Enables ACTIVE1_INT out onto the argon interrupt line.
+ * If this isn't set, the interrupt logic will work but no interrupt will be
+ * set to the interrupt controller
+ * Reset value is *1* decimal.
+ *
+ * [JC] The above appears to be a lie - if unset then b0 is never set
+ */
+#define ARG_IC_ICTRL_ACTIVE1_EN_SET		BIT(2)
+
+/* acc=RO ACTIVE1_STATUS FIELD ACCESS: RO
+ *
+ * The current status of the hevc_active1 signal
+ */
+#define ARG_IC_ICTRL_ACTIVE1_STATUS_SET		BIT(3)
+
+/* acc=LWC ACTIVE2_INT FIELD ACCESS: LWC
+ *
+ * Interrupt 2
+ * This is set and held when an hevc_active2 interrupt edge is detected
+ * The polarity of the edge is set by the ACTIVE2_EDGE field
+ * Write a 1 to this bit to clear down the latched interrupt
+ * The latched interrupt is only enabled out onto the interrupt line if
+ * ACTIVE2_EN is set
+ * Reset value is *0* decimal.
+ */
+#define ARG_IC_ICTRL_ACTIVE2_INT_SET		BIT(4)
+
+/* ACTIVE2_EDGE Sets the polarity of the interrupt edge detection logic
+ * This logic detects edges of the hevc_active2 line from the argon core
+ * 0 = negedge, 1 = posedge
+ * Reset value is *0* decimal.
+ */
+#define ARG_IC_ICTRL_ACTIVE2_EDGE_SET		BIT(5)
+
+/* ACTIVE2_EN Enables ACTIVE2_INT out onto the argon interrupt line.
+ * If this isn't set, the interrupt logic will work but no interrupt will be
+ * set to the interrupt controller
+ * Reset value is *1* decimal.
+ */
+#define ARG_IC_ICTRL_ACTIVE2_EN_SET		BIT(6)
+
+/* acc=RO ACTIVE2_STATUS FIELD ACCESS: RO
+ *
+ * The current status of the hevc_active2 signal
+ */
+#define ARG_IC_ICTRL_ACTIVE2_STATUS_SET		BIT(7)
+
+/* TEST_INT Forces the argon int high for test purposes.
+ * Reset value is *0* decimal.
+ */
+#define ARG_IC_ICTRL_TEST_INT			BIT(8)
+#define ARG_IC_ICTRL_SPARE			BIT(9)
+
+/* acc=RO VP9_INTERRUPT_STATUS FIELD ACCESS: RO
+ *
+ * The current status of the vp9_interrupt signal
+ */
+#define ARG_IC_ICTRL_VP9_INTERRUPT_STATUS	BIT(10)
+
+/* AIO_INT_ENABLE 1 = Or the AIO int in with the Argon int so the VPU can see
+ * it
+ * 0 = the AIO int is masked. (It should still be connected to the GIC though).
+ */
+#define ARG_IC_ICTRL_AIO_INT_ENABLE		BIT(20)
+#define ARG_IC_ICTRL_H264_ACTIVE_INT		BIT(21)
+#define ARG_IC_ICTRL_H264_ACTIVE_EDGE		BIT(22)
+#define ARG_IC_ICTRL_H264_ACTIVE_EN		BIT(23)
+#define ARG_IC_ICTRL_H264_ACTIVE_STATUS		BIT(24)
+#define ARG_IC_ICTRL_H264_INTERRUPT_INT		BIT(25)
+#define ARG_IC_ICTRL_H264_INTERRUPT_EDGE	BIT(26)
+#define ARG_IC_ICTRL_H264_INTERRUPT_EN		BIT(27)
+
+/* acc=RO H264_INTERRUPT_STATUS FIELD ACCESS: RO
+ *
+ * The current status of the h264_interrupt signal
+ */
+#define ARG_IC_ICTRL_H264_INTERRUPT_STATUS	BIT(28)
+
+/* acc=LWC VP9_INTERRUPT_INT FIELD ACCESS: LWC
+ *
+ * Interrupt 1
+ * This is set and held when an vp9_interrupt interrupt edge is detected
+ * The polarity of the edge is set by the VP9_INTERRUPT_EDGE field
+ * Write a 1 to this bit to clear down the latched interrupt
+ * The latched interrupt is only enabled out onto the interrupt line if
+ * VP9_INTERRUPT_EN is set
+ * Reset value is *0* decimal.
+ */
+#define ARG_IC_ICTRL_VP9_INTERRUPT_INT		BIT(29)
+
+/* VP9_INTERRUPT_EDGE Sets the polarity of the interrupt edge detection logic
+ * This logic detects edges of the vp9_interrupt line from the argon h264 core
+ * 0 = negedge, 1 = posedge
+ * Reset value is *0* decimal.
+ */
+#define ARG_IC_ICTRL_VP9_INTERRUPT_EDGE		BIT(30)
+
+/* VP9_INTERRUPT_EN Enables VP9_INTERRUPT_INT out onto the argon interrupt line.
+ * If this isn't set, the interrupt logic will work but no interrupt will be
+ * set to the interrupt controller
+ * Reset value is *1* decimal.
+ */
+#define ARG_IC_ICTRL_VP9_INTERRUPT_EN		BIT(31)
+
+/* Bits 19:12, 11 reserved - read ?, write 0 */
+#define ARG_IC_ICTRL_SET_ZERO_MASK		((0xff << 12) | BIT(11))
+
+/* All IRQ bits */
+#define ARG_IC_ICTRL_ALL_IRQ_MASK   (\
+		ARG_IC_ICTRL_VP9_INTERRUPT_INT  |\
+		ARG_IC_ICTRL_H264_INTERRUPT_INT |\
+		ARG_IC_ICTRL_ACTIVE1_INT_SET    |\
+		ARG_IC_ICTRL_ACTIVE2_INT_SET)
+
+/* Regulate claim Q */
+void rpivid_hw_irq_active1_enable_claim(struct rpivid_dev *dev,
+					int n);
+/* Auto release once all CBs called */
+void rpivid_hw_irq_active1_claim(struct rpivid_dev *dev,
+				 struct rpivid_hw_irq_ent *ient,
+				 rpivid_irq_callback ready_cb, void *ctx);
+/* May only be called in claim cb */
+void rpivid_hw_irq_active1_irq(struct rpivid_dev *dev,
+			       struct rpivid_hw_irq_ent *ient,
+			       rpivid_irq_callback irq_cb, void *ctx);
+/* May only be called in irq cb */
+void rpivid_hw_irq_active1_thread(struct rpivid_dev *dev,
+				  struct rpivid_hw_irq_ent *ient,
+				  rpivid_irq_callback thread_cb, void *ctx);
+
+/* Auto release once all CBs called */
+void rpivid_hw_irq_active2_claim(struct rpivid_dev *dev,
+				 struct rpivid_hw_irq_ent *ient,
+				 rpivid_irq_callback ready_cb, void *ctx);
+/* May only be called in claim cb */
+void rpivid_hw_irq_active2_irq(struct rpivid_dev *dev,
+			       struct rpivid_hw_irq_ent *ient,
+			       rpivid_irq_callback irq_cb, void *ctx);
+
+int rpivid_hw_probe(struct rpivid_dev *dev);
+void rpivid_hw_remove(struct rpivid_dev *dev);
+
+#endif
diff -Nur --no-dereference linux-5.10.52-orig/drivers/staging/media/rpivid/rpivid_video.c linux-5.10.52-v7l+/drivers/staging/media/rpivid/rpivid_video.c
--- linux-5.10.52-orig/drivers/staging/media/rpivid/rpivid_video.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/staging/media/rpivid/rpivid_video.c	2021-07-25 16:46:25.637961078 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Raspberry Pi HEVC driver
+ *
+ * Copyright (C) 2020 Raspberry Pi (Trading) Ltd
+ *
+ * Based on the Cedrus VPU driver, that is:
+ *
+ * Copyright (C) 2016 Florent Revest <florent.revest@free-electrons.com>
+ * Copyright (C) 2018 Paul Kocialkowski <paul.kocialkowski@bootlin.com>
+ * Copyright (C) 2018 Bootlin
+ */
+
+#include <media/videobuf2-dma-contig.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-mem2mem.h>
+
+#include "rpivid.h"
+#include "rpivid_hw.h"
+#include "rpivid_video.h"
+#include "rpivid_dec.h"
+
+#define RPIVID_DECODE_SRC	BIT(0)
+#define RPIVID_DECODE_DST	BIT(1)
+
+#define RPIVID_MIN_WIDTH	16U
+#define RPIVID_MIN_HEIGHT	16U
+#define RPIVID_MAX_WIDTH	4096U
+#define RPIVID_MAX_HEIGHT	4096U
+
+static inline struct rpivid_ctx *rpivid_file2ctx(struct file *file)
+{
+	return container_of(file->private_data, struct rpivid_ctx, fh);
+}
+
+/* constrain x to y,y*2 */
+static inline unsigned int constrain2x(unsigned int x, unsigned int y)
+{
+	return (x < y) ?
+			y :
+			(x > y * 2) ? y : x;
+}
+
+size_t rpivid_round_up_size(const size_t x)
+{
+	/* Admit no size < 256 */
+	const unsigned int n = x < 256 ? 8 : ilog2(x);
+
+	return x >= (3 << n) ? 4 << n : (3 << n);
+}
+
+size_t rpivid_bit_buf_size(unsigned int w, unsigned int h, unsigned int bits_minus8)
+{
+	const size_t wxh = w * h;
+	size_t bits_alloc;
+
+	/* Annex A gives a min compression of 2 @ lvl 3.1
+	 * (wxh <= 983040) and min 4 thereafter but avoid
+	 * the odity of 983041 having a lower limit than
+	 * 983040.
+	 * Multiply by 3/2 for 4:2:0
+	 */
+	bits_alloc = wxh < 983040 ? wxh * 3 / 4 :
+		wxh < 983040 * 2 ? 983040 * 3 / 4 :
+		wxh * 3 / 8;
+	/* Allow for bit depth */
+	bits_alloc += (bits_alloc * bits_minus8) / 8;
+	return rpivid_round_up_size(bits_alloc);
+}
+
+int rpivid_prepare_src_format(struct v4l2_pix_format_mplane *pix_fmt)
+{
+	size_t size;
+	u32 w;
+	u32 h;
+
+	if (pix_fmt->pixelformat != V4L2_PIX_FMT_HEVC_SLICE)
+		return -EINVAL;
+
+	w = pix_fmt->width;
+	h = pix_fmt->height;
+	if (!w || !h) {
+		w = 1920;
+		h = 1080;
+	}
+	if (w > 4096)
+		w = 4096;
+	if (h > 4096)
+		h = 4096;
+
+	if (!pix_fmt->plane_fmt[0].sizeimage ||
+	    pix_fmt->plane_fmt[0].sizeimage > SZ_32M) {
+		/* Unspecified or way too big - pick max for size */
+		size = rpivid_bit_buf_size(w, h, 2);
+	}
+	/* Set a minimum */
+	size = max_t(u32, SZ_4K, pix_fmt->plane_fmt[0].sizeimage);
+
+	pix_fmt->width = w;
+	pix_fmt->height = h;
+	pix_fmt->num_planes = 1;
+	pix_fmt->field = V4L2_FIELD_NONE;
+	/* Zero bytes per line for encoded source. */
+	pix_fmt->plane_fmt[0].bytesperline = 0;
+	pix_fmt->plane_fmt[0].sizeimage = size;
+
+	return 0;
+}
+
+int rpivid_prepare_dst_format(struct v4l2_pix_format_mplane *pix_fmt)
+{
+	unsigned int width = pix_fmt->width;
+	unsigned int height = pix_fmt->height;
+	unsigned int sizeimage = pix_fmt->plane_fmt[0].sizeimage;
+	unsigned int bytesperline = pix_fmt->plane_fmt[0].bytesperline;
+
+	switch (pix_fmt->pixelformat) {
+	/* For column formats set bytesperline to column height (stride2) */
+	case V4L2_PIX_FMT_NV12_COL128:
+		/* Width rounds up to columns */
+		width = ALIGN(min(width, RPIVID_MAX_WIDTH), 128);
+
+		/* 16 aligned height - not sure we even need that */
+		height = ALIGN(height, 16);
+		/* column height
+		 * Accept suggested shape if at least min & < 2 * min
+		 */
+		bytesperline = constrain2x(bytesperline, height * 3 / 2);
+
+		/* image size
+		 * Again allow plausible variation in case added padding is
+		 * required
+		 */
+		sizeimage = constrain2x(sizeimage, bytesperline * width);
+		break;
+
+	case V4L2_PIX_FMT_NV12_10_COL128:
+		/* width in pixels (3 pels = 4 bytes) rounded to 128 byte
+		 * columns
+		 */
+		width = ALIGN(((min(width, RPIVID_MAX_WIDTH) + 2) / 3), 32) * 3;
+
+		/* 16-aligned height. */
+		height = ALIGN(height, 16);
+
+		/* column height
+		 * Accept suggested shape if at least min & < 2 * min
+		 */
+		bytesperline = constrain2x(bytesperline, height * 3 / 2);
+
+		/* image size
+		 * Again allow plausible variation in case added padding is
+		 * required
+		 */
+		sizeimage = constrain2x(sizeimage,
+					bytesperline * width * 4 / 3);
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	pix_fmt->width = width;
+	pix_fmt->height = height;
+
+	pix_fmt->field = V4L2_FIELD_NONE;
+	pix_fmt->plane_fmt[0].bytesperline = bytesperline;
+	pix_fmt->plane_fmt[0].sizeimage = sizeimage;
+	pix_fmt->num_planes = 1;
+	return 0;
+}
+
+static int rpivid_querycap(struct file *file, void *priv,
+			   struct v4l2_capability *cap)
+{
+	strscpy(cap->driver, RPIVID_NAME, sizeof(cap->driver));
+	strscpy(cap->card, RPIVID_NAME, sizeof(cap->card));
+	snprintf(cap->bus_info, sizeof(cap->bus_info),
+		 "platform:%s", RPIVID_NAME);
+
+	return 0;
+}
+
+static int rpivid_enum_fmt_vid_out(struct file *file, void *priv,
+				   struct v4l2_fmtdesc *f)
+{
+	// Input formats
+
+	// H.265 Slice only currently
+	if (f->index == 0) {
+		f->pixelformat = V4L2_PIX_FMT_HEVC_SLICE;
+		return 0;
+	}
+
+	return -EINVAL;
+}
+
+static int rpivid_hevc_validate_sps(const struct v4l2_ctrl_hevc_sps * const sps)
+{
+	const unsigned int ctb_log2_size_y =
+			sps->log2_min_luma_coding_block_size_minus3 + 3 +
+			sps->log2_diff_max_min_luma_coding_block_size;
+	const unsigned int min_tb_log2_size_y =
+			sps->log2_min_luma_transform_block_size_minus2 + 2;
+	const unsigned int max_tb_log2_size_y = min_tb_log2_size_y +
+			sps->log2_diff_max_min_luma_transform_block_size;
+
+	/* Local limitations */
+	if (sps->pic_width_in_luma_samples < 32 ||
+	    sps->pic_width_in_luma_samples > 4096)
+		return 0;
+	if (sps->pic_height_in_luma_samples < 32 ||
+	    sps->pic_height_in_luma_samples > 4096)
+		return 0;
+	if (!(sps->bit_depth_luma_minus8 == 0 ||
+	      sps->bit_depth_luma_minus8 == 2))
+		return 0;
+	if (sps->bit_depth_luma_minus8 != sps->bit_depth_chroma_minus8)
+		return 0;
+	if (sps->chroma_format_idc != 1)
+		return 0;
+
+	/*  Limits from H.265 7.4.3.2.1 */
+	if (sps->log2_max_pic_order_cnt_lsb_minus4 > 12)
+		return 0;
+	if (sps->sps_max_dec_pic_buffering_minus1 > 15)
+		return 0;
+	if (sps->sps_max_num_reorder_pics >
+				sps->sps_max_dec_pic_buffering_minus1)
+		return 0;
+	if (ctb_log2_size_y > 6)
+		return 0;
+	if (max_tb_log2_size_y > 5)
+		return 0;
+	if (max_tb_log2_size_y > ctb_log2_size_y)
+		return 0;
+	if (sps->max_transform_hierarchy_depth_inter >
+				(ctb_log2_size_y - min_tb_log2_size_y))
+		return 0;
+	if (sps->max_transform_hierarchy_depth_intra >
+				(ctb_log2_size_y - min_tb_log2_size_y))
+		return 0;
+	/* Check pcm stuff */
+	if (sps->num_short_term_ref_pic_sets > 64)
+		return 0;
+	if (sps->num_long_term_ref_pics_sps > 32)
+		return 0;
+	return 1;
+}
+
+static inline int is_sps_set(const struct v4l2_ctrl_hevc_sps * const sps)
+{
+	return sps && sps->pic_width_in_luma_samples != 0;
+}
+
+static u32 pixelformat_from_sps(const struct v4l2_ctrl_hevc_sps * const sps,
+				const int index)
+{
+	u32 pf = 0;
+
+	// Use width 0 as a signifier of unsetness
+	if (!is_sps_set(sps)) {
+		/* Treat this as an error? For now return both */
+		if (index == 0)
+			pf = V4L2_PIX_FMT_NV12_COL128;
+		else if (index == 1)
+			pf = V4L2_PIX_FMT_NV12_10_COL128;
+	} else if (index == 0 && rpivid_hevc_validate_sps(sps)) {
+		if (sps->bit_depth_luma_minus8 == 0)
+			pf = V4L2_PIX_FMT_NV12_COL128;
+		else if (sps->bit_depth_luma_minus8 == 2)
+			pf = V4L2_PIX_FMT_NV12_10_COL128;
+	}
+
+	return pf;
+}
+
+static struct v4l2_pix_format_mplane
+rpivid_hevc_default_dst_fmt(struct rpivid_ctx * const ctx)
+{
+	const struct v4l2_ctrl_hevc_sps * const sps =
+		rpivid_find_control_data(ctx, V4L2_CID_MPEG_VIDEO_HEVC_SPS);
+	struct v4l2_pix_format_mplane pix_fmt = {
+		.width = sps->pic_width_in_luma_samples,
+		.height = sps->pic_height_in_luma_samples,
+		.pixelformat = pixelformat_from_sps(sps, 0)
+	};
+
+	rpivid_prepare_dst_format(&pix_fmt);
+	return pix_fmt;
+}
+
+static u32 rpivid_hevc_get_dst_pixelformat(struct rpivid_ctx * const ctx,
+					   const int index)
+{
+	const struct v4l2_ctrl_hevc_sps * const sps =
+		rpivid_find_control_data(ctx, V4L2_CID_MPEG_VIDEO_HEVC_SPS);
+
+	return pixelformat_from_sps(sps, index);
+}
+
+static int rpivid_enum_fmt_vid_cap(struct file *file, void *priv,
+				   struct v4l2_fmtdesc *f)
+{
+	struct rpivid_ctx * const ctx = rpivid_file2ctx(file);
+
+	const u32 pf = rpivid_hevc_get_dst_pixelformat(ctx, f->index);
+
+	if (pf == 0)
+		return -EINVAL;
+
+	f->pixelformat = pf;
+	return 0;
+}
+
+static int rpivid_g_fmt_vid_cap(struct file *file, void *priv,
+				struct v4l2_format *f)
+{
+	struct rpivid_ctx *ctx = rpivid_file2ctx(file);
+
+	if (!ctx->dst_fmt_set)
+		ctx->dst_fmt = rpivid_hevc_default_dst_fmt(ctx);
+	f->fmt.pix_mp = ctx->dst_fmt;
+	return 0;
+}
+
+static int rpivid_g_fmt_vid_out(struct file *file, void *priv,
+				struct v4l2_format *f)
+{
+	struct rpivid_ctx *ctx = rpivid_file2ctx(file);
+
+	f->fmt.pix_mp = ctx->src_fmt;
+	return 0;
+}
+
+static inline void copy_color(struct v4l2_pix_format_mplane *d,
+			      const struct v4l2_pix_format_mplane *s)
+{
+	d->colorspace   = s->colorspace;
+	d->xfer_func    = s->xfer_func;
+	d->ycbcr_enc    = s->ycbcr_enc;
+	d->quantization = s->quantization;
+}
+
+static int rpivid_try_fmt_vid_cap(struct file *file, void *priv,
+				  struct v4l2_format *f)
+{
+	struct rpivid_ctx *ctx = rpivid_file2ctx(file);
+	const struct v4l2_ctrl_hevc_sps * const sps =
+		rpivid_find_control_data(ctx, V4L2_CID_MPEG_VIDEO_HEVC_SPS);
+	u32 pixelformat;
+	int i;
+
+	for (i = 0; (pixelformat = pixelformat_from_sps(sps, i)) != 0; i++) {
+		if (f->fmt.pix_mp.pixelformat == pixelformat)
+			break;
+	}
+
+	// If we can't use requested fmt then set to default
+	if (pixelformat == 0) {
+		pixelformat = pixelformat_from_sps(sps, 0);
+		// If we don't have a default then give up
+		if (pixelformat == 0)
+			return -EINVAL;
+	}
+
+	// We don't have any way of finding out colourspace so believe
+	// anything we are told - take anything set in src as a default
+	if (f->fmt.pix_mp.colorspace == V4L2_COLORSPACE_DEFAULT)
+		copy_color(&f->fmt.pix_mp, &ctx->src_fmt);
+
+	f->fmt.pix_mp.pixelformat = pixelformat;
+	return rpivid_prepare_dst_format(&f->fmt.pix_mp);
+}
+
+static int rpivid_try_fmt_vid_out(struct file *file, void *priv,
+				  struct v4l2_format *f)
+{
+	if (rpivid_prepare_src_format(&f->fmt.pix_mp)) {
+		// Set default src format
+		f->fmt.pix_mp.pixelformat = RPIVID_SRC_PIXELFORMAT_DEFAULT;
+		rpivid_prepare_src_format(&f->fmt.pix_mp);
+	}
+	return 0;
+}
+
+static int rpivid_s_fmt_vid_cap(struct file *file, void *priv,
+				struct v4l2_format *f)
+{
+	struct rpivid_ctx *ctx = rpivid_file2ctx(file);
+	struct vb2_queue *vq;
+	int ret;
+
+	vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
+	if (vb2_is_busy(vq))
+		return -EBUSY;
+
+	ret = rpivid_try_fmt_vid_cap(file, priv, f);
+	if (ret)
+		return ret;
+
+	ctx->dst_fmt = f->fmt.pix_mp;
+	ctx->dst_fmt_set = 1;
+
+	return 0;
+}
+
+static int rpivid_s_fmt_vid_out(struct file *file, void *priv,
+				struct v4l2_format *f)
+{
+	struct rpivid_ctx *ctx = rpivid_file2ctx(file);
+	struct vb2_queue *vq;
+	int ret;
+
+	vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
+	if (vb2_is_busy(vq))
+		return -EBUSY;
+
+	ret = rpivid_try_fmt_vid_out(file, priv, f);
+	if (ret)
+		return ret;
+
+	ctx->src_fmt = f->fmt.pix_mp;
+	ctx->dst_fmt_set = 0;  // Setting src invalidates dst
+
+	vq->subsystem_flags |=
+		VB2_V4L2_FL_SUPPORTS_M2M_HOLD_CAPTURE_BUF;
+
+	/* Propagate colorspace information to capture. */
+	copy_color(&ctx->dst_fmt, &f->fmt.pix_mp);
+	return 0;
+}
+
+const struct v4l2_ioctl_ops rpivid_ioctl_ops = {
+	.vidioc_querycap		= rpivid_querycap,
+
+	.vidioc_enum_fmt_vid_cap	= rpivid_enum_fmt_vid_cap,
+	.vidioc_g_fmt_vid_cap_mplane	= rpivid_g_fmt_vid_cap,
+	.vidioc_try_fmt_vid_cap_mplane	= rpivid_try_fmt_vid_cap,
+	.vidioc_s_fmt_vid_cap_mplane	= rpivid_s_fmt_vid_cap,
+
+	.vidioc_enum_fmt_vid_out	= rpivid_enum_fmt_vid_out,
+	.vidioc_g_fmt_vid_out_mplane	= rpivid_g_fmt_vid_out,
+	.vidioc_try_fmt_vid_out_mplane	= rpivid_try_fmt_vid_out,
+	.vidioc_s_fmt_vid_out_mplane	= rpivid_s_fmt_vid_out,
+
+	.vidioc_reqbufs			= v4l2_m2m_ioctl_reqbufs,
+	.vidioc_querybuf		= v4l2_m2m_ioctl_querybuf,
+	.vidioc_qbuf			= v4l2_m2m_ioctl_qbuf,
+	.vidioc_dqbuf			= v4l2_m2m_ioctl_dqbuf,
+	.vidioc_prepare_buf		= v4l2_m2m_ioctl_prepare_buf,
+	.vidioc_create_bufs		= v4l2_m2m_ioctl_create_bufs,
+	.vidioc_expbuf			= v4l2_m2m_ioctl_expbuf,
+
+	.vidioc_streamon		= v4l2_m2m_ioctl_streamon,
+	.vidioc_streamoff		= v4l2_m2m_ioctl_streamoff,
+
+	.vidioc_try_decoder_cmd		= v4l2_m2m_ioctl_stateless_try_decoder_cmd,
+	.vidioc_decoder_cmd		= v4l2_m2m_ioctl_stateless_decoder_cmd,
+
+	.vidioc_subscribe_event		= v4l2_ctrl_subscribe_event,
+	.vidioc_unsubscribe_event	= v4l2_event_unsubscribe,
+};
+
+static int rpivid_queue_setup(struct vb2_queue *vq, unsigned int *nbufs,
+			      unsigned int *nplanes, unsigned int sizes[],
+			      struct device *alloc_devs[])
+{
+	struct rpivid_ctx *ctx = vb2_get_drv_priv(vq);
+	struct v4l2_pix_format_mplane *pix_fmt;
+
+	if (V4L2_TYPE_IS_OUTPUT(vq->type))
+		pix_fmt = &ctx->src_fmt;
+	else
+		pix_fmt = &ctx->dst_fmt;
+
+	if (*nplanes) {
+		if (sizes[0] < pix_fmt->plane_fmt[0].sizeimage)
+			return -EINVAL;
+	} else {
+		sizes[0] = pix_fmt->plane_fmt[0].sizeimage;
+		*nplanes = 1;
+	}
+
+	return 0;
+}
+
+static void rpivid_queue_cleanup(struct vb2_queue *vq, u32 state)
+{
+	struct rpivid_ctx *ctx = vb2_get_drv_priv(vq);
+	struct vb2_v4l2_buffer *vbuf;
+
+	for (;;) {
+		if (V4L2_TYPE_IS_OUTPUT(vq->type))
+			vbuf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
+		else
+			vbuf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
+
+		if (!vbuf)
+			return;
+
+		v4l2_ctrl_request_complete(vbuf->vb2_buf.req_obj.req,
+					   &ctx->hdl);
+		v4l2_m2m_buf_done(vbuf, state);
+	}
+}
+
+static int rpivid_buf_out_validate(struct vb2_buffer *vb)
+{
+	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+
+	vbuf->field = V4L2_FIELD_NONE;
+	return 0;
+}
+
+static int rpivid_buf_prepare(struct vb2_buffer *vb)
+{
+	struct vb2_queue *vq = vb->vb2_queue;
+	struct rpivid_ctx *ctx = vb2_get_drv_priv(vq);
+	struct v4l2_pix_format_mplane *pix_fmt;
+
+	if (V4L2_TYPE_IS_OUTPUT(vq->type))
+		pix_fmt = &ctx->src_fmt;
+	else
+		pix_fmt = &ctx->dst_fmt;
+
+	if (vb2_plane_size(vb, 0) < pix_fmt->plane_fmt[0].sizeimage)
+		return -EINVAL;
+
+	vb2_set_plane_payload(vb, 0, pix_fmt->plane_fmt[0].sizeimage);
+
+	return 0;
+}
+
+/* Only stops the clock if streaom off on both output & capture */
+static void stop_clock(struct rpivid_dev *dev, struct rpivid_ctx *ctx)
+{
+	if (ctx->src_stream_on ||
+	    ctx->dst_stream_on ||
+	    !ctx->clk_req)
+		return;
+
+	clk_request_done(ctx->clk_req);
+	ctx->clk_req = NULL;
+
+	clk_disable_unprepare(dev->clock);
+}
+
+/* Always starts the clock if it isn't already on this ctx */
+static int start_clock(struct rpivid_dev *dev, struct rpivid_ctx *ctx)
+{
+	long max_hevc_clock;
+	int rv;
+
+	if (ctx->clk_req)
+		return 0;
+
+	max_hevc_clock = clk_round_rate(dev->clock, ULONG_MAX);
+
+	ctx->clk_req = clk_request_start(dev->clock, max_hevc_clock);
+	if (!ctx->clk_req) {
+		dev_err(dev->dev, "Failed to set clock rate\n");
+		return -EIO;
+	}
+
+	rv = clk_prepare_enable(dev->clock);
+	if (rv) {
+		dev_err(dev->dev, "Failed to enable clock\n");
+		clk_request_done(ctx->clk_req);
+		ctx->clk_req = NULL;
+		return rv;
+	}
+
+	return 0;
+}
+
+static int rpivid_start_streaming(struct vb2_queue *vq, unsigned int count)
+{
+	struct rpivid_ctx *ctx = vb2_get_drv_priv(vq);
+	struct rpivid_dev *dev = ctx->dev;
+	int ret = 0;
+
+	if (!V4L2_TYPE_IS_OUTPUT(vq->type)) {
+		ctx->dst_stream_on = 1;
+		goto ok;
+	}
+
+	if (ctx->src_fmt.pixelformat != V4L2_PIX_FMT_HEVC_SLICE) {
+		ret = -EINVAL;
+		goto fail_cleanup;
+	}
+
+	if (ctx->src_stream_on)
+		goto ok;
+
+	ret = start_clock(dev, ctx);
+	if (ret)
+		goto fail_cleanup;
+
+	if (dev->dec_ops->start)
+		ret = dev->dec_ops->start(ctx);
+	if (ret)
+		goto fail_stop_clock;
+
+	ctx->src_stream_on = 1;
+ok:
+	return 0;
+
+fail_stop_clock:
+	stop_clock(dev, ctx);
+fail_cleanup:
+	v4l2_err(&dev->v4l2_dev, "%s: qtype=%d: FAIL\n", __func__, vq->type);
+	rpivid_queue_cleanup(vq, VB2_BUF_STATE_QUEUED);
+	return ret;
+}
+
+static void rpivid_stop_streaming(struct vb2_queue *vq)
+{
+	struct rpivid_ctx *ctx = vb2_get_drv_priv(vq);
+	struct rpivid_dev *dev = ctx->dev;
+
+	if (V4L2_TYPE_IS_OUTPUT(vq->type)) {
+		ctx->src_stream_on = 0;
+		if (dev->dec_ops->stop)
+			dev->dec_ops->stop(ctx);
+	} else {
+		ctx->dst_stream_on = 0;
+	}
+
+	rpivid_queue_cleanup(vq, VB2_BUF_STATE_ERROR);
+
+	vb2_wait_for_all_buffers(vq);
+
+	stop_clock(dev, ctx);
+}
+
+static void rpivid_buf_queue(struct vb2_buffer *vb)
+{
+	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+	struct rpivid_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+
+	v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf);
+}
+
+static void rpivid_buf_request_complete(struct vb2_buffer *vb)
+{
+	struct rpivid_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+
+	v4l2_ctrl_request_complete(vb->req_obj.req, &ctx->hdl);
+}
+
+static struct vb2_ops rpivid_qops = {
+	.queue_setup		= rpivid_queue_setup,
+	.buf_prepare		= rpivid_buf_prepare,
+	.buf_queue		= rpivid_buf_queue,
+	.buf_out_validate	= rpivid_buf_out_validate,
+	.buf_request_complete	= rpivid_buf_request_complete,
+	.start_streaming	= rpivid_start_streaming,
+	.stop_streaming		= rpivid_stop_streaming,
+	.wait_prepare		= vb2_ops_wait_prepare,
+	.wait_finish		= vb2_ops_wait_finish,
+};
+
+int rpivid_queue_init(void *priv, struct vb2_queue *src_vq,
+		      struct vb2_queue *dst_vq)
+{
+	struct rpivid_ctx *ctx = priv;
+	int ret;
+
+	src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+	src_vq->io_modes = VB2_MMAP | VB2_DMABUF;
+	src_vq->drv_priv = ctx;
+	src_vq->buf_struct_size = sizeof(struct rpivid_buffer);
+	src_vq->min_buffers_needed = 1;
+	src_vq->ops = &rpivid_qops;
+	src_vq->mem_ops = &vb2_dma_contig_memops;
+	src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+	src_vq->lock = &ctx->ctx_mutex;
+	src_vq->dev = ctx->dev->dev;
+	src_vq->supports_requests = true;
+	src_vq->requires_requests = true;
+
+	ret = vb2_queue_init(src_vq);
+	if (ret)
+		return ret;
+
+	dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+	dst_vq->io_modes = VB2_MMAP | VB2_DMABUF;
+	dst_vq->drv_priv = ctx;
+	dst_vq->buf_struct_size = sizeof(struct rpivid_buffer);
+	dst_vq->min_buffers_needed = 1;
+	dst_vq->ops = &rpivid_qops;
+	dst_vq->mem_ops = &vb2_dma_contig_memops;
+	dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+	dst_vq->lock = &ctx->ctx_mutex;
+	dst_vq->dev = ctx->dev->dev;
+
+	return vb2_queue_init(dst_vq);
+}
diff -Nur --no-dereference linux-5.10.52-orig/drivers/staging/media/rpivid/rpivid_video.h linux-5.10.52-v7l+/drivers/staging/media/rpivid/rpivid_video.h
--- linux-5.10.52-orig/drivers/staging/media/rpivid/rpivid_video.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/staging/media/rpivid/rpivid_video.h	2021-07-25 16:46:25.637961078 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Raspberry Pi HEVC driver
+ *
+ * Copyright (C) 2020 Raspberry Pi (Trading) Ltd
+ *
+ * Based on the Cedrus VPU driver, that is:
+ *
+ * Copyright (C) 2016 Florent Revest <florent.revest@free-electrons.com>
+ * Copyright (C) 2018 Paul Kocialkowski <paul.kocialkowski@bootlin.com>
+ * Copyright (C) 2018 Bootlin
+ */
+
+#ifndef _RPIVID_VIDEO_H_
+#define _RPIVID_VIDEO_H_
+
+struct rpivid_format {
+	u32		pixelformat;
+	u32		directions;
+	unsigned int	capabilities;
+};
+
+extern const struct v4l2_ioctl_ops rpivid_ioctl_ops;
+
+int rpivid_queue_init(void *priv, struct vb2_queue *src_vq,
+		      struct vb2_queue *dst_vq);
+
+size_t rpivid_bit_buf_size(unsigned int w, unsigned int h, unsigned int bits_minus8);
+size_t rpivid_round_up_size(const size_t x);
+
+int rpivid_prepare_src_format(struct v4l2_pix_format_mplane *pix_fmt);
+int rpivid_prepare_dst_format(struct v4l2_pix_format_mplane *pix_fmt);
+
+#endif
diff -Nur --no-dereference linux-5.10.52-orig/drivers/staging/vc04_services/bcm2835-audio/bcm2835.c linux-5.10.52-v7l+/drivers/staging/vc04_services/bcm2835-audio/bcm2835.c
--- linux-5.10.52-orig/drivers/staging/vc04_services/bcm2835-audio/bcm2835.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/staging/vc04_services/bcm2835-audio/bcm2835.c	2021-07-25 16:46:26.507946492 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:9 @
 #include <linux/init.h>
 #include <linux/slab.h>
 #include <linux/module.h>
+#include <linux/of.h>
 
 #include "bcm2835.h"
+#include <soc/bcm2835/raspberrypi-firmware.h>
 
-static bool enable_hdmi;
+static bool enable_hdmi, enable_hdmi0, enable_hdmi1;
 static bool enable_headphones;
 static bool enable_compat_alsa = true;
-static int num_channels = MAX_SUBSTREAMS;
 
 module_param(enable_hdmi, bool, 0444);
 MODULE_PARM_DESC(enable_hdmi, "Enables HDMI virtual audio device");
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:25 @
 module_param(enable_compat_alsa, bool, 0444);
 MODULE_PARM_DESC(enable_compat_alsa,
 		 "Enables ALSA compatibility virtual audio device");
-module_param(num_channels, int, 0644);
-MODULE_PARM_DESC(num_channels, "Number of audio channels (default: 8)");
 
 static void bcm2835_devm_free_vchi_ctx(struct device *dev, void *res)
 {
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:83 @
 	if (err)
 		return err;
 
-	err = snd_bcm2835_new_pcm(chip, "bcm2835 IEC958/HDMI", 1, 0, 1, true);
+	err = snd_bcm2835_new_pcm(chip, "bcm2835 IEC958/HDMI", 1, AUDIO_DEST_HDMI0, 1, true);
+	if (err)
+		return err;
+
+	err = snd_bcm2835_new_pcm(chip, "bcm2835 IEC958/HDMI1", 2, AUDIO_DEST_HDMI1, 1, true);
 	if (err)
 		return err;
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:114 @
 	.newctl = snd_bcm2835_new_ctl,
 };
 
-static struct bcm2835_audio_driver bcm2835_audio_hdmi = {
+static struct bcm2835_audio_driver bcm2835_audio_hdmi0 = {
 	.driver = {
 		.name = "bcm2835_hdmi",
 		.owner = THIS_MODULE,
 	},
-	.shortname = "bcm2835 HDMI",
-	.longname  = "bcm2835 HDMI",
+	.shortname = "bcm2835 HDMI 1",
+	.longname  = "bcm2835 HDMI 1",
 	.minchannels = 1,
 	.newpcm = bcm2835_audio_simple_newpcm,
 	.newctl = snd_bcm2835_new_hdmi_ctl,
-	.route = AUDIO_DEST_HDMI
+	.route = AUDIO_DEST_HDMI0
+};
+
+static struct bcm2835_audio_driver bcm2835_audio_hdmi1 = {
+	.driver = {
+		.name = "bcm2835_hdmi",
+		.owner = THIS_MODULE,
+	},
+	.shortname = "bcm2835 HDMI 2",
+	.longname  = "bcm2835 HDMI 2",
+	.minchannels = 1,
+	.newpcm = bcm2835_audio_simple_newpcm,
+	.newctl = snd_bcm2835_new_hdmi_ctl,
+	.route = AUDIO_DEST_HDMI1
 };
 
 static struct bcm2835_audio_driver bcm2835_audio_headphones = {
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:164 @
 		.is_enabled = &enable_compat_alsa,
 	},
 	{
-		.audio_driver = &bcm2835_audio_hdmi,
-		.is_enabled = &enable_hdmi,
+		.audio_driver = &bcm2835_audio_hdmi0,
+		.is_enabled = &enable_hdmi0,
+	},
+	{
+		.audio_driver = &bcm2835_audio_hdmi1,
+		.is_enabled = &enable_hdmi1,
 	},
 	{
 		.audio_driver = &bcm2835_audio_headphones,
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:316 @
 	return 0;
 }
 
+static void set_hdmi_enables(struct device *dev)
+{
+	struct device_node *firmware_node;
+	struct rpi_firmware *firmware;
+	u32 num_displays, i, display_id;
+	int ret;
+
+	firmware_node = of_parse_phandle(dev->of_node, "brcm,firmware", 0);
+	firmware = rpi_firmware_get(firmware_node);
+
+	if (!firmware)
+		return;
+
+	of_node_put(firmware_node);
+
+	ret = rpi_firmware_property(firmware,
+				    RPI_FIRMWARE_FRAMEBUFFER_GET_NUM_DISPLAYS,
+				    &num_displays, sizeof(u32));
+
+	if (ret)
+		return;
+
+	for (i = 0; i < num_displays; i++) {
+		display_id = i;
+		ret = rpi_firmware_property(firmware,
+				RPI_FIRMWARE_FRAMEBUFFER_GET_DISPLAY_ID,
+				&display_id, sizeof(display_id));
+		if (!ret) {
+			if (display_id == 2)
+				enable_hdmi0 = true;
+			if (display_id == 7)
+				enable_hdmi1 = true;
+		}
+	}
+
+	if (!enable_hdmi0 && enable_hdmi1) {
+		/* Swap them over and reassign route. This means
+		 * that if we only have one connected, it is always named
+		 *  HDMI1, irrespective of if its on port HDMI0 or HDMI1.
+		 *  This should match with the naming of HDMI ports in DRM
+		 */
+		enable_hdmi0 = true;
+		enable_hdmi1 = false;
+		bcm2835_audio_hdmi0.route = AUDIO_DEST_HDMI1;
+	}
+}
+
 static int snd_bcm2835_alsa_probe(struct platform_device *pdev)
 {
 	struct device *dev = &pdev->dev;
+	u32 numchans;
 	int err;
 
-	if (num_channels <= 0 || num_channels > MAX_SUBSTREAMS) {
-		num_channels = MAX_SUBSTREAMS;
-		dev_warn(dev, "Illegal num_channels value, will use %u\n",
-			 num_channels);
+	err = of_property_read_u32(dev->of_node, "brcm,pwm-channels",
+				   &numchans);
+	if (err) {
+		dev_err(dev, "Failed to get DT property 'brcm,pwm-channels'");
+		return err;
+	}
+
+	if (numchans == 0 || numchans > MAX_SUBSTREAMS) {
+		numchans = MAX_SUBSTREAMS;
+		dev_warn(dev,
+			 "Illegal 'brcm,pwm-channels' value, will use %u\n",
+			 numchans);
+	}
+
+	if (!enable_compat_alsa) {
+		// In this mode, enable analog output by default
+		u32 disable_headphones = 0;
+
+		if (!of_property_read_bool(dev->of_node, "brcm,disable-hdmi"))
+			set_hdmi_enables(dev);
+
+		of_property_read_u32(dev->of_node,
+				     "brcm,disable-headphones",
+				     &disable_headphones);
+		enable_headphones = !disable_headphones;
+	} else {
+		enable_hdmi0 = enable_hdmi;
 	}
 
 	err = bcm2835_devm_add_vchi_ctx(dev);
 	if (err)
 		return err;
 
-	err = snd_add_child_devices(dev, num_channels);
+	err = snd_add_child_devices(dev, numchans);
 	if (err)
 		return err;
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:424 @
 
 #endif
 
+static const struct of_device_id snd_bcm2835_of_match_table[] = {
+	{ .compatible = "brcm,bcm2835-audio",},
+	{},
+};
+MODULE_DEVICE_TABLE(of, snd_bcm2835_of_match_table);
+
 static struct platform_driver bcm2835_alsa_driver = {
 	.probe = snd_bcm2835_alsa_probe,
 #ifdef CONFIG_PM
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:438 @
 #endif
 	.driver = {
 		.name = "bcm2835_audio",
+		.of_match_table = snd_bcm2835_of_match_table,
 	},
 };
 module_platform_driver(bcm2835_alsa_driver);
diff -Nur --no-dereference linux-5.10.52-orig/drivers/staging/vc04_services/bcm2835-audio/bcm2835.h linux-5.10.52-v7l+/drivers/staging/vc04_services/bcm2835-audio/bcm2835.h
--- linux-5.10.52-orig/drivers/staging/vc04_services/bcm2835-audio/bcm2835.h	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/staging/vc04_services/bcm2835-audio/bcm2835.h	2021-07-25 16:46:26.507946492 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:36 @
 enum snd_bcm2835_route {
 	AUDIO_DEST_AUTO = 0,
 	AUDIO_DEST_HEADPHONES = 1,
-	AUDIO_DEST_HDMI = 2,
+	AUDIO_DEST_HDMI = 2,  // for backwards compatibility.
+	AUDIO_DEST_HDMI0 = 2,
+	AUDIO_DEST_HDMI1 = 3,
 	AUDIO_DEST_MAX,
 };
 
diff -Nur --no-dereference linux-5.10.52-orig/drivers/staging/vc04_services/bcm2835-audio/bcm2835-pcm.c linux-5.10.52-v7l+/drivers/staging/vc04_services/bcm2835-audio/bcm2835-pcm.c
--- linux-5.10.52-orig/drivers/staging/vc04_services/bcm2835-audio/bcm2835-pcm.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/staging/vc04_services/bcm2835-audio/bcm2835-pcm.c	2021-07-25 16:46:26.507946492 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:17 @
 		 SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
 		 SNDRV_PCM_INFO_SYNC_APPLPTR),
 	.formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
-	.rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+	.rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_192000,
 	.rate_min = 8000,
-	.rate_max = 48000,
+	.rate_max = 192000,
 	.channels_min = 1,
-	.channels_max = 2,
-	.buffer_bytes_max = 128 * 1024,
+	.channels_max = 8,
+	.buffer_bytes_max = 512 * 1024,
 	.period_bytes_min = 1 * 1024,
-	.period_bytes_max = 128 * 1024,
+	.period_bytes_max = 512 * 1024,
 	.periods_min = 1,
 	.periods_max = 128,
 };
diff -Nur --no-dereference linux-5.10.52-orig/drivers/staging/vc04_services/bcm2835-camera/bcm2835-camera.c linux-5.10.52-v7l+/drivers/staging/vc04_services/bcm2835-camera/bcm2835-camera.c
--- linux-5.10.52-orig/drivers/staging/vc04_services/bcm2835-camera/bcm2835-camera.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/staging/vc04_services/bcm2835-camera/bcm2835-camera.c	2021-07-25 16:46:26.507946492 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:181 @
 		.ybbp = 1,
 		.remove_padding = 1,
 	}, {
-		.fourcc = V4L2_PIX_FMT_BGR32,
+		.fourcc = V4L2_PIX_FMT_BGRX32,
 		.mmal = MMAL_ENCODING_BGRA,
 		.depth = 32,
 		.mmal_component = COMP_CAMERA,
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1456 @
 	.vidioc_querybuf = vb2_ioctl_querybuf,
 	.vidioc_qbuf = vb2_ioctl_qbuf,
 	.vidioc_dqbuf = vb2_ioctl_dqbuf,
+	.vidioc_expbuf = vb2_ioctl_expbuf,
 	.vidioc_enum_framesizes = vidioc_enum_framesizes,
 	.vidioc_enum_frameintervals = vidioc_enum_frameintervals,
 	.vidioc_g_parm        = vidioc_g_parm,
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1738 @
 					      MMAL_PARAMETER_MINIMISE_FRAGMENTATION,
 					      &enable,
 					      sizeof(enable));
+
+		/* Enable inserting headers into the first frame */
+		vchiq_mmal_port_parameter_set(dev->instance,
+					      &dev->component[COMP_VIDEO_ENCODE]->control,
+					      MMAL_PARAMETER_VIDEO_ENCODE_HEADERS_WITH_FRAME,
+					      &enable, sizeof(enable));
 	}
 	ret = bm2835_mmal_set_all_camera_controls(dev);
 	if (ret < 0) {
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1938 @
 		q = &dev->capture.vb_vidq;
 		memset(q, 0, sizeof(*q));
 		q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-		q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_READ;
+		q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_READ | VB2_DMABUF;
 		q->drv_priv = dev;
 		q->buf_struct_size = sizeof(struct vb2_mmal_buffer);
 		q->ops = &bm2835_mmal_video_qops;
diff -Nur --no-dereference linux-5.10.52-orig/drivers/staging/vc04_services/bcm2835-camera/controls.c linux-5.10.52-v7l+/drivers/staging/vc04_services/bcm2835-camera/controls.c
--- linux-5.10.52-orig/drivers/staging/vc04_services/bcm2835-camera/controls.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/staging/vc04_services/bcm2835-camera/controls.c	2021-07-25 16:46:26.517946325 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:477 @
 	case V4L2_WHITE_BALANCE_SHADE:
 		u32_value = MMAL_PARAM_AWBMODE_SHADE;
 		break;
+
+	case V4L2_WHITE_BALANCE_GREYWORLD:
+		u32_value = MMAL_PARAM_AWBMODE_GREYWORLD;
+		break;
 	}
 
 	return vchiq_mmal_port_parameter_set(dev->instance, control,
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:712 @
 		case V4L2_MPEG_VIDEO_H264_LEVEL_3_1:
 		case V4L2_MPEG_VIDEO_H264_LEVEL_3_2:
 		case V4L2_MPEG_VIDEO_H264_LEVEL_4_0:
+		case V4L2_MPEG_VIDEO_H264_LEVEL_4_1:
+		case V4L2_MPEG_VIDEO_H264_LEVEL_4_2:
 			dev->capture.enc_level = ctrl->val;
 			break;
 		default:
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:779 @
 		case V4L2_MPEG_VIDEO_H264_LEVEL_4_0:
 			param.level = MMAL_VIDEO_LEVEL_H264_4;
 			break;
+		/*
+		 * Note that the hardware spec is level 4.0. Achieving levels
+		 * above that depend on exactly the resolution and frame rate
+		 * being requested.
+		 */
+		case V4L2_MPEG_VIDEO_H264_LEVEL_4_1:
+			param.level = MMAL_VIDEO_LEVEL_H264_41;
+			break;
+		case V4L2_MPEG_VIDEO_H264_LEVEL_4_2:
+			param.level = MMAL_VIDEO_LEVEL_H264_42;
+			break;
 		default:
 			/* Should never get here */
 			break;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1072 @
 	{
 		.id = V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE,
 		.type = MMAL_CONTROL_TYPE_STD_MENU,
-		.min = ~0x3ff,
-		.max = V4L2_WHITE_BALANCE_SHADE,
+		.min = ~0x7ff,
+		.max = V4L2_WHITE_BALANCE_GREYWORLD,
 		.def = V4L2_WHITE_BALANCE_AUTO,
 		.step = 0,
 		.imenu = NULL,
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1240 @
 			 BIT(V4L2_MPEG_VIDEO_H264_LEVEL_3_0) |
 			 BIT(V4L2_MPEG_VIDEO_H264_LEVEL_3_1) |
 			 BIT(V4L2_MPEG_VIDEO_H264_LEVEL_3_2) |
-			 BIT(V4L2_MPEG_VIDEO_H264_LEVEL_4_0)),
-		.max = V4L2_MPEG_VIDEO_H264_LEVEL_4_0,
+			 BIT(V4L2_MPEG_VIDEO_H264_LEVEL_4_0) |
+			 BIT(V4L2_MPEG_VIDEO_H264_LEVEL_4_1) |
+			 BIT(V4L2_MPEG_VIDEO_H264_LEVEL_4_2)),
+		.max = V4L2_MPEG_VIDEO_H264_LEVEL_4_2,
 		.def = V4L2_MPEG_VIDEO_H264_LEVEL_4_0,
 		.step = 1,
 		.imenu = NULL,
diff -Nur --no-dereference linux-5.10.52-orig/drivers/staging/vc04_services/bcm2835-codec/bcm2835-v4l2-codec.c linux-5.10.52-v7l+/drivers/staging/vc04_services/bcm2835-codec/bcm2835-v4l2-codec.c
--- linux-5.10.52-orig/drivers/staging/vc04_services/bcm2835-codec/bcm2835-v4l2-codec.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/staging/vc04_services/bcm2835-codec/bcm2835-v4l2-codec.c	2021-07-25 16:46:26.537945989 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * A v4l2-mem2mem device that wraps the video codec MMAL component.
+ *
+ * Copyright 2018 Raspberry Pi (Trading) Ltd.
+ * Author: Dave Stevenson (dave.stevenson@raspberrypi.com)
+ *
+ * Loosely based on the vim2m virtual driver by Pawel Osciak
+ * Copyright (c) 2009-2010 Samsung Electronics Co., Ltd.
+ * Pawel Osciak, <pawel@osciak.com>
+ * Marek Szyprowski, <m.szyprowski@samsung.com>
+ *
+ * Whilst this driver uses the v4l2_mem2mem framework, it does not need the
+ * scheduling aspects, so will always take the buffers, pass them to the VPU,
+ * and then signal the job as complete.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version
+ */
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/fs.h>
+#include <linux/timer.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/syscalls.h>
+
+#include <media/v4l2-mem2mem.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-event.h>
+#include <media/videobuf2-dma-contig.h>
+
+#include "vchiq-mmal/mmal-encodings.h"
+#include "vchiq-mmal/mmal-msg.h"
+#include "vchiq-mmal/mmal-parameters.h"
+#include "vchiq-mmal/mmal-vchiq.h"
+
+/*
+ * Default /dev/videoN node numbers for decode and encode.
+ * Deliberately avoid the very low numbers as these are often taken by webcams
+ * etc, and simple apps tend to only go for /dev/video0.
+ */
+static int decode_video_nr = 10;
+module_param(decode_video_nr, int, 0644);
+MODULE_PARM_DESC(decode_video_nr, "decoder video device number");
+
+static int encode_video_nr = 11;
+module_param(encode_video_nr, int, 0644);
+MODULE_PARM_DESC(encode_video_nr, "encoder video device number");
+
+static int isp_video_nr = 12;
+module_param(isp_video_nr, int, 0644);
+MODULE_PARM_DESC(isp_video_nr, "isp video device number");
+
+/*
+ * Workaround for GStreamer v4l2convert component not considering Bayer formats
+ * as raw, and therefore not considering a V4L2 device that supports them as
+ * a suitable candidate.
+ */
+static bool disable_bayer;
+module_param(disable_bayer, bool, 0644);
+MODULE_PARM_DESC(disable_bayer, "Disable support for Bayer formats");
+
+static unsigned int debug;
+module_param(debug, uint, 0644);
+MODULE_PARM_DESC(debug, "activates debug info (0-3)");
+
+enum bcm2835_codec_role {
+	DECODE,
+	ENCODE,
+	ISP,
+};
+
+static const char * const roles[] = {
+	"decode",
+	"encode",
+	"isp"
+};
+
+static const char * const components[] = {
+	"ril.video_decode",
+	"ril.video_encode",
+	"ril.isp",
+};
+
+/* Timeout for stop_streaming to allow all buffers to return */
+#define COMPLETE_TIMEOUT (2 * HZ)
+
+#define MIN_W		32
+#define MIN_H		32
+#define MAX_W		1920
+#define MAX_H		1920
+#define BPL_ALIGN	32
+#define DEFAULT_WIDTH	640
+#define DEFAULT_HEIGHT	480
+/*
+ * The unanswered question - what is the maximum size of a compressed frame?
+ * V4L2 mandates that the encoded frame must fit in a single buffer. Sizing
+ * that buffer is a compromise between wasting memory and risking not fitting.
+ * The 1080P version of Big Buck Bunny has some frames that exceed 512kB.
+ * Adopt a moderately arbitrary split at 720P for switching between 512 and
+ * 768kB buffers.
+ */
+#define DEF_COMP_BUF_SIZE_GREATER_720P	(768 << 10)
+#define DEF_COMP_BUF_SIZE_720P_OR_LESS	(512 << 10)
+
+/* Flags that indicate a format can be used for capture/output */
+#define MEM2MEM_CAPTURE		BIT(0)
+#define MEM2MEM_OUTPUT		BIT(1)
+
+#define MEM2MEM_NAME		"bcm2835-codec"
+
+struct bcm2835_codec_fmt {
+	u32	fourcc;
+	int	depth;
+	int	bytesperline_align;
+	u32	flags;
+	u32	mmal_fmt;
+	int	size_multiplier_x2;
+	bool	is_bayer;
+};
+
+static const struct bcm2835_codec_fmt supported_formats[] = {
+	{
+		/* YUV formats */
+		.fourcc			= V4L2_PIX_FMT_YUV420,
+		.depth			= 8,
+		.bytesperline_align	= 32,
+		.flags			= 0,
+		.mmal_fmt		= MMAL_ENCODING_I420,
+		.size_multiplier_x2	= 3,
+	}, {
+		.fourcc			= V4L2_PIX_FMT_YVU420,
+		.depth			= 8,
+		.bytesperline_align	= 32,
+		.flags			= 0,
+		.mmal_fmt		= MMAL_ENCODING_YV12,
+		.size_multiplier_x2	= 3,
+	}, {
+		.fourcc			= V4L2_PIX_FMT_NV12,
+		.depth			= 8,
+		.bytesperline_align	= 32,
+		.flags			= 0,
+		.mmal_fmt		= MMAL_ENCODING_NV12,
+		.size_multiplier_x2	= 3,
+	}, {
+		.fourcc			= V4L2_PIX_FMT_NV21,
+		.depth			= 8,
+		.bytesperline_align	= 32,
+		.flags			= 0,
+		.mmal_fmt		= MMAL_ENCODING_NV21,
+		.size_multiplier_x2	= 3,
+	}, {
+		.fourcc			= V4L2_PIX_FMT_RGB565,
+		.depth			= 16,
+		.bytesperline_align	= 32,
+		.flags			= 0,
+		.mmal_fmt		= MMAL_ENCODING_RGB16,
+		.size_multiplier_x2	= 2,
+	}, {
+		.fourcc			= V4L2_PIX_FMT_YUYV,
+		.depth			= 16,
+		.bytesperline_align	= 32,
+		.flags			= 0,
+		.mmal_fmt		= MMAL_ENCODING_YUYV,
+		.size_multiplier_x2	= 2,
+	}, {
+		.fourcc			= V4L2_PIX_FMT_UYVY,
+		.depth			= 16,
+		.bytesperline_align	= 32,
+		.flags			= 0,
+		.mmal_fmt		= MMAL_ENCODING_UYVY,
+		.size_multiplier_x2	= 2,
+	}, {
+		.fourcc			= V4L2_PIX_FMT_YVYU,
+		.depth			= 16,
+		.bytesperline_align	= 32,
+		.flags			= 0,
+		.mmal_fmt		= MMAL_ENCODING_YVYU,
+		.size_multiplier_x2	= 2,
+	}, {
+		.fourcc			= V4L2_PIX_FMT_VYUY,
+		.depth			= 16,
+		.bytesperline_align	= 32,
+		.flags			= 0,
+		.mmal_fmt		= MMAL_ENCODING_VYUY,
+		.size_multiplier_x2	= 2,
+	}, {
+		/* RGB formats */
+		.fourcc			= V4L2_PIX_FMT_RGB24,
+		.depth			= 24,
+		.bytesperline_align	= 32,
+		.flags			= 0,
+		.mmal_fmt		= MMAL_ENCODING_RGB24,
+		.size_multiplier_x2	= 2,
+	}, {
+		.fourcc			= V4L2_PIX_FMT_BGR24,
+		.depth			= 24,
+		.bytesperline_align	= 32,
+		.flags			= 0,
+		.mmal_fmt		= MMAL_ENCODING_BGR24,
+		.size_multiplier_x2	= 2,
+	}, {
+		.fourcc			= V4L2_PIX_FMT_BGR32,
+		.depth			= 32,
+		.bytesperline_align	= 32,
+		.flags			= 0,
+		.mmal_fmt		= MMAL_ENCODING_BGRA,
+		.size_multiplier_x2	= 2,
+	}, {
+		/* Bayer formats */
+		/* 8 bit */
+		.fourcc			= V4L2_PIX_FMT_SRGGB8,
+		.depth			= 8,
+		.bytesperline_align	= 32,
+		.flags			= 0,
+		.mmal_fmt		= MMAL_ENCODING_BAYER_SRGGB8,
+		.size_multiplier_x2	= 2,
+		.is_bayer		= true,
+	}, {
+		.fourcc			= V4L2_PIX_FMT_SBGGR8,
+		.depth			= 8,
+		.bytesperline_align	= 32,
+		.flags			= 0,
+		.mmal_fmt		= MMAL_ENCODING_BAYER_SBGGR8,
+		.size_multiplier_x2	= 2,
+		.is_bayer		= true,
+	}, {
+		.fourcc			= V4L2_PIX_FMT_SGRBG8,
+		.depth			= 8,
+		.bytesperline_align	= 32,
+		.flags			= 0,
+		.mmal_fmt		= MMAL_ENCODING_BAYER_SGRBG8,
+		.size_multiplier_x2	= 2,
+		.is_bayer		= true,
+	}, {
+		.fourcc			= V4L2_PIX_FMT_SGBRG8,
+		.depth			= 8,
+		.bytesperline_align	= 32,
+		.flags			= 0,
+		.mmal_fmt		= MMAL_ENCODING_BAYER_SGBRG8,
+		.size_multiplier_x2	= 2,
+		.is_bayer		= true,
+	}, {
+		/* 10 bit */
+		.fourcc			= V4L2_PIX_FMT_SRGGB10P,
+		.depth			= 10,
+		.bytesperline_align	= 32,
+		.flags			= 0,
+		.mmal_fmt		= MMAL_ENCODING_BAYER_SRGGB10P,
+		.size_multiplier_x2	= 2,
+		.is_bayer		= true,
+	}, {
+		.fourcc			= V4L2_PIX_FMT_SBGGR10P,
+		.depth			= 10,
+		.bytesperline_align	= 32,
+		.flags			= 0,
+		.mmal_fmt		= MMAL_ENCODING_BAYER_SBGGR10P,
+		.size_multiplier_x2	= 2,
+		.is_bayer		= true,
+	}, {
+		.fourcc			= V4L2_PIX_FMT_SGRBG10P,
+		.depth			= 10,
+		.bytesperline_align	= 32,
+		.flags			= 0,
+		.mmal_fmt		= MMAL_ENCODING_BAYER_SGRBG10P,
+		.size_multiplier_x2	= 2,
+		.is_bayer		= true,
+	}, {
+		.fourcc			= V4L2_PIX_FMT_SGBRG10P,
+		.depth			= 10,
+		.bytesperline_align	= 32,
+		.flags			= 0,
+		.mmal_fmt		= MMAL_ENCODING_BAYER_SGBRG10P,
+		.size_multiplier_x2	= 2,
+		.is_bayer		= true,
+	}, {
+		/* 12 bit */
+		.fourcc			= V4L2_PIX_FMT_SRGGB12P,
+		.depth			= 12,
+		.bytesperline_align	= 32,
+		.flags			= 0,
+		.mmal_fmt		= MMAL_ENCODING_BAYER_SRGGB12P,
+		.size_multiplier_x2	= 2,
+		.is_bayer		= true,
+	}, {
+		.fourcc			= V4L2_PIX_FMT_SBGGR12P,
+		.depth			= 12,
+		.bytesperline_align	= 32,
+		.flags			= 0,
+		.mmal_fmt		= MMAL_ENCODING_BAYER_SBGGR12P,
+		.size_multiplier_x2	= 2,
+		.is_bayer		= true,
+	}, {
+		.fourcc			= V4L2_PIX_FMT_SGRBG12P,
+		.depth			= 12,
+		.bytesperline_align	= 32,
+		.flags			= 0,
+		.mmal_fmt		= MMAL_ENCODING_BAYER_SGRBG12P,
+		.size_multiplier_x2	= 2,
+		.is_bayer		= true,
+	}, {
+		.fourcc			= V4L2_PIX_FMT_SGBRG12P,
+		.depth			= 12,
+		.bytesperline_align	= 32,
+		.flags			= 0,
+		.mmal_fmt		= MMAL_ENCODING_BAYER_SGBRG12P,
+		.size_multiplier_x2	= 2,
+		.is_bayer		= true,
+	}, {
+		/* 14 bit */
+		.fourcc			= V4L2_PIX_FMT_SRGGB14P,
+		.depth			= 14,
+		.bytesperline_align	= 32,
+		.flags			= 0,
+		.mmal_fmt		= MMAL_ENCODING_BAYER_SRGGB14P,
+		.size_multiplier_x2	= 2,
+		.is_bayer		= true,
+	}, {
+		.fourcc			= V4L2_PIX_FMT_SBGGR14P,
+		.depth			= 14,
+		.bytesperline_align	= 32,
+		.flags			= 0,
+		.mmal_fmt		= MMAL_ENCODING_BAYER_SBGGR14P,
+		.size_multiplier_x2	= 2,
+		.is_bayer		= true,
+
+	}, {
+		.fourcc			= V4L2_PIX_FMT_SGRBG14P,
+		.depth			= 14,
+		.bytesperline_align	= 32,
+		.flags			= 0,
+		.mmal_fmt		= MMAL_ENCODING_BAYER_SGRBG14P,
+		.size_multiplier_x2	= 2,
+		.is_bayer		= true,
+	}, {
+		.fourcc			= V4L2_PIX_FMT_SGBRG14P,
+		.depth			= 14,
+		.bytesperline_align	= 32,
+		.flags			= 0,
+		.mmal_fmt		= MMAL_ENCODING_BAYER_SGBRG14P,
+		.size_multiplier_x2	= 2,
+		.is_bayer		= true,
+	}, {
+		/* 16 bit */
+		.fourcc			= V4L2_PIX_FMT_SRGGB16,
+		.depth			= 16,
+		.bytesperline_align	= 32,
+		.flags			= 0,
+		.mmal_fmt		= MMAL_ENCODING_BAYER_SRGGB16,
+		.size_multiplier_x2	= 2,
+		.is_bayer		= true,
+	}, {
+		.fourcc			= V4L2_PIX_FMT_SBGGR16,
+		.depth			= 16,
+		.bytesperline_align	= 32,
+		.flags			= 0,
+		.mmal_fmt		= MMAL_ENCODING_BAYER_SBGGR16,
+		.size_multiplier_x2	= 2,
+		.is_bayer		= true,
+	}, {
+		.fourcc			= V4L2_PIX_FMT_SGRBG16,
+		.depth			= 16,
+		.bytesperline_align	= 32,
+		.flags			= 0,
+		.mmal_fmt		= MMAL_ENCODING_BAYER_SGRBG16,
+		.size_multiplier_x2	= 2,
+		.is_bayer		= true,
+	}, {
+		.fourcc			= V4L2_PIX_FMT_SGBRG16,
+		.depth			= 16,
+		.bytesperline_align	= 32,
+		.flags			= 0,
+		.mmal_fmt		= MMAL_ENCODING_BAYER_SGBRG16,
+		.size_multiplier_x2	= 2,
+		.is_bayer		= true,
+	}, {
+		/* Bayer formats unpacked to 16bpp */
+		/* 10 bit */
+		.fourcc			= V4L2_PIX_FMT_SRGGB10,
+		.depth			= 16,
+		.bytesperline_align	= 32,
+		.flags			= 0,
+		.mmal_fmt		= MMAL_ENCODING_BAYER_SRGGB10,
+		.size_multiplier_x2	= 2,
+		.is_bayer		= true,
+	}, {
+		.fourcc			= V4L2_PIX_FMT_SBGGR10,
+		.depth			= 16,
+		.bytesperline_align	= 32,
+		.flags			= 0,
+		.mmal_fmt		= MMAL_ENCODING_BAYER_SBGGR10,
+		.size_multiplier_x2	= 2,
+		.is_bayer		= true,
+	}, {
+		.fourcc			= V4L2_PIX_FMT_SGRBG10,
+		.depth			= 16,
+		.bytesperline_align	= 32,
+		.flags			= 0,
+		.mmal_fmt		= MMAL_ENCODING_BAYER_SGRBG10,
+		.size_multiplier_x2	= 2,
+		.is_bayer		= true,
+	}, {
+		.fourcc			= V4L2_PIX_FMT_SGBRG10,
+		.depth			= 16,
+		.bytesperline_align	= 32,
+		.flags			= 0,
+		.mmal_fmt		= MMAL_ENCODING_BAYER_SGBRG10,
+		.size_multiplier_x2	= 2,
+		.is_bayer		= true,
+	}, {
+		/* 12 bit */
+		.fourcc			= V4L2_PIX_FMT_SRGGB12,
+		.depth			= 16,
+		.bytesperline_align	= 32,
+		.flags			= 0,
+		.mmal_fmt		= MMAL_ENCODING_BAYER_SRGGB12,
+		.size_multiplier_x2	= 2,
+		.is_bayer		= true,
+	}, {
+		.fourcc			= V4L2_PIX_FMT_SBGGR12,
+		.depth			= 16,
+		.bytesperline_align	= 32,
+		.flags			= 0,
+		.mmal_fmt		= MMAL_ENCODING_BAYER_SBGGR12,
+		.size_multiplier_x2	= 2,
+		.is_bayer		= true,
+	}, {
+		.fourcc			= V4L2_PIX_FMT_SGRBG12,
+		.depth			= 16,
+		.bytesperline_align	= 32,
+		.flags			= 0,
+		.mmal_fmt		= MMAL_ENCODING_BAYER_SGRBG12,
+		.size_multiplier_x2	= 2,
+		.is_bayer		= true,
+	}, {
+		.fourcc			= V4L2_PIX_FMT_SGBRG12,
+		.depth			= 16,
+		.bytesperline_align	= 32,
+		.flags			= 0,
+		.mmal_fmt		= MMAL_ENCODING_BAYER_SGBRG12,
+		.size_multiplier_x2	= 2,
+		.is_bayer		= true,
+	}, {
+		/* 14 bit */
+		.fourcc			= V4L2_PIX_FMT_SRGGB14,
+		.depth			= 16,
+		.bytesperline_align	= 32,
+		.flags			= 0,
+		.mmal_fmt		= MMAL_ENCODING_BAYER_SRGGB14,
+		.size_multiplier_x2	= 2,
+		.is_bayer		= true,
+	}, {
+		.fourcc			= V4L2_PIX_FMT_SBGGR14,
+		.depth			= 16,
+		.bytesperline_align	= 32,
+		.flags			= 0,
+		.mmal_fmt		= MMAL_ENCODING_BAYER_SBGGR14,
+		.size_multiplier_x2	= 2,
+		.is_bayer		= true,
+	}, {
+		.fourcc			= V4L2_PIX_FMT_SGRBG14,
+		.depth			= 16,
+		.bytesperline_align	= 32,
+		.flags			= 0,
+		.mmal_fmt		= MMAL_ENCODING_BAYER_SGRBG14,
+		.size_multiplier_x2	= 2,
+		.is_bayer		= true,
+	}, {
+		.fourcc			= V4L2_PIX_FMT_SGBRG14,
+		.depth			= 16,
+		.bytesperline_align	= 32,
+		.flags			= 0,
+		.mmal_fmt		= MMAL_ENCODING_BAYER_SGBRG14,
+		.size_multiplier_x2	= 2,
+		.is_bayer		= true,
+	}, {
+		/* Monochrome MIPI formats */
+		/* 8 bit */
+		.fourcc			= V4L2_PIX_FMT_GREY,
+		.depth			= 8,
+		.bytesperline_align	= 32,
+		.flags			= 0,
+		.mmal_fmt		= MMAL_ENCODING_GREY,
+		.size_multiplier_x2	= 2,
+	}, {
+		/* 10 bit */
+		.fourcc			= V4L2_PIX_FMT_Y10P,
+		.depth			= 10,
+		.bytesperline_align	= 32,
+		.flags			= 0,
+		.mmal_fmt		= MMAL_ENCODING_Y10P,
+		.size_multiplier_x2	= 2,
+	}, {
+		/* 12 bit */
+		.fourcc			= V4L2_PIX_FMT_Y12P,
+		.depth			= 12,
+		.bytesperline_align	= 32,
+		.flags			= 0,
+		.mmal_fmt		= MMAL_ENCODING_Y12P,
+		.size_multiplier_x2	= 2,
+	}, {
+		/* 14 bit */
+		.fourcc			= V4L2_PIX_FMT_Y14P,
+		.depth			= 14,
+		.bytesperline_align	= 32,
+		.flags			= 0,
+		.mmal_fmt		= MMAL_ENCODING_Y14P,
+		.size_multiplier_x2	= 2,
+	}, {
+		/* 16 bit */
+		.fourcc			= V4L2_PIX_FMT_Y16,
+		.depth			= 16,
+		.bytesperline_align	= 32,
+		.flags			= 0,
+		.mmal_fmt		= MMAL_ENCODING_Y16,
+		.size_multiplier_x2	= 2,
+	}, {
+		/* 10 bit as 16bpp */
+		.fourcc			= V4L2_PIX_FMT_Y10,
+		.depth			= 16,
+		.bytesperline_align	= 32,
+		.flags			= 0,
+		.mmal_fmt		= MMAL_ENCODING_Y10,
+		.size_multiplier_x2	= 2,
+	}, {
+		/* 12 bit as 16bpp */
+		.fourcc			= V4L2_PIX_FMT_Y12,
+		.depth			= 16,
+		.bytesperline_align	= 32,
+		.flags			= 0,
+		.mmal_fmt		= MMAL_ENCODING_Y12,
+		.size_multiplier_x2	= 2,
+	}, {
+		/* 14 bit as 16bpp */
+		.fourcc			= V4L2_PIX_FMT_Y14,
+		.depth			= 16,
+		.bytesperline_align	= 32,
+		.flags			= 0,
+		.mmal_fmt		= MMAL_ENCODING_Y14,
+		.size_multiplier_x2	= 2,
+	}, {
+		/* Compressed formats */
+		.fourcc			= V4L2_PIX_FMT_H264,
+		.depth			= 0,
+		.flags			= V4L2_FMT_FLAG_COMPRESSED,
+		.mmal_fmt		= MMAL_ENCODING_H264,
+	}, {
+		.fourcc			= V4L2_PIX_FMT_MJPEG,
+		.depth			= 0,
+		.flags			= V4L2_FMT_FLAG_COMPRESSED,
+		.mmal_fmt		= MMAL_ENCODING_MJPEG,
+	}, {
+		.fourcc			= V4L2_PIX_FMT_MPEG4,
+		.depth			= 0,
+		.flags			= V4L2_FMT_FLAG_COMPRESSED,
+		.mmal_fmt		= MMAL_ENCODING_MP4V,
+	}, {
+		.fourcc			= V4L2_PIX_FMT_H263,
+		.depth			= 0,
+		.flags			= V4L2_FMT_FLAG_COMPRESSED,
+		.mmal_fmt		= MMAL_ENCODING_H263,
+	}, {
+		.fourcc			= V4L2_PIX_FMT_MPEG2,
+		.depth			= 0,
+		.flags			= V4L2_FMT_FLAG_COMPRESSED,
+		.mmal_fmt		= MMAL_ENCODING_MP2V,
+	}, {
+		.fourcc			= V4L2_PIX_FMT_VP8,
+		.depth			= 0,
+		.flags			= V4L2_FMT_FLAG_COMPRESSED,
+		.mmal_fmt		= MMAL_ENCODING_VP8,
+	}, {
+		.fourcc			= V4L2_PIX_FMT_VC1_ANNEX_G,
+		.depth			= 0,
+		.flags			= V4L2_FMT_FLAG_COMPRESSED,
+		.mmal_fmt		= MMAL_ENCODING_WVC1,
+	}
+};
+
+struct bcm2835_codec_fmt_list {
+	struct bcm2835_codec_fmt *list;
+	unsigned int num_entries;
+};
+
+struct m2m_mmal_buffer {
+	struct v4l2_m2m_buffer	m2m;
+	struct mmal_buffer	mmal;
+};
+
+/* Per-queue, driver-specific private data */
+struct bcm2835_codec_q_data {
+	/*
+	 * These parameters should be treated as gospel, with everything else
+	 * being determined from them.
+	 */
+	/* Buffer width/height */
+	unsigned int		bytesperline;
+	unsigned int		height;
+	/* Crop size used for selection handling */
+	unsigned int		crop_width;
+	unsigned int		crop_height;
+	bool			selection_set;
+	struct v4l2_fract	aspect_ratio;
+
+	unsigned int		sizeimage;
+	unsigned int		sequence;
+	struct bcm2835_codec_fmt	*fmt;
+
+	/* One extra buffer header so we can send an EOS. */
+	struct m2m_mmal_buffer	eos_buffer;
+	bool			eos_buffer_in_use;	/* debug only */
+};
+
+struct bcm2835_codec_dev {
+	struct platform_device *pdev;
+
+	/* v4l2 devices */
+	struct v4l2_device	v4l2_dev;
+	struct video_device	vfd;
+	/* mutex for the v4l2 device */
+	struct mutex		dev_mutex;
+	atomic_t		num_inst;
+
+	/* allocated mmal instance and components */
+	enum bcm2835_codec_role	role;
+	/* The list of formats supported on input and output queues. */
+	struct bcm2835_codec_fmt_list	supported_fmts[2];
+
+	struct vchiq_mmal_instance	*instance;
+
+	struct v4l2_m2m_dev	*m2m_dev;
+};
+
+struct bcm2835_codec_ctx {
+	struct v4l2_fh		fh;
+	struct bcm2835_codec_dev	*dev;
+
+	struct v4l2_ctrl_handler hdl;
+
+	struct vchiq_mmal_component  *component;
+	bool component_enabled;
+
+	enum v4l2_colorspace	colorspace;
+	enum v4l2_ycbcr_encoding ycbcr_enc;
+	enum v4l2_xfer_func	xfer_func;
+	enum v4l2_quantization	quant;
+
+	/* Source and destination queue data */
+	struct bcm2835_codec_q_data   q_data[2];
+	s32  bitrate;
+	unsigned int	framerate_num;
+	unsigned int	framerate_denom;
+
+	bool aborting;
+	int num_ip_buffers;
+	int num_op_buffers;
+	struct completion frame_cmplt;
+};
+
+struct bcm2835_codec_driver {
+	struct platform_device *pdev;
+	struct media_device	mdev;
+
+	struct bcm2835_codec_dev *encode;
+	struct bcm2835_codec_dev *decode;
+	struct bcm2835_codec_dev *isp;
+};
+
+enum {
+	V4L2_M2M_SRC = 0,
+	V4L2_M2M_DST = 1,
+};
+
+static const struct bcm2835_codec_fmt *get_fmt(u32 mmal_fmt)
+{
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(supported_formats); i++) {
+		if (supported_formats[i].mmal_fmt == mmal_fmt &&
+		    (!disable_bayer || !supported_formats[i].is_bayer))
+			return &supported_formats[i];
+	}
+	return NULL;
+}
+
+static inline
+struct bcm2835_codec_fmt_list *get_format_list(struct bcm2835_codec_dev *dev,
+					       bool capture)
+{
+	return &dev->supported_fmts[capture ? 1 : 0];
+}
+
+static
+struct bcm2835_codec_fmt *get_default_format(struct bcm2835_codec_dev *dev,
+					     bool capture)
+{
+	return &dev->supported_fmts[capture ? 1 : 0].list[0];
+}
+
+static
+struct bcm2835_codec_fmt *find_format_pix_fmt(u32 pix_fmt,
+					      struct bcm2835_codec_dev *dev,
+					      bool capture)
+{
+	struct bcm2835_codec_fmt *fmt;
+	unsigned int k;
+	struct bcm2835_codec_fmt_list *fmts =
+					&dev->supported_fmts[capture ? 1 : 0];
+
+	for (k = 0; k < fmts->num_entries; k++) {
+		fmt = &fmts->list[k];
+		if (fmt->fourcc == pix_fmt)
+			break;
+	}
+	if (k == fmts->num_entries)
+		return NULL;
+
+	return &fmts->list[k];
+}
+
+static inline
+struct bcm2835_codec_fmt *find_format(struct v4l2_format *f,
+				      struct bcm2835_codec_dev *dev,
+				      bool capture)
+{
+	return find_format_pix_fmt(f->fmt.pix_mp.pixelformat, dev, capture);
+}
+
+static inline struct bcm2835_codec_ctx *file2ctx(struct file *file)
+{
+	return container_of(file->private_data, struct bcm2835_codec_ctx, fh);
+}
+
+static struct bcm2835_codec_q_data *get_q_data(struct bcm2835_codec_ctx *ctx,
+					       enum v4l2_buf_type type)
+{
+	switch (type) {
+	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+		return &ctx->q_data[V4L2_M2M_SRC];
+	case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+		return &ctx->q_data[V4L2_M2M_DST];
+	default:
+		v4l2_err(&ctx->dev->v4l2_dev, "%s: Invalid queue type %u\n",
+			 __func__, type);
+		break;
+	}
+	return NULL;
+}
+
+static struct vchiq_mmal_port *get_port_data(struct bcm2835_codec_ctx *ctx,
+					     enum v4l2_buf_type type)
+{
+	if (!ctx->component)
+		return NULL;
+
+	switch (type) {
+	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+		return &ctx->component->input[0];
+	case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+		return &ctx->component->output[0];
+	default:
+		v4l2_err(&ctx->dev->v4l2_dev, "%s: Invalid queue type %u\n",
+			 __func__, type);
+		break;
+	}
+	return NULL;
+}
+
+/*
+ * mem2mem callbacks
+ */
+
+/*
+ * job_ready() - check whether an instance is ready to be scheduled to run
+ */
+static int job_ready(void *priv)
+{
+	struct bcm2835_codec_ctx *ctx = priv;
+
+	if (!v4l2_m2m_num_src_bufs_ready(ctx->fh.m2m_ctx) &&
+	    !v4l2_m2m_num_dst_bufs_ready(ctx->fh.m2m_ctx))
+		return 0;
+
+	return 1;
+}
+
+static void job_abort(void *priv)
+{
+	struct bcm2835_codec_ctx *ctx = priv;
+
+	v4l2_dbg(1, debug, &ctx->dev->v4l2_dev, "%s\n", __func__);
+	/* Will cancel the transaction in the next interrupt handler */
+	ctx->aborting = 1;
+}
+
+static inline unsigned int get_sizeimage(int bpl, int width, int height,
+					 struct bcm2835_codec_fmt *fmt)
+{
+	if (fmt->flags & V4L2_FMT_FLAG_COMPRESSED) {
+		if (width * height > 1280 * 720)
+			return DEF_COMP_BUF_SIZE_GREATER_720P;
+		else
+			return DEF_COMP_BUF_SIZE_720P_OR_LESS;
+	} else {
+		return (bpl * height * fmt->size_multiplier_x2) >> 1;
+	}
+}
+
+static inline unsigned int get_bytesperline(int width,
+					    struct bcm2835_codec_fmt *fmt)
+{
+	return ALIGN((width * fmt->depth) >> 3, fmt->bytesperline_align);
+}
+
+static void setup_mmal_port_format(struct bcm2835_codec_ctx *ctx,
+				   struct bcm2835_codec_q_data *q_data,
+				   struct vchiq_mmal_port *port)
+{
+	port->format.encoding = q_data->fmt->mmal_fmt;
+
+	if (!(q_data->fmt->flags & V4L2_FMT_FLAG_COMPRESSED)) {
+		/* Raw image format - set width/height */
+		port->es.video.width = (q_data->bytesperline << 3) /
+						q_data->fmt->depth;
+		port->es.video.height = q_data->height;
+		port->es.video.crop.width = q_data->crop_width;
+		port->es.video.crop.height = q_data->crop_height;
+		port->es.video.frame_rate.num = ctx->framerate_num;
+		port->es.video.frame_rate.den = ctx->framerate_denom;
+	} else {
+		/* Compressed format - leave resolution as 0 for decode */
+		if (ctx->dev->role == DECODE) {
+			port->es.video.width = 0;
+			port->es.video.height = 0;
+			port->es.video.crop.width = 0;
+			port->es.video.crop.height = 0;
+		} else {
+			port->es.video.width = q_data->crop_width;
+			port->es.video.height = q_data->height;
+			port->es.video.crop.width = q_data->crop_width;
+			port->es.video.crop.height = q_data->crop_height;
+			port->format.bitrate = ctx->bitrate;
+			port->es.video.frame_rate.num = ctx->framerate_num;
+			port->es.video.frame_rate.den = ctx->framerate_denom;
+		}
+	}
+	port->es.video.crop.x = 0;
+	port->es.video.crop.y = 0;
+
+	port->current_buffer.size = q_data->sizeimage;
+};
+
+static void ip_buffer_cb(struct vchiq_mmal_instance *instance,
+			 struct vchiq_mmal_port *port, int status,
+			 struct mmal_buffer *mmal_buf)
+{
+	struct bcm2835_codec_ctx *ctx = port->cb_ctx/*, *curr_ctx*/;
+	struct m2m_mmal_buffer *buf =
+			container_of(mmal_buf, struct m2m_mmal_buffer, mmal);
+
+	v4l2_dbg(2, debug, &ctx->dev->v4l2_dev, "%s: port %p buf %p length %lu, flags %x\n",
+		 __func__, port, mmal_buf, mmal_buf->length,
+		 mmal_buf->mmal_flags);
+
+	if (buf == &ctx->q_data[V4L2_M2M_SRC].eos_buffer) {
+		/* Do we need to add lcoking to prevent multiple submission of
+		 * the EOS, and therefore handle mutliple return here?
+		 */
+		v4l2_dbg(1, debug, &ctx->dev->v4l2_dev, "%s: eos buffer returned.\n",
+			 __func__);
+		ctx->q_data[V4L2_M2M_SRC].eos_buffer_in_use = false;
+		return;
+	}
+
+	if (status) {
+		/* error in transfer */
+		if (buf)
+			/* there was a buffer with the error so return it */
+			vb2_buffer_done(&buf->m2m.vb.vb2_buf,
+					VB2_BUF_STATE_ERROR);
+		return;
+	}
+	if (mmal_buf->cmd) {
+		v4l2_err(&ctx->dev->v4l2_dev, "%s: Not expecting cmd msgs on ip callback - %08x\n",
+			 __func__, mmal_buf->cmd);
+		/*
+		 * CHECKME: Should we return here. The buffer shouldn't have a
+		 * message context or vb2 buf associated.
+		 */
+	}
+
+	v4l2_dbg(3, debug, &ctx->dev->v4l2_dev, "%s: no error. Return buffer %p\n",
+		 __func__, &buf->m2m.vb.vb2_buf);
+	vb2_buffer_done(&buf->m2m.vb.vb2_buf, VB2_BUF_STATE_DONE);
+
+	ctx->num_ip_buffers++;
+	v4l2_dbg(2, debug, &ctx->dev->v4l2_dev, "%s: done %d input buffers\n",
+		 __func__, ctx->num_ip_buffers);
+
+	if (!port->enabled)
+		complete(&ctx->frame_cmplt);
+}
+
+static void queue_res_chg_event(struct bcm2835_codec_ctx *ctx)
+{
+	static const struct v4l2_event ev_src_ch = {
+		.type = V4L2_EVENT_SOURCE_CHANGE,
+		.u.src_change.changes =
+		V4L2_EVENT_SRC_CH_RESOLUTION,
+	};
+
+	v4l2_event_queue_fh(&ctx->fh, &ev_src_ch);
+}
+
+static void send_eos_event(struct bcm2835_codec_ctx *ctx)
+{
+	static const struct v4l2_event ev = {
+		.type = V4L2_EVENT_EOS,
+	};
+
+	v4l2_dbg(1, debug, &ctx->dev->v4l2_dev, "Sending EOS event\n");
+
+	v4l2_event_queue_fh(&ctx->fh, &ev);
+}
+
+static void color_mmal2v4l(struct bcm2835_codec_ctx *ctx, u32 encoding,
+			   u32 color_space)
+{
+	int is_rgb;
+
+	switch (encoding) {
+	case MMAL_ENCODING_I420:
+	case MMAL_ENCODING_YV12:
+	case MMAL_ENCODING_NV12:
+	case MMAL_ENCODING_NV21:
+	case V4L2_PIX_FMT_YUYV:
+	case V4L2_PIX_FMT_YVYU:
+	case V4L2_PIX_FMT_UYVY:
+	case V4L2_PIX_FMT_VYUY:
+		/* YUV based colourspaces */
+		switch (color_space) {
+		case MMAL_COLOR_SPACE_ITUR_BT601:
+			ctx->colorspace = V4L2_COLORSPACE_SMPTE170M;
+			break;
+
+		case MMAL_COLOR_SPACE_ITUR_BT709:
+			ctx->colorspace = V4L2_COLORSPACE_REC709;
+			break;
+		default:
+			break;
+		}
+		break;
+	default:
+		/* RGB based colourspaces */
+		ctx->colorspace = V4L2_COLORSPACE_SRGB;
+		break;
+	}
+	ctx->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(ctx->colorspace);
+	ctx->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(ctx->colorspace);
+	is_rgb = ctx->colorspace == V4L2_COLORSPACE_SRGB;
+	ctx->quant = V4L2_MAP_QUANTIZATION_DEFAULT(is_rgb, ctx->colorspace,
+						   ctx->ycbcr_enc);
+}
+
+static void handle_fmt_changed(struct bcm2835_codec_ctx *ctx,
+			       struct mmal_buffer *mmal_buf)
+{
+	struct bcm2835_codec_q_data *q_data;
+	struct mmal_msg_event_format_changed *format =
+		(struct mmal_msg_event_format_changed *)mmal_buf->buffer;
+	v4l2_dbg(1, debug, &ctx->dev->v4l2_dev, "%s: Format changed: buff size min %u, rec %u, buff num min %u, rec %u\n",
+		 __func__,
+		 format->buffer_size_min,
+		 format->buffer_size_recommended,
+		 format->buffer_num_min,
+		 format->buffer_num_recommended
+		);
+	if (format->format.type != MMAL_ES_TYPE_VIDEO) {
+		v4l2_dbg(1, debug, &ctx->dev->v4l2_dev, "%s: Format changed but not video %u\n",
+			 __func__, format->format.type);
+		return;
+	}
+	v4l2_dbg(1, debug, &ctx->dev->v4l2_dev, "%s: Format changed to %ux%u, crop %ux%u, colourspace %08X\n",
+		 __func__, format->es.video.width, format->es.video.height,
+		 format->es.video.crop.width, format->es.video.crop.height,
+		 format->es.video.color_space);
+
+	q_data = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
+	v4l2_dbg(1, debug, &ctx->dev->v4l2_dev, "%s: Format was %ux%u, crop %ux%u\n",
+		 __func__, q_data->bytesperline, q_data->height,
+		 q_data->crop_width, q_data->crop_height);
+
+	q_data->crop_width = format->es.video.crop.width;
+	q_data->crop_height = format->es.video.crop.height;
+	/*
+	 * Stop S_FMT updating crop_height should it be unaligned.
+	 * Client can still update the crop region via S_SELECTION should it
+	 * really want to, but the decoder is likely to complain that the
+	 * format then doesn't match.
+	 */
+	q_data->selection_set = true;
+	q_data->bytesperline = get_bytesperline(format->es.video.width,
+						q_data->fmt);
+
+	q_data->height = format->es.video.height;
+	q_data->sizeimage = format->buffer_size_min;
+	if (format->es.video.color_space)
+		color_mmal2v4l(ctx, format->format.encoding,
+			       format->es.video.color_space);
+
+	q_data->aspect_ratio.numerator = format->es.video.par.num;
+	q_data->aspect_ratio.denominator = format->es.video.par.den;
+
+	queue_res_chg_event(ctx);
+}
+
+static void op_buffer_cb(struct vchiq_mmal_instance *instance,
+			 struct vchiq_mmal_port *port, int status,
+			 struct mmal_buffer *mmal_buf)
+{
+	struct bcm2835_codec_ctx *ctx = port->cb_ctx;
+	enum vb2_buffer_state buf_state = VB2_BUF_STATE_DONE;
+	struct m2m_mmal_buffer *buf;
+	struct vb2_v4l2_buffer *vb2;
+
+	v4l2_dbg(2, debug, &ctx->dev->v4l2_dev,
+		 "%s: status:%d, buf:%p, length:%lu, flags %u, pts %lld\n",
+		 __func__, status, mmal_buf, mmal_buf->length,
+		 mmal_buf->mmal_flags, mmal_buf->pts);
+
+	buf = container_of(mmal_buf, struct m2m_mmal_buffer, mmal);
+	vb2 = &buf->m2m.vb;
+
+	if (status) {
+		/* error in transfer */
+		if (vb2) {
+			/* there was a buffer with the error so return it */
+			vb2_buffer_done(&vb2->vb2_buf, VB2_BUF_STATE_ERROR);
+		}
+		return;
+	}
+
+	if (mmal_buf->cmd) {
+		switch (mmal_buf->cmd) {
+		case MMAL_EVENT_FORMAT_CHANGED:
+		{
+			handle_fmt_changed(ctx, mmal_buf);
+			break;
+		}
+		default:
+			v4l2_err(&ctx->dev->v4l2_dev, "%s: Unexpected event on output callback - %08x\n",
+				 __func__, mmal_buf->cmd);
+			break;
+		}
+		return;
+	}
+
+	v4l2_dbg(3, debug, &ctx->dev->v4l2_dev, "%s: length %lu, flags %x, idx %u\n",
+		 __func__, mmal_buf->length, mmal_buf->mmal_flags,
+		 vb2->vb2_buf.index);
+
+	if (mmal_buf->length == 0) {
+		/* stream ended, or buffer being returned during disable. */
+		v4l2_dbg(2, debug, &ctx->dev->v4l2_dev, "%s: Empty buffer - flags %04x",
+			 __func__, mmal_buf->mmal_flags);
+		if (!(mmal_buf->mmal_flags & MMAL_BUFFER_HEADER_FLAG_EOS)) {
+			vb2_buffer_done(&vb2->vb2_buf, VB2_BUF_STATE_ERROR);
+			if (!port->enabled)
+				complete(&ctx->frame_cmplt);
+			return;
+		}
+	}
+	if (mmal_buf->mmal_flags & MMAL_BUFFER_HEADER_FLAG_EOS) {
+		/* EOS packet from the VPU */
+		send_eos_event(ctx);
+		vb2->flags |= V4L2_BUF_FLAG_LAST;
+	}
+
+	if (mmal_buf->mmal_flags & MMAL_BUFFER_HEADER_FLAG_CORRUPTED)
+		buf_state = VB2_BUF_STATE_ERROR;
+
+	/* vb2 timestamps in nsecs, mmal in usecs */
+	vb2->vb2_buf.timestamp = mmal_buf->pts * 1000;
+
+	vb2_set_plane_payload(&vb2->vb2_buf, 0, mmal_buf->length);
+	if (mmal_buf->mmal_flags & MMAL_BUFFER_HEADER_FLAG_KEYFRAME)
+		vb2->flags |= V4L2_BUF_FLAG_KEYFRAME;
+
+	vb2_buffer_done(&vb2->vb2_buf, buf_state);
+	ctx->num_op_buffers++;
+
+	v4l2_dbg(2, debug, &ctx->dev->v4l2_dev, "%s: done %d output buffers\n",
+		 __func__, ctx->num_op_buffers);
+
+	if (!port->enabled)
+		complete(&ctx->frame_cmplt);
+}
+
+/* vb2_to_mmal_buffer() - converts vb2 buffer header to MMAL
+ *
+ * Copies all the required fields from a VB2 buffer to the MMAL buffer header,
+ * ready for sending to the VPU.
+ */
+static void vb2_to_mmal_buffer(struct m2m_mmal_buffer *buf,
+			       struct vb2_v4l2_buffer *vb2)
+{
+	u64 pts;
+
+	buf->mmal.mmal_flags = 0;
+	if (vb2->flags & V4L2_BUF_FLAG_KEYFRAME)
+		buf->mmal.mmal_flags |= MMAL_BUFFER_HEADER_FLAG_KEYFRAME;
+
+	/*
+	 * Adding this means that the data must be framed correctly as one frame
+	 * per buffer. The underlying decoder has no such requirement, but it
+	 * will reduce latency as the bistream parser will be kicked immediately
+	 * to parse the frame, rather than relying on its own heuristics for
+	 * when to wake up.
+	 */
+	buf->mmal.mmal_flags |= MMAL_BUFFER_HEADER_FLAG_FRAME_END;
+
+	buf->mmal.length = vb2->vb2_buf.planes[0].bytesused;
+	/*
+	 * Minor ambiguity in the V4L2 spec as to whether passing in a 0 length
+	 * buffer, or one with V4L2_BUF_FLAG_LAST set denotes end of stream.
+	 * Handle either.
+	 */
+	if (!buf->mmal.length || vb2->flags & V4L2_BUF_FLAG_LAST)
+		buf->mmal.mmal_flags |= MMAL_BUFFER_HEADER_FLAG_EOS;
+
+	/* vb2 timestamps in nsecs, mmal in usecs */
+	pts = vb2->vb2_buf.timestamp;
+	do_div(pts, 1000);
+	buf->mmal.pts = pts;
+	buf->mmal.dts = MMAL_TIME_UNKNOWN;
+}
+
+/* device_run() - prepares and starts the device
+ *
+ * This simulates all the immediate preparations required before starting
+ * a device. This will be called by the framework when it decides to schedule
+ * a particular instance.
+ */
+static void device_run(void *priv)
+{
+	struct bcm2835_codec_ctx *ctx = priv;
+	struct bcm2835_codec_dev *dev = ctx->dev;
+	struct vb2_v4l2_buffer *src_buf, *dst_buf;
+	struct m2m_mmal_buffer *src_m2m_buf = NULL, *dst_m2m_buf = NULL;
+	struct v4l2_m2m_buffer *m2m;
+	int ret;
+
+	v4l2_dbg(3, debug, &ctx->dev->v4l2_dev, "%s: off we go\n", __func__);
+
+	src_buf = v4l2_m2m_buf_remove(&ctx->fh.m2m_ctx->out_q_ctx);
+	if (src_buf) {
+		m2m = container_of(src_buf, struct v4l2_m2m_buffer, vb);
+		src_m2m_buf = container_of(m2m, struct m2m_mmal_buffer, m2m);
+		vb2_to_mmal_buffer(src_m2m_buf, src_buf);
+
+		ret = vchiq_mmal_submit_buffer(dev->instance,
+					       &ctx->component->input[0],
+					       &src_m2m_buf->mmal);
+		v4l2_dbg(3, debug, &ctx->dev->v4l2_dev, "%s: Submitted ip buffer len %lu, pts %llu, flags %04x\n",
+			 __func__, src_m2m_buf->mmal.length,
+			 src_m2m_buf->mmal.pts, src_m2m_buf->mmal.mmal_flags);
+		if (ret)
+			v4l2_err(&ctx->dev->v4l2_dev, "%s: Failed submitting ip buffer\n",
+				 __func__);
+	}
+
+	dst_buf = v4l2_m2m_buf_remove(&ctx->fh.m2m_ctx->cap_q_ctx);
+	if (dst_buf) {
+		m2m = container_of(dst_buf, struct v4l2_m2m_buffer, vb);
+		dst_m2m_buf = container_of(m2m, struct m2m_mmal_buffer, m2m);
+		vb2_to_mmal_buffer(dst_m2m_buf, dst_buf);
+
+		ret = vchiq_mmal_submit_buffer(dev->instance,
+					       &ctx->component->output[0],
+					       &dst_m2m_buf->mmal);
+		if (ret)
+			v4l2_err(&ctx->dev->v4l2_dev, "%s: Failed submitting op buffer\n",
+				 __func__);
+	}
+
+	v4l2_dbg(3, debug, &ctx->dev->v4l2_dev, "%s: Submitted src %p, dst %p\n",
+		 __func__, src_m2m_buf, dst_m2m_buf);
+
+	/* Complete the job here. */
+	v4l2_m2m_job_finish(ctx->dev->m2m_dev, ctx->fh.m2m_ctx);
+}
+
+/*
+ * video ioctls
+ */
+static int vidioc_querycap(struct file *file, void *priv,
+			   struct v4l2_capability *cap)
+{
+	struct bcm2835_codec_dev *dev = video_drvdata(file);
+
+	strncpy(cap->driver, MEM2MEM_NAME, sizeof(cap->driver) - 1);
+	strncpy(cap->card, dev->vfd.name, sizeof(cap->card) - 1);
+	snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s",
+		 MEM2MEM_NAME);
+	return 0;
+}
+
+static int enum_fmt(struct v4l2_fmtdesc *f, struct bcm2835_codec_ctx *ctx,
+		    bool capture)
+{
+	struct bcm2835_codec_fmt *fmt;
+	struct bcm2835_codec_fmt_list *fmts =
+					get_format_list(ctx->dev, capture);
+
+	if (f->index < fmts->num_entries) {
+		/* Format found */
+		fmt = &fmts->list[f->index];
+		f->pixelformat = fmt->fourcc;
+		f->flags = fmt->flags;
+		return 0;
+	}
+
+	/* Format not found */
+	return -EINVAL;
+}
+
+static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
+				   struct v4l2_fmtdesc *f)
+{
+	struct bcm2835_codec_ctx *ctx = file2ctx(file);
+
+	return enum_fmt(f, ctx, true);
+}
+
+static int vidioc_enum_fmt_vid_out(struct file *file, void *priv,
+				   struct v4l2_fmtdesc *f)
+{
+	struct bcm2835_codec_ctx *ctx = file2ctx(file);
+
+	return enum_fmt(f, ctx, false);
+}
+
+static int vidioc_g_fmt(struct bcm2835_codec_ctx *ctx, struct v4l2_format *f)
+{
+	struct vb2_queue *vq;
+	struct bcm2835_codec_q_data *q_data;
+
+	vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
+	if (!vq)
+		return -EINVAL;
+
+	q_data = get_q_data(ctx, f->type);
+
+	f->fmt.pix_mp.width			= q_data->crop_width;
+	f->fmt.pix_mp.height			= q_data->height;
+	f->fmt.pix_mp.pixelformat		= q_data->fmt->fourcc;
+	f->fmt.pix_mp.field			= V4L2_FIELD_NONE;
+	f->fmt.pix_mp.colorspace		= ctx->colorspace;
+	f->fmt.pix_mp.plane_fmt[0].sizeimage	= q_data->sizeimage;
+	f->fmt.pix_mp.plane_fmt[0].bytesperline	= q_data->bytesperline;
+	f->fmt.pix_mp.num_planes		= 1;
+	f->fmt.pix_mp.ycbcr_enc			= ctx->ycbcr_enc;
+	f->fmt.pix_mp.quantization		= ctx->quant;
+	f->fmt.pix_mp.xfer_func			= ctx->xfer_func;
+
+	memset(f->fmt.pix_mp.plane_fmt[0].reserved, 0,
+	       sizeof(f->fmt.pix_mp.plane_fmt[0].reserved));
+
+	return 0;
+}
+
+static int vidioc_g_fmt_vid_out(struct file *file, void *priv,
+				struct v4l2_format *f)
+{
+	return vidioc_g_fmt(file2ctx(file), f);
+}
+
+static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
+				struct v4l2_format *f)
+{
+	return vidioc_g_fmt(file2ctx(file), f);
+}
+
+static int vidioc_try_fmt(struct bcm2835_codec_ctx *ctx, struct v4l2_format *f,
+			  struct bcm2835_codec_fmt *fmt)
+{
+	unsigned int sizeimage;
+
+	/*
+	 * The V4L2 specification requires the driver to correct the format
+	 * struct if any of the dimensions is unsupported
+	 */
+	if (f->fmt.pix_mp.width > MAX_W)
+		f->fmt.pix_mp.width = MAX_W;
+	if (f->fmt.pix_mp.height > MAX_H)
+		f->fmt.pix_mp.height = MAX_H;
+
+	if (!(fmt->flags & V4L2_FMT_FLAG_COMPRESSED)) {
+		/* Only clip min w/h on capture. Treat 0x0 as unknown. */
+		if (f->fmt.pix_mp.width < MIN_W)
+			f->fmt.pix_mp.width = MIN_W;
+		if (f->fmt.pix_mp.height < MIN_H)
+			f->fmt.pix_mp.height = MIN_H;
+
+		/*
+		 * For decoders the buffer must have a vertical alignment of 16
+		 * lines.
+		 * The selection will reflect any cropping rectangle when only
+		 * some of the pixels are active.
+		 */
+		if (ctx->dev->role == DECODE)
+			f->fmt.pix_mp.height = ALIGN(f->fmt.pix_mp.height, 16);
+	}
+	f->fmt.pix_mp.num_planes = 1;
+	f->fmt.pix_mp.plane_fmt[0].bytesperline =
+		get_bytesperline(f->fmt.pix_mp.width, fmt);
+	sizeimage = get_sizeimage(f->fmt.pix_mp.plane_fmt[0].bytesperline,
+				  f->fmt.pix_mp.width, f->fmt.pix_mp.height,
+				  fmt);
+	/*
+	 * Drivers must set sizeimage for uncompressed formats
+	 * Compressed formats allow the client to request an alternate
+	 * size for the buffer.
+	 */
+	if (!(fmt->flags & V4L2_FMT_FLAG_COMPRESSED) ||
+	    f->fmt.pix_mp.plane_fmt[0].sizeimage < sizeimage)
+		f->fmt.pix_mp.plane_fmt[0].sizeimage = sizeimage;
+
+	memset(f->fmt.pix_mp.plane_fmt[0].reserved, 0,
+	       sizeof(f->fmt.pix_mp.plane_fmt[0].reserved));
+
+	f->fmt.pix_mp.field = V4L2_FIELD_NONE;
+
+	return 0;
+}
+
+static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
+				  struct v4l2_format *f)
+{
+	struct bcm2835_codec_fmt *fmt;
+	struct bcm2835_codec_ctx *ctx = file2ctx(file);
+
+	fmt = find_format(f, ctx->dev, true);
+	if (!fmt) {
+		f->fmt.pix_mp.pixelformat = get_default_format(ctx->dev,
+							       true)->fourcc;
+		fmt = find_format(f, ctx->dev, true);
+	}
+
+	return vidioc_try_fmt(ctx, f, fmt);
+}
+
+static int vidioc_try_fmt_vid_out(struct file *file, void *priv,
+				  struct v4l2_format *f)
+{
+	struct bcm2835_codec_fmt *fmt;
+	struct bcm2835_codec_ctx *ctx = file2ctx(file);
+
+	fmt = find_format(f, ctx->dev, false);
+	if (!fmt) {
+		f->fmt.pix_mp.pixelformat = get_default_format(ctx->dev,
+							       false)->fourcc;
+		fmt = find_format(f, ctx->dev, false);
+	}
+
+	if (!f->fmt.pix_mp.colorspace)
+		f->fmt.pix_mp.colorspace = ctx->colorspace;
+
+	return vidioc_try_fmt(ctx, f, fmt);
+}
+
+static int vidioc_s_fmt(struct bcm2835_codec_ctx *ctx, struct v4l2_format *f,
+			unsigned int requested_height)
+{
+	struct bcm2835_codec_q_data *q_data;
+	struct vb2_queue *vq;
+	struct vchiq_mmal_port *port;
+	bool update_capture_port = false;
+	int ret;
+
+	v4l2_dbg(1, debug, &ctx->dev->v4l2_dev,	"Setting format for type %d, wxh: %dx%d, fmt: %08x, size %u\n",
+		 f->type, f->fmt.pix_mp.width, f->fmt.pix_mp.height,
+		 f->fmt.pix_mp.pixelformat,
+		 f->fmt.pix_mp.plane_fmt[0].sizeimage);
+
+	vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
+	if (!vq)
+		return -EINVAL;
+
+	q_data = get_q_data(ctx, f->type);
+	if (!q_data)
+		return -EINVAL;
+
+	if (vb2_is_busy(vq)) {
+		v4l2_err(&ctx->dev->v4l2_dev, "%s queue busy\n", __func__);
+		return -EBUSY;
+	}
+
+	q_data->fmt = find_format(f, ctx->dev,
+				  f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
+	q_data->crop_width = f->fmt.pix_mp.width;
+	q_data->height = f->fmt.pix_mp.height;
+	if (!q_data->selection_set)
+		q_data->crop_height = requested_height;
+
+	/*
+	 * Copying the behaviour of vicodec which retains a single set of
+	 * colorspace parameters for both input and output.
+	 */
+	ctx->colorspace = f->fmt.pix_mp.colorspace;
+	ctx->xfer_func = f->fmt.pix_mp.xfer_func;
+	ctx->ycbcr_enc = f->fmt.pix_mp.ycbcr_enc;
+	ctx->quant = f->fmt.pix_mp.quantization;
+
+	/* All parameters should have been set correctly by try_fmt */
+	q_data->bytesperline = f->fmt.pix_mp.plane_fmt[0].bytesperline;
+	q_data->sizeimage = f->fmt.pix_mp.plane_fmt[0].sizeimage;
+
+	v4l2_dbg(1, debug, &ctx->dev->v4l2_dev,	"Calulated bpl as %u, size %u\n",
+		 q_data->bytesperline, q_data->sizeimage);
+
+	if (ctx->dev->role == DECODE &&
+	    q_data->fmt->flags & V4L2_FMT_FLAG_COMPRESSED &&
+	    q_data->crop_width && q_data->height) {
+		/*
+		 * On the decoder, if provided with a resolution on the input
+		 * side, then replicate that to the output side.
+		 * GStreamer appears not to support V4L2_EVENT_SOURCE_CHANGE,
+		 * nor set up a resolution on the output side, therefore
+		 * we can't decode anything at a resolution other than the
+		 * default one.
+		 */
+		struct bcm2835_codec_q_data *q_data_dst =
+						&ctx->q_data[V4L2_M2M_DST];
+
+		q_data_dst->crop_width = q_data->crop_width;
+		q_data_dst->crop_height = q_data->crop_height;
+		q_data_dst->height = ALIGN(q_data->crop_height, 16);
+
+		q_data_dst->bytesperline =
+			get_bytesperline(f->fmt.pix_mp.width, q_data_dst->fmt);
+		q_data_dst->sizeimage = get_sizeimage(q_data_dst->bytesperline,
+						      q_data_dst->crop_width,
+						      q_data_dst->height,
+						      q_data_dst->fmt);
+		update_capture_port = true;
+	}
+
+	/* If we have a component then setup the port as well */
+	port = get_port_data(ctx, vq->type);
+	if (!port)
+		return 0;
+
+	setup_mmal_port_format(ctx, q_data, port);
+	ret = vchiq_mmal_port_set_format(ctx->dev->instance, port);
+	if (ret) {
+		v4l2_err(&ctx->dev->v4l2_dev, "%s: Failed vchiq_mmal_port_set_format on port, ret %d\n",
+			 __func__, ret);
+		ret = -EINVAL;
+	}
+
+	if (q_data->sizeimage < port->minimum_buffer.size) {
+		v4l2_err(&ctx->dev->v4l2_dev, "%s: Current buffer size of %u < min buf size %u - driver mismatch to MMAL\n",
+			 __func__, q_data->sizeimage,
+			 port->minimum_buffer.size);
+	}
+
+	v4l2_dbg(1, debug, &ctx->dev->v4l2_dev,	"Set format for type %d, wxh: %dx%d, fmt: %08x, size %u\n",
+		 f->type, q_data->crop_width, q_data->height,
+		 q_data->fmt->fourcc, q_data->sizeimage);
+
+	if (update_capture_port) {
+		struct vchiq_mmal_port *port_dst = &ctx->component->output[0];
+		struct bcm2835_codec_q_data *q_data_dst =
+						&ctx->q_data[V4L2_M2M_DST];
+
+		setup_mmal_port_format(ctx, q_data_dst, port_dst);
+		ret = vchiq_mmal_port_set_format(ctx->dev->instance, port_dst);
+		if (ret) {
+			v4l2_err(&ctx->dev->v4l2_dev, "%s: Failed vchiq_mmal_port_set_format on output port, ret %d\n",
+				 __func__, ret);
+			ret = -EINVAL;
+		}
+	}
+	return ret;
+}
+
+static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
+				struct v4l2_format *f)
+{
+	unsigned int height = f->fmt.pix_mp.height;
+	int ret;
+
+	ret = vidioc_try_fmt_vid_cap(file, priv, f);
+	if (ret)
+		return ret;
+
+	return vidioc_s_fmt(file2ctx(file), f, height);
+}
+
+static int vidioc_s_fmt_vid_out(struct file *file, void *priv,
+				struct v4l2_format *f)
+{
+	unsigned int height = f->fmt.pix_mp.height;
+	int ret;
+
+	ret = vidioc_try_fmt_vid_out(file, priv, f);
+	if (ret)
+		return ret;
+
+	ret = vidioc_s_fmt(file2ctx(file), f, height);
+	return ret;
+}
+
+static int vidioc_g_selection(struct file *file, void *priv,
+			      struct v4l2_selection *s)
+{
+	struct bcm2835_codec_ctx *ctx = file2ctx(file);
+	struct bcm2835_codec_q_data *q_data;
+
+	/*
+	 * The selection API takes V4L2_BUF_TYPE_VIDEO_CAPTURE and
+	 * V4L2_BUF_TYPE_VIDEO_OUTPUT, even if the device implements the MPLANE
+	 * API. The V4L2 core will have converted the MPLANE variants to
+	 * non-MPLANE.
+	 * Open code this instead of using get_q_data in this case.
+	 */
+	switch (s->type) {
+	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+		/* CAPTURE on encoder is not valid. */
+		if (ctx->dev->role == ENCODE)
+			return -EINVAL;
+		q_data = &ctx->q_data[V4L2_M2M_DST];
+		break;
+	case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+		/* OUTPUT on deoder is not valid. */
+		if (ctx->dev->role == DECODE)
+			return -EINVAL;
+		q_data = &ctx->q_data[V4L2_M2M_SRC];
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	switch (ctx->dev->role) {
+	case DECODE:
+		switch (s->target) {
+		case V4L2_SEL_TGT_COMPOSE_DEFAULT:
+		case V4L2_SEL_TGT_COMPOSE:
+			s->r.left = 0;
+			s->r.top = 0;
+			s->r.width = q_data->crop_width;
+			s->r.height = q_data->crop_height;
+			break;
+		case V4L2_SEL_TGT_COMPOSE_BOUNDS:
+			s->r.left = 0;
+			s->r.top = 0;
+			s->r.width = q_data->crop_width;
+			s->r.height = q_data->crop_height;
+			break;
+		case V4L2_SEL_TGT_CROP_BOUNDS:
+		case V4L2_SEL_TGT_CROP_DEFAULT:
+			s->r.left = 0;
+			s->r.top = 0;
+			s->r.width = (q_data->bytesperline << 3) /
+						q_data->fmt->depth;
+			s->r.height = q_data->height;
+			break;
+		default:
+			return -EINVAL;
+		}
+		break;
+	case ENCODE:
+		switch (s->target) {
+		case V4L2_SEL_TGT_CROP_DEFAULT:
+		case V4L2_SEL_TGT_CROP_BOUNDS:
+			s->r.top = 0;
+			s->r.left = 0;
+			s->r.width = q_data->bytesperline;
+			s->r.height = q_data->height;
+			break;
+		case V4L2_SEL_TGT_CROP:
+			s->r.top = 0;
+			s->r.left = 0;
+			s->r.width = q_data->crop_width;
+			s->r.height = q_data->crop_height;
+			break;
+		default:
+			return -EINVAL;
+		}
+		break;
+	case ISP:
+		break;
+	}
+
+	return 0;
+}
+
+static int vidioc_s_selection(struct file *file, void *priv,
+			      struct v4l2_selection *s)
+{
+	struct bcm2835_codec_ctx *ctx = file2ctx(file);
+	struct bcm2835_codec_q_data *q_data = NULL;
+
+	/*
+	 * The selection API takes V4L2_BUF_TYPE_VIDEO_CAPTURE and
+	 * V4L2_BUF_TYPE_VIDEO_OUTPUT, even if the device implements the MPLANE
+	 * API. The V4L2 core will have converted the MPLANE variants to
+	 * non-MPLANE.
+	 *
+	 * Open code this instead of using get_q_data in this case.
+	 */
+	switch (s->type) {
+	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+		/* CAPTURE on encoder is not valid. */
+		if (ctx->dev->role == ENCODE)
+			return -EINVAL;
+		q_data = &ctx->q_data[V4L2_M2M_DST];
+		break;
+	case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+		/* OUTPUT on deoder is not valid. */
+		if (ctx->dev->role == DECODE)
+			return -EINVAL;
+		q_data = &ctx->q_data[V4L2_M2M_SRC];
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	v4l2_dbg(1, debug, &ctx->dev->v4l2_dev, "%s: ctx %p, type %d, q_data %p, target %d, rect x/y %d/%d, w/h %ux%u\n",
+		 __func__, ctx, s->type, q_data, s->target, s->r.left, s->r.top,
+		 s->r.width, s->r.height);
+
+	switch (ctx->dev->role) {
+	case DECODE:
+		switch (s->target) {
+		case V4L2_SEL_TGT_COMPOSE:
+			/* Accept cropped image */
+			s->r.left = 0;
+			s->r.top = 0;
+			s->r.width = min(s->r.width, q_data->crop_width);
+			s->r.height = min(s->r.height, q_data->height);
+			q_data->crop_width = s->r.width;
+			q_data->crop_height = s->r.height;
+			q_data->selection_set = true;
+			break;
+		default:
+			return -EINVAL;
+		}
+		break;
+	case ENCODE:
+		switch (s->target) {
+		case V4L2_SEL_TGT_CROP:
+			/* Only support crop from (0,0) */
+			s->r.top = 0;
+			s->r.left = 0;
+			s->r.width = min(s->r.width, q_data->crop_width);
+			s->r.height = min(s->r.height, q_data->crop_height);
+			q_data->crop_width = s->r.width;
+			q_data->crop_height = s->r.height;
+			q_data->selection_set = true;
+			break;
+		default:
+			return -EINVAL;
+		}
+		break;
+	case ISP:
+		break;
+	}
+
+	return 0;
+}
+
+static int vidioc_s_parm(struct file *file, void *priv,
+			 struct v4l2_streamparm *parm)
+{
+	struct bcm2835_codec_ctx *ctx = file2ctx(file);
+
+	if (parm->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+		return -EINVAL;
+
+	if (!parm->parm.output.timeperframe.denominator ||
+	    !parm->parm.output.timeperframe.numerator)
+		return -EINVAL;
+
+	ctx->framerate_num =
+			parm->parm.output.timeperframe.denominator;
+	ctx->framerate_denom =
+			parm->parm.output.timeperframe.numerator;
+
+	parm->parm.output.capability = V4L2_CAP_TIMEPERFRAME;
+
+	return 0;
+}
+
+static int vidioc_g_parm(struct file *file, void *priv,
+			 struct v4l2_streamparm *parm)
+{
+	struct bcm2835_codec_ctx *ctx = file2ctx(file);
+
+	if (parm->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+		return -EINVAL;
+
+	parm->parm.output.capability = V4L2_CAP_TIMEPERFRAME;
+	parm->parm.output.timeperframe.denominator =
+			ctx->framerate_num;
+	parm->parm.output.timeperframe.numerator =
+			ctx->framerate_denom;
+
+	return 0;
+}
+
+static int vidioc_g_pixelaspect(struct file *file, void *fh, int type,
+				struct v4l2_fract *f)
+{
+	struct bcm2835_codec_ctx *ctx = file2ctx(file);
+
+	/*
+	 * The selection API takes V4L2_BUF_TYPE_VIDEO_CAPTURE and
+	 * V4L2_BUF_TYPE_VIDEO_OUTPUT, even if the device implements the MPLANE
+	 * API. The V4L2 core will have converted the MPLANE variants to
+	 * non-MPLANE.
+	 * Open code this instead of using get_q_data in this case.
+	 */
+	if (ctx->dev->role != DECODE)
+		return -ENOIOCTLCMD;
+
+	if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		return -EINVAL;
+
+	*f = ctx->q_data[V4L2_M2M_DST].aspect_ratio;
+
+	return 0;
+}
+
+static int vidioc_subscribe_evt(struct v4l2_fh *fh,
+				const struct v4l2_event_subscription *sub)
+{
+	switch (sub->type) {
+	case V4L2_EVENT_EOS:
+		return v4l2_event_subscribe(fh, sub, 2, NULL);
+	case V4L2_EVENT_SOURCE_CHANGE:
+		return v4l2_src_change_event_subscribe(fh, sub);
+	default:
+		return v4l2_ctrl_subscribe_event(fh, sub);
+	}
+}
+
+static int bcm2835_codec_set_level_profile(struct bcm2835_codec_ctx *ctx,
+					   struct v4l2_ctrl *ctrl)
+{
+	struct mmal_parameter_video_profile param;
+	int param_size = sizeof(param);
+	int ret;
+
+	/*
+	 * Level and Profile are set via the same MMAL parameter.
+	 * Retrieve the current settings and amend the one that has changed.
+	 */
+	ret = vchiq_mmal_port_parameter_get(ctx->dev->instance,
+					    &ctx->component->output[0],
+					    MMAL_PARAMETER_PROFILE,
+					    &param,
+					    &param_size);
+	if (ret)
+		return ret;
+
+	switch (ctrl->id) {
+	case V4L2_CID_MPEG_VIDEO_H264_PROFILE:
+		switch (ctrl->val) {
+		case V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE:
+			param.profile = MMAL_VIDEO_PROFILE_H264_BASELINE;
+			break;
+		case V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE:
+			param.profile =
+				MMAL_VIDEO_PROFILE_H264_CONSTRAINED_BASELINE;
+			break;
+		case V4L2_MPEG_VIDEO_H264_PROFILE_MAIN:
+			param.profile = MMAL_VIDEO_PROFILE_H264_MAIN;
+			break;
+		case V4L2_MPEG_VIDEO_H264_PROFILE_HIGH:
+			param.profile = MMAL_VIDEO_PROFILE_H264_HIGH;
+			break;
+		default:
+			/* Should never get here */
+			break;
+		}
+		break;
+
+	case V4L2_CID_MPEG_VIDEO_H264_LEVEL:
+		switch (ctrl->val) {
+		case V4L2_MPEG_VIDEO_H264_LEVEL_1_0:
+			param.level = MMAL_VIDEO_LEVEL_H264_1;
+			break;
+		case V4L2_MPEG_VIDEO_H264_LEVEL_1B:
+			param.level = MMAL_VIDEO_LEVEL_H264_1b;
+			break;
+		case V4L2_MPEG_VIDEO_H264_LEVEL_1_1:
+			param.level = MMAL_VIDEO_LEVEL_H264_11;
+			break;
+		case V4L2_MPEG_VIDEO_H264_LEVEL_1_2:
+			param.level = MMAL_VIDEO_LEVEL_H264_12;
+			break;
+		case V4L2_MPEG_VIDEO_H264_LEVEL_1_3:
+			param.level = MMAL_VIDEO_LEVEL_H264_13;
+			break;
+		case V4L2_MPEG_VIDEO_H264_LEVEL_2_0:
+			param.level = MMAL_VIDEO_LEVEL_H264_2;
+			break;
+		case V4L2_MPEG_VIDEO_H264_LEVEL_2_1:
+			param.level = MMAL_VIDEO_LEVEL_H264_21;
+			break;
+		case V4L2_MPEG_VIDEO_H264_LEVEL_2_2:
+			param.level = MMAL_VIDEO_LEVEL_H264_22;
+			break;
+		case V4L2_MPEG_VIDEO_H264_LEVEL_3_0:
+			param.level = MMAL_VIDEO_LEVEL_H264_3;
+			break;
+		case V4L2_MPEG_VIDEO_H264_LEVEL_3_1:
+			param.level = MMAL_VIDEO_LEVEL_H264_31;
+			break;
+		case V4L2_MPEG_VIDEO_H264_LEVEL_3_2:
+			param.level = MMAL_VIDEO_LEVEL_H264_32;
+			break;
+		case V4L2_MPEG_VIDEO_H264_LEVEL_4_0:
+			param.level = MMAL_VIDEO_LEVEL_H264_4;
+			break;
+		/*
+		 * Note that the hardware spec is level 4.0. Levels above that
+		 * are there for correctly encoding the headers and may not
+		 * be able to keep up with real-time.
+		 */
+		case V4L2_MPEG_VIDEO_H264_LEVEL_4_1:
+			param.level = MMAL_VIDEO_LEVEL_H264_41;
+			break;
+		case V4L2_MPEG_VIDEO_H264_LEVEL_4_2:
+			param.level = MMAL_VIDEO_LEVEL_H264_42;
+			break;
+		default:
+			/* Should never get here */
+			break;
+		}
+	}
+	ret = vchiq_mmal_port_parameter_set(ctx->dev->instance,
+					    &ctx->component->output[0],
+					    MMAL_PARAMETER_PROFILE,
+					    &param,
+					    param_size);
+
+	return ret;
+}
+
+static int bcm2835_codec_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct bcm2835_codec_ctx *ctx =
+		container_of(ctrl->handler, struct bcm2835_codec_ctx, hdl);
+	int ret = 0;
+
+	switch (ctrl->id) {
+	case V4L2_CID_MPEG_VIDEO_BITRATE:
+		ctx->bitrate = ctrl->val;
+		if (!ctx->component)
+			break;
+
+		ret = vchiq_mmal_port_parameter_set(ctx->dev->instance,
+						    &ctx->component->output[0],
+						    MMAL_PARAMETER_VIDEO_BIT_RATE,
+						    &ctrl->val,
+						    sizeof(ctrl->val));
+		break;
+
+	case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: {
+		u32 bitrate_mode;
+
+		if (!ctx->component)
+			break;
+
+		switch (ctrl->val) {
+		default:
+		case V4L2_MPEG_VIDEO_BITRATE_MODE_VBR:
+			bitrate_mode = MMAL_VIDEO_RATECONTROL_VARIABLE;
+			break;
+		case V4L2_MPEG_VIDEO_BITRATE_MODE_CBR:
+			bitrate_mode = MMAL_VIDEO_RATECONTROL_CONSTANT;
+			break;
+		}
+
+		ret = vchiq_mmal_port_parameter_set(ctx->dev->instance,
+						    &ctx->component->output[0],
+						    MMAL_PARAMETER_RATECONTROL,
+						    &bitrate_mode,
+						    sizeof(bitrate_mode));
+		break;
+	}
+	case V4L2_CID_MPEG_VIDEO_REPEAT_SEQ_HEADER:
+		if (!ctx->component)
+			break;
+
+		ret = vchiq_mmal_port_parameter_set(ctx->dev->instance,
+						    &ctx->component->output[0],
+						    MMAL_PARAMETER_VIDEO_ENCODE_INLINE_HEADER,
+						    &ctrl->val,
+						    sizeof(ctrl->val));
+		break;
+
+	case V4L2_CID_MPEG_VIDEO_HEADER_MODE:
+		if (!ctx->component)
+			break;
+
+		ret = vchiq_mmal_port_parameter_set(ctx->dev->instance,
+						    &ctx->component->output[0],
+						    MMAL_PARAMETER_VIDEO_ENCODE_HEADERS_WITH_FRAME,
+						    &ctrl->val,
+						    sizeof(ctrl->val));
+		break;
+
+	case V4L2_CID_MPEG_VIDEO_H264_I_PERIOD:
+		if (!ctx->component)
+			break;
+
+		ret = vchiq_mmal_port_parameter_set(ctx->dev->instance,
+						    &ctx->component->output[0],
+						    MMAL_PARAMETER_INTRAPERIOD,
+						    &ctrl->val,
+						    sizeof(ctrl->val));
+		break;
+
+	case V4L2_CID_MPEG_VIDEO_H264_PROFILE:
+	case V4L2_CID_MPEG_VIDEO_H264_LEVEL:
+		if (!ctx->component)
+			break;
+
+		ret = bcm2835_codec_set_level_profile(ctx, ctrl);
+		break;
+
+	case V4L2_CID_MPEG_VIDEO_FORCE_KEY_FRAME: {
+		u32 mmal_bool = 1;
+
+		if (!ctx->component)
+			break;
+
+		ret = vchiq_mmal_port_parameter_set(ctx->dev->instance,
+						    &ctx->component->output[0],
+						    MMAL_PARAMETER_VIDEO_REQUEST_I_FRAME,
+						    &mmal_bool,
+						    sizeof(mmal_bool));
+		break;
+	}
+
+	default:
+		v4l2_err(&ctx->dev->v4l2_dev, "Invalid control\n");
+		return -EINVAL;
+	}
+
+	if (ret)
+		v4l2_err(&ctx->dev->v4l2_dev, "Failed setting ctrl %08x, ret %d\n",
+			 ctrl->id, ret);
+	return ret ? -EINVAL : 0;
+}
+
+static const struct v4l2_ctrl_ops bcm2835_codec_ctrl_ops = {
+	.s_ctrl = bcm2835_codec_s_ctrl,
+};
+
+static int vidioc_try_decoder_cmd(struct file *file, void *priv,
+				  struct v4l2_decoder_cmd *cmd)
+{
+	struct bcm2835_codec_ctx *ctx = file2ctx(file);
+
+	if (ctx->dev->role != DECODE)
+		return -EINVAL;
+
+	switch (cmd->cmd) {
+	case V4L2_DEC_CMD_STOP:
+		if (cmd->flags & V4L2_DEC_CMD_STOP_TO_BLACK) {
+			v4l2_err(&ctx->dev->v4l2_dev, "%s: DEC cmd->flags=%u stop to black not supported",
+				 __func__, cmd->flags);
+			return -EINVAL;
+		}
+		break;
+	case V4L2_DEC_CMD_START:
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int vidioc_decoder_cmd(struct file *file, void *priv,
+			      struct v4l2_decoder_cmd *cmd)
+{
+	struct bcm2835_codec_ctx *ctx = file2ctx(file);
+	struct bcm2835_codec_q_data *q_data = &ctx->q_data[V4L2_M2M_SRC];
+	struct vb2_queue *dst_vq;
+	int ret;
+
+	v4l2_dbg(2, debug, &ctx->dev->v4l2_dev, "%s, cmd %u", __func__,
+		 cmd->cmd);
+	ret = vidioc_try_decoder_cmd(file, priv, cmd);
+	if (ret)
+		return ret;
+
+	switch (cmd->cmd) {
+	case V4L2_DEC_CMD_STOP:
+		if (q_data->eos_buffer_in_use)
+			v4l2_err(&ctx->dev->v4l2_dev, "EOS buffers already in use\n");
+		q_data->eos_buffer_in_use = true;
+
+		q_data->eos_buffer.mmal.buffer_size = 0;
+		q_data->eos_buffer.mmal.length = 0;
+		q_data->eos_buffer.mmal.mmal_flags =
+						MMAL_BUFFER_HEADER_FLAG_EOS;
+		q_data->eos_buffer.mmal.pts = 0;
+		q_data->eos_buffer.mmal.dts = 0;
+
+		if (!ctx->component)
+			break;
+
+		ret = vchiq_mmal_submit_buffer(ctx->dev->instance,
+					       &ctx->component->input[0],
+					       &q_data->eos_buffer.mmal);
+		if (ret)
+			v4l2_err(&ctx->dev->v4l2_dev,
+				 "%s: EOS buffer submit failed %d\n",
+				 __func__, ret);
+
+		break;
+
+	case V4L2_DEC_CMD_START:
+		dst_vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx,
+					 V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
+		vb2_clear_last_buffer_dequeued(dst_vq);
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int vidioc_try_encoder_cmd(struct file *file, void *priv,
+				  struct v4l2_encoder_cmd *cmd)
+{
+	struct bcm2835_codec_ctx *ctx = file2ctx(file);
+
+	if (ctx->dev->role != ENCODE)
+		return -EINVAL;
+
+	switch (cmd->cmd) {
+	case V4L2_ENC_CMD_STOP:
+		break;
+
+	case V4L2_ENC_CMD_START:
+		/* Do we need to do anything here? */
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int vidioc_encoder_cmd(struct file *file, void *priv,
+			      struct v4l2_encoder_cmd *cmd)
+{
+	struct bcm2835_codec_ctx *ctx = file2ctx(file);
+	struct bcm2835_codec_q_data *q_data = &ctx->q_data[V4L2_M2M_SRC];
+	int ret;
+
+	v4l2_dbg(2, debug, &ctx->dev->v4l2_dev, "%s, cmd %u", __func__,
+		 cmd->cmd);
+	ret = vidioc_try_encoder_cmd(file, priv, cmd);
+	if (ret)
+		return ret;
+
+	switch (cmd->cmd) {
+	case V4L2_ENC_CMD_STOP:
+		if (q_data->eos_buffer_in_use)
+			v4l2_err(&ctx->dev->v4l2_dev, "EOS buffers already in use\n");
+		q_data->eos_buffer_in_use = true;
+
+		q_data->eos_buffer.mmal.buffer_size = 0;
+		q_data->eos_buffer.mmal.length = 0;
+		q_data->eos_buffer.mmal.mmal_flags =
+						MMAL_BUFFER_HEADER_FLAG_EOS;
+		q_data->eos_buffer.mmal.pts = 0;
+		q_data->eos_buffer.mmal.dts = 0;
+
+		if (!ctx->component)
+			break;
+
+		ret = vchiq_mmal_submit_buffer(ctx->dev->instance,
+					       &ctx->component->input[0],
+					       &q_data->eos_buffer.mmal);
+		if (ret)
+			v4l2_err(&ctx->dev->v4l2_dev,
+				 "%s: EOS buffer submit failed %d\n",
+				 __func__, ret);
+
+		break;
+	case V4L2_ENC_CMD_START:
+		/* Do we need to do anything here? */
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int vidioc_enum_framesizes(struct file *file, void *fh,
+				  struct v4l2_frmsizeenum *fsize)
+{
+	struct bcm2835_codec_fmt *fmt;
+
+	fmt = find_format_pix_fmt(fsize->pixel_format, file2ctx(file)->dev,
+				  true);
+	if (!fmt)
+		fmt = find_format_pix_fmt(fsize->pixel_format,
+					  file2ctx(file)->dev,
+					  false);
+
+	if (!fmt)
+		return -EINVAL;
+
+	if (fsize->index)
+		return -EINVAL;
+
+	fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE;
+
+	fsize->stepwise.min_width = MIN_W;
+	fsize->stepwise.max_width = MAX_W;
+	fsize->stepwise.step_width = 1;
+	fsize->stepwise.min_height = MIN_H;
+	fsize->stepwise.max_height = MAX_H;
+	fsize->stepwise.step_height = 1;
+
+	return 0;
+}
+
+static const struct v4l2_ioctl_ops bcm2835_codec_ioctl_ops = {
+	.vidioc_querycap	= vidioc_querycap,
+
+	.vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
+	.vidioc_g_fmt_vid_cap_mplane	= vidioc_g_fmt_vid_cap,
+	.vidioc_try_fmt_vid_cap_mplane	= vidioc_try_fmt_vid_cap,
+	.vidioc_s_fmt_vid_cap_mplane	= vidioc_s_fmt_vid_cap,
+
+	.vidioc_enum_fmt_vid_out = vidioc_enum_fmt_vid_out,
+	.vidioc_g_fmt_vid_out_mplane	= vidioc_g_fmt_vid_out,
+	.vidioc_try_fmt_vid_out_mplane	= vidioc_try_fmt_vid_out,
+	.vidioc_s_fmt_vid_out_mplane	= vidioc_s_fmt_vid_out,
+
+	.vidioc_reqbufs		= v4l2_m2m_ioctl_reqbufs,
+	.vidioc_querybuf	= v4l2_m2m_ioctl_querybuf,
+	.vidioc_qbuf		= v4l2_m2m_ioctl_qbuf,
+	.vidioc_dqbuf		= v4l2_m2m_ioctl_dqbuf,
+	.vidioc_prepare_buf	= v4l2_m2m_ioctl_prepare_buf,
+	.vidioc_create_bufs	= v4l2_m2m_ioctl_create_bufs,
+	.vidioc_expbuf		= v4l2_m2m_ioctl_expbuf,
+
+	.vidioc_streamon	= v4l2_m2m_ioctl_streamon,
+	.vidioc_streamoff	= v4l2_m2m_ioctl_streamoff,
+
+	.vidioc_g_selection	= vidioc_g_selection,
+	.vidioc_s_selection	= vidioc_s_selection,
+
+	.vidioc_g_parm		= vidioc_g_parm,
+	.vidioc_s_parm		= vidioc_s_parm,
+
+	.vidioc_g_pixelaspect	= vidioc_g_pixelaspect,
+
+	.vidioc_subscribe_event = vidioc_subscribe_evt,
+	.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+
+	.vidioc_decoder_cmd = vidioc_decoder_cmd,
+	.vidioc_try_decoder_cmd = vidioc_try_decoder_cmd,
+	.vidioc_encoder_cmd = vidioc_encoder_cmd,
+	.vidioc_try_encoder_cmd = vidioc_try_encoder_cmd,
+	.vidioc_enum_framesizes = vidioc_enum_framesizes,
+};
+
+static int bcm2835_codec_set_ctrls(struct bcm2835_codec_ctx *ctx)
+{
+	/*
+	 * Query the control handler for the value of the various controls and
+	 * set them.
+	 */
+	const u32 control_ids[] = {
+		V4L2_CID_MPEG_VIDEO_BITRATE_MODE,
+		V4L2_CID_MPEG_VIDEO_REPEAT_SEQ_HEADER,
+		V4L2_CID_MPEG_VIDEO_HEADER_MODE,
+		V4L2_CID_MPEG_VIDEO_H264_I_PERIOD,
+		V4L2_CID_MPEG_VIDEO_H264_LEVEL,
+		V4L2_CID_MPEG_VIDEO_H264_PROFILE,
+	};
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(control_ids); i++) {
+		struct v4l2_ctrl *ctrl;
+
+		ctrl = v4l2_ctrl_find(&ctx->hdl, control_ids[i]);
+		if (ctrl)
+			bcm2835_codec_s_ctrl(ctrl);
+	}
+
+	return 0;
+}
+
+static int bcm2835_codec_create_component(struct bcm2835_codec_ctx *ctx)
+{
+	struct bcm2835_codec_dev *dev = ctx->dev;
+	unsigned int enable = 1;
+	int ret;
+
+	ret = vchiq_mmal_component_init(dev->instance, components[dev->role],
+					&ctx->component);
+	if (ret < 0) {
+		v4l2_err(&dev->v4l2_dev, "%s: failed to create component %s\n",
+			 __func__, components[dev->role]);
+		return -ENOMEM;
+	}
+
+	vchiq_mmal_port_parameter_set(dev->instance, &ctx->component->input[0],
+				      MMAL_PARAMETER_ZERO_COPY, &enable,
+				      sizeof(enable));
+	vchiq_mmal_port_parameter_set(dev->instance, &ctx->component->output[0],
+				      MMAL_PARAMETER_ZERO_COPY, &enable,
+				      sizeof(enable));
+
+	if (dev->role == DECODE) {
+		/*
+		 * Disable firmware option that ensures decoded timestamps
+		 * always increase.
+		 */
+		enable = 0;
+		vchiq_mmal_port_parameter_set(dev->instance,
+					      &ctx->component->output[0],
+					      MMAL_PARAMETER_VIDEO_VALIDATE_TIMESTAMPS,
+					      &enable,
+					      sizeof(enable));
+	}
+
+	setup_mmal_port_format(ctx, &ctx->q_data[V4L2_M2M_SRC],
+			       &ctx->component->input[0]);
+
+	setup_mmal_port_format(ctx, &ctx->q_data[V4L2_M2M_DST],
+			       &ctx->component->output[0]);
+
+	ret = vchiq_mmal_port_set_format(dev->instance,
+					 &ctx->component->input[0]);
+	if (ret < 0) {
+		v4l2_dbg(1, debug, &dev->v4l2_dev,
+			 "%s: vchiq_mmal_port_set_format ip port failed\n",
+			 __func__);
+		goto destroy_component;
+	}
+
+	ret = vchiq_mmal_port_set_format(dev->instance,
+					 &ctx->component->output[0]);
+	if (ret < 0) {
+		v4l2_dbg(1, debug, &dev->v4l2_dev,
+			 "%s: vchiq_mmal_port_set_format op port failed\n",
+			 __func__);
+		goto destroy_component;
+	}
+
+	if (dev->role == ENCODE) {
+		u32 param = 1;
+
+		if (ctx->q_data[V4L2_M2M_SRC].sizeimage <
+			ctx->component->output[0].minimum_buffer.size)
+			v4l2_err(&dev->v4l2_dev, "buffer size mismatch sizeimage %u < min size %u\n",
+				 ctx->q_data[V4L2_M2M_SRC].sizeimage,
+				 ctx->component->output[0].minimum_buffer.size);
+
+		/* Now we have a component we can set all the ctrls */
+		bcm2835_codec_set_ctrls(ctx);
+
+		/* Enable SPS Timing header so framerate information is encoded
+		 * in the H264 header.
+		 */
+		vchiq_mmal_port_parameter_set(ctx->dev->instance,
+					      &ctx->component->output[0],
+					      MMAL_PARAMETER_VIDEO_ENCODE_SPS_TIMING,
+					      &param, sizeof(param));
+
+		/* Enable inserting headers into the first frame */
+		vchiq_mmal_port_parameter_set(ctx->dev->instance,
+					      &ctx->component->control,
+					      MMAL_PARAMETER_VIDEO_ENCODE_HEADERS_WITH_FRAME,
+					      &param, sizeof(param));
+		/*
+		 * Avoid fragmenting the buffers over multiple frames (unless
+		 * the frame is bigger than the whole buffer)
+		 */
+		vchiq_mmal_port_parameter_set(ctx->dev->instance,
+					      &ctx->component->control,
+					      MMAL_PARAMETER_MINIMISE_FRAGMENTATION,
+					      &param, sizeof(param));
+	} else {
+		if (ctx->q_data[V4L2_M2M_DST].sizeimage <
+			ctx->component->output[0].minimum_buffer.size)
+			v4l2_err(&dev->v4l2_dev, "buffer size mismatch sizeimage %u < min size %u\n",
+				 ctx->q_data[V4L2_M2M_DST].sizeimage,
+				 ctx->component->output[0].minimum_buffer.size);
+	}
+	v4l2_dbg(2, debug, &dev->v4l2_dev, "%s: component created as %s\n",
+		 __func__, components[dev->role]);
+
+	return 0;
+
+destroy_component:
+	vchiq_mmal_component_finalise(ctx->dev->instance, ctx->component);
+	ctx->component = NULL;
+
+	return ret;
+}
+
+/*
+ * Queue operations
+ */
+
+static int bcm2835_codec_queue_setup(struct vb2_queue *vq,
+				     unsigned int *nbuffers,
+				     unsigned int *nplanes,
+				     unsigned int sizes[],
+				     struct device *alloc_devs[])
+{
+	struct bcm2835_codec_ctx *ctx = vb2_get_drv_priv(vq);
+	struct bcm2835_codec_q_data *q_data;
+	struct vchiq_mmal_port *port;
+	unsigned int size;
+
+	q_data = get_q_data(ctx, vq->type);
+	if (!q_data)
+		return -EINVAL;
+
+	if (!ctx->component)
+		if (bcm2835_codec_create_component(ctx))
+			return -EINVAL;
+
+	port = get_port_data(ctx, vq->type);
+
+	size = q_data->sizeimage;
+
+	if (*nplanes)
+		return sizes[0] < size ? -EINVAL : 0;
+
+	*nplanes = 1;
+
+	sizes[0] = size;
+	port->current_buffer.size = size;
+
+	if (*nbuffers < port->minimum_buffer.num)
+		*nbuffers = port->minimum_buffer.num;
+	/* Add one buffer to take an EOS */
+	port->current_buffer.num = *nbuffers + 1;
+
+	return 0;
+}
+
+static int bcm2835_codec_mmal_buf_cleanup(struct mmal_buffer *mmal_buf)
+{
+	mmal_vchi_buffer_cleanup(mmal_buf);
+
+	if (mmal_buf->dma_buf) {
+		dma_buf_put(mmal_buf->dma_buf);
+		mmal_buf->dma_buf = NULL;
+	}
+
+	return 0;
+}
+
+static int bcm2835_codec_buf_init(struct vb2_buffer *vb)
+{
+	struct bcm2835_codec_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+	struct vb2_v4l2_buffer *vb2 = to_vb2_v4l2_buffer(vb);
+	struct v4l2_m2m_buffer *m2m = container_of(vb2, struct v4l2_m2m_buffer,
+						   vb);
+	struct m2m_mmal_buffer *buf = container_of(m2m, struct m2m_mmal_buffer,
+						   m2m);
+
+	v4l2_dbg(2, debug, &ctx->dev->v4l2_dev, "%s: ctx:%p, vb %p\n",
+		 __func__, ctx, vb);
+	buf->mmal.buffer = vb2_plane_vaddr(&buf->m2m.vb.vb2_buf, 0);
+	buf->mmal.buffer_size = vb2_plane_size(&buf->m2m.vb.vb2_buf, 0);
+
+	mmal_vchi_buffer_init(ctx->dev->instance, &buf->mmal);
+
+	return 0;
+}
+
+static int bcm2835_codec_buf_prepare(struct vb2_buffer *vb)
+{
+	struct bcm2835_codec_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+	struct bcm2835_codec_q_data *q_data;
+	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+	struct v4l2_m2m_buffer *m2m = container_of(vbuf, struct v4l2_m2m_buffer,
+						   vb);
+	struct m2m_mmal_buffer *buf = container_of(m2m, struct m2m_mmal_buffer,
+						   m2m);
+	struct dma_buf *dma_buf;
+	int ret;
+
+	v4l2_dbg(4, debug, &ctx->dev->v4l2_dev, "%s: type: %d ptr %p\n",
+		 __func__, vb->vb2_queue->type, vb);
+
+	q_data = get_q_data(ctx, vb->vb2_queue->type);
+	if (V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type)) {
+		if (vbuf->field == V4L2_FIELD_ANY)
+			vbuf->field = V4L2_FIELD_NONE;
+		if (vbuf->field != V4L2_FIELD_NONE) {
+			v4l2_err(&ctx->dev->v4l2_dev, "%s field isn't supported\n",
+				 __func__);
+			return -EINVAL;
+		}
+	}
+
+	if (vb2_plane_size(vb, 0) < q_data->sizeimage) {
+		v4l2_err(&ctx->dev->v4l2_dev, "%s data will not fit into plane (%lu < %lu)\n",
+			 __func__, vb2_plane_size(vb, 0),
+			 (long)q_data->sizeimage);
+		return -EINVAL;
+	}
+
+	if (!V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type))
+		vb2_set_plane_payload(vb, 0, q_data->sizeimage);
+
+	switch (vb->memory) {
+	case VB2_MEMORY_DMABUF:
+		dma_buf = dma_buf_get(vb->planes[0].m.fd);
+
+		if (dma_buf != buf->mmal.dma_buf) {
+			/* dmabuf either hasn't already been mapped, or it has
+			 * changed.
+			 */
+			if (buf->mmal.dma_buf) {
+				v4l2_err(&ctx->dev->v4l2_dev,
+					 "%s Buffer changed - why did the core not call cleanup?\n",
+					 __func__);
+				bcm2835_codec_mmal_buf_cleanup(&buf->mmal);
+			}
+
+			buf->mmal.dma_buf = dma_buf;
+		} else {
+			/* We already have a reference count on the dmabuf, so
+			 * release the one we acquired above.
+			 */
+			dma_buf_put(dma_buf);
+		}
+		ret = 0;
+		break;
+	case VB2_MEMORY_MMAP:
+		/*
+		 * We want to do this at init, but vb2_core_expbuf checks that
+		 * the index < q->num_buffers, and q->num_buffers only gets
+		 * updated once all the buffers are allocated.
+		 */
+		if (!buf->mmal.dma_buf) {
+			ret = vb2_core_expbuf_dmabuf(vb->vb2_queue,
+						     vb->vb2_queue->type,
+						     vb->index, 0,
+						     O_CLOEXEC,
+						     &buf->mmal.dma_buf);
+			if (ret)
+				v4l2_err(&ctx->dev->v4l2_dev,
+					 "%s: Failed to expbuf idx %d, ret %d\n",
+					 __func__, vb->index, ret);
+		} else {
+			ret = 0;
+		}
+		break;
+	default:
+		ret = -EINVAL;
+		break;
+	}
+
+	return ret;
+}
+
+static void bcm2835_codec_buf_queue(struct vb2_buffer *vb)
+{
+	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+	struct bcm2835_codec_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+
+	v4l2_dbg(4, debug, &ctx->dev->v4l2_dev, "%s: type: %d ptr %p vbuf->flags %u, seq %u, bytesused %u\n",
+		 __func__, vb->vb2_queue->type, vb, vbuf->flags, vbuf->sequence,
+		 vb->planes[0].bytesused);
+	v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf);
+}
+
+static void bcm2835_codec_buffer_cleanup(struct vb2_buffer *vb)
+{
+	struct bcm2835_codec_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+	struct vb2_v4l2_buffer *vb2 = to_vb2_v4l2_buffer(vb);
+	struct v4l2_m2m_buffer *m2m = container_of(vb2, struct v4l2_m2m_buffer,
+						   vb);
+	struct m2m_mmal_buffer *buf = container_of(m2m, struct m2m_mmal_buffer,
+						   m2m);
+
+	v4l2_dbg(2, debug, &ctx->dev->v4l2_dev, "%s: ctx:%p, vb %p\n",
+		 __func__, ctx, vb);
+
+	bcm2835_codec_mmal_buf_cleanup(&buf->mmal);
+}
+
+static int bcm2835_codec_start_streaming(struct vb2_queue *q,
+					 unsigned int count)
+{
+	struct bcm2835_codec_ctx *ctx = vb2_get_drv_priv(q);
+	struct bcm2835_codec_dev *dev = ctx->dev;
+	struct bcm2835_codec_q_data *q_data = get_q_data(ctx, q->type);
+	struct vchiq_mmal_port *port = get_port_data(ctx, q->type);
+	int ret;
+
+	v4l2_dbg(1, debug, &ctx->dev->v4l2_dev, "%s: type: %d count %d\n",
+		 __func__, q->type, count);
+	q_data->sequence = 0;
+
+	if (!ctx->component_enabled) {
+		ret = vchiq_mmal_component_enable(dev->instance,
+						  ctx->component);
+		if (ret)
+			v4l2_err(&ctx->dev->v4l2_dev, "%s: Failed enabling component, ret %d\n",
+				 __func__, ret);
+		ctx->component_enabled = true;
+	}
+
+	if (count < port->minimum_buffer.num)
+		count = port->minimum_buffer.num;
+
+	if (port->current_buffer.num < count + 1) {
+		v4l2_dbg(2, debug, &ctx->dev->v4l2_dev, "%s: ctx:%p, buffer count changed %u to %u\n",
+			 __func__, ctx, port->current_buffer.num, count + 1);
+
+		port->current_buffer.num = count + 1;
+		ret = vchiq_mmal_port_set_format(dev->instance, port);
+		if (ret)
+			v4l2_err(&ctx->dev->v4l2_dev, "%s: Error updating buffer count, ret %d\n",
+				 __func__, ret);
+	}
+
+	if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+		/*
+		 * Create the EOS buffer.
+		 * We only need the MMAL part, and want to NOT attach a memory
+		 * buffer to it as it should only take flags.
+		 */
+		memset(&q_data->eos_buffer, 0, sizeof(q_data->eos_buffer));
+		mmal_vchi_buffer_init(dev->instance,
+				      &q_data->eos_buffer.mmal);
+		q_data->eos_buffer_in_use = false;
+
+		port->cb_ctx = ctx;
+		ret = vchiq_mmal_port_enable(dev->instance,
+					     port,
+					     ip_buffer_cb);
+		if (ret)
+			v4l2_err(&ctx->dev->v4l2_dev, "%s: Failed enabling i/p port, ret %d\n",
+				 __func__, ret);
+	} else {
+		port->cb_ctx = ctx;
+		ret = vchiq_mmal_port_enable(dev->instance,
+					     port,
+					     op_buffer_cb);
+		if (ret)
+			v4l2_err(&ctx->dev->v4l2_dev, "%s: Failed enabling o/p port, ret %d\n",
+				 __func__, ret);
+	}
+	return ret;
+}
+
+static void bcm2835_codec_stop_streaming(struct vb2_queue *q)
+{
+	struct bcm2835_codec_ctx *ctx = vb2_get_drv_priv(q);
+	struct bcm2835_codec_dev *dev = ctx->dev;
+	struct bcm2835_codec_q_data *q_data = get_q_data(ctx, q->type);
+	struct vchiq_mmal_port *port = get_port_data(ctx, q->type);
+	struct vb2_v4l2_buffer *vbuf;
+	int ret;
+
+	v4l2_dbg(1, debug, &ctx->dev->v4l2_dev, "%s: type: %d - return buffers\n",
+		 __func__, q->type);
+
+	init_completion(&ctx->frame_cmplt);
+
+	/* Clear out all buffers held by m2m framework */
+	for (;;) {
+		if (V4L2_TYPE_IS_OUTPUT(q->type))
+			vbuf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
+		else
+			vbuf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
+		if (!vbuf)
+			break;
+		v4l2_dbg(1, debug, &ctx->dev->v4l2_dev, "%s: return buffer %p\n",
+			 __func__, vbuf);
+
+		v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_ERROR);
+	}
+
+	/* Disable MMAL port - this will flush buffers back */
+	ret = vchiq_mmal_port_disable(dev->instance, port);
+	if (ret)
+		v4l2_err(&ctx->dev->v4l2_dev, "%s: Failed disabling %s port, ret %d\n",
+			 __func__, V4L2_TYPE_IS_OUTPUT(q->type) ? "i/p" : "o/p",
+			 ret);
+
+	while (atomic_read(&port->buffers_with_vpu)) {
+		v4l2_dbg(1, debug, &ctx->dev->v4l2_dev, "%s: Waiting for buffers to be returned - %d outstanding\n",
+			 __func__, atomic_read(&port->buffers_with_vpu));
+		ret = wait_for_completion_timeout(&ctx->frame_cmplt,
+						  COMPLETE_TIMEOUT);
+		if (ret <= 0) {
+			v4l2_err(&ctx->dev->v4l2_dev, "%s: Timeout waiting for buffers to be returned - %d outstanding\n",
+				 __func__,
+				 atomic_read(&port->buffers_with_vpu));
+			break;
+		}
+	}
+
+	/* If both ports disabled, then disable the component */
+	if (ctx->component_enabled &&
+	    !ctx->component->input[0].enabled &&
+	    !ctx->component->output[0].enabled) {
+		ret = vchiq_mmal_component_disable(dev->instance,
+						   ctx->component);
+		if (ret)
+			v4l2_err(&ctx->dev->v4l2_dev, "%s: Failed enabling component, ret %d\n",
+				 __func__, ret);
+		ctx->component_enabled = false;
+	}
+
+	if (V4L2_TYPE_IS_OUTPUT(q->type))
+		mmal_vchi_buffer_cleanup(&q_data->eos_buffer.mmal);
+
+	v4l2_dbg(1, debug, &ctx->dev->v4l2_dev, "%s: done\n", __func__);
+}
+
+static const struct vb2_ops bcm2835_codec_qops = {
+	.queue_setup	 = bcm2835_codec_queue_setup,
+	.buf_init	 = bcm2835_codec_buf_init,
+	.buf_prepare	 = bcm2835_codec_buf_prepare,
+	.buf_queue	 = bcm2835_codec_buf_queue,
+	.buf_cleanup	 = bcm2835_codec_buffer_cleanup,
+	.start_streaming = bcm2835_codec_start_streaming,
+	.stop_streaming  = bcm2835_codec_stop_streaming,
+	.wait_prepare	 = vb2_ops_wait_prepare,
+	.wait_finish	 = vb2_ops_wait_finish,
+};
+
+static int queue_init(void *priv, struct vb2_queue *src_vq,
+		      struct vb2_queue *dst_vq)
+{
+	struct bcm2835_codec_ctx *ctx = priv;
+	int ret;
+
+	src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+	src_vq->io_modes = VB2_MMAP | VB2_DMABUF;
+	src_vq->drv_priv = ctx;
+	src_vq->buf_struct_size = sizeof(struct m2m_mmal_buffer);
+	src_vq->ops = &bcm2835_codec_qops;
+	src_vq->mem_ops = &vb2_dma_contig_memops;
+	src_vq->dev = &ctx->dev->pdev->dev;
+	src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+	src_vq->lock = &ctx->dev->dev_mutex;
+
+	ret = vb2_queue_init(src_vq);
+	if (ret)
+		return ret;
+
+	dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+	dst_vq->io_modes = VB2_MMAP | VB2_DMABUF;
+	dst_vq->drv_priv = ctx;
+	dst_vq->buf_struct_size = sizeof(struct m2m_mmal_buffer);
+	dst_vq->ops = &bcm2835_codec_qops;
+	dst_vq->mem_ops = &vb2_dma_contig_memops;
+	dst_vq->dev = &ctx->dev->pdev->dev;
+	dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+	dst_vq->lock = &ctx->dev->dev_mutex;
+
+	return vb2_queue_init(dst_vq);
+}
+
+/*
+ * File operations
+ */
+static int bcm2835_codec_open(struct file *file)
+{
+	struct bcm2835_codec_dev *dev = video_drvdata(file);
+	struct bcm2835_codec_ctx *ctx = NULL;
+	struct v4l2_ctrl_handler *hdl;
+	int rc = 0;
+
+	if (mutex_lock_interruptible(&dev->dev_mutex)) {
+		v4l2_err(&dev->v4l2_dev, "Mutex fail\n");
+		return -ERESTARTSYS;
+	}
+	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+	if (!ctx) {
+		rc = -ENOMEM;
+		goto open_unlock;
+	}
+
+	ctx->q_data[V4L2_M2M_SRC].fmt = get_default_format(dev, false);
+	ctx->q_data[V4L2_M2M_DST].fmt = get_default_format(dev, true);
+
+	ctx->q_data[V4L2_M2M_SRC].crop_width = DEFAULT_WIDTH;
+	ctx->q_data[V4L2_M2M_SRC].crop_height = DEFAULT_HEIGHT;
+	ctx->q_data[V4L2_M2M_SRC].height = DEFAULT_HEIGHT;
+	ctx->q_data[V4L2_M2M_SRC].bytesperline =
+			get_bytesperline(DEFAULT_WIDTH,
+					 ctx->q_data[V4L2_M2M_SRC].fmt);
+	ctx->q_data[V4L2_M2M_SRC].sizeimage =
+		get_sizeimage(ctx->q_data[V4L2_M2M_SRC].bytesperline,
+			      ctx->q_data[V4L2_M2M_SRC].crop_width,
+			      ctx->q_data[V4L2_M2M_SRC].height,
+			      ctx->q_data[V4L2_M2M_SRC].fmt);
+
+	ctx->q_data[V4L2_M2M_DST].crop_width = DEFAULT_WIDTH;
+	ctx->q_data[V4L2_M2M_DST].crop_height = DEFAULT_HEIGHT;
+	ctx->q_data[V4L2_M2M_DST].height = DEFAULT_HEIGHT;
+	ctx->q_data[V4L2_M2M_DST].bytesperline =
+			get_bytesperline(DEFAULT_WIDTH,
+					 ctx->q_data[V4L2_M2M_DST].fmt);
+	ctx->q_data[V4L2_M2M_DST].sizeimage =
+		get_sizeimage(ctx->q_data[V4L2_M2M_DST].bytesperline,
+			      ctx->q_data[V4L2_M2M_DST].crop_width,
+			      ctx->q_data[V4L2_M2M_DST].height,
+			      ctx->q_data[V4L2_M2M_DST].fmt);
+	ctx->q_data[V4L2_M2M_DST].aspect_ratio.numerator = 1;
+	ctx->q_data[V4L2_M2M_DST].aspect_ratio.denominator = 1;
+
+	ctx->colorspace = V4L2_COLORSPACE_REC709;
+	ctx->bitrate = 10 * 1000 * 1000;
+
+	ctx->framerate_num = 30;
+	ctx->framerate_denom = 1;
+
+	/* Initialise V4L2 contexts */
+	v4l2_fh_init(&ctx->fh, video_devdata(file));
+	file->private_data = &ctx->fh;
+	ctx->dev = dev;
+	hdl = &ctx->hdl;
+	if (dev->role == ENCODE) {
+		/* Encode controls */
+		v4l2_ctrl_handler_init(hdl, 9);
+
+		v4l2_ctrl_new_std_menu(hdl, &bcm2835_codec_ctrl_ops,
+				       V4L2_CID_MPEG_VIDEO_BITRATE_MODE,
+				       V4L2_MPEG_VIDEO_BITRATE_MODE_CBR, 0,
+				       V4L2_MPEG_VIDEO_BITRATE_MODE_VBR);
+		v4l2_ctrl_new_std(hdl, &bcm2835_codec_ctrl_ops,
+				  V4L2_CID_MPEG_VIDEO_BITRATE,
+				  25 * 1000, 25 * 1000 * 1000,
+				  25 * 1000, 10 * 1000 * 1000);
+		v4l2_ctrl_new_std_menu(hdl, &bcm2835_codec_ctrl_ops,
+				       V4L2_CID_MPEG_VIDEO_HEADER_MODE,
+				       V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME,
+				       0, V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME);
+		v4l2_ctrl_new_std(hdl, &bcm2835_codec_ctrl_ops,
+				  V4L2_CID_MPEG_VIDEO_REPEAT_SEQ_HEADER,
+				  0, 1,
+				  1, 0);
+		v4l2_ctrl_new_std(hdl, &bcm2835_codec_ctrl_ops,
+				  V4L2_CID_MPEG_VIDEO_H264_I_PERIOD,
+				  0, 0x7FFFFFFF,
+				  1, 60);
+		v4l2_ctrl_new_std_menu(hdl, &bcm2835_codec_ctrl_ops,
+				       V4L2_CID_MPEG_VIDEO_H264_LEVEL,
+				       V4L2_MPEG_VIDEO_H264_LEVEL_4_2,
+				       ~(BIT(V4L2_MPEG_VIDEO_H264_LEVEL_1_0) |
+					 BIT(V4L2_MPEG_VIDEO_H264_LEVEL_1B) |
+					 BIT(V4L2_MPEG_VIDEO_H264_LEVEL_1_1) |
+					 BIT(V4L2_MPEG_VIDEO_H264_LEVEL_1_2) |
+					 BIT(V4L2_MPEG_VIDEO_H264_LEVEL_1_3) |
+					 BIT(V4L2_MPEG_VIDEO_H264_LEVEL_2_0) |
+					 BIT(V4L2_MPEG_VIDEO_H264_LEVEL_2_1) |
+					 BIT(V4L2_MPEG_VIDEO_H264_LEVEL_2_2) |
+					 BIT(V4L2_MPEG_VIDEO_H264_LEVEL_3_0) |
+					 BIT(V4L2_MPEG_VIDEO_H264_LEVEL_3_1) |
+					 BIT(V4L2_MPEG_VIDEO_H264_LEVEL_3_2) |
+					 BIT(V4L2_MPEG_VIDEO_H264_LEVEL_4_0) |
+					 BIT(V4L2_MPEG_VIDEO_H264_LEVEL_4_1) |
+					 BIT(V4L2_MPEG_VIDEO_H264_LEVEL_4_2)),
+				       V4L2_MPEG_VIDEO_H264_LEVEL_4_0);
+		v4l2_ctrl_new_std_menu(hdl, &bcm2835_codec_ctrl_ops,
+				       V4L2_CID_MPEG_VIDEO_H264_PROFILE,
+				       V4L2_MPEG_VIDEO_H264_PROFILE_HIGH,
+				       ~(BIT(V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE) |
+					 BIT(V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE) |
+					 BIT(V4L2_MPEG_VIDEO_H264_PROFILE_MAIN) |
+					 BIT(V4L2_MPEG_VIDEO_H264_PROFILE_HIGH)),
+					V4L2_MPEG_VIDEO_H264_PROFILE_HIGH);
+		v4l2_ctrl_new_std(hdl, &bcm2835_codec_ctrl_ops,
+				  V4L2_CID_MPEG_VIDEO_FORCE_KEY_FRAME,
+				  0, 0, 0, 0);
+		if (hdl->error) {
+			rc = hdl->error;
+			goto free_ctrl_handler;
+		}
+		ctx->fh.ctrl_handler = hdl;
+		v4l2_ctrl_handler_setup(hdl);
+	} else if (dev->role == DECODE) {
+		v4l2_ctrl_handler_init(hdl, 1);
+
+		v4l2_ctrl_new_std(hdl, &bcm2835_codec_ctrl_ops,
+				  V4L2_CID_MIN_BUFFERS_FOR_CAPTURE,
+				  1, 1, 1, 1);
+		if (hdl->error) {
+			rc = hdl->error;
+			goto free_ctrl_handler;
+		}
+		ctx->fh.ctrl_handler = hdl;
+		v4l2_ctrl_handler_setup(hdl);
+	}
+
+	ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev, ctx, &queue_init);
+
+	if (IS_ERR(ctx->fh.m2m_ctx)) {
+		rc = PTR_ERR(ctx->fh.m2m_ctx);
+
+		goto free_ctrl_handler;
+	}
+
+	/* Set both queues as buffered as we have buffering in the VPU. That
+	 * means that we will be scheduled whenever either an input or output
+	 * buffer is available (otherwise one of each are required).
+	 */
+	v4l2_m2m_set_src_buffered(ctx->fh.m2m_ctx, true);
+	v4l2_m2m_set_dst_buffered(ctx->fh.m2m_ctx, true);
+
+	v4l2_fh_add(&ctx->fh);
+	atomic_inc(&dev->num_inst);
+
+	mutex_unlock(&dev->dev_mutex);
+	return 0;
+
+free_ctrl_handler:
+	v4l2_ctrl_handler_free(hdl);
+	kfree(ctx);
+open_unlock:
+	mutex_unlock(&dev->dev_mutex);
+	return rc;
+}
+
+static int bcm2835_codec_release(struct file *file)
+{
+	struct bcm2835_codec_dev *dev = video_drvdata(file);
+	struct bcm2835_codec_ctx *ctx = file2ctx(file);
+
+	v4l2_dbg(1, debug, &dev->v4l2_dev, "%s: Releasing instance %p\n",
+		 __func__, ctx);
+
+	v4l2_fh_del(&ctx->fh);
+	v4l2_fh_exit(&ctx->fh);
+	v4l2_ctrl_handler_free(&ctx->hdl);
+	mutex_lock(&dev->dev_mutex);
+	v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);
+
+	if (ctx->component)
+		vchiq_mmal_component_finalise(dev->instance, ctx->component);
+
+	mutex_unlock(&dev->dev_mutex);
+	kfree(ctx);
+
+	atomic_dec(&dev->num_inst);
+
+	return 0;
+}
+
+static const struct v4l2_file_operations bcm2835_codec_fops = {
+	.owner		= THIS_MODULE,
+	.open		= bcm2835_codec_open,
+	.release	= bcm2835_codec_release,
+	.poll		= v4l2_m2m_fop_poll,
+	.unlocked_ioctl	= video_ioctl2,
+	.mmap		= v4l2_m2m_fop_mmap,
+};
+
+static const struct video_device bcm2835_codec_videodev = {
+	.name		= MEM2MEM_NAME,
+	.vfl_dir	= VFL_DIR_M2M,
+	.fops		= &bcm2835_codec_fops,
+	.ioctl_ops	= &bcm2835_codec_ioctl_ops,
+	.minor		= -1,
+	.release	= video_device_release_empty,
+};
+
+static const struct v4l2_m2m_ops m2m_ops = {
+	.device_run	= device_run,
+	.job_ready	= job_ready,
+	.job_abort	= job_abort,
+};
+
+/* Size of the array to provide to the VPU when asking for the list of supported
+ * formats.
+ * The ISP component currently advertises 62 input formats, so add a small
+ * overhead on that.
+ */
+#define MAX_SUPPORTED_ENCODINGS 70
+
+/* Populate dev->supported_fmts with the formats supported by those ports. */
+static int bcm2835_codec_get_supported_fmts(struct bcm2835_codec_dev *dev)
+{
+	struct bcm2835_codec_fmt *list;
+	struct vchiq_mmal_component *component;
+	u32 fourccs[MAX_SUPPORTED_ENCODINGS];
+	u32 param_size = sizeof(fourccs);
+	unsigned int i, j, num_encodings;
+	int ret;
+
+	ret = vchiq_mmal_component_init(dev->instance, components[dev->role],
+					&component);
+	if (ret < 0) {
+		v4l2_err(&dev->v4l2_dev, "%s: failed to create component %s\n",
+			 __func__, components[dev->role]);
+		return -ENOMEM;
+	}
+
+	ret = vchiq_mmal_port_parameter_get(dev->instance,
+					    &component->input[0],
+					    MMAL_PARAMETER_SUPPORTED_ENCODINGS,
+					    &fourccs,
+					    &param_size);
+
+	if (ret) {
+		if (ret == MMAL_MSG_STATUS_ENOSPC) {
+			v4l2_err(&dev->v4l2_dev,
+				 "%s: port has more encodings than we provided space for. Some are dropped (%zu vs %u).\n",
+				 __func__, param_size / sizeof(u32),
+				 MAX_SUPPORTED_ENCODINGS);
+			num_encodings = MAX_SUPPORTED_ENCODINGS;
+		} else {
+			v4l2_err(&dev->v4l2_dev, "%s: get_param ret %u.\n",
+				 __func__, ret);
+			ret = -EINVAL;
+			goto destroy_component;
+		}
+	} else {
+		num_encodings = param_size / sizeof(u32);
+	}
+
+	/* Assume at this stage that all encodings will be supported in V4L2.
+	 * Any that aren't supported will waste a very small amount of memory.
+	 */
+	list = devm_kzalloc(&dev->pdev->dev,
+			    sizeof(struct bcm2835_codec_fmt) * num_encodings,
+			    GFP_KERNEL);
+	if (!list) {
+		ret = -ENOMEM;
+		goto destroy_component;
+	}
+	dev->supported_fmts[0].list = list;
+
+	for (i = 0, j = 0; i < num_encodings; i++) {
+		const struct bcm2835_codec_fmt *fmt = get_fmt(fourccs[i]);
+
+		if (fmt) {
+			list[j] = *fmt;
+			j++;
+		}
+	}
+	dev->supported_fmts[0].num_entries = j;
+
+	param_size = sizeof(fourccs);
+	ret = vchiq_mmal_port_parameter_get(dev->instance,
+					    &component->output[0],
+					    MMAL_PARAMETER_SUPPORTED_ENCODINGS,
+					    &fourccs,
+					    &param_size);
+
+	if (ret) {
+		if (ret == MMAL_MSG_STATUS_ENOSPC) {
+			v4l2_err(&dev->v4l2_dev,
+				 "%s: port has more encodings than we provided space for. Some are dropped (%zu vs %u).\n",
+				 __func__, param_size / sizeof(u32),
+				 MAX_SUPPORTED_ENCODINGS);
+			num_encodings = MAX_SUPPORTED_ENCODINGS;
+		} else {
+			ret = -EINVAL;
+			goto destroy_component;
+		}
+	} else {
+		num_encodings = param_size / sizeof(u32);
+	}
+	/* Assume at this stage that all encodings will be supported in V4L2. */
+	list = devm_kzalloc(&dev->pdev->dev,
+			    sizeof(struct bcm2835_codec_fmt) * num_encodings,
+			    GFP_KERNEL);
+	if (!list) {
+		ret = -ENOMEM;
+		goto destroy_component;
+	}
+	dev->supported_fmts[1].list = list;
+
+	for (i = 0, j = 0; i < num_encodings; i++) {
+		const struct bcm2835_codec_fmt *fmt = get_fmt(fourccs[i]);
+
+		if (fmt) {
+			list[j] = *fmt;
+			j++;
+		}
+	}
+	dev->supported_fmts[1].num_entries = j;
+
+	ret = 0;
+
+destroy_component:
+	vchiq_mmal_component_finalise(dev->instance, component);
+
+	return ret;
+}
+
+static int bcm2835_codec_create(struct bcm2835_codec_driver *drv,
+				struct bcm2835_codec_dev **new_dev,
+				enum bcm2835_codec_role role)
+{
+	struct platform_device *pdev = drv->pdev;
+	struct bcm2835_codec_dev *dev;
+	struct video_device *vfd;
+	int function;
+	int video_nr;
+	int ret;
+
+	dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
+	if (!dev)
+		return -ENOMEM;
+
+	dev->pdev = pdev;
+
+	dev->role = role;
+
+	ret = vchiq_mmal_init(&dev->instance);
+	if (ret)
+		return ret;
+
+	ret = bcm2835_codec_get_supported_fmts(dev);
+	if (ret)
+		goto vchiq_finalise;
+
+	atomic_set(&dev->num_inst, 0);
+	mutex_init(&dev->dev_mutex);
+
+	/* Initialise the video device */
+	dev->vfd = bcm2835_codec_videodev;
+
+	vfd = &dev->vfd;
+	vfd->lock = &dev->dev_mutex;
+	vfd->v4l2_dev = &dev->v4l2_dev;
+	vfd->device_caps = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING;
+	vfd->v4l2_dev->mdev = &drv->mdev;
+
+	ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev);
+	if (ret)
+		goto vchiq_finalise;
+
+	switch (role) {
+	case DECODE:
+		v4l2_disable_ioctl(vfd, VIDIOC_ENCODER_CMD);
+		v4l2_disable_ioctl(vfd, VIDIOC_TRY_ENCODER_CMD);
+		v4l2_disable_ioctl(vfd, VIDIOC_S_PARM);
+		v4l2_disable_ioctl(vfd, VIDIOC_G_PARM);
+		function = MEDIA_ENT_F_PROC_VIDEO_DECODER;
+		video_nr = decode_video_nr;
+		break;
+	case ENCODE:
+		v4l2_disable_ioctl(vfd, VIDIOC_DECODER_CMD);
+		v4l2_disable_ioctl(vfd, VIDIOC_TRY_DECODER_CMD);
+		function = MEDIA_ENT_F_PROC_VIDEO_ENCODER;
+		video_nr = encode_video_nr;
+		break;
+	case ISP:
+		v4l2_disable_ioctl(vfd, VIDIOC_ENCODER_CMD);
+		v4l2_disable_ioctl(vfd, VIDIOC_TRY_ENCODER_CMD);
+		v4l2_disable_ioctl(vfd, VIDIOC_DECODER_CMD);
+		v4l2_disable_ioctl(vfd, VIDIOC_TRY_DECODER_CMD);
+		v4l2_disable_ioctl(vfd, VIDIOC_S_PARM);
+		v4l2_disable_ioctl(vfd, VIDIOC_G_PARM);
+		function = MEDIA_ENT_F_PROC_VIDEO_SCALER;
+		video_nr = isp_video_nr;
+		break;
+	default:
+		ret = -EINVAL;
+		goto unreg_dev;
+	}
+
+	ret = video_register_device(vfd, VFL_TYPE_VIDEO, video_nr);
+	if (ret) {
+		v4l2_err(&dev->v4l2_dev, "Failed to register video device\n");
+		goto unreg_dev;
+	}
+
+	video_set_drvdata(vfd, dev);
+	snprintf(vfd->name, sizeof(vfd->name), "%s-%s",
+		 bcm2835_codec_videodev.name, roles[role]);
+	v4l2_info(&dev->v4l2_dev, "Device registered as /dev/video%d\n",
+		  vfd->num);
+
+	*new_dev = dev;
+
+	dev->m2m_dev = v4l2_m2m_init(&m2m_ops);
+	if (IS_ERR(dev->m2m_dev)) {
+		v4l2_err(&dev->v4l2_dev, "Failed to init mem2mem device\n");
+		ret = PTR_ERR(dev->m2m_dev);
+		goto err_m2m;
+	}
+
+	ret = v4l2_m2m_register_media_controller(dev->m2m_dev, vfd, function);
+	if (ret)
+		goto err_m2m;
+
+	v4l2_info(&dev->v4l2_dev, "Loaded V4L2 %s\n",
+		  roles[role]);
+	return 0;
+
+err_m2m:
+	v4l2_m2m_release(dev->m2m_dev);
+	video_unregister_device(&dev->vfd);
+unreg_dev:
+	v4l2_device_unregister(&dev->v4l2_dev);
+vchiq_finalise:
+	vchiq_mmal_finalise(dev->instance);
+	return ret;
+}
+
+static int bcm2835_codec_destroy(struct bcm2835_codec_dev *dev)
+{
+	if (!dev)
+		return -ENODEV;
+
+	v4l2_info(&dev->v4l2_dev, "Removing " MEM2MEM_NAME ", %s\n",
+		  roles[dev->role]);
+	v4l2_m2m_unregister_media_controller(dev->m2m_dev);
+	v4l2_m2m_release(dev->m2m_dev);
+	video_unregister_device(&dev->vfd);
+	v4l2_device_unregister(&dev->v4l2_dev);
+	vchiq_mmal_finalise(dev->instance);
+
+	return 0;
+}
+
+static int bcm2835_codec_probe(struct platform_device *pdev)
+{
+	struct bcm2835_codec_driver *drv;
+	struct media_device *mdev;
+	int ret = 0;
+
+	drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL);
+	if (!drv)
+		return -ENOMEM;
+
+	drv->pdev = pdev;
+	mdev = &drv->mdev;
+	mdev->dev = &pdev->dev;
+
+	strscpy(mdev->model, bcm2835_codec_videodev.name, sizeof(mdev->model));
+	strscpy(mdev->serial, "0000", sizeof(mdev->serial));
+	snprintf(mdev->bus_info, sizeof(mdev->bus_info), "platform:%s",
+		 pdev->name);
+
+	/* This should return the vgencmd version information or such .. */
+	mdev->hw_revision = 1;
+	media_device_init(mdev);
+
+	ret = bcm2835_codec_create(drv, &drv->decode, DECODE);
+	if (ret)
+		goto out;
+
+	ret = bcm2835_codec_create(drv, &drv->encode, ENCODE);
+	if (ret)
+		goto out;
+
+	ret = bcm2835_codec_create(drv, &drv->isp, ISP);
+	if (ret)
+		goto out;
+
+	/* Register the media device node */
+	if (media_device_register(mdev) < 0)
+		goto out;
+
+	platform_set_drvdata(pdev, drv);
+
+	return 0;
+
+out:
+	if (drv->isp) {
+		bcm2835_codec_destroy(drv->isp);
+		drv->isp = NULL;
+	}
+	if (drv->encode) {
+		bcm2835_codec_destroy(drv->encode);
+		drv->encode = NULL;
+	}
+	if (drv->decode) {
+		bcm2835_codec_destroy(drv->decode);
+		drv->decode = NULL;
+	}
+	return ret;
+}
+
+static int bcm2835_codec_remove(struct platform_device *pdev)
+{
+	struct bcm2835_codec_driver *drv = platform_get_drvdata(pdev);
+
+	media_device_unregister(&drv->mdev);
+
+	bcm2835_codec_destroy(drv->isp);
+
+	bcm2835_codec_destroy(drv->encode);
+
+	bcm2835_codec_destroy(drv->decode);
+
+	media_device_cleanup(&drv->mdev);
+
+	return 0;
+}
+
+static struct platform_driver bcm2835_v4l2_codec_driver = {
+	.probe = bcm2835_codec_probe,
+	.remove = bcm2835_codec_remove,
+	.driver = {
+		   .name = "bcm2835-codec",
+		   .owner = THIS_MODULE,
+		   },
+};
+
+module_platform_driver(bcm2835_v4l2_codec_driver);
+
+MODULE_DESCRIPTION("BCM2835 codec V4L2 driver");
+MODULE_AUTHOR("Dave Stevenson, <dave.stevenson@raspberrypi.com>");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("0.0.1");
+MODULE_ALIAS("platform:bcm2835-codec");
diff -Nur --no-dereference linux-5.10.52-orig/drivers/staging/vc04_services/bcm2835-codec/Kconfig linux-5.10.52-v7l+/drivers/staging/vc04_services/bcm2835-codec/Kconfig
--- linux-5.10.52-orig/drivers/staging/vc04_services/bcm2835-codec/Kconfig	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/staging/vc04_services/bcm2835-codec/Kconfig	2021-07-25 16:46:26.517946325 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+config VIDEO_CODEC_BCM2835
+	tristate "BCM2835 Video codec support"
+	depends on MEDIA_SUPPORT && MEDIA_CONTROLLER
+	depends on VIDEO_V4L2 && (ARCH_BCM2835 || COMPILE_TEST)
+	select BCM2835_VCHIQ_MMAL
+	select VIDEOBUF2_DMA_CONTIG
+	select V4L2_MEM2MEM_DEV
+	help
+	  Say Y here to enable the V4L2 video codecs for
+	  Broadcom BCM2835 SoC. This operates over the VCHIQ interface
+	  to a service running on VideoCore.
diff -Nur --no-dereference linux-5.10.52-orig/drivers/staging/vc04_services/bcm2835-codec/Makefile linux-5.10.52-v7l+/drivers/staging/vc04_services/bcm2835-codec/Makefile
--- linux-5.10.52-orig/drivers/staging/vc04_services/bcm2835-codec/Makefile	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/staging/vc04_services/bcm2835-codec/Makefile	2021-07-25 16:46:26.517946325 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+# SPDX-License-Identifier: GPL-2.0
+bcm2835-codec-objs := bcm2835-v4l2-codec.o
+
+obj-$(CONFIG_VIDEO_CODEC_BCM2835) += bcm2835-codec.o
+
+ccflags-y += \
+	-I$(srctree)/drivers/staging/vc04_services \
+	-D__VCCOREVER__=0x04000000
diff -Nur --no-dereference linux-5.10.52-orig/drivers/staging/vc04_services/bcm2835-codec/TODO linux-5.10.52-v7l+/drivers/staging/vc04_services/bcm2835-codec/TODO
--- linux-5.10.52-orig/drivers/staging/vc04_services/bcm2835-codec/TODO	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/staging/vc04_services/bcm2835-codec/TODO	2021-07-25 16:46:26.517946325 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1 @
+No issues. Depends on VCHIQ which is in staging.
\ No newline at end of file
diff -Nur --no-dereference linux-5.10.52-orig/drivers/staging/vc04_services/bcm2835-isp/bcm2835-isp-ctrls.h linux-5.10.52-v7l+/drivers/staging/vc04_services/bcm2835-isp/bcm2835-isp-ctrls.h
--- linux-5.10.52-orig/drivers/staging/vc04_services/bcm2835-isp/bcm2835-isp-ctrls.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/staging/vc04_services/bcm2835-isp/bcm2835-isp-ctrls.h	2021-07-25 16:46:26.537945989 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Broadcom BCM2835 ISP driver
+ *
+ * Copyright © 2019-2020 Raspberry Pi (Trading) Ltd.
+ *
+ * Author: Naushir Patuck (naush@raspberrypi.com)
+ *
+ */
+
+#ifndef BCM2835_ISP_CTRLS
+#define BCM2835_ISP_CTRLS
+
+#include <linux/bcm2835-isp.h>
+
+struct bcm2835_isp_custom_ctrl {
+	const char *name;
+	u32 id;
+	u32 size;
+	u32 flags;
+};
+
+static const struct bcm2835_isp_custom_ctrl custom_ctrls[] = {
+	{
+		.name	= "Colour Correction Matrix",
+		.id	= V4L2_CID_USER_BCM2835_ISP_CC_MATRIX,
+		.size	= sizeof(struct bcm2835_isp_custom_ccm),
+		.flags  = 0
+	}, {
+		.name	= "Lens Shading",
+		.id	= V4L2_CID_USER_BCM2835_ISP_LENS_SHADING,
+		.size	= sizeof(struct bcm2835_isp_lens_shading),
+		.flags  = V4L2_CTRL_FLAG_EXECUTE_ON_WRITE
+	}, {
+		.name	= "Black Level",
+		.id	= V4L2_CID_USER_BCM2835_ISP_BLACK_LEVEL,
+		.size	= sizeof(struct bcm2835_isp_black_level),
+		.flags  = 0
+	}, {
+		.name	= "Green Equalisation",
+		.id	= V4L2_CID_USER_BCM2835_ISP_GEQ,
+		.size	= sizeof(struct bcm2835_isp_geq),
+		.flags  = 0
+	}, {
+		.name	= "Gamma",
+		.id	= V4L2_CID_USER_BCM2835_ISP_GAMMA,
+		.size	= sizeof(struct bcm2835_isp_gamma),
+		.flags  = 0
+	}, {
+		.name	= "Sharpen",
+		.id	= V4L2_CID_USER_BCM2835_ISP_SHARPEN,
+		.size	= sizeof(struct bcm2835_isp_sharpen),
+		.flags  = 0
+	}, {
+		.name	= "Denoise",
+		.id	= V4L2_CID_USER_BCM2835_ISP_DENOISE,
+		.size	= sizeof(struct bcm2835_isp_denoise),
+		.flags  = 0
+	}, {
+		.name	= "Colour Denoise",
+		.id	= V4L2_CID_USER_BCM2835_ISP_CDN,
+		.size	= sizeof(struct bcm2835_isp_cdn),
+		.flags  = 0
+	}, {
+		.name	= "Defective Pixel Correction",
+		.id	= V4L2_CID_USER_BCM2835_ISP_DPC,
+		.size	= sizeof(struct bcm2835_isp_dpc),
+		.flags  = 0
+	}
+};
+
+#endif
diff -Nur --no-dereference linux-5.10.52-orig/drivers/staging/vc04_services/bcm2835-isp/bcm2835-isp-fmts.h linux-5.10.52-v7l+/drivers/staging/vc04_services/bcm2835-isp/bcm2835-isp-fmts.h
--- linux-5.10.52-orig/drivers/staging/vc04_services/bcm2835-isp/bcm2835-isp-fmts.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/staging/vc04_services/bcm2835-isp/bcm2835-isp-fmts.h	2021-07-25 16:46:26.537945989 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Broadcom BCM2835 ISP driver
+ *
+ * Copyright © 2019-2020 Raspberry Pi (Trading) Ltd.
+ *
+ * Author: Naushir Patuck (naush@raspberrypi.com)
+ *
+ */
+
+#ifndef BCM2835_ISP_FMTS
+#define BCM2835_ISP_FMTS
+
+#include <linux/videodev2.h>
+#include "vchiq-mmal/mmal-encodings.h"
+
+struct bcm2835_isp_fmt {
+	u32 fourcc;
+	int depth;
+	int bytesperline_align;
+	u32 mmal_fmt;
+	int size_multiplier_x2;
+	u32 colorspace_mask;
+	enum v4l2_colorspace colorspace_default;
+	unsigned int step_size;
+};
+
+#define V4L2_COLORSPACE_MASK(colorspace) BIT(colorspace)
+
+#define V4L2_COLORSPACE_MASK_JPEG V4L2_COLORSPACE_MASK(V4L2_COLORSPACE_JPEG)
+#define V4L2_COLORSPACE_MASK_SMPTE170M V4L2_COLORSPACE_MASK(V4L2_COLORSPACE_SMPTE170M)
+#define V4L2_COLORSPACE_MASK_REC709 V4L2_COLORSPACE_MASK(V4L2_COLORSPACE_REC709)
+#define V4L2_COLORSPACE_MASK_SRGB V4L2_COLORSPACE_MASK(V4L2_COLORSPACE_SRGB)
+#define V4L2_COLORSPACE_MASK_RAW V4L2_COLORSPACE_MASK(V4L2_COLORSPACE_RAW)
+
+/*
+ * The colour spaces we support for YUV outputs. SRGB features here because,
+ * once you assign the default transfer func and so on, it and JPEG effectively
+ * mean the same.
+ */
+#define V4L2_COLORSPACE_MASK_YUV (V4L2_COLORSPACE_MASK_JPEG | \
+				  V4L2_COLORSPACE_MASK_SRGB | \
+				  V4L2_COLORSPACE_MASK_SMPTE170M | \
+				  V4L2_COLORSPACE_MASK_REC709)
+
+static const struct bcm2835_isp_fmt supported_formats[] = {
+	{
+		/* YUV formats */
+		.fourcc		    = V4L2_PIX_FMT_YUV420,
+		.depth		    = 8,
+		.bytesperline_align = 64,
+		.mmal_fmt	    = MMAL_ENCODING_I420,
+		.size_multiplier_x2 = 3,
+		.colorspace_mask    = V4L2_COLORSPACE_MASK_YUV,
+		.colorspace_default = V4L2_COLORSPACE_JPEG,
+		.step_size	    = 2,
+	}, {
+		.fourcc		    = V4L2_PIX_FMT_YVU420,
+		.depth		    = 8,
+		.bytesperline_align = 64,
+		.mmal_fmt	    = MMAL_ENCODING_YV12,
+		.size_multiplier_x2 = 3,
+		.colorspace_mask    = V4L2_COLORSPACE_MASK_YUV,
+		.colorspace_default = V4L2_COLORSPACE_SMPTE170M,
+		.step_size	    = 2,
+	}, {
+		.fourcc		    = V4L2_PIX_FMT_NV12,
+		.depth		    = 8,
+		.bytesperline_align = 32,
+		.mmal_fmt	    = MMAL_ENCODING_NV12,
+		.size_multiplier_x2 = 3,
+		.colorspace_mask    = V4L2_COLORSPACE_MASK_YUV,
+		.colorspace_default = V4L2_COLORSPACE_SMPTE170M,
+		.step_size	    = 2,
+	}, {
+		.fourcc		    = V4L2_PIX_FMT_NV21,
+		.depth		    = 8,
+		.bytesperline_align = 32,
+		.mmal_fmt	    = MMAL_ENCODING_NV21,
+		.size_multiplier_x2 = 3,
+		.colorspace_mask    = V4L2_COLORSPACE_MASK_YUV,
+		.colorspace_default = V4L2_COLORSPACE_SMPTE170M,
+		.step_size	    = 2,
+	}, {
+		.fourcc		    = V4L2_PIX_FMT_YUYV,
+		.depth		    = 16,
+		.bytesperline_align = 64,
+		.mmal_fmt	    = MMAL_ENCODING_YUYV,
+		.size_multiplier_x2 = 2,
+		.colorspace_mask    = V4L2_COLORSPACE_MASK_YUV,
+		.colorspace_default = V4L2_COLORSPACE_SMPTE170M,
+		.step_size	    = 2,
+	}, {
+		.fourcc		    = V4L2_PIX_FMT_UYVY,
+		.depth		    = 16,
+		.bytesperline_align = 64,
+		.mmal_fmt	    = MMAL_ENCODING_UYVY,
+		.size_multiplier_x2 = 2,
+		.colorspace_mask    = V4L2_COLORSPACE_MASK_YUV,
+		.colorspace_default = V4L2_COLORSPACE_SMPTE170M,
+		.step_size	    = 2,
+	}, {
+		.fourcc		    = V4L2_PIX_FMT_YVYU,
+		.depth		    = 16,
+		.bytesperline_align = 64,
+		.mmal_fmt	    = MMAL_ENCODING_YVYU,
+		.size_multiplier_x2 = 2,
+		.colorspace_mask    = V4L2_COLORSPACE_MASK_YUV,
+		.colorspace_default = V4L2_COLORSPACE_SMPTE170M,
+		.step_size	    = 2,
+	}, {
+		.fourcc		    = V4L2_PIX_FMT_VYUY,
+		.depth		    = 16,
+		.bytesperline_align = 64,
+		.mmal_fmt	    = MMAL_ENCODING_VYUY,
+		.size_multiplier_x2 = 2,
+		.colorspace_mask    = V4L2_COLORSPACE_MASK_YUV,
+		.colorspace_default = V4L2_COLORSPACE_SMPTE170M,
+		.step_size	    = 2,
+	}, {
+		/* RGB formats */
+		.fourcc		    = V4L2_PIX_FMT_RGB24,
+		.depth		    = 24,
+		.bytesperline_align = 32,
+		.mmal_fmt	    = MMAL_ENCODING_RGB24,
+		.size_multiplier_x2 = 2,
+		.colorspace_mask    = V4L2_COLORSPACE_MASK_SRGB,
+		.colorspace_default = V4L2_COLORSPACE_SRGB,
+		.step_size	    = 1,
+	}, {
+		.fourcc		    = V4L2_PIX_FMT_RGB565,
+		.depth		    = 16,
+		.bytesperline_align = 32,
+		.mmal_fmt	    = MMAL_ENCODING_RGB16,
+		.size_multiplier_x2 = 2,
+		.colorspace_mask    = V4L2_COLORSPACE_MASK_SRGB,
+		.colorspace_default = V4L2_COLORSPACE_SRGB,
+		.step_size	    = 1,
+	}, {
+		.fourcc		    = V4L2_PIX_FMT_BGR24,
+		.depth		    = 24,
+		.bytesperline_align = 32,
+		.mmal_fmt	    = MMAL_ENCODING_BGR24,
+		.size_multiplier_x2 = 2,
+		.colorspace_mask    = V4L2_COLORSPACE_MASK_SRGB,
+		.colorspace_default = V4L2_COLORSPACE_SRGB,
+		.step_size	    = 1,
+	}, {
+		.fourcc		    = V4L2_PIX_FMT_XBGR32,
+		.depth		    = 32,
+		.bytesperline_align = 64,
+		.mmal_fmt	    = MMAL_ENCODING_BGRA,
+		.size_multiplier_x2 = 2,
+		.colorspace_mask    = V4L2_COLORSPACE_MASK_SRGB,
+		.colorspace_default = V4L2_COLORSPACE_SRGB,
+		.step_size	    = 1,
+	}, {
+		.fourcc		    = V4L2_PIX_FMT_RGBX32,
+		.depth		    = 32,
+		.bytesperline_align = 64,
+		.mmal_fmt	    = MMAL_ENCODING_RGBA,
+		.size_multiplier_x2 = 2,
+		.colorspace_mask    = V4L2_COLORSPACE_MASK_SRGB,
+		.colorspace_default = V4L2_COLORSPACE_SRGB,
+		.step_size	    = 1,
+	}, {
+		/* Bayer formats */
+		/* 8 bit */
+		.fourcc		    = V4L2_PIX_FMT_SRGGB8,
+		.depth		    = 8,
+		.bytesperline_align = 32,
+		.mmal_fmt	    = MMAL_ENCODING_BAYER_SRGGB8,
+		.size_multiplier_x2 = 2,
+		.colorspace_mask    = V4L2_COLORSPACE_MASK_RAW,
+		.colorspace_default = V4L2_COLORSPACE_RAW,
+		.step_size	    = 2,
+	}, {
+		.fourcc		    = V4L2_PIX_FMT_SBGGR8,
+		.depth		    = 8,
+		.bytesperline_align = 32,
+		.mmal_fmt	    = MMAL_ENCODING_BAYER_SBGGR8,
+		.size_multiplier_x2 = 2,
+		.colorspace_mask    = V4L2_COLORSPACE_MASK_RAW,
+		.colorspace_default = V4L2_COLORSPACE_RAW,
+		.step_size	    = 2,
+	}, {
+		.fourcc		    = V4L2_PIX_FMT_SGRBG8,
+		.depth		    = 8,
+		.bytesperline_align = 32,
+		.mmal_fmt	    = MMAL_ENCODING_BAYER_SGRBG8,
+		.size_multiplier_x2 = 2,
+		.colorspace_mask    = V4L2_COLORSPACE_MASK_RAW,
+		.colorspace_default = V4L2_COLORSPACE_RAW,
+		.step_size	    = 2,
+	}, {
+		.fourcc		    = V4L2_PIX_FMT_SGBRG8,
+		.depth		    = 8,
+		.bytesperline_align = 32,
+		.mmal_fmt	    = MMAL_ENCODING_BAYER_SGBRG8,
+		.size_multiplier_x2 = 2,
+		.colorspace_mask    = V4L2_COLORSPACE_MASK_RAW,
+		.colorspace_default = V4L2_COLORSPACE_RAW,
+		.step_size	    = 2,
+	}, {
+		/* 10 bit */
+		.fourcc		    = V4L2_PIX_FMT_SRGGB10P,
+		.depth		    = 10,
+		.bytesperline_align = 32,
+		.mmal_fmt	    = MMAL_ENCODING_BAYER_SRGGB10P,
+		.size_multiplier_x2 = 2,
+		.colorspace_mask    = V4L2_COLORSPACE_MASK_RAW,
+		.colorspace_default = V4L2_COLORSPACE_RAW,
+		.step_size	    = 2,
+	}, {
+		.fourcc		    = V4L2_PIX_FMT_SBGGR10P,
+		.depth		    = 10,
+		.bytesperline_align = 32,
+		.mmal_fmt	    = MMAL_ENCODING_BAYER_SBGGR10P,
+		.size_multiplier_x2 = 2,
+		.colorspace_mask    = V4L2_COLORSPACE_MASK_RAW,
+		.colorspace_default = V4L2_COLORSPACE_RAW,
+		.step_size	    = 2,
+	}, {
+		.fourcc		    = V4L2_PIX_FMT_SGRBG10P,
+		.depth		    = 10,
+		.bytesperline_align = 32,
+		.mmal_fmt	    = MMAL_ENCODING_BAYER_SGRBG10P,
+		.size_multiplier_x2 = 2,
+		.colorspace_mask    = V4L2_COLORSPACE_MASK_RAW,
+		.colorspace_default = V4L2_COLORSPACE_RAW,
+		.step_size	    = 2,
+	}, {
+		.fourcc		    = V4L2_PIX_FMT_SGBRG10P,
+		.depth		    = 10,
+		.bytesperline_align = 32,
+		.mmal_fmt	    = MMAL_ENCODING_BAYER_SGBRG10P,
+		.size_multiplier_x2 = 2,
+		.colorspace_mask    = V4L2_COLORSPACE_MASK_RAW,
+		.colorspace_default = V4L2_COLORSPACE_RAW,
+		.step_size	    = 2,
+	}, {
+		/* 12 bit */
+		.fourcc		    = V4L2_PIX_FMT_SRGGB12P,
+		.depth		    = 12,
+		.bytesperline_align = 32,
+		.mmal_fmt	    = MMAL_ENCODING_BAYER_SRGGB12P,
+		.size_multiplier_x2 = 2,
+		.colorspace_mask    = V4L2_COLORSPACE_MASK_RAW,
+		.colorspace_default = V4L2_COLORSPACE_RAW,
+		.step_size	    = 2,
+	}, {
+		.fourcc		    = V4L2_PIX_FMT_SBGGR12P,
+		.depth		    = 12,
+		.bytesperline_align = 32,
+		.mmal_fmt	    = MMAL_ENCODING_BAYER_SBGGR12P,
+		.size_multiplier_x2 = 2,
+		.colorspace_mask    = V4L2_COLORSPACE_MASK_RAW,
+		.colorspace_default = V4L2_COLORSPACE_RAW,
+		.step_size	    = 2,
+	}, {
+		.fourcc		    = V4L2_PIX_FMT_SGRBG12P,
+		.depth		    = 12,
+		.bytesperline_align = 32,
+		.mmal_fmt	    = MMAL_ENCODING_BAYER_SGRBG12P,
+		.size_multiplier_x2 = 2,
+		.colorspace_mask    = V4L2_COLORSPACE_MASK_RAW,
+		.colorspace_default = V4L2_COLORSPACE_RAW,
+		.step_size	    = 2,
+	}, {
+		.fourcc		    = V4L2_PIX_FMT_SGBRG12P,
+		.depth		    = 12,
+		.bytesperline_align = 32,
+		.mmal_fmt	    = MMAL_ENCODING_BAYER_SGBRG12P,
+		.size_multiplier_x2 = 2,
+		.colorspace_mask    = V4L2_COLORSPACE_MASK_RAW,
+		.colorspace_default = V4L2_COLORSPACE_RAW,
+		.step_size	    = 2,
+	}, {
+		/* 14 bit */
+		.fourcc		    = V4L2_PIX_FMT_SRGGB14P,
+		.depth		    = 14,
+		.bytesperline_align = 32,
+		.mmal_fmt	    = MMAL_ENCODING_BAYER_SRGGB14P,
+		.size_multiplier_x2 = 2,
+		.colorspace_mask    = V4L2_COLORSPACE_MASK_RAW,
+		.colorspace_default = V4L2_COLORSPACE_RAW,
+		.step_size	    = 2,
+	}, {
+		.fourcc		    = V4L2_PIX_FMT_SBGGR14P,
+		.depth		    = 14,
+		.bytesperline_align = 32,
+		.mmal_fmt	    = MMAL_ENCODING_BAYER_SBGGR14P,
+		.size_multiplier_x2 = 2,
+		.colorspace_mask    = V4L2_COLORSPACE_MASK_RAW,
+		.colorspace_default = V4L2_COLORSPACE_RAW,
+		.step_size	    = 2,
+	}, {
+		.fourcc		    = V4L2_PIX_FMT_SGRBG14P,
+		.depth		    = 14,
+		.bytesperline_align = 32,
+		.mmal_fmt	    = MMAL_ENCODING_BAYER_SGRBG14P,
+		.size_multiplier_x2 = 2,
+		.colorspace_mask    = V4L2_COLORSPACE_MASK_RAW,
+		.colorspace_default = V4L2_COLORSPACE_RAW,
+		.step_size	    = 2,
+	}, {
+		.fourcc		    = V4L2_PIX_FMT_SGBRG14P,
+		.depth		    = 14,
+		.bytesperline_align = 32,
+		.mmal_fmt	    = MMAL_ENCODING_BAYER_SGBRG14P,
+		.size_multiplier_x2 = 2,
+		.colorspace_mask    = V4L2_COLORSPACE_MASK_RAW,
+		.colorspace_default = V4L2_COLORSPACE_RAW,
+		.step_size	    = 2,
+	}, {
+		/* 16 bit */
+		.fourcc		    = V4L2_PIX_FMT_SRGGB16,
+		.depth		    = 16,
+		.bytesperline_align = 32,
+		.mmal_fmt	    = MMAL_ENCODING_BAYER_SRGGB16,
+		.size_multiplier_x2 = 2,
+		.colorspace_mask    = V4L2_COLORSPACE_MASK_RAW,
+		.colorspace_default = V4L2_COLORSPACE_RAW,
+		.step_size	    = 2,
+	}, {
+		.fourcc		    = V4L2_PIX_FMT_SBGGR16,
+		.depth		    = 16,
+		.bytesperline_align = 32,
+		.mmal_fmt	    = MMAL_ENCODING_BAYER_SBGGR16,
+		.size_multiplier_x2 = 2,
+		.colorspace_mask    = V4L2_COLORSPACE_MASK_RAW,
+		.colorspace_default = V4L2_COLORSPACE_RAW,
+		.step_size	    = 2,
+	}, {
+		.fourcc		    = V4L2_PIX_FMT_SGRBG16,
+		.depth		    = 16,
+		.bytesperline_align = 32,
+		.mmal_fmt	    = MMAL_ENCODING_BAYER_SGRBG16,
+		.size_multiplier_x2 = 2,
+		.colorspace_mask    = V4L2_COLORSPACE_MASK_RAW,
+		.colorspace_default = V4L2_COLORSPACE_RAW,
+		.step_size	    = 2,
+	}, {
+		.fourcc		    = V4L2_PIX_FMT_SGBRG16,
+		.depth		    = 16,
+		.bytesperline_align = 32,
+		.mmal_fmt	    = MMAL_ENCODING_BAYER_SGBRG16,
+		.size_multiplier_x2 = 2,
+		.colorspace_mask    = V4L2_COLORSPACE_MASK_RAW,
+		.colorspace_default = V4L2_COLORSPACE_RAW,
+		.step_size	    = 2,
+	}, {
+		/* Bayer formats unpacked to 16bpp */
+		/* 10 bit */
+		.fourcc		    = V4L2_PIX_FMT_SRGGB10,
+		.depth		    = 16,
+		.bytesperline_align = 32,
+		.mmal_fmt	    = MMAL_ENCODING_BAYER_SRGGB10,
+		.size_multiplier_x2 = 2,
+		.colorspace_mask    = V4L2_COLORSPACE_MASK_RAW,
+		.colorspace_default = V4L2_COLORSPACE_RAW,
+		.step_size	    = 2,
+	}, {
+		.fourcc		    = V4L2_PIX_FMT_SBGGR10,
+		.depth		    = 16,
+		.bytesperline_align = 32,
+		.mmal_fmt	    = MMAL_ENCODING_BAYER_SBGGR10,
+		.size_multiplier_x2 = 2,
+		.colorspace_mask    = V4L2_COLORSPACE_MASK_RAW,
+		.colorspace_default = V4L2_COLORSPACE_RAW,
+		.step_size	    = 2,
+	}, {
+		.fourcc		    = V4L2_PIX_FMT_SGRBG10,
+		.depth		    = 16,
+		.bytesperline_align = 32,
+		.mmal_fmt	    = MMAL_ENCODING_BAYER_SGRBG10,
+		.size_multiplier_x2 = 2,
+		.colorspace_mask    = V4L2_COLORSPACE_MASK_RAW,
+		.colorspace_default = V4L2_COLORSPACE_RAW,
+		.step_size	    = 2,
+	}, {
+		.fourcc		    = V4L2_PIX_FMT_SGBRG10,
+		.depth		    = 16,
+		.bytesperline_align = 32,
+		.mmal_fmt	    = MMAL_ENCODING_BAYER_SGBRG10,
+		.size_multiplier_x2 = 2,
+		.colorspace_mask    = V4L2_COLORSPACE_MASK_RAW,
+		.colorspace_default = V4L2_COLORSPACE_RAW,
+		.step_size	    = 2,
+	}, {
+		/* 12 bit */
+		.fourcc		    = V4L2_PIX_FMT_SRGGB12,
+		.depth		    = 16,
+		.bytesperline_align = 32,
+		.mmal_fmt	    = MMAL_ENCODING_BAYER_SRGGB12,
+		.size_multiplier_x2 = 2,
+		.colorspace_mask    = V4L2_COLORSPACE_MASK_RAW,
+		.colorspace_default = V4L2_COLORSPACE_RAW,
+		.step_size	    = 2,
+	}, {
+		.fourcc		    = V4L2_PIX_FMT_SBGGR12,
+		.depth		    = 16,
+		.bytesperline_align = 32,
+		.mmal_fmt	    = MMAL_ENCODING_BAYER_SBGGR12,
+		.size_multiplier_x2 = 2,
+		.colorspace_mask    = V4L2_COLORSPACE_MASK_RAW,
+		.colorspace_default = V4L2_COLORSPACE_RAW,
+		.step_size	    = 2,
+	}, {
+		.fourcc		    = V4L2_PIX_FMT_SGRBG12,
+		.depth		    = 16,
+		.bytesperline_align = 32,
+		.mmal_fmt	    = MMAL_ENCODING_BAYER_SGRBG12,
+		.size_multiplier_x2 = 2,
+		.colorspace_mask    = V4L2_COLORSPACE_MASK_RAW,
+		.colorspace_default = V4L2_COLORSPACE_RAW,
+		.step_size	    = 2,
+	}, {
+		.fourcc		    = V4L2_PIX_FMT_SGBRG12,
+		.depth		    = 16,
+		.bytesperline_align = 32,
+		.mmal_fmt	    = MMAL_ENCODING_BAYER_SGBRG12,
+		.size_multiplier_x2 = 2,
+		.colorspace_mask    = V4L2_COLORSPACE_MASK_RAW,
+		.colorspace_default = V4L2_COLORSPACE_RAW,
+		.step_size	    = 2,
+	}, {
+		/* 14 bit */
+		.fourcc		    = V4L2_PIX_FMT_SRGGB14,
+		.depth		    = 16,
+		.bytesperline_align = 32,
+		.mmal_fmt	    = MMAL_ENCODING_BAYER_SRGGB14,
+		.size_multiplier_x2 = 2,
+		.colorspace_mask    = V4L2_COLORSPACE_MASK_RAW,
+		.colorspace_default = V4L2_COLORSPACE_RAW,
+		.step_size	    = 2,
+	}, {
+		.fourcc		    = V4L2_PIX_FMT_SBGGR14,
+		.depth		    = 16,
+		.bytesperline_align = 32,
+		.mmal_fmt	    = MMAL_ENCODING_BAYER_SBGGR14,
+		.size_multiplier_x2 = 2,
+		.colorspace_mask    = V4L2_COLORSPACE_MASK_RAW,
+		.colorspace_default = V4L2_COLORSPACE_RAW,
+		.step_size	    = 2,
+	}, {
+		.fourcc		    = V4L2_PIX_FMT_SGRBG14,
+		.depth		    = 16,
+		.bytesperline_align = 32,
+		.mmal_fmt	    = MMAL_ENCODING_BAYER_SGRBG14,
+		.size_multiplier_x2 = 2,
+		.colorspace_mask    = V4L2_COLORSPACE_MASK_RAW,
+		.colorspace_default = V4L2_COLORSPACE_RAW,
+		.step_size	    = 2,
+	}, {
+		.fourcc		    = V4L2_PIX_FMT_SGBRG14,
+		.depth		    = 16,
+		.bytesperline_align = 32,
+		.mmal_fmt	    = MMAL_ENCODING_BAYER_SGBRG14,
+		.size_multiplier_x2 = 2,
+		.colorspace_mask    = V4L2_COLORSPACE_MASK_RAW,
+		.colorspace_default = V4L2_COLORSPACE_RAW,
+		.step_size	    = 2,
+	}, {
+		/* Monochrome MIPI formats */
+		/* 8 bit */
+		.fourcc		    = V4L2_PIX_FMT_GREY,
+		.depth		    = 8,
+		.bytesperline_align = 32,
+		.mmal_fmt	    = MMAL_ENCODING_GREY,
+		.size_multiplier_x2 = 2,
+		.colorspace_mask    = V4L2_COLORSPACE_MASK_RAW,
+		.colorspace_default = V4L2_COLORSPACE_RAW,
+		.step_size	    = 2,
+	}, {
+		/* 10 bit */
+		.fourcc		    = V4L2_PIX_FMT_Y10P,
+		.depth		    = 10,
+		.bytesperline_align = 32,
+		.mmal_fmt	    = MMAL_ENCODING_Y10P,
+		.size_multiplier_x2 = 2,
+		.colorspace_mask    = V4L2_COLORSPACE_MASK_RAW,
+		.colorspace_default = V4L2_COLORSPACE_RAW,
+		.step_size	    = 2,
+	}, {
+		/* 12 bit */
+		.fourcc		    = V4L2_PIX_FMT_Y12P,
+		.depth		    = 12,
+		.bytesperline_align = 32,
+		.mmal_fmt	    = MMAL_ENCODING_Y12P,
+		.size_multiplier_x2 = 2,
+		.colorspace_mask    = V4L2_COLORSPACE_MASK_RAW,
+		.colorspace_default = V4L2_COLORSPACE_RAW,
+		.step_size	    = 2,
+	}, {
+		/* 14 bit */
+		.fourcc		    = V4L2_PIX_FMT_Y14P,
+		.depth		    = 14,
+		.bytesperline_align = 32,
+		.mmal_fmt	    = MMAL_ENCODING_Y14P,
+		.size_multiplier_x2 = 2,
+		.colorspace_mask    = V4L2_COLORSPACE_MASK_RAW,
+		.colorspace_default = V4L2_COLORSPACE_RAW,
+		.step_size	    = 2,
+	}, {
+		/* 16 bit */
+		.fourcc		    = V4L2_PIX_FMT_Y16,
+		.depth		    = 16,
+		.bytesperline_align = 32,
+		.mmal_fmt	    = MMAL_ENCODING_Y16,
+		.size_multiplier_x2 = 2,
+		.colorspace_mask    = V4L2_COLORSPACE_MASK_RAW,
+		.colorspace_default = V4L2_COLORSPACE_RAW,
+		.step_size	    = 2,
+	}, {
+		/* 10 bit as 16bpp */
+		.fourcc		    = V4L2_PIX_FMT_Y10,
+		.depth		    = 16,
+		.bytesperline_align = 32,
+		.mmal_fmt	    = MMAL_ENCODING_Y10,
+		.size_multiplier_x2 = 2,
+		.colorspace_mask    = V4L2_COLORSPACE_MASK_RAW,
+		.colorspace_default = V4L2_COLORSPACE_RAW,
+		.step_size	    = 2,
+	}, {
+		/* 12 bit as 16bpp */
+		.fourcc		    = V4L2_PIX_FMT_Y12,
+		.depth		    = 16,
+		.bytesperline_align = 32,
+		.mmal_fmt	    = MMAL_ENCODING_Y12,
+		.size_multiplier_x2 = 2,
+		.colorspace_mask    = V4L2_COLORSPACE_MASK_RAW,
+		.colorspace_default = V4L2_COLORSPACE_RAW,
+		.step_size	    = 2,
+	}, {
+		/* 14 bit as 16bpp */
+		.fourcc		    = V4L2_PIX_FMT_Y14,
+		.depth		    = 16,
+		.bytesperline_align = 32,
+		.mmal_fmt	    = MMAL_ENCODING_Y14,
+		.size_multiplier_x2 = 2,
+		.colorspace_mask    = V4L2_COLORSPACE_MASK_RAW,
+		.colorspace_default = V4L2_COLORSPACE_RAW,
+		.step_size	    = 2,
+	}, {
+		.fourcc		    = V4L2_META_FMT_BCM2835_ISP_STATS,
+		.mmal_fmt	    = MMAL_ENCODING_BRCM_STATS,
+		/* The rest are not valid fields for stats. */
+	}
+};
+
+#endif
diff -Nur --no-dereference linux-5.10.52-orig/drivers/staging/vc04_services/bcm2835-isp/bcm2835-v4l2-isp.c linux-5.10.52-v7l+/drivers/staging/vc04_services/bcm2835-isp/bcm2835-v4l2-isp.c
--- linux-5.10.52-orig/drivers/staging/vc04_services/bcm2835-isp/bcm2835-v4l2-isp.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/staging/vc04_services/bcm2835-isp/bcm2835-v4l2-isp.c	2021-07-25 16:46:26.537945989 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Broadcom BCM2835 ISP driver
+ *
+ * Copyright © 2019-2020 Raspberry Pi (Trading) Ltd.
+ *
+ * Author: Naushir Patuck (naush@raspberrypi.com)
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-ioctl.h>
+#include <media/videobuf2-dma-contig.h>
+
+#include "vchiq-mmal/mmal-msg.h"
+#include "vchiq-mmal/mmal-parameters.h"
+#include "vchiq-mmal/mmal-vchiq.h"
+
+#include "vc-sm-cma/vc_sm_knl.h"
+
+#include "bcm2835-isp-ctrls.h"
+#include "bcm2835-isp-fmts.h"
+
+static unsigned int debug;
+module_param(debug, uint, 0644);
+MODULE_PARM_DESC(debug, "activates debug info");
+
+static unsigned int video_nr = 13;
+module_param(video_nr, uint, 0644);
+MODULE_PARM_DESC(video_nr, "base video device number");
+
+#define BCM2835_ISP_NAME "bcm2835-isp"
+#define BCM2835_ISP_ENTITY_NAME_LEN 32
+
+#define BCM2835_ISP_NUM_OUTPUTS 1
+#define BCM2835_ISP_NUM_CAPTURES 2
+#define BCM2835_ISP_NUM_METADATA 1
+
+#define BCM2835_ISP_NUM_NODES						\
+		(BCM2835_ISP_NUM_OUTPUTS + BCM2835_ISP_NUM_CAPTURES +	\
+		 BCM2835_ISP_NUM_METADATA)
+
+/* Default frame dimension of 1280 pixels. */
+#define DEFAULT_DIM 1280U
+/*
+ * Maximum frame dimension of 16384 pixels.  Even though the ISP runs in tiles,
+ * have a sensible limit so that we do not create an excessive number of tiles
+ * to process.
+ */
+#define MAX_DIM 16384U
+/*
+ * Minimum frame dimension of 64 pixels.  Anything lower, and the tiling
+ * algorithm may not be able to cope when applying filter context.
+ */
+#define MIN_DIM 64U
+
+/* Timeout for stop_streaming to allow all buffers to return */
+#define COMPLETE_TIMEOUT (2 * HZ)
+
+/* Per-queue, driver-specific private data */
+struct bcm2835_isp_q_data {
+	/*
+	 * These parameters should be treated as gospel, with everything else
+	 * being determined from them.
+	 */
+	unsigned int bytesperline;
+	unsigned int width;
+	unsigned int height;
+	unsigned int sizeimage;
+	enum v4l2_colorspace colorspace;
+	const struct bcm2835_isp_fmt *fmt;
+};
+
+/*
+ * Structure to describe a single node /dev/video<N> which represents a single
+ * input or output queue to the ISP device.
+ */
+struct bcm2835_isp_node {
+	int vfl_dir;
+	unsigned int id;
+	const char *name;
+	struct vchiq_mmal_port *port;
+	struct video_device vfd;
+	struct media_pad pad;
+	struct media_intf_devnode *intf_devnode;
+	struct media_link *intf_link;
+	struct mutex lock; /* top level device node lock */
+	struct mutex queue_lock;
+
+	struct vb2_queue queue;
+	unsigned int sequence;
+
+	/* The list of formats supported on the node. */
+	struct bcm2835_isp_fmt const **supported_fmts;
+	unsigned int num_supported_fmts;
+
+	struct bcm2835_isp_q_data q_data;
+
+	/* Parent device structure */
+	struct bcm2835_isp_dev *dev;
+
+	bool registered;
+	bool media_node_registered;
+};
+
+/*
+ * Structure representing the entire ISP device, comprising several input and
+ * output nodes /dev/video<N>.
+ */
+struct bcm2835_isp_dev {
+	struct v4l2_device v4l2_dev;
+	struct device *dev;
+	struct v4l2_ctrl_handler ctrl_handler;
+	struct media_device mdev;
+	struct media_entity entity;
+	bool media_device_registered;
+	bool media_entity_registered;
+	struct vchiq_mmal_instance *mmal_instance;
+	struct vchiq_mmal_component *component;
+	struct completion frame_cmplt;
+
+	struct bcm2835_isp_node node[BCM2835_ISP_NUM_NODES];
+	struct media_pad pad[BCM2835_ISP_NUM_NODES];
+	atomic_t num_streaming;
+
+	/* Image pipeline controls. */
+	int r_gain;
+	int b_gain;
+};
+
+struct bcm2835_isp_buffer {
+	struct vb2_v4l2_buffer vb;
+	struct mmal_buffer mmal;
+};
+
+static
+inline struct bcm2835_isp_dev *node_get_dev(struct bcm2835_isp_node *node)
+{
+	return node->dev;
+}
+
+static inline bool node_is_output(struct bcm2835_isp_node *node)
+{
+	return node->queue.type == V4L2_BUF_TYPE_VIDEO_OUTPUT;
+}
+
+static inline bool node_is_capture(struct bcm2835_isp_node *node)
+{
+	return node->queue.type == V4L2_BUF_TYPE_VIDEO_CAPTURE;
+}
+
+static inline bool node_is_stats(struct bcm2835_isp_node *node)
+{
+	return node->queue.type == V4L2_BUF_TYPE_META_CAPTURE;
+}
+
+static inline enum v4l2_buf_type index_to_queue_type(int index)
+{
+	if (index < BCM2835_ISP_NUM_OUTPUTS)
+		return V4L2_BUF_TYPE_VIDEO_OUTPUT;
+	else if (index < BCM2835_ISP_NUM_OUTPUTS + BCM2835_ISP_NUM_CAPTURES)
+		return V4L2_BUF_TYPE_VIDEO_CAPTURE;
+	else
+		return V4L2_BUF_TYPE_META_CAPTURE;
+}
+
+static int set_isp_param(struct bcm2835_isp_node *node, u32 parameter,
+			 void *value, u32 value_size)
+{
+	struct bcm2835_isp_dev *dev = node_get_dev(node);
+
+	return vchiq_mmal_port_parameter_set(dev->mmal_instance, node->port,
+					     parameter, value, value_size);
+}
+
+static int set_wb_gains(struct bcm2835_isp_node *node)
+{
+	struct bcm2835_isp_dev *dev = node_get_dev(node);
+	struct mmal_parameter_awbgains gains = {
+		.r_gain = { dev->r_gain, 1000 },
+		.b_gain = { dev->b_gain, 1000 }
+	};
+
+	return set_isp_param(node, MMAL_PARAMETER_CUSTOM_AWB_GAINS,
+			     &gains, sizeof(gains));
+}
+
+static int set_digital_gain(struct bcm2835_isp_node *node, uint32_t gain)
+{
+	struct mmal_parameter_rational digital_gain = {
+		.num = gain,
+		.den = 1000
+	};
+
+	return set_isp_param(node, MMAL_PARAMETER_DIGITAL_GAIN,
+			     &digital_gain, sizeof(digital_gain));
+}
+
+static const struct bcm2835_isp_fmt *get_fmt(u32 mmal_fmt)
+{
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(supported_formats); i++) {
+		if (supported_formats[i].mmal_fmt == mmal_fmt)
+			return &supported_formats[i];
+	}
+	return NULL;
+}
+
+static const
+struct bcm2835_isp_fmt *find_format_by_fourcc(unsigned int fourcc,
+					      struct bcm2835_isp_node *node)
+{
+	const struct bcm2835_isp_fmt *fmt;
+	unsigned int i;
+
+	for (i = 0; i < node->num_supported_fmts; i++) {
+		fmt = node->supported_fmts[i];
+		if (fmt->fourcc == fourcc)
+			return fmt;
+	}
+
+	return NULL;
+}
+
+static const
+struct bcm2835_isp_fmt *find_format(struct v4l2_format *f,
+				    struct bcm2835_isp_node *node)
+{
+	return find_format_by_fourcc(node_is_stats(node) ?
+				     f->fmt.meta.dataformat :
+				     f->fmt.pix.pixelformat,
+				     node);
+}
+
+/* vb2_to_mmal_buffer() - converts vb2 buffer header to MMAL
+ *
+ * Copies all the required fields from a VB2 buffer to the MMAL buffer header,
+ * ready for sending to the VPU.
+ */
+static void vb2_to_mmal_buffer(struct mmal_buffer *buf,
+			       struct vb2_v4l2_buffer *vb2)
+{
+	u64 pts;
+
+	buf->mmal_flags = 0;
+	if (vb2->flags & V4L2_BUF_FLAG_KEYFRAME)
+		buf->mmal_flags |= MMAL_BUFFER_HEADER_FLAG_KEYFRAME;
+
+	/* Data must be framed correctly as one frame per buffer. */
+	buf->mmal_flags |= MMAL_BUFFER_HEADER_FLAG_FRAME_END;
+
+	buf->length = vb2->vb2_buf.planes[0].bytesused;
+	/*
+	 * Minor ambiguity in the V4L2 spec as to whether passing in a 0 length
+	 * buffer, or one with V4L2_BUF_FLAG_LAST set denotes end of stream.
+	 * Handle either.
+	 */
+	if (!buf->length || vb2->flags & V4L2_BUF_FLAG_LAST)
+		buf->mmal_flags |= MMAL_BUFFER_HEADER_FLAG_EOS;
+
+	/* vb2 timestamps in nsecs, mmal in usecs */
+	pts = vb2->vb2_buf.timestamp;
+	do_div(pts, 1000);
+	buf->pts = pts;
+	buf->dts = MMAL_TIME_UNKNOWN;
+}
+
+static void mmal_buffer_cb(struct vchiq_mmal_instance *instance,
+			   struct vchiq_mmal_port *port, int status,
+			   struct mmal_buffer *mmal_buf)
+{
+	struct bcm2835_isp_buffer *q_buf;
+	struct bcm2835_isp_node *node = port->cb_ctx;
+	struct bcm2835_isp_dev *dev = node_get_dev(node);
+	struct vb2_v4l2_buffer *vb2;
+
+	q_buf = container_of(mmal_buf, struct bcm2835_isp_buffer, mmal);
+	vb2 = &q_buf->vb;
+	v4l2_dbg(2, debug, &dev->v4l2_dev,
+		 "%s: port:%s[%d], status:%d, buf:%p, dmabuf:%p, length:%lu, flags %u, pts %lld\n",
+		 __func__, node_is_output(node) ? "input" : "output", node->id,
+		 status, mmal_buf, mmal_buf->dma_buf, mmal_buf->length,
+		 mmal_buf->mmal_flags, mmal_buf->pts);
+
+	if (mmal_buf->cmd)
+		v4l2_err(&dev->v4l2_dev,
+			 "%s: Unexpected event on output callback - %08x\n",
+			 __func__, mmal_buf->cmd);
+
+	if (status) {
+		/* error in transfer */
+		if (vb2) {
+			/* there was a buffer with the error so return it */
+			vb2_buffer_done(&vb2->vb2_buf, VB2_BUF_STATE_ERROR);
+		}
+		return;
+	}
+
+	/* vb2 timestamps in nsecs, mmal in usecs */
+	vb2->vb2_buf.timestamp = mmal_buf->pts * 1000;
+	vb2->sequence = node->sequence++;
+	vb2_set_plane_payload(&vb2->vb2_buf, 0, mmal_buf->length);
+	vb2_buffer_done(&vb2->vb2_buf, VB2_BUF_STATE_DONE);
+
+	if (!port->enabled)
+		complete(&dev->frame_cmplt);
+}
+
+struct colorspace_translation {
+	enum v4l2_colorspace v4l2_value;
+	u32 mmal_value;
+};
+
+static u32 translate_color_space(enum v4l2_colorspace color_space)
+{
+	static const struct colorspace_translation translations[] = {
+		{ V4L2_COLORSPACE_DEFAULT, MMAL_COLOR_SPACE_UNKNOWN },
+		{ V4L2_COLORSPACE_SMPTE170M, MMAL_COLOR_SPACE_ITUR_BT601 },
+		{ V4L2_COLORSPACE_SMPTE240M, MMAL_COLOR_SPACE_SMPTE240M },
+		{ V4L2_COLORSPACE_REC709, MMAL_COLOR_SPACE_ITUR_BT709 },
+		/* V4L2_COLORSPACE_BT878 unavailable */
+		{ V4L2_COLORSPACE_470_SYSTEM_M, MMAL_COLOR_SPACE_BT470_2_M },
+		{ V4L2_COLORSPACE_470_SYSTEM_BG, MMAL_COLOR_SPACE_BT470_2_BG },
+		{ V4L2_COLORSPACE_JPEG, MMAL_COLOR_SPACE_JPEG_JFIF },
+		/*
+		 * We don't have an encoding for SRGB as such, but VideoCore
+		 * will do the right thing if it gets "unknown".
+		 */
+		{ V4L2_COLORSPACE_SRGB, MMAL_COLOR_SPACE_UNKNOWN },
+		/* V4L2_COLORSPACE_OPRGB unavailable */
+		/* V4L2_COLORSPACE_BT2020 unavailable */
+		/* V4L2_COLORSPACE_RAW unavailable */
+		/* V4L2_COLORSPACE_DCI_P3 unavailable */
+	};
+
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(translations); i++) {
+		if (color_space == translations[i].v4l2_value)
+			return translations[i].mmal_value;
+	}
+
+	return MMAL_COLOR_SPACE_UNKNOWN;
+}
+
+static void setup_mmal_port_format(struct bcm2835_isp_node *node,
+				   struct vchiq_mmal_port *port)
+{
+	struct bcm2835_isp_q_data *q_data = &node->q_data;
+
+	port->format.encoding = q_data->fmt->mmal_fmt;
+	/* Raw image format - set width/height */
+	port->es.video.width = (q_data->bytesperline << 3) / q_data->fmt->depth;
+	port->es.video.height = q_data->height;
+	port->es.video.crop.width = q_data->width;
+	port->es.video.crop.height = q_data->height;
+	port->es.video.crop.x = 0;
+	port->es.video.crop.y = 0;
+	port->es.video.color_space = translate_color_space(q_data->colorspace);
+};
+
+static int setup_mmal_port(struct bcm2835_isp_node *node)
+{
+	struct bcm2835_isp_dev *dev = node_get_dev(node);
+	unsigned int enable = 1;
+	int ret;
+
+	v4l2_dbg(2, debug, &dev->v4l2_dev, "%s: setup %s[%d]\n", __func__,
+		 node->name, node->id);
+
+	vchiq_mmal_port_parameter_set(dev->mmal_instance, node->port,
+				      MMAL_PARAMETER_ZERO_COPY, &enable,
+				      sizeof(enable));
+	setup_mmal_port_format(node, node->port);
+	ret = vchiq_mmal_port_set_format(dev->mmal_instance, node->port);
+	if (ret < 0) {
+		v4l2_dbg(1, debug, &dev->v4l2_dev,
+			 "%s: vchiq_mmal_port_set_format failed\n",
+			 __func__);
+		return ret;
+	}
+
+	if (node->q_data.sizeimage < node->port->minimum_buffer.size) {
+		v4l2_err(&dev->v4l2_dev,
+			 "buffer size mismatch sizeimage %u < min size %u\n",
+			 node->q_data.sizeimage,
+			 node->port->minimum_buffer.size);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int bcm2835_isp_mmal_buf_cleanup(struct mmal_buffer *mmal_buf)
+{
+	mmal_vchi_buffer_cleanup(mmal_buf);
+
+	if (mmal_buf->dma_buf) {
+		dma_buf_put(mmal_buf->dma_buf);
+		mmal_buf->dma_buf = NULL;
+	}
+
+	return 0;
+}
+
+static int bcm2835_isp_node_queue_setup(struct vb2_queue *q,
+					unsigned int *nbuffers,
+					unsigned int *nplanes,
+					unsigned int sizes[],
+					struct device *alloc_devs[])
+{
+	struct bcm2835_isp_node *node = vb2_get_drv_priv(q);
+	unsigned int size;
+
+	if (setup_mmal_port(node))
+		return -EINVAL;
+
+	size = node->q_data.sizeimage;
+	if (size == 0) {
+		v4l2_info(&node_get_dev(node)->v4l2_dev,
+			  "%s: Image size unset in queue_setup for node %s[%d]\n",
+			  __func__, node->name, node->id);
+		return -EINVAL;
+	}
+
+	if (*nplanes)
+		return sizes[0] < size ? -EINVAL : 0;
+
+	*nplanes = 1;
+	sizes[0] = size;
+
+	node->port->current_buffer.size = size;
+
+	if (*nbuffers < node->port->minimum_buffer.num)
+		*nbuffers = node->port->minimum_buffer.num;
+
+	node->port->current_buffer.num = *nbuffers;
+
+	v4l2_dbg(2, debug, &node_get_dev(node)->v4l2_dev,
+		 "%s: Image size %u, nbuffers %u for node %s[%d]\n",
+		 __func__, sizes[0], *nbuffers, node->name, node->id);
+	return 0;
+}
+
+static int bcm2835_isp_buf_init(struct vb2_buffer *vb)
+{
+	struct bcm2835_isp_node *node = vb2_get_drv_priv(vb->vb2_queue);
+	struct bcm2835_isp_dev *dev = node_get_dev(node);
+	struct vb2_v4l2_buffer *vb2 = to_vb2_v4l2_buffer(vb);
+	struct bcm2835_isp_buffer *buf =
+		container_of(vb2, struct bcm2835_isp_buffer, vb);
+
+	v4l2_dbg(3, debug, &dev->v4l2_dev, "%s: vb %p\n", __func__, vb);
+
+	buf->mmal.buffer = vb2_plane_vaddr(&buf->vb.vb2_buf, 0);
+	buf->mmal.buffer_size = vb2_plane_size(&buf->vb.vb2_buf, 0);
+	mmal_vchi_buffer_init(dev->mmal_instance, &buf->mmal);
+	return 0;
+}
+
+static int bcm2835_isp_buf_prepare(struct vb2_buffer *vb)
+{
+	struct bcm2835_isp_node *node = vb2_get_drv_priv(vb->vb2_queue);
+	struct bcm2835_isp_dev *dev = node_get_dev(node);
+	struct vb2_v4l2_buffer *vb2 = to_vb2_v4l2_buffer(vb);
+	struct bcm2835_isp_buffer *buf =
+		container_of(vb2, struct bcm2835_isp_buffer, vb);
+	struct dma_buf *dma_buf;
+	int ret;
+
+	v4l2_dbg(3, debug, &dev->v4l2_dev, "%s: type: %d ptr %p\n",
+		 __func__, vb->vb2_queue->type, vb);
+
+	if (V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type)) {
+		if (vb2->field == V4L2_FIELD_ANY)
+			vb2->field = V4L2_FIELD_NONE;
+		if (vb2->field != V4L2_FIELD_NONE) {
+			v4l2_err(&dev->v4l2_dev,
+				 "%s field isn't supported\n", __func__);
+			return -EINVAL;
+		}
+	}
+
+	if (vb2_plane_size(vb, 0) < node->q_data.sizeimage) {
+		v4l2_err(&dev->v4l2_dev,
+			 "%s data will not fit into plane (%lu < %lu)\n",
+			 __func__, vb2_plane_size(vb, 0),
+			 (long)node->q_data.sizeimage);
+		return -EINVAL;
+	}
+
+	if (!V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type))
+		vb2_set_plane_payload(vb, 0, node->q_data.sizeimage);
+
+	switch (vb->memory) {
+	case VB2_MEMORY_DMABUF:
+		dma_buf = dma_buf_get(vb->planes[0].m.fd);
+
+		if (dma_buf != buf->mmal.dma_buf) {
+			/*
+			 * dmabuf either hasn't already been mapped, or it has
+			 * changed.
+			 */
+			if (buf->mmal.dma_buf) {
+				v4l2_err(&dev->v4l2_dev,
+					 "%s Buffer changed - why did the core not call cleanup?\n",
+					 __func__);
+				bcm2835_isp_mmal_buf_cleanup(&buf->mmal);
+			}
+
+			buf->mmal.dma_buf = dma_buf;
+		} else {
+			/*
+			 * Already have a reference to the buffer, so release it
+			 * here.
+			 */
+			dma_buf_put(dma_buf);
+		}
+		ret = 0;
+		break;
+	case VB2_MEMORY_MMAP:
+		/*
+		 * We want to do this at init, but vb2_core_expbuf checks that
+		 * the index < q->num_buffers, and q->num_buffers only gets
+		 * updated once all the buffers are allocated.
+		 */
+		if (!buf->mmal.dma_buf) {
+			ret = vb2_core_expbuf_dmabuf(vb->vb2_queue,
+						     vb->vb2_queue->type,
+						     vb->index, 0, O_CLOEXEC,
+						     &buf->mmal.dma_buf);
+			v4l2_dbg(3, debug, &dev->v4l2_dev,
+				 "%s: exporting ptr %p to dmabuf %p\n",
+				 __func__, vb, buf->mmal.dma_buf);
+			if (ret)
+				v4l2_err(&dev->v4l2_dev,
+					 "%s: Failed to expbuf idx %d, ret %d\n",
+					 __func__, vb->index, ret);
+		} else {
+			ret = 0;
+		}
+		break;
+	default:
+		ret = -EINVAL;
+		break;
+	}
+
+	return ret;
+}
+
+static void bcm2835_isp_node_buffer_queue(struct vb2_buffer *buf)
+{
+	struct bcm2835_isp_node *node = vb2_get_drv_priv(buf->vb2_queue);
+	struct vb2_v4l2_buffer *vbuf =
+		container_of(buf, struct vb2_v4l2_buffer, vb2_buf);
+	struct bcm2835_isp_buffer *buffer =
+		container_of(vbuf, struct bcm2835_isp_buffer, vb);
+	struct bcm2835_isp_dev *dev = node_get_dev(node);
+
+	v4l2_dbg(3, debug, &dev->v4l2_dev, "%s: node %s[%d], buffer %p\n",
+		 __func__, node->name, node->id, buffer);
+
+	vb2_to_mmal_buffer(&buffer->mmal, &buffer->vb);
+	v4l2_dbg(3, debug, &dev->v4l2_dev,
+		 "%s: node %s[%d] - submitting  mmal dmabuf %p\n", __func__,
+		 node->name, node->id, buffer->mmal.dma_buf);
+	vchiq_mmal_submit_buffer(dev->mmal_instance, node->port, &buffer->mmal);
+}
+
+static void bcm2835_isp_buffer_cleanup(struct vb2_buffer *vb)
+{
+	struct vb2_v4l2_buffer *vb2 = to_vb2_v4l2_buffer(vb);
+	struct bcm2835_isp_buffer *buffer =
+		container_of(vb2, struct bcm2835_isp_buffer, vb);
+
+	bcm2835_isp_mmal_buf_cleanup(&buffer->mmal);
+}
+
+static int bcm2835_isp_node_start_streaming(struct vb2_queue *q,
+					    unsigned int count)
+{
+	struct bcm2835_isp_node *node = vb2_get_drv_priv(q);
+	struct bcm2835_isp_dev *dev = node_get_dev(node);
+	int ret;
+
+	v4l2_dbg(1, debug, &dev->v4l2_dev, "%s: node %s[%d] (count %u)\n",
+		 __func__, node->name, node->id, count);
+
+	ret = vchiq_mmal_component_enable(dev->mmal_instance, dev->component);
+	if (ret) {
+		v4l2_err(&dev->v4l2_dev, "%s: Failed enabling component, ret %d\n",
+			 __func__, ret);
+		return -EIO;
+	}
+
+	node->sequence = 0;
+	node->port->cb_ctx = node;
+	ret = vchiq_mmal_port_enable(dev->mmal_instance, node->port,
+				     mmal_buffer_cb);
+	if (!ret)
+		atomic_inc(&dev->num_streaming);
+	else
+		v4l2_err(&dev->v4l2_dev,
+			 "%s: Failed enabling port, ret %d\n", __func__, ret);
+
+	return ret;
+}
+
+static void bcm2835_isp_node_stop_streaming(struct vb2_queue *q)
+{
+	struct bcm2835_isp_node *node = vb2_get_drv_priv(q);
+	struct bcm2835_isp_dev *dev = node_get_dev(node);
+	unsigned int i;
+	int ret;
+
+	v4l2_dbg(1, debug, &dev->v4l2_dev, "%s: node %s[%d], mmal port %p\n",
+		 __func__, node->name, node->id, node->port);
+
+	init_completion(&dev->frame_cmplt);
+
+	/* Disable MMAL port - this will flush buffers back */
+	ret = vchiq_mmal_port_disable(dev->mmal_instance, node->port);
+	if (ret)
+		v4l2_err(&dev->v4l2_dev,
+			 "%s: Failed disabling %s port, ret %d\n", __func__,
+			 node_is_output(node) ? "i/p" : "o/p",
+			 ret);
+
+	while (atomic_read(&node->port->buffers_with_vpu)) {
+		v4l2_dbg(1, debug, &dev->v4l2_dev,
+			 "%s: Waiting for buffers to be returned - %d outstanding\n",
+			 __func__, atomic_read(&node->port->buffers_with_vpu));
+		ret = wait_for_completion_timeout(&dev->frame_cmplt,
+						  COMPLETE_TIMEOUT);
+		if (ret <= 0) {
+			v4l2_err(&dev->v4l2_dev,
+				 "%s: Timeout waiting for buffers to be returned - %d outstanding\n",
+				 __func__,
+				 atomic_read(&node->port->buffers_with_vpu));
+			break;
+		}
+	}
+
+	/* Release the VCSM handle here to release the associated dmabuf */
+	for (i = 0; i < q->num_buffers; i++) {
+		struct vb2_v4l2_buffer *vb2 = to_vb2_v4l2_buffer(q->bufs[i]);
+		struct bcm2835_isp_buffer *buf =
+			container_of(vb2, struct bcm2835_isp_buffer, vb);
+		bcm2835_isp_mmal_buf_cleanup(&buf->mmal);
+	}
+
+	atomic_dec(&dev->num_streaming);
+	/* If all ports disabled, then disable the component */
+	if (atomic_read(&dev->num_streaming) == 0) {
+		ret = vchiq_mmal_component_disable(dev->mmal_instance,
+						   dev->component);
+		if (ret) {
+			v4l2_err(&dev->v4l2_dev,
+				 "%s: Failed disabling component, ret %d\n",
+				 __func__, ret);
+		}
+	}
+
+	/*
+	 * Simply wait for any vb2 buffers to finish. We could take steps to
+	 * make them complete more quickly if we care, or even return them
+	 * ourselves.
+	 */
+	vb2_wait_for_all_buffers(&node->queue);
+}
+
+static const struct vb2_ops bcm2835_isp_node_queue_ops = {
+	.queue_setup		= bcm2835_isp_node_queue_setup,
+	.buf_init		= bcm2835_isp_buf_init,
+	.buf_prepare		= bcm2835_isp_buf_prepare,
+	.buf_queue		= bcm2835_isp_node_buffer_queue,
+	.buf_cleanup		= bcm2835_isp_buffer_cleanup,
+	.start_streaming	= bcm2835_isp_node_start_streaming,
+	.stop_streaming		= bcm2835_isp_node_stop_streaming,
+};
+
+static const
+struct bcm2835_isp_fmt *get_default_format(struct bcm2835_isp_node *node)
+{
+	return node->supported_fmts[0];
+}
+
+static inline unsigned int get_bytesperline(int width,
+					    const struct bcm2835_isp_fmt *fmt)
+{
+	/* GPU aligns 24bpp images to a multiple of 32 pixels (not bytes). */
+	if (fmt->depth == 24)
+		return ALIGN(width, 32) * 3;
+	else
+		return ALIGN((width * fmt->depth) >> 3, fmt->bytesperline_align);
+}
+
+static inline unsigned int get_sizeimage(int bpl, int width, int height,
+					 const struct bcm2835_isp_fmt *fmt)
+{
+	return (bpl * height * fmt->size_multiplier_x2) >> 1;
+}
+
+static int bcm2835_isp_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct bcm2835_isp_dev *dev =
+	      container_of(ctrl->handler, struct bcm2835_isp_dev, ctrl_handler);
+	struct bcm2835_isp_node *node = &dev->node[0];
+	int ret = 0;
+
+	/*
+	 * The ISP firmware driver will ensure these settings are applied on
+	 * a frame boundary, so we are safe to write them as they come in.
+	 *
+	 * Note that the bcm2835_isp_* param structures are identical to the
+	 * mmal-parameters.h definitions.  This avoids the need for unnecessary
+	 * field-by-field copying between structures.
+	 */
+	switch (ctrl->id) {
+	case V4L2_CID_RED_BALANCE:
+		dev->r_gain = ctrl->val;
+		ret = set_wb_gains(node);
+		break;
+	case V4L2_CID_BLUE_BALANCE:
+		dev->b_gain = ctrl->val;
+		ret = set_wb_gains(node);
+		break;
+	case V4L2_CID_DIGITAL_GAIN:
+		ret = set_digital_gain(node, ctrl->val);
+		break;
+	case V4L2_CID_USER_BCM2835_ISP_CC_MATRIX:
+		ret = set_isp_param(node, MMAL_PARAMETER_CUSTOM_CCM,
+				    ctrl->p_new.p_u8,
+				    sizeof(struct bcm2835_isp_custom_ccm));
+		break;
+	case V4L2_CID_USER_BCM2835_ISP_LENS_SHADING:
+	{
+		struct bcm2835_isp_lens_shading *v4l2_ls;
+		struct mmal_parameter_lens_shading_v2 ls;
+		struct dma_buf *dmabuf;
+		void *vcsm_handle;
+
+		v4l2_ls = (struct bcm2835_isp_lens_shading *)ctrl->p_new.p_u8;
+		/*
+		 * struct bcm2835_isp_lens_shading and struct
+		 * mmal_parameter_lens_shading_v2 match so that we can do a
+		 * simple memcpy here.
+		 * Only the dmabuf to the actual table needs any manipulation.
+		 */
+		memcpy(&ls, v4l2_ls, sizeof(ls));
+
+		dmabuf = dma_buf_get(v4l2_ls->dmabuf);
+		if (IS_ERR_OR_NULL(dmabuf))
+			return -EINVAL;
+
+		ret = vc_sm_cma_import_dmabuf(dmabuf, &vcsm_handle);
+		if (ret) {
+			dma_buf_put(dmabuf);
+			return -EINVAL;
+		}
+
+		ls.mem_handle_table = vc_sm_cma_int_handle(vcsm_handle);
+		if (ls.mem_handle_table)
+			/* The VPU will take a reference on the vcsm handle,
+			 * which in turn will retain a reference on the dmabuf.
+			 * This code can therefore safely release all
+			 * references to the buffer.
+			 */
+			ret = set_isp_param(node,
+					    MMAL_PARAMETER_LENS_SHADING_OVERRIDE,
+					    &ls,
+					    sizeof(ls));
+		else
+			ret = -EINVAL;
+
+		vc_sm_cma_free(vcsm_handle);
+		dma_buf_put(dmabuf);
+		break;
+	}
+	case V4L2_CID_USER_BCM2835_ISP_BLACK_LEVEL:
+		ret = set_isp_param(node, MMAL_PARAMETER_BLACK_LEVEL,
+				    ctrl->p_new.p_u8,
+				    sizeof(struct bcm2835_isp_black_level));
+		break;
+	case V4L2_CID_USER_BCM2835_ISP_GEQ:
+		ret = set_isp_param(node, MMAL_PARAMETER_GEQ,
+				    ctrl->p_new.p_u8,
+				    sizeof(struct bcm2835_isp_geq));
+		break;
+	case V4L2_CID_USER_BCM2835_ISP_GAMMA:
+		ret = set_isp_param(node, MMAL_PARAMETER_GAMMA,
+				    ctrl->p_new.p_u8,
+				    sizeof(struct bcm2835_isp_gamma));
+		break;
+	case V4L2_CID_USER_BCM2835_ISP_DENOISE:
+		ret = set_isp_param(node, MMAL_PARAMETER_DENOISE,
+				    ctrl->p_new.p_u8,
+				    sizeof(struct bcm2835_isp_denoise));
+		break;
+	case V4L2_CID_USER_BCM2835_ISP_CDN:
+		ret = set_isp_param(node, MMAL_PARAMETER_CDN,
+				    ctrl->p_new.p_u8,
+				    sizeof(struct bcm2835_isp_cdn));
+		break;
+	case V4L2_CID_USER_BCM2835_ISP_SHARPEN:
+		ret = set_isp_param(node, MMAL_PARAMETER_SHARPEN,
+				    ctrl->p_new.p_u8,
+				    sizeof(struct bcm2835_isp_sharpen));
+		break;
+	case V4L2_CID_USER_BCM2835_ISP_DPC:
+		ret = set_isp_param(node, MMAL_PARAMETER_DPC,
+				    ctrl->p_new.p_u8,
+				    sizeof(struct bcm2835_isp_dpc));
+		break;
+	default:
+		v4l2_info(&dev->v4l2_dev, "Unrecognised control\n");
+		ret = -EINVAL;
+	}
+
+	if (ret) {
+		v4l2_err(&dev->v4l2_dev, "%s: Failed setting ctrl \"%s\" (%08x), err %d\n",
+			 __func__, ctrl->name, ctrl->id, ret);
+		ret = -EIO;
+	}
+
+	return ret;
+}
+
+static const struct v4l2_ctrl_ops bcm2835_isp_ctrl_ops = {
+	.s_ctrl = bcm2835_isp_s_ctrl,
+};
+
+static const struct v4l2_file_operations bcm2835_isp_fops = {
+	.owner		= THIS_MODULE,
+	.open		= v4l2_fh_open,
+	.release	= vb2_fop_release,
+	.poll		= vb2_fop_poll,
+	.unlocked_ioctl = video_ioctl2,
+	.mmap		= vb2_fop_mmap
+};
+
+static int populate_qdata_fmt(struct v4l2_format *f,
+			      struct bcm2835_isp_node *node)
+{
+	struct bcm2835_isp_dev *dev = node_get_dev(node);
+	struct bcm2835_isp_q_data *q_data = &node->q_data;
+	int ret;
+
+	if (!node_is_stats(node)) {
+		v4l2_dbg(1, debug, &dev->v4l2_dev,
+			 "%s: Setting pix format for type %d, wxh: %ux%u, fmt: %08x, size %u\n",
+			 __func__, f->type, f->fmt.pix.width, f->fmt.pix.height,
+			 f->fmt.pix.pixelformat, f->fmt.pix.sizeimage);
+
+		q_data->fmt = find_format(f, node);
+		q_data->width = f->fmt.pix.width;
+		q_data->height = f->fmt.pix.height;
+		q_data->height = f->fmt.pix.height;
+
+		/* All parameters should have been set correctly by try_fmt */
+		q_data->bytesperline = f->fmt.pix.bytesperline;
+		q_data->sizeimage = f->fmt.pix.sizeimage;
+
+		/* We must indicate which of the allowed colour spaces we have. */
+		q_data->colorspace = f->fmt.pix.colorspace;
+	} else {
+		v4l2_dbg(1, debug, &dev->v4l2_dev,
+			 "%s: Setting meta format for fmt: %08x, size %u\n",
+			 __func__, f->fmt.meta.dataformat,
+			 f->fmt.meta.buffersize);
+
+		q_data->fmt = find_format(f, node);
+		q_data->width = 0;
+		q_data->height = 0;
+		q_data->bytesperline = 0;
+		q_data->sizeimage = f->fmt.meta.buffersize;
+
+		/* This won't mean anything for metadata, but may as well fill it in. */
+		q_data->colorspace = V4L2_COLORSPACE_DEFAULT;
+	}
+
+	v4l2_dbg(1, debug, &dev->v4l2_dev,
+		 "%s: Calculated bpl as %u, size %u\n", __func__,
+		 q_data->bytesperline, q_data->sizeimage);
+
+	setup_mmal_port_format(node, node->port);
+	ret = vchiq_mmal_port_set_format(dev->mmal_instance, node->port);
+	if (ret) {
+		v4l2_err(&dev->v4l2_dev,
+			 "%s: Failed vchiq_mmal_port_set_format on port, ret %d\n",
+			 __func__, ret);
+		ret = -EINVAL;
+	}
+
+	if (q_data->sizeimage < node->port->minimum_buffer.size) {
+		v4l2_err(&dev->v4l2_dev,
+			 "%s: Current buffer size of %u < min buf size %u - driver mismatch to MMAL\n",
+			 __func__,
+			 q_data->sizeimage,
+			 node->port->minimum_buffer.size);
+	}
+
+	v4l2_dbg(1, debug, &dev->v4l2_dev,
+		 "%s: Set format for type %d, wxh: %dx%d, fmt: %08x, size %u\n",
+		 __func__, f->type, q_data->width, q_data->height,
+		 q_data->fmt->fourcc, q_data->sizeimage);
+
+	return ret;
+}
+
+static int bcm2835_isp_node_querycap(struct file *file, void *priv,
+				     struct v4l2_capability *cap)
+{
+	strscpy(cap->driver, BCM2835_ISP_NAME, sizeof(cap->driver));
+	strscpy(cap->card, BCM2835_ISP_NAME, sizeof(cap->card));
+	snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s",
+		 BCM2835_ISP_NAME);
+
+	return 0;
+}
+
+static int bcm2835_isp_node_g_fmt(struct file *file, void *priv,
+				  struct v4l2_format *f)
+{
+	struct bcm2835_isp_node *node = video_drvdata(file);
+
+	if (f->type != node->queue.type)
+		return -EINVAL;
+
+	if (node_is_stats(node)) {
+		f->fmt.meta.dataformat = V4L2_META_FMT_BCM2835_ISP_STATS;
+		f->fmt.meta.buffersize =
+			node->port->minimum_buffer.size;
+	} else {
+		struct bcm2835_isp_q_data *q_data = &node->q_data;
+
+		f->fmt.pix.width = q_data->width;
+		f->fmt.pix.height = q_data->height;
+		f->fmt.pix.field = V4L2_FIELD_NONE;
+		f->fmt.pix.pixelformat = q_data->fmt->fourcc;
+		f->fmt.pix.bytesperline = q_data->bytesperline;
+		f->fmt.pix.sizeimage = q_data->sizeimage;
+		f->fmt.pix.colorspace = q_data->colorspace;
+	}
+
+	return 0;
+}
+
+static int bcm2835_isp_node_enum_fmt(struct file *file, void  *priv,
+				     struct v4l2_fmtdesc *f)
+{
+	struct bcm2835_isp_node *node = video_drvdata(file);
+
+	if (f->type != node->queue.type)
+		return -EINVAL;
+
+	if (f->index < node->num_supported_fmts) {
+		/* Format found */
+		f->pixelformat = node->supported_fmts[f->index]->fourcc;
+		f->flags = 0;
+		return 0;
+	}
+
+	return -EINVAL;
+}
+
+static int bcm2835_isp_enum_framesizes(struct file *file, void *priv,
+				       struct v4l2_frmsizeenum *fsize)
+{
+	struct bcm2835_isp_node *node = video_drvdata(file);
+	struct bcm2835_isp_dev *dev = node_get_dev(node);
+	const struct bcm2835_isp_fmt *fmt;
+
+	if (node_is_stats(node) || fsize->index)
+		return -EINVAL;
+
+	fmt = find_format_by_fourcc(fsize->pixel_format, node);
+	if (!fmt) {
+		v4l2_err(&dev->v4l2_dev, "Invalid pixel code: %x\n",
+			 fsize->pixel_format);
+		return -EINVAL;
+	}
+
+	fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE;
+	fsize->stepwise.min_width = MIN_DIM;
+	fsize->stepwise.max_width = MAX_DIM;
+	fsize->stepwise.step_width = fmt->step_size;
+
+	fsize->stepwise.min_height = MIN_DIM;
+	fsize->stepwise.max_height = MAX_DIM;
+	fsize->stepwise.step_height = fmt->step_size;
+
+	return 0;
+}
+
+static int bcm2835_isp_node_try_fmt(struct file *file, void *priv,
+				    struct v4l2_format *f)
+{
+	struct bcm2835_isp_node *node = video_drvdata(file);
+	const struct bcm2835_isp_fmt *fmt;
+
+	if (f->type != node->queue.type)
+		return -EINVAL;
+
+	fmt = find_format(f, node);
+	if (!fmt)
+		fmt = get_default_format(node);
+
+	if (!node_is_stats(node)) {
+		int is_rgb;
+
+		f->fmt.pix.width = max(min(f->fmt.pix.width, MAX_DIM),
+				       MIN_DIM);
+		f->fmt.pix.height = max(min(f->fmt.pix.height, MAX_DIM),
+					MIN_DIM);
+
+		f->fmt.pix.pixelformat = fmt->fourcc;
+
+		/*
+		 * Fill in the actual colour space when the requested one was
+		 * not supported. This also catches the case when the "default"
+		 * colour space was requested (as that's never in the mask).
+		 */
+		if (!(V4L2_COLORSPACE_MASK(f->fmt.pix.colorspace) & fmt->colorspace_mask))
+			f->fmt.pix.colorspace = fmt->colorspace_default;
+		/* In all cases, we only support the defaults for these: */
+		f->fmt.pix.ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(f->fmt.pix.colorspace);
+		f->fmt.pix.xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(f->fmt.pix.colorspace);
+		is_rgb = f->fmt.pix.colorspace == V4L2_COLORSPACE_SRGB;
+		f->fmt.pix.quantization = V4L2_MAP_QUANTIZATION_DEFAULT(is_rgb, f->fmt.pix.colorspace,
+									f->fmt.pix.ycbcr_enc);
+
+		f->fmt.pix.bytesperline = get_bytesperline(f->fmt.pix.width,
+							   fmt);
+		f->fmt.pix.field = V4L2_FIELD_NONE;
+		f->fmt.pix.sizeimage =
+			get_sizeimage(f->fmt.pix.bytesperline, f->fmt.pix.width,
+				      f->fmt.pix.height, fmt);
+	} else {
+		f->fmt.meta.dataformat = fmt->fourcc;
+		f->fmt.meta.buffersize = node->port->minimum_buffer.size;
+	}
+
+	return 0;
+}
+
+static int bcm2835_isp_node_s_fmt(struct file *file, void *priv,
+				  struct v4l2_format *f)
+{
+	struct bcm2835_isp_node *node = video_drvdata(file);
+	int ret;
+
+	if (f->type != node->queue.type)
+		return -EINVAL;
+
+	ret = bcm2835_isp_node_try_fmt(file, priv, f);
+	if (ret)
+		return ret;
+
+	v4l2_dbg(1, debug, &node_get_dev(node)->v4l2_dev,
+		 "%s: Set format for node %s[%d]\n",
+		 __func__, node->name, node->id);
+
+	return populate_qdata_fmt(f, node);
+}
+
+static int bcm2835_isp_node_s_selection(struct file *file, void *fh,
+					struct v4l2_selection *s)
+{
+	struct mmal_parameter_crop crop;
+	struct bcm2835_isp_node *node = video_drvdata(file);
+	struct bcm2835_isp_dev *dev = node_get_dev(node);
+
+	/* This return value is required fro V4L2 compliance. */
+	if (node_is_stats(node))
+		return -ENOTTY;
+
+	if (!s->r.width || !s->r.height)
+		return -EINVAL;
+
+	/* We can only set crop on the input. */
+	switch (s->target) {
+	case V4L2_SEL_TGT_CROP:
+		/*
+		 * Adjust the crop window if it goes outside of the frame
+		 * dimensions.
+		 */
+		s->r.left = min((unsigned int)max(s->r.left, 0),
+				node->q_data.width - MIN_DIM);
+		s->r.top = min((unsigned int)max(s->r.top, 0),
+			       node->q_data.height - MIN_DIM);
+		s->r.width = max(min(s->r.width,
+				     node->q_data.width - s->r.left), MIN_DIM);
+		s->r.height = max(min(s->r.height,
+				      node->q_data.height - s->r.top), MIN_DIM);
+		break;
+	case V4L2_SEL_TGT_CROP_DEFAULT:
+		/* Default (i.e. no) crop window. */
+		s->r.left = 0;
+		s->r.top = 0;
+		s->r.width = node->q_data.width;
+		s->r.height = node->q_data.height;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	crop.rect.x = s->r.left;
+	crop.rect.y = s->r.top;
+	crop.rect.width = s->r.width;
+	crop.rect.height = s->r.height;
+
+	return vchiq_mmal_port_parameter_set(dev->mmal_instance, node->port,
+					     MMAL_PARAMETER_CROP,
+					     &crop, sizeof(crop));
+}
+
+static int bcm2835_isp_node_g_selection(struct file *file, void *fh,
+					struct v4l2_selection *s)
+{
+	struct mmal_parameter_crop crop;
+	struct bcm2835_isp_node *node = video_drvdata(file);
+	struct bcm2835_isp_dev *dev = node_get_dev(node);
+	u32 crop_size = sizeof(crop);
+	int ret;
+
+	/* We can only return out an input crop. */
+	switch (s->target) {
+	case V4L2_SEL_TGT_CROP:
+		ret = vchiq_mmal_port_parameter_get(dev->mmal_instance,
+						    node->port,
+						    MMAL_PARAMETER_CROP,
+						    &crop, &crop_size);
+		if (!ret) {
+			s->r.left = crop.rect.x;
+			s->r.top = crop.rect.y;
+			s->r.width = crop.rect.width;
+			s->r.height = crop.rect.height;
+		}
+		break;
+	case V4L2_SEL_TGT_CROP_DEFAULT:
+	case V4L2_SEL_TGT_CROP_BOUNDS:
+		/* Default (i.e. no) crop window. */
+		s->r.left = 0;
+		s->r.top = 0;
+		s->r.width = node->q_data.width;
+		s->r.height = node->q_data.height;
+		ret = 0;
+		break;
+	default:
+		ret =  -EINVAL;
+	}
+
+	return ret;
+}
+
+static int bcm3285_isp_subscribe_event(struct v4l2_fh *fh,
+				       const struct v4l2_event_subscription *s)
+{
+	switch (s->type) {
+	/* Cannot change source parameters dynamically at runtime. */
+	case V4L2_EVENT_SOURCE_CHANGE:
+		return -EINVAL;
+	case V4L2_EVENT_CTRL:
+		return v4l2_ctrl_subscribe_event(fh, s);
+	default:
+		return v4l2_event_subscribe(fh, s, 4, NULL);
+	}
+}
+
+static const struct v4l2_ioctl_ops bcm2835_isp_node_ioctl_ops = {
+	.vidioc_querycap		= bcm2835_isp_node_querycap,
+	.vidioc_g_fmt_vid_cap		= bcm2835_isp_node_g_fmt,
+	.vidioc_g_fmt_vid_out		= bcm2835_isp_node_g_fmt,
+	.vidioc_g_fmt_meta_cap		= bcm2835_isp_node_g_fmt,
+	.vidioc_s_fmt_vid_cap		= bcm2835_isp_node_s_fmt,
+	.vidioc_s_fmt_vid_out		= bcm2835_isp_node_s_fmt,
+	.vidioc_s_fmt_meta_cap		= bcm2835_isp_node_s_fmt,
+	.vidioc_try_fmt_vid_cap		= bcm2835_isp_node_try_fmt,
+	.vidioc_try_fmt_vid_out		= bcm2835_isp_node_try_fmt,
+	.vidioc_try_fmt_meta_cap	= bcm2835_isp_node_try_fmt,
+	.vidioc_s_selection		= bcm2835_isp_node_s_selection,
+	.vidioc_g_selection		= bcm2835_isp_node_g_selection,
+
+	.vidioc_enum_fmt_vid_cap	= bcm2835_isp_node_enum_fmt,
+	.vidioc_enum_fmt_vid_out	= bcm2835_isp_node_enum_fmt,
+	.vidioc_enum_fmt_meta_cap	= bcm2835_isp_node_enum_fmt,
+	.vidioc_enum_framesizes		= bcm2835_isp_enum_framesizes,
+
+	.vidioc_reqbufs			= vb2_ioctl_reqbufs,
+	.vidioc_querybuf		= vb2_ioctl_querybuf,
+	.vidioc_qbuf			= vb2_ioctl_qbuf,
+	.vidioc_dqbuf			= vb2_ioctl_dqbuf,
+	.vidioc_expbuf			= vb2_ioctl_expbuf,
+	.vidioc_create_bufs		= vb2_ioctl_create_bufs,
+	.vidioc_prepare_buf		= vb2_ioctl_prepare_buf,
+
+	.vidioc_streamon		= vb2_ioctl_streamon,
+	.vidioc_streamoff		= vb2_ioctl_streamoff,
+
+	.vidioc_subscribe_event		= bcm3285_isp_subscribe_event,
+	.vidioc_unsubscribe_event	= v4l2_event_unsubscribe,
+};
+
+/*
+ * Size of the array to provide to the VPU when asking for the list of supported
+ * formats.
+ *
+ * The ISP component currently advertises 62 input formats, so add a small
+ * overhead on that. Should the component advertise more formats then the excess
+ * will be dropped and a warning logged.
+ */
+#define MAX_SUPPORTED_ENCODINGS 70
+
+/* Populate node->supported_fmts with the formats supported by those ports. */
+static int bcm2835_isp_get_supported_fmts(struct bcm2835_isp_node *node)
+{
+	struct bcm2835_isp_dev *dev = node_get_dev(node);
+	struct bcm2835_isp_fmt const **list;
+	unsigned int i, j, num_encodings;
+	u32 fourccs[MAX_SUPPORTED_ENCODINGS];
+	u32 param_size = sizeof(fourccs);
+	int ret;
+
+	ret = vchiq_mmal_port_parameter_get(dev->mmal_instance, node->port,
+					    MMAL_PARAMETER_SUPPORTED_ENCODINGS,
+					    &fourccs, &param_size);
+
+	if (ret) {
+		if (ret == MMAL_MSG_STATUS_ENOSPC) {
+			v4l2_err(&dev->v4l2_dev,
+				 "%s: port has more encodings than we provided space for. Some are dropped (%zu vs %u).\n",
+				 __func__, param_size / sizeof(u32),
+				 MAX_SUPPORTED_ENCODINGS);
+			num_encodings = MAX_SUPPORTED_ENCODINGS;
+		} else {
+			v4l2_err(&dev->v4l2_dev, "%s: get_param ret %u.\n",
+				 __func__, ret);
+			return -EINVAL;
+		}
+	} else {
+		num_encodings = param_size / sizeof(u32);
+	}
+
+	/*
+	 * Assume at this stage that all encodings will be supported in V4L2.
+	 * Any that aren't supported will waste a very small amount of memory.
+	 */
+	list = devm_kzalloc(dev->dev,
+			    sizeof(struct bcm2835_isp_fmt *) * num_encodings,
+			    GFP_KERNEL);
+	if (!list)
+		return -ENOMEM;
+	node->supported_fmts = list;
+
+	for (i = 0, j = 0; i < num_encodings; i++) {
+		const struct bcm2835_isp_fmt *fmt = get_fmt(fourccs[i]);
+
+		if (fmt) {
+			list[j] = fmt;
+			j++;
+		}
+	}
+	node->num_supported_fmts = j;
+
+	return 0;
+}
+
+/*
+ * Register a device node /dev/video<N> to go along with one of the ISP's input
+ * or output nodes.
+ */
+static int register_node(struct bcm2835_isp_dev *dev,
+			 struct bcm2835_isp_node *node,
+			 int index)
+{
+	struct video_device *vfd;
+	struct vb2_queue *queue;
+	int ret;
+
+	mutex_init(&node->lock);
+
+	node->dev = dev;
+	vfd = &node->vfd;
+	queue = &node->queue;
+	queue->type = index_to_queue_type(index);
+	/*
+	 * Setup the node type-specific params.
+	 *
+	 * Only the OUTPUT node can set controls and crop windows. However,
+	 * we must allow the s/g_selection ioctl on the stats node as v4l2
+	 * compliance expects it to return a -ENOTTY, and the framework
+	 * does not handle it if the ioctl is disabled.
+	 */
+	switch (queue->type) {
+	case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+		vfd->device_caps = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING;
+		node->id = index;
+		node->vfl_dir = VFL_DIR_TX;
+		node->name = "output";
+		node->port = &dev->component->input[node->id];
+		break;
+	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+		vfd->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
+		/* First Capture node starts at id 0, etc. */
+		node->id = index - BCM2835_ISP_NUM_OUTPUTS;
+		node->vfl_dir = VFL_DIR_RX;
+		node->name = "capture";
+		node->port = &dev->component->output[node->id];
+		v4l2_disable_ioctl(&node->vfd, VIDIOC_S_CTRL);
+		v4l2_disable_ioctl(&node->vfd, VIDIOC_S_SELECTION);
+		v4l2_disable_ioctl(&node->vfd, VIDIOC_G_SELECTION);
+		break;
+	case V4L2_BUF_TYPE_META_CAPTURE:
+		vfd->device_caps = V4L2_CAP_META_CAPTURE | V4L2_CAP_STREAMING;
+		node->id = index - BCM2835_ISP_NUM_OUTPUTS;
+		node->vfl_dir = VFL_DIR_RX;
+		node->name = "stats";
+		node->port = &dev->component->output[node->id];
+		v4l2_disable_ioctl(&node->vfd, VIDIOC_S_CTRL);
+		v4l2_disable_ioctl(&node->vfd, VIDIOC_S_SELECTION);
+		v4l2_disable_ioctl(&node->vfd, VIDIOC_G_SELECTION);
+		break;
+	}
+
+	/* We use the selection API instead of the old crop API. */
+	v4l2_disable_ioctl(vfd, VIDIOC_CROPCAP);
+	v4l2_disable_ioctl(vfd, VIDIOC_G_CROP);
+	v4l2_disable_ioctl(vfd, VIDIOC_S_CROP);
+
+	ret = bcm2835_isp_get_supported_fmts(node);
+	if (ret)
+		return ret;
+
+	/* Initialise the video node. */
+	vfd->vfl_type	= VFL_TYPE_VIDEO;
+	vfd->fops	= &bcm2835_isp_fops,
+	vfd->ioctl_ops	= &bcm2835_isp_node_ioctl_ops,
+	vfd->minor	= -1,
+	vfd->release	= video_device_release_empty,
+	vfd->queue	= &node->queue;
+	vfd->lock	= &node->lock;
+	vfd->v4l2_dev	= &dev->v4l2_dev;
+	vfd->vfl_dir	= node->vfl_dir;
+
+	node->q_data.fmt = get_default_format(node);
+	node->q_data.width = DEFAULT_DIM;
+	node->q_data.height = DEFAULT_DIM;
+	node->q_data.bytesperline =
+		get_bytesperline(DEFAULT_DIM, node->q_data.fmt);
+	node->q_data.sizeimage = node_is_stats(node) ?
+				 node->port->recommended_buffer.size :
+				 get_sizeimage(node->q_data.bytesperline,
+					       node->q_data.width,
+					       node->q_data.height,
+					       node->q_data.fmt);
+	node->q_data.colorspace = node->q_data.fmt->colorspace_default;
+
+	queue->io_modes = VB2_MMAP | VB2_DMABUF;
+	queue->drv_priv = node;
+	queue->ops = &bcm2835_isp_node_queue_ops;
+	queue->mem_ops = &vb2_dma_contig_memops;
+	queue->buf_struct_size = sizeof(struct bcm2835_isp_buffer);
+	queue->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+	queue->dev = dev->dev;
+	queue->lock = &node->queue_lock;
+
+	ret = vb2_queue_init(queue);
+	if (ret < 0) {
+		v4l2_info(&dev->v4l2_dev, "vb2_queue_init failed\n");
+		return ret;
+	}
+
+	/* Set some controls and defaults, but only on the VIDEO_OUTPUT node. */
+	if (node_is_output(node)) {
+		unsigned int i;
+
+		/* Use this ctrl template to assign custom ISP ctrls. */
+		struct v4l2_ctrl_config ctrl_template = {
+			.ops		= &bcm2835_isp_ctrl_ops,
+			.type		= V4L2_CTRL_TYPE_U8,
+			.def		= 0,
+			.min		= 0x00,
+			.max		= 0xff,
+			.step		= 1,
+		};
+
+		/* 3 standard controls, and an array of custom controls */
+		ret = v4l2_ctrl_handler_init(&dev->ctrl_handler,
+					     3 + ARRAY_SIZE(custom_ctrls));
+		if (ret) {
+			v4l2_err(&dev->v4l2_dev, "ctrl_handler init failed (%d)\n",
+				 ret);
+			goto queue_cleanup;
+		}
+
+		dev->r_gain = 1000;
+		dev->b_gain = 1000;
+
+		v4l2_ctrl_new_std(&dev->ctrl_handler,  &bcm2835_isp_ctrl_ops,
+				  V4L2_CID_RED_BALANCE, 1, 0xffff, 1,
+				  dev->r_gain);
+
+		v4l2_ctrl_new_std(&dev->ctrl_handler, &bcm2835_isp_ctrl_ops,
+				  V4L2_CID_BLUE_BALANCE, 1, 0xffff, 1,
+				  dev->b_gain);
+
+		v4l2_ctrl_new_std(&dev->ctrl_handler, &bcm2835_isp_ctrl_ops,
+				  V4L2_CID_DIGITAL_GAIN, 1, 0xffff, 1, 1000);
+
+		for (i = 0; i < ARRAY_SIZE(custom_ctrls); i++) {
+			ctrl_template.name = custom_ctrls[i].name;
+			ctrl_template.id = custom_ctrls[i].id;
+			ctrl_template.dims[0] = custom_ctrls[i].size;
+			ctrl_template.flags = custom_ctrls[i].flags;
+			v4l2_ctrl_new_custom(&dev->ctrl_handler,
+					     &ctrl_template, NULL);
+		}
+
+		node->vfd.ctrl_handler = &dev->ctrl_handler;
+		if (dev->ctrl_handler.error) {
+			ret = dev->ctrl_handler.error;
+			v4l2_err(&dev->v4l2_dev, "controls init failed (%d)\n",
+				 ret);
+			v4l2_ctrl_handler_free(&dev->ctrl_handler);
+			goto ctrl_cleanup;
+		}
+	}
+
+	/* Define the device names */
+	snprintf(vfd->name, sizeof(node->vfd.name), "%s-%s%d", BCM2835_ISP_NAME,
+		 node->name, node->id);
+
+	ret = video_register_device(vfd, VFL_TYPE_VIDEO, video_nr + index);
+	if (ret) {
+		v4l2_err(&dev->v4l2_dev,
+			 "Failed to register video %s[%d] device node\n",
+			 node->name, node->id);
+		goto ctrl_cleanup;
+	}
+
+	node->registered = true;
+	video_set_drvdata(vfd, node);
+
+	v4l2_info(&dev->v4l2_dev,
+		  "Device node %s[%d] registered as /dev/video%d\n",
+		  node->name, node->id, vfd->num);
+
+	return 0;
+
+ctrl_cleanup:
+	if (node_is_output(node))
+		v4l2_ctrl_handler_free(&dev->ctrl_handler);
+queue_cleanup:
+	vb2_queue_release(&node->queue);
+	return ret;
+}
+
+/* Unregister one of the /dev/video<N> nodes associated with the ISP. */
+static void unregister_node(struct bcm2835_isp_node *node)
+{
+	struct bcm2835_isp_dev *dev = node_get_dev(node);
+
+	v4l2_info(&dev->v4l2_dev,
+		  "Unregistering node %s[%d] device node /dev/video%d\n",
+		  node->name, node->id, node->vfd.num);
+
+	if (node->registered) {
+		video_unregister_device(&node->vfd);
+		if (node_is_output(node))
+			v4l2_ctrl_handler_free(&dev->ctrl_handler);
+		vb2_queue_release(&node->queue);
+	}
+
+	/*
+	 * node->supported_fmts.list is free'd automatically
+	 * as a managed resource.
+	 */
+	node->supported_fmts = NULL;
+	node->num_supported_fmts = 0;
+	node->vfd.ctrl_handler = NULL;
+	node->registered = false;
+}
+
+static void media_controller_unregister(struct bcm2835_isp_dev *dev)
+{
+	unsigned int i;
+
+	v4l2_info(&dev->v4l2_dev, "Unregister from media controller\n");
+
+	if (dev->media_device_registered) {
+		media_device_unregister(&dev->mdev);
+		media_device_cleanup(&dev->mdev);
+		dev->media_device_registered = false;
+	}
+
+	kfree(dev->entity.name);
+	dev->entity.name = NULL;
+
+	if (dev->media_entity_registered) {
+		media_device_unregister_entity(&dev->entity);
+		dev->media_entity_registered = false;
+	}
+
+	for (i = 0; i < BCM2835_ISP_NUM_NODES; i++) {
+		struct bcm2835_isp_node *node = &dev->node[i];
+
+		if (node->media_node_registered) {
+			media_remove_intf_links(node->intf_link->intf);
+			media_entity_remove_links(&dev->node[i].vfd.entity);
+			media_devnode_remove(node->intf_devnode);
+			media_device_unregister_entity(&node->vfd.entity);
+			kfree(node->vfd.entity.name);
+		}
+		node->media_node_registered = false;
+	}
+
+	dev->v4l2_dev.mdev = NULL;
+}
+
+static int media_controller_register_node(struct bcm2835_isp_dev *dev, int num)
+{
+	struct bcm2835_isp_node *node = &dev->node[num];
+	struct media_entity *entity = &node->vfd.entity;
+	int output = node_is_output(node);
+	char *name;
+	int ret;
+
+	v4l2_info(&dev->v4l2_dev,
+		  "Register %s node %d with media controller\n",
+		  output ? "output" : "capture", num);
+	entity->obj_type = MEDIA_ENTITY_TYPE_VIDEO_DEVICE;
+	entity->function = MEDIA_ENT_F_IO_V4L;
+	entity->info.dev.major = VIDEO_MAJOR;
+	entity->info.dev.minor = node->vfd.minor;
+	name = kmalloc(BCM2835_ISP_ENTITY_NAME_LEN, GFP_KERNEL);
+	if (!name) {
+		ret = -ENOMEM;
+		goto error_no_mem;
+	}
+	snprintf(name, BCM2835_ISP_ENTITY_NAME_LEN, "%s0-%s%d",
+		 BCM2835_ISP_NAME, output ? "output" : "capture", num);
+	entity->name = name;
+	node->pad.flags = output ? MEDIA_PAD_FL_SOURCE : MEDIA_PAD_FL_SINK;
+	ret = media_entity_pads_init(entity, 1, &node->pad);
+	if (ret)
+		goto error_pads_init;
+	ret = media_device_register_entity(&dev->mdev, entity);
+	if (ret)
+		goto error_register_entity;
+
+	node->intf_devnode = media_devnode_create(&dev->mdev,
+						  MEDIA_INTF_T_V4L_VIDEO, 0,
+						  VIDEO_MAJOR, node->vfd.minor);
+	if (!node->intf_devnode) {
+		ret = -ENOMEM;
+		goto error_devnode_create;
+	}
+
+	node->intf_link = media_create_intf_link(entity,
+						 &node->intf_devnode->intf,
+						 MEDIA_LNK_FL_IMMUTABLE |
+						 MEDIA_LNK_FL_ENABLED);
+	if (!node->intf_link) {
+		ret = -ENOMEM;
+		goto error_create_intf_link;
+	}
+
+	if (output)
+		ret = media_create_pad_link(entity, 0, &dev->entity, num,
+					    MEDIA_LNK_FL_IMMUTABLE |
+						    MEDIA_LNK_FL_ENABLED);
+	else
+		ret = media_create_pad_link(&dev->entity, num, entity, 0,
+					    MEDIA_LNK_FL_IMMUTABLE |
+					    MEDIA_LNK_FL_ENABLED);
+	if (ret)
+		goto error_create_pad_link;
+
+	dev->node[num].media_node_registered = true;
+	return 0;
+
+error_create_pad_link:
+	media_remove_intf_links(&node->intf_devnode->intf);
+error_create_intf_link:
+	media_devnode_remove(node->intf_devnode);
+error_devnode_create:
+	media_device_unregister_entity(&node->vfd.entity);
+error_register_entity:
+error_pads_init:
+	kfree(entity->name);
+	entity->name = NULL;
+error_no_mem:
+	if (ret)
+		v4l2_info(&dev->v4l2_dev, "Error registering node\n");
+
+	return ret;
+}
+
+static int media_controller_register(struct bcm2835_isp_dev *dev)
+{
+	char *name;
+	unsigned int i;
+	int ret;
+
+	v4l2_dbg(2, debug, &dev->v4l2_dev, "Registering with media controller\n");
+	dev->mdev.dev = dev->dev;
+	strscpy(dev->mdev.model, "bcm2835-isp",
+		sizeof(dev->mdev.model));
+	strscpy(dev->mdev.bus_info, "platform:bcm2835-isp",
+		sizeof(dev->mdev.bus_info));
+	media_device_init(&dev->mdev);
+	dev->v4l2_dev.mdev = &dev->mdev;
+
+	v4l2_dbg(2, debug, &dev->v4l2_dev, "Register entity for nodes\n");
+
+	name = kmalloc(BCM2835_ISP_ENTITY_NAME_LEN, GFP_KERNEL);
+	if (!name) {
+		ret = -ENOMEM;
+		goto done;
+	}
+	snprintf(name, BCM2835_ISP_ENTITY_NAME_LEN, "bcm2835_isp0");
+	dev->entity.name = name;
+	dev->entity.obj_type = MEDIA_ENTITY_TYPE_BASE;
+	dev->entity.function = MEDIA_ENT_F_PROC_VIDEO_SCALER;
+
+	for (i = 0; i < BCM2835_ISP_NUM_NODES; i++) {
+		dev->pad[i].flags = node_is_output(&dev->node[i]) ?
+					MEDIA_PAD_FL_SINK : MEDIA_PAD_FL_SOURCE;
+	}
+
+	ret = media_entity_pads_init(&dev->entity, BCM2835_ISP_NUM_NODES,
+				     dev->pad);
+	if (ret)
+		goto done;
+
+	ret = media_device_register_entity(&dev->mdev, &dev->entity);
+	if (ret)
+		goto done;
+
+	dev->media_entity_registered = true;
+	for (i = 0; i < BCM2835_ISP_NUM_NODES; i++) {
+		ret = media_controller_register_node(dev, i);
+		if (ret)
+			goto done;
+	}
+
+	ret = media_device_register(&dev->mdev);
+	if (!ret)
+		dev->media_device_registered = true;
+done:
+	return ret;
+}
+
+static int bcm2835_isp_remove(struct platform_device *pdev)
+{
+	struct bcm2835_isp_dev *dev = platform_get_drvdata(pdev);
+	unsigned int i;
+
+	media_controller_unregister(dev);
+
+	for (i = 0; i < BCM2835_ISP_NUM_NODES; i++)
+		unregister_node(&dev->node[i]);
+
+	v4l2_device_unregister(&dev->v4l2_dev);
+
+	if (dev->component)
+		vchiq_mmal_component_finalise(dev->mmal_instance,
+					      dev->component);
+
+	vchiq_mmal_finalise(dev->mmal_instance);
+
+	return 0;
+}
+
+static int bcm2835_isp_probe(struct platform_device *pdev)
+{
+	struct bcm2835_isp_dev *dev;
+	unsigned int i;
+	int ret;
+
+	dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
+	if (!dev)
+		return -ENOMEM;
+
+	dev->dev = &pdev->dev;
+
+	ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev);
+	if (ret)
+		return ret;
+
+	ret = vchiq_mmal_init(&dev->mmal_instance);
+	if (ret) {
+		v4l2_device_unregister(&dev->v4l2_dev);
+		return ret;
+	}
+
+	ret = vchiq_mmal_component_init(dev->mmal_instance, "ril.isp",
+					&dev->component);
+	if (ret) {
+		v4l2_err(&dev->v4l2_dev,
+			 "%s: failed to create ril.isp component\n", __func__);
+		goto error;
+	}
+
+	if (dev->component->inputs < BCM2835_ISP_NUM_OUTPUTS ||
+	    dev->component->outputs < BCM2835_ISP_NUM_CAPTURES +
+					BCM2835_ISP_NUM_METADATA) {
+		v4l2_err(&dev->v4l2_dev,
+			 "%s: ril.isp returned %d i/p (%d expected), %d o/p (%d expected) ports\n",
+			  __func__, dev->component->inputs,
+			  BCM2835_ISP_NUM_OUTPUTS,
+			  dev->component->outputs,
+			  BCM2835_ISP_NUM_CAPTURES + BCM2835_ISP_NUM_METADATA);
+		goto error;
+	}
+
+	atomic_set(&dev->num_streaming, 0);
+
+	for (i = 0; i < BCM2835_ISP_NUM_NODES; i++) {
+		struct bcm2835_isp_node *node = &dev->node[i];
+
+		ret = register_node(dev, node, i);
+		if (ret)
+			goto error;
+	}
+
+	ret = media_controller_register(dev);
+	if (ret)
+		goto error;
+
+	platform_set_drvdata(pdev, dev);
+	v4l2_info(&dev->v4l2_dev, "Loaded V4L2 %s\n", BCM2835_ISP_NAME);
+	return 0;
+
+error:
+	bcm2835_isp_remove(pdev);
+
+	return ret;
+}
+
+static struct platform_driver bcm2835_isp_pdrv = {
+	.probe = bcm2835_isp_probe,
+	.remove = bcm2835_isp_remove,
+	.driver = {
+			.name = BCM2835_ISP_NAME,
+		  },
+};
+
+module_platform_driver(bcm2835_isp_pdrv);
+
+MODULE_DESCRIPTION("BCM2835 ISP driver");
+MODULE_AUTHOR("Naushir Patuck <naush@raspberrypi.com>");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("1.0");
+MODULE_ALIAS("platform:bcm2835-isp");
diff -Nur --no-dereference linux-5.10.52-orig/drivers/staging/vc04_services/bcm2835-isp/Kconfig linux-5.10.52-v7l+/drivers/staging/vc04_services/bcm2835-isp/Kconfig
--- linux-5.10.52-orig/drivers/staging/vc04_services/bcm2835-isp/Kconfig	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/staging/vc04_services/bcm2835-isp/Kconfig	2021-07-25 16:46:26.537945989 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+config VIDEO_ISP_BCM2835
+	tristate "BCM2835 ISP support"
+	depends on MEDIA_SUPPORT
+	depends on VIDEO_V4L2 && (ARCH_BCM2835 || COMPILE_TEST)
+	depends on MEDIA_CONTROLLER
+	select BCM2835_VCHIQ_MMAL
+	select VIDEOBUF2_DMA_CONTIG
+	help
+	  This is the V4L2 driver for the Broadcom BCM2835 ISP hardware.
+	  This operates over the VCHIQ interface to a service running on
+	  VideoCore.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called bcm2835-isp.
diff -Nur --no-dereference linux-5.10.52-orig/drivers/staging/vc04_services/bcm2835-isp/Makefile linux-5.10.52-v7l+/drivers/staging/vc04_services/bcm2835-isp/Makefile
--- linux-5.10.52-orig/drivers/staging/vc04_services/bcm2835-isp/Makefile	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/staging/vc04_services/bcm2835-isp/Makefile	2021-07-25 16:46:26.537945989 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+# SPDX-License-Identifier: GPL-2.0
+bcm2835-isp-objs := bcm2835-v4l2-isp.o
+
+obj-$(CONFIG_VIDEO_ISP_BCM2835) += bcm2835-isp.o
+
+ccflags-y += \
+	-I$(srctree)/drivers/staging/vc04_services \
+	-D__VCCOREVER__=0x04000000
diff -Nur --no-dereference linux-5.10.52-orig/drivers/staging/vc04_services/include/linux/broadcom/vc_sm_cma_ioctl.h linux-5.10.52-v7l+/drivers/staging/vc04_services/include/linux/broadcom/vc_sm_cma_ioctl.h
--- linux-5.10.52-orig/drivers/staging/vc04_services/include/linux/broadcom/vc_sm_cma_ioctl.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/staging/vc04_services/include/linux/broadcom/vc_sm_cma_ioctl.h	2021-07-25 16:46:26.537945989 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/* SPDX-License-Identifier: GPL-2.0 */
+
+/*
+ * Copyright 2019 Raspberry Pi (Trading) Ltd.  All rights reserved.
+ *
+ * Based on vmcs_sm_ioctl.h Copyright Broadcom Corporation.
+ */
+
+#ifndef __VC_SM_CMA_IOCTL_H
+#define __VC_SM_CMA_IOCTL_H
+
+/* ---- Include Files ---------------------------------------------------- */
+
+#if defined(__KERNEL__)
+#include <linux/types.h>	/* Needed for standard types */
+#else
+#include <stdint.h>
+#endif
+
+#include <linux/ioctl.h>
+
+/* ---- Constants and Types ---------------------------------------------- */
+
+#define VC_SM_CMA_RESOURCE_NAME               32
+#define VC_SM_CMA_RESOURCE_NAME_DEFAULT       "sm-host-resource"
+
+/* Type define used to create unique IOCTL number */
+#define VC_SM_CMA_MAGIC_TYPE                  'J'
+
+/* IOCTL commands on /dev/vc-sm-cma */
+enum vc_sm_cma_cmd_e {
+	VC_SM_CMA_CMD_ALLOC = 0x5A,	/* Start at 0x5A arbitrarily */
+
+	VC_SM_CMA_CMD_IMPORT_DMABUF,
+
+	VC_SM_CMA_CMD_CLEAN_INVALID2,
+
+	VC_SM_CMA_CMD_LAST	/* Do not delete */
+};
+
+/* Cache type supported, conveniently matches the user space definition in
+ * user-vcsm.h.
+ */
+enum vc_sm_cma_cache_e {
+	VC_SM_CMA_CACHE_NONE,
+	VC_SM_CMA_CACHE_HOST,
+	VC_SM_CMA_CACHE_VC,
+	VC_SM_CMA_CACHE_BOTH,
+};
+
+/* IOCTL Data structures */
+struct vc_sm_cma_ioctl_alloc {
+	/* user -> kernel */
+	__u32 size;
+	__u32 num;
+	__u32 cached;		/* enum vc_sm_cma_cache_e */
+	__u32 pad;
+	__u8 name[VC_SM_CMA_RESOURCE_NAME];
+
+	/* kernel -> user */
+	__s32 handle;
+	__u32 vc_handle;
+	__u64 dma_addr;
+};
+
+struct vc_sm_cma_ioctl_import_dmabuf {
+	/* user -> kernel */
+	__s32 dmabuf_fd;
+	__u32 cached;		/* enum vc_sm_cma_cache_e */
+	__u8 name[VC_SM_CMA_RESOURCE_NAME];
+
+	/* kernel -> user */
+	__s32 handle;
+	__u32 vc_handle;
+	__u32 size;
+	__u32 pad;
+	__u64 dma_addr;
+};
+
+/*
+ * Cache functions to be set to struct vc_sm_cma_ioctl_clean_invalid2
+ * invalidate_mode.
+ */
+#define VC_SM_CACHE_OP_NOP       0x00
+#define VC_SM_CACHE_OP_INV       0x01
+#define VC_SM_CACHE_OP_CLEAN     0x02
+#define VC_SM_CACHE_OP_FLUSH     0x03
+
+struct vc_sm_cma_ioctl_clean_invalid2 {
+	__u32 op_count;
+	__u32 pad;
+	struct vc_sm_cma_ioctl_clean_invalid_block {
+		__u32 invalidate_mode;
+		__u32 block_count;
+		void *  __user start_address;
+		__u32 block_size;
+		__u32 inter_block_stride;
+	} s[0];
+};
+
+/* IOCTL numbers */
+#define VC_SM_CMA_IOCTL_MEM_ALLOC\
+	_IOR(VC_SM_CMA_MAGIC_TYPE, VC_SM_CMA_CMD_ALLOC,\
+	 struct vc_sm_cma_ioctl_alloc)
+
+#define VC_SM_CMA_IOCTL_MEM_IMPORT_DMABUF\
+	_IOR(VC_SM_CMA_MAGIC_TYPE, VC_SM_CMA_CMD_IMPORT_DMABUF,\
+	 struct vc_sm_cma_ioctl_import_dmabuf)
+
+#define VC_SM_CMA_IOCTL_MEM_CLEAN_INVALID2\
+	_IOR(VC_SM_CMA_MAGIC_TYPE, VC_SM_CMA_CMD_CLEAN_INVALID2,\
+	 struct vc_sm_cma_ioctl_clean_invalid2)
+
+#endif /* __VC_SM_CMA_IOCTL_H */
diff -Nur --no-dereference linux-5.10.52-orig/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c linux-5.10.52-v7l+/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c
--- linux-5.10.52-orig/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c	2021-07-25 16:46:26.537945989 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:10 @
 #include <linux/interrupt.h>
 #include <linux/pagemap.h>
 #include <linux/dma-mapping.h>
+#include <linux/dmapool.h>
 #include <linux/io.h>
 #include <linux/platform_device.h>
 #include <linux/uaccess.h>
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:33 @
 #define BELL0	0x00
 #define BELL2	0x08
 
+#define VCHIQ_DMA_POOL_SIZE PAGE_SIZE
+
 struct vchiq_2835_state {
 	int inited;
 	struct vchiq_arm_state arm_state;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:44 @
 	struct pagelist *pagelist;
 	size_t pagelist_buffer_size;
 	dma_addr_t dma_addr;
+	bool is_from_pool;
 	enum dma_data_direction dma_dir;
 	unsigned int num_pages;
 	unsigned int pages_need_release;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:65 @
  * of 32.
  */
 static unsigned int g_cache_line_size = 32;
+static struct dma_pool *g_dma_pool;
+static unsigned int g_use_36bit_addrs = 0;
 static unsigned int g_fragments_size;
 static char *g_fragments_base;
 static char *g_free_fragments;
 static struct semaphore g_free_fragments_sema;
 static struct device *g_dev;
+static struct device *g_dma_dev;
 
 static DEFINE_SEMAPHORE(g_free_fragments_mutex);
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:89 @
 int vchiq_platform_init(struct platform_device *pdev, struct vchiq_state *state)
 {
 	struct device *dev = &pdev->dev;
+	struct device *dma_dev = NULL;
 	struct vchiq_drvdata *drvdata = platform_get_drvdata(pdev);
 	struct rpi_firmware *fw = drvdata->fw;
 	struct vchiq_slot_zero *vchiq_slot_zero;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:111 @
 	g_cache_line_size = drvdata->cache_line_size;
 	g_fragments_size = 2 * g_cache_line_size;
 
+	if (drvdata->use_36bit_addrs) {
+		struct device_node *dma_node =
+			of_find_compatible_node(NULL, NULL, "brcm,bcm2711-dma");
+
+		if (dma_node) {
+			struct platform_device *pdev;
+
+			pdev = of_find_device_by_node(dma_node);
+			if (pdev)
+				dma_dev = &pdev->dev;
+			of_node_put(dma_node);
+			g_use_36bit_addrs = true;
+		} else {
+			dev_err(dev, "40-bit DMA controller not found\n");
+			return -EINVAL;
+		}
+	}
+
 	/* Allocate space for the channels in coherent memory */
 	slot_mem_size = PAGE_ALIGN(TOTAL_SLOTS * VCHIQ_SLOT_SIZE);
 	frag_mem_size = PAGE_ALIGN(g_fragments_size * MAX_FRAGMENTS);
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:141 @
 	}
 
 	WARN_ON(((unsigned long)slot_mem & (PAGE_SIZE - 1)) != 0);
+	channelbase = slot_phys;
 
 	vchiq_slot_zero = vchiq_init_slots(slot_mem, slot_mem_size);
 	if (!vchiq_slot_zero)
 		return -EINVAL;
 
 	vchiq_slot_zero->platform_data[VCHIQ_PLATFORM_FRAGMENTS_OFFSET_IDX] =
-		(int)slot_phys + slot_mem_size;
+		channelbase + slot_mem_size;
 	vchiq_slot_zero->platform_data[VCHIQ_PLATFORM_FRAGMENTS_COUNT_IDX] =
 		MAX_FRAGMENTS;
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:181 @
 	}
 
 	/* Send the base address of the slots to VideoCore */
-	channelbase = slot_phys;
 	err = rpi_firmware_property(fw, RPI_FIRMWARE_VCHIQ_INIT,
 				    &channelbase, sizeof(channelbase));
 	if (err || channelbase) {
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:189 @
 	}
 
 	g_dev = dev;
+	g_dma_dev = dma_dev ?: dev;
+	g_dma_pool = dmam_pool_create("vchiq_scatter_pool", dev,
+				      VCHIQ_DMA_POOL_SIZE, g_cache_line_size,
+				      0);
+	if (!g_dma_pool) {
+		dev_err(dev, "failed to create dma pool");
+		return -ENOMEM;
+	}
+
 	vchiq_log_info(vchiq_arm_log_level,
 		"vchiq_init - done (slots %pK, phys %pad)",
 		vchiq_slot_zero, &slot_phys);
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:322 @
 cleanup_pagelistinfo(struct vchiq_pagelist_info *pagelistinfo)
 {
 	if (pagelistinfo->scatterlist_mapped) {
-		dma_unmap_sg(g_dev, pagelistinfo->scatterlist,
+		dma_unmap_sg(g_dma_dev, pagelistinfo->scatterlist,
 			     pagelistinfo->num_pages, pagelistinfo->dma_dir);
 	}
 
 	if (pagelistinfo->pages_need_release)
 		unpin_user_pages(pagelistinfo->pages, pagelistinfo->num_pages);
 
-	dma_free_coherent(g_dev, pagelistinfo->pagelist_buffer_size,
-			  pagelistinfo->pagelist, pagelistinfo->dma_addr);
+	if (pagelistinfo->is_from_pool) {
+		dma_pool_free(g_dma_pool, pagelistinfo->pagelist,
+			      pagelistinfo->dma_addr);
+	} else {
+		dma_free_coherent(g_dev, pagelistinfo->pagelist_buffer_size,
+				  pagelistinfo->pagelist,
+				  pagelistinfo->dma_addr);
+	}
 }
 
 /* There is a potential problem with partial cache lines (pages?)
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:357 @
 	u32 *addrs;
 	unsigned int num_pages, offset, i, k;
 	int actual_pages;
+	bool is_from_pool;
 	size_t pagelist_size;
 	struct scatterlist *scatterlist, *sg;
 	int dma_buffers;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:387 @
 	/* Allocate enough storage to hold the page pointers and the page
 	 * list
 	 */
-	pagelist = dma_alloc_coherent(g_dev, pagelist_size, &dma_addr,
-				      GFP_KERNEL);
+	if (pagelist_size > VCHIQ_DMA_POOL_SIZE) {
+		pagelist = dma_alloc_coherent(g_dev,
+					       pagelist_size,
+					       &dma_addr,
+					       GFP_KERNEL);
+		is_from_pool = false;
+	} else {
+		pagelist = dma_pool_alloc(g_dma_pool, GFP_KERNEL, &dma_addr);
+		is_from_pool = true;
+	}
 
 	vchiq_log_trace(vchiq_arm_log_level, "%s - %pK", __func__, pagelist);
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:417 @
 	pagelistinfo->pagelist = pagelist;
 	pagelistinfo->pagelist_buffer_size = pagelist_size;
 	pagelistinfo->dma_addr = dma_addr;
+	pagelistinfo->is_from_pool = is_from_pool;
 	pagelistinfo->dma_dir =  (type == PAGELIST_WRITE) ?
 				  DMA_TO_DEVICE : DMA_FROM_DEVICE;
 	pagelistinfo->num_pages = num_pages;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:487 @
 		count -= len;
 	}
 
-	dma_buffers = dma_map_sg(g_dev,
+	dma_buffers = dma_map_sg(g_dma_dev,
 				 scatterlist,
 				 num_pages,
 				 pagelistinfo->dma_dir);
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:501 @
 
 	/* Combine adjacent blocks for performance */
 	k = 0;
-	for_each_sg(scatterlist, sg, dma_buffers, i) {
-		u32 len = sg_dma_len(sg);
-		u32 addr = sg_dma_address(sg);
-
-		/* Note: addrs is the address + page_count - 1
-		 * The firmware expects blocks after the first to be page-
-		 * aligned and a multiple of the page size
-		 */
-		WARN_ON(len == 0);
-		WARN_ON(i && (i != (dma_buffers - 1)) && (len & ~PAGE_MASK));
-		WARN_ON(i && (addr & ~PAGE_MASK));
-		if (k > 0 &&
-		    ((addrs[k - 1] & PAGE_MASK) +
-		     (((addrs[k - 1] & ~PAGE_MASK) + 1) << PAGE_SHIFT))
-		    == (addr & PAGE_MASK))
-			addrs[k - 1] += ((len + PAGE_SIZE - 1) >> PAGE_SHIFT);
-		else
-			addrs[k++] = (addr & PAGE_MASK) |
-				(((len + PAGE_SIZE - 1) >> PAGE_SHIFT) - 1);
+	if (g_use_36bit_addrs) {
+		for_each_sg(scatterlist, sg, dma_buffers, i) {
+			u32 len = sg_dma_len(sg);
+			u64 addr = sg_dma_address(sg);
+			u32 page_id = (u32)((addr >> 4) & ~0xff);
+			u32 sg_pages = (len + PAGE_SIZE - 1) >> PAGE_SHIFT;
+
+			/* Note: addrs is the address + page_count - 1
+			 * The firmware expects blocks after the first to be page-
+			 * aligned and a multiple of the page size
+			 */
+			WARN_ON(len == 0);
+			WARN_ON(i &&
+				(i != (dma_buffers - 1)) && (len & ~PAGE_MASK));
+			WARN_ON(i && (addr & ~PAGE_MASK));
+			WARN_ON(upper_32_bits(addr) > 0xf);
+			if (k > 0 &&
+			    ((addrs[k - 1] & ~0xff) +
+			     (((addrs[k - 1] & 0xff) + 1) << 8)
+			     == page_id)) {
+				u32 inc_pages = min(sg_pages,
+						    0xff - (addrs[k - 1] & 0xff));
+				addrs[k - 1] += inc_pages;
+				page_id += inc_pages << 8;
+				sg_pages -= inc_pages;
+			}
+			while (sg_pages) {
+				u32 inc_pages = min(sg_pages, 0x100u);
+				addrs[k++] = page_id | (inc_pages - 1);
+				page_id += inc_pages << 8;
+				sg_pages -= inc_pages;
+			}
+		}
+	} else {
+		for_each_sg(scatterlist, sg, dma_buffers, i) {
+			u32 len = sg_dma_len(sg);
+			u32 addr = sg_dma_address(sg);
+			u32 new_pages = (len + PAGE_SIZE - 1) >> PAGE_SHIFT;
+
+			/* Note: addrs is the address + page_count - 1
+			 * The firmware expects blocks after the first to be page-
+			 * aligned and a multiple of the page size
+			 */
+			WARN_ON(len == 0);
+			WARN_ON(i && (i != (dma_buffers - 1)) && (len & ~PAGE_MASK));
+			WARN_ON(i && (addr & ~PAGE_MASK));
+			if (k > 0 &&
+			    ((addrs[k - 1] & PAGE_MASK) +
+			     (((addrs[k - 1] & ~PAGE_MASK) + 1) << PAGE_SHIFT))
+			    == (addr & PAGE_MASK))
+				addrs[k - 1] += new_pages;
+			else
+				addrs[k++] = (addr & PAGE_MASK) | (new_pages - 1);
+		}
 	}
 
 	/* Partial cache lines (fragments) require special measures */
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:598 @
 	 * NOTE: dma_unmap_sg must be called before the
 	 * cpu can touch any of the data/pages.
 	 */
-	dma_unmap_sg(g_dev, pagelistinfo->scatterlist,
+	dma_unmap_sg(g_dma_dev, pagelistinfo->scatterlist,
 		     pagelistinfo->num_pages, pagelistinfo->dma_dir);
 	pagelistinfo->scatterlist_mapped = 0;
 
diff -Nur --no-dereference linux-5.10.52-orig/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c linux-5.10.52-v7l+/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c
--- linux-5.10.52-orig/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c	2021-07-25 16:46:26.537945989 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:112 @
 static DEFINE_SPINLOCK(msg_queue_spinlock);
 static struct platform_device *bcm2835_camera;
 static struct platform_device *bcm2835_audio;
+static struct platform_device *bcm2835_codec;
+static struct platform_device *vcsm_cma;
+static struct platform_device *bcm2835_isp;
 
 static struct vchiq_drvdata bcm2835_drvdata = {
 	.cache_line_size = 32,
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:124 @
 	.cache_line_size = 64,
 };
 
+static struct vchiq_drvdata bcm2711_drvdata = {
+	.cache_line_size = 64,
+	.use_36bit_addrs = true,
+};
+
 static const char *const ioctl_names[] = {
 	"CONNECT",
 	"SHUTDOWN",
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:2690 @
 static const struct of_device_id vchiq_of_match[] = {
 	{ .compatible = "brcm,bcm2835-vchiq", .data = &bcm2835_drvdata },
 	{ .compatible = "brcm,bcm2836-vchiq", .data = &bcm2836_drvdata },
+	{ .compatible = "brcm,bcm2711-vchiq", .data = &bcm2711_drvdata },
 	{},
 };
 MODULE_DEVICE_TABLE(of, vchiq_of_match);
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:2700 @
 {
 	struct platform_device_info pdevinfo;
 	struct platform_device *child;
+	struct device_node *np;
 
 	memset(&pdevinfo, 0, sizeof(pdevinfo));
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:2709 @
 	pdevinfo.id = PLATFORM_DEVID_NONE;
 	pdevinfo.dma_mask = DMA_BIT_MASK(32);
 
+	np = of_get_child_by_name(pdev->dev.of_node, name);
+
+	/* Skip the child if it is explicitly disabled */
+	if (np && !of_device_is_available(np))
+		return NULL;
+
 	child = platform_device_register_full(&pdevinfo);
 	if (IS_ERR(child)) {
 		dev_warn(&pdev->dev, "%s not registered\n", name);
 		child = NULL;
 	}
 
+	child->dev.of_node = np;
+
+	/*
+	 * We want the dma-ranges etc to be copied from the parent VCHIQ device
+	 * to be passed on to the children without a node of their own.
+	 */
+	if (!np)
+		np = pdev->dev.of_node;
+
+	of_dma_configure(&child->dev, np, true);
+
+	if (np != pdev->dev.of_node)
+		of_node_put(np);
+
 	return child;
 }
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:2792 @
 		VCHIQ_VERSION, VCHIQ_VERSION_MIN,
 		MAJOR(vchiq_devid), MINOR(vchiq_devid));
 
+	vcsm_cma = vchiq_register_child(pdev, "vcsm-cma");
+	bcm2835_codec = vchiq_register_child(pdev, "bcm2835-codec");
 	bcm2835_camera = vchiq_register_child(pdev, "bcm2835-camera");
 	bcm2835_audio = vchiq_register_child(pdev, "bcm2835_audio");
+	bcm2835_isp = vchiq_register_child(pdev, "bcm2835-isp");
 
 	return 0;
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:2809 @
 
 static int vchiq_remove(struct platform_device *pdev)
 {
+	platform_device_unregister(bcm2835_isp);
 	platform_device_unregister(bcm2835_audio);
 	platform_device_unregister(bcm2835_camera);
+	platform_device_unregister(bcm2835_codec);
+	platform_device_unregister(vcsm_cma);
 	vchiq_debugfs_deinit();
 	device_destroy(vchiq_class, vchiq_devid);
 	cdev_del(&vchiq_cdev);
diff -Nur --no-dereference linux-5.10.52-orig/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.h linux-5.10.52-v7l+/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.h
--- linux-5.10.52-orig/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.h	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.h	2021-07-25 16:46:26.537945989 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:55 @
 
 struct vchiq_drvdata {
 	const unsigned int cache_line_size;
+	const bool use_36bit_addrs;
 	struct rpi_firmware *fw;
 };
 
diff -Nur --no-dereference linux-5.10.52-orig/drivers/staging/vc04_services/Kconfig linux-5.10.52-v7l+/drivers/staging/vc04_services/Kconfig
--- linux-5.10.52-orig/drivers/staging/vc04_services/Kconfig	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/staging/vc04_services/Kconfig	2021-07-25 16:46:26.507946492 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:26 @
 
 source "drivers/staging/vc04_services/bcm2835-camera/Kconfig"
 
+source "drivers/staging/vc04_services/vc-sm-cma/Kconfig"
+source "drivers/staging/vc04_services/bcm2835-codec/Kconfig"
+source "drivers/staging/vc04_services/bcm2835-isp/Kconfig"
+
 source "drivers/staging/vc04_services/vchiq-mmal/Kconfig"
 
 endif
diff -Nur --no-dereference linux-5.10.52-orig/drivers/staging/vc04_services/Makefile linux-5.10.52-v7l+/drivers/staging/vc04_services/Makefile
--- linux-5.10.52-orig/drivers/staging/vc04_services/Makefile	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/staging/vc04_services/Makefile	2021-07-25 16:46:26.507946492 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:14 @
 obj-$(CONFIG_SND_BCM2835)		+= bcm2835-audio/
 obj-$(CONFIG_VIDEO_BCM2835)		+= bcm2835-camera/
 obj-$(CONFIG_BCM2835_VCHIQ_MMAL)	+= vchiq-mmal/
+obj-$(CONFIG_BCM_VC_SM_CMA)		+= vc-sm-cma/
+obj-$(CONFIG_VIDEO_CODEC_BCM2835)	+= bcm2835-codec/
+obj-$(CONFIG_VIDEO_ISP_BCM2835)		+= bcm2835-isp/
 
 ccflags-y += -I $(srctree)/$(src)/include  -D__VCCOREVER__=0x04000000
 
diff -Nur --no-dereference linux-5.10.52-orig/drivers/staging/vc04_services/vchiq-mmal/Kconfig linux-5.10.52-v7l+/drivers/staging/vc04_services/vchiq-mmal/Kconfig
--- linux-5.10.52-orig/drivers/staging/vc04_services/vchiq-mmal/Kconfig	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/staging/vc04_services/vchiq-mmal/Kconfig	2021-07-25 16:46:26.567945487 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
 config BCM2835_VCHIQ_MMAL
 	tristate "BCM2835 MMAL VCHIQ service"
-	depends on BCM2835_VCHIQ
+	select BCM2835_VCHIQ
+	select BCM_VC_SM_CMA
 	help
 	  Enables the MMAL API over VCHIQ interface as used for the
 	  majority of the multimedia services on VideoCore.
diff -Nur --no-dereference linux-5.10.52-orig/drivers/staging/vc04_services/vchiq-mmal/mmal-common.h linux-5.10.52-v7l+/drivers/staging/vc04_services/vchiq-mmal/mmal-common.h
--- linux-5.10.52-orig/drivers/staging/vc04_services/vchiq-mmal/mmal-common.h	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/staging/vc04_services/vchiq-mmal/mmal-common.h	2021-07-25 16:46:26.567945487 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:53 @
 
 	struct mmal_msg_context *msg_context;
 
+	struct dma_buf *dma_buf;/* Exported dmabuf fd from videobuf2 */
+	void *vcsm_handle;	/* VCSM handle having imported the dmabuf */
+	u32 vc_handle;		/* VC handle to that dmabuf */
+
+	u32 cmd;		/* MMAL command. 0=data. */
 	unsigned long length;
 	u32 mmal_flags;
 	s64 dts;
diff -Nur --no-dereference linux-5.10.52-orig/drivers/staging/vc04_services/vchiq-mmal/mmal-encodings.h linux-5.10.52-v7l+/drivers/staging/vc04_services/vchiq-mmal/mmal-encodings.h
--- linux-5.10.52-orig/drivers/staging/vc04_services/vchiq-mmal/mmal-encodings.h	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/staging/vc04_services/vchiq-mmal/mmal-encodings.h	2021-07-25 16:46:26.567945487 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:72 @
  */
 #define MMAL_ENCODING_OPAQUE           MMAL_FOURCC('O', 'P', 'Q', 'V')
 
+/* Bayer formats
+ * FourCC values copied from V4L2 where defined.
+ */
+/* 8 bit per pixel Bayer formats. */
+#define MMAL_ENCODING_BAYER_SBGGR8     MMAL_FOURCC('B', 'A', '8', '1')
+#define MMAL_ENCODING_BAYER_SGBRG8     MMAL_FOURCC('G', 'B', 'R', 'G')
+#define MMAL_ENCODING_BAYER_SGRBG8     MMAL_FOURCC('G', 'R', 'B', 'G')
+#define MMAL_ENCODING_BAYER_SRGGB8     MMAL_FOURCC('R', 'G', 'G', 'B')
+
+/* 10 bit per pixel packed Bayer formats. */
+#define MMAL_ENCODING_BAYER_SBGGR10P   MMAL_FOURCC('p', 'B', 'A', 'A')
+#define MMAL_ENCODING_BAYER_SGRBG10P   MMAL_FOURCC('p', 'g', 'A', 'A')
+#define MMAL_ENCODING_BAYER_SGBRG10P   MMAL_FOURCC('p', 'G', 'A', 'A')
+#define MMAL_ENCODING_BAYER_SRGGB10P   MMAL_FOURCC('p', 'R', 'A', 'A')
+
+/* 12 bit per pixel packed Bayer formats. */
+#define MMAL_ENCODING_BAYER_SBGGR12P   MMAL_FOURCC('p', 'B', '1', '2')
+#define MMAL_ENCODING_BAYER_SGRBG12P   MMAL_FOURCC('p', 'g', '1', '2')
+#define MMAL_ENCODING_BAYER_SGBRG12P   MMAL_FOURCC('p', 'G', '1', '2')
+#define MMAL_ENCODING_BAYER_SRGGB12P   MMAL_FOURCC('p', 'R', '1', '2')
+
+//14 bit per pixel Bayer formats.
+#define MMAL_ENCODING_BAYER_SBGGR14P   MMAL_FOURCC('p', 'B', 'E', 'E')
+#define MMAL_ENCODING_BAYER_SGBRG14P   MMAL_FOURCC('p', 'G', 'E', 'E')
+#define MMAL_ENCODING_BAYER_SGRBG14P   MMAL_FOURCC('p', 'g', 'E', 'E')
+#define MMAL_ENCODING_BAYER_SRGGB14P   MMAL_FOURCC('p', 'R', 'E', 'E')
+
+/* 16 bit per pixel Bayer formats. */
+#define MMAL_ENCODING_BAYER_SBGGR16    MMAL_FOURCC('B', 'G', '1', '6')
+#define MMAL_ENCODING_BAYER_SGBRG16    MMAL_FOURCC('G', 'B', '1', '6')
+#define MMAL_ENCODING_BAYER_SGRBG16    MMAL_FOURCC('G', 'R', '1', '6')
+#define MMAL_ENCODING_BAYER_SRGGB16    MMAL_FOURCC('R', 'G', '1', '6')
+
+/* 10 bit per pixel unpacked (16bit) Bayer formats. */
+#define MMAL_ENCODING_BAYER_SBGGR10    MMAL_FOURCC('B', 'G', '1', '0')
+#define MMAL_ENCODING_BAYER_SGRBG10    MMAL_FOURCC('B', 'A', '1', '0')
+#define MMAL_ENCODING_BAYER_SGBRG10    MMAL_FOURCC('G', 'B', '1', '0')
+#define MMAL_ENCODING_BAYER_SRGGB10    MMAL_FOURCC('R', 'G', '1', '0')
+
+/* 12 bit per pixel unpacked (16bit) Bayer formats */
+#define MMAL_ENCODING_BAYER_SBGGR12    MMAL_FOURCC('B', 'G', '1', '2')
+#define MMAL_ENCODING_BAYER_SGRBG12    MMAL_FOURCC('B', 'A', '1', '2')
+#define MMAL_ENCODING_BAYER_SGBRG12    MMAL_FOURCC('G', 'B', '1', '2')
+#define MMAL_ENCODING_BAYER_SRGGB12    MMAL_FOURCC('R', 'G', '1', '2')
+
+/* 14 bit per pixel unpacked (16bit) Bayer formats */
+#define MMAL_ENCODING_BAYER_SBGGR14    MMAL_FOURCC('B', 'G', '1', '4')
+#define MMAL_ENCODING_BAYER_SGBRG14    MMAL_FOURCC('G', 'B', '1', '4')
+#define MMAL_ENCODING_BAYER_SGRBG14    MMAL_FOURCC('G', 'R', '1', '4')
+#define MMAL_ENCODING_BAYER_SRGGB14    MMAL_FOURCC('R', 'G', '1', '4')
+
+/* MIPI packed monochrome images */
+#define MMAL_ENCODING_GREY    MMAL_FOURCC('G', 'R', 'E', 'Y')
+#define MMAL_ENCODING_Y10P    MMAL_FOURCC('Y', '1', '0', 'P')
+#define MMAL_ENCODING_Y12P    MMAL_FOURCC('Y', '1', '2', 'P')
+#define MMAL_ENCODING_Y14P    MMAL_FOURCC('Y', '1', '4', 'P')
+#define MMAL_ENCODING_Y16     MMAL_FOURCC('Y', '1', '6', ' ')
+/* Unpacked monochrome formats (16bit per sample, but only N LSBs used) */
+#define MMAL_ENCODING_Y10     MMAL_FOURCC('Y', '1', '0', ' ')
+#define MMAL_ENCODING_Y12     MMAL_FOURCC('Y', '1', '2', ' ')
+#define MMAL_ENCODING_Y14     MMAL_FOURCC('Y', '1', '4', ' ')
+
 /** An EGL image handle
  */
 #define MMAL_ENCODING_EGL_IMAGE        MMAL_FOURCC('E', 'G', 'L', 'I')
 
+/** ISP image statistics format
+ */
+#define MMAL_ENCODING_BRCM_STATS       MMAL_FOURCC('S', 'T', 'A', 'T')
+
 /* }@ */
 
 /** \name Pre-defined audio encodings */
diff -Nur --no-dereference linux-5.10.52-orig/drivers/staging/vc04_services/vchiq-mmal/mmal-msg.h linux-5.10.52-v7l+/drivers/staging/vc04_services/vchiq-mmal/mmal-msg.h
--- linux-5.10.52-orig/drivers/staging/vc04_services/vchiq-mmal/mmal-msg.h	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/staging/vc04_services/vchiq-mmal/mmal-msg.h	2021-07-25 16:46:26.567945487 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:349 @
 /* event messages */
 #define MMAL_WORKER_EVENT_SPACE 256
 
+/* Four CC's for events */
+#define MMAL_FOURCC(a, b, c, d) ((a) | (b << 8) | (c << 16) | (d << 24))
+
+#define MMAL_EVENT_ERROR		MMAL_FOURCC('E', 'R', 'R', 'O')
+#define MMAL_EVENT_EOS			MMAL_FOURCC('E', 'E', 'O', 'S')
+#define MMAL_EVENT_FORMAT_CHANGED	MMAL_FOURCC('E', 'F', 'C', 'H')
+#define MMAL_EVENT_PARAMETER_CHANGED	MMAL_FOURCC('E', 'P', 'C', 'H')
+
+/* Structs for each of the event message payloads */
+struct mmal_msg_event_eos {
+	u32 port_type;	/**< Type of port that received the end of stream */
+	u32 port_index;	/**< Index of port that received the end of stream */
+};
+
+/** Format changed event data. */
+struct mmal_msg_event_format_changed {
+	/* Minimum size of buffers the port requires */
+	u32 buffer_size_min;
+	/* Minimum number of buffers the port requires */
+	u32 buffer_num_min;
+	/* Size of buffers the port recommends for optimal performance.
+	 * A value of zero means no special recommendation.
+	 */
+	u32 buffer_size_recommended;
+	/* Number of buffers the port recommends for optimal
+	 * performance. A value of zero means no special recommendation.
+	 */
+	u32 buffer_num_recommended;
+
+	u32 es_ptr;
+	struct mmal_es_format format;
+	union mmal_es_specific_format es;
+	u8 extradata[MMAL_FORMAT_EXTRADATA_MAX_SIZE];
+};
+
 struct mmal_msg_event_to_host {
 	u32 client_component;	/* component context */
 
diff -Nur --no-dereference linux-5.10.52-orig/drivers/staging/vc04_services/vchiq-mmal/mmal-parameters.h linux-5.10.52-v7l+/drivers/staging/vc04_services/vchiq-mmal/mmal-parameters.h
--- linux-5.10.52-orig/drivers/staging/vc04_services/vchiq-mmal/mmal-parameters.h	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/staging/vc04_services/vchiq-mmal/mmal-parameters.h	2021-07-25 16:46:26.567945487 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:224 @
 	MMAL_PARAMETER_SHUTTER_SPEED,
 		/**< Takes a @ref MMAL_PARAMETER_AWB_GAINS_T */
 	MMAL_PARAMETER_CUSTOM_AWB_GAINS,
+		/**< Takes a @ref MMAL_PARAMETER_CAMERA_SETTINGS_T */
+	MMAL_PARAMETER_CAMERA_SETTINGS,
+		/**< Takes a @ref MMAL_PARAMETER_PRIVACY_INDICATOR_T */
+	MMAL_PARAMETER_PRIVACY_INDICATOR,
+		/**< Takes a @ref MMAL_PARAMETER_BOOLEAN_T */
+	MMAL_PARAMETER_VIDEO_DENOISE,
+		/**< Takes a @ref MMAL_PARAMETER_BOOLEAN_T */
+	MMAL_PARAMETER_STILLS_DENOISE,
+		/**< Takes a @ref MMAL_PARAMETER_CAMERA_ANNOTATE_T */
+	MMAL_PARAMETER_ANNOTATE,
+		/**< Takes a @ref MMAL_PARAMETER_STEREOSCOPIC_MODE_T */
+	MMAL_PARAMETER_STEREOSCOPIC_MODE,
+		/**< Takes a @ref MMAL_PARAMETER_CAMERA_INTERFACE_T */
+	MMAL_PARAMETER_CAMERA_INTERFACE,
+		/**< Takes a @ref MMAL_PARAMETER_CAMERA_CLOCKING_MODE_T */
+	MMAL_PARAMETER_CAMERA_CLOCKING_MODE,
+		/**< Takes a @ref MMAL_PARAMETER_CAMERA_RX_CONFIG_T */
+	MMAL_PARAMETER_CAMERA_RX_CONFIG,
+		/**< Takes a @ref MMAL_PARAMETER_CAMERA_RX_TIMING_T */
+	MMAL_PARAMETER_CAMERA_RX_TIMING,
+		/**< Takes a @ref MMAL_PARAMETER_UINT32_T */
+	MMAL_PARAMETER_DPF_CONFIG,
+
+	/* 0x50 */
+		/**< Takes a @ref MMAL_PARAMETER_UINT32_T */
+	MMAL_PARAMETER_JPEG_RESTART_INTERVAL,
+		/**< Takes a @ref MMAL_PARAMETER_UINT32_T */
+	MMAL_PARAMETER_CAMERA_ISP_BLOCK_OVERRIDE,
+		/**< Takes a @ref MMAL_PARAMETER_LENS_SHADING_T */
+	MMAL_PARAMETER_LENS_SHADING_OVERRIDE,
+		/**< Takes a @ref MMAL_PARAMETER_UINT32_T */
+	MMAL_PARAMETER_BLACK_LEVEL,
+		/**< Takes a @ref MMAL_PARAMETER_RESIZE_T */
+	MMAL_PARAMETER_RESIZE_PARAMS,
+		/**< Takes a @ref MMAL_PARAMETER_CROP_T */
+	MMAL_PARAMETER_CROP,
+		/**< Takes a @ref MMAL_PARAMETER_INT32_T */
+	MMAL_PARAMETER_OUTPUT_SHIFT,
+		/**< Takes a @ref MMAL_PARAMETER_INT32_T */
+	MMAL_PARAMETER_CCM_SHIFT,
+		/**< Takes a @ref MMAL_PARAMETER_CUSTOM_CCM_T */
+	MMAL_PARAMETER_CUSTOM_CCM,
+		/**< Takes a @ref MMAL_PARAMETER_RATIONAL_T */
+	MMAL_PARAMETER_ANALOG_GAIN,
+		/**< Takes a @ref MMAL_PARAMETER_RATIONAL_T */
+	MMAL_PARAMETER_DIGITAL_GAIN,
+		/**< Takes a @ref MMAL_PARAMETER_DENOISE_T */
+	MMAL_PARAMETER_DENOISE,
+		/**< Takes a @ref MMAL_PARAMETER_SHARPEN_T */
+	MMAL_PARAMETER_SHARPEN,
+		/**< Takes a @ref MMAL_PARAMETER_GEQ_T */
+	MMAL_PARAMETER_GEQ,
+		/**< Tales a @ref MMAP_PARAMETER_DPC_T */
+	MMAL_PARAMETER_DPC,
+		/**< Tales a @ref MMAP_PARAMETER_GAMMA_T */
+	MMAL_PARAMETER_GAMMA,
+		/**< Takes a @ref MMAL_PARAMETER_CDN_T */
+	MMAL_PARAMETER_CDN,
 };
 
 struct mmal_parameter_rational {
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:374 @
 	MMAL_PARAM_AWBMODE_INCANDESCENT,
 	MMAL_PARAM_AWBMODE_FLASH,
 	MMAL_PARAM_AWBMODE_HORIZON,
+	MMAL_PARAM_AWBMODE_GREYWORLD,
 };
 
 enum mmal_parameter_imagefx {
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:642 @
 	MMAL_PARAMETER_VIDEO_ENCODE_H264_LOW_DELAY_HRD_FLAG,
 
 	/**< @ref MMAL_PARAMETER_BOOLEAN_T */
-	MMAL_PARAMETER_VIDEO_ENCODE_INLINE_HEADER
+	MMAL_PARAMETER_VIDEO_ENCODE_INLINE_HEADER,
+
+	/**< Take a @ref MMAL_PARAMETER_BOOLEAN_T. */
+	MMAL_PARAMETER_VIDEO_ENCODE_SEI_ENABLE,
+
+	/**< Take a @ref MMAL_PARAMETER_BOOLEAN_T. */
+	MMAL_PARAMETER_VIDEO_ENCODE_INLINE_VECTORS,
+
+	/**< Take a @ref MMAL_PARAMETER_VIDEO_RENDER_STATS_T. */
+	MMAL_PARAMETER_VIDEO_RENDER_STATS,
+
+	/**< Take a @ref MMAL_PARAMETER_VIDEO_INTERLACE_TYPE_T. */
+	MMAL_PARAMETER_VIDEO_INTERLACE_TYPE,
+
+	/**< Takes a @ref MMAL_PARAMETER_BOOLEAN_T */
+	MMAL_PARAMETER_VIDEO_INTERPOLATE_TIMESTAMPS,
+
+	/**< Takes a @ref MMAL_PARAMETER_BOOLEAN_T */
+	MMAL_PARAMETER_VIDEO_ENCODE_SPS_TIMING,
+
+	/**< Takes a @ref MMAL_PARAMETER_UINT32_T */
+	MMAL_PARAMETER_VIDEO_MAX_NUM_CALLBACKS,
+
+	/**< Takes a @ref MMAL_PARAMETER_SOURCE_PATTERN_T */
+	MMAL_PARAMETER_VIDEO_SOURCE_PATTERN,
+
+	/**< Takes a @ref MMAL_PARAMETER_BOOLEAN_T */
+	MMAL_PARAMETER_VIDEO_ENCODE_SEPARATE_NAL_BUFS,
+
+	/**< Takes a @ref MMAL_PARAMETER_UINT32_T */
+	MMAL_PARAMETER_VIDEO_DROPPABLE_PFRAME_LENGTH,
+
+	/**< Take a @ref MMAL_PARAMETER_VIDEO_STALL_T */
+	MMAL_PARAMETER_VIDEO_STALL_THRESHOLD,
+
+	/**< Take a @ref MMAL_PARAMETER_BOOLEAN_T */
+	MMAL_PARAMETER_VIDEO_ENCODE_HEADERS_WITH_FRAME,
+
+	/**< Take a @ref MMAL_PARAMETER_BOOLEAN_T */
+	MMAL_PARAMETER_VIDEO_VALIDATE_TIMESTAMPS,
 };
 
 /** Valid mirror modes */
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:850 @
 	struct mmal_parameter_camera_info_camera
 		cameras[MMAL_PARAMETER_CAMERA_INFO_MAX_CAMERAS];
 	struct mmal_parameter_camera_info_flash
-				flashes[MMAL_PARAMETER_CAMERA_INFO_MAX_FLASHES];
+		flashes[MMAL_PARAMETER_CAMERA_INFO_MAX_FLASHES];
+};
+
+struct mmal_parameter_ccm {
+	struct mmal_parameter_rational ccm[3][3];
+	s32 offsets[3];
+};
+
+struct mmal_parameter_custom_ccm {
+	u32 enabled; /**< Enable the custom CCM. */
+	struct mmal_parameter_ccm ccm; /**< CCM to be used. */
+};
+
+struct mmal_parameter_lens_shading {
+	u32 enabled;
+	u32 grid_cell_size;
+	u32 grid_width;
+	u32 grid_stride;
+	u32 grid_height;
+	u32 mem_handle_table;
+	u32 ref_transform;
+};
+
+enum mmal_parameter_ls_gain_format_type {
+	MMAL_PARAMETER_LS_GAIN_FORMAT_TYPE_U0P8_1 = 0,
+	MMAL_PARAMETER_LS_GAIN_FORMAT_TYPE_U1P7_0 = 1,
+	MMAL_PARAMETER_LS_GAIN_FORMAT_TYPE_U1P7_1 = 2,
+	MMAL_PARAMETER_LS_GAIN_FORMAT_TYPE_U2P6_0 = 3,
+	MMAL_PARAMETER_LS_GAIN_FORMAT_TYPE_U2P6_1 = 4,
+	MMAL_PARAMETER_LS_GAIN_FORMAT_TYPE_U3P5_0 = 5,
+	MMAL_PARAMETER_LS_GAIN_FORMAT_TYPE_U3P5_1 = 6,
+	MMAL_PARAMETER_LS_GAIN_FORMAT_TYPE_U4P10  = 7,
+	MMAL_PARAMETER_LS_GAIN_FORMAT_TYPE_DUMMY  = 0x7FFFFFFF
+};
+
+struct mmal_parameter_lens_shading_v2 {
+	u32 enabled;
+	u32 grid_cell_size;
+	u32 grid_width;
+	u32 grid_stride;
+	u32 grid_height;
+	u32 mem_handle_table;
+	u32 ref_transform;
+	u32 corner_sampled;
+	enum mmal_parameter_ls_gain_format_type gain_format;
+};
+
+struct mmal_parameter_black_level {
+	u32 enabled;
+	u16 black_level_r;
+	u16 black_level_g;
+	u16 black_level_b;
+	u8 pad_[2]; /* Unused */
+};
+
+struct mmal_parameter_geq {
+	u32 enabled;
+	u32 offset;
+	struct mmal_parameter_rational slope;
+};
+
+#define MMAL_NUM_GAMMA_PTS 33
+struct mmal_parameter_gamma {
+	u32 enabled;
+	u16 x[MMAL_NUM_GAMMA_PTS];
+	u16 y[MMAL_NUM_GAMMA_PTS];
+};
+
+enum mmal_parameter_cdn_mode {
+	MMAL_PARAM_CDN_FAST = 0,
+	MMAL_PARAM_CDN_HIGH_QUALITY = 1,
+	MMAL_PARAM_CDN_DUMMY  = 0x7FFFFFFF
+};
+
+struct mmal_parameter_colour_denoise {
+	u32 enabled;
+	enum mmal_parameter_cdn_mode mode;
+};
+
+struct mmal_parameter_denoise {
+	u32 enabled;
+	u32 constant;
+	struct mmal_parameter_rational slope;
+	struct mmal_parameter_rational strength;
+};
+
+struct mmal_parameter_sharpen {
+	u32 enabled;
+	struct mmal_parameter_rational threshold;
+	struct mmal_parameter_rational strength;
+	struct mmal_parameter_rational limit;
+};
+
+enum mmal_dpc_mode {
+	MMAL_DPC_MODE_OFF = 0,
+	MMAL_DPC_MODE_NORMAL = 1,
+	MMAL_DPC_MODE_STRONG = 2,
+	MMAL_DPC_MODE_MAX = 0x7FFFFFFF,
+};
+
+struct mmal_parameter_dpc {
+	u32 enabled;
+	u32 strength;
+};
+
+struct mmal_parameter_crop {
+	struct vchiq_mmal_rect rect;
 };
 
 #endif
diff -Nur --no-dereference linux-5.10.52-orig/drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.c linux-5.10.52-v7l+/drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.c
--- linux-5.10.52-orig/drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.c	2021-07-25 16:46:26.567945487 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:30 @
 #include <media/videobuf2-vmalloc.h>
 
 #include "mmal-common.h"
+#include "mmal-parameters.h"
 #include "mmal-vchiq.h"
 #include "mmal-msg.h"
 
+#include "vc-sm-cma/vc_sm_knl.h"
 /*
  * maximum number of components supported.
  * This matches the maximum permitted by default on the VPU
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:148 @
 			/* Presentation and Decode timestamps */
 			s64 pts;
 			s64 dts;
+			/* MMAL buffer command flag */
+			u32 cmd;
 
 			int status;	/* context status */
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:240 @
 	kfree(msg_context);
 }
 
-/* deals with receipt of event to host message */
-static void event_to_host_cb(struct vchiq_mmal_instance *instance,
-			     struct mmal_msg *msg, u32 msg_len)
-{
-	pr_debug("unhandled event\n");
-	pr_debug("component:%u port type:%d num:%d cmd:0x%x length:%d\n",
-		 msg->u.event_to_host.client_component,
-		 msg->u.event_to_host.port_type,
-		 msg->u.event_to_host.port_num,
-		 msg->u.event_to_host.cmd, msg->u.event_to_host.length);
-}
-
 /* workqueue scheduled callback
  *
  * we do this because it is important we do not call any other vchiq
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:261 @
 	buffer->mmal_flags = msg_context->u.bulk.mmal_flags;
 	buffer->dts = msg_context->u.bulk.dts;
 	buffer->pts = msg_context->u.bulk.pts;
+	buffer->cmd = msg_context->u.bulk.cmd;
 
-	atomic_dec(&msg_context->u.bulk.port->buffers_with_vpu);
+	if (!buffer->cmd)
+		atomic_dec(&msg_context->u.bulk.port->buffers_with_vpu);
 
 	msg_context->u.bulk.port->buffer_cb(msg_context->u.bulk.instance,
 					    msg_context->u.bulk.port,
 					    msg_context->u.bulk.status,
 					    msg_context->u.bulk.buffer);
+
+	if (buffer->cmd)
+		mutex_unlock(&msg_context->u.bulk.port->event_context_mutex);
 }
 
 /* workqueue scheduled callback to handle receiving buffers
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:350 @
 	msg_context->u.bulk.buffer_used = rd_len;
 	msg_context->u.bulk.dts = msg->u.buffer_from_host.buffer_header.dts;
 	msg_context->u.bulk.pts = msg->u.buffer_from_host.buffer_header.pts;
+	msg_context->u.bulk.cmd = msg->u.buffer_from_host.buffer_header.cmd;
 
 	queue_work(msg_context->instance->bulk_wq,
 		   &msg_context->u.bulk.buffer_to_host_work);
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:424 @
 
 	/* buffer header */
 	m.u.buffer_from_host.buffer_header.cmd = 0;
-	m.u.buffer_from_host.buffer_header.data =
-		(u32)(unsigned long)buf->buffer;
+	if (port->zero_copy) {
+		m.u.buffer_from_host.buffer_header.data = buf->vc_handle;
+	} else {
+		m.u.buffer_from_host.buffer_header.data =
+			(u32)(unsigned long)buf->buffer;
+	}
+
 	m.u.buffer_from_host.buffer_header.alloc_size = buf->buffer_size;
-	m.u.buffer_from_host.buffer_header.length = 0;	/* nothing used yet */
-	m.u.buffer_from_host.buffer_header.offset = 0;	/* no offset */
-	m.u.buffer_from_host.buffer_header.flags = 0;	/* no flags */
-	m.u.buffer_from_host.buffer_header.pts = MMAL_TIME_UNKNOWN;
-	m.u.buffer_from_host.buffer_header.dts = MMAL_TIME_UNKNOWN;
+	if (port->type == MMAL_PORT_TYPE_OUTPUT) {
+		m.u.buffer_from_host.buffer_header.length = 0;
+		m.u.buffer_from_host.buffer_header.offset = 0;
+		m.u.buffer_from_host.buffer_header.flags = 0;
+		m.u.buffer_from_host.buffer_header.pts = MMAL_TIME_UNKNOWN;
+		m.u.buffer_from_host.buffer_header.dts = MMAL_TIME_UNKNOWN;
+	} else {
+		m.u.buffer_from_host.buffer_header.length = buf->length;
+		m.u.buffer_from_host.buffer_header.offset = 0;
+		m.u.buffer_from_host.buffer_header.flags = buf->mmal_flags;
+		m.u.buffer_from_host.buffer_header.pts = buf->pts;
+		m.u.buffer_from_host.buffer_header.dts = buf->dts;
+	}
 
 	/* clear buffer type sepecific data */
 	memset(&m.u.buffer_from_host.buffer_header_type_specific, 0,
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:466 @
 	return ret;
 }
 
+/* deals with receipt of event to host message */
+static void event_to_host_cb(struct vchiq_mmal_instance *instance,
+			     struct mmal_msg *msg, u32 msg_len)
+{
+	int comp_idx = msg->u.event_to_host.client_component;
+	struct vchiq_mmal_component *component =
+					&instance->component[comp_idx];
+	struct vchiq_mmal_port *port = NULL;
+	struct mmal_msg_context *msg_context;
+	u32 port_num = msg->u.event_to_host.port_num;
+
+	if (msg->u.buffer_from_host.drvbuf.magic == MMAL_MAGIC) {
+		pr_err("%s: MMAL_MSG_TYPE_BUFFER_TO_HOST with bad magic\n",
+		       __func__);
+		return;
+	}
+
+	switch (msg->u.event_to_host.port_type) {
+	case MMAL_PORT_TYPE_CONTROL:
+		if (port_num) {
+			pr_err("%s: port_num of %u >= number of ports 1",
+			       __func__, port_num);
+			return;
+		}
+		port = &component->control;
+		break;
+	case MMAL_PORT_TYPE_INPUT:
+		if (port_num >= component->inputs) {
+			pr_err("%s: port_num of %u >= number of ports %u",
+			       __func__, port_num,
+			       port_num >= component->inputs);
+			return;
+		}
+		port = &component->input[port_num];
+		break;
+	case MMAL_PORT_TYPE_OUTPUT:
+		if (port_num >= component->outputs) {
+			pr_err("%s: port_num of %u >= number of ports %u",
+			       __func__, port_num,
+			       port_num >= component->outputs);
+			return;
+		}
+		port = &component->output[port_num];
+		break;
+	case MMAL_PORT_TYPE_CLOCK:
+		if (port_num >= component->clocks) {
+			pr_err("%s: port_num of %u >= number of ports %u",
+			       __func__, port_num,
+			       port_num >= component->clocks);
+			return;
+		}
+		port = &component->clock[port_num];
+		break;
+	default:
+		break;
+	}
+
+	if (!mutex_trylock(&port->event_context_mutex)) {
+		pr_err("dropping event 0x%x\n", msg->u.event_to_host.cmd);
+		return;
+	}
+	msg_context = port->event_context;
+
+	if (msg->h.status != MMAL_MSG_STATUS_SUCCESS) {
+		/* message reception had an error */
+		//pr_warn
+		pr_err("%s: error %d in reply\n", __func__, msg->h.status);
+
+		msg_context->u.bulk.status = msg->h.status;
+	} else if (msg->u.event_to_host.length > MMAL_WORKER_EVENT_SPACE) {
+		/* data is not in message, queue a bulk receive */
+		pr_err("%s: payload not in message - bulk receive??! NOT SUPPORTED\n",
+		       __func__);
+		msg_context->u.bulk.status = -1;
+	} else {
+		memcpy(msg_context->u.bulk.buffer->buffer,
+		       msg->u.event_to_host.data,
+		       msg->u.event_to_host.length);
+
+		msg_context->u.bulk.buffer_used =
+		    msg->u.event_to_host.length;
+
+		msg_context->u.bulk.mmal_flags = 0;
+		msg_context->u.bulk.dts = MMAL_TIME_UNKNOWN;
+		msg_context->u.bulk.pts = MMAL_TIME_UNKNOWN;
+		msg_context->u.bulk.cmd = msg->u.event_to_host.cmd;
+
+		pr_debug("event component:%u port type:%d num:%d cmd:0x%x length:%d\n",
+			 msg->u.event_to_host.client_component,
+			 msg->u.event_to_host.port_type,
+			 msg->u.event_to_host.port_num,
+			 msg->u.event_to_host.cmd, msg->u.event_to_host.length);
+	}
+
+	schedule_work(&msg_context->u.bulk.work);
+}
+
 /* deals with receipt of buffer to host message */
 static void buffer_to_host_cb(struct vchiq_mmal_instance *instance,
 			      struct mmal_msg *msg, u32 msg_len)
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:596 @
 
 		msg_context->u.bulk.status = msg->h.status;
 
+	} else if (msg->u.buffer_from_host.is_zero_copy) {
+		/*
+		 * Zero copy buffer, so nothing to do.
+		 * Copy buffer info and make callback.
+		 */
+		msg_context->u.bulk.buffer_used =
+				msg->u.buffer_from_host.buffer_header.length;
+		msg_context->u.bulk.mmal_flags =
+				msg->u.buffer_from_host.buffer_header.flags;
+		msg_context->u.bulk.dts =
+				msg->u.buffer_from_host.buffer_header.dts;
+		msg_context->u.bulk.pts =
+				msg->u.buffer_from_host.buffer_header.pts;
+		msg_context->u.bulk.cmd =
+				msg->u.buffer_from_host.buffer_header.cmd;
+
 	} else if (msg->u.buffer_from_host.buffer_header.length == 0) {
 		/* empty buffer */
 		if (msg->u.buffer_from_host.buffer_header.flags &
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1459 @
 				mmalbuf->mmal_flags = 0;
 				mmalbuf->dts = MMAL_TIME_UNKNOWN;
 				mmalbuf->pts = MMAL_TIME_UNKNOWN;
+				mmalbuf->cmd = 0;
 				port->buffer_cb(instance,
 						port, 0, mmalbuf);
 			}
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1556 @
 
 	mutex_unlock(&instance->vchiq_mutex);
 
+	if (parameter == MMAL_PARAMETER_ZERO_COPY && !ret)
+		port->zero_copy = !!(*(bool *)value);
+
 	return ret;
 }
 EXPORT_SYMBOL_GPL(vchiq_mmal_port_parameter_set);
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1727 @
 	unsigned long flags = 0;
 	int ret;
 
+	/*
+	 * We really want to do this in mmal_vchi_buffer_init but can't as
+	 * videobuf2 won't let us have the dmabuf there.
+	 */
+	if (port->zero_copy && buffer->dma_buf && !buffer->vcsm_handle) {
+		pr_debug("%s: import dmabuf %p\n", __func__, buffer->dma_buf);
+		ret = vc_sm_cma_import_dmabuf(buffer->dma_buf,
+					      &buffer->vcsm_handle);
+		if (ret) {
+			pr_err("%s: vc_sm_import_dmabuf_fd failed, ret %d\n",
+			       __func__, ret);
+			return ret;
+		}
+
+		buffer->vc_handle = vc_sm_cma_int_handle(buffer->vcsm_handle);
+		if (!buffer->vc_handle) {
+			pr_err("%s: vc_sm_int_handle failed %d\n",
+			       __func__, ret);
+			vc_sm_cma_free(buffer->vcsm_handle);
+			return ret;
+		}
+		pr_debug("%s: import dmabuf %p - got vc handle %08X\n",
+			 __func__, buffer->dma_buf, buffer->vc_handle);
+	}
+
 	ret = buffer_from_host(instance, port, buffer);
 	if (ret == -EINVAL) {
 		/* Port is disabled. Queue for when it is enabled. */
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1785 @
 		release_msg_context(msg_context);
 	buf->msg_context = NULL;
 
+	if (buf->vcsm_handle) {
+		int ret;
+
+		pr_debug("%s: vc_sm_cma_free on handle %p\n", __func__,
+			 buf->vcsm_handle);
+		ret = vc_sm_cma_free(buf->vcsm_handle);
+		if (ret)
+			pr_err("%s: vcsm_free failed, ret %d\n", __func__, ret);
+		buf->vcsm_handle = 0;
+	}
 	return 0;
 }
 EXPORT_SYMBOL_GPL(mmal_vchi_buffer_cleanup);
 
+static void init_event_context(struct vchiq_mmal_instance *instance,
+			       struct vchiq_mmal_port *port)
+{
+	struct mmal_msg_context *ctx = get_msg_context(instance);
+
+	mutex_init(&port->event_context_mutex);
+
+	port->event_context = ctx;
+	ctx->u.bulk.instance = instance;
+	ctx->u.bulk.port = port;
+	ctx->u.bulk.buffer =
+		kzalloc(sizeof(*ctx->u.bulk.buffer), GFP_KERNEL);
+	if (!ctx->u.bulk.buffer)
+		goto release_msg_context;
+	ctx->u.bulk.buffer->buffer = kzalloc(MMAL_WORKER_EVENT_SPACE,
+					     GFP_KERNEL);
+	if (!ctx->u.bulk.buffer->buffer)
+		goto release_buffer;
+
+	INIT_WORK(&ctx->u.bulk.work, buffer_work_cb);
+	return;
+
+release_buffer:
+	kfree(ctx->u.bulk.buffer);
+release_msg_context:
+	release_msg_context(ctx);
+}
+
+static void free_event_context(struct vchiq_mmal_port *port)
+{
+	struct mmal_msg_context *ctx = port->event_context;
+
+	if (!ctx)
+		return;
+
+	kfree(ctx->u.bulk.buffer->buffer);
+	kfree(ctx->u.bulk.buffer);
+	release_msg_context(ctx);
+	port->event_context = NULL;
+}
+
+static void release_all_event_contexts(struct vchiq_mmal_component *component)
+{
+	int idx;
+
+	for (idx = 0; idx < component->inputs; idx++)
+		free_event_context(&component->input[idx]);
+	for (idx = 0; idx < component->outputs; idx++)
+		free_event_context(&component->output[idx]);
+	for (idx = 0; idx < component->clocks; idx++)
+		free_event_context(&component->clock[idx]);
+	free_event_context(&component->control);
+}
+
 /* Initialise a mmal component and its ports
  *
  */
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1902 @
 	ret = port_info_get(instance, &component->control);
 	if (ret < 0)
 		goto release_component;
+	init_event_context(instance, &component->control);
 
 	for (idx = 0; idx < component->inputs; idx++) {
 		component->input[idx].type = MMAL_PORT_TYPE_INPUT;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1913 @
 		ret = port_info_get(instance, &component->input[idx]);
 		if (ret < 0)
 			goto release_component;
+		init_event_context(instance, &component->input[idx]);
 	}
 
 	for (idx = 0; idx < component->outputs; idx++) {
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1925 @
 		ret = port_info_get(instance, &component->output[idx]);
 		if (ret < 0)
 			goto release_component;
+		init_event_context(instance, &component->output[idx]);
 	}
 
 	for (idx = 0; idx < component->clocks; idx++) {
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1937 @
 		ret = port_info_get(instance, &component->clock[idx]);
 		if (ret < 0)
 			goto release_component;
+		init_event_context(instance, &component->clock[idx]);
 	}
 
 	*component_out = component;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1948 @
 
 release_component:
 	destroy_component(instance, component);
+	release_all_event_contexts(component);
 unlock:
 	if (component)
 		component->in_use = 0;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1976 @
 
 	component->in_use = 0;
 
+	release_all_event_contexts(component);
+
 	mutex_unlock(&instance->vchiq_mutex);
 
 	return ret;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:2002 @
 
 	ret = enable_component(instance, component);
 	if (ret == 0)
-		component->enabled = true;
+		component->enabled = 1;
 
 	mutex_unlock(&instance->vchiq_mutex);
 
diff -Nur --no-dereference linux-5.10.52-orig/drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.h linux-5.10.52-v7l+/drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.h
--- linux-5.10.52-orig/drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.h	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.h	2021-07-25 16:46:26.567945487 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:52 @
 
 struct vchiq_mmal_port {
 	u32 enabled:1;
+	u32 zero_copy:1;
 	u32 handle;
 	u32 type; /* port type, cached to use on port info set */
 	u32 index; /* port index, cached to use on port info set */
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:83 @
 	vchiq_mmal_buffer_cb buffer_cb;
 	/* callback context */
 	void *cb_ctx;
+
+	/* ensure serialised use of the one event context structure */
+	struct mutex event_context_mutex;
+	struct mmal_msg_context *event_context;
 };
 
 struct vchiq_mmal_component {
diff -Nur --no-dereference linux-5.10.52-orig/drivers/staging/vc04_services/vc-sm-cma/Kconfig linux-5.10.52-v7l+/drivers/staging/vc04_services/vc-sm-cma/Kconfig
--- linux-5.10.52-orig/drivers/staging/vc04_services/vc-sm-cma/Kconfig	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/staging/vc04_services/vc-sm-cma/Kconfig	2021-07-25 16:46:26.547945822 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+config BCM_VC_SM_CMA
+	tristate "VideoCore Shared Memory (CMA) driver"
+	select BCM2835_VCHIQ
+	select RBTREE
+	select DMA_SHARED_BUFFER
+	help
+	  Say Y here to enable the shared memory interface that
+	  supports sharing dmabufs with VideoCore.
+	  This operates over the VCHIQ interface to a service
+	  running on VideoCore.
diff -Nur --no-dereference linux-5.10.52-orig/drivers/staging/vc04_services/vc-sm-cma/Makefile linux-5.10.52-v7l+/drivers/staging/vc04_services/vc-sm-cma/Makefile
--- linux-5.10.52-orig/drivers/staging/vc04_services/vc-sm-cma/Makefile	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/staging/vc04_services/vc-sm-cma/Makefile	2021-07-25 16:46:26.547945822 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+ccflags-y += \
+	-I$(srctree)/$(src)/../ \
+	-I$(srctree)/$(src)/../interface/vchiq_arm\
+	-I$(srctree)/$(src)/../include
+
+ccflags-y += \
+	-D__VCCOREVER__=0
+
+vc-sm-cma-$(CONFIG_BCM_VC_SM_CMA) := \
+	vc_sm.o vc_sm_cma_vchi.o
+
+obj-$(CONFIG_BCM_VC_SM_CMA) += vc-sm-cma.o
diff -Nur --no-dereference linux-5.10.52-orig/drivers/staging/vc04_services/vc-sm-cma/TODO linux-5.10.52-v7l+/drivers/staging/vc04_services/vc-sm-cma/TODO
--- linux-5.10.52-orig/drivers/staging/vc04_services/vc-sm-cma/TODO	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/staging/vc04_services/vc-sm-cma/TODO	2021-07-25 16:46:26.547945822 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1 @
+No currently outstanding tasks except some clean-up.
diff -Nur --no-dereference linux-5.10.52-orig/drivers/staging/vc04_services/vc-sm-cma/vc_sm.c linux-5.10.52-v7l+/drivers/staging/vc04_services/vc-sm-cma/vc_sm.c
--- linux-5.10.52-orig/drivers/staging/vc04_services/vc-sm-cma/vc_sm.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/staging/vc04_services/vc-sm-cma/vc_sm.c	2021-07-25 16:46:26.567945487 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * VideoCore Shared Memory driver using CMA.
+ *
+ * Copyright: 2018, Raspberry Pi (Trading) Ltd
+ * Dave Stevenson <dave.stevenson@raspberrypi.org>
+ *
+ * Based on vmcs_sm driver from Broadcom Corporation for some API,
+ * and taking some code for buffer allocation and dmabuf handling from
+ * videobuf2.
+ *
+ *
+ * This driver has 3 main uses:
+ * 1) Allocating buffers for the kernel or userspace that can be shared with the
+ *    VPU.
+ * 2) Importing dmabufs from elsewhere for sharing with the VPU.
+ * 3) Allocating buffers for use by the VPU.
+ *
+ * In the first and second cases the native handle is a dmabuf. Releasing the
+ * resource inherently comes from releasing the dmabuf, and this will trigger
+ * unmapping on the VPU. The underlying allocation and our buffer structure are
+ * retained until the VPU has confirmed that it has finished with it.
+ *
+ * For the VPU allocations the VPU is responsible for triggering the release,
+ * and therefore the released message decrements the dma_buf refcount (with the
+ * VPU mapping having already been marked as released).
+ */
+
+/* ---- Include Files ----------------------------------------------------- */
+#include <linux/cdev.h>
+#include <linux/device.h>
+#include <linux/debugfs.h>
+#include <linux/dma-mapping.h>
+#include <linux/dma-buf.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/mm.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/proc_fs.h>
+#include <linux/slab.h>
+#include <linux/seq_file.h>
+#include <linux/syscalls.h>
+#include <linux/types.h>
+#include <asm/cacheflush.h>
+
+#include "vchiq_connected.h"
+#include "vc_sm_cma_vchi.h"
+
+#include "vc_sm.h"
+#include "vc_sm_knl.h"
+#include <linux/broadcom/vc_sm_cma_ioctl.h>
+
+/* ---- Private Constants and Types --------------------------------------- */
+
+#define DEVICE_NAME		"vcsm-cma"
+#define DEVICE_MINOR		0
+
+#define VC_SM_RESOURCE_NAME_DEFAULT       "sm-host-resource"
+
+#define VC_SM_DIR_ROOT_NAME	"vcsm-cma"
+#define VC_SM_STATE		"state"
+
+/* Private file data associated with each opened device. */
+struct vc_sm_privdata_t {
+	pid_t pid;                      /* PID of creator. */
+
+	int restart_sys;		/* Tracks restart on interrupt. */
+	enum vc_sm_msg_type int_action;	/* Interrupted action. */
+	u32 int_trans_id;		/* Interrupted transaction. */
+};
+
+typedef int (*VC_SM_SHOW) (struct seq_file *s, void *v);
+struct sm_pde_t {
+	VC_SM_SHOW show;          /* Debug fs function hookup. */
+	struct dentry *dir_entry; /* Debug fs directory entry. */
+	void *priv_data;          /* Private data */
+};
+
+/* Global state information. */
+struct sm_state_t {
+	struct platform_device *pdev;
+
+	struct miscdevice misc_dev;
+
+	struct sm_instance *sm_handle;	/* Handle for videocore service. */
+
+	spinlock_t kernelid_map_lock;	/* Spinlock protecting kernelid_map */
+	struct idr kernelid_map;
+
+	struct mutex map_lock;          /* Global map lock. */
+	struct list_head buffer_list;	/* List of buffer. */
+
+	struct vc_sm_privdata_t *data_knl;  /* Kernel internal data tracking. */
+	struct vc_sm_privdata_t *vpu_allocs; /* All allocations from the VPU */
+	struct dentry *dir_root;	/* Debug fs entries root. */
+	struct sm_pde_t dir_state;	/* Debug fs entries state sub-tree. */
+
+	bool require_released_callback;	/* VPU will send a released msg when it
+					 * has finished with a resource.
+					 */
+	u32 int_trans_id;		/* Interrupted transaction. */
+};
+
+struct vc_sm_dma_buf_attachment {
+	struct device *dev;
+	struct sg_table sg_table;
+	struct list_head list;
+	enum dma_data_direction	dma_dir;
+};
+
+/* ---- Private Variables ----------------------------------------------- */
+
+static struct sm_state_t *sm_state;
+static int sm_inited;
+
+/* ---- Private Function Prototypes -------------------------------------- */
+
+/* ---- Private Functions ------------------------------------------------ */
+
+static int get_kernel_id(struct vc_sm_buffer *buffer)
+{
+	int handle;
+
+	spin_lock(&sm_state->kernelid_map_lock);
+	handle = idr_alloc(&sm_state->kernelid_map, buffer, 0, 0, GFP_KERNEL);
+	spin_unlock(&sm_state->kernelid_map_lock);
+
+	return handle;
+}
+
+static struct vc_sm_buffer *lookup_kernel_id(int handle)
+{
+	return idr_find(&sm_state->kernelid_map, handle);
+}
+
+static void free_kernel_id(int handle)
+{
+	spin_lock(&sm_state->kernelid_map_lock);
+	idr_remove(&sm_state->kernelid_map, handle);
+	spin_unlock(&sm_state->kernelid_map_lock);
+}
+
+static int vc_sm_cma_seq_file_show(struct seq_file *s, void *v)
+{
+	struct sm_pde_t *sm_pde;
+
+	sm_pde = (struct sm_pde_t *)(s->private);
+
+	if (sm_pde && sm_pde->show)
+		sm_pde->show(s, v);
+
+	return 0;
+}
+
+static int vc_sm_cma_single_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, vc_sm_cma_seq_file_show, inode->i_private);
+}
+
+static const struct file_operations vc_sm_cma_debug_fs_fops = {
+	.open = vc_sm_cma_single_open,
+	.read = seq_read,
+	.llseek = seq_lseek,
+	.release = single_release,
+};
+
+static int vc_sm_cma_global_state_show(struct seq_file *s, void *v)
+{
+	struct vc_sm_buffer *resource = NULL;
+	int resource_count = 0;
+
+	if (!sm_state)
+		return 0;
+
+	seq_printf(s, "\nVC-ServiceHandle     %p\n", sm_state->sm_handle);
+
+	/* Log all applicable mapping(s). */
+
+	mutex_lock(&sm_state->map_lock);
+	seq_puts(s, "\nResources\n");
+	if (!list_empty(&sm_state->buffer_list)) {
+		list_for_each_entry(resource, &sm_state->buffer_list,
+				    global_buffer_list) {
+			resource_count++;
+
+			seq_printf(s, "\nResource                %p\n",
+				   resource);
+			seq_printf(s, "           NAME         %s\n",
+				   resource->name);
+			seq_printf(s, "           SIZE         %zu\n",
+				   resource->size);
+			seq_printf(s, "           DMABUF       %p\n",
+				   resource->dma_buf);
+			if (resource->imported) {
+				seq_printf(s, "           ATTACH       %p\n",
+					   resource->import.attach);
+				seq_printf(s, "           SGT          %p\n",
+					   resource->import.sgt);
+			} else {
+				seq_printf(s, "           SGT          %p\n",
+					   resource->alloc.sg_table);
+			}
+			seq_printf(s, "           DMA_ADDR     %pad\n",
+				   &resource->dma_addr);
+			seq_printf(s, "           VC_HANDLE     %08x\n",
+				   resource->vc_handle);
+			seq_printf(s, "           VC_MAPPING    %d\n",
+				   resource->vpu_state);
+		}
+	}
+	seq_printf(s, "\n\nTotal resource count:   %d\n\n", resource_count);
+
+	mutex_unlock(&sm_state->map_lock);
+
+	return 0;
+}
+
+/*
+ * Adds a buffer to the private data list which tracks all the allocated
+ * data.
+ */
+static void vc_sm_add_resource(struct vc_sm_privdata_t *privdata,
+			       struct vc_sm_buffer *buffer)
+{
+	mutex_lock(&sm_state->map_lock);
+	list_add(&buffer->global_buffer_list, &sm_state->buffer_list);
+	mutex_unlock(&sm_state->map_lock);
+
+	pr_debug("[%s]: added buffer %p (name %s, size %zu)\n",
+		 __func__, buffer, buffer->name, buffer->size);
+}
+
+/*
+ * Cleans up imported dmabuf.
+ * Should be called with mutex held.
+ */
+static void vc_sm_clean_up_dmabuf(struct vc_sm_buffer *buffer)
+{
+	if (!buffer->imported)
+		return;
+
+	/* Handle cleaning up imported dmabufs */
+	if (buffer->import.sgt) {
+		dma_buf_unmap_attachment(buffer->import.attach,
+					 buffer->import.sgt,
+					 DMA_BIDIRECTIONAL);
+		buffer->import.sgt = NULL;
+	}
+	if (buffer->import.attach) {
+		dma_buf_detach(buffer->dma_buf, buffer->import.attach);
+		buffer->import.attach = NULL;
+	}
+}
+
+/*
+ * Instructs VPU to decrement the refcount on a buffer.
+ */
+static void vc_sm_vpu_free(struct vc_sm_buffer *buffer)
+{
+	if (buffer->vc_handle && buffer->vpu_state == VPU_MAPPED) {
+		struct vc_sm_free_t free = { buffer->vc_handle, 0 };
+		int status = vc_sm_cma_vchi_free(sm_state->sm_handle, &free,
+					     &sm_state->int_trans_id);
+		if (status != 0 && status != -EINTR) {
+			pr_err("[%s]: failed to free memory on videocore (status: %u, trans_id: %u)\n",
+			       __func__, status, sm_state->int_trans_id);
+		}
+
+		if (sm_state->require_released_callback) {
+			/* Need to wait for the VPU to confirm the free. */
+
+			/* Retain a reference on this until the VPU has
+			 * released it
+			 */
+			buffer->vpu_state = VPU_UNMAPPING;
+		} else {
+			buffer->vpu_state = VPU_NOT_MAPPED;
+			buffer->vc_handle = 0;
+		}
+	}
+}
+
+/*
+ * Release an allocation.
+ * All refcounting is done via the dma buf object.
+ *
+ * Must be called with the mutex held. The function will either release the
+ * mutex (if defering the release) or destroy it. The caller must therefore not
+ * reuse the buffer on return.
+ */
+static void vc_sm_release_resource(struct vc_sm_buffer *buffer)
+{
+	pr_debug("[%s]: buffer %p (name %s, size %zu), imported %u\n",
+		 __func__, buffer, buffer->name, buffer->size,
+		 buffer->imported);
+
+	if (buffer->vc_handle) {
+		/* We've sent the unmap request but not had the response. */
+		pr_debug("[%s]: Waiting for VPU unmap response on %p\n",
+			 __func__, buffer);
+		goto defer;
+	}
+	if (buffer->in_use) {
+		/* dmabuf still in use - we await the release */
+		pr_debug("[%s]: buffer %p is still in use\n", __func__, buffer);
+		goto defer;
+	}
+
+	/* Release the allocation (whether imported dmabuf or CMA allocation) */
+	if (buffer->imported) {
+		if (buffer->import.dma_buf)
+			dma_buf_put(buffer->import.dma_buf);
+		else
+			pr_err("%s: Imported dmabuf already been put for buf %p\n",
+			       __func__, buffer);
+		buffer->import.dma_buf = NULL;
+	} else {
+		dma_free_coherent(&sm_state->pdev->dev, buffer->size,
+				  buffer->cookie, buffer->dma_addr);
+	}
+
+	/* Free our buffer. Start by removing it from the list */
+	mutex_lock(&sm_state->map_lock);
+	list_del(&buffer->global_buffer_list);
+	mutex_unlock(&sm_state->map_lock);
+
+	pr_debug("%s: Release our allocation - done\n", __func__);
+	mutex_unlock(&buffer->lock);
+
+	mutex_destroy(&buffer->lock);
+
+	kfree(buffer);
+	return;
+
+defer:
+	mutex_unlock(&buffer->lock);
+}
+
+/* Create support for private data tracking. */
+static struct vc_sm_privdata_t *vc_sm_cma_create_priv_data(pid_t id)
+{
+	char alloc_name[32];
+	struct vc_sm_privdata_t *file_data = NULL;
+
+	/* Allocate private structure. */
+	file_data = kzalloc(sizeof(*file_data), GFP_KERNEL);
+
+	if (!file_data)
+		return NULL;
+
+	snprintf(alloc_name, sizeof(alloc_name), "%d", id);
+
+	file_data->pid = id;
+
+	return file_data;
+}
+
+/* Dma buf operations for use with our own allocations */
+
+static int vc_sm_dma_buf_attach(struct dma_buf *dmabuf,
+				struct dma_buf_attachment *attachment)
+
+{
+	struct vc_sm_dma_buf_attachment *a;
+	struct sg_table *sgt;
+	struct vc_sm_buffer *buf = dmabuf->priv;
+	struct scatterlist *rd, *wr;
+	int ret, i;
+
+	a = kzalloc(sizeof(*a), GFP_KERNEL);
+	if (!a)
+		return -ENOMEM;
+
+	pr_debug("%s dmabuf %p attachment %p\n", __func__, dmabuf, attachment);
+
+	mutex_lock(&buf->lock);
+
+	INIT_LIST_HEAD(&a->list);
+
+	sgt = &a->sg_table;
+
+	/* Copy the buf->base_sgt scatter list to the attachment, as we can't
+	 * map the same scatter list to multiple attachments at the same time.
+	 */
+	ret = sg_alloc_table(sgt, buf->alloc.sg_table->orig_nents, GFP_KERNEL);
+	if (ret) {
+		kfree(a);
+		return -ENOMEM;
+	}
+
+	rd = buf->alloc.sg_table->sgl;
+	wr = sgt->sgl;
+	for (i = 0; i < sgt->orig_nents; ++i) {
+		sg_set_page(wr, sg_page(rd), rd->length, rd->offset);
+		rd = sg_next(rd);
+		wr = sg_next(wr);
+	}
+
+	a->dma_dir = DMA_NONE;
+	attachment->priv = a;
+
+	list_add(&a->list, &buf->attachments);
+	mutex_unlock(&buf->lock);
+
+	return 0;
+}
+
+static void vc_sm_dma_buf_detach(struct dma_buf *dmabuf,
+				 struct dma_buf_attachment *attachment)
+{
+	struct vc_sm_dma_buf_attachment *a = attachment->priv;
+	struct vc_sm_buffer *buf = dmabuf->priv;
+	struct sg_table *sgt;
+
+	pr_debug("%s dmabuf %p attachment %p\n", __func__, dmabuf, attachment);
+	if (!a)
+		return;
+
+	sgt = &a->sg_table;
+
+	/* release the scatterlist cache */
+	if (a->dma_dir != DMA_NONE)
+		dma_unmap_sg(attachment->dev, sgt->sgl, sgt->orig_nents,
+			     a->dma_dir);
+	sg_free_table(sgt);
+
+	mutex_lock(&buf->lock);
+	list_del(&a->list);
+	mutex_unlock(&buf->lock);
+
+	kfree(a);
+}
+
+static struct sg_table *vc_sm_map_dma_buf(struct dma_buf_attachment *attachment,
+					  enum dma_data_direction direction)
+{
+	struct vc_sm_dma_buf_attachment *a = attachment->priv;
+	/* stealing dmabuf mutex to serialize map/unmap operations */
+	struct mutex *lock = &attachment->dmabuf->lock;
+	struct sg_table *table;
+
+	mutex_lock(lock);
+	pr_debug("%s attachment %p\n", __func__, attachment);
+	table = &a->sg_table;
+
+	/* return previously mapped sg table */
+	if (a->dma_dir == direction) {
+		mutex_unlock(lock);
+		return table;
+	}
+
+	/* release any previous cache */
+	if (a->dma_dir != DMA_NONE) {
+		dma_unmap_sg(attachment->dev, table->sgl, table->orig_nents,
+			     a->dma_dir);
+		a->dma_dir = DMA_NONE;
+	}
+
+	/* mapping to the client with new direction */
+	table->nents = dma_map_sg(attachment->dev, table->sgl,
+				  table->orig_nents, direction);
+	if (!table->nents) {
+		pr_err("failed to map scatterlist\n");
+		mutex_unlock(lock);
+		return ERR_PTR(-EIO);
+	}
+
+	a->dma_dir = direction;
+	mutex_unlock(lock);
+
+	pr_debug("%s attachment %p\n", __func__, attachment);
+	return table;
+}
+
+static void vc_sm_unmap_dma_buf(struct dma_buf_attachment *attachment,
+				struct sg_table *table,
+				enum dma_data_direction direction)
+{
+	pr_debug("%s attachment %p\n", __func__, attachment);
+	dma_unmap_sg(attachment->dev, table->sgl, table->nents, direction);
+}
+
+static int vc_sm_dmabuf_mmap(struct dma_buf *dmabuf, struct vm_area_struct *vma)
+{
+	struct vc_sm_buffer *buf = dmabuf->priv;
+	int ret;
+
+	pr_debug("%s dmabuf %p, buf %p, vm_start %08lX\n", __func__, dmabuf,
+		 buf, vma->vm_start);
+
+	mutex_lock(&buf->lock);
+
+	/* now map it to userspace */
+	vma->vm_pgoff = 0;
+
+	ret = dma_mmap_coherent(&sm_state->pdev->dev, vma, buf->cookie,
+				buf->dma_addr, buf->size);
+
+	if (ret) {
+		pr_err("Remapping memory failed, error: %d\n", ret);
+		return ret;
+	}
+
+	vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP;
+
+	mutex_unlock(&buf->lock);
+
+	if (ret)
+		pr_err("%s: failure mapping buffer to userspace\n",
+		       __func__);
+
+	return ret;
+}
+
+static void vc_sm_dma_buf_release(struct dma_buf *dmabuf)
+{
+	struct vc_sm_buffer *buffer;
+
+	if (!dmabuf)
+		return;
+
+	buffer = (struct vc_sm_buffer *)dmabuf->priv;
+
+	mutex_lock(&buffer->lock);
+
+	pr_debug("%s dmabuf %p, buffer %p\n", __func__, dmabuf, buffer);
+
+	buffer->in_use = 0;
+
+	/* Unmap on the VPU */
+	vc_sm_vpu_free(buffer);
+	pr_debug("%s vpu_free done\n", __func__);
+
+	/* Unmap our dma_buf object (the vc_sm_buffer remains until released
+	 * on the VPU).
+	 */
+	vc_sm_clean_up_dmabuf(buffer);
+	pr_debug("%s clean_up dmabuf done\n", __func__);
+
+	/* buffer->lock will be destroyed by vc_sm_release_resource if finished
+	 * with, otherwise unlocked. Do NOT unlock here.
+	 */
+	vc_sm_release_resource(buffer);
+	pr_debug("%s done\n", __func__);
+}
+
+static int vc_sm_dma_buf_begin_cpu_access(struct dma_buf *dmabuf,
+					  enum dma_data_direction direction)
+{
+	struct vc_sm_buffer *buf;
+	struct vc_sm_dma_buf_attachment *a;
+
+	if (!dmabuf)
+		return -EFAULT;
+
+	buf = dmabuf->priv;
+	if (!buf)
+		return -EFAULT;
+
+	mutex_lock(&buf->lock);
+
+	list_for_each_entry(a, &buf->attachments, list) {
+		dma_sync_sg_for_cpu(a->dev, a->sg_table.sgl,
+				    a->sg_table.nents, direction);
+	}
+	mutex_unlock(&buf->lock);
+
+	return 0;
+}
+
+static int vc_sm_dma_buf_end_cpu_access(struct dma_buf *dmabuf,
+					enum dma_data_direction direction)
+{
+	struct vc_sm_buffer *buf;
+	struct vc_sm_dma_buf_attachment *a;
+
+	if (!dmabuf)
+		return -EFAULT;
+	buf = dmabuf->priv;
+	if (!buf)
+		return -EFAULT;
+
+	mutex_lock(&buf->lock);
+
+	list_for_each_entry(a, &buf->attachments, list) {
+		dma_sync_sg_for_device(a->dev, a->sg_table.sgl,
+				       a->sg_table.nents, direction);
+	}
+	mutex_unlock(&buf->lock);
+
+	return 0;
+}
+
+static const struct dma_buf_ops dma_buf_ops = {
+	.map_dma_buf = vc_sm_map_dma_buf,
+	.unmap_dma_buf = vc_sm_unmap_dma_buf,
+	.mmap = vc_sm_dmabuf_mmap,
+	.release = vc_sm_dma_buf_release,
+	.attach = vc_sm_dma_buf_attach,
+	.detach = vc_sm_dma_buf_detach,
+	.begin_cpu_access = vc_sm_dma_buf_begin_cpu_access,
+	.end_cpu_access = vc_sm_dma_buf_end_cpu_access,
+};
+
+/* Dma_buf operations for chaining through to an imported dma_buf */
+
+static
+int vc_sm_import_dma_buf_attach(struct dma_buf *dmabuf,
+				struct dma_buf_attachment *attachment)
+{
+	struct vc_sm_buffer *buf = dmabuf->priv;
+
+	if (!buf->imported)
+		return -EINVAL;
+	return buf->import.dma_buf->ops->attach(buf->import.dma_buf,
+						attachment);
+}
+
+static
+void vc_sm_import_dma_buf_detatch(struct dma_buf *dmabuf,
+				  struct dma_buf_attachment *attachment)
+{
+	struct vc_sm_buffer *buf = dmabuf->priv;
+
+	if (!buf->imported)
+		return;
+	buf->import.dma_buf->ops->detach(buf->import.dma_buf, attachment);
+}
+
+static
+struct sg_table *vc_sm_import_map_dma_buf(struct dma_buf_attachment *attachment,
+					  enum dma_data_direction direction)
+{
+	struct vc_sm_buffer *buf = attachment->dmabuf->priv;
+
+	if (!buf->imported)
+		return NULL;
+	return buf->import.dma_buf->ops->map_dma_buf(attachment,
+						     direction);
+}
+
+static
+void vc_sm_import_unmap_dma_buf(struct dma_buf_attachment *attachment,
+				struct sg_table *table,
+				enum dma_data_direction direction)
+{
+	struct vc_sm_buffer *buf = attachment->dmabuf->priv;
+
+	if (!buf->imported)
+		return;
+	buf->import.dma_buf->ops->unmap_dma_buf(attachment, table, direction);
+}
+
+static
+int vc_sm_import_dmabuf_mmap(struct dma_buf *dmabuf, struct vm_area_struct *vma)
+{
+	struct vc_sm_buffer *buf = dmabuf->priv;
+
+	pr_debug("%s: mmap dma_buf %p, buf %p, imported db %p\n", __func__,
+		 dmabuf, buf, buf->import.dma_buf);
+	if (!buf->imported) {
+		pr_err("%s: mmap dma_buf %p- not an imported buffer\n",
+		       __func__, dmabuf);
+		return -EINVAL;
+	}
+	return buf->import.dma_buf->ops->mmap(buf->import.dma_buf, vma);
+}
+
+static
+int vc_sm_import_dma_buf_begin_cpu_access(struct dma_buf *dmabuf,
+					  enum dma_data_direction direction)
+{
+	struct vc_sm_buffer *buf = dmabuf->priv;
+
+	if (!buf->imported)
+		return -EINVAL;
+	return buf->import.dma_buf->ops->begin_cpu_access(buf->import.dma_buf,
+							  direction);
+}
+
+static
+int vc_sm_import_dma_buf_end_cpu_access(struct dma_buf *dmabuf,
+					enum dma_data_direction direction)
+{
+	struct vc_sm_buffer *buf = dmabuf->priv;
+
+	if (!buf->imported)
+		return -EINVAL;
+	return buf->import.dma_buf->ops->end_cpu_access(buf->import.dma_buf,
+							  direction);
+}
+
+static const struct dma_buf_ops dma_buf_import_ops = {
+	.map_dma_buf = vc_sm_import_map_dma_buf,
+	.unmap_dma_buf = vc_sm_import_unmap_dma_buf,
+	.mmap = vc_sm_import_dmabuf_mmap,
+	.release = vc_sm_dma_buf_release,
+	.attach = vc_sm_import_dma_buf_attach,
+	.detach = vc_sm_import_dma_buf_detatch,
+	.begin_cpu_access = vc_sm_import_dma_buf_begin_cpu_access,
+	.end_cpu_access = vc_sm_import_dma_buf_end_cpu_access,
+};
+
+/* Import a dma_buf to be shared with VC. */
+int
+vc_sm_cma_import_dmabuf_internal(struct vc_sm_privdata_t *private,
+				 struct dma_buf *dma_buf,
+				 int fd,
+				 struct dma_buf **imported_buf)
+{
+	DEFINE_DMA_BUF_EXPORT_INFO(exp_info);
+	struct vc_sm_buffer *buffer = NULL;
+	struct vc_sm_import import = { };
+	struct vc_sm_import_result result = { };
+	struct dma_buf_attachment *attach = NULL;
+	struct sg_table *sgt = NULL;
+	dma_addr_t dma_addr;
+	int ret = 0;
+	int status;
+
+	/* Setup our allocation parameters */
+	pr_debug("%s: importing dma_buf %p/fd %d\n", __func__, dma_buf, fd);
+
+	if (fd < 0)
+		get_dma_buf(dma_buf);
+	else
+		dma_buf = dma_buf_get(fd);
+
+	if (!dma_buf)
+		return -EINVAL;
+
+	attach = dma_buf_attach(dma_buf, &sm_state->pdev->dev);
+	if (IS_ERR(attach)) {
+		ret = PTR_ERR(attach);
+		goto error;
+	}
+
+	sgt = dma_buf_map_attachment(attach, DMA_BIDIRECTIONAL);
+	if (IS_ERR(sgt)) {
+		ret = PTR_ERR(sgt);
+		goto error;
+	}
+
+	/* Verify that the address block is contiguous */
+	if (sgt->nents != 1) {
+		ret = -ENOMEM;
+		goto error;
+	}
+
+	/* Allocate local buffer to track this allocation. */
+	buffer = kzalloc(sizeof(*buffer), GFP_KERNEL);
+	if (!buffer) {
+		ret = -ENOMEM;
+		goto error;
+	}
+
+	import.type = VC_SM_ALLOC_NON_CACHED;
+	dma_addr = sg_dma_address(sgt->sgl);
+	import.addr = (u32)dma_addr;
+	if ((import.addr & 0xC0000000) != 0xC0000000) {
+		pr_err("%s: Expecting an uncached alias for dma_addr %pad\n",
+		       __func__, &dma_addr);
+		import.addr |= 0xC0000000;
+	}
+	import.size = sg_dma_len(sgt->sgl);
+	import.allocator = current->tgid;
+	import.kernel_id = get_kernel_id(buffer);
+
+	memcpy(import.name, VC_SM_RESOURCE_NAME_DEFAULT,
+	       sizeof(VC_SM_RESOURCE_NAME_DEFAULT));
+
+	pr_debug("[%s]: attempt to import \"%s\" data - type %u, addr %pad, size %u.\n",
+		 __func__, import.name, import.type, &dma_addr, import.size);
+
+	/* Allocate the videocore buffer. */
+	status = vc_sm_cma_vchi_import(sm_state->sm_handle, &import, &result,
+				       &sm_state->int_trans_id);
+	if (status == -EINTR) {
+		pr_debug("[%s]: requesting import memory action restart (trans_id: %u)\n",
+			 __func__, sm_state->int_trans_id);
+		ret = -ERESTARTSYS;
+		private->restart_sys = -EINTR;
+		private->int_action = VC_SM_MSG_TYPE_IMPORT;
+		goto error;
+	} else if (status || !result.res_handle) {
+		pr_debug("[%s]: failed to import memory on videocore (status: %u, trans_id: %u)\n",
+			 __func__, status, sm_state->int_trans_id);
+		ret = -ENOMEM;
+		goto error;
+	}
+
+	mutex_init(&buffer->lock);
+	INIT_LIST_HEAD(&buffer->attachments);
+	memcpy(buffer->name, import.name,
+	       min(sizeof(buffer->name), sizeof(import.name) - 1));
+
+	/* Keep track of the buffer we created. */
+	buffer->private = private;
+	buffer->vc_handle = result.res_handle;
+	buffer->size = import.size;
+	buffer->vpu_state = VPU_MAPPED;
+
+	buffer->imported = 1;
+	buffer->import.dma_buf = dma_buf;
+
+	buffer->import.attach = attach;
+	buffer->import.sgt = sgt;
+	buffer->dma_addr = dma_addr;
+	buffer->in_use = 1;
+	buffer->kernel_id = import.kernel_id;
+
+	/*
+	 * We're done - we need to export a new dmabuf chaining through most
+	 * functions, but enabling us to release our own internal references
+	 * here.
+	 */
+	exp_info.ops = &dma_buf_import_ops;
+	exp_info.size = import.size;
+	exp_info.flags = O_RDWR;
+	exp_info.priv = buffer;
+
+	buffer->dma_buf = dma_buf_export(&exp_info);
+	if (IS_ERR(buffer->dma_buf)) {
+		ret = PTR_ERR(buffer->dma_buf);
+		goto error;
+	}
+
+	vc_sm_add_resource(private, buffer);
+
+	*imported_buf = buffer->dma_buf;
+
+	return 0;
+
+error:
+	if (result.res_handle) {
+		struct vc_sm_free_t free = { result.res_handle, 0 };
+
+		vc_sm_cma_vchi_free(sm_state->sm_handle, &free,
+				    &sm_state->int_trans_id);
+	}
+	free_kernel_id(import.kernel_id);
+	kfree(buffer);
+	if (sgt)
+		dma_buf_unmap_attachment(attach, sgt, DMA_BIDIRECTIONAL);
+	if (attach)
+		dma_buf_detach(dma_buf, attach);
+	dma_buf_put(dma_buf);
+	return ret;
+}
+
+static int vc_sm_cma_vpu_alloc(u32 size, u32 align, const char *name,
+			       u32 mem_handle, struct vc_sm_buffer **ret_buffer)
+{
+	DEFINE_DMA_BUF_EXPORT_INFO(exp_info);
+	struct vc_sm_buffer *buffer = NULL;
+	struct sg_table *sgt;
+	int aligned_size;
+	int ret = 0;
+
+	/* Align to the user requested align */
+	aligned_size = ALIGN(size, align);
+	/* and then to a page boundary */
+	aligned_size = PAGE_ALIGN(aligned_size);
+
+	if (!aligned_size)
+		return -EINVAL;
+
+	/* Allocate local buffer to track this allocation. */
+	buffer = kzalloc(sizeof(*buffer), GFP_KERNEL);
+	if (!buffer)
+		return -ENOMEM;
+
+	mutex_init(&buffer->lock);
+	/* Acquire the mutex as vc_sm_release_resource will release it in the
+	 * error path.
+	 */
+	mutex_lock(&buffer->lock);
+
+	buffer->cookie = dma_alloc_coherent(&sm_state->pdev->dev,
+					    aligned_size, &buffer->dma_addr,
+					    GFP_KERNEL);
+	if (!buffer->cookie) {
+		pr_err("[%s]: dma_alloc_coherent alloc of %d bytes failed\n",
+		       __func__, aligned_size);
+		ret = -ENOMEM;
+		goto error;
+	}
+
+	pr_debug("[%s]: alloc of %d bytes success\n",
+		 __func__, aligned_size);
+
+	sgt = kmalloc(sizeof(*sgt), GFP_KERNEL);
+	if (!sgt) {
+		ret = -ENOMEM;
+		goto error;
+	}
+
+	ret = dma_get_sgtable(&sm_state->pdev->dev, sgt, buffer->cookie,
+			      buffer->dma_addr, buffer->size);
+	if (ret < 0) {
+		pr_err("failed to get scatterlist from DMA API\n");
+		kfree(sgt);
+		ret = -ENOMEM;
+		goto error;
+	}
+	buffer->alloc.sg_table = sgt;
+
+	INIT_LIST_HEAD(&buffer->attachments);
+
+	memcpy(buffer->name, name,
+	       min(sizeof(buffer->name), strlen(name)));
+
+	exp_info.ops = &dma_buf_ops;
+	exp_info.size = aligned_size;
+	exp_info.flags = O_RDWR;
+	exp_info.priv = buffer;
+
+	buffer->dma_buf = dma_buf_export(&exp_info);
+	if (IS_ERR(buffer->dma_buf)) {
+		ret = PTR_ERR(buffer->dma_buf);
+		goto error;
+	}
+	buffer->dma_addr = (u32)sg_dma_address(buffer->alloc.sg_table->sgl);
+	if ((buffer->dma_addr & 0xC0000000) != 0xC0000000) {
+		pr_warn_once("%s: Expecting an uncached alias for dma_addr %pad\n",
+			     __func__, &buffer->dma_addr);
+		buffer->dma_addr |= 0xC0000000;
+	}
+	buffer->private = sm_state->vpu_allocs;
+
+	buffer->vc_handle = mem_handle;
+	buffer->vpu_state = VPU_MAPPED;
+	buffer->vpu_allocated = 1;
+	buffer->size = size;
+	/*
+	 * Create an ID that will be passed along with our message so
+	 * that when we service the release reply, we can look up which
+	 * resource is being released.
+	 */
+	buffer->kernel_id = get_kernel_id(buffer);
+
+	vc_sm_add_resource(sm_state->vpu_allocs, buffer);
+
+	mutex_unlock(&buffer->lock);
+
+	*ret_buffer = buffer;
+	return 0;
+error:
+	if (buffer)
+		vc_sm_release_resource(buffer);
+	return ret;
+}
+
+static void
+vc_sm_vpu_event(struct sm_instance *instance, struct vc_sm_result_t *reply,
+		int reply_len)
+{
+	switch (reply->trans_id & ~0x80000000) {
+	case VC_SM_MSG_TYPE_CLIENT_VERSION:
+	{
+		/* Acknowledge that the firmware supports the version command */
+		pr_debug("%s: firmware acked version msg. Require release cb\n",
+			 __func__);
+		sm_state->require_released_callback = true;
+	}
+	break;
+	case VC_SM_MSG_TYPE_RELEASED:
+	{
+		struct vc_sm_released *release = (struct vc_sm_released *)reply;
+		struct vc_sm_buffer *buffer =
+					lookup_kernel_id(release->kernel_id);
+		if (!buffer) {
+			pr_err("%s: VC released a buffer that is already released, kernel_id %d\n",
+			       __func__, release->kernel_id);
+			break;
+		}
+		mutex_lock(&buffer->lock);
+
+		pr_debug("%s: Released addr %08x, size %u, id %08x, mem_handle %08x\n",
+			 __func__, release->addr, release->size,
+			 release->kernel_id, release->vc_handle);
+
+		buffer->vc_handle = 0;
+		buffer->vpu_state = VPU_NOT_MAPPED;
+		free_kernel_id(release->kernel_id);
+
+		if (buffer->vpu_allocated) {
+			/* VPU allocation, so release the dmabuf which will
+			 * trigger the clean up.
+			 */
+			mutex_unlock(&buffer->lock);
+			dma_buf_put(buffer->dma_buf);
+		} else {
+			vc_sm_release_resource(buffer);
+		}
+	}
+	break;
+	case VC_SM_MSG_TYPE_VC_MEM_REQUEST:
+	{
+		struct vc_sm_buffer *buffer = NULL;
+		struct vc_sm_vc_mem_request *req =
+					(struct vc_sm_vc_mem_request *)reply;
+		struct vc_sm_vc_mem_request_result reply;
+		int ret;
+
+		pr_debug("%s: Request %u bytes of memory, align %d name %s, trans_id %08x\n",
+			 __func__, req->size, req->align, req->name,
+			 req->trans_id);
+		ret = vc_sm_cma_vpu_alloc(req->size, req->align, req->name,
+					  req->vc_handle, &buffer);
+
+		reply.trans_id = req->trans_id;
+		if (!ret) {
+			reply.addr = buffer->dma_addr;
+			reply.kernel_id = buffer->kernel_id;
+			pr_debug("%s: Allocated resource buffer %p, addr %pad\n",
+				 __func__, buffer, &buffer->dma_addr);
+		} else {
+			pr_err("%s: Allocation failed size %u, name %s, vc_handle %u\n",
+			       __func__, req->size, req->name, req->vc_handle);
+			reply.addr = 0;
+			reply.kernel_id = 0;
+		}
+		vc_sm_vchi_client_vc_mem_req_reply(sm_state->sm_handle, &reply,
+						   &sm_state->int_trans_id);
+		break;
+	}
+	break;
+	default:
+		pr_err("%s: Unknown vpu cmd %x\n", __func__, reply->trans_id);
+		break;
+	}
+}
+
+/* Userspace handling */
+/*
+ * Open the device.  Creates a private state to help track all allocation
+ * associated with this device.
+ */
+static int vc_sm_cma_open(struct inode *inode, struct file *file)
+{
+	/* Make sure the device was started properly. */
+	if (!sm_state) {
+		pr_err("[%s]: invalid device\n", __func__);
+		return -EPERM;
+	}
+
+	file->private_data = vc_sm_cma_create_priv_data(current->tgid);
+	if (!file->private_data) {
+		pr_err("[%s]: failed to create data tracker\n", __func__);
+
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
+/*
+ * Close the vcsm-cma device.
+ * All allocations are file descriptors to the dmabuf objects, so we will get
+ * the clean up request on those as those are cleaned up.
+ */
+static int vc_sm_cma_release(struct inode *inode, struct file *file)
+{
+	struct vc_sm_privdata_t *file_data =
+	    (struct vc_sm_privdata_t *)file->private_data;
+	int ret = 0;
+
+	/* Make sure the device was started properly. */
+	if (!sm_state || !file_data) {
+		pr_err("[%s]: invalid device\n", __func__);
+		ret = -EPERM;
+		goto out;
+	}
+
+	pr_debug("[%s]: using private data %p\n", __func__, file_data);
+
+	/* Terminate the private data. */
+	kfree(file_data);
+
+out:
+	return ret;
+}
+
+/*
+ * Allocate a shared memory handle and block.
+ * Allocation is from CMA, and then imported into the VPU mappings.
+ */
+int vc_sm_cma_ioctl_alloc(struct vc_sm_privdata_t *private,
+			  struct vc_sm_cma_ioctl_alloc *ioparam)
+{
+	DEFINE_DMA_BUF_EXPORT_INFO(exp_info);
+	struct vc_sm_buffer *buffer = NULL;
+	struct vc_sm_import import = { 0 };
+	struct vc_sm_import_result result = { 0 };
+	struct dma_buf *dmabuf = NULL;
+	struct sg_table *sgt;
+	int aligned_size;
+	int ret = 0;
+	int status;
+	int fd = -1;
+
+	aligned_size = PAGE_ALIGN(ioparam->size);
+
+	if (!aligned_size)
+		return -EINVAL;
+
+	/* Allocate local buffer to track this allocation. */
+	buffer = kzalloc(sizeof(*buffer), GFP_KERNEL);
+	if (!buffer) {
+		ret = -ENOMEM;
+		goto error;
+	}
+
+	buffer->cookie = dma_alloc_coherent(&sm_state->pdev->dev,
+					    aligned_size,
+					    &buffer->dma_addr,
+					    GFP_KERNEL);
+	if (!buffer->cookie) {
+		pr_err("[%s]: dma_alloc_coherent alloc of %d bytes failed\n",
+		       __func__, aligned_size);
+		ret = -ENOMEM;
+		goto error;
+	}
+
+	import.type = VC_SM_ALLOC_NON_CACHED;
+	import.allocator = current->tgid;
+
+	if (*ioparam->name)
+		memcpy(import.name, ioparam->name, sizeof(import.name) - 1);
+	else
+		memcpy(import.name, VC_SM_RESOURCE_NAME_DEFAULT,
+		       sizeof(VC_SM_RESOURCE_NAME_DEFAULT));
+
+	mutex_init(&buffer->lock);
+	INIT_LIST_HEAD(&buffer->attachments);
+	memcpy(buffer->name, import.name,
+	       min(sizeof(buffer->name), sizeof(import.name) - 1));
+
+	exp_info.ops = &dma_buf_ops;
+	exp_info.size = aligned_size;
+	exp_info.flags = O_RDWR;
+	exp_info.priv = buffer;
+
+	dmabuf = dma_buf_export(&exp_info);
+	if (IS_ERR(dmabuf)) {
+		ret = PTR_ERR(dmabuf);
+		goto error;
+	}
+	buffer->dma_buf = dmabuf;
+
+	import.addr = buffer->dma_addr;
+	import.size = aligned_size;
+	import.kernel_id = get_kernel_id(buffer);
+
+	/* Wrap it into a videocore buffer. */
+	status = vc_sm_cma_vchi_import(sm_state->sm_handle, &import, &result,
+				       &sm_state->int_trans_id);
+	if (status == -EINTR) {
+		pr_debug("[%s]: requesting import memory action restart (trans_id: %u)\n",
+			 __func__, sm_state->int_trans_id);
+		ret = -ERESTARTSYS;
+		private->restart_sys = -EINTR;
+		private->int_action = VC_SM_MSG_TYPE_IMPORT;
+		goto error;
+	} else if (status || !result.res_handle) {
+		pr_err("[%s]: failed to import memory on videocore (status: %u, trans_id: %u)\n",
+		       __func__, status, sm_state->int_trans_id);
+		ret = -ENOMEM;
+		goto error;
+	}
+
+	/* Keep track of the buffer we created. */
+	buffer->private = private;
+	buffer->vc_handle = result.res_handle;
+	buffer->size = import.size;
+	buffer->vpu_state = VPU_MAPPED;
+	buffer->kernel_id = import.kernel_id;
+
+	sgt = kmalloc(sizeof(*sgt), GFP_KERNEL);
+	if (!sgt) {
+		ret = -ENOMEM;
+		goto error;
+	}
+
+	ret = dma_get_sgtable(&sm_state->pdev->dev, sgt, buffer->cookie,
+			      buffer->dma_addr, buffer->size);
+	if (ret < 0) {
+		/* FIXME: error handling */
+		pr_err("failed to get scatterlist from DMA API\n");
+		kfree(sgt);
+		ret = -ENOMEM;
+		goto error;
+	}
+	buffer->alloc.sg_table = sgt;
+
+	fd = dma_buf_fd(dmabuf, O_CLOEXEC);
+	if (fd < 0)
+		goto error;
+
+	vc_sm_add_resource(private, buffer);
+
+	pr_debug("[%s]: Added resource as fd %d, buffer %p, private %p, dma_addr %pad\n",
+		 __func__, fd, buffer, private, &buffer->dma_addr);
+
+	/* We're done */
+	ioparam->handle = fd;
+	ioparam->vc_handle = buffer->vc_handle;
+	ioparam->dma_addr = buffer->dma_addr;
+	return 0;
+
+error:
+	pr_err("[%s]: something failed - cleanup. ret %d\n", __func__, ret);
+
+	if (dmabuf) {
+		/* dmabuf has been exported, therefore allow dmabuf cleanup to
+		 * deal with this
+		 */
+		dma_buf_put(dmabuf);
+	} else {
+		/* No dmabuf, therefore just free the buffer here */
+		if (buffer->cookie)
+			dma_free_coherent(&sm_state->pdev->dev, buffer->size,
+					  buffer->cookie, buffer->dma_addr);
+		kfree(buffer);
+	}
+	return ret;
+}
+
+#ifndef CONFIG_ARM64
+/* Converts VCSM_CACHE_OP_* to an operating function. */
+static void (*cache_op_to_func(const unsigned int cache_op))
+						(const void*, const void*)
+{
+	switch (cache_op) {
+	case VC_SM_CACHE_OP_NOP:
+		return NULL;
+
+	case VC_SM_CACHE_OP_INV:
+	case VC_SM_CACHE_OP_CLEAN:
+	case VC_SM_CACHE_OP_FLUSH:
+		return dmac_flush_range;
+
+	default:
+		pr_err("[%s]: Invalid cache_op: 0x%08x\n", __func__, cache_op);
+		return NULL;
+	}
+}
+
+/*
+ * Clean/invalid/flush cache of which buffer is already pinned (i.e. accessed).
+ */
+static int clean_invalid_contig_2d(const void __user *addr,
+				   const size_t block_count,
+				   const size_t block_size,
+				   const size_t stride,
+				   const unsigned int cache_op)
+{
+	size_t i;
+	void (*op_fn)(const void *start, const void *end);
+
+	if (!block_size) {
+		pr_err("[%s]: size cannot be 0\n", __func__);
+		return -EINVAL;
+	}
+
+	op_fn = cache_op_to_func(cache_op);
+	if (!op_fn)
+		return -EINVAL;
+
+	for (i = 0; i < block_count; i ++, addr += stride)
+		op_fn(addr, addr + block_size);
+
+	return 0;
+}
+
+static int vc_sm_cma_clean_invalid2(unsigned int cmdnr, unsigned long arg)
+{
+	struct vc_sm_cma_ioctl_clean_invalid2 ioparam;
+	struct vc_sm_cma_ioctl_clean_invalid_block *block = NULL;
+	int i, ret = 0;
+
+	/* Get parameter data. */
+	if (copy_from_user(&ioparam, (void *)arg, sizeof(ioparam))) {
+		pr_err("[%s]: failed to copy-from-user header for cmd %x\n",
+		       __func__, cmdnr);
+		return -EFAULT;
+	}
+	block = kmalloc(ioparam.op_count * sizeof(*block), GFP_KERNEL);
+	if (!block)
+		return -EFAULT;
+
+	if (copy_from_user(block, (void *)(arg + sizeof(ioparam)),
+			   ioparam.op_count * sizeof(*block)) != 0) {
+		pr_err("[%s]: failed to copy-from-user payload for cmd %x\n",
+		       __func__, cmdnr);
+		ret = -EFAULT;
+		goto out;
+	}
+
+	for (i = 0; i < ioparam.op_count; i++) {
+		const struct vc_sm_cma_ioctl_clean_invalid_block * const op =
+								block + i;
+
+		if (op->invalidate_mode == VC_SM_CACHE_OP_NOP)
+			continue;
+
+		ret = clean_invalid_contig_2d((void __user *)op->start_address,
+					      op->block_count, op->block_size,
+					      op->inter_block_stride,
+					      op->invalidate_mode);
+		if (ret)
+			break;
+	}
+out:
+	kfree(block);
+
+	return ret;
+}
+#endif
+
+static long vc_sm_cma_ioctl(struct file *file, unsigned int cmd,
+			    unsigned long arg)
+{
+	int ret = 0;
+	unsigned int cmdnr = _IOC_NR(cmd);
+	struct vc_sm_privdata_t *file_data =
+	    (struct vc_sm_privdata_t *)file->private_data;
+
+	/* Validate we can work with this device. */
+	if (!sm_state || !file_data) {
+		pr_err("[%s]: invalid device\n", __func__);
+		return -EPERM;
+	}
+
+	/* Action is a re-post of a previously interrupted action? */
+	if (file_data->restart_sys == -EINTR) {
+		pr_debug("[%s]: clean up of action %u (trans_id: %u) following EINTR\n",
+			 __func__, file_data->int_action,
+			 file_data->int_trans_id);
+
+		file_data->restart_sys = 0;
+	}
+
+	/* Now process the command. */
+	switch (cmdnr) {
+		/* New memory allocation.
+		 */
+	case VC_SM_CMA_CMD_ALLOC:
+	{
+		struct vc_sm_cma_ioctl_alloc ioparam;
+
+		/* Get the parameter data. */
+		if (copy_from_user
+		    (&ioparam, (void *)arg, sizeof(ioparam)) != 0) {
+			pr_err("[%s]: failed to copy-from-user for cmd %x\n",
+			       __func__, cmdnr);
+			ret = -EFAULT;
+			break;
+		}
+
+		ret = vc_sm_cma_ioctl_alloc(file_data, &ioparam);
+		if (!ret &&
+		    (copy_to_user((void *)arg, &ioparam,
+				  sizeof(ioparam)) != 0)) {
+			/* FIXME: Release allocation */
+			pr_err("[%s]: failed to copy-to-user for cmd %x\n",
+			       __func__, cmdnr);
+			ret = -EFAULT;
+		}
+		break;
+	}
+
+	case VC_SM_CMA_CMD_IMPORT_DMABUF:
+	{
+		struct vc_sm_cma_ioctl_import_dmabuf ioparam;
+		struct dma_buf *new_dmabuf;
+
+		/* Get the parameter data. */
+		if (copy_from_user
+		    (&ioparam, (void *)arg, sizeof(ioparam)) != 0) {
+			pr_err("[%s]: failed to copy-from-user for cmd %x\n",
+			       __func__, cmdnr);
+			ret = -EFAULT;
+			break;
+		}
+
+		ret = vc_sm_cma_import_dmabuf_internal(file_data,
+						       NULL,
+						       ioparam.dmabuf_fd,
+						       &new_dmabuf);
+
+		if (!ret) {
+			struct vc_sm_buffer *buf = new_dmabuf->priv;
+
+			ioparam.size = buf->size;
+			ioparam.handle = dma_buf_fd(new_dmabuf,
+						    O_CLOEXEC);
+			ioparam.vc_handle = buf->vc_handle;
+			ioparam.dma_addr = buf->dma_addr;
+
+			if (ioparam.handle < 0 ||
+			    (copy_to_user((void *)arg, &ioparam,
+					  sizeof(ioparam)) != 0)) {
+				dma_buf_put(new_dmabuf);
+				/* FIXME: Release allocation */
+				ret = -EFAULT;
+			}
+		}
+		break;
+	}
+
+#ifndef CONFIG_ARM64
+	/*
+	 * Flush/Invalidate the cache for a given mapping.
+	 * Blocks must be pinned (i.e. accessed) before this call.
+	 */
+	case VC_SM_CMA_CMD_CLEAN_INVALID2:
+		ret = vc_sm_cma_clean_invalid2(cmdnr, arg);
+		break;
+#endif
+
+	default:
+		pr_debug("[%s]: cmd %x tgid %u, owner %u\n", __func__, cmdnr,
+			 current->tgid, file_data->pid);
+
+		ret = -EINVAL;
+		break;
+	}
+
+	return ret;
+}
+
+#ifdef CONFIG_COMPAT
+struct vc_sm_cma_ioctl_clean_invalid2_32 {
+	u32 op_count;
+	struct vc_sm_cma_ioctl_clean_invalid_block_32 {
+		u16 invalidate_mode;
+		u16 block_count;
+		compat_uptr_t start_address;
+		u32 block_size;
+		u32 inter_block_stride;
+	} s[0];
+};
+
+#define VC_SM_CMA_CMD_CLEAN_INVALID2_32\
+	_IOR(VC_SM_CMA_MAGIC_TYPE, VC_SM_CMA_CMD_CLEAN_INVALID2,\
+	 struct vc_sm_cma_ioctl_clean_invalid2_32)
+
+static long vc_sm_cma_compat_ioctl(struct file *file, unsigned int cmd,
+				   unsigned long arg)
+{
+	switch (cmd) {
+	case VC_SM_CMA_CMD_CLEAN_INVALID2_32:
+		/* FIXME */
+		return -EINVAL;
+
+	default:
+		return vc_sm_cma_ioctl(file, cmd, arg);
+	}
+}
+#endif
+
+/* Device operations that we managed in this driver. */
+static const struct file_operations vc_sm_ops = {
+	.owner = THIS_MODULE,
+	.unlocked_ioctl = vc_sm_cma_ioctl,
+#ifdef CONFIG_COMPAT
+	.compat_ioctl = vc_sm_cma_compat_ioctl,
+#endif
+	.open = vc_sm_cma_open,
+	.release = vc_sm_cma_release,
+};
+
+/* Driver load/unload functions */
+/* Videocore connected.  */
+static void vc_sm_connected_init(void)
+{
+	int ret;
+	struct vchiq_instance *vchiq_instance;
+	struct vc_sm_version version;
+	struct vc_sm_result_t version_result;
+
+	pr_info("[%s]: start\n", __func__);
+
+	/*
+	 * Initialize and create a VCHI connection for the shared memory service
+	 * running on videocore.
+	 */
+	ret = vchiq_initialise(&vchiq_instance);
+	if (ret) {
+		pr_err("[%s]: failed to initialise VCHI instance (ret=%d)\n",
+		       __func__, ret);
+
+		return;
+	}
+
+	ret = vchiq_connect(vchiq_instance);
+	if (ret) {
+		pr_err("[%s]: failed to connect VCHI instance (ret=%d)\n",
+		       __func__, ret);
+
+		return;
+	}
+
+	/* Initialize an instance of the shared memory service. */
+	sm_state->sm_handle = vc_sm_cma_vchi_init(vchiq_instance, 1,
+						  vc_sm_vpu_event);
+	if (!sm_state->sm_handle) {
+		pr_err("[%s]: failed to initialize shared memory service\n",
+		       __func__);
+
+		return;
+	}
+
+	/* Create a debug fs directory entry (root). */
+	sm_state->dir_root = debugfs_create_dir(VC_SM_DIR_ROOT_NAME, NULL);
+
+	sm_state->dir_state.show = &vc_sm_cma_global_state_show;
+	sm_state->dir_state.dir_entry =
+		debugfs_create_file(VC_SM_STATE, 0444, sm_state->dir_root,
+				    &sm_state->dir_state,
+				    &vc_sm_cma_debug_fs_fops);
+
+	INIT_LIST_HEAD(&sm_state->buffer_list);
+
+	/* Create a shared memory device. */
+	sm_state->misc_dev.minor = MISC_DYNAMIC_MINOR;
+	sm_state->misc_dev.name = DEVICE_NAME;
+	sm_state->misc_dev.fops = &vc_sm_ops;
+	sm_state->misc_dev.parent = NULL;
+	/* Temporarily set as 666 until udev rules have been sorted */
+	sm_state->misc_dev.mode = 0666;
+	ret = misc_register(&sm_state->misc_dev);
+	if (ret) {
+		pr_err("vcsm-cma: failed to register misc device.\n");
+		goto err_remove_debugfs;
+	}
+
+	sm_state->data_knl = vc_sm_cma_create_priv_data(0);
+	if (!sm_state->data_knl) {
+		pr_err("[%s]: failed to create kernel private data tracker\n",
+		       __func__);
+		goto err_remove_misc_dev;
+	}
+
+	version.version = 2;
+	ret = vc_sm_cma_vchi_client_version(sm_state->sm_handle, &version,
+					    &version_result,
+					    &sm_state->int_trans_id);
+	if (ret) {
+		pr_err("[%s]: Failed to send version request %d\n", __func__,
+		       ret);
+	}
+
+	/* Done! */
+	sm_inited = 1;
+	pr_info("[%s]: installed successfully\n", __func__);
+	return;
+
+err_remove_misc_dev:
+	misc_deregister(&sm_state->misc_dev);
+err_remove_debugfs:
+	debugfs_remove_recursive(sm_state->dir_root);
+	vc_sm_cma_vchi_stop(&sm_state->sm_handle);
+}
+
+/* Driver loading. */
+static int bcm2835_vc_sm_cma_probe(struct platform_device *pdev)
+{
+	pr_info("%s: Videocore shared memory driver\n", __func__);
+
+	sm_state = devm_kzalloc(&pdev->dev, sizeof(*sm_state), GFP_KERNEL);
+	if (!sm_state)
+		return -ENOMEM;
+	sm_state->pdev = pdev;
+	mutex_init(&sm_state->map_lock);
+
+	spin_lock_init(&sm_state->kernelid_map_lock);
+	idr_init_base(&sm_state->kernelid_map, 1);
+
+	pdev->dev.dma_parms = devm_kzalloc(&pdev->dev,
+					   sizeof(*pdev->dev.dma_parms),
+					   GFP_KERNEL);
+	/* dma_set_max_seg_size checks if dma_parms is NULL. */
+	dma_set_max_seg_size(&pdev->dev, 0x3FFFFFFF);
+
+	vchiq_add_connected_callback(vc_sm_connected_init);
+	return 0;
+}
+
+/* Driver unloading. */
+static int bcm2835_vc_sm_cma_remove(struct platform_device *pdev)
+{
+	pr_debug("[%s]: start\n", __func__);
+	if (sm_inited) {
+		misc_deregister(&sm_state->misc_dev);
+
+		/* Remove all proc entries. */
+		debugfs_remove_recursive(sm_state->dir_root);
+
+		/* Stop the videocore shared memory service. */
+		vc_sm_cma_vchi_stop(&sm_state->sm_handle);
+	}
+
+	if (sm_state) {
+		idr_destroy(&sm_state->kernelid_map);
+
+		/* Free the memory for the state structure. */
+		mutex_destroy(&sm_state->map_lock);
+	}
+
+	pr_debug("[%s]: end\n", __func__);
+	return 0;
+}
+
+/* Kernel API calls */
+/* Get an internal resource handle mapped from the external one. */
+int vc_sm_cma_int_handle(void *handle)
+{
+	struct dma_buf *dma_buf = (struct dma_buf *)handle;
+	struct vc_sm_buffer *buf;
+
+	/* Validate we can work with this device. */
+	if (!sm_state || !handle) {
+		pr_err("[%s]: invalid input\n", __func__);
+		return 0;
+	}
+
+	buf = (struct vc_sm_buffer *)dma_buf->priv;
+	return buf->vc_handle;
+}
+EXPORT_SYMBOL_GPL(vc_sm_cma_int_handle);
+
+/* Free a previously allocated shared memory handle and block. */
+int vc_sm_cma_free(void *handle)
+{
+	struct dma_buf *dma_buf = (struct dma_buf *)handle;
+
+	/* Validate we can work with this device. */
+	if (!sm_state || !handle) {
+		pr_err("[%s]: invalid input\n", __func__);
+		return -EPERM;
+	}
+
+	pr_debug("%s: handle %p/dmabuf %p\n", __func__, handle, dma_buf);
+
+	dma_buf_put(dma_buf);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(vc_sm_cma_free);
+
+/* Import a dmabuf to be shared with VC. */
+int vc_sm_cma_import_dmabuf(struct dma_buf *src_dmabuf, void **handle)
+{
+	struct dma_buf *new_dma_buf;
+	int ret;
+
+	/* Validate we can work with this device. */
+	if (!sm_state || !src_dmabuf || !handle) {
+		pr_err("[%s]: invalid input\n", __func__);
+		return -EPERM;
+	}
+
+	ret = vc_sm_cma_import_dmabuf_internal(sm_state->data_knl, src_dmabuf,
+					       -1, &new_dma_buf);
+
+	if (!ret) {
+		pr_debug("%s: imported to ptr %p\n", __func__, new_dma_buf);
+
+		/* Assign valid handle at this time.*/
+		*handle = new_dma_buf;
+	} else {
+		/*
+		 * succeeded in importing the dma_buf, but then
+		 * failed to look it up again. How?
+		 * Release the fd again.
+		 */
+		pr_err("%s: imported vc_sm_cma_get_buffer failed %d\n",
+		       __func__, ret);
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(vc_sm_cma_import_dmabuf);
+
+static struct platform_driver bcm2835_vcsm_cma_driver = {
+	.probe = bcm2835_vc_sm_cma_probe,
+	.remove = bcm2835_vc_sm_cma_remove,
+	.driver = {
+		   .name = DEVICE_NAME,
+		   .owner = THIS_MODULE,
+		   },
+};
+
+module_platform_driver(bcm2835_vcsm_cma_driver);
+
+MODULE_AUTHOR("Dave Stevenson");
+MODULE_DESCRIPTION("VideoCore CMA Shared Memory Driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:vcsm-cma");
diff -Nur --no-dereference linux-5.10.52-orig/drivers/staging/vc04_services/vc-sm-cma/vc_sm_cma_vchi.c linux-5.10.52-v7l+/drivers/staging/vc04_services/vc-sm-cma/vc_sm_cma_vchi.c
--- linux-5.10.52-orig/drivers/staging/vc04_services/vc-sm-cma/vc_sm_cma_vchi.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/staging/vc04_services/vc-sm-cma/vc_sm_cma_vchi.c	2021-07-25 16:46:26.567945487 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * VideoCore Shared Memory CMA allocator
+ *
+ * Copyright: 2018, Raspberry Pi (Trading) Ltd
+ * Copyright 2011-2012 Broadcom Corporation.  All rights reserved.
+ *
+ * Based on vmcs_sm driver from Broadcom Corporation.
+ *
+ */
+
+/* ---- Include Files ----------------------------------------------------- */
+#include <linux/completion.h>
+#include <linux/kernel.h>
+#include <linux/kthread.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/semaphore.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+
+#include "vc_sm_cma_vchi.h"
+
+#define VC_SM_VER  1
+#define VC_SM_MIN_VER 0
+
+/* ---- Private Constants and Types -------------------------------------- */
+
+/* Command blocks come from a pool */
+#define SM_MAX_NUM_CMD_RSP_BLKS 32
+
+/* The number of supported connections */
+#define SM_MAX_NUM_CONNECTIONS 3
+
+struct sm_cmd_rsp_blk {
+	struct list_head head;	/* To create lists */
+	/* To be signaled when the response is there */
+	struct completion cmplt;
+
+	u32 id;
+	u16 length;
+
+	u8 msg[VC_SM_MAX_MSG_LEN];
+
+	uint32_t wait:1;
+	uint32_t sent:1;
+	uint32_t alloc:1;
+
+};
+
+struct sm_instance {
+	u32 num_connections;
+	unsigned int service_handle[SM_MAX_NUM_CONNECTIONS];
+	struct task_struct *io_thread;
+	struct completion io_cmplt;
+
+	vpu_event_cb vpu_event;
+
+	/* Mutex over the following lists */
+	struct mutex lock;
+	u32 trans_id;
+	struct list_head cmd_list;
+	struct list_head rsp_list;
+	struct list_head dead_list;
+
+	struct sm_cmd_rsp_blk free_blk[SM_MAX_NUM_CMD_RSP_BLKS];
+
+	/* Mutex over the free_list */
+	struct mutex free_lock;
+	struct list_head free_list;
+
+	struct semaphore free_sema;
+
+};
+
+/* ---- Private Variables ------------------------------------------------ */
+
+/* ---- Private Function Prototypes -------------------------------------- */
+
+/* ---- Private Functions ------------------------------------------------ */
+static int
+bcm2835_vchi_msg_queue(unsigned int handle,
+		       void *data,
+		       unsigned int size)
+{
+	return vchiq_queue_kernel_message(handle, data, size);
+}
+
+static struct
+sm_cmd_rsp_blk *vc_vchi_cmd_create(struct sm_instance *instance,
+				   enum vc_sm_msg_type id, void *msg,
+				   u32 size, int wait)
+{
+	struct sm_cmd_rsp_blk *blk;
+	struct vc_sm_msg_hdr_t *hdr;
+
+	if (down_interruptible(&instance->free_sema)) {
+		blk = kmalloc(sizeof(*blk), GFP_KERNEL);
+		if (!blk)
+			return NULL;
+
+		blk->alloc = 1;
+		init_completion(&blk->cmplt);
+	} else {
+		mutex_lock(&instance->free_lock);
+		blk =
+		    list_first_entry(&instance->free_list,
+				     struct sm_cmd_rsp_blk, head);
+		list_del(&blk->head);
+		mutex_unlock(&instance->free_lock);
+	}
+
+	blk->sent = 0;
+	blk->wait = wait;
+	blk->length = sizeof(*hdr) + size;
+
+	hdr = (struct vc_sm_msg_hdr_t *)blk->msg;
+	hdr->type = id;
+	mutex_lock(&instance->lock);
+	instance->trans_id++;
+	/*
+	 * Retain the top bit for identifying asynchronous events, or VPU cmds.
+	 */
+	instance->trans_id &= ~0x80000000;
+	hdr->trans_id = instance->trans_id;
+	blk->id = instance->trans_id;
+	mutex_unlock(&instance->lock);
+
+	if (size)
+		memcpy(hdr->body, msg, size);
+
+	return blk;
+}
+
+static void
+vc_vchi_cmd_delete(struct sm_instance *instance, struct sm_cmd_rsp_blk *blk)
+{
+	if (blk->alloc) {
+		kfree(blk);
+		return;
+	}
+
+	mutex_lock(&instance->free_lock);
+	list_add(&blk->head, &instance->free_list);
+	mutex_unlock(&instance->free_lock);
+	up(&instance->free_sema);
+}
+
+static void vc_sm_cma_vchi_rx_ack(struct sm_instance *instance,
+				  struct sm_cmd_rsp_blk *cmd,
+				  struct vc_sm_result_t *reply,
+				  u32 reply_len)
+{
+	mutex_lock(&instance->lock);
+	list_for_each_entry(cmd,
+			    &instance->rsp_list,
+			    head) {
+		if (cmd->id == reply->trans_id)
+			break;
+	}
+	mutex_unlock(&instance->lock);
+
+	if (&cmd->head == &instance->rsp_list) {
+		//pr_debug("%s: received response %u, throw away...",
+		pr_err("%s: received response %u, throw away...",
+		       __func__,
+		       reply->trans_id);
+	} else if (reply_len > sizeof(cmd->msg)) {
+		pr_err("%s: reply too big (%u) %u, throw away...",
+		       __func__, reply_len,
+		     reply->trans_id);
+	} else {
+		memcpy(cmd->msg, reply,
+		       reply_len);
+		complete(&cmd->cmplt);
+	}
+}
+
+static int vc_sm_cma_vchi_videocore_io(void *arg)
+{
+	struct sm_instance *instance = arg;
+	struct sm_cmd_rsp_blk *cmd = NULL, *cmd_tmp;
+	struct vc_sm_result_t *reply;
+	struct vchiq_header *header;
+	s32 status;
+	int svc_use = 1;
+
+	while (1) {
+		if (svc_use)
+			vchiq_release_service(instance->service_handle[0]);
+		svc_use = 0;
+
+		if (wait_for_completion_interruptible(&instance->io_cmplt))
+			continue;
+		vchiq_use_service(instance->service_handle[0]);
+		svc_use = 1;
+
+		do {
+			/*
+			 * Get new command and move it to response list
+			 */
+			mutex_lock(&instance->lock);
+			if (list_empty(&instance->cmd_list)) {
+				/* no more commands to process */
+				mutex_unlock(&instance->lock);
+				break;
+			}
+			cmd = list_first_entry(&instance->cmd_list,
+					       struct sm_cmd_rsp_blk, head);
+			list_move(&cmd->head, &instance->rsp_list);
+			cmd->sent = 1;
+			mutex_unlock(&instance->lock);
+			/* Send the command */
+			status =
+				bcm2835_vchi_msg_queue(instance->service_handle[0],
+						       cmd->msg, cmd->length);
+			if (status) {
+				pr_err("%s: failed to queue message (%d)",
+				       __func__, status);
+			}
+
+			/* If no reply is needed then we're done */
+			if (!cmd->wait) {
+				mutex_lock(&instance->lock);
+				list_del(&cmd->head);
+				mutex_unlock(&instance->lock);
+				vc_vchi_cmd_delete(instance, cmd);
+				continue;
+			}
+
+			if (status) {
+				complete(&cmd->cmplt);
+				continue;
+			}
+
+		} while (1);
+
+		while ((header = vchiq_msg_hold(instance->service_handle[0]))) {
+			reply = (struct vc_sm_result_t *)header->data;
+			if (reply->trans_id & 0x80000000) {
+				/* Async event or cmd from the VPU */
+				if (instance->vpu_event)
+					instance->vpu_event(instance, reply,
+							    header->size);
+			} else {
+				vc_sm_cma_vchi_rx_ack(instance, cmd, reply,
+						      header->size);
+			}
+
+			vchiq_release_message(instance->service_handle[0],
+					      header);
+		}
+
+		/* Go through the dead list and free them */
+		mutex_lock(&instance->lock);
+		list_for_each_entry_safe(cmd, cmd_tmp, &instance->dead_list,
+					 head) {
+			list_del(&cmd->head);
+			vc_vchi_cmd_delete(instance, cmd);
+		}
+		mutex_unlock(&instance->lock);
+	}
+
+	return 0;
+}
+
+static enum vchiq_status vc_sm_cma_vchi_callback(enum vchiq_reason reason,
+						 struct vchiq_header *header,
+						 unsigned int handle, void *userdata)
+{
+	struct sm_instance *instance = vchiq_get_service_userdata(handle);
+
+	switch (reason) {
+	case VCHIQ_MESSAGE_AVAILABLE:
+		vchiq_msg_queue_push(handle, header);
+		complete(&instance->io_cmplt);
+		break;
+
+	case VCHIQ_SERVICE_CLOSED:
+		pr_info("%s: service CLOSED!!", __func__);
+	default:
+		break;
+	}
+
+	return VCHIQ_SUCCESS;
+}
+
+struct sm_instance *vc_sm_cma_vchi_init(struct vchiq_instance *vchiq_instance,
+					unsigned int num_connections,
+					vpu_event_cb vpu_event)
+{
+	u32 i;
+	struct sm_instance *instance;
+	int status;
+
+	pr_debug("%s: start", __func__);
+
+	if (num_connections > SM_MAX_NUM_CONNECTIONS) {
+		pr_err("%s: unsupported number of connections %u (max=%u)",
+		       __func__, num_connections, SM_MAX_NUM_CONNECTIONS);
+
+		goto err_null;
+	}
+	/* Allocate memory for this instance */
+	instance = kzalloc(sizeof(*instance), GFP_KERNEL);
+
+	/* Misc initialisations */
+	mutex_init(&instance->lock);
+	init_completion(&instance->io_cmplt);
+	INIT_LIST_HEAD(&instance->cmd_list);
+	INIT_LIST_HEAD(&instance->rsp_list);
+	INIT_LIST_HEAD(&instance->dead_list);
+	INIT_LIST_HEAD(&instance->free_list);
+	sema_init(&instance->free_sema, SM_MAX_NUM_CMD_RSP_BLKS);
+	mutex_init(&instance->free_lock);
+	for (i = 0; i < SM_MAX_NUM_CMD_RSP_BLKS; i++) {
+		init_completion(&instance->free_blk[i].cmplt);
+		list_add(&instance->free_blk[i].head, &instance->free_list);
+	}
+
+	/* Open the VCHI service connections */
+	instance->num_connections = num_connections;
+	for (i = 0; i < num_connections; i++) {
+		struct vchiq_service_params_kernel params = {
+			.version = VC_SM_VER,
+			.version_min = VC_SM_MIN_VER,
+			.fourcc = VCHIQ_MAKE_FOURCC('S', 'M', 'E', 'M'),
+			.callback = vc_sm_cma_vchi_callback,
+			.userdata = instance,
+		};
+
+		status = vchiq_open_service(vchiq_instance, &params,
+					    &instance->service_handle[i]);
+		if (status) {
+			pr_err("%s: failed to open VCHI service (%d)",
+			       __func__, status);
+
+			goto err_close_services;
+		}
+	}
+	/* Create the thread which takes care of all io to/from videoocore. */
+	instance->io_thread = kthread_create(&vc_sm_cma_vchi_videocore_io,
+					     (void *)instance, "SMIO");
+	if (!instance->io_thread) {
+		pr_err("%s: failed to create SMIO thread", __func__);
+
+		goto err_close_services;
+	}
+	instance->vpu_event = vpu_event;
+	set_user_nice(instance->io_thread, -10);
+	wake_up_process(instance->io_thread);
+
+	pr_debug("%s: success - instance %p", __func__, instance);
+	return instance;
+
+err_close_services:
+	for (i = 0; i < instance->num_connections; i++) {
+		if (instance->service_handle[i])
+			vchiq_close_service(instance->service_handle[i]);
+	}
+	kfree(instance);
+err_null:
+	pr_debug("%s: FAILED", __func__);
+	return NULL;
+}
+
+int vc_sm_cma_vchi_stop(struct sm_instance **handle)
+{
+	struct sm_instance *instance;
+	u32 i;
+
+	if (!handle) {
+		pr_err("%s: invalid pointer to handle %p", __func__, handle);
+		goto lock;
+	}
+
+	if (!*handle) {
+		pr_err("%s: invalid handle %p", __func__, *handle);
+		goto lock;
+	}
+
+	instance = *handle;
+
+	/* Close all VCHI service connections */
+	for (i = 0; i < instance->num_connections; i++) {
+		vchiq_use_service(instance->service_handle[i]);
+		vchiq_close_service(instance->service_handle[i]);
+	}
+
+	kfree(instance);
+
+	*handle = NULL;
+	return 0;
+
+lock:
+	return -EINVAL;
+}
+
+static int vc_sm_cma_vchi_send_msg(struct sm_instance *handle,
+				   enum vc_sm_msg_type msg_id, void *msg,
+				   u32 msg_size, void *result, u32 result_size,
+				   u32 *cur_trans_id, u8 wait_reply)
+{
+	int status = 0;
+	struct sm_instance *instance = handle;
+	struct sm_cmd_rsp_blk *cmd_blk;
+
+	if (!handle) {
+		pr_err("%s: invalid handle", __func__);
+		return -EINVAL;
+	}
+	if (!msg) {
+		pr_err("%s: invalid msg pointer", __func__);
+		return -EINVAL;
+	}
+
+	cmd_blk =
+	    vc_vchi_cmd_create(instance, msg_id, msg, msg_size, wait_reply);
+	if (!cmd_blk) {
+		pr_err("[%s]: failed to allocate global tracking resource",
+		       __func__);
+		return -ENOMEM;
+	}
+
+	if (cur_trans_id)
+		*cur_trans_id = cmd_blk->id;
+
+	mutex_lock(&instance->lock);
+	list_add_tail(&cmd_blk->head, &instance->cmd_list);
+	mutex_unlock(&instance->lock);
+	complete(&instance->io_cmplt);
+
+	if (!wait_reply)
+		/* We're done */
+		return 0;
+
+	/* Wait for the response */
+	if (wait_for_completion_interruptible(&cmd_blk->cmplt)) {
+		mutex_lock(&instance->lock);
+		if (!cmd_blk->sent) {
+			list_del(&cmd_blk->head);
+			mutex_unlock(&instance->lock);
+			vc_vchi_cmd_delete(instance, cmd_blk);
+			return -ENXIO;
+		}
+
+		list_move(&cmd_blk->head, &instance->dead_list);
+		mutex_unlock(&instance->lock);
+		complete(&instance->io_cmplt);
+		return -EINTR;	/* We're done */
+	}
+
+	if (result && result_size) {
+		memcpy(result, cmd_blk->msg, result_size);
+	} else {
+		struct vc_sm_result_t *res =
+			(struct vc_sm_result_t *)cmd_blk->msg;
+		status = (res->success == 0) ? 0 : -ENXIO;
+	}
+
+	mutex_lock(&instance->lock);
+	list_del(&cmd_blk->head);
+	mutex_unlock(&instance->lock);
+	vc_vchi_cmd_delete(instance, cmd_blk);
+	return status;
+}
+
+int vc_sm_cma_vchi_free(struct sm_instance *handle, struct vc_sm_free_t *msg,
+			u32 *cur_trans_id)
+{
+	return vc_sm_cma_vchi_send_msg(handle, VC_SM_MSG_TYPE_FREE,
+				   msg, sizeof(*msg), 0, 0, cur_trans_id, 0);
+}
+
+int vc_sm_cma_vchi_import(struct sm_instance *handle, struct vc_sm_import *msg,
+			  struct vc_sm_import_result *result, u32 *cur_trans_id)
+{
+	return vc_sm_cma_vchi_send_msg(handle, VC_SM_MSG_TYPE_IMPORT,
+				   msg, sizeof(*msg), result, sizeof(*result),
+				   cur_trans_id, 1);
+}
+
+int vc_sm_cma_vchi_client_version(struct sm_instance *handle,
+				  struct vc_sm_version *msg,
+				  struct vc_sm_result_t *result,
+				  u32 *cur_trans_id)
+{
+	return vc_sm_cma_vchi_send_msg(handle, VC_SM_MSG_TYPE_CLIENT_VERSION,
+				   //msg, sizeof(*msg), result, sizeof(*result),
+				   //cur_trans_id, 1);
+				   msg, sizeof(*msg), NULL, 0,
+				   cur_trans_id, 0);
+}
+
+int vc_sm_vchi_client_vc_mem_req_reply(struct sm_instance *handle,
+				       struct vc_sm_vc_mem_request_result *msg,
+				       uint32_t *cur_trans_id)
+{
+	return vc_sm_cma_vchi_send_msg(handle,
+				       VC_SM_MSG_TYPE_VC_MEM_REQUEST_REPLY,
+				       msg, sizeof(*msg), 0, 0, cur_trans_id,
+				       0);
+}
diff -Nur --no-dereference linux-5.10.52-orig/drivers/staging/vc04_services/vc-sm-cma/vc_sm_cma_vchi.h linux-5.10.52-v7l+/drivers/staging/vc04_services/vc-sm-cma/vc_sm_cma_vchi.h
--- linux-5.10.52-orig/drivers/staging/vc04_services/vc-sm-cma/vc_sm_cma_vchi.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/staging/vc04_services/vc-sm-cma/vc_sm_cma_vchi.h	2021-07-25 16:46:26.567945487 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/* SPDX-License-Identifier: GPL-2.0 */
+
+/*
+ * VideoCore Shared Memory CMA allocator
+ *
+ * Copyright: 2018, Raspberry Pi (Trading) Ltd
+ * Copyright 2011-2012 Broadcom Corporation.  All rights reserved.
+ *
+ * Based on vmcs_sm driver from Broadcom Corporation.
+ *
+ */
+
+#ifndef __VC_SM_CMA_VCHI_H__INCLUDED__
+#define __VC_SM_CMA_VCHI_H__INCLUDED__
+
+#include <linux/raspberrypi/vchiq.h>
+
+#include "vc_sm_defs.h"
+
+/*
+ * Forward declare.
+ */
+struct sm_instance;
+
+typedef void (*vpu_event_cb)(struct sm_instance *instance,
+			     struct vc_sm_result_t *reply, int reply_len);
+
+/*
+ * Initialize the shared memory service, opens up vchi connection to talk to it.
+ */
+struct sm_instance *vc_sm_cma_vchi_init(struct vchiq_instance *vchi_instance,
+					unsigned int num_connections,
+					vpu_event_cb vpu_event);
+
+/*
+ * Terminates the shared memory service.
+ */
+int vc_sm_cma_vchi_stop(struct sm_instance **handle);
+
+/*
+ * Ask the shared memory service to free up some memory that was previously
+ * allocated by the vc_sm_cma_vchi_alloc function call.
+ */
+int vc_sm_cma_vchi_free(struct sm_instance *handle, struct vc_sm_free_t *msg,
+			u32 *cur_trans_id);
+
+/*
+ * Import a contiguous block of memory and wrap it in a GPU MEM_HANDLE_T.
+ */
+int vc_sm_cma_vchi_import(struct sm_instance *handle, struct vc_sm_import *msg,
+			  struct vc_sm_import_result *result,
+			  u32 *cur_trans_id);
+
+int vc_sm_cma_vchi_client_version(struct sm_instance *handle,
+				  struct vc_sm_version *msg,
+				  struct vc_sm_result_t *result,
+				  u32 *cur_trans_id);
+
+int vc_sm_vchi_client_vc_mem_req_reply(struct sm_instance *handle,
+				       struct vc_sm_vc_mem_request_result *msg,
+				       uint32_t *cur_trans_id);
+
+#endif /* __VC_SM_CMA_VCHI_H__INCLUDED__ */
diff -Nur --no-dereference linux-5.10.52-orig/drivers/staging/vc04_services/vc-sm-cma/vc_sm_defs.h linux-5.10.52-v7l+/drivers/staging/vc04_services/vc-sm-cma/vc_sm_defs.h
--- linux-5.10.52-orig/drivers/staging/vc04_services/vc-sm-cma/vc_sm_defs.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/staging/vc04_services/vc-sm-cma/vc_sm_defs.h	2021-07-25 16:46:26.567945487 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/* SPDX-License-Identifier: GPL-2.0 */
+
+/*
+ * VideoCore Shared Memory CMA allocator
+ *
+ * Copyright: 2018, Raspberry Pi (Trading) Ltd
+ *
+ * Based on vc_sm_defs.h from the vmcs_sm driver Copyright Broadcom Corporation.
+ * All IPC messages are copied across to this file, even if the vc-sm-cma
+ * driver is not currently using them.
+ *
+ ****************************************************************************
+ */
+
+#ifndef __VC_SM_DEFS_H__INCLUDED__
+#define __VC_SM_DEFS_H__INCLUDED__
+
+/* Maximum message length */
+#define VC_SM_MAX_MSG_LEN (sizeof(union vc_sm_msg_union_t) + \
+	sizeof(struct vc_sm_msg_hdr_t))
+#define VC_SM_MAX_RSP_LEN (sizeof(union vc_sm_msg_union_t))
+
+/* Resource name maximum size */
+#define VC_SM_RESOURCE_NAME 32
+
+/*
+ * Version to be reported to the VPU
+ * VPU assumes 0 (aka 1) which does not require the released callback, nor
+ * expect the client to handle VC_MEM_REQUESTS.
+ * Version 2 requires the released callback, and must support VC_MEM_REQUESTS.
+ */
+#define VC_SM_PROTOCOL_VERSION	2
+
+enum vc_sm_msg_type {
+	/* Message types supported for HOST->VC direction */
+
+	/* Allocate shared memory block */
+	VC_SM_MSG_TYPE_ALLOC,
+	/* Lock allocated shared memory block */
+	VC_SM_MSG_TYPE_LOCK,
+	/* Unlock allocated shared memory block */
+	VC_SM_MSG_TYPE_UNLOCK,
+	/* Unlock allocated shared memory block, do not answer command */
+	VC_SM_MSG_TYPE_UNLOCK_NOANS,
+	/* Free shared memory block */
+	VC_SM_MSG_TYPE_FREE,
+	/* Resize a shared memory block */
+	VC_SM_MSG_TYPE_RESIZE,
+	/* Walk the allocated shared memory block(s) */
+	VC_SM_MSG_TYPE_WALK_ALLOC,
+
+	/* A previously applied action will need to be reverted */
+	VC_SM_MSG_TYPE_ACTION_CLEAN,
+
+	/*
+	 * Import a physical address and wrap into a MEM_HANDLE_T.
+	 * Release with VC_SM_MSG_TYPE_FREE.
+	 */
+	VC_SM_MSG_TYPE_IMPORT,
+	/*
+	 *Tells VC the protocol version supported by this client.
+	 * 2 supports the async/cmd messages from the VPU for final release
+	 * of memory, and for VC allocations.
+	 */
+	VC_SM_MSG_TYPE_CLIENT_VERSION,
+	/* Response to VC request for memory */
+	VC_SM_MSG_TYPE_VC_MEM_REQUEST_REPLY,
+
+	/*
+	 * Asynchronous/cmd messages supported for VC->HOST direction.
+	 * Signalled by setting the top bit in vc_sm_result_t trans_id.
+	 */
+
+	/*
+	 * VC has finished with an imported memory allocation.
+	 * Release any Linux reference counts on the underlying block.
+	 */
+	VC_SM_MSG_TYPE_RELEASED,
+	/* VC request for memory */
+	VC_SM_MSG_TYPE_VC_MEM_REQUEST,
+
+	VC_SM_MSG_TYPE_MAX
+};
+
+/* Type of memory to be allocated */
+enum vc_sm_alloc_type_t {
+	VC_SM_ALLOC_CACHED,
+	VC_SM_ALLOC_NON_CACHED,
+};
+
+/* Message header for all messages in HOST->VC direction */
+struct vc_sm_msg_hdr_t {
+	u32 type;
+	u32 trans_id;
+	u8 body[0];
+
+};
+
+/* Request to allocate memory (HOST->VC) */
+struct vc_sm_alloc_t {
+	/* type of memory to allocate */
+	enum vc_sm_alloc_type_t type;
+	/* byte amount of data to allocate per unit */
+	u32 base_unit;
+	/* number of unit to allocate */
+	u32 num_unit;
+	/* alignment to be applied on allocation */
+	u32 alignment;
+	/* identity of who allocated this block */
+	u32 allocator;
+	/* resource name (for easier tracking on vc side) */
+	char name[VC_SM_RESOURCE_NAME];
+
+};
+
+/* Result of a requested memory allocation (VC->HOST) */
+struct vc_sm_alloc_result_t {
+	/* Transaction identifier */
+	u32 trans_id;
+
+	/* Resource handle */
+	u32 res_handle;
+	/* Pointer to resource buffer */
+	u32 res_mem;
+	/* Resource base size (bytes) */
+	u32 res_base_size;
+	/* Resource number */
+	u32 res_num;
+
+};
+
+/* Request to free a previously allocated memory (HOST->VC) */
+struct vc_sm_free_t {
+	/* Resource handle (returned from alloc) */
+	u32 res_handle;
+	/* Resource buffer (returned from alloc) */
+	u32 res_mem;
+
+};
+
+/* Request to lock a previously allocated memory (HOST->VC) */
+struct vc_sm_lock_unlock_t {
+	/* Resource handle (returned from alloc) */
+	u32 res_handle;
+	/* Resource buffer (returned from alloc) */
+	u32 res_mem;
+
+};
+
+/* Request to resize a previously allocated memory (HOST->VC) */
+struct vc_sm_resize_t {
+	/* Resource handle (returned from alloc) */
+	u32 res_handle;
+	/* Resource buffer (returned from alloc) */
+	u32 res_mem;
+	/* Resource *new* size requested (bytes) */
+	u32 res_new_size;
+
+};
+
+/* Result of a requested memory lock (VC->HOST) */
+struct vc_sm_lock_result_t {
+	/* Transaction identifier */
+	u32 trans_id;
+
+	/* Resource handle */
+	u32 res_handle;
+	/* Pointer to resource buffer */
+	u32 res_mem;
+	/*
+	 * Pointer to former resource buffer if the memory
+	 * was reallocated
+	 */
+	u32 res_old_mem;
+
+};
+
+/* Generic result for a request (VC->HOST) */
+struct vc_sm_result_t {
+	/* Transaction identifier */
+	u32 trans_id;
+
+	s32 success;
+
+};
+
+/* Request to revert a previously applied action (HOST->VC) */
+struct vc_sm_action_clean_t {
+	/* Action of interest */
+	enum vc_sm_msg_type res_action;
+	/* Transaction identifier for the action of interest */
+	u32 action_trans_id;
+
+};
+
+/* Request to remove all data associated with a given allocator (HOST->VC) */
+struct vc_sm_free_all_t {
+	/* Allocator identifier */
+	u32 allocator;
+};
+
+/* Request to import memory (HOST->VC) */
+struct vc_sm_import {
+	/* type of memory to allocate */
+	enum vc_sm_alloc_type_t type;
+	/* pointer to the VC (ie physical) address of the allocated memory */
+	u32 addr;
+	/* size of buffer */
+	u32 size;
+	/* opaque handle returned in RELEASED messages */
+	u32 kernel_id;
+	/* Allocator identifier */
+	u32 allocator;
+	/* resource name (for easier tracking on vc side) */
+	char     name[VC_SM_RESOURCE_NAME];
+};
+
+/* Result of a requested memory import (VC->HOST) */
+struct vc_sm_import_result {
+	/* Transaction identifier */
+	u32 trans_id;
+
+	/* Resource handle */
+	u32 res_handle;
+};
+
+/* Notification that VC has finished with an allocation (VC->HOST) */
+struct vc_sm_released {
+	/* cmd type / trans_id */
+	u32 cmd;
+
+	/* pointer to the VC (ie physical) address of the allocated memory */
+	u32 addr;
+	/* size of buffer */
+	u32 size;
+	/* opaque handle returned in RELEASED messages */
+	u32 kernel_id;
+	u32 vc_handle;
+};
+
+/*
+ * Client informing VC as to the protocol version it supports.
+ * >=2 requires the released callback, and supports VC asking for memory.
+ * Failure means that the firmware doesn't support this call, and therefore the
+ * client should either fail, or NOT rely on getting the released callback.
+ */
+struct vc_sm_version {
+	u32 version;
+};
+
+/* Request FROM VideoCore for some memory */
+struct vc_sm_vc_mem_request {
+	/* cmd type */
+	u32 cmd;
+
+	/* trans_id (from VPU) */
+	u32 trans_id;
+	/* size of buffer */
+	u32 size;
+	/* alignment of buffer */
+	u32 align;
+	/* resource name (for easier tracking) */
+	char     name[VC_SM_RESOURCE_NAME];
+	/* VPU handle for the resource */
+	u32 vc_handle;
+};
+
+/* Response from the kernel to provide the VPU with some memory */
+struct vc_sm_vc_mem_request_result {
+	/* Transaction identifier for the VPU */
+	u32 trans_id;
+	/* pointer to the physical address of the allocated memory */
+	u32 addr;
+	/* opaque handle returned in RELEASED messages */
+	u32 kernel_id;
+};
+
+/* Union of ALL messages */
+union vc_sm_msg_union_t {
+	struct vc_sm_alloc_t alloc;
+	struct vc_sm_alloc_result_t alloc_result;
+	struct vc_sm_free_t free;
+	struct vc_sm_lock_unlock_t lock_unlock;
+	struct vc_sm_action_clean_t action_clean;
+	struct vc_sm_resize_t resize;
+	struct vc_sm_lock_result_t lock_result;
+	struct vc_sm_result_t result;
+	struct vc_sm_free_all_t free_all;
+	struct vc_sm_import import;
+	struct vc_sm_import_result import_result;
+	struct vc_sm_version version;
+	struct vc_sm_released released;
+	struct vc_sm_vc_mem_request vc_request;
+	struct vc_sm_vc_mem_request_result vc_request_result;
+};
+
+#endif /* __VC_SM_DEFS_H__INCLUDED__ */
diff -Nur --no-dereference linux-5.10.52-orig/drivers/staging/vc04_services/vc-sm-cma/vc_sm.h linux-5.10.52-v7l+/drivers/staging/vc04_services/vc-sm-cma/vc_sm.h
--- linux-5.10.52-orig/drivers/staging/vc04_services/vc-sm-cma/vc_sm.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/staging/vc04_services/vc-sm-cma/vc_sm.h	2021-07-25 16:46:26.567945487 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/* SPDX-License-Identifier: GPL-2.0 */
+
+/*
+ * VideoCore Shared Memory driver using CMA.
+ *
+ * Copyright: 2018, Raspberry Pi (Trading) Ltd
+ *
+ */
+
+#ifndef VC_SM_H
+#define VC_SM_H
+
+#include <linux/device.h>
+#include <linux/dma-direction.h>
+#include <linux/kref.h>
+#include <linux/mm_types.h>
+#include <linux/mutex.h>
+#include <linux/rbtree.h>
+#include <linux/sched.h>
+#include <linux/shrinker.h>
+#include <linux/types.h>
+#include <linux/miscdevice.h>
+
+#define VC_SM_MAX_NAME_LEN 32
+
+enum vc_sm_vpu_mapping_state {
+	VPU_NOT_MAPPED,
+	VPU_MAPPED,
+	VPU_UNMAPPING
+};
+
+struct vc_sm_alloc_data {
+	unsigned long num_pages;
+	void *priv_virt;
+	struct sg_table *sg_table;
+};
+
+struct vc_sm_imported {
+	struct dma_buf *dma_buf;
+	struct dma_buf_attachment *attach;
+	struct sg_table *sgt;
+};
+
+struct vc_sm_buffer {
+	struct list_head global_buffer_list;	/* Global list of buffers. */
+
+	/* Index in the kernel_id idr so that we can find the
+	 * mmal_msg_context again when servicing the VCHI reply.
+	 */
+	int kernel_id;
+
+	size_t size;
+
+	/* Lock over all the following state for this buffer */
+	struct mutex lock;
+	struct list_head attachments;
+
+	char name[VC_SM_MAX_NAME_LEN];
+
+	int in_use:1;	/* Kernel is still using this resource */
+	int imported:1;	/* Imported dmabuf */
+
+	enum vc_sm_vpu_mapping_state vpu_state;
+	u32 vc_handle;	/* VideoCore handle for this buffer */
+	int vpu_allocated;	/*
+				 * The VPU made this allocation. Release the
+				 * local dma_buf when the VPU releases the
+				 * resource.
+				 */
+
+	/* DMABUF related fields */
+	struct dma_buf *dma_buf;
+	dma_addr_t dma_addr;
+	void *cookie;
+
+	struct vc_sm_privdata_t *private;
+
+	union {
+		struct vc_sm_alloc_data alloc;
+		struct vc_sm_imported import;
+	};
+};
+
+#endif
diff -Nur --no-dereference linux-5.10.52-orig/drivers/staging/vc04_services/vc-sm-cma/vc_sm_knl.h linux-5.10.52-v7l+/drivers/staging/vc04_services/vc-sm-cma/vc_sm_knl.h
--- linux-5.10.52-orig/drivers/staging/vc04_services/vc-sm-cma/vc_sm_knl.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/staging/vc04_services/vc-sm-cma/vc_sm_knl.h	2021-07-25 16:46:26.567945487 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/* SPDX-License-Identifier: GPL-2.0 */
+
+/*
+ * VideoCore Shared Memory CMA allocator
+ *
+ * Copyright: 2018, Raspberry Pi (Trading) Ltd
+ *
+ * Based on vc_sm_defs.h from the vmcs_sm driver Copyright Broadcom Corporation.
+ *
+ */
+
+#ifndef __VC_SM_KNL_H__INCLUDED__
+#define __VC_SM_KNL_H__INCLUDED__
+
+#if !defined(__KERNEL__)
+#error "This interface is for kernel use only..."
+#endif
+
+/* Free a previously allocated or imported shared memory handle and block. */
+int vc_sm_cma_free(void *handle);
+
+/* Get an internal resource handle mapped from the external one. */
+int vc_sm_cma_int_handle(void *handle);
+
+/* Import a block of memory into the GPU space. */
+int vc_sm_cma_import_dmabuf(struct dma_buf *dmabuf, void **handle);
+
+#endif /* __VC_SM_KNL_H__INCLUDED__ */
diff -Nur --no-dereference linux-5.10.52-orig/drivers/thermal/broadcom/bcm2711_thermal.c linux-5.10.52-v7l+/drivers/thermal/broadcom/bcm2711_thermal.c
--- linux-5.10.52-orig/drivers/thermal/broadcom/bcm2711_thermal.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/thermal/broadcom/bcm2711_thermal.c	2021-07-25 16:46:26.787941798 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:55 @
 	/* Convert a HW code to a temperature reading (millidegree celsius) */
 	t = slope * val + offset;
 
-	*temp = t < 0 ? 0 : t;
+	*temp = t;
 
 	return 0;
 }
diff -Nur --no-dereference linux-5.10.52-orig/drivers/thermal/gov_step_wise.c linux-5.10.52-v7l+/drivers/thermal/gov_step_wise.c
--- linux-5.10.52-orig/drivers/thermal/gov_step_wise.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/thermal/gov_step_wise.c	2021-07-25 16:46:26.797941631 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:27 @
  *       for this trip point
  *    d. if the trend is THERMAL_TREND_DROP_FULL, use lower limit
  *       for this trip point
- * If the temperature is lower than a trip point,
+ * If the temperature is lower than a hysteresis temperature,
  *    a. if the trend is THERMAL_TREND_RAISING, do nothing
  *    b. if the trend is THERMAL_TREND_DROPPING, use lower cooling
  *       state for this trip point, if the cooling state already
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:118 @
 
 static void thermal_zone_trip_update(struct thermal_zone_device *tz, int trip)
 {
-	int trip_temp;
+	int trip_temp, hyst_temp;
 	enum thermal_trip_type trip_type;
 	enum thermal_trend trend;
 	struct thermal_instance *instance;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:126 @
 	int old_target;
 
 	if (trip == THERMAL_TRIPS_NONE) {
-		trip_temp = tz->forced_passive;
+		hyst_temp = trip_temp = tz->forced_passive;
 		trip_type = THERMAL_TRIPS_NONE;
 	} else {
 		tz->ops->get_trip_temp(tz, trip, &trip_temp);
+		hyst_temp = trip_temp;
+		if (tz->ops->get_trip_hyst) {
+			tz->ops->get_trip_hyst(tz, trip, &hyst_temp);
+			hyst_temp = trip_temp - hyst_temp;
+		}
 		tz->ops->get_trip_type(tz, trip, &trip_type);
 	}
 
 	trend = get_tz_trend(tz, trip);
 
-	if (tz->temperature >= trip_temp) {
-		throttle = true;
-		trace_thermal_zone_trip(tz, trip, trip_type);
-	}
-
-	dev_dbg(&tz->device, "Trip%d[type=%d,temp=%d]:trend=%d,throttle=%d\n",
-				trip, trip_type, trip_temp, trend, throttle);
+	dev_dbg(&tz->device,
+		"Trip%d[type=%d,temp=%d,hyst=%d]:trend=%d,throttle=%d\n",
+		trip, trip_type, trip_temp, hyst_temp, trend, throttle);
 
 	mutex_lock(&tz->lock);
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:151 @
 			continue;
 
 		old_target = instance->target;
+		throttle = false;
+		/*
+		 * Lower the mitigation only if the temperature
+		 * goes below the hysteresis temperature.
+		 */
+		if (tz->temperature >= trip_temp ||
+		   (tz->temperature >= hyst_temp &&
+		   old_target == instance->upper)) {
+			throttle = true;
+			trace_thermal_zone_trip(tz, trip, trip_type);
+		}
+
 		instance->target = get_target_state(instance, trend, throttle);
 		dev_dbg(&instance->cdev->device, "old_target=%d, target=%d\n",
 					old_target, (int)instance->target);
diff -Nur --no-dereference linux-5.10.52-orig/drivers/tty/serial/8250/8250_bcm2835aux.c linux-5.10.52-v7l+/drivers/tty/serial/8250/8250_bcm2835aux.c
--- linux-5.10.52-orig/drivers/tty/serial/8250/8250_bcm2835aux.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/tty/serial/8250/8250_bcm2835aux.c	2021-07-25 16:46:26.947939116 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:151 @
 	 */
 	up.port.uartclk = clk_get_rate(data->clk) * 2;
 
+	/* The clock is only queried at probe time, which means we get one shot
+	 * at this. A zero clock is never going to work and is almost certainly
+	 * due to a parent not being ready, so prefer to defer.
+	 */
+	if (!up.port.uartclk)
+	    return -EPROBE_DEFER;
+
 	/* register the port */
 	ret = serial8250_register_8250_port(&up);
 	if (ret < 0) {
diff -Nur --no-dereference linux-5.10.52-orig/drivers/tty/serial/amba-pl011.c linux-5.10.52-v7l+/drivers/tty/serial/amba-pl011.c
--- linux-5.10.52-orig/drivers/tty/serial/amba-pl011.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/tty/serial/amba-pl011.c	2021-07-25 16:46:26.997938278 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:268 @
 	unsigned int		old_cr;		/* state during shutdown */
 	unsigned int		fixed_baud;	/* vendor-set fixed baud rate */
 	char			type[12];
+	bool			irq_locked;	/* in irq, unreleased lock */
 #ifdef CONFIG_DMA_ENGINE
 	/* DMA stuff */
 	bool			using_tx_dma;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:815 @
 	if (!uap->using_tx_dma)
 		return;
 
+	uap->irq_locked = 0;
 	dmaengine_terminate_async(uap->dmatx.chan);
 
 	if (uap->dmatx.queued) {
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:942 @
 		fifotaken = pl011_fifo_to_tty(uap);
 	}
 
+	uap->irq_locked = 0;
 	spin_unlock(&uap->port.lock);
 	dev_vdbg(uap->port.dev,
 		 "Took %d chars from DMA buffer and %d chars from the FIFO\n",
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1320 @
 		pl011_start_tx_pio(uap);
 }
 
+static void pl011_throttle(struct uart_port *port)
+{
+	struct uart_amba_port *uap =
+	    container_of(port, struct uart_amba_port, port);
+	unsigned long flags;
+
+	spin_lock_irqsave(&uap->port.lock, flags);
+	uap->im &= ~(UART011_RTIM | UART011_RXIM);
+	pl011_write(uap->im, uap, REG_IMSC);
+	spin_unlock_irqrestore(&uap->port.lock, flags);
+}
+
+static void pl011_unthrottle(struct uart_port *port)
+{
+	struct uart_amba_port *uap =
+	    container_of(port, struct uart_amba_port, port);
+	unsigned long flags;
+
+	spin_lock_irqsave(&uap->port.lock, flags);
+	uap->im |= UART011_RTIM;
+	if (!pl011_dma_rx_running(uap))
+	    uap->im |= UART011_RXIM;
+	pl011_write(uap->im, uap, REG_IMSC);
+	spin_unlock_irqrestore(&uap->port.lock, flags);
+}
+
 static void pl011_stop_rx(struct uart_port *port)
 {
 	struct uart_amba_port *uap =
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1373 @
 {
 	pl011_fifo_to_tty(uap);
 
+	uap->irq_locked = 0;
 	spin_unlock(&uap->port.lock);
 	tty_flip_buffer_push(&uap->port.state->port);
 	/*
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1410 @
 		return false; /* unable to transmit character */
 
 	pl011_write(c, uap, REG_DR);
+	mb();
 	uap->port.icount.tx++;
 
 	return true;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1441 @
 		if (likely(from_irq) && count-- == 0)
 			break;
 
+		if (likely(from_irq) && count == 0 &&
+		    pl011_read(uap, REG_FR) & UART01x_FR_TXFF)
+			break;
+
 		if (!pl011_tx_char(uap, xmit->buf[xmit->tail], from_irq))
 			break;
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1511 @
 	int handled = 0;
 
 	spin_lock_irqsave(&uap->port.lock, flags);
+	uap->irq_locked = 1;
 	status = pl011_read(uap, REG_RIS) & uap->im;
 	if (status) {
 		do {
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1531 @
 				      UART011_CTSMIS|UART011_RIMIS))
 				pl011_modem_status(uap);
 			if (status & UART011_TXIS)
-				pl011_tx_chars(uap, true);
+				pl011_tx_chars(uap, uap->irq_locked);
 
 			if (pass_counter-- == 0)
 				break;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1681 @
 
 #endif /* CONFIG_CONSOLE_POLL */
 
+unsigned long pl011_clk_round(unsigned long clk)
+{
+	unsigned long scaler;
+
+	/*
+	 * If increasing a clock by less than 0.1% changes it
+	 * from ..999.. to ..000.., round up.
+	 */
+	scaler = 1;
+	while (scaler * 100000 < clk)
+		scaler *= 10;
+	if ((clk + scaler - 1)/scaler % 1000 == 0)
+		clk = (clk/scaler + 1) * scaler;
+
+	return clk;
+}
+
 static int pl011_hwinit(struct uart_port *port)
 {
 	struct uart_amba_port *uap =
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1714 @
 	if (retval)
 		return retval;
 
-	uap->port.uartclk = clk_get_rate(uap->clk);
+	uap->port.uartclk = pl011_clk_round(clk_get_rate(uap->clk));
 
 	/* Clear pending error and receive interrupts */
 	pl011_write(UART011_OEIS | UART011_BEIS | UART011_PEIS |
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:2189 @
 	.stop_tx	= pl011_stop_tx,
 	.start_tx	= pl011_start_tx,
 	.stop_rx	= pl011_stop_rx,
+	.throttle	= pl011_throttle,
+	.unthrottle	= pl011_unthrottle,
 	.enable_ms	= pl011_enable_ms,
 	.break_ctl	= pl011_break_ctl,
 	.startup	= pl011_startup,
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:2371 @
 			plat->init();
 	}
 
-	uap->port.uartclk = clk_get_rate(uap->clk);
+	uap->port.uartclk = pl011_clk_round(clk_get_rate(uap->clk));
 
 	if (uap->vendor->fixed_options) {
 		baud = uap->fixed_baud;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:2588 @
 	.cons			= AMBA_CONSOLE,
 };
 
+#if 0
 static int pl011_probe_dt_alias(int index, struct device *dev)
 {
 	struct device_node *np;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:2620 @
 
 	return ret;
 }
+#endif
 
 /* unregisters the driver also if no more ports are left */
 static void pl011_unregister_port(struct uart_amba_port *uap)
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:2659 @
 	if (IS_ERR(base))
 		return PTR_ERR(base);
 
+	/* Don't use DT serial<n> aliases - it causes the device to
+	   be renumbered to ttyAMA1 if it is the second serial port in the
+	   system, even though the other one is ttyS0. The 8250 driver
+	   doesn't use this logic, so always remains ttyS0.
 	index = pl011_probe_dt_alias(index, dev);
+	*/
 
 	uap->old_cr = 0;
 	uap->port.dev = dev;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:2726 @
 	if (IS_ERR(uap->clk))
 		return PTR_ERR(uap->clk);
 
+	if (of_property_read_bool(dev->dev.of_node, "cts-event-workaround")) {
+	    vendor->cts_event_workaround = true;
+	    dev_info(&dev->dev, "cts_event_workaround enabled\n");
+	}
+
 	uap->reg_offset = vendor->reg_offset;
 	uap->vendor = vendor;
 	uap->fifosize = vendor->get_fifosize(dev);
diff -Nur --no-dereference linux-5.10.52-orig/drivers/tty/serial/sc16is7xx.c linux-5.10.52-v7l+/drivers/tty/serial/sc16is7xx.c
--- linux-5.10.52-orig/drivers/tty/serial/sc16is7xx.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/tty/serial/sc16is7xx.c	2021-07-25 16:46:27.067937104 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:526 @
 
 	/* Enable enhanced features */
 	regcache_cache_bypass(s->regmap, true);
-	sc16is7xx_port_write(port, SC16IS7XX_EFR_REG,
-			     SC16IS7XX_EFR_ENABLE_BIT);
+	sc16is7xx_port_update(port, SC16IS7XX_EFR_REG,
+			      SC16IS7XX_EFR_ENABLE_BIT,
+			      SC16IS7XX_EFR_ENABLE_BIT);
 	regcache_cache_bypass(s->regmap, false);
 
 	/* Put LCR back to the normal mode */
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:700 @
 			rxlen = sc16is7xx_port_read(port, SC16IS7XX_RXLVL_REG);
 			if (rxlen)
 				sc16is7xx_handle_rx(port, rxlen, iir);
+			else
+				return false;
 			break;
 		case SC16IS7XX_IIR_THRI_SRC:
 			sc16is7xx_handle_tx(port);
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:843 @
 	/* DCD and DSR are not wired and CTS/RTS is handled automatically
 	 * so just indicate DSR and CAR asserted
 	 */
-	return TIOCM_DSR | TIOCM_CAR;
+	return TIOCM_DSR | TIOCM_CAR | TIOCM_RI | TIOCM_CTS;
 }
 
 static void sc16is7xx_set_mctrl(struct uart_port *port, unsigned int mctrl)
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:930 @
 	regcache_cache_bypass(s->regmap, true);
 	sc16is7xx_port_write(port, SC16IS7XX_XON1_REG, termios->c_cc[VSTART]);
 	sc16is7xx_port_write(port, SC16IS7XX_XOFF1_REG, termios->c_cc[VSTOP]);
-	if (termios->c_cflag & CRTSCTS)
+	if (termios->c_cflag & CRTSCTS) {
 		flow |= SC16IS7XX_EFR_AUTOCTS_BIT |
 			SC16IS7XX_EFR_AUTORTS_BIT;
+		port->status |= UPSTAT_AUTOCTS;
+	};
 	if (termios->c_iflag & IXON)
 		flow |= SC16IS7XX_EFR_SWFLOW3_BIT;
 	if (termios->c_iflag & IXOFF)
 		flow |= SC16IS7XX_EFR_SWFLOW1_BIT;
 
+	/* Always set enable enhanced */
+	flow |= SC16IS7XX_EFR_ENABLE_BIT;
+
 	sc16is7xx_port_write(port, SC16IS7XX_EFR_REG, flow);
 	regcache_cache_bypass(s->regmap, false);
 
diff -Nur --no-dereference linux-5.10.52-orig/drivers/usb/core/generic.c linux-5.10.52-v7l+/drivers/usb/core/generic.c
--- linux-5.10.52-orig/drivers/usb/core/generic.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/usb/core/generic.c	2021-07-25 16:46:28.437914136 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:193 @
 		dev_warn(&udev->dev,
 			"no configuration chosen from %d choice%s\n",
 			num_configs, plural(num_configs));
+		dev_warn(&udev->dev, "No support over %dmA\n", udev->bus_mA);
 	}
 	return i;
 }
diff -Nur --no-dereference linux-5.10.52-orig/drivers/usb/core/hcd.c linux-5.10.52-v7l+/drivers/usb/core/hcd.c
--- linux-5.10.52-orig/drivers/usb/core/hcd.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/usb/core/hcd.c	2021-07-25 16:46:28.437914136 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1948 @
 	return ret;
 }
 
+void usb_hcd_fixup_endpoint(struct usb_device *udev,
+			    struct usb_host_endpoint *ep, int interval)
+{
+	struct usb_hcd *hcd;
+
+	hcd = bus_to_hcd(udev->bus);
+	if (hcd->driver->fixup_endpoint)
+		hcd->driver->fixup_endpoint(hcd, udev, ep, interval);
+}
+
 /* Disables the endpoint: synchronizes with the hcd to make sure all
  * endpoint state is gone from hardware.  usb_hcd_flush_endpoint() must
  * have been called previously.  Use for set_configuration, set_interface,
diff -Nur --no-dereference linux-5.10.52-orig/drivers/usb/core/hub.c linux-5.10.52-v7l+/drivers/usb/core/hub.c
--- linux-5.10.52-orig/drivers/usb/core/hub.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/usb/core/hub.c	2021-07-25 16:46:28.447913969 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:5449 @
 		port_dev->over_current_count++;
 		port_over_current_notify(port_dev);
 
-		dev_dbg(&port_dev->dev, "over-current change #%u\n",
+		dev_notice(&port_dev->dev, "over-current change #%u\n",
 			port_dev->over_current_count);
 		usb_clear_port_feature(hdev, port1,
 				USB_PORT_FEAT_C_OVER_CURRENT);
diff -Nur --no-dereference linux-5.10.52-orig/drivers/usb/core/message.c linux-5.10.52-v7l+/drivers/usb/core/message.c
--- linux-5.10.52-orig/drivers/usb/core/message.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/usb/core/message.c	2021-07-25 16:46:28.447913969 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1262 @
 	intf->ep_devs_created = 0;
 }
 
+void usb_fixup_endpoint(struct usb_device *dev, int epaddr, int interval)
+{
+	unsigned int epnum = epaddr & USB_ENDPOINT_NUMBER_MASK;
+	struct usb_host_endpoint *ep;
+
+	if (usb_endpoint_out(epaddr))
+		ep = dev->ep_out[epnum];
+	else
+		ep = dev->ep_in[epnum];
+
+	if (ep && usb_endpoint_xfer_int(&ep->desc))
+		usb_hcd_fixup_endpoint(dev, ep, interval);
+}
+EXPORT_SYMBOL_GPL(usb_fixup_endpoint);
+
 /**
  * usb_disable_endpoint -- Disable an endpoint by address
  * @dev: the device whose endpoint is being disabled
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:2147 @
 	if (cp->string == NULL &&
 			!(dev->quirks & USB_QUIRK_CONFIG_INTF_STRINGS))
 		cp->string = usb_cache_string(dev, cp->desc.iConfiguration);
+/* Uncomment this define to enable the HS Electrical Test support */
+#define DWC_HS_ELECT_TST 1
+#ifdef DWC_HS_ELECT_TST
+		/* Here we implement the HS Electrical Test support. The
+		 * tester uses a vendor ID of 0x1A0A to indicate we should
+		 * run a special test sequence. The product ID tells us
+		 * which sequence to run. We invoke the test sequence by
+		 * sending a non-standard SetFeature command to our root
+		 * hub port. Our dwc_otg_hcd_hub_control() routine will
+		 * recognize the command and perform the desired test
+		 * sequence.
+		 */
+		if (dev->descriptor.idVendor == 0x1A0A) {
+			/* HSOTG Electrical Test */
+			dev_warn(&dev->dev, "VID from HSOTG Electrical Test Fixture\n");
+
+			if (dev->bus && dev->bus->root_hub) {
+				struct usb_device *hdev = dev->bus->root_hub;
+				dev_warn(&dev->dev, "Got PID 0x%x\n", dev->descriptor.idProduct);
+
+				switch (dev->descriptor.idProduct) {
+				case 0x0101:	/* TEST_SE0_NAK */
+					dev_warn(&dev->dev, "TEST_SE0_NAK\n");
+					usb_control_msg(hdev, usb_sndctrlpipe(hdev, 0),
+							USB_REQ_SET_FEATURE, USB_RT_PORT,
+							USB_PORT_FEAT_TEST, 0x300, NULL, 0, HZ);
+					break;
+
+				case 0x0102:	/* TEST_J */
+					dev_warn(&dev->dev, "TEST_J\n");
+					usb_control_msg(hdev, usb_sndctrlpipe(hdev, 0),
+							USB_REQ_SET_FEATURE, USB_RT_PORT,
+							USB_PORT_FEAT_TEST, 0x100, NULL, 0, HZ);
+					break;
+
+				case 0x0103:	/* TEST_K */
+					dev_warn(&dev->dev, "TEST_K\n");
+					usb_control_msg(hdev, usb_sndctrlpipe(hdev, 0),
+							USB_REQ_SET_FEATURE, USB_RT_PORT,
+							USB_PORT_FEAT_TEST, 0x200, NULL, 0, HZ);
+					break;
+
+				case 0x0104:	/* TEST_PACKET */
+					dev_warn(&dev->dev, "TEST_PACKET\n");
+					usb_control_msg(hdev, usb_sndctrlpipe(hdev, 0),
+							USB_REQ_SET_FEATURE, USB_RT_PORT,
+							USB_PORT_FEAT_TEST, 0x400, NULL, 0, HZ);
+					break;
+
+				case 0x0105:	/* TEST_FORCE_ENABLE */
+					dev_warn(&dev->dev, "TEST_FORCE_ENABLE\n");
+					usb_control_msg(hdev, usb_sndctrlpipe(hdev, 0),
+							USB_REQ_SET_FEATURE, USB_RT_PORT,
+							USB_PORT_FEAT_TEST, 0x500, NULL, 0, HZ);
+					break;
+
+				case 0x0106:	/* HS_HOST_PORT_SUSPEND_RESUME */
+					dev_warn(&dev->dev, "HS_HOST_PORT_SUSPEND_RESUME\n");
+					usb_control_msg(hdev, usb_sndctrlpipe(hdev, 0),
+							USB_REQ_SET_FEATURE, USB_RT_PORT,
+							USB_PORT_FEAT_TEST, 0x600, NULL, 0, 40 * HZ);
+					break;
+
+				case 0x0107:	/* SINGLE_STEP_GET_DEVICE_DESCRIPTOR setup */
+					dev_warn(&dev->dev, "SINGLE_STEP_GET_DEVICE_DESCRIPTOR setup\n");
+					usb_control_msg(hdev, usb_sndctrlpipe(hdev, 0),
+							USB_REQ_SET_FEATURE, USB_RT_PORT,
+							USB_PORT_FEAT_TEST, 0x700, NULL, 0, 40 * HZ);
+					break;
+
+				case 0x0108:	/* SINGLE_STEP_GET_DEVICE_DESCRIPTOR execute */
+					dev_warn(&dev->dev, "SINGLE_STEP_GET_DEVICE_DESCRIPTOR execute\n");
+					usb_control_msg(hdev, usb_sndctrlpipe(hdev, 0),
+							USB_REQ_SET_FEATURE, USB_RT_PORT,
+							USB_PORT_FEAT_TEST, 0x800, NULL, 0, 40 * HZ);
+				}
+			}
+		}
+#endif /* DWC_HS_ELECT_TST */
 
 	/* Now that the interfaces are installed, re-enable LPM. */
 	usb_unlocked_enable_lpm(dev);
diff -Nur --no-dereference linux-5.10.52-orig/drivers/usb/core/otg_productlist.h linux-5.10.52-v7l+/drivers/usb/core/otg_productlist.h
--- linux-5.10.52-orig/drivers/usb/core/otg_productlist.h	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/usb/core/otg_productlist.h	2021-07-25 16:46:28.447913969 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:14 @
 static struct usb_device_id productlist_table[] = {
 
 /* hubs are optional in OTG, but very handy ... */
+#define CERT_WITHOUT_HUBS
+#if defined(CERT_WITHOUT_HUBS)
+{ USB_DEVICE( 0x0000, 0x0000 ), }, /* Root HUB Only*/
+#else
 { USB_DEVICE_INFO(USB_CLASS_HUB, 0, 0), },
 { USB_DEVICE_INFO(USB_CLASS_HUB, 0, 1), },
+{ USB_DEVICE_INFO(USB_CLASS_HUB, 0, 2), },
+#endif
 
 #ifdef	CONFIG_USB_PRINTER		/* ignoring nonstatic linkage! */
 /* FIXME actually, printers are NOT supposed to use device classes;
  * they're supposed to use interface classes...
  */
-{ USB_DEVICE_INFO(7, 1, 1) },
-{ USB_DEVICE_INFO(7, 1, 2) },
-{ USB_DEVICE_INFO(7, 1, 3) },
+//{ USB_DEVICE_INFO(7, 1, 1) },
+//{ USB_DEVICE_INFO(7, 1, 2) },
+//{ USB_DEVICE_INFO(7, 1, 3) },
 #endif
 
 #ifdef	CONFIG_USB_NET_CDCETHER
 /* Linux-USB CDC Ethernet gadget */
-{ USB_DEVICE(0x0525, 0xa4a1), },
+//{ USB_DEVICE(0x0525, 0xa4a1), },
 /* Linux-USB CDC Ethernet + RNDIS gadget */
-{ USB_DEVICE(0x0525, 0xa4a2), },
+//{ USB_DEVICE(0x0525, 0xa4a2), },
 #endif
 
 #if	IS_ENABLED(CONFIG_USB_TEST)
 /* gadget zero, for testing */
-{ USB_DEVICE(0x0525, 0xa4a0), },
+//{ USB_DEVICE(0x0525, 0xa4a0), },
 #endif
 
+/* OPT Tester */
+{ USB_DEVICE( 0x1a0a, 0x0101 ), }, /* TEST_SE0_NAK */
+{ USB_DEVICE( 0x1a0a, 0x0102 ), }, /* Test_J */
+{ USB_DEVICE( 0x1a0a, 0x0103 ), }, /* Test_K */
+{ USB_DEVICE( 0x1a0a, 0x0104 ), }, /* Test_PACKET */
+{ USB_DEVICE( 0x1a0a, 0x0105 ), }, /* Test_FORCE_ENABLE */
+{ USB_DEVICE( 0x1a0a, 0x0106 ), }, /* HS_PORT_SUSPEND_RESUME  */
+{ USB_DEVICE( 0x1a0a, 0x0107 ), }, /* SINGLE_STEP_GET_DESCRIPTOR setup */
+{ USB_DEVICE( 0x1a0a, 0x0108 ), }, /* SINGLE_STEP_GET_DESCRIPTOR execute */
+
+/* Sony cameras */
+{ USB_DEVICE_VER(0x054c,0x0010,0x0410, 0x0500), },
+
+/* Memory Devices */
+//{ USB_DEVICE( 0x0781, 0x5150 ), }, /* SanDisk */
+//{ USB_DEVICE( 0x05DC, 0x0080 ), }, /* Lexar */
+//{ USB_DEVICE( 0x4146, 0x9281 ), }, /* IOMEGA */
+//{ USB_DEVICE( 0x067b, 0x2507 ), }, /* Hammer 20GB External HD  */
+{ USB_DEVICE( 0x0EA0, 0x2168 ), }, /* Ours Technology Inc. (BUFFALO ClipDrive)*/
+//{ USB_DEVICE( 0x0457, 0x0150 ), }, /* Silicon Integrated Systems Corp. */
+
+/* HP Printers */
+//{ USB_DEVICE( 0x03F0, 0x1102 ), }, /* HP Photosmart 245 */
+//{ USB_DEVICE( 0x03F0, 0x1302 ), }, /* HP Photosmart 370 Series */
+
+/* Speakers */
+//{ USB_DEVICE( 0x0499, 0x3002 ), }, /* YAMAHA YST-MS35D USB Speakers */
+//{ USB_DEVICE( 0x0672, 0x1041 ), }, /* Labtec USB Headset */
+
 { }	/* Terminating entry */
 };
 
+static inline void report_errors(struct usb_device *dev)
+{
+	/* OTG MESSAGE: report errors here, customize to match your product */
+	dev_info(&dev->dev, "device Vendor:%04x Product:%04x is not supported\n",
+		 le16_to_cpu(dev->descriptor.idVendor),
+		 le16_to_cpu(dev->descriptor.idProduct));
+        if (USB_CLASS_HUB == dev->descriptor.bDeviceClass){
+                dev_printk(KERN_CRIT, &dev->dev, "Unsupported Hub Topology\n");
+        } else {
+                dev_printk(KERN_CRIT, &dev->dev, "Attached Device is not Supported\n");
+        }
+}
+
+
 static int is_targeted(struct usb_device *dev)
 {
 	struct usb_device_id	*id = productlist_table;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:139 @
 			continue;
 
 		return 1;
-	}
+		/* NOTE: can't use usb_match_id() since interface caches
+		 * aren't set up yet. this is cut/paste from that code.
+		 */
+		for (id = productlist_table; id->match_flags; id++) {
+#ifdef DEBUG
+			dev_dbg(&dev->dev,
+				"ID: V:%04x P:%04x DC:%04x SC:%04x PR:%04x \n",
+				id->idVendor,
+				id->idProduct,
+				id->bDeviceClass,
+				id->bDeviceSubClass,
+				id->bDeviceProtocol);
+#endif
 
-	/* add other match criteria here ... */
+			if ((id->match_flags & USB_DEVICE_ID_MATCH_VENDOR) &&
+			    id->idVendor != le16_to_cpu(dev->descriptor.idVendor))
+				continue;
+
+			if ((id->match_flags & USB_DEVICE_ID_MATCH_PRODUCT) &&
+			    id->idProduct != le16_to_cpu(dev->descriptor.idProduct))
+				continue;
+
+			/* No need to test id->bcdDevice_lo != 0, since 0 is never
+			   greater than any unsigned number. */
+			if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_LO) &&
+			    (id->bcdDevice_lo > le16_to_cpu(dev->descriptor.bcdDevice)))
+				continue;
+
+			if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_HI) &&
+			    (id->bcdDevice_hi < le16_to_cpu(dev->descriptor.bcdDevice)))
+				continue;
+
+			if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_CLASS) &&
+			    (id->bDeviceClass != dev->descriptor.bDeviceClass))
+				continue;
+
+			if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_SUBCLASS) &&
+			    (id->bDeviceSubClass != dev->descriptor.bDeviceSubClass))
+				continue;
+
+			if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_PROTOCOL) &&
+			    (id->bDeviceProtocol != dev->descriptor.bDeviceProtocol))
+				continue;
 
+			return 1;
+		}
+	}
 
-	/* OTG MESSAGE: report errors here, customize to match your product */
-	dev_err(&dev->dev, "device v%04x p%04x is not supported\n",
-		le16_to_cpu(dev->descriptor.idVendor),
-		le16_to_cpu(dev->descriptor.idProduct));
+	/* add other match criteria here ... */
 
+	report_errors(dev);
 	return 0;
 }
 
diff -Nur --no-dereference linux-5.10.52-orig/drivers/usb/gadget/file_storage.c linux-5.10.52-v7l+/drivers/usb/gadget/file_storage.c
--- linux-5.10.52-orig/drivers/usb/gadget/file_storage.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/usb/gadget/file_storage.c	2021-07-25 16:46:28.497913130 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/*
+ * file_storage.c -- File-backed USB Storage Gadget, for USB development
+ *
+ * Copyright (C) 2003-2008 Alan Stern
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The names of the above-listed copyright holders may not be used
+ *    to endorse or promote products derived from this software without
+ *    specific prior written permission.
+ *
+ * ALTERNATIVELY, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") as published by the Free Software
+ * Foundation, either version 2 of that License or (at your option) any
+ * later version.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+/*
+ * The File-backed Storage Gadget acts as a USB Mass Storage device,
+ * appearing to the host as a disk drive or as a CD-ROM drive.  In addition
+ * to providing an example of a genuinely useful gadget driver for a USB
+ * device, it also illustrates a technique of double-buffering for increased
+ * throughput.  Last but not least, it gives an easy way to probe the
+ * behavior of the Mass Storage drivers in a USB host.
+ *
+ * Backing storage is provided by a regular file or a block device, specified
+ * by the "file" module parameter.  Access can be limited to read-only by
+ * setting the optional "ro" module parameter.  (For CD-ROM emulation,
+ * access is always read-only.)  The gadget will indicate that it has
+ * removable media if the optional "removable" module parameter is set.
+ *
+ * The gadget supports the Control-Bulk (CB), Control-Bulk-Interrupt (CBI),
+ * and Bulk-Only (also known as Bulk-Bulk-Bulk or BBB) transports, selected
+ * by the optional "transport" module parameter.  It also supports the
+ * following protocols: RBC (0x01), ATAPI or SFF-8020i (0x02), QIC-157 (0c03),
+ * UFI (0x04), SFF-8070i (0x05), and transparent SCSI (0x06), selected by
+ * the optional "protocol" module parameter.  In addition, the default
+ * Vendor ID, Product ID, release number and serial number can be overridden.
+ *
+ * There is support for multiple logical units (LUNs), each of which has
+ * its own backing file.  The number of LUNs can be set using the optional
+ * "luns" module parameter (anywhere from 1 to 8), and the corresponding
+ * files are specified using comma-separated lists for "file" and "ro".
+ * The default number of LUNs is taken from the number of "file" elements;
+ * it is 1 if "file" is not given.  If "removable" is not set then a backing
+ * file must be specified for each LUN.  If it is set, then an unspecified
+ * or empty backing filename means the LUN's medium is not loaded.  Ideally
+ * each LUN would be settable independently as a disk drive or a CD-ROM
+ * drive, but currently all LUNs have to be the same type.  The CD-ROM
+ * emulation includes a single data track and no audio tracks; hence there
+ * need be only one backing file per LUN.
+ *
+ * Requirements are modest; only a bulk-in and a bulk-out endpoint are
+ * needed (an interrupt-out endpoint is also needed for CBI).  The memory
+ * requirement amounts to two 16K buffers, size configurable by a parameter.
+ * Support is included for both full-speed and high-speed operation.
+ *
+ * Note that the driver is slightly non-portable in that it assumes a
+ * single memory/DMA buffer will be useable for bulk-in, bulk-out, and
+ * interrupt-in endpoints.  With most device controllers this isn't an
+ * issue, but there may be some with hardware restrictions that prevent
+ * a buffer from being used by more than one endpoint.
+ *
+ * Module options:
+ *
+ *	file=filename[,filename...]
+ *				Required if "removable" is not set, names of
+ *					the files or block devices used for
+ *					backing storage
+ *	serial=HHHH...		Required serial number (string of hex chars)
+ *	ro=b[,b...]		Default false, booleans for read-only access
+ *	removable		Default false, boolean for removable media
+ *	luns=N			Default N = number of filenames, number of
+ *					LUNs to support
+ *	nofua=b[,b...]		Default false, booleans for ignore FUA flag
+ *					in SCSI WRITE(10,12) commands
+ *	stall			Default determined according to the type of
+ *					USB device controller (usually true),
+ *					boolean to permit the driver to halt
+ *					bulk endpoints
+ *	cdrom			Default false, boolean for whether to emulate
+ *					a CD-ROM drive
+ *	transport=XXX		Default BBB, transport name (CB, CBI, or BBB)
+ *	protocol=YYY		Default SCSI, protocol name (RBC, 8020 or
+ *					ATAPI, QIC, UFI, 8070, or SCSI;
+ *					also 1 - 6)
+ *	vendor=0xVVVV		Default 0x0525 (NetChip), USB Vendor ID
+ *	product=0xPPPP		Default 0xa4a5 (FSG), USB Product ID
+ *	release=0xRRRR		Override the USB release number (bcdDevice)
+ *	buflen=N		Default N=16384, buffer size used (will be
+ *					rounded down to a multiple of
+ *					PAGE_CACHE_SIZE)
+ *
+ * If CONFIG_USB_FILE_STORAGE_TEST is not set, only the "file", "serial", "ro",
+ * "removable", "luns", "nofua", "stall", and "cdrom" options are available;
+ * default values are used for everything else.
+ *
+ * The pathnames of the backing files and the ro settings are available in
+ * the attribute files "file", "nofua", and "ro" in the lun<n> subdirectory of
+ * the gadget's sysfs directory.  If the "removable" option is set, writing to
+ * these files will simulate ejecting/loading the medium (writing an empty
+ * line means eject) and adjusting a write-enable tab.  Changes to the ro
+ * setting are not allowed when the medium is loaded or if CD-ROM emulation
+ * is being used.
+ *
+ * This gadget driver is heavily based on "Gadget Zero" by David Brownell.
+ * The driver's SCSI command interface was based on the "Information
+ * technology - Small Computer System Interface - 2" document from
+ * X3T9.2 Project 375D, Revision 10L, 7-SEP-93, available at
+ * <http://www.t10.org/ftp/t10/drafts/s2/s2-r10l.pdf>.  The single exception
+ * is opcode 0x23 (READ FORMAT CAPACITIES), which was based on the
+ * "Universal Serial Bus Mass Storage Class UFI Command Specification"
+ * document, Revision 1.0, December 14, 1998, available at
+ * <http://www.usb.org/developers/devclass_docs/usbmass-ufi10.pdf>.
+ */
+
+
+/*
+ *				Driver Design
+ *
+ * The FSG driver is fairly straightforward.  There is a main kernel
+ * thread that handles most of the work.  Interrupt routines field
+ * callbacks from the controller driver: bulk- and interrupt-request
+ * completion notifications, endpoint-0 events, and disconnect events.
+ * Completion events are passed to the main thread by wakeup calls.  Many
+ * ep0 requests are handled at interrupt time, but SetInterface,
+ * SetConfiguration, and device reset requests are forwarded to the
+ * thread in the form of "exceptions" using SIGUSR1 signals (since they
+ * should interrupt any ongoing file I/O operations).
+ *
+ * The thread's main routine implements the standard command/data/status
+ * parts of a SCSI interaction.  It and its subroutines are full of tests
+ * for pending signals/exceptions -- all this polling is necessary since
+ * the kernel has no setjmp/longjmp equivalents.  (Maybe this is an
+ * indication that the driver really wants to be running in userspace.)
+ * An important point is that so long as the thread is alive it keeps an
+ * open reference to the backing file.  This will prevent unmounting
+ * the backing file's underlying filesystem and could cause problems
+ * during system shutdown, for example.  To prevent such problems, the
+ * thread catches INT, TERM, and KILL signals and converts them into
+ * an EXIT exception.
+ *
+ * In normal operation the main thread is started during the gadget's
+ * fsg_bind() callback and stopped during fsg_unbind().  But it can also
+ * exit when it receives a signal, and there's no point leaving the
+ * gadget running when the thread is dead.  So just before the thread
+ * exits, it deregisters the gadget driver.  This makes things a little
+ * tricky: The driver is deregistered at two places, and the exiting
+ * thread can indirectly call fsg_unbind() which in turn can tell the
+ * thread to exit.  The first problem is resolved through the use of the
+ * REGISTERED atomic bitflag; the driver will only be deregistered once.
+ * The second problem is resolved by having fsg_unbind() check
+ * fsg->state; it won't try to stop the thread if the state is already
+ * FSG_STATE_TERMINATED.
+ *
+ * To provide maximum throughput, the driver uses a circular pipeline of
+ * buffer heads (struct fsg_buffhd).  In principle the pipeline can be
+ * arbitrarily long; in practice the benefits don't justify having more
+ * than 2 stages (i.e., double buffering).  But it helps to think of the
+ * pipeline as being a long one.  Each buffer head contains a bulk-in and
+ * a bulk-out request pointer (since the buffer can be used for both
+ * output and input -- directions always are given from the host's
+ * point of view) as well as a pointer to the buffer and various state
+ * variables.
+ *
+ * Use of the pipeline follows a simple protocol.  There is a variable
+ * (fsg->next_buffhd_to_fill) that points to the next buffer head to use.
+ * At any time that buffer head may still be in use from an earlier
+ * request, so each buffer head has a state variable indicating whether
+ * it is EMPTY, FULL, or BUSY.  Typical use involves waiting for the
+ * buffer head to be EMPTY, filling the buffer either by file I/O or by
+ * USB I/O (during which the buffer head is BUSY), and marking the buffer
+ * head FULL when the I/O is complete.  Then the buffer will be emptied
+ * (again possibly by USB I/O, during which it is marked BUSY) and
+ * finally marked EMPTY again (possibly by a completion routine).
+ *
+ * A module parameter tells the driver to avoid stalling the bulk
+ * endpoints wherever the transport specification allows.  This is
+ * necessary for some UDCs like the SuperH, which cannot reliably clear a
+ * halt on a bulk endpoint.  However, under certain circumstances the
+ * Bulk-only specification requires a stall.  In such cases the driver
+ * will halt the endpoint and set a flag indicating that it should clear
+ * the halt in software during the next device reset.  Hopefully this
+ * will permit everything to work correctly.  Furthermore, although the
+ * specification allows the bulk-out endpoint to halt when the host sends
+ * too much data, implementing this would cause an unavoidable race.
+ * The driver will always use the "no-stall" approach for OUT transfers.
+ *
+ * One subtle point concerns sending status-stage responses for ep0
+ * requests.  Some of these requests, such as device reset, can involve
+ * interrupting an ongoing file I/O operation, which might take an
+ * arbitrarily long time.  During that delay the host might give up on
+ * the original ep0 request and issue a new one.  When that happens the
+ * driver should not notify the host about completion of the original
+ * request, as the host will no longer be waiting for it.  So the driver
+ * assigns to each ep0 request a unique tag, and it keeps track of the
+ * tag value of the request associated with a long-running exception
+ * (device-reset, interface-change, or configuration-change).  When the
+ * exception handler is finished, the status-stage response is submitted
+ * only if the current ep0 request tag is equal to the exception request
+ * tag.  Thus only the most recently received ep0 request will get a
+ * status-stage response.
+ *
+ * Warning: This driver source file is too long.  It ought to be split up
+ * into a header file plus about 3 separate .c files, to handle the details
+ * of the Gadget, USB Mass Storage, and SCSI protocols.
+ */
+
+
+/* #define VERBOSE_DEBUG */
+/* #define DUMP_MSGS */
+
+
+#include <linux/blkdev.h>
+#include <linux/completion.h>
+#include <linux/dcache.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/fcntl.h>
+#include <linux/file.h>
+#include <linux/fs.h>
+#include <linux/kref.h>
+#include <linux/kthread.h>
+#include <linux/limits.h>
+#include <linux/module.h>
+#include <linux/rwsem.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/string.h>
+#include <linux/freezer.h>
+#include <linux/utsname.h>
+
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+
+#include "gadget_chips.h"
+
+
+
+/*
+ * Kbuild is not very cooperative with respect to linking separately
+ * compiled library objects into one module.  So for now we won't use
+ * separate compilation ... ensuring init/exit sections work to shrink
+ * the runtime footprint, and giving us at least some parts of what
+ * a "gcc --combine ... part1.c part2.c part3.c ... " build would.
+ */
+#include "usbstring.c"
+#include "config.c"
+#include "epautoconf.c"
+
+/*-------------------------------------------------------------------------*/
+
+#define DRIVER_DESC		"File-backed Storage Gadget"
+#define DRIVER_NAME		"g_file_storage"
+#define DRIVER_VERSION		"1 September 2010"
+
+static       char fsg_string_manufacturer[64];
+static const char fsg_string_product[] = DRIVER_DESC;
+static const char fsg_string_config[] = "Self-powered";
+static const char fsg_string_interface[] = "Mass Storage";
+
+
+#include "storage_common.c"
+
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR("Alan Stern");
+MODULE_LICENSE("Dual BSD/GPL");
+
+/*
+ * This driver assumes self-powered hardware and has no way for users to
+ * trigger remote wakeup.  It uses autoconfiguration to select endpoints
+ * and endpoint addresses.
+ */
+
+
+/*-------------------------------------------------------------------------*/
+
+
+/* Encapsulate the module parameter settings */
+
+static struct {
+	char		*file[FSG_MAX_LUNS];
+	char		*serial;
+	bool		ro[FSG_MAX_LUNS];
+	bool		nofua[FSG_MAX_LUNS];
+	unsigned int	num_filenames;
+	unsigned int	num_ros;
+	unsigned int	num_nofuas;
+	unsigned int	nluns;
+
+	bool		removable;
+	bool		can_stall;
+	bool		cdrom;
+
+	char		*transport_parm;
+	char		*protocol_parm;
+	unsigned short	vendor;
+	unsigned short	product;
+	unsigned short	release;
+	unsigned int	buflen;
+
+	int		transport_type;
+	char		*transport_name;
+	int		protocol_type;
+	char		*protocol_name;
+
+} mod_data = {					// Default values
+	.transport_parm		= "BBB",
+	.protocol_parm		= "SCSI",
+	.removable		= 0,
+	.can_stall		= 1,
+	.cdrom			= 0,
+	.vendor			= FSG_VENDOR_ID,
+	.product		= FSG_PRODUCT_ID,
+	.release		= 0xffff,	// Use controller chip type
+	.buflen			= 16384,
+	};
+
+
+module_param_array_named(file, mod_data.file, charp, &mod_data.num_filenames,
+		S_IRUGO);
+MODULE_PARM_DESC(file, "names of backing files or devices");
+
+module_param_named(serial, mod_data.serial, charp, S_IRUGO);
+MODULE_PARM_DESC(serial, "USB serial number");
+
+module_param_array_named(ro, mod_data.ro, bool, &mod_data.num_ros, S_IRUGO);
+MODULE_PARM_DESC(ro, "true to force read-only");
+
+module_param_array_named(nofua, mod_data.nofua, bool, &mod_data.num_nofuas,
+		S_IRUGO);
+MODULE_PARM_DESC(nofua, "true to ignore SCSI WRITE(10,12) FUA bit");
+
+module_param_named(luns, mod_data.nluns, uint, S_IRUGO);
+MODULE_PARM_DESC(luns, "number of LUNs");
+
+module_param_named(removable, mod_data.removable, bool, S_IRUGO);
+MODULE_PARM_DESC(removable, "true to simulate removable media");
+
+module_param_named(stall, mod_data.can_stall, bool, S_IRUGO);
+MODULE_PARM_DESC(stall, "false to prevent bulk stalls");
+
+module_param_named(cdrom, mod_data.cdrom, bool, S_IRUGO);
+MODULE_PARM_DESC(cdrom, "true to emulate cdrom instead of disk");
+
+/* In the non-TEST version, only the module parameters listed above
+ * are available. */
+#ifdef CONFIG_USB_FILE_STORAGE_TEST
+
+module_param_named(transport, mod_data.transport_parm, charp, S_IRUGO);
+MODULE_PARM_DESC(transport, "type of transport (BBB, CBI, or CB)");
+
+module_param_named(protocol, mod_data.protocol_parm, charp, S_IRUGO);
+MODULE_PARM_DESC(protocol, "type of protocol (RBC, 8020, QIC, UFI, "
+		"8070, or SCSI)");
+
+module_param_named(vendor, mod_data.vendor, ushort, S_IRUGO);
+MODULE_PARM_DESC(vendor, "USB Vendor ID");
+
+module_param_named(product, mod_data.product, ushort, S_IRUGO);
+MODULE_PARM_DESC(product, "USB Product ID");
+
+module_param_named(release, mod_data.release, ushort, S_IRUGO);
+MODULE_PARM_DESC(release, "USB release number");
+
+module_param_named(buflen, mod_data.buflen, uint, S_IRUGO);
+MODULE_PARM_DESC(buflen, "I/O buffer size");
+
+#endif /* CONFIG_USB_FILE_STORAGE_TEST */
+
+
+/*
+ * These definitions will permit the compiler to avoid generating code for
+ * parts of the driver that aren't used in the non-TEST version.  Even gcc
+ * can recognize when a test of a constant expression yields a dead code
+ * path.
+ */
+
+#ifdef CONFIG_USB_FILE_STORAGE_TEST
+
+#define transport_is_bbb()	(mod_data.transport_type == USB_PR_BULK)
+#define transport_is_cbi()	(mod_data.transport_type == USB_PR_CBI)
+#define protocol_is_scsi()	(mod_data.protocol_type == USB_SC_SCSI)
+
+#else
+
+#define transport_is_bbb()	1
+#define transport_is_cbi()	0
+#define protocol_is_scsi()	1
+
+#endif /* CONFIG_USB_FILE_STORAGE_TEST */
+
+
+/*-------------------------------------------------------------------------*/
+
+
+struct fsg_dev {
+	/* lock protects: state, all the req_busy's, and cbbuf_cmnd */
+	spinlock_t		lock;
+	struct usb_gadget	*gadget;
+
+	/* filesem protects: backing files in use */
+	struct rw_semaphore	filesem;
+
+	/* reference counting: wait until all LUNs are released */
+	struct kref		ref;
+
+	struct usb_ep		*ep0;		// Handy copy of gadget->ep0
+	struct usb_request	*ep0req;	// For control responses
+	unsigned int		ep0_req_tag;
+	const char		*ep0req_name;
+
+	struct usb_request	*intreq;	// For interrupt responses
+	int			intreq_busy;
+	struct fsg_buffhd	*intr_buffhd;
+
+	unsigned int		bulk_out_maxpacket;
+	enum fsg_state		state;		// For exception handling
+	unsigned int		exception_req_tag;
+
+	u8			config, new_config;
+
+	unsigned int		running : 1;
+	unsigned int		bulk_in_enabled : 1;
+	unsigned int		bulk_out_enabled : 1;
+	unsigned int		intr_in_enabled : 1;
+	unsigned int		phase_error : 1;
+	unsigned int		short_packet_received : 1;
+	unsigned int		bad_lun_okay : 1;
+
+	unsigned long		atomic_bitflags;
+#define REGISTERED		0
+#define IGNORE_BULK_OUT		1
+#define SUSPENDED		2
+
+	struct usb_ep		*bulk_in;
+	struct usb_ep		*bulk_out;
+	struct usb_ep		*intr_in;
+
+	struct fsg_buffhd	*next_buffhd_to_fill;
+	struct fsg_buffhd	*next_buffhd_to_drain;
+
+	int			thread_wakeup_needed;
+	struct completion	thread_notifier;
+	struct task_struct	*thread_task;
+
+	int			cmnd_size;
+	u8			cmnd[MAX_COMMAND_SIZE];
+	enum data_direction	data_dir;
+	u32			data_size;
+	u32			data_size_from_cmnd;
+	u32			tag;
+	unsigned int		lun;
+	u32			residue;
+	u32			usb_amount_left;
+
+	/* The CB protocol offers no way for a host to know when a command
+	 * has completed.  As a result the next command may arrive early,
+	 * and we will still have to handle it.  For that reason we need
+	 * a buffer to store new commands when using CB (or CBI, which
+	 * does not oblige a host to wait for command completion either). */
+	int			cbbuf_cmnd_size;
+	u8			cbbuf_cmnd[MAX_COMMAND_SIZE];
+
+	unsigned int		nluns;
+	struct fsg_lun		*luns;
+	struct fsg_lun		*curlun;
+	/* Must be the last entry */
+	struct fsg_buffhd	buffhds[];
+};
+
+typedef void (*fsg_routine_t)(struct fsg_dev *);
+
+static int exception_in_progress(struct fsg_dev *fsg)
+{
+	return (fsg->state > FSG_STATE_IDLE);
+}
+
+/* Make bulk-out requests be divisible by the maxpacket size */
+static void set_bulk_out_req_length(struct fsg_dev *fsg,
+		struct fsg_buffhd *bh, unsigned int length)
+{
+	unsigned int	rem;
+
+	bh->bulk_out_intended_length = length;
+	rem = length % fsg->bulk_out_maxpacket;
+	if (rem > 0)
+		length += fsg->bulk_out_maxpacket - rem;
+	bh->outreq->length = length;
+}
+
+static struct fsg_dev			*the_fsg;
+static struct usb_gadget_driver		fsg_driver;
+
+
+/*-------------------------------------------------------------------------*/
+
+static int fsg_set_halt(struct fsg_dev *fsg, struct usb_ep *ep)
+{
+	const char	*name;
+
+	if (ep == fsg->bulk_in)
+		name = "bulk-in";
+	else if (ep == fsg->bulk_out)
+		name = "bulk-out";
+	else
+		name = ep->name;
+	DBG(fsg, "%s set halt\n", name);
+	return usb_ep_set_halt(ep);
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * DESCRIPTORS ... most are static, but strings and (full) configuration
+ * descriptors are built on demand.  Also the (static) config and interface
+ * descriptors are adjusted during fsg_bind().
+ */
+
+/* There is only one configuration. */
+#define	CONFIG_VALUE		1
+
+static struct usb_device_descriptor
+device_desc = {
+	.bLength =		sizeof device_desc,
+	.bDescriptorType =	USB_DT_DEVICE,
+
+	.bcdUSB =		cpu_to_le16(0x0200),
+	.bDeviceClass =		USB_CLASS_PER_INTERFACE,
+
+	/* The next three values can be overridden by module parameters */
+	.idVendor =		cpu_to_le16(FSG_VENDOR_ID),
+	.idProduct =		cpu_to_le16(FSG_PRODUCT_ID),
+	.bcdDevice =		cpu_to_le16(0xffff),
+
+	.iManufacturer =	FSG_STRING_MANUFACTURER,
+	.iProduct =		FSG_STRING_PRODUCT,
+	.iSerialNumber =	FSG_STRING_SERIAL,
+	.bNumConfigurations =	1,
+};
+
+static struct usb_config_descriptor
+config_desc = {
+	.bLength =		sizeof config_desc,
+	.bDescriptorType =	USB_DT_CONFIG,
+
+	/* wTotalLength computed by usb_gadget_config_buf() */
+	.bNumInterfaces =	1,
+	.bConfigurationValue =	CONFIG_VALUE,
+	.iConfiguration =	FSG_STRING_CONFIG,
+	.bmAttributes =		USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER,
+	.bMaxPower =		CONFIG_USB_GADGET_VBUS_DRAW / 2,
+};
+
+
+static struct usb_qualifier_descriptor
+dev_qualifier = {
+	.bLength =		sizeof dev_qualifier,
+	.bDescriptorType =	USB_DT_DEVICE_QUALIFIER,
+
+	.bcdUSB =		cpu_to_le16(0x0200),
+	.bDeviceClass =		USB_CLASS_PER_INTERFACE,
+
+	.bNumConfigurations =	1,
+};
+
+static int populate_bos(struct fsg_dev *fsg, u8 *buf)
+{
+	memcpy(buf, &fsg_bos_desc, USB_DT_BOS_SIZE);
+	buf += USB_DT_BOS_SIZE;
+
+	memcpy(buf, &fsg_ext_cap_desc, USB_DT_USB_EXT_CAP_SIZE);
+	buf += USB_DT_USB_EXT_CAP_SIZE;
+
+	memcpy(buf, &fsg_ss_cap_desc, USB_DT_USB_SS_CAP_SIZE);
+
+	return USB_DT_BOS_SIZE + USB_DT_USB_SS_CAP_SIZE
+		+ USB_DT_USB_EXT_CAP_SIZE;
+}
+
+/*
+ * Config descriptors must agree with the code that sets configurations
+ * and with code managing interfaces and their altsettings.  They must
+ * also handle different speeds and other-speed requests.
+ */
+static int populate_config_buf(struct usb_gadget *gadget,
+		u8 *buf, u8 type, unsigned index)
+{
+	enum usb_device_speed			speed = gadget->speed;
+	int					len;
+	const struct usb_descriptor_header	**function;
+
+	if (index > 0)
+		return -EINVAL;
+
+	if (gadget_is_dualspeed(gadget) && type == USB_DT_OTHER_SPEED_CONFIG)
+		speed = (USB_SPEED_FULL + USB_SPEED_HIGH) - speed;
+	function = gadget_is_dualspeed(gadget) && speed == USB_SPEED_HIGH
+		? (const struct usb_descriptor_header **)fsg_hs_function
+		: (const struct usb_descriptor_header **)fsg_fs_function;
+
+	/* for now, don't advertise srp-only devices */
+	if (!gadget_is_otg(gadget))
+		function++;
+
+	len = usb_gadget_config_buf(&config_desc, buf, EP0_BUFSIZE, function);
+	((struct usb_config_descriptor *) buf)->bDescriptorType = type;
+	return len;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+/* These routines may be called in process context or in_irq */
+
+/* Caller must hold fsg->lock */
+static void wakeup_thread(struct fsg_dev *fsg)
+{
+	/* Tell the main thread that something has happened */
+	fsg->thread_wakeup_needed = 1;
+	if (fsg->thread_task)
+		wake_up_process(fsg->thread_task);
+}
+
+
+static void raise_exception(struct fsg_dev *fsg, enum fsg_state new_state)
+{
+	unsigned long		flags;
+
+	/* Do nothing if a higher-priority exception is already in progress.
+	 * If a lower-or-equal priority exception is in progress, preempt it
+	 * and notify the main thread by sending it a signal. */
+	spin_lock_irqsave(&fsg->lock, flags);
+	if (fsg->state <= new_state) {
+		fsg->exception_req_tag = fsg->ep0_req_tag;
+		fsg->state = new_state;
+		if (fsg->thread_task)
+			send_sig_info(SIGUSR1, SEND_SIG_FORCED,
+					fsg->thread_task);
+	}
+	spin_unlock_irqrestore(&fsg->lock, flags);
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+/* The disconnect callback and ep0 routines.  These always run in_irq,
+ * except that ep0_queue() is called in the main thread to acknowledge
+ * completion of various requests: set config, set interface, and
+ * Bulk-only device reset. */
+
+static void fsg_disconnect(struct usb_gadget *gadget)
+{
+	struct fsg_dev		*fsg = get_gadget_data(gadget);
+
+	DBG(fsg, "disconnect or port reset\n");
+	raise_exception(fsg, FSG_STATE_DISCONNECT);
+}
+
+
+static int ep0_queue(struct fsg_dev *fsg)
+{
+	int	rc;
+
+	rc = usb_ep_queue(fsg->ep0, fsg->ep0req, GFP_ATOMIC);
+	if (rc != 0 && rc != -ESHUTDOWN) {
+
+		/* We can't do much more than wait for a reset */
+		WARNING(fsg, "error in submission: %s --> %d\n",
+				fsg->ep0->name, rc);
+	}
+	return rc;
+}
+
+static void ep0_complete(struct usb_ep *ep, struct usb_request *req)
+{
+	struct fsg_dev		*fsg = ep->driver_data;
+
+	if (req->actual > 0)
+		dump_msg(fsg, fsg->ep0req_name, req->buf, req->actual);
+	if (req->status || req->actual != req->length)
+		DBG(fsg, "%s --> %d, %u/%u\n", __func__,
+				req->status, req->actual, req->length);
+	if (req->status == -ECONNRESET)		// Request was cancelled
+		usb_ep_fifo_flush(ep);
+
+	if (req->status == 0 && req->context)
+		((fsg_routine_t) (req->context))(fsg);
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+/* Bulk and interrupt endpoint completion handlers.
+ * These always run in_irq. */
+
+static void bulk_in_complete(struct usb_ep *ep, struct usb_request *req)
+{
+	struct fsg_dev		*fsg = ep->driver_data;
+	struct fsg_buffhd	*bh = req->context;
+
+	if (req->status || req->actual != req->length)
+		DBG(fsg, "%s --> %d, %u/%u\n", __func__,
+				req->status, req->actual, req->length);
+	if (req->status == -ECONNRESET)		// Request was cancelled
+		usb_ep_fifo_flush(ep);
+
+	/* Hold the lock while we update the request and buffer states */
+	smp_wmb();
+	spin_lock(&fsg->lock);
+	bh->inreq_busy = 0;
+	bh->state = BUF_STATE_EMPTY;
+	wakeup_thread(fsg);
+	spin_unlock(&fsg->lock);
+}
+
+static void bulk_out_complete(struct usb_ep *ep, struct usb_request *req)
+{
+	struct fsg_dev		*fsg = ep->driver_data;
+	struct fsg_buffhd	*bh = req->context;
+
+	dump_msg(fsg, "bulk-out", req->buf, req->actual);
+	if (req->status || req->actual != bh->bulk_out_intended_length)
+		DBG(fsg, "%s --> %d, %u/%u\n", __func__,
+				req->status, req->actual,
+				bh->bulk_out_intended_length);
+	if (req->status == -ECONNRESET)		// Request was cancelled
+		usb_ep_fifo_flush(ep);
+
+	/* Hold the lock while we update the request and buffer states */
+	smp_wmb();
+	spin_lock(&fsg->lock);
+	bh->outreq_busy = 0;
+	bh->state = BUF_STATE_FULL;
+	wakeup_thread(fsg);
+	spin_unlock(&fsg->lock);
+}
+
+
+#ifdef CONFIG_USB_FILE_STORAGE_TEST
+static void intr_in_complete(struct usb_ep *ep, struct usb_request *req)
+{
+	struct fsg_dev		*fsg = ep->driver_data;
+	struct fsg_buffhd	*bh = req->context;
+
+	if (req->status || req->actual != req->length)
+		DBG(fsg, "%s --> %d, %u/%u\n", __func__,
+				req->status, req->actual, req->length);
+	if (req->status == -ECONNRESET)		// Request was cancelled
+		usb_ep_fifo_flush(ep);
+
+	/* Hold the lock while we update the request and buffer states */
+	smp_wmb();
+	spin_lock(&fsg->lock);
+	fsg->intreq_busy = 0;
+	bh->state = BUF_STATE_EMPTY;
+	wakeup_thread(fsg);
+	spin_unlock(&fsg->lock);
+}
+
+#else
+static void intr_in_complete(struct usb_ep *ep, struct usb_request *req)
+{}
+#endif /* CONFIG_USB_FILE_STORAGE_TEST */
+
+
+/*-------------------------------------------------------------------------*/
+
+/* Ep0 class-specific handlers.  These always run in_irq. */
+
+#ifdef CONFIG_USB_FILE_STORAGE_TEST
+static void received_cbi_adsc(struct fsg_dev *fsg, struct fsg_buffhd *bh)
+{
+	struct usb_request	*req = fsg->ep0req;
+	static u8		cbi_reset_cmnd[6] = {
+			SEND_DIAGNOSTIC, 4, 0xff, 0xff, 0xff, 0xff};
+
+	/* Error in command transfer? */
+	if (req->status || req->length != req->actual ||
+			req->actual < 6 || req->actual > MAX_COMMAND_SIZE) {
+
+		/* Not all controllers allow a protocol stall after
+		 * receiving control-out data, but we'll try anyway. */
+		fsg_set_halt(fsg, fsg->ep0);
+		return;			// Wait for reset
+	}
+
+	/* Is it the special reset command? */
+	if (req->actual >= sizeof cbi_reset_cmnd &&
+			memcmp(req->buf, cbi_reset_cmnd,
+				sizeof cbi_reset_cmnd) == 0) {
+
+		/* Raise an exception to stop the current operation
+		 * and reinitialize our state. */
+		DBG(fsg, "cbi reset request\n");
+		raise_exception(fsg, FSG_STATE_RESET);
+		return;
+	}
+
+	VDBG(fsg, "CB[I] accept device-specific command\n");
+	spin_lock(&fsg->lock);
+
+	/* Save the command for later */
+	if (fsg->cbbuf_cmnd_size)
+		WARNING(fsg, "CB[I] overwriting previous command\n");
+	fsg->cbbuf_cmnd_size = req->actual;
+	memcpy(fsg->cbbuf_cmnd, req->buf, fsg->cbbuf_cmnd_size);
+
+	wakeup_thread(fsg);
+	spin_unlock(&fsg->lock);
+}
+
+#else
+static void received_cbi_adsc(struct fsg_dev *fsg, struct fsg_buffhd *bh)
+{}
+#endif /* CONFIG_USB_FILE_STORAGE_TEST */
+
+
+static int class_setup_req(struct fsg_dev *fsg,
+		const struct usb_ctrlrequest *ctrl)
+{
+	struct usb_request	*req = fsg->ep0req;
+	int			value = -EOPNOTSUPP;
+	u16			w_index = le16_to_cpu(ctrl->wIndex);
+	u16                     w_value = le16_to_cpu(ctrl->wValue);
+	u16			w_length = le16_to_cpu(ctrl->wLength);
+
+	if (!fsg->config)
+		return value;
+
+	/* Handle Bulk-only class-specific requests */
+	if (transport_is_bbb()) {
+		switch (ctrl->bRequest) {
+
+		case US_BULK_RESET_REQUEST:
+			if (ctrl->bRequestType != (USB_DIR_OUT |
+					USB_TYPE_CLASS | USB_RECIP_INTERFACE))
+				break;
+			if (w_index != 0 || w_value != 0 || w_length != 0) {
+				value = -EDOM;
+				break;
+			}
+
+			/* Raise an exception to stop the current operation
+			 * and reinitialize our state. */
+			DBG(fsg, "bulk reset request\n");
+			raise_exception(fsg, FSG_STATE_RESET);
+			value = DELAYED_STATUS;
+			break;
+
+		case US_BULK_GET_MAX_LUN:
+			if (ctrl->bRequestType != (USB_DIR_IN |
+					USB_TYPE_CLASS | USB_RECIP_INTERFACE))
+				break;
+			if (w_index != 0 || w_value != 0 || w_length != 1) {
+				value = -EDOM;
+				break;
+			}
+			VDBG(fsg, "get max LUN\n");
+			*(u8 *) req->buf = fsg->nluns - 1;
+			value = 1;
+			break;
+		}
+	}
+
+	/* Handle CBI class-specific requests */
+	else {
+		switch (ctrl->bRequest) {
+
+		case USB_CBI_ADSC_REQUEST:
+			if (ctrl->bRequestType != (USB_DIR_OUT |
+					USB_TYPE_CLASS | USB_RECIP_INTERFACE))
+				break;
+			if (w_index != 0 || w_value != 0) {
+				value = -EDOM;
+				break;
+			}
+			if (w_length > MAX_COMMAND_SIZE) {
+				value = -EOVERFLOW;
+				break;
+			}
+			value = w_length;
+			fsg->ep0req->context = received_cbi_adsc;
+			break;
+		}
+	}
+
+	if (value == -EOPNOTSUPP)
+		VDBG(fsg,
+			"unknown class-specific control req "
+			"%02x.%02x v%04x i%04x l%u\n",
+			ctrl->bRequestType, ctrl->bRequest,
+			le16_to_cpu(ctrl->wValue), w_index, w_length);
+	return value;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+/* Ep0 standard request handlers.  These always run in_irq. */
+
+static int standard_setup_req(struct fsg_dev *fsg,
+		const struct usb_ctrlrequest *ctrl)
+{
+	struct usb_request	*req = fsg->ep0req;
+	int			value = -EOPNOTSUPP;
+	u16			w_index = le16_to_cpu(ctrl->wIndex);
+	u16			w_value = le16_to_cpu(ctrl->wValue);
+
+	/* Usually this just stores reply data in the pre-allocated ep0 buffer,
+	 * but config change events will also reconfigure hardware. */
+	switch (ctrl->bRequest) {
+
+	case USB_REQ_GET_DESCRIPTOR:
+		if (ctrl->bRequestType != (USB_DIR_IN | USB_TYPE_STANDARD |
+				USB_RECIP_DEVICE))
+			break;
+		switch (w_value >> 8) {
+
+		case USB_DT_DEVICE:
+			VDBG(fsg, "get device descriptor\n");
+			device_desc.bMaxPacketSize0 = fsg->ep0->maxpacket;
+			value = sizeof device_desc;
+			memcpy(req->buf, &device_desc, value);
+			break;
+		case USB_DT_DEVICE_QUALIFIER:
+			VDBG(fsg, "get device qualifier\n");
+			if (!gadget_is_dualspeed(fsg->gadget) ||
+					fsg->gadget->speed == USB_SPEED_SUPER)
+				break;
+			/*
+			 * Assume ep0 uses the same maxpacket value for both
+			 * speeds
+			 */
+			dev_qualifier.bMaxPacketSize0 = fsg->ep0->maxpacket;
+			value = sizeof dev_qualifier;
+			memcpy(req->buf, &dev_qualifier, value);
+			break;
+
+		case USB_DT_OTHER_SPEED_CONFIG:
+			VDBG(fsg, "get other-speed config descriptor\n");
+			if (!gadget_is_dualspeed(fsg->gadget) ||
+					fsg->gadget->speed == USB_SPEED_SUPER)
+				break;
+			goto get_config;
+		case USB_DT_CONFIG:
+			VDBG(fsg, "get configuration descriptor\n");
+get_config:
+			value = populate_config_buf(fsg->gadget,
+					req->buf,
+					w_value >> 8,
+					w_value & 0xff);
+			break;
+
+		case USB_DT_STRING:
+			VDBG(fsg, "get string descriptor\n");
+
+			/* wIndex == language code */
+			value = usb_gadget_get_string(&fsg_stringtab,
+					w_value & 0xff, req->buf);
+			break;
+
+		case USB_DT_BOS:
+			VDBG(fsg, "get bos descriptor\n");
+
+			if (gadget_is_superspeed(fsg->gadget))
+				value = populate_bos(fsg, req->buf);
+			break;
+		}
+
+		break;
+
+	/* One config, two speeds */
+	case USB_REQ_SET_CONFIGURATION:
+		if (ctrl->bRequestType != (USB_DIR_OUT | USB_TYPE_STANDARD |
+				USB_RECIP_DEVICE))
+			break;
+		VDBG(fsg, "set configuration\n");
+		if (w_value == CONFIG_VALUE || w_value == 0) {
+			fsg->new_config = w_value;
+
+			/* Raise an exception to wipe out previous transaction
+			 * state (queued bufs, etc) and set the new config. */
+			raise_exception(fsg, FSG_STATE_CONFIG_CHANGE);
+			value = DELAYED_STATUS;
+		}
+		break;
+	case USB_REQ_GET_CONFIGURATION:
+		if (ctrl->bRequestType != (USB_DIR_IN | USB_TYPE_STANDARD |
+				USB_RECIP_DEVICE))
+			break;
+		VDBG(fsg, "get configuration\n");
+		*(u8 *) req->buf = fsg->config;
+		value = 1;
+		break;
+
+	case USB_REQ_SET_INTERFACE:
+		if (ctrl->bRequestType != (USB_DIR_OUT| USB_TYPE_STANDARD |
+				USB_RECIP_INTERFACE))
+			break;
+		if (fsg->config && w_index == 0) {
+
+			/* Raise an exception to wipe out previous transaction
+			 * state (queued bufs, etc) and install the new
+			 * interface altsetting. */
+			raise_exception(fsg, FSG_STATE_INTERFACE_CHANGE);
+			value = DELAYED_STATUS;
+		}
+		break;
+	case USB_REQ_GET_INTERFACE:
+		if (ctrl->bRequestType != (USB_DIR_IN | USB_TYPE_STANDARD |
+				USB_RECIP_INTERFACE))
+			break;
+		if (!fsg->config)
+			break;
+		if (w_index != 0) {
+			value = -EDOM;
+			break;
+		}
+		VDBG(fsg, "get interface\n");
+		*(u8 *) req->buf = 0;
+		value = 1;
+		break;
+
+	default:
+		VDBG(fsg,
+			"unknown control req %02x.%02x v%04x i%04x l%u\n",
+			ctrl->bRequestType, ctrl->bRequest,
+			w_value, w_index, le16_to_cpu(ctrl->wLength));
+	}
+
+	return value;
+}
+
+
+static int fsg_setup(struct usb_gadget *gadget,
+		const struct usb_ctrlrequest *ctrl)
+{
+	struct fsg_dev		*fsg = get_gadget_data(gadget);
+	int			rc;
+	int			w_length = le16_to_cpu(ctrl->wLength);
+
+	++fsg->ep0_req_tag;		// Record arrival of a new request
+	fsg->ep0req->context = NULL;
+	fsg->ep0req->length = 0;
+	dump_msg(fsg, "ep0-setup", (u8 *) ctrl, sizeof(*ctrl));
+
+	if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_CLASS)
+		rc = class_setup_req(fsg, ctrl);
+	else
+		rc = standard_setup_req(fsg, ctrl);
+
+	/* Respond with data/status or defer until later? */
+	if (rc >= 0 && rc != DELAYED_STATUS) {
+		rc = min(rc, w_length);
+		fsg->ep0req->length = rc;
+		fsg->ep0req->zero = rc < w_length;
+		fsg->ep0req_name = (ctrl->bRequestType & USB_DIR_IN ?
+				"ep0-in" : "ep0-out");
+		rc = ep0_queue(fsg);
+	}
+
+	/* Device either stalls (rc < 0) or reports success */
+	return rc;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+/* All the following routines run in process context */
+
+
+/* Use this for bulk or interrupt transfers, not ep0 */
+static void start_transfer(struct fsg_dev *fsg, struct usb_ep *ep,
+		struct usb_request *req, int *pbusy,
+		enum fsg_buffer_state *state)
+{
+	int	rc;
+
+	if (ep == fsg->bulk_in)
+		dump_msg(fsg, "bulk-in", req->buf, req->length);
+	else if (ep == fsg->intr_in)
+		dump_msg(fsg, "intr-in", req->buf, req->length);
+
+	spin_lock_irq(&fsg->lock);
+	*pbusy = 1;
+	*state = BUF_STATE_BUSY;
+	spin_unlock_irq(&fsg->lock);
+	rc = usb_ep_queue(ep, req, GFP_KERNEL);
+	if (rc != 0) {
+		*pbusy = 0;
+		*state = BUF_STATE_EMPTY;
+
+		/* We can't do much more than wait for a reset */
+
+		/* Note: currently the net2280 driver fails zero-length
+		 * submissions if DMA is enabled. */
+		if (rc != -ESHUTDOWN && !(rc == -EOPNOTSUPP &&
+						req->length == 0))
+			WARNING(fsg, "error in submission: %s --> %d\n",
+					ep->name, rc);
+	}
+}
+
+
+static int sleep_thread(struct fsg_dev *fsg)
+{
+	int	rc = 0;
+
+	/* Wait until a signal arrives or we are woken up */
+	for (;;) {
+		try_to_freeze();
+		set_current_state(TASK_INTERRUPTIBLE);
+		if (signal_pending(current)) {
+			rc = -EINTR;
+			break;
+		}
+		if (fsg->thread_wakeup_needed)
+			break;
+		schedule();
+	}
+	__set_current_state(TASK_RUNNING);
+	fsg->thread_wakeup_needed = 0;
+	return rc;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+static int do_read(struct fsg_dev *fsg)
+{
+	struct fsg_lun		*curlun = fsg->curlun;
+	u32			lba;
+	struct fsg_buffhd	*bh;
+	int			rc;
+	u32			amount_left;
+	loff_t			file_offset, file_offset_tmp;
+	unsigned int		amount;
+	ssize_t			nread;
+
+	/* Get the starting Logical Block Address and check that it's
+	 * not too big */
+	if (fsg->cmnd[0] == READ_6)
+		lba = get_unaligned_be24(&fsg->cmnd[1]);
+	else {
+		lba = get_unaligned_be32(&fsg->cmnd[2]);
+
+		/* We allow DPO (Disable Page Out = don't save data in the
+		 * cache) and FUA (Force Unit Access = don't read from the
+		 * cache), but we don't implement them. */
+		if ((fsg->cmnd[1] & ~0x18) != 0) {
+			curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
+			return -EINVAL;
+		}
+	}
+	if (lba >= curlun->num_sectors) {
+		curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
+		return -EINVAL;
+	}
+	file_offset = ((loff_t) lba) << curlun->blkbits;
+
+	/* Carry out the file reads */
+	amount_left = fsg->data_size_from_cmnd;
+	if (unlikely(amount_left == 0))
+		return -EIO;		// No default reply
+
+	for (;;) {
+
+		/* Figure out how much we need to read:
+		 * Try to read the remaining amount.
+		 * But don't read more than the buffer size.
+		 * And don't try to read past the end of the file.
+		 */
+		amount = min((unsigned int) amount_left, mod_data.buflen);
+		amount = min((loff_t) amount,
+				curlun->file_length - file_offset);
+
+		/* Wait for the next buffer to become available */
+		bh = fsg->next_buffhd_to_fill;
+		while (bh->state != BUF_STATE_EMPTY) {
+			rc = sleep_thread(fsg);
+			if (rc)
+				return rc;
+		}
+
+		/* If we were asked to read past the end of file,
+		 * end with an empty buffer. */
+		if (amount == 0) {
+			curlun->sense_data =
+					SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
+			curlun->sense_data_info = file_offset >> curlun->blkbits;
+			curlun->info_valid = 1;
+			bh->inreq->length = 0;
+			bh->state = BUF_STATE_FULL;
+			break;
+		}
+
+		/* Perform the read */
+		file_offset_tmp = file_offset;
+		nread = vfs_read(curlun->filp,
+				(char __user *) bh->buf,
+				amount, &file_offset_tmp);
+		VLDBG(curlun, "file read %u @ %llu -> %d\n", amount,
+				(unsigned long long) file_offset,
+				(int) nread);
+		if (signal_pending(current))
+			return -EINTR;
+
+		if (nread < 0) {
+			LDBG(curlun, "error in file read: %d\n",
+					(int) nread);
+			nread = 0;
+		} else if (nread < amount) {
+			LDBG(curlun, "partial file read: %d/%u\n",
+					(int) nread, amount);
+			nread = round_down(nread, curlun->blksize);
+		}
+		file_offset  += nread;
+		amount_left  -= nread;
+		fsg->residue -= nread;
+
+		/* Except at the end of the transfer, nread will be
+		 * equal to the buffer size, which is divisible by the
+		 * bulk-in maxpacket size.
+		 */
+		bh->inreq->length = nread;
+		bh->state = BUF_STATE_FULL;
+
+		/* If an error occurred, report it and its position */
+		if (nread < amount) {
+			curlun->sense_data = SS_UNRECOVERED_READ_ERROR;
+			curlun->sense_data_info = file_offset >> curlun->blkbits;
+			curlun->info_valid = 1;
+			break;
+		}
+
+		if (amount_left == 0)
+			break;		// No more left to read
+
+		/* Send this buffer and go read some more */
+		bh->inreq->zero = 0;
+		start_transfer(fsg, fsg->bulk_in, bh->inreq,
+				&bh->inreq_busy, &bh->state);
+		fsg->next_buffhd_to_fill = bh->next;
+	}
+
+	return -EIO;		// No default reply
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+static int do_write(struct fsg_dev *fsg)
+{
+	struct fsg_lun		*curlun = fsg->curlun;
+	u32			lba;
+	struct fsg_buffhd	*bh;
+	int			get_some_more;
+	u32			amount_left_to_req, amount_left_to_write;
+	loff_t			usb_offset, file_offset, file_offset_tmp;
+	unsigned int		amount;
+	ssize_t			nwritten;
+	int			rc;
+
+	if (curlun->ro) {
+		curlun->sense_data = SS_WRITE_PROTECTED;
+		return -EINVAL;
+	}
+	spin_lock(&curlun->filp->f_lock);
+	curlun->filp->f_flags &= ~O_SYNC;	// Default is not to wait
+	spin_unlock(&curlun->filp->f_lock);
+
+	/* Get the starting Logical Block Address and check that it's
+	 * not too big */
+	if (fsg->cmnd[0] == WRITE_6)
+		lba = get_unaligned_be24(&fsg->cmnd[1]);
+	else {
+		lba = get_unaligned_be32(&fsg->cmnd[2]);
+
+		/* We allow DPO (Disable Page Out = don't save data in the
+		 * cache) and FUA (Force Unit Access = write directly to the
+		 * medium).  We don't implement DPO; we implement FUA by
+		 * performing synchronous output. */
+		if ((fsg->cmnd[1] & ~0x18) != 0) {
+			curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
+			return -EINVAL;
+		}
+		/* FUA */
+		if (!curlun->nofua && (fsg->cmnd[1] & 0x08)) {
+			spin_lock(&curlun->filp->f_lock);
+			curlun->filp->f_flags |= O_DSYNC;
+			spin_unlock(&curlun->filp->f_lock);
+		}
+	}
+	if (lba >= curlun->num_sectors) {
+		curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
+		return -EINVAL;
+	}
+
+	/* Carry out the file writes */
+	get_some_more = 1;
+	file_offset = usb_offset = ((loff_t) lba) << curlun->blkbits;
+	amount_left_to_req = amount_left_to_write = fsg->data_size_from_cmnd;
+
+	while (amount_left_to_write > 0) {
+
+		/* Queue a request for more data from the host */
+		bh = fsg->next_buffhd_to_fill;
+		if (bh->state == BUF_STATE_EMPTY && get_some_more) {
+
+			/* Figure out how much we want to get:
+			 * Try to get the remaining amount,
+			 * but not more than the buffer size.
+			 */
+			amount = min(amount_left_to_req, mod_data.buflen);
+
+			/* Beyond the end of the backing file? */
+			if (usb_offset >= curlun->file_length) {
+				get_some_more = 0;
+				curlun->sense_data =
+					SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
+				curlun->sense_data_info = usb_offset >> curlun->blkbits;
+				curlun->info_valid = 1;
+				continue;
+			}
+
+			/* Get the next buffer */
+			usb_offset += amount;
+			fsg->usb_amount_left -= amount;
+			amount_left_to_req -= amount;
+			if (amount_left_to_req == 0)
+				get_some_more = 0;
+
+			/* Except at the end of the transfer, amount will be
+			 * equal to the buffer size, which is divisible by
+			 * the bulk-out maxpacket size.
+			 */
+			set_bulk_out_req_length(fsg, bh, amount);
+			start_transfer(fsg, fsg->bulk_out, bh->outreq,
+					&bh->outreq_busy, &bh->state);
+			fsg->next_buffhd_to_fill = bh->next;
+			continue;
+		}
+
+		/* Write the received data to the backing file */
+		bh = fsg->next_buffhd_to_drain;
+		if (bh->state == BUF_STATE_EMPTY && !get_some_more)
+			break;			// We stopped early
+		if (bh->state == BUF_STATE_FULL) {
+			smp_rmb();
+			fsg->next_buffhd_to_drain = bh->next;
+			bh->state = BUF_STATE_EMPTY;
+
+			/* Did something go wrong with the transfer? */
+			if (bh->outreq->status != 0) {
+				curlun->sense_data = SS_COMMUNICATION_FAILURE;
+				curlun->sense_data_info = file_offset >> curlun->blkbits;
+				curlun->info_valid = 1;
+				break;
+			}
+
+			amount = bh->outreq->actual;
+			if (curlun->file_length - file_offset < amount) {
+				LERROR(curlun,
+	"write %u @ %llu beyond end %llu\n",
+	amount, (unsigned long long) file_offset,
+	(unsigned long long) curlun->file_length);
+				amount = curlun->file_length - file_offset;
+			}
+
+			/* Don't accept excess data.  The spec doesn't say
+			 * what to do in this case.  We'll ignore the error.
+			 */
+			amount = min(amount, bh->bulk_out_intended_length);
+
+			/* Don't write a partial block */
+			amount = round_down(amount, curlun->blksize);
+			if (amount == 0)
+				goto empty_write;
+
+			/* Perform the write */
+			file_offset_tmp = file_offset;
+			nwritten = vfs_write(curlun->filp,
+					(char __user *) bh->buf,
+					amount, &file_offset_tmp);
+			VLDBG(curlun, "file write %u @ %llu -> %d\n", amount,
+					(unsigned long long) file_offset,
+					(int) nwritten);
+			if (signal_pending(current))
+				return -EINTR;		// Interrupted!
+
+			if (nwritten < 0) {
+				LDBG(curlun, "error in file write: %d\n",
+						(int) nwritten);
+				nwritten = 0;
+			} else if (nwritten < amount) {
+				LDBG(curlun, "partial file write: %d/%u\n",
+						(int) nwritten, amount);
+				nwritten = round_down(nwritten, curlun->blksize);
+			}
+			file_offset += nwritten;
+			amount_left_to_write -= nwritten;
+			fsg->residue -= nwritten;
+
+			/* If an error occurred, report it and its position */
+			if (nwritten < amount) {
+				curlun->sense_data = SS_WRITE_ERROR;
+				curlun->sense_data_info = file_offset >> curlun->blkbits;
+				curlun->info_valid = 1;
+				break;
+			}
+
+ empty_write:
+			/* Did the host decide to stop early? */
+			if (bh->outreq->actual < bh->bulk_out_intended_length) {
+				fsg->short_packet_received = 1;
+				break;
+			}
+			continue;
+		}
+
+		/* Wait for something to happen */
+		rc = sleep_thread(fsg);
+		if (rc)
+			return rc;
+	}
+
+	return -EIO;		// No default reply
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+static int do_synchronize_cache(struct fsg_dev *fsg)
+{
+	struct fsg_lun	*curlun = fsg->curlun;
+	int		rc;
+
+	/* We ignore the requested LBA and write out all file's
+	 * dirty data buffers. */
+	rc = fsg_lun_fsync_sub(curlun);
+	if (rc)
+		curlun->sense_data = SS_WRITE_ERROR;
+	return 0;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+static void invalidate_sub(struct fsg_lun *curlun)
+{
+	struct file	*filp = curlun->filp;
+	struct inode	*inode = filp->f_path.dentry->d_inode;
+	unsigned long	rc;
+
+	rc = invalidate_mapping_pages(inode->i_mapping, 0, -1);
+	VLDBG(curlun, "invalidate_mapping_pages -> %ld\n", rc);
+}
+
+static int do_verify(struct fsg_dev *fsg)
+{
+	struct fsg_lun		*curlun = fsg->curlun;
+	u32			lba;
+	u32			verification_length;
+	struct fsg_buffhd	*bh = fsg->next_buffhd_to_fill;
+	loff_t			file_offset, file_offset_tmp;
+	u32			amount_left;
+	unsigned int		amount;
+	ssize_t			nread;
+
+	/* Get the starting Logical Block Address and check that it's
+	 * not too big */
+	lba = get_unaligned_be32(&fsg->cmnd[2]);
+	if (lba >= curlun->num_sectors) {
+		curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
+		return -EINVAL;
+	}
+
+	/* We allow DPO (Disable Page Out = don't save data in the
+	 * cache) but we don't implement it. */
+	if ((fsg->cmnd[1] & ~0x10) != 0) {
+		curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
+		return -EINVAL;
+	}
+
+	verification_length = get_unaligned_be16(&fsg->cmnd[7]);
+	if (unlikely(verification_length == 0))
+		return -EIO;		// No default reply
+
+	/* Prepare to carry out the file verify */
+	amount_left = verification_length << curlun->blkbits;
+	file_offset = ((loff_t) lba) << curlun->blkbits;
+
+	/* Write out all the dirty buffers before invalidating them */
+	fsg_lun_fsync_sub(curlun);
+	if (signal_pending(current))
+		return -EINTR;
+
+	invalidate_sub(curlun);
+	if (signal_pending(current))
+		return -EINTR;
+
+	/* Just try to read the requested blocks */
+	while (amount_left > 0) {
+
+		/* Figure out how much we need to read:
+		 * Try to read the remaining amount, but not more than
+		 * the buffer size.
+		 * And don't try to read past the end of the file.
+		 */
+		amount = min((unsigned int) amount_left, mod_data.buflen);
+		amount = min((loff_t) amount,
+				curlun->file_length - file_offset);
+		if (amount == 0) {
+			curlun->sense_data =
+					SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
+			curlun->sense_data_info = file_offset >> curlun->blkbits;
+			curlun->info_valid = 1;
+			break;
+		}
+
+		/* Perform the read */
+		file_offset_tmp = file_offset;
+		nread = vfs_read(curlun->filp,
+				(char __user *) bh->buf,
+				amount, &file_offset_tmp);
+		VLDBG(curlun, "file read %u @ %llu -> %d\n", amount,
+				(unsigned long long) file_offset,
+				(int) nread);
+		if (signal_pending(current))
+			return -EINTR;
+
+		if (nread < 0) {
+			LDBG(curlun, "error in file verify: %d\n",
+					(int) nread);
+			nread = 0;
+		} else if (nread < amount) {
+			LDBG(curlun, "partial file verify: %d/%u\n",
+					(int) nread, amount);
+			nread = round_down(nread, curlun->blksize);
+		}
+		if (nread == 0) {
+			curlun->sense_data = SS_UNRECOVERED_READ_ERROR;
+			curlun->sense_data_info = file_offset >> curlun->blkbits;
+			curlun->info_valid = 1;
+			break;
+		}
+		file_offset += nread;
+		amount_left -= nread;
+	}
+	return 0;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+static int do_inquiry(struct fsg_dev *fsg, struct fsg_buffhd *bh)
+{
+	u8	*buf = (u8 *) bh->buf;
+
+	static char vendor_id[] = "Linux   ";
+	static char product_disk_id[] = "File-Stor Gadget";
+	static char product_cdrom_id[] = "File-CD Gadget  ";
+
+	if (!fsg->curlun) {		// Unsupported LUNs are okay
+		fsg->bad_lun_okay = 1;
+		memset(buf, 0, 36);
+		buf[0] = 0x7f;		// Unsupported, no device-type
+		buf[4] = 31;		// Additional length
+		return 36;
+	}
+
+	memset(buf, 0, 8);
+	buf[0] = (mod_data.cdrom ? TYPE_ROM : TYPE_DISK);
+	if (mod_data.removable)
+		buf[1] = 0x80;
+	buf[2] = 2;		// ANSI SCSI level 2
+	buf[3] = 2;		// SCSI-2 INQUIRY data format
+	buf[4] = 31;		// Additional length
+				// No special options
+	sprintf(buf + 8, "%-8s%-16s%04x", vendor_id,
+			(mod_data.cdrom ? product_cdrom_id :
+				product_disk_id),
+			mod_data.release);
+	return 36;
+}
+
+
+static int do_request_sense(struct fsg_dev *fsg, struct fsg_buffhd *bh)
+{
+	struct fsg_lun	*curlun = fsg->curlun;
+	u8		*buf = (u8 *) bh->buf;
+	u32		sd, sdinfo;
+	int		valid;
+
+	/*
+	 * From the SCSI-2 spec., section 7.9 (Unit attention condition):
+	 *
+	 * If a REQUEST SENSE command is received from an initiator
+	 * with a pending unit attention condition (before the target
+	 * generates the contingent allegiance condition), then the
+	 * target shall either:
+	 *   a) report any pending sense data and preserve the unit
+	 *	attention condition on the logical unit, or,
+	 *   b) report the unit attention condition, may discard any
+	 *	pending sense data, and clear the unit attention
+	 *	condition on the logical unit for that initiator.
+	 *
+	 * FSG normally uses option a); enable this code to use option b).
+	 */
+#if 0
+	if (curlun && curlun->unit_attention_data != SS_NO_SENSE) {
+		curlun->sense_data = curlun->unit_attention_data;
+		curlun->unit_attention_data = SS_NO_SENSE;
+	}
+#endif
+
+	if (!curlun) {		// Unsupported LUNs are okay
+		fsg->bad_lun_okay = 1;
+		sd = SS_LOGICAL_UNIT_NOT_SUPPORTED;
+		sdinfo = 0;
+		valid = 0;
+	} else {
+		sd = curlun->sense_data;
+		sdinfo = curlun->sense_data_info;
+		valid = curlun->info_valid << 7;
+		curlun->sense_data = SS_NO_SENSE;
+		curlun->sense_data_info = 0;
+		curlun->info_valid = 0;
+	}
+
+	memset(buf, 0, 18);
+	buf[0] = valid | 0x70;			// Valid, current error
+	buf[2] = SK(sd);
+	put_unaligned_be32(sdinfo, &buf[3]);	/* Sense information */
+	buf[7] = 18 - 8;			// Additional sense length
+	buf[12] = ASC(sd);
+	buf[13] = ASCQ(sd);
+	return 18;
+}
+
+
+static int do_read_capacity(struct fsg_dev *fsg, struct fsg_buffhd *bh)
+{
+	struct fsg_lun	*curlun = fsg->curlun;
+	u32		lba = get_unaligned_be32(&fsg->cmnd[2]);
+	int		pmi = fsg->cmnd[8];
+	u8		*buf = (u8 *) bh->buf;
+
+	/* Check the PMI and LBA fields */
+	if (pmi > 1 || (pmi == 0 && lba != 0)) {
+		curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
+		return -EINVAL;
+	}
+
+	put_unaligned_be32(curlun->num_sectors - 1, &buf[0]);
+						/* Max logical block */
+	put_unaligned_be32(curlun->blksize, &buf[4]);	/* Block length */
+	return 8;
+}
+
+
+static int do_read_header(struct fsg_dev *fsg, struct fsg_buffhd *bh)
+{
+	struct fsg_lun	*curlun = fsg->curlun;
+	int		msf = fsg->cmnd[1] & 0x02;
+	u32		lba = get_unaligned_be32(&fsg->cmnd[2]);
+	u8		*buf = (u8 *) bh->buf;
+
+	if ((fsg->cmnd[1] & ~0x02) != 0) {		/* Mask away MSF */
+		curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
+		return -EINVAL;
+	}
+	if (lba >= curlun->num_sectors) {
+		curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
+		return -EINVAL;
+	}
+
+	memset(buf, 0, 8);
+	buf[0] = 0x01;		/* 2048 bytes of user data, rest is EC */
+	store_cdrom_address(&buf[4], msf, lba);
+	return 8;
+}
+
+
+static int do_read_toc(struct fsg_dev *fsg, struct fsg_buffhd *bh)
+{
+	struct fsg_lun	*curlun = fsg->curlun;
+	int		msf = fsg->cmnd[1] & 0x02;
+	int		start_track = fsg->cmnd[6];
+	u8		*buf = (u8 *) bh->buf;
+
+	if ((fsg->cmnd[1] & ~0x02) != 0 ||		/* Mask away MSF */
+			start_track > 1) {
+		curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
+		return -EINVAL;
+	}
+
+	memset(buf, 0, 20);
+	buf[1] = (20-2);		/* TOC data length */
+	buf[2] = 1;			/* First track number */
+	buf[3] = 1;			/* Last track number */
+	buf[5] = 0x16;			/* Data track, copying allowed */
+	buf[6] = 0x01;			/* Only track is number 1 */
+	store_cdrom_address(&buf[8], msf, 0);
+
+	buf[13] = 0x16;			/* Lead-out track is data */
+	buf[14] = 0xAA;			/* Lead-out track number */
+	store_cdrom_address(&buf[16], msf, curlun->num_sectors);
+	return 20;
+}
+
+
+static int do_mode_sense(struct fsg_dev *fsg, struct fsg_buffhd *bh)
+{
+	struct fsg_lun	*curlun = fsg->curlun;
+	int		mscmnd = fsg->cmnd[0];
+	u8		*buf = (u8 *) bh->buf;
+	u8		*buf0 = buf;
+	int		pc, page_code;
+	int		changeable_values, all_pages;
+	int		valid_page = 0;
+	int		len, limit;
+
+	if ((fsg->cmnd[1] & ~0x08) != 0) {		// Mask away DBD
+		curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
+		return -EINVAL;
+	}
+	pc = fsg->cmnd[2] >> 6;
+	page_code = fsg->cmnd[2] & 0x3f;
+	if (pc == 3) {
+		curlun->sense_data = SS_SAVING_PARAMETERS_NOT_SUPPORTED;
+		return -EINVAL;
+	}
+	changeable_values = (pc == 1);
+	all_pages = (page_code == 0x3f);
+
+	/* Write the mode parameter header.  Fixed values are: default
+	 * medium type, no cache control (DPOFUA), and no block descriptors.
+	 * The only variable value is the WriteProtect bit.  We will fill in
+	 * the mode data length later. */
+	memset(buf, 0, 8);
+	if (mscmnd == MODE_SENSE) {
+		buf[2] = (curlun->ro ? 0x80 : 0x00);		// WP, DPOFUA
+		buf += 4;
+		limit = 255;
+	} else {			// MODE_SENSE_10
+		buf[3] = (curlun->ro ? 0x80 : 0x00);		// WP, DPOFUA
+		buf += 8;
+		limit = 65535;		// Should really be mod_data.buflen
+	}
+
+	/* No block descriptors */
+
+	/* The mode pages, in numerical order.  The only page we support
+	 * is the Caching page. */
+	if (page_code == 0x08 || all_pages) {
+		valid_page = 1;
+		buf[0] = 0x08;		// Page code
+		buf[1] = 10;		// Page length
+		memset(buf+2, 0, 10);	// None of the fields are changeable
+
+		if (!changeable_values) {
+			buf[2] = 0x04;	// Write cache enable,
+					// Read cache not disabled
+					// No cache retention priorities
+			put_unaligned_be16(0xffff, &buf[4]);
+					/* Don't disable prefetch */
+					/* Minimum prefetch = 0 */
+			put_unaligned_be16(0xffff, &buf[8]);
+					/* Maximum prefetch */
+			put_unaligned_be16(0xffff, &buf[10]);
+					/* Maximum prefetch ceiling */
+		}
+		buf += 12;
+	}
+
+	/* Check that a valid page was requested and the mode data length
+	 * isn't too long. */
+	len = buf - buf0;
+	if (!valid_page || len > limit) {
+		curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
+		return -EINVAL;
+	}
+
+	/*  Store the mode data length */
+	if (mscmnd == MODE_SENSE)
+		buf0[0] = len - 1;
+	else
+		put_unaligned_be16(len - 2, buf0);
+	return len;
+}
+
+
+static int do_start_stop(struct fsg_dev *fsg)
+{
+	struct fsg_lun	*curlun = fsg->curlun;
+	int		loej, start;
+
+	if (!mod_data.removable) {
+		curlun->sense_data = SS_INVALID_COMMAND;
+		return -EINVAL;
+	}
+
+	// int immed = fsg->cmnd[1] & 0x01;
+	loej = fsg->cmnd[4] & 0x02;
+	start = fsg->cmnd[4] & 0x01;
+
+#ifdef CONFIG_USB_FILE_STORAGE_TEST
+	if ((fsg->cmnd[1] & ~0x01) != 0 ||		// Mask away Immed
+			(fsg->cmnd[4] & ~0x03) != 0) {	// Mask LoEj, Start
+		curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
+		return -EINVAL;
+	}
+
+	if (!start) {
+
+		/* Are we allowed to unload the media? */
+		if (curlun->prevent_medium_removal) {
+			LDBG(curlun, "unload attempt prevented\n");
+			curlun->sense_data = SS_MEDIUM_REMOVAL_PREVENTED;
+			return -EINVAL;
+		}
+		if (loej) {		// Simulate an unload/eject
+			up_read(&fsg->filesem);
+			down_write(&fsg->filesem);
+			fsg_lun_close(curlun);
+			up_write(&fsg->filesem);
+			down_read(&fsg->filesem);
+		}
+	} else {
+
+		/* Our emulation doesn't support mounting; the medium is
+		 * available for use as soon as it is loaded. */
+		if (!fsg_lun_is_open(curlun)) {
+			curlun->sense_data = SS_MEDIUM_NOT_PRESENT;
+			return -EINVAL;
+		}
+	}
+#endif
+	return 0;
+}
+
+
+static int do_prevent_allow(struct fsg_dev *fsg)
+{
+	struct fsg_lun	*curlun = fsg->curlun;
+	int		prevent;
+
+	if (!mod_data.removable) {
+		curlun->sense_data = SS_INVALID_COMMAND;
+		return -EINVAL;
+	}
+
+	prevent = fsg->cmnd[4] & 0x01;
+	if ((fsg->cmnd[4] & ~0x01) != 0) {		// Mask away Prevent
+		curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
+		return -EINVAL;
+	}
+
+	if (curlun->prevent_medium_removal && !prevent)
+		fsg_lun_fsync_sub(curlun);
+	curlun->prevent_medium_removal = prevent;
+	return 0;
+}
+
+
+static int do_read_format_capacities(struct fsg_dev *fsg,
+			struct fsg_buffhd *bh)
+{
+	struct fsg_lun	*curlun = fsg->curlun;
+	u8		*buf = (u8 *) bh->buf;
+
+	buf[0] = buf[1] = buf[2] = 0;
+	buf[3] = 8;		// Only the Current/Maximum Capacity Descriptor
+	buf += 4;
+
+	put_unaligned_be32(curlun->num_sectors, &buf[0]);
+						/* Number of blocks */
+	put_unaligned_be32(curlun->blksize, &buf[4]);	/* Block length */
+	buf[4] = 0x02;				/* Current capacity */
+	return 12;
+}
+
+
+static int do_mode_select(struct fsg_dev *fsg, struct fsg_buffhd *bh)
+{
+	struct fsg_lun	*curlun = fsg->curlun;
+
+	/* We don't support MODE SELECT */
+	curlun->sense_data = SS_INVALID_COMMAND;
+	return -EINVAL;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+static int halt_bulk_in_endpoint(struct fsg_dev *fsg)
+{
+	int	rc;
+
+	rc = fsg_set_halt(fsg, fsg->bulk_in);
+	if (rc == -EAGAIN)
+		VDBG(fsg, "delayed bulk-in endpoint halt\n");
+	while (rc != 0) {
+		if (rc != -EAGAIN) {
+			WARNING(fsg, "usb_ep_set_halt -> %d\n", rc);
+			rc = 0;
+			break;
+		}
+
+		/* Wait for a short time and then try again */
+		if (msleep_interruptible(100) != 0)
+			return -EINTR;
+		rc = usb_ep_set_halt(fsg->bulk_in);
+	}
+	return rc;
+}
+
+static int wedge_bulk_in_endpoint(struct fsg_dev *fsg)
+{
+	int	rc;
+
+	DBG(fsg, "bulk-in set wedge\n");
+	rc = usb_ep_set_wedge(fsg->bulk_in);
+	if (rc == -EAGAIN)
+		VDBG(fsg, "delayed bulk-in endpoint wedge\n");
+	while (rc != 0) {
+		if (rc != -EAGAIN) {
+			WARNING(fsg, "usb_ep_set_wedge -> %d\n", rc);
+			rc = 0;
+			break;
+		}
+
+		/* Wait for a short time and then try again */
+		if (msleep_interruptible(100) != 0)
+			return -EINTR;
+		rc = usb_ep_set_wedge(fsg->bulk_in);
+	}
+	return rc;
+}
+
+static int throw_away_data(struct fsg_dev *fsg)
+{
+	struct fsg_buffhd	*bh;
+	u32			amount;
+	int			rc;
+
+	while ((bh = fsg->next_buffhd_to_drain)->state != BUF_STATE_EMPTY ||
+			fsg->usb_amount_left > 0) {
+
+		/* Throw away the data in a filled buffer */
+		if (bh->state == BUF_STATE_FULL) {
+			smp_rmb();
+			bh->state = BUF_STATE_EMPTY;
+			fsg->next_buffhd_to_drain = bh->next;
+
+			/* A short packet or an error ends everything */
+			if (bh->outreq->actual < bh->bulk_out_intended_length ||
+					bh->outreq->status != 0) {
+				raise_exception(fsg, FSG_STATE_ABORT_BULK_OUT);
+				return -EINTR;
+			}
+			continue;
+		}
+
+		/* Try to submit another request if we need one */
+		bh = fsg->next_buffhd_to_fill;
+		if (bh->state == BUF_STATE_EMPTY && fsg->usb_amount_left > 0) {
+			amount = min(fsg->usb_amount_left,
+					(u32) mod_data.buflen);
+
+			/* Except at the end of the transfer, amount will be
+			 * equal to the buffer size, which is divisible by
+			 * the bulk-out maxpacket size.
+			 */
+			set_bulk_out_req_length(fsg, bh, amount);
+			start_transfer(fsg, fsg->bulk_out, bh->outreq,
+					&bh->outreq_busy, &bh->state);
+			fsg->next_buffhd_to_fill = bh->next;
+			fsg->usb_amount_left -= amount;
+			continue;
+		}
+
+		/* Otherwise wait for something to happen */
+		rc = sleep_thread(fsg);
+		if (rc)
+			return rc;
+	}
+	return 0;
+}
+
+
+static int finish_reply(struct fsg_dev *fsg)
+{
+	struct fsg_buffhd	*bh = fsg->next_buffhd_to_fill;
+	int			rc = 0;
+
+	switch (fsg->data_dir) {
+	case DATA_DIR_NONE:
+		break;			// Nothing to send
+
+	/* If we don't know whether the host wants to read or write,
+	 * this must be CB or CBI with an unknown command.  We mustn't
+	 * try to send or receive any data.  So stall both bulk pipes
+	 * if we can and wait for a reset. */
+	case DATA_DIR_UNKNOWN:
+		if (mod_data.can_stall) {
+			fsg_set_halt(fsg, fsg->bulk_out);
+			rc = halt_bulk_in_endpoint(fsg);
+		}
+		break;
+
+	/* All but the last buffer of data must have already been sent */
+	case DATA_DIR_TO_HOST:
+		if (fsg->data_size == 0)
+			;		// Nothing to send
+
+		/* If there's no residue, simply send the last buffer */
+		else if (fsg->residue == 0) {
+			bh->inreq->zero = 0;
+			start_transfer(fsg, fsg->bulk_in, bh->inreq,
+					&bh->inreq_busy, &bh->state);
+			fsg->next_buffhd_to_fill = bh->next;
+		}
+
+		/* There is a residue.  For CB and CBI, simply mark the end
+		 * of the data with a short packet.  However, if we are
+		 * allowed to stall, there was no data at all (residue ==
+		 * data_size), and the command failed (invalid LUN or
+		 * sense data is set), then halt the bulk-in endpoint
+		 * instead. */
+		else if (!transport_is_bbb()) {
+			if (mod_data.can_stall &&
+					fsg->residue == fsg->data_size &&
+	(!fsg->curlun || fsg->curlun->sense_data != SS_NO_SENSE)) {
+				bh->state = BUF_STATE_EMPTY;
+				rc = halt_bulk_in_endpoint(fsg);
+			} else {
+				bh->inreq->zero = 1;
+				start_transfer(fsg, fsg->bulk_in, bh->inreq,
+						&bh->inreq_busy, &bh->state);
+				fsg->next_buffhd_to_fill = bh->next;
+			}
+		}
+
+		/*
+		 * For Bulk-only, mark the end of the data with a short
+		 * packet.  If we are allowed to stall, halt the bulk-in
+		 * endpoint.  (Note: This violates the Bulk-Only Transport
+		 * specification, which requires us to pad the data if we
+		 * don't halt the endpoint.  Presumably nobody will mind.)
+		 */
+		else {
+			bh->inreq->zero = 1;
+			start_transfer(fsg, fsg->bulk_in, bh->inreq,
+					&bh->inreq_busy, &bh->state);
+			fsg->next_buffhd_to_fill = bh->next;
+			if (mod_data.can_stall)
+				rc = halt_bulk_in_endpoint(fsg);
+		}
+		break;
+
+	/* We have processed all we want from the data the host has sent.
+	 * There may still be outstanding bulk-out requests. */
+	case DATA_DIR_FROM_HOST:
+		if (fsg->residue == 0)
+			;		// Nothing to receive
+
+		/* Did the host stop sending unexpectedly early? */
+		else if (fsg->short_packet_received) {
+			raise_exception(fsg, FSG_STATE_ABORT_BULK_OUT);
+			rc = -EINTR;
+		}
+
+		/* We haven't processed all the incoming data.  Even though
+		 * we may be allowed to stall, doing so would cause a race.
+		 * The controller may already have ACK'ed all the remaining
+		 * bulk-out packets, in which case the host wouldn't see a
+		 * STALL.  Not realizing the endpoint was halted, it wouldn't
+		 * clear the halt -- leading to problems later on. */
+#if 0
+		else if (mod_data.can_stall) {
+			fsg_set_halt(fsg, fsg->bulk_out);
+			raise_exception(fsg, FSG_STATE_ABORT_BULK_OUT);
+			rc = -EINTR;
+		}
+#endif
+
+		/* We can't stall.  Read in the excess data and throw it
+		 * all away. */
+		else
+			rc = throw_away_data(fsg);
+		break;
+	}
+	return rc;
+}
+
+
+static int send_status(struct fsg_dev *fsg)
+{
+	struct fsg_lun		*curlun = fsg->curlun;
+	struct fsg_buffhd	*bh;
+	int			rc;
+	u8			status = US_BULK_STAT_OK;
+	u32			sd, sdinfo = 0;
+
+	/* Wait for the next buffer to become available */
+	bh = fsg->next_buffhd_to_fill;
+	while (bh->state != BUF_STATE_EMPTY) {
+		rc = sleep_thread(fsg);
+		if (rc)
+			return rc;
+	}
+
+	if (curlun) {
+		sd = curlun->sense_data;
+		sdinfo = curlun->sense_data_info;
+	} else if (fsg->bad_lun_okay)
+		sd = SS_NO_SENSE;
+	else
+		sd = SS_LOGICAL_UNIT_NOT_SUPPORTED;
+
+	if (fsg->phase_error) {
+		DBG(fsg, "sending phase-error status\n");
+		status = US_BULK_STAT_PHASE;
+		sd = SS_INVALID_COMMAND;
+	} else if (sd != SS_NO_SENSE) {
+		DBG(fsg, "sending command-failure status\n");
+		status = US_BULK_STAT_FAIL;
+		VDBG(fsg, "  sense data: SK x%02x, ASC x%02x, ASCQ x%02x;"
+				"  info x%x\n",
+				SK(sd), ASC(sd), ASCQ(sd), sdinfo);
+	}
+
+	if (transport_is_bbb()) {
+		struct bulk_cs_wrap	*csw = bh->buf;
+
+		/* Store and send the Bulk-only CSW */
+		csw->Signature = cpu_to_le32(US_BULK_CS_SIGN);
+		csw->Tag = fsg->tag;
+		csw->Residue = cpu_to_le32(fsg->residue);
+		csw->Status = status;
+
+		bh->inreq->length = US_BULK_CS_WRAP_LEN;
+		bh->inreq->zero = 0;
+		start_transfer(fsg, fsg->bulk_in, bh->inreq,
+				&bh->inreq_busy, &bh->state);
+
+	} else if (mod_data.transport_type == USB_PR_CB) {
+
+		/* Control-Bulk transport has no status phase! */
+		return 0;
+
+	} else {			// USB_PR_CBI
+		struct interrupt_data	*buf = bh->buf;
+
+		/* Store and send the Interrupt data.  UFI sends the ASC
+		 * and ASCQ bytes.  Everything else sends a Type (which
+		 * is always 0) and the status Value. */
+		if (mod_data.protocol_type == USB_SC_UFI) {
+			buf->bType = ASC(sd);
+			buf->bValue = ASCQ(sd);
+		} else {
+			buf->bType = 0;
+			buf->bValue = status;
+		}
+		fsg->intreq->length = CBI_INTERRUPT_DATA_LEN;
+
+		fsg->intr_buffhd = bh;		// Point to the right buffhd
+		fsg->intreq->buf = bh->inreq->buf;
+		fsg->intreq->context = bh;
+		start_transfer(fsg, fsg->intr_in, fsg->intreq,
+				&fsg->intreq_busy, &bh->state);
+	}
+
+	fsg->next_buffhd_to_fill = bh->next;
+	return 0;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+/* Check whether the command is properly formed and whether its data size
+ * and direction agree with the values we already have. */
+static int check_command(struct fsg_dev *fsg, int cmnd_size,
+		enum data_direction data_dir, unsigned int mask,
+		int needs_medium, const char *name)
+{
+	int			i;
+	int			lun = fsg->cmnd[1] >> 5;
+	static const char	dirletter[4] = {'u', 'o', 'i', 'n'};
+	char			hdlen[20];
+	struct fsg_lun		*curlun;
+
+	/* Adjust the expected cmnd_size for protocol encapsulation padding.
+	 * Transparent SCSI doesn't pad. */
+	if (protocol_is_scsi())
+		;
+
+	/* There's some disagreement as to whether RBC pads commands or not.
+	 * We'll play it safe and accept either form. */
+	else if (mod_data.protocol_type == USB_SC_RBC) {
+		if (fsg->cmnd_size == 12)
+			cmnd_size = 12;
+
+	/* All the other protocols pad to 12 bytes */
+	} else
+		cmnd_size = 12;
+
+	hdlen[0] = 0;
+	if (fsg->data_dir != DATA_DIR_UNKNOWN)
+		sprintf(hdlen, ", H%c=%u", dirletter[(int) fsg->data_dir],
+				fsg->data_size);
+	VDBG(fsg, "SCSI command: %s;  Dc=%d, D%c=%u;  Hc=%d%s\n",
+			name, cmnd_size, dirletter[(int) data_dir],
+			fsg->data_size_from_cmnd, fsg->cmnd_size, hdlen);
+
+	/* We can't reply at all until we know the correct data direction
+	 * and size. */
+	if (fsg->data_size_from_cmnd == 0)
+		data_dir = DATA_DIR_NONE;
+	if (fsg->data_dir == DATA_DIR_UNKNOWN) {	// CB or CBI
+		fsg->data_dir = data_dir;
+		fsg->data_size = fsg->data_size_from_cmnd;
+
+	} else {					// Bulk-only
+		if (fsg->data_size < fsg->data_size_from_cmnd) {
+
+			/* Host data size < Device data size is a phase error.
+			 * Carry out the command, but only transfer as much
+			 * as we are allowed. */
+			fsg->data_size_from_cmnd = fsg->data_size;
+			fsg->phase_error = 1;
+		}
+	}
+	fsg->residue = fsg->usb_amount_left = fsg->data_size;
+
+	/* Conflicting data directions is a phase error */
+	if (fsg->data_dir != data_dir && fsg->data_size_from_cmnd > 0) {
+		fsg->phase_error = 1;
+		return -EINVAL;
+	}
+
+	/* Verify the length of the command itself */
+	if (cmnd_size != fsg->cmnd_size) {
+
+		/* Special case workaround: There are plenty of buggy SCSI
+		 * implementations. Many have issues with cbw->Length
+		 * field passing a wrong command size. For those cases we
+		 * always try to work around the problem by using the length
+		 * sent by the host side provided it is at least as large
+		 * as the correct command length.
+		 * Examples of such cases would be MS-Windows, which issues
+		 * REQUEST SENSE with cbw->Length == 12 where it should
+		 * be 6, and xbox360 issuing INQUIRY, TEST UNIT READY and
+		 * REQUEST SENSE with cbw->Length == 10 where it should
+		 * be 6 as well.
+		 */
+		if (cmnd_size <= fsg->cmnd_size) {
+			DBG(fsg, "%s is buggy! Expected length %d "
+					"but we got %d\n", name,
+					cmnd_size, fsg->cmnd_size);
+			cmnd_size = fsg->cmnd_size;
+		} else {
+			fsg->phase_error = 1;
+			return -EINVAL;
+		}
+	}
+
+	/* Check that the LUN values are consistent */
+	if (transport_is_bbb()) {
+		if (fsg->lun != lun)
+			DBG(fsg, "using LUN %d from CBW, "
+					"not LUN %d from CDB\n",
+					fsg->lun, lun);
+	}
+
+	/* Check the LUN */
+	curlun = fsg->curlun;
+	if (curlun) {
+		if (fsg->cmnd[0] != REQUEST_SENSE) {
+			curlun->sense_data = SS_NO_SENSE;
+			curlun->sense_data_info = 0;
+			curlun->info_valid = 0;
+		}
+	} else {
+		fsg->bad_lun_okay = 0;
+
+		/* INQUIRY and REQUEST SENSE commands are explicitly allowed
+		 * to use unsupported LUNs; all others may not. */
+		if (fsg->cmnd[0] != INQUIRY &&
+				fsg->cmnd[0] != REQUEST_SENSE) {
+			DBG(fsg, "unsupported LUN %d\n", fsg->lun);
+			return -EINVAL;
+		}
+	}
+
+	/* If a unit attention condition exists, only INQUIRY and
+	 * REQUEST SENSE commands are allowed; anything else must fail. */
+	if (curlun && curlun->unit_attention_data != SS_NO_SENSE &&
+			fsg->cmnd[0] != INQUIRY &&
+			fsg->cmnd[0] != REQUEST_SENSE) {
+		curlun->sense_data = curlun->unit_attention_data;
+		curlun->unit_attention_data = SS_NO_SENSE;
+		return -EINVAL;
+	}
+
+	/* Check that only command bytes listed in the mask are non-zero */
+	fsg->cmnd[1] &= 0x1f;			// Mask away the LUN
+	for (i = 1; i < cmnd_size; ++i) {
+		if (fsg->cmnd[i] && !(mask & (1 << i))) {
+			if (curlun)
+				curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
+			return -EINVAL;
+		}
+	}
+
+	/* If the medium isn't mounted and the command needs to access
+	 * it, return an error. */
+	if (curlun && !fsg_lun_is_open(curlun) && needs_medium) {
+		curlun->sense_data = SS_MEDIUM_NOT_PRESENT;
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/* wrapper of check_command for data size in blocks handling */
+static int check_command_size_in_blocks(struct fsg_dev *fsg, int cmnd_size,
+		enum data_direction data_dir, unsigned int mask,
+		int needs_medium, const char *name)
+{
+	if (fsg->curlun)
+		fsg->data_size_from_cmnd <<= fsg->curlun->blkbits;
+	return check_command(fsg, cmnd_size, data_dir,
+			mask, needs_medium, name);
+}
+
+static int do_scsi_command(struct fsg_dev *fsg)
+{
+	struct fsg_buffhd	*bh;
+	int			rc;
+	int			reply = -EINVAL;
+	int			i;
+	static char		unknown[16];
+
+	dump_cdb(fsg);
+
+	/* Wait for the next buffer to become available for data or status */
+	bh = fsg->next_buffhd_to_drain = fsg->next_buffhd_to_fill;
+	while (bh->state != BUF_STATE_EMPTY) {
+		rc = sleep_thread(fsg);
+		if (rc)
+			return rc;
+	}
+	fsg->phase_error = 0;
+	fsg->short_packet_received = 0;
+
+	down_read(&fsg->filesem);	// We're using the backing file
+	switch (fsg->cmnd[0]) {
+
+	case INQUIRY:
+		fsg->data_size_from_cmnd = fsg->cmnd[4];
+		if ((reply = check_command(fsg, 6, DATA_DIR_TO_HOST,
+				(1<<4), 0,
+				"INQUIRY")) == 0)
+			reply = do_inquiry(fsg, bh);
+		break;
+
+	case MODE_SELECT:
+		fsg->data_size_from_cmnd = fsg->cmnd[4];
+		if ((reply = check_command(fsg, 6, DATA_DIR_FROM_HOST,
+				(1<<1) | (1<<4), 0,
+				"MODE SELECT(6)")) == 0)
+			reply = do_mode_select(fsg, bh);
+		break;
+
+	case MODE_SELECT_10:
+		fsg->data_size_from_cmnd = get_unaligned_be16(&fsg->cmnd[7]);
+		if ((reply = check_command(fsg, 10, DATA_DIR_FROM_HOST,
+				(1<<1) | (3<<7), 0,
+				"MODE SELECT(10)")) == 0)
+			reply = do_mode_select(fsg, bh);
+		break;
+
+	case MODE_SENSE:
+		fsg->data_size_from_cmnd = fsg->cmnd[4];
+		if ((reply = check_command(fsg, 6, DATA_DIR_TO_HOST,
+				(1<<1) | (1<<2) | (1<<4), 0,
+				"MODE SENSE(6)")) == 0)
+			reply = do_mode_sense(fsg, bh);
+		break;
+
+	case MODE_SENSE_10:
+		fsg->data_size_from_cmnd = get_unaligned_be16(&fsg->cmnd[7]);
+		if ((reply = check_command(fsg, 10, DATA_DIR_TO_HOST,
+				(1<<1) | (1<<2) | (3<<7), 0,
+				"MODE SENSE(10)")) == 0)
+			reply = do_mode_sense(fsg, bh);
+		break;
+
+	case ALLOW_MEDIUM_REMOVAL:
+		fsg->data_size_from_cmnd = 0;
+		if ((reply = check_command(fsg, 6, DATA_DIR_NONE,
+				(1<<4), 0,
+				"PREVENT-ALLOW MEDIUM REMOVAL")) == 0)
+			reply = do_prevent_allow(fsg);
+		break;
+
+	case READ_6:
+		i = fsg->cmnd[4];
+		fsg->data_size_from_cmnd = (i == 0) ? 256 : i;
+		if ((reply = check_command_size_in_blocks(fsg, 6,
+				DATA_DIR_TO_HOST,
+				(7<<1) | (1<<4), 1,
+				"READ(6)")) == 0)
+			reply = do_read(fsg);
+		break;
+
+	case READ_10:
+		fsg->data_size_from_cmnd = get_unaligned_be16(&fsg->cmnd[7]);
+		if ((reply = check_command_size_in_blocks(fsg, 10,
+				DATA_DIR_TO_HOST,
+				(1<<1) | (0xf<<2) | (3<<7), 1,
+				"READ(10)")) == 0)
+			reply = do_read(fsg);
+		break;
+
+	case READ_12:
+		fsg->data_size_from_cmnd = get_unaligned_be32(&fsg->cmnd[6]);
+		if ((reply = check_command_size_in_blocks(fsg, 12,
+				DATA_DIR_TO_HOST,
+				(1<<1) | (0xf<<2) | (0xf<<6), 1,
+				"READ(12)")) == 0)
+			reply = do_read(fsg);
+		break;
+
+	case READ_CAPACITY:
+		fsg->data_size_from_cmnd = 8;
+		if ((reply = check_command(fsg, 10, DATA_DIR_TO_HOST,
+				(0xf<<2) | (1<<8), 1,
+				"READ CAPACITY")) == 0)
+			reply = do_read_capacity(fsg, bh);
+		break;
+
+	case READ_HEADER:
+		if (!mod_data.cdrom)
+			goto unknown_cmnd;
+		fsg->data_size_from_cmnd = get_unaligned_be16(&fsg->cmnd[7]);
+		if ((reply = check_command(fsg, 10, DATA_DIR_TO_HOST,
+				(3<<7) | (0x1f<<1), 1,
+				"READ HEADER")) == 0)
+			reply = do_read_header(fsg, bh);
+		break;
+
+	case READ_TOC:
+		if (!mod_data.cdrom)
+			goto unknown_cmnd;
+		fsg->data_size_from_cmnd = get_unaligned_be16(&fsg->cmnd[7]);
+		if ((reply = check_command(fsg, 10, DATA_DIR_TO_HOST,
+				(7<<6) | (1<<1), 1,
+				"READ TOC")) == 0)
+			reply = do_read_toc(fsg, bh);
+		break;
+
+	case READ_FORMAT_CAPACITIES:
+		fsg->data_size_from_cmnd = get_unaligned_be16(&fsg->cmnd[7]);
+		if ((reply = check_command(fsg, 10, DATA_DIR_TO_HOST,
+				(3<<7), 1,
+				"READ FORMAT CAPACITIES")) == 0)
+			reply = do_read_format_capacities(fsg, bh);
+		break;
+
+	case REQUEST_SENSE:
+		fsg->data_size_from_cmnd = fsg->cmnd[4];
+		if ((reply = check_command(fsg, 6, DATA_DIR_TO_HOST,
+				(1<<4), 0,
+				"REQUEST SENSE")) == 0)
+			reply = do_request_sense(fsg, bh);
+		break;
+
+	case START_STOP:
+		fsg->data_size_from_cmnd = 0;
+		if ((reply = check_command(fsg, 6, DATA_DIR_NONE,
+				(1<<1) | (1<<4), 0,
+				"START-STOP UNIT")) == 0)
+			reply = do_start_stop(fsg);
+		break;
+
+	case SYNCHRONIZE_CACHE:
+		fsg->data_size_from_cmnd = 0;
+		if ((reply = check_command(fsg, 10, DATA_DIR_NONE,
+				(0xf<<2) | (3<<7), 1,
+				"SYNCHRONIZE CACHE")) == 0)
+			reply = do_synchronize_cache(fsg);
+		break;
+
+	case TEST_UNIT_READY:
+		fsg->data_size_from_cmnd = 0;
+		reply = check_command(fsg, 6, DATA_DIR_NONE,
+				0, 1,
+				"TEST UNIT READY");
+		break;
+
+	/* Although optional, this command is used by MS-Windows.  We
+	 * support a minimal version: BytChk must be 0. */
+	case VERIFY:
+		fsg->data_size_from_cmnd = 0;
+		if ((reply = check_command(fsg, 10, DATA_DIR_NONE,
+				(1<<1) | (0xf<<2) | (3<<7), 1,
+				"VERIFY")) == 0)
+			reply = do_verify(fsg);
+		break;
+
+	case WRITE_6:
+		i = fsg->cmnd[4];
+		fsg->data_size_from_cmnd = (i == 0) ? 256 : i;
+		if ((reply = check_command_size_in_blocks(fsg, 6,
+				DATA_DIR_FROM_HOST,
+				(7<<1) | (1<<4), 1,
+				"WRITE(6)")) == 0)
+			reply = do_write(fsg);
+		break;
+
+	case WRITE_10:
+		fsg->data_size_from_cmnd = get_unaligned_be16(&fsg->cmnd[7]);
+		if ((reply = check_command_size_in_blocks(fsg, 10,
+				DATA_DIR_FROM_HOST,
+				(1<<1) | (0xf<<2) | (3<<7), 1,
+				"WRITE(10)")) == 0)
+			reply = do_write(fsg);
+		break;
+
+	case WRITE_12:
+		fsg->data_size_from_cmnd = get_unaligned_be32(&fsg->cmnd[6]);
+		if ((reply = check_command_size_in_blocks(fsg, 12,
+				DATA_DIR_FROM_HOST,
+				(1<<1) | (0xf<<2) | (0xf<<6), 1,
+				"WRITE(12)")) == 0)
+			reply = do_write(fsg);
+		break;
+
+	/* Some mandatory commands that we recognize but don't implement.
+	 * They don't mean much in this setting.  It's left as an exercise
+	 * for anyone interested to implement RESERVE and RELEASE in terms
+	 * of Posix locks. */
+	case FORMAT_UNIT:
+	case RELEASE:
+	case RESERVE:
+	case SEND_DIAGNOSTIC:
+		// Fall through
+
+	default:
+ unknown_cmnd:
+		fsg->data_size_from_cmnd = 0;
+		sprintf(unknown, "Unknown x%02x", fsg->cmnd[0]);
+		if ((reply = check_command(fsg, fsg->cmnd_size,
+				DATA_DIR_UNKNOWN, ~0, 0, unknown)) == 0) {
+			fsg->curlun->sense_data = SS_INVALID_COMMAND;
+			reply = -EINVAL;
+		}
+		break;
+	}
+	up_read(&fsg->filesem);
+
+	if (reply == -EINTR || signal_pending(current))
+		return -EINTR;
+
+	/* Set up the single reply buffer for finish_reply() */
+	if (reply == -EINVAL)
+		reply = 0;		// Error reply length
+	if (reply >= 0 && fsg->data_dir == DATA_DIR_TO_HOST) {
+		reply = min((u32) reply, fsg->data_size_from_cmnd);
+		bh->inreq->length = reply;
+		bh->state = BUF_STATE_FULL;
+		fsg->residue -= reply;
+	}				// Otherwise it's already set
+
+	return 0;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+static int received_cbw(struct fsg_dev *fsg, struct fsg_buffhd *bh)
+{
+	struct usb_request		*req = bh->outreq;
+	struct bulk_cb_wrap	*cbw = req->buf;
+
+	/* Was this a real packet?  Should it be ignored? */
+	if (req->status || test_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags))
+		return -EINVAL;
+
+	/* Is the CBW valid? */
+	if (req->actual != US_BULK_CB_WRAP_LEN ||
+			cbw->Signature != cpu_to_le32(
+				US_BULK_CB_SIGN)) {
+		DBG(fsg, "invalid CBW: len %u sig 0x%x\n",
+				req->actual,
+				le32_to_cpu(cbw->Signature));
+
+		/* The Bulk-only spec says we MUST stall the IN endpoint
+		 * (6.6.1), so it's unavoidable.  It also says we must
+		 * retain this state until the next reset, but there's
+		 * no way to tell the controller driver it should ignore
+		 * Clear-Feature(HALT) requests.
+		 *
+		 * We aren't required to halt the OUT endpoint; instead
+		 * we can simply accept and discard any data received
+		 * until the next reset. */
+		wedge_bulk_in_endpoint(fsg);
+		set_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags);
+		return -EINVAL;
+	}
+
+	/* Is the CBW meaningful? */
+	if (cbw->Lun >= FSG_MAX_LUNS || cbw->Flags & ~US_BULK_FLAG_IN ||
+			cbw->Length <= 0 || cbw->Length > MAX_COMMAND_SIZE) {
+		DBG(fsg, "non-meaningful CBW: lun = %u, flags = 0x%x, "
+				"cmdlen %u\n",
+				cbw->Lun, cbw->Flags, cbw->Length);
+
+		/* We can do anything we want here, so let's stall the
+		 * bulk pipes if we are allowed to. */
+		if (mod_data.can_stall) {
+			fsg_set_halt(fsg, fsg->bulk_out);
+			halt_bulk_in_endpoint(fsg);
+		}
+		return -EINVAL;
+	}
+
+	/* Save the command for later */
+	fsg->cmnd_size = cbw->Length;
+	memcpy(fsg->cmnd, cbw->CDB, fsg->cmnd_size);
+	if (cbw->Flags & US_BULK_FLAG_IN)
+		fsg->data_dir = DATA_DIR_TO_HOST;
+	else
+		fsg->data_dir = DATA_DIR_FROM_HOST;
+	fsg->data_size = le32_to_cpu(cbw->DataTransferLength);
+	if (fsg->data_size == 0)
+		fsg->data_dir = DATA_DIR_NONE;
+	fsg->lun = cbw->Lun;
+	fsg->tag = cbw->Tag;
+	return 0;
+}
+
+
+static int get_next_command(struct fsg_dev *fsg)
+{
+	struct fsg_buffhd	*bh;
+	int			rc = 0;
+
+	if (transport_is_bbb()) {
+
+		/* Wait for the next buffer to become available */
+		bh = fsg->next_buffhd_to_fill;
+		while (bh->state != BUF_STATE_EMPTY) {
+			rc = sleep_thread(fsg);
+			if (rc)
+				return rc;
+		}
+
+		/* Queue a request to read a Bulk-only CBW */
+		set_bulk_out_req_length(fsg, bh, US_BULK_CB_WRAP_LEN);
+		start_transfer(fsg, fsg->bulk_out, bh->outreq,
+				&bh->outreq_busy, &bh->state);
+
+		/* We will drain the buffer in software, which means we
+		 * can reuse it for the next filling.  No need to advance
+		 * next_buffhd_to_fill. */
+
+		/* Wait for the CBW to arrive */
+		while (bh->state != BUF_STATE_FULL) {
+			rc = sleep_thread(fsg);
+			if (rc)
+				return rc;
+		}
+		smp_rmb();
+		rc = received_cbw(fsg, bh);
+		bh->state = BUF_STATE_EMPTY;
+
+	} else {		// USB_PR_CB or USB_PR_CBI
+
+		/* Wait for the next command to arrive */
+		while (fsg->cbbuf_cmnd_size == 0) {
+			rc = sleep_thread(fsg);
+			if (rc)
+				return rc;
+		}
+
+		/* Is the previous status interrupt request still busy?
+		 * The host is allowed to skip reading the status,
+		 * so we must cancel it. */
+		if (fsg->intreq_busy)
+			usb_ep_dequeue(fsg->intr_in, fsg->intreq);
+
+		/* Copy the command and mark the buffer empty */
+		fsg->data_dir = DATA_DIR_UNKNOWN;
+		spin_lock_irq(&fsg->lock);
+		fsg->cmnd_size = fsg->cbbuf_cmnd_size;
+		memcpy(fsg->cmnd, fsg->cbbuf_cmnd, fsg->cmnd_size);
+		fsg->cbbuf_cmnd_size = 0;
+		spin_unlock_irq(&fsg->lock);
+
+		/* Use LUN from the command */
+		fsg->lun = fsg->cmnd[1] >> 5;
+	}
+
+	/* Update current lun */
+	if (fsg->lun >= 0 && fsg->lun < fsg->nluns)
+		fsg->curlun = &fsg->luns[fsg->lun];
+	else
+		fsg->curlun = NULL;
+
+	return rc;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+static int enable_endpoint(struct fsg_dev *fsg, struct usb_ep *ep,
+		const struct usb_endpoint_descriptor *d)
+{
+	int	rc;
+
+	ep->driver_data = fsg;
+	ep->desc = d;
+	rc = usb_ep_enable(ep);
+	if (rc)
+		ERROR(fsg, "can't enable %s, result %d\n", ep->name, rc);
+	return rc;
+}
+
+static int alloc_request(struct fsg_dev *fsg, struct usb_ep *ep,
+		struct usb_request **preq)
+{
+	*preq = usb_ep_alloc_request(ep, GFP_ATOMIC);
+	if (*preq)
+		return 0;
+	ERROR(fsg, "can't allocate request for %s\n", ep->name);
+	return -ENOMEM;
+}
+
+/*
+ * Reset interface setting and re-init endpoint state (toggle etc).
+ * Call with altsetting < 0 to disable the interface.  The only other
+ * available altsetting is 0, which enables the interface.
+ */
+static int do_set_interface(struct fsg_dev *fsg, int altsetting)
+{
+	int	rc = 0;
+	int	i;
+	const struct usb_endpoint_descriptor	*d;
+
+	if (fsg->running)
+		DBG(fsg, "reset interface\n");
+
+reset:
+	/* Deallocate the requests */
+	for (i = 0; i < fsg_num_buffers; ++i) {
+		struct fsg_buffhd *bh = &fsg->buffhds[i];
+
+		if (bh->inreq) {
+			usb_ep_free_request(fsg->bulk_in, bh->inreq);
+			bh->inreq = NULL;
+		}
+		if (bh->outreq) {
+			usb_ep_free_request(fsg->bulk_out, bh->outreq);
+			bh->outreq = NULL;
+		}
+	}
+	if (fsg->intreq) {
+		usb_ep_free_request(fsg->intr_in, fsg->intreq);
+		fsg->intreq = NULL;
+	}
+
+	/* Disable the endpoints */
+	if (fsg->bulk_in_enabled) {
+		usb_ep_disable(fsg->bulk_in);
+		fsg->bulk_in_enabled = 0;
+	}
+	if (fsg->bulk_out_enabled) {
+		usb_ep_disable(fsg->bulk_out);
+		fsg->bulk_out_enabled = 0;
+	}
+	if (fsg->intr_in_enabled) {
+		usb_ep_disable(fsg->intr_in);
+		fsg->intr_in_enabled = 0;
+	}
+
+	fsg->running = 0;
+	if (altsetting < 0 || rc != 0)
+		return rc;
+
+	DBG(fsg, "set interface %d\n", altsetting);
+
+	/* Enable the endpoints */
+	d = fsg_ep_desc(fsg->gadget,
+			&fsg_fs_bulk_in_desc, &fsg_hs_bulk_in_desc,
+			&fsg_ss_bulk_in_desc);
+	if ((rc = enable_endpoint(fsg, fsg->bulk_in, d)) != 0)
+		goto reset;
+	fsg->bulk_in_enabled = 1;
+
+	d = fsg_ep_desc(fsg->gadget,
+			&fsg_fs_bulk_out_desc, &fsg_hs_bulk_out_desc,
+			&fsg_ss_bulk_out_desc);
+	if ((rc = enable_endpoint(fsg, fsg->bulk_out, d)) != 0)
+		goto reset;
+	fsg->bulk_out_enabled = 1;
+	fsg->bulk_out_maxpacket = usb_endpoint_maxp(d);
+	clear_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags);
+
+	if (transport_is_cbi()) {
+		d = fsg_ep_desc(fsg->gadget,
+				&fsg_fs_intr_in_desc, &fsg_hs_intr_in_desc,
+				&fsg_ss_intr_in_desc);
+		if ((rc = enable_endpoint(fsg, fsg->intr_in, d)) != 0)
+			goto reset;
+		fsg->intr_in_enabled = 1;
+	}
+
+	/* Allocate the requests */
+	for (i = 0; i < fsg_num_buffers; ++i) {
+		struct fsg_buffhd	*bh = &fsg->buffhds[i];
+
+		if ((rc = alloc_request(fsg, fsg->bulk_in, &bh->inreq)) != 0)
+			goto reset;
+		if ((rc = alloc_request(fsg, fsg->bulk_out, &bh->outreq)) != 0)
+			goto reset;
+		bh->inreq->buf = bh->outreq->buf = bh->buf;
+		bh->inreq->context = bh->outreq->context = bh;
+		bh->inreq->complete = bulk_in_complete;
+		bh->outreq->complete = bulk_out_complete;
+	}
+	if (transport_is_cbi()) {
+		if ((rc = alloc_request(fsg, fsg->intr_in, &fsg->intreq)) != 0)
+			goto reset;
+		fsg->intreq->complete = intr_in_complete;
+	}
+
+	fsg->running = 1;
+	for (i = 0; i < fsg->nluns; ++i)
+		fsg->luns[i].unit_attention_data = SS_RESET_OCCURRED;
+	return rc;
+}
+
+
+/*
+ * Change our operational configuration.  This code must agree with the code
+ * that returns config descriptors, and with interface altsetting code.
+ *
+ * It's also responsible for power management interactions.  Some
+ * configurations might not work with our current power sources.
+ * For now we just assume the gadget is always self-powered.
+ */
+static int do_set_config(struct fsg_dev *fsg, u8 new_config)
+{
+	int	rc = 0;
+
+	/* Disable the single interface */
+	if (fsg->config != 0) {
+		DBG(fsg, "reset config\n");
+		fsg->config = 0;
+		rc = do_set_interface(fsg, -1);
+	}
+
+	/* Enable the interface */
+	if (new_config != 0) {
+		fsg->config = new_config;
+		if ((rc = do_set_interface(fsg, 0)) != 0)
+			fsg->config = 0;	// Reset on errors
+		else
+			INFO(fsg, "%s config #%d\n",
+			     usb_speed_string(fsg->gadget->speed),
+			     fsg->config);
+	}
+	return rc;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+static void handle_exception(struct fsg_dev *fsg)
+{
+	siginfo_t		info;
+	int			sig;
+	int			i;
+	int			num_active;
+	struct fsg_buffhd	*bh;
+	enum fsg_state		old_state;
+	u8			new_config;
+	struct fsg_lun		*curlun;
+	unsigned int		exception_req_tag;
+	int			rc;
+
+	/* Clear the existing signals.  Anything but SIGUSR1 is converted
+	 * into a high-priority EXIT exception. */
+	for (;;) {
+		sig = dequeue_signal_lock(current, &current->blocked, &info);
+		if (!sig)
+			break;
+		if (sig != SIGUSR1) {
+			if (fsg->state < FSG_STATE_EXIT)
+				DBG(fsg, "Main thread exiting on signal\n");
+			raise_exception(fsg, FSG_STATE_EXIT);
+		}
+	}
+
+	/* Cancel all the pending transfers */
+	if (fsg->intreq_busy)
+		usb_ep_dequeue(fsg->intr_in, fsg->intreq);
+	for (i = 0; i < fsg_num_buffers; ++i) {
+		bh = &fsg->buffhds[i];
+		if (bh->inreq_busy)
+			usb_ep_dequeue(fsg->bulk_in, bh->inreq);
+		if (bh->outreq_busy)
+			usb_ep_dequeue(fsg->bulk_out, bh->outreq);
+	}
+
+	/* Wait until everything is idle */
+	for (;;) {
+		num_active = fsg->intreq_busy;
+		for (i = 0; i < fsg_num_buffers; ++i) {
+			bh = &fsg->buffhds[i];
+			num_active += bh->inreq_busy + bh->outreq_busy;
+		}
+		if (num_active == 0)
+			break;
+		if (sleep_thread(fsg))
+			return;
+	}
+
+	/* Clear out the controller's fifos */
+	if (fsg->bulk_in_enabled)
+		usb_ep_fifo_flush(fsg->bulk_in);
+	if (fsg->bulk_out_enabled)
+		usb_ep_fifo_flush(fsg->bulk_out);
+	if (fsg->intr_in_enabled)
+		usb_ep_fifo_flush(fsg->intr_in);
+
+	/* Reset the I/O buffer states and pointers, the SCSI
+	 * state, and the exception.  Then invoke the handler. */
+	spin_lock_irq(&fsg->lock);
+
+	for (i = 0; i < fsg_num_buffers; ++i) {
+		bh = &fsg->buffhds[i];
+		bh->state = BUF_STATE_EMPTY;
+	}
+	fsg->next_buffhd_to_fill = fsg->next_buffhd_to_drain =
+			&fsg->buffhds[0];
+
+	exception_req_tag = fsg->exception_req_tag;
+	new_config = fsg->new_config;
+	old_state = fsg->state;
+
+	if (old_state == FSG_STATE_ABORT_BULK_OUT)
+		fsg->state = FSG_STATE_STATUS_PHASE;
+	else {
+		for (i = 0; i < fsg->nluns; ++i) {
+			curlun = &fsg->luns[i];
+			curlun->prevent_medium_removal = 0;
+			curlun->sense_data = curlun->unit_attention_data =
+					SS_NO_SENSE;
+			curlun->sense_data_info = 0;
+			curlun->info_valid = 0;
+		}
+		fsg->state = FSG_STATE_IDLE;
+	}
+	spin_unlock_irq(&fsg->lock);
+
+	/* Carry out any extra actions required for the exception */
+	switch (old_state) {
+	default:
+		break;
+
+	case FSG_STATE_ABORT_BULK_OUT:
+		send_status(fsg);
+		spin_lock_irq(&fsg->lock);
+		if (fsg->state == FSG_STATE_STATUS_PHASE)
+			fsg->state = FSG_STATE_IDLE;
+		spin_unlock_irq(&fsg->lock);
+		break;
+
+	case FSG_STATE_RESET:
+		/* In case we were forced against our will to halt a
+		 * bulk endpoint, clear the halt now.  (The SuperH UDC
+		 * requires this.) */
+		if (test_and_clear_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags))
+			usb_ep_clear_halt(fsg->bulk_in);
+
+		if (transport_is_bbb()) {
+			if (fsg->ep0_req_tag == exception_req_tag)
+				ep0_queue(fsg);	// Complete the status stage
+
+		} else if (transport_is_cbi())
+			send_status(fsg);	// Status by interrupt pipe
+
+		/* Technically this should go here, but it would only be
+		 * a waste of time.  Ditto for the INTERFACE_CHANGE and
+		 * CONFIG_CHANGE cases. */
+		// for (i = 0; i < fsg->nluns; ++i)
+		//	fsg->luns[i].unit_attention_data = SS_RESET_OCCURRED;
+		break;
+
+	case FSG_STATE_INTERFACE_CHANGE:
+		rc = do_set_interface(fsg, 0);
+		if (fsg->ep0_req_tag != exception_req_tag)
+			break;
+		if (rc != 0)			// STALL on errors
+			fsg_set_halt(fsg, fsg->ep0);
+		else				// Complete the status stage
+			ep0_queue(fsg);
+		break;
+
+	case FSG_STATE_CONFIG_CHANGE:
+		rc = do_set_config(fsg, new_config);
+		if (fsg->ep0_req_tag != exception_req_tag)
+			break;
+		if (rc != 0)			// STALL on errors
+			fsg_set_halt(fsg, fsg->ep0);
+		else				// Complete the status stage
+			ep0_queue(fsg);
+		break;
+
+	case FSG_STATE_DISCONNECT:
+		for (i = 0; i < fsg->nluns; ++i)
+			fsg_lun_fsync_sub(fsg->luns + i);
+		do_set_config(fsg, 0);		// Unconfigured state
+		break;
+
+	case FSG_STATE_EXIT:
+	case FSG_STATE_TERMINATED:
+		do_set_config(fsg, 0);			// Free resources
+		spin_lock_irq(&fsg->lock);
+		fsg->state = FSG_STATE_TERMINATED;	// Stop the thread
+		spin_unlock_irq(&fsg->lock);
+		break;
+	}
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+static int fsg_main_thread(void *fsg_)
+{
+	struct fsg_dev		*fsg = fsg_;
+
+	/* Allow the thread to be killed by a signal, but set the signal mask
+	 * to block everything but INT, TERM, KILL, and USR1. */
+	allow_signal(SIGINT);
+	allow_signal(SIGTERM);
+	allow_signal(SIGKILL);
+	allow_signal(SIGUSR1);
+
+	/* Allow the thread to be frozen */
+	set_freezable();
+
+	/* Arrange for userspace references to be interpreted as kernel
+	 * pointers.  That way we can pass a kernel pointer to a routine
+	 * that expects a __user pointer and it will work okay. */
+	set_fs(get_ds());
+
+	/* The main loop */
+	while (fsg->state != FSG_STATE_TERMINATED) {
+		if (exception_in_progress(fsg) || signal_pending(current)) {
+			handle_exception(fsg);
+			continue;
+		}
+
+		if (!fsg->running) {
+			sleep_thread(fsg);
+			continue;
+		}
+
+		if (get_next_command(fsg))
+			continue;
+
+		spin_lock_irq(&fsg->lock);
+		if (!exception_in_progress(fsg))
+			fsg->state = FSG_STATE_DATA_PHASE;
+		spin_unlock_irq(&fsg->lock);
+
+		if (do_scsi_command(fsg) || finish_reply(fsg))
+			continue;
+
+		spin_lock_irq(&fsg->lock);
+		if (!exception_in_progress(fsg))
+			fsg->state = FSG_STATE_STATUS_PHASE;
+		spin_unlock_irq(&fsg->lock);
+
+		if (send_status(fsg))
+			continue;
+
+		spin_lock_irq(&fsg->lock);
+		if (!exception_in_progress(fsg))
+			fsg->state = FSG_STATE_IDLE;
+		spin_unlock_irq(&fsg->lock);
+		}
+
+	spin_lock_irq(&fsg->lock);
+	fsg->thread_task = NULL;
+	spin_unlock_irq(&fsg->lock);
+
+	/* If we are exiting because of a signal, unregister the
+	 * gadget driver. */
+	if (test_and_clear_bit(REGISTERED, &fsg->atomic_bitflags))
+		usb_gadget_unregister_driver(&fsg_driver);
+
+	/* Let the unbind and cleanup routines know the thread has exited */
+	complete_and_exit(&fsg->thread_notifier, 0);
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+
+/* The write permissions and store_xxx pointers are set in fsg_bind() */
+static DEVICE_ATTR(ro, 0444, fsg_show_ro, NULL);
+static DEVICE_ATTR(nofua, 0644, fsg_show_nofua, NULL);
+static DEVICE_ATTR(file, 0444, fsg_show_file, NULL);
+
+
+/*-------------------------------------------------------------------------*/
+
+static void fsg_release(struct kref *ref)
+{
+	struct fsg_dev	*fsg = container_of(ref, struct fsg_dev, ref);
+
+	kfree(fsg->luns);
+	kfree(fsg);
+}
+
+static void lun_release(struct device *dev)
+{
+	struct rw_semaphore	*filesem = dev_get_drvdata(dev);
+	struct fsg_dev		*fsg =
+		container_of(filesem, struct fsg_dev, filesem);
+
+	kref_put(&fsg->ref, fsg_release);
+}
+
+static void /* __init_or_exit */ fsg_unbind(struct usb_gadget *gadget)
+{
+	struct fsg_dev		*fsg = get_gadget_data(gadget);
+	int			i;
+	struct fsg_lun		*curlun;
+	struct usb_request	*req = fsg->ep0req;
+
+	DBG(fsg, "unbind\n");
+	clear_bit(REGISTERED, &fsg->atomic_bitflags);
+
+	/* If the thread isn't already dead, tell it to exit now */
+	if (fsg->state != FSG_STATE_TERMINATED) {
+		raise_exception(fsg, FSG_STATE_EXIT);
+		wait_for_completion(&fsg->thread_notifier);
+
+		/* The cleanup routine waits for this completion also */
+		complete(&fsg->thread_notifier);
+	}
+
+	/* Unregister the sysfs attribute files and the LUNs */
+	for (i = 0; i < fsg->nluns; ++i) {
+		curlun = &fsg->luns[i];
+		if (curlun->registered) {
+			device_remove_file(&curlun->dev, &dev_attr_nofua);
+			device_remove_file(&curlun->dev, &dev_attr_ro);
+			device_remove_file(&curlun->dev, &dev_attr_file);
+			fsg_lun_close(curlun);
+			device_unregister(&curlun->dev);
+			curlun->registered = 0;
+		}
+	}
+
+	/* Free the data buffers */
+	for (i = 0; i < fsg_num_buffers; ++i)
+		kfree(fsg->buffhds[i].buf);
+
+	/* Free the request and buffer for endpoint 0 */
+	if (req) {
+		kfree(req->buf);
+		usb_ep_free_request(fsg->ep0, req);
+	}
+
+	set_gadget_data(gadget, NULL);
+}
+
+
+static int __init check_parameters(struct fsg_dev *fsg)
+{
+	int	prot;
+	int	gcnum;
+
+	/* Store the default values */
+	mod_data.transport_type = USB_PR_BULK;
+	mod_data.transport_name = "Bulk-only";
+	mod_data.protocol_type = USB_SC_SCSI;
+	mod_data.protocol_name = "Transparent SCSI";
+
+	/* Some peripheral controllers are known not to be able to
+	 * halt bulk endpoints correctly.  If one of them is present,
+	 * disable stalls.
+	 */
+	if (gadget_is_at91(fsg->gadget))
+		mod_data.can_stall = 0;
+
+	if (mod_data.release == 0xffff) {	// Parameter wasn't set
+		gcnum = usb_gadget_controller_number(fsg->gadget);
+		if (gcnum >= 0)
+			mod_data.release = 0x0300 + gcnum;
+		else {
+			WARNING(fsg, "controller '%s' not recognized\n",
+				fsg->gadget->name);
+			mod_data.release = 0x0399;
+		}
+	}
+
+	prot = simple_strtol(mod_data.protocol_parm, NULL, 0);
+
+#ifdef CONFIG_USB_FILE_STORAGE_TEST
+	if (strnicmp(mod_data.transport_parm, "BBB", 10) == 0) {
+		;		// Use default setting
+	} else if (strnicmp(mod_data.transport_parm, "CB", 10) == 0) {
+		mod_data.transport_type = USB_PR_CB;
+		mod_data.transport_name = "Control-Bulk";
+	} else if (strnicmp(mod_data.transport_parm, "CBI", 10) == 0) {
+		mod_data.transport_type = USB_PR_CBI;
+		mod_data.transport_name = "Control-Bulk-Interrupt";
+	} else {
+		ERROR(fsg, "invalid transport: %s\n", mod_data.transport_parm);
+		return -EINVAL;
+	}
+
+	if (strnicmp(mod_data.protocol_parm, "SCSI", 10) == 0 ||
+			prot == USB_SC_SCSI) {
+		;		// Use default setting
+	} else if (strnicmp(mod_data.protocol_parm, "RBC", 10) == 0 ||
+			prot == USB_SC_RBC) {
+		mod_data.protocol_type = USB_SC_RBC;
+		mod_data.protocol_name = "RBC";
+	} else if (strnicmp(mod_data.protocol_parm, "8020", 4) == 0 ||
+			strnicmp(mod_data.protocol_parm, "ATAPI", 10) == 0 ||
+			prot == USB_SC_8020) {
+		mod_data.protocol_type = USB_SC_8020;
+		mod_data.protocol_name = "8020i (ATAPI)";
+	} else if (strnicmp(mod_data.protocol_parm, "QIC", 3) == 0 ||
+			prot == USB_SC_QIC) {
+		mod_data.protocol_type = USB_SC_QIC;
+		mod_data.protocol_name = "QIC-157";
+	} else if (strnicmp(mod_data.protocol_parm, "UFI", 10) == 0 ||
+			prot == USB_SC_UFI) {
+		mod_data.protocol_type = USB_SC_UFI;
+		mod_data.protocol_name = "UFI";
+	} else if (strnicmp(mod_data.protocol_parm, "8070", 4) == 0 ||
+			prot == USB_SC_8070) {
+		mod_data.protocol_type = USB_SC_8070;
+		mod_data.protocol_name = "8070i";
+	} else {
+		ERROR(fsg, "invalid protocol: %s\n", mod_data.protocol_parm);
+		return -EINVAL;
+	}
+
+	mod_data.buflen &= PAGE_CACHE_MASK;
+	if (mod_data.buflen <= 0) {
+		ERROR(fsg, "invalid buflen\n");
+		return -ETOOSMALL;
+	}
+
+#endif /* CONFIG_USB_FILE_STORAGE_TEST */
+
+	/* Serial string handling.
+	 * On a real device, the serial string would be loaded
+	 * from permanent storage. */
+	if (mod_data.serial) {
+		const char *ch;
+		unsigned len = 0;
+
+		/* Sanity check :
+		 * The CB[I] specification limits the serial string to
+		 * 12 uppercase hexadecimal characters.
+		 * BBB need at least 12 uppercase hexadecimal characters,
+		 * with a maximum of 126. */
+		for (ch = mod_data.serial; *ch; ++ch) {
+			++len;
+			if ((*ch < '0' || *ch > '9') &&
+			    (*ch < 'A' || *ch > 'F')) { /* not uppercase hex */
+				WARNING(fsg,
+					"Invalid serial string character: %c\n",
+					*ch);
+				goto no_serial;
+			}
+		}
+		if (len > 126 ||
+		    (mod_data.transport_type == USB_PR_BULK && len < 12) ||
+		    (mod_data.transport_type != USB_PR_BULK && len > 12)) {
+			WARNING(fsg, "Invalid serial string length!\n");
+			goto no_serial;
+		}
+		fsg_strings[FSG_STRING_SERIAL - 1].s = mod_data.serial;
+	} else {
+		WARNING(fsg, "No serial-number string provided!\n");
+ no_serial:
+		device_desc.iSerialNumber = 0;
+	}
+
+	return 0;
+}
+
+
+static int __init fsg_bind(struct usb_gadget *gadget)
+{
+	struct fsg_dev		*fsg = the_fsg;
+	int			rc;
+	int			i;
+	struct fsg_lun		*curlun;
+	struct usb_ep		*ep;
+	struct usb_request	*req;
+	char			*pathbuf, *p;
+
+	fsg->gadget = gadget;
+	set_gadget_data(gadget, fsg);
+	fsg->ep0 = gadget->ep0;
+	fsg->ep0->driver_data = fsg;
+
+	if ((rc = check_parameters(fsg)) != 0)
+		goto out;
+
+	if (mod_data.removable) {	// Enable the store_xxx attributes
+		dev_attr_file.attr.mode = 0644;
+		dev_attr_file.store = fsg_store_file;
+		if (!mod_data.cdrom) {
+			dev_attr_ro.attr.mode = 0644;
+			dev_attr_ro.store = fsg_store_ro;
+		}
+	}
+
+	/* Only for removable media? */
+	dev_attr_nofua.attr.mode = 0644;
+	dev_attr_nofua.store = fsg_store_nofua;
+
+	/* Find out how many LUNs there should be */
+	i = mod_data.nluns;
+	if (i == 0)
+		i = max(mod_data.num_filenames, 1u);
+	if (i > FSG_MAX_LUNS) {
+		ERROR(fsg, "invalid number of LUNs: %d\n", i);
+		rc = -EINVAL;
+		goto out;
+	}
+
+	/* Create the LUNs, open their backing files, and register the
+	 * LUN devices in sysfs. */
+	fsg->luns = kzalloc(i * sizeof(struct fsg_lun), GFP_KERNEL);
+	if (!fsg->luns) {
+		rc = -ENOMEM;
+		goto out;
+	}
+	fsg->nluns = i;
+
+	for (i = 0; i < fsg->nluns; ++i) {
+		curlun = &fsg->luns[i];
+		curlun->cdrom = !!mod_data.cdrom;
+		curlun->ro = mod_data.cdrom || mod_data.ro[i];
+		curlun->initially_ro = curlun->ro;
+		curlun->removable = mod_data.removable;
+		curlun->nofua = mod_data.nofua[i];
+		curlun->dev.release = lun_release;
+		curlun->dev.parent = &gadget->dev;
+		curlun->dev.driver = &fsg_driver.driver;
+		dev_set_drvdata(&curlun->dev, &fsg->filesem);
+		dev_set_name(&curlun->dev,"%s-lun%d",
+			     dev_name(&gadget->dev), i);
+
+		kref_get(&fsg->ref);
+		rc = device_register(&curlun->dev);
+		if (rc) {
+			INFO(fsg, "failed to register LUN%d: %d\n", i, rc);
+			put_device(&curlun->dev);
+			goto out;
+		}
+		curlun->registered = 1;
+
+		rc = device_create_file(&curlun->dev, &dev_attr_ro);
+		if (rc)
+			goto out;
+		rc = device_create_file(&curlun->dev, &dev_attr_nofua);
+		if (rc)
+			goto out;
+		rc = device_create_file(&curlun->dev, &dev_attr_file);
+		if (rc)
+			goto out;
+
+		if (mod_data.file[i] && *mod_data.file[i]) {
+			rc = fsg_lun_open(curlun, mod_data.file[i]);
+			if (rc)
+				goto out;
+		} else if (!mod_data.removable) {
+			ERROR(fsg, "no file given for LUN%d\n", i);
+			rc = -EINVAL;
+			goto out;
+		}
+	}
+
+	/* Find all the endpoints we will use */
+	usb_ep_autoconfig_reset(gadget);
+	ep = usb_ep_autoconfig(gadget, &fsg_fs_bulk_in_desc);
+	if (!ep)
+		goto autoconf_fail;
+	ep->driver_data = fsg;		// claim the endpoint
+	fsg->bulk_in = ep;
+
+	ep = usb_ep_autoconfig(gadget, &fsg_fs_bulk_out_desc);
+	if (!ep)
+		goto autoconf_fail;
+	ep->driver_data = fsg;		// claim the endpoint
+	fsg->bulk_out = ep;
+
+	if (transport_is_cbi()) {
+		ep = usb_ep_autoconfig(gadget, &fsg_fs_intr_in_desc);
+		if (!ep)
+			goto autoconf_fail;
+		ep->driver_data = fsg;		// claim the endpoint
+		fsg->intr_in = ep;
+	}
+
+	/* Fix up the descriptors */
+	device_desc.idVendor = cpu_to_le16(mod_data.vendor);
+	device_desc.idProduct = cpu_to_le16(mod_data.product);
+	device_desc.bcdDevice = cpu_to_le16(mod_data.release);
+
+	i = (transport_is_cbi() ? 3 : 2);	// Number of endpoints
+	fsg_intf_desc.bNumEndpoints = i;
+	fsg_intf_desc.bInterfaceSubClass = mod_data.protocol_type;
+	fsg_intf_desc.bInterfaceProtocol = mod_data.transport_type;
+	fsg_fs_function[i + FSG_FS_FUNCTION_PRE_EP_ENTRIES] = NULL;
+
+	if (gadget_is_dualspeed(gadget)) {
+		fsg_hs_function[i + FSG_HS_FUNCTION_PRE_EP_ENTRIES] = NULL;
+
+		/* Assume endpoint addresses are the same for both speeds */
+		fsg_hs_bulk_in_desc.bEndpointAddress =
+			fsg_fs_bulk_in_desc.bEndpointAddress;
+		fsg_hs_bulk_out_desc.bEndpointAddress =
+			fsg_fs_bulk_out_desc.bEndpointAddress;
+		fsg_hs_intr_in_desc.bEndpointAddress =
+			fsg_fs_intr_in_desc.bEndpointAddress;
+	}
+
+	if (gadget_is_superspeed(gadget)) {
+		unsigned		max_burst;
+
+		fsg_ss_function[i + FSG_SS_FUNCTION_PRE_EP_ENTRIES] = NULL;
+
+		/* Calculate bMaxBurst, we know packet size is 1024 */
+		max_burst = min_t(unsigned, mod_data.buflen / 1024, 15);
+
+		/* Assume endpoint addresses are the same for both speeds */
+		fsg_ss_bulk_in_desc.bEndpointAddress =
+			fsg_fs_bulk_in_desc.bEndpointAddress;
+		fsg_ss_bulk_in_comp_desc.bMaxBurst = max_burst;
+
+		fsg_ss_bulk_out_desc.bEndpointAddress =
+			fsg_fs_bulk_out_desc.bEndpointAddress;
+		fsg_ss_bulk_out_comp_desc.bMaxBurst = max_burst;
+	}
+
+	if (gadget_is_otg(gadget))
+		fsg_otg_desc.bmAttributes |= USB_OTG_HNP;
+
+	rc = -ENOMEM;
+
+	/* Allocate the request and buffer for endpoint 0 */
+	fsg->ep0req = req = usb_ep_alloc_request(fsg->ep0, GFP_KERNEL);
+	if (!req)
+		goto out;
+	req->buf = kmalloc(EP0_BUFSIZE, GFP_KERNEL);
+	if (!req->buf)
+		goto out;
+	req->complete = ep0_complete;
+
+	/* Allocate the data buffers */
+	for (i = 0; i < fsg_num_buffers; ++i) {
+		struct fsg_buffhd	*bh = &fsg->buffhds[i];
+
+		/* Allocate for the bulk-in endpoint.  We assume that
+		 * the buffer will also work with the bulk-out (and
+		 * interrupt-in) endpoint. */
+		bh->buf = kmalloc(mod_data.buflen, GFP_KERNEL);
+		if (!bh->buf)
+			goto out;
+		bh->next = bh + 1;
+	}
+	fsg->buffhds[fsg_num_buffers - 1].next = &fsg->buffhds[0];
+
+	/* This should reflect the actual gadget power source */
+	usb_gadget_set_selfpowered(gadget);
+
+	snprintf(fsg_string_manufacturer, sizeof fsg_string_manufacturer,
+			"%s %s with %s",
+			init_utsname()->sysname, init_utsname()->release,
+			gadget->name);
+
+	fsg->thread_task = kthread_create(fsg_main_thread, fsg,
+			"file-storage-gadget");
+	if (IS_ERR(fsg->thread_task)) {
+		rc = PTR_ERR(fsg->thread_task);
+		goto out;
+	}
+
+	INFO(fsg, DRIVER_DESC ", version: " DRIVER_VERSION "\n");
+	INFO(fsg, "NOTE: This driver is deprecated.  "
+			"Consider using g_mass_storage instead.\n");
+	INFO(fsg, "Number of LUNs=%d\n", fsg->nluns);
+
+	pathbuf = kmalloc(PATH_MAX, GFP_KERNEL);
+	for (i = 0; i < fsg->nluns; ++i) {
+		curlun = &fsg->luns[i];
+		if (fsg_lun_is_open(curlun)) {
+			p = NULL;
+			if (pathbuf) {
+				p = d_path(&curlun->filp->f_path,
+					   pathbuf, PATH_MAX);
+				if (IS_ERR(p))
+					p = NULL;
+			}
+			LINFO(curlun, "ro=%d, nofua=%d, file: %s\n",
+			      curlun->ro, curlun->nofua, (p ? p : "(error)"));
+		}
+	}
+	kfree(pathbuf);
+
+	DBG(fsg, "transport=%s (x%02x)\n",
+			mod_data.transport_name, mod_data.transport_type);
+	DBG(fsg, "protocol=%s (x%02x)\n",
+			mod_data.protocol_name, mod_data.protocol_type);
+	DBG(fsg, "VendorID=x%04x, ProductID=x%04x, Release=x%04x\n",
+			mod_data.vendor, mod_data.product, mod_data.release);
+	DBG(fsg, "removable=%d, stall=%d, cdrom=%d, buflen=%u\n",
+			mod_data.removable, mod_data.can_stall,
+			mod_data.cdrom, mod_data.buflen);
+	DBG(fsg, "I/O thread pid: %d\n", task_pid_nr(fsg->thread_task));
+
+	set_bit(REGISTERED, &fsg->atomic_bitflags);
+
+	/* Tell the thread to start working */
+	wake_up_process(fsg->thread_task);
+	return 0;
+
+autoconf_fail:
+	ERROR(fsg, "unable to autoconfigure all endpoints\n");
+	rc = -ENOTSUPP;
+
+out:
+	fsg->state = FSG_STATE_TERMINATED;	// The thread is dead
+	fsg_unbind(gadget);
+	complete(&fsg->thread_notifier);
+	return rc;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+static void fsg_suspend(struct usb_gadget *gadget)
+{
+	struct fsg_dev		*fsg = get_gadget_data(gadget);
+
+	DBG(fsg, "suspend\n");
+	set_bit(SUSPENDED, &fsg->atomic_bitflags);
+}
+
+static void fsg_resume(struct usb_gadget *gadget)
+{
+	struct fsg_dev		*fsg = get_gadget_data(gadget);
+
+	DBG(fsg, "resume\n");
+	clear_bit(SUSPENDED, &fsg->atomic_bitflags);
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+static struct usb_gadget_driver		fsg_driver = {
+	.max_speed	= USB_SPEED_SUPER,
+	.function	= (char *) fsg_string_product,
+	.unbind		= fsg_unbind,
+	.disconnect	= fsg_disconnect,
+	.setup		= fsg_setup,
+	.suspend	= fsg_suspend,
+	.resume		= fsg_resume,
+
+	.driver		= {
+		.name		= DRIVER_NAME,
+		.owner		= THIS_MODULE,
+		// .release = ...
+		// .suspend = ...
+		// .resume = ...
+	},
+};
+
+
+static int __init fsg_alloc(void)
+{
+	struct fsg_dev		*fsg;
+
+	fsg = kzalloc(sizeof *fsg +
+		      fsg_num_buffers * sizeof *(fsg->buffhds), GFP_KERNEL);
+
+	if (!fsg)
+		return -ENOMEM;
+	spin_lock_init(&fsg->lock);
+	init_rwsem(&fsg->filesem);
+	kref_init(&fsg->ref);
+	init_completion(&fsg->thread_notifier);
+
+	the_fsg = fsg;
+	return 0;
+}
+
+
+static int __init fsg_init(void)
+{
+	int		rc;
+	struct fsg_dev	*fsg;
+
+	rc = fsg_num_buffers_validate();
+	if (rc != 0)
+		return rc;
+
+	if ((rc = fsg_alloc()) != 0)
+		return rc;
+	fsg = the_fsg;
+	if ((rc = usb_gadget_probe_driver(&fsg_driver, fsg_bind)) != 0)
+		kref_put(&fsg->ref, fsg_release);
+	return rc;
+}
+module_init(fsg_init);
+
+
+static void __exit fsg_cleanup(void)
+{
+	struct fsg_dev	*fsg = the_fsg;
+
+	/* Unregister the driver iff the thread hasn't already done so */
+	if (test_and_clear_bit(REGISTERED, &fsg->atomic_bitflags))
+		usb_gadget_unregister_driver(&fsg_driver);
+
+	/* Wait for the thread to finish up */
+	wait_for_completion(&fsg->thread_notifier);
+
+	kref_put(&fsg->ref, fsg_release);
+}
+module_exit(fsg_cleanup);
diff -Nur --no-dereference linux-5.10.52-orig/drivers/usb/gadget/function/f_hid.c linux-5.10.52-v7l+/drivers/usb/gadget/function/f_hid.c
--- linux-5.10.52-orig/drivers/usb/gadget/function/f_hid.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/usb/gadget/function/f_hid.c	2021-07-25 16:46:28.497913130 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:341 @
 
 	spin_lock_irqsave(&hidg->write_spinlock, flags);
 
+	if (!hidg->req) {
+		spin_unlock_irqrestore(&hidg->write_spinlock, flags);
+		return -ESHUTDOWN;
+	}
+
 #define WRITE_COND (!hidg->write_pending)
 try_again:
 	/* write queue */
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:366 @
 	count  = min_t(unsigned, count, hidg->report_length);
 
 	spin_unlock_irqrestore(&hidg->write_spinlock, flags);
-	status = copy_from_user(req->buf, buffer, count);
+	if (req) {
+		status = copy_from_user(req->buf, buffer, count);
+	} else {
+		ERROR(hidg->func.config->cdev, "hidg->req is NULL\n");
+		status = -ESHUTDOWN;
+		goto release_write_pending;
+	}
 
 	if (status != 0) {
 		ERROR(hidg->func.config->cdev,
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:401 @
 
 	spin_unlock_irqrestore(&hidg->write_spinlock, flags);
 
+	if (!hidg->in_ep->enabled) {
+		ERROR(hidg->func.config->cdev, "in_ep is disabled\n");
+		status = -ESHUTDOWN;
+		goto release_write_pending;
+	}
 	status = usb_ep_queue(hidg->in_ep, req, GFP_ATOMIC);
 	if (status < 0) {
-		ERROR(hidg->func.config->cdev,
-			"usb_ep_queue error on int endpoint %zd\n", status);
 		goto release_write_pending;
 	} else {
 		status = count;
diff -Nur --no-dereference linux-5.10.52-orig/drivers/usb/host/dwc_common_port/changes.txt linux-5.10.52-v7l+/drivers/usb/host/dwc_common_port/changes.txt
--- linux-5.10.52-orig/drivers/usb/host/dwc_common_port/changes.txt	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/usb/host/dwc_common_port/changes.txt	2021-07-25 16:46:28.657910448 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+
+dwc_read_reg32() and friends now take an additional parameter, a pointer to an
+IO context struct. The IO context struct should live in an os-dependent struct
+in your driver. As an example, the dwc_usb3 driver has an os-dependent struct
+named 'os_dep' embedded in the main device struct. So there these calls look
+like this:
+
+	dwc_read_reg32(&usb3_dev->os_dep.ioctx, &pcd->dev_global_regs->dcfg);
+
+	dwc_write_reg32(&usb3_dev->os_dep.ioctx,
+			&pcd->dev_global_regs->dcfg, 0);
+
+Note that for the existing Linux driver ports, it is not necessary to actually
+define the 'ioctx' member in the os-dependent struct. Since Linux does not
+require an IO context, its macros for dwc_read_reg32() and friends do not
+use the context pointer, so it is optimized away by the compiler. But it is
+necessary to add the pointer parameter to all of the call sites, to be ready
+for any future ports (such as FreeBSD) which do require an IO context.
+
+
+Similarly, dwc_alloc(), dwc_alloc_atomic(), dwc_strdup(), and dwc_free() now
+take an additional parameter, a pointer to a memory context. Examples:
+
+	addr = dwc_alloc(&usb3_dev->os_dep.memctx, size);
+
+	dwc_free(&usb3_dev->os_dep.memctx, addr);
+
+Again, for the Linux ports, it is not necessary to actually define the memctx
+member, but it is necessary to add the pointer parameter to all of the call
+sites.
+
+
+Same for dwc_dma_alloc() and dwc_dma_free(). Examples:
+
+	virt_addr = dwc_dma_alloc(&usb3_dev->os_dep.dmactx, size, &phys_addr);
+
+	dwc_dma_free(&usb3_dev->os_dep.dmactx, size, virt_addr, phys_addr);
+
+
+Same for dwc_mutex_alloc() and dwc_mutex_free(). Examples:
+
+	mutex = dwc_mutex_alloc(&usb3_dev->os_dep.mtxctx);
+
+	dwc_mutex_free(&usb3_dev->os_dep.mtxctx, mutex);
+
+
+Same for dwc_spinlock_alloc() and dwc_spinlock_free(). Examples:
+
+	lock = dwc_spinlock_alloc(&usb3_dev->osdep.splctx);
+
+	dwc_spinlock_free(&usb3_dev->osdep.splctx, lock);
+
+
+Same for dwc_timer_alloc(). Example:
+
+	timer = dwc_timer_alloc(&usb3_dev->os_dep.tmrctx, "dwc_usb3_tmr1",
+				cb_func, cb_data);
+
+
+Same for dwc_waitq_alloc(). Example:
+
+	waitq = dwc_waitq_alloc(&usb3_dev->os_dep.wtqctx);
+
+
+Same for dwc_thread_run(). Example:
+
+	thread = dwc_thread_run(&usb3_dev->os_dep.thdctx, func,
+				"dwc_usb3_thd1", data);
+
+
+Same for dwc_workq_alloc(). Example:
+
+	workq = dwc_workq_alloc(&usb3_dev->osdep.wkqctx, "dwc_usb3_wkq1");
+
+
+Same for dwc_task_alloc(). Example:
+
+	task = dwc_task_alloc(&usb3_dev->os_dep.tskctx, "dwc_usb3_tsk1",
+			      cb_func, cb_data);
+
+
+In addition to the context pointer additions, a few core functions have had
+other changes made to their parameters:
+
+The 'flags' parameter to dwc_spinlock_irqsave() and dwc_spinunlock_irqrestore()
+has been changed from a uint64_t to a dwc_irqflags_t.
+
+dwc_thread_should_stop() now takes a 'dwc_thread_t *' parameter, because the
+FreeBSD equivalent of that function requires it.
+
+And, in addition to the context pointer, dwc_task_alloc() also adds a
+'char *name' parameter, to be consistent with dwc_thread_run() and
+dwc_workq_alloc(), and because the FreeBSD equivalent of that function
+requires a unique name.
+
+
+Here is a complete list of the core functions that now take a pointer to a
+context as their first parameter:
+
+	dwc_read_reg32
+	dwc_read_reg64
+	dwc_write_reg32
+	dwc_write_reg64
+	dwc_modify_reg32
+	dwc_modify_reg64
+	dwc_alloc
+	dwc_alloc_atomic
+	dwc_strdup
+	dwc_free
+	dwc_dma_alloc
+	dwc_dma_free
+	dwc_mutex_alloc
+	dwc_mutex_free
+	dwc_spinlock_alloc
+	dwc_spinlock_free
+	dwc_timer_alloc
+	dwc_waitq_alloc
+	dwc_thread_run
+	dwc_workq_alloc
+	dwc_task_alloc     Also adds a 'char *name' as its 2nd parameter
+
+And here are the core functions that have other changes to their parameters:
+
+	dwc_spinlock_irqsave      'flags' param is now a 'dwc_irqflags_t *'
+	dwc_spinunlock_irqrestore 'flags' param is now a 'dwc_irqflags_t'
+	dwc_thread_should_stop    Adds a 'dwc_thread_t *' parameter
+
+
+
+The changes to the core functions also require some of the other library
+functions to change:
+
+	dwc_cc_if_alloc() and dwc_cc_if_free() now take a 'void *memctx'
+	(for memory allocation) as the 1st param and a 'void *mtxctx'
+	(for mutex allocation) as the 2nd param.
+
+	dwc_cc_clear(), dwc_cc_add(), dwc_cc_change(), dwc_cc_remove(),
+	dwc_cc_data_for_save(), and dwc_cc_restore_from_data() now take a
+	'void *memctx' as the 1st param.
+
+	dwc_dh_modpow(), dwc_dh_pk(), and dwc_dh_derive_keys() now take a
+	'void *memctx' as the 1st param.
+
+	dwc_modpow() now takes a 'void *memctx' as the 1st param.
+
+	dwc_alloc_notification_manager() now takes a 'void *memctx' as the
+	1st param and a 'void *wkqctx' (for work queue allocation) as the 2nd
+	param, and also now returns an integer value that is non-zero if
+	allocation of its data structures or work queue fails.
+
+	dwc_register_notifier() now takes a 'void *memctx' as the 1st param.
+
+	dwc_memory_debug_start() now takes a 'void *mem_ctx' as the first
+	param, and also now returns an integer value that is non-zero if
+	allocation of its data structures fails.
+
+
+
+Other miscellaneous changes:
+
+The DEBUG_MEMORY and DEBUG_REGS #define's have been renamed to
+DWC_DEBUG_MEMORY and DWC_DEBUG_REGS.
+
+The following #define's have been added to allow selectively compiling library
+features:
+
+	DWC_CCLIB
+	DWC_CRYPTOLIB
+	DWC_NOTIFYLIB
+	DWC_UTFLIB
+
+A DWC_LIBMODULE #define has also been added. If this is not defined, then the
+module code in dwc_common_linux.c is not compiled in. This allows linking the
+library code directly into a driver module, instead of as a standalone module.
diff -Nur --no-dereference linux-5.10.52-orig/drivers/usb/host/dwc_common_port/doc/doxygen.cfg linux-5.10.52-v7l+/drivers/usb/host/dwc_common_port/doc/doxygen.cfg
--- linux-5.10.52-orig/drivers/usb/host/dwc_common_port/doc/doxygen.cfg	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/usb/host/dwc_common_port/doc/doxygen.cfg	2021-07-25 16:46:28.657910448 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+# Doxyfile 1.4.5
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+PROJECT_NAME           = "Synopsys DWC Portability and Common Library for UWB"
+PROJECT_NUMBER         =
+OUTPUT_DIRECTORY       = doc
+CREATE_SUBDIRS         = NO
+OUTPUT_LANGUAGE        = English
+BRIEF_MEMBER_DESC      = YES
+REPEAT_BRIEF           = YES
+ABBREVIATE_BRIEF       = "The $name class" \
+                         "The $name widget" \
+                         "The $name file" \
+                         is \
+                         provides \
+                         specifies \
+                         contains \
+                         represents \
+                         a \
+                         an \
+                         the
+ALWAYS_DETAILED_SEC    = YES
+INLINE_INHERITED_MEMB  = NO
+FULL_PATH_NAMES        = NO
+STRIP_FROM_PATH        = ..
+STRIP_FROM_INC_PATH    =
+SHORT_NAMES            = NO
+JAVADOC_AUTOBRIEF      = YES
+MULTILINE_CPP_IS_BRIEF = NO
+DETAILS_AT_TOP         = YES
+INHERIT_DOCS           = YES
+SEPARATE_MEMBER_PAGES  = NO
+TAB_SIZE               = 8
+ALIASES                =
+OPTIMIZE_OUTPUT_FOR_C  = YES
+OPTIMIZE_OUTPUT_JAVA   = NO
+BUILTIN_STL_SUPPORT    = NO
+DISTRIBUTE_GROUP_DOC   = NO
+SUBGROUPING            = NO
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+EXTRACT_ALL            = NO
+EXTRACT_PRIVATE        = NO
+EXTRACT_STATIC         = YES
+EXTRACT_LOCAL_CLASSES  = NO
+EXTRACT_LOCAL_METHODS  = NO
+HIDE_UNDOC_MEMBERS     = NO
+HIDE_UNDOC_CLASSES     = NO
+HIDE_FRIEND_COMPOUNDS  = NO
+HIDE_IN_BODY_DOCS      = NO
+INTERNAL_DOCS          = NO
+CASE_SENSE_NAMES       = YES
+HIDE_SCOPE_NAMES       = NO
+SHOW_INCLUDE_FILES     = NO
+INLINE_INFO            = YES
+SORT_MEMBER_DOCS       = NO
+SORT_BRIEF_DOCS        = NO
+SORT_BY_SCOPE_NAME     = NO
+GENERATE_TODOLIST      = YES
+GENERATE_TESTLIST      = YES
+GENERATE_BUGLIST       = YES
+GENERATE_DEPRECATEDLIST= YES
+ENABLED_SECTIONS       =
+MAX_INITIALIZER_LINES  = 30
+SHOW_USED_FILES        = YES
+SHOW_DIRECTORIES       = YES
+FILE_VERSION_FILTER    =
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+QUIET                  = YES
+WARNINGS               = YES
+WARN_IF_UNDOCUMENTED   = NO
+WARN_IF_DOC_ERROR      = YES
+WARN_NO_PARAMDOC       = YES
+WARN_FORMAT            = "$file:$line: $text"
+WARN_LOGFILE           =
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+INPUT                  = .
+FILE_PATTERNS          = *.c \
+                         *.cc \
+                         *.cxx \
+                         *.cpp \
+                         *.c++ \
+                         *.d \
+                         *.java \
+                         *.ii \
+                         *.ixx \
+                         *.ipp \
+                         *.i++ \
+                         *.inl \
+                         *.h \
+                         *.hh \
+                         *.hxx \
+                         *.hpp \
+                         *.h++ \
+                         *.idl \
+                         *.odl \
+                         *.cs \
+                         *.php \
+                         *.php3 \
+                         *.inc \
+                         *.m \
+                         *.mm \
+                         *.dox \
+                         *.py \
+                         *.C \
+                         *.CC \
+                         *.C++ \
+                         *.II \
+                         *.I++ \
+                         *.H \
+                         *.HH \
+                         *.H++ \
+                         *.CS \
+                         *.PHP \
+                         *.PHP3 \
+                         *.M \
+                         *.MM \
+                         *.PY
+RECURSIVE              = NO
+EXCLUDE                =
+EXCLUDE_SYMLINKS       = NO
+EXCLUDE_PATTERNS       =
+EXAMPLE_PATH           =
+EXAMPLE_PATTERNS       = *
+EXAMPLE_RECURSIVE      = NO
+IMAGE_PATH             =
+INPUT_FILTER           =
+FILTER_PATTERNS        =
+FILTER_SOURCE_FILES    = NO
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+SOURCE_BROWSER         = NO
+INLINE_SOURCES         = NO
+STRIP_CODE_COMMENTS    = YES
+REFERENCED_BY_RELATION = YES
+REFERENCES_RELATION    = YES
+USE_HTAGS              = NO
+VERBATIM_HEADERS       = NO
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+ALPHABETICAL_INDEX     = NO
+COLS_IN_ALPHA_INDEX    = 5
+IGNORE_PREFIX          =
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+GENERATE_HTML          = YES
+HTML_OUTPUT            = html
+HTML_FILE_EXTENSION    = .html
+HTML_HEADER            =
+HTML_FOOTER            =
+HTML_STYLESHEET        =
+HTML_ALIGN_MEMBERS     = YES
+GENERATE_HTMLHELP      = NO
+CHM_FILE               =
+HHC_LOCATION           =
+GENERATE_CHI           = NO
+BINARY_TOC             = NO
+TOC_EXPAND             = NO
+DISABLE_INDEX          = NO
+ENUM_VALUES_PER_LINE   = 4
+GENERATE_TREEVIEW      = YES
+TREEVIEW_WIDTH         = 250
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+GENERATE_LATEX         = NO
+LATEX_OUTPUT           = latex
+LATEX_CMD_NAME         = latex
+MAKEINDEX_CMD_NAME     = makeindex
+COMPACT_LATEX          = NO
+PAPER_TYPE             = a4wide
+EXTRA_PACKAGES         =
+LATEX_HEADER           =
+PDF_HYPERLINKS         = NO
+USE_PDFLATEX           = NO
+LATEX_BATCHMODE        = NO
+LATEX_HIDE_INDICES     = NO
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+GENERATE_RTF           = NO
+RTF_OUTPUT             = rtf
+COMPACT_RTF            = NO
+RTF_HYPERLINKS         = NO
+RTF_STYLESHEET_FILE    =
+RTF_EXTENSIONS_FILE    =
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+GENERATE_MAN           = NO
+MAN_OUTPUT             = man
+MAN_EXTENSION          = .3
+MAN_LINKS              = NO
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+GENERATE_XML           = NO
+XML_OUTPUT             = xml
+XML_SCHEMA             =
+XML_DTD                =
+XML_PROGRAMLISTING     = YES
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+GENERATE_AUTOGEN_DEF   = NO
+#---------------------------------------------------------------------------
+# configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+GENERATE_PERLMOD       = NO
+PERLMOD_LATEX          = NO
+PERLMOD_PRETTY         = YES
+PERLMOD_MAKEVAR_PREFIX =
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+ENABLE_PREPROCESSING   = YES
+MACRO_EXPANSION        = NO
+EXPAND_ONLY_PREDEF     = NO
+SEARCH_INCLUDES        = YES
+INCLUDE_PATH           =
+INCLUDE_FILE_PATTERNS  =
+PREDEFINED             = DEBUG DEBUG_MEMORY
+EXPAND_AS_DEFINED      =
+SKIP_FUNCTION_MACROS   = YES
+#---------------------------------------------------------------------------
+# Configuration::additions related to external references
+#---------------------------------------------------------------------------
+TAGFILES               =
+GENERATE_TAGFILE       =
+ALLEXTERNALS           = NO
+EXTERNAL_GROUPS        = YES
+PERL_PATH              = /usr/bin/perl
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+CLASS_DIAGRAMS         = YES
+HIDE_UNDOC_RELATIONS   = YES
+HAVE_DOT               = NO
+CLASS_GRAPH            = YES
+COLLABORATION_GRAPH    = YES
+GROUP_GRAPHS           = YES
+UML_LOOK               = NO
+TEMPLATE_RELATIONS     = NO
+INCLUDE_GRAPH          = NO
+INCLUDED_BY_GRAPH      = YES
+CALL_GRAPH             = NO
+GRAPHICAL_HIERARCHY    = YES
+DIRECTORY_GRAPH        = YES
+DOT_IMAGE_FORMAT       = png
+DOT_PATH               =
+DOTFILE_DIRS           =
+MAX_DOT_GRAPH_DEPTH    = 1000
+DOT_TRANSPARENT        = NO
+DOT_MULTI_TARGETS      = NO
+GENERATE_LEGEND        = YES
+DOT_CLEANUP            = YES
+#---------------------------------------------------------------------------
+# Configuration::additions related to the search engine
+#---------------------------------------------------------------------------
+SEARCHENGINE           = NO
diff -Nur --no-dereference linux-5.10.52-orig/drivers/usb/host/dwc_common_port/dwc_cc.c linux-5.10.52-v7l+/drivers/usb/host/dwc_common_port/dwc_cc.c
--- linux-5.10.52-orig/drivers/usb/host/dwc_common_port/dwc_cc.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/usb/host/dwc_common_port/dwc_cc.c	2021-07-25 16:46:28.657910448 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/* =========================================================================
+ * $File: //dwh/usb_iip/dev/software/dwc_common_port_2/dwc_cc.c $
+ * $Revision: #4 $
+ * $Date: 2010/11/04 $
+ * $Change: 1621692 $
+ *
+ * Synopsys Portability Library Software and documentation
+ * (hereinafter, "Software") is an Unsupported proprietary work of
+ * Synopsys, Inc. unless otherwise expressly agreed to in writing
+ * between Synopsys and you.
+ *
+ * The Software IS NOT an item of Licensed Software or Licensed Product
+ * under any End User Software License Agreement or Agreement for
+ * Licensed Product with Synopsys or any supplement thereto. You are
+ * permitted to use and redistribute this Software in source and binary
+ * forms, with or without modification, provided that redistributions
+ * of source code must retain this notice. You may not view, use,
+ * disclose, copy or distribute this file or any information contained
+ * herein except pursuant to this license grant from Synopsys. If you
+ * do not agree with this notice, including the disclaimer below, then
+ * you are not authorized to use the Software.
+ *
+ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS"
+ * BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL
+ * SYNOPSYS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * ========================================================================= */
+#ifdef DWC_CCLIB
+
+#include "dwc_cc.h"
+
+typedef struct dwc_cc
+{
+	uint32_t uid;
+	uint8_t chid[16];
+	uint8_t cdid[16];
+	uint8_t ck[16];
+	uint8_t *name;
+	uint8_t length;
+        DWC_CIRCLEQ_ENTRY(dwc_cc) list_entry;
+} dwc_cc_t;
+
+DWC_CIRCLEQ_HEAD(context_list, dwc_cc);
+
+/** The main structure for CC management.  */
+struct dwc_cc_if
+{
+	dwc_mutex_t *mutex;
+	char *filename;
+
+	unsigned is_host:1;
+
+	dwc_notifier_t *notifier;
+
+	struct context_list list;
+};
+
+#ifdef DEBUG
+static inline void dump_bytes(char *name, uint8_t *bytes, int len)
+{
+	int i;
+	DWC_PRINTF("%s: ", name);
+	for (i=0; i<len; i++) {
+		DWC_PRINTF("%02x ", bytes[i]);
+	}
+	DWC_PRINTF("\n");
+}
+#else
+#define dump_bytes(x...)
+#endif
+
+static dwc_cc_t *alloc_cc(void *mem_ctx, uint8_t *name, uint32_t length)
+{
+	dwc_cc_t *cc = dwc_alloc(mem_ctx, sizeof(dwc_cc_t));
+	if (!cc) {
+		return NULL;
+	}
+	DWC_MEMSET(cc, 0, sizeof(dwc_cc_t));
+
+	if (name) {
+		cc->length = length;
+		cc->name = dwc_alloc(mem_ctx, length);
+		if (!cc->name) {
+			dwc_free(mem_ctx, cc);
+			return NULL;
+		}
+
+		DWC_MEMCPY(cc->name, name, length);
+	}
+
+	return cc;
+}
+
+static void free_cc(void *mem_ctx, dwc_cc_t *cc)
+{
+	if (cc->name) {
+		dwc_free(mem_ctx, cc->name);
+	}
+	dwc_free(mem_ctx, cc);
+}
+
+static uint32_t next_uid(dwc_cc_if_t *cc_if)
+{
+	uint32_t uid = 0;
+	dwc_cc_t *cc;
+	DWC_CIRCLEQ_FOREACH(cc, &cc_if->list, list_entry) {
+		if (cc->uid > uid) {
+			uid = cc->uid;
+		}
+	}
+
+	if (uid == 0) {
+		uid = 255;
+	}
+
+	return uid + 1;
+}
+
+static dwc_cc_t *cc_find(dwc_cc_if_t *cc_if, uint32_t uid)
+{
+	dwc_cc_t *cc;
+	DWC_CIRCLEQ_FOREACH(cc, &cc_if->list, list_entry) {
+		if (cc->uid == uid) {
+			return cc;
+		}
+	}
+	return NULL;
+}
+
+static unsigned int cc_data_size(dwc_cc_if_t *cc_if)
+{
+	unsigned int size = 0;
+	dwc_cc_t *cc;
+	DWC_CIRCLEQ_FOREACH(cc, &cc_if->list, list_entry) {
+		size += (48 + 1);
+		if (cc->name) {
+			size += cc->length;
+		}
+	}
+	return size;
+}
+
+static uint32_t cc_match_chid(dwc_cc_if_t *cc_if, uint8_t *chid)
+{
+	uint32_t uid = 0;
+	dwc_cc_t *cc;
+
+	DWC_CIRCLEQ_FOREACH(cc, &cc_if->list, list_entry) {
+		if (DWC_MEMCMP(cc->chid, chid, 16) == 0) {
+			uid = cc->uid;
+			break;
+		}
+	}
+	return uid;
+}
+static uint32_t cc_match_cdid(dwc_cc_if_t *cc_if, uint8_t *cdid)
+{
+	uint32_t uid = 0;
+	dwc_cc_t *cc;
+
+	DWC_CIRCLEQ_FOREACH(cc, &cc_if->list, list_entry) {
+		if (DWC_MEMCMP(cc->cdid, cdid, 16) == 0) {
+			uid = cc->uid;
+			break;
+		}
+	}
+	return uid;
+}
+
+/* Internal cc_add */
+static int32_t cc_add(void *mem_ctx, dwc_cc_if_t *cc_if, uint8_t *chid,
+		      uint8_t *cdid, uint8_t *ck, uint8_t *name, uint8_t length)
+{
+	dwc_cc_t *cc;
+	uint32_t uid;
+
+	if (cc_if->is_host) {
+		uid = cc_match_cdid(cc_if, cdid);
+	}
+	else {
+		uid = cc_match_chid(cc_if, chid);
+	}
+
+	if (uid) {
+		DWC_DEBUGC("Replacing previous connection context id=%d name=%p name_len=%d", uid, name, length);
+		cc = cc_find(cc_if, uid);
+	}
+	else {
+		cc = alloc_cc(mem_ctx, name, length);
+		cc->uid = next_uid(cc_if);
+		DWC_CIRCLEQ_INSERT_TAIL(&cc_if->list, cc, list_entry);
+	}
+
+	DWC_MEMCPY(&(cc->chid[0]), chid, 16);
+	DWC_MEMCPY(&(cc->cdid[0]), cdid, 16);
+	DWC_MEMCPY(&(cc->ck[0]), ck, 16);
+
+	DWC_DEBUGC("Added connection context id=%d name=%p name_len=%d", cc->uid, name, length);
+	dump_bytes("CHID", cc->chid, 16);
+	dump_bytes("CDID", cc->cdid, 16);
+	dump_bytes("CK", cc->ck, 16);
+	return cc->uid;
+}
+
+/* Internal cc_clear */
+static void cc_clear(void *mem_ctx, dwc_cc_if_t *cc_if)
+{
+	while (!DWC_CIRCLEQ_EMPTY(&cc_if->list)) {
+		dwc_cc_t *cc = DWC_CIRCLEQ_FIRST(&cc_if->list);
+		DWC_CIRCLEQ_REMOVE_INIT(&cc_if->list, cc, list_entry);
+		free_cc(mem_ctx, cc);
+	}
+}
+
+dwc_cc_if_t *dwc_cc_if_alloc(void *mem_ctx, void *mtx_ctx,
+			     dwc_notifier_t *notifier, unsigned is_host)
+{
+	dwc_cc_if_t *cc_if = NULL;
+
+	/* Allocate a common_cc_if structure */
+	cc_if = dwc_alloc(mem_ctx, sizeof(dwc_cc_if_t));
+
+	if (!cc_if)
+		return NULL;
+
+#if (defined(DWC_LINUX) && defined(CONFIG_DEBUG_MUTEXES))
+	DWC_MUTEX_ALLOC_LINUX_DEBUG(cc_if->mutex);
+#else
+	cc_if->mutex = dwc_mutex_alloc(mtx_ctx);
+#endif
+	if (!cc_if->mutex) {
+		dwc_free(mem_ctx, cc_if);
+		return NULL;
+	}
+
+	DWC_CIRCLEQ_INIT(&cc_if->list);
+	cc_if->is_host = is_host;
+	cc_if->notifier = notifier;
+	return cc_if;
+}
+
+void dwc_cc_if_free(void *mem_ctx, void *mtx_ctx, dwc_cc_if_t *cc_if)
+{
+#if (defined(DWC_LINUX) && defined(CONFIG_DEBUG_MUTEXES))
+	DWC_MUTEX_FREE(cc_if->mutex);
+#else
+	dwc_mutex_free(mtx_ctx, cc_if->mutex);
+#endif
+	cc_clear(mem_ctx, cc_if);
+	dwc_free(mem_ctx, cc_if);
+}
+
+static void cc_changed(dwc_cc_if_t *cc_if)
+{
+	if (cc_if->notifier) {
+		dwc_notify(cc_if->notifier, DWC_CC_LIST_CHANGED_NOTIFICATION, cc_if);
+	}
+}
+
+void dwc_cc_clear(void *mem_ctx, dwc_cc_if_t *cc_if)
+{
+	DWC_MUTEX_LOCK(cc_if->mutex);
+	cc_clear(mem_ctx, cc_if);
+	DWC_MUTEX_UNLOCK(cc_if->mutex);
+	cc_changed(cc_if);
+}
+
+int32_t dwc_cc_add(void *mem_ctx, dwc_cc_if_t *cc_if, uint8_t *chid,
+		   uint8_t *cdid, uint8_t *ck, uint8_t *name, uint8_t length)
+{
+	uint32_t uid;
+
+	DWC_MUTEX_LOCK(cc_if->mutex);
+	uid = cc_add(mem_ctx, cc_if, chid, cdid, ck, name, length);
+	DWC_MUTEX_UNLOCK(cc_if->mutex);
+	cc_changed(cc_if);
+
+	return uid;
+}
+
+void dwc_cc_change(void *mem_ctx, dwc_cc_if_t *cc_if, int32_t id, uint8_t *chid,
+		   uint8_t *cdid, uint8_t *ck, uint8_t *name, uint8_t length)
+{
+	dwc_cc_t* cc;
+
+	DWC_DEBUGC("Change connection context %d", id);
+
+	DWC_MUTEX_LOCK(cc_if->mutex);
+	cc = cc_find(cc_if, id);
+	if (!cc) {
+		DWC_ERROR("Uid %d not found in cc list\n", id);
+		DWC_MUTEX_UNLOCK(cc_if->mutex);
+		return;
+	}
+
+	if (chid) {
+		DWC_MEMCPY(&(cc->chid[0]), chid, 16);
+	}
+	if (cdid) {
+		DWC_MEMCPY(&(cc->cdid[0]), cdid, 16);
+	}
+	if (ck) {
+		DWC_MEMCPY(&(cc->ck[0]), ck, 16);
+	}
+
+	if (name) {
+		if (cc->name) {
+			dwc_free(mem_ctx, cc->name);
+		}
+		cc->name = dwc_alloc(mem_ctx, length);
+		if (!cc->name) {
+			DWC_ERROR("Out of memory in dwc_cc_change()\n");
+			DWC_MUTEX_UNLOCK(cc_if->mutex);
+			return;
+		}
+		cc->length = length;
+		DWC_MEMCPY(cc->name, name, length);
+	}
+
+	DWC_MUTEX_UNLOCK(cc_if->mutex);
+
+	cc_changed(cc_if);
+
+	DWC_DEBUGC("Changed connection context id=%d\n", id);
+	dump_bytes("New CHID", cc->chid, 16);
+	dump_bytes("New CDID", cc->cdid, 16);
+	dump_bytes("New CK", cc->ck, 16);
+}
+
+void dwc_cc_remove(void *mem_ctx, dwc_cc_if_t *cc_if, int32_t id)
+{
+	dwc_cc_t *cc;
+
+	DWC_DEBUGC("Removing connection context %d", id);
+
+	DWC_MUTEX_LOCK(cc_if->mutex);
+	cc = cc_find(cc_if, id);
+	if (!cc) {
+		DWC_ERROR("Uid %d not found in cc list\n", id);
+		DWC_MUTEX_UNLOCK(cc_if->mutex);
+		return;
+	}
+
+	DWC_CIRCLEQ_REMOVE_INIT(&cc_if->list, cc, list_entry);
+	DWC_MUTEX_UNLOCK(cc_if->mutex);
+	free_cc(mem_ctx, cc);
+
+	cc_changed(cc_if);
+}
+
+uint8_t *dwc_cc_data_for_save(void *mem_ctx, dwc_cc_if_t *cc_if, unsigned int *length)
+{
+	uint8_t *buf, *x;
+	uint8_t zero = 0;
+	dwc_cc_t *cc;
+
+	DWC_MUTEX_LOCK(cc_if->mutex);
+	*length = cc_data_size(cc_if);
+	if (!(*length)) {
+		DWC_MUTEX_UNLOCK(cc_if->mutex);
+		return NULL;
+	}
+
+	DWC_DEBUGC("Creating data for saving (length=%d)", *length);
+
+	buf = dwc_alloc(mem_ctx, *length);
+	if (!buf) {
+		*length = 0;
+		DWC_MUTEX_UNLOCK(cc_if->mutex);
+		return NULL;
+	}
+
+	x = buf;
+	DWC_CIRCLEQ_FOREACH(cc, &cc_if->list, list_entry) {
+		DWC_MEMCPY(x, cc->chid, 16);
+		x += 16;
+		DWC_MEMCPY(x, cc->cdid, 16);
+		x += 16;
+		DWC_MEMCPY(x, cc->ck, 16);
+		x += 16;
+		if (cc->name) {
+			DWC_MEMCPY(x, &cc->length, 1);
+			x += 1;
+			DWC_MEMCPY(x, cc->name, cc->length);
+			x += cc->length;
+		}
+		else {
+			DWC_MEMCPY(x, &zero, 1);
+			x += 1;
+		}
+	}
+	DWC_MUTEX_UNLOCK(cc_if->mutex);
+
+	return buf;
+}
+
+void dwc_cc_restore_from_data(void *mem_ctx, dwc_cc_if_t *cc_if, uint8_t *data, uint32_t length)
+{
+	uint8_t name_length;
+	uint8_t *name;
+	uint8_t *chid;
+	uint8_t *cdid;
+	uint8_t *ck;
+	uint32_t i = 0;
+
+	DWC_MUTEX_LOCK(cc_if->mutex);
+	cc_clear(mem_ctx, cc_if);
+
+	while (i < length) {
+		chid = &data[i];
+		i += 16;
+		cdid = &data[i];
+		i += 16;
+		ck = &data[i];
+		i += 16;
+
+		name_length = data[i];
+		i ++;
+
+		if (name_length) {
+			name = &data[i];
+			i += name_length;
+		}
+		else {
+			name = NULL;
+		}
+
+		/* check to see if we haven't overflown the buffer */
+		if (i > length) {
+			DWC_ERROR("Data format error while attempting to load CCs "
+				  "(nlen=%d, iter=%d, buflen=%d).\n", name_length, i, length);
+			break;
+		}
+
+		cc_add(mem_ctx, cc_if, chid, cdid, ck, name, name_length);
+	}
+	DWC_MUTEX_UNLOCK(cc_if->mutex);
+
+	cc_changed(cc_if);
+}
+
+uint32_t dwc_cc_match_chid(dwc_cc_if_t *cc_if, uint8_t *chid)
+{
+	uint32_t uid = 0;
+
+	DWC_MUTEX_LOCK(cc_if->mutex);
+	uid = cc_match_chid(cc_if, chid);
+	DWC_MUTEX_UNLOCK(cc_if->mutex);
+	return uid;
+}
+uint32_t dwc_cc_match_cdid(dwc_cc_if_t *cc_if, uint8_t *cdid)
+{
+	uint32_t uid = 0;
+
+	DWC_MUTEX_LOCK(cc_if->mutex);
+	uid = cc_match_cdid(cc_if, cdid);
+	DWC_MUTEX_UNLOCK(cc_if->mutex);
+	return uid;
+}
+
+uint8_t *dwc_cc_ck(dwc_cc_if_t *cc_if, int32_t id)
+{
+	uint8_t *ck = NULL;
+	dwc_cc_t *cc;
+
+	DWC_MUTEX_LOCK(cc_if->mutex);
+	cc = cc_find(cc_if, id);
+	if (cc) {
+		ck = cc->ck;
+	}
+	DWC_MUTEX_UNLOCK(cc_if->mutex);
+
+	return ck;
+
+}
+
+uint8_t *dwc_cc_chid(dwc_cc_if_t *cc_if, int32_t id)
+{
+	uint8_t *retval = NULL;
+	dwc_cc_t *cc;
+
+	DWC_MUTEX_LOCK(cc_if->mutex);
+	cc = cc_find(cc_if, id);
+	if (cc) {
+		retval = cc->chid;
+	}
+	DWC_MUTEX_UNLOCK(cc_if->mutex);
+
+	return retval;
+}
+
+uint8_t *dwc_cc_cdid(dwc_cc_if_t *cc_if, int32_t id)
+{
+	uint8_t *retval = NULL;
+	dwc_cc_t *cc;
+
+	DWC_MUTEX_LOCK(cc_if->mutex);
+	cc = cc_find(cc_if, id);
+	if (cc) {
+		retval = cc->cdid;
+	}
+	DWC_MUTEX_UNLOCK(cc_if->mutex);
+
+	return retval;
+}
+
+uint8_t *dwc_cc_name(dwc_cc_if_t *cc_if, int32_t id, uint8_t *length)
+{
+	uint8_t *retval = NULL;
+	dwc_cc_t *cc;
+
+	DWC_MUTEX_LOCK(cc_if->mutex);
+	*length = 0;
+	cc = cc_find(cc_if, id);
+	if (cc) {
+		*length = cc->length;
+		retval = cc->name;
+	}
+	DWC_MUTEX_UNLOCK(cc_if->mutex);
+
+	return retval;
+}
+
+#endif	/* DWC_CCLIB */
diff -Nur --no-dereference linux-5.10.52-orig/drivers/usb/host/dwc_common_port/dwc_cc.h linux-5.10.52-v7l+/drivers/usb/host/dwc_common_port/dwc_cc.h
--- linux-5.10.52-orig/drivers/usb/host/dwc_common_port/dwc_cc.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/usb/host/dwc_common_port/dwc_cc.h	2021-07-25 16:46:28.657910448 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/* =========================================================================
+ * $File: //dwh/usb_iip/dev/software/dwc_common_port_2/dwc_cc.h $
+ * $Revision: #4 $
+ * $Date: 2010/09/28 $
+ * $Change: 1596182 $
+ *
+ * Synopsys Portability Library Software and documentation
+ * (hereinafter, "Software") is an Unsupported proprietary work of
+ * Synopsys, Inc. unless otherwise expressly agreed to in writing
+ * between Synopsys and you.
+ *
+ * The Software IS NOT an item of Licensed Software or Licensed Product
+ * under any End User Software License Agreement or Agreement for
+ * Licensed Product with Synopsys or any supplement thereto. You are
+ * permitted to use and redistribute this Software in source and binary
+ * forms, with or without modification, provided that redistributions
+ * of source code must retain this notice. You may not view, use,
+ * disclose, copy or distribute this file or any information contained
+ * herein except pursuant to this license grant from Synopsys. If you
+ * do not agree with this notice, including the disclaimer below, then
+ * you are not authorized to use the Software.
+ *
+ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS"
+ * BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL
+ * SYNOPSYS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * ========================================================================= */
+#ifndef _DWC_CC_H_
+#define _DWC_CC_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** @file
+ *
+ * This file defines the Context Context library.
+ *
+ * The main data structure is dwc_cc_if_t which is returned by either the
+ * dwc_cc_if_alloc function or returned by the module to the user via a provided
+ * function. The data structure is opaque and should only be manipulated via the
+ * functions provied in this API.
+ *
+ * It manages a list of connection contexts and operations can be performed to
+ * add, remove, query, search, and change, those contexts.  Additionally,
+ * a dwc_notifier_t object can be requested from the manager so that
+ * the user can be notified whenever the context list has changed.
+ */
+
+#include "dwc_os.h"
+#include "dwc_list.h"
+#include "dwc_notifier.h"
+
+
+/* Notifications */
+#define DWC_CC_LIST_CHANGED_NOTIFICATION "DWC_CC_LIST_CHANGED_NOTIFICATION"
+
+struct dwc_cc_if;
+typedef struct dwc_cc_if dwc_cc_if_t;
+
+
+/** @name Connection Context Operations */
+/** @{ */
+
+/** This function allocates memory for a dwc_cc_if_t structure, initializes
+ * fields to default values, and returns a pointer to the structure or NULL on
+ * error. */
+extern dwc_cc_if_t *dwc_cc_if_alloc(void *mem_ctx, void *mtx_ctx,
+				    dwc_notifier_t *notifier, unsigned is_host);
+
+/** Frees the memory for the specified CC structure allocated from
+ * dwc_cc_if_alloc(). */
+extern void dwc_cc_if_free(void *mem_ctx, void *mtx_ctx, dwc_cc_if_t *cc_if);
+
+/** Removes all contexts from the connection context list */
+extern void dwc_cc_clear(void *mem_ctx, dwc_cc_if_t *cc_if);
+
+/** Adds a connection context (CHID, CK, CDID, Name) to the connection context list.
+ * If a CHID already exists, the CK and name are overwritten.  Statistics are
+ * not overwritten.
+ *
+ * @param cc_if The cc_if structure.
+ * @param chid A pointer to the 16-byte CHID.  This value will be copied.
+ * @param ck A pointer to the 16-byte CK.  This value will be copied.
+ * @param cdid A pointer to the 16-byte CDID.  This value will be copied.
+ * @param name An optional host friendly name as defined in the association model
+ * spec.  Must be a UTF16-LE unicode string.  Can be NULL to indicated no name.
+ * @param length The length othe unicode string.
+ * @return A unique identifier used to refer to this context that is valid for
+ * as long as this context is still in the list. */
+extern int32_t dwc_cc_add(void *mem_ctx, dwc_cc_if_t *cc_if, uint8_t *chid,
+			  uint8_t *cdid, uint8_t *ck, uint8_t *name,
+			  uint8_t length);
+
+/** Changes the CHID, CK, CDID, or Name values of a connection context in the
+ * list, preserving any accumulated statistics.  This would typically be called
+ * if the host decideds to change the context with a SET_CONNECTION request.
+ *
+ * @param cc_if The cc_if structure.
+ * @param id The identifier of the connection context.
+ * @param chid A pointer to the 16-byte CHID.  This value will be copied.  NULL
+ * indicates no change.
+ * @param cdid A pointer to the 16-byte CDID.  This value will be copied.  NULL
+ * indicates no change.
+ * @param ck A pointer to the 16-byte CK.  This value will be copied.  NULL
+ * indicates no change.
+ * @param name Host friendly name UTF16-LE.  NULL indicates no change.
+ * @param length Length of name. */
+extern void dwc_cc_change(void *mem_ctx, dwc_cc_if_t *cc_if, int32_t id,
+			  uint8_t *chid, uint8_t *cdid, uint8_t *ck,
+			  uint8_t *name, uint8_t length);
+
+/** Remove the specified connection context.
+ * @param cc_if The cc_if structure.
+ * @param id The identifier of the connection context to remove. */
+extern void dwc_cc_remove(void *mem_ctx, dwc_cc_if_t *cc_if, int32_t id);
+
+/** Get a binary block of data for the connection context list and attributes.
+ * This data can be used by the OS specific driver to save the connection
+ * context list into non-volatile memory.
+ *
+ * @param cc_if The cc_if structure.
+ * @param length Return the length of the data buffer.
+ * @return A pointer to the data buffer.  The memory for this buffer should be
+ * freed with DWC_FREE() after use. */
+extern uint8_t *dwc_cc_data_for_save(void *mem_ctx, dwc_cc_if_t *cc_if,
+				     unsigned int *length);
+
+/** Restore the connection context list from the binary data that was previously
+ * returned from a call to dwc_cc_data_for_save.  This can be used by the OS specific
+ * driver to load a connection context list from non-volatile memory.
+ *
+ * @param cc_if The cc_if structure.
+ * @param data The data bytes as returned from dwc_cc_data_for_save.
+ * @param length The length of the data. */
+extern void dwc_cc_restore_from_data(void *mem_ctx, dwc_cc_if_t *cc_if,
+				     uint8_t *data, unsigned int length);
+
+/** Find the connection context from the specified CHID.
+ *
+ * @param cc_if The cc_if structure.
+ * @param chid A pointer to the CHID data.
+ * @return A non-zero identifier of the connection context if the CHID matches.
+ * Otherwise returns 0. */
+extern uint32_t dwc_cc_match_chid(dwc_cc_if_t *cc_if, uint8_t *chid);
+
+/** Find the connection context from the specified CDID.
+ *
+ * @param cc_if The cc_if structure.
+ * @param cdid A pointer to the CDID data.
+ * @return A non-zero identifier of the connection context if the CHID matches.
+ * Otherwise returns 0. */
+extern uint32_t dwc_cc_match_cdid(dwc_cc_if_t *cc_if, uint8_t *cdid);
+
+/** Retrieve the CK from the specified connection context.
+ *
+ * @param cc_if The cc_if structure.
+ * @param id The identifier of the connection context.
+ * @return A pointer to the CK data.  The memory does not need to be freed. */
+extern uint8_t *dwc_cc_ck(dwc_cc_if_t *cc_if, int32_t id);
+
+/** Retrieve the CHID from the specified connection context.
+ *
+ * @param cc_if The cc_if structure.
+ * @param id The identifier of the connection context.
+ * @return A pointer to the CHID data.  The memory does not need to be freed. */
+extern uint8_t *dwc_cc_chid(dwc_cc_if_t *cc_if, int32_t id);
+
+/** Retrieve the CDID from the specified connection context.
+ *
+ * @param cc_if The cc_if structure.
+ * @param id The identifier of the connection context.
+ * @return A pointer to the CDID data.  The memory does not need to be freed. */
+extern uint8_t *dwc_cc_cdid(dwc_cc_if_t *cc_if, int32_t id);
+
+extern uint8_t *dwc_cc_name(dwc_cc_if_t *cc_if, int32_t id, uint8_t *length);
+
+/** Checks a buffer for non-zero.
+ * @param id A pointer to a 16 byte buffer.
+ * @return true if the 16 byte value is non-zero. */
+static inline unsigned dwc_assoc_is_not_zero_id(uint8_t *id) {
+	int i;
+	for (i=0; i<16; i++) {
+		if (id[i]) return 1;
+	}
+	return 0;
+}
+
+/** Checks a buffer for zero.
+ * @param id A pointer to a 16 byte buffer.
+ * @return true if the 16 byte value is zero. */
+static inline unsigned dwc_assoc_is_zero_id(uint8_t *id) {
+	return !dwc_assoc_is_not_zero_id(id);
+}
+
+/** Prints an ASCII representation for the 16-byte chid, cdid, or ck, into
+ * buffer. */
+static inline int dwc_print_id_string(char *buffer, uint8_t *id) {
+	char *ptr = buffer;
+	int i;
+	for (i=0; i<16; i++) {
+		ptr += DWC_SPRINTF(ptr, "%02x", id[i]);
+		if (i < 15) {
+			ptr += DWC_SPRINTF(ptr, " ");
+		}
+	}
+	return ptr - buffer;
+}
+
+/** @} */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _DWC_CC_H_ */
diff -Nur --no-dereference linux-5.10.52-orig/drivers/usb/host/dwc_common_port/dwc_common_fbsd.c linux-5.10.52-v7l+/drivers/usb/host/dwc_common_port/dwc_common_fbsd.c
--- linux-5.10.52-orig/drivers/usb/host/dwc_common_port/dwc_common_fbsd.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/usb/host/dwc_common_port/dwc_common_fbsd.c	2021-07-25 16:46:28.657910448 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+#include "dwc_os.h"
+#include "dwc_list.h"
+
+#ifdef DWC_CCLIB
+# include "dwc_cc.h"
+#endif
+
+#ifdef DWC_CRYPTOLIB
+# include "dwc_modpow.h"
+# include "dwc_dh.h"
+# include "dwc_crypto.h"
+#endif
+
+#ifdef DWC_NOTIFYLIB
+# include "dwc_notifier.h"
+#endif
+
+/* OS-Level Implementations */
+
+/* This is the FreeBSD 7.0 kernel implementation of the DWC platform library. */
+
+
+/* MISC */
+
+void *DWC_MEMSET(void *dest, uint8_t byte, uint32_t size)
+{
+	return memset(dest, byte, size);
+}
+
+void *DWC_MEMCPY(void *dest, void const *src, uint32_t size)
+{
+	return memcpy(dest, src, size);
+}
+
+void *DWC_MEMMOVE(void *dest, void *src, uint32_t size)
+{
+	bcopy(src, dest, size);
+	return dest;
+}
+
+int DWC_MEMCMP(void *m1, void *m2, uint32_t size)
+{
+	return memcmp(m1, m2, size);
+}
+
+int DWC_STRNCMP(void *s1, void *s2, uint32_t size)
+{
+	return strncmp(s1, s2, size);
+}
+
+int DWC_STRCMP(void *s1, void *s2)
+{
+	return strcmp(s1, s2);
+}
+
+int DWC_STRLEN(char const *str)
+{
+	return strlen(str);
+}
+
+char *DWC_STRCPY(char *to, char const *from)
+{
+	return strcpy(to, from);
+}
+
+char *DWC_STRDUP(char const *str)
+{
+	int len = DWC_STRLEN(str) + 1;
+	char *new = DWC_ALLOC_ATOMIC(len);
+
+	if (!new) {
+		return NULL;
+	}
+
+	DWC_MEMCPY(new, str, len);
+	return new;
+}
+
+int DWC_ATOI(char *str, int32_t *value)
+{
+	char *end = NULL;
+
+	*value = strtol(str, &end, 0);
+	if (*end == '\0') {
+		return 0;
+	}
+
+	return -1;
+}
+
+int DWC_ATOUI(char *str, uint32_t *value)
+{
+	char *end = NULL;
+
+	*value = strtoul(str, &end, 0);
+	if (*end == '\0') {
+		return 0;
+	}
+
+	return -1;
+}
+
+
+#ifdef DWC_UTFLIB
+/* From usbstring.c */
+
+int DWC_UTF8_TO_UTF16LE(uint8_t const *s, uint16_t *cp, unsigned len)
+{
+	int	count = 0;
+	u8	c;
+	u16	uchar;
+
+	/* this insists on correct encodings, though not minimal ones.
+	 * BUT it currently rejects legit 4-byte UTF-8 code points,
+	 * which need surrogate pairs.  (Unicode 3.1 can use them.)
+	 */
+	while (len != 0 && (c = (u8) *s++) != 0) {
+		if (unlikely(c & 0x80)) {
+			// 2-byte sequence:
+			// 00000yyyyyxxxxxx = 110yyyyy 10xxxxxx
+			if ((c & 0xe0) == 0xc0) {
+				uchar = (c & 0x1f) << 6;
+
+				c = (u8) *s++;
+				if ((c & 0xc0) != 0xc0)
+					goto fail;
+				c &= 0x3f;
+				uchar |= c;
+
+			// 3-byte sequence (most CJKV characters):
+			// zzzzyyyyyyxxxxxx = 1110zzzz 10yyyyyy 10xxxxxx
+			} else if ((c & 0xf0) == 0xe0) {
+				uchar = (c & 0x0f) << 12;
+
+				c = (u8) *s++;
+				if ((c & 0xc0) != 0xc0)
+					goto fail;
+				c &= 0x3f;
+				uchar |= c << 6;
+
+				c = (u8) *s++;
+				if ((c & 0xc0) != 0xc0)
+					goto fail;
+				c &= 0x3f;
+				uchar |= c;
+
+				/* no bogus surrogates */
+				if (0xd800 <= uchar && uchar <= 0xdfff)
+					goto fail;
+
+			// 4-byte sequence (surrogate pairs, currently rare):
+			// 11101110wwwwzzzzyy + 110111yyyyxxxxxx
+			//     = 11110uuu 10uuzzzz 10yyyyyy 10xxxxxx
+			// (uuuuu = wwww + 1)
+			// FIXME accept the surrogate code points (only)
+			} else
+				goto fail;
+		} else
+			uchar = c;
+		put_unaligned (cpu_to_le16 (uchar), cp++);
+		count++;
+		len--;
+	}
+	return count;
+fail:
+	return -1;
+}
+
+#endif	/* DWC_UTFLIB */
+
+
+/* dwc_debug.h */
+
+dwc_bool_t DWC_IN_IRQ(void)
+{
+//	return in_irq();
+	return 0;
+}
+
+dwc_bool_t DWC_IN_BH(void)
+{
+//	return in_softirq();
+	return 0;
+}
+
+void DWC_VPRINTF(char *format, va_list args)
+{
+	vprintf(format, args);
+}
+
+int DWC_VSNPRINTF(char *str, int size, char *format, va_list args)
+{
+	return vsnprintf(str, size, format, args);
+}
+
+void DWC_PRINTF(char *format, ...)
+{
+	va_list args;
+
+	va_start(args, format);
+	DWC_VPRINTF(format, args);
+	va_end(args);
+}
+
+int DWC_SPRINTF(char *buffer, char *format, ...)
+{
+	int retval;
+	va_list args;
+
+	va_start(args, format);
+	retval = vsprintf(buffer, format, args);
+	va_end(args);
+	return retval;
+}
+
+int DWC_SNPRINTF(char *buffer, int size, char *format, ...)
+{
+	int retval;
+	va_list args;
+
+	va_start(args, format);
+	retval = vsnprintf(buffer, size, format, args);
+	va_end(args);
+	return retval;
+}
+
+void __DWC_WARN(char *format, ...)
+{
+	va_list args;
+
+	va_start(args, format);
+	DWC_VPRINTF(format, args);
+	va_end(args);
+}
+
+void __DWC_ERROR(char *format, ...)
+{
+	va_list args;
+
+	va_start(args, format);
+	DWC_VPRINTF(format, args);
+	va_end(args);
+}
+
+void DWC_EXCEPTION(char *format, ...)
+{
+	va_list args;
+
+	va_start(args, format);
+	DWC_VPRINTF(format, args);
+	va_end(args);
+//	BUG_ON(1);	???
+}
+
+#ifdef DEBUG
+void __DWC_DEBUG(char *format, ...)
+{
+	va_list args;
+
+	va_start(args, format);
+	DWC_VPRINTF(format, args);
+	va_end(args);
+}
+#endif
+
+
+/* dwc_mem.h */
+
+#if 0
+dwc_pool_t *DWC_DMA_POOL_CREATE(uint32_t size,
+				uint32_t align,
+				uint32_t alloc)
+{
+	struct dma_pool *pool = dma_pool_create("Pool", NULL,
+						size, align, alloc);
+	return (dwc_pool_t *)pool;
+}
+
+void DWC_DMA_POOL_DESTROY(dwc_pool_t *pool)
+{
+	dma_pool_destroy((struct dma_pool *)pool);
+}
+
+void *DWC_DMA_POOL_ALLOC(dwc_pool_t *pool, uint64_t *dma_addr)
+{
+//	return dma_pool_alloc((struct dma_pool *)pool, GFP_KERNEL, dma_addr);
+	return dma_pool_alloc((struct dma_pool *)pool, M_WAITOK, dma_addr);
+}
+
+void *DWC_DMA_POOL_ZALLOC(dwc_pool_t *pool, uint64_t *dma_addr)
+{
+	void *vaddr = DWC_DMA_POOL_ALLOC(pool, dma_addr);
+	memset(..);
+}
+
+void DWC_DMA_POOL_FREE(dwc_pool_t *pool, void *vaddr, void *daddr)
+{
+	dma_pool_free(pool, vaddr, daddr);
+}
+#endif
+
+static void dmamap_cb(void *arg, bus_dma_segment_t *segs, int nseg, int error)
+{
+	if (error)
+		return;
+	*(bus_addr_t *)arg = segs[0].ds_addr;
+}
+
+void *__DWC_DMA_ALLOC(void *dma_ctx, uint32_t size, dwc_dma_t *dma_addr)
+{
+	dwc_dmactx_t *dma = (dwc_dmactx_t *)dma_ctx;
+	int error;
+
+	error = bus_dma_tag_create(
+#if __FreeBSD_version >= 700000
+			bus_get_dma_tag(dma->dev),	/* parent */
+#else
+			NULL,				/* parent */
+#endif
+			4, 0,				/* alignment, bounds */
+			BUS_SPACE_MAXADDR_32BIT,	/* lowaddr */
+			BUS_SPACE_MAXADDR,		/* highaddr */
+			NULL, NULL,			/* filter, filterarg */
+			size,				/* maxsize */
+			1,				/* nsegments */
+			size,				/* maxsegsize */
+			0,				/* flags */
+			NULL,				/* lockfunc */
+			NULL,				/* lockarg */
+			&dma->dma_tag);
+	if (error) {
+		device_printf(dma->dev, "%s: bus_dma_tag_create failed: %d\n",
+			      __func__, error);
+		goto fail_0;
+	}
+
+	error = bus_dmamem_alloc(dma->dma_tag, &dma->dma_vaddr,
+				 BUS_DMA_NOWAIT | BUS_DMA_COHERENT, &dma->dma_map);
+	if (error) {
+		device_printf(dma->dev, "%s: bus_dmamem_alloc(%ju) failed: %d\n",
+			      __func__, (uintmax_t)size, error);
+		goto fail_1;
+	}
+
+	dma->dma_paddr = 0;
+	error = bus_dmamap_load(dma->dma_tag, dma->dma_map, dma->dma_vaddr, size,
+				dmamap_cb, &dma->dma_paddr, BUS_DMA_NOWAIT);
+	if (error || dma->dma_paddr == 0) {
+		device_printf(dma->dev, "%s: bus_dmamap_load failed: %d\n",
+			      __func__, error);
+		goto fail_2;
+	}
+
+	*dma_addr = dma->dma_paddr;
+	return dma->dma_vaddr;
+
+fail_2:
+	bus_dmamap_unload(dma->dma_tag, dma->dma_map);
+fail_1:
+	bus_dmamem_free(dma->dma_tag, dma->dma_vaddr, dma->dma_map);
+	bus_dma_tag_destroy(dma->dma_tag);
+fail_0:
+	dma->dma_map = NULL;
+	dma->dma_tag = NULL;
+
+	return NULL;
+}
+
+void __DWC_DMA_FREE(void *dma_ctx, uint32_t size, void *virt_addr, dwc_dma_t dma_addr)
+{
+	dwc_dmactx_t *dma = (dwc_dmactx_t *)dma_ctx;
+
+	if (dma->dma_tag == NULL)
+		return;
+	if (dma->dma_map != NULL) {
+		bus_dmamap_sync(dma->dma_tag, dma->dma_map,
+				BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
+		bus_dmamap_unload(dma->dma_tag, dma->dma_map);
+		bus_dmamem_free(dma->dma_tag, dma->dma_vaddr, dma->dma_map);
+		dma->dma_map = NULL;
+	}
+
+	bus_dma_tag_destroy(dma->dma_tag);
+	dma->dma_tag = NULL;
+}
+
+void *__DWC_ALLOC(void *mem_ctx, uint32_t size)
+{
+	return malloc(size, M_DEVBUF, M_WAITOK | M_ZERO);
+}
+
+void *__DWC_ALLOC_ATOMIC(void *mem_ctx, uint32_t size)
+{
+	return malloc(size, M_DEVBUF, M_NOWAIT | M_ZERO);
+}
+
+void __DWC_FREE(void *mem_ctx, void *addr)
+{
+	free(addr, M_DEVBUF);
+}
+
+
+#ifdef DWC_CRYPTOLIB
+/* dwc_crypto.h */
+
+void DWC_RANDOM_BYTES(uint8_t *buffer, uint32_t length)
+{
+	get_random_bytes(buffer, length);
+}
+
+int DWC_AES_CBC(uint8_t *message, uint32_t messagelen, uint8_t *key, uint32_t keylen, uint8_t iv[16], uint8_t *out)
+{
+	struct crypto_blkcipher *tfm;
+	struct blkcipher_desc desc;
+	struct scatterlist sgd;
+	struct scatterlist sgs;
+
+	tfm = crypto_alloc_blkcipher("cbc(aes)", 0, CRYPTO_ALG_ASYNC);
+	if (tfm == NULL) {
+		printk("failed to load transform for aes CBC\n");
+		return -1;
+	}
+
+	crypto_blkcipher_setkey(tfm, key, keylen);
+	crypto_blkcipher_set_iv(tfm, iv, 16);
+
+	sg_init_one(&sgd, out, messagelen);
+	sg_init_one(&sgs, message, messagelen);
+
+	desc.tfm = tfm;
+	desc.flags = 0;
+
+	if (crypto_blkcipher_encrypt(&desc, &sgd, &sgs, messagelen)) {
+		crypto_free_blkcipher(tfm);
+		DWC_ERROR("AES CBC encryption failed");
+		return -1;
+	}
+
+	crypto_free_blkcipher(tfm);
+	return 0;
+}
+
+int DWC_SHA256(uint8_t *message, uint32_t len, uint8_t *out)
+{
+	struct crypto_hash *tfm;
+	struct hash_desc desc;
+	struct scatterlist sg;
+
+	tfm = crypto_alloc_hash("sha256", 0, CRYPTO_ALG_ASYNC);
+	if (IS_ERR(tfm)) {
+		DWC_ERROR("Failed to load transform for sha256: %ld", PTR_ERR(tfm));
+		return 0;
+	}
+	desc.tfm = tfm;
+	desc.flags = 0;
+
+	sg_init_one(&sg, message, len);
+	crypto_hash_digest(&desc, &sg, len, out);
+	crypto_free_hash(tfm);
+
+	return 1;
+}
+
+int DWC_HMAC_SHA256(uint8_t *message, uint32_t messagelen,
+		    uint8_t *key, uint32_t keylen, uint8_t *out)
+{
+	struct crypto_hash *tfm;
+	struct hash_desc desc;
+	struct scatterlist sg;
+
+	tfm = crypto_alloc_hash("hmac(sha256)", 0, CRYPTO_ALG_ASYNC);
+	if (IS_ERR(tfm)) {
+		DWC_ERROR("Failed to load transform for hmac(sha256): %ld", PTR_ERR(tfm));
+		return 0;
+	}
+	desc.tfm = tfm;
+	desc.flags = 0;
+
+	sg_init_one(&sg, message, messagelen);
+	crypto_hash_setkey(tfm, key, keylen);
+	crypto_hash_digest(&desc, &sg, messagelen, out);
+	crypto_free_hash(tfm);
+
+	return 1;
+}
+
+#endif	/* DWC_CRYPTOLIB */
+
+
+/* Byte Ordering Conversions */
+
+uint32_t DWC_CPU_TO_LE32(uint32_t *p)
+{
+#ifdef __LITTLE_ENDIAN
+	return *p;
+#else
+	uint8_t *u_p = (uint8_t *)p;
+
+	return (u_p[3] | (u_p[2] << 8) | (u_p[1] << 16) | (u_p[0] << 24));
+#endif
+}
+
+uint32_t DWC_CPU_TO_BE32(uint32_t *p)
+{
+#ifdef __BIG_ENDIAN
+	return *p;
+#else
+	uint8_t *u_p = (uint8_t *)p;
+
+	return (u_p[3] | (u_p[2] << 8) | (u_p[1] << 16) | (u_p[0] << 24));
+#endif
+}
+
+uint32_t DWC_LE32_TO_CPU(uint32_t *p)
+{
+#ifdef __LITTLE_ENDIAN
+	return *p;
+#else
+	uint8_t *u_p = (uint8_t *)p;
+
+	return (u_p[3] | (u_p[2] << 8) | (u_p[1] << 16) | (u_p[0] << 24));
+#endif
+}
+
+uint32_t DWC_BE32_TO_CPU(uint32_t *p)
+{
+#ifdef __BIG_ENDIAN
+	return *p;
+#else
+	uint8_t *u_p = (uint8_t *)p;
+
+	return (u_p[3] | (u_p[2] << 8) | (u_p[1] << 16) | (u_p[0] << 24));
+#endif
+}
+
+uint16_t DWC_CPU_TO_LE16(uint16_t *p)
+{
+#ifdef __LITTLE_ENDIAN
+	return *p;
+#else
+	uint8_t *u_p = (uint8_t *)p;
+	return (u_p[1] | (u_p[0] << 8));
+#endif
+}
+
+uint16_t DWC_CPU_TO_BE16(uint16_t *p)
+{
+#ifdef __BIG_ENDIAN
+	return *p;
+#else
+	uint8_t *u_p = (uint8_t *)p;
+	return (u_p[1] | (u_p[0] << 8));
+#endif
+}
+
+uint16_t DWC_LE16_TO_CPU(uint16_t *p)
+{
+#ifdef __LITTLE_ENDIAN
+	return *p;
+#else
+	uint8_t *u_p = (uint8_t *)p;
+	return (u_p[1] | (u_p[0] << 8));
+#endif
+}
+
+uint16_t DWC_BE16_TO_CPU(uint16_t *p)
+{
+#ifdef __BIG_ENDIAN
+	return *p;
+#else
+	uint8_t *u_p = (uint8_t *)p;
+	return (u_p[1] | (u_p[0] << 8));
+#endif
+}
+
+
+/* Registers */
+
+uint32_t DWC_READ_REG32(void *io_ctx, uint32_t volatile *reg)
+{
+	dwc_ioctx_t *io = (dwc_ioctx_t *)io_ctx;
+	bus_size_t ior = (bus_size_t)reg;
+
+	return bus_space_read_4(io->iot, io->ioh, ior);
+}
+
+#if 0
+uint64_t DWC_READ_REG64(void *io_ctx, uint64_t volatile *reg)
+{
+	dwc_ioctx_t *io = (dwc_ioctx_t *)io_ctx;
+	bus_size_t ior = (bus_size_t)reg;
+
+	return bus_space_read_8(io->iot, io->ioh, ior);
+}
+#endif
+
+void DWC_WRITE_REG32(void *io_ctx, uint32_t volatile *reg, uint32_t value)
+{
+	dwc_ioctx_t *io = (dwc_ioctx_t *)io_ctx;
+	bus_size_t ior = (bus_size_t)reg;
+
+	bus_space_write_4(io->iot, io->ioh, ior, value);
+}
+
+#if 0
+void DWC_WRITE_REG64(void *io_ctx, uint64_t volatile *reg, uint64_t value)
+{
+	dwc_ioctx_t *io = (dwc_ioctx_t *)io_ctx;
+	bus_size_t ior = (bus_size_t)reg;
+
+	bus_space_write_8(io->iot, io->ioh, ior, value);
+}
+#endif
+
+void DWC_MODIFY_REG32(void *io_ctx, uint32_t volatile *reg, uint32_t clear_mask,
+		      uint32_t set_mask)
+{
+	dwc_ioctx_t *io = (dwc_ioctx_t *)io_ctx;
+	bus_size_t ior = (bus_size_t)reg;
+
+	bus_space_write_4(io->iot, io->ioh, ior,
+			  (bus_space_read_4(io->iot, io->ioh, ior) &
+			   ~clear_mask) | set_mask);
+}
+
+#if 0
+void DWC_MODIFY_REG64(void *io_ctx, uint64_t volatile *reg, uint64_t clear_mask,
+		      uint64_t set_mask)
+{
+	dwc_ioctx_t *io = (dwc_ioctx_t *)io_ctx;
+	bus_size_t ior = (bus_size_t)reg;
+
+	bus_space_write_8(io->iot, io->ioh, ior,
+			  (bus_space_read_8(io->iot, io->ioh, ior) &
+			   ~clear_mask) | set_mask);
+}
+#endif
+
+
+/* Locking */
+
+dwc_spinlock_t *DWC_SPINLOCK_ALLOC(void)
+{
+	struct mtx *sl = DWC_ALLOC(sizeof(*sl));
+
+	if (!sl) {
+		DWC_ERROR("Cannot allocate memory for spinlock");
+		return NULL;
+	}
+
+	mtx_init(sl, "dw3spn", NULL, MTX_SPIN);
+	return (dwc_spinlock_t *)sl;
+}
+
+void DWC_SPINLOCK_FREE(dwc_spinlock_t *lock)
+{
+	struct mtx *sl = (struct mtx *)lock;
+
+	mtx_destroy(sl);
+	DWC_FREE(sl);
+}
+
+void DWC_SPINLOCK(dwc_spinlock_t *lock)
+{
+	mtx_lock_spin((struct mtx *)lock);	// ???
+}
+
+void DWC_SPINUNLOCK(dwc_spinlock_t *lock)
+{
+	mtx_unlock_spin((struct mtx *)lock);	// ???
+}
+
+void DWC_SPINLOCK_IRQSAVE(dwc_spinlock_t *lock, dwc_irqflags_t *flags)
+{
+	mtx_lock_spin((struct mtx *)lock);
+}
+
+void DWC_SPINUNLOCK_IRQRESTORE(dwc_spinlock_t *lock, dwc_irqflags_t flags)
+{
+	mtx_unlock_spin((struct mtx *)lock);
+}
+
+dwc_mutex_t *DWC_MUTEX_ALLOC(void)
+{
+	struct mtx *m;
+	dwc_mutex_t *mutex = (dwc_mutex_t *)DWC_ALLOC(sizeof(struct mtx));
+
+	if (!mutex) {
+		DWC_ERROR("Cannot allocate memory for mutex");
+		return NULL;
+	}
+
+	m = (struct mtx *)mutex;
+	mtx_init(m, "dw3mtx", NULL, MTX_DEF);
+	return mutex;
+}
+
+#if (defined(DWC_LINUX) && defined(CONFIG_DEBUG_MUTEXES))
+#else
+void DWC_MUTEX_FREE(dwc_mutex_t *mutex)
+{
+	mtx_destroy((struct mtx *)mutex);
+	DWC_FREE(mutex);
+}
+#endif
+
+void DWC_MUTEX_LOCK(dwc_mutex_t *mutex)
+{
+	struct mtx *m = (struct mtx *)mutex;
+
+	mtx_lock(m);
+}
+
+int DWC_MUTEX_TRYLOCK(dwc_mutex_t *mutex)
+{
+	struct mtx *m = (struct mtx *)mutex;
+
+	return mtx_trylock(m);
+}
+
+void DWC_MUTEX_UNLOCK(dwc_mutex_t *mutex)
+{
+	struct mtx *m = (struct mtx *)mutex;
+
+	mtx_unlock(m);
+}
+
+
+/* Timing */
+
+void DWC_UDELAY(uint32_t usecs)
+{
+	DELAY(usecs);
+}
+
+void DWC_MDELAY(uint32_t msecs)
+{
+	do {
+		DELAY(1000);
+	} while (--msecs);
+}
+
+void DWC_MSLEEP(uint32_t msecs)
+{
+	struct timeval tv;
+
+	tv.tv_sec = msecs / 1000;
+	tv.tv_usec = (msecs - tv.tv_sec * 1000) * 1000;
+	pause("dw3slp", tvtohz(&tv));
+}
+
+uint32_t DWC_TIME(void)
+{
+	struct timeval tv;
+
+	microuptime(&tv);	// or getmicrouptime? (less precise, but faster)
+	return tv.tv_sec * 1000 + tv.tv_usec / 1000;
+}
+
+
+/* Timers */
+
+struct dwc_timer {
+	struct callout t;
+	char *name;
+	dwc_spinlock_t *lock;
+	dwc_timer_callback_t cb;
+	void *data;
+};
+
+dwc_timer_t *DWC_TIMER_ALLOC(char *name, dwc_timer_callback_t cb, void *data)
+{
+	dwc_timer_t *t = DWC_ALLOC(sizeof(*t));
+
+	if (!t) {
+		DWC_ERROR("Cannot allocate memory for timer");
+		return NULL;
+	}
+
+	callout_init(&t->t, 1);
+
+	t->name = DWC_STRDUP(name);
+	if (!t->name) {
+		DWC_ERROR("Cannot allocate memory for timer->name");
+		goto no_name;
+	}
+
+	t->lock = DWC_SPINLOCK_ALLOC();
+	if (!t->lock) {
+		DWC_ERROR("Cannot allocate memory for lock");
+		goto no_lock;
+	}
+
+	t->cb = cb;
+	t->data = data;
+
+	return t;
+
+ no_lock:
+	DWC_FREE(t->name);
+ no_name:
+	DWC_FREE(t);
+
+	return NULL;
+}
+
+void DWC_TIMER_FREE(dwc_timer_t *timer)
+{
+	callout_stop(&timer->t);
+	DWC_SPINLOCK_FREE(timer->lock);
+	DWC_FREE(timer->name);
+	DWC_FREE(timer);
+}
+
+void DWC_TIMER_SCHEDULE(dwc_timer_t *timer, uint32_t time)
+{
+	struct timeval tv;
+
+	tv.tv_sec = time / 1000;
+	tv.tv_usec = (time - tv.tv_sec * 1000) * 1000;
+	callout_reset(&timer->t, tvtohz(&tv), timer->cb, timer->data);
+}
+
+void DWC_TIMER_CANCEL(dwc_timer_t *timer)
+{
+	callout_stop(&timer->t);
+}
+
+
+/* Wait Queues */
+
+struct dwc_waitq {
+	struct mtx lock;
+	int abort;
+};
+
+dwc_waitq_t *DWC_WAITQ_ALLOC(void)
+{
+	dwc_waitq_t *wq = DWC_ALLOC(sizeof(*wq));
+
+	if (!wq) {
+		DWC_ERROR("Cannot allocate memory for waitqueue");
+		return NULL;
+	}
+
+	mtx_init(&wq->lock, "dw3wtq", NULL, MTX_DEF);
+	wq->abort = 0;
+
+	return wq;
+}
+
+void DWC_WAITQ_FREE(dwc_waitq_t *wq)
+{
+	mtx_destroy(&wq->lock);
+	DWC_FREE(wq);
+}
+
+int32_t DWC_WAITQ_WAIT(dwc_waitq_t *wq, dwc_waitq_condition_t cond, void *data)
+{
+//	intrmask_t ipl;
+	int result = 0;
+
+	mtx_lock(&wq->lock);
+//	ipl = splbio();
+
+	/* Skip the sleep if already aborted or triggered */
+	if (!wq->abort && !cond(data)) {
+//		splx(ipl);
+		result = msleep(wq, &wq->lock, PCATCH, "dw3wat", 0); // infinite timeout
+//		ipl = splbio();
+	}
+
+	if (result == ERESTART) {	// signaled - restart
+		result = -DWC_E_RESTART;
+
+	} else if (result == EINTR) {	// signaled - interrupt
+		result = -DWC_E_ABORT;
+
+	} else if (wq->abort) {
+		result = -DWC_E_ABORT;
+
+	} else {
+		result = 0;
+	}
+
+	wq->abort = 0;
+//	splx(ipl);
+	mtx_unlock(&wq->lock);
+	return result;
+}
+
+int32_t DWC_WAITQ_WAIT_TIMEOUT(dwc_waitq_t *wq, dwc_waitq_condition_t cond,
+			       void *data, int32_t msecs)
+{
+	struct timeval tv, tv1, tv2;
+//	intrmask_t ipl;
+	int result = 0;
+
+	tv.tv_sec = msecs / 1000;
+	tv.tv_usec = (msecs - tv.tv_sec * 1000) * 1000;
+
+	mtx_lock(&wq->lock);
+//	ipl = splbio();
+
+	/* Skip the sleep if already aborted or triggered */
+	if (!wq->abort && !cond(data)) {
+//		splx(ipl);
+		getmicrouptime(&tv1);
+		result = msleep(wq, &wq->lock, PCATCH, "dw3wto", tvtohz(&tv));
+		getmicrouptime(&tv2);
+//		ipl = splbio();
+	}
+
+	if (result == 0) {			// awoken
+		if (wq->abort) {
+			result = -DWC_E_ABORT;
+		} else {
+			tv2.tv_usec -= tv1.tv_usec;
+			if (tv2.tv_usec < 0) {
+				tv2.tv_usec += 1000000;
+				tv2.tv_sec--;
+			}
+
+			tv2.tv_sec -= tv1.tv_sec;
+			result = tv2.tv_sec * 1000 + tv2.tv_usec / 1000;
+			result = msecs - result;
+			if (result <= 0)
+				result = 1;
+		}
+	} else if (result == ERESTART) {	// signaled - restart
+		result = -DWC_E_RESTART;
+
+	} else if (result == EINTR) {		// signaled - interrupt
+		result = -DWC_E_ABORT;
+
+	} else {				// timed out
+		result = -DWC_E_TIMEOUT;
+	}
+
+	wq->abort = 0;
+//	splx(ipl);
+	mtx_unlock(&wq->lock);
+	return result;
+}
+
+void DWC_WAITQ_TRIGGER(dwc_waitq_t *wq)
+{
+	wakeup(wq);
+}
+
+void DWC_WAITQ_ABORT(dwc_waitq_t *wq)
+{
+//	intrmask_t ipl;
+
+	mtx_lock(&wq->lock);
+//	ipl = splbio();
+	wq->abort = 1;
+	wakeup(wq);
+//	splx(ipl);
+	mtx_unlock(&wq->lock);
+}
+
+
+/* Threading */
+
+struct dwc_thread {
+	struct proc *proc;
+	int abort;
+};
+
+dwc_thread_t *DWC_THREAD_RUN(dwc_thread_function_t func, char *name, void *data)
+{
+	int retval;
+	dwc_thread_t *thread = DWC_ALLOC(sizeof(*thread));
+
+	if (!thread) {
+		return NULL;
+	}
+
+	thread->abort = 0;
+	retval = kthread_create((void (*)(void *))func, data, &thread->proc,
+				RFPROC | RFNOWAIT, 0, "%s", name);
+	if (retval) {
+		DWC_FREE(thread);
+		return NULL;
+	}
+
+	return thread;
+}
+
+int DWC_THREAD_STOP(dwc_thread_t *thread)
+{
+	int retval;
+
+	thread->abort = 1;
+	retval = tsleep(&thread->abort, 0, "dw3stp", 60 * hz);
+
+	if (retval == 0) {
+		/* DWC_THREAD_EXIT() will free the thread struct */
+		return 0;
+	}
+
+	/* NOTE: We leak the thread struct if thread doesn't die */
+
+	if (retval == EWOULDBLOCK) {
+		return -DWC_E_TIMEOUT;
+	}
+
+	return -DWC_E_UNKNOWN;
+}
+
+dwc_bool_t DWC_THREAD_SHOULD_STOP(dwc_thread_t *thread)
+{
+	return thread->abort;
+}
+
+void DWC_THREAD_EXIT(dwc_thread_t *thread)
+{
+	wakeup(&thread->abort);
+	DWC_FREE(thread);
+	kthread_exit(0);
+}
+
+
+/* tasklets
+ - Runs in interrupt context (cannot sleep)
+ - Each tasklet runs on a single CPU [ How can we ensure this on FreeBSD? Does it matter? ]
+ - Different tasklets can be running simultaneously on different CPUs [ shouldn't matter ]
+ */
+struct dwc_tasklet {
+	struct task t;
+	dwc_tasklet_callback_t cb;
+	void *data;
+};
+
+static void tasklet_callback(void *data, int pending)	// what to do with pending ???
+{
+	dwc_tasklet_t *task = (dwc_tasklet_t *)data;
+
+	task->cb(task->data);
+}
+
+dwc_tasklet_t *DWC_TASK_ALLOC(char *name, dwc_tasklet_callback_t cb, void *data)
+{
+	dwc_tasklet_t *task = DWC_ALLOC(sizeof(*task));
+
+	if (task) {
+		task->cb = cb;
+		task->data = data;
+		TASK_INIT(&task->t, 0, tasklet_callback, task);
+	} else {
+		DWC_ERROR("Cannot allocate memory for tasklet");
+	}
+
+	return task;
+}
+
+void DWC_TASK_FREE(dwc_tasklet_t *task)
+{
+	taskqueue_drain(taskqueue_fast, &task->t);	// ???
+	DWC_FREE(task);
+}
+
+void DWC_TASK_SCHEDULE(dwc_tasklet_t *task)
+{
+	/* Uses predefined system queue */
+	taskqueue_enqueue_fast(taskqueue_fast, &task->t);
+}
+
+
+/* workqueues
+ - Runs in process context (can sleep)
+ */
+typedef struct work_container {
+	dwc_work_callback_t cb;
+	void *data;
+	dwc_workq_t *wq;
+	char *name;
+	int hz;
+
+#ifdef DEBUG
+	DWC_CIRCLEQ_ENTRY(work_container) entry;
+#endif
+	struct task task;
+} work_container_t;
+
+#ifdef DEBUG
+DWC_CIRCLEQ_HEAD(work_container_queue, work_container);
+#endif
+
+struct dwc_workq {
+	struct taskqueue *taskq;
+	dwc_spinlock_t *lock;
+	dwc_waitq_t *waitq;
+	int pending;
+
+#ifdef DEBUG
+	struct work_container_queue entries;
+#endif
+};
+
+static void do_work(void *data, int pending)	// what to do with pending ???
+{
+	work_container_t *container = (work_container_t *)data;
+	dwc_workq_t *wq = container->wq;
+	dwc_irqflags_t flags;
+
+	if (container->hz) {
+		pause("dw3wrk", container->hz);
+	}
+
+	container->cb(container->data);
+	DWC_DEBUG("Work done: %s, container=%p", container->name, container);
+
+	DWC_SPINLOCK_IRQSAVE(wq->lock, &flags);
+
+#ifdef DEBUG
+	DWC_CIRCLEQ_REMOVE(&wq->entries, container, entry);
+#endif
+	if (container->name)
+		DWC_FREE(container->name);
+	DWC_FREE(container);
+	wq->pending--;
+	DWC_SPINUNLOCK_IRQRESTORE(wq->lock, flags);
+	DWC_WAITQ_TRIGGER(wq->waitq);
+}
+
+static int work_done(void *data)
+{
+	dwc_workq_t *workq = (dwc_workq_t *)data;
+
+	return workq->pending == 0;
+}
+
+int DWC_WORKQ_WAIT_WORK_DONE(dwc_workq_t *workq, int timeout)
+{
+	return DWC_WAITQ_WAIT_TIMEOUT(workq->waitq, work_done, workq, timeout);
+}
+
+dwc_workq_t *DWC_WORKQ_ALLOC(char *name)
+{
+	dwc_workq_t *wq = DWC_ALLOC(sizeof(*wq));
+
+	if (!wq) {
+		DWC_ERROR("Cannot allocate memory for workqueue");
+		return NULL;
+	}
+
+	wq->taskq = taskqueue_create(name, M_NOWAIT, taskqueue_thread_enqueue, &wq->taskq);
+	if (!wq->taskq) {
+		DWC_ERROR("Cannot allocate memory for taskqueue");
+		goto no_taskq;
+	}
+
+	wq->pending = 0;
+
+	wq->lock = DWC_SPINLOCK_ALLOC();
+	if (!wq->lock) {
+		DWC_ERROR("Cannot allocate memory for spinlock");
+		goto no_lock;
+	}
+
+	wq->waitq = DWC_WAITQ_ALLOC();
+	if (!wq->waitq) {
+		DWC_ERROR("Cannot allocate memory for waitqueue");
+		goto no_waitq;
+	}
+
+	taskqueue_start_threads(&wq->taskq, 1, PWAIT, "%s taskq", "dw3tsk");
+
+#ifdef DEBUG
+	DWC_CIRCLEQ_INIT(&wq->entries);
+#endif
+	return wq;
+
+ no_waitq:
+	DWC_SPINLOCK_FREE(wq->lock);
+ no_lock:
+	taskqueue_free(wq->taskq);
+ no_taskq:
+	DWC_FREE(wq);
+
+	return NULL;
+}
+
+void DWC_WORKQ_FREE(dwc_workq_t *wq)
+{
+#ifdef DEBUG
+	dwc_irqflags_t flags;
+
+	DWC_SPINLOCK_IRQSAVE(wq->lock, &flags);
+
+	if (wq->pending != 0) {
+		struct work_container *container;
+
+		DWC_ERROR("Destroying work queue with pending work");
+
+		DWC_CIRCLEQ_FOREACH(container, &wq->entries, entry) {
+			DWC_ERROR("Work %s still pending", container->name);
+		}
+	}
+
+	DWC_SPINUNLOCK_IRQRESTORE(wq->lock, flags);
+#endif
+	DWC_WAITQ_FREE(wq->waitq);
+	DWC_SPINLOCK_FREE(wq->lock);
+	taskqueue_free(wq->taskq);
+	DWC_FREE(wq);
+}
+
+void DWC_WORKQ_SCHEDULE(dwc_workq_t *wq, dwc_work_callback_t cb, void *data,
+			char *format, ...)
+{
+	dwc_irqflags_t flags;
+	work_container_t *container;
+	static char name[128];
+	va_list args;
+
+	va_start(args, format);
+	DWC_VSNPRINTF(name, 128, format, args);
+	va_end(args);
+
+	DWC_SPINLOCK_IRQSAVE(wq->lock, &flags);
+	wq->pending++;
+	DWC_SPINUNLOCK_IRQRESTORE(wq->lock, flags);
+	DWC_WAITQ_TRIGGER(wq->waitq);
+
+	container = DWC_ALLOC_ATOMIC(sizeof(*container));
+	if (!container) {
+		DWC_ERROR("Cannot allocate memory for container");
+		return;
+	}
+
+	container->name = DWC_STRDUP(name);
+	if (!container->name) {
+		DWC_ERROR("Cannot allocate memory for container->name");
+		DWC_FREE(container);
+		return;
+	}
+
+	container->cb = cb;
+	container->data = data;
+	container->wq = wq;
+	container->hz = 0;
+
+	DWC_DEBUG("Queueing work: %s, container=%p", container->name, container);
+
+	TASK_INIT(&container->task, 0, do_work, container);
+
+#ifdef DEBUG
+	DWC_CIRCLEQ_INSERT_TAIL(&wq->entries, container, entry);
+#endif
+	taskqueue_enqueue_fast(wq->taskq, &container->task);
+}
+
+void DWC_WORKQ_SCHEDULE_DELAYED(dwc_workq_t *wq, dwc_work_callback_t cb,
+				void *data, uint32_t time, char *format, ...)
+{
+	dwc_irqflags_t flags;
+	work_container_t *container;
+	static char name[128];
+	struct timeval tv;
+	va_list args;
+
+	va_start(args, format);
+	DWC_VSNPRINTF(name, 128, format, args);
+	va_end(args);
+
+	DWC_SPINLOCK_IRQSAVE(wq->lock, &flags);
+	wq->pending++;
+	DWC_SPINUNLOCK_IRQRESTORE(wq->lock, flags);
+	DWC_WAITQ_TRIGGER(wq->waitq);
+
+	container = DWC_ALLOC_ATOMIC(sizeof(*container));
+	if (!container) {
+		DWC_ERROR("Cannot allocate memory for container");
+		return;
+	}
+
+	container->name = DWC_STRDUP(name);
+	if (!container->name) {
+		DWC_ERROR("Cannot allocate memory for container->name");
+		DWC_FREE(container);
+		return;
+	}
+
+	container->cb = cb;
+	container->data = data;
+	container->wq = wq;
+
+	tv.tv_sec = time / 1000;
+	tv.tv_usec = (time - tv.tv_sec * 1000) * 1000;
+	container->hz = tvtohz(&tv);
+
+	DWC_DEBUG("Queueing work: %s, container=%p", container->name, container);
+
+	TASK_INIT(&container->task, 0, do_work, container);
+
+#ifdef DEBUG
+	DWC_CIRCLEQ_INSERT_TAIL(&wq->entries, container, entry);
+#endif
+	taskqueue_enqueue_fast(wq->taskq, &container->task);
+}
+
+int DWC_WORKQ_PENDING(dwc_workq_t *wq)
+{
+	return wq->pending;
+}
diff -Nur --no-dereference linux-5.10.52-orig/drivers/usb/host/dwc_common_port/dwc_common_linux.c linux-5.10.52-v7l+/drivers/usb/host/dwc_common_port/dwc_common_linux.c
--- linux-5.10.52-orig/drivers/usb/host/dwc_common_port/dwc_common_linux.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/usb/host/dwc_common_port/dwc_common_linux.c	2021-07-25 16:46:28.657910448 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kthread.h>
+
+#ifdef DWC_CCLIB
+# include "dwc_cc.h"
+#endif
+
+#ifdef DWC_CRYPTOLIB
+# include "dwc_modpow.h"
+# include "dwc_dh.h"
+# include "dwc_crypto.h"
+#endif
+
+#ifdef DWC_NOTIFYLIB
+# include "dwc_notifier.h"
+#endif
+
+/* OS-Level Implementations */
+
+/* This is the Linux kernel implementation of the DWC platform library. */
+#include <linux/moduleparam.h>
+#include <linux/ctype.h>
+#include <linux/crypto.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/cdev.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/jiffies.h>
+#include <linux/list.h>
+#include <linux/pci.h>
+#include <linux/random.h>
+#include <linux/scatterlist.h>
+#include <linux/slab.h>
+#include <linux/stat.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/usb.h>
+
+#include <linux/version.h>
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
+# include <linux/usb/gadget.h>
+#else
+# include <linux/usb_gadget.h>
+#endif
+
+#include <asm/io.h>
+#include <asm/page.h>
+#include <asm/uaccess.h>
+#include <asm/unaligned.h>
+
+#include "dwc_os.h"
+#include "dwc_list.h"
+
+
+/* MISC */
+
+void *DWC_MEMSET(void *dest, uint8_t byte, uint32_t size)
+{
+	return memset(dest, byte, size);
+}
+
+void *DWC_MEMCPY(void *dest, void const *src, uint32_t size)
+{
+	return memcpy(dest, src, size);
+}
+
+void *DWC_MEMMOVE(void *dest, void *src, uint32_t size)
+{
+	return memmove(dest, src, size);
+}
+
+int DWC_MEMCMP(void *m1, void *m2, uint32_t size)
+{
+	return memcmp(m1, m2, size);
+}
+
+int DWC_STRNCMP(void *s1, void *s2, uint32_t size)
+{
+	return strncmp(s1, s2, size);
+}
+
+int DWC_STRCMP(void *s1, void *s2)
+{
+	return strcmp(s1, s2);
+}
+
+int DWC_STRLEN(char const *str)
+{
+	return strlen(str);
+}
+
+char *DWC_STRCPY(char *to, char const *from)
+{
+	return strcpy(to, from);
+}
+
+char *DWC_STRDUP(char const *str)
+{
+	int len = DWC_STRLEN(str) + 1;
+	char *new = DWC_ALLOC_ATOMIC(len);
+
+	if (!new) {
+		return NULL;
+	}
+
+	DWC_MEMCPY(new, str, len);
+	return new;
+}
+
+int DWC_ATOI(const char *str, int32_t *value)
+{
+	char *end = NULL;
+
+	*value = simple_strtol(str, &end, 0);
+	if (*end == '\0') {
+		return 0;
+	}
+
+	return -1;
+}
+
+int DWC_ATOUI(const char *str, uint32_t *value)
+{
+	char *end = NULL;
+
+	*value = simple_strtoul(str, &end, 0);
+	if (*end == '\0') {
+		return 0;
+	}
+
+	return -1;
+}
+
+
+#ifdef DWC_UTFLIB
+/* From usbstring.c */
+
+int DWC_UTF8_TO_UTF16LE(uint8_t const *s, uint16_t *cp, unsigned len)
+{
+	int	count = 0;
+	u8	c;
+	u16	uchar;
+
+	/* this insists on correct encodings, though not minimal ones.
+	 * BUT it currently rejects legit 4-byte UTF-8 code points,
+	 * which need surrogate pairs.  (Unicode 3.1 can use them.)
+	 */
+	while (len != 0 && (c = (u8) *s++) != 0) {
+		if (unlikely(c & 0x80)) {
+			// 2-byte sequence:
+			// 00000yyyyyxxxxxx = 110yyyyy 10xxxxxx
+			if ((c & 0xe0) == 0xc0) {
+				uchar = (c & 0x1f) << 6;
+
+				c = (u8) *s++;
+				if ((c & 0xc0) != 0xc0)
+					goto fail;
+				c &= 0x3f;
+				uchar |= c;
+
+			// 3-byte sequence (most CJKV characters):
+			// zzzzyyyyyyxxxxxx = 1110zzzz 10yyyyyy 10xxxxxx
+			} else if ((c & 0xf0) == 0xe0) {
+				uchar = (c & 0x0f) << 12;
+
+				c = (u8) *s++;
+				if ((c & 0xc0) != 0xc0)
+					goto fail;
+				c &= 0x3f;
+				uchar |= c << 6;
+
+				c = (u8) *s++;
+				if ((c & 0xc0) != 0xc0)
+					goto fail;
+				c &= 0x3f;
+				uchar |= c;
+
+				/* no bogus surrogates */
+				if (0xd800 <= uchar && uchar <= 0xdfff)
+					goto fail;
+
+			// 4-byte sequence (surrogate pairs, currently rare):
+			// 11101110wwwwzzzzyy + 110111yyyyxxxxxx
+			//     = 11110uuu 10uuzzzz 10yyyyyy 10xxxxxx
+			// (uuuuu = wwww + 1)
+			// FIXME accept the surrogate code points (only)
+			} else
+				goto fail;
+		} else
+			uchar = c;
+		put_unaligned (cpu_to_le16 (uchar), cp++);
+		count++;
+		len--;
+	}
+	return count;
+fail:
+	return -1;
+}
+#endif	/* DWC_UTFLIB */
+
+
+/* dwc_debug.h */
+
+dwc_bool_t DWC_IN_IRQ(void)
+{
+	return in_irq();
+}
+
+dwc_bool_t DWC_IN_BH(void)
+{
+	return in_softirq();
+}
+
+void DWC_VPRINTF(char *format, va_list args)
+{
+	vprintk(format, args);
+}
+
+int DWC_VSNPRINTF(char *str, int size, char *format, va_list args)
+{
+	return vsnprintf(str, size, format, args);
+}
+
+void DWC_PRINTF(char *format, ...)
+{
+	va_list args;
+
+	va_start(args, format);
+	DWC_VPRINTF(format, args);
+	va_end(args);
+}
+
+int DWC_SPRINTF(char *buffer, char *format, ...)
+{
+	int retval;
+	va_list args;
+
+	va_start(args, format);
+	retval = vsprintf(buffer, format, args);
+	va_end(args);
+	return retval;
+}
+
+int DWC_SNPRINTF(char *buffer, int size, char *format, ...)
+{
+	int retval;
+	va_list args;
+
+	va_start(args, format);
+	retval = vsnprintf(buffer, size, format, args);
+	va_end(args);
+	return retval;
+}
+
+void __DWC_WARN(char *format, ...)
+{
+	va_list args;
+
+	va_start(args, format);
+	DWC_PRINTF(KERN_WARNING);
+	DWC_VPRINTF(format, args);
+	va_end(args);
+}
+
+void __DWC_ERROR(char *format, ...)
+{
+	va_list args;
+
+	va_start(args, format);
+	DWC_PRINTF(KERN_ERR);
+	DWC_VPRINTF(format, args);
+	va_end(args);
+}
+
+void DWC_EXCEPTION(char *format, ...)
+{
+	va_list args;
+
+	va_start(args, format);
+	DWC_PRINTF(KERN_ERR);
+	DWC_VPRINTF(format, args);
+	va_end(args);
+	BUG_ON(1);
+}
+
+#ifdef DEBUG
+void __DWC_DEBUG(char *format, ...)
+{
+	va_list args;
+
+	va_start(args, format);
+	DWC_PRINTF(KERN_DEBUG);
+	DWC_VPRINTF(format, args);
+	va_end(args);
+}
+#endif
+
+
+/* dwc_mem.h */
+
+#if 0
+dwc_pool_t *DWC_DMA_POOL_CREATE(uint32_t size,
+				uint32_t align,
+				uint32_t alloc)
+{
+	struct dma_pool *pool = dma_pool_create("Pool", NULL,
+						size, align, alloc);
+	return (dwc_pool_t *)pool;
+}
+
+void DWC_DMA_POOL_DESTROY(dwc_pool_t *pool)
+{
+	dma_pool_destroy((struct dma_pool *)pool);
+}
+
+void *DWC_DMA_POOL_ALLOC(dwc_pool_t *pool, uint64_t *dma_addr)
+{
+	return dma_pool_alloc((struct dma_pool *)pool, GFP_KERNEL, dma_addr);
+}
+
+void *DWC_DMA_POOL_ZALLOC(dwc_pool_t *pool, uint64_t *dma_addr)
+{
+	void *vaddr = DWC_DMA_POOL_ALLOC(pool, dma_addr);
+	memset(..);
+}
+
+void DWC_DMA_POOL_FREE(dwc_pool_t *pool, void *vaddr, void *daddr)
+{
+	dma_pool_free(pool, vaddr, daddr);
+}
+#endif
+
+void *__DWC_DMA_ALLOC(void *dma_ctx, uint32_t size, dwc_dma_t *dma_addr)
+{
+	return dma_alloc_coherent(dma_ctx, size, dma_addr, GFP_KERNEL | GFP_DMA32);
+}
+
+void *__DWC_DMA_ALLOC_ATOMIC(void *dma_ctx, uint32_t size, dwc_dma_t *dma_addr)
+{
+	return dma_alloc_coherent(dma_ctx, size, dma_addr, GFP_ATOMIC);
+}
+
+void __DWC_DMA_FREE(void *dma_ctx, uint32_t size, void *virt_addr, dwc_dma_t dma_addr)
+{
+	dma_free_coherent(dma_ctx, size, virt_addr, dma_addr);
+}
+
+void *__DWC_ALLOC(void *mem_ctx, uint32_t size)
+{
+	return kzalloc(size, GFP_KERNEL);
+}
+
+void *__DWC_ALLOC_ATOMIC(void *mem_ctx, uint32_t size)
+{
+	return kzalloc(size, GFP_ATOMIC);
+}
+
+void __DWC_FREE(void *mem_ctx, void *addr)
+{
+	kfree(addr);
+}
+
+
+#ifdef DWC_CRYPTOLIB
+/* dwc_crypto.h */
+
+void DWC_RANDOM_BYTES(uint8_t *buffer, uint32_t length)
+{
+	get_random_bytes(buffer, length);
+}
+
+int DWC_AES_CBC(uint8_t *message, uint32_t messagelen, uint8_t *key, uint32_t keylen, uint8_t iv[16], uint8_t *out)
+{
+	struct crypto_blkcipher *tfm;
+	struct blkcipher_desc desc;
+	struct scatterlist sgd;
+	struct scatterlist sgs;
+
+	tfm = crypto_alloc_blkcipher("cbc(aes)", 0, CRYPTO_ALG_ASYNC);
+	if (tfm == NULL) {
+		printk("failed to load transform for aes CBC\n");
+		return -1;
+	}
+
+	crypto_blkcipher_setkey(tfm, key, keylen);
+	crypto_blkcipher_set_iv(tfm, iv, 16);
+
+	sg_init_one(&sgd, out, messagelen);
+	sg_init_one(&sgs, message, messagelen);
+
+	desc.tfm = tfm;
+	desc.flags = 0;
+
+	if (crypto_blkcipher_encrypt(&desc, &sgd, &sgs, messagelen)) {
+		crypto_free_blkcipher(tfm);
+		DWC_ERROR("AES CBC encryption failed");
+		return -1;
+	}
+
+	crypto_free_blkcipher(tfm);
+	return 0;
+}
+
+int DWC_SHA256(uint8_t *message, uint32_t len, uint8_t *out)
+{
+	struct crypto_hash *tfm;
+	struct hash_desc desc;
+	struct scatterlist sg;
+
+	tfm = crypto_alloc_hash("sha256", 0, CRYPTO_ALG_ASYNC);
+	if (IS_ERR(tfm)) {
+		DWC_ERROR("Failed to load transform for sha256: %ld\n", PTR_ERR(tfm));
+		return 0;
+	}
+	desc.tfm = tfm;
+	desc.flags = 0;
+
+	sg_init_one(&sg, message, len);
+	crypto_hash_digest(&desc, &sg, len, out);
+	crypto_free_hash(tfm);
+
+	return 1;
+}
+
+int DWC_HMAC_SHA256(uint8_t *message, uint32_t messagelen,
+		    uint8_t *key, uint32_t keylen, uint8_t *out)
+{
+	struct crypto_hash *tfm;
+	struct hash_desc desc;
+	struct scatterlist sg;
+
+	tfm = crypto_alloc_hash("hmac(sha256)", 0, CRYPTO_ALG_ASYNC);
+	if (IS_ERR(tfm)) {
+		DWC_ERROR("Failed to load transform for hmac(sha256): %ld\n", PTR_ERR(tfm));
+		return 0;
+	}
+	desc.tfm = tfm;
+	desc.flags = 0;
+
+	sg_init_one(&sg, message, messagelen);
+	crypto_hash_setkey(tfm, key, keylen);
+	crypto_hash_digest(&desc, &sg, messagelen, out);
+	crypto_free_hash(tfm);
+
+	return 1;
+}
+#endif	/* DWC_CRYPTOLIB */
+
+
+/* Byte Ordering Conversions */
+
+uint32_t DWC_CPU_TO_LE32(uint32_t *p)
+{
+#ifdef __LITTLE_ENDIAN
+	return *p;
+#else
+	uint8_t *u_p = (uint8_t *)p;
+
+	return (u_p[3] | (u_p[2] << 8) | (u_p[1] << 16) | (u_p[0] << 24));
+#endif
+}
+
+uint32_t DWC_CPU_TO_BE32(uint32_t *p)
+{
+#ifdef __BIG_ENDIAN
+	return *p;
+#else
+	uint8_t *u_p = (uint8_t *)p;
+
+	return (u_p[3] | (u_p[2] << 8) | (u_p[1] << 16) | (u_p[0] << 24));
+#endif
+}
+
+uint32_t DWC_LE32_TO_CPU(uint32_t *p)
+{
+#ifdef __LITTLE_ENDIAN
+	return *p;
+#else
+	uint8_t *u_p = (uint8_t *)p;
+
+	return (u_p[3] | (u_p[2] << 8) | (u_p[1] << 16) | (u_p[0] << 24));
+#endif
+}
+
+uint32_t DWC_BE32_TO_CPU(uint32_t *p)
+{
+#ifdef __BIG_ENDIAN
+	return *p;
+#else
+	uint8_t *u_p = (uint8_t *)p;
+
+	return (u_p[3] | (u_p[2] << 8) | (u_p[1] << 16) | (u_p[0] << 24));
+#endif
+}
+
+uint16_t DWC_CPU_TO_LE16(uint16_t *p)
+{
+#ifdef __LITTLE_ENDIAN
+	return *p;
+#else
+	uint8_t *u_p = (uint8_t *)p;
+	return (u_p[1] | (u_p[0] << 8));
+#endif
+}
+
+uint16_t DWC_CPU_TO_BE16(uint16_t *p)
+{
+#ifdef __BIG_ENDIAN
+	return *p;
+#else
+	uint8_t *u_p = (uint8_t *)p;
+	return (u_p[1] | (u_p[0] << 8));
+#endif
+}
+
+uint16_t DWC_LE16_TO_CPU(uint16_t *p)
+{
+#ifdef __LITTLE_ENDIAN
+	return *p;
+#else
+	uint8_t *u_p = (uint8_t *)p;
+	return (u_p[1] | (u_p[0] << 8));
+#endif
+}
+
+uint16_t DWC_BE16_TO_CPU(uint16_t *p)
+{
+#ifdef __BIG_ENDIAN
+	return *p;
+#else
+	uint8_t *u_p = (uint8_t *)p;
+	return (u_p[1] | (u_p[0] << 8));
+#endif
+}
+
+
+/* Registers */
+
+uint32_t DWC_READ_REG32(uint32_t volatile *reg)
+{
+	return readl(reg);
+}
+
+#if 0
+uint64_t DWC_READ_REG64(uint64_t volatile *reg)
+{
+}
+#endif
+
+void DWC_WRITE_REG32(uint32_t volatile *reg, uint32_t value)
+{
+	writel(value, reg);
+}
+
+#if 0
+void DWC_WRITE_REG64(uint64_t volatile *reg, uint64_t value)
+{
+}
+#endif
+
+void DWC_MODIFY_REG32(uint32_t volatile *reg, uint32_t clear_mask, uint32_t set_mask)
+{
+	writel((readl(reg) & ~clear_mask) | set_mask, reg);
+}
+
+#if 0
+void DWC_MODIFY_REG64(uint64_t volatile *reg, uint64_t clear_mask, uint64_t set_mask)
+{
+}
+#endif
+
+
+/* Locking */
+
+dwc_spinlock_t *DWC_SPINLOCK_ALLOC(void)
+{
+	spinlock_t *sl = (spinlock_t *)1;
+
+#if defined(CONFIG_PREEMPT) || defined(CONFIG_SMP)
+	sl = DWC_ALLOC(sizeof(*sl));
+	if (!sl) {
+		DWC_ERROR("Cannot allocate memory for spinlock\n");
+		return NULL;
+	}
+
+	spin_lock_init(sl);
+#endif
+	return (dwc_spinlock_t *)sl;
+}
+
+void DWC_SPINLOCK_FREE(dwc_spinlock_t *lock)
+{
+#if defined(CONFIG_PREEMPT) || defined(CONFIG_SMP)
+	DWC_FREE(lock);
+#endif
+}
+
+void DWC_SPINLOCK(dwc_spinlock_t *lock)
+{
+#if defined(CONFIG_PREEMPT) || defined(CONFIG_SMP)
+	spin_lock((spinlock_t *)lock);
+#endif
+}
+
+void DWC_SPINUNLOCK(dwc_spinlock_t *lock)
+{
+#if defined(CONFIG_PREEMPT) || defined(CONFIG_SMP)
+	spin_unlock((spinlock_t *)lock);
+#endif
+}
+
+void DWC_SPINLOCK_IRQSAVE(dwc_spinlock_t *lock, dwc_irqflags_t *flags)
+{
+	dwc_irqflags_t f;
+
+#if defined(CONFIG_PREEMPT) || defined(CONFIG_SMP)
+	spin_lock_irqsave((spinlock_t *)lock, f);
+#else
+	local_irq_save(f);
+#endif
+	*flags = f;
+}
+
+void DWC_SPINUNLOCK_IRQRESTORE(dwc_spinlock_t *lock, dwc_irqflags_t flags)
+{
+#if defined(CONFIG_PREEMPT) || defined(CONFIG_SMP)
+	spin_unlock_irqrestore((spinlock_t *)lock, flags);
+#else
+	local_irq_restore(flags);
+#endif
+}
+
+dwc_mutex_t *DWC_MUTEX_ALLOC(void)
+{
+	struct mutex *m;
+	dwc_mutex_t *mutex = (dwc_mutex_t *)DWC_ALLOC(sizeof(struct mutex));
+
+	if (!mutex) {
+		DWC_ERROR("Cannot allocate memory for mutex\n");
+		return NULL;
+	}
+
+	m = (struct mutex *)mutex;
+	mutex_init(m);
+	return mutex;
+}
+
+#if (defined(DWC_LINUX) && defined(CONFIG_DEBUG_MUTEXES))
+#else
+void DWC_MUTEX_FREE(dwc_mutex_t *mutex)
+{
+	mutex_destroy((struct mutex *)mutex);
+	DWC_FREE(mutex);
+}
+#endif
+
+void DWC_MUTEX_LOCK(dwc_mutex_t *mutex)
+{
+	struct mutex *m = (struct mutex *)mutex;
+	mutex_lock(m);
+}
+
+int DWC_MUTEX_TRYLOCK(dwc_mutex_t *mutex)
+{
+	struct mutex *m = (struct mutex *)mutex;
+	return mutex_trylock(m);
+}
+
+void DWC_MUTEX_UNLOCK(dwc_mutex_t *mutex)
+{
+	struct mutex *m = (struct mutex *)mutex;
+	mutex_unlock(m);
+}
+
+
+/* Timing */
+
+void DWC_UDELAY(uint32_t usecs)
+{
+	udelay(usecs);
+}
+
+void DWC_MDELAY(uint32_t msecs)
+{
+	mdelay(msecs);
+}
+
+void DWC_MSLEEP(uint32_t msecs)
+{
+	msleep(msecs);
+}
+
+uint32_t DWC_TIME(void)
+{
+	return jiffies_to_msecs(jiffies);
+}
+
+
+/* Timers */
+
+struct dwc_timer {
+	struct timer_list t;
+	char *name;
+	dwc_timer_callback_t cb;
+	void *data;
+	uint8_t scheduled;
+	dwc_spinlock_t *lock;
+};
+
+static void timer_callback(struct timer_list *tt)
+{
+	dwc_timer_t *timer = from_timer(timer, tt, t);
+	dwc_irqflags_t flags;
+
+	DWC_SPINLOCK_IRQSAVE(timer->lock, &flags);
+	timer->scheduled = 0;
+	DWC_SPINUNLOCK_IRQRESTORE(timer->lock, flags);
+	DWC_DEBUGC("Timer %s callback", timer->name);
+	timer->cb(timer->data);
+}
+
+dwc_timer_t *DWC_TIMER_ALLOC(char *name, dwc_timer_callback_t cb, void *data)
+{
+	dwc_timer_t *t = DWC_ALLOC(sizeof(*t));
+
+	if (!t) {
+		DWC_ERROR("Cannot allocate memory for timer");
+		return NULL;
+	}
+
+	t->name = DWC_STRDUP(name);
+	if (!t->name) {
+		DWC_ERROR("Cannot allocate memory for timer->name");
+		goto no_name;
+	}
+
+#if (defined(DWC_LINUX) && defined(CONFIG_DEBUG_SPINLOCK))
+	DWC_SPINLOCK_ALLOC_LINUX_DEBUG(t->lock);
+#else
+	t->lock = DWC_SPINLOCK_ALLOC();
+#endif
+	if (!t->lock) {
+		DWC_ERROR("Cannot allocate memory for lock");
+		goto no_lock;
+	}
+
+	t->scheduled = 0;
+	t->t.expires = jiffies;
+	timer_setup(&t->t, timer_callback, 0);
+
+	t->cb = cb;
+	t->data = data;
+
+	return t;
+
+ no_lock:
+	DWC_FREE(t->name);
+ no_name:
+	DWC_FREE(t);
+	return NULL;
+}
+
+void DWC_TIMER_FREE(dwc_timer_t *timer)
+{
+	dwc_irqflags_t flags;
+
+	DWC_SPINLOCK_IRQSAVE(timer->lock, &flags);
+
+	if (timer->scheduled) {
+		del_timer(&timer->t);
+		timer->scheduled = 0;
+	}
+
+	DWC_SPINUNLOCK_IRQRESTORE(timer->lock, flags);
+	DWC_SPINLOCK_FREE(timer->lock);
+	DWC_FREE(timer->name);
+	DWC_FREE(timer);
+}
+
+void DWC_TIMER_SCHEDULE(dwc_timer_t *timer, uint32_t time)
+{
+	dwc_irqflags_t flags;
+
+	DWC_SPINLOCK_IRQSAVE(timer->lock, &flags);
+
+	if (!timer->scheduled) {
+		timer->scheduled = 1;
+		DWC_DEBUGC("Scheduling timer %s to expire in +%d msec", timer->name, time);
+		timer->t.expires = jiffies + msecs_to_jiffies(time);
+		add_timer(&timer->t);
+	} else {
+		DWC_DEBUGC("Modifying timer %s to expire in +%d msec", timer->name, time);
+		mod_timer(&timer->t, jiffies + msecs_to_jiffies(time));
+	}
+
+	DWC_SPINUNLOCK_IRQRESTORE(timer->lock, flags);
+}
+
+void DWC_TIMER_CANCEL(dwc_timer_t *timer)
+{
+	del_timer(&timer->t);
+}
+
+
+/* Wait Queues */
+
+struct dwc_waitq {
+	wait_queue_head_t queue;
+	int abort;
+};
+
+dwc_waitq_t *DWC_WAITQ_ALLOC(void)
+{
+	dwc_waitq_t *wq = DWC_ALLOC(sizeof(*wq));
+
+	if (!wq) {
+		DWC_ERROR("Cannot allocate memory for waitqueue\n");
+		return NULL;
+	}
+
+	init_waitqueue_head(&wq->queue);
+	wq->abort = 0;
+	return wq;
+}
+
+void DWC_WAITQ_FREE(dwc_waitq_t *wq)
+{
+	DWC_FREE(wq);
+}
+
+int32_t DWC_WAITQ_WAIT(dwc_waitq_t *wq, dwc_waitq_condition_t cond, void *data)
+{
+	int result = wait_event_interruptible(wq->queue,
+					      cond(data) || wq->abort);
+	if (result == -ERESTARTSYS) {
+		wq->abort = 0;
+		return -DWC_E_RESTART;
+	}
+
+	if (wq->abort == 1) {
+		wq->abort = 0;
+		return -DWC_E_ABORT;
+	}
+
+	wq->abort = 0;
+
+	if (result == 0) {
+		return 0;
+	}
+
+	return -DWC_E_UNKNOWN;
+}
+
+int32_t DWC_WAITQ_WAIT_TIMEOUT(dwc_waitq_t *wq, dwc_waitq_condition_t cond,
+			       void *data, int32_t msecs)
+{
+	int32_t tmsecs;
+	int result = wait_event_interruptible_timeout(wq->queue,
+						      cond(data) || wq->abort,
+						      msecs_to_jiffies(msecs));
+	if (result == -ERESTARTSYS) {
+		wq->abort = 0;
+		return -DWC_E_RESTART;
+	}
+
+	if (wq->abort == 1) {
+		wq->abort = 0;
+		return -DWC_E_ABORT;
+	}
+
+	wq->abort = 0;
+
+	if (result > 0) {
+		tmsecs = jiffies_to_msecs(result);
+		if (!tmsecs) {
+			return 1;
+		}
+
+		return tmsecs;
+	}
+
+	if (result == 0) {
+		return -DWC_E_TIMEOUT;
+	}
+
+	return -DWC_E_UNKNOWN;
+}
+
+void DWC_WAITQ_TRIGGER(dwc_waitq_t *wq)
+{
+	wq->abort = 0;
+	wake_up_interruptible(&wq->queue);
+}
+
+void DWC_WAITQ_ABORT(dwc_waitq_t *wq)
+{
+	wq->abort = 1;
+	wake_up_interruptible(&wq->queue);
+}
+
+
+/* Threading */
+
+dwc_thread_t *DWC_THREAD_RUN(dwc_thread_function_t func, char *name, void *data)
+{
+	struct task_struct *thread = kthread_run(func, data, name);
+
+	if (thread == ERR_PTR(-ENOMEM)) {
+		return NULL;
+	}
+
+	return (dwc_thread_t *)thread;
+}
+
+int DWC_THREAD_STOP(dwc_thread_t *thread)
+{
+	return kthread_stop((struct task_struct *)thread);
+}
+
+dwc_bool_t DWC_THREAD_SHOULD_STOP(void)
+{
+	return kthread_should_stop();
+}
+
+
+/* tasklets
+ - run in interrupt context (cannot sleep)
+ - each tasklet runs on a single CPU
+ - different tasklets can be running simultaneously on different CPUs
+ */
+struct dwc_tasklet {
+	struct tasklet_struct t;
+	dwc_tasklet_callback_t cb;
+	void *data;
+};
+
+static void tasklet_callback(unsigned long data)
+{
+	dwc_tasklet_t *t = (dwc_tasklet_t *)data;
+	t->cb(t->data);
+}
+
+dwc_tasklet_t *DWC_TASK_ALLOC(char *name, dwc_tasklet_callback_t cb, void *data)
+{
+	dwc_tasklet_t *t = DWC_ALLOC(sizeof(*t));
+
+	if (t) {
+		t->cb = cb;
+		t->data = data;
+		tasklet_init(&t->t, tasklet_callback, (unsigned long)t);
+	} else {
+		DWC_ERROR("Cannot allocate memory for tasklet\n");
+	}
+
+	return t;
+}
+
+void DWC_TASK_FREE(dwc_tasklet_t *task)
+{
+	DWC_FREE(task);
+}
+
+void DWC_TASK_SCHEDULE(dwc_tasklet_t *task)
+{
+	tasklet_schedule(&task->t);
+}
+
+void DWC_TASK_HI_SCHEDULE(dwc_tasklet_t *task)
+{
+	tasklet_hi_schedule(&task->t);
+}
+
+
+/* workqueues
+ - run in process context (can sleep)
+ */
+typedef struct work_container {
+	dwc_work_callback_t cb;
+	void *data;
+	dwc_workq_t *wq;
+	char *name;
+
+#ifdef DEBUG
+	DWC_CIRCLEQ_ENTRY(work_container) entry;
+#endif
+	struct delayed_work work;
+} work_container_t;
+
+#ifdef DEBUG
+DWC_CIRCLEQ_HEAD(work_container_queue, work_container);
+#endif
+
+struct dwc_workq {
+	struct workqueue_struct *wq;
+	dwc_spinlock_t *lock;
+	dwc_waitq_t *waitq;
+	int pending;
+
+#ifdef DEBUG
+	struct work_container_queue entries;
+#endif
+};
+
+static void do_work(struct work_struct *work)
+{
+	dwc_irqflags_t flags;
+	struct delayed_work *dw = container_of(work, struct delayed_work, work);
+	work_container_t *container = container_of(dw, struct work_container, work);
+	dwc_workq_t *wq = container->wq;
+
+	container->cb(container->data);
+
+#ifdef DEBUG
+	DWC_CIRCLEQ_REMOVE(&wq->entries, container, entry);
+#endif
+	DWC_DEBUGC("Work done: %s, container=%p", container->name, container);
+	if (container->name) {
+		DWC_FREE(container->name);
+	}
+	DWC_FREE(container);
+
+	DWC_SPINLOCK_IRQSAVE(wq->lock, &flags);
+	wq->pending--;
+	DWC_SPINUNLOCK_IRQRESTORE(wq->lock, flags);
+	DWC_WAITQ_TRIGGER(wq->waitq);
+}
+
+static int work_done(void *data)
+{
+	dwc_workq_t *workq = (dwc_workq_t *)data;
+	return workq->pending == 0;
+}
+
+int DWC_WORKQ_WAIT_WORK_DONE(dwc_workq_t *workq, int timeout)
+{
+	return DWC_WAITQ_WAIT_TIMEOUT(workq->waitq, work_done, workq, timeout);
+}
+
+dwc_workq_t *DWC_WORKQ_ALLOC(char *name)
+{
+	dwc_workq_t *wq = DWC_ALLOC(sizeof(*wq));
+
+	if (!wq) {
+		return NULL;
+	}
+
+	wq->wq = create_singlethread_workqueue(name);
+	if (!wq->wq) {
+		goto no_wq;
+	}
+
+	wq->pending = 0;
+
+#if (defined(DWC_LINUX) && defined(CONFIG_DEBUG_SPINLOCK))
+	DWC_SPINLOCK_ALLOC_LINUX_DEBUG(wq->lock);
+#else
+	wq->lock = DWC_SPINLOCK_ALLOC();
+#endif
+	if (!wq->lock) {
+		goto no_lock;
+	}
+
+	wq->waitq = DWC_WAITQ_ALLOC();
+	if (!wq->waitq) {
+		goto no_waitq;
+	}
+
+#ifdef DEBUG
+	DWC_CIRCLEQ_INIT(&wq->entries);
+#endif
+	return wq;
+
+ no_waitq:
+	DWC_SPINLOCK_FREE(wq->lock);
+ no_lock:
+	destroy_workqueue(wq->wq);
+ no_wq:
+	DWC_FREE(wq);
+
+	return NULL;
+}
+
+void DWC_WORKQ_FREE(dwc_workq_t *wq)
+{
+#ifdef DEBUG
+	if (wq->pending != 0) {
+		struct work_container *wc;
+		DWC_ERROR("Destroying work queue with pending work");
+		DWC_CIRCLEQ_FOREACH(wc, &wq->entries, entry) {
+			DWC_ERROR("Work %s still pending", wc->name);
+		}
+	}
+#endif
+	destroy_workqueue(wq->wq);
+	DWC_SPINLOCK_FREE(wq->lock);
+	DWC_WAITQ_FREE(wq->waitq);
+	DWC_FREE(wq);
+}
+
+void DWC_WORKQ_SCHEDULE(dwc_workq_t *wq, dwc_work_callback_t cb, void *data,
+			char *format, ...)
+{
+	dwc_irqflags_t flags;
+	work_container_t *container;
+	static char name[128];
+	va_list args;
+
+	va_start(args, format);
+	DWC_VSNPRINTF(name, 128, format, args);
+	va_end(args);
+
+	DWC_SPINLOCK_IRQSAVE(wq->lock, &flags);
+	wq->pending++;
+	DWC_SPINUNLOCK_IRQRESTORE(wq->lock, flags);
+	DWC_WAITQ_TRIGGER(wq->waitq);
+
+	container = DWC_ALLOC_ATOMIC(sizeof(*container));
+	if (!container) {
+		DWC_ERROR("Cannot allocate memory for container\n");
+		return;
+	}
+
+	container->name = DWC_STRDUP(name);
+	if (!container->name) {
+		DWC_ERROR("Cannot allocate memory for container->name\n");
+		DWC_FREE(container);
+		return;
+	}
+
+	container->cb = cb;
+	container->data = data;
+	container->wq = wq;
+	DWC_DEBUGC("Queueing work: %s, container=%p", container->name, container);
+	INIT_WORK(&container->work.work, do_work);
+
+#ifdef DEBUG
+	DWC_CIRCLEQ_INSERT_TAIL(&wq->entries, container, entry);
+#endif
+	queue_work(wq->wq, &container->work.work);
+}
+
+void DWC_WORKQ_SCHEDULE_DELAYED(dwc_workq_t *wq, dwc_work_callback_t cb,
+				void *data, uint32_t time, char *format, ...)
+{
+	dwc_irqflags_t flags;
+	work_container_t *container;
+	static char name[128];
+	va_list args;
+
+	va_start(args, format);
+	DWC_VSNPRINTF(name, 128, format, args);
+	va_end(args);
+
+	DWC_SPINLOCK_IRQSAVE(wq->lock, &flags);
+	wq->pending++;
+	DWC_SPINUNLOCK_IRQRESTORE(wq->lock, flags);
+	DWC_WAITQ_TRIGGER(wq->waitq);
+
+	container = DWC_ALLOC_ATOMIC(sizeof(*container));
+	if (!container) {
+		DWC_ERROR("Cannot allocate memory for container\n");
+		return;
+	}
+
+	container->name = DWC_STRDUP(name);
+	if (!container->name) {
+		DWC_ERROR("Cannot allocate memory for container->name\n");
+		DWC_FREE(container);
+		return;
+	}
+
+	container->cb = cb;
+	container->data = data;
+	container->wq = wq;
+	DWC_DEBUGC("Queueing work: %s, container=%p", container->name, container);
+	INIT_DELAYED_WORK(&container->work, do_work);
+
+#ifdef DEBUG
+	DWC_CIRCLEQ_INSERT_TAIL(&wq->entries, container, entry);
+#endif
+	queue_delayed_work(wq->wq, &container->work, msecs_to_jiffies(time));
+}
+
+int DWC_WORKQ_PENDING(dwc_workq_t *wq)
+{
+	return wq->pending;
+}
+
+
+#ifdef DWC_LIBMODULE
+
+#ifdef DWC_CCLIB
+/* CC */
+EXPORT_SYMBOL(dwc_cc_if_alloc);
+EXPORT_SYMBOL(dwc_cc_if_free);
+EXPORT_SYMBOL(dwc_cc_clear);
+EXPORT_SYMBOL(dwc_cc_add);
+EXPORT_SYMBOL(dwc_cc_remove);
+EXPORT_SYMBOL(dwc_cc_change);
+EXPORT_SYMBOL(dwc_cc_data_for_save);
+EXPORT_SYMBOL(dwc_cc_restore_from_data);
+EXPORT_SYMBOL(dwc_cc_match_chid);
+EXPORT_SYMBOL(dwc_cc_match_cdid);
+EXPORT_SYMBOL(dwc_cc_ck);
+EXPORT_SYMBOL(dwc_cc_chid);
+EXPORT_SYMBOL(dwc_cc_cdid);
+EXPORT_SYMBOL(dwc_cc_name);
+#endif	/* DWC_CCLIB */
+
+#ifdef DWC_CRYPTOLIB
+# ifndef CONFIG_MACH_IPMATE
+/* Modpow */
+EXPORT_SYMBOL(dwc_modpow);
+
+/* DH */
+EXPORT_SYMBOL(dwc_dh_modpow);
+EXPORT_SYMBOL(dwc_dh_derive_keys);
+EXPORT_SYMBOL(dwc_dh_pk);
+# endif	/* CONFIG_MACH_IPMATE */
+
+/* Crypto */
+EXPORT_SYMBOL(dwc_wusb_aes_encrypt);
+EXPORT_SYMBOL(dwc_wusb_cmf);
+EXPORT_SYMBOL(dwc_wusb_prf);
+EXPORT_SYMBOL(dwc_wusb_fill_ccm_nonce);
+EXPORT_SYMBOL(dwc_wusb_gen_nonce);
+EXPORT_SYMBOL(dwc_wusb_gen_key);
+EXPORT_SYMBOL(dwc_wusb_gen_mic);
+#endif	/* DWC_CRYPTOLIB */
+
+/* Notification */
+#ifdef DWC_NOTIFYLIB
+EXPORT_SYMBOL(dwc_alloc_notification_manager);
+EXPORT_SYMBOL(dwc_free_notification_manager);
+EXPORT_SYMBOL(dwc_register_notifier);
+EXPORT_SYMBOL(dwc_unregister_notifier);
+EXPORT_SYMBOL(dwc_add_observer);
+EXPORT_SYMBOL(dwc_remove_observer);
+EXPORT_SYMBOL(dwc_notify);
+#endif
+
+/* Memory Debugging Routines */
+#ifdef DWC_DEBUG_MEMORY
+EXPORT_SYMBOL(dwc_alloc_debug);
+EXPORT_SYMBOL(dwc_alloc_atomic_debug);
+EXPORT_SYMBOL(dwc_free_debug);
+EXPORT_SYMBOL(dwc_dma_alloc_debug);
+EXPORT_SYMBOL(dwc_dma_free_debug);
+#endif
+
+EXPORT_SYMBOL(DWC_MEMSET);
+EXPORT_SYMBOL(DWC_MEMCPY);
+EXPORT_SYMBOL(DWC_MEMMOVE);
+EXPORT_SYMBOL(DWC_MEMCMP);
+EXPORT_SYMBOL(DWC_STRNCMP);
+EXPORT_SYMBOL(DWC_STRCMP);
+EXPORT_SYMBOL(DWC_STRLEN);
+EXPORT_SYMBOL(DWC_STRCPY);
+EXPORT_SYMBOL(DWC_STRDUP);
+EXPORT_SYMBOL(DWC_ATOI);
+EXPORT_SYMBOL(DWC_ATOUI);
+
+#ifdef DWC_UTFLIB
+EXPORT_SYMBOL(DWC_UTF8_TO_UTF16LE);
+#endif	/* DWC_UTFLIB */
+
+EXPORT_SYMBOL(DWC_IN_IRQ);
+EXPORT_SYMBOL(DWC_IN_BH);
+EXPORT_SYMBOL(DWC_VPRINTF);
+EXPORT_SYMBOL(DWC_VSNPRINTF);
+EXPORT_SYMBOL(DWC_PRINTF);
+EXPORT_SYMBOL(DWC_SPRINTF);
+EXPORT_SYMBOL(DWC_SNPRINTF);
+EXPORT_SYMBOL(__DWC_WARN);
+EXPORT_SYMBOL(__DWC_ERROR);
+EXPORT_SYMBOL(DWC_EXCEPTION);
+
+#ifdef DEBUG
+EXPORT_SYMBOL(__DWC_DEBUG);
+#endif
+
+EXPORT_SYMBOL(__DWC_DMA_ALLOC);
+EXPORT_SYMBOL(__DWC_DMA_ALLOC_ATOMIC);
+EXPORT_SYMBOL(__DWC_DMA_FREE);
+EXPORT_SYMBOL(__DWC_ALLOC);
+EXPORT_SYMBOL(__DWC_ALLOC_ATOMIC);
+EXPORT_SYMBOL(__DWC_FREE);
+
+#ifdef DWC_CRYPTOLIB
+EXPORT_SYMBOL(DWC_RANDOM_BYTES);
+EXPORT_SYMBOL(DWC_AES_CBC);
+EXPORT_SYMBOL(DWC_SHA256);
+EXPORT_SYMBOL(DWC_HMAC_SHA256);
+#endif
+
+EXPORT_SYMBOL(DWC_CPU_TO_LE32);
+EXPORT_SYMBOL(DWC_CPU_TO_BE32);
+EXPORT_SYMBOL(DWC_LE32_TO_CPU);
+EXPORT_SYMBOL(DWC_BE32_TO_CPU);
+EXPORT_SYMBOL(DWC_CPU_TO_LE16);
+EXPORT_SYMBOL(DWC_CPU_TO_BE16);
+EXPORT_SYMBOL(DWC_LE16_TO_CPU);
+EXPORT_SYMBOL(DWC_BE16_TO_CPU);
+EXPORT_SYMBOL(DWC_READ_REG32);
+EXPORT_SYMBOL(DWC_WRITE_REG32);
+EXPORT_SYMBOL(DWC_MODIFY_REG32);
+
+#if 0
+EXPORT_SYMBOL(DWC_READ_REG64);
+EXPORT_SYMBOL(DWC_WRITE_REG64);
+EXPORT_SYMBOL(DWC_MODIFY_REG64);
+#endif
+
+EXPORT_SYMBOL(DWC_SPINLOCK_ALLOC);
+EXPORT_SYMBOL(DWC_SPINLOCK_FREE);
+EXPORT_SYMBOL(DWC_SPINLOCK);
+EXPORT_SYMBOL(DWC_SPINUNLOCK);
+EXPORT_SYMBOL(DWC_SPINLOCK_IRQSAVE);
+EXPORT_SYMBOL(DWC_SPINUNLOCK_IRQRESTORE);
+EXPORT_SYMBOL(DWC_MUTEX_ALLOC);
+
+#if (!defined(DWC_LINUX) || !defined(CONFIG_DEBUG_MUTEXES))
+EXPORT_SYMBOL(DWC_MUTEX_FREE);
+#endif
+
+EXPORT_SYMBOL(DWC_MUTEX_LOCK);
+EXPORT_SYMBOL(DWC_MUTEX_TRYLOCK);
+EXPORT_SYMBOL(DWC_MUTEX_UNLOCK);
+EXPORT_SYMBOL(DWC_UDELAY);
+EXPORT_SYMBOL(DWC_MDELAY);
+EXPORT_SYMBOL(DWC_MSLEEP);
+EXPORT_SYMBOL(DWC_TIME);
+EXPORT_SYMBOL(DWC_TIMER_ALLOC);
+EXPORT_SYMBOL(DWC_TIMER_FREE);
+EXPORT_SYMBOL(DWC_TIMER_SCHEDULE);
+EXPORT_SYMBOL(DWC_TIMER_CANCEL);
+EXPORT_SYMBOL(DWC_WAITQ_ALLOC);
+EXPORT_SYMBOL(DWC_WAITQ_FREE);
+EXPORT_SYMBOL(DWC_WAITQ_WAIT);
+EXPORT_SYMBOL(DWC_WAITQ_WAIT_TIMEOUT);
+EXPORT_SYMBOL(DWC_WAITQ_TRIGGER);
+EXPORT_SYMBOL(DWC_WAITQ_ABORT);
+EXPORT_SYMBOL(DWC_THREAD_RUN);
+EXPORT_SYMBOL(DWC_THREAD_STOP);
+EXPORT_SYMBOL(DWC_THREAD_SHOULD_STOP);
+EXPORT_SYMBOL(DWC_TASK_ALLOC);
+EXPORT_SYMBOL(DWC_TASK_FREE);
+EXPORT_SYMBOL(DWC_TASK_SCHEDULE);
+EXPORT_SYMBOL(DWC_WORKQ_WAIT_WORK_DONE);
+EXPORT_SYMBOL(DWC_WORKQ_ALLOC);
+EXPORT_SYMBOL(DWC_WORKQ_FREE);
+EXPORT_SYMBOL(DWC_WORKQ_SCHEDULE);
+EXPORT_SYMBOL(DWC_WORKQ_SCHEDULE_DELAYED);
+EXPORT_SYMBOL(DWC_WORKQ_PENDING);
+
+static int dwc_common_port_init_module(void)
+{
+	int result = 0;
+
+	printk(KERN_DEBUG "Module dwc_common_port init\n" );
+
+#ifdef DWC_DEBUG_MEMORY
+	result = dwc_memory_debug_start(NULL);
+	if (result) {
+		printk(KERN_ERR
+		       "dwc_memory_debug_start() failed with error %d\n",
+		       result);
+		return result;
+	}
+#endif
+
+#ifdef DWC_NOTIFYLIB
+	result = dwc_alloc_notification_manager(NULL, NULL);
+	if (result) {
+		printk(KERN_ERR
+		       "dwc_alloc_notification_manager() failed with error %d\n",
+		       result);
+		return result;
+	}
+#endif
+	return result;
+}
+
+static void dwc_common_port_exit_module(void)
+{
+	printk(KERN_DEBUG "Module dwc_common_port exit\n" );
+
+#ifdef DWC_NOTIFYLIB
+	dwc_free_notification_manager();
+#endif
+
+#ifdef DWC_DEBUG_MEMORY
+	dwc_memory_debug_stop();
+#endif
+}
+
+module_init(dwc_common_port_init_module);
+module_exit(dwc_common_port_exit_module);
+
+MODULE_DESCRIPTION("DWC Common Library - Portable version");
+MODULE_AUTHOR("Synopsys Inc.");
+MODULE_LICENSE ("GPL");
+
+#endif	/* DWC_LIBMODULE */
diff -Nur --no-dereference linux-5.10.52-orig/drivers/usb/host/dwc_common_port/dwc_common_nbsd.c linux-5.10.52-v7l+/drivers/usb/host/dwc_common_port/dwc_common_nbsd.c
--- linux-5.10.52-orig/drivers/usb/host/dwc_common_port/dwc_common_nbsd.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/usb/host/dwc_common_port/dwc_common_nbsd.c	2021-07-25 16:46:28.657910448 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+#include "dwc_os.h"
+#include "dwc_list.h"
+
+#ifdef DWC_CCLIB
+# include "dwc_cc.h"
+#endif
+
+#ifdef DWC_CRYPTOLIB
+# include "dwc_modpow.h"
+# include "dwc_dh.h"
+# include "dwc_crypto.h"
+#endif
+
+#ifdef DWC_NOTIFYLIB
+# include "dwc_notifier.h"
+#endif
+
+/* OS-Level Implementations */
+
+/* This is the NetBSD 4.0.1 kernel implementation of the DWC platform library. */
+
+
+/* MISC */
+
+void *DWC_MEMSET(void *dest, uint8_t byte, uint32_t size)
+{
+	return memset(dest, byte, size);
+}
+
+void *DWC_MEMCPY(void *dest, void const *src, uint32_t size)
+{
+	return memcpy(dest, src, size);
+}
+
+void *DWC_MEMMOVE(void *dest, void *src, uint32_t size)
+{
+	bcopy(src, dest, size);
+	return dest;
+}
+
+int DWC_MEMCMP(void *m1, void *m2, uint32_t size)
+{
+	return memcmp(m1, m2, size);
+}
+
+int DWC_STRNCMP(void *s1, void *s2, uint32_t size)
+{
+	return strncmp(s1, s2, size);
+}
+
+int DWC_STRCMP(void *s1, void *s2)
+{
+	return strcmp(s1, s2);
+}
+
+int DWC_STRLEN(char const *str)
+{
+	return strlen(str);
+}
+
+char *DWC_STRCPY(char *to, char const *from)
+{
+	return strcpy(to, from);
+}
+
+char *DWC_STRDUP(char const *str)
+{
+	int len = DWC_STRLEN(str) + 1;
+	char *new = DWC_ALLOC_ATOMIC(len);
+
+	if (!new) {
+		return NULL;
+	}
+
+	DWC_MEMCPY(new, str, len);
+	return new;
+}
+
+int DWC_ATOI(char *str, int32_t *value)
+{
+	char *end = NULL;
+
+	/* NetBSD doesn't have 'strtol' in the kernel, but 'strtoul'
+	 * should be equivalent on 2's complement machines
+	 */
+	*value = strtoul(str, &end, 0);
+	if (*end == '\0') {
+		return 0;
+	}
+
+	return -1;
+}
+
+int DWC_ATOUI(char *str, uint32_t *value)
+{
+	char *end = NULL;
+
+	*value = strtoul(str, &end, 0);
+	if (*end == '\0') {
+		return 0;
+	}
+
+	return -1;
+}
+
+
+#ifdef DWC_UTFLIB
+/* From usbstring.c */
+
+int DWC_UTF8_TO_UTF16LE(uint8_t const *s, uint16_t *cp, unsigned len)
+{
+	int	count = 0;
+	u8	c;
+	u16	uchar;
+
+	/* this insists on correct encodings, though not minimal ones.
+	 * BUT it currently rejects legit 4-byte UTF-8 code points,
+	 * which need surrogate pairs.  (Unicode 3.1 can use them.)
+	 */
+	while (len != 0 && (c = (u8) *s++) != 0) {
+		if (unlikely(c & 0x80)) {
+			// 2-byte sequence:
+			// 00000yyyyyxxxxxx = 110yyyyy 10xxxxxx
+			if ((c & 0xe0) == 0xc0) {
+				uchar = (c & 0x1f) << 6;
+
+				c = (u8) *s++;
+				if ((c & 0xc0) != 0xc0)
+					goto fail;
+				c &= 0x3f;
+				uchar |= c;
+
+			// 3-byte sequence (most CJKV characters):
+			// zzzzyyyyyyxxxxxx = 1110zzzz 10yyyyyy 10xxxxxx
+			} else if ((c & 0xf0) == 0xe0) {
+				uchar = (c & 0x0f) << 12;
+
+				c = (u8) *s++;
+				if ((c & 0xc0) != 0xc0)
+					goto fail;
+				c &= 0x3f;
+				uchar |= c << 6;
+
+				c = (u8) *s++;
+				if ((c & 0xc0) != 0xc0)
+					goto fail;
+				c &= 0x3f;
+				uchar |= c;
+
+				/* no bogus surrogates */
+				if (0xd800 <= uchar && uchar <= 0xdfff)
+					goto fail;
+
+			// 4-byte sequence (surrogate pairs, currently rare):
+			// 11101110wwwwzzzzyy + 110111yyyyxxxxxx
+			//     = 11110uuu 10uuzzzz 10yyyyyy 10xxxxxx
+			// (uuuuu = wwww + 1)
+			// FIXME accept the surrogate code points (only)
+			} else
+				goto fail;
+		} else
+			uchar = c;
+		put_unaligned (cpu_to_le16 (uchar), cp++);
+		count++;
+		len--;
+	}
+	return count;
+fail:
+	return -1;
+}
+
+#endif	/* DWC_UTFLIB */
+
+
+/* dwc_debug.h */
+
+dwc_bool_t DWC_IN_IRQ(void)
+{
+//	return in_irq();
+	return 0;
+}
+
+dwc_bool_t DWC_IN_BH(void)
+{
+//	return in_softirq();
+	return 0;
+}
+
+void DWC_VPRINTF(char *format, va_list args)
+{
+	vprintf(format, args);
+}
+
+int DWC_VSNPRINTF(char *str, int size, char *format, va_list args)
+{
+	return vsnprintf(str, size, format, args);
+}
+
+void DWC_PRINTF(char *format, ...)
+{
+	va_list args;
+
+	va_start(args, format);
+	DWC_VPRINTF(format, args);
+	va_end(args);
+}
+
+int DWC_SPRINTF(char *buffer, char *format, ...)
+{
+	int retval;
+	va_list args;
+
+	va_start(args, format);
+	retval = vsprintf(buffer, format, args);
+	va_end(args);
+	return retval;
+}
+
+int DWC_SNPRINTF(char *buffer, int size, char *format, ...)
+{
+	int retval;
+	va_list args;
+
+	va_start(args, format);
+	retval = vsnprintf(buffer, size, format, args);
+	va_end(args);
+	return retval;
+}
+
+void __DWC_WARN(char *format, ...)
+{
+	va_list args;
+
+	va_start(args, format);
+	DWC_VPRINTF(format, args);
+	va_end(args);
+}
+
+void __DWC_ERROR(char *format, ...)
+{
+	va_list args;
+
+	va_start(args, format);
+	DWC_VPRINTF(format, args);
+	va_end(args);
+}
+
+void DWC_EXCEPTION(char *format, ...)
+{
+	va_list args;
+
+	va_start(args, format);
+	DWC_VPRINTF(format, args);
+	va_end(args);
+//	BUG_ON(1);	???
+}
+
+#ifdef DEBUG
+void __DWC_DEBUG(char *format, ...)
+{
+	va_list args;
+
+	va_start(args, format);
+	DWC_VPRINTF(format, args);
+	va_end(args);
+}
+#endif
+
+
+/* dwc_mem.h */
+
+#if 0
+dwc_pool_t *DWC_DMA_POOL_CREATE(uint32_t size,
+				uint32_t align,
+				uint32_t alloc)
+{
+	struct dma_pool *pool = dma_pool_create("Pool", NULL,
+						size, align, alloc);
+	return (dwc_pool_t *)pool;
+}
+
+void DWC_DMA_POOL_DESTROY(dwc_pool_t *pool)
+{
+	dma_pool_destroy((struct dma_pool *)pool);
+}
+
+void *DWC_DMA_POOL_ALLOC(dwc_pool_t *pool, uint64_t *dma_addr)
+{
+//	return dma_pool_alloc((struct dma_pool *)pool, GFP_KERNEL, dma_addr);
+	return dma_pool_alloc((struct dma_pool *)pool, M_WAITOK, dma_addr);
+}
+
+void *DWC_DMA_POOL_ZALLOC(dwc_pool_t *pool, uint64_t *dma_addr)
+{
+	void *vaddr = DWC_DMA_POOL_ALLOC(pool, dma_addr);
+	memset(..);
+}
+
+void DWC_DMA_POOL_FREE(dwc_pool_t *pool, void *vaddr, void *daddr)
+{
+	dma_pool_free(pool, vaddr, daddr);
+}
+#endif
+
+void *__DWC_DMA_ALLOC(void *dma_ctx, uint32_t size, dwc_dma_t *dma_addr)
+{
+	dwc_dmactx_t *dma = (dwc_dmactx_t *)dma_ctx;
+	int error;
+
+	error = bus_dmamem_alloc(dma->dma_tag, size, 1, size, dma->segs,
+				 sizeof(dma->segs) / sizeof(dma->segs[0]),
+				 &dma->nsegs, BUS_DMA_NOWAIT);
+	if (error) {
+		printf("%s: bus_dmamem_alloc(%ju) failed: %d\n", __func__,
+		       (uintmax_t)size, error);
+		goto fail_0;
+	}
+
+	error = bus_dmamem_map(dma->dma_tag, dma->segs, dma->nsegs, size,
+			       (caddr_t *)&dma->dma_vaddr,
+			       BUS_DMA_NOWAIT | BUS_DMA_COHERENT);
+	if (error) {
+		printf("%s: bus_dmamem_map failed: %d\n", __func__, error);
+		goto fail_1;
+	}
+
+	error = bus_dmamap_create(dma->dma_tag, size, 1, size, 0,
+				  BUS_DMA_NOWAIT, &dma->dma_map);
+	if (error) {
+		printf("%s: bus_dmamap_create failed: %d\n", __func__, error);
+		goto fail_2;
+	}
+
+	error = bus_dmamap_load(dma->dma_tag, dma->dma_map, dma->dma_vaddr,
+				size, NULL, BUS_DMA_NOWAIT);
+	if (error) {
+		printf("%s: bus_dmamap_load failed: %d\n", __func__, error);
+		goto fail_3;
+	}
+
+	dma->dma_paddr = (bus_addr_t)dma->segs[0].ds_addr;
+	*dma_addr = dma->dma_paddr;
+	return dma->dma_vaddr;
+
+fail_3:
+	bus_dmamap_destroy(dma->dma_tag, dma->dma_map);
+fail_2:
+	bus_dmamem_unmap(dma->dma_tag, dma->dma_vaddr, size);
+fail_1:
+	bus_dmamem_free(dma->dma_tag, dma->segs, dma->nsegs);
+fail_0:
+	dma->dma_map = NULL;
+	dma->dma_vaddr = NULL;
+	dma->nsegs = 0;
+
+	return NULL;
+}
+
+void __DWC_DMA_FREE(void *dma_ctx, uint32_t size, void *virt_addr, dwc_dma_t dma_addr)
+{
+	dwc_dmactx_t *dma = (dwc_dmactx_t *)dma_ctx;
+
+	if (dma->dma_map != NULL) {
+		bus_dmamap_sync(dma->dma_tag, dma->dma_map, 0, size,
+				BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
+		bus_dmamap_unload(dma->dma_tag, dma->dma_map);
+		bus_dmamap_destroy(dma->dma_tag, dma->dma_map);
+		bus_dmamem_unmap(dma->dma_tag, dma->dma_vaddr, size);
+		bus_dmamem_free(dma->dma_tag, dma->segs, dma->nsegs);
+		dma->dma_paddr = 0;
+		dma->dma_map = NULL;
+		dma->dma_vaddr = NULL;
+		dma->nsegs = 0;
+	}
+}
+
+void *__DWC_ALLOC(void *mem_ctx, uint32_t size)
+{
+	return malloc(size, M_DEVBUF, M_WAITOK | M_ZERO);
+}
+
+void *__DWC_ALLOC_ATOMIC(void *mem_ctx, uint32_t size)
+{
+	return malloc(size, M_DEVBUF, M_NOWAIT | M_ZERO);
+}
+
+void __DWC_FREE(void *mem_ctx, void *addr)
+{
+	free(addr, M_DEVBUF);
+}
+
+
+#ifdef DWC_CRYPTOLIB
+/* dwc_crypto.h */
+
+void DWC_RANDOM_BYTES(uint8_t *buffer, uint32_t length)
+{
+	get_random_bytes(buffer, length);
+}
+
+int DWC_AES_CBC(uint8_t *message, uint32_t messagelen, uint8_t *key, uint32_t keylen, uint8_t iv[16], uint8_t *out)
+{
+	struct crypto_blkcipher *tfm;
+	struct blkcipher_desc desc;
+	struct scatterlist sgd;
+	struct scatterlist sgs;
+
+	tfm = crypto_alloc_blkcipher("cbc(aes)", 0, CRYPTO_ALG_ASYNC);
+	if (tfm == NULL) {
+		printk("failed to load transform for aes CBC\n");
+		return -1;
+	}
+
+	crypto_blkcipher_setkey(tfm, key, keylen);
+	crypto_blkcipher_set_iv(tfm, iv, 16);
+
+	sg_init_one(&sgd, out, messagelen);
+	sg_init_one(&sgs, message, messagelen);
+
+	desc.tfm = tfm;
+	desc.flags = 0;
+
+	if (crypto_blkcipher_encrypt(&desc, &sgd, &sgs, messagelen)) {
+		crypto_free_blkcipher(tfm);
+		DWC_ERROR("AES CBC encryption failed");
+		return -1;
+	}
+
+	crypto_free_blkcipher(tfm);
+	return 0;
+}
+
+int DWC_SHA256(uint8_t *message, uint32_t len, uint8_t *out)
+{
+	struct crypto_hash *tfm;
+	struct hash_desc desc;
+	struct scatterlist sg;
+
+	tfm = crypto_alloc_hash("sha256", 0, CRYPTO_ALG_ASYNC);
+	if (IS_ERR(tfm)) {
+		DWC_ERROR("Failed to load transform for sha256: %ld", PTR_ERR(tfm));
+		return 0;
+	}
+	desc.tfm = tfm;
+	desc.flags = 0;
+
+	sg_init_one(&sg, message, len);
+	crypto_hash_digest(&desc, &sg, len, out);
+	crypto_free_hash(tfm);
+
+	return 1;
+}
+
+int DWC_HMAC_SHA256(uint8_t *message, uint32_t messagelen,
+		    uint8_t *key, uint32_t keylen, uint8_t *out)
+{
+	struct crypto_hash *tfm;
+	struct hash_desc desc;
+	struct scatterlist sg;
+
+	tfm = crypto_alloc_hash("hmac(sha256)", 0, CRYPTO_ALG_ASYNC);
+	if (IS_ERR(tfm)) {
+		DWC_ERROR("Failed to load transform for hmac(sha256): %ld", PTR_ERR(tfm));
+		return 0;
+	}
+	desc.tfm = tfm;
+	desc.flags = 0;
+
+	sg_init_one(&sg, message, messagelen);
+	crypto_hash_setkey(tfm, key, keylen);
+	crypto_hash_digest(&desc, &sg, messagelen, out);
+	crypto_free_hash(tfm);
+
+	return 1;
+}
+
+#endif	/* DWC_CRYPTOLIB */
+
+
+/* Byte Ordering Conversions */
+
+uint32_t DWC_CPU_TO_LE32(uint32_t *p)
+{
+#ifdef __LITTLE_ENDIAN
+	return *p;
+#else
+	uint8_t *u_p = (uint8_t *)p;
+
+	return (u_p[3] | (u_p[2] << 8) | (u_p[1] << 16) | (u_p[0] << 24));
+#endif
+}
+
+uint32_t DWC_CPU_TO_BE32(uint32_t *p)
+{
+#ifdef __BIG_ENDIAN
+	return *p;
+#else
+	uint8_t *u_p = (uint8_t *)p;
+
+	return (u_p[3] | (u_p[2] << 8) | (u_p[1] << 16) | (u_p[0] << 24));
+#endif
+}
+
+uint32_t DWC_LE32_TO_CPU(uint32_t *p)
+{
+#ifdef __LITTLE_ENDIAN
+	return *p;
+#else
+	uint8_t *u_p = (uint8_t *)p;
+
+	return (u_p[3] | (u_p[2] << 8) | (u_p[1] << 16) | (u_p[0] << 24));
+#endif
+}
+
+uint32_t DWC_BE32_TO_CPU(uint32_t *p)
+{
+#ifdef __BIG_ENDIAN
+	return *p;
+#else
+	uint8_t *u_p = (uint8_t *)p;
+
+	return (u_p[3] | (u_p[2] << 8) | (u_p[1] << 16) | (u_p[0] << 24));
+#endif
+}
+
+uint16_t DWC_CPU_TO_LE16(uint16_t *p)
+{
+#ifdef __LITTLE_ENDIAN
+	return *p;
+#else
+	uint8_t *u_p = (uint8_t *)p;
+	return (u_p[1] | (u_p[0] << 8));
+#endif
+}
+
+uint16_t DWC_CPU_TO_BE16(uint16_t *p)
+{
+#ifdef __BIG_ENDIAN
+	return *p;
+#else
+	uint8_t *u_p = (uint8_t *)p;
+	return (u_p[1] | (u_p[0] << 8));
+#endif
+}
+
+uint16_t DWC_LE16_TO_CPU(uint16_t *p)
+{
+#ifdef __LITTLE_ENDIAN
+	return *p;
+#else
+	uint8_t *u_p = (uint8_t *)p;
+	return (u_p[1] | (u_p[0] << 8));
+#endif
+}
+
+uint16_t DWC_BE16_TO_CPU(uint16_t *p)
+{
+#ifdef __BIG_ENDIAN
+	return *p;
+#else
+	uint8_t *u_p = (uint8_t *)p;
+	return (u_p[1] | (u_p[0] << 8));
+#endif
+}
+
+
+/* Registers */
+
+uint32_t DWC_READ_REG32(void *io_ctx, uint32_t volatile *reg)
+{
+	dwc_ioctx_t *io = (dwc_ioctx_t *)io_ctx;
+	bus_size_t ior = (bus_size_t)reg;
+
+	return bus_space_read_4(io->iot, io->ioh, ior);
+}
+
+#if 0
+uint64_t DWC_READ_REG64(void *io_ctx, uint64_t volatile *reg)
+{
+	dwc_ioctx_t *io = (dwc_ioctx_t *)io_ctx;
+	bus_size_t ior = (bus_size_t)reg;
+
+	return bus_space_read_8(io->iot, io->ioh, ior);
+}
+#endif
+
+void DWC_WRITE_REG32(void *io_ctx, uint32_t volatile *reg, uint32_t value)
+{
+	dwc_ioctx_t *io = (dwc_ioctx_t *)io_ctx;
+	bus_size_t ior = (bus_size_t)reg;
+
+	bus_space_write_4(io->iot, io->ioh, ior, value);
+}
+
+#if 0
+void DWC_WRITE_REG64(void *io_ctx, uint64_t volatile *reg, uint64_t value)
+{
+	dwc_ioctx_t *io = (dwc_ioctx_t *)io_ctx;
+	bus_size_t ior = (bus_size_t)reg;
+
+	bus_space_write_8(io->iot, io->ioh, ior, value);
+}
+#endif
+
+void DWC_MODIFY_REG32(void *io_ctx, uint32_t volatile *reg, uint32_t clear_mask,
+		      uint32_t set_mask)
+{
+	dwc_ioctx_t *io = (dwc_ioctx_t *)io_ctx;
+	bus_size_t ior = (bus_size_t)reg;
+
+	bus_space_write_4(io->iot, io->ioh, ior,
+			  (bus_space_read_4(io->iot, io->ioh, ior) &
+			   ~clear_mask) | set_mask);
+}
+
+#if 0
+void DWC_MODIFY_REG64(void *io_ctx, uint64_t volatile *reg, uint64_t clear_mask,
+		      uint64_t set_mask)
+{
+	dwc_ioctx_t *io = (dwc_ioctx_t *)io_ctx;
+	bus_size_t ior = (bus_size_t)reg;
+
+	bus_space_write_8(io->iot, io->ioh, ior,
+			  (bus_space_read_8(io->iot, io->ioh, ior) &
+			   ~clear_mask) | set_mask);
+}
+#endif
+
+
+/* Locking */
+
+dwc_spinlock_t *DWC_SPINLOCK_ALLOC(void)
+{
+	struct simplelock *sl = DWC_ALLOC(sizeof(*sl));
+
+	if (!sl) {
+		DWC_ERROR("Cannot allocate memory for spinlock");
+		return NULL;
+	}
+
+	simple_lock_init(sl);
+	return (dwc_spinlock_t *)sl;
+}
+
+void DWC_SPINLOCK_FREE(dwc_spinlock_t *lock)
+{
+	struct simplelock *sl = (struct simplelock *)lock;
+
+	DWC_FREE(sl);
+}
+
+void DWC_SPINLOCK(dwc_spinlock_t *lock)
+{
+	simple_lock((struct simplelock *)lock);
+}
+
+void DWC_SPINUNLOCK(dwc_spinlock_t *lock)
+{
+	simple_unlock((struct simplelock *)lock);
+}
+
+void DWC_SPINLOCK_IRQSAVE(dwc_spinlock_t *lock, dwc_irqflags_t *flags)
+{
+	simple_lock((struct simplelock *)lock);
+	*flags = splbio();
+}
+
+void DWC_SPINUNLOCK_IRQRESTORE(dwc_spinlock_t *lock, dwc_irqflags_t flags)
+{
+	splx(flags);
+	simple_unlock((struct simplelock *)lock);
+}
+
+dwc_mutex_t *DWC_MUTEX_ALLOC(void)
+{
+	dwc_mutex_t *mutex = DWC_ALLOC(sizeof(struct lock));
+
+	if (!mutex) {
+		DWC_ERROR("Cannot allocate memory for mutex");
+		return NULL;
+	}
+
+	lockinit((struct lock *)mutex, 0, "dw3mtx", 0, 0);
+	return mutex;
+}
+
+#if (defined(DWC_LINUX) && defined(CONFIG_DEBUG_MUTEXES))
+#else
+void DWC_MUTEX_FREE(dwc_mutex_t *mutex)
+{
+	DWC_FREE(mutex);
+}
+#endif
+
+void DWC_MUTEX_LOCK(dwc_mutex_t *mutex)
+{
+	lockmgr((struct lock *)mutex, LK_EXCLUSIVE, NULL);
+}
+
+int DWC_MUTEX_TRYLOCK(dwc_mutex_t *mutex)
+{
+	int status;
+
+	status = lockmgr((struct lock *)mutex, LK_EXCLUSIVE | LK_NOWAIT, NULL);
+	return status == 0;
+}
+
+void DWC_MUTEX_UNLOCK(dwc_mutex_t *mutex)
+{
+	lockmgr((struct lock *)mutex, LK_RELEASE, NULL);
+}
+
+
+/* Timing */
+
+void DWC_UDELAY(uint32_t usecs)
+{
+	DELAY(usecs);
+}
+
+void DWC_MDELAY(uint32_t msecs)
+{
+	do {
+		DELAY(1000);
+	} while (--msecs);
+}
+
+void DWC_MSLEEP(uint32_t msecs)
+{
+	struct timeval tv;
+
+	tv.tv_sec = msecs / 1000;
+	tv.tv_usec = (msecs - tv.tv_sec * 1000) * 1000;
+	tsleep(&tv, 0, "dw3slp", tvtohz(&tv));
+}
+
+uint32_t DWC_TIME(void)
+{
+	struct timeval tv;
+
+	microuptime(&tv);	// or getmicrouptime? (less precise, but faster)
+	return tv.tv_sec * 1000 + tv.tv_usec / 1000;
+}
+
+
+/* Timers */
+
+struct dwc_timer {
+	struct callout t;
+	char *name;
+	dwc_spinlock_t *lock;
+	dwc_timer_callback_t cb;
+	void *data;
+};
+
+dwc_timer_t *DWC_TIMER_ALLOC(char *name, dwc_timer_callback_t cb, void *data)
+{
+	dwc_timer_t *t = DWC_ALLOC(sizeof(*t));
+
+	if (!t) {
+		DWC_ERROR("Cannot allocate memory for timer");
+		return NULL;
+	}
+
+	callout_init(&t->t);
+
+	t->name = DWC_STRDUP(name);
+	if (!t->name) {
+		DWC_ERROR("Cannot allocate memory for timer->name");
+		goto no_name;
+	}
+
+	t->lock = DWC_SPINLOCK_ALLOC();
+	if (!t->lock) {
+		DWC_ERROR("Cannot allocate memory for timer->lock");
+		goto no_lock;
+	}
+
+	t->cb = cb;
+	t->data = data;
+
+	return t;
+
+ no_lock:
+	DWC_FREE(t->name);
+ no_name:
+	DWC_FREE(t);
+
+	return NULL;
+}
+
+void DWC_TIMER_FREE(dwc_timer_t *timer)
+{
+	callout_stop(&timer->t);
+	DWC_SPINLOCK_FREE(timer->lock);
+	DWC_FREE(timer->name);
+	DWC_FREE(timer);
+}
+
+void DWC_TIMER_SCHEDULE(dwc_timer_t *timer, uint32_t time)
+{
+	struct timeval tv;
+
+	tv.tv_sec = time / 1000;
+	tv.tv_usec = (time - tv.tv_sec * 1000) * 1000;
+	callout_reset(&timer->t, tvtohz(&tv), timer->cb, timer->data);
+}
+
+void DWC_TIMER_CANCEL(dwc_timer_t *timer)
+{
+	callout_stop(&timer->t);
+}
+
+
+/* Wait Queues */
+
+struct dwc_waitq {
+	struct simplelock lock;
+	int abort;
+};
+
+dwc_waitq_t *DWC_WAITQ_ALLOC(void)
+{
+	dwc_waitq_t *wq = DWC_ALLOC(sizeof(*wq));
+
+	if (!wq) {
+		DWC_ERROR("Cannot allocate memory for waitqueue");
+		return NULL;
+	}
+
+	simple_lock_init(&wq->lock);
+	wq->abort = 0;
+
+	return wq;
+}
+
+void DWC_WAITQ_FREE(dwc_waitq_t *wq)
+{
+	DWC_FREE(wq);
+}
+
+int32_t DWC_WAITQ_WAIT(dwc_waitq_t *wq, dwc_waitq_condition_t cond, void *data)
+{
+	int ipl;
+	int result = 0;
+
+	simple_lock(&wq->lock);
+	ipl = splbio();
+
+	/* Skip the sleep if already aborted or triggered */
+	if (!wq->abort && !cond(data)) {
+		splx(ipl);
+		result = ltsleep(wq, PCATCH, "dw3wat", 0, &wq->lock); // infinite timeout
+		ipl = splbio();
+	}
+
+	if (result == 0) {			// awoken
+		if (wq->abort) {
+			wq->abort = 0;
+			result = -DWC_E_ABORT;
+		} else {
+			result = 0;
+		}
+
+		splx(ipl);
+		simple_unlock(&wq->lock);
+	} else {
+		wq->abort = 0;
+		splx(ipl);
+		simple_unlock(&wq->lock);
+
+		if (result == ERESTART) {	// signaled - restart
+			result = -DWC_E_RESTART;
+		} else {			// signaled - must be EINTR
+			result = -DWC_E_ABORT;
+		}
+	}
+
+	return result;
+}
+
+int32_t DWC_WAITQ_WAIT_TIMEOUT(dwc_waitq_t *wq, dwc_waitq_condition_t cond,
+			       void *data, int32_t msecs)
+{
+	struct timeval tv, tv1, tv2;
+	int ipl;
+	int result = 0;
+
+	tv.tv_sec = msecs / 1000;
+	tv.tv_usec = (msecs - tv.tv_sec * 1000) * 1000;
+
+	simple_lock(&wq->lock);
+	ipl = splbio();
+
+	/* Skip the sleep if already aborted or triggered */
+	if (!wq->abort && !cond(data)) {
+		splx(ipl);
+		getmicrouptime(&tv1);
+		result = ltsleep(wq, PCATCH, "dw3wto", tvtohz(&tv), &wq->lock);
+		getmicrouptime(&tv2);
+		ipl = splbio();
+	}
+
+	if (result == 0) {			// awoken
+		if (wq->abort) {
+			wq->abort = 0;
+			splx(ipl);
+			simple_unlock(&wq->lock);
+			result = -DWC_E_ABORT;
+		} else {
+			splx(ipl);
+			simple_unlock(&wq->lock);
+
+			tv2.tv_usec -= tv1.tv_usec;
+			if (tv2.tv_usec < 0) {
+				tv2.tv_usec += 1000000;
+				tv2.tv_sec--;
+			}
+
+			tv2.tv_sec -= tv1.tv_sec;
+			result = tv2.tv_sec * 1000 + tv2.tv_usec / 1000;
+			result = msecs - result;
+			if (result <= 0)
+				result = 1;
+		}
+	} else {
+		wq->abort = 0;
+		splx(ipl);
+		simple_unlock(&wq->lock);
+
+		if (result == ERESTART) {	// signaled - restart
+			result = -DWC_E_RESTART;
+
+		} else if (result == EINTR) {		// signaled - interrupt
+			result = -DWC_E_ABORT;
+
+		} else {				// timed out
+			result = -DWC_E_TIMEOUT;
+		}
+	}
+
+	return result;
+}
+
+void DWC_WAITQ_TRIGGER(dwc_waitq_t *wq)
+{
+	wakeup(wq);
+}
+
+void DWC_WAITQ_ABORT(dwc_waitq_t *wq)
+{
+	int ipl;
+
+	simple_lock(&wq->lock);
+	ipl = splbio();
+	wq->abort = 1;
+	wakeup(wq);
+	splx(ipl);
+	simple_unlock(&wq->lock);
+}
+
+
+/* Threading */
+
+struct dwc_thread {
+	struct proc *proc;
+	int abort;
+};
+
+dwc_thread_t *DWC_THREAD_RUN(dwc_thread_function_t func, char *name, void *data)
+{
+	int retval;
+	dwc_thread_t *thread = DWC_ALLOC(sizeof(*thread));
+
+	if (!thread) {
+		return NULL;
+	}
+
+	thread->abort = 0;
+	retval = kthread_create1((void (*)(void *))func, data, &thread->proc,
+				 "%s", name);
+	if (retval) {
+		DWC_FREE(thread);
+		return NULL;
+	}
+
+	return thread;
+}
+
+int DWC_THREAD_STOP(dwc_thread_t *thread)
+{
+	int retval;
+
+	thread->abort = 1;
+	retval = tsleep(&thread->abort, 0, "dw3stp", 60 * hz);
+
+	if (retval == 0) {
+		/* DWC_THREAD_EXIT() will free the thread struct */
+		return 0;
+	}
+
+	/* NOTE: We leak the thread struct if thread doesn't die */
+
+	if (retval == EWOULDBLOCK) {
+		return -DWC_E_TIMEOUT;
+	}
+
+	return -DWC_E_UNKNOWN;
+}
+
+dwc_bool_t DWC_THREAD_SHOULD_STOP(dwc_thread_t *thread)
+{
+	return thread->abort;
+}
+
+void DWC_THREAD_EXIT(dwc_thread_t *thread)
+{
+	wakeup(&thread->abort);
+	DWC_FREE(thread);
+	kthread_exit(0);
+}
+
+/* tasklets
+ - Runs in interrupt context (cannot sleep)
+ - Each tasklet runs on a single CPU
+ - Different tasklets can be running simultaneously on different CPUs
+ [ On NetBSD there is no corresponding mechanism, drivers don't have bottom-
+   halves. So we just call the callback directly from DWC_TASK_SCHEDULE() ]
+ */
+struct dwc_tasklet {
+	dwc_tasklet_callback_t cb;
+	void *data;
+};
+
+static void tasklet_callback(void *data)
+{
+	dwc_tasklet_t *task = (dwc_tasklet_t *)data;
+
+	task->cb(task->data);
+}
+
+dwc_tasklet_t *DWC_TASK_ALLOC(char *name, dwc_tasklet_callback_t cb, void *data)
+{
+	dwc_tasklet_t *task = DWC_ALLOC(sizeof(*task));
+
+	if (task) {
+		task->cb = cb;
+		task->data = data;
+	} else {
+		DWC_ERROR("Cannot allocate memory for tasklet");
+	}
+
+	return task;
+}
+
+void DWC_TASK_FREE(dwc_tasklet_t *task)
+{
+	DWC_FREE(task);
+}
+
+void DWC_TASK_SCHEDULE(dwc_tasklet_t *task)
+{
+	tasklet_callback(task);
+}
+
+
+/* workqueues
+ - Runs in process context (can sleep)
+ */
+typedef struct work_container {
+	dwc_work_callback_t cb;
+	void *data;
+	dwc_workq_t *wq;
+	char *name;
+	int hz;
+	struct work task;
+} work_container_t;
+
+struct dwc_workq {
+	struct workqueue *taskq;
+	dwc_spinlock_t *lock;
+	dwc_waitq_t *waitq;
+	int pending;
+	struct work_container *container;
+};
+
+static void do_work(struct work *task, void *data)
+{
+	dwc_workq_t *wq = (dwc_workq_t *)data;
+	work_container_t *container = wq->container;
+	dwc_irqflags_t flags;
+
+	if (container->hz) {
+		tsleep(container, 0, "dw3wrk", container->hz);
+	}
+
+	container->cb(container->data);
+	DWC_DEBUG("Work done: %s, container=%p", container->name, container);
+
+	DWC_SPINLOCK_IRQSAVE(wq->lock, &flags);
+	if (container->name)
+		DWC_FREE(container->name);
+	DWC_FREE(container);
+	wq->pending--;
+	DWC_SPINUNLOCK_IRQRESTORE(wq->lock, flags);
+	DWC_WAITQ_TRIGGER(wq->waitq);
+}
+
+static int work_done(void *data)
+{
+	dwc_workq_t *workq = (dwc_workq_t *)data;
+
+	return workq->pending == 0;
+}
+
+int DWC_WORKQ_WAIT_WORK_DONE(dwc_workq_t *workq, int timeout)
+{
+	return DWC_WAITQ_WAIT_TIMEOUT(workq->waitq, work_done, workq, timeout);
+}
+
+dwc_workq_t *DWC_WORKQ_ALLOC(char *name)
+{
+	int result;
+	dwc_workq_t *wq = DWC_ALLOC(sizeof(*wq));
+
+	if (!wq) {
+		DWC_ERROR("Cannot allocate memory for workqueue");
+		return NULL;
+	}
+
+	result = workqueue_create(&wq->taskq, name, do_work, wq, 0 /*PWAIT*/,
+				  IPL_BIO, 0);
+	if (result) {
+		DWC_ERROR("Cannot create workqueue");
+		goto no_taskq;
+	}
+
+	wq->pending = 0;
+
+	wq->lock = DWC_SPINLOCK_ALLOC();
+	if (!wq->lock) {
+		DWC_ERROR("Cannot allocate memory for spinlock");
+		goto no_lock;
+	}
+
+	wq->waitq = DWC_WAITQ_ALLOC();
+	if (!wq->waitq) {
+		DWC_ERROR("Cannot allocate memory for waitqueue");
+		goto no_waitq;
+	}
+
+	return wq;
+
+ no_waitq:
+	DWC_SPINLOCK_FREE(wq->lock);
+ no_lock:
+	workqueue_destroy(wq->taskq);
+ no_taskq:
+	DWC_FREE(wq);
+
+	return NULL;
+}
+
+void DWC_WORKQ_FREE(dwc_workq_t *wq)
+{
+#ifdef DEBUG
+	dwc_irqflags_t flags;
+
+	DWC_SPINLOCK_IRQSAVE(wq->lock, &flags);
+
+	if (wq->pending != 0) {
+		struct work_container *container = wq->container;
+
+		DWC_ERROR("Destroying work queue with pending work");
+
+		if (container && container->name) {
+			DWC_ERROR("Work %s still pending", container->name);
+		}
+	}
+
+	DWC_SPINUNLOCK_IRQRESTORE(wq->lock, flags);
+#endif
+	DWC_WAITQ_FREE(wq->waitq);
+	DWC_SPINLOCK_FREE(wq->lock);
+	workqueue_destroy(wq->taskq);
+	DWC_FREE(wq);
+}
+
+void DWC_WORKQ_SCHEDULE(dwc_workq_t *wq, dwc_work_callback_t cb, void *data,
+			char *format, ...)
+{
+	dwc_irqflags_t flags;
+	work_container_t *container;
+	static char name[128];
+	va_list args;
+
+	va_start(args, format);
+	DWC_VSNPRINTF(name, 128, format, args);
+	va_end(args);
+
+	DWC_SPINLOCK_IRQSAVE(wq->lock, &flags);
+	wq->pending++;
+	DWC_SPINUNLOCK_IRQRESTORE(wq->lock, flags);
+	DWC_WAITQ_TRIGGER(wq->waitq);
+
+	container = DWC_ALLOC_ATOMIC(sizeof(*container));
+	if (!container) {
+		DWC_ERROR("Cannot allocate memory for container");
+		return;
+	}
+
+	container->name = DWC_STRDUP(name);
+	if (!container->name) {
+		DWC_ERROR("Cannot allocate memory for container->name");
+		DWC_FREE(container);
+		return;
+	}
+
+	container->cb = cb;
+	container->data = data;
+	container->wq = wq;
+	container->hz = 0;
+	wq->container = container;
+
+	DWC_DEBUG("Queueing work: %s, container=%p", container->name, container);
+	workqueue_enqueue(wq->taskq, &container->task);
+}
+
+void DWC_WORKQ_SCHEDULE_DELAYED(dwc_workq_t *wq, dwc_work_callback_t cb,
+				void *data, uint32_t time, char *format, ...)
+{
+	dwc_irqflags_t flags;
+	work_container_t *container;
+	static char name[128];
+	struct timeval tv;
+	va_list args;
+
+	va_start(args, format);
+	DWC_VSNPRINTF(name, 128, format, args);
+	va_end(args);
+
+	DWC_SPINLOCK_IRQSAVE(wq->lock, &flags);
+	wq->pending++;
+	DWC_SPINUNLOCK_IRQRESTORE(wq->lock, flags);
+	DWC_WAITQ_TRIGGER(wq->waitq);
+
+	container = DWC_ALLOC_ATOMIC(sizeof(*container));
+	if (!container) {
+		DWC_ERROR("Cannot allocate memory for container");
+		return;
+	}
+
+	container->name = DWC_STRDUP(name);
+	if (!container->name) {
+		DWC_ERROR("Cannot allocate memory for container->name");
+		DWC_FREE(container);
+		return;
+	}
+
+	container->cb = cb;
+	container->data = data;
+	container->wq = wq;
+	tv.tv_sec = time / 1000;
+	tv.tv_usec = (time - tv.tv_sec * 1000) * 1000;
+	container->hz = tvtohz(&tv);
+	wq->container = container;
+
+	DWC_DEBUG("Queueing work: %s, container=%p", container->name, container);
+	workqueue_enqueue(wq->taskq, &container->task);
+}
+
+int DWC_WORKQ_PENDING(dwc_workq_t *wq)
+{
+	return wq->pending;
+}
diff -Nur --no-dereference linux-5.10.52-orig/drivers/usb/host/dwc_common_port/dwc_crypto.c linux-5.10.52-v7l+/drivers/usb/host/dwc_common_port/dwc_crypto.c
--- linux-5.10.52-orig/drivers/usb/host/dwc_common_port/dwc_crypto.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/usb/host/dwc_common_port/dwc_crypto.c	2021-07-25 16:46:28.657910448 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/* =========================================================================
+ * $File: //dwh/usb_iip/dev/software/dwc_common_port_2/dwc_crypto.c $
+ * $Revision: #5 $
+ * $Date: 2010/09/28 $
+ * $Change: 1596182 $
+ *
+ * Synopsys Portability Library Software and documentation
+ * (hereinafter, "Software") is an Unsupported proprietary work of
+ * Synopsys, Inc. unless otherwise expressly agreed to in writing
+ * between Synopsys and you.
+ *
+ * The Software IS NOT an item of Licensed Software or Licensed Product
+ * under any End User Software License Agreement or Agreement for
+ * Licensed Product with Synopsys or any supplement thereto. You are
+ * permitted to use and redistribute this Software in source and binary
+ * forms, with or without modification, provided that redistributions
+ * of source code must retain this notice. You may not view, use,
+ * disclose, copy or distribute this file or any information contained
+ * herein except pursuant to this license grant from Synopsys. If you
+ * do not agree with this notice, including the disclaimer below, then
+ * you are not authorized to use the Software.
+ *
+ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS"
+ * BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL
+ * SYNOPSYS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * ========================================================================= */
+
+/** @file
+ * This file contains the WUSB cryptographic routines.
+ */
+
+#ifdef DWC_CRYPTOLIB
+
+#include "dwc_crypto.h"
+#include "usb.h"
+
+#ifdef DEBUG
+static inline void dump_bytes(char *name, uint8_t *bytes, int len)
+{
+	int i;
+	DWC_PRINTF("%s: ", name);
+	for (i=0; i<len; i++) {
+		DWC_PRINTF("%02x ", bytes[i]);
+	}
+	DWC_PRINTF("\n");
+}
+#else
+#define dump_bytes(x...)
+#endif
+
+/* Display a block */
+void show_block(const u8 *blk, const char *prefix, const char *suffix, int a)
+{
+#ifdef DWC_DEBUG_CRYPTO
+	int i, blksize = 16;
+
+	DWC_DEBUG("%s", prefix);
+
+	if (suffix == NULL) {
+		suffix = "\n";
+		blksize = a;
+	}
+
+	for (i = 0; i < blksize; i++)
+		DWC_PRINT("%02x%s", *blk++, ((i & 3) == 3) ? "  " : " ");
+	DWC_PRINT(suffix);
+#endif
+}
+
+/**
+ * Encrypts an array of bytes using the AES encryption engine.
+ * If <code>dst</code> == <code>src</code>, then the bytes will be encrypted
+ * in-place.
+ *
+ * @return  0 on success, negative error code on error.
+ */
+int dwc_wusb_aes_encrypt(u8 *src, u8 *key, u8 *dst)
+{
+	u8 block_t[16];
+	DWC_MEMSET(block_t, 0, 16);
+
+	return DWC_AES_CBC(src, 16, key, 16, block_t, dst);
+}
+
+/**
+ * The CCM-MAC-FUNCTION described in section 6.5 of the WUSB spec.
+ * This function takes a data string and returns the encrypted CBC
+ * Counter-mode MIC.
+ *
+ * @param key     The 128-bit symmetric key.
+ * @param nonce   The CCM nonce.
+ * @param label   The unique 14-byte ASCII text label.
+ * @param bytes   The byte array to be encrypted.
+ * @param len     Length of the byte array.
+ * @param result  Byte array to receive the 8-byte encrypted MIC.
+ */
+void dwc_wusb_cmf(u8 *key, u8 *nonce,
+		  char *label, u8 *bytes, int len, u8 *result)
+{
+	u8 block_m[16];
+	u8 block_x[16];
+	u8 block_t[8];
+	int idx, blkNum;
+	u16 la = (u16)(len + 14);
+
+	/* Set the AES-128 key */
+	//dwc_aes_setkey(tfm, key, 16);
+
+	/* Fill block B0 from flags = 0x59, N, and l(m) = 0 */
+	block_m[0] = 0x59;
+	for (idx = 0; idx < 13; idx++)
+		block_m[idx + 1] = nonce[idx];
+	block_m[14] = 0;
+	block_m[15] = 0;
+
+	/* Produce the CBC IV */
+	dwc_wusb_aes_encrypt(block_m, key, block_x);
+	show_block(block_m, "CBC IV in: ", "\n", 0);
+	show_block(block_x, "CBC IV out:", "\n", 0);
+
+	/* Fill block B1 from l(a) = Blen + 14, and A */
+	block_x[0] ^= (u8)(la >> 8);
+	block_x[1] ^= (u8)la;
+	for (idx = 0; idx < 14; idx++)
+		block_x[idx + 2] ^= label[idx];
+	show_block(block_x, "After xor: ", "b1\n", 16);
+
+	dwc_wusb_aes_encrypt(block_x, key, block_x);
+	show_block(block_x, "After AES: ", "b1\n", 16);
+
+	idx = 0;
+	blkNum = 0;
+
+	/* Fill remaining blocks with B */
+	while (len-- > 0) {
+		block_x[idx] ^= *bytes++;
+		if (++idx >= 16) {
+			idx = 0;
+			show_block(block_x, "After xor: ", "\n", blkNum);
+			dwc_wusb_aes_encrypt(block_x, key, block_x);
+			show_block(block_x, "After AES: ", "\n", blkNum);
+			blkNum++;
+		}
+	}
+
+	/* Handle partial last block */
+	if (idx > 0) {
+		show_block(block_x, "After xor: ", "\n", blkNum);
+		dwc_wusb_aes_encrypt(block_x, key, block_x);
+		show_block(block_x, "After AES: ", "\n", blkNum);
+	}
+
+	/* Save the MIC tag */
+	DWC_MEMCPY(block_t, block_x, 8);
+	show_block(block_t, "MIC tag  : ", NULL, 8);
+
+	/* Fill block A0 from flags = 0x01, N, and counter = 0 */
+	block_m[0] = 0x01;
+	block_m[14] = 0;
+	block_m[15] = 0;
+
+	/* Encrypt the counter */
+	dwc_wusb_aes_encrypt(block_m, key, block_x);
+	show_block(block_x, "CTR[MIC] : ", NULL, 8);
+
+	/* XOR with MIC tag */
+	for (idx = 0; idx < 8; idx++) {
+		block_t[idx] ^= block_x[idx];
+	}
+
+	/* Return result to caller */
+	DWC_MEMCPY(result, block_t, 8);
+	show_block(result, "CCM-MIC  : ", NULL, 8);
+
+}
+
+/**
+ * The PRF function described in section 6.5 of the WUSB spec. This function
+ * concatenates MIC values returned from dwc_cmf() to create a value of
+ * the requested length.
+ *
+ * @param prf_len  Length of the PRF function in bits (64, 128, or 256).
+ * @param key, nonce, label, bytes, len  Same as for dwc_cmf().
+ * @param result   Byte array to receive the result.
+ */
+void dwc_wusb_prf(int prf_len, u8 *key,
+		  u8 *nonce, char *label, u8 *bytes, int len, u8 *result)
+{
+	int i;
+
+	nonce[0] = 0;
+	for (i = 0; i < prf_len >> 6; i++, nonce[0]++) {
+		dwc_wusb_cmf(key, nonce, label, bytes, len, result);
+		result += 8;
+	}
+}
+
+/**
+ * Fills in CCM Nonce per the WUSB spec.
+ *
+ * @param[in] haddr Host address.
+ * @param[in] daddr Device address.
+ * @param[in] tkid Session Key(PTK) identifier.
+ * @param[out] nonce Pointer to where the CCM Nonce output is to be written.
+ */
+void dwc_wusb_fill_ccm_nonce(uint16_t haddr, uint16_t daddr, uint8_t *tkid,
+			     uint8_t *nonce)
+{
+
+	DWC_DEBUG("%s %x %x\n", __func__, daddr, haddr);
+
+	DWC_MEMSET(&nonce[0], 0, 16);
+
+	DWC_MEMCPY(&nonce[6], tkid, 3);
+	nonce[9] = daddr & 0xFF;
+	nonce[10] = (daddr >> 8) & 0xFF;
+	nonce[11] = haddr & 0xFF;
+	nonce[12] = (haddr >> 8) & 0xFF;
+
+	dump_bytes("CCM nonce", nonce, 16);
+}
+
+/**
+ * Generates a 16-byte cryptographic-grade random number for the Host/Device
+ * Nonce.
+ */
+void dwc_wusb_gen_nonce(uint16_t addr, uint8_t *nonce)
+{
+	uint8_t inonce[16];
+	uint32_t temp[4];
+
+	/* Fill in the Nonce */
+	DWC_MEMSET(&inonce[0], 0, sizeof(inonce));
+	inonce[9] = addr & 0xFF;
+	inonce[10] = (addr >> 8) & 0xFF;
+	inonce[11] = inonce[9];
+	inonce[12] = inonce[10];
+
+	/* Collect "randomness samples" */
+	DWC_RANDOM_BYTES((uint8_t *)temp, 16);
+
+	dwc_wusb_prf_128((uint8_t *)temp, nonce,
+			 "Random Numbers", (uint8_t *)temp, sizeof(temp),
+			 nonce);
+}
+
+/**
+ * Generates the Session Key (PTK) and Key Confirmation Key (KCK) per the
+ * WUSB spec.
+ *
+ * @param[in] ccm_nonce Pointer to CCM Nonce.
+ * @param[in] mk Master Key to derive the session from
+ * @param[in] hnonce Pointer to Host Nonce.
+ * @param[in] dnonce Pointer to Device Nonce.
+ * @param[out] kck Pointer to where the KCK output is to be written.
+ * @param[out] ptk Pointer to where the PTK output is to be written.
+ */
+void dwc_wusb_gen_key(uint8_t *ccm_nonce, uint8_t *mk, uint8_t *hnonce,
+		      uint8_t *dnonce, uint8_t *kck, uint8_t *ptk)
+{
+	uint8_t idata[32];
+	uint8_t odata[32];
+
+	dump_bytes("ck", mk, 16);
+	dump_bytes("hnonce", hnonce, 16);
+	dump_bytes("dnonce", dnonce, 16);
+
+	/* The data is the HNonce and DNonce concatenated */
+	DWC_MEMCPY(&idata[0], hnonce, 16);
+	DWC_MEMCPY(&idata[16], dnonce, 16);
+
+	dwc_wusb_prf_256(mk, ccm_nonce, "Pair-wise keys", idata, 32, odata);
+
+	/* Low 16 bytes of the result is the KCK, high 16 is the PTK */
+	DWC_MEMCPY(kck, &odata[0], 16);
+	DWC_MEMCPY(ptk, &odata[16], 16);
+
+	dump_bytes("kck", kck, 16);
+	dump_bytes("ptk", ptk, 16);
+}
+
+/**
+ * Generates the Message Integrity Code over the Handshake data per the
+ * WUSB spec.
+ *
+ * @param ccm_nonce Pointer to CCM Nonce.
+ * @param kck   Pointer to Key Confirmation Key.
+ * @param data  Pointer to Handshake data to be checked.
+ * @param mic   Pointer to where the MIC output is to be written.
+ */
+void dwc_wusb_gen_mic(uint8_t *ccm_nonce, uint8_t *kck,
+		      uint8_t *data, uint8_t *mic)
+{
+
+	dwc_wusb_prf_64(kck, ccm_nonce, "out-of-bandMIC",
+			data, WUSB_HANDSHAKE_LEN_FOR_MIC, mic);
+}
+
+#endif	/* DWC_CRYPTOLIB */
diff -Nur --no-dereference linux-5.10.52-orig/drivers/usb/host/dwc_common_port/dwc_crypto.h linux-5.10.52-v7l+/drivers/usb/host/dwc_common_port/dwc_crypto.h
--- linux-5.10.52-orig/drivers/usb/host/dwc_common_port/dwc_crypto.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/usb/host/dwc_common_port/dwc_crypto.h	2021-07-25 16:46:28.657910448 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/* =========================================================================
+ * $File: //dwh/usb_iip/dev/software/dwc_common_port_2/dwc_crypto.h $
+ * $Revision: #3 $
+ * $Date: 2010/09/28 $
+ * $Change: 1596182 $
+ *
+ * Synopsys Portability Library Software and documentation
+ * (hereinafter, "Software") is an Unsupported proprietary work of
+ * Synopsys, Inc. unless otherwise expressly agreed to in writing
+ * between Synopsys and you.
+ *
+ * The Software IS NOT an item of Licensed Software or Licensed Product
+ * under any End User Software License Agreement or Agreement for
+ * Licensed Product with Synopsys or any supplement thereto. You are
+ * permitted to use and redistribute this Software in source and binary
+ * forms, with or without modification, provided that redistributions
+ * of source code must retain this notice. You may not view, use,
+ * disclose, copy or distribute this file or any information contained
+ * herein except pursuant to this license grant from Synopsys. If you
+ * do not agree with this notice, including the disclaimer below, then
+ * you are not authorized to use the Software.
+ *
+ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS"
+ * BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL
+ * SYNOPSYS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * ========================================================================= */
+
+#ifndef _DWC_CRYPTO_H_
+#define _DWC_CRYPTO_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** @file
+ *
+ * This file contains declarations for the WUSB Cryptographic routines as
+ * defined in the WUSB spec.  They are only to be used internally by the DWC UWB
+ * modules.
+ */
+
+#include "dwc_os.h"
+
+int dwc_wusb_aes_encrypt(u8 *src, u8 *key, u8 *dst);
+
+void dwc_wusb_cmf(u8 *key, u8 *nonce,
+		  char *label, u8 *bytes, int len, u8 *result);
+void dwc_wusb_prf(int prf_len, u8 *key,
+		  u8 *nonce, char *label, u8 *bytes, int len, u8 *result);
+
+/**
+ * The PRF-64 function described in section 6.5 of the WUSB spec.
+ *
+ * @param key, nonce, label, bytes, len, result  Same as for dwc_prf().
+ */
+static inline void dwc_wusb_prf_64(u8 *key, u8 *nonce,
+				   char *label, u8 *bytes, int len, u8 *result)
+{
+	dwc_wusb_prf(64, key, nonce, label, bytes, len, result);
+}
+
+/**
+ * The PRF-128 function described in section 6.5 of the WUSB spec.
+ *
+ * @param key, nonce, label, bytes, len, result  Same as for dwc_prf().
+ */
+static inline void dwc_wusb_prf_128(u8 *key, u8 *nonce,
+				    char *label, u8 *bytes, int len, u8 *result)
+{
+	dwc_wusb_prf(128, key, nonce, label, bytes, len, result);
+}
+
+/**
+ * The PRF-256 function described in section 6.5 of the WUSB spec.
+ *
+ * @param key, nonce, label, bytes, len, result  Same as for dwc_prf().
+ */
+static inline void dwc_wusb_prf_256(u8 *key, u8 *nonce,
+				    char *label, u8 *bytes, int len, u8 *result)
+{
+	dwc_wusb_prf(256, key, nonce, label, bytes, len, result);
+}
+
+
+void dwc_wusb_fill_ccm_nonce(uint16_t haddr, uint16_t daddr, uint8_t *tkid,
+			       uint8_t *nonce);
+void dwc_wusb_gen_nonce(uint16_t addr,
+			  uint8_t *nonce);
+
+void dwc_wusb_gen_key(uint8_t *ccm_nonce, uint8_t *mk,
+			uint8_t *hnonce, uint8_t *dnonce,
+			uint8_t *kck, uint8_t *ptk);
+
+
+void dwc_wusb_gen_mic(uint8_t *ccm_nonce, uint8_t
+			*kck, uint8_t *data, uint8_t *mic);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _DWC_CRYPTO_H_ */
diff -Nur --no-dereference linux-5.10.52-orig/drivers/usb/host/dwc_common_port/dwc_dh.c linux-5.10.52-v7l+/drivers/usb/host/dwc_common_port/dwc_dh.c
--- linux-5.10.52-orig/drivers/usb/host/dwc_common_port/dwc_dh.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/usb/host/dwc_common_port/dwc_dh.c	2021-07-25 16:46:28.657910448 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/* =========================================================================
+ * $File: //dwh/usb_iip/dev/software/dwc_common_port_2/dwc_dh.c $
+ * $Revision: #3 $
+ * $Date: 2010/09/28 $
+ * $Change: 1596182 $
+ *
+ * Synopsys Portability Library Software and documentation
+ * (hereinafter, "Software") is an Unsupported proprietary work of
+ * Synopsys, Inc. unless otherwise expressly agreed to in writing
+ * between Synopsys and you.
+ *
+ * The Software IS NOT an item of Licensed Software or Licensed Product
+ * under any End User Software License Agreement or Agreement for
+ * Licensed Product with Synopsys or any supplement thereto. You are
+ * permitted to use and redistribute this Software in source and binary
+ * forms, with or without modification, provided that redistributions
+ * of source code must retain this notice. You may not view, use,
+ * disclose, copy or distribute this file or any information contained
+ * herein except pursuant to this license grant from Synopsys. If you
+ * do not agree with this notice, including the disclaimer below, then
+ * you are not authorized to use the Software.
+ *
+ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS"
+ * BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL
+ * SYNOPSYS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * ========================================================================= */
+#ifdef DWC_CRYPTOLIB
+
+#ifndef CONFIG_MACH_IPMATE
+
+#include "dwc_dh.h"
+#include "dwc_modpow.h"
+
+#ifdef DEBUG
+/* This function prints out a buffer in the format described in the Association
+ * Model specification. */
+static void dh_dump(char *str, void *_num, int len)
+{
+	uint8_t *num = _num;
+	int i;
+	DWC_PRINTF("%s\n", str);
+	for (i = 0; i < len; i ++) {
+		DWC_PRINTF("%02x", num[i]);
+		if (((i + 1) % 2) == 0) DWC_PRINTF(" ");
+		if (((i + 1) % 26) == 0) DWC_PRINTF("\n");
+	}
+
+	DWC_PRINTF("\n");
+}
+#else
+#define dh_dump(_x...) do {; } while(0)
+#endif
+
+/* Constant g value */
+static __u32 dh_g[] = {
+	0x02000000,
+};
+
+/* Constant p value */
+static __u32 dh_p[] = {
+	0xFFFFFFFF, 0xFFFFFFFF, 0xA2DA0FC9, 0x34C26821, 0x8B62C6C4, 0xD11CDC80, 0x084E0229, 0x74CC678A,
+	0xA6BE0B02, 0x229B133B, 0x79084A51, 0xDD04348E, 0xB31995EF, 0x1B433ACD, 0x6D0A2B30, 0x37145FF2,
+	0x6D35E14F, 0x45C2516D, 0x76B585E4, 0xC67E5E62, 0xE9424CF4, 0x6BED37A6, 0xB65CFF0B, 0xEDB706F4,
+	0xFB6B38EE, 0xA59F895A, 0x11249FAE, 0xE61F4B7C, 0x51662849, 0x3D5BE4EC, 0xB87C00C2, 0x05BF63A1,
+	0x3648DA98, 0x9AD3551C, 0xA83F1669, 0x5FCF24FD, 0x235D6583, 0x96ADA3DC, 0x56F3621C, 0xBB528520,
+	0x0729D59E, 0x6D969670, 0x4E350C67, 0x0498BC4A, 0x086C74F1, 0x7C2118CA, 0x465E9032, 0x3BCE362E,
+	0x2C779EE3, 0x03860E18, 0xA283279B, 0x8FA207EC, 0xF05DC5B5, 0xC9524C6F, 0xF6CB2BDE, 0x18175895,
+	0x7C499539, 0xE56A95EA, 0x1826D215, 0x1005FA98, 0x5A8E7215, 0x2DC4AA8A, 0x0D1733AD, 0x337A5004,
+	0xAB2155A8, 0x64BA1CDF, 0x0485FBEC, 0x0AEFDB58, 0x5771EA8A, 0x7D0C065D, 0x850F97B3, 0xC7E4E1A6,
+	0x8CAEF5AB, 0xD73309DB, 0xE0948C1E, 0x9D61254A, 0x26D2E3CE, 0x6BEED21A, 0x06FA2FF1, 0x64088AD9,
+	0x730276D8, 0x646AC83E, 0x182B1F52, 0x0C207B17, 0x5717E1BB, 0x6C5D617A, 0xC0880977, 0xE246D9BA,
+	0xA04FE208, 0x31ABE574, 0xFC5BDB43, 0x8E10FDE0, 0x20D1824B, 0xCAD23AA9, 0xFFFFFFFF, 0xFFFFFFFF,
+};
+
+static void dh_swap_bytes(void *_in, void *_out, uint32_t len)
+{
+	uint8_t *in = _in;
+	uint8_t *out = _out;
+	int i;
+	for (i=0; i<len; i++) {
+		out[i] = in[len-1-i];
+	}
+}
+
+/* Computes the modular exponentiation (num^exp % mod).  num, exp, and mod are
+ * big endian numbers of size len, in bytes.  Each len value must be a multiple
+ * of 4. */
+int dwc_dh_modpow(void *mem_ctx, void *num, uint32_t num_len,
+		  void *exp, uint32_t exp_len,
+		  void *mod, uint32_t mod_len,
+		  void *out)
+{
+	/* modpow() takes little endian numbers.  AM uses big-endian.  This
+	 * function swaps bytes of numbers before passing onto modpow. */
+
+	int retval = 0;
+	uint32_t *result;
+
+	uint32_t *bignum_num = dwc_alloc(mem_ctx, num_len + 4);
+	uint32_t *bignum_exp = dwc_alloc(mem_ctx, exp_len + 4);
+	uint32_t *bignum_mod = dwc_alloc(mem_ctx, mod_len + 4);
+
+	dh_swap_bytes(num, &bignum_num[1], num_len);
+	bignum_num[0] = num_len / 4;
+
+	dh_swap_bytes(exp, &bignum_exp[1], exp_len);
+	bignum_exp[0] = exp_len / 4;
+
+	dh_swap_bytes(mod, &bignum_mod[1], mod_len);
+	bignum_mod[0] = mod_len / 4;
+
+	result = dwc_modpow(mem_ctx, bignum_num, bignum_exp, bignum_mod);
+	if (!result) {
+		retval = -1;
+		goto dh_modpow_nomem;
+	}
+
+	dh_swap_bytes(&result[1], out, result[0] * 4);
+	dwc_free(mem_ctx, result);
+
+ dh_modpow_nomem:
+	dwc_free(mem_ctx, bignum_num);
+	dwc_free(mem_ctx, bignum_exp);
+	dwc_free(mem_ctx, bignum_mod);
+	return retval;
+}
+
+
+int dwc_dh_pk(void *mem_ctx, uint8_t nd, uint8_t *exp, uint8_t *pk, uint8_t *hash)
+{
+	int retval;
+	uint8_t m3[385];
+
+#ifndef DH_TEST_VECTORS
+	DWC_RANDOM_BYTES(exp, 32);
+#endif
+
+	/* Compute the pkd */
+	if ((retval = dwc_dh_modpow(mem_ctx, dh_g, 4,
+				    exp, 32,
+				    dh_p, 384, pk))) {
+		return retval;
+	}
+
+	m3[384] = nd;
+	DWC_MEMCPY(&m3[0], pk, 384);
+	DWC_SHA256(m3, 385, hash);
+
+	dh_dump("PK", pk, 384);
+	dh_dump("SHA-256(M3)", hash, 32);
+	return 0;
+}
+
+int dwc_dh_derive_keys(void *mem_ctx, uint8_t nd, uint8_t *pkh, uint8_t *pkd,
+		       uint8_t *exp, int is_host,
+		       char *dd, uint8_t *ck, uint8_t *kdk)
+{
+	int retval;
+	uint8_t mv[784];
+	uint8_t sha_result[32];
+	uint8_t dhkey[384];
+	uint8_t shared_secret[384];
+	char *message;
+	uint32_t vd;
+
+	uint8_t *pk;
+
+	if (is_host) {
+		pk = pkd;
+	}
+	else {
+		pk = pkh;
+	}
+
+	if ((retval = dwc_dh_modpow(mem_ctx, pk, 384,
+				    exp, 32,
+				    dh_p, 384, shared_secret))) {
+		return retval;
+	}
+	dh_dump("Shared Secret", shared_secret, 384);
+
+	DWC_SHA256(shared_secret, 384, dhkey);
+	dh_dump("DHKEY", dhkey, 384);
+
+	DWC_MEMCPY(&mv[0], pkd, 384);
+	DWC_MEMCPY(&mv[384], pkh, 384);
+	DWC_MEMCPY(&mv[768], "displayed digest", 16);
+	dh_dump("MV", mv, 784);
+
+	DWC_SHA256(mv, 784, sha_result);
+	dh_dump("SHA-256(MV)", sha_result, 32);
+	dh_dump("First 32-bits of SHA-256(MV)", sha_result, 4);
+
+	dh_swap_bytes(sha_result, &vd, 4);
+#ifdef DEBUG
+	DWC_PRINTF("Vd (decimal) = %d\n", vd);
+#endif
+
+	switch (nd) {
+	case 2:
+		vd = vd % 100;
+		DWC_SPRINTF(dd, "%02d", vd);
+		break;
+	case 3:
+		vd = vd % 1000;
+		DWC_SPRINTF(dd, "%03d", vd);
+		break;
+	case 4:
+		vd = vd % 10000;
+		DWC_SPRINTF(dd, "%04d", vd);
+		break;
+	}
+#ifdef DEBUG
+	DWC_PRINTF("Display Digits: %s\n", dd);
+#endif
+
+	message = "connection key";
+	DWC_HMAC_SHA256(message, DWC_STRLEN(message), dhkey, 32, sha_result);
+	dh_dump("HMAC(SHA-256, DHKey, connection key)", sha_result, 32);
+	DWC_MEMCPY(ck, sha_result, 16);
+
+	message = "key derivation key";
+	DWC_HMAC_SHA256(message, DWC_STRLEN(message), dhkey, 32, sha_result);
+	dh_dump("HMAC(SHA-256, DHKey, key derivation key)", sha_result, 32);
+	DWC_MEMCPY(kdk, sha_result, 32);
+
+	return 0;
+}
+
+
+#ifdef DH_TEST_VECTORS
+
+static __u8 dh_a[] = {
+	0x44, 0x00, 0x51, 0xd6,
+	0xf0, 0xb5, 0x5e, 0xa9,
+	0x67, 0xab, 0x31, 0xc6,
+	0x8a, 0x8b, 0x5e, 0x37,
+	0xd9, 0x10, 0xda, 0xe0,
+	0xe2, 0xd4, 0x59, 0xa4,
+	0x86, 0x45, 0x9c, 0xaa,
+	0xdf, 0x36, 0x75, 0x16,
+};
+
+static __u8 dh_b[] = {
+	0x5d, 0xae, 0xc7, 0x86,
+	0x79, 0x80, 0xa3, 0x24,
+	0x8c, 0xe3, 0x57, 0x8f,
+	0xc7, 0x5f, 0x1b, 0x0f,
+	0x2d, 0xf8, 0x9d, 0x30,
+	0x6f, 0xa4, 0x52, 0xcd,
+	0xe0, 0x7a, 0x04, 0x8a,
+	0xde, 0xd9, 0x26, 0x56,
+};
+
+void dwc_run_dh_test_vectors(void *mem_ctx)
+{
+	uint8_t pkd[384];
+	uint8_t pkh[384];
+	uint8_t hashd[32];
+	uint8_t hashh[32];
+	uint8_t ck[16];
+	uint8_t kdk[32];
+	char dd[5];
+
+	DWC_PRINTF("\n\n\nDH_TEST_VECTORS\n\n");
+
+	/* compute the PKd and SHA-256(PKd || Nd) */
+	DWC_PRINTF("Computing PKd\n");
+	dwc_dh_pk(mem_ctx, 2, dh_a, pkd, hashd);
+
+	/* compute the PKd and SHA-256(PKh || Nd) */
+	DWC_PRINTF("Computing PKh\n");
+	dwc_dh_pk(mem_ctx, 2, dh_b, pkh, hashh);
+
+	/* compute the dhkey */
+	dwc_dh_derive_keys(mem_ctx, 2, pkh, pkd, dh_a, 0, dd, ck, kdk);
+}
+#endif /* DH_TEST_VECTORS */
+
+#endif /* !CONFIG_MACH_IPMATE */
+
+#endif /* DWC_CRYPTOLIB */
diff -Nur --no-dereference linux-5.10.52-orig/drivers/usb/host/dwc_common_port/dwc_dh.h linux-5.10.52-v7l+/drivers/usb/host/dwc_common_port/dwc_dh.h
--- linux-5.10.52-orig/drivers/usb/host/dwc_common_port/dwc_dh.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/usb/host/dwc_common_port/dwc_dh.h	2021-07-25 16:46:28.657910448 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/* =========================================================================
+ * $File: //dwh/usb_iip/dev/software/dwc_common_port_2/dwc_dh.h $
+ * $Revision: #4 $
+ * $Date: 2010/09/28 $
+ * $Change: 1596182 $
+ *
+ * Synopsys Portability Library Software and documentation
+ * (hereinafter, "Software") is an Unsupported proprietary work of
+ * Synopsys, Inc. unless otherwise expressly agreed to in writing
+ * between Synopsys and you.
+ *
+ * The Software IS NOT an item of Licensed Software or Licensed Product
+ * under any End User Software License Agreement or Agreement for
+ * Licensed Product with Synopsys or any supplement thereto. You are
+ * permitted to use and redistribute this Software in source and binary
+ * forms, with or without modification, provided that redistributions
+ * of source code must retain this notice. You may not view, use,
+ * disclose, copy or distribute this file or any information contained
+ * herein except pursuant to this license grant from Synopsys. If you
+ * do not agree with this notice, including the disclaimer below, then
+ * you are not authorized to use the Software.
+ *
+ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS"
+ * BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL
+ * SYNOPSYS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * ========================================================================= */
+#ifndef _DWC_DH_H_
+#define _DWC_DH_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "dwc_os.h"
+
+/** @file
+ *
+ * This file defines the common functions on device and host for performing
+ * numeric association as defined in the WUSB spec.  They are only to be
+ * used internally by the DWC UWB modules. */
+
+extern int dwc_dh_sha256(uint8_t *message, uint32_t len, uint8_t *out);
+extern int dwc_dh_hmac_sha256(uint8_t *message, uint32_t messagelen,
+			      uint8_t *key, uint32_t keylen,
+			      uint8_t *out);
+extern int dwc_dh_modpow(void *mem_ctx, void *num, uint32_t num_len,
+			 void *exp, uint32_t exp_len,
+			 void *mod, uint32_t mod_len,
+			 void *out);
+
+/** Computes PKD or PKH, and SHA-256(PKd || Nd)
+ *
+ * PK = g^exp mod p.
+ *
+ * Input:
+ * Nd = Number of digits on the device.
+ *
+ * Output:
+ * exp = A 32-byte buffer to be filled with a randomly generated number.
+ *       used as either A or B.
+ * pk = A 384-byte buffer to be filled with the PKH or PKD.
+ * hash = A 32-byte buffer to be filled with SHA-256(PK || ND).
+ */
+extern int dwc_dh_pk(void *mem_ctx, uint8_t nd, uint8_t *exp, uint8_t *pkd, uint8_t *hash);
+
+/** Computes the DHKEY, and VD.
+ *
+ * If called from host, then it will comput DHKEY=PKD^exp % p.
+ * If called from device, then it will comput DHKEY=PKH^exp % p.
+ *
+ * Input:
+ * pkd = The PKD value.
+ * pkh = The PKH value.
+ * exp = The A value (if device) or B value (if host) generated in dwc_wudev_dh_pk.
+ * is_host = Set to non zero if a WUSB host is calling this function.
+ *
+ * Output:
+
+ * dd = A pointer to an buffer to be set to the displayed digits string to be shown
+ *      to the user.  This buffer should be at 5 bytes long to hold 4 digits plus a
+ *      null termination character.  This buffer can be used directly for display.
+ * ck = A 16-byte buffer to be filled with the CK.
+ * kdk = A 32-byte buffer to be filled with the KDK.
+ */
+extern int dwc_dh_derive_keys(void *mem_ctx, uint8_t nd, uint8_t *pkh, uint8_t *pkd,
+			      uint8_t *exp, int is_host,
+			      char *dd, uint8_t *ck, uint8_t *kdk);
+
+#ifdef DH_TEST_VECTORS
+extern void dwc_run_dh_test_vectors(void);
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _DWC_DH_H_ */
diff -Nur --no-dereference linux-5.10.52-orig/drivers/usb/host/dwc_common_port/dwc_list.h linux-5.10.52-v7l+/drivers/usb/host/dwc_common_port/dwc_list.h
--- linux-5.10.52-orig/drivers/usb/host/dwc_common_port/dwc_list.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/usb/host/dwc_common_port/dwc_list.h	2021-07-25 16:46:28.657910448 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/*	$OpenBSD: queue.h,v 1.26 2004/05/04 16:59:32 grange Exp $	*/
+/*	$NetBSD: queue.h,v 1.11 1996/05/16 05:17:14 mycroft Exp $	*/
+
+/*
+ * Copyright (c) 1991, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *	@(#)queue.h	8.5 (Berkeley) 8/20/94
+ */
+
+#ifndef _DWC_LIST_H_
+#define _DWC_LIST_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** @file
+ *
+ * This file defines linked list operations.  It is derived from BSD with
+ * only the MACRO names being prefixed with DWC_.  This is because a few of
+ * these names conflict with those on Linux.  For documentation on use, see the
+ * inline comments in the source code.  The original license for this source
+ * code applies and is preserved in the dwc_list.h source file.
+ */
+
+/*
+ * This file defines five types of data structures: singly-linked lists,
+ * lists, simple queues, tail queues, and circular queues.
+ *
+ *
+ * A singly-linked list is headed by a single forward pointer. The elements
+ * are singly linked for minimum space and pointer manipulation overhead at
+ * the expense of O(n) removal for arbitrary elements. New elements can be
+ * added to the list after an existing element or at the head of the list.
+ * Elements being removed from the head of the list should use the explicit
+ * macro for this purpose for optimum efficiency. A singly-linked list may
+ * only be traversed in the forward direction.  Singly-linked lists are ideal
+ * for applications with large datasets and few or no removals or for
+ * implementing a LIFO queue.
+ *
+ * A list is headed by a single forward pointer (or an array of forward
+ * pointers for a hash table header). The elements are doubly linked
+ * so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before
+ * or after an existing element or at the head of the list. A list
+ * may only be traversed in the forward direction.
+ *
+ * A simple queue is headed by a pair of pointers, one the head of the
+ * list and the other to the tail of the list. The elements are singly
+ * linked to save space, so elements can only be removed from the
+ * head of the list. New elements can be added to the list before or after
+ * an existing element, at the head of the list, or at the end of the
+ * list. A simple queue may only be traversed in the forward direction.
+ *
+ * A tail queue is headed by a pair of pointers, one to the head of the
+ * list and the other to the tail of the list. The elements are doubly
+ * linked so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before or
+ * after an existing element, at the head of the list, or at the end of
+ * the list. A tail queue may be traversed in either direction.
+ *
+ * A circle queue is headed by a pair of pointers, one to the head of the
+ * list and the other to the tail of the list. The elements are doubly
+ * linked so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before or after
+ * an existing element, at the head of the list, or at the end of the list.
+ * A circle queue may be traversed in either direction, but has a more
+ * complex end of list detection.
+ *
+ * For details on the use of these macros, see the queue(3) manual page.
+ */
+
+/*
+ * Double-linked List.
+ */
+
+typedef struct dwc_list_link {
+	struct dwc_list_link *next;
+	struct dwc_list_link *prev;
+} dwc_list_link_t;
+
+#define DWC_LIST_INIT(link) do {	\
+	(link)->next = (link);		\
+	(link)->prev = (link);		\
+} while (0)
+
+#define DWC_LIST_FIRST(link)	((link)->next)
+#define DWC_LIST_LAST(link)	((link)->prev)
+#define DWC_LIST_END(link)	(link)
+#define DWC_LIST_NEXT(link)	((link)->next)
+#define DWC_LIST_PREV(link)	((link)->prev)
+#define DWC_LIST_EMPTY(link)	\
+	(DWC_LIST_FIRST(link) == DWC_LIST_END(link))
+#define DWC_LIST_ENTRY(link, type, field)			\
+	(type *)((uint8_t *)(link) - (size_t)(&((type *)0)->field))
+
+#if 0
+#define DWC_LIST_INSERT_HEAD(list, link) do {			\
+	(link)->next = (list)->next;				\
+	(link)->prev = (list);					\
+	(list)->next->prev = (link);				\
+	(list)->next = (link);					\
+} while (0)
+
+#define DWC_LIST_INSERT_TAIL(list, link) do {			\
+	(link)->next = (list);					\
+	(link)->prev = (list)->prev;				\
+	(list)->prev->next = (link);				\
+	(list)->prev = (link);					\
+} while (0)
+#else
+#define DWC_LIST_INSERT_HEAD(list, link) do {			\
+	dwc_list_link_t *__next__ = (list)->next;		\
+	__next__->prev = (link);				\
+	(link)->next = __next__;				\
+	(link)->prev = (list);					\
+	(list)->next = (link);					\
+} while (0)
+
+#define DWC_LIST_INSERT_TAIL(list, link) do {			\
+	dwc_list_link_t *__prev__ = (list)->prev;		\
+	(list)->prev = (link);					\
+	(link)->next = (list);					\
+	(link)->prev = __prev__;				\
+	__prev__->next = (link);				\
+} while (0)
+#endif
+
+#if 0
+static inline void __list_add(struct list_head *new,
+                              struct list_head *prev,
+                              struct list_head *next)
+{
+        next->prev = new;
+        new->next = next;
+        new->prev = prev;
+        prev->next = new;
+}
+
+static inline void list_add(struct list_head *new, struct list_head *head)
+{
+        __list_add(new, head, head->next);
+}
+
+static inline void list_add_tail(struct list_head *new, struct list_head *head)
+{
+        __list_add(new, head->prev, head);
+}
+
+static inline void __list_del(struct list_head * prev, struct list_head * next)
+{
+        next->prev = prev;
+        prev->next = next;
+}
+
+static inline void list_del(struct list_head *entry)
+{
+        __list_del(entry->prev, entry->next);
+        entry->next = LIST_POISON1;
+        entry->prev = LIST_POISON2;
+}
+#endif
+
+#define DWC_LIST_REMOVE(link) do {				\
+	(link)->next->prev = (link)->prev;			\
+	(link)->prev->next = (link)->next;			\
+} while (0)
+
+#define DWC_LIST_REMOVE_INIT(link) do {				\
+	DWC_LIST_REMOVE(link);					\
+	DWC_LIST_INIT(link);					\
+} while (0)
+
+#define DWC_LIST_MOVE_HEAD(list, link) do {			\
+	DWC_LIST_REMOVE(link);					\
+	DWC_LIST_INSERT_HEAD(list, link);			\
+} while (0)
+
+#define DWC_LIST_MOVE_TAIL(list, link) do {			\
+	DWC_LIST_REMOVE(link);					\
+	DWC_LIST_INSERT_TAIL(list, link);			\
+} while (0)
+
+#define DWC_LIST_FOREACH(var, list)				\
+	for((var) = DWC_LIST_FIRST(list);			\
+	    (var) != DWC_LIST_END(list);			\
+	    (var) = DWC_LIST_NEXT(var))
+
+#define DWC_LIST_FOREACH_SAFE(var, var2, list)			\
+	for((var) = DWC_LIST_FIRST(list), (var2) = DWC_LIST_NEXT(var);	\
+	    (var) != DWC_LIST_END(list);			\
+	    (var) = (var2), (var2) = DWC_LIST_NEXT(var2))
+
+#define DWC_LIST_FOREACH_REVERSE(var, list)			\
+	for((var) = DWC_LIST_LAST(list);			\
+	    (var) != DWC_LIST_END(list);			\
+	    (var) = DWC_LIST_PREV(var))
+
+/*
+ * Singly-linked List definitions.
+ */
+#define DWC_SLIST_HEAD(name, type)					\
+struct name {								\
+	struct type *slh_first;	/* first element */			\
+}
+
+#define DWC_SLIST_HEAD_INITIALIZER(head)				\
+	{ NULL }
+
+#define DWC_SLIST_ENTRY(type)						\
+struct {								\
+	struct type *sle_next;	/* next element */			\
+}
+
+/*
+ * Singly-linked List access methods.
+ */
+#define DWC_SLIST_FIRST(head)	((head)->slh_first)
+#define DWC_SLIST_END(head)		NULL
+#define DWC_SLIST_EMPTY(head)	(SLIST_FIRST(head) == SLIST_END(head))
+#define DWC_SLIST_NEXT(elm, field)	((elm)->field.sle_next)
+
+#define DWC_SLIST_FOREACH(var, head, field)				\
+	for((var) = SLIST_FIRST(head);					\
+	    (var) != SLIST_END(head);					\
+	    (var) = SLIST_NEXT(var, field))
+
+#define DWC_SLIST_FOREACH_PREVPTR(var, varp, head, field)		\
+	for((varp) = &SLIST_FIRST((head));				\
+	    ((var) = *(varp)) != SLIST_END(head);			\
+	    (varp) = &SLIST_NEXT((var), field))
+
+/*
+ * Singly-linked List functions.
+ */
+#define DWC_SLIST_INIT(head) {						\
+	SLIST_FIRST(head) = SLIST_END(head);				\
+}
+
+#define DWC_SLIST_INSERT_AFTER(slistelm, elm, field) do {		\
+	(elm)->field.sle_next = (slistelm)->field.sle_next;		\
+	(slistelm)->field.sle_next = (elm);				\
+} while (0)
+
+#define DWC_SLIST_INSERT_HEAD(head, elm, field) do {			\
+	(elm)->field.sle_next = (head)->slh_first;			\
+	(head)->slh_first = (elm);					\
+} while (0)
+
+#define DWC_SLIST_REMOVE_NEXT(head, elm, field) do {			\
+	(elm)->field.sle_next = (elm)->field.sle_next->field.sle_next;	\
+} while (0)
+
+#define DWC_SLIST_REMOVE_HEAD(head, field) do {				\
+	(head)->slh_first = (head)->slh_first->field.sle_next;		\
+} while (0)
+
+#define DWC_SLIST_REMOVE(head, elm, type, field) do {			\
+	if ((head)->slh_first == (elm)) {				\
+		SLIST_REMOVE_HEAD((head), field);			\
+	}								\
+	else {								\
+		struct type *curelm = (head)->slh_first;		\
+		while( curelm->field.sle_next != (elm) )		\
+			curelm = curelm->field.sle_next;		\
+		curelm->field.sle_next =				\
+		    curelm->field.sle_next->field.sle_next;		\
+	}								\
+} while (0)
+
+/*
+ * Simple queue definitions.
+ */
+#define DWC_SIMPLEQ_HEAD(name, type)					\
+struct name {								\
+	struct type *sqh_first;	/* first element */			\
+	struct type **sqh_last;	/* addr of last next element */		\
+}
+
+#define DWC_SIMPLEQ_HEAD_INITIALIZER(head)				\
+	{ NULL, &(head).sqh_first }
+
+#define DWC_SIMPLEQ_ENTRY(type)						\
+struct {								\
+	struct type *sqe_next;	/* next element */			\
+}
+
+/*
+ * Simple queue access methods.
+ */
+#define DWC_SIMPLEQ_FIRST(head)	    ((head)->sqh_first)
+#define DWC_SIMPLEQ_END(head)	    NULL
+#define DWC_SIMPLEQ_EMPTY(head)	    (SIMPLEQ_FIRST(head) == SIMPLEQ_END(head))
+#define DWC_SIMPLEQ_NEXT(elm, field)    ((elm)->field.sqe_next)
+
+#define DWC_SIMPLEQ_FOREACH(var, head, field)				\
+	for((var) = SIMPLEQ_FIRST(head);				\
+	    (var) != SIMPLEQ_END(head);					\
+	    (var) = SIMPLEQ_NEXT(var, field))
+
+/*
+ * Simple queue functions.
+ */
+#define DWC_SIMPLEQ_INIT(head) do {					\
+	(head)->sqh_first = NULL;					\
+	(head)->sqh_last = &(head)->sqh_first;				\
+} while (0)
+
+#define DWC_SIMPLEQ_INSERT_HEAD(head, elm, field) do {			\
+	if (((elm)->field.sqe_next = (head)->sqh_first) == NULL)	\
+		(head)->sqh_last = &(elm)->field.sqe_next;		\
+	(head)->sqh_first = (elm);					\
+} while (0)
+
+#define DWC_SIMPLEQ_INSERT_TAIL(head, elm, field) do {			\
+	(elm)->field.sqe_next = NULL;					\
+	*(head)->sqh_last = (elm);					\
+	(head)->sqh_last = &(elm)->field.sqe_next;			\
+} while (0)
+
+#define DWC_SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do {	\
+	if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\
+		(head)->sqh_last = &(elm)->field.sqe_next;		\
+	(listelm)->field.sqe_next = (elm);				\
+} while (0)
+
+#define DWC_SIMPLEQ_REMOVE_HEAD(head, field) do {			\
+	if (((head)->sqh_first = (head)->sqh_first->field.sqe_next) == NULL) \
+		(head)->sqh_last = &(head)->sqh_first;			\
+} while (0)
+
+/*
+ * Tail queue definitions.
+ */
+#define DWC_TAILQ_HEAD(name, type)					\
+struct name {								\
+	struct type *tqh_first;	/* first element */			\
+	struct type **tqh_last;	/* addr of last next element */		\
+}
+
+#define DWC_TAILQ_HEAD_INITIALIZER(head)				\
+	{ NULL, &(head).tqh_first }
+
+#define DWC_TAILQ_ENTRY(type)						\
+struct {								\
+	struct type *tqe_next;	/* next element */			\
+	struct type **tqe_prev;	/* address of previous next element */	\
+}
+
+/*
+ * tail queue access methods
+ */
+#define DWC_TAILQ_FIRST(head)		((head)->tqh_first)
+#define DWC_TAILQ_END(head)		NULL
+#define DWC_TAILQ_NEXT(elm, field)	((elm)->field.tqe_next)
+#define DWC_TAILQ_LAST(head, headname)					\
+	(*(((struct headname *)((head)->tqh_last))->tqh_last))
+/* XXX */
+#define DWC_TAILQ_PREV(elm, headname, field)				\
+	(*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
+#define DWC_TAILQ_EMPTY(head)						\
+	(DWC_TAILQ_FIRST(head) == DWC_TAILQ_END(head))
+
+#define DWC_TAILQ_FOREACH(var, head, field)				\
+	for ((var) = DWC_TAILQ_FIRST(head);				\
+	    (var) != DWC_TAILQ_END(head);				\
+	    (var) = DWC_TAILQ_NEXT(var, field))
+
+#define DWC_TAILQ_FOREACH_REVERSE(var, head, headname, field)		\
+	for ((var) = DWC_TAILQ_LAST(head, headname);			\
+	    (var) != DWC_TAILQ_END(head);				\
+	    (var) = DWC_TAILQ_PREV(var, headname, field))
+
+/*
+ * Tail queue functions.
+ */
+#define DWC_TAILQ_INIT(head) do {					\
+	(head)->tqh_first = NULL;					\
+	(head)->tqh_last = &(head)->tqh_first;				\
+} while (0)
+
+#define DWC_TAILQ_INSERT_HEAD(head, elm, field) do {			\
+	if (((elm)->field.tqe_next = (head)->tqh_first) != NULL)	\
+		(head)->tqh_first->field.tqe_prev =			\
+		    &(elm)->field.tqe_next;				\
+	else								\
+		(head)->tqh_last = &(elm)->field.tqe_next;		\
+	(head)->tqh_first = (elm);					\
+	(elm)->field.tqe_prev = &(head)->tqh_first;			\
+} while (0)
+
+#define DWC_TAILQ_INSERT_TAIL(head, elm, field) do {			\
+	(elm)->field.tqe_next = NULL;					\
+	(elm)->field.tqe_prev = (head)->tqh_last;			\
+	*(head)->tqh_last = (elm);					\
+	(head)->tqh_last = &(elm)->field.tqe_next;			\
+} while (0)
+
+#define DWC_TAILQ_INSERT_AFTER(head, listelm, elm, field) do {		\
+	if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\
+		(elm)->field.tqe_next->field.tqe_prev =			\
+		    &(elm)->field.tqe_next;				\
+	else								\
+		(head)->tqh_last = &(elm)->field.tqe_next;		\
+	(listelm)->field.tqe_next = (elm);				\
+	(elm)->field.tqe_prev = &(listelm)->field.tqe_next;		\
+} while (0)
+
+#define DWC_TAILQ_INSERT_BEFORE(listelm, elm, field) do {		\
+	(elm)->field.tqe_prev = (listelm)->field.tqe_prev;		\
+	(elm)->field.tqe_next = (listelm);				\
+	*(listelm)->field.tqe_prev = (elm);				\
+	(listelm)->field.tqe_prev = &(elm)->field.tqe_next;		\
+} while (0)
+
+#define DWC_TAILQ_REMOVE(head, elm, field) do {				\
+	if (((elm)->field.tqe_next) != NULL)				\
+		(elm)->field.tqe_next->field.tqe_prev =			\
+		    (elm)->field.tqe_prev;				\
+	else								\
+		(head)->tqh_last = (elm)->field.tqe_prev;		\
+	*(elm)->field.tqe_prev = (elm)->field.tqe_next;			\
+} while (0)
+
+#define DWC_TAILQ_REPLACE(head, elm, elm2, field) do {			\
+	if (((elm2)->field.tqe_next = (elm)->field.tqe_next) != NULL)	\
+		(elm2)->field.tqe_next->field.tqe_prev =		\
+		    &(elm2)->field.tqe_next;				\
+	else								\
+		(head)->tqh_last = &(elm2)->field.tqe_next;		\
+	(elm2)->field.tqe_prev = (elm)->field.tqe_prev;			\
+	*(elm2)->field.tqe_prev = (elm2);				\
+} while (0)
+
+/*
+ * Circular queue definitions.
+ */
+#define DWC_CIRCLEQ_HEAD(name, type)					\
+struct name {								\
+	struct type *cqh_first;		/* first element */		\
+	struct type *cqh_last;		/* last element */		\
+}
+
+#define DWC_CIRCLEQ_HEAD_INITIALIZER(head)				\
+	{ DWC_CIRCLEQ_END(&head), DWC_CIRCLEQ_END(&head) }
+
+#define DWC_CIRCLEQ_ENTRY(type)						\
+struct {								\
+	struct type *cqe_next;		/* next element */		\
+	struct type *cqe_prev;		/* previous element */		\
+}
+
+/*
+ * Circular queue access methods
+ */
+#define DWC_CIRCLEQ_FIRST(head)		((head)->cqh_first)
+#define DWC_CIRCLEQ_LAST(head)		((head)->cqh_last)
+#define DWC_CIRCLEQ_END(head)		((void *)(head))
+#define DWC_CIRCLEQ_NEXT(elm, field)	((elm)->field.cqe_next)
+#define DWC_CIRCLEQ_PREV(elm, field)	((elm)->field.cqe_prev)
+#define DWC_CIRCLEQ_EMPTY(head)						\
+	(DWC_CIRCLEQ_FIRST(head) == DWC_CIRCLEQ_END(head))
+
+#define DWC_CIRCLEQ_EMPTY_ENTRY(elm, field) (((elm)->field.cqe_next == NULL) && ((elm)->field.cqe_prev == NULL))
+
+#define DWC_CIRCLEQ_FOREACH(var, head, field)				\
+	for((var) = DWC_CIRCLEQ_FIRST(head);				\
+	    (var) != DWC_CIRCLEQ_END(head);				\
+	    (var) = DWC_CIRCLEQ_NEXT(var, field))
+
+#define DWC_CIRCLEQ_FOREACH_SAFE(var, var2, head, field)			\
+	for((var) = DWC_CIRCLEQ_FIRST(head), var2 = DWC_CIRCLEQ_NEXT(var, field); \
+	    (var) != DWC_CIRCLEQ_END(head);					\
+	    (var) = var2, var2 = DWC_CIRCLEQ_NEXT(var, field))
+
+#define DWC_CIRCLEQ_FOREACH_REVERSE(var, head, field)			\
+	for((var) = DWC_CIRCLEQ_LAST(head);				\
+	    (var) != DWC_CIRCLEQ_END(head);				\
+	    (var) = DWC_CIRCLEQ_PREV(var, field))
+
+/*
+ * Circular queue functions.
+ */
+#define DWC_CIRCLEQ_INIT(head) do {					\
+	(head)->cqh_first = DWC_CIRCLEQ_END(head);			\
+	(head)->cqh_last = DWC_CIRCLEQ_END(head);			\
+} while (0)
+
+#define DWC_CIRCLEQ_INIT_ENTRY(elm, field) do {				\
+	(elm)->field.cqe_next = NULL;					\
+	(elm)->field.cqe_prev = NULL;					\
+} while (0)
+
+#define DWC_CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) do {	\
+	(elm)->field.cqe_next = (listelm)->field.cqe_next;		\
+	(elm)->field.cqe_prev = (listelm);				\
+	if ((listelm)->field.cqe_next == DWC_CIRCLEQ_END(head))		\
+		(head)->cqh_last = (elm);				\
+	else								\
+		(listelm)->field.cqe_next->field.cqe_prev = (elm);	\
+	(listelm)->field.cqe_next = (elm);				\
+} while (0)
+
+#define DWC_CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) do {	\
+	(elm)->field.cqe_next = (listelm);				\
+	(elm)->field.cqe_prev = (listelm)->field.cqe_prev;		\
+	if ((listelm)->field.cqe_prev == DWC_CIRCLEQ_END(head))		\
+		(head)->cqh_first = (elm);				\
+	else								\
+		(listelm)->field.cqe_prev->field.cqe_next = (elm);	\
+	(listelm)->field.cqe_prev = (elm);				\
+} while (0)
+
+#define DWC_CIRCLEQ_INSERT_HEAD(head, elm, field) do {			\
+	(elm)->field.cqe_next = (head)->cqh_first;			\
+	(elm)->field.cqe_prev = DWC_CIRCLEQ_END(head);			\
+	if ((head)->cqh_last == DWC_CIRCLEQ_END(head))			\
+		(head)->cqh_last = (elm);				\
+	else								\
+		(head)->cqh_first->field.cqe_prev = (elm);		\
+	(head)->cqh_first = (elm);					\
+} while (0)
+
+#define DWC_CIRCLEQ_INSERT_TAIL(head, elm, field) do {			\
+	(elm)->field.cqe_next = DWC_CIRCLEQ_END(head);			\
+	(elm)->field.cqe_prev = (head)->cqh_last;			\
+	if ((head)->cqh_first == DWC_CIRCLEQ_END(head))			\
+		(head)->cqh_first = (elm);				\
+	else								\
+		(head)->cqh_last->field.cqe_next = (elm);		\
+	(head)->cqh_last = (elm);					\
+} while (0)
+
+#define DWC_CIRCLEQ_REMOVE(head, elm, field) do {			\
+	if ((elm)->field.cqe_next == DWC_CIRCLEQ_END(head))		\
+		(head)->cqh_last = (elm)->field.cqe_prev;		\
+	else								\
+		(elm)->field.cqe_next->field.cqe_prev =			\
+		    (elm)->field.cqe_prev;				\
+	if ((elm)->field.cqe_prev == DWC_CIRCLEQ_END(head))		\
+		(head)->cqh_first = (elm)->field.cqe_next;		\
+	else								\
+		(elm)->field.cqe_prev->field.cqe_next =			\
+		    (elm)->field.cqe_next;				\
+} while (0)
+
+#define DWC_CIRCLEQ_REMOVE_INIT(head, elm, field) do {			\
+	DWC_CIRCLEQ_REMOVE(head, elm, field);				\
+	DWC_CIRCLEQ_INIT_ENTRY(elm, field);				\
+} while (0)
+
+#define DWC_CIRCLEQ_REPLACE(head, elm, elm2, field) do {		\
+	if (((elm2)->field.cqe_next = (elm)->field.cqe_next) ==		\
+	    DWC_CIRCLEQ_END(head))					\
+		(head).cqh_last = (elm2);				\
+	else								\
+		(elm2)->field.cqe_next->field.cqe_prev = (elm2);	\
+	if (((elm2)->field.cqe_prev = (elm)->field.cqe_prev) ==		\
+	    DWC_CIRCLEQ_END(head))					\
+		(head).cqh_first = (elm2);				\
+	else								\
+		(elm2)->field.cqe_prev->field.cqe_next = (elm2);	\
+} while (0)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _DWC_LIST_H_ */
diff -Nur --no-dereference linux-5.10.52-orig/drivers/usb/host/dwc_common_port/dwc_mem.c linux-5.10.52-v7l+/drivers/usb/host/dwc_common_port/dwc_mem.c
--- linux-5.10.52-orig/drivers/usb/host/dwc_common_port/dwc_mem.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/usb/host/dwc_common_port/dwc_mem.c	2021-07-25 16:46:28.657910448 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/* Memory Debugging */
+#ifdef DWC_DEBUG_MEMORY
+
+#include "dwc_os.h"
+#include "dwc_list.h"
+
+struct allocation {
+	void *addr;
+	void *ctx;
+	char *func;
+	int line;
+	uint32_t size;
+	int dma;
+	DWC_CIRCLEQ_ENTRY(allocation) entry;
+};
+
+DWC_CIRCLEQ_HEAD(allocation_queue, allocation);
+
+struct allocation_manager {
+	void *mem_ctx;
+	struct allocation_queue allocations;
+
+	/* statistics */
+	int num;
+	int num_freed;
+	int num_active;
+	uint32_t total;
+	uint32_t cur;
+	uint32_t max;
+};
+
+static struct allocation_manager *manager = NULL;
+
+static int add_allocation(void *ctx, uint32_t size, char const *func, int line, void *addr,
+			  int dma)
+{
+	struct allocation *a;
+
+	DWC_ASSERT(manager != NULL, "manager not allocated");
+
+	a = __DWC_ALLOC_ATOMIC(manager->mem_ctx, sizeof(*a));
+	if (!a) {
+		return -DWC_E_NO_MEMORY;
+	}
+
+	a->func = __DWC_ALLOC_ATOMIC(manager->mem_ctx, DWC_STRLEN(func) + 1);
+	if (!a->func) {
+		__DWC_FREE(manager->mem_ctx, a);
+		return -DWC_E_NO_MEMORY;
+	}
+
+	DWC_MEMCPY(a->func, func, DWC_STRLEN(func) + 1);
+	a->addr = addr;
+	a->ctx = ctx;
+	a->line = line;
+	a->size = size;
+	a->dma = dma;
+	DWC_CIRCLEQ_INSERT_TAIL(&manager->allocations, a, entry);
+
+	/* Update stats */
+	manager->num++;
+	manager->num_active++;
+	manager->total += size;
+	manager->cur += size;
+
+	if (manager->max < manager->cur) {
+		manager->max = manager->cur;
+	}
+
+	return 0;
+}
+
+static struct allocation *find_allocation(void *ctx, void *addr)
+{
+	struct allocation *a;
+
+	DWC_CIRCLEQ_FOREACH(a, &manager->allocations, entry) {
+		if (a->ctx == ctx && a->addr == addr) {
+			return a;
+		}
+	}
+
+	return NULL;
+}
+
+static void free_allocation(void *ctx, void *addr, char const *func, int line)
+{
+	struct allocation *a = find_allocation(ctx, addr);
+
+	if (!a) {
+		DWC_ASSERT(0,
+			   "Free of address %p that was never allocated or already freed %s:%d",
+			   addr, func, line);
+		return;
+	}
+
+	DWC_CIRCLEQ_REMOVE(&manager->allocations, a, entry);
+
+	manager->num_active--;
+	manager->num_freed++;
+	manager->cur -= a->size;
+	__DWC_FREE(manager->mem_ctx, a->func);
+	__DWC_FREE(manager->mem_ctx, a);
+}
+
+int dwc_memory_debug_start(void *mem_ctx)
+{
+	DWC_ASSERT(manager == NULL, "Memory debugging has already started\n");
+
+	if (manager) {
+		return -DWC_E_BUSY;
+	}
+
+	manager = __DWC_ALLOC(mem_ctx, sizeof(*manager));
+	if (!manager) {
+		return -DWC_E_NO_MEMORY;
+	}
+
+	DWC_CIRCLEQ_INIT(&manager->allocations);
+	manager->mem_ctx = mem_ctx;
+	manager->num = 0;
+	manager->num_freed = 0;
+	manager->num_active = 0;
+	manager->total = 0;
+	manager->cur = 0;
+	manager->max = 0;
+
+	return 0;
+}
+
+void dwc_memory_debug_stop(void)
+{
+	struct allocation *a;
+
+	dwc_memory_debug_report();
+
+	DWC_CIRCLEQ_FOREACH(a, &manager->allocations, entry) {
+		DWC_ERROR("Memory leaked from %s:%d\n", a->func, a->line);
+		free_allocation(a->ctx, a->addr, NULL, -1);
+	}
+
+	__DWC_FREE(manager->mem_ctx, manager);
+}
+
+void dwc_memory_debug_report(void)
+{
+	struct allocation *a;
+
+	DWC_PRINTF("\n\n\n----------------- Memory Debugging Report -----------------\n\n");
+	DWC_PRINTF("Num Allocations = %d\n", manager->num);
+	DWC_PRINTF("Freed = %d\n", manager->num_freed);
+	DWC_PRINTF("Active = %d\n", manager->num_active);
+	DWC_PRINTF("Current Memory Used = %d\n", manager->cur);
+	DWC_PRINTF("Total Memory Used = %d\n", manager->total);
+	DWC_PRINTF("Maximum Memory Used at Once = %d\n", manager->max);
+	DWC_PRINTF("Unfreed allocations:\n");
+
+	DWC_CIRCLEQ_FOREACH(a, &manager->allocations, entry) {
+		DWC_PRINTF("    addr=%p, size=%d from %s:%d, DMA=%d\n",
+			   a->addr, a->size, a->func, a->line, a->dma);
+	}
+}
+
+/* The replacement functions */
+void *dwc_alloc_debug(void *mem_ctx, uint32_t size, char const *func, int line)
+{
+	void *addr = __DWC_ALLOC(mem_ctx, size);
+
+	if (!addr) {
+		return NULL;
+	}
+
+	if (add_allocation(mem_ctx, size, func, line, addr, 0)) {
+		__DWC_FREE(mem_ctx, addr);
+		return NULL;
+	}
+
+	return addr;
+}
+
+void *dwc_alloc_atomic_debug(void *mem_ctx, uint32_t size, char const *func,
+			     int line)
+{
+	void *addr = __DWC_ALLOC_ATOMIC(mem_ctx, size);
+
+	if (!addr) {
+		return NULL;
+	}
+
+	if (add_allocation(mem_ctx, size, func, line, addr, 0)) {
+		__DWC_FREE(mem_ctx, addr);
+		return NULL;
+	}
+
+	return addr;
+}
+
+void dwc_free_debug(void *mem_ctx, void *addr, char const *func, int line)
+{
+	free_allocation(mem_ctx, addr, func, line);
+	__DWC_FREE(mem_ctx, addr);
+}
+
+void *dwc_dma_alloc_debug(void *dma_ctx, uint32_t size, dwc_dma_t *dma_addr,
+			  char const *func, int line)
+{
+	void *addr = __DWC_DMA_ALLOC(dma_ctx, size, dma_addr);
+
+	if (!addr) {
+		return NULL;
+	}
+
+	if (add_allocation(dma_ctx, size, func, line, addr, 1)) {
+		__DWC_DMA_FREE(dma_ctx, size, addr, *dma_addr);
+		return NULL;
+	}
+
+	return addr;
+}
+
+void *dwc_dma_alloc_atomic_debug(void *dma_ctx, uint32_t size,
+				 dwc_dma_t *dma_addr, char const *func, int line)
+{
+	void *addr = __DWC_DMA_ALLOC_ATOMIC(dma_ctx, size, dma_addr);
+
+	if (!addr) {
+		return NULL;
+	}
+
+	if (add_allocation(dma_ctx, size, func, line, addr, 1)) {
+		__DWC_DMA_FREE(dma_ctx, size, addr, *dma_addr);
+		return NULL;
+	}
+
+	return addr;
+}
+
+void dwc_dma_free_debug(void *dma_ctx, uint32_t size, void *virt_addr,
+			dwc_dma_t dma_addr, char const *func, int line)
+{
+	free_allocation(dma_ctx, virt_addr, func, line);
+	__DWC_DMA_FREE(dma_ctx, size, virt_addr, dma_addr);
+}
+
+#endif /* DWC_DEBUG_MEMORY */
diff -Nur --no-dereference linux-5.10.52-orig/drivers/usb/host/dwc_common_port/dwc_modpow.c linux-5.10.52-v7l+/drivers/usb/host/dwc_common_port/dwc_modpow.c
--- linux-5.10.52-orig/drivers/usb/host/dwc_common_port/dwc_modpow.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/usb/host/dwc_common_port/dwc_modpow.c	2021-07-25 16:46:28.657910448 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/* Bignum routines adapted from PUTTY sources.  PuTTY copyright notice follows.
+ *
+ * PuTTY is copyright 1997-2007 Simon Tatham.
+ *
+ * Portions copyright Robert de Bath, Joris van Rantwijk, Delian
+ * Delchev, Andreas Schultz, Jeroen Massar, Wez Furlong, Nicolas Barry,
+ * Justin Bradford, Ben Harris, Malcolm Smith, Ahmad Khalifa, Markus
+ * Kuhn, and CORE SDI S.A.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT.  IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE
+ * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+#ifdef DWC_CRYPTOLIB
+
+#ifndef CONFIG_MACH_IPMATE
+
+#include "dwc_modpow.h"
+
+#define BIGNUM_INT_MASK  0xFFFFFFFFUL
+#define BIGNUM_TOP_BIT   0x80000000UL
+#define BIGNUM_INT_BITS  32
+
+
+static void *snmalloc(void *mem_ctx, size_t n, size_t size)
+{
+    void *p;
+    size *= n;
+    if (size == 0) size = 1;
+    p = dwc_alloc(mem_ctx, size);
+    return p;
+}
+
+#define snewn(ctx, n, type) ((type *)snmalloc((ctx), (n), sizeof(type)))
+#define sfree dwc_free
+
+/*
+ * Usage notes:
+ *  * Do not call the DIVMOD_WORD macro with expressions such as array
+ *    subscripts, as some implementations object to this (see below).
+ *  * Note that none of the division methods below will cope if the
+ *    quotient won't fit into BIGNUM_INT_BITS. Callers should be careful
+ *    to avoid this case.
+ *    If this condition occurs, in the case of the x86 DIV instruction,
+ *    an overflow exception will occur, which (according to a correspondent)
+ *    will manifest on Windows as something like
+ *      0xC0000095: Integer overflow
+ *    The C variant won't give the right answer, either.
+ */
+
+#define MUL_WORD(w1, w2) ((BignumDblInt)w1 * w2)
+
+#if defined __GNUC__ && defined __i386__
+#define DIVMOD_WORD(q, r, hi, lo, w) \
+    __asm__("div %2" : \
+	    "=d" (r), "=a" (q) : \
+	    "r" (w), "d" (hi), "a" (lo))
+#else
+#define DIVMOD_WORD(q, r, hi, lo, w) do { \
+    BignumDblInt n = (((BignumDblInt)hi) << BIGNUM_INT_BITS) | lo; \
+    q = n / w; \
+    r = n % w; \
+} while (0)
+#endif
+
+//    q = n / w;
+//    r = n % w;
+
+#define BIGNUM_INT_BYTES (BIGNUM_INT_BITS / 8)
+
+#define BIGNUM_INTERNAL
+
+static Bignum newbn(void *mem_ctx, int length)
+{
+    Bignum b = snewn(mem_ctx, length + 1, BignumInt);
+    //if (!b)
+    //abort();		       /* FIXME */
+    DWC_MEMSET(b, 0, (length + 1) * sizeof(*b));
+    b[0] = length;
+    return b;
+}
+
+void freebn(void *mem_ctx, Bignum b)
+{
+    /*
+     * Burn the evidence, just in case.
+     */
+    DWC_MEMSET(b, 0, sizeof(b[0]) * (b[0] + 1));
+    sfree(mem_ctx, b);
+}
+
+/*
+ * Compute c = a * b.
+ * Input is in the first len words of a and b.
+ * Result is returned in the first 2*len words of c.
+ */
+static void internal_mul(BignumInt *a, BignumInt *b,
+			 BignumInt *c, int len)
+{
+    int i, j;
+    BignumDblInt t;
+
+    for (j = 0; j < 2 * len; j++)
+	c[j] = 0;
+
+    for (i = len - 1; i >= 0; i--) {
+	t = 0;
+	for (j = len - 1; j >= 0; j--) {
+	    t += MUL_WORD(a[i], (BignumDblInt) b[j]);
+	    t += (BignumDblInt) c[i + j + 1];
+	    c[i + j + 1] = (BignumInt) t;
+	    t = t >> BIGNUM_INT_BITS;
+	}
+	c[i] = (BignumInt) t;
+    }
+}
+
+static void internal_add_shifted(BignumInt *number,
+				 unsigned n, int shift)
+{
+    int word = 1 + (shift / BIGNUM_INT_BITS);
+    int bshift = shift % BIGNUM_INT_BITS;
+    BignumDblInt addend;
+
+    addend = (BignumDblInt)n << bshift;
+
+    while (addend) {
+	addend += number[word];
+	number[word] = (BignumInt) addend & BIGNUM_INT_MASK;
+	addend >>= BIGNUM_INT_BITS;
+	word++;
+    }
+}
+
+/*
+ * Compute a = a % m.
+ * Input in first alen words of a and first mlen words of m.
+ * Output in first alen words of a
+ * (of which first alen-mlen words will be zero).
+ * The MSW of m MUST have its high bit set.
+ * Quotient is accumulated in the `quotient' array, which is a Bignum
+ * rather than the internal bigendian format. Quotient parts are shifted
+ * left by `qshift' before adding into quot.
+ */
+static void internal_mod(BignumInt *a, int alen,
+			 BignumInt *m, int mlen,
+			 BignumInt *quot, int qshift)
+{
+    BignumInt m0, m1;
+    unsigned int h;
+    int i, k;
+
+    m0 = m[0];
+    if (mlen > 1)
+	m1 = m[1];
+    else
+	m1 = 0;
+
+    for (i = 0; i <= alen - mlen; i++) {
+	BignumDblInt t;
+	unsigned int q, r, c, ai1;
+
+	if (i == 0) {
+	    h = 0;
+	} else {
+	    h = a[i - 1];
+	    a[i - 1] = 0;
+	}
+
+	if (i == alen - 1)
+	    ai1 = 0;
+	else
+	    ai1 = a[i + 1];
+
+	/* Find q = h:a[i] / m0 */
+	if (h >= m0) {
+	    /*
+	     * Special case.
+	     *
+	     * To illustrate it, suppose a BignumInt is 8 bits, and
+	     * we are dividing (say) A1:23:45:67 by A1:B2:C3. Then
+	     * our initial division will be 0xA123 / 0xA1, which
+	     * will give a quotient of 0x100 and a divide overflow.
+	     * However, the invariants in this division algorithm
+	     * are not violated, since the full number A1:23:... is
+	     * _less_ than the quotient prefix A1:B2:... and so the
+	     * following correction loop would have sorted it out.
+	     *
+	     * In this situation we set q to be the largest
+	     * quotient we _can_ stomach (0xFF, of course).
+	     */
+	    q = BIGNUM_INT_MASK;
+	} else {
+	    /* Macro doesn't want an array subscript expression passed
+	     * into it (see definition), so use a temporary. */
+	    BignumInt tmplo = a[i];
+	    DIVMOD_WORD(q, r, h, tmplo, m0);
+
+	    /* Refine our estimate of q by looking at
+	     h:a[i]:a[i+1] / m0:m1 */
+	    t = MUL_WORD(m1, q);
+	    if (t > ((BignumDblInt) r << BIGNUM_INT_BITS) + ai1) {
+		q--;
+		t -= m1;
+		r = (r + m0) & BIGNUM_INT_MASK;     /* overflow? */
+		if (r >= (BignumDblInt) m0 &&
+		    t > ((BignumDblInt) r << BIGNUM_INT_BITS) + ai1) q--;
+	    }
+	}
+
+	/* Subtract q * m from a[i...] */
+	c = 0;
+	for (k = mlen - 1; k >= 0; k--) {
+	    t = MUL_WORD(q, m[k]);
+	    t += c;
+	    c = (unsigned)(t >> BIGNUM_INT_BITS);
+	    if ((BignumInt) t > a[i + k])
+		c++;
+	    a[i + k] -= (BignumInt) t;
+	}
+
+	/* Add back m in case of borrow */
+	if (c != h) {
+	    t = 0;
+	    for (k = mlen - 1; k >= 0; k--) {
+		t += m[k];
+		t += a[i + k];
+		a[i + k] = (BignumInt) t;
+		t = t >> BIGNUM_INT_BITS;
+	    }
+	    q--;
+	}
+	if (quot)
+	    internal_add_shifted(quot, q, qshift + BIGNUM_INT_BITS * (alen - mlen - i));
+    }
+}
+
+/*
+ * Compute p % mod.
+ * The most significant word of mod MUST be non-zero.
+ * We assume that the result array is the same size as the mod array.
+ * We optionally write out a quotient if `quotient' is non-NULL.
+ * We can avoid writing out the result if `result' is NULL.
+ */
+void bigdivmod(void *mem_ctx, Bignum p, Bignum mod, Bignum result, Bignum quotient)
+{
+    BignumInt *n, *m;
+    int mshift;
+    int plen, mlen, i, j;
+
+    /* Allocate m of size mlen, copy mod to m */
+    /* We use big endian internally */
+    mlen = mod[0];
+    m = snewn(mem_ctx, mlen, BignumInt);
+    //if (!m)
+    //abort();		       /* FIXME */
+    for (j = 0; j < mlen; j++)
+	m[j] = mod[mod[0] - j];
+
+    /* Shift m left to make msb bit set */
+    for (mshift = 0; mshift < BIGNUM_INT_BITS-1; mshift++)
+	if ((m[0] << mshift) & BIGNUM_TOP_BIT)
+	    break;
+    if (mshift) {
+	for (i = 0; i < mlen - 1; i++)
+	    m[i] = (m[i] << mshift) | (m[i + 1] >> (BIGNUM_INT_BITS - mshift));
+	m[mlen - 1] = m[mlen - 1] << mshift;
+    }
+
+    plen = p[0];
+    /* Ensure plen > mlen */
+    if (plen <= mlen)
+	plen = mlen + 1;
+
+    /* Allocate n of size plen, copy p to n */
+    n = snewn(mem_ctx, plen, BignumInt);
+    //if (!n)
+    //abort();		       /* FIXME */
+    for (j = 0; j < plen; j++)
+	n[j] = 0;
+    for (j = 1; j <= (int)p[0]; j++)
+	n[plen - j] = p[j];
+
+    /* Main computation */
+    internal_mod(n, plen, m, mlen, quotient, mshift);
+
+    /* Fixup result in case the modulus was shifted */
+    if (mshift) {
+	for (i = plen - mlen - 1; i < plen - 1; i++)
+	    n[i] = (n[i] << mshift) | (n[i + 1] >> (BIGNUM_INT_BITS - mshift));
+	n[plen - 1] = n[plen - 1] << mshift;
+	internal_mod(n, plen, m, mlen, quotient, 0);
+	for (i = plen - 1; i >= plen - mlen; i--)
+	    n[i] = (n[i] >> mshift) | (n[i - 1] << (BIGNUM_INT_BITS - mshift));
+    }
+
+    /* Copy result to buffer */
+    if (result) {
+	for (i = 1; i <= (int)result[0]; i++) {
+	    int j = plen - i;
+	    result[i] = j >= 0 ? n[j] : 0;
+	}
+    }
+
+    /* Free temporary arrays */
+    for (i = 0; i < mlen; i++)
+	m[i] = 0;
+    sfree(mem_ctx, m);
+    for (i = 0; i < plen; i++)
+	n[i] = 0;
+    sfree(mem_ctx, n);
+}
+
+/*
+ * Simple remainder.
+ */
+Bignum bigmod(void *mem_ctx, Bignum a, Bignum b)
+{
+    Bignum r = newbn(mem_ctx, b[0]);
+    bigdivmod(mem_ctx, a, b, r, NULL);
+    return r;
+}
+
+/*
+ * Compute (base ^ exp) % mod.
+ */
+Bignum dwc_modpow(void *mem_ctx, Bignum base_in, Bignum exp, Bignum mod)
+{
+    BignumInt *a, *b, *n, *m;
+    int mshift;
+    int mlen, i, j;
+    Bignum base, result;
+
+    /*
+     * The most significant word of mod needs to be non-zero. It
+     * should already be, but let's make sure.
+     */
+    //assert(mod[mod[0]] != 0);
+
+    /*
+     * Make sure the base is smaller than the modulus, by reducing
+     * it modulo the modulus if not.
+     */
+    base = bigmod(mem_ctx, base_in, mod);
+
+    /* Allocate m of size mlen, copy mod to m */
+    /* We use big endian internally */
+    mlen = mod[0];
+    m = snewn(mem_ctx, mlen, BignumInt);
+    //if (!m)
+    //abort();		       /* FIXME */
+    for (j = 0; j < mlen; j++)
+	m[j] = mod[mod[0] - j];
+
+    /* Shift m left to make msb bit set */
+    for (mshift = 0; mshift < BIGNUM_INT_BITS - 1; mshift++)
+	if ((m[0] << mshift) & BIGNUM_TOP_BIT)
+	    break;
+    if (mshift) {
+	for (i = 0; i < mlen - 1; i++)
+	    m[i] =
+		(m[i] << mshift) | (m[i + 1] >>
+				    (BIGNUM_INT_BITS - mshift));
+	m[mlen - 1] = m[mlen - 1] << mshift;
+    }
+
+    /* Allocate n of size mlen, copy base to n */
+    n = snewn(mem_ctx, mlen, BignumInt);
+    //if (!n)
+    //abort();		       /* FIXME */
+    i = mlen - base[0];
+    for (j = 0; j < i; j++)
+	n[j] = 0;
+    for (j = 0; j < base[0]; j++)
+	n[i + j] = base[base[0] - j];
+
+    /* Allocate a and b of size 2*mlen. Set a = 1 */
+    a = snewn(mem_ctx, 2 * mlen, BignumInt);
+    //if (!a)
+    //abort();		       /* FIXME */
+    b = snewn(mem_ctx, 2 * mlen, BignumInt);
+    //if (!b)
+    //abort();		       /* FIXME */
+    for (i = 0; i < 2 * mlen; i++)
+	a[i] = 0;
+    a[2 * mlen - 1] = 1;
+
+    /* Skip leading zero bits of exp. */
+    i = 0;
+    j = BIGNUM_INT_BITS - 1;
+    while (i < exp[0] && (exp[exp[0] - i] & (1 << j)) == 0) {
+	j--;
+	if (j < 0) {
+	    i++;
+	    j = BIGNUM_INT_BITS - 1;
+	}
+    }
+
+    /* Main computation */
+    while (i < exp[0]) {
+	while (j >= 0) {
+	    internal_mul(a + mlen, a + mlen, b, mlen);
+	    internal_mod(b, mlen * 2, m, mlen, NULL, 0);
+	    if ((exp[exp[0] - i] & (1 << j)) != 0) {
+		internal_mul(b + mlen, n, a, mlen);
+		internal_mod(a, mlen * 2, m, mlen, NULL, 0);
+	    } else {
+		BignumInt *t;
+		t = a;
+		a = b;
+		b = t;
+	    }
+	    j--;
+	}
+	i++;
+	j = BIGNUM_INT_BITS - 1;
+    }
+
+    /* Fixup result in case the modulus was shifted */
+    if (mshift) {
+	for (i = mlen - 1; i < 2 * mlen - 1; i++)
+	    a[i] =
+		(a[i] << mshift) | (a[i + 1] >>
+				    (BIGNUM_INT_BITS - mshift));
+	a[2 * mlen - 1] = a[2 * mlen - 1] << mshift;
+	internal_mod(a, mlen * 2, m, mlen, NULL, 0);
+	for (i = 2 * mlen - 1; i >= mlen; i--)
+	    a[i] =
+		(a[i] >> mshift) | (a[i - 1] <<
+				    (BIGNUM_INT_BITS - mshift));
+    }
+
+    /* Copy result to buffer */
+    result = newbn(mem_ctx, mod[0]);
+    for (i = 0; i < mlen; i++)
+	result[result[0] - i] = a[i + mlen];
+    while (result[0] > 1 && result[result[0]] == 0)
+	result[0]--;
+
+    /* Free temporary arrays */
+    for (i = 0; i < 2 * mlen; i++)
+	a[i] = 0;
+    sfree(mem_ctx, a);
+    for (i = 0; i < 2 * mlen; i++)
+	b[i] = 0;
+    sfree(mem_ctx, b);
+    for (i = 0; i < mlen; i++)
+	m[i] = 0;
+    sfree(mem_ctx, m);
+    for (i = 0; i < mlen; i++)
+	n[i] = 0;
+    sfree(mem_ctx, n);
+
+    freebn(mem_ctx, base);
+
+    return result;
+}
+
+
+#ifdef UNITTEST
+
+static __u32 dh_p[] = {
+	96,
+	0xFFFFFFFF,
+	0xFFFFFFFF,
+	0xA93AD2CA,
+	0x4B82D120,
+	0xE0FD108E,
+	0x43DB5BFC,
+	0x74E5AB31,
+	0x08E24FA0,
+	0xBAD946E2,
+	0x770988C0,
+	0x7A615D6C,
+	0xBBE11757,
+	0x177B200C,
+	0x521F2B18,
+	0x3EC86A64,
+	0xD8760273,
+	0xD98A0864,
+	0xF12FFA06,
+	0x1AD2EE6B,
+	0xCEE3D226,
+	0x4A25619D,
+	0x1E8C94E0,
+	0xDB0933D7,
+	0xABF5AE8C,
+	0xA6E1E4C7,
+	0xB3970F85,
+	0x5D060C7D,
+	0x8AEA7157,
+	0x58DBEF0A,
+	0xECFB8504,
+	0xDF1CBA64,
+	0xA85521AB,
+	0x04507A33,
+	0xAD33170D,
+	0x8AAAC42D,
+	0x15728E5A,
+	0x98FA0510,
+	0x15D22618,
+	0xEA956AE5,
+	0x3995497C,
+	0x95581718,
+	0xDE2BCBF6,
+	0x6F4C52C9,
+	0xB5C55DF0,
+	0xEC07A28F,
+	0x9B2783A2,
+	0x180E8603,
+	0xE39E772C,
+	0x2E36CE3B,
+	0x32905E46,
+	0xCA18217C,
+	0xF1746C08,
+	0x4ABC9804,
+	0x670C354E,
+	0x7096966D,
+	0x9ED52907,
+	0x208552BB,
+	0x1C62F356,
+	0xDCA3AD96,
+	0x83655D23,
+	0xFD24CF5F,
+	0x69163FA8,
+	0x1C55D39A,
+	0x98DA4836,
+	0xA163BF05,
+	0xC2007CB8,
+	0xECE45B3D,
+	0x49286651,
+	0x7C4B1FE6,
+	0xAE9F2411,
+	0x5A899FA5,
+	0xEE386BFB,
+	0xF406B7ED,
+	0x0BFF5CB6,
+	0xA637ED6B,
+	0xF44C42E9,
+	0x625E7EC6,
+	0xE485B576,
+	0x6D51C245,
+	0x4FE1356D,
+	0xF25F1437,
+	0x302B0A6D,
+	0xCD3A431B,
+	0xEF9519B3,
+	0x8E3404DD,
+	0x514A0879,
+	0x3B139B22,
+	0x020BBEA6,
+	0x8A67CC74,
+	0x29024E08,
+	0x80DC1CD1,
+	0xC4C6628B,
+	0x2168C234,
+	0xC90FDAA2,
+	0xFFFFFFFF,
+	0xFFFFFFFF,
+};
+
+static __u32 dh_a[] = {
+	8,
+	0xdf367516,
+	0x86459caa,
+	0xe2d459a4,
+	0xd910dae0,
+	0x8a8b5e37,
+	0x67ab31c6,
+	0xf0b55ea9,
+	0x440051d6,
+};
+
+static __u32 dh_b[] = {
+	8,
+	0xded92656,
+	0xe07a048a,
+	0x6fa452cd,
+	0x2df89d30,
+	0xc75f1b0f,
+	0x8ce3578f,
+	0x7980a324,
+	0x5daec786,
+};
+
+static __u32 dh_g[] = {
+	1,
+	2,
+};
+
+int main(void)
+{
+	int i;
+	__u32 *k;
+	k = dwc_modpow(NULL, dh_g, dh_a, dh_p);
+
+	printf("\n\n");
+	for (i=0; i<k[0]; i++) {
+		__u32 word32 = k[k[0] - i];
+		__u16 l = word32 & 0xffff;
+		__u16 m = (word32 & 0xffff0000) >> 16;
+		printf("%04x %04x ", m, l);
+		if (!((i + 1)%13)) printf("\n");
+	}
+	printf("\n\n");
+
+	if ((k[0] == 0x60) && (k[1] == 0x28e490e5) && (k[0x60] == 0x5a0d3d4e)) {
+		printf("PASS\n\n");
+	}
+	else {
+		printf("FAIL\n\n");
+	}
+
+}
+
+#endif /* UNITTEST */
+
+#endif /* CONFIG_MACH_IPMATE */
+
+#endif /*DWC_CRYPTOLIB */
diff -Nur --no-dereference linux-5.10.52-orig/drivers/usb/host/dwc_common_port/dwc_modpow.h linux-5.10.52-v7l+/drivers/usb/host/dwc_common_port/dwc_modpow.h
--- linux-5.10.52-orig/drivers/usb/host/dwc_common_port/dwc_modpow.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/usb/host/dwc_common_port/dwc_modpow.h	2021-07-25 16:46:28.657910448 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/*
+ * dwc_modpow.h
+ * See dwc_modpow.c for license and changes
+ */
+#ifndef _DWC_MODPOW_H
+#define _DWC_MODPOW_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "dwc_os.h"
+
+/** @file
+ *
+ * This file defines the module exponentiation function which is only used
+ * internally by the DWC UWB modules for calculation of PKs during numeric
+ * association.  The routine is taken from the PUTTY, an open source terminal
+ * emulator.  The PUTTY License is preserved in the dwc_modpow.c file.
+ *
+ */
+
+typedef uint32_t BignumInt;
+typedef uint64_t BignumDblInt;
+typedef BignumInt *Bignum;
+
+/* Compute modular exponentiaion */
+extern Bignum dwc_modpow(void *mem_ctx, Bignum base_in, Bignum exp, Bignum mod);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LINUX_BIGNUM_H */
diff -Nur --no-dereference linux-5.10.52-orig/drivers/usb/host/dwc_common_port/dwc_notifier.c linux-5.10.52-v7l+/drivers/usb/host/dwc_common_port/dwc_notifier.c
--- linux-5.10.52-orig/drivers/usb/host/dwc_common_port/dwc_notifier.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/usb/host/dwc_common_port/dwc_notifier.c	2021-07-25 16:46:28.657910448 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+#ifdef DWC_NOTIFYLIB
+
+#include "dwc_notifier.h"
+#include "dwc_list.h"
+
+typedef struct dwc_observer {
+	void *observer;
+	dwc_notifier_callback_t callback;
+	void *data;
+	char *notification;
+	DWC_CIRCLEQ_ENTRY(dwc_observer) list_entry;
+} observer_t;
+
+DWC_CIRCLEQ_HEAD(observer_queue, dwc_observer);
+
+typedef struct dwc_notifier {
+	void *mem_ctx;
+	void *object;
+	struct observer_queue observers;
+	DWC_CIRCLEQ_ENTRY(dwc_notifier) list_entry;
+} notifier_t;
+
+DWC_CIRCLEQ_HEAD(notifier_queue, dwc_notifier);
+
+typedef struct manager {
+	void *mem_ctx;
+	void *wkq_ctx;
+	dwc_workq_t *wq;
+//	dwc_mutex_t *mutex;
+	struct notifier_queue notifiers;
+} manager_t;
+
+static manager_t *manager = NULL;
+
+static int create_manager(void *mem_ctx, void *wkq_ctx)
+{
+	manager = dwc_alloc(mem_ctx, sizeof(manager_t));
+	if (!manager) {
+		return -DWC_E_NO_MEMORY;
+	}
+
+	DWC_CIRCLEQ_INIT(&manager->notifiers);
+
+	manager->wq = dwc_workq_alloc(wkq_ctx, "DWC Notification WorkQ");
+	if (!manager->wq) {
+		return -DWC_E_NO_MEMORY;
+	}
+
+	return 0;
+}
+
+static void free_manager(void)
+{
+	dwc_workq_free(manager->wq);
+
+	/* All notifiers must have unregistered themselves before this module
+	 * can be removed.  Hitting this assertion indicates a programmer
+	 * error. */
+	DWC_ASSERT(DWC_CIRCLEQ_EMPTY(&manager->notifiers),
+		   "Notification manager being freed before all notifiers have been removed");
+	dwc_free(manager->mem_ctx, manager);
+}
+
+#ifdef DEBUG
+static void dump_manager(void)
+{
+	notifier_t *n;
+	observer_t *o;
+
+	DWC_ASSERT(manager, "Notification manager not found");
+
+	DWC_DEBUG("List of all notifiers and observers:\n");
+	DWC_CIRCLEQ_FOREACH(n, &manager->notifiers, list_entry) {
+		DWC_DEBUG("Notifier %p has observers:\n", n->object);
+		DWC_CIRCLEQ_FOREACH(o, &n->observers, list_entry) {
+			DWC_DEBUG("    %p watching %s\n", o->observer, o->notification);
+		}
+	}
+}
+#else
+#define dump_manager(...)
+#endif
+
+static observer_t *alloc_observer(void *mem_ctx, void *observer, char *notification,
+				  dwc_notifier_callback_t callback, void *data)
+{
+	observer_t *new_observer = dwc_alloc(mem_ctx, sizeof(observer_t));
+
+	if (!new_observer) {
+		return NULL;
+	}
+
+	DWC_CIRCLEQ_INIT_ENTRY(new_observer, list_entry);
+	new_observer->observer = observer;
+	new_observer->notification = notification;
+	new_observer->callback = callback;
+	new_observer->data = data;
+	return new_observer;
+}
+
+static void free_observer(void *mem_ctx, observer_t *observer)
+{
+	dwc_free(mem_ctx, observer);
+}
+
+static notifier_t *alloc_notifier(void *mem_ctx, void *object)
+{
+	notifier_t *notifier;
+
+	if (!object) {
+		return NULL;
+	}
+
+	notifier = dwc_alloc(mem_ctx, sizeof(notifier_t));
+	if (!notifier) {
+		return NULL;
+	}
+
+	DWC_CIRCLEQ_INIT(&notifier->observers);
+	DWC_CIRCLEQ_INIT_ENTRY(notifier, list_entry);
+
+	notifier->mem_ctx = mem_ctx;
+	notifier->object = object;
+	return notifier;
+}
+
+static void free_notifier(notifier_t *notifier)
+{
+	observer_t *observer;
+
+	DWC_CIRCLEQ_FOREACH(observer, &notifier->observers, list_entry) {
+		free_observer(notifier->mem_ctx, observer);
+	}
+
+	dwc_free(notifier->mem_ctx, notifier);
+}
+
+static notifier_t *find_notifier(void *object)
+{
+	notifier_t *notifier;
+
+	DWC_ASSERT(manager, "Notification manager not found");
+
+	if (!object) {
+		return NULL;
+	}
+
+	DWC_CIRCLEQ_FOREACH(notifier, &manager->notifiers, list_entry) {
+		if (notifier->object == object) {
+			return notifier;
+		}
+	}
+
+	return NULL;
+}
+
+int dwc_alloc_notification_manager(void *mem_ctx, void *wkq_ctx)
+{
+	return create_manager(mem_ctx, wkq_ctx);
+}
+
+void dwc_free_notification_manager(void)
+{
+	free_manager();
+}
+
+dwc_notifier_t *dwc_register_notifier(void *mem_ctx, void *object)
+{
+	notifier_t *notifier;
+
+	DWC_ASSERT(manager, "Notification manager not found");
+
+	notifier = find_notifier(object);
+	if (notifier) {
+		DWC_ERROR("Notifier %p is already registered\n", object);
+		return NULL;
+	}
+
+	notifier = alloc_notifier(mem_ctx, object);
+	if (!notifier) {
+		return NULL;
+	}
+
+	DWC_CIRCLEQ_INSERT_TAIL(&manager->notifiers, notifier, list_entry);
+
+	DWC_INFO("Notifier %p registered", object);
+	dump_manager();
+
+	return notifier;
+}
+
+void dwc_unregister_notifier(dwc_notifier_t *notifier)
+{
+	DWC_ASSERT(manager, "Notification manager not found");
+
+	if (!DWC_CIRCLEQ_EMPTY(&notifier->observers)) {
+		observer_t *o;
+
+		DWC_ERROR("Notifier %p has active observers when removing\n", notifier->object);
+		DWC_CIRCLEQ_FOREACH(o, &notifier->observers, list_entry) {
+			DWC_DEBUGC("    %p watching %s\n", o->observer, o->notification);
+		}
+
+		DWC_ASSERT(DWC_CIRCLEQ_EMPTY(&notifier->observers),
+			   "Notifier %p has active observers when removing", notifier);
+	}
+
+	DWC_CIRCLEQ_REMOVE_INIT(&manager->notifiers, notifier, list_entry);
+	free_notifier(notifier);
+
+	DWC_INFO("Notifier unregistered");
+	dump_manager();
+}
+
+/* Add an observer to observe the notifier for a particular state, event, or notification. */
+int dwc_add_observer(void *observer, void *object, char *notification,
+		     dwc_notifier_callback_t callback, void *data)
+{
+	notifier_t *notifier = find_notifier(object);
+	observer_t *new_observer;
+
+	if (!notifier) {
+		DWC_ERROR("Notifier %p is not found when adding observer\n", object);
+		return -DWC_E_INVALID;
+	}
+
+	new_observer = alloc_observer(notifier->mem_ctx, observer, notification, callback, data);
+	if (!new_observer) {
+		return -DWC_E_NO_MEMORY;
+	}
+
+	DWC_CIRCLEQ_INSERT_TAIL(&notifier->observers, new_observer, list_entry);
+
+	DWC_INFO("Added observer %p to notifier %p observing notification %s, callback=%p, data=%p",
+		 observer, object, notification, callback, data);
+
+	dump_manager();
+	return 0;
+}
+
+int dwc_remove_observer(void *observer)
+{
+	notifier_t *n;
+
+	DWC_ASSERT(manager, "Notification manager not found");
+
+	DWC_CIRCLEQ_FOREACH(n, &manager->notifiers, list_entry) {
+		observer_t *o;
+		observer_t *o2;
+
+		DWC_CIRCLEQ_FOREACH_SAFE(o, o2, &n->observers, list_entry) {
+			if (o->observer == observer) {
+				DWC_CIRCLEQ_REMOVE_INIT(&n->observers, o, list_entry);
+				DWC_INFO("Removing observer %p from notifier %p watching notification %s:",
+					 o->observer, n->object, o->notification);
+				free_observer(n->mem_ctx, o);
+			}
+		}
+	}
+
+	dump_manager();
+	return 0;
+}
+
+typedef struct callback_data {
+	void *mem_ctx;
+	dwc_notifier_callback_t cb;
+	void *observer;
+	void *data;
+	void *object;
+	char *notification;
+	void *notification_data;
+} cb_data_t;
+
+static void cb_task(void *data)
+{
+	cb_data_t *cb = (cb_data_t *)data;
+
+	cb->cb(cb->object, cb->notification, cb->observer, cb->notification_data, cb->data);
+	dwc_free(cb->mem_ctx, cb);
+}
+
+void dwc_notify(dwc_notifier_t *notifier, char *notification, void *notification_data)
+{
+	observer_t *o;
+
+	DWC_ASSERT(manager, "Notification manager not found");
+
+	DWC_CIRCLEQ_FOREACH(o, &notifier->observers, list_entry) {
+		int len = DWC_STRLEN(notification);
+
+		if (DWC_STRLEN(o->notification) != len) {
+			continue;
+		}
+
+		if (DWC_STRNCMP(o->notification, notification, len) == 0) {
+			cb_data_t *cb_data = dwc_alloc(notifier->mem_ctx, sizeof(cb_data_t));
+
+			if (!cb_data) {
+				DWC_ERROR("Failed to allocate callback data\n");
+				return;
+			}
+
+			cb_data->mem_ctx = notifier->mem_ctx;
+			cb_data->cb = o->callback;
+			cb_data->observer = o->observer;
+			cb_data->data = o->data;
+			cb_data->object = notifier->object;
+			cb_data->notification = notification;
+			cb_data->notification_data = notification_data;
+			DWC_DEBUGC("Observer found %p for notification %s\n", o->observer, notification);
+			DWC_WORKQ_SCHEDULE(manager->wq, cb_task, cb_data,
+					   "Notify callback from %p for Notification %s, to observer %p",
+					   cb_data->object, notification, cb_data->observer);
+		}
+	}
+}
+
+#endif	/* DWC_NOTIFYLIB */
diff -Nur --no-dereference linux-5.10.52-orig/drivers/usb/host/dwc_common_port/dwc_notifier.h linux-5.10.52-v7l+/drivers/usb/host/dwc_common_port/dwc_notifier.h
--- linux-5.10.52-orig/drivers/usb/host/dwc_common_port/dwc_notifier.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/usb/host/dwc_common_port/dwc_notifier.h	2021-07-25 16:46:28.657910448 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+
+#ifndef __DWC_NOTIFIER_H__
+#define __DWC_NOTIFIER_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "dwc_os.h"
+
+/** @file
+ *
+ * A simple implementation of the Observer pattern.  Any "module" can
+ * register as an observer or notifier.  The notion of "module" is abstract and
+ * can mean anything used to identify either an observer or notifier.  Usually
+ * it will be a pointer to a data structure which contains some state, ie an
+ * object.
+ *
+ * Before any notifiers can be added, the global notification manager must be
+ * brought up with dwc_alloc_notification_manager().
+ * dwc_free_notification_manager() will bring it down and free all resources.
+ * These would typically be called upon module load and unload.  The
+ * notification manager is a single global instance that handles all registered
+ * observable modules and observers so this should be done only once.
+ *
+ * A module can be observable by using Notifications to publicize some general
+ * information about it's state or operation.  It does not care who listens, or
+ * even if anyone listens, or what they do with the information.  The observable
+ * modules do not need to know any information about it's observers or their
+ * interface, or their state or data.
+ *
+ * Any module can register to emit Notifications.  It should publish a list of
+ * notifications that it can emit and their behavior, such as when they will get
+ * triggered, and what information will be provided to the observer.  Then it
+ * should register itself as an observable module. See dwc_register_notifier().
+ *
+ * Any module can observe any observable, registered module, provided it has a
+ * handle to the other module and knows what notifications to observe.  See
+ * dwc_add_observer().
+ *
+ * A function of type dwc_notifier_callback_t is called whenever a notification
+ * is triggered with one or more observers observing it.  This function is
+ * called in it's own process so it may sleep or block if needed.  It is
+ * guaranteed to be called sometime after the notification has occurred and will
+ * be called once per each time the notification is triggered.  It will NOT be
+ * called in the same process context used to trigger the notification.
+ *
+ * @section Limitiations
+ *
+ * Keep in mind that Notifications that can be triggered in rapid sucession may
+ * schedule too many processes too handle.  Be aware of this limitation when
+ * designing to use notifications, and only add notifications for appropriate
+ * observable information.
+ *
+ * Also Notification callbacks are not synchronous.  If you need to synchronize
+ * the behavior between module/observer you must use other means.  And perhaps
+ * that will mean Notifications are not the proper solution.
+ */
+
+struct dwc_notifier;
+typedef struct dwc_notifier dwc_notifier_t;
+
+/** The callback function must be of this type.
+ *
+ * @param object This is the object that is being observed.
+ * @param notification This is the notification that was triggered.
+ * @param observer This is the observer
+ * @param notification_data This is notification-specific data that the notifier
+ * has included in this notification.  The value of this should be published in
+ * the documentation of the observable module with the notifications.
+ * @param user_data This is any custom data that the observer provided when
+ * adding itself as an observer to the notification. */
+typedef void (*dwc_notifier_callback_t)(void *object, char *notification, void *observer,
+					void *notification_data, void *user_data);
+
+/** Brings up the notification manager. */
+extern int dwc_alloc_notification_manager(void *mem_ctx, void *wkq_ctx);
+/** Brings down the notification manager. */
+extern void dwc_free_notification_manager(void);
+
+/** This function registers an observable module.  A dwc_notifier_t object is
+ * returned to the observable module.  This is an opaque object that is used by
+ * the observable module to trigger notifications.  This object should only be
+ * accessible to functions that are authorized to trigger notifications for this
+ * module.  Observers do not need this object. */
+extern dwc_notifier_t *dwc_register_notifier(void *mem_ctx, void *object);
+
+/** This function unregisters an observable module.  All observers have to be
+ * removed prior to unregistration. */
+extern void dwc_unregister_notifier(dwc_notifier_t *notifier);
+
+/** Add a module as an observer to the observable module.  The observable module
+ * needs to have previously registered with the notification manager.
+ *
+ * @param observer The observer module
+ * @param object The module to observe
+ * @param notification The notification to observe
+ * @param callback The callback function to call
+ * @param user_data Any additional user data to pass into the callback function */
+extern int dwc_add_observer(void *observer, void *object, char *notification,
+			    dwc_notifier_callback_t callback, void *user_data);
+
+/** Removes the specified observer from all notifications that it is currently
+ * observing. */
+extern int dwc_remove_observer(void *observer);
+
+/** This function triggers a Notification.  It should be called by the
+ * observable module, or any module or library which the observable module
+ * allows to trigger notification on it's behalf.  Such as the dwc_cc_t.
+ *
+ * dwc_notify is a non-blocking function.  Callbacks are scheduled called in
+ * their own process context for each trigger.  Callbacks can be blocking.
+ * dwc_notify can be called from interrupt context if needed.
+ *
+ */
+void dwc_notify(dwc_notifier_t *notifier, char *notification, void *notification_data);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __DWC_NOTIFIER_H__ */
diff -Nur --no-dereference linux-5.10.52-orig/drivers/usb/host/dwc_common_port/dwc_os.h linux-5.10.52-v7l+/drivers/usb/host/dwc_common_port/dwc_os.h
--- linux-5.10.52-orig/drivers/usb/host/dwc_common_port/dwc_os.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/usb/host/dwc_common_port/dwc_os.h	2021-07-25 16:46:28.657910448 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/* =========================================================================
+ * $File: //dwh/usb_iip/dev/software/dwc_common_port_2/dwc_os.h $
+ * $Revision: #14 $
+ * $Date: 2010/11/04 $
+ * $Change: 1621695 $
+ *
+ * Synopsys Portability Library Software and documentation
+ * (hereinafter, "Software") is an Unsupported proprietary work of
+ * Synopsys, Inc. unless otherwise expressly agreed to in writing
+ * between Synopsys and you.
+ *
+ * The Software IS NOT an item of Licensed Software or Licensed Product
+ * under any End User Software License Agreement or Agreement for
+ * Licensed Product with Synopsys or any supplement thereto. You are
+ * permitted to use and redistribute this Software in source and binary
+ * forms, with or without modification, provided that redistributions
+ * of source code must retain this notice. You may not view, use,
+ * disclose, copy or distribute this file or any information contained
+ * herein except pursuant to this license grant from Synopsys. If you
+ * do not agree with this notice, including the disclaimer below, then
+ * you are not authorized to use the Software.
+ *
+ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS"
+ * BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL
+ * SYNOPSYS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * ========================================================================= */
+#ifndef _DWC_OS_H_
+#define _DWC_OS_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** @file
+ *
+ * DWC portability library, low level os-wrapper functions
+ *
+ */
+
+/* These basic types need to be defined by some OS header file or custom header
+ * file for your specific target architecture.
+ *
+ * uint8_t, int8_t, uint16_t, int16_t, uint32_t, int32_t, uint64_t, int64_t
+ *
+ * Any custom or alternate header file must be added and enabled here.
+ */
+
+#ifdef DWC_LINUX
+# include <linux/types.h>
+# ifdef CONFIG_DEBUG_MUTEXES
+#  include <linux/mutex.h>
+# endif
+# include <linux/spinlock.h>
+# include <linux/errno.h>
+# include <stdarg.h>
+#endif
+
+#if defined(DWC_FREEBSD) || defined(DWC_NETBSD)
+# include <os_dep.h>
+#endif
+
+
+/** @name Primitive Types and Values */
+
+/** We define a boolean type for consistency.  Can be either YES or NO */
+typedef uint8_t dwc_bool_t;
+#define YES  1
+#define NO   0
+
+#ifdef DWC_LINUX
+
+/** @name Error Codes */
+#define DWC_E_INVALID		EINVAL
+#define DWC_E_NO_MEMORY		ENOMEM
+#define DWC_E_NO_DEVICE		ENODEV
+#define DWC_E_NOT_SUPPORTED	EOPNOTSUPP
+#define DWC_E_TIMEOUT		ETIMEDOUT
+#define DWC_E_BUSY		EBUSY
+#define DWC_E_AGAIN		EAGAIN
+#define DWC_E_RESTART		ERESTART
+#define DWC_E_ABORT		ECONNABORTED
+#define DWC_E_SHUTDOWN		ESHUTDOWN
+#define DWC_E_NO_DATA		ENODATA
+#define DWC_E_DISCONNECT	ECONNRESET
+#define DWC_E_UNKNOWN		EINVAL
+#define DWC_E_NO_STREAM_RES	ENOSR
+#define DWC_E_COMMUNICATION	ECOMM
+#define DWC_E_OVERFLOW		EOVERFLOW
+#define DWC_E_PROTOCOL		EPROTO
+#define DWC_E_IN_PROGRESS	EINPROGRESS
+#define DWC_E_PIPE		EPIPE
+#define DWC_E_IO		EIO
+#define DWC_E_NO_SPACE		ENOSPC
+
+#else
+
+/** @name Error Codes */
+#define DWC_E_INVALID		1001
+#define DWC_E_NO_MEMORY		1002
+#define DWC_E_NO_DEVICE		1003
+#define DWC_E_NOT_SUPPORTED	1004
+#define DWC_E_TIMEOUT		1005
+#define DWC_E_BUSY		1006
+#define DWC_E_AGAIN		1007
+#define DWC_E_RESTART		1008
+#define DWC_E_ABORT		1009
+#define DWC_E_SHUTDOWN		1010
+#define DWC_E_NO_DATA		1011
+#define DWC_E_DISCONNECT	2000
+#define DWC_E_UNKNOWN		3000
+#define DWC_E_NO_STREAM_RES	4001
+#define DWC_E_COMMUNICATION	4002
+#define DWC_E_OVERFLOW		4003
+#define DWC_E_PROTOCOL		4004
+#define DWC_E_IN_PROGRESS	4005
+#define DWC_E_PIPE		4006
+#define DWC_E_IO		4007
+#define DWC_E_NO_SPACE		4008
+
+#endif
+
+
+/** @name Tracing/Logging Functions
+ *
+ * These function provide the capability to add tracing, debugging, and error
+ * messages, as well exceptions as assertions.  The WUDEV uses these
+ * extensively.  These could be logged to the main console, the serial port, an
+ * internal buffer, etc.  These functions could also be no-op if they are too
+ * expensive on your system.  By default undefining the DEBUG macro already
+ * no-ops some of these functions. */
+
+/** Returns non-zero if in interrupt context. */
+extern dwc_bool_t DWC_IN_IRQ(void);
+#define dwc_in_irq DWC_IN_IRQ
+
+/** Returns "IRQ" if DWC_IN_IRQ is true. */
+static inline char *dwc_irq(void) {
+	return DWC_IN_IRQ() ? "IRQ" : "";
+}
+
+/** Returns non-zero if in bottom-half context. */
+extern dwc_bool_t DWC_IN_BH(void);
+#define dwc_in_bh DWC_IN_BH
+
+/** Returns "BH" if DWC_IN_BH is true. */
+static inline char *dwc_bh(void) {
+	return DWC_IN_BH() ? "BH" : "";
+}
+
+/**
+ * A vprintf() clone.  Just call vprintf if you've got it.
+ */
+extern void DWC_VPRINTF(char *format, va_list args);
+#define dwc_vprintf DWC_VPRINTF
+
+/**
+ * A vsnprintf() clone.  Just call vprintf if you've got it.
+ */
+extern int DWC_VSNPRINTF(char *str, int size, char *format, va_list args);
+#define dwc_vsnprintf DWC_VSNPRINTF
+
+/**
+ * printf() clone.  Just call printf if you've go it.
+ */
+extern void DWC_PRINTF(char *format, ...)
+/* This provides compiler level static checking of the parameters if you're
+ * using GCC. */
+#ifdef __GNUC__
+	__attribute__ ((format(printf, 1, 2)));
+#else
+	;
+#endif
+#define dwc_printf DWC_PRINTF
+
+/**
+ * sprintf() clone.  Just call sprintf if you've got it.
+ */
+extern int DWC_SPRINTF(char *string, char *format, ...)
+#ifdef __GNUC__
+	__attribute__ ((format(printf, 2, 3)));
+#else
+	;
+#endif
+#define dwc_sprintf DWC_SPRINTF
+
+/**
+ * snprintf() clone.  Just call snprintf if you've got it.
+ */
+extern int DWC_SNPRINTF(char *string, int size, char *format, ...)
+#ifdef __GNUC__
+	__attribute__ ((format(printf, 3, 4)));
+#else
+	;
+#endif
+#define dwc_snprintf DWC_SNPRINTF
+
+/**
+ * Prints a WARNING message.  On systems that don't differentiate between
+ * warnings and regular log messages, just print it.  Indicates that something
+ * may be wrong with the driver.  Works like printf().
+ *
+ * Use the DWC_WARN macro to call this function.
+ */
+extern void __DWC_WARN(char *format, ...)
+#ifdef __GNUC__
+	__attribute__ ((format(printf, 1, 2)));
+#else
+	;
+#endif
+
+/**
+ * Prints an error message.  On systems that don't differentiate between errors
+ * and regular log messages, just print it.  Indicates that something went wrong
+ * with the driver.  Works like printf().
+ *
+ * Use the DWC_ERROR macro to call this function.
+ */
+extern void __DWC_ERROR(char *format, ...)
+#ifdef __GNUC__
+	__attribute__ ((format(printf, 1, 2)));
+#else
+	;
+#endif
+
+/**
+ * Prints an exception error message and takes some user-defined action such as
+ * print out a backtrace or trigger a breakpoint.  Indicates that something went
+ * abnormally wrong with the driver such as programmer error, or other
+ * exceptional condition.  It should not be ignored so even on systems without
+ * printing capability, some action should be taken to notify the developer of
+ * it.  Works like printf().
+ */
+extern void DWC_EXCEPTION(char *format, ...)
+#ifdef __GNUC__
+	__attribute__ ((format(printf, 1, 2)));
+#else
+	;
+#endif
+#define dwc_exception DWC_EXCEPTION
+
+#ifndef DWC_OTG_DEBUG_LEV
+#define DWC_OTG_DEBUG_LEV 0
+#endif
+
+#ifdef DEBUG
+/**
+ * Prints out a debug message.  Used for logging/trace messages.
+ *
+ * Use the DWC_DEBUG macro to call this function
+ */
+extern void __DWC_DEBUG(char *format, ...)
+#ifdef __GNUC__
+	__attribute__ ((format(printf, 1, 2)));
+#else
+	;
+#endif
+#else
+#define __DWC_DEBUG printk
+#endif
+
+/**
+ * Prints out a Debug message.
+ */
+#define DWC_DEBUG(_format, _args...) __DWC_DEBUG("DEBUG:%s:%s: " _format "\n", \
+						 __func__, dwc_irq(), ## _args)
+#define dwc_debug DWC_DEBUG
+/**
+ * Prints out a Debug message if enabled at compile time.
+ */
+#if DWC_OTG_DEBUG_LEV > 0
+#define DWC_DEBUGC(_format, _args...) DWC_DEBUG(_format, ##_args )
+#else
+#define DWC_DEBUGC(_format, _args...)
+#endif
+#define dwc_debugc DWC_DEBUGC
+/**
+ * Prints out an informative message.
+ */
+#define DWC_INFO(_format, _args...) DWC_PRINTF("INFO:%s: " _format "\n", \
+					       dwc_irq(), ## _args)
+#define dwc_info DWC_INFO
+/**
+ * Prints out an informative message if enabled at compile time.
+ */
+#if DWC_OTG_DEBUG_LEV > 1
+#define DWC_INFOC(_format, _args...) DWC_INFO(_format, ##_args )
+#else
+#define DWC_INFOC(_format, _args...)
+#endif
+#define dwc_infoc DWC_INFOC
+/**
+ * Prints out a warning message.
+ */
+#define DWC_WARN(_format, _args...) __DWC_WARN("WARN:%s:%s:%d: " _format "\n", \
+					dwc_irq(), __func__, __LINE__, ## _args)
+#define dwc_warn DWC_WARN
+/**
+ * Prints out an error message.
+ */
+#define DWC_ERROR(_format, _args...) __DWC_ERROR("ERROR:%s:%s:%d: " _format "\n", \
+					dwc_irq(), __func__, __LINE__, ## _args)
+#define dwc_error DWC_ERROR
+
+#define DWC_PROTO_ERROR(_format, _args...) __DWC_WARN("ERROR:%s:%s:%d: " _format "\n", \
+						dwc_irq(), __func__, __LINE__, ## _args)
+#define dwc_proto_error DWC_PROTO_ERROR
+
+#ifdef DEBUG
+/** Prints out a exception error message if the _expr expression fails.  Disabled
+ * if DEBUG is not enabled. */
+#define DWC_ASSERT(_expr, _format, _args...) do { \
+	if (!(_expr)) { DWC_EXCEPTION("%s:%s:%d: " _format "\n", dwc_irq(), \
+				      __FILE__, __LINE__, ## _args); } \
+	} while (0)
+#else
+#define DWC_ASSERT(_x...)
+#endif
+#define dwc_assert DWC_ASSERT
+
+
+/** @name Byte Ordering
+ * The following functions are for conversions between processor's byte ordering
+ * and specific ordering you want.
+ */
+
+/** Converts 32 bit data in CPU byte ordering to little endian. */
+extern uint32_t DWC_CPU_TO_LE32(uint32_t *p);
+#define dwc_cpu_to_le32 DWC_CPU_TO_LE32
+
+/** Converts 32 bit data in CPU byte orderint to big endian. */
+extern uint32_t DWC_CPU_TO_BE32(uint32_t *p);
+#define dwc_cpu_to_be32 DWC_CPU_TO_BE32
+
+/** Converts 32 bit little endian data to CPU byte ordering. */
+extern uint32_t DWC_LE32_TO_CPU(uint32_t *p);
+#define dwc_le32_to_cpu DWC_LE32_TO_CPU
+
+/** Converts 32 bit big endian data to CPU byte ordering. */
+extern uint32_t DWC_BE32_TO_CPU(uint32_t *p);
+#define dwc_be32_to_cpu DWC_BE32_TO_CPU
+
+/** Converts 16 bit data in CPU byte ordering to little endian. */
+extern uint16_t DWC_CPU_TO_LE16(uint16_t *p);
+#define dwc_cpu_to_le16 DWC_CPU_TO_LE16
+
+/** Converts 16 bit data in CPU byte orderint to big endian. */
+extern uint16_t DWC_CPU_TO_BE16(uint16_t *p);
+#define dwc_cpu_to_be16 DWC_CPU_TO_BE16
+
+/** Converts 16 bit little endian data to CPU byte ordering. */
+extern uint16_t DWC_LE16_TO_CPU(uint16_t *p);
+#define dwc_le16_to_cpu DWC_LE16_TO_CPU
+
+/** Converts 16 bit bi endian data to CPU byte ordering. */
+extern uint16_t DWC_BE16_TO_CPU(uint16_t *p);
+#define dwc_be16_to_cpu DWC_BE16_TO_CPU
+
+
+/** @name Register Read/Write
+ *
+ * The following six functions should be implemented to read/write registers of
+ * 32-bit and 64-bit sizes.  All modules use this to read/write register values.
+ * The reg value is a pointer to the register calculated from the void *base
+ * variable passed into the driver when it is started.  */
+
+#ifdef DWC_LINUX
+/* Linux doesn't need any extra parameters for register read/write, so we
+ * just throw away the IO context parameter.
+ */
+/** Reads the content of a 32-bit register. */
+extern uint32_t DWC_READ_REG32(uint32_t volatile *reg);
+#define dwc_read_reg32(_ctx_,_reg_) DWC_READ_REG32(_reg_)
+
+/** Reads the content of a 64-bit register. */
+extern uint64_t DWC_READ_REG64(uint64_t volatile *reg);
+#define dwc_read_reg64(_ctx_,_reg_) DWC_READ_REG64(_reg_)
+
+/** Writes to a 32-bit register. */
+extern void DWC_WRITE_REG32(uint32_t volatile *reg, uint32_t value);
+#define dwc_write_reg32(_ctx_,_reg_,_val_) DWC_WRITE_REG32(_reg_, _val_)
+
+/** Writes to a 64-bit register. */
+extern void DWC_WRITE_REG64(uint64_t volatile *reg, uint64_t value);
+#define dwc_write_reg64(_ctx_,_reg_,_val_) DWC_WRITE_REG64(_reg_, _val_)
+
+/**
+ * Modify bit values in a register.  Using the
+ * algorithm: (reg_contents & ~clear_mask) | set_mask.
+ */
+extern void DWC_MODIFY_REG32(uint32_t volatile *reg, uint32_t clear_mask, uint32_t set_mask);
+#define dwc_modify_reg32(_ctx_,_reg_,_cmsk_,_smsk_) DWC_MODIFY_REG32(_reg_,_cmsk_,_smsk_)
+extern void DWC_MODIFY_REG64(uint64_t volatile *reg, uint64_t clear_mask, uint64_t set_mask);
+#define dwc_modify_reg64(_ctx_,_reg_,_cmsk_,_smsk_) DWC_MODIFY_REG64(_reg_,_cmsk_,_smsk_)
+
+#endif	/* DWC_LINUX */
+
+#if defined(DWC_FREEBSD) || defined(DWC_NETBSD)
+typedef struct dwc_ioctx {
+	struct device *dev;
+	bus_space_tag_t iot;
+	bus_space_handle_t ioh;
+} dwc_ioctx_t;
+
+/** BSD needs two extra parameters for register read/write, so we pass
+ * them in using the IO context parameter.
+ */
+/** Reads the content of a 32-bit register. */
+extern uint32_t DWC_READ_REG32(void *io_ctx, uint32_t volatile *reg);
+#define dwc_read_reg32 DWC_READ_REG32
+
+/** Reads the content of a 64-bit register. */
+extern uint64_t DWC_READ_REG64(void *io_ctx, uint64_t volatile *reg);
+#define dwc_read_reg64 DWC_READ_REG64
+
+/** Writes to a 32-bit register. */
+extern void DWC_WRITE_REG32(void *io_ctx, uint32_t volatile *reg, uint32_t value);
+#define dwc_write_reg32 DWC_WRITE_REG32
+
+/** Writes to a 64-bit register. */
+extern void DWC_WRITE_REG64(void *io_ctx, uint64_t volatile *reg, uint64_t value);
+#define dwc_write_reg64 DWC_WRITE_REG64
+
+/**
+ * Modify bit values in a register.  Using the
+ * algorithm: (reg_contents & ~clear_mask) | set_mask.
+ */
+extern void DWC_MODIFY_REG32(void *io_ctx, uint32_t volatile *reg, uint32_t clear_mask, uint32_t set_mask);
+#define dwc_modify_reg32 DWC_MODIFY_REG32
+extern void DWC_MODIFY_REG64(void *io_ctx, uint64_t volatile *reg, uint64_t clear_mask, uint64_t set_mask);
+#define dwc_modify_reg64 DWC_MODIFY_REG64
+
+#endif	/* DWC_FREEBSD || DWC_NETBSD */
+
+/** @cond */
+
+/** @name Some convenience MACROS used internally.  Define DWC_DEBUG_REGS to log the
+ * register writes. */
+
+#ifdef DWC_LINUX
+
+# ifdef DWC_DEBUG_REGS
+
+#define dwc_define_read_write_reg_n(_reg,_container_type) \
+static inline uint32_t dwc_read_##_reg##_n(_container_type *container, int num) { \
+	return DWC_READ_REG32(&container->regs->_reg[num]); \
+} \
+static inline void dwc_write_##_reg##_n(_container_type *container, int num, uint32_t data) { \
+	DWC_DEBUG("WRITING %8s[%d]: %p: %08x", #_reg, num, \
+		  &(((uint32_t*)container->regs->_reg)[num]), data); \
+	DWC_WRITE_REG32(&(((uint32_t*)container->regs->_reg)[num]), data); \
+}
+
+#define dwc_define_read_write_reg(_reg,_container_type) \
+static inline uint32_t dwc_read_##_reg(_container_type *container) { \
+	return DWC_READ_REG32(&container->regs->_reg); \
+} \
+static inline void dwc_write_##_reg(_container_type *container, uint32_t data) { \
+	DWC_DEBUG("WRITING %11s: %p: %08x", #_reg, &container->regs->_reg, data); \
+	DWC_WRITE_REG32(&container->regs->_reg, data); \
+}
+
+# else	/* DWC_DEBUG_REGS */
+
+#define dwc_define_read_write_reg_n(_reg,_container_type) \
+static inline uint32_t dwc_read_##_reg##_n(_container_type *container, int num) { \
+	return DWC_READ_REG32(&container->regs->_reg[num]); \
+} \
+static inline void dwc_write_##_reg##_n(_container_type *container, int num, uint32_t data) { \
+	DWC_WRITE_REG32(&(((uint32_t*)container->regs->_reg)[num]), data); \
+}
+
+#define dwc_define_read_write_reg(_reg,_container_type) \
+static inline uint32_t dwc_read_##_reg(_container_type *container) { \
+	return DWC_READ_REG32(&container->regs->_reg); \
+} \
+static inline void dwc_write_##_reg(_container_type *container, uint32_t data) { \
+	DWC_WRITE_REG32(&container->regs->_reg, data); \
+}
+
+# endif	/* DWC_DEBUG_REGS */
+
+#endif	/* DWC_LINUX */
+
+#if defined(DWC_FREEBSD) || defined(DWC_NETBSD)
+
+# ifdef DWC_DEBUG_REGS
+
+#define dwc_define_read_write_reg_n(_reg,_container_type) \
+static inline uint32_t dwc_read_##_reg##_n(void *io_ctx, _container_type *container, int num) { \
+	return DWC_READ_REG32(io_ctx, &container->regs->_reg[num]); \
+} \
+static inline void dwc_write_##_reg##_n(void *io_ctx, _container_type *container, int num, uint32_t data) { \
+	DWC_DEBUG("WRITING %8s[%d]: %p: %08x", #_reg, num, \
+		  &(((uint32_t*)container->regs->_reg)[num]), data); \
+	DWC_WRITE_REG32(io_ctx, &(((uint32_t*)container->regs->_reg)[num]), data); \
+}
+
+#define dwc_define_read_write_reg(_reg,_container_type) \
+static inline uint32_t dwc_read_##_reg(void *io_ctx, _container_type *container) { \
+	return DWC_READ_REG32(io_ctx, &container->regs->_reg); \
+} \
+static inline void dwc_write_##_reg(void *io_ctx, _container_type *container, uint32_t data) { \
+	DWC_DEBUG("WRITING %11s: %p: %08x", #_reg, &container->regs->_reg, data); \
+	DWC_WRITE_REG32(io_ctx, &container->regs->_reg, data); \
+}
+
+# else	/* DWC_DEBUG_REGS */
+
+#define dwc_define_read_write_reg_n(_reg,_container_type) \
+static inline uint32_t dwc_read_##_reg##_n(void *io_ctx, _container_type *container, int num) { \
+	return DWC_READ_REG32(io_ctx, &container->regs->_reg[num]); \
+} \
+static inline void dwc_write_##_reg##_n(void *io_ctx, _container_type *container, int num, uint32_t data) { \
+	DWC_WRITE_REG32(io_ctx, &(((uint32_t*)container->regs->_reg)[num]), data); \
+}
+
+#define dwc_define_read_write_reg(_reg,_container_type) \
+static inline uint32_t dwc_read_##_reg(void *io_ctx, _container_type *container) { \
+	return DWC_READ_REG32(io_ctx, &container->regs->_reg); \
+} \
+static inline void dwc_write_##_reg(void *io_ctx, _container_type *container, uint32_t data) { \
+	DWC_WRITE_REG32(io_ctx, &container->regs->_reg, data); \
+}
+
+# endif	/* DWC_DEBUG_REGS */
+
+#endif	/* DWC_FREEBSD || DWC_NETBSD */
+
+/** @endcond */
+
+
+#ifdef DWC_CRYPTOLIB
+/** @name Crypto Functions
+ *
+ * These are the low-level cryptographic functions used by the driver. */
+
+/** Perform AES CBC */
+extern int DWC_AES_CBC(uint8_t *message, uint32_t messagelen, uint8_t *key, uint32_t keylen, uint8_t iv[16], uint8_t *out);
+#define dwc_aes_cbc DWC_AES_CBC
+
+/** Fill the provided buffer with random bytes.  These should be cryptographic grade random numbers. */
+extern void DWC_RANDOM_BYTES(uint8_t *buffer, uint32_t length);
+#define dwc_random_bytes DWC_RANDOM_BYTES
+
+/** Perform the SHA-256 hash function */
+extern int DWC_SHA256(uint8_t *message, uint32_t len, uint8_t *out);
+#define dwc_sha256 DWC_SHA256
+
+/** Calculated the HMAC-SHA256 */
+extern int DWC_HMAC_SHA256(uint8_t *message, uint32_t messagelen, uint8_t *key, uint32_t keylen, uint8_t *out);
+#define dwc_hmac_sha256 DWC_HMAC_SHA256
+
+#endif	/* DWC_CRYPTOLIB */
+
+
+/** @name Memory Allocation
+ *
+ * These function provide access to memory allocation.  There are only 2 DMA
+ * functions and 3 Regular memory functions that need to be implemented.  None
+ * of the memory debugging routines need to be implemented.  The allocation
+ * routines all ZERO the contents of the memory.
+ *
+ * Defining DWC_DEBUG_MEMORY turns on memory debugging and statistic gathering.
+ * This checks for memory leaks, keeping track of alloc/free pairs.  It also
+ * keeps track of how much memory the driver is using at any given time. */
+
+#define DWC_PAGE_SIZE 4096
+#define DWC_PAGE_OFFSET(addr) (((uint32_t)addr) & 0xfff)
+#define DWC_PAGE_ALIGNED(addr) ((((uint32_t)addr) & 0xfff) == 0)
+
+#define DWC_INVALID_DMA_ADDR 0x0
+
+#ifdef DWC_LINUX
+/** Type for a DMA address */
+typedef dma_addr_t dwc_dma_t;
+#endif
+
+#if defined(DWC_FREEBSD) || defined(DWC_NETBSD)
+typedef bus_addr_t dwc_dma_t;
+#endif
+
+#ifdef DWC_FREEBSD
+typedef struct dwc_dmactx {
+	struct device *dev;
+	bus_dma_tag_t dma_tag;
+	bus_dmamap_t dma_map;
+	bus_addr_t dma_paddr;
+	void *dma_vaddr;
+} dwc_dmactx_t;
+#endif
+
+#ifdef DWC_NETBSD
+typedef struct dwc_dmactx {
+	struct device *dev;
+	bus_dma_tag_t dma_tag;
+	bus_dmamap_t dma_map;
+	bus_dma_segment_t segs[1];
+	int nsegs;
+	bus_addr_t dma_paddr;
+	void *dma_vaddr;
+} dwc_dmactx_t;
+#endif
+
+/* @todo these functions will be added in the future */
+#if 0
+/**
+ * Creates a DMA pool from which you can allocate DMA buffers.  Buffers
+ * allocated from this pool will be guaranteed to meet the size, alignment, and
+ * boundary requirements specified.
+ *
+ * @param[in] size Specifies the size of the buffers that will be allocated from
+ * this pool.
+ * @param[in] align Specifies the byte alignment requirements of the buffers
+ * allocated from this pool.  Must be a power of 2.
+ * @param[in] boundary Specifies the N-byte boundary that buffers allocated from
+ * this pool must not cross.
+ *
+ * @returns A pointer to an internal opaque structure which is not to be
+ * accessed outside of these library functions.  Use this handle to specify
+ * which pools to allocate/free DMA buffers from and also to destroy the pool,
+ * when you are done with it.
+ */
+extern dwc_pool_t *DWC_DMA_POOL_CREATE(uint32_t size, uint32_t align, uint32_t boundary);
+
+/**
+ * Destroy a DMA pool.  All buffers allocated from that pool must be freed first.
+ */
+extern void DWC_DMA_POOL_DESTROY(dwc_pool_t *pool);
+
+/**
+ * Allocate a buffer from the specified DMA pool and zeros its contents.
+ */
+extern void *DWC_DMA_POOL_ALLOC(dwc_pool_t *pool, uint64_t *dma_addr);
+
+/**
+ * Free a previously allocated buffer from the DMA pool.
+ */
+extern void DWC_DMA_POOL_FREE(dwc_pool_t *pool, void *vaddr, void *daddr);
+#endif
+
+/** Allocates a DMA capable buffer and zeroes its contents. */
+extern void *__DWC_DMA_ALLOC(void *dma_ctx, uint32_t size, dwc_dma_t *dma_addr);
+
+/** Allocates a DMA capable buffer and zeroes its contents in atomic contest */
+extern void *__DWC_DMA_ALLOC_ATOMIC(void *dma_ctx, uint32_t size, dwc_dma_t *dma_addr);
+
+/** Frees a previously allocated buffer. */
+extern void __DWC_DMA_FREE(void *dma_ctx, uint32_t size, void *virt_addr, dwc_dma_t dma_addr);
+
+/** Allocates a block of memory and zeroes its contents. */
+extern void *__DWC_ALLOC(void *mem_ctx, uint32_t size);
+
+/** Allocates a block of memory and zeroes its contents, in an atomic manner
+ * which can be used inside interrupt context.  The size should be sufficiently
+ * small, a few KB at most, such that failures are not likely to occur.  Can just call
+ * __DWC_ALLOC if it is atomic. */
+extern void *__DWC_ALLOC_ATOMIC(void *mem_ctx, uint32_t size);
+
+/** Frees a previously allocated buffer. */
+extern void __DWC_FREE(void *mem_ctx, void *addr);
+
+#ifndef DWC_DEBUG_MEMORY
+
+#define DWC_ALLOC(_size_) __DWC_ALLOC(NULL, _size_)
+#define DWC_ALLOC_ATOMIC(_size_) __DWC_ALLOC_ATOMIC(NULL, _size_)
+#define DWC_FREE(_addr_) __DWC_FREE(NULL, _addr_)
+
+# ifdef DWC_LINUX
+#define DWC_DMA_ALLOC(_dev, _size_, _dma_) __DWC_DMA_ALLOC(_dev, _size_, _dma_)
+#define DWC_DMA_ALLOC_ATOMIC(_dev, _size_, _dma_) __DWC_DMA_ALLOC_ATOMIC(_dev, _size_, _dma_)
+#define DWC_DMA_FREE(_dev, _size_,_virt_, _dma_) __DWC_DMA_FREE(_dev, _size_, _virt_, _dma_)
+# endif
+
+# if defined(DWC_FREEBSD) || defined(DWC_NETBSD)
+#define DWC_DMA_ALLOC __DWC_DMA_ALLOC
+#define DWC_DMA_FREE __DWC_DMA_FREE
+# endif
+extern void *dwc_dma_alloc_atomic_debug(uint32_t size, dwc_dma_t *dma_addr, char const *func, int line);
+
+#else	/* DWC_DEBUG_MEMORY */
+
+extern void *dwc_alloc_debug(void *mem_ctx, uint32_t size, char const *func, int line);
+extern void *dwc_alloc_atomic_debug(void *mem_ctx, uint32_t size, char const *func, int line);
+extern void dwc_free_debug(void *mem_ctx, void *addr, char const *func, int line);
+extern void *dwc_dma_alloc_debug(void *dma_ctx, uint32_t size, dwc_dma_t *dma_addr,
+				 char const *func, int line);
+extern void *dwc_dma_alloc_atomic_debug(void *dma_ctx, uint32_t size, dwc_dma_t *dma_addr,
+				char const *func, int line);
+extern void dwc_dma_free_debug(void *dma_ctx, uint32_t size, void *virt_addr,
+			       dwc_dma_t dma_addr, char const *func, int line);
+
+extern int dwc_memory_debug_start(void *mem_ctx);
+extern void dwc_memory_debug_stop(void);
+extern void dwc_memory_debug_report(void);
+
+#define DWC_ALLOC(_size_) dwc_alloc_debug(NULL, _size_, __func__, __LINE__)
+#define DWC_ALLOC_ATOMIC(_size_) dwc_alloc_atomic_debug(NULL, _size_, \
+							__func__, __LINE__)
+#define DWC_FREE(_addr_) dwc_free_debug(NULL, _addr_, __func__, __LINE__)
+
+# ifdef DWC_LINUX
+#define DWC_DMA_ALLOC(_dev, _size_, _dma_) \
+	dwc_dma_alloc_debug(_dev, _size_, _dma_, __func__, __LINE__)
+#define DWC_DMA_ALLOC_ATOMIC(_dev, _size_, _dma_) \
+	dwc_dma_alloc_atomic_debug(_dev, _size_, _dma_, __func__, __LINE__)
+#define DWC_DMA_FREE(_dev, _size_, _virt_, _dma_) \
+	dwc_dma_free_debug(_dev, _size_, _virt_, _dma_, __func__, __LINE__)
+# endif
+
+# if defined(DWC_FREEBSD) || defined(DWC_NETBSD)
+#define DWC_DMA_ALLOC(_ctx_,_size_,_dma_) dwc_dma_alloc_debug(_ctx_, _size_, \
+						_dma_, __func__, __LINE__)
+#define DWC_DMA_FREE(_ctx_,_size_,_virt_,_dma_) dwc_dma_free_debug(_ctx_, _size_, \
+						 _virt_, _dma_, __func__, __LINE__)
+# endif
+
+#endif /* DWC_DEBUG_MEMORY */
+
+#define dwc_alloc(_ctx_,_size_) DWC_ALLOC(_size_)
+#define dwc_alloc_atomic(_ctx_,_size_) DWC_ALLOC_ATOMIC(_size_)
+#define dwc_free(_ctx_,_addr_) DWC_FREE(_addr_)
+
+#ifdef DWC_LINUX
+/* Linux doesn't need any extra parameters for DMA buffer allocation, so we
+ * just throw away the DMA context parameter.
+ */
+#define dwc_dma_alloc(_ctx_,_size_,_dma_) DWC_DMA_ALLOC(_size_, _dma_)
+#define dwc_dma_alloc_atomic(_ctx_,_size_,_dma_) DWC_DMA_ALLOC_ATOMIC(_size_, _dma_)
+#define dwc_dma_free(_ctx_,_size_,_virt_,_dma_) DWC_DMA_FREE(_size_, _virt_, _dma_)
+#endif
+
+#if defined(DWC_FREEBSD) || defined(DWC_NETBSD)
+/** BSD needs several extra parameters for DMA buffer allocation, so we pass
+ * them in using the DMA context parameter.
+ */
+#define dwc_dma_alloc DWC_DMA_ALLOC
+#define dwc_dma_free DWC_DMA_FREE
+#endif
+
+
+/** @name Memory and String Processing */
+
+/** memset() clone */
+extern void *DWC_MEMSET(void *dest, uint8_t byte, uint32_t size);
+#define dwc_memset DWC_MEMSET
+
+/** memcpy() clone */
+extern void *DWC_MEMCPY(void *dest, void const *src, uint32_t size);
+#define dwc_memcpy DWC_MEMCPY
+
+/** memmove() clone */
+extern void *DWC_MEMMOVE(void *dest, void *src, uint32_t size);
+#define dwc_memmove DWC_MEMMOVE
+
+/** memcmp() clone */
+extern int DWC_MEMCMP(void *m1, void *m2, uint32_t size);
+#define dwc_memcmp DWC_MEMCMP
+
+/** strcmp() clone */
+extern int DWC_STRCMP(void *s1, void *s2);
+#define dwc_strcmp DWC_STRCMP
+
+/** strncmp() clone */
+extern int DWC_STRNCMP(void *s1, void *s2, uint32_t size);
+#define dwc_strncmp DWC_STRNCMP
+
+/** strlen() clone, for NULL terminated ASCII strings */
+extern int DWC_STRLEN(char const *str);
+#define dwc_strlen DWC_STRLEN
+
+/** strcpy() clone, for NULL terminated ASCII strings */
+extern char *DWC_STRCPY(char *to, const char *from);
+#define dwc_strcpy DWC_STRCPY
+
+/** strdup() clone.  If you wish to use memory allocation debugging, this
+ * implementation of strdup should use the DWC_* memory routines instead of
+ * calling a predefined strdup.  Otherwise the memory allocated by this routine
+ * will not be seen by the debugging routines. */
+extern char *DWC_STRDUP(char const *str);
+#define dwc_strdup(_ctx_,_str_) DWC_STRDUP(_str_)
+
+/** NOT an atoi() clone.  Read the description carefully.  Returns an integer
+ * converted from the string str in base 10 unless the string begins with a "0x"
+ * in which case it is base 16.  String must be a NULL terminated sequence of
+ * ASCII characters and may optionally begin with whitespace, a + or -, and a
+ * "0x" prefix if base 16.  The remaining characters must be valid digits for
+ * the number and end with a NULL character.  If any invalid characters are
+ * encountered or it returns with a negative error code and the results of the
+ * conversion are undefined.  On sucess it returns 0.  Overflow conditions are
+ * undefined.  An example implementation using atoi() can be referenced from the
+ * Linux implementation. */
+extern int DWC_ATOI(const char *str, int32_t *value);
+#define dwc_atoi DWC_ATOI
+
+/** Same as above but for unsigned. */
+extern int DWC_ATOUI(const char *str, uint32_t *value);
+#define dwc_atoui DWC_ATOUI
+
+#ifdef DWC_UTFLIB
+/** This routine returns a UTF16LE unicode encoded string from a UTF8 string. */
+extern int DWC_UTF8_TO_UTF16LE(uint8_t const *utf8string, uint16_t *utf16string, unsigned len);
+#define dwc_utf8_to_utf16le DWC_UTF8_TO_UTF16LE
+#endif
+
+
+/** @name Wait queues
+ *
+ * Wait queues provide a means of synchronizing between threads or processes.  A
+ * process can block on a waitq if some condition is not true, waiting for it to
+ * become true.  When the waitq is triggered all waiting process will get
+ * unblocked and the condition will be check again.  Waitqs should be triggered
+ * every time a condition can potentially change.*/
+struct dwc_waitq;
+
+/** Type for a waitq */
+typedef struct dwc_waitq dwc_waitq_t;
+
+/** The type of waitq condition callback function.  This is called every time
+ * condition is evaluated. */
+typedef int (*dwc_waitq_condition_t)(void *data);
+
+/** Allocate a waitq */
+extern dwc_waitq_t *DWC_WAITQ_ALLOC(void);
+#define dwc_waitq_alloc(_ctx_) DWC_WAITQ_ALLOC()
+
+/** Free a waitq */
+extern void DWC_WAITQ_FREE(dwc_waitq_t *wq);
+#define dwc_waitq_free DWC_WAITQ_FREE
+
+/** Check the condition and if it is false, block on the waitq.  When unblocked, check the
+ * condition again.  The function returns when the condition becomes true.  The return value
+ * is 0 on condition true, DWC_WAITQ_ABORTED on abort or killed, or DWC_WAITQ_UNKNOWN on error. */
+extern int32_t DWC_WAITQ_WAIT(dwc_waitq_t *wq, dwc_waitq_condition_t cond, void *data);
+#define dwc_waitq_wait DWC_WAITQ_WAIT
+
+/** Check the condition and if it is false, block on the waitq.  When unblocked,
+ * check the condition again.  The function returns when the condition become
+ * true or the timeout has passed.  The return value is 0 on condition true or
+ * DWC_TIMED_OUT on timeout, or DWC_WAITQ_ABORTED, or DWC_WAITQ_UNKNOWN on
+ * error. */
+extern int32_t DWC_WAITQ_WAIT_TIMEOUT(dwc_waitq_t *wq, dwc_waitq_condition_t cond,
+				      void *data, int32_t msecs);
+#define dwc_waitq_wait_timeout DWC_WAITQ_WAIT_TIMEOUT
+
+/** Trigger a waitq, unblocking all processes.  This should be called whenever a condition
+ * has potentially changed. */
+extern void DWC_WAITQ_TRIGGER(dwc_waitq_t *wq);
+#define dwc_waitq_trigger DWC_WAITQ_TRIGGER
+
+/** Unblock all processes waiting on the waitq with an ABORTED result. */
+extern void DWC_WAITQ_ABORT(dwc_waitq_t *wq);
+#define dwc_waitq_abort DWC_WAITQ_ABORT
+
+
+/** @name Threads
+ *
+ * A thread must be explicitly stopped.  It must check DWC_THREAD_SHOULD_STOP
+ * whenever it is woken up, and then return.  The DWC_THREAD_STOP function
+ * returns the value from the thread.
+ */
+
+struct dwc_thread;
+
+/** Type for a thread */
+typedef struct dwc_thread dwc_thread_t;
+
+/** The thread function */
+typedef int (*dwc_thread_function_t)(void *data);
+
+/** Create a thread and start it running the thread_function.  Returns a handle
+ * to the thread */
+extern dwc_thread_t *DWC_THREAD_RUN(dwc_thread_function_t func, char *name, void *data);
+#define dwc_thread_run(_ctx_,_func_,_name_,_data_) DWC_THREAD_RUN(_func_, _name_, _data_)
+
+/** Stops a thread.  Return the value returned by the thread.  Or will return
+ * DWC_ABORT if the thread never started. */
+extern int DWC_THREAD_STOP(dwc_thread_t *thread);
+#define dwc_thread_stop DWC_THREAD_STOP
+
+/** Signifies to the thread that it must stop. */
+#ifdef DWC_LINUX
+/* Linux doesn't need any parameters for kthread_should_stop() */
+extern dwc_bool_t DWC_THREAD_SHOULD_STOP(void);
+#define dwc_thread_should_stop(_thrd_) DWC_THREAD_SHOULD_STOP()
+
+/* No thread_exit function in Linux */
+#define dwc_thread_exit(_thrd_)
+#endif
+
+#if defined(DWC_FREEBSD) || defined(DWC_NETBSD)
+/** BSD needs the thread pointer for kthread_suspend_check() */
+extern dwc_bool_t DWC_THREAD_SHOULD_STOP(dwc_thread_t *thread);
+#define dwc_thread_should_stop DWC_THREAD_SHOULD_STOP
+
+/** The thread must call this to exit. */
+extern void DWC_THREAD_EXIT(dwc_thread_t *thread);
+#define dwc_thread_exit DWC_THREAD_EXIT
+#endif
+
+
+/** @name Work queues
+ *
+ * Workqs are used to queue a callback function to be called at some later time,
+ * in another thread. */
+struct dwc_workq;
+
+/** Type for a workq */
+typedef struct dwc_workq dwc_workq_t;
+
+/** The type of the callback function to be called. */
+typedef void (*dwc_work_callback_t)(void *data);
+
+/** Allocate a workq */
+extern dwc_workq_t *DWC_WORKQ_ALLOC(char *name);
+#define dwc_workq_alloc(_ctx_,_name_) DWC_WORKQ_ALLOC(_name_)
+
+/** Free a workq.  All work must be completed before being freed. */
+extern void DWC_WORKQ_FREE(dwc_workq_t *workq);
+#define dwc_workq_free DWC_WORKQ_FREE
+
+/** Schedule a callback on the workq, passing in data.  The function will be
+ * scheduled at some later time. */
+extern void DWC_WORKQ_SCHEDULE(dwc_workq_t *workq, dwc_work_callback_t cb,
+			       void *data, char *format, ...)
+#ifdef __GNUC__
+	__attribute__ ((format(printf, 4, 5)));
+#else
+	;
+#endif
+#define dwc_workq_schedule DWC_WORKQ_SCHEDULE
+
+/** Schedule a callback on the workq, that will be called until at least
+ * given number miliseconds have passed. */
+extern void DWC_WORKQ_SCHEDULE_DELAYED(dwc_workq_t *workq, dwc_work_callback_t cb,
+				       void *data, uint32_t time, char *format, ...)
+#ifdef __GNUC__
+	__attribute__ ((format(printf, 5, 6)));
+#else
+	;
+#endif
+#define dwc_workq_schedule_delayed DWC_WORKQ_SCHEDULE_DELAYED
+
+/** The number of processes in the workq */
+extern int DWC_WORKQ_PENDING(dwc_workq_t *workq);
+#define dwc_workq_pending DWC_WORKQ_PENDING
+
+/** Blocks until all the work in the workq is complete or timed out.  Returns <
+ * 0 on timeout. */
+extern int DWC_WORKQ_WAIT_WORK_DONE(dwc_workq_t *workq, int timeout);
+#define dwc_workq_wait_work_done DWC_WORKQ_WAIT_WORK_DONE
+
+
+/** @name Tasklets
+ *
+ */
+struct dwc_tasklet;
+
+/** Type for a tasklet */
+typedef struct dwc_tasklet dwc_tasklet_t;
+
+/** The type of the callback function to be called */
+typedef void (*dwc_tasklet_callback_t)(void *data);
+
+/** Allocates a tasklet */
+extern dwc_tasklet_t *DWC_TASK_ALLOC(char *name, dwc_tasklet_callback_t cb, void *data);
+#define dwc_task_alloc(_ctx_,_name_,_cb_,_data_) DWC_TASK_ALLOC(_name_, _cb_, _data_)
+
+/** Frees a tasklet */
+extern void DWC_TASK_FREE(dwc_tasklet_t *task);
+#define dwc_task_free DWC_TASK_FREE
+
+/** Schedules a tasklet to run */
+extern void DWC_TASK_SCHEDULE(dwc_tasklet_t *task);
+#define dwc_task_schedule DWC_TASK_SCHEDULE
+
+extern void DWC_TASK_HI_SCHEDULE(dwc_tasklet_t *task);
+#define dwc_task_hi_schedule DWC_TASK_HI_SCHEDULE
+
+/** @name Timer
+ *
+ * Callbacks must be small and atomic.
+ */
+struct dwc_timer;
+
+/** Type for a timer */
+typedef struct dwc_timer dwc_timer_t;
+
+/** The type of the callback function to be called */
+typedef void (*dwc_timer_callback_t)(void *data);
+
+/** Allocates a timer */
+extern dwc_timer_t *DWC_TIMER_ALLOC(char *name, dwc_timer_callback_t cb, void *data);
+#define dwc_timer_alloc(_ctx_,_name_,_cb_,_data_) DWC_TIMER_ALLOC(_name_,_cb_,_data_)
+
+/** Frees a timer */
+extern void DWC_TIMER_FREE(dwc_timer_t *timer);
+#define dwc_timer_free DWC_TIMER_FREE
+
+/** Schedules the timer to run at time ms from now.  And will repeat at every
+ * repeat_interval msec therafter
+ *
+ * Modifies a timer that is still awaiting execution to a new expiration time.
+ * The mod_time is added to the old time.  */
+extern void DWC_TIMER_SCHEDULE(dwc_timer_t *timer, uint32_t time);
+#define dwc_timer_schedule DWC_TIMER_SCHEDULE
+
+/** Disables the timer from execution. */
+extern void DWC_TIMER_CANCEL(dwc_timer_t *timer);
+#define dwc_timer_cancel DWC_TIMER_CANCEL
+
+
+/** @name Spinlocks
+ *
+ * These locks are used when the work between the lock/unlock is atomic and
+ * short.  Interrupts are also disabled during the lock/unlock and thus they are
+ * suitable to lock between interrupt/non-interrupt context.  They also lock
+ * between processes if you have multiple CPUs or Preemption.  If you don't have
+ * multiple CPUS or Preemption, then the you can simply implement the
+ * DWC_SPINLOCK and DWC_SPINUNLOCK to disable and enable interrupts.  Because
+ * the work between the lock/unlock is atomic, the process context will never
+ * change, and so you never have to lock between processes.  */
+
+struct dwc_spinlock;
+
+/** Type for a spinlock */
+typedef struct dwc_spinlock dwc_spinlock_t;
+
+/** Type for the 'flags' argument to spinlock funtions */
+typedef unsigned long dwc_irqflags_t;
+
+/** Returns an initialized lock variable.  This function should allocate and
+ * initialize the OS-specific data structure used for locking.  This data
+ * structure is to be used for the DWC_LOCK and DWC_UNLOCK functions and should
+ * be freed by the DWC_FREE_LOCK when it is no longer used.
+ *
+ * For Linux Spinlock Debugging make it macro because the debugging routines use
+ * the symbol name to determine recursive locking. Using a wrapper function
+ * makes it falsely think recursive locking occurs. */
+#if defined(DWC_LINUX) && defined(CONFIG_DEBUG_SPINLOCK)
+#define DWC_SPINLOCK_ALLOC_LINUX_DEBUG(lock) ({ \
+	lock = DWC_ALLOC(sizeof(spinlock_t)); \
+	if (lock) { \
+		spin_lock_init((spinlock_t *)lock); \
+	} \
+})
+#else
+extern dwc_spinlock_t *DWC_SPINLOCK_ALLOC(void);
+#define dwc_spinlock_alloc(_ctx_) DWC_SPINLOCK_ALLOC()
+#endif
+
+/** Frees an initialized lock variable. */
+extern void DWC_SPINLOCK_FREE(dwc_spinlock_t *lock);
+#define dwc_spinlock_free(_ctx_,_lock_) DWC_SPINLOCK_FREE(_lock_)
+
+/** Disables interrupts and blocks until it acquires the lock.
+ *
+ * @param lock Pointer to the spinlock.
+ * @param flags Unsigned long for irq flags storage.
+ */
+extern void DWC_SPINLOCK_IRQSAVE(dwc_spinlock_t *lock, dwc_irqflags_t *flags);
+#define dwc_spinlock_irqsave DWC_SPINLOCK_IRQSAVE
+
+/** Re-enables the interrupt and releases the lock.
+ *
+ * @param lock Pointer to the spinlock.
+ * @param flags Unsigned long for irq flags storage.  Must be the same as was
+ * passed into DWC_LOCK.
+ */
+extern void DWC_SPINUNLOCK_IRQRESTORE(dwc_spinlock_t *lock, dwc_irqflags_t flags);
+#define dwc_spinunlock_irqrestore DWC_SPINUNLOCK_IRQRESTORE
+
+/** Blocks until it acquires the lock.
+ *
+ * @param lock Pointer to the spinlock.
+ */
+extern void DWC_SPINLOCK(dwc_spinlock_t *lock);
+#define dwc_spinlock DWC_SPINLOCK
+
+/** Releases the lock.
+ *
+ * @param lock Pointer to the spinlock.
+ */
+extern void DWC_SPINUNLOCK(dwc_spinlock_t *lock);
+#define dwc_spinunlock DWC_SPINUNLOCK
+
+
+/** @name Mutexes
+ *
+ * Unlike spinlocks Mutexes lock only between processes and the work between the
+ * lock/unlock CAN block, therefore it CANNOT be called from interrupt context.
+ */
+
+struct dwc_mutex;
+
+/** Type for a mutex */
+typedef struct dwc_mutex dwc_mutex_t;
+
+/* For Linux Mutex Debugging make it inline because the debugging routines use
+ * the symbol to determine recursive locking.  This makes it falsely think
+ * recursive locking occurs. */
+#if defined(DWC_LINUX) && defined(CONFIG_DEBUG_MUTEXES)
+#define DWC_MUTEX_ALLOC_LINUX_DEBUG(__mutexp) ({ \
+	__mutexp = (dwc_mutex_t *)DWC_ALLOC(sizeof(struct mutex)); \
+	mutex_init((struct mutex *)__mutexp); \
+})
+#endif
+
+/** Allocate a mutex */
+extern dwc_mutex_t *DWC_MUTEX_ALLOC(void);
+#define dwc_mutex_alloc(_ctx_) DWC_MUTEX_ALLOC()
+
+/* For memory leak debugging when using Linux Mutex Debugging */
+#if defined(DWC_LINUX) && defined(CONFIG_DEBUG_MUTEXES)
+#define DWC_MUTEX_FREE(__mutexp) do { \
+	mutex_destroy((struct mutex *)__mutexp); \
+	DWC_FREE(__mutexp); \
+} while(0)
+#else
+/** Free a mutex */
+extern void DWC_MUTEX_FREE(dwc_mutex_t *mutex);
+#define dwc_mutex_free(_ctx_,_mutex_) DWC_MUTEX_FREE(_mutex_)
+#endif
+
+/** Lock a mutex */
+extern void DWC_MUTEX_LOCK(dwc_mutex_t *mutex);
+#define dwc_mutex_lock DWC_MUTEX_LOCK
+
+/** Non-blocking lock returns 1 on successful lock. */
+extern int DWC_MUTEX_TRYLOCK(dwc_mutex_t *mutex);
+#define dwc_mutex_trylock DWC_MUTEX_TRYLOCK
+
+/** Unlock a mutex */
+extern void DWC_MUTEX_UNLOCK(dwc_mutex_t *mutex);
+#define dwc_mutex_unlock DWC_MUTEX_UNLOCK
+
+
+/** @name Time */
+
+/** Microsecond delay.
+ *
+ * @param usecs  Microseconds to delay.
+ */
+extern void DWC_UDELAY(uint32_t usecs);
+#define dwc_udelay DWC_UDELAY
+
+/** Millisecond delay.
+ *
+ * @param msecs  Milliseconds to delay.
+ */
+extern void DWC_MDELAY(uint32_t msecs);
+#define dwc_mdelay DWC_MDELAY
+
+/** Non-busy waiting.
+ * Sleeps for specified number of milliseconds.
+ *
+ * @param msecs Milliseconds to sleep.
+ */
+extern void DWC_MSLEEP(uint32_t msecs);
+#define dwc_msleep DWC_MSLEEP
+
+/**
+ * Returns number of milliseconds since boot.
+ */
+extern uint32_t DWC_TIME(void);
+#define dwc_time DWC_TIME
+
+
+
+
+/* @mainpage DWC Portability and Common Library
+ *
+ * This is the documentation for the DWC Portability and Common Library.
+ *
+ * @section intro Introduction
+ *
+ * The DWC Portability library consists of wrapper calls and data structures to
+ * all low-level functions which are typically provided by the OS.  The WUDEV
+ * driver uses only these functions.  In order to port the WUDEV driver, only
+ * the functions in this library need to be re-implemented, with the same
+ * behavior as documented here.
+ *
+ * The Common library consists of higher level functions, which rely only on
+ * calling the functions from the DWC Portability library.  These common
+ * routines are shared across modules.  Some of the common libraries need to be
+ * used directly by the driver programmer when porting WUDEV.  Such as the
+ * parameter and notification libraries.
+ *
+ * @section low Portability Library OS Wrapper Functions
+ *
+ * Any function starting with DWC and in all CAPS is a low-level OS-wrapper that
+ * needs to be implemented when porting, for example DWC_MUTEX_ALLOC().  All of
+ * these functions are included in the dwc_os.h file.
+ *
+ * There are many functions here covering a wide array of OS services.  Please
+ * see dwc_os.h for details, and implementation notes for each function.
+ *
+ * @section common Common Library Functions
+ *
+ * Any function starting with dwc and in all lowercase is a common library
+ * routine.  These functions have a portable implementation and do not need to
+ * be reimplemented when porting.  The common routines can be used by any
+ * driver, and some must be used by the end user to control the drivers.  For
+ * example, you must use the Parameter common library in order to set the
+ * parameters in the WUDEV module.
+ *
+ * The common libraries consist of the following:
+ *
+ * - Connection Contexts - Used internally and can be used by end-user.  See dwc_cc.h
+ * - Parameters - Used internally and can be used by end-user.  See dwc_params.h
+ * - Notifications - Used internally and can be used by end-user.  See dwc_notifier.h
+ * - Lists - Used internally and can be used by end-user.  See dwc_list.h
+ * - Memory Debugging - Used internally and can be used by end-user.  See dwc_os.h
+ * - Modpow - Used internally only.  See dwc_modpow.h
+ * - DH - Used internally only.  See dwc_dh.h
+ * - Crypto - Used internally only.  See dwc_crypto.h
+ *
+ *
+ * @section prereq Prerequistes For dwc_os.h
+ * @subsection types Data Types
+ *
+ * The dwc_os.h file assumes that several low-level data types are pre defined for the
+ * compilation environment.  These data types are:
+ *
+ * - uint8_t - unsigned 8-bit data type
+ * - int8_t - signed 8-bit data type
+ * - uint16_t - unsigned 16-bit data type
+ * - int16_t - signed 16-bit data type
+ * - uint32_t - unsigned 32-bit data type
+ * - int32_t - signed 32-bit data type
+ * - uint64_t - unsigned 64-bit data type
+ * - int64_t - signed 64-bit data type
+ *
+ * Ensure that these are defined before using dwc_os.h.  The easiest way to do
+ * that is to modify the top of the file to include the appropriate header.
+ * This is already done for the Linux environment.  If the DWC_LINUX macro is
+ * defined, the correct header will be added.  A standard header <stdint.h> is
+ * also used for environments where standard C headers are available.
+ *
+ * @subsection stdarg Variable Arguments
+ *
+ * Variable arguments are provided by a standard C header <stdarg.h>.  it is
+ * available in Both the Linux and ANSI C enviornment.  An equivalent must be
+ * provided in your enviornment in order to use dwc_os.h with the debug and
+ * tracing message functionality.
+ *
+ * @subsection thread Threading
+ *
+ * WUDEV Core must be run on an operating system that provides for multiple
+ * threads/processes.  Threading can be implemented in many ways, even in
+ * embedded systems without an operating system.  At the bare minimum, the
+ * system should be able to start any number of processes at any time to handle
+ * special work.  It need not be a pre-emptive system.  Process context can
+ * change upon a call to a blocking function.  The hardware interrupt context
+ * that calls the module's ISR() function must be differentiable from process
+ * context, even if your processes are impemented via a hardware interrupt.
+ * Further locking mechanism between process must exist (or be implemented), and
+ * process context must have a way to disable interrupts for a period of time to
+ * lock them out.  If all of this exists, the functions in dwc_os.h related to
+ * threading should be able to be implemented with the defined behavior.
+ *
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _DWC_OS_H_ */
diff -Nur --no-dereference linux-5.10.52-orig/drivers/usb/host/dwc_common_port/Makefile linux-5.10.52-v7l+/drivers/usb/host/dwc_common_port/Makefile
--- linux-5.10.52-orig/drivers/usb/host/dwc_common_port/Makefile	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/usb/host/dwc_common_port/Makefile	2021-07-25 16:46:28.657910448 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+#
+# Makefile for DWC_common library
+#
+
+ifneq ($(KERNELRELEASE),)
+
+ccflags-y	+= -DDWC_LINUX
+#ccflags-y	+= -DDEBUG
+#ccflags-y	+= -DDWC_DEBUG_REGS
+#ccflags-y	+= -DDWC_DEBUG_MEMORY
+
+ccflags-y	+= -DDWC_LIBMODULE
+ccflags-y	+= -DDWC_CCLIB
+#ccflags-y	+= -DDWC_CRYPTOLIB
+ccflags-y	+= -DDWC_NOTIFYLIB
+ccflags-y	+= -DDWC_UTFLIB
+
+obj-$(CONFIG_USB_DWCOTG)	+= dwc_common_port_lib.o
+dwc_common_port_lib-objs := dwc_cc.o dwc_modpow.o dwc_dh.o \
+			    dwc_crypto.o dwc_notifier.o \
+			    dwc_common_linux.o dwc_mem.o
+
+kernrelwd := $(subst ., ,$(KERNELRELEASE))
+kernrel3 := $(word 1,$(kernrelwd)).$(word 2,$(kernrelwd)).$(word 3,$(kernrelwd))
+
+ifneq ($(kernrel3),2.6.20)
+# grayg - I only know that we use ccflags-y in 2.6.31 actually
+ccflags-y += $(CPPFLAGS)
+endif
+
+else
+
+#ifeq ($(KDIR),)
+#$(error Must give "KDIR=/path/to/kernel/source" on command line or in environment)
+#endif
+
+ifeq ($(ARCH),)
+$(error Must give "ARCH=<arch>" on command line or in environment. Also, if \
+ cross-compiling, must give "CROSS_COMPILE=/path/to/compiler/plus/tool-prefix-")
+endif
+
+ifeq ($(DOXYGEN),)
+DOXYGEN		:= doxygen
+endif
+
+default:
+	$(MAKE) -C$(KDIR) M=$(PWD) ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE) modules
+
+docs:	$(wildcard *.[hc]) doc/doxygen.cfg
+	$(DOXYGEN) doc/doxygen.cfg
+
+tags:	$(wildcard *.[hc])
+	$(CTAGS) -e $(wildcard *.[hc]) $(wildcard linux/*.[hc]) $(wildcard $(KDIR)/include/linux/usb*.h)
+
+endif
+
+clean:
+	rm -rf *.o *.ko .*.cmd *.mod.c .*.o.d .*.o.tmp modules.order Module.markers Module.symvers .tmp_versions/
diff -Nur --no-dereference linux-5.10.52-orig/drivers/usb/host/dwc_common_port/Makefile.fbsd linux-5.10.52-v7l+/drivers/usb/host/dwc_common_port/Makefile.fbsd
--- linux-5.10.52-orig/drivers/usb/host/dwc_common_port/Makefile.fbsd	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/usb/host/dwc_common_port/Makefile.fbsd	2021-07-25 16:46:28.657910448 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+CFLAGS	+= -I/sys/i386/compile/GENERIC -I/sys/i386/include -I/usr/include
+CFLAGS	+= -DDWC_FREEBSD
+CFLAGS	+= -DDEBUG
+#CFLAGS	+= -DDWC_DEBUG_REGS
+#CFLAGS	+= -DDWC_DEBUG_MEMORY
+
+#CFLAGS	+= -DDWC_LIBMODULE
+#CFLAGS	+= -DDWC_CCLIB
+#CFLAGS	+= -DDWC_CRYPTOLIB
+#CFLAGS	+= -DDWC_NOTIFYLIB
+#CFLAGS	+= -DDWC_UTFLIB
+
+KMOD = dwc_common_port_lib
+SRCS = dwc_cc.c dwc_modpow.c dwc_dh.c dwc_crypto.c dwc_notifier.c \
+       dwc_common_fbsd.c dwc_mem.c
+
+.include <bsd.kmod.mk>
diff -Nur --no-dereference linux-5.10.52-orig/drivers/usb/host/dwc_common_port/Makefile.linux linux-5.10.52-v7l+/drivers/usb/host/dwc_common_port/Makefile.linux
--- linux-5.10.52-orig/drivers/usb/host/dwc_common_port/Makefile.linux	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/usb/host/dwc_common_port/Makefile.linux	2021-07-25 16:46:28.657910448 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+#
+# Makefile for DWC_common library
+#
+ifneq ($(KERNELRELEASE),)
+
+ccflags-y	+= -DDWC_LINUX
+#ccflags-y	+= -DDEBUG
+#ccflags-y	+= -DDWC_DEBUG_REGS
+#ccflags-y	+= -DDWC_DEBUG_MEMORY
+
+ccflags-y	+= -DDWC_LIBMODULE
+ccflags-y	+= -DDWC_CCLIB
+ccflags-y	+= -DDWC_CRYPTOLIB
+ccflags-y	+= -DDWC_NOTIFYLIB
+ccflags-y	+= -DDWC_UTFLIB
+
+obj-m			 := dwc_common_port_lib.o
+dwc_common_port_lib-objs := dwc_cc.o dwc_modpow.o dwc_dh.o \
+			    dwc_crypto.o dwc_notifier.o \
+			    dwc_common_linux.o dwc_mem.o
+
+else
+
+ifeq ($(KDIR),)
+$(error Must give "KDIR=/path/to/kernel/source" on command line or in environment)
+endif
+
+ifeq ($(ARCH),)
+$(error Must give "ARCH=<arch>" on command line or in environment. Also, if \
+ cross-compiling, must give "CROSS_COMPILE=/path/to/compiler/plus/tool-prefix-")
+endif
+
+ifeq ($(DOXYGEN),)
+DOXYGEN		:= doxygen
+endif
+
+default:
+	$(MAKE) -C$(KDIR) M=$(PWD) ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE) modules
+
+docs:	$(wildcard *.[hc]) doc/doxygen.cfg
+	$(DOXYGEN) doc/doxygen.cfg
+
+tags:	$(wildcard *.[hc])
+	$(CTAGS) -e $(wildcard *.[hc]) $(wildcard linux/*.[hc]) $(wildcard $(KDIR)/include/linux/usb*.h)
+
+endif
+
+clean:
+	rm -rf *.o *.ko .*.cmd *.mod.c .*.o.d .*.o.tmp modules.order Module.markers Module.symvers .tmp_versions/
diff -Nur --no-dereference linux-5.10.52-orig/drivers/usb/host/dwc_common_port/usb.h linux-5.10.52-v7l+/drivers/usb/host/dwc_common_port/usb.h
--- linux-5.10.52-orig/drivers/usb/host/dwc_common_port/usb.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/usb/host/dwc_common_port/usb.h	2021-07-25 16:46:28.657910448 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/*
+ * Copyright (c) 1998 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Lennart Augustsson (lennart@augustsson.net) at
+ * Carlstedt Research & Technology.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* Modified by Synopsys, Inc, 12/12/2007 */
+
+
+#ifndef _USB_H_
+#define _USB_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * The USB records contain some unaligned little-endian word
+ * components.  The U[SG]ETW macros take care of both the alignment
+ * and endian problem and should always be used to access non-byte
+ * values.
+ */
+typedef u_int8_t uByte;
+typedef u_int8_t uWord[2];
+typedef u_int8_t uDWord[4];
+
+#define UGETW(w) ((w)[0] | ((w)[1] << 8))
+#define USETW(w,v) ((w)[0] = (u_int8_t)(v), (w)[1] = (u_int8_t)((v) >> 8))
+#define UGETDW(w) ((w)[0] | ((w)[1] << 8) | ((w)[2] << 16) | ((w)[3] << 24))
+#define USETDW(w,v) ((w)[0] = (u_int8_t)(v), \
+		     (w)[1] = (u_int8_t)((v) >> 8), \
+		     (w)[2] = (u_int8_t)((v) >> 16), \
+		     (w)[3] = (u_int8_t)((v) >> 24))
+
+#define UPACKED __attribute__((__packed__))
+
+typedef struct {
+	uByte		bmRequestType;
+	uByte		bRequest;
+	uWord		wValue;
+	uWord		wIndex;
+	uWord		wLength;
+} UPACKED usb_device_request_t;
+
+#define UT_GET_DIR(a) ((a) & 0x80)
+#define UT_WRITE		0x00
+#define UT_READ			0x80
+
+#define UT_GET_TYPE(a) ((a) & 0x60)
+#define UT_STANDARD		0x00
+#define UT_CLASS		0x20
+#define UT_VENDOR		0x40
+
+#define UT_GET_RECIPIENT(a) ((a) & 0x1f)
+#define UT_DEVICE		0x00
+#define UT_INTERFACE		0x01
+#define UT_ENDPOINT		0x02
+#define UT_OTHER		0x03
+
+/* Requests */
+#define UR_GET_STATUS		0x00
+#define  USTAT_STANDARD_STATUS  0x00
+#define  WUSTAT_WUSB_FEATURE    0x01
+#define  WUSTAT_CHANNEL_INFO    0x02
+#define  WUSTAT_RECEIVED_DATA   0x03
+#define  WUSTAT_MAS_AVAILABILITY 0x04
+#define  WUSTAT_CURRENT_TRANSMIT_POWER 0x05
+#define UR_CLEAR_FEATURE	0x01
+#define UR_SET_FEATURE		0x03
+#define UR_SET_AND_TEST_FEATURE 0x0c
+#define UR_SET_ADDRESS		0x05
+#define UR_GET_DESCRIPTOR	0x06
+#define  UDESC_DEVICE		0x01
+#define  UDESC_CONFIG		0x02
+#define  UDESC_STRING		0x03
+#define  UDESC_INTERFACE	0x04
+#define  UDESC_ENDPOINT		0x05
+#define  UDESC_SS_USB_COMPANION	0x30
+#define  UDESC_DEVICE_QUALIFIER	0x06
+#define  UDESC_OTHER_SPEED_CONFIGURATION 0x07
+#define  UDESC_INTERFACE_POWER	0x08
+#define  UDESC_OTG		0x09
+#define  WUDESC_SECURITY	0x0c
+#define  WUDESC_KEY		0x0d
+#define   WUD_GET_KEY_INDEX(_wValue_) ((_wValue_) & 0xf)
+#define   WUD_GET_KEY_TYPE(_wValue_) (((_wValue_) & 0x30) >> 4)
+#define    WUD_KEY_TYPE_ASSOC    0x01
+#define    WUD_KEY_TYPE_GTK      0x02
+#define   WUD_GET_KEY_ORIGIN(_wValue_) (((_wValue_) & 0x40) >> 6)
+#define    WUD_KEY_ORIGIN_HOST   0x00
+#define    WUD_KEY_ORIGIN_DEVICE 0x01
+#define  WUDESC_ENCRYPTION_TYPE	0x0e
+#define  WUDESC_BOS		0x0f
+#define  WUDESC_DEVICE_CAPABILITY 0x10
+#define  WUDESC_WIRELESS_ENDPOINT_COMPANION 0x11
+#define  UDESC_BOS		0x0f
+#define  UDESC_DEVICE_CAPABILITY 0x10
+#define  UDESC_CS_DEVICE	0x21	/* class specific */
+#define  UDESC_CS_CONFIG	0x22
+#define  UDESC_CS_STRING	0x23
+#define  UDESC_CS_INTERFACE	0x24
+#define  UDESC_CS_ENDPOINT	0x25
+#define  UDESC_HUB		0x29
+#define UR_SET_DESCRIPTOR	0x07
+#define UR_GET_CONFIG		0x08
+#define UR_SET_CONFIG		0x09
+#define UR_GET_INTERFACE	0x0a
+#define UR_SET_INTERFACE	0x0b
+#define UR_SYNCH_FRAME		0x0c
+#define WUR_SET_ENCRYPTION      0x0d
+#define WUR_GET_ENCRYPTION	0x0e
+#define WUR_SET_HANDSHAKE	0x0f
+#define WUR_GET_HANDSHAKE	0x10
+#define WUR_SET_CONNECTION	0x11
+#define WUR_SET_SECURITY_DATA	0x12
+#define WUR_GET_SECURITY_DATA	0x13
+#define WUR_SET_WUSB_DATA	0x14
+#define  WUDATA_DRPIE_INFO	0x01
+#define  WUDATA_TRANSMIT_DATA	0x02
+#define  WUDATA_TRANSMIT_PARAMS	0x03
+#define  WUDATA_RECEIVE_PARAMS	0x04
+#define  WUDATA_TRANSMIT_POWER	0x05
+#define WUR_LOOPBACK_DATA_WRITE	0x15
+#define WUR_LOOPBACK_DATA_READ	0x16
+#define WUR_SET_INTERFACE_DS	0x17
+
+/* Feature numbers */
+#define UF_ENDPOINT_HALT	0
+#define UF_DEVICE_REMOTE_WAKEUP	1
+#define UF_TEST_MODE		2
+#define UF_DEVICE_B_HNP_ENABLE	3
+#define UF_DEVICE_A_HNP_SUPPORT	4
+#define UF_DEVICE_A_ALT_HNP_SUPPORT 5
+#define WUF_WUSB		3
+#define  WUF_TX_DRPIE		0x0
+#define  WUF_DEV_XMIT_PACKET	0x1
+#define  WUF_COUNT_PACKETS	0x2
+#define  WUF_CAPTURE_PACKETS	0x3
+#define UF_FUNCTION_SUSPEND	0
+#define UF_U1_ENABLE		48
+#define UF_U2_ENABLE		49
+#define UF_LTM_ENABLE		50
+
+/* Class requests from the USB 2.0 hub spec, table 11-15 */
+#define UCR_CLEAR_HUB_FEATURE		(0x2000 | UR_CLEAR_FEATURE)
+#define UCR_CLEAR_PORT_FEATURE		(0x2300 | UR_CLEAR_FEATURE)
+#define UCR_GET_HUB_DESCRIPTOR		(0xa000 | UR_GET_DESCRIPTOR)
+#define UCR_GET_HUB_STATUS		(0xa000 | UR_GET_STATUS)
+#define UCR_GET_PORT_STATUS		(0xa300 | UR_GET_STATUS)
+#define UCR_SET_HUB_FEATURE		(0x2000 | UR_SET_FEATURE)
+#define UCR_SET_PORT_FEATURE		(0x2300 | UR_SET_FEATURE)
+#define UCR_SET_AND_TEST_PORT_FEATURE	(0xa300 | UR_SET_AND_TEST_FEATURE)
+
+#ifdef _MSC_VER
+#include <pshpack1.h>
+#endif
+
+typedef struct {
+	uByte		bLength;
+	uByte		bDescriptorType;
+	uByte		bEndpointAddress;
+#define UE_GET_DIR(a)	((a) & 0x80)
+#define UE_SET_DIR(a,d)	((a) | (((d)&1) << 7))
+#define UE_DIR_IN	0x80
+#define UE_DIR_OUT	0x00
+#define UE_ADDR		0x0f
+#define UE_GET_ADDR(a)	((a) & UE_ADDR)
+	uByte		bmAttributes;
+#define UE_XFERTYPE	0x03
+#define  UE_CONTROL	0x00
+#define  UE_ISOCHRONOUS	0x01
+#define  UE_BULK	0x02
+#define  UE_INTERRUPT	0x03
+#define UE_GET_XFERTYPE(a)	((a) & UE_XFERTYPE)
+#define UE_ISO_TYPE	0x0c
+#define  UE_ISO_ASYNC	0x04
+#define  UE_ISO_ADAPT	0x08
+#define  UE_ISO_SYNC	0x0c
+#define UE_GET_ISO_TYPE(a)	((a) & UE_ISO_TYPE)
+	uWord		wMaxPacketSize;
+	uByte		bInterval;
+} UPACKED usb_endpoint_descriptor_t;
+#define USB_ENDPOINT_DESCRIPTOR_SIZE 7
+
+/* Hub specific request */
+#define UR_GET_BUS_STATE	0x02
+#define UR_CLEAR_TT_BUFFER	0x08
+#define UR_RESET_TT		0x09
+#define UR_GET_TT_STATE		0x0a
+#define UR_STOP_TT		0x0b
+
+/* Hub features */
+#define UHF_C_HUB_LOCAL_POWER	0
+#define UHF_C_HUB_OVER_CURRENT	1
+#define UHF_PORT_CONNECTION	0
+#define UHF_PORT_ENABLE		1
+#define UHF_PORT_SUSPEND	2
+#define UHF_PORT_OVER_CURRENT	3
+#define UHF_PORT_RESET		4
+#define UHF_PORT_L1		5
+#define UHF_PORT_POWER		8
+#define UHF_PORT_LOW_SPEED	9
+#define UHF_PORT_HIGH_SPEED	10
+#define UHF_C_PORT_CONNECTION	16
+#define UHF_C_PORT_ENABLE	17
+#define UHF_C_PORT_SUSPEND	18
+#define UHF_C_PORT_OVER_CURRENT	19
+#define UHF_C_PORT_RESET	20
+#define UHF_C_PORT_L1		23
+#define UHF_PORT_TEST		21
+#define UHF_PORT_INDICATOR	22
+
+typedef struct {
+	uByte		bDescLength;
+	uByte		bDescriptorType;
+	uByte		bNbrPorts;
+	uWord		wHubCharacteristics;
+#define UHD_PWR			0x0003
+#define  UHD_PWR_GANGED		0x0000
+#define  UHD_PWR_INDIVIDUAL	0x0001
+#define  UHD_PWR_NO_SWITCH	0x0002
+#define UHD_COMPOUND		0x0004
+#define UHD_OC			0x0018
+#define  UHD_OC_GLOBAL		0x0000
+#define  UHD_OC_INDIVIDUAL	0x0008
+#define  UHD_OC_NONE		0x0010
+#define UHD_TT_THINK		0x0060
+#define  UHD_TT_THINK_8		0x0000
+#define  UHD_TT_THINK_16	0x0020
+#define  UHD_TT_THINK_24	0x0040
+#define  UHD_TT_THINK_32	0x0060
+#define UHD_PORT_IND		0x0080
+	uByte		bPwrOn2PwrGood;	/* delay in 2 ms units */
+#define UHD_PWRON_FACTOR 2
+	uByte		bHubContrCurrent;
+	uByte		DeviceRemovable[32]; /* max 255 ports */
+#define UHD_NOT_REMOV(desc, i) \
+    (((desc)->DeviceRemovable[(i)/8] >> ((i) % 8)) & 1)
+	/* deprecated */ uByte		PortPowerCtrlMask[1];
+} UPACKED usb_hub_descriptor_t;
+#define USB_HUB_DESCRIPTOR_SIZE 9 /* includes deprecated PortPowerCtrlMask */
+
+#ifdef _MSC_VER
+#include <poppack.h>
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _USB_H_ */
diff -Nur --no-dereference linux-5.10.52-orig/drivers/usb/host/dwc_otg/doc/doxygen.cfg linux-5.10.52-v7l+/drivers/usb/host/dwc_otg/doc/doxygen.cfg
--- linux-5.10.52-orig/drivers/usb/host/dwc_otg/doc/doxygen.cfg	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/usb/host/dwc_otg/doc/doxygen.cfg	2021-07-25 16:46:28.657910448 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+# Doxyfile 1.3.9.1
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+PROJECT_NAME           = "DesignWare USB 2.0 OTG Controller (DWC_otg) Device Driver"
+PROJECT_NUMBER         = v3.00a
+OUTPUT_DIRECTORY       = ./doc/
+CREATE_SUBDIRS         = NO
+OUTPUT_LANGUAGE        = English
+BRIEF_MEMBER_DESC      = YES
+REPEAT_BRIEF           = YES
+ABBREVIATE_BRIEF       = "The $name class" \
+                         "The $name widget" \
+                         "The $name file" \
+                         is \
+                         provides \
+                         specifies \
+                         contains \
+                         represents \
+                         a \
+                         an \
+                         the
+ALWAYS_DETAILED_SEC    = NO
+INLINE_INHERITED_MEMB  = NO
+FULL_PATH_NAMES        = NO
+STRIP_FROM_PATH        =
+STRIP_FROM_INC_PATH    =
+SHORT_NAMES            = NO
+JAVADOC_AUTOBRIEF      = YES
+MULTILINE_CPP_IS_BRIEF = NO
+INHERIT_DOCS           = YES
+DISTRIBUTE_GROUP_DOC   = NO
+TAB_SIZE               = 8
+ALIASES                =
+OPTIMIZE_OUTPUT_FOR_C  = YES
+OPTIMIZE_OUTPUT_JAVA   = NO
+SUBGROUPING            = YES
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+EXTRACT_ALL            = NO
+EXTRACT_PRIVATE        = YES
+EXTRACT_STATIC         = YES
+EXTRACT_LOCAL_CLASSES  = YES
+EXTRACT_LOCAL_METHODS  = NO
+HIDE_UNDOC_MEMBERS     = NO
+HIDE_UNDOC_CLASSES     = NO
+HIDE_FRIEND_COMPOUNDS  = NO
+HIDE_IN_BODY_DOCS      = NO
+INTERNAL_DOCS          = NO
+CASE_SENSE_NAMES       = NO
+HIDE_SCOPE_NAMES       = NO
+SHOW_INCLUDE_FILES     = YES
+INLINE_INFO            = YES
+SORT_MEMBER_DOCS       = NO
+SORT_BRIEF_DOCS        = NO
+SORT_BY_SCOPE_NAME     = NO
+GENERATE_TODOLIST      = YES
+GENERATE_TESTLIST      = YES
+GENERATE_BUGLIST       = YES
+GENERATE_DEPRECATEDLIST= YES
+ENABLED_SECTIONS       =
+MAX_INITIALIZER_LINES  = 30
+SHOW_USED_FILES        = YES
+SHOW_DIRECTORIES       = YES
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+QUIET                  = YES
+WARNINGS               = YES
+WARN_IF_UNDOCUMENTED   = NO
+WARN_IF_DOC_ERROR      = YES
+WARN_FORMAT            = "$file:$line: $text"
+WARN_LOGFILE           =
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+INPUT                  = .
+FILE_PATTERNS          = *.c \
+                         *.h \
+                         ./linux/*.c \
+                         ./linux/*.h
+RECURSIVE              = NO
+EXCLUDE                = ./test/ \
+                         ./dwc_otg/.AppleDouble/
+EXCLUDE_SYMLINKS       = YES
+EXCLUDE_PATTERNS       = *.mod.*
+EXAMPLE_PATH           =
+EXAMPLE_PATTERNS       = *
+EXAMPLE_RECURSIVE      = NO
+IMAGE_PATH             =
+INPUT_FILTER           =
+FILTER_PATTERNS        =
+FILTER_SOURCE_FILES    = NO
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+SOURCE_BROWSER         = YES
+INLINE_SOURCES         = NO
+STRIP_CODE_COMMENTS    = YES
+REFERENCED_BY_RELATION = NO
+REFERENCES_RELATION    = NO
+VERBATIM_HEADERS       = NO
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+ALPHABETICAL_INDEX     = NO
+COLS_IN_ALPHA_INDEX    = 5
+IGNORE_PREFIX          =
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+GENERATE_HTML          = YES
+HTML_OUTPUT            = html
+HTML_FILE_EXTENSION    = .html
+HTML_HEADER            =
+HTML_FOOTER            =
+HTML_STYLESHEET        =
+HTML_ALIGN_MEMBERS     = YES
+GENERATE_HTMLHELP      = NO
+CHM_FILE               =
+HHC_LOCATION           =
+GENERATE_CHI           = NO
+BINARY_TOC             = NO
+TOC_EXPAND             = NO
+DISABLE_INDEX          = NO
+ENUM_VALUES_PER_LINE   = 4
+GENERATE_TREEVIEW      = YES
+TREEVIEW_WIDTH         = 250
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+GENERATE_LATEX         = NO
+LATEX_OUTPUT           = latex
+LATEX_CMD_NAME         = latex
+MAKEINDEX_CMD_NAME     = makeindex
+COMPACT_LATEX          = NO
+PAPER_TYPE             = a4wide
+EXTRA_PACKAGES         =
+LATEX_HEADER           =
+PDF_HYPERLINKS         = NO
+USE_PDFLATEX           = NO
+LATEX_BATCHMODE        = NO
+LATEX_HIDE_INDICES     = NO
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+GENERATE_RTF           = NO
+RTF_OUTPUT             = rtf
+COMPACT_RTF            = NO
+RTF_HYPERLINKS         = NO
+RTF_STYLESHEET_FILE    =
+RTF_EXTENSIONS_FILE    =
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+GENERATE_MAN           = NO
+MAN_OUTPUT             = man
+MAN_EXTENSION          = .3
+MAN_LINKS              = NO
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+GENERATE_XML           = NO
+XML_OUTPUT             = xml
+XML_SCHEMA             =
+XML_DTD                =
+XML_PROGRAMLISTING     = YES
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+GENERATE_AUTOGEN_DEF   = NO
+#---------------------------------------------------------------------------
+# configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+GENERATE_PERLMOD       = NO
+PERLMOD_LATEX          = NO
+PERLMOD_PRETTY         = YES
+PERLMOD_MAKEVAR_PREFIX =
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+ENABLE_PREPROCESSING   = YES
+MACRO_EXPANSION        = YES
+EXPAND_ONLY_PREDEF     = YES
+SEARCH_INCLUDES        = YES
+INCLUDE_PATH           =
+INCLUDE_FILE_PATTERNS  =
+PREDEFINED             = DEVICE_ATTR DWC_EN_ISOC
+EXPAND_AS_DEFINED      = DWC_OTG_DEVICE_ATTR_BITFIELD_SHOW DWC_OTG_DEVICE_ATTR_BITFIELD_STORE DWC_OTG_DEVICE_ATTR_BITFIELD_RW DWC_OTG_DEVICE_ATTR_BITFIELD_RO DWC_OTG_DEVICE_ATTR_REG_SHOW DWC_OTG_DEVICE_ATTR_REG_STORE DWC_OTG_DEVICE_ATTR_REG32_RW DWC_OTG_DEVICE_ATTR_REG32_RO DWC_EN_ISOC
+SKIP_FUNCTION_MACROS   = NO
+#---------------------------------------------------------------------------
+# Configuration::additions related to external references
+#---------------------------------------------------------------------------
+TAGFILES               =
+GENERATE_TAGFILE       =
+ALLEXTERNALS           = NO
+EXTERNAL_GROUPS        = YES
+PERL_PATH              = /usr/bin/perl
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+CLASS_DIAGRAMS         = YES
+HIDE_UNDOC_RELATIONS   = YES
+HAVE_DOT               = NO
+CLASS_GRAPH            = YES
+COLLABORATION_GRAPH    = YES
+UML_LOOK               = NO
+TEMPLATE_RELATIONS     = NO
+INCLUDE_GRAPH          = YES
+INCLUDED_BY_GRAPH      = YES
+CALL_GRAPH             = NO
+GRAPHICAL_HIERARCHY    = YES
+DOT_IMAGE_FORMAT       = png
+DOT_PATH               =
+DOTFILE_DIRS           =
+MAX_DOT_GRAPH_DEPTH    = 1000
+GENERATE_LEGEND        = YES
+DOT_CLEANUP            = YES
+#---------------------------------------------------------------------------
+# Configuration::additions related to the search engine
+#---------------------------------------------------------------------------
+SEARCHENGINE           = NO
diff -Nur --no-dereference linux-5.10.52-orig/drivers/usb/host/dwc_otg/dummy_audio.c linux-5.10.52-v7l+/drivers/usb/host/dwc_otg/dummy_audio.c
--- linux-5.10.52-orig/drivers/usb/host/dwc_otg/dummy_audio.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/usb/host/dwc_otg/dummy_audio.c	2021-07-25 16:46:28.657910448 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/*
+ * zero.c -- Gadget Zero, for USB development
+ *
+ * Copyright (C) 2003-2004 David Brownell
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The names of the above-listed copyright holders may not be used
+ *    to endorse or promote products derived from this software without
+ *    specific prior written permission.
+ *
+ * ALTERNATIVELY, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") as published by the Free Software
+ * Foundation, either version 2 of that License or (at your option) any
+ * later version.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+/*
+ * Gadget Zero only needs two bulk endpoints, and is an example of how you
+ * can write a hardware-agnostic gadget driver running inside a USB device.
+ *
+ * Hardware details are visible (see CONFIG_USB_ZERO_* below) but don't
+ * affect most of the driver.
+ *
+ * Use it with the Linux host/master side "usbtest" driver to get a basic
+ * functional test of your device-side usb stack, or with "usb-skeleton".
+ *
+ * It supports two similar configurations.  One sinks whatever the usb host
+ * writes, and in return sources zeroes.  The other loops whatever the host
+ * writes back, so the host can read it.  Module options include:
+ *
+ *   buflen=N		default N=4096, buffer size used
+ *   qlen=N		default N=32, how many buffers in the loopback queue
+ *   loopdefault	default false, list loopback config first
+ *
+ * Many drivers will only have one configuration, letting them be much
+ * simpler if they also don't support high speed operation (like this
+ * driver does).
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/timer.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/uts.h>
+#include <linux/version.h>
+#include <linux/device.h>
+#include <linux/moduleparam.h>
+#include <linux/proc_fs.h>
+
+#include <asm/byteorder.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+#include <asm/unaligned.h>
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,21)
+# include <linux/usb/ch9.h>
+#else
+# include <linux/usb_ch9.h>
+#endif
+
+#include <linux/usb_gadget.h>
+
+
+/*-------------------------------------------------------------------------*/
+/*-------------------------------------------------------------------------*/
+
+
+static int utf8_to_utf16le(const char *s, u16 *cp, unsigned len)
+{
+	int	count = 0;
+	u8	c;
+	u16	uchar;
+
+	/* this insists on correct encodings, though not minimal ones.
+	 * BUT it currently rejects legit 4-byte UTF-8 code points,
+	 * which need surrogate pairs.  (Unicode 3.1 can use them.)
+	 */
+	while (len != 0 && (c = (u8) *s++) != 0) {
+		if (unlikely(c & 0x80)) {
+			// 2-byte sequence:
+			// 00000yyyyyxxxxxx = 110yyyyy 10xxxxxx
+			if ((c & 0xe0) == 0xc0) {
+				uchar = (c & 0x1f) << 6;
+
+				c = (u8) *s++;
+				if ((c & 0xc0) != 0xc0)
+					goto fail;
+				c &= 0x3f;
+				uchar |= c;
+
+			// 3-byte sequence (most CJKV characters):
+			// zzzzyyyyyyxxxxxx = 1110zzzz 10yyyyyy 10xxxxxx
+			} else if ((c & 0xf0) == 0xe0) {
+				uchar = (c & 0x0f) << 12;
+
+				c = (u8) *s++;
+				if ((c & 0xc0) != 0xc0)
+					goto fail;
+				c &= 0x3f;
+				uchar |= c << 6;
+
+				c = (u8) *s++;
+				if ((c & 0xc0) != 0xc0)
+					goto fail;
+				c &= 0x3f;
+				uchar |= c;
+
+				/* no bogus surrogates */
+				if (0xd800 <= uchar && uchar <= 0xdfff)
+					goto fail;
+
+			// 4-byte sequence (surrogate pairs, currently rare):
+			// 11101110wwwwzzzzyy + 110111yyyyxxxxxx
+			//     = 11110uuu 10uuzzzz 10yyyyyy 10xxxxxx
+			// (uuuuu = wwww + 1)
+			// FIXME accept the surrogate code points (only)
+
+			} else
+				goto fail;
+		} else
+			uchar = c;
+		put_unaligned (cpu_to_le16 (uchar), cp++);
+		count++;
+		len--;
+	}
+	return count;
+fail:
+	return -1;
+}
+
+
+/**
+ * usb_gadget_get_string - fill out a string descriptor
+ * @table: of c strings encoded using UTF-8
+ * @id: string id, from low byte of wValue in get string descriptor
+ * @buf: at least 256 bytes
+ *
+ * Finds the UTF-8 string matching the ID, and converts it into a
+ * string descriptor in utf16-le.
+ * Returns length of descriptor (always even) or negative errno
+ *
+ * If your driver needs stings in multiple languages, you'll probably
+ * "switch (wIndex) { ... }"  in your ep0 string descriptor logic,
+ * using this routine after choosing which set of UTF-8 strings to use.
+ * Note that US-ASCII is a strict subset of UTF-8; any string bytes with
+ * the eighth bit set will be multibyte UTF-8 characters, not ISO-8859/1
+ * characters (which are also widely used in C strings).
+ */
+int
+usb_gadget_get_string (struct usb_gadget_strings *table, int id, u8 *buf)
+{
+	struct usb_string	*s;
+	int			len;
+
+	/* descriptor 0 has the language id */
+	if (id == 0) {
+		buf [0] = 4;
+		buf [1] = USB_DT_STRING;
+		buf [2] = (u8) table->language;
+		buf [3] = (u8) (table->language >> 8);
+		return 4;
+	}
+	for (s = table->strings; s && s->s; s++)
+		if (s->id == id)
+			break;
+
+	/* unrecognized: stall. */
+	if (!s || !s->s)
+		return -EINVAL;
+
+	/* string descriptors have length, tag, then UTF16-LE text */
+	len = min ((size_t) 126, strlen (s->s));
+	memset (buf + 2, 0, 2 * len);	/* zero all the bytes */
+	len = utf8_to_utf16le(s->s, (u16 *)&buf[2], len);
+	if (len < 0)
+		return -EINVAL;
+	buf [0] = (len + 1) * 2;
+	buf [1] = USB_DT_STRING;
+	return buf [0];
+}
+
+
+/*-------------------------------------------------------------------------*/
+/*-------------------------------------------------------------------------*/
+
+
+/**
+ * usb_descriptor_fillbuf - fill buffer with descriptors
+ * @buf: Buffer to be filled
+ * @buflen: Size of buf
+ * @src: Array of descriptor pointers, terminated by null pointer.
+ *
+ * Copies descriptors into the buffer, returning the length or a
+ * negative error code if they can't all be copied.  Useful when
+ * assembling descriptors for an associated set of interfaces used
+ * as part of configuring a composite device; or in other cases where
+ * sets of descriptors need to be marshaled.
+ */
+int
+usb_descriptor_fillbuf(void *buf, unsigned buflen,
+		const struct usb_descriptor_header **src)
+{
+	u8	*dest = buf;
+
+	if (!src)
+		return -EINVAL;
+
+	/* fill buffer from src[] until null descriptor ptr */
+	for (; 0 != *src; src++) {
+		unsigned		len = (*src)->bLength;
+
+		if (len > buflen)
+			return -EINVAL;
+		memcpy(dest, *src, len);
+		buflen -= len;
+		dest += len;
+	}
+	return dest - (u8 *)buf;
+}
+
+
+/**
+ * usb_gadget_config_buf - builts a complete configuration descriptor
+ * @config: Header for the descriptor, including characteristics such
+ *	as power requirements and number of interfaces.
+ * @desc: Null-terminated vector of pointers to the descriptors (interface,
+ *	endpoint, etc) defining all functions in this device configuration.
+ * @buf: Buffer for the resulting configuration descriptor.
+ * @length: Length of buffer.  If this is not big enough to hold the
+ *	entire configuration descriptor, an error code will be returned.
+ *
+ * This copies descriptors into the response buffer, building a descriptor
+ * for that configuration.  It returns the buffer length or a negative
+ * status code.  The config.wTotalLength field is set to match the length
+ * of the result, but other descriptor fields (including power usage and
+ * interface count) must be set by the caller.
+ *
+ * Gadget drivers could use this when constructing a config descriptor
+ * in response to USB_REQ_GET_DESCRIPTOR.  They will need to patch the
+ * resulting bDescriptorType value if USB_DT_OTHER_SPEED_CONFIG is needed.
+ */
+int usb_gadget_config_buf(
+	const struct usb_config_descriptor	*config,
+	void					*buf,
+	unsigned				length,
+	const struct usb_descriptor_header	**desc
+)
+{
+	struct usb_config_descriptor		*cp = buf;
+	int					len;
+
+	/* config descriptor first */
+	if (length < USB_DT_CONFIG_SIZE || !desc)
+		return -EINVAL;
+	*cp = *config;
+
+	/* then interface/endpoint/class/vendor/... */
+	len = usb_descriptor_fillbuf(USB_DT_CONFIG_SIZE + (u8*)buf,
+			length - USB_DT_CONFIG_SIZE, desc);
+	if (len < 0)
+		return len;
+	len += USB_DT_CONFIG_SIZE;
+	if (len > 0xffff)
+		return -EINVAL;
+
+	/* patch up the config descriptor */
+	cp->bLength = USB_DT_CONFIG_SIZE;
+	cp->bDescriptorType = USB_DT_CONFIG;
+	cp->wTotalLength = cpu_to_le16(len);
+	cp->bmAttributes |= USB_CONFIG_ATT_ONE;
+	return len;
+}
+
+/*-------------------------------------------------------------------------*/
+/*-------------------------------------------------------------------------*/
+
+
+#define RBUF_LEN (1024*1024)
+static int rbuf_start;
+static int rbuf_len;
+static __u8 rbuf[RBUF_LEN];
+
+/*-------------------------------------------------------------------------*/
+
+#define DRIVER_VERSION		"St Patrick's Day 2004"
+
+static const char shortname [] = "zero";
+static const char longname [] = "YAMAHA YST-MS35D USB Speaker  ";
+
+static const char source_sink [] = "source and sink data";
+static const char loopback [] = "loop input to output";
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * driver assumes self-powered hardware, and
+ * has no way for users to trigger remote wakeup.
+ *
+ * this version autoconfigures as much as possible,
+ * which is reasonable for most "bulk-only" drivers.
+ */
+static const char *EP_IN_NAME;		/* source */
+static const char *EP_OUT_NAME;		/* sink */
+
+/*-------------------------------------------------------------------------*/
+
+/* big enough to hold our biggest descriptor */
+#define USB_BUFSIZ	512
+
+struct zero_dev {
+	spinlock_t		lock;
+	struct usb_gadget	*gadget;
+	struct usb_request	*req;		/* for control responses */
+
+	/* when configured, we have one of two configs:
+	 * - source data (in to host) and sink it (out from host)
+	 * - or loop it back (out from host back in to host)
+	 */
+	u8			config;
+	struct usb_ep		*in_ep, *out_ep;
+
+	/* autoresume timer */
+	struct timer_list	resume;
+};
+
+#define xprintk(d,level,fmt,args...) \
+	dev_printk(level , &(d)->gadget->dev , fmt , ## args)
+
+#ifdef DEBUG
+#define DBG(dev,fmt,args...) \
+	xprintk(dev , KERN_DEBUG , fmt , ## args)
+#else
+#define DBG(dev,fmt,args...) \
+	do { } while (0)
+#endif /* DEBUG */
+
+#ifdef VERBOSE
+#define VDBG	DBG
+#else
+#define VDBG(dev,fmt,args...) \
+	do { } while (0)
+#endif /* VERBOSE */
+
+#define ERROR(dev,fmt,args...) \
+	xprintk(dev , KERN_ERR , fmt , ## args)
+#define WARN(dev,fmt,args...) \
+	xprintk(dev , KERN_WARNING , fmt , ## args)
+#define INFO(dev,fmt,args...) \
+	xprintk(dev , KERN_INFO , fmt , ## args)
+
+/*-------------------------------------------------------------------------*/
+
+static unsigned buflen = 4096;
+static unsigned qlen = 32;
+static unsigned pattern = 0;
+
+module_param (buflen, uint, S_IRUGO|S_IWUSR);
+module_param (qlen, uint, S_IRUGO|S_IWUSR);
+module_param (pattern, uint, S_IRUGO|S_IWUSR);
+
+/*
+ * if it's nonzero, autoresume says how many seconds to wait
+ * before trying to wake up the host after suspend.
+ */
+static unsigned autoresume = 0;
+module_param (autoresume, uint, 0);
+
+/*
+ * Normally the "loopback" configuration is second (index 1) so
+ * it's not the default.  Here's where to change that order, to
+ * work better with hosts where config changes are problematic.
+ * Or controllers (like superh) that only support one config.
+ */
+static int loopdefault = 0;
+
+module_param (loopdefault, bool, S_IRUGO|S_IWUSR);
+
+/*-------------------------------------------------------------------------*/
+
+/* Thanks to NetChip Technologies for donating this product ID.
+ *
+ * DO NOT REUSE THESE IDs with a protocol-incompatible driver!!  Ever!!
+ * Instead:  allocate your own, using normal USB-IF procedures.
+ */
+#ifndef	CONFIG_USB_ZERO_HNPTEST
+#define DRIVER_VENDOR_NUM	0x0525		/* NetChip */
+#define DRIVER_PRODUCT_NUM	0xa4a0		/* Linux-USB "Gadget Zero" */
+#else
+#define DRIVER_VENDOR_NUM	0x1a0a		/* OTG test device IDs */
+#define DRIVER_PRODUCT_NUM	0xbadd
+#endif
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * DESCRIPTORS ... most are static, but strings and (full)
+ * configuration descriptors are built on demand.
+ */
+
+/*
+#define STRING_MANUFACTURER		25
+#define STRING_PRODUCT			42
+#define STRING_SERIAL			101
+*/
+#define STRING_MANUFACTURER		1
+#define STRING_PRODUCT			2
+#define STRING_SERIAL			3
+
+#define STRING_SOURCE_SINK		250
+#define STRING_LOOPBACK			251
+
+/*
+ * This device advertises two configurations; these numbers work
+ * on a pxa250 as well as more flexible hardware.
+ */
+#define	CONFIG_SOURCE_SINK	3
+#define	CONFIG_LOOPBACK		2
+
+/*
+static struct usb_device_descriptor
+device_desc = {
+	.bLength =		sizeof device_desc,
+	.bDescriptorType =	USB_DT_DEVICE,
+
+	.bcdUSB =		__constant_cpu_to_le16 (0x0200),
+	.bDeviceClass =		USB_CLASS_VENDOR_SPEC,
+
+	.idVendor =		__constant_cpu_to_le16 (DRIVER_VENDOR_NUM),
+	.idProduct =		__constant_cpu_to_le16 (DRIVER_PRODUCT_NUM),
+	.iManufacturer =	STRING_MANUFACTURER,
+	.iProduct =		STRING_PRODUCT,
+	.iSerialNumber =	STRING_SERIAL,
+	.bNumConfigurations =	2,
+};
+*/
+static struct usb_device_descriptor
+device_desc = {
+	.bLength =		sizeof device_desc,
+	.bDescriptorType =	USB_DT_DEVICE,
+	.bcdUSB =		__constant_cpu_to_le16 (0x0100),
+	.bDeviceClass =		USB_CLASS_PER_INTERFACE,
+	.bDeviceSubClass =      0,
+	.bDeviceProtocol =      0,
+	.bMaxPacketSize0 =      64,
+	.bcdDevice =            __constant_cpu_to_le16 (0x0100),
+	.idVendor =		__constant_cpu_to_le16 (0x0499),
+	.idProduct =		__constant_cpu_to_le16 (0x3002),
+	.iManufacturer =	STRING_MANUFACTURER,
+	.iProduct =		STRING_PRODUCT,
+	.iSerialNumber =	STRING_SERIAL,
+	.bNumConfigurations =	1,
+};
+
+static struct usb_config_descriptor
+z_config = {
+	.bLength =		sizeof z_config,
+	.bDescriptorType =	USB_DT_CONFIG,
+
+	/* compute wTotalLength on the fly */
+	.bNumInterfaces =	2,
+	.bConfigurationValue =	1,
+	.iConfiguration =	0,
+	.bmAttributes =		0x40,
+	.bMaxPower =		0,	/* self-powered */
+};
+
+
+static struct usb_otg_descriptor
+otg_descriptor = {
+	.bLength =		sizeof otg_descriptor,
+	.bDescriptorType =	USB_DT_OTG,
+
+	.bmAttributes =		USB_OTG_SRP,
+};
+
+/* one interface in each configuration */
+#ifdef	CONFIG_USB_GADGET_DUALSPEED
+
+/*
+ * usb 2.0 devices need to expose both high speed and full speed
+ * descriptors, unless they only run at full speed.
+ *
+ * that means alternate endpoint descriptors (bigger packets)
+ * and a "device qualifier" ... plus more construction options
+ * for the config descriptor.
+ */
+
+static struct usb_qualifier_descriptor
+dev_qualifier = {
+	.bLength =		sizeof dev_qualifier,
+	.bDescriptorType =	USB_DT_DEVICE_QUALIFIER,
+
+	.bcdUSB =		__constant_cpu_to_le16 (0x0200),
+	.bDeviceClass =		USB_CLASS_VENDOR_SPEC,
+
+	.bNumConfigurations =	2,
+};
+
+
+struct usb_cs_as_general_descriptor {
+	__u8  bLength;
+	__u8  bDescriptorType;
+
+	__u8  bDescriptorSubType;
+	__u8  bTerminalLink;
+	__u8  bDelay;
+	__u16  wFormatTag;
+} __attribute__ ((packed));
+
+struct usb_cs_as_format_descriptor {
+	__u8  bLength;
+	__u8  bDescriptorType;
+
+	__u8  bDescriptorSubType;
+	__u8  bFormatType;
+	__u8  bNrChannels;
+	__u8  bSubframeSize;
+	__u8  bBitResolution;
+	__u8  bSamfreqType;
+	__u8  tLowerSamFreq[3];
+	__u8  tUpperSamFreq[3];
+} __attribute__ ((packed));
+
+static const struct usb_interface_descriptor
+z_audio_control_if_desc = {
+	.bLength =		sizeof z_audio_control_if_desc,
+	.bDescriptorType =	USB_DT_INTERFACE,
+	.bInterfaceNumber = 0,
+	.bAlternateSetting = 0,
+	.bNumEndpoints = 0,
+	.bInterfaceClass = USB_CLASS_AUDIO,
+	.bInterfaceSubClass = 0x1,
+	.bInterfaceProtocol = 0,
+	.iInterface = 0,
+};
+
+static const struct usb_interface_descriptor
+z_audio_if_desc = {
+	.bLength =		sizeof z_audio_if_desc,
+	.bDescriptorType =	USB_DT_INTERFACE,
+	.bInterfaceNumber = 1,
+	.bAlternateSetting = 0,
+	.bNumEndpoints = 0,
+	.bInterfaceClass = USB_CLASS_AUDIO,
+	.bInterfaceSubClass = 0x2,
+	.bInterfaceProtocol = 0,
+	.iInterface = 0,
+};
+
+static const struct usb_interface_descriptor
+z_audio_if_desc2 = {
+	.bLength =		sizeof z_audio_if_desc,
+	.bDescriptorType =	USB_DT_INTERFACE,
+	.bInterfaceNumber = 1,
+	.bAlternateSetting = 1,
+	.bNumEndpoints = 1,
+	.bInterfaceClass = USB_CLASS_AUDIO,
+	.bInterfaceSubClass = 0x2,
+	.bInterfaceProtocol = 0,
+	.iInterface = 0,
+};
+
+static const struct usb_cs_as_general_descriptor
+z_audio_cs_as_if_desc = {
+	.bLength = 7,
+	.bDescriptorType = 0x24,
+
+	.bDescriptorSubType = 0x01,
+	.bTerminalLink = 0x01,
+	.bDelay = 0x0,
+	.wFormatTag = __constant_cpu_to_le16 (0x0001)
+};
+
+
+static const struct usb_cs_as_format_descriptor
+z_audio_cs_as_format_desc = {
+	.bLength = 0xe,
+	.bDescriptorType = 0x24,
+
+	.bDescriptorSubType = 2,
+	.bFormatType = 1,
+	.bNrChannels = 1,
+	.bSubframeSize = 1,
+	.bBitResolution = 8,
+	.bSamfreqType = 0,
+	.tLowerSamFreq = {0x7e, 0x13, 0x00},
+	.tUpperSamFreq = {0xe2, 0xd6, 0x00},
+};
+
+static const struct usb_endpoint_descriptor
+z_iso_ep = {
+	.bLength = 0x09,
+	.bDescriptorType = 0x05,
+	.bEndpointAddress = 0x04,
+	.bmAttributes = 0x09,
+	.wMaxPacketSize = 0x0038,
+	.bInterval = 0x01,
+	.bRefresh = 0x00,
+	.bSynchAddress = 0x00,
+};
+
+static char z_iso_ep2[] = {0x07, 0x25, 0x01, 0x00, 0x02, 0x00, 0x02};
+
+// 9 bytes
+static char z_ac_interface_header_desc[] =
+{ 0x09, 0x24, 0x01, 0x00, 0x01, 0x2b, 0x00, 0x01, 0x01 };
+
+// 12 bytes
+static char z_0[] = {0x0c, 0x24, 0x02, 0x01, 0x01, 0x01, 0x00, 0x02,
+		     0x03, 0x00, 0x00, 0x00};
+// 13 bytes
+static char z_1[] = {0x0d, 0x24, 0x06, 0x02, 0x01, 0x02, 0x15, 0x00,
+		     0x02, 0x00, 0x02, 0x00, 0x00};
+// 9 bytes
+static char z_2[] = {0x09, 0x24, 0x03, 0x03, 0x01, 0x03, 0x00, 0x02,
+		     0x00};
+
+static char za_0[] = {0x09, 0x04, 0x01, 0x02, 0x01, 0x01, 0x02, 0x00,
+		      0x00};
+
+static char za_1[] = {0x07, 0x24, 0x01, 0x01, 0x00, 0x01, 0x00};
+
+static char za_2[] = {0x0e, 0x24, 0x02, 0x01, 0x02, 0x01, 0x08, 0x00,
+		      0x7e, 0x13, 0x00, 0xe2, 0xd6, 0x00};
+
+static char za_3[] = {0x09, 0x05, 0x04, 0x09, 0x70, 0x00, 0x01, 0x00,
+		      0x00};
+
+static char za_4[] = {0x07, 0x25, 0x01, 0x00, 0x02, 0x00, 0x02};
+
+static char za_5[] = {0x09, 0x04, 0x01, 0x03, 0x01, 0x01, 0x02, 0x00,
+		      0x00};
+
+static char za_6[] = {0x07, 0x24, 0x01, 0x01, 0x00, 0x01, 0x00};
+
+static char za_7[] = {0x0e, 0x24, 0x02, 0x01, 0x01, 0x02, 0x10, 0x00,
+		      0x7e, 0x13, 0x00, 0xe2, 0xd6, 0x00};
+
+static char za_8[] = {0x09, 0x05, 0x04, 0x09, 0x70, 0x00, 0x01, 0x00,
+		      0x00};
+
+static char za_9[] = {0x07, 0x25, 0x01, 0x00, 0x02, 0x00, 0x02};
+
+static char za_10[] = {0x09, 0x04, 0x01, 0x04, 0x01, 0x01, 0x02, 0x00,
+		       0x00};
+
+static char za_11[] = {0x07, 0x24, 0x01, 0x01, 0x00, 0x01, 0x00};
+
+static char za_12[] = {0x0e, 0x24, 0x02, 0x01, 0x02, 0x02, 0x10, 0x00,
+		       0x73, 0x13, 0x00, 0xe2, 0xd6, 0x00};
+
+static char za_13[] = {0x09, 0x05, 0x04, 0x09, 0xe0, 0x00, 0x01, 0x00,
+		       0x00};
+
+static char za_14[] = {0x07, 0x25, 0x01, 0x00, 0x02, 0x00, 0x02};
+
+static char za_15[] = {0x09, 0x04, 0x01, 0x05, 0x01, 0x01, 0x02, 0x00,
+		       0x00};
+
+static char za_16[] = {0x07, 0x24, 0x01, 0x01, 0x00, 0x01, 0x00};
+
+static char za_17[] = {0x0e, 0x24, 0x02, 0x01, 0x01, 0x03, 0x14, 0x00,
+		       0x7e, 0x13, 0x00, 0xe2, 0xd6, 0x00};
+
+static char za_18[] = {0x09, 0x05, 0x04, 0x09, 0xa8, 0x00, 0x01, 0x00,
+		       0x00};
+
+static char za_19[] = {0x07, 0x25, 0x01, 0x00, 0x02, 0x00, 0x02};
+
+static char za_20[] = {0x09, 0x04, 0x01, 0x06, 0x01, 0x01, 0x02, 0x00,
+		       0x00};
+
+static char za_21[] = {0x07, 0x24, 0x01, 0x01, 0x00, 0x01, 0x00};
+
+static char za_22[] = {0x0e, 0x24, 0x02, 0x01, 0x02, 0x03, 0x14, 0x00,
+		       0x7e, 0x13, 0x00, 0xe2, 0xd6, 0x00};
+
+static char za_23[] = {0x09, 0x05, 0x04, 0x09, 0x50, 0x01, 0x01, 0x00,
+		       0x00};
+
+static char za_24[] = {0x07, 0x25, 0x01, 0x00, 0x02, 0x00, 0x02};
+
+
+
+static const struct usb_descriptor_header *z_function [] = {
+	(struct usb_descriptor_header *) &z_audio_control_if_desc,
+	(struct usb_descriptor_header *) &z_ac_interface_header_desc,
+	(struct usb_descriptor_header *) &z_0,
+	(struct usb_descriptor_header *) &z_1,
+	(struct usb_descriptor_header *) &z_2,
+	(struct usb_descriptor_header *) &z_audio_if_desc,
+	(struct usb_descriptor_header *) &z_audio_if_desc2,
+	(struct usb_descriptor_header *) &z_audio_cs_as_if_desc,
+	(struct usb_descriptor_header *) &z_audio_cs_as_format_desc,
+	(struct usb_descriptor_header *) &z_iso_ep,
+	(struct usb_descriptor_header *) &z_iso_ep2,
+	(struct usb_descriptor_header *) &za_0,
+	(struct usb_descriptor_header *) &za_1,
+	(struct usb_descriptor_header *) &za_2,
+	(struct usb_descriptor_header *) &za_3,
+	(struct usb_descriptor_header *) &za_4,
+	(struct usb_descriptor_header *) &za_5,
+	(struct usb_descriptor_header *) &za_6,
+	(struct usb_descriptor_header *) &za_7,
+	(struct usb_descriptor_header *) &za_8,
+	(struct usb_descriptor_header *) &za_9,
+	(struct usb_descriptor_header *) &za_10,
+	(struct usb_descriptor_header *) &za_11,
+	(struct usb_descriptor_header *) &za_12,
+	(struct usb_descriptor_header *) &za_13,
+	(struct usb_descriptor_header *) &za_14,
+	(struct usb_descriptor_header *) &za_15,
+	(struct usb_descriptor_header *) &za_16,
+	(struct usb_descriptor_header *) &za_17,
+	(struct usb_descriptor_header *) &za_18,
+	(struct usb_descriptor_header *) &za_19,
+	(struct usb_descriptor_header *) &za_20,
+	(struct usb_descriptor_header *) &za_21,
+	(struct usb_descriptor_header *) &za_22,
+	(struct usb_descriptor_header *) &za_23,
+	(struct usb_descriptor_header *) &za_24,
+	NULL,
+};
+
+/* maxpacket and other transfer characteristics vary by speed. */
+#define ep_desc(g,hs,fs) (((g)->speed==USB_SPEED_HIGH)?(hs):(fs))
+
+#else
+
+/* if there's no high speed support, maxpacket doesn't change. */
+#define ep_desc(g,hs,fs) fs
+
+#endif	/* !CONFIG_USB_GADGET_DUALSPEED */
+
+static char				manufacturer [40];
+//static char				serial [40];
+static char				serial [] = "Ser 00 em";
+
+/* static strings, in UTF-8 */
+static struct usb_string		strings [] = {
+	{ STRING_MANUFACTURER, manufacturer, },
+	{ STRING_PRODUCT, longname, },
+	{ STRING_SERIAL, serial, },
+	{ STRING_LOOPBACK, loopback, },
+	{ STRING_SOURCE_SINK, source_sink, },
+	{  }			/* end of list */
+};
+
+static struct usb_gadget_strings	stringtab = {
+	.language	= 0x0409,	/* en-us */
+	.strings	= strings,
+};
+
+/*
+ * config descriptors are also handcrafted.  these must agree with code
+ * that sets configurations, and with code managing interfaces and their
+ * altsettings.  other complexity may come from:
+ *
+ *  - high speed support, including "other speed config" rules
+ *  - multiple configurations
+ *  - interfaces with alternate settings
+ *  - embedded class or vendor-specific descriptors
+ *
+ * this handles high speed, and has a second config that could as easily
+ * have been an alternate interface setting (on most hardware).
+ *
+ * NOTE:  to demonstrate (and test) more USB capabilities, this driver
+ * should include an altsetting to test interrupt transfers, including
+ * high bandwidth modes at high speed.  (Maybe work like Intel's test
+ * device?)
+ */
+static int
+config_buf (struct usb_gadget *gadget, u8 *buf, u8 type, unsigned index)
+{
+	int len;
+	const struct usb_descriptor_header **function;
+
+	function = z_function;
+	len = usb_gadget_config_buf (&z_config, buf, USB_BUFSIZ, function);
+	if (len < 0)
+		return len;
+	((struct usb_config_descriptor *) buf)->bDescriptorType = type;
+	return len;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static struct usb_request *
+alloc_ep_req (struct usb_ep *ep, unsigned length)
+{
+	struct usb_request	*req;
+
+	req = usb_ep_alloc_request (ep, GFP_ATOMIC);
+	if (req) {
+		req->length = length;
+		req->buf = usb_ep_alloc_buffer (ep, length,
+				&req->dma, GFP_ATOMIC);
+		if (!req->buf) {
+			usb_ep_free_request (ep, req);
+			req = NULL;
+		}
+	}
+	return req;
+}
+
+static void free_ep_req (struct usb_ep *ep, struct usb_request *req)
+{
+	if (req->buf)
+		usb_ep_free_buffer (ep, req->buf, req->dma, req->length);
+	usb_ep_free_request (ep, req);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* optionally require specific source/sink data patterns  */
+
+static int
+check_read_data (
+	struct zero_dev		*dev,
+	struct usb_ep		*ep,
+	struct usb_request	*req
+)
+{
+	unsigned	i;
+	u8		*buf = req->buf;
+
+	for (i = 0; i < req->actual; i++, buf++) {
+		switch (pattern) {
+		/* all-zeroes has no synchronization issues */
+		case 0:
+			if (*buf == 0)
+				continue;
+			break;
+		/* mod63 stays in sync with short-terminated transfers,
+		 * or otherwise when host and gadget agree on how large
+		 * each usb transfer request should be.  resync is done
+		 * with set_interface or set_config.
+		 */
+		case 1:
+			if (*buf == (u8)(i % 63))
+				continue;
+			break;
+		}
+		ERROR (dev, "bad OUT byte, buf [%d] = %d\n", i, *buf);
+		usb_ep_set_halt (ep);
+		return -EINVAL;
+	}
+	return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void zero_reset_config (struct zero_dev *dev)
+{
+	if (dev->config == 0)
+		return;
+
+	DBG (dev, "reset config\n");
+
+	/* just disable endpoints, forcing completion of pending i/o.
+	 * all our completion handlers free their requests in this case.
+	 */
+	if (dev->in_ep) {
+		usb_ep_disable (dev->in_ep);
+		dev->in_ep = NULL;
+	}
+	if (dev->out_ep) {
+		usb_ep_disable (dev->out_ep);
+		dev->out_ep = NULL;
+	}
+	dev->config = 0;
+	del_timer (&dev->resume);
+}
+
+#define _write(f, buf, sz) (f->f_op->write(f, buf, sz, &f->f_pos))
+
+static void
+zero_isoc_complete (struct usb_ep *ep, struct usb_request *req)
+{
+	struct zero_dev	*dev = ep->driver_data;
+	int		status = req->status;
+	int i, j;
+
+	switch (status) {
+
+	case 0: 			/* normal completion? */
+		//printk ("\nzero ---------------> isoc normal completion %d bytes\n", req->actual);
+		for (i=0, j=rbuf_start; i<req->actual; i++) {
+			//printk ("%02x ", ((__u8*)req->buf)[i]);
+			rbuf[j] = ((__u8*)req->buf)[i];
+			j++;
+			if (j >= RBUF_LEN) j=0;
+		}
+		rbuf_start = j;
+		//printk ("\n\n");
+
+		if (rbuf_len < RBUF_LEN) {
+			rbuf_len += req->actual;
+			if (rbuf_len > RBUF_LEN) {
+				rbuf_len = RBUF_LEN;
+			}
+		}
+
+		break;
+
+	/* this endpoint is normally active while we're configured */
+	case -ECONNABORTED: 		/* hardware forced ep reset */
+	case -ECONNRESET:		/* request dequeued */
+	case -ESHUTDOWN:		/* disconnect from host */
+		VDBG (dev, "%s gone (%d), %d/%d\n", ep->name, status,
+				req->actual, req->length);
+		if (ep == dev->out_ep)
+			check_read_data (dev, ep, req);
+		free_ep_req (ep, req);
+		return;
+
+	case -EOVERFLOW:		/* buffer overrun on read means that
+					 * we didn't provide a big enough
+					 * buffer.
+					 */
+	default:
+#if 1
+		DBG (dev, "%s complete --> %d, %d/%d\n", ep->name,
+				status, req->actual, req->length);
+#endif
+	case -EREMOTEIO:		/* short read */
+		break;
+	}
+
+	status = usb_ep_queue (ep, req, GFP_ATOMIC);
+	if (status) {
+		ERROR (dev, "kill %s:  resubmit %d bytes --> %d\n",
+				ep->name, req->length, status);
+		usb_ep_set_halt (ep);
+		/* FIXME recover later ... somehow */
+	}
+}
+
+static struct usb_request *
+zero_start_isoc_ep (struct usb_ep *ep, int gfp_flags)
+{
+	struct usb_request	*req;
+	int			status;
+
+	req = alloc_ep_req (ep, 512);
+	if (!req)
+		return NULL;
+
+	req->complete = zero_isoc_complete;
+
+	status = usb_ep_queue (ep, req, gfp_flags);
+	if (status) {
+		struct zero_dev	*dev = ep->driver_data;
+
+		ERROR (dev, "start %s --> %d\n", ep->name, status);
+		free_ep_req (ep, req);
+		req = NULL;
+	}
+
+	return req;
+}
+
+/* change our operational config.  this code must agree with the code
+ * that returns config descriptors, and altsetting code.
+ *
+ * it's also responsible for power management interactions. some
+ * configurations might not work with our current power sources.
+ *
+ * note that some device controller hardware will constrain what this
+ * code can do, perhaps by disallowing more than one configuration or
+ * by limiting configuration choices (like the pxa2xx).
+ */
+static int
+zero_set_config (struct zero_dev *dev, unsigned number, int gfp_flags)
+{
+	int			result = 0;
+	struct usb_gadget	*gadget = dev->gadget;
+	const struct usb_endpoint_descriptor	*d;
+	struct usb_ep		*ep;
+
+	if (number == dev->config)
+		return 0;
+
+	zero_reset_config (dev);
+
+	gadget_for_each_ep (ep, gadget) {
+
+		if (strcmp (ep->name, "ep4") == 0) {
+
+			d = (struct usb_endpoint_descripter *)&za_23; // isoc ep desc for audio i/f alt setting 6
+			result = usb_ep_enable (ep, d);
+
+			if (result == 0) {
+				ep->driver_data = dev;
+				dev->in_ep = ep;
+
+				if (zero_start_isoc_ep (ep, gfp_flags) != 0) {
+
+					dev->in_ep = ep;
+					continue;
+				}
+
+				usb_ep_disable (ep);
+				result = -EIO;
+			}
+		}
+
+	}
+
+	dev->config = number;
+	return result;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void zero_setup_complete (struct usb_ep *ep, struct usb_request *req)
+{
+	if (req->status || req->actual != req->length)
+		DBG ((struct zero_dev *) ep->driver_data,
+				"setup complete --> %d, %d/%d\n",
+				req->status, req->actual, req->length);
+}
+
+/*
+ * The setup() callback implements all the ep0 functionality that's
+ * not handled lower down, in hardware or the hardware driver (like
+ * device and endpoint feature flags, and their status).  It's all
+ * housekeeping for the gadget function we're implementing.  Most of
+ * the work is in config-specific setup.
+ */
+static int
+zero_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
+{
+	struct zero_dev		*dev = get_gadget_data (gadget);
+	struct usb_request	*req = dev->req;
+	int			value = -EOPNOTSUPP;
+
+	/* usually this stores reply data in the pre-allocated ep0 buffer,
+	 * but config change events will reconfigure hardware.
+	 */
+	req->zero = 0;
+	switch (ctrl->bRequest) {
+
+	case USB_REQ_GET_DESCRIPTOR:
+
+		switch (ctrl->wValue >> 8) {
+
+		case USB_DT_DEVICE:
+			value = min (ctrl->wLength, (u16) sizeof device_desc);
+			memcpy (req->buf, &device_desc, value);
+			break;
+#ifdef CONFIG_USB_GADGET_DUALSPEED
+		case USB_DT_DEVICE_QUALIFIER:
+			if (!gadget->is_dualspeed)
+				break;
+			value = min (ctrl->wLength, (u16) sizeof dev_qualifier);
+			memcpy (req->buf, &dev_qualifier, value);
+			break;
+
+		case USB_DT_OTHER_SPEED_CONFIG:
+			if (!gadget->is_dualspeed)
+				break;
+			// FALLTHROUGH
+#endif /* CONFIG_USB_GADGET_DUALSPEED */
+		case USB_DT_CONFIG:
+			value = config_buf (gadget, req->buf,
+					ctrl->wValue >> 8,
+					ctrl->wValue & 0xff);
+			if (value >= 0)
+				value = min (ctrl->wLength, (u16) value);
+			break;
+
+		case USB_DT_STRING:
+			/* wIndex == language code.
+			 * this driver only handles one language, you can
+			 * add string tables for other languages, using
+			 * any UTF-8 characters
+			 */
+			value = usb_gadget_get_string (&stringtab,
+					ctrl->wValue & 0xff, req->buf);
+			if (value >= 0) {
+				value = min (ctrl->wLength, (u16) value);
+			}
+			break;
+		}
+		break;
+
+	/* currently two configs, two speeds */
+	case USB_REQ_SET_CONFIGURATION:
+		if (ctrl->bRequestType != 0)
+			goto unknown;
+
+		spin_lock (&dev->lock);
+		value = zero_set_config (dev, ctrl->wValue, GFP_ATOMIC);
+		spin_unlock (&dev->lock);
+		break;
+	case USB_REQ_GET_CONFIGURATION:
+		if (ctrl->bRequestType != USB_DIR_IN)
+			goto unknown;
+		*(u8 *)req->buf = dev->config;
+		value = min (ctrl->wLength, (u16) 1);
+		break;
+
+	/* until we add altsetting support, or other interfaces,
+	 * only 0/0 are possible.  pxa2xx only supports 0/0 (poorly)
+	 * and already killed pending endpoint I/O.
+	 */
+	case USB_REQ_SET_INTERFACE:
+
+		if (ctrl->bRequestType != USB_RECIP_INTERFACE)
+			goto unknown;
+		spin_lock (&dev->lock);
+		if (dev->config) {
+			u8		config = dev->config;
+
+			/* resets interface configuration, forgets about
+			 * previous transaction state (queued bufs, etc)
+			 * and re-inits endpoint state (toggle etc)
+			 * no response queued, just zero status == success.
+			 * if we had more than one interface we couldn't
+			 * use this "reset the config" shortcut.
+			 */
+			zero_reset_config (dev);
+			zero_set_config (dev, config, GFP_ATOMIC);
+			value = 0;
+		}
+		spin_unlock (&dev->lock);
+		break;
+	case USB_REQ_GET_INTERFACE:
+		if ((ctrl->bRequestType == 0x21) && (ctrl->wIndex == 0x02)) {
+			value = ctrl->wLength;
+			break;
+		}
+		else {
+			if (ctrl->bRequestType != (USB_DIR_IN|USB_RECIP_INTERFACE))
+				goto unknown;
+			if (!dev->config)
+				break;
+			if (ctrl->wIndex != 0) {
+				value = -EDOM;
+				break;
+			}
+			*(u8 *)req->buf = 0;
+			value = min (ctrl->wLength, (u16) 1);
+		}
+		break;
+
+	/*
+	 * These are the same vendor-specific requests supported by
+	 * Intel's USB 2.0 compliance test devices.  We exceed that
+	 * device spec by allowing multiple-packet requests.
+	 */
+	case 0x5b:	/* control WRITE test -- fill the buffer */
+		if (ctrl->bRequestType != (USB_DIR_OUT|USB_TYPE_VENDOR))
+			goto unknown;
+		if (ctrl->wValue || ctrl->wIndex)
+			break;
+		/* just read that many bytes into the buffer */
+		if (ctrl->wLength > USB_BUFSIZ)
+			break;
+		value = ctrl->wLength;
+		break;
+	case 0x5c:	/* control READ test -- return the buffer */
+		if (ctrl->bRequestType != (USB_DIR_IN|USB_TYPE_VENDOR))
+			goto unknown;
+		if (ctrl->wValue || ctrl->wIndex)
+			break;
+		/* expect those bytes are still in the buffer; send back */
+		if (ctrl->wLength > USB_BUFSIZ
+				|| ctrl->wLength != req->length)
+			break;
+		value = ctrl->wLength;
+		break;
+
+	case 0x01: // SET_CUR
+	case 0x02:
+	case 0x03:
+	case 0x04:
+	case 0x05:
+		value = ctrl->wLength;
+		break;
+	case 0x81:
+		switch (ctrl->wValue) {
+		case 0x0201:
+		case 0x0202:
+			((u8*)req->buf)[0] = 0x00;
+			((u8*)req->buf)[1] = 0xe3;
+			break;
+		case 0x0300:
+		case 0x0500:
+			((u8*)req->buf)[0] = 0x00;
+			break;
+		}
+		//((u8*)req->buf)[0] = 0x81;
+		//((u8*)req->buf)[1] = 0x81;
+		value = ctrl->wLength;
+		break;
+	case 0x82:
+		switch (ctrl->wValue) {
+		case 0x0201:
+		case 0x0202:
+			((u8*)req->buf)[0] = 0x00;
+			((u8*)req->buf)[1] = 0xc3;
+			break;
+		case 0x0300:
+		case 0x0500:
+			((u8*)req->buf)[0] = 0x00;
+			break;
+		}
+		//((u8*)req->buf)[0] = 0x82;
+		//((u8*)req->buf)[1] = 0x82;
+		value = ctrl->wLength;
+		break;
+	case 0x83:
+		switch (ctrl->wValue) {
+		case 0x0201:
+		case 0x0202:
+			((u8*)req->buf)[0] = 0x00;
+			((u8*)req->buf)[1] = 0x00;
+			break;
+		case 0x0300:
+			((u8*)req->buf)[0] = 0x60;
+			break;
+		case 0x0500:
+			((u8*)req->buf)[0] = 0x18;
+			break;
+		}
+		//((u8*)req->buf)[0] = 0x83;
+		//((u8*)req->buf)[1] = 0x83;
+		value = ctrl->wLength;
+		break;
+	case 0x84:
+		switch (ctrl->wValue) {
+		case 0x0201:
+		case 0x0202:
+			((u8*)req->buf)[0] = 0x00;
+			((u8*)req->buf)[1] = 0x01;
+			break;
+		case 0x0300:
+		case 0x0500:
+			((u8*)req->buf)[0] = 0x08;
+			break;
+		}
+		//((u8*)req->buf)[0] = 0x84;
+		//((u8*)req->buf)[1] = 0x84;
+		value = ctrl->wLength;
+		break;
+	case 0x85:
+		((u8*)req->buf)[0] = 0x85;
+		((u8*)req->buf)[1] = 0x85;
+		value = ctrl->wLength;
+		break;
+
+
+	default:
+unknown:
+		printk("unknown control req%02x.%02x v%04x i%04x l%d\n",
+			ctrl->bRequestType, ctrl->bRequest,
+			ctrl->wValue, ctrl->wIndex, ctrl->wLength);
+	}
+
+	/* respond with data transfer before status phase? */
+	if (value >= 0) {
+		req->length = value;
+		req->zero = value < ctrl->wLength
+				&& (value % gadget->ep0->maxpacket) == 0;
+		value = usb_ep_queue (gadget->ep0, req, GFP_ATOMIC);
+		if (value < 0) {
+			DBG (dev, "ep_queue < 0 --> %d\n", value);
+			req->status = 0;
+			zero_setup_complete (gadget->ep0, req);
+		}
+	}
+
+	/* device either stalls (value < 0) or reports success */
+	return value;
+}
+
+static void
+zero_disconnect (struct usb_gadget *gadget)
+{
+	struct zero_dev		*dev = get_gadget_data (gadget);
+	unsigned long		flags;
+
+	spin_lock_irqsave (&dev->lock, flags);
+	zero_reset_config (dev);
+
+	/* a more significant application might have some non-usb
+	 * activities to quiesce here, saving resources like power
+	 * or pushing the notification up a network stack.
+	 */
+	spin_unlock_irqrestore (&dev->lock, flags);
+
+	/* next we may get setup() calls to enumerate new connections;
+	 * or an unbind() during shutdown (including removing module).
+	 */
+}
+
+static void
+zero_autoresume (unsigned long _dev)
+{
+	struct zero_dev	*dev = (struct zero_dev *) _dev;
+	int		status;
+
+	/* normally the host would be woken up for something
+	 * more significant than just a timer firing...
+	 */
+	if (dev->gadget->speed != USB_SPEED_UNKNOWN) {
+		status = usb_gadget_wakeup (dev->gadget);
+		DBG (dev, "wakeup --> %d\n", status);
+	}
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void
+zero_unbind (struct usb_gadget *gadget)
+{
+	struct zero_dev		*dev = get_gadget_data (gadget);
+
+	DBG (dev, "unbind\n");
+
+	/* we've already been disconnected ... no i/o is active */
+	if (dev->req)
+		free_ep_req (gadget->ep0, dev->req);
+	del_timer_sync (&dev->resume);
+	kfree (dev);
+	set_gadget_data (gadget, NULL);
+}
+
+static int
+zero_bind (struct usb_gadget *gadget)
+{
+	struct zero_dev		*dev;
+	//struct usb_ep		*ep;
+
+	printk("binding\n");
+	/*
+	 * DRIVER POLICY CHOICE:  you may want to do this differently.
+	 * One thing to avoid is reusing a bcdDevice revision code
+	 * with different host-visible configurations or behavior
+	 * restrictions -- using ep1in/ep2out vs ep1out/ep3in, etc
+	 */
+	//device_desc.bcdDevice = __constant_cpu_to_le16 (0x0201);
+
+
+	/* ok, we made sense of the hardware ... */
+	dev = kzalloc (sizeof *dev, SLAB_KERNEL);
+	if (!dev)
+		return -ENOMEM;
+	spin_lock_init (&dev->lock);
+	dev->gadget = gadget;
+	set_gadget_data (gadget, dev);
+
+	/* preallocate control response and buffer */
+	dev->req = usb_ep_alloc_request (gadget->ep0, GFP_KERNEL);
+	if (!dev->req)
+		goto enomem;
+	dev->req->buf = usb_ep_alloc_buffer (gadget->ep0, USB_BUFSIZ,
+				&dev->req->dma, GFP_KERNEL);
+	if (!dev->req->buf)
+		goto enomem;
+
+	dev->req->complete = zero_setup_complete;
+
+	device_desc.bMaxPacketSize0 = gadget->ep0->maxpacket;
+
+#ifdef CONFIG_USB_GADGET_DUALSPEED
+	/* assume ep0 uses the same value for both speeds ... */
+	dev_qualifier.bMaxPacketSize0 = device_desc.bMaxPacketSize0;
+
+	/* and that all endpoints are dual-speed */
+	//hs_source_desc.bEndpointAddress = fs_source_desc.bEndpointAddress;
+	//hs_sink_desc.bEndpointAddress = fs_sink_desc.bEndpointAddress;
+#endif
+
+	usb_gadget_set_selfpowered (gadget);
+
+	init_timer (&dev->resume);
+	dev->resume.function = zero_autoresume;
+	dev->resume.data = (unsigned long) dev;
+
+	gadget->ep0->driver_data = dev;
+
+	INFO (dev, "%s, version: " DRIVER_VERSION "\n", longname);
+	INFO (dev, "using %s, OUT %s IN %s\n", gadget->name,
+		EP_OUT_NAME, EP_IN_NAME);
+
+	snprintf (manufacturer, sizeof manufacturer,
+		UTS_SYSNAME " " UTS_RELEASE " with %s",
+		gadget->name);
+
+	return 0;
+
+enomem:
+	zero_unbind (gadget);
+	return -ENOMEM;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void
+zero_suspend (struct usb_gadget *gadget)
+{
+	struct zero_dev		*dev = get_gadget_data (gadget);
+
+	if (gadget->speed == USB_SPEED_UNKNOWN)
+		return;
+
+	if (autoresume) {
+		mod_timer (&dev->resume, jiffies + (HZ * autoresume));
+		DBG (dev, "suspend, wakeup in %d seconds\n", autoresume);
+	} else
+		DBG (dev, "suspend\n");
+}
+
+static void
+zero_resume (struct usb_gadget *gadget)
+{
+	struct zero_dev		*dev = get_gadget_data (gadget);
+
+	DBG (dev, "resume\n");
+	del_timer (&dev->resume);
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+static struct usb_gadget_driver zero_driver = {
+#ifdef CONFIG_USB_GADGET_DUALSPEED
+	.speed		= USB_SPEED_HIGH,
+#else
+	.speed		= USB_SPEED_FULL,
+#endif
+	.function	= (char *) longname,
+	.bind		= zero_bind,
+	.unbind		= zero_unbind,
+
+	.setup		= zero_setup,
+	.disconnect	= zero_disconnect,
+
+	.suspend	= zero_suspend,
+	.resume		= zero_resume,
+
+	.driver 	= {
+		.name		= (char *) shortname,
+		// .shutdown = ...
+		// .suspend = ...
+		// .resume = ...
+	},
+};
+
+MODULE_AUTHOR ("David Brownell");
+MODULE_LICENSE ("Dual BSD/GPL");
+
+static struct proc_dir_entry *pdir, *pfile;
+
+static int isoc_read_data (char *page, char **start,
+			   off_t off, int count,
+			   int *eof, void *data)
+{
+	int i;
+	static int c = 0;
+	static int done = 0;
+	static int s = 0;
+
+/*
+	printk ("\ncount: %d\n", count);
+	printk ("rbuf_start: %d\n", rbuf_start);
+	printk ("rbuf_len: %d\n", rbuf_len);
+	printk ("off: %d\n", off);
+	printk ("start: %p\n\n", *start);
+*/
+	if (done) {
+		c = 0;
+		done = 0;
+		*eof = 1;
+		return 0;
+	}
+
+	if (c == 0) {
+		if (rbuf_len == RBUF_LEN)
+			s = rbuf_start;
+		else s = 0;
+	}
+
+	for (i=0; i<count && c<rbuf_len; i++, c++) {
+		page[i] = rbuf[(c+s) % RBUF_LEN];
+	}
+	*start = page;
+
+	if (c >= rbuf_len) {
+		*eof = 1;
+		done = 1;
+	}
+
+
+	return i;
+}
+
+static int __init init (void)
+{
+
+	int retval = 0;
+
+	pdir = proc_mkdir("isoc_test", NULL);
+	if(pdir == NULL) {
+		retval = -ENOMEM;
+		printk("Error creating dir\n");
+		goto done;
+	}
+	pdir->owner = THIS_MODULE;
+
+	pfile = create_proc_read_entry("isoc_data",
+				       0444, pdir,
+				       isoc_read_data,
+				       NULL);
+	if (pfile == NULL) {
+		retval = -ENOMEM;
+		printk("Error creating file\n");
+		goto no_file;
+	}
+	pfile->owner = THIS_MODULE;
+
+	return usb_gadget_register_driver (&zero_driver);
+
+ no_file:
+	remove_proc_entry("isoc_data", NULL);
+ done:
+	return retval;
+}
+module_init (init);
+
+static void __exit cleanup (void)
+{
+
+	usb_gadget_unregister_driver (&zero_driver);
+
+	remove_proc_entry("isoc_data", pdir);
+	remove_proc_entry("isoc_test", NULL);
+}
+module_exit (cleanup);
diff -Nur --no-dereference linux-5.10.52-orig/drivers/usb/host/dwc_otg/dwc_cfi_common.h linux-5.10.52-v7l+/drivers/usb/host/dwc_otg/dwc_cfi_common.h
--- linux-5.10.52-orig/drivers/usb/host/dwc_otg/dwc_cfi_common.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/usb/host/dwc_otg/dwc_cfi_common.h	2021-07-25 16:46:28.657910448 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/* ==========================================================================
+ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter,
+ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless
+ * otherwise expressly agreed to in writing between Synopsys and you.
+ *
+ * The Software IS NOT an item of Licensed Software or Licensed Product under
+ * any End User Software License Agreement or Agreement for Licensed Product
+ * with Synopsys or any supplement thereto. You are permitted to use and
+ * redistribute this Software in source and binary forms, with or without
+ * modification, provided that redistributions of source code must retain this
+ * notice. You may not view, use, disclose, copy or distribute this file or
+ * any information contained herein except pursuant to this license grant from
+ * Synopsys. If you do not agree with this notice, including the disclaimer
+ * below, then you are not authorized to use the Software.
+ *
+ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * ========================================================================== */
+
+#if !defined(__DWC_CFI_COMMON_H__)
+#define __DWC_CFI_COMMON_H__
+
+//#include <linux/types.h>
+
+/**
+ * @file
+ *
+ * This file contains the CFI specific common constants, interfaces
+ * (functions and macros) and structures for Linux. No PCD specific
+ * data structure or definition is to be included in this file.
+ *
+ */
+
+/** This is a request for all Core Features */
+#define VEN_CORE_GET_FEATURES		0xB1
+
+/** This is a request to get the value of a specific Core Feature */
+#define VEN_CORE_GET_FEATURE		0xB2
+
+/** This command allows the host to set the value of a specific Core Feature */
+#define VEN_CORE_SET_FEATURE		0xB3
+
+/** This command allows the host to set the default values of
+ * either all or any specific Core Feature
+ */
+#define VEN_CORE_RESET_FEATURES		0xB4
+
+/** This command forces the PCD to write the deferred values of a Core Features */
+#define VEN_CORE_ACTIVATE_FEATURES	0xB5
+
+/** This request reads a DWORD value from a register at the specified offset */
+#define VEN_CORE_READ_REGISTER		0xB6
+
+/** This request writes a DWORD value into a register at the specified offset */
+#define VEN_CORE_WRITE_REGISTER		0xB7
+
+/** This structure is the header of the Core Features dataset returned to
+ *  the Host
+ */
+struct cfi_all_features_header {
+/** The features header structure length is */
+#define CFI_ALL_FEATURES_HDR_LEN		8
+	/**
+	 * The total length of the features dataset returned to the Host
+	 */
+	uint16_t wTotalLen;
+
+	/**
+	 * CFI version number inBinary-Coded Decimal (i.e., 1.00 is 100H).
+	 * This field identifies the version of the CFI Specification with which
+	 * the device is compliant.
+	 */
+	uint16_t wVersion;
+
+	/** The ID of the Core */
+	uint16_t wCoreID;
+#define CFI_CORE_ID_UDC		1
+#define CFI_CORE_ID_OTG		2
+#define CFI_CORE_ID_WUDEV	3
+
+	/** Number of features returned by VEN_CORE_GET_FEATURES request */
+	uint16_t wNumFeatures;
+} UPACKED;
+
+typedef struct cfi_all_features_header cfi_all_features_header_t;
+
+/** This structure is a header of the Core Feature descriptor dataset returned to
+ *  the Host after the VEN_CORE_GET_FEATURES request
+ */
+struct cfi_feature_desc_header {
+#define CFI_FEATURE_DESC_HDR_LEN	8
+
+	/** The feature ID */
+	uint16_t wFeatureID;
+
+	/** Length of this feature descriptor in bytes - including the
+	 * length of the feature name string
+	 */
+	uint16_t wLength;
+
+	/** The data length of this feature in bytes */
+	uint16_t wDataLength;
+
+	/**
+	 * Attributes of this features
+	 * D0: Access rights
+	 * 0 - Read/Write
+	 * 1 - Read only
+	 */
+	uint8_t bmAttributes;
+#define CFI_FEATURE_ATTR_RO		1
+#define CFI_FEATURE_ATTR_RW		0
+
+	/** Length of the feature name in bytes */
+	uint8_t bNameLen;
+
+	/** The feature name buffer */
+	//uint8_t *name;
+} UPACKED;
+
+typedef struct cfi_feature_desc_header cfi_feature_desc_header_t;
+
+/**
+ * This structure describes a NULL terminated string referenced by its id field.
+ * It is very similar to usb_string structure but has the id field type set to 16-bit.
+ */
+struct cfi_string {
+	uint16_t id;
+	const uint8_t *s;
+};
+typedef struct cfi_string cfi_string_t;
+
+#endif
diff -Nur --no-dereference linux-5.10.52-orig/drivers/usb/host/dwc_otg/dwc_otg_adp.c linux-5.10.52-v7l+/drivers/usb/host/dwc_otg/dwc_otg_adp.c
--- linux-5.10.52-orig/drivers/usb/host/dwc_otg/dwc_otg_adp.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/usb/host/dwc_otg/dwc_otg_adp.c	2021-07-25 16:46:28.657910448 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/* ==========================================================================
+ * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_adp.c $
+ * $Revision: #12 $
+ * $Date: 2011/10/26 $
+ * $Change: 1873028 $
+ *
+ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter,
+ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless
+ * otherwise expressly agreed to in writing between Synopsys and you.
+ *
+ * The Software IS NOT an item of Licensed Software or Licensed Product under
+ * any End User Software License Agreement or Agreement for Licensed Product
+ * with Synopsys or any supplement thereto. You are permitted to use and
+ * redistribute this Software in source and binary forms, with or without
+ * modification, provided that redistributions of source code must retain this
+ * notice. You may not view, use, disclose, copy or distribute this file or
+ * any information contained herein except pursuant to this license grant from
+ * Synopsys. If you do not agree with this notice, including the disclaimer
+ * below, then you are not authorized to use the Software.
+ *
+ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * ========================================================================== */
+
+#include "dwc_os.h"
+#include "dwc_otg_regs.h"
+#include "dwc_otg_cil.h"
+#include "dwc_otg_adp.h"
+
+/** @file
+ *
+ * This file contains the most of the Attach Detect Protocol implementation for
+ * the driver to support OTG Rev2.0.
+ *
+ */
+
+void dwc_otg_adp_write_reg(dwc_otg_core_if_t * core_if, uint32_t value)
+{
+	adpctl_data_t adpctl;
+
+	adpctl.d32 = value;
+	adpctl.b.ar = 0x2;
+
+	DWC_WRITE_REG32(&core_if->core_global_regs->adpctl, adpctl.d32);
+
+	while (adpctl.b.ar) {
+		adpctl.d32 = DWC_READ_REG32(&core_if->core_global_regs->adpctl);
+	}
+
+}
+
+/**
+ * Function is called to read ADP registers
+ */
+uint32_t dwc_otg_adp_read_reg(dwc_otg_core_if_t * core_if)
+{
+	adpctl_data_t adpctl;
+
+	adpctl.d32 = 0;
+	adpctl.b.ar = 0x1;
+
+	DWC_WRITE_REG32(&core_if->core_global_regs->adpctl, adpctl.d32);
+
+	while (adpctl.b.ar) {
+		adpctl.d32 = DWC_READ_REG32(&core_if->core_global_regs->adpctl);
+	}
+
+	return adpctl.d32;
+}
+
+/**
+ * Function is called to read ADPCTL register and filter Write-clear bits
+ */
+uint32_t dwc_otg_adp_read_reg_filter(dwc_otg_core_if_t * core_if)
+{
+	adpctl_data_t adpctl;
+
+	adpctl.d32 = dwc_otg_adp_read_reg(core_if);
+	adpctl.b.adp_tmout_int = 0;
+	adpctl.b.adp_prb_int = 0;
+	adpctl.b.adp_tmout_int = 0;
+
+	return adpctl.d32;
+}
+
+/**
+ * Function is called to write ADP registers
+ */
+void dwc_otg_adp_modify_reg(dwc_otg_core_if_t * core_if, uint32_t clr,
+			    uint32_t set)
+{
+	dwc_otg_adp_write_reg(core_if,
+			      (dwc_otg_adp_read_reg(core_if) & (~clr)) | set);
+}
+
+static void adp_sense_timeout(void *ptr)
+{
+	dwc_otg_core_if_t *core_if = (dwc_otg_core_if_t *) ptr;
+	core_if->adp.sense_timer_started = 0;
+	DWC_PRINTF("ADP SENSE TIMEOUT\n");
+	if (core_if->adp_enable) {
+		dwc_otg_adp_sense_stop(core_if);
+		dwc_otg_adp_probe_start(core_if);
+	}
+}
+
+/**
+ * This function is called when the ADP vbus timer expires. Timeout is 1.1s.
+ */
+static void adp_vbuson_timeout(void *ptr)
+{
+	gpwrdn_data_t gpwrdn;
+	dwc_otg_core_if_t *core_if = (dwc_otg_core_if_t *) ptr;
+	hprt0_data_t hprt0 = {.d32 = 0 };
+	pcgcctl_data_t pcgcctl = {.d32 = 0 };
+	DWC_PRINTF("%s: 1.1 seconds expire after turning on VBUS\n",__FUNCTION__);
+	if (core_if) {
+		core_if->adp.vbuson_timer_started = 0;
+		/* Turn off vbus */
+		hprt0.b.prtpwr = 1;
+		DWC_MODIFY_REG32(core_if->host_if->hprt0, hprt0.d32, 0);
+		gpwrdn.d32 = 0;
+
+		/* Power off the core */
+		if (core_if->power_down == 2) {
+			/* Enable Wakeup Logic */
+//                      gpwrdn.b.wkupactiv = 1;
+			gpwrdn.b.pmuactv = 0;
+			gpwrdn.b.pwrdnrstn = 1;
+			gpwrdn.b.pwrdnclmp = 1;
+			DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, 0,
+					 gpwrdn.d32);
+
+			/* Suspend the Phy Clock */
+			pcgcctl.b.stoppclk = 1;
+			DWC_MODIFY_REG32(core_if->pcgcctl, 0, pcgcctl.d32);
+
+			/* Switch on VDD */
+//                      gpwrdn.b.wkupactiv = 1;
+			gpwrdn.b.pmuactv = 1;
+			gpwrdn.b.pwrdnrstn = 1;
+			gpwrdn.b.pwrdnclmp = 1;
+			DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, 0,
+					 gpwrdn.d32);
+		} else {
+			/* Enable Power Down Logic */
+			gpwrdn.b.pmuintsel = 1;
+			gpwrdn.b.pmuactv = 1;
+			DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, 0, gpwrdn.d32);
+		}
+
+		/* Power off the core */
+		if (core_if->power_down == 2) {
+			gpwrdn.d32 = 0;
+			gpwrdn.b.pwrdnswtch = 1;
+			DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn,
+					 gpwrdn.d32, 0);
+		}
+
+		/* Unmask SRP detected interrupt from Power Down Logic */
+		gpwrdn.d32 = 0;
+		gpwrdn.b.srp_det_msk = 1;
+		DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, 0, gpwrdn.d32);
+
+		dwc_otg_adp_probe_start(core_if);
+		dwc_otg_dump_global_registers(core_if);
+		dwc_otg_dump_host_registers(core_if);
+	}
+
+}
+
+/**
+ * Start the ADP Initial Probe timer to detect if Port Connected interrupt is
+ * not asserted within 1.1 seconds.
+ *
+ * @param core_if the pointer to core_if strucure.
+ */
+void dwc_otg_adp_vbuson_timer_start(dwc_otg_core_if_t * core_if)
+{
+	core_if->adp.vbuson_timer_started = 1;
+	if (core_if->adp.vbuson_timer)
+	{
+		DWC_PRINTF("SCHEDULING VBUSON TIMER\n");
+		/* 1.1 secs + 60ms necessary for cil_hcd_start*/
+		DWC_TIMER_SCHEDULE(core_if->adp.vbuson_timer, 1160);
+	} else {
+		DWC_WARN("VBUSON_TIMER = %p\n",core_if->adp.vbuson_timer);
+	}
+}
+
+#if 0
+/**
+ * Masks all DWC OTG core interrupts
+ *
+ */
+static void mask_all_interrupts(dwc_otg_core_if_t * core_if)
+{
+	int i;
+	gahbcfg_data_t ahbcfg = {.d32 = 0 };
+
+	/* Mask Host Interrupts */
+
+	/* Clear and disable HCINTs */
+	for (i = 0; i < core_if->core_params->host_channels; i++) {
+		DWC_WRITE_REG32(&core_if->host_if->hc_regs[i]->hcintmsk, 0);
+		DWC_WRITE_REG32(&core_if->host_if->hc_regs[i]->hcint, 0xFFFFFFFF);
+
+	}
+
+	/* Clear and disable HAINT */
+	DWC_WRITE_REG32(&core_if->host_if->host_global_regs->haintmsk, 0x0000);
+	DWC_WRITE_REG32(&core_if->host_if->host_global_regs->haint, 0xFFFFFFFF);
+
+	/* Mask Device Interrupts */
+	if (!core_if->multiproc_int_enable) {
+		/* Clear and disable IN Endpoint interrupts */
+		DWC_WRITE_REG32(&core_if->dev_if->dev_global_regs->diepmsk, 0);
+		for (i = 0; i <= core_if->dev_if->num_in_eps; i++) {
+			DWC_WRITE_REG32(&core_if->dev_if->in_ep_regs[i]->
+					diepint, 0xFFFFFFFF);
+		}
+
+		/* Clear and disable OUT Endpoint interrupts */
+		DWC_WRITE_REG32(&core_if->dev_if->dev_global_regs->doepmsk, 0);
+		for (i = 0; i <= core_if->dev_if->num_out_eps; i++) {
+			DWC_WRITE_REG32(&core_if->dev_if->out_ep_regs[i]->
+					doepint, 0xFFFFFFFF);
+		}
+
+		/* Clear and disable DAINT */
+		DWC_WRITE_REG32(&core_if->dev_if->dev_global_regs->daint,
+				0xFFFFFFFF);
+		DWC_WRITE_REG32(&core_if->dev_if->dev_global_regs->daintmsk, 0);
+	} else {
+		for (i = 0; i < core_if->dev_if->num_in_eps; ++i) {
+			DWC_WRITE_REG32(&core_if->dev_if->dev_global_regs->
+					diepeachintmsk[i], 0);
+			DWC_WRITE_REG32(&core_if->dev_if->in_ep_regs[i]->
+					diepint, 0xFFFFFFFF);
+		}
+
+		for (i = 0; i < core_if->dev_if->num_out_eps; ++i) {
+			DWC_WRITE_REG32(&core_if->dev_if->dev_global_regs->
+					doepeachintmsk[i], 0);
+			DWC_WRITE_REG32(&core_if->dev_if->out_ep_regs[i]->
+					doepint, 0xFFFFFFFF);
+		}
+
+		DWC_WRITE_REG32(&core_if->dev_if->dev_global_regs->deachintmsk,
+				0);
+		DWC_WRITE_REG32(&core_if->dev_if->dev_global_regs->deachint,
+				0xFFFFFFFF);
+
+	}
+
+	/* Disable interrupts */
+	ahbcfg.b.glblintrmsk = 1;
+	DWC_MODIFY_REG32(&core_if->core_global_regs->gahbcfg, ahbcfg.d32, 0);
+
+	/* Disable all interrupts. */
+	DWC_WRITE_REG32(&core_if->core_global_regs->gintmsk, 0);
+
+	/* Clear any pending interrupts */
+	DWC_WRITE_REG32(&core_if->core_global_regs->gintsts, 0xFFFFFFFF);
+
+	/* Clear any pending OTG Interrupts */
+	DWC_WRITE_REG32(&core_if->core_global_regs->gotgint, 0xFFFFFFFF);
+}
+
+/**
+ * Unmask Port Connection Detected interrupt
+ *
+ */
+static void unmask_conn_det_intr(dwc_otg_core_if_t * core_if)
+{
+	gintmsk_data_t gintmsk = {.d32 = 0,.b.portintr = 1 };
+
+	DWC_WRITE_REG32(&core_if->core_global_regs->gintmsk, gintmsk.d32);
+}
+#endif
+
+/**
+ * Starts the ADP Probing
+ *
+ * @param core_if the pointer to core_if structure.
+ */
+uint32_t dwc_otg_adp_probe_start(dwc_otg_core_if_t * core_if)
+{
+
+	adpctl_data_t adpctl = {.d32 = 0};
+	gpwrdn_data_t gpwrdn;
+#if 0
+	adpctl_data_t adpctl_int = {.d32 = 0, .b.adp_prb_int = 1,
+								.b.adp_sns_int = 1, b.adp_tmout_int};
+#endif
+	dwc_otg_disable_global_interrupts(core_if);
+	DWC_PRINTF("ADP Probe Start\n");
+	core_if->adp.probe_enabled = 1;
+
+	adpctl.b.adpres = 1;
+	dwc_otg_adp_write_reg(core_if, adpctl.d32);
+
+	while (adpctl.b.adpres) {
+		adpctl.d32 = dwc_otg_adp_read_reg(core_if);
+	}
+
+	adpctl.d32 = 0;
+	gpwrdn.d32 = DWC_READ_REG32(&core_if->core_global_regs->gpwrdn);
+
+	/* In Host mode unmask SRP detected interrupt */
+	gpwrdn.d32 = 0;
+	gpwrdn.b.sts_chngint_msk = 1;
+	if (!gpwrdn.b.idsts) {
+		gpwrdn.b.srp_det_msk = 1;
+	}
+	DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, 0, gpwrdn.d32);
+
+	adpctl.b.adp_tmout_int_msk = 1;
+	adpctl.b.adp_prb_int_msk = 1;
+	adpctl.b.prb_dschg = 1;
+	adpctl.b.prb_delta = 1;
+	adpctl.b.prb_per = 1;
+	adpctl.b.adpen = 1;
+	adpctl.b.enaprb = 1;
+
+	dwc_otg_adp_write_reg(core_if, adpctl.d32);
+	DWC_PRINTF("ADP Probe Finish\n");
+	return 0;
+}
+
+/**
+ * Starts the ADP Sense timer to detect if ADP Sense interrupt is not asserted
+ * within 3 seconds.
+ *
+ * @param core_if the pointer to core_if strucure.
+ */
+void dwc_otg_adp_sense_timer_start(dwc_otg_core_if_t * core_if)
+{
+	core_if->adp.sense_timer_started = 1;
+	DWC_TIMER_SCHEDULE(core_if->adp.sense_timer, 3000 /* 3 secs */ );
+}
+
+/**
+ * Starts the ADP Sense
+ *
+ * @param core_if the pointer to core_if strucure.
+ */
+uint32_t dwc_otg_adp_sense_start(dwc_otg_core_if_t * core_if)
+{
+	adpctl_data_t adpctl;
+
+	DWC_PRINTF("ADP Sense Start\n");
+
+	/* Unmask ADP sense interrupt and mask all other from the core */
+	adpctl.d32 = dwc_otg_adp_read_reg_filter(core_if);
+	adpctl.b.adp_sns_int_msk = 1;
+	dwc_otg_adp_write_reg(core_if, adpctl.d32);
+	dwc_otg_disable_global_interrupts(core_if); // vahrama
+
+	/* Set ADP reset bit*/
+	adpctl.d32 = dwc_otg_adp_read_reg_filter(core_if);
+	adpctl.b.adpres = 1;
+	dwc_otg_adp_write_reg(core_if, adpctl.d32);
+
+	while (adpctl.b.adpres) {
+		adpctl.d32 = dwc_otg_adp_read_reg(core_if);
+	}
+
+	adpctl.b.adpres = 0;
+	adpctl.b.adpen = 1;
+	adpctl.b.enasns = 1;
+	dwc_otg_adp_write_reg(core_if, adpctl.d32);
+
+	dwc_otg_adp_sense_timer_start(core_if);
+
+	return 0;
+}
+
+/**
+ * Stops the ADP Probing
+ *
+ * @param core_if the pointer to core_if strucure.
+ */
+uint32_t dwc_otg_adp_probe_stop(dwc_otg_core_if_t * core_if)
+{
+
+	adpctl_data_t adpctl;
+	DWC_PRINTF("Stop ADP probe\n");
+	core_if->adp.probe_enabled = 0;
+	core_if->adp.probe_counter = 0;
+	adpctl.d32 = dwc_otg_adp_read_reg(core_if);
+
+	adpctl.b.adpen = 0;
+	adpctl.b.adp_prb_int = 1;
+	adpctl.b.adp_tmout_int = 1;
+	adpctl.b.adp_sns_int = 1;
+	dwc_otg_adp_write_reg(core_if, adpctl.d32);
+
+	return 0;
+}
+
+/**
+ * Stops the ADP Sensing
+ *
+ * @param core_if the pointer to core_if strucure.
+ */
+uint32_t dwc_otg_adp_sense_stop(dwc_otg_core_if_t * core_if)
+{
+	adpctl_data_t adpctl;
+
+	core_if->adp.sense_enabled = 0;
+
+	adpctl.d32 = dwc_otg_adp_read_reg_filter(core_if);
+	adpctl.b.enasns = 0;
+	adpctl.b.adp_sns_int = 1;
+	dwc_otg_adp_write_reg(core_if, adpctl.d32);
+
+	return 0;
+}
+
+/**
+ * Called to turn on the VBUS after initial ADP probe in host mode.
+ * If port power was already enabled in cil_hcd_start function then
+ * only schedule a timer.
+ *
+ * @param core_if the pointer to core_if structure.
+ */
+void dwc_otg_adp_turnon_vbus(dwc_otg_core_if_t * core_if)
+{
+	hprt0_data_t hprt0 = {.d32 = 0 };
+	hprt0.d32 = dwc_otg_read_hprt0(core_if);
+	DWC_PRINTF("Turn on VBUS for 1.1s, port power is %d\n", hprt0.b.prtpwr);
+
+	if (hprt0.b.prtpwr == 0) {
+		hprt0.b.prtpwr = 1;
+		//DWC_WRITE_REG32(core_if->host_if->hprt0, hprt0.d32);
+	}
+
+	dwc_otg_adp_vbuson_timer_start(core_if);
+}
+
+/**
+ * Called right after driver is loaded
+ * to perform initial actions for ADP
+ *
+ * @param core_if the pointer to core_if structure.
+ * @param is_host - flag for current mode of operation either from GINTSTS or GPWRDN
+ */
+void dwc_otg_adp_start(dwc_otg_core_if_t * core_if, uint8_t is_host)
+{
+	gpwrdn_data_t gpwrdn;
+
+	DWC_PRINTF("ADP Initial Start\n");
+	core_if->adp.adp_started = 1;
+
+	DWC_WRITE_REG32(&core_if->core_global_regs->gintsts, 0xFFFFFFFF);
+	dwc_otg_disable_global_interrupts(core_if);
+	if (is_host) {
+		DWC_PRINTF("HOST MODE\n");
+		/* Enable Power Down Logic Interrupt*/
+		gpwrdn.d32 = 0;
+		gpwrdn.b.pmuintsel = 1;
+		gpwrdn.b.pmuactv = 1;
+		DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, 0, gpwrdn.d32);
+		/* Initialize first ADP probe to obtain Ramp Time value */
+		core_if->adp.initial_probe = 1;
+		dwc_otg_adp_probe_start(core_if);
+	} else {
+		gotgctl_data_t gotgctl;
+		gotgctl.d32 = DWC_READ_REG32(&core_if->core_global_regs->gotgctl);
+		DWC_PRINTF("DEVICE MODE\n");
+		if (gotgctl.b.bsesvld == 0) {
+			/* Enable Power Down Logic Interrupt*/
+			gpwrdn.d32 = 0;
+			DWC_PRINTF("VBUS is not valid - start ADP probe\n");
+			gpwrdn.b.pmuintsel = 1;
+			gpwrdn.b.pmuactv = 1;
+			DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, 0, gpwrdn.d32);
+			core_if->adp.initial_probe = 1;
+			dwc_otg_adp_probe_start(core_if);
+		} else {
+			DWC_PRINTF("VBUS is valid - initialize core as a Device\n");
+			core_if->op_state = B_PERIPHERAL;
+			dwc_otg_core_init(core_if);
+			dwc_otg_enable_global_interrupts(core_if);
+			cil_pcd_start(core_if);
+			dwc_otg_dump_global_registers(core_if);
+			dwc_otg_dump_dev_registers(core_if);
+		}
+	}
+}
+
+void dwc_otg_adp_init(dwc_otg_core_if_t * core_if)
+{
+	core_if->adp.adp_started = 0;
+	core_if->adp.initial_probe = 0;
+	core_if->adp.probe_timer_values[0] = -1;
+	core_if->adp.probe_timer_values[1] = -1;
+	core_if->adp.probe_enabled = 0;
+	core_if->adp.sense_enabled = 0;
+	core_if->adp.sense_timer_started = 0;
+	core_if->adp.vbuson_timer_started = 0;
+	core_if->adp.probe_counter = 0;
+	core_if->adp.gpwrdn = 0;
+	core_if->adp.attached = DWC_OTG_ADP_UNKOWN;
+	/* Initialize timers */
+	core_if->adp.sense_timer =
+	    DWC_TIMER_ALLOC("ADP SENSE TIMER", adp_sense_timeout, core_if);
+	core_if->adp.vbuson_timer =
+	    DWC_TIMER_ALLOC("ADP VBUS ON TIMER", adp_vbuson_timeout, core_if);
+	if (!core_if->adp.sense_timer || !core_if->adp.vbuson_timer)
+	{
+		DWC_ERROR("Could not allocate memory for ADP timers\n");
+	}
+}
+
+void dwc_otg_adp_remove(dwc_otg_core_if_t * core_if)
+{
+	gpwrdn_data_t gpwrdn = { .d32 = 0 };
+	gpwrdn.b.pmuintsel = 1;
+	gpwrdn.b.pmuactv = 1;
+	DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
+
+	if (core_if->adp.probe_enabled)
+		dwc_otg_adp_probe_stop(core_if);
+	if (core_if->adp.sense_enabled)
+		dwc_otg_adp_sense_stop(core_if);
+	if (core_if->adp.sense_timer_started)
+		DWC_TIMER_CANCEL(core_if->adp.sense_timer);
+	if (core_if->adp.vbuson_timer_started)
+		DWC_TIMER_CANCEL(core_if->adp.vbuson_timer);
+	DWC_TIMER_FREE(core_if->adp.sense_timer);
+	DWC_TIMER_FREE(core_if->adp.vbuson_timer);
+}
+
+/////////////////////////////////////////////////////////////////////
+////////////// ADP Interrupt Handlers ///////////////////////////////
+/////////////////////////////////////////////////////////////////////
+/**
+ * This function sets Ramp Timer values
+ */
+static uint32_t set_timer_value(dwc_otg_core_if_t * core_if, uint32_t val)
+{
+	if (core_if->adp.probe_timer_values[0] == -1) {
+		core_if->adp.probe_timer_values[0] = val;
+		core_if->adp.probe_timer_values[1] = -1;
+		return 1;
+	} else {
+		core_if->adp.probe_timer_values[1] =
+		    core_if->adp.probe_timer_values[0];
+		core_if->adp.probe_timer_values[0] = val;
+		return 0;
+	}
+}
+
+/**
+ * This function compares Ramp Timer values
+ */
+static uint32_t compare_timer_values(dwc_otg_core_if_t * core_if)
+{
+	uint32_t diff;
+	if (core_if->adp.probe_timer_values[0]>=core_if->adp.probe_timer_values[1])
+			diff = core_if->adp.probe_timer_values[0]-core_if->adp.probe_timer_values[1];
+	else
+			diff = core_if->adp.probe_timer_values[1]-core_if->adp.probe_timer_values[0];
+	if(diff < 2) {
+		return 0;
+	} else {
+		return 1;
+	}
+}
+
+/**
+ * This function handles ADP Probe Interrupts
+ */
+static int32_t dwc_otg_adp_handle_prb_intr(dwc_otg_core_if_t * core_if,
+						 uint32_t val)
+{
+	adpctl_data_t adpctl = {.d32 = 0 };
+	gpwrdn_data_t gpwrdn, temp;
+	adpctl.d32 = val;
+
+	temp.d32 = DWC_READ_REG32(&core_if->core_global_regs->gpwrdn);
+	core_if->adp.probe_counter++;
+	core_if->adp.gpwrdn = DWC_READ_REG32(&core_if->core_global_regs->gpwrdn);
+	if (adpctl.b.rtim == 0 && !temp.b.idsts){
+		DWC_PRINTF("RTIM value is 0\n");
+		goto exit;
+	}
+	if (set_timer_value(core_if, adpctl.b.rtim) &&
+	    core_if->adp.initial_probe) {
+		core_if->adp.initial_probe = 0;
+		dwc_otg_adp_probe_stop(core_if);
+		gpwrdn.d32 = 0;
+		gpwrdn.b.pmuactv = 1;
+		gpwrdn.b.pmuintsel = 1;
+		DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
+		DWC_WRITE_REG32(&core_if->core_global_regs->gintsts, 0xFFFFFFFF);
+
+		/* check which value is for device mode and which for Host mode */
+		if (!temp.b.idsts) {	/* considered host mode value is 0 */
+			/*
+			 * Turn on VBUS after initial ADP probe.
+			 */
+			core_if->op_state = A_HOST;
+			dwc_otg_enable_global_interrupts(core_if);
+			DWC_SPINUNLOCK(core_if->lock);
+			cil_hcd_start(core_if);
+			dwc_otg_adp_turnon_vbus(core_if);
+			DWC_SPINLOCK(core_if->lock);
+		} else {
+			/*
+			 * Initiate SRP after initial ADP probe.
+			 */
+			dwc_otg_enable_global_interrupts(core_if);
+			dwc_otg_initiate_srp(core_if);
+		}
+	} else if (core_if->adp.probe_counter > 2){
+		gpwrdn.d32 = DWC_READ_REG32(&core_if->core_global_regs->gpwrdn);
+		if (compare_timer_values(core_if)) {
+			DWC_PRINTF("Difference in timer values !!! \n");
+//                      core_if->adp.attached = DWC_OTG_ADP_ATTACHED;
+			dwc_otg_adp_probe_stop(core_if);
+
+			/* Power on the core */
+			if (core_if->power_down == 2) {
+				gpwrdn.b.pwrdnswtch = 1;
+				DWC_MODIFY_REG32(&core_if->core_global_regs->
+						 gpwrdn, 0, gpwrdn.d32);
+			}
+
+			/* check which value is for device mode and which for Host mode */
+			if (!temp.b.idsts) {	/* considered host mode value is 0 */
+				/* Disable Interrupt from Power Down Logic */
+				gpwrdn.d32 = 0;
+				gpwrdn.b.pmuintsel = 1;
+				gpwrdn.b.pmuactv = 1;
+				DWC_MODIFY_REG32(&core_if->core_global_regs->
+						 gpwrdn, gpwrdn.d32, 0);
+
+				/*
+				 * Initialize the Core for Host mode.
+				 */
+				core_if->op_state = A_HOST;
+				dwc_otg_core_init(core_if);
+				dwc_otg_enable_global_interrupts(core_if);
+				cil_hcd_start(core_if);
+			} else {
+				gotgctl_data_t gotgctl;
+				/* Mask SRP detected interrupt from Power Down Logic */
+				gpwrdn.d32 = 0;
+				gpwrdn.b.srp_det_msk = 1;
+				DWC_MODIFY_REG32(&core_if->core_global_regs->
+						 gpwrdn, gpwrdn.d32, 0);
+
+				/* Disable Power Down Logic */
+				gpwrdn.d32 = 0;
+				gpwrdn.b.pmuintsel = 1;
+				gpwrdn.b.pmuactv = 1;
+				DWC_MODIFY_REG32(&core_if->core_global_regs->
+						 gpwrdn, gpwrdn.d32, 0);
+
+				/*
+				 * Initialize the Core for Device mode.
+				 */
+				core_if->op_state = B_PERIPHERAL;
+				dwc_otg_core_init(core_if);
+				dwc_otg_enable_global_interrupts(core_if);
+				cil_pcd_start(core_if);
+
+				gotgctl.d32 = DWC_READ_REG32(&core_if->core_global_regs->gotgctl);
+				if (!gotgctl.b.bsesvld) {
+					dwc_otg_initiate_srp(core_if);
+				}
+			}
+		}
+		if (core_if->power_down == 2) {
+			if (gpwrdn.b.bsessvld) {
+				/* Mask SRP detected interrupt from Power Down Logic */
+				gpwrdn.d32 = 0;
+				gpwrdn.b.srp_det_msk = 1;
+				DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
+
+				/* Disable Power Down Logic */
+				gpwrdn.d32 = 0;
+				gpwrdn.b.pmuactv = 1;
+				DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
+
+				/*
+				 * Initialize the Core for Device mode.
+				 */
+				core_if->op_state = B_PERIPHERAL;
+				dwc_otg_core_init(core_if);
+				dwc_otg_enable_global_interrupts(core_if);
+				cil_pcd_start(core_if);
+			}
+		}
+	}
+exit:
+	/* Clear interrupt */
+	adpctl.d32 = dwc_otg_adp_read_reg(core_if);
+	adpctl.b.adp_prb_int = 1;
+	dwc_otg_adp_write_reg(core_if, adpctl.d32);
+
+	return 0;
+}
+
+/**
+ * This function hadles ADP Sense Interrupt
+ */
+static int32_t dwc_otg_adp_handle_sns_intr(dwc_otg_core_if_t * core_if)
+{
+	adpctl_data_t adpctl;
+	/* Stop ADP Sense timer */
+	DWC_TIMER_CANCEL(core_if->adp.sense_timer);
+
+	/* Restart ADP Sense timer */
+	dwc_otg_adp_sense_timer_start(core_if);
+
+	/* Clear interrupt */
+	adpctl.d32 = dwc_otg_adp_read_reg(core_if);
+	adpctl.b.adp_sns_int = 1;
+	dwc_otg_adp_write_reg(core_if, adpctl.d32);
+
+	return 0;
+}
+
+/**
+ * This function handles ADP Probe Interrupts
+ */
+static int32_t dwc_otg_adp_handle_prb_tmout_intr(dwc_otg_core_if_t * core_if,
+						 uint32_t val)
+{
+	adpctl_data_t adpctl = {.d32 = 0 };
+	adpctl.d32 = val;
+	set_timer_value(core_if, adpctl.b.rtim);
+
+	/* Clear interrupt */
+	adpctl.d32 = dwc_otg_adp_read_reg(core_if);
+	adpctl.b.adp_tmout_int = 1;
+	dwc_otg_adp_write_reg(core_if, adpctl.d32);
+
+	return 0;
+}
+
+/**
+ * ADP Interrupt handler.
+ *
+ */
+int32_t dwc_otg_adp_handle_intr(dwc_otg_core_if_t * core_if)
+{
+	int retval = 0;
+	adpctl_data_t adpctl = {.d32 = 0};
+
+	adpctl.d32 = dwc_otg_adp_read_reg(core_if);
+	DWC_PRINTF("ADPCTL = %08x\n",adpctl.d32);
+
+	if (adpctl.b.adp_sns_int & adpctl.b.adp_sns_int_msk) {
+		DWC_PRINTF("ADP Sense interrupt\n");
+		retval |= dwc_otg_adp_handle_sns_intr(core_if);
+	}
+	if (adpctl.b.adp_tmout_int & adpctl.b.adp_tmout_int_msk) {
+		DWC_PRINTF("ADP timeout interrupt\n");
+		retval |= dwc_otg_adp_handle_prb_tmout_intr(core_if, adpctl.d32);
+	}
+	if (adpctl.b.adp_prb_int & adpctl.b.adp_prb_int_msk) {
+		DWC_PRINTF("ADP Probe interrupt\n");
+		adpctl.b.adp_prb_int = 1;
+		retval |= dwc_otg_adp_handle_prb_intr(core_if, adpctl.d32);
+	}
+
+//	dwc_otg_adp_modify_reg(core_if, adpctl.d32, 0);
+	//dwc_otg_adp_write_reg(core_if, adpctl.d32);
+	DWC_PRINTF("RETURN FROM ADP ISR\n");
+
+	return retval;
+}
+
+/**
+ *
+ * @param core_if Programming view of DWC_otg controller.
+ */
+int32_t dwc_otg_adp_handle_srp_intr(dwc_otg_core_if_t * core_if)
+{
+
+#ifndef DWC_HOST_ONLY
+	hprt0_data_t hprt0;
+	gpwrdn_data_t gpwrdn;
+	DWC_DEBUGPL(DBG_ANY, "++ Power Down Logic Session Request Interrupt++\n");
+
+	gpwrdn.d32 = DWC_READ_REG32(&core_if->core_global_regs->gpwrdn);
+	/* check which value is for device mode and which for Host mode */
+	if (!gpwrdn.b.idsts) {	/* considered host mode value is 0 */
+		DWC_PRINTF("SRP: Host mode\n");
+
+		if (core_if->adp_enable) {
+			dwc_otg_adp_probe_stop(core_if);
+
+			/* Power on the core */
+			if (core_if->power_down == 2) {
+				gpwrdn.b.pwrdnswtch = 1;
+				DWC_MODIFY_REG32(&core_if->core_global_regs->
+						 gpwrdn, 0, gpwrdn.d32);
+			}
+
+			core_if->op_state = A_HOST;
+			dwc_otg_core_init(core_if);
+			dwc_otg_enable_global_interrupts(core_if);
+			cil_hcd_start(core_if);
+		}
+
+		/* Turn on the port power bit. */
+		hprt0.d32 = dwc_otg_read_hprt0(core_if);
+		hprt0.b.prtpwr = 1;
+		DWC_WRITE_REG32(core_if->host_if->hprt0, hprt0.d32);
+
+		/* Start the Connection timer. So a message can be displayed
+		 * if connect does not occur within 10 seconds. */
+		cil_hcd_session_start(core_if);
+	} else {
+		DWC_PRINTF("SRP: Device mode %s\n", __FUNCTION__);
+		if (core_if->adp_enable) {
+			dwc_otg_adp_probe_stop(core_if);
+
+			/* Power on the core */
+			if (core_if->power_down == 2) {
+				gpwrdn.b.pwrdnswtch = 1;
+				DWC_MODIFY_REG32(&core_if->core_global_regs->
+						 gpwrdn, 0, gpwrdn.d32);
+			}
+
+			gpwrdn.d32 = 0;
+			gpwrdn.b.pmuactv = 0;
+			DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, 0,
+					 gpwrdn.d32);
+
+			core_if->op_state = B_PERIPHERAL;
+			dwc_otg_core_init(core_if);
+			dwc_otg_enable_global_interrupts(core_if);
+			cil_pcd_start(core_if);
+		}
+	}
+#endif
+	return 1;
+}
diff -Nur --no-dereference linux-5.10.52-orig/drivers/usb/host/dwc_otg/dwc_otg_adp.h linux-5.10.52-v7l+/drivers/usb/host/dwc_otg/dwc_otg_adp.h
--- linux-5.10.52-orig/drivers/usb/host/dwc_otg/dwc_otg_adp.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/usb/host/dwc_otg/dwc_otg_adp.h	2021-07-25 16:46:28.657910448 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/* ==========================================================================
+ * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_adp.h $
+ * $Revision: #7 $
+ * $Date: 2011/10/24 $
+ * $Change: 1871159 $
+ *
+ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter,
+ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless
+ * otherwise expressly agreed to in writing between Synopsys and you.
+ *
+ * The Software IS NOT an item of Licensed Software or Licensed Product under
+ * any End User Software License Agreement or Agreement for Licensed Product
+ * with Synopsys or any supplement thereto. You are permitted to use and
+ * redistribute this Software in source and binary forms, with or without
+ * modification, provided that redistributions of source code must retain this
+ * notice. You may not view, use, disclose, copy or distribute this file or
+ * any information contained herein except pursuant to this license grant from
+ * Synopsys. If you do not agree with this notice, including the disclaimer
+ * below, then you are not authorized to use the Software.
+ *
+ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * ========================================================================== */
+
+#ifndef __DWC_OTG_ADP_H__
+#define __DWC_OTG_ADP_H__
+
+/**
+ * @file
+ *
+ * This file contains the Attach Detect Protocol interfaces and defines
+ * (functions) and structures for Linux.
+ *
+ */
+
+#define DWC_OTG_ADP_UNATTACHED	0
+#define DWC_OTG_ADP_ATTACHED	1
+#define DWC_OTG_ADP_UNKOWN	2
+
+typedef struct dwc_otg_adp {
+	uint32_t adp_started;
+	uint32_t initial_probe;
+	int32_t probe_timer_values[2];
+	uint32_t probe_enabled;
+	uint32_t sense_enabled;
+	dwc_timer_t *sense_timer;
+	uint32_t sense_timer_started;
+	dwc_timer_t *vbuson_timer;
+	uint32_t vbuson_timer_started;
+	uint32_t attached;
+	uint32_t probe_counter;
+	uint32_t gpwrdn;
+} dwc_otg_adp_t;
+
+/**
+ * Attach Detect Protocol functions
+ */
+
+extern void dwc_otg_adp_write_reg(dwc_otg_core_if_t * core_if, uint32_t value);
+extern uint32_t dwc_otg_adp_read_reg(dwc_otg_core_if_t * core_if);
+extern uint32_t dwc_otg_adp_probe_start(dwc_otg_core_if_t * core_if);
+extern uint32_t dwc_otg_adp_sense_start(dwc_otg_core_if_t * core_if);
+extern uint32_t dwc_otg_adp_probe_stop(dwc_otg_core_if_t * core_if);
+extern uint32_t dwc_otg_adp_sense_stop(dwc_otg_core_if_t * core_if);
+extern void dwc_otg_adp_start(dwc_otg_core_if_t * core_if, uint8_t is_host);
+extern void dwc_otg_adp_init(dwc_otg_core_if_t * core_if);
+extern void dwc_otg_adp_remove(dwc_otg_core_if_t * core_if);
+extern int32_t dwc_otg_adp_handle_intr(dwc_otg_core_if_t * core_if);
+extern int32_t dwc_otg_adp_handle_srp_intr(dwc_otg_core_if_t * core_if);
+
+#endif //__DWC_OTG_ADP_H__
diff -Nur --no-dereference linux-5.10.52-orig/drivers/usb/host/dwc_otg/dwc_otg_attr.c linux-5.10.52-v7l+/drivers/usb/host/dwc_otg/dwc_otg_attr.c
--- linux-5.10.52-orig/drivers/usb/host/dwc_otg/dwc_otg_attr.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/usb/host/dwc_otg/dwc_otg_attr.c	2021-07-25 16:46:28.657910448 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/* ==========================================================================
+ * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_attr.c $
+ * $Revision: #44 $
+ * $Date: 2010/11/29 $
+ * $Change: 1636033 $
+ *
+ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter,
+ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless
+ * otherwise expressly agreed to in writing between Synopsys and you.
+ *
+ * The Software IS NOT an item of Licensed Software or Licensed Product under
+ * any End User Software License Agreement or Agreement for Licensed Product
+ * with Synopsys or any supplement thereto. You are permitted to use and
+ * redistribute this Software in source and binary forms, with or without
+ * modification, provided that redistributions of source code must retain this
+ * notice. You may not view, use, disclose, copy or distribute this file or
+ * any information contained herein except pursuant to this license grant from
+ * Synopsys. If you do not agree with this notice, including the disclaimer
+ * below, then you are not authorized to use the Software.
+ *
+ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * ========================================================================== */
+
+/** @file
+ *
+ * The diagnostic interface will provide access to the controller for
+ * bringing up the hardware and testing.  The Linux driver attributes
+ * feature will be used to provide the Linux Diagnostic
+ * Interface. These attributes are accessed through sysfs.
+ */
+
+/** @page "Linux Module Attributes"
+ *
+ * The Linux module attributes feature is used to provide the Linux
+ * Diagnostic Interface.  These attributes are accessed through sysfs.
+ * The diagnostic interface will provide access to the controller for
+ * bringing up the hardware and testing.
+
+ The following table shows the attributes.
+ <table>
+ <tr>
+ <td><b> Name</b></td>
+ <td><b> Description</b></td>
+ <td><b> Access</b></td>
+ </tr>
+
+ <tr>
+ <td> mode </td>
+ <td> Returns the current mode: 0 for device mode, 1 for host mode</td>
+ <td> Read</td>
+ </tr>
+
+ <tr>
+ <td> hnpcapable </td>
+ <td> Gets or sets the "HNP-capable" bit in the Core USB Configuraton Register.
+ Read returns the current value.</td>
+ <td> Read/Write</td>
+ </tr>
+
+ <tr>
+ <td> srpcapable </td>
+ <td> Gets or sets the "SRP-capable" bit in the Core USB Configuraton Register.
+ Read returns the current value.</td>
+ <td> Read/Write</td>
+ </tr>
+
+ <tr>
+ <td> hsic_connect </td>
+ <td> Gets or sets the "HSIC-Connect" bit in the GLPMCFG Register.
+ Read returns the current value.</td>
+ <td> Read/Write</td>
+ </tr>
+
+ <tr>
+ <td> inv_sel_hsic </td>
+ <td> Gets or sets the "Invert Select HSIC" bit in the GLPMFG Register.
+ Read returns the current value.</td>
+ <td> Read/Write</td>
+ </tr>
+
+ <tr>
+ <td> hnp </td>
+ <td> Initiates the Host Negotiation Protocol.  Read returns the status.</td>
+ <td> Read/Write</td>
+ </tr>
+
+ <tr>
+ <td> srp </td>
+ <td> Initiates the Session Request Protocol.  Read returns the status.</td>
+ <td> Read/Write</td>
+ </tr>
+
+ <tr>
+ <td> buspower </td>
+ <td> Gets or sets the Power State of the bus (0 - Off or 1 - On)</td>
+ <td> Read/Write</td>
+ </tr>
+
+ <tr>
+ <td> bussuspend </td>
+ <td> Suspends the USB bus.</td>
+ <td> Read/Write</td>
+ </tr>
+
+ <tr>
+ <td> busconnected </td>
+ <td> Gets the connection status of the bus</td>
+ <td> Read</td>
+ </tr>
+
+ <tr>
+ <td> gotgctl </td>
+ <td> Gets or sets the Core Control Status Register.</td>
+ <td> Read/Write</td>
+ </tr>
+
+ <tr>
+ <td> gusbcfg </td>
+ <td> Gets or sets the Core USB Configuration Register</td>
+ <td> Read/Write</td>
+ </tr>
+
+ <tr>
+ <td> grxfsiz </td>
+ <td> Gets or sets the Receive FIFO Size Register</td>
+ <td> Read/Write</td>
+ </tr>
+
+ <tr>
+ <td> gnptxfsiz </td>
+ <td> Gets or sets the non-periodic Transmit Size Register</td>
+ <td> Read/Write</td>
+ </tr>
+
+ <tr>
+ <td> gpvndctl </td>
+ <td> Gets or sets the PHY Vendor Control Register</td>
+ <td> Read/Write</td>
+ </tr>
+
+ <tr>
+ <td> ggpio </td>
+ <td> Gets the value in the lower 16-bits of the General Purpose IO Register
+ or sets the upper 16 bits.</td>
+ <td> Read/Write</td>
+ </tr>
+
+ <tr>
+ <td> guid </td>
+ <td> Gets or sets the value of the User ID Register</td>
+ <td> Read/Write</td>
+ </tr>
+
+ <tr>
+ <td> gsnpsid </td>
+ <td> Gets the value of the Synopsys ID Regester</td>
+ <td> Read</td>
+ </tr>
+
+ <tr>
+ <td> devspeed </td>
+ <td> Gets or sets the device speed setting in the DCFG register</td>
+ <td> Read/Write</td>
+ </tr>
+
+ <tr>
+ <td> enumspeed </td>
+ <td> Gets the device enumeration Speed.</td>
+ <td> Read</td>
+ </tr>
+
+ <tr>
+ <td> hptxfsiz </td>
+ <td> Gets the value of the Host Periodic Transmit FIFO</td>
+ <td> Read</td>
+ </tr>
+
+ <tr>
+ <td> hprt0 </td>
+ <td> Gets or sets the value in the Host Port Control and Status Register</td>
+ <td> Read/Write</td>
+ </tr>
+
+ <tr>
+ <td> regoffset </td>
+ <td> Sets the register offset for the next Register Access</td>
+ <td> Read/Write</td>
+ </tr>
+
+ <tr>
+ <td> regvalue </td>
+ <td> Gets or sets the value of the register at the offset in the regoffset attribute.</td>
+ <td> Read/Write</td>
+ </tr>
+
+ <tr>
+ <td> remote_wakeup </td>
+ <td> On read, shows the status of Remote Wakeup. On write, initiates a remote
+ wakeup of the host. When bit 0 is 1 and Remote Wakeup is enabled, the Remote
+ Wakeup signalling bit in the Device Control Register is set for 1
+ milli-second.</td>
+ <td> Read/Write</td>
+ </tr>
+
+ <tr>
+ <td> rem_wakeup_pwrdn </td>
+ <td> On read, shows the status core - hibernated or not. On write, initiates
+ a remote wakeup of the device from Hibernation. </td>
+ <td> Read/Write</td>
+ </tr>
+
+ <tr>
+ <td> mode_ch_tim_en </td>
+ <td> This bit is used to enable or disable the host core to wait for 200 PHY
+ clock cycles at the end of Resume to change the opmode signal to the PHY to 00
+ after Suspend or LPM. </td>
+ <td> Read/Write</td>
+ </tr>
+
+ <tr>
+ <td> fr_interval </td>
+ <td> On read, shows the value of HFIR Frame Interval. On write, dynamically
+ reload HFIR register during runtime. The application can write a value to this
+ register only after the Port Enable bit of the Host Port Control and Status
+ register (HPRT.PrtEnaPort) has been set </td>
+ <td> Read/Write</td>
+ </tr>
+
+ <tr>
+ <td> disconnect_us </td>
+ <td> On read, shows the status of disconnect_device_us. On write, sets disconnect_us
+ which causes soft disconnect for 100us. Applicable only for device mode of operation.</td>
+ <td> Read/Write</td>
+ </tr>
+
+ <tr>
+ <td> regdump </td>
+ <td> Dumps the contents of core registers.</td>
+ <td> Read</td>
+ </tr>
+
+ <tr>
+ <td> spramdump </td>
+ <td> Dumps the contents of core registers.</td>
+ <td> Read</td>
+ </tr>
+
+ <tr>
+ <td> hcddump </td>
+ <td> Dumps the current HCD state.</td>
+ <td> Read</td>
+ </tr>
+
+ <tr>
+ <td> hcd_frrem </td>
+ <td> Shows the average value of the Frame Remaining
+ field in the Host Frame Number/Frame Remaining register when an SOF interrupt
+ occurs. This can be used to determine the average interrupt latency. Also
+ shows the average Frame Remaining value for start_transfer and the "a" and
+ "b" sample points. The "a" and "b" sample points may be used during debugging
+ bto determine how long it takes to execute a section of the HCD code.</td>
+ <td> Read</td>
+ </tr>
+
+ <tr>
+ <td> rd_reg_test </td>
+ <td> Displays the time required to read the GNPTXFSIZ register many times
+ (the output shows the number of times the register is read).
+ <td> Read</td>
+ </tr>
+
+ <tr>
+ <td> wr_reg_test </td>
+ <td> Displays the time required to write the GNPTXFSIZ register many times
+ (the output shows the number of times the register is written).
+ <td> Read</td>
+ </tr>
+
+ <tr>
+ <td> lpm_response </td>
+ <td> Gets or sets lpm_response mode. Applicable only in device mode.
+ <td> Write</td>
+ </tr>
+
+ <tr>
+ <td> sleep_status </td>
+ <td> Shows sleep status of device.
+ <td> Read</td>
+ </tr>
+
+ </table>
+
+ Example usage:
+ To get the current mode:
+ cat /sys/devices/lm0/mode
+
+ To power down the USB:
+ echo 0 > /sys/devices/lm0/buspower
+ */
+
+#include "dwc_otg_os_dep.h"
+#include "dwc_os.h"
+#include "dwc_otg_driver.h"
+#include "dwc_otg_attr.h"
+#include "dwc_otg_core_if.h"
+#include "dwc_otg_pcd_if.h"
+#include "dwc_otg_hcd_if.h"
+
+/*
+ * MACROs for defining sysfs attribute
+ */
+#ifdef LM_INTERFACE
+
+#define DWC_OTG_DEVICE_ATTR_BITFIELD_SHOW(_otg_attr_name_,_string_) \
+static ssize_t _otg_attr_name_##_show (struct device *_dev, struct device_attribute *attr, char *buf) \
+{ \
+	struct lm_device *lm_dev = container_of(_dev, struct lm_device, dev); \
+	dwc_otg_device_t *otg_dev = lm_get_drvdata(lm_dev);		\
+	uint32_t val; \
+	val = dwc_otg_get_##_otg_attr_name_ (otg_dev->core_if); \
+	return sprintf (buf, "%s = 0x%x\n", _string_, val); \
+}
+#define DWC_OTG_DEVICE_ATTR_BITFIELD_STORE(_otg_attr_name_,_string_) \
+static ssize_t _otg_attr_name_##_store (struct device *_dev, struct device_attribute *attr, \
+					const char *buf, size_t count) \
+{ \
+	struct lm_device *lm_dev = container_of(_dev, struct lm_device, dev); \
+	dwc_otg_device_t *otg_dev = lm_get_drvdata(lm_dev); \
+	uint32_t set = simple_strtoul(buf, NULL, 16); \
+	dwc_otg_set_##_otg_attr_name_(otg_dev->core_if, set);\
+	return count; \
+}
+
+#elif defined(PCI_INTERFACE)
+
+#define DWC_OTG_DEVICE_ATTR_BITFIELD_SHOW(_otg_attr_name_,_string_) \
+static ssize_t _otg_attr_name_##_show (struct device *_dev, struct device_attribute *attr, char *buf) \
+{ \
+	dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev);	\
+	uint32_t val; \
+	val = dwc_otg_get_##_otg_attr_name_ (otg_dev->core_if); \
+	return sprintf (buf, "%s = 0x%x\n", _string_, val); \
+}
+#define DWC_OTG_DEVICE_ATTR_BITFIELD_STORE(_otg_attr_name_,_string_) \
+static ssize_t _otg_attr_name_##_store (struct device *_dev, struct device_attribute *attr, \
+					const char *buf, size_t count) \
+{ \
+	dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev);  \
+	uint32_t set = simple_strtoul(buf, NULL, 16); \
+	dwc_otg_set_##_otg_attr_name_(otg_dev->core_if, set);\
+	return count; \
+}
+
+#elif defined(PLATFORM_INTERFACE)
+
+#define DWC_OTG_DEVICE_ATTR_BITFIELD_SHOW(_otg_attr_name_,_string_) \
+static ssize_t _otg_attr_name_##_show (struct device *_dev, struct device_attribute *attr, char *buf) \
+{ \
+        struct platform_device *platform_dev = \
+                container_of(_dev, struct platform_device, dev); \
+        dwc_otg_device_t *otg_dev = platform_get_drvdata(platform_dev);  \
+	uint32_t val; \
+	DWC_PRINTF("%s(%p) -> platform_dev %p, otg_dev %p\n", \
+                    __func__, _dev, platform_dev, otg_dev); \
+	val = dwc_otg_get_##_otg_attr_name_ (otg_dev->core_if); \
+	return sprintf (buf, "%s = 0x%x\n", _string_, val); \
+}
+#define DWC_OTG_DEVICE_ATTR_BITFIELD_STORE(_otg_attr_name_,_string_) \
+static ssize_t _otg_attr_name_##_store (struct device *_dev, struct device_attribute *attr, \
+					const char *buf, size_t count) \
+{ \
+        struct platform_device *platform_dev = container_of(_dev, struct platform_device, dev); \
+        dwc_otg_device_t *otg_dev = platform_get_drvdata(platform_dev); \
+	uint32_t set = simple_strtoul(buf, NULL, 16); \
+	dwc_otg_set_##_otg_attr_name_(otg_dev->core_if, set);\
+	return count; \
+}
+#endif
+
+/*
+ * MACROs for defining sysfs attribute for 32-bit registers
+ */
+#ifdef LM_INTERFACE
+#define DWC_OTG_DEVICE_ATTR_REG_SHOW(_otg_attr_name_,_string_) \
+static ssize_t _otg_attr_name_##_show (struct device *_dev, struct device_attribute *attr, char *buf) \
+{ \
+	struct lm_device *lm_dev = container_of(_dev, struct lm_device, dev); \
+	dwc_otg_device_t *otg_dev = lm_get_drvdata(lm_dev); \
+	uint32_t val; \
+	val = dwc_otg_get_##_otg_attr_name_ (otg_dev->core_if); \
+	return sprintf (buf, "%s = 0x%08x\n", _string_, val); \
+}
+#define DWC_OTG_DEVICE_ATTR_REG_STORE(_otg_attr_name_,_string_) \
+static ssize_t _otg_attr_name_##_store (struct device *_dev, struct device_attribute *attr, \
+					const char *buf, size_t count) \
+{ \
+	struct lm_device *lm_dev = container_of(_dev, struct lm_device, dev); \
+	dwc_otg_device_t *otg_dev = lm_get_drvdata(lm_dev); \
+	uint32_t val = simple_strtoul(buf, NULL, 16); \
+	dwc_otg_set_##_otg_attr_name_ (otg_dev->core_if, val); \
+	return count; \
+}
+#elif defined(PCI_INTERFACE)
+#define DWC_OTG_DEVICE_ATTR_REG_SHOW(_otg_attr_name_,_string_) \
+static ssize_t _otg_attr_name_##_show (struct device *_dev, struct device_attribute *attr, char *buf) \
+{ \
+	dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev);  \
+	uint32_t val; \
+	val = dwc_otg_get_##_otg_attr_name_ (otg_dev->core_if); \
+	return sprintf (buf, "%s = 0x%08x\n", _string_, val); \
+}
+#define DWC_OTG_DEVICE_ATTR_REG_STORE(_otg_attr_name_,_string_) \
+static ssize_t _otg_attr_name_##_store (struct device *_dev, struct device_attribute *attr, \
+					const char *buf, size_t count) \
+{ \
+	dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev);  \
+	uint32_t val = simple_strtoul(buf, NULL, 16); \
+	dwc_otg_set_##_otg_attr_name_ (otg_dev->core_if, val); \
+	return count; \
+}
+
+#elif defined(PLATFORM_INTERFACE)
+#include "dwc_otg_dbg.h"
+#define DWC_OTG_DEVICE_ATTR_REG_SHOW(_otg_attr_name_,_string_) \
+static ssize_t _otg_attr_name_##_show (struct device *_dev, struct device_attribute *attr, char *buf) \
+{ \
+	struct platform_device *platform_dev = container_of(_dev, struct platform_device, dev); \
+	dwc_otg_device_t *otg_dev = platform_get_drvdata(platform_dev); \
+	uint32_t val; \
+	DWC_PRINTF("%s(%p) -> platform_dev %p, otg_dev %p\n", \
+                    __func__, _dev, platform_dev, otg_dev); \
+	val = dwc_otg_get_##_otg_attr_name_ (otg_dev->core_if); \
+	return sprintf (buf, "%s = 0x%08x\n", _string_, val); \
+}
+#define DWC_OTG_DEVICE_ATTR_REG_STORE(_otg_attr_name_,_string_) \
+static ssize_t _otg_attr_name_##_store (struct device *_dev, struct device_attribute *attr, \
+					const char *buf, size_t count) \
+{ \
+	struct platform_device *platform_dev = container_of(_dev, struct platform_device, dev); \
+	dwc_otg_device_t *otg_dev = platform_get_drvdata(platform_dev); \
+	uint32_t val = simple_strtoul(buf, NULL, 16); \
+	dwc_otg_set_##_otg_attr_name_ (otg_dev->core_if, val); \
+	return count; \
+}
+
+#endif
+
+#define DWC_OTG_DEVICE_ATTR_BITFIELD_RW(_otg_attr_name_,_string_) \
+DWC_OTG_DEVICE_ATTR_BITFIELD_SHOW(_otg_attr_name_,_string_) \
+DWC_OTG_DEVICE_ATTR_BITFIELD_STORE(_otg_attr_name_,_string_) \
+DEVICE_ATTR(_otg_attr_name_,0644,_otg_attr_name_##_show,_otg_attr_name_##_store);
+
+#define DWC_OTG_DEVICE_ATTR_BITFIELD_RO(_otg_attr_name_,_string_) \
+DWC_OTG_DEVICE_ATTR_BITFIELD_SHOW(_otg_attr_name_,_string_) \
+DEVICE_ATTR(_otg_attr_name_,0444,_otg_attr_name_##_show,NULL);
+
+#define DWC_OTG_DEVICE_ATTR_REG32_RW(_otg_attr_name_,_addr_,_string_) \
+DWC_OTG_DEVICE_ATTR_REG_SHOW(_otg_attr_name_,_string_) \
+DWC_OTG_DEVICE_ATTR_REG_STORE(_otg_attr_name_,_string_) \
+DEVICE_ATTR(_otg_attr_name_,0644,_otg_attr_name_##_show,_otg_attr_name_##_store);
+
+#define DWC_OTG_DEVICE_ATTR_REG32_RO(_otg_attr_name_,_addr_,_string_) \
+DWC_OTG_DEVICE_ATTR_REG_SHOW(_otg_attr_name_,_string_) \
+DEVICE_ATTR(_otg_attr_name_,0444,_otg_attr_name_##_show,NULL);
+
+/** @name Functions for Show/Store of Attributes */
+/**@{*/
+
+/**
+ * Helper function returning the otg_device structure of the given device
+ */
+static dwc_otg_device_t *dwc_otg_drvdev(struct device *_dev)
+{
+        dwc_otg_device_t *otg_dev;
+        DWC_OTG_GETDRVDEV(otg_dev, _dev);
+        return otg_dev;
+}
+
+/**
+ * Show the register offset of the Register Access.
+ */
+static ssize_t regoffset_show(struct device *_dev,
+			      struct device_attribute *attr, char *buf)
+{
+        dwc_otg_device_t *otg_dev = dwc_otg_drvdev(_dev);
+	return snprintf(buf, sizeof("0xFFFFFFFF\n") + 1, "0x%08x\n",
+			otg_dev->os_dep.reg_offset);
+}
+
+/**
+ * Set the register offset for the next Register Access 	Read/Write
+ */
+static ssize_t regoffset_store(struct device *_dev,
+			       struct device_attribute *attr,
+			       const char *buf, size_t count)
+{
+        dwc_otg_device_t *otg_dev = dwc_otg_drvdev(_dev);
+	uint32_t offset = simple_strtoul(buf, NULL, 16);
+#if defined(LM_INTERFACE) || defined(PLATFORM_INTERFACE)
+	if (offset < SZ_256K) {
+#elif  defined(PCI_INTERFACE)
+	if (offset < 0x00040000) {
+#endif
+		otg_dev->os_dep.reg_offset = offset;
+	} else {
+		dev_err(_dev, "invalid offset\n");
+	}
+
+	return count;
+}
+
+DEVICE_ATTR(regoffset, S_IRUGO | S_IWUSR, regoffset_show, regoffset_store);
+
+/**
+ * Show the value of the register at the offset in the reg_offset
+ * attribute.
+ */
+static ssize_t regvalue_show(struct device *_dev,
+			     struct device_attribute *attr, char *buf)
+{
+        dwc_otg_device_t *otg_dev = dwc_otg_drvdev(_dev);
+	uint32_t val;
+	volatile uint32_t *addr;
+
+	if (otg_dev->os_dep.reg_offset != 0xFFFFFFFF && 0 != otg_dev->os_dep.base) {
+		/* Calculate the address */
+		addr = (uint32_t *) (otg_dev->os_dep.reg_offset +
+				     (uint8_t *) otg_dev->os_dep.base);
+		val = DWC_READ_REG32(addr);
+		return snprintf(buf,
+				sizeof("Reg@0xFFFFFFFF = 0xFFFFFFFF\n") + 1,
+				"Reg@0x%06x = 0x%08x\n", otg_dev->os_dep.reg_offset,
+				val);
+	} else {
+		dev_err(_dev, "Invalid offset (0x%0x)\n", otg_dev->os_dep.reg_offset);
+		return sprintf(buf, "invalid offset\n");
+	}
+}
+
+/**
+ * Store the value in the register at the offset in the reg_offset
+ * attribute.
+ *
+ */
+static ssize_t regvalue_store(struct device *_dev,
+			      struct device_attribute *attr,
+			      const char *buf, size_t count)
+{
+        dwc_otg_device_t *otg_dev = dwc_otg_drvdev(_dev);
+	volatile uint32_t *addr;
+	uint32_t val = simple_strtoul(buf, NULL, 16);
+	//dev_dbg(_dev, "Offset=0x%08x Val=0x%08x\n", otg_dev->reg_offset, val);
+	if (otg_dev->os_dep.reg_offset != 0xFFFFFFFF && 0 != otg_dev->os_dep.base) {
+		/* Calculate the address */
+		addr = (uint32_t *) (otg_dev->os_dep.reg_offset +
+				     (uint8_t *) otg_dev->os_dep.base);
+		DWC_WRITE_REG32(addr, val);
+	} else {
+		dev_err(_dev, "Invalid Register Offset (0x%08x)\n",
+			otg_dev->os_dep.reg_offset);
+	}
+	return count;
+}
+
+DEVICE_ATTR(regvalue, S_IRUGO | S_IWUSR, regvalue_show, regvalue_store);
+
+/*
+ * Attributes
+ */
+DWC_OTG_DEVICE_ATTR_BITFIELD_RO(mode, "Mode");
+DWC_OTG_DEVICE_ATTR_BITFIELD_RW(hnpcapable, "HNPCapable");
+DWC_OTG_DEVICE_ATTR_BITFIELD_RW(srpcapable, "SRPCapable");
+DWC_OTG_DEVICE_ATTR_BITFIELD_RW(hsic_connect, "HSIC Connect");
+DWC_OTG_DEVICE_ATTR_BITFIELD_RW(inv_sel_hsic, "Invert Select HSIC");
+
+//DWC_OTG_DEVICE_ATTR_BITFIELD_RW(buspower,&(otg_dev->core_if->core_global_regs->gotgctl),(1<<8),8,"Mode");
+//DWC_OTG_DEVICE_ATTR_BITFIELD_RW(bussuspend,&(otg_dev->core_if->core_global_regs->gotgctl),(1<<8),8,"Mode");
+DWC_OTG_DEVICE_ATTR_BITFIELD_RO(busconnected, "Bus Connected");
+
+DWC_OTG_DEVICE_ATTR_REG32_RW(gotgctl, 0, "GOTGCTL");
+DWC_OTG_DEVICE_ATTR_REG32_RW(gusbcfg,
+			     &(otg_dev->core_if->core_global_regs->gusbcfg),
+			     "GUSBCFG");
+DWC_OTG_DEVICE_ATTR_REG32_RW(grxfsiz,
+			     &(otg_dev->core_if->core_global_regs->grxfsiz),
+			     "GRXFSIZ");
+DWC_OTG_DEVICE_ATTR_REG32_RW(gnptxfsiz,
+			     &(otg_dev->core_if->core_global_regs->gnptxfsiz),
+			     "GNPTXFSIZ");
+DWC_OTG_DEVICE_ATTR_REG32_RW(gpvndctl,
+			     &(otg_dev->core_if->core_global_regs->gpvndctl),
+			     "GPVNDCTL");
+DWC_OTG_DEVICE_ATTR_REG32_RW(ggpio,
+			     &(otg_dev->core_if->core_global_regs->ggpio),
+			     "GGPIO");
+DWC_OTG_DEVICE_ATTR_REG32_RW(guid, &(otg_dev->core_if->core_global_regs->guid),
+			     "GUID");
+DWC_OTG_DEVICE_ATTR_REG32_RO(gsnpsid,
+			     &(otg_dev->core_if->core_global_regs->gsnpsid),
+			     "GSNPSID");
+DWC_OTG_DEVICE_ATTR_BITFIELD_RW(devspeed, "Device Speed");
+DWC_OTG_DEVICE_ATTR_BITFIELD_RO(enumspeed, "Device Enumeration Speed");
+
+DWC_OTG_DEVICE_ATTR_REG32_RO(hptxfsiz,
+			     &(otg_dev->core_if->core_global_regs->hptxfsiz),
+			     "HPTXFSIZ");
+DWC_OTG_DEVICE_ATTR_REG32_RW(hprt0, otg_dev->core_if->host_if->hprt0, "HPRT0");
+
+/**
+ * @todo Add code to initiate the HNP.
+ */
+/**
+ * Show the HNP status bit
+ */
+static ssize_t hnp_show(struct device *_dev,
+			struct device_attribute *attr, char *buf)
+{
+        dwc_otg_device_t *otg_dev = dwc_otg_drvdev(_dev);
+	return sprintf(buf, "HstNegScs = 0x%x\n",
+		       dwc_otg_get_hnpstatus(otg_dev->core_if));
+}
+
+/**
+ * Set the HNP Request bit
+ */
+static ssize_t hnp_store(struct device *_dev,
+			 struct device_attribute *attr,
+			 const char *buf, size_t count)
+{
+        dwc_otg_device_t *otg_dev = dwc_otg_drvdev(_dev);
+	uint32_t in = simple_strtoul(buf, NULL, 16);
+	dwc_otg_set_hnpreq(otg_dev->core_if, in);
+	return count;
+}
+
+DEVICE_ATTR(hnp, 0644, hnp_show, hnp_store);
+
+/**
+ * @todo Add code to initiate the SRP.
+ */
+/**
+ * Show the SRP status bit
+ */
+static ssize_t srp_show(struct device *_dev,
+			struct device_attribute *attr, char *buf)
+{
+#ifndef DWC_HOST_ONLY
+        dwc_otg_device_t *otg_dev = dwc_otg_drvdev(_dev);
+	return sprintf(buf, "SesReqScs = 0x%x\n",
+		       dwc_otg_get_srpstatus(otg_dev->core_if));
+#else
+	return sprintf(buf, "Host Only Mode!\n");
+#endif
+}
+
+/**
+ * Set the SRP Request bit
+ */
+static ssize_t srp_store(struct device *_dev,
+			 struct device_attribute *attr,
+			 const char *buf, size_t count)
+{
+#ifndef DWC_HOST_ONLY
+        dwc_otg_device_t *otg_dev = dwc_otg_drvdev(_dev);
+	dwc_otg_pcd_initiate_srp(otg_dev->pcd);
+#endif
+	return count;
+}
+
+DEVICE_ATTR(srp, 0644, srp_show, srp_store);
+
+/**
+ * @todo Need to do more for power on/off?
+ */
+/**
+ * Show the Bus Power status
+ */
+static ssize_t buspower_show(struct device *_dev,
+			     struct device_attribute *attr, char *buf)
+{
+        dwc_otg_device_t *otg_dev = dwc_otg_drvdev(_dev);
+	return sprintf(buf, "Bus Power = 0x%x\n",
+		       dwc_otg_get_prtpower(otg_dev->core_if));
+}
+
+/**
+ * Set the Bus Power status
+ */
+static ssize_t buspower_store(struct device *_dev,
+			      struct device_attribute *attr,
+			      const char *buf, size_t count)
+{
+        dwc_otg_device_t *otg_dev = dwc_otg_drvdev(_dev);
+	uint32_t on = simple_strtoul(buf, NULL, 16);
+	dwc_otg_set_prtpower(otg_dev->core_if, on);
+	return count;
+}
+
+DEVICE_ATTR(buspower, 0644, buspower_show, buspower_store);
+
+/**
+ * @todo Need to do more for suspend?
+ */
+/**
+ * Show the Bus Suspend status
+ */
+static ssize_t bussuspend_show(struct device *_dev,
+			       struct device_attribute *attr, char *buf)
+{
+        dwc_otg_device_t *otg_dev = dwc_otg_drvdev(_dev);
+	return sprintf(buf, "Bus Suspend = 0x%x\n",
+		       dwc_otg_get_prtsuspend(otg_dev->core_if));
+}
+
+/**
+ * Set the Bus Suspend status
+ */
+static ssize_t bussuspend_store(struct device *_dev,
+				struct device_attribute *attr,
+				const char *buf, size_t count)
+{
+        dwc_otg_device_t *otg_dev = dwc_otg_drvdev(_dev);
+	uint32_t in = simple_strtoul(buf, NULL, 16);
+	dwc_otg_set_prtsuspend(otg_dev->core_if, in);
+	return count;
+}
+
+DEVICE_ATTR(bussuspend, 0644, bussuspend_show, bussuspend_store);
+
+/**
+ * Show the Mode Change Ready Timer status
+ */
+static ssize_t mode_ch_tim_en_show(struct device *_dev,
+				   struct device_attribute *attr, char *buf)
+{
+        dwc_otg_device_t *otg_dev = dwc_otg_drvdev(_dev);
+	return sprintf(buf, "Mode Change Ready Timer Enable = 0x%x\n",
+		       dwc_otg_get_mode_ch_tim(otg_dev->core_if));
+}
+
+/**
+ * Set the Mode Change Ready Timer status
+ */
+static ssize_t mode_ch_tim_en_store(struct device *_dev,
+				    struct device_attribute *attr,
+				    const char *buf, size_t count)
+{
+        dwc_otg_device_t *otg_dev = dwc_otg_drvdev(_dev);
+	uint32_t in = simple_strtoul(buf, NULL, 16);
+	dwc_otg_set_mode_ch_tim(otg_dev->core_if, in);
+	return count;
+}
+
+DEVICE_ATTR(mode_ch_tim_en, 0644, mode_ch_tim_en_show, mode_ch_tim_en_store);
+
+/**
+ * Show the value of HFIR Frame Interval bitfield
+ */
+static ssize_t fr_interval_show(struct device *_dev,
+				struct device_attribute *attr, char *buf)
+{
+        dwc_otg_device_t *otg_dev = dwc_otg_drvdev(_dev);
+	return sprintf(buf, "Frame Interval = 0x%x\n",
+		       dwc_otg_get_fr_interval(otg_dev->core_if));
+}
+
+/**
+ * Set the HFIR Frame Interval value
+ */
+static ssize_t fr_interval_store(struct device *_dev,
+				 struct device_attribute *attr,
+				 const char *buf, size_t count)
+{
+        dwc_otg_device_t *otg_dev = dwc_otg_drvdev(_dev);
+	uint32_t in = simple_strtoul(buf, NULL, 10);
+	dwc_otg_set_fr_interval(otg_dev->core_if, in);
+	return count;
+}
+
+DEVICE_ATTR(fr_interval, 0644, fr_interval_show, fr_interval_store);
+
+/**
+ * Show the status of Remote Wakeup.
+ */
+static ssize_t remote_wakeup_show(struct device *_dev,
+				  struct device_attribute *attr, char *buf)
+{
+#ifndef DWC_HOST_ONLY
+        dwc_otg_device_t *otg_dev = dwc_otg_drvdev(_dev);
+
+	return sprintf(buf,
+		       "Remote Wakeup Sig = %d Enabled = %d LPM Remote Wakeup = %d\n",
+		       dwc_otg_get_remotewakesig(otg_dev->core_if),
+		       dwc_otg_pcd_get_rmwkup_enable(otg_dev->pcd),
+		       dwc_otg_get_lpm_remotewakeenabled(otg_dev->core_if));
+#else
+	return sprintf(buf, "Host Only Mode!\n");
+#endif /* DWC_HOST_ONLY */
+}
+
+/**
+ * Initiate a remote wakeup of the host.  The Device control register
+ * Remote Wakeup Signal bit is written if the PCD Remote wakeup enable
+ * flag is set.
+ *
+ */
+static ssize_t remote_wakeup_store(struct device *_dev,
+				   struct device_attribute *attr,
+				   const char *buf, size_t count)
+{
+#ifndef DWC_HOST_ONLY
+        dwc_otg_device_t *otg_dev = dwc_otg_drvdev(_dev);
+	uint32_t val = simple_strtoul(buf, NULL, 16);
+
+	if (val & 1) {
+		dwc_otg_pcd_remote_wakeup(otg_dev->pcd, 1);
+	} else {
+		dwc_otg_pcd_remote_wakeup(otg_dev->pcd, 0);
+	}
+#endif /* DWC_HOST_ONLY */
+	return count;
+}
+
+DEVICE_ATTR(remote_wakeup, S_IRUGO | S_IWUSR, remote_wakeup_show,
+	    remote_wakeup_store);
+
+/**
+ * Show the whether core is hibernated or not.
+ */
+static ssize_t rem_wakeup_pwrdn_show(struct device *_dev,
+				     struct device_attribute *attr, char *buf)
+{
+#ifndef DWC_HOST_ONLY
+        dwc_otg_device_t *otg_dev = dwc_otg_drvdev(_dev);
+
+	if (dwc_otg_get_core_state(otg_dev->core_if)) {
+		DWC_PRINTF("Core is in hibernation\n");
+	} else {
+		DWC_PRINTF("Core is not in hibernation\n");
+	}
+#endif /* DWC_HOST_ONLY */
+	return 0;
+}
+
+extern int dwc_otg_device_hibernation_restore(dwc_otg_core_if_t * core_if,
+					      int rem_wakeup, int reset);
+
+/**
+ * Initiate a remote wakeup of the device to exit from hibernation.
+ */
+static ssize_t rem_wakeup_pwrdn_store(struct device *_dev,
+				      struct device_attribute *attr,
+				      const char *buf, size_t count)
+{
+#ifndef DWC_HOST_ONLY
+        dwc_otg_device_t *otg_dev = dwc_otg_drvdev(_dev);
+	dwc_otg_device_hibernation_restore(otg_dev->core_if, 1, 0);
+#endif
+	return count;
+}
+
+DEVICE_ATTR(rem_wakeup_pwrdn, S_IRUGO | S_IWUSR, rem_wakeup_pwrdn_show,
+	    rem_wakeup_pwrdn_store);
+
+static ssize_t disconnect_us(struct device *_dev,
+			     struct device_attribute *attr,
+			     const char *buf, size_t count)
+{
+
+#ifndef DWC_HOST_ONLY
+        dwc_otg_device_t *otg_dev = dwc_otg_drvdev(_dev);
+	uint32_t val = simple_strtoul(buf, NULL, 16);
+	DWC_PRINTF("The Passed value is %04x\n", val);
+
+	dwc_otg_pcd_disconnect_us(otg_dev->pcd, 50);
+
+#endif /* DWC_HOST_ONLY */
+	return count;
+}
+
+DEVICE_ATTR(disconnect_us, S_IWUSR, 0, disconnect_us);
+
+/**
+ * Dump global registers and either host or device registers (depending on the
+ * current mode of the core).
+ */
+static ssize_t regdump_show(struct device *_dev,
+			    struct device_attribute *attr, char *buf)
+{
+        dwc_otg_device_t *otg_dev = dwc_otg_drvdev(_dev);
+
+	dwc_otg_dump_global_registers(otg_dev->core_if);
+	if (dwc_otg_is_host_mode(otg_dev->core_if)) {
+		dwc_otg_dump_host_registers(otg_dev->core_if);
+	} else {
+		dwc_otg_dump_dev_registers(otg_dev->core_if);
+
+	}
+	return sprintf(buf, "Register Dump\n");
+}
+
+DEVICE_ATTR(regdump, S_IRUGO, regdump_show, 0);
+
+/**
+ * Dump global registers and either host or device registers (depending on the
+ * current mode of the core).
+ */
+static ssize_t spramdump_show(struct device *_dev,
+			      struct device_attribute *attr, char *buf)
+{
+#if 0
+	dwc_otg_device_t *otg_dev = dwc_otg_drvdev(_dev);
+
+	dwc_otg_dump_spram(otg_dev->core_if);
+#endif
+
+	return sprintf(buf, "SPRAM Dump\n");
+}
+
+DEVICE_ATTR(spramdump, S_IRUGO, spramdump_show, 0);
+
+/**
+ * Dump the current hcd state.
+ */
+static ssize_t hcddump_show(struct device *_dev,
+			    struct device_attribute *attr, char *buf)
+{
+#ifndef DWC_DEVICE_ONLY
+        dwc_otg_device_t *otg_dev = dwc_otg_drvdev(_dev);
+	dwc_otg_hcd_dump_state(otg_dev->hcd);
+#endif /* DWC_DEVICE_ONLY */
+	return sprintf(buf, "HCD Dump\n");
+}
+
+DEVICE_ATTR(hcddump, S_IRUGO, hcddump_show, 0);
+
+/**
+ * Dump the average frame remaining at SOF. This can be used to
+ * determine average interrupt latency. Frame remaining is also shown for
+ * start transfer and two additional sample points.
+ */
+static ssize_t hcd_frrem_show(struct device *_dev,
+			      struct device_attribute *attr, char *buf)
+{
+#ifndef DWC_DEVICE_ONLY
+        dwc_otg_device_t *otg_dev = dwc_otg_drvdev(_dev);
+
+	dwc_otg_hcd_dump_frrem(otg_dev->hcd);
+#endif /* DWC_DEVICE_ONLY */
+	return sprintf(buf, "HCD Dump Frame Remaining\n");
+}
+
+DEVICE_ATTR(hcd_frrem, S_IRUGO, hcd_frrem_show, 0);
+
+/**
+ * Displays the time required to read the GNPTXFSIZ register many times (the
+ * output shows the number of times the register is read).
+ */
+#define RW_REG_COUNT 10000000
+#define MSEC_PER_JIFFIE 1000/HZ
+static ssize_t rd_reg_test_show(struct device *_dev,
+				struct device_attribute *attr, char *buf)
+{
+        dwc_otg_device_t *otg_dev = dwc_otg_drvdev(_dev);
+	int i;
+	int time;
+	int start_jiffies;
+
+	printk("HZ %d, MSEC_PER_JIFFIE %d, loops_per_jiffy %lu\n",
+	       HZ, MSEC_PER_JIFFIE, loops_per_jiffy);
+	start_jiffies = jiffies;
+	for (i = 0; i < RW_REG_COUNT; i++) {
+		dwc_otg_get_gnptxfsiz(otg_dev->core_if);
+	}
+	time = jiffies - start_jiffies;
+	return sprintf(buf,
+		       "Time to read GNPTXFSIZ reg %d times: %d msecs (%d jiffies)\n",
+		       RW_REG_COUNT, time * MSEC_PER_JIFFIE, time);
+}
+
+DEVICE_ATTR(rd_reg_test, S_IRUGO, rd_reg_test_show, 0);
+
+/**
+ * Displays the time required to write the GNPTXFSIZ register many times (the
+ * output shows the number of times the register is written).
+ */
+static ssize_t wr_reg_test_show(struct device *_dev,
+				struct device_attribute *attr, char *buf)
+{
+        dwc_otg_device_t *otg_dev = dwc_otg_drvdev(_dev);
+	uint32_t reg_val;
+	int i;
+	int time;
+	int start_jiffies;
+
+	printk("HZ %d, MSEC_PER_JIFFIE %d, loops_per_jiffy %lu\n",
+	       HZ, MSEC_PER_JIFFIE, loops_per_jiffy);
+	reg_val = dwc_otg_get_gnptxfsiz(otg_dev->core_if);
+	start_jiffies = jiffies;
+	for (i = 0; i < RW_REG_COUNT; i++) {
+		dwc_otg_set_gnptxfsiz(otg_dev->core_if, reg_val);
+	}
+	time = jiffies - start_jiffies;
+	return sprintf(buf,
+		       "Time to write GNPTXFSIZ reg %d times: %d msecs (%d jiffies)\n",
+		       RW_REG_COUNT, time * MSEC_PER_JIFFIE, time);
+}
+
+DEVICE_ATTR(wr_reg_test, S_IRUGO, wr_reg_test_show, 0);
+
+#ifdef CONFIG_USB_DWC_OTG_LPM
+
+/**
+* Show the lpm_response attribute.
+*/
+static ssize_t lpmresp_show(struct device *_dev,
+			    struct device_attribute *attr, char *buf)
+{
+        dwc_otg_device_t *otg_dev = dwc_otg_drvdev(_dev);
+
+	if (!dwc_otg_get_param_lpm_enable(otg_dev->core_if))
+		return sprintf(buf, "** LPM is DISABLED **\n");
+
+	if (!dwc_otg_is_device_mode(otg_dev->core_if)) {
+		return sprintf(buf, "** Current mode is not device mode\n");
+	}
+	return sprintf(buf, "lpm_response = %d\n",
+		       dwc_otg_get_lpmresponse(otg_dev->core_if));
+}
+
+/**
+* Store the lpm_response attribute.
+*/
+static ssize_t lpmresp_store(struct device *_dev,
+			     struct device_attribute *attr,
+			     const char *buf, size_t count)
+{
+        dwc_otg_device_t *otg_dev = dwc_otg_drvdev(_dev);
+	uint32_t val = simple_strtoul(buf, NULL, 16);
+
+	if (!dwc_otg_get_param_lpm_enable(otg_dev->core_if)) {
+		return 0;
+	}
+
+	if (!dwc_otg_is_device_mode(otg_dev->core_if)) {
+		return 0;
+	}
+
+	dwc_otg_set_lpmresponse(otg_dev->core_if, val);
+	return count;
+}
+
+DEVICE_ATTR(lpm_response, S_IRUGO | S_IWUSR, lpmresp_show, lpmresp_store);
+
+/**
+* Show the sleep_status attribute.
+*/
+static ssize_t sleepstatus_show(struct device *_dev,
+				struct device_attribute *attr, char *buf)
+{
+        dwc_otg_device_t *otg_dev = dwc_otg_drvdev(_dev);
+	return sprintf(buf, "Sleep Status = %d\n",
+		       dwc_otg_get_lpm_portsleepstatus(otg_dev->core_if));
+}
+
+/**
+ * Store the sleep_status attribure.
+ */
+static ssize_t sleepstatus_store(struct device *_dev,
+				 struct device_attribute *attr,
+				 const char *buf, size_t count)
+{
+        dwc_otg_device_t *otg_dev = dwc_otg_drvdev(_dev);
+	dwc_otg_core_if_t *core_if = otg_dev->core_if;
+
+	if (dwc_otg_get_lpm_portsleepstatus(otg_dev->core_if)) {
+		if (dwc_otg_is_host_mode(core_if)) {
+
+			DWC_PRINTF("Host initiated resume\n");
+			dwc_otg_set_prtresume(otg_dev->core_if, 1);
+		}
+	}
+
+	return count;
+}
+
+DEVICE_ATTR(sleep_status, S_IRUGO | S_IWUSR, sleepstatus_show,
+	    sleepstatus_store);
+
+#endif /* CONFIG_USB_DWC_OTG_LPM_ENABLE */
+
+/**@}*/
+
+/**
+ * Create the device files
+ */
+void dwc_otg_attr_create(
+#ifdef LM_INTERFACE
+	struct lm_device *dev
+#elif  defined(PCI_INTERFACE)
+	struct pci_dev *dev
+#elif  defined(PLATFORM_INTERFACE)
+        struct platform_device *dev
+#endif
+    )
+{
+	int error;
+
+	error = device_create_file(&dev->dev, &dev_attr_regoffset);
+	error = device_create_file(&dev->dev, &dev_attr_regvalue);
+	error = device_create_file(&dev->dev, &dev_attr_mode);
+	error = device_create_file(&dev->dev, &dev_attr_hnpcapable);
+	error = device_create_file(&dev->dev, &dev_attr_srpcapable);
+	error = device_create_file(&dev->dev, &dev_attr_hsic_connect);
+	error = device_create_file(&dev->dev, &dev_attr_inv_sel_hsic);
+	error = device_create_file(&dev->dev, &dev_attr_hnp);
+	error = device_create_file(&dev->dev, &dev_attr_srp);
+	error = device_create_file(&dev->dev, &dev_attr_buspower);
+	error = device_create_file(&dev->dev, &dev_attr_bussuspend);
+	error = device_create_file(&dev->dev, &dev_attr_mode_ch_tim_en);
+	error = device_create_file(&dev->dev, &dev_attr_fr_interval);
+	error = device_create_file(&dev->dev, &dev_attr_busconnected);
+	error = device_create_file(&dev->dev, &dev_attr_gotgctl);
+	error = device_create_file(&dev->dev, &dev_attr_gusbcfg);
+	error = device_create_file(&dev->dev, &dev_attr_grxfsiz);
+	error = device_create_file(&dev->dev, &dev_attr_gnptxfsiz);
+	error = device_create_file(&dev->dev, &dev_attr_gpvndctl);
+	error = device_create_file(&dev->dev, &dev_attr_ggpio);
+	error = device_create_file(&dev->dev, &dev_attr_guid);
+	error = device_create_file(&dev->dev, &dev_attr_gsnpsid);
+	error = device_create_file(&dev->dev, &dev_attr_devspeed);
+	error = device_create_file(&dev->dev, &dev_attr_enumspeed);
+	error = device_create_file(&dev->dev, &dev_attr_hptxfsiz);
+	error = device_create_file(&dev->dev, &dev_attr_hprt0);
+	error = device_create_file(&dev->dev, &dev_attr_remote_wakeup);
+	error = device_create_file(&dev->dev, &dev_attr_rem_wakeup_pwrdn);
+	error = device_create_file(&dev->dev, &dev_attr_disconnect_us);
+	error = device_create_file(&dev->dev, &dev_attr_regdump);
+	error = device_create_file(&dev->dev, &dev_attr_spramdump);
+	error = device_create_file(&dev->dev, &dev_attr_hcddump);
+	error = device_create_file(&dev->dev, &dev_attr_hcd_frrem);
+	error = device_create_file(&dev->dev, &dev_attr_rd_reg_test);
+	error = device_create_file(&dev->dev, &dev_attr_wr_reg_test);
+#ifdef CONFIG_USB_DWC_OTG_LPM
+	error = device_create_file(&dev->dev, &dev_attr_lpm_response);
+	error = device_create_file(&dev->dev, &dev_attr_sleep_status);
+#endif
+}
+
+/**
+ * Remove the device files
+ */
+void dwc_otg_attr_remove(
+#ifdef LM_INTERFACE
+	struct lm_device *dev
+#elif  defined(PCI_INTERFACE)
+	struct pci_dev *dev
+#elif  defined(PLATFORM_INTERFACE)
+	struct platform_device *dev
+#endif
+    )
+{
+	device_remove_file(&dev->dev, &dev_attr_regoffset);
+	device_remove_file(&dev->dev, &dev_attr_regvalue);
+	device_remove_file(&dev->dev, &dev_attr_mode);
+	device_remove_file(&dev->dev, &dev_attr_hnpcapable);
+	device_remove_file(&dev->dev, &dev_attr_srpcapable);
+	device_remove_file(&dev->dev, &dev_attr_hsic_connect);
+	device_remove_file(&dev->dev, &dev_attr_inv_sel_hsic);
+	device_remove_file(&dev->dev, &dev_attr_hnp);
+	device_remove_file(&dev->dev, &dev_attr_srp);
+	device_remove_file(&dev->dev, &dev_attr_buspower);
+	device_remove_file(&dev->dev, &dev_attr_bussuspend);
+	device_remove_file(&dev->dev, &dev_attr_mode_ch_tim_en);
+	device_remove_file(&dev->dev, &dev_attr_fr_interval);
+	device_remove_file(&dev->dev, &dev_attr_busconnected);
+	device_remove_file(&dev->dev, &dev_attr_gotgctl);
+	device_remove_file(&dev->dev, &dev_attr_gusbcfg);
+	device_remove_file(&dev->dev, &dev_attr_grxfsiz);
+	device_remove_file(&dev->dev, &dev_attr_gnptxfsiz);
+	device_remove_file(&dev->dev, &dev_attr_gpvndctl);
+	device_remove_file(&dev->dev, &dev_attr_ggpio);
+	device_remove_file(&dev->dev, &dev_attr_guid);
+	device_remove_file(&dev->dev, &dev_attr_gsnpsid);
+	device_remove_file(&dev->dev, &dev_attr_devspeed);
+	device_remove_file(&dev->dev, &dev_attr_enumspeed);
+	device_remove_file(&dev->dev, &dev_attr_hptxfsiz);
+	device_remove_file(&dev->dev, &dev_attr_hprt0);
+	device_remove_file(&dev->dev, &dev_attr_remote_wakeup);
+	device_remove_file(&dev->dev, &dev_attr_rem_wakeup_pwrdn);
+	device_remove_file(&dev->dev, &dev_attr_disconnect_us);
+	device_remove_file(&dev->dev, &dev_attr_regdump);
+	device_remove_file(&dev->dev, &dev_attr_spramdump);
+	device_remove_file(&dev->dev, &dev_attr_hcddump);
+	device_remove_file(&dev->dev, &dev_attr_hcd_frrem);
+	device_remove_file(&dev->dev, &dev_attr_rd_reg_test);
+	device_remove_file(&dev->dev, &dev_attr_wr_reg_test);
+#ifdef CONFIG_USB_DWC_OTG_LPM
+	device_remove_file(&dev->dev, &dev_attr_lpm_response);
+	device_remove_file(&dev->dev, &dev_attr_sleep_status);
+#endif
+}
diff -Nur --no-dereference linux-5.10.52-orig/drivers/usb/host/dwc_otg/dwc_otg_attr.h linux-5.10.52-v7l+/drivers/usb/host/dwc_otg/dwc_otg_attr.h
--- linux-5.10.52-orig/drivers/usb/host/dwc_otg/dwc_otg_attr.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/usb/host/dwc_otg/dwc_otg_attr.h	2021-07-25 16:46:28.657910448 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/* ==========================================================================
+ * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_attr.h $
+ * $Revision: #13 $
+ * $Date: 2010/06/21 $
+ * $Change: 1532021 $
+ *
+ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter,
+ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless
+ * otherwise expressly agreed to in writing between Synopsys and you.
+ *
+ * The Software IS NOT an item of Licensed Software or Licensed Product under
+ * any End User Software License Agreement or Agreement for Licensed Product
+ * with Synopsys or any supplement thereto. You are permitted to use and
+ * redistribute this Software in source and binary forms, with or without
+ * modification, provided that redistributions of source code must retain this
+ * notice. You may not view, use, disclose, copy or distribute this file or
+ * any information contained herein except pursuant to this license grant from
+ * Synopsys. If you do not agree with this notice, including the disclaimer
+ * below, then you are not authorized to use the Software.
+ *
+ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * ========================================================================== */
+
+#if !defined(__DWC_OTG_ATTR_H__)
+#define __DWC_OTG_ATTR_H__
+
+/** @file
+ * This file contains the interface to the Linux device attributes.
+ */
+extern struct device_attribute dev_attr_regoffset;
+extern struct device_attribute dev_attr_regvalue;
+
+extern struct device_attribute dev_attr_mode;
+extern struct device_attribute dev_attr_hnpcapable;
+extern struct device_attribute dev_attr_srpcapable;
+extern struct device_attribute dev_attr_hnp;
+extern struct device_attribute dev_attr_srp;
+extern struct device_attribute dev_attr_buspower;
+extern struct device_attribute dev_attr_bussuspend;
+extern struct device_attribute dev_attr_mode_ch_tim_en;
+extern struct device_attribute dev_attr_fr_interval;
+extern struct device_attribute dev_attr_busconnected;
+extern struct device_attribute dev_attr_gotgctl;
+extern struct device_attribute dev_attr_gusbcfg;
+extern struct device_attribute dev_attr_grxfsiz;
+extern struct device_attribute dev_attr_gnptxfsiz;
+extern struct device_attribute dev_attr_gpvndctl;
+extern struct device_attribute dev_attr_ggpio;
+extern struct device_attribute dev_attr_guid;
+extern struct device_attribute dev_attr_gsnpsid;
+extern struct device_attribute dev_attr_devspeed;
+extern struct device_attribute dev_attr_enumspeed;
+extern struct device_attribute dev_attr_hptxfsiz;
+extern struct device_attribute dev_attr_hprt0;
+#ifdef CONFIG_USB_DWC_OTG_LPM
+extern struct device_attribute dev_attr_lpm_response;
+extern struct device_attribute devi_attr_sleep_status;
+#endif
+
+void dwc_otg_attr_create(
+#ifdef LM_INTERFACE
+				struct lm_device *dev
+#elif  defined(PCI_INTERFACE)
+				struct pci_dev *dev
+#elif  defined(PLATFORM_INTERFACE)
+	struct platform_device *dev
+#endif
+    );
+
+void dwc_otg_attr_remove(
+#ifdef LM_INTERFACE
+				struct lm_device *dev
+#elif  defined(PCI_INTERFACE)
+				struct pci_dev *dev
+#elif  defined(PLATFORM_INTERFACE)
+	struct platform_device *dev
+#endif
+    );
+#endif
diff -Nur --no-dereference linux-5.10.52-orig/drivers/usb/host/dwc_otg/dwc_otg_cfi.c linux-5.10.52-v7l+/drivers/usb/host/dwc_otg/dwc_otg_cfi.c
--- linux-5.10.52-orig/drivers/usb/host/dwc_otg/dwc_otg_cfi.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/usb/host/dwc_otg/dwc_otg_cfi.c	2021-07-25 16:46:28.677910113 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/* ==========================================================================
+ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter,
+ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless
+ * otherwise expressly agreed to in writing between Synopsys and you.
+ *
+ * The Software IS NOT an item of Licensed Software or Licensed Product under
+ * any End User Software License Agreement or Agreement for Licensed Product
+ * with Synopsys or any supplement thereto. You are permitted to use and
+ * redistribute this Software in source and binary forms, with or without
+ * modification, provided that redistributions of source code must retain this
+ * notice. You may not view, use, disclose, copy or distribute this file or
+ * any information contained herein except pursuant to this license grant from
+ * Synopsys. If you do not agree with this notice, including the disclaimer
+ * below, then you are not authorized to use the Software.
+ *
+ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * ========================================================================== */
+
+/** @file
+ *
+ * This file contains the most of the CFI(Core Feature Interface)
+ * implementation for the OTG.
+ */
+
+#ifdef DWC_UTE_CFI
+
+#include "dwc_otg_pcd.h"
+#include "dwc_otg_cfi.h"
+
+/** This definition should actually migrate to the Portability Library */
+#define DWC_CONSTANT_CPU_TO_LE16(x) (x)
+
+extern dwc_otg_pcd_ep_t *get_ep_by_addr(dwc_otg_pcd_t * pcd, u16 wIndex);
+
+static int cfi_core_features_buf(uint8_t * buf, uint16_t buflen);
+static int cfi_get_feature_value(uint8_t * buf, uint16_t buflen,
+				 struct dwc_otg_pcd *pcd,
+				 struct cfi_usb_ctrlrequest *ctrl_req);
+static int cfi_set_feature_value(struct dwc_otg_pcd *pcd);
+static int cfi_ep_get_sg_val(uint8_t * buf, struct dwc_otg_pcd *pcd,
+			     struct cfi_usb_ctrlrequest *req);
+static int cfi_ep_get_concat_val(uint8_t * buf, struct dwc_otg_pcd *pcd,
+				 struct cfi_usb_ctrlrequest *req);
+static int cfi_ep_get_align_val(uint8_t * buf, struct dwc_otg_pcd *pcd,
+				struct cfi_usb_ctrlrequest *req);
+static int cfi_preproc_reset(struct dwc_otg_pcd *pcd,
+			     struct cfi_usb_ctrlrequest *req);
+static void cfi_free_ep_bs_dyn_data(cfi_ep_t * cfiep);
+
+static uint16_t get_dfifo_size(dwc_otg_core_if_t * core_if);
+static int32_t get_rxfifo_size(dwc_otg_core_if_t * core_if, uint16_t wValue);
+static int32_t get_txfifo_size(struct dwc_otg_pcd *pcd, uint16_t wValue);
+
+static uint8_t resize_fifos(dwc_otg_core_if_t * core_if);
+
+/** This is the header of the all features descriptor */
+static cfi_all_features_header_t all_props_desc_header = {
+	.wVersion = DWC_CONSTANT_CPU_TO_LE16(0x100),
+	.wCoreID = DWC_CONSTANT_CPU_TO_LE16(CFI_CORE_ID_OTG),
+	.wNumFeatures = DWC_CONSTANT_CPU_TO_LE16(9),
+};
+
+/** This is an array of statically allocated feature descriptors */
+static cfi_feature_desc_header_t prop_descs[] = {
+
+	/* FT_ID_DMA_MODE */
+	{
+	 .wFeatureID = DWC_CONSTANT_CPU_TO_LE16(FT_ID_DMA_MODE),
+	 .bmAttributes = CFI_FEATURE_ATTR_RW,
+	 .wDataLength = DWC_CONSTANT_CPU_TO_LE16(1),
+	 },
+
+	/* FT_ID_DMA_BUFFER_SETUP */
+	{
+	 .wFeatureID = DWC_CONSTANT_CPU_TO_LE16(FT_ID_DMA_BUFFER_SETUP),
+	 .bmAttributes = CFI_FEATURE_ATTR_RW,
+	 .wDataLength = DWC_CONSTANT_CPU_TO_LE16(6),
+	 },
+
+	/* FT_ID_DMA_BUFF_ALIGN */
+	{
+	 .wFeatureID = DWC_CONSTANT_CPU_TO_LE16(FT_ID_DMA_BUFF_ALIGN),
+	 .bmAttributes = CFI_FEATURE_ATTR_RW,
+	 .wDataLength = DWC_CONSTANT_CPU_TO_LE16(2),
+	 },
+
+	/* FT_ID_DMA_CONCAT_SETUP */
+	{
+	 .wFeatureID = DWC_CONSTANT_CPU_TO_LE16(FT_ID_DMA_CONCAT_SETUP),
+	 .bmAttributes = CFI_FEATURE_ATTR_RW,
+	 //.wDataLength  = DWC_CONSTANT_CPU_TO_LE16(6),
+	 },
+
+	/* FT_ID_DMA_CIRCULAR */
+	{
+	 .wFeatureID = DWC_CONSTANT_CPU_TO_LE16(FT_ID_DMA_CIRCULAR),
+	 .bmAttributes = CFI_FEATURE_ATTR_RW,
+	 .wDataLength = DWC_CONSTANT_CPU_TO_LE16(6),
+	 },
+
+	/* FT_ID_THRESHOLD_SETUP */
+	{
+	 .wFeatureID = DWC_CONSTANT_CPU_TO_LE16(FT_ID_THRESHOLD_SETUP),
+	 .bmAttributes = CFI_FEATURE_ATTR_RW,
+	 .wDataLength = DWC_CONSTANT_CPU_TO_LE16(6),
+	 },
+
+	/* FT_ID_DFIFO_DEPTH */
+	{
+	 .wFeatureID = DWC_CONSTANT_CPU_TO_LE16(FT_ID_DFIFO_DEPTH),
+	 .bmAttributes = CFI_FEATURE_ATTR_RO,
+	 .wDataLength = DWC_CONSTANT_CPU_TO_LE16(2),
+	 },
+
+	/* FT_ID_TX_FIFO_DEPTH */
+	{
+	 .wFeatureID = DWC_CONSTANT_CPU_TO_LE16(FT_ID_TX_FIFO_DEPTH),
+	 .bmAttributes = CFI_FEATURE_ATTR_RW,
+	 .wDataLength = DWC_CONSTANT_CPU_TO_LE16(2),
+	 },
+
+	/* FT_ID_RX_FIFO_DEPTH */
+	{
+	 .wFeatureID = DWC_CONSTANT_CPU_TO_LE16(FT_ID_RX_FIFO_DEPTH),
+	 .bmAttributes = CFI_FEATURE_ATTR_RW,
+	 .wDataLength = DWC_CONSTANT_CPU_TO_LE16(2),
+	 }
+};
+
+/** The table of feature names */
+cfi_string_t prop_name_table[] = {
+	{FT_ID_DMA_MODE, "dma_mode"},
+	{FT_ID_DMA_BUFFER_SETUP, "buffer_setup"},
+	{FT_ID_DMA_BUFF_ALIGN, "buffer_align"},
+	{FT_ID_DMA_CONCAT_SETUP, "concat_setup"},
+	{FT_ID_DMA_CIRCULAR, "buffer_circular"},
+	{FT_ID_THRESHOLD_SETUP, "threshold_setup"},
+	{FT_ID_DFIFO_DEPTH, "dfifo_depth"},
+	{FT_ID_TX_FIFO_DEPTH, "txfifo_depth"},
+	{FT_ID_RX_FIFO_DEPTH, "rxfifo_depth"},
+	{}
+};
+
+/************************************************************************/
+
+/**
+ * Returns the name of the feature by its ID
+ * or NULL if no featute ID matches.
+ *
+ */
+const uint8_t *get_prop_name(uint16_t prop_id, int *len)
+{
+	cfi_string_t *pstr;
+	*len = 0;
+
+	for (pstr = prop_name_table; pstr && pstr->s; pstr++) {
+		if (pstr->id == prop_id) {
+			*len = DWC_STRLEN(pstr->s);
+			return pstr->s;
+		}
+	}
+	return NULL;
+}
+
+/**
+ * This function handles all CFI specific control requests.
+ *
+ * Return a negative value to stall the DCE.
+ */
+int cfi_setup(struct dwc_otg_pcd *pcd, struct cfi_usb_ctrlrequest *ctrl)
+{
+	int retval = 0;
+	dwc_otg_pcd_ep_t *ep = NULL;
+	cfiobject_t *cfi = pcd->cfi;
+	struct dwc_otg_core_if *coreif = GET_CORE_IF(pcd);
+	uint16_t wLen = DWC_LE16_TO_CPU(&ctrl->wLength);
+	uint16_t wValue = DWC_LE16_TO_CPU(&ctrl->wValue);
+	uint16_t wIndex = DWC_LE16_TO_CPU(&ctrl->wIndex);
+	uint32_t regaddr = 0;
+	uint32_t regval = 0;
+
+	/* Save this Control Request in the CFI object.
+	 * The data field will be assigned in the data stage completion CB function.
+	 */
+	cfi->ctrl_req = *ctrl;
+	cfi->ctrl_req.data = NULL;
+
+	cfi->need_gadget_att = 0;
+	cfi->need_status_in_complete = 0;
+
+	switch (ctrl->bRequest) {
+	case VEN_CORE_GET_FEATURES:
+		retval = cfi_core_features_buf(cfi->buf_in.buf, CFI_IN_BUF_LEN);
+		if (retval >= 0) {
+			//dump_msg(cfi->buf_in.buf, retval);
+			ep = &pcd->ep0;
+
+			retval = min((uint16_t) retval, wLen);
+			/* Transfer this buffer to the host through the EP0-IN EP */
+			ep->dwc_ep.dma_addr = cfi->buf_in.addr;
+			ep->dwc_ep.start_xfer_buff = cfi->buf_in.buf;
+			ep->dwc_ep.xfer_buff = cfi->buf_in.buf;
+			ep->dwc_ep.xfer_len = retval;
+			ep->dwc_ep.xfer_count = 0;
+			ep->dwc_ep.sent_zlp = 0;
+			ep->dwc_ep.total_len = ep->dwc_ep.xfer_len;
+
+			pcd->ep0_pending = 1;
+			dwc_otg_ep0_start_transfer(coreif, &ep->dwc_ep);
+		}
+		retval = 0;
+		break;
+
+	case VEN_CORE_GET_FEATURE:
+		CFI_INFO("VEN_CORE_GET_FEATURE\n");
+		retval = cfi_get_feature_value(cfi->buf_in.buf, CFI_IN_BUF_LEN,
+					       pcd, ctrl);
+		if (retval >= 0) {
+			ep = &pcd->ep0;
+
+			retval = min((uint16_t) retval, wLen);
+			/* Transfer this buffer to the host through the EP0-IN EP */
+			ep->dwc_ep.dma_addr = cfi->buf_in.addr;
+			ep->dwc_ep.start_xfer_buff = cfi->buf_in.buf;
+			ep->dwc_ep.xfer_buff = cfi->buf_in.buf;
+			ep->dwc_ep.xfer_len = retval;
+			ep->dwc_ep.xfer_count = 0;
+			ep->dwc_ep.sent_zlp = 0;
+			ep->dwc_ep.total_len = ep->dwc_ep.xfer_len;
+
+			pcd->ep0_pending = 1;
+			dwc_otg_ep0_start_transfer(coreif, &ep->dwc_ep);
+		}
+		CFI_INFO("VEN_CORE_GET_FEATURE=%d\n", retval);
+		dump_msg(cfi->buf_in.buf, retval);
+		break;
+
+	case VEN_CORE_SET_FEATURE:
+		CFI_INFO("VEN_CORE_SET_FEATURE\n");
+		/* Set up an XFER to get the data stage of the control request,
+		 * which is the new value of the feature to be modified.
+		 */
+		ep = &pcd->ep0;
+		ep->dwc_ep.is_in = 0;
+		ep->dwc_ep.dma_addr = cfi->buf_out.addr;
+		ep->dwc_ep.start_xfer_buff = cfi->buf_out.buf;
+		ep->dwc_ep.xfer_buff = cfi->buf_out.buf;
+		ep->dwc_ep.xfer_len = wLen;
+		ep->dwc_ep.xfer_count = 0;
+		ep->dwc_ep.sent_zlp = 0;
+		ep->dwc_ep.total_len = ep->dwc_ep.xfer_len;
+
+		pcd->ep0_pending = 1;
+		/* Read the control write's data stage */
+		dwc_otg_ep0_start_transfer(coreif, &ep->dwc_ep);
+		retval = 0;
+		break;
+
+	case VEN_CORE_RESET_FEATURES:
+		CFI_INFO("VEN_CORE_RESET_FEATURES\n");
+		cfi->need_gadget_att = 1;
+		cfi->need_status_in_complete = 1;
+		retval = cfi_preproc_reset(pcd, ctrl);
+		CFI_INFO("VEN_CORE_RESET_FEATURES = (%d)\n", retval);
+		break;
+
+	case VEN_CORE_ACTIVATE_FEATURES:
+		CFI_INFO("VEN_CORE_ACTIVATE_FEATURES\n");
+		break;
+
+	case VEN_CORE_READ_REGISTER:
+		CFI_INFO("VEN_CORE_READ_REGISTER\n");
+		/* wValue optionally contains the HI WORD of the register offset and
+		 * wIndex contains the LOW WORD of the register offset
+		 */
+		if (wValue == 0) {
+			/* @TODO - MAS - fix the access to the base field */
+			regaddr = 0;
+			//regaddr = (uint32_t) pcd->otg_dev->os_dep.base;
+			//GET_CORE_IF(pcd)->co
+			regaddr |= wIndex;
+		} else {
+			regaddr = (wValue << 16) | wIndex;
+		}
+
+		/* Read a 32-bit value of the memory at the regaddr */
+		regval = DWC_READ_REG32((uint32_t *) regaddr);
+
+		ep = &pcd->ep0;
+		dwc_memcpy(cfi->buf_in.buf, &regval, sizeof(uint32_t));
+		ep->dwc_ep.is_in = 1;
+		ep->dwc_ep.dma_addr = cfi->buf_in.addr;
+		ep->dwc_ep.start_xfer_buff = cfi->buf_in.buf;
+		ep->dwc_ep.xfer_buff = cfi->buf_in.buf;
+		ep->dwc_ep.xfer_len = wLen;
+		ep->dwc_ep.xfer_count = 0;
+		ep->dwc_ep.sent_zlp = 0;
+		ep->dwc_ep.total_len = ep->dwc_ep.xfer_len;
+
+		pcd->ep0_pending = 1;
+		dwc_otg_ep0_start_transfer(coreif, &ep->dwc_ep);
+		cfi->need_gadget_att = 0;
+		retval = 0;
+		break;
+
+	case VEN_CORE_WRITE_REGISTER:
+		CFI_INFO("VEN_CORE_WRITE_REGISTER\n");
+		/* Set up an XFER to get the data stage of the control request,
+		 * which is the new value of the register to be modified.
+		 */
+		ep = &pcd->ep0;
+		ep->dwc_ep.is_in = 0;
+		ep->dwc_ep.dma_addr = cfi->buf_out.addr;
+		ep->dwc_ep.start_xfer_buff = cfi->buf_out.buf;
+		ep->dwc_ep.xfer_buff = cfi->buf_out.buf;
+		ep->dwc_ep.xfer_len = wLen;
+		ep->dwc_ep.xfer_count = 0;
+		ep->dwc_ep.sent_zlp = 0;
+		ep->dwc_ep.total_len = ep->dwc_ep.xfer_len;
+
+		pcd->ep0_pending = 1;
+		/* Read the control write's data stage */
+		dwc_otg_ep0_start_transfer(coreif, &ep->dwc_ep);
+		retval = 0;
+		break;
+
+	default:
+		retval = -DWC_E_NOT_SUPPORTED;
+		break;
+	}
+
+	return retval;
+}
+
+/**
+ * This function prepares the core features descriptors and copies its
+ * raw representation into the buffer <buf>.
+ *
+ * The buffer structure is as follows:
+ *	all_features_header (8 bytes)
+ *	features_#1 (8 bytes + feature name string length)
+ *	features_#2 (8 bytes + feature name string length)
+ *	.....
+ *	features_#n - where n=the total count of feature descriptors
+ */
+static int cfi_core_features_buf(uint8_t * buf, uint16_t buflen)
+{
+	cfi_feature_desc_header_t *prop_hdr = prop_descs;
+	cfi_feature_desc_header_t *prop;
+	cfi_all_features_header_t *all_props_hdr = &all_props_desc_header;
+	cfi_all_features_header_t *tmp;
+	uint8_t *tmpbuf = buf;
+	const uint8_t *pname = NULL;
+	int i, j, namelen = 0, totlen;
+
+	/* Prepare and copy the core features into the buffer */
+	CFI_INFO("%s:\n", __func__);
+
+	tmp = (cfi_all_features_header_t *) tmpbuf;
+	*tmp = *all_props_hdr;
+	tmpbuf += CFI_ALL_FEATURES_HDR_LEN;
+
+	j = sizeof(prop_descs) / sizeof(cfi_all_features_header_t);
+	for (i = 0; i < j; i++, prop_hdr++) {
+		pname = get_prop_name(prop_hdr->wFeatureID, &namelen);
+		prop = (cfi_feature_desc_header_t *) tmpbuf;
+		*prop = *prop_hdr;
+
+		prop->bNameLen = namelen;
+		prop->wLength =
+		    DWC_CONSTANT_CPU_TO_LE16(CFI_FEATURE_DESC_HDR_LEN +
+					     namelen);
+
+		tmpbuf += CFI_FEATURE_DESC_HDR_LEN;
+		dwc_memcpy(tmpbuf, pname, namelen);
+		tmpbuf += namelen;
+	}
+
+	totlen = tmpbuf - buf;
+
+	if (totlen > 0) {
+		tmp = (cfi_all_features_header_t *) buf;
+		tmp->wTotalLen = DWC_CONSTANT_CPU_TO_LE16(totlen);
+	}
+
+	return totlen;
+}
+
+/**
+ * This function releases all the dynamic memory in the CFI object.
+ */
+static void cfi_release(cfiobject_t * cfiobj)
+{
+	cfi_ep_t *cfiep;
+	dwc_list_link_t *tmp;
+
+	CFI_INFO("%s\n", __func__);
+
+	if (cfiobj->buf_in.buf) {
+		DWC_DMA_FREE(CFI_IN_BUF_LEN, cfiobj->buf_in.buf,
+			     cfiobj->buf_in.addr);
+		cfiobj->buf_in.buf = NULL;
+	}
+
+	if (cfiobj->buf_out.buf) {
+		DWC_DMA_FREE(CFI_OUT_BUF_LEN, cfiobj->buf_out.buf,
+			     cfiobj->buf_out.addr);
+		cfiobj->buf_out.buf = NULL;
+	}
+
+	/* Free the Buffer Setup values for each EP */
+	//list_for_each_entry(cfiep, &cfiobj->active_eps, lh) {
+	DWC_LIST_FOREACH(tmp, &cfiobj->active_eps) {
+		cfiep = DWC_LIST_ENTRY(tmp, struct cfi_ep, lh);
+		cfi_free_ep_bs_dyn_data(cfiep);
+	}
+}
+
+/**
+ * This function frees the dynamically allocated EP buffer setup data.
+ */
+static void cfi_free_ep_bs_dyn_data(cfi_ep_t * cfiep)
+{
+	if (cfiep->bm_sg) {
+		DWC_FREE(cfiep->bm_sg);
+		cfiep->bm_sg = NULL;
+	}
+
+	if (cfiep->bm_align) {
+		DWC_FREE(cfiep->bm_align);
+		cfiep->bm_align = NULL;
+	}
+
+	if (cfiep->bm_concat) {
+		if (NULL != cfiep->bm_concat->wTxBytes) {
+			DWC_FREE(cfiep->bm_concat->wTxBytes);
+			cfiep->bm_concat->wTxBytes = NULL;
+		}
+		DWC_FREE(cfiep->bm_concat);
+		cfiep->bm_concat = NULL;
+	}
+}
+
+/**
+ * This function initializes the default values of the features
+ * for a specific endpoint and should be called only once when
+ * the EP is enabled first time.
+ */
+static int cfi_ep_init_defaults(struct dwc_otg_pcd *pcd, cfi_ep_t * cfiep)
+{
+	int retval = 0;
+
+	cfiep->bm_sg = DWC_ALLOC(sizeof(ddma_sg_buffer_setup_t));
+	if (NULL == cfiep->bm_sg) {
+		CFI_INFO("Failed to allocate memory for SG feature value\n");
+		return -DWC_E_NO_MEMORY;
+	}
+	dwc_memset(cfiep->bm_sg, 0, sizeof(ddma_sg_buffer_setup_t));
+
+	/* For the Concatenation feature's default value we do not allocate
+	 * memory for the wTxBytes field - it will be done in the set_feature_value
+	 * request handler.
+	 */
+	cfiep->bm_concat = DWC_ALLOC(sizeof(ddma_concat_buffer_setup_t));
+	if (NULL == cfiep->bm_concat) {
+		CFI_INFO
+		    ("Failed to allocate memory for CONCATENATION feature value\n");
+		DWC_FREE(cfiep->bm_sg);
+		return -DWC_E_NO_MEMORY;
+	}
+	dwc_memset(cfiep->bm_concat, 0, sizeof(ddma_concat_buffer_setup_t));
+
+	cfiep->bm_align = DWC_ALLOC(sizeof(ddma_align_buffer_setup_t));
+	if (NULL == cfiep->bm_align) {
+		CFI_INFO
+		    ("Failed to allocate memory for Alignment feature value\n");
+		DWC_FREE(cfiep->bm_sg);
+		DWC_FREE(cfiep->bm_concat);
+		return -DWC_E_NO_MEMORY;
+	}
+	dwc_memset(cfiep->bm_align, 0, sizeof(ddma_align_buffer_setup_t));
+
+	return retval;
+}
+
+/**
+ * The callback function that notifies the CFI on the activation of
+ * an endpoint in the PCD. The following steps are done in this function:
+ *
+ *	Create a dynamically allocated cfi_ep_t object (a CFI wrapper to the PCD's
+ *		active endpoint)
+ *	Create MAX_DMA_DESCS_PER_EP count DMA Descriptors for the EP
+ *	Set the Buffer Mode to standard
+ *	Initialize the default values for all EP modes (SG, Circular, Concat, Align)
+ *	Add the cfi_ep_t object to the list of active endpoints in the CFI object
+ */
+static int cfi_ep_enable(struct cfiobject *cfi, struct dwc_otg_pcd *pcd,
+			 struct dwc_otg_pcd_ep *ep)
+{
+	cfi_ep_t *cfiep;
+	int retval = -DWC_E_NOT_SUPPORTED;
+
+	CFI_INFO("%s: epname=%s; epnum=0x%02x\n", __func__,
+		 "EP_" /*ep->ep.name */ , ep->desc->bEndpointAddress);
+	/* MAS - Check whether this endpoint already is in the list */
+	cfiep = get_cfi_ep_by_pcd_ep(cfi, ep);
+
+	if (NULL == cfiep) {
+		/* Allocate a cfi_ep_t object */
+		cfiep = DWC_ALLOC(sizeof(cfi_ep_t));
+		if (NULL == cfiep) {
+			CFI_INFO
+			    ("Unable to allocate memory for <cfiep> in function %s\n",
+			     __func__);
+			return -DWC_E_NO_MEMORY;
+		}
+		dwc_memset(cfiep, 0, sizeof(cfi_ep_t));
+
+		/* Save the dwc_otg_pcd_ep pointer in the cfiep object */
+		cfiep->ep = ep;
+
+		/* Allocate the DMA Descriptors chain of MAX_DMA_DESCS_PER_EP count */
+		ep->dwc_ep.descs =
+		    DWC_DMA_ALLOC(MAX_DMA_DESCS_PER_EP *
+				  sizeof(dwc_otg_dma_desc_t),
+				  &ep->dwc_ep.descs_dma_addr);
+
+		if (NULL == ep->dwc_ep.descs) {
+			DWC_FREE(cfiep);
+			return -DWC_E_NO_MEMORY;
+		}
+
+		DWC_LIST_INIT(&cfiep->lh);
+
+		/* Set the buffer mode to BM_STANDARD. It will be modified
+		 * when building descriptors for a specific buffer mode */
+		ep->dwc_ep.buff_mode = BM_STANDARD;
+
+		/* Create and initialize the default values for this EP's Buffer modes */
+		if ((retval = cfi_ep_init_defaults(pcd, cfiep)) < 0)
+			return retval;
+
+		/* Add the cfi_ep_t object to the CFI object's list of active endpoints */
+		DWC_LIST_INSERT_TAIL(&cfi->active_eps, &cfiep->lh);
+		retval = 0;
+	} else {		/* The sought EP already is in the list */
+		CFI_INFO("%s: The sought EP already is in the list\n",
+			 __func__);
+	}
+
+	return retval;
+}
+
+/**
+ * This function is called when the data stage of a 3-stage Control Write request
+ * is complete.
+ *
+ */
+static int cfi_ctrl_write_complete(struct cfiobject *cfi,
+				   struct dwc_otg_pcd *pcd)
+{
+	uint32_t addr, reg_value;
+	uint16_t wIndex, wValue;
+	uint8_t bRequest;
+	uint8_t *buf = cfi->buf_out.buf;
+	//struct usb_ctrlrequest *ctrl_req = &cfi->ctrl_req_saved;
+	struct cfi_usb_ctrlrequest *ctrl_req = &cfi->ctrl_req;
+	int retval = -DWC_E_NOT_SUPPORTED;
+
+	CFI_INFO("%s\n", __func__);
+
+	bRequest = ctrl_req->bRequest;
+	wIndex = DWC_CONSTANT_CPU_TO_LE16(ctrl_req->wIndex);
+	wValue = DWC_CONSTANT_CPU_TO_LE16(ctrl_req->wValue);
+
+	/*
+	 * Save the pointer to the data stage in the ctrl_req's <data> field.
+	 * The request should be already saved in the command stage by now.
+	 */
+	ctrl_req->data = cfi->buf_out.buf;
+	cfi->need_status_in_complete = 0;
+	cfi->need_gadget_att = 0;
+
+	switch (bRequest) {
+	case VEN_CORE_WRITE_REGISTER:
+		/* The buffer contains raw data of the new value for the register */
+		reg_value = *((uint32_t *) buf);
+		if (wValue == 0) {
+			addr = 0;
+			//addr = (uint32_t) pcd->otg_dev->os_dep.base;
+			addr += wIndex;
+		} else {
+			addr = (wValue << 16) | wIndex;
+		}
+
+		//writel(reg_value, addr);
+
+		retval = 0;
+		cfi->need_status_in_complete = 1;
+		break;
+
+	case VEN_CORE_SET_FEATURE:
+		/* The buffer contains raw data of the new value of the feature */
+		retval = cfi_set_feature_value(pcd);
+		if (retval < 0)
+			return retval;
+
+		cfi->need_status_in_complete = 1;
+		break;
+
+	default:
+		break;
+	}
+
+	return retval;
+}
+
+/**
+ * This function builds the DMA descriptors for the SG buffer mode.
+ */
+static void cfi_build_sg_descs(struct cfiobject *cfi, cfi_ep_t * cfiep,
+			       dwc_otg_pcd_request_t * req)
+{
+	struct dwc_otg_pcd_ep *ep = cfiep->ep;
+	ddma_sg_buffer_setup_t *sgval = cfiep->bm_sg;
+	struct dwc_otg_dma_desc *desc = cfiep->ep->dwc_ep.descs;
+	struct dwc_otg_dma_desc *desc_last = cfiep->ep->dwc_ep.descs;
+	dma_addr_t buff_addr = req->dma;
+	int i;
+	uint32_t txsize, off;
+
+	txsize = sgval->wSize;
+	off = sgval->bOffset;
+
+//      CFI_INFO("%s: %s TXSIZE=0x%08x; OFFSET=0x%08x\n",
+//              __func__, cfiep->ep->ep.name, txsize, off);
+
+	for (i = 0; i < sgval->bCount; i++) {
+		desc->status.b.bs = BS_HOST_BUSY;
+		desc->buf = buff_addr;
+		desc->status.b.l = 0;
+		desc->status.b.ioc = 0;
+		desc->status.b.sp = 0;
+		desc->status.b.bytes = txsize;
+		desc->status.b.bs = BS_HOST_READY;
+
+		/* Set the next address of the buffer */
+		buff_addr += txsize + off;
+		desc_last = desc;
+		desc++;
+	}
+
+	/* Set the last, ioc and sp bits on the Last DMA Descriptor */
+	desc_last->status.b.l = 1;
+	desc_last->status.b.ioc = 1;
+	desc_last->status.b.sp = ep->dwc_ep.sent_zlp;
+	/* Save the last DMA descriptor pointer */
+	cfiep->dma_desc_last = desc_last;
+	cfiep->desc_count = sgval->bCount;
+}
+
+/**
+ * This function builds the DMA descriptors for the Concatenation buffer mode.
+ */
+static void cfi_build_concat_descs(struct cfiobject *cfi, cfi_ep_t * cfiep,
+				   dwc_otg_pcd_request_t * req)
+{
+	struct dwc_otg_pcd_ep *ep = cfiep->ep;
+	ddma_concat_buffer_setup_t *concatval = cfiep->bm_concat;
+	struct dwc_otg_dma_desc *desc = cfiep->ep->dwc_ep.descs;
+	struct dwc_otg_dma_desc *desc_last = cfiep->ep->dwc_ep.descs;
+	dma_addr_t buff_addr = req->dma;
+	int i;
+	uint16_t *txsize;
+
+	txsize = concatval->wTxBytes;
+
+	for (i = 0; i < concatval->hdr.bDescCount; i++) {
+		desc->buf = buff_addr;
+		desc->status.b.bs = BS_HOST_BUSY;
+		desc->status.b.l = 0;
+		desc->status.b.ioc = 0;
+		desc->status.b.sp = 0;
+		desc->status.b.bytes = *txsize;
+		desc->status.b.bs = BS_HOST_READY;
+
+		txsize++;
+		/* Set the next address of the buffer */
+		buff_addr += UGETW(ep->desc->wMaxPacketSize);
+		desc_last = desc;
+		desc++;
+	}
+
+	/* Set the last, ioc and sp bits on the Last DMA Descriptor */
+	desc_last->status.b.l = 1;
+	desc_last->status.b.ioc = 1;
+	desc_last->status.b.sp = ep->dwc_ep.sent_zlp;
+	cfiep->dma_desc_last = desc_last;
+	cfiep->desc_count = concatval->hdr.bDescCount;
+}
+
+/**
+ * This function builds the DMA descriptors for the Circular buffer mode
+ */
+static void cfi_build_circ_descs(struct cfiobject *cfi, cfi_ep_t * cfiep,
+				 dwc_otg_pcd_request_t * req)
+{
+	/* @todo: MAS - add implementation when this feature needs to be tested */
+}
+
+/**
+ * This function builds the DMA descriptors for the Alignment buffer mode
+ */
+static void cfi_build_align_descs(struct cfiobject *cfi, cfi_ep_t * cfiep,
+				  dwc_otg_pcd_request_t * req)
+{
+	struct dwc_otg_pcd_ep *ep = cfiep->ep;
+	ddma_align_buffer_setup_t *alignval = cfiep->bm_align;
+	struct dwc_otg_dma_desc *desc = cfiep->ep->dwc_ep.descs;
+	dma_addr_t buff_addr = req->dma;
+
+	desc->status.b.bs = BS_HOST_BUSY;
+	desc->status.b.l = 1;
+	desc->status.b.ioc = 1;
+	desc->status.b.sp = ep->dwc_ep.sent_zlp;
+	desc->status.b.bytes = req->length;
+	/* Adjust the buffer alignment */
+	desc->buf = (buff_addr + alignval->bAlign);
+	desc->status.b.bs = BS_HOST_READY;
+	cfiep->dma_desc_last = desc;
+	cfiep->desc_count = 1;
+}
+
+/**
+ * This function builds the DMA descriptors chain for different modes of the
+ * buffer setup of an endpoint.
+ */
+static void cfi_build_descriptors(struct cfiobject *cfi,
+				  struct dwc_otg_pcd *pcd,
+				  struct dwc_otg_pcd_ep *ep,
+				  dwc_otg_pcd_request_t * req)
+{
+	cfi_ep_t *cfiep;
+
+	/* Get the cfiep by the dwc_otg_pcd_ep */
+	cfiep = get_cfi_ep_by_pcd_ep(cfi, ep);
+	if (NULL == cfiep) {
+		CFI_INFO("%s: Unable to find a matching active endpoint\n",
+			 __func__);
+		return;
+	}
+
+	cfiep->xfer_len = req->length;
+
+	/* Iterate through all the DMA descriptors */
+	switch (cfiep->ep->dwc_ep.buff_mode) {
+	case BM_SG:
+		cfi_build_sg_descs(cfi, cfiep, req);
+		break;
+
+	case BM_CONCAT:
+		cfi_build_concat_descs(cfi, cfiep, req);
+		break;
+
+	case BM_CIRCULAR:
+		cfi_build_circ_descs(cfi, cfiep, req);
+		break;
+
+	case BM_ALIGN:
+		cfi_build_align_descs(cfi, cfiep, req);
+		break;
+
+	default:
+		break;
+	}
+}
+
+/**
+ * Allocate DMA buffer for different Buffer modes.
+ */
+static void *cfi_ep_alloc_buf(struct cfiobject *cfi, struct dwc_otg_pcd *pcd,
+			      struct dwc_otg_pcd_ep *ep, dma_addr_t * dma,
+			      unsigned size, gfp_t flags)
+{
+	return DWC_DMA_ALLOC(size, dma);
+}
+
+/**
+ * This function initializes the CFI object.
+ */
+int init_cfi(cfiobject_t * cfiobj)
+{
+	CFI_INFO("%s\n", __func__);
+
+	/* Allocate a buffer for IN XFERs */
+	cfiobj->buf_in.buf =
+	    DWC_DMA_ALLOC(CFI_IN_BUF_LEN, &cfiobj->buf_in.addr);
+	if (NULL == cfiobj->buf_in.buf) {
+		CFI_INFO("Unable to allocate buffer for INs\n");
+		return -DWC_E_NO_MEMORY;
+	}
+
+	/* Allocate a buffer for OUT XFERs */
+	cfiobj->buf_out.buf =
+	    DWC_DMA_ALLOC(CFI_OUT_BUF_LEN, &cfiobj->buf_out.addr);
+	if (NULL == cfiobj->buf_out.buf) {
+		CFI_INFO("Unable to allocate buffer for OUT\n");
+		return -DWC_E_NO_MEMORY;
+	}
+
+	/* Initialize the callback function pointers */
+	cfiobj->ops.release = cfi_release;
+	cfiobj->ops.ep_enable = cfi_ep_enable;
+	cfiobj->ops.ctrl_write_complete = cfi_ctrl_write_complete;
+	cfiobj->ops.build_descriptors = cfi_build_descriptors;
+	cfiobj->ops.ep_alloc_buf = cfi_ep_alloc_buf;
+
+	/* Initialize the list of active endpoints in the CFI object */
+	DWC_LIST_INIT(&cfiobj->active_eps);
+
+	return 0;
+}
+
+/**
+ * This function reads the required feature's current value into the buffer
+ *
+ * @retval: Returns negative as error, or the data length of the feature
+ */
+static int cfi_get_feature_value(uint8_t * buf, uint16_t buflen,
+				 struct dwc_otg_pcd *pcd,
+				 struct cfi_usb_ctrlrequest *ctrl_req)
+{
+	int retval = -DWC_E_NOT_SUPPORTED;
+	struct dwc_otg_core_if *coreif = GET_CORE_IF(pcd);
+	uint16_t dfifo, rxfifo, txfifo;
+
+	switch (ctrl_req->wIndex) {
+		/* Whether the DDMA is enabled or not */
+	case FT_ID_DMA_MODE:
+		*buf = (coreif->dma_enable && coreif->dma_desc_enable) ? 1 : 0;
+		retval = 1;
+		break;
+
+	case FT_ID_DMA_BUFFER_SETUP:
+		retval = cfi_ep_get_sg_val(buf, pcd, ctrl_req);
+		break;
+
+	case FT_ID_DMA_BUFF_ALIGN:
+		retval = cfi_ep_get_align_val(buf, pcd, ctrl_req);
+		break;
+
+	case FT_ID_DMA_CONCAT_SETUP:
+		retval = cfi_ep_get_concat_val(buf, pcd, ctrl_req);
+		break;
+
+	case FT_ID_DMA_CIRCULAR:
+		CFI_INFO("GetFeature value (FT_ID_DMA_CIRCULAR)\n");
+		break;
+
+	case FT_ID_THRESHOLD_SETUP:
+		CFI_INFO("GetFeature value (FT_ID_THRESHOLD_SETUP)\n");
+		break;
+
+	case FT_ID_DFIFO_DEPTH:
+		dfifo = get_dfifo_size(coreif);
+		*((uint16_t *) buf) = dfifo;
+		retval = sizeof(uint16_t);
+		break;
+
+	case FT_ID_TX_FIFO_DEPTH:
+		retval = get_txfifo_size(pcd, ctrl_req->wValue);
+		if (retval >= 0) {
+			txfifo = retval;
+			*((uint16_t *) buf) = txfifo;
+			retval = sizeof(uint16_t);
+		}
+		break;
+
+	case FT_ID_RX_FIFO_DEPTH:
+		retval = get_rxfifo_size(coreif, ctrl_req->wValue);
+		if (retval >= 0) {
+			rxfifo = retval;
+			*((uint16_t *) buf) = rxfifo;
+			retval = sizeof(uint16_t);
+		}
+		break;
+	}
+
+	return retval;
+}
+
+/**
+ * This function resets the SG for the specified EP to its default value
+ */
+static int cfi_reset_sg_val(cfi_ep_t * cfiep)
+{
+	dwc_memset(cfiep->bm_sg, 0, sizeof(ddma_sg_buffer_setup_t));
+	return 0;
+}
+
+/**
+ * This function resets the Alignment for the specified EP to its default value
+ */
+static int cfi_reset_align_val(cfi_ep_t * cfiep)
+{
+	dwc_memset(cfiep->bm_sg, 0, sizeof(ddma_sg_buffer_setup_t));
+	return 0;
+}
+
+/**
+ * This function resets the Concatenation for the specified EP to its default value
+ * This function will also set the value of the wTxBytes field to NULL after
+ * freeing the memory previously allocated for this field.
+ */
+static int cfi_reset_concat_val(cfi_ep_t * cfiep)
+{
+	/* First we need to free the wTxBytes field */
+	if (cfiep->bm_concat->wTxBytes) {
+		DWC_FREE(cfiep->bm_concat->wTxBytes);
+		cfiep->bm_concat->wTxBytes = NULL;
+	}
+
+	dwc_memset(cfiep->bm_concat, 0, sizeof(ddma_concat_buffer_setup_t));
+	return 0;
+}
+
+/**
+ * This function resets all the buffer setups of the specified endpoint
+ */
+static int cfi_ep_reset_all_setup_vals(cfi_ep_t * cfiep)
+{
+	cfi_reset_sg_val(cfiep);
+	cfi_reset_align_val(cfiep);
+	cfi_reset_concat_val(cfiep);
+	return 0;
+}
+
+static int cfi_handle_reset_fifo_val(struct dwc_otg_pcd *pcd, uint8_t ep_addr,
+				     uint8_t rx_rst, uint8_t tx_rst)
+{
+	int retval = -DWC_E_INVALID;
+	uint16_t tx_siz[15];
+	uint16_t rx_siz = 0;
+	dwc_otg_pcd_ep_t *ep = NULL;
+	dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd);
+	dwc_otg_core_params_t *params = GET_CORE_IF(pcd)->core_params;
+
+	if (rx_rst) {
+		rx_siz = params->dev_rx_fifo_size;
+		params->dev_rx_fifo_size = GET_CORE_IF(pcd)->init_rxfsiz;
+	}
+
+	if (tx_rst) {
+		if (ep_addr == 0) {
+			int i;
+
+			for (i = 0; i < core_if->hwcfg4.b.num_in_eps; i++) {
+				tx_siz[i] =
+				    core_if->core_params->dev_tx_fifo_size[i];
+				core_if->core_params->dev_tx_fifo_size[i] =
+				    core_if->init_txfsiz[i];
+			}
+		} else {
+
+			ep = get_ep_by_addr(pcd, ep_addr);
+
+			if (NULL == ep) {
+				CFI_INFO
+				    ("%s: Unable to get the endpoint addr=0x%02x\n",
+				     __func__, ep_addr);
+				return -DWC_E_INVALID;
+			}
+
+			tx_siz[0] =
+			    params->dev_tx_fifo_size[ep->dwc_ep.tx_fifo_num -
+						     1];
+			params->dev_tx_fifo_size[ep->dwc_ep.tx_fifo_num - 1] =
+			    GET_CORE_IF(pcd)->init_txfsiz[ep->
+							  dwc_ep.tx_fifo_num -
+							  1];
+		}
+	}
+
+	if (resize_fifos(GET_CORE_IF(pcd))) {
+		retval = 0;
+	} else {
+		CFI_INFO
+		    ("%s: Error resetting the feature Reset All(FIFO size)\n",
+		     __func__);
+		if (rx_rst) {
+			params->dev_rx_fifo_size = rx_siz;
+		}
+
+		if (tx_rst) {
+			if (ep_addr == 0) {
+				int i;
+				for (i = 0; i < core_if->hwcfg4.b.num_in_eps;
+				     i++) {
+					core_if->
+					    core_params->dev_tx_fifo_size[i] =
+					    tx_siz[i];
+				}
+			} else {
+				params->dev_tx_fifo_size[ep->
+							 dwc_ep.tx_fifo_num -
+							 1] = tx_siz[0];
+			}
+		}
+		retval = -DWC_E_INVALID;
+	}
+	return retval;
+}
+
+static int cfi_handle_reset_all(struct dwc_otg_pcd *pcd, uint8_t addr)
+{
+	int retval = 0;
+	cfi_ep_t *cfiep;
+	cfiobject_t *cfi = pcd->cfi;
+	dwc_list_link_t *tmp;
+
+	retval = cfi_handle_reset_fifo_val(pcd, addr, 1, 1);
+	if (retval < 0) {
+		return retval;
+	}
+
+	/* If the EP address is known then reset the features for only that EP */
+	if (addr) {
+		cfiep = get_cfi_ep_by_addr(pcd->cfi, addr);
+		if (NULL == cfiep) {
+			CFI_INFO("%s: Error getting the EP address 0x%02x\n",
+				 __func__, addr);
+			return -DWC_E_INVALID;
+		}
+		retval = cfi_ep_reset_all_setup_vals(cfiep);
+		cfiep->ep->dwc_ep.buff_mode = BM_STANDARD;
+	}
+	/* Otherwise (wValue == 0), reset all features of all EP's */
+	else {
+		/* Traverse all the active EP's and reset the feature(s) value(s) */
+		//list_for_each_entry(cfiep, &cfi->active_eps, lh) {
+		DWC_LIST_FOREACH(tmp, &cfi->active_eps) {
+			cfiep = DWC_LIST_ENTRY(tmp, struct cfi_ep, lh);
+			retval = cfi_ep_reset_all_setup_vals(cfiep);
+			cfiep->ep->dwc_ep.buff_mode = BM_STANDARD;
+			if (retval < 0) {
+				CFI_INFO
+				    ("%s: Error resetting the feature Reset All\n",
+				     __func__);
+				return retval;
+			}
+		}
+	}
+	return retval;
+}
+
+static int cfi_handle_reset_dma_buff_setup(struct dwc_otg_pcd *pcd,
+					   uint8_t addr)
+{
+	int retval = 0;
+	cfi_ep_t *cfiep;
+	cfiobject_t *cfi = pcd->cfi;
+	dwc_list_link_t *tmp;
+
+	/* If the EP address is known then reset the features for only that EP */
+	if (addr) {
+		cfiep = get_cfi_ep_by_addr(pcd->cfi, addr);
+		if (NULL == cfiep) {
+			CFI_INFO("%s: Error getting the EP address 0x%02x\n",
+				 __func__, addr);
+			return -DWC_E_INVALID;
+		}
+		retval = cfi_reset_sg_val(cfiep);
+	}
+	/* Otherwise (wValue == 0), reset all features of all EP's */
+	else {
+		/* Traverse all the active EP's and reset the feature(s) value(s) */
+		//list_for_each_entry(cfiep, &cfi->active_eps, lh) {
+		DWC_LIST_FOREACH(tmp, &cfi->active_eps) {
+			cfiep = DWC_LIST_ENTRY(tmp, struct cfi_ep, lh);
+			retval = cfi_reset_sg_val(cfiep);
+			if (retval < 0) {
+				CFI_INFO
+				    ("%s: Error resetting the feature Buffer Setup\n",
+				     __func__);
+				return retval;
+			}
+		}
+	}
+	return retval;
+}
+
+static int cfi_handle_reset_concat_val(struct dwc_otg_pcd *pcd, uint8_t addr)
+{
+	int retval = 0;
+	cfi_ep_t *cfiep;
+	cfiobject_t *cfi = pcd->cfi;
+	dwc_list_link_t *tmp;
+
+	/* If the EP address is known then reset the features for only that EP */
+	if (addr) {
+		cfiep = get_cfi_ep_by_addr(pcd->cfi, addr);
+		if (NULL == cfiep) {
+			CFI_INFO("%s: Error getting the EP address 0x%02x\n",
+				 __func__, addr);
+			return -DWC_E_INVALID;
+		}
+		retval = cfi_reset_concat_val(cfiep);
+	}
+	/* Otherwise (wValue == 0), reset all features of all EP's */
+	else {
+		/* Traverse all the active EP's and reset the feature(s) value(s) */
+		//list_for_each_entry(cfiep, &cfi->active_eps, lh) {
+		DWC_LIST_FOREACH(tmp, &cfi->active_eps) {
+			cfiep = DWC_LIST_ENTRY(tmp, struct cfi_ep, lh);
+			retval = cfi_reset_concat_val(cfiep);
+			if (retval < 0) {
+				CFI_INFO
+				    ("%s: Error resetting the feature Concatenation Value\n",
+				     __func__);
+				return retval;
+			}
+		}
+	}
+	return retval;
+}
+
+static int cfi_handle_reset_align_val(struct dwc_otg_pcd *pcd, uint8_t addr)
+{
+	int retval = 0;
+	cfi_ep_t *cfiep;
+	cfiobject_t *cfi = pcd->cfi;
+	dwc_list_link_t *tmp;
+
+	/* If the EP address is known then reset the features for only that EP */
+	if (addr) {
+		cfiep = get_cfi_ep_by_addr(pcd->cfi, addr);
+		if (NULL == cfiep) {
+			CFI_INFO("%s: Error getting the EP address 0x%02x\n",
+				 __func__, addr);
+			return -DWC_E_INVALID;
+		}
+		retval = cfi_reset_align_val(cfiep);
+	}
+	/* Otherwise (wValue == 0), reset all features of all EP's */
+	else {
+		/* Traverse all the active EP's and reset the feature(s) value(s) */
+		//list_for_each_entry(cfiep, &cfi->active_eps, lh) {
+		DWC_LIST_FOREACH(tmp, &cfi->active_eps) {
+			cfiep = DWC_LIST_ENTRY(tmp, struct cfi_ep, lh);
+			retval = cfi_reset_align_val(cfiep);
+			if (retval < 0) {
+				CFI_INFO
+				    ("%s: Error resetting the feature Aliignment Value\n",
+				     __func__);
+				return retval;
+			}
+		}
+	}
+	return retval;
+
+}
+
+static int cfi_preproc_reset(struct dwc_otg_pcd *pcd,
+			     struct cfi_usb_ctrlrequest *req)
+{
+	int retval = 0;
+
+	switch (req->wIndex) {
+	case 0:
+		/* Reset all features */
+		retval = cfi_handle_reset_all(pcd, req->wValue & 0xff);
+		break;
+
+	case FT_ID_DMA_BUFFER_SETUP:
+		/* Reset the SG buffer setup */
+		retval =
+		    cfi_handle_reset_dma_buff_setup(pcd, req->wValue & 0xff);
+		break;
+
+	case FT_ID_DMA_CONCAT_SETUP:
+		/* Reset the Concatenation buffer setup */
+		retval = cfi_handle_reset_concat_val(pcd, req->wValue & 0xff);
+		break;
+
+	case FT_ID_DMA_BUFF_ALIGN:
+		/* Reset the Alignment buffer setup */
+		retval = cfi_handle_reset_align_val(pcd, req->wValue & 0xff);
+		break;
+
+	case FT_ID_TX_FIFO_DEPTH:
+		retval =
+		    cfi_handle_reset_fifo_val(pcd, req->wValue & 0xff, 0, 1);
+		pcd->cfi->need_gadget_att = 0;
+		break;
+
+	case FT_ID_RX_FIFO_DEPTH:
+		retval = cfi_handle_reset_fifo_val(pcd, 0, 1, 0);
+		pcd->cfi->need_gadget_att = 0;
+		break;
+	default:
+		break;
+	}
+	return retval;
+}
+
+/**
+ * This function sets a new value for the SG buffer setup.
+ */
+static int cfi_ep_set_sg_val(uint8_t * buf, struct dwc_otg_pcd *pcd)
+{
+	uint8_t inaddr, outaddr;
+	cfi_ep_t *epin, *epout;
+	ddma_sg_buffer_setup_t *psgval;
+	uint32_t desccount, size;
+
+	CFI_INFO("%s\n", __func__);
+
+	psgval = (ddma_sg_buffer_setup_t *) buf;
+	desccount = (uint32_t) psgval->bCount;
+	size = (uint32_t) psgval->wSize;
+
+	/* Check the DMA descriptor count */
+	if ((desccount > MAX_DMA_DESCS_PER_EP) || (desccount == 0)) {
+		CFI_INFO
+		    ("%s: The count of DMA Descriptors should be between 1 and %d\n",
+		     __func__, MAX_DMA_DESCS_PER_EP);
+		return -DWC_E_INVALID;
+	}
+
+	/* Check the DMA descriptor count */
+
+	if (size == 0) {
+
+		CFI_INFO("%s: The transfer size should be at least 1 byte\n",
+			 __func__);
+
+		return -DWC_E_INVALID;
+
+	}
+
+	inaddr = psgval->bInEndpointAddress;
+	outaddr = psgval->bOutEndpointAddress;
+
+	epin = get_cfi_ep_by_addr(pcd->cfi, inaddr);
+	epout = get_cfi_ep_by_addr(pcd->cfi, outaddr);
+
+	if (NULL == epin || NULL == epout) {
+		CFI_INFO
+		    ("%s: Unable to get the endpoints inaddr=0x%02x outaddr=0x%02x\n",
+		     __func__, inaddr, outaddr);
+		return -DWC_E_INVALID;
+	}
+
+	epin->ep->dwc_ep.buff_mode = BM_SG;
+	dwc_memcpy(epin->bm_sg, psgval, sizeof(ddma_sg_buffer_setup_t));
+
+	epout->ep->dwc_ep.buff_mode = BM_SG;
+	dwc_memcpy(epout->bm_sg, psgval, sizeof(ddma_sg_buffer_setup_t));
+
+	return 0;
+}
+
+/**
+ * This function sets a new value for the buffer Alignment setup.
+ */
+static int cfi_ep_set_alignment_val(uint8_t * buf, struct dwc_otg_pcd *pcd)
+{
+	cfi_ep_t *ep;
+	uint8_t addr;
+	ddma_align_buffer_setup_t *palignval;
+
+	palignval = (ddma_align_buffer_setup_t *) buf;
+	addr = palignval->bEndpointAddress;
+
+	ep = get_cfi_ep_by_addr(pcd->cfi, addr);
+
+	if (NULL == ep) {
+		CFI_INFO("%s: Unable to get the endpoint addr=0x%02x\n",
+			 __func__, addr);
+		return -DWC_E_INVALID;
+	}
+
+	ep->ep->dwc_ep.buff_mode = BM_ALIGN;
+	dwc_memcpy(ep->bm_align, palignval, sizeof(ddma_align_buffer_setup_t));
+
+	return 0;
+}
+
+/**
+ * This function sets a new value for the Concatenation buffer setup.
+ */
+static int cfi_ep_set_concat_val(uint8_t * buf, struct dwc_otg_pcd *pcd)
+{
+	uint8_t addr;
+	cfi_ep_t *ep;
+	struct _ddma_concat_buffer_setup_hdr *pConcatValHdr;
+	uint16_t *pVals;
+	uint32_t desccount;
+	int i;
+	uint16_t mps;
+
+	pConcatValHdr = (struct _ddma_concat_buffer_setup_hdr *)buf;
+	desccount = (uint32_t) pConcatValHdr->bDescCount;
+	pVals = (uint16_t *) (buf + BS_CONCAT_VAL_HDR_LEN);
+
+	/* Check the DMA descriptor count */
+	if (desccount > MAX_DMA_DESCS_PER_EP) {
+		CFI_INFO("%s: Maximum DMA Descriptor count should be %d\n",
+			 __func__, MAX_DMA_DESCS_PER_EP);
+		return -DWC_E_INVALID;
+	}
+
+	addr = pConcatValHdr->bEndpointAddress;
+	ep = get_cfi_ep_by_addr(pcd->cfi, addr);
+	if (NULL == ep) {
+		CFI_INFO("%s: Unable to get the endpoint addr=0x%02x\n",
+			 __func__, addr);
+		return -DWC_E_INVALID;
+	}
+
+	mps = UGETW(ep->ep->desc->wMaxPacketSize);
+
+#if 0
+	for (i = 0; i < desccount; i++) {
+		CFI_INFO("%s: wTxSize[%d]=0x%04x\n", __func__, i, pVals[i]);
+	}
+	CFI_INFO("%s: epname=%s; mps=%d\n", __func__, ep->ep->ep.name, mps);
+#endif
+
+	/* Check the wTxSizes to be less than or equal to the mps */
+	for (i = 0; i < desccount; i++) {
+		if (pVals[i] > mps) {
+			CFI_INFO
+			    ("%s: ERROR - the wTxSize[%d] should be <= MPS (wTxSize=%d)\n",
+			     __func__, i, pVals[i]);
+			return -DWC_E_INVALID;
+		}
+	}
+
+	ep->ep->dwc_ep.buff_mode = BM_CONCAT;
+	dwc_memcpy(ep->bm_concat, pConcatValHdr, BS_CONCAT_VAL_HDR_LEN);
+
+	/* Free the previously allocated storage for the wTxBytes */
+	if (ep->bm_concat->wTxBytes) {
+		DWC_FREE(ep->bm_concat->wTxBytes);
+	}
+
+	/* Allocate a new storage for the wTxBytes field */
+	ep->bm_concat->wTxBytes =
+	    DWC_ALLOC(sizeof(uint16_t) * pConcatValHdr->bDescCount);
+	if (NULL == ep->bm_concat->wTxBytes) {
+		CFI_INFO("%s: Unable to allocate memory\n", __func__);
+		return -DWC_E_NO_MEMORY;
+	}
+
+	/* Copy the new values into the wTxBytes filed */
+	dwc_memcpy(ep->bm_concat->wTxBytes, buf + BS_CONCAT_VAL_HDR_LEN,
+		   sizeof(uint16_t) * pConcatValHdr->bDescCount);
+
+	return 0;
+}
+
+/**
+ * This function calculates the total of all FIFO sizes
+ *
+ * @param core_if Programming view of DWC_otg controller
+ *
+ * @return The total of data FIFO sizes.
+ *
+ */
+static uint16_t get_dfifo_size(dwc_otg_core_if_t * core_if)
+{
+	dwc_otg_core_params_t *params = core_if->core_params;
+	uint16_t dfifo_total = 0;
+	int i;
+
+	/* The shared RxFIFO size */
+	dfifo_total =
+	    params->dev_rx_fifo_size + params->dev_nperio_tx_fifo_size;
+
+	/* Add up each TxFIFO size to the total */
+	for (i = 0; i < core_if->hwcfg4.b.num_in_eps; i++) {
+		dfifo_total += params->dev_tx_fifo_size[i];
+	}
+
+	return dfifo_total;
+}
+
+/**
+ * This function returns Rx FIFO size
+ *
+ * @param core_if Programming view of DWC_otg controller
+ *
+ * @return The total of data FIFO sizes.
+ *
+ */
+static int32_t get_rxfifo_size(dwc_otg_core_if_t * core_if, uint16_t wValue)
+{
+	switch (wValue >> 8) {
+	case 0:
+		return (core_if->pwron_rxfsiz <
+			32768) ? core_if->pwron_rxfsiz : 32768;
+		break;
+	case 1:
+		return core_if->core_params->dev_rx_fifo_size;
+		break;
+	default:
+		return -DWC_E_INVALID;
+		break;
+	}
+}
+
+/**
+ * This function returns Tx FIFO size for IN EP
+ *
+ * @param core_if Programming view of DWC_otg controller
+ *
+ * @return The total of data FIFO sizes.
+ *
+ */
+static int32_t get_txfifo_size(struct dwc_otg_pcd *pcd, uint16_t wValue)
+{
+	dwc_otg_pcd_ep_t *ep;
+
+	ep = get_ep_by_addr(pcd, wValue & 0xff);
+
+	if (NULL == ep) {
+		CFI_INFO("%s: Unable to get the endpoint addr=0x%02x\n",
+			 __func__, wValue & 0xff);
+		return -DWC_E_INVALID;
+	}
+
+	if (!ep->dwc_ep.is_in) {
+		CFI_INFO
+		    ("%s: No Tx FIFO assingned to the Out endpoint addr=0x%02x\n",
+		     __func__, wValue & 0xff);
+		return -DWC_E_INVALID;
+	}
+
+	switch (wValue >> 8) {
+	case 0:
+		return (GET_CORE_IF(pcd)->pwron_txfsiz
+			[ep->dwc_ep.tx_fifo_num - 1] <
+			768) ? GET_CORE_IF(pcd)->pwron_txfsiz[ep->
+							      dwc_ep.tx_fifo_num
+							      - 1] : 32768;
+		break;
+	case 1:
+		return GET_CORE_IF(pcd)->core_params->
+		    dev_tx_fifo_size[ep->dwc_ep.num - 1];
+		break;
+	default:
+		return -DWC_E_INVALID;
+		break;
+	}
+}
+
+/**
+ * This function checks if the submitted combination of
+ * device mode FIFO sizes is possible or not.
+ *
+ * @param core_if Programming view of DWC_otg controller
+ *
+ * @return 1 if possible, 0 otherwise.
+ *
+ */
+static uint8_t check_fifo_sizes(dwc_otg_core_if_t * core_if)
+{
+	uint16_t dfifo_actual = 0;
+	dwc_otg_core_params_t *params = core_if->core_params;
+	uint16_t start_addr = 0;
+	int i;
+
+	dfifo_actual =
+	    params->dev_rx_fifo_size + params->dev_nperio_tx_fifo_size;
+
+	for (i = 0; i < core_if->hwcfg4.b.num_in_eps; i++) {
+		dfifo_actual += params->dev_tx_fifo_size[i];
+	}
+
+	if (dfifo_actual > core_if->total_fifo_size) {
+		return 0;
+	}
+
+	if (params->dev_rx_fifo_size > 32768 || params->dev_rx_fifo_size < 16)
+		return 0;
+
+	if (params->dev_nperio_tx_fifo_size > 32768
+	    || params->dev_nperio_tx_fifo_size < 16)
+		return 0;
+
+	for (i = 0; i < core_if->hwcfg4.b.num_in_eps; i++) {
+
+		if (params->dev_tx_fifo_size[i] > 768
+		    || params->dev_tx_fifo_size[i] < 4)
+			return 0;
+	}
+
+	if (params->dev_rx_fifo_size > core_if->pwron_rxfsiz)
+		return 0;
+	start_addr = params->dev_rx_fifo_size;
+
+	if (params->dev_nperio_tx_fifo_size > core_if->pwron_gnptxfsiz)
+		return 0;
+	start_addr += params->dev_nperio_tx_fifo_size;
+
+	for (i = 0; i < core_if->hwcfg4.b.num_in_eps; i++) {
+
+		if (params->dev_tx_fifo_size[i] > core_if->pwron_txfsiz[i])
+			return 0;
+		start_addr += params->dev_tx_fifo_size[i];
+	}
+
+	return 1;
+}
+
+/**
+ * This function resizes Device mode FIFOs
+ *
+ * @param core_if Programming view of DWC_otg controller
+ *
+ * @return 1 if successful, 0 otherwise
+ *
+ */
+static uint8_t resize_fifos(dwc_otg_core_if_t * core_if)
+{
+	int i = 0;
+	dwc_otg_core_global_regs_t *global_regs = core_if->core_global_regs;
+	dwc_otg_core_params_t *params = core_if->core_params;
+	uint32_t rx_fifo_size;
+	fifosize_data_t nptxfifosize;
+	fifosize_data_t txfifosize[15];
+
+	uint32_t rx_fsz_bak;
+	uint32_t nptxfsz_bak;
+	uint32_t txfsz_bak[15];
+
+	uint16_t start_address;
+	uint8_t retval = 1;
+
+	if (!check_fifo_sizes(core_if)) {
+		return 0;
+	}
+
+	/* Configure data FIFO sizes */
+	if (core_if->hwcfg2.b.dynamic_fifo && params->enable_dynamic_fifo) {
+		rx_fsz_bak = DWC_READ_REG32(&global_regs->grxfsiz);
+		rx_fifo_size = params->dev_rx_fifo_size;
+		DWC_WRITE_REG32(&global_regs->grxfsiz, rx_fifo_size);
+
+		/*
+		 * Tx FIFOs These FIFOs are numbered from 1 to 15.
+		 * Indexes of the FIFO size module parameters in the
+		 * dev_tx_fifo_size array and the FIFO size registers in
+		 * the dtxfsiz array run from 0 to 14.
+		 */
+
+		/* Non-periodic Tx FIFO */
+		nptxfsz_bak = DWC_READ_REG32(&global_regs->gnptxfsiz);
+		nptxfifosize.b.depth = params->dev_nperio_tx_fifo_size;
+		start_address = params->dev_rx_fifo_size;
+		nptxfifosize.b.startaddr = start_address;
+
+		DWC_WRITE_REG32(&global_regs->gnptxfsiz, nptxfifosize.d32);
+
+		start_address += nptxfifosize.b.depth;
+
+		for (i = 0; i < core_if->hwcfg4.b.num_in_eps; i++) {
+			txfsz_bak[i] = DWC_READ_REG32(&global_regs->dtxfsiz[i]);
+
+			txfifosize[i].b.depth = params->dev_tx_fifo_size[i];
+			txfifosize[i].b.startaddr = start_address;
+			DWC_WRITE_REG32(&global_regs->dtxfsiz[i],
+					txfifosize[i].d32);
+
+			start_address += txfifosize[i].b.depth;
+		}
+
+		/** Check if register values are set correctly */
+		if (rx_fifo_size != DWC_READ_REG32(&global_regs->grxfsiz)) {
+			retval = 0;
+		}
+
+		if (nptxfifosize.d32 != DWC_READ_REG32(&global_regs->gnptxfsiz)) {
+			retval = 0;
+		}
+
+		for (i = 0; i < core_if->hwcfg4.b.num_in_eps; i++) {
+			if (txfifosize[i].d32 !=
+			    DWC_READ_REG32(&global_regs->dtxfsiz[i])) {
+				retval = 0;
+			}
+		}
+
+		/** If register values are not set correctly, reset old values */
+		if (retval == 0) {
+			DWC_WRITE_REG32(&global_regs->grxfsiz, rx_fsz_bak);
+
+			/* Non-periodic Tx FIFO */
+			DWC_WRITE_REG32(&global_regs->gnptxfsiz, nptxfsz_bak);
+
+			for (i = 0; i < core_if->hwcfg4.b.num_in_eps; i++) {
+				DWC_WRITE_REG32(&global_regs->dtxfsiz[i],
+						txfsz_bak[i]);
+			}
+		}
+	} else {
+		return 0;
+	}
+
+	/* Flush the FIFOs */
+	dwc_otg_flush_tx_fifo(core_if, 0x10);	/* all Tx FIFOs */
+	dwc_otg_flush_rx_fifo(core_if);
+
+	return retval;
+}
+
+/**
+ * This function sets a new value for the buffer Alignment setup.
+ */
+static int cfi_ep_set_tx_fifo_val(uint8_t * buf, dwc_otg_pcd_t * pcd)
+{
+	int retval;
+	uint32_t fsiz;
+	uint16_t size;
+	uint16_t ep_addr;
+	dwc_otg_pcd_ep_t *ep;
+	dwc_otg_core_params_t *params = GET_CORE_IF(pcd)->core_params;
+	tx_fifo_size_setup_t *ptxfifoval;
+
+	ptxfifoval = (tx_fifo_size_setup_t *) buf;
+	ep_addr = ptxfifoval->bEndpointAddress;
+	size = ptxfifoval->wDepth;
+
+	ep = get_ep_by_addr(pcd, ep_addr);
+
+	CFI_INFO
+	    ("%s: Set Tx FIFO size: endpoint addr=0x%02x, depth=%d, FIFO Num=%d\n",
+	     __func__, ep_addr, size, ep->dwc_ep.tx_fifo_num);
+
+	if (NULL == ep) {
+		CFI_INFO("%s: Unable to get the endpoint addr=0x%02x\n",
+			 __func__, ep_addr);
+		return -DWC_E_INVALID;
+	}
+
+	fsiz = params->dev_tx_fifo_size[ep->dwc_ep.tx_fifo_num - 1];
+	params->dev_tx_fifo_size[ep->dwc_ep.tx_fifo_num - 1] = size;
+
+	if (resize_fifos(GET_CORE_IF(pcd))) {
+		retval = 0;
+	} else {
+		CFI_INFO
+		    ("%s: Error setting the feature Tx FIFO Size for EP%d\n",
+		     __func__, ep_addr);
+		params->dev_tx_fifo_size[ep->dwc_ep.tx_fifo_num - 1] = fsiz;
+		retval = -DWC_E_INVALID;
+	}
+
+	return retval;
+}
+
+/**
+ * This function sets a new value for the buffer Alignment setup.
+ */
+static int cfi_set_rx_fifo_val(uint8_t * buf, dwc_otg_pcd_t * pcd)
+{
+	int retval;
+	uint32_t fsiz;
+	uint16_t size;
+	dwc_otg_core_params_t *params = GET_CORE_IF(pcd)->core_params;
+	rx_fifo_size_setup_t *prxfifoval;
+
+	prxfifoval = (rx_fifo_size_setup_t *) buf;
+	size = prxfifoval->wDepth;
+
+	fsiz = params->dev_rx_fifo_size;
+	params->dev_rx_fifo_size = size;
+
+	if (resize_fifos(GET_CORE_IF(pcd))) {
+		retval = 0;
+	} else {
+		CFI_INFO("%s: Error setting the feature Rx FIFO Size\n",
+			 __func__);
+		params->dev_rx_fifo_size = fsiz;
+		retval = -DWC_E_INVALID;
+	}
+
+	return retval;
+}
+
+/**
+ * This function reads the SG of an EP's buffer setup into the buffer buf
+ */
+static int cfi_ep_get_sg_val(uint8_t * buf, struct dwc_otg_pcd *pcd,
+			     struct cfi_usb_ctrlrequest *req)
+{
+	int retval = -DWC_E_INVALID;
+	uint8_t addr;
+	cfi_ep_t *ep;
+
+	/* The Low Byte of the wValue contains a non-zero address of the endpoint */
+	addr = req->wValue & 0xFF;
+	if (addr == 0)		/* The address should be non-zero */
+		return retval;
+
+	ep = get_cfi_ep_by_addr(pcd->cfi, addr);
+	if (NULL == ep) {
+		CFI_INFO("%s: Unable to get the endpoint address(0x%02x)\n",
+			 __func__, addr);
+		return retval;
+	}
+
+	dwc_memcpy(buf, ep->bm_sg, BS_SG_VAL_DESC_LEN);
+	retval = BS_SG_VAL_DESC_LEN;
+	return retval;
+}
+
+/**
+ * This function reads the Concatenation value of an EP's buffer mode into
+ * the buffer buf
+ */
+static int cfi_ep_get_concat_val(uint8_t * buf, struct dwc_otg_pcd *pcd,
+				 struct cfi_usb_ctrlrequest *req)
+{
+	int retval = -DWC_E_INVALID;
+	uint8_t addr;
+	cfi_ep_t *ep;
+	uint8_t desc_count;
+
+	/* The Low Byte of the wValue contains a non-zero address of the endpoint */
+	addr = req->wValue & 0xFF;
+	if (addr == 0)		/* The address should be non-zero */
+		return retval;
+
+	ep = get_cfi_ep_by_addr(pcd->cfi, addr);
+	if (NULL == ep) {
+		CFI_INFO("%s: Unable to get the endpoint address(0x%02x)\n",
+			 __func__, addr);
+		return retval;
+	}
+
+	/* Copy the header to the buffer */
+	dwc_memcpy(buf, ep->bm_concat, BS_CONCAT_VAL_HDR_LEN);
+	/* Advance the buffer pointer by the header size */
+	buf += BS_CONCAT_VAL_HDR_LEN;
+
+	desc_count = ep->bm_concat->hdr.bDescCount;
+	/* Copy alll the wTxBytes to the buffer */
+	dwc_memcpy(buf, ep->bm_concat->wTxBytes, sizeof(uid16_t) * desc_count);
+
+	retval = BS_CONCAT_VAL_HDR_LEN + sizeof(uid16_t) * desc_count;
+	return retval;
+}
+
+/**
+ * This function reads the buffer Alignment value of an EP's buffer mode into
+ * the buffer buf
+ *
+ * @return The total number of bytes copied to the buffer or negative error code.
+ */
+static int cfi_ep_get_align_val(uint8_t * buf, struct dwc_otg_pcd *pcd,
+				struct cfi_usb_ctrlrequest *req)
+{
+	int retval = -DWC_E_INVALID;
+	uint8_t addr;
+	cfi_ep_t *ep;
+
+	/* The Low Byte of the wValue contains a non-zero address of the endpoint */
+	addr = req->wValue & 0xFF;
+	if (addr == 0)		/* The address should be non-zero */
+		return retval;
+
+	ep = get_cfi_ep_by_addr(pcd->cfi, addr);
+	if (NULL == ep) {
+		CFI_INFO("%s: Unable to get the endpoint address(0x%02x)\n",
+			 __func__, addr);
+		return retval;
+	}
+
+	dwc_memcpy(buf, ep->bm_align, BS_ALIGN_VAL_HDR_LEN);
+	retval = BS_ALIGN_VAL_HDR_LEN;
+
+	return retval;
+}
+
+/**
+ * This function sets a new value for the specified feature
+ *
+ * @param	pcd	A pointer to the PCD object
+ *
+ * @return 0 if successful, negative error code otherwise to stall the DCE.
+ */
+static int cfi_set_feature_value(struct dwc_otg_pcd *pcd)
+{
+	int retval = -DWC_E_NOT_SUPPORTED;
+	uint16_t wIndex, wValue;
+	uint8_t bRequest;
+	struct dwc_otg_core_if *coreif;
+	cfiobject_t *cfi = pcd->cfi;
+	struct cfi_usb_ctrlrequest *ctrl_req;
+	uint8_t *buf;
+	ctrl_req = &cfi->ctrl_req;
+
+	buf = pcd->cfi->ctrl_req.data;
+
+	coreif = GET_CORE_IF(pcd);
+	bRequest = ctrl_req->bRequest;
+	wIndex = DWC_CONSTANT_CPU_TO_LE16(ctrl_req->wIndex);
+	wValue = DWC_CONSTANT_CPU_TO_LE16(ctrl_req->wValue);
+
+	/* See which feature is to be modified */
+	switch (wIndex) {
+	case FT_ID_DMA_BUFFER_SETUP:
+		/* Modify the feature */
+		if ((retval = cfi_ep_set_sg_val(buf, pcd)) < 0)
+			return retval;
+
+		/* And send this request to the gadget */
+		cfi->need_gadget_att = 1;
+		break;
+
+	case FT_ID_DMA_BUFF_ALIGN:
+		if ((retval = cfi_ep_set_alignment_val(buf, pcd)) < 0)
+			return retval;
+		cfi->need_gadget_att = 1;
+		break;
+
+	case FT_ID_DMA_CONCAT_SETUP:
+		/* Modify the feature */
+		if ((retval = cfi_ep_set_concat_val(buf, pcd)) < 0)
+			return retval;
+		cfi->need_gadget_att = 1;
+		break;
+
+	case FT_ID_DMA_CIRCULAR:
+		CFI_INFO("FT_ID_DMA_CIRCULAR\n");
+		break;
+
+	case FT_ID_THRESHOLD_SETUP:
+		CFI_INFO("FT_ID_THRESHOLD_SETUP\n");
+		break;
+
+	case FT_ID_DFIFO_DEPTH:
+		CFI_INFO("FT_ID_DFIFO_DEPTH\n");
+		break;
+
+	case FT_ID_TX_FIFO_DEPTH:
+		CFI_INFO("FT_ID_TX_FIFO_DEPTH\n");
+		if ((retval = cfi_ep_set_tx_fifo_val(buf, pcd)) < 0)
+			return retval;
+		cfi->need_gadget_att = 0;
+		break;
+
+	case FT_ID_RX_FIFO_DEPTH:
+		CFI_INFO("FT_ID_RX_FIFO_DEPTH\n");
+		if ((retval = cfi_set_rx_fifo_val(buf, pcd)) < 0)
+			return retval;
+		cfi->need_gadget_att = 0;
+		break;
+	}
+
+	return retval;
+}
+
+#endif //DWC_UTE_CFI
diff -Nur --no-dereference linux-5.10.52-orig/drivers/usb/host/dwc_otg/dwc_otg_cfi.h linux-5.10.52-v7l+/drivers/usb/host/dwc_otg/dwc_otg_cfi.h
--- linux-5.10.52-orig/drivers/usb/host/dwc_otg/dwc_otg_cfi.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/usb/host/dwc_otg/dwc_otg_cfi.h	2021-07-25 16:46:28.677910113 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/* ==========================================================================
+ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter,
+ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless
+ * otherwise expressly agreed to in writing between Synopsys and you.
+ *
+ * The Software IS NOT an item of Licensed Software or Licensed Product under
+ * any End User Software License Agreement or Agreement for Licensed Product
+ * with Synopsys or any supplement thereto. You are permitted to use and
+ * redistribute this Software in source and binary forms, with or without
+ * modification, provided that redistributions of source code must retain this
+ * notice. You may not view, use, disclose, copy or distribute this file or
+ * any information contained herein except pursuant to this license grant from
+ * Synopsys. If you do not agree with this notice, including the disclaimer
+ * below, then you are not authorized to use the Software.
+ *
+ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * ========================================================================== */
+
+#if !defined(__DWC_OTG_CFI_H__)
+#define __DWC_OTG_CFI_H__
+
+#include "dwc_otg_pcd.h"
+#include "dwc_cfi_common.h"
+
+/**
+ * @file
+ * This file contains the CFI related OTG PCD specific common constants,
+ * interfaces(functions and macros) and data structures.The CFI Protocol is an
+ * optional interface for internal testing purposes that a DUT may implement to
+ * support testing of configurable features.
+ *
+ */
+
+struct dwc_otg_pcd;
+struct dwc_otg_pcd_ep;
+
+/** OTG CFI Features (properties) ID constants */
+/** This is a request for all Core Features */
+#define FT_ID_DMA_MODE					0x0001
+#define FT_ID_DMA_BUFFER_SETUP			0x0002
+#define FT_ID_DMA_BUFF_ALIGN			0x0003
+#define FT_ID_DMA_CONCAT_SETUP			0x0004
+#define FT_ID_DMA_CIRCULAR				0x0005
+#define FT_ID_THRESHOLD_SETUP			0x0006
+#define FT_ID_DFIFO_DEPTH				0x0007
+#define FT_ID_TX_FIFO_DEPTH				0x0008
+#define FT_ID_RX_FIFO_DEPTH				0x0009
+
+/**********************************************************/
+#define CFI_INFO_DEF
+
+#ifdef CFI_INFO_DEF
+#define CFI_INFO(fmt...)	DWC_PRINTF("CFI: " fmt);
+#else
+#define CFI_INFO(fmt...)
+#endif
+
+#define min(x,y) ({ \
+	x < y ? x : y; })
+
+#define max(x,y) ({ \
+	x > y ? x : y; })
+
+/**
+ * Descriptor DMA SG Buffer setup structure (SG buffer). This structure is
+ * also used for setting up a buffer for Circular DDMA.
+ */
+struct _ddma_sg_buffer_setup {
+#define BS_SG_VAL_DESC_LEN	6
+	/* The OUT EP address */
+	uint8_t bOutEndpointAddress;
+	/* The IN EP address */
+	uint8_t bInEndpointAddress;
+	/* Number of bytes to put between transfer segments (must be DWORD boundaries) */
+	uint8_t bOffset;
+	/* The number of transfer segments (a DMA descriptors per each segment) */
+	uint8_t bCount;
+	/* Size (in byte) of each transfer segment */
+	uint16_t wSize;
+} __attribute__ ((packed));
+typedef struct _ddma_sg_buffer_setup ddma_sg_buffer_setup_t;
+
+/** Descriptor DMA Concatenation Buffer setup structure */
+struct _ddma_concat_buffer_setup_hdr {
+#define BS_CONCAT_VAL_HDR_LEN	4
+	/* The endpoint for which the buffer is to be set up */
+	uint8_t bEndpointAddress;
+	/* The count of descriptors to be used */
+	uint8_t bDescCount;
+	/* The total size of the transfer */
+	uint16_t wSize;
+} __attribute__ ((packed));
+typedef struct _ddma_concat_buffer_setup_hdr ddma_concat_buffer_setup_hdr_t;
+
+/** Descriptor DMA Concatenation Buffer setup structure */
+struct _ddma_concat_buffer_setup {
+	/* The SG header */
+	ddma_concat_buffer_setup_hdr_t hdr;
+
+	/* The XFER sizes pointer (allocated dynamically) */
+	uint16_t *wTxBytes;
+} __attribute__ ((packed));
+typedef struct _ddma_concat_buffer_setup ddma_concat_buffer_setup_t;
+
+/** Descriptor DMA Alignment Buffer setup structure */
+struct _ddma_align_buffer_setup {
+#define BS_ALIGN_VAL_HDR_LEN	2
+	uint8_t bEndpointAddress;
+	uint8_t bAlign;
+} __attribute__ ((packed));
+typedef struct _ddma_align_buffer_setup ddma_align_buffer_setup_t;
+
+/** Transmit FIFO Size setup structure */
+struct _tx_fifo_size_setup {
+	uint8_t bEndpointAddress;
+	uint16_t wDepth;
+} __attribute__ ((packed));
+typedef struct _tx_fifo_size_setup tx_fifo_size_setup_t;
+
+/** Transmit FIFO Size setup structure */
+struct _rx_fifo_size_setup {
+	uint16_t wDepth;
+} __attribute__ ((packed));
+typedef struct _rx_fifo_size_setup rx_fifo_size_setup_t;
+
+/**
+ * struct cfi_usb_ctrlrequest - the CFI implementation of the struct usb_ctrlrequest
+ * This structure encapsulates the standard usb_ctrlrequest and adds a pointer
+ * to the data returned in the data stage of a 3-stage Control Write requests.
+ */
+struct cfi_usb_ctrlrequest {
+	uint8_t bRequestType;
+	uint8_t bRequest;
+	uint16_t wValue;
+	uint16_t wIndex;
+	uint16_t wLength;
+	uint8_t *data;
+} UPACKED;
+
+/*---------------------------------------------------------------------------*/
+
+/**
+ * The CFI wrapper of the enabled and activated dwc_otg_pcd_ep structures.
+ * This structure is used to store the buffer setup data for any
+ * enabled endpoint in the PCD.
+ */
+struct cfi_ep {
+	/* Entry for the list container */
+	dwc_list_link_t lh;
+	/* Pointer to the active PCD endpoint structure */
+	struct dwc_otg_pcd_ep *ep;
+	/* The last descriptor in the chain of DMA descriptors of the endpoint */
+	struct dwc_otg_dma_desc *dma_desc_last;
+	/* The SG feature value */
+	ddma_sg_buffer_setup_t *bm_sg;
+	/* The Circular feature value */
+	ddma_sg_buffer_setup_t *bm_circ;
+	/* The Concatenation feature value */
+	ddma_concat_buffer_setup_t *bm_concat;
+	/* The Alignment feature value */
+	ddma_align_buffer_setup_t *bm_align;
+	/* XFER length */
+	uint32_t xfer_len;
+	/*
+	 * Count of DMA descriptors currently used.
+	 * The total should not exceed the MAX_DMA_DESCS_PER_EP value
+	 * defined in the dwc_otg_cil.h
+	 */
+	uint32_t desc_count;
+};
+typedef struct cfi_ep cfi_ep_t;
+
+typedef struct cfi_dma_buff {
+#define CFI_IN_BUF_LEN	1024
+#define CFI_OUT_BUF_LEN	1024
+	dma_addr_t addr;
+	uint8_t *buf;
+} cfi_dma_buff_t;
+
+struct cfiobject;
+
+/**
+ * This is the interface for the CFI operations.
+ *
+ * @param	ep_enable			Called when any endpoint is enabled and activated.
+ * @param	release				Called when the CFI object is released and it needs to correctly
+ *								deallocate the dynamic memory
+ * @param	ctrl_write_complete	Called when the data stage of the request is complete
+ */
+typedef struct cfi_ops {
+	int (*ep_enable) (struct cfiobject * cfi, struct dwc_otg_pcd * pcd,
+			  struct dwc_otg_pcd_ep * ep);
+	void *(*ep_alloc_buf) (struct cfiobject * cfi, struct dwc_otg_pcd * pcd,
+			       struct dwc_otg_pcd_ep * ep, dma_addr_t * dma,
+			       unsigned size, gfp_t flags);
+	void (*release) (struct cfiobject * cfi);
+	int (*ctrl_write_complete) (struct cfiobject * cfi,
+				    struct dwc_otg_pcd * pcd);
+	void (*build_descriptors) (struct cfiobject * cfi,
+				   struct dwc_otg_pcd * pcd,
+				   struct dwc_otg_pcd_ep * ep,
+				   dwc_otg_pcd_request_t * req);
+} cfi_ops_t;
+
+struct cfiobject {
+	cfi_ops_t ops;
+	struct dwc_otg_pcd *pcd;
+	struct usb_gadget *gadget;
+
+	/* Buffers used to send/receive CFI-related request data */
+	cfi_dma_buff_t buf_in;
+	cfi_dma_buff_t buf_out;
+
+	/* CFI specific Control request wrapper */
+	struct cfi_usb_ctrlrequest ctrl_req;
+
+	/* The list of active EP's in the PCD of type cfi_ep_t */
+	dwc_list_link_t active_eps;
+
+	/* This flag shall control the propagation of a specific request
+	 * to the gadget's processing routines.
+	 * 0 - no gadget handling
+	 * 1 - the gadget needs to know about this request (w/o completing a status
+	 * phase - just return a 0 to the _setup callback)
+	 */
+	uint8_t need_gadget_att;
+
+	/* Flag indicating whether the status IN phase needs to be
+	 * completed by the PCD
+	 */
+	uint8_t need_status_in_complete;
+};
+typedef struct cfiobject cfiobject_t;
+
+#define DUMP_MSG
+
+#if defined(DUMP_MSG)
+static inline void dump_msg(const u8 * buf, unsigned int length)
+{
+	unsigned int start, num, i;
+	char line[52], *p;
+
+	if (length >= 512)
+		return;
+
+	start = 0;
+	while (length > 0) {
+		num = min(length, 16u);
+		p = line;
+		for (i = 0; i < num; ++i) {
+			if (i == 8)
+				*p++ = ' ';
+			DWC_SPRINTF(p, " %02x", buf[i]);
+			p += 3;
+		}
+		*p = 0;
+		DWC_DEBUG("%6x: %s\n", start, line);
+		buf += num;
+		start += num;
+		length -= num;
+	}
+}
+#else
+static inline void dump_msg(const u8 * buf, unsigned int length)
+{
+}
+#endif
+
+/**
+ * This function returns a pointer to cfi_ep_t object with the addr address.
+ */
+static inline struct cfi_ep *get_cfi_ep_by_addr(struct cfiobject *cfi,
+						uint8_t addr)
+{
+	struct cfi_ep *pcfiep;
+	dwc_list_link_t *tmp;
+
+	DWC_LIST_FOREACH(tmp, &cfi->active_eps) {
+		pcfiep = DWC_LIST_ENTRY(tmp, struct cfi_ep, lh);
+
+		if (pcfiep->ep->desc->bEndpointAddress == addr) {
+			return pcfiep;
+		}
+	}
+
+	return NULL;
+}
+
+/**
+ * This function returns a pointer to cfi_ep_t object that matches
+ * the dwc_otg_pcd_ep object.
+ */
+static inline struct cfi_ep *get_cfi_ep_by_pcd_ep(struct cfiobject *cfi,
+						  struct dwc_otg_pcd_ep *ep)
+{
+	struct cfi_ep *pcfiep = NULL;
+	dwc_list_link_t *tmp;
+
+	DWC_LIST_FOREACH(tmp, &cfi->active_eps) {
+		pcfiep = DWC_LIST_ENTRY(tmp, struct cfi_ep, lh);
+		if (pcfiep->ep == ep) {
+			return pcfiep;
+		}
+	}
+	return NULL;
+}
+
+int cfi_setup(struct dwc_otg_pcd *pcd, struct cfi_usb_ctrlrequest *ctrl);
+
+#endif /* (__DWC_OTG_CFI_H__) */
diff -Nur --no-dereference linux-5.10.52-orig/drivers/usb/host/dwc_otg/dwc_otg_cil.c linux-5.10.52-v7l+/drivers/usb/host/dwc_otg/dwc_otg_cil.c
--- linux-5.10.52-orig/drivers/usb/host/dwc_otg/dwc_otg_cil.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/usb/host/dwc_otg/dwc_otg_cil.c	2021-07-25 16:46:28.677910113 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/* ==========================================================================
+ * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_cil.c $
+ * $Revision: #191 $
+ * $Date: 2012/08/10 $
+ * $Change: 2047372 $
+ *
+ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter,
+ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless
+ * otherwise expressly agreed to in writing between Synopsys and you.
+ *
+ * The Software IS NOT an item of Licensed Software or Licensed Product under
+ * any End User Software License Agreement or Agreement for Licensed Product
+ * with Synopsys or any supplement thereto. You are permitted to use and
+ * redistribute this Software in source and binary forms, with or without
+ * modification, provided that redistributions of source code must retain this
+ * notice. You may not view, use, disclose, copy or distribute this file or
+ * any information contained herein except pursuant to this license grant from
+ * Synopsys. If you do not agree with this notice, including the disclaimer
+ * below, then you are not authorized to use the Software.
+ *
+ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * ========================================================================== */
+
+/** @file
+ *
+ * The Core Interface Layer provides basic services for accessing and
+ * managing the DWC_otg hardware. These services are used by both the
+ * Host Controller Driver and the Peripheral Controller Driver.
+ *
+ * The CIL manages the memory map for the core so that the HCD and PCD
+ * don't have to do this separately. It also handles basic tasks like
+ * reading/writing the registers and data FIFOs in the controller.
+ * Some of the data access functions provide encapsulation of several
+ * operations required to perform a task, such as writing multiple
+ * registers to start a transfer. Finally, the CIL performs basic
+ * services that are not specific to either the host or device modes
+ * of operation. These services include management of the OTG Host
+ * Negotiation Protocol (HNP) and Session Request Protocol (SRP). A
+ * Diagnostic API is also provided to allow testing of the controller
+ * hardware.
+ *
+ * The Core Interface Layer has the following requirements:
+ * - Provides basic controller operations.
+ * - Minimal use of OS services.
+ * - The OS services used will be abstracted by using inline functions
+ *	 or macros.
+ *
+ */
+
+#include "dwc_os.h"
+#include "dwc_otg_regs.h"
+#include "dwc_otg_cil.h"
+
+extern bool cil_force_host;
+
+static int dwc_otg_setup_params(dwc_otg_core_if_t * core_if);
+
+/**
+ * This function is called to initialize the DWC_otg CSR data
+ * structures. The register addresses in the device and host
+ * structures are initialized from the base address supplied by the
+ * caller. The calling function must make the OS calls to get the
+ * base address of the DWC_otg controller registers. The core_params
+ * argument holds the parameters that specify how the core should be
+ * configured.
+ *
+ * @param reg_base_addr Base address of DWC_otg core registers
+ *
+ */
+dwc_otg_core_if_t *dwc_otg_cil_init(const uint32_t * reg_base_addr)
+{
+	dwc_otg_core_if_t *core_if = 0;
+	dwc_otg_dev_if_t *dev_if = 0;
+	dwc_otg_host_if_t *host_if = 0;
+	uint8_t *reg_base = (uint8_t *) reg_base_addr;
+	int i = 0;
+
+	DWC_DEBUGPL(DBG_CILV, "%s(%p)\n", __func__, reg_base_addr);
+
+	core_if = DWC_ALLOC(sizeof(dwc_otg_core_if_t));
+
+	if (core_if == NULL) {
+		DWC_DEBUGPL(DBG_CIL,
+			    "Allocation of dwc_otg_core_if_t failed\n");
+		return 0;
+	}
+	core_if->core_global_regs = (dwc_otg_core_global_regs_t *) reg_base;
+
+	/*
+	 * Allocate the Device Mode structures.
+	 */
+	dev_if = DWC_ALLOC(sizeof(dwc_otg_dev_if_t));
+
+	if (dev_if == NULL) {
+		DWC_DEBUGPL(DBG_CIL, "Allocation of dwc_otg_dev_if_t failed\n");
+		DWC_FREE(core_if);
+		return 0;
+	}
+
+	dev_if->dev_global_regs =
+	    (dwc_otg_device_global_regs_t *) (reg_base +
+					      DWC_DEV_GLOBAL_REG_OFFSET);
+
+	for (i = 0; i < MAX_EPS_CHANNELS; i++) {
+		dev_if->in_ep_regs[i] = (dwc_otg_dev_in_ep_regs_t *)
+		    (reg_base + DWC_DEV_IN_EP_REG_OFFSET +
+		     (i * DWC_EP_REG_OFFSET));
+
+		dev_if->out_ep_regs[i] = (dwc_otg_dev_out_ep_regs_t *)
+		    (reg_base + DWC_DEV_OUT_EP_REG_OFFSET +
+		     (i * DWC_EP_REG_OFFSET));
+		DWC_DEBUGPL(DBG_CILV, "in_ep_regs[%d]->diepctl=%p\n",
+			    i, &dev_if->in_ep_regs[i]->diepctl);
+		DWC_DEBUGPL(DBG_CILV, "out_ep_regs[%d]->doepctl=%p\n",
+			    i, &dev_if->out_ep_regs[i]->doepctl);
+	}
+
+	dev_if->speed = 0;	// unknown
+
+	core_if->dev_if = dev_if;
+
+	/*
+	 * Allocate the Host Mode structures.
+	 */
+	host_if = DWC_ALLOC(sizeof(dwc_otg_host_if_t));
+
+	if (host_if == NULL) {
+		DWC_DEBUGPL(DBG_CIL,
+			    "Allocation of dwc_otg_host_if_t failed\n");
+		DWC_FREE(dev_if);
+		DWC_FREE(core_if);
+		return 0;
+	}
+
+	host_if->host_global_regs = (dwc_otg_host_global_regs_t *)
+	    (reg_base + DWC_OTG_HOST_GLOBAL_REG_OFFSET);
+
+	host_if->hprt0 =
+	    (uint32_t *) (reg_base + DWC_OTG_HOST_PORT_REGS_OFFSET);
+
+	for (i = 0; i < MAX_EPS_CHANNELS; i++) {
+		host_if->hc_regs[i] = (dwc_otg_hc_regs_t *)
+		    (reg_base + DWC_OTG_HOST_CHAN_REGS_OFFSET +
+		     (i * DWC_OTG_CHAN_REGS_OFFSET));
+		DWC_DEBUGPL(DBG_CILV, "hc_reg[%d]->hcchar=%p\n",
+			    i, &host_if->hc_regs[i]->hcchar);
+	}
+
+	host_if->num_host_channels = MAX_EPS_CHANNELS;
+	core_if->host_if = host_if;
+
+	for (i = 0; i < MAX_EPS_CHANNELS; i++) {
+		core_if->data_fifo[i] =
+		    (uint32_t *) (reg_base + DWC_OTG_DATA_FIFO_OFFSET +
+				  (i * DWC_OTG_DATA_FIFO_SIZE));
+		DWC_DEBUGPL(DBG_CILV, "data_fifo[%d]=0x%08lx\n",
+			    i, (unsigned long)core_if->data_fifo[i]);
+	}
+
+	core_if->pcgcctl = (uint32_t *) (reg_base + DWC_OTG_PCGCCTL_OFFSET);
+
+	/* Initiate lx_state to L3 disconnected state */
+	core_if->lx_state = DWC_OTG_L3;
+	/*
+	 * Store the contents of the hardware configuration registers here for
+	 * easy access later.
+	 */
+	core_if->hwcfg1.d32 =
+	    DWC_READ_REG32(&core_if->core_global_regs->ghwcfg1);
+	core_if->hwcfg2.d32 =
+	    DWC_READ_REG32(&core_if->core_global_regs->ghwcfg2);
+	core_if->hwcfg3.d32 =
+	    DWC_READ_REG32(&core_if->core_global_regs->ghwcfg3);
+	core_if->hwcfg4.d32 =
+	    DWC_READ_REG32(&core_if->core_global_regs->ghwcfg4);
+
+	/* Force host mode to get HPTXFSIZ exact power on value */
+	{
+		gusbcfg_data_t gusbcfg = {.d32 = 0 };
+		gusbcfg.d32 =  DWC_READ_REG32(&core_if->core_global_regs->gusbcfg);
+		gusbcfg.b.force_host_mode = 1;
+		DWC_WRITE_REG32(&core_if->core_global_regs->gusbcfg, gusbcfg.d32);
+		dwc_mdelay(100);
+		core_if->hptxfsiz.d32 =
+		DWC_READ_REG32(&core_if->core_global_regs->hptxfsiz);
+		gusbcfg.d32 =  DWC_READ_REG32(&core_if->core_global_regs->gusbcfg);
+		if (cil_force_host)
+			gusbcfg.b.force_host_mode = 1;
+		else
+			gusbcfg.b.force_host_mode = 0;
+		DWC_WRITE_REG32(&core_if->core_global_regs->gusbcfg, gusbcfg.d32);
+		dwc_mdelay(100);
+	}
+
+	DWC_DEBUGPL(DBG_CILV, "hwcfg1=%08x\n", core_if->hwcfg1.d32);
+	DWC_DEBUGPL(DBG_CILV, "hwcfg2=%08x\n", core_if->hwcfg2.d32);
+	DWC_DEBUGPL(DBG_CILV, "hwcfg3=%08x\n", core_if->hwcfg3.d32);
+	DWC_DEBUGPL(DBG_CILV, "hwcfg4=%08x\n", core_if->hwcfg4.d32);
+
+	core_if->hcfg.d32 =
+	    DWC_READ_REG32(&core_if->host_if->host_global_regs->hcfg);
+	core_if->dcfg.d32 =
+	    DWC_READ_REG32(&core_if->dev_if->dev_global_regs->dcfg);
+
+	DWC_DEBUGPL(DBG_CILV, "hcfg=%08x\n", core_if->hcfg.d32);
+	DWC_DEBUGPL(DBG_CILV, "dcfg=%08x\n", core_if->dcfg.d32);
+
+	DWC_DEBUGPL(DBG_CILV, "op_mode=%0x\n", core_if->hwcfg2.b.op_mode);
+	DWC_DEBUGPL(DBG_CILV, "arch=%0x\n", core_if->hwcfg2.b.architecture);
+	DWC_DEBUGPL(DBG_CILV, "num_dev_ep=%d\n", core_if->hwcfg2.b.num_dev_ep);
+	DWC_DEBUGPL(DBG_CILV, "num_host_chan=%d\n",
+		    core_if->hwcfg2.b.num_host_chan);
+	DWC_DEBUGPL(DBG_CILV, "nonperio_tx_q_depth=0x%0x\n",
+		    core_if->hwcfg2.b.nonperio_tx_q_depth);
+	DWC_DEBUGPL(DBG_CILV, "host_perio_tx_q_depth=0x%0x\n",
+		    core_if->hwcfg2.b.host_perio_tx_q_depth);
+	DWC_DEBUGPL(DBG_CILV, "dev_token_q_depth=0x%0x\n",
+		    core_if->hwcfg2.b.dev_token_q_depth);
+
+	DWC_DEBUGPL(DBG_CILV, "Total FIFO SZ=%d\n",
+		    core_if->hwcfg3.b.dfifo_depth);
+	DWC_DEBUGPL(DBG_CILV, "xfer_size_cntr_width=%0x\n",
+		    core_if->hwcfg3.b.xfer_size_cntr_width);
+
+	/*
+	 * Set the SRP sucess bit for FS-I2c
+	 */
+	core_if->srp_success = 0;
+	core_if->srp_timer_started = 0;
+
+	/*
+	 * Create new workqueue and init works
+	 */
+	core_if->wq_otg = DWC_WORKQ_ALLOC("dwc_otg");
+	if (core_if->wq_otg == 0) {
+		DWC_WARN("DWC_WORKQ_ALLOC failed\n");
+		DWC_FREE(host_if);
+		DWC_FREE(dev_if);
+		DWC_FREE(core_if);
+		return 0;
+	}
+
+	core_if->snpsid = DWC_READ_REG32(&core_if->core_global_regs->gsnpsid);
+
+	DWC_PRINTF("Core Release: %x.%x%x%x\n",
+		   (core_if->snpsid >> 12 & 0xF),
+		   (core_if->snpsid >> 8 & 0xF),
+		   (core_if->snpsid >> 4 & 0xF), (core_if->snpsid & 0xF));
+
+	core_if->wkp_timer = DWC_TIMER_ALLOC("Wake Up Timer",
+					     w_wakeup_detected, core_if);
+	if (core_if->wkp_timer == 0) {
+		DWC_WARN("DWC_TIMER_ALLOC failed\n");
+		DWC_FREE(host_if);
+		DWC_FREE(dev_if);
+		DWC_WORKQ_FREE(core_if->wq_otg);
+		DWC_FREE(core_if);
+		return 0;
+	}
+
+	if (dwc_otg_setup_params(core_if)) {
+		DWC_WARN("Error while setting core params\n");
+	}
+
+	core_if->hibernation_suspend = 0;
+
+	/** ADP initialization */
+	dwc_otg_adp_init(core_if);
+
+	return core_if;
+}
+
+/**
+ * This function frees the structures allocated by dwc_otg_cil_init().
+ *
+ * @param core_if The core interface pointer returned from
+ * 		  dwc_otg_cil_init().
+ *
+ */
+void dwc_otg_cil_remove(dwc_otg_core_if_t * core_if)
+{
+	dctl_data_t dctl = {.d32 = 0 };
+	DWC_DEBUGPL(DBG_CILV, "%s(%p)\n", __func__, core_if);
+
+	/* Disable all interrupts */
+	DWC_MODIFY_REG32(&core_if->core_global_regs->gahbcfg, 1, 0);
+	DWC_WRITE_REG32(&core_if->core_global_regs->gintmsk, 0);
+
+	dctl.b.sftdiscon = 1;
+	if (core_if->snpsid >= OTG_CORE_REV_3_00a) {
+		DWC_MODIFY_REG32(&core_if->dev_if->dev_global_regs->dctl, 0,
+				 dctl.d32);
+	}
+
+	if (core_if->wq_otg) {
+		DWC_WORKQ_WAIT_WORK_DONE(core_if->wq_otg, 500);
+		DWC_WORKQ_FREE(core_if->wq_otg);
+	}
+	if (core_if->dev_if) {
+		DWC_FREE(core_if->dev_if);
+	}
+	if (core_if->host_if) {
+		DWC_FREE(core_if->host_if);
+	}
+
+	/** Remove ADP Stuff  */
+	dwc_otg_adp_remove(core_if);
+	if (core_if->core_params) {
+		DWC_FREE(core_if->core_params);
+	}
+	if (core_if->wkp_timer) {
+		DWC_TIMER_FREE(core_if->wkp_timer);
+	}
+	if (core_if->srp_timer) {
+		DWC_TIMER_FREE(core_if->srp_timer);
+	}
+	DWC_FREE(core_if);
+}
+
+/**
+ * This function enables the controller's Global Interrupt in the AHB Config
+ * register.
+ *
+ * @param core_if Programming view of DWC_otg controller.
+ */
+void dwc_otg_enable_global_interrupts(dwc_otg_core_if_t * core_if)
+{
+	gahbcfg_data_t ahbcfg = {.d32 = 0 };
+	ahbcfg.b.glblintrmsk = 1;	/* Enable interrupts */
+	DWC_MODIFY_REG32(&core_if->core_global_regs->gahbcfg, 0, ahbcfg.d32);
+}
+
+/**
+ * This function disables the controller's Global Interrupt in the AHB Config
+ * register.
+ *
+ * @param core_if Programming view of DWC_otg controller.
+ */
+void dwc_otg_disable_global_interrupts(dwc_otg_core_if_t * core_if)
+{
+	gahbcfg_data_t ahbcfg = {.d32 = 0 };
+	ahbcfg.b.glblintrmsk = 1;	/* Disable interrupts */
+	DWC_MODIFY_REG32(&core_if->core_global_regs->gahbcfg, ahbcfg.d32, 0);
+}
+
+/**
+ * This function initializes the commmon interrupts, used in both
+ * device and host modes.
+ *
+ * @param core_if Programming view of the DWC_otg controller
+ *
+ */
+static void dwc_otg_enable_common_interrupts(dwc_otg_core_if_t * core_if)
+{
+	dwc_otg_core_global_regs_t *global_regs = core_if->core_global_regs;
+	gintmsk_data_t intr_mask = {.d32 = 0 };
+
+	/* Clear any pending OTG Interrupts */
+	DWC_WRITE_REG32(&global_regs->gotgint, 0xFFFFFFFF);
+
+	/* Clear any pending interrupts */
+	DWC_WRITE_REG32(&global_regs->gintsts, 0xFFFFFFFF);
+
+	/*
+	 * Enable the interrupts in the GINTMSK.
+	 */
+	intr_mask.b.modemismatch = 1;
+	intr_mask.b.otgintr = 1;
+
+	if (!core_if->dma_enable) {
+		intr_mask.b.rxstsqlvl = 1;
+	}
+
+	intr_mask.b.conidstschng = 1;
+	intr_mask.b.wkupintr = 1;
+	intr_mask.b.disconnect = 0;
+	intr_mask.b.usbsuspend = 1;
+	intr_mask.b.sessreqintr = 1;
+#ifdef CONFIG_USB_DWC_OTG_LPM
+	if (core_if->core_params->lpm_enable) {
+		intr_mask.b.lpmtranrcvd = 1;
+	}
+#endif
+	DWC_WRITE_REG32(&global_regs->gintmsk, intr_mask.d32);
+}
+
+/*
+ * The restore operation is modified to support Synopsys Emulated Powerdown and
+ * Hibernation. This function is for exiting from Device mode hibernation by
+ * Host Initiated Resume/Reset and Device Initiated Remote-Wakeup.
+ * @param core_if Programming view of DWC_otg controller.
+ * @param rem_wakeup - indicates whether resume is initiated by Device or Host.
+ * @param reset - indicates whether resume is initiated by Reset.
+ */
+int dwc_otg_device_hibernation_restore(dwc_otg_core_if_t * core_if,
+				       int rem_wakeup, int reset)
+{
+	gpwrdn_data_t gpwrdn = {.d32 = 0 };
+	pcgcctl_data_t pcgcctl = {.d32 = 0 };
+	dctl_data_t dctl = {.d32 = 0 };
+
+	int timeout = 2000;
+
+	if (!core_if->hibernation_suspend) {
+		DWC_PRINTF("Already exited from Hibernation\n");
+		return 1;
+	}
+
+	DWC_DEBUGPL(DBG_PCD, "%s called\n", __FUNCTION__);
+	/* Switch-on voltage to the core */
+	gpwrdn.b.pwrdnswtch = 1;
+	DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
+	dwc_udelay(10);
+
+	/* Reset core */
+	gpwrdn.d32 = 0;
+	gpwrdn.b.pwrdnrstn = 1;
+	DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
+	dwc_udelay(10);
+
+	/* Assert Restore signal */
+	gpwrdn.d32 = 0;
+	gpwrdn.b.restore = 1;
+	DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, 0, gpwrdn.d32);
+	dwc_udelay(10);
+
+	/* Disable power clamps */
+	gpwrdn.d32 = 0;
+	gpwrdn.b.pwrdnclmp = 1;
+	DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
+
+	if (rem_wakeup) {
+		dwc_udelay(70);
+	}
+
+	/* Deassert Reset core */
+	gpwrdn.d32 = 0;
+	gpwrdn.b.pwrdnrstn = 1;
+	DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, 0, gpwrdn.d32);
+	dwc_udelay(10);
+
+	/* Disable PMU interrupt */
+	gpwrdn.d32 = 0;
+	gpwrdn.b.pmuintsel = 1;
+	DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
+
+	/* Mask interrupts from gpwrdn */
+	gpwrdn.d32 = 0;
+	gpwrdn.b.connect_det_msk = 1;
+	gpwrdn.b.srp_det_msk = 1;
+	gpwrdn.b.disconn_det_msk = 1;
+	gpwrdn.b.rst_det_msk = 1;
+	gpwrdn.b.lnstchng_msk = 1;
+	DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
+
+	/* Indicates that we are going out from hibernation */
+	core_if->hibernation_suspend = 0;
+
+	/*
+	 * Set Restore Essential Regs bit in PCGCCTL register, restore_mode = 1
+	 * indicates restore from remote_wakeup
+	 */
+	restore_essential_regs(core_if, rem_wakeup, 0);
+
+	/*
+	 * Wait a little for seeing new value of variable hibernation_suspend if
+	 * Restore done interrupt received before polling
+	 */
+	dwc_udelay(10);
+
+	if (core_if->hibernation_suspend == 0) {
+		/*
+		 * Wait For Restore_done Interrupt. This mechanism of polling the
+		 * interrupt is introduced to avoid any possible race conditions
+		 */
+		do {
+			gintsts_data_t gintsts;
+			gintsts.d32 =
+			    DWC_READ_REG32(&core_if->core_global_regs->gintsts);
+			if (gintsts.b.restoredone) {
+				gintsts.d32 = 0;
+				gintsts.b.restoredone = 1;
+				DWC_WRITE_REG32(&core_if->core_global_regs->
+						gintsts, gintsts.d32);
+				DWC_PRINTF("Restore Done Interrupt seen\n");
+				break;
+			}
+			dwc_udelay(10);
+		} while (--timeout);
+		if (!timeout) {
+			DWC_PRINTF("Restore Done interrupt wasn't generated here\n");
+		}
+	}
+	/* Clear all pending interupts */
+	DWC_WRITE_REG32(&core_if->core_global_regs->gintsts, 0xFFFFFFFF);
+
+	/* De-assert Restore */
+	gpwrdn.d32 = 0;
+	gpwrdn.b.restore = 1;
+	DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
+	dwc_udelay(10);
+
+	if (!rem_wakeup) {
+		pcgcctl.d32 = 0;
+		pcgcctl.b.rstpdwnmodule = 1;
+		DWC_MODIFY_REG32(core_if->pcgcctl, pcgcctl.d32, 0);
+	}
+
+	/* Restore GUSBCFG and DCFG */
+	DWC_WRITE_REG32(&core_if->core_global_regs->gusbcfg,
+			core_if->gr_backup->gusbcfg_local);
+	DWC_WRITE_REG32(&core_if->dev_if->dev_global_regs->dcfg,
+			core_if->dr_backup->dcfg);
+
+	/* De-assert Wakeup Logic */
+	gpwrdn.d32 = 0;
+	gpwrdn.b.pmuactv = 1;
+	DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
+	dwc_udelay(10);
+
+	if (!rem_wakeup) {
+		/* Set Device programming done bit */
+		dctl.b.pwronprgdone = 1;
+		DWC_MODIFY_REG32(&core_if->dev_if->dev_global_regs->dctl, 0, dctl.d32);
+	} else {
+		/* Start Remote Wakeup Signaling */
+		dctl.d32 = core_if->dr_backup->dctl;
+		dctl.b.rmtwkupsig = 1;
+		DWC_WRITE_REG32(&core_if->dev_if->dev_global_regs->dctl, dctl.d32);
+	}
+
+	dwc_mdelay(2);
+	/* Clear all pending interupts */
+	DWC_WRITE_REG32(&core_if->core_global_regs->gintsts, 0xFFFFFFFF);
+
+	/* Restore global registers */
+	dwc_otg_restore_global_regs(core_if);
+	/* Restore device global registers */
+	dwc_otg_restore_dev_regs(core_if, rem_wakeup);
+
+	if (rem_wakeup) {
+		dwc_mdelay(7);
+		dctl.d32 = 0;
+		dctl.b.rmtwkupsig = 1;
+		DWC_MODIFY_REG32(&core_if->dev_if->dev_global_regs->dctl, dctl.d32, 0);
+	}
+
+	core_if->hibernation_suspend = 0;
+	/* The core will be in ON STATE */
+	core_if->lx_state = DWC_OTG_L0;
+	DWC_PRINTF("Hibernation recovery completes here\n");
+
+	return 1;
+}
+
+/*
+ * The restore operation is modified to support Synopsys Emulated Powerdown and
+ * Hibernation. This function is for exiting from Host mode hibernation by
+ * Host Initiated Resume/Reset and Device Initiated Remote-Wakeup.
+ * @param core_if Programming view of DWC_otg controller.
+ * @param rem_wakeup - indicates whether resume is initiated by Device or Host.
+ * @param reset - indicates whether resume is initiated by Reset.
+ */
+int dwc_otg_host_hibernation_restore(dwc_otg_core_if_t * core_if,
+				     int rem_wakeup, int reset)
+{
+	gpwrdn_data_t gpwrdn = {.d32 = 0 };
+	hprt0_data_t hprt0 = {.d32 = 0 };
+
+	int timeout = 2000;
+
+	DWC_DEBUGPL(DBG_HCD, "%s called\n", __FUNCTION__);
+	/* Switch-on voltage to the core */
+	gpwrdn.b.pwrdnswtch = 1;
+	DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
+	dwc_udelay(10);
+
+	/* Reset core */
+	gpwrdn.d32 = 0;
+	gpwrdn.b.pwrdnrstn = 1;
+	DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
+	dwc_udelay(10);
+
+	/* Assert Restore signal */
+	gpwrdn.d32 = 0;
+	gpwrdn.b.restore = 1;
+	DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, 0, gpwrdn.d32);
+	dwc_udelay(10);
+
+	/* Disable power clamps */
+	gpwrdn.d32 = 0;
+	gpwrdn.b.pwrdnclmp = 1;
+	DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
+
+	if (!rem_wakeup) {
+		dwc_udelay(50);
+	}
+
+	/* Deassert Reset core */
+	gpwrdn.d32 = 0;
+	gpwrdn.b.pwrdnrstn = 1;
+	DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, 0, gpwrdn.d32);
+	dwc_udelay(10);
+
+	/* Disable PMU interrupt */
+	gpwrdn.d32 = 0;
+	gpwrdn.b.pmuintsel = 1;
+	DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
+
+	gpwrdn.d32 = 0;
+	gpwrdn.b.connect_det_msk = 1;
+	gpwrdn.b.srp_det_msk = 1;
+	gpwrdn.b.disconn_det_msk = 1;
+	gpwrdn.b.rst_det_msk = 1;
+	gpwrdn.b.lnstchng_msk = 1;
+	DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
+
+	/* Indicates that we are going out from hibernation */
+	core_if->hibernation_suspend = 0;
+
+	/* Set Restore Essential Regs bit in PCGCCTL register */
+	restore_essential_regs(core_if, rem_wakeup, 1);
+
+	/* Wait a little for seeing new value of variable hibernation_suspend if
+	 * Restore done interrupt received before polling */
+	dwc_udelay(10);
+
+	if (core_if->hibernation_suspend == 0) {
+		/* Wait For Restore_done Interrupt. This mechanism of polling the
+		 * interrupt is introduced to avoid any possible race conditions
+		 */
+		do {
+			gintsts_data_t gintsts;
+			gintsts.d32 = DWC_READ_REG32(&core_if->core_global_regs->gintsts);
+			if (gintsts.b.restoredone) {
+				gintsts.d32 = 0;
+				gintsts.b.restoredone = 1;
+			DWC_WRITE_REG32(&core_if->core_global_regs->gintsts, gintsts.d32);
+				DWC_DEBUGPL(DBG_HCD,"Restore Done Interrupt seen\n");
+				break;
+			}
+			dwc_udelay(10);
+		} while (--timeout);
+		if (!timeout) {
+			DWC_WARN("Restore Done interrupt wasn't generated\n");
+		}
+	}
+
+	/* Set the flag's value to 0 again after receiving restore done interrupt */
+	core_if->hibernation_suspend = 0;
+
+	/* This step is not described in functional spec but if not wait for this
+	 * delay, mismatch interrupts occurred because just after restore core is
+	 * in Device mode(gintsts.curmode == 0) */
+	dwc_mdelay(100);
+
+	/* Clear all pending interrupts */
+	DWC_WRITE_REG32(&core_if->core_global_regs->gintsts, 0xFFFFFFFF);
+
+	/* De-assert Restore */
+	gpwrdn.d32 = 0;
+	gpwrdn.b.restore = 1;
+	DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
+	dwc_udelay(10);
+
+	/* Restore GUSBCFG and HCFG */
+	DWC_WRITE_REG32(&core_if->core_global_regs->gusbcfg,
+			core_if->gr_backup->gusbcfg_local);
+	DWC_WRITE_REG32(&core_if->host_if->host_global_regs->hcfg,
+			core_if->hr_backup->hcfg_local);
+
+	/* De-assert Wakeup Logic */
+	gpwrdn.d32 = 0;
+	gpwrdn.b.pmuactv = 1;
+	DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
+	dwc_udelay(10);
+
+	/* Start the Resume operation by programming HPRT0 */
+	hprt0.d32 = core_if->hr_backup->hprt0_local;
+	hprt0.b.prtpwr = 1;
+	hprt0.b.prtena = 0;
+	hprt0.b.prtsusp = 0;
+	DWC_WRITE_REG32(core_if->host_if->hprt0, hprt0.d32);
+
+	DWC_PRINTF("Resume Starts Now\n");
+	if (!reset) {		// Indicates it is Resume Operation
+		hprt0.d32 = core_if->hr_backup->hprt0_local;
+		hprt0.b.prtres = 1;
+		hprt0.b.prtpwr = 1;
+		hprt0.b.prtena = 0;
+		hprt0.b.prtsusp = 0;
+		DWC_WRITE_REG32(core_if->host_if->hprt0, hprt0.d32);
+
+		if (!rem_wakeup)
+			hprt0.b.prtres = 0;
+		/* Wait for Resume time and then program HPRT again */
+		dwc_mdelay(100);
+		DWC_WRITE_REG32(core_if->host_if->hprt0, hprt0.d32);
+
+	} else {		// Indicates it is Reset Operation
+		hprt0.d32 = core_if->hr_backup->hprt0_local;
+		hprt0.b.prtrst = 1;
+		hprt0.b.prtpwr = 1;
+		hprt0.b.prtena = 0;
+		hprt0.b.prtsusp = 0;
+		DWC_WRITE_REG32(core_if->host_if->hprt0, hprt0.d32);
+		/* Wait for Reset time and then program HPRT again */
+		dwc_mdelay(60);
+		hprt0.b.prtrst = 0;
+		DWC_WRITE_REG32(core_if->host_if->hprt0, hprt0.d32);
+	}
+	/* Clear all interrupt status */
+	hprt0.d32 = dwc_otg_read_hprt0(core_if);
+	hprt0.b.prtconndet = 1;
+	hprt0.b.prtenchng = 1;
+	DWC_WRITE_REG32(core_if->host_if->hprt0, hprt0.d32);
+
+	/* Clear all pending interupts */
+	DWC_WRITE_REG32(&core_if->core_global_regs->gintsts, 0xFFFFFFFF);
+
+	/* Restore global registers */
+	dwc_otg_restore_global_regs(core_if);
+	/* Restore host global registers */
+	dwc_otg_restore_host_regs(core_if, reset);
+
+	/* The core will be in ON STATE */
+	core_if->lx_state = DWC_OTG_L0;
+	DWC_PRINTF("Hibernation recovery is complete here\n");
+	return 0;
+}
+
+/** Saves some register values into system memory. */
+int dwc_otg_save_global_regs(dwc_otg_core_if_t * core_if)
+{
+	struct dwc_otg_global_regs_backup *gr;
+	int i;
+
+	gr = core_if->gr_backup;
+	if (!gr) {
+		gr = DWC_ALLOC(sizeof(*gr));
+		if (!gr) {
+			return -DWC_E_NO_MEMORY;
+		}
+		core_if->gr_backup = gr;
+	}
+
+	gr->gotgctl_local = DWC_READ_REG32(&core_if->core_global_regs->gotgctl);
+	gr->gintmsk_local = DWC_READ_REG32(&core_if->core_global_regs->gintmsk);
+	gr->gahbcfg_local = DWC_READ_REG32(&core_if->core_global_regs->gahbcfg);
+	gr->gusbcfg_local = DWC_READ_REG32(&core_if->core_global_regs->gusbcfg);
+	gr->grxfsiz_local = DWC_READ_REG32(&core_if->core_global_regs->grxfsiz);
+	gr->gnptxfsiz_local = DWC_READ_REG32(&core_if->core_global_regs->gnptxfsiz);
+	gr->hptxfsiz_local = DWC_READ_REG32(&core_if->core_global_regs->hptxfsiz);
+#ifdef CONFIG_USB_DWC_OTG_LPM
+	gr->glpmcfg_local = DWC_READ_REG32(&core_if->core_global_regs->glpmcfg);
+#endif
+	gr->gi2cctl_local = DWC_READ_REG32(&core_if->core_global_regs->gi2cctl);
+	gr->pcgcctl_local = DWC_READ_REG32(core_if->pcgcctl);
+	gr->gdfifocfg_local =
+	    DWC_READ_REG32(&core_if->core_global_regs->gdfifocfg);
+	for (i = 0; i < MAX_EPS_CHANNELS; i++) {
+		gr->dtxfsiz_local[i] =
+		    DWC_READ_REG32(&(core_if->core_global_regs->dtxfsiz[i]));
+	}
+
+	DWC_DEBUGPL(DBG_ANY, "===========Backing Global registers==========\n");
+	DWC_DEBUGPL(DBG_ANY, "Backed up gotgctl   = %08x\n", gr->gotgctl_local);
+	DWC_DEBUGPL(DBG_ANY, "Backed up gintmsk   = %08x\n", gr->gintmsk_local);
+	DWC_DEBUGPL(DBG_ANY, "Backed up gahbcfg   = %08x\n", gr->gahbcfg_local);
+	DWC_DEBUGPL(DBG_ANY, "Backed up gusbcfg   = %08x\n", gr->gusbcfg_local);
+	DWC_DEBUGPL(DBG_ANY, "Backed up grxfsiz   = %08x\n", gr->grxfsiz_local);
+	DWC_DEBUGPL(DBG_ANY, "Backed up gnptxfsiz = %08x\n",
+		    gr->gnptxfsiz_local);
+	DWC_DEBUGPL(DBG_ANY, "Backed up hptxfsiz  = %08x\n",
+		    gr->hptxfsiz_local);
+#ifdef CONFIG_USB_DWC_OTG_LPM
+	DWC_DEBUGPL(DBG_ANY, "Backed up glpmcfg   = %08x\n", gr->glpmcfg_local);
+#endif
+	DWC_DEBUGPL(DBG_ANY, "Backed up gi2cctl   = %08x\n", gr->gi2cctl_local);
+	DWC_DEBUGPL(DBG_ANY, "Backed up pcgcctl   = %08x\n", gr->pcgcctl_local);
+	DWC_DEBUGPL(DBG_ANY,"Backed up gdfifocfg   = %08x\n",gr->gdfifocfg_local);
+
+	return 0;
+}
+
+/** Saves GINTMSK register before setting the msk bits. */
+int dwc_otg_save_gintmsk_reg(dwc_otg_core_if_t * core_if)
+{
+	struct dwc_otg_global_regs_backup *gr;
+
+	gr = core_if->gr_backup;
+	if (!gr) {
+		gr = DWC_ALLOC(sizeof(*gr));
+		if (!gr) {
+			return -DWC_E_NO_MEMORY;
+		}
+		core_if->gr_backup = gr;
+	}
+
+	gr->gintmsk_local = DWC_READ_REG32(&core_if->core_global_regs->gintmsk);
+
+	DWC_DEBUGPL(DBG_ANY,"=============Backing GINTMSK registers============\n");
+	DWC_DEBUGPL(DBG_ANY, "Backed up gintmsk   = %08x\n", gr->gintmsk_local);
+
+	return 0;
+}
+
+int dwc_otg_save_dev_regs(dwc_otg_core_if_t * core_if)
+{
+	struct dwc_otg_dev_regs_backup *dr;
+	int i;
+
+	dr = core_if->dr_backup;
+	if (!dr) {
+		dr = DWC_ALLOC(sizeof(*dr));
+		if (!dr) {
+			return -DWC_E_NO_MEMORY;
+		}
+		core_if->dr_backup = dr;
+	}
+
+	dr->dcfg = DWC_READ_REG32(&core_if->dev_if->dev_global_regs->dcfg);
+	dr->dctl = DWC_READ_REG32(&core_if->dev_if->dev_global_regs->dctl);
+	dr->daintmsk =
+	    DWC_READ_REG32(&core_if->dev_if->dev_global_regs->daintmsk);
+	dr->diepmsk =
+	    DWC_READ_REG32(&core_if->dev_if->dev_global_regs->diepmsk);
+	dr->doepmsk =
+	    DWC_READ_REG32(&core_if->dev_if->dev_global_regs->doepmsk);
+
+	for (i = 0; i < core_if->dev_if->num_in_eps; ++i) {
+		dr->diepctl[i] =
+		    DWC_READ_REG32(&core_if->dev_if->in_ep_regs[i]->diepctl);
+		dr->dieptsiz[i] =
+		    DWC_READ_REG32(&core_if->dev_if->in_ep_regs[i]->dieptsiz);
+		dr->diepdma[i] =
+		    DWC_READ_REG32(&core_if->dev_if->in_ep_regs[i]->diepdma);
+	}
+
+	DWC_DEBUGPL(DBG_ANY,
+		    "=============Backing Host registers==============\n");
+	DWC_DEBUGPL(DBG_ANY, "Backed up dcfg            = %08x\n", dr->dcfg);
+	DWC_DEBUGPL(DBG_ANY, "Backed up dctl        = %08x\n", dr->dctl);
+	DWC_DEBUGPL(DBG_ANY, "Backed up daintmsk            = %08x\n",
+		    dr->daintmsk);
+	DWC_DEBUGPL(DBG_ANY, "Backed up diepmsk        = %08x\n", dr->diepmsk);
+	DWC_DEBUGPL(DBG_ANY, "Backed up doepmsk        = %08x\n", dr->doepmsk);
+	for (i = 0; i < core_if->dev_if->num_in_eps; ++i) {
+		DWC_DEBUGPL(DBG_ANY, "Backed up diepctl[%d]        = %08x\n", i,
+			    dr->diepctl[i]);
+		DWC_DEBUGPL(DBG_ANY, "Backed up dieptsiz[%d]        = %08x\n",
+			    i, dr->dieptsiz[i]);
+		DWC_DEBUGPL(DBG_ANY, "Backed up diepdma[%d]        = %08x\n", i,
+			    dr->diepdma[i]);
+	}
+
+	return 0;
+}
+
+int dwc_otg_save_host_regs(dwc_otg_core_if_t * core_if)
+{
+	struct dwc_otg_host_regs_backup *hr;
+	int i;
+
+	hr = core_if->hr_backup;
+	if (!hr) {
+		hr = DWC_ALLOC(sizeof(*hr));
+		if (!hr) {
+			return -DWC_E_NO_MEMORY;
+		}
+		core_if->hr_backup = hr;
+	}
+
+	hr->hcfg_local =
+	    DWC_READ_REG32(&core_if->host_if->host_global_regs->hcfg);
+	hr->haintmsk_local =
+	    DWC_READ_REG32(&core_if->host_if->host_global_regs->haintmsk);
+	for (i = 0; i < dwc_otg_get_param_host_channels(core_if); ++i) {
+		hr->hcintmsk_local[i] =
+		    DWC_READ_REG32(&core_if->host_if->hc_regs[i]->hcintmsk);
+	}
+	hr->hprt0_local = DWC_READ_REG32(core_if->host_if->hprt0);
+	hr->hfir_local =
+	    DWC_READ_REG32(&core_if->host_if->host_global_regs->hfir);
+
+	DWC_DEBUGPL(DBG_ANY,
+		    "=============Backing Host registers===============\n");
+	DWC_DEBUGPL(DBG_ANY, "Backed up hcfg		= %08x\n",
+		    hr->hcfg_local);
+	DWC_DEBUGPL(DBG_ANY, "Backed up haintmsk = %08x\n", hr->haintmsk_local);
+	for (i = 0; i < dwc_otg_get_param_host_channels(core_if); ++i) {
+		DWC_DEBUGPL(DBG_ANY, "Backed up hcintmsk[%02d]=%08x\n", i,
+			    hr->hcintmsk_local[i]);
+	}
+	DWC_DEBUGPL(DBG_ANY, "Backed up hprt0           = %08x\n",
+		    hr->hprt0_local);
+	DWC_DEBUGPL(DBG_ANY, "Backed up hfir           = %08x\n",
+		    hr->hfir_local);
+
+	return 0;
+}
+
+int dwc_otg_restore_global_regs(dwc_otg_core_if_t *core_if)
+{
+	struct dwc_otg_global_regs_backup *gr;
+	int i;
+
+	gr = core_if->gr_backup;
+	if (!gr) {
+		return -DWC_E_INVALID;
+	}
+
+	DWC_WRITE_REG32(&core_if->core_global_regs->gotgctl, gr->gotgctl_local);
+	DWC_WRITE_REG32(&core_if->core_global_regs->gintmsk, gr->gintmsk_local);
+	DWC_WRITE_REG32(&core_if->core_global_regs->gusbcfg, gr->gusbcfg_local);
+	DWC_WRITE_REG32(&core_if->core_global_regs->gahbcfg, gr->gahbcfg_local);
+	DWC_WRITE_REG32(&core_if->core_global_regs->grxfsiz, gr->grxfsiz_local);
+	DWC_WRITE_REG32(&core_if->core_global_regs->gnptxfsiz,
+			gr->gnptxfsiz_local);
+	DWC_WRITE_REG32(&core_if->core_global_regs->hptxfsiz,
+			gr->hptxfsiz_local);
+	DWC_WRITE_REG32(&core_if->core_global_regs->gdfifocfg,
+			gr->gdfifocfg_local);
+	for (i = 0; i < MAX_EPS_CHANNELS; i++) {
+		DWC_WRITE_REG32(&core_if->core_global_regs->dtxfsiz[i],
+				gr->dtxfsiz_local[i]);
+	}
+
+	DWC_WRITE_REG32(&core_if->core_global_regs->gintsts, 0xFFFFFFFF);
+	DWC_WRITE_REG32(core_if->host_if->hprt0, 0x0000100A);
+	DWC_WRITE_REG32(&core_if->core_global_regs->gahbcfg,
+			(gr->gahbcfg_local));
+	return 0;
+}
+
+int dwc_otg_restore_dev_regs(dwc_otg_core_if_t * core_if, int rem_wakeup)
+{
+	struct dwc_otg_dev_regs_backup *dr;
+	int i;
+
+	dr = core_if->dr_backup;
+
+	if (!dr) {
+		return -DWC_E_INVALID;
+	}
+
+	if (!rem_wakeup) {
+		DWC_WRITE_REG32(&core_if->dev_if->dev_global_regs->dctl,
+				dr->dctl);
+	}
+
+	DWC_WRITE_REG32(&core_if->dev_if->dev_global_regs->daintmsk, dr->daintmsk);
+	DWC_WRITE_REG32(&core_if->dev_if->dev_global_regs->diepmsk, dr->diepmsk);
+	DWC_WRITE_REG32(&core_if->dev_if->dev_global_regs->doepmsk, dr->doepmsk);
+
+	for (i = 0; i < core_if->dev_if->num_in_eps; ++i) {
+		DWC_WRITE_REG32(&core_if->dev_if->in_ep_regs[i]->dieptsiz, dr->dieptsiz[i]);
+		DWC_WRITE_REG32(&core_if->dev_if->in_ep_regs[i]->diepdma, dr->diepdma[i]);
+		DWC_WRITE_REG32(&core_if->dev_if->in_ep_regs[i]->diepctl, dr->diepctl[i]);
+	}
+
+	return 0;
+}
+
+int dwc_otg_restore_host_regs(dwc_otg_core_if_t * core_if, int reset)
+{
+	struct dwc_otg_host_regs_backup *hr;
+	int i;
+	hr = core_if->hr_backup;
+
+	if (!hr) {
+		return -DWC_E_INVALID;
+	}
+
+	DWC_WRITE_REG32(&core_if->host_if->host_global_regs->hcfg, hr->hcfg_local);
+	//if (!reset)
+	//{
+	//      DWC_WRITE_REG32(&core_if->host_if->host_global_regs->hfir, hr->hfir_local);
+	//}
+
+	DWC_WRITE_REG32(&core_if->host_if->host_global_regs->haintmsk,
+			hr->haintmsk_local);
+	for (i = 0; i < dwc_otg_get_param_host_channels(core_if); ++i) {
+		DWC_WRITE_REG32(&core_if->host_if->hc_regs[i]->hcintmsk,
+				hr->hcintmsk_local[i]);
+	}
+
+	return 0;
+}
+
+int restore_lpm_i2c_regs(dwc_otg_core_if_t * core_if)
+{
+	struct dwc_otg_global_regs_backup *gr;
+
+	gr = core_if->gr_backup;
+
+	/* Restore values for LPM and I2C */
+#ifdef CONFIG_USB_DWC_OTG_LPM
+	DWC_WRITE_REG32(&core_if->core_global_regs->glpmcfg, gr->glpmcfg_local);
+#endif
+	DWC_WRITE_REG32(&core_if->core_global_regs->gi2cctl, gr->gi2cctl_local);
+
+	return 0;
+}
+
+int restore_essential_regs(dwc_otg_core_if_t * core_if, int rmode, int is_host)
+{
+	struct dwc_otg_global_regs_backup *gr;
+	pcgcctl_data_t pcgcctl = {.d32 = 0 };
+	gahbcfg_data_t gahbcfg = {.d32 = 0 };
+	gusbcfg_data_t gusbcfg = {.d32 = 0 };
+	gintmsk_data_t gintmsk = {.d32 = 0 };
+
+	/* Restore LPM and I2C registers */
+	restore_lpm_i2c_regs(core_if);
+
+	/* Set PCGCCTL to 0 */
+	DWC_WRITE_REG32(core_if->pcgcctl, 0x00000000);
+
+	gr = core_if->gr_backup;
+	/* Load restore values for [31:14] bits */
+	DWC_WRITE_REG32(core_if->pcgcctl,
+			((gr->pcgcctl_local & 0xffffc000) | 0x00020000));
+
+	/* Umnask global Interrupt in GAHBCFG and restore it */
+	gahbcfg.d32 = gr->gahbcfg_local;
+	gahbcfg.b.glblintrmsk = 1;
+	DWC_WRITE_REG32(&core_if->core_global_regs->gahbcfg, gahbcfg.d32);
+
+	/* Clear all pending interupts */
+	DWC_WRITE_REG32(&core_if->core_global_regs->gintsts, 0xFFFFFFFF);
+
+	/* Unmask restore done interrupt */
+	gintmsk.b.restoredone = 1;
+	DWC_WRITE_REG32(&core_if->core_global_regs->gintmsk, gintmsk.d32);
+
+	/* Restore GUSBCFG and HCFG/DCFG */
+	gusbcfg.d32 = core_if->gr_backup->gusbcfg_local;
+	DWC_WRITE_REG32(&core_if->core_global_regs->gusbcfg, gusbcfg.d32);
+
+	if (is_host) {
+		hcfg_data_t hcfg = {.d32 = 0 };
+		hcfg.d32 = core_if->hr_backup->hcfg_local;
+		DWC_WRITE_REG32(&core_if->host_if->host_global_regs->hcfg,
+				hcfg.d32);
+
+		/* Load restore values for [31:14] bits */
+		pcgcctl.d32 = gr->pcgcctl_local & 0xffffc000;
+		pcgcctl.d32 = gr->pcgcctl_local | 0x00020000;
+
+		if (rmode)
+			pcgcctl.b.restoremode = 1;
+		DWC_WRITE_REG32(core_if->pcgcctl, pcgcctl.d32);
+		dwc_udelay(10);
+
+		/* Load restore values for [31:14] bits and set EssRegRestored bit */
+		pcgcctl.d32 = gr->pcgcctl_local | 0xffffc000;
+		pcgcctl.d32 = gr->pcgcctl_local & 0xffffc000;
+		pcgcctl.b.ess_reg_restored = 1;
+		if (rmode)
+			pcgcctl.b.restoremode = 1;
+		DWC_WRITE_REG32(core_if->pcgcctl, pcgcctl.d32);
+	} else {
+		dcfg_data_t dcfg = {.d32 = 0 };
+		dcfg.d32 = core_if->dr_backup->dcfg;
+		DWC_WRITE_REG32(&core_if->dev_if->dev_global_regs->dcfg, dcfg.d32);
+
+		/* Load restore values for [31:14] bits */
+		pcgcctl.d32 = gr->pcgcctl_local & 0xffffc000;
+		pcgcctl.d32 = gr->pcgcctl_local | 0x00020000;
+		if (!rmode) {
+			pcgcctl.d32 |= 0x208;
+		}
+		DWC_WRITE_REG32(core_if->pcgcctl, pcgcctl.d32);
+		dwc_udelay(10);
+
+		/* Load restore values for [31:14] bits */
+		pcgcctl.d32 = gr->pcgcctl_local & 0xffffc000;
+		pcgcctl.d32 = gr->pcgcctl_local | 0x00020000;
+		pcgcctl.b.ess_reg_restored = 1;
+		if (!rmode)
+			pcgcctl.d32 |= 0x208;
+		DWC_WRITE_REG32(core_if->pcgcctl, pcgcctl.d32);
+	}
+
+	return 0;
+}
+
+/**
+ * Initializes the FSLSPClkSel field of the HCFG register depending on the PHY
+ * type.
+ */
+static void init_fslspclksel(dwc_otg_core_if_t * core_if)
+{
+	uint32_t val;
+	hcfg_data_t hcfg;
+
+	if (((core_if->hwcfg2.b.hs_phy_type == 2) &&
+	     (core_if->hwcfg2.b.fs_phy_type == 1) &&
+	     (core_if->core_params->ulpi_fs_ls)) ||
+	    (core_if->core_params->phy_type == DWC_PHY_TYPE_PARAM_FS)) {
+		/* Full speed PHY */
+		val = DWC_HCFG_48_MHZ;
+	} else {
+		/* High speed PHY running at full speed or high speed */
+		val = DWC_HCFG_30_60_MHZ;
+	}
+
+	DWC_DEBUGPL(DBG_CIL, "Initializing HCFG.FSLSPClkSel to 0x%1x\n", val);
+	hcfg.d32 = DWC_READ_REG32(&core_if->host_if->host_global_regs->hcfg);
+	hcfg.b.fslspclksel = val;
+	DWC_WRITE_REG32(&core_if->host_if->host_global_regs->hcfg, hcfg.d32);
+}
+
+/**
+ * Initializes the DevSpd field of the DCFG register depending on the PHY type
+ * and the enumeration speed of the device.
+ */
+static void init_devspd(dwc_otg_core_if_t * core_if)
+{
+	uint32_t val;
+	dcfg_data_t dcfg;
+
+	if (((core_if->hwcfg2.b.hs_phy_type == 2) &&
+	     (core_if->hwcfg2.b.fs_phy_type == 1) &&
+	     (core_if->core_params->ulpi_fs_ls)) ||
+	    (core_if->core_params->phy_type == DWC_PHY_TYPE_PARAM_FS)) {
+		/* Full speed PHY */
+		val = 0x3;
+	} else if (core_if->core_params->speed == DWC_SPEED_PARAM_FULL) {
+		/* High speed PHY running at full speed */
+		val = 0x1;
+	} else {
+		/* High speed PHY running at high speed */
+		val = 0x0;
+	}
+
+	DWC_DEBUGPL(DBG_CIL, "Initializing DCFG.DevSpd to 0x%1x\n", val);
+
+	dcfg.d32 = DWC_READ_REG32(&core_if->dev_if->dev_global_regs->dcfg);
+	dcfg.b.devspd = val;
+	DWC_WRITE_REG32(&core_if->dev_if->dev_global_regs->dcfg, dcfg.d32);
+}
+
+/**
+ * This function calculates the number of IN EPS
+ * using GHWCFG1 and GHWCFG2 registers values
+ *
+ * @param core_if Programming view of the DWC_otg controller
+ */
+static uint32_t calc_num_in_eps(dwc_otg_core_if_t * core_if)
+{
+	uint32_t num_in_eps = 0;
+	uint32_t num_eps = core_if->hwcfg2.b.num_dev_ep;
+	uint32_t hwcfg1 = core_if->hwcfg1.d32 >> 3;
+	uint32_t num_tx_fifos = core_if->hwcfg4.b.num_in_eps;
+	int i;
+
+	for (i = 0; i < num_eps; ++i) {
+		if (!(hwcfg1 & 0x1))
+			num_in_eps++;
+
+		hwcfg1 >>= 2;
+	}
+
+	if (core_if->hwcfg4.b.ded_fifo_en) {
+		num_in_eps =
+		    (num_in_eps > num_tx_fifos) ? num_tx_fifos : num_in_eps;
+	}
+
+	return num_in_eps;
+}
+
+/**
+ * This function calculates the number of OUT EPS
+ * using GHWCFG1 and GHWCFG2 registers values
+ *
+ * @param core_if Programming view of the DWC_otg controller
+ */
+static uint32_t calc_num_out_eps(dwc_otg_core_if_t * core_if)
+{
+	uint32_t num_out_eps = 0;
+	uint32_t num_eps = core_if->hwcfg2.b.num_dev_ep;
+	uint32_t hwcfg1 = core_if->hwcfg1.d32 >> 2;
+	int i;
+
+	for (i = 0; i < num_eps; ++i) {
+		if (!(hwcfg1 & 0x1))
+			num_out_eps++;
+
+		hwcfg1 >>= 2;
+	}
+	return num_out_eps;
+}
+
+/**
+ * This function initializes the DWC_otg controller registers and
+ * prepares the core for device mode or host mode operation.
+ *
+ * @param core_if Programming view of the DWC_otg controller
+ *
+ */
+void dwc_otg_core_init(dwc_otg_core_if_t * core_if)
+{
+	int i = 0;
+	dwc_otg_core_global_regs_t *global_regs = core_if->core_global_regs;
+	dwc_otg_dev_if_t *dev_if = core_if->dev_if;
+	gahbcfg_data_t ahbcfg = {.d32 = 0 };
+	gusbcfg_data_t usbcfg = {.d32 = 0 };
+	gi2cctl_data_t i2cctl = {.d32 = 0 };
+
+	DWC_DEBUGPL(DBG_CILV, "dwc_otg_core_init(%p) regs at %p\n",
+                    core_if, global_regs);
+
+	/* Common Initialization */
+	usbcfg.d32 = DWC_READ_REG32(&global_regs->gusbcfg);
+
+	/* Program the ULPI External VBUS bit if needed */
+	usbcfg.b.ulpi_ext_vbus_drv =
+	    (core_if->core_params->phy_ulpi_ext_vbus ==
+	     DWC_PHY_ULPI_EXTERNAL_VBUS) ? 1 : 0;
+
+	/* Set external TS Dline pulsing */
+	usbcfg.b.term_sel_dl_pulse =
+	    (core_if->core_params->ts_dline == 1) ? 1 : 0;
+	DWC_WRITE_REG32(&global_regs->gusbcfg, usbcfg.d32);
+
+	/* Reset the Controller */
+	dwc_otg_core_reset(core_if);
+
+	core_if->adp_enable = core_if->core_params->adp_supp_enable;
+	core_if->power_down = core_if->core_params->power_down;
+	core_if->otg_sts = 0;
+
+	/* Initialize parameters from Hardware configuration registers. */
+	dev_if->num_in_eps = calc_num_in_eps(core_if);
+	dev_if->num_out_eps = calc_num_out_eps(core_if);
+
+	DWC_DEBUGPL(DBG_CIL, "num_dev_perio_in_ep=%d\n",
+		    core_if->hwcfg4.b.num_dev_perio_in_ep);
+
+	for (i = 0; i < core_if->hwcfg4.b.num_dev_perio_in_ep; i++) {
+		dev_if->perio_tx_fifo_size[i] =
+		    DWC_READ_REG32(&global_regs->dtxfsiz[i]) >> 16;
+		DWC_DEBUGPL(DBG_CIL, "Periodic Tx FIFO SZ #%d=0x%0x\n",
+			    i, dev_if->perio_tx_fifo_size[i]);
+	}
+
+	for (i = 0; i < core_if->hwcfg4.b.num_in_eps; i++) {
+		dev_if->tx_fifo_size[i] =
+		    DWC_READ_REG32(&global_regs->dtxfsiz[i]) >> 16;
+		DWC_DEBUGPL(DBG_CIL, "Tx FIFO SZ #%d=0x%0x\n",
+			    i, dev_if->tx_fifo_size[i]);
+	}
+
+	core_if->total_fifo_size = core_if->hwcfg3.b.dfifo_depth;
+	core_if->rx_fifo_size = DWC_READ_REG32(&global_regs->grxfsiz);
+	core_if->nperio_tx_fifo_size =
+	    DWC_READ_REG32(&global_regs->gnptxfsiz) >> 16;
+
+	DWC_DEBUGPL(DBG_CIL, "Total FIFO SZ=%d\n", core_if->total_fifo_size);
+	DWC_DEBUGPL(DBG_CIL, "Rx FIFO SZ=%d\n", core_if->rx_fifo_size);
+	DWC_DEBUGPL(DBG_CIL, "NP Tx FIFO SZ=%d\n",
+		    core_if->nperio_tx_fifo_size);
+
+	/* This programming sequence needs to happen in FS mode before any other
+	 * programming occurs */
+	if ((core_if->core_params->speed == DWC_SPEED_PARAM_FULL) &&
+	    (core_if->core_params->phy_type == DWC_PHY_TYPE_PARAM_FS)) {
+		/* If FS mode with FS PHY */
+
+		/* core_init() is now called on every switch so only call the
+		 * following for the first time through. */
+		if (!core_if->phy_init_done) {
+			core_if->phy_init_done = 1;
+			DWC_DEBUGPL(DBG_CIL, "FS_PHY detected\n");
+			usbcfg.d32 = DWC_READ_REG32(&global_regs->gusbcfg);
+			usbcfg.b.physel = 1;
+			DWC_WRITE_REG32(&global_regs->gusbcfg, usbcfg.d32);
+
+			/* Reset after a PHY select */
+			dwc_otg_core_reset(core_if);
+		}
+
+		/* Program DCFG.DevSpd or HCFG.FSLSPclkSel to 48Mhz in FS.      Also
+		 * do this on HNP Dev/Host mode switches (done in dev_init and
+		 * host_init). */
+		if (dwc_otg_is_host_mode(core_if)) {
+			init_fslspclksel(core_if);
+		} else {
+			init_devspd(core_if);
+		}
+
+		if (core_if->core_params->i2c_enable) {
+			DWC_DEBUGPL(DBG_CIL, "FS_PHY Enabling I2c\n");
+			/* Program GUSBCFG.OtgUtmifsSel to I2C */
+			usbcfg.d32 = DWC_READ_REG32(&global_regs->gusbcfg);
+			usbcfg.b.otgutmifssel = 1;
+			DWC_WRITE_REG32(&global_regs->gusbcfg, usbcfg.d32);
+
+			/* Program GI2CCTL.I2CEn */
+			i2cctl.d32 = DWC_READ_REG32(&global_regs->gi2cctl);
+			i2cctl.b.i2cdevaddr = 1;
+			i2cctl.b.i2cen = 0;
+			DWC_WRITE_REG32(&global_regs->gi2cctl, i2cctl.d32);
+			i2cctl.b.i2cen = 1;
+			DWC_WRITE_REG32(&global_regs->gi2cctl, i2cctl.d32);
+		}
+
+	} /* endif speed == DWC_SPEED_PARAM_FULL */
+	else {
+		/* High speed PHY. */
+		if (!core_if->phy_init_done) {
+			core_if->phy_init_done = 1;
+			/* HS PHY parameters.  These parameters are preserved
+			 * during soft reset so only program the first time.  Do
+			 * a soft reset immediately after setting phyif.  */
+
+			if (core_if->core_params->phy_type == 2) {
+				/* ULPI interface */
+				usbcfg.b.ulpi_utmi_sel = 1;
+				usbcfg.b.phyif = 0;
+				usbcfg.b.ddrsel =
+				    core_if->core_params->phy_ulpi_ddr;
+			} else if (core_if->core_params->phy_type == 1) {
+				/* UTMI+ interface */
+				usbcfg.b.ulpi_utmi_sel = 0;
+				if (core_if->core_params->phy_utmi_width == 16) {
+					usbcfg.b.phyif = 1;
+
+				} else {
+					usbcfg.b.phyif = 0;
+				}
+			} else {
+				DWC_ERROR("FS PHY TYPE\n");
+			}
+			DWC_WRITE_REG32(&global_regs->gusbcfg, usbcfg.d32);
+			/* Reset after setting the PHY parameters */
+			dwc_otg_core_reset(core_if);
+		}
+	}
+
+	if ((core_if->hwcfg2.b.hs_phy_type == 2) &&
+	    (core_if->hwcfg2.b.fs_phy_type == 1) &&
+	    (core_if->core_params->ulpi_fs_ls)) {
+		DWC_DEBUGPL(DBG_CIL, "Setting ULPI FSLS\n");
+		usbcfg.d32 = DWC_READ_REG32(&global_regs->gusbcfg);
+		usbcfg.b.ulpi_fsls = 1;
+		usbcfg.b.ulpi_clk_sus_m = 1;
+		DWC_WRITE_REG32(&global_regs->gusbcfg, usbcfg.d32);
+	} else {
+		usbcfg.d32 = DWC_READ_REG32(&global_regs->gusbcfg);
+		usbcfg.b.ulpi_fsls = 0;
+		usbcfg.b.ulpi_clk_sus_m = 0;
+		DWC_WRITE_REG32(&global_regs->gusbcfg, usbcfg.d32);
+	}
+
+	/* Program the GAHBCFG Register. */
+	switch (core_if->hwcfg2.b.architecture) {
+
+	case DWC_SLAVE_ONLY_ARCH:
+		DWC_DEBUGPL(DBG_CIL, "Slave Only Mode\n");
+		ahbcfg.b.nptxfemplvl_txfemplvl =
+		    DWC_GAHBCFG_TXFEMPTYLVL_HALFEMPTY;
+		ahbcfg.b.ptxfemplvl = DWC_GAHBCFG_TXFEMPTYLVL_HALFEMPTY;
+		core_if->dma_enable = 0;
+		core_if->dma_desc_enable = 0;
+		break;
+
+	case DWC_EXT_DMA_ARCH:
+		DWC_DEBUGPL(DBG_CIL, "External DMA Mode\n");
+		{
+			uint8_t brst_sz = core_if->core_params->dma_burst_size;
+			ahbcfg.b.hburstlen = 0;
+			while (brst_sz > 1) {
+				ahbcfg.b.hburstlen++;
+				brst_sz >>= 1;
+			}
+		}
+		core_if->dma_enable = (core_if->core_params->dma_enable != 0);
+		core_if->dma_desc_enable =
+		    (core_if->core_params->dma_desc_enable != 0);
+		break;
+
+	case DWC_INT_DMA_ARCH:
+		DWC_DEBUGPL(DBG_CIL, "Internal DMA Mode\n");
+		/* Old value was DWC_GAHBCFG_INT_DMA_BURST_INCR - done for
+		  Host mode ISOC in issue fix - vahrama */
+		/* Broadcom had altered to (1<<3)|(0<<0) - WRESP=1, max 4 beats */
+		ahbcfg.b.hburstlen = (1<<3)|(0<<0);//DWC_GAHBCFG_INT_DMA_BURST_INCR4;
+		core_if->dma_enable = (core_if->core_params->dma_enable != 0);
+		core_if->dma_desc_enable =
+		    (core_if->core_params->dma_desc_enable != 0);
+		break;
+
+	}
+	if (core_if->dma_enable) {
+		if (core_if->dma_desc_enable) {
+			DWC_PRINTF("Using Descriptor DMA mode\n");
+		} else {
+			DWC_PRINTF("Using Buffer DMA mode\n");
+
+		}
+	} else {
+		DWC_PRINTF("Using Slave mode\n");
+		core_if->dma_desc_enable = 0;
+	}
+
+	if (core_if->core_params->ahb_single) {
+		ahbcfg.b.ahbsingle = 1;
+	}
+
+	ahbcfg.b.dmaenable = core_if->dma_enable;
+	DWC_WRITE_REG32(&global_regs->gahbcfg, ahbcfg.d32);
+
+	core_if->en_multiple_tx_fifo = core_if->hwcfg4.b.ded_fifo_en;
+
+	core_if->pti_enh_enable = core_if->core_params->pti_enable != 0;
+	core_if->multiproc_int_enable = core_if->core_params->mpi_enable;
+	DWC_PRINTF("Periodic Transfer Interrupt Enhancement - %s\n",
+		   ((core_if->pti_enh_enable) ? "enabled" : "disabled"));
+	DWC_PRINTF("Multiprocessor Interrupt Enhancement - %s\n",
+		   ((core_if->multiproc_int_enable) ? "enabled" : "disabled"));
+
+	/*
+	 * Program the GUSBCFG register.
+	 */
+	usbcfg.d32 = DWC_READ_REG32(&global_regs->gusbcfg);
+
+	switch (core_if->hwcfg2.b.op_mode) {
+	case DWC_MODE_HNP_SRP_CAPABLE:
+		usbcfg.b.hnpcap = (core_if->core_params->otg_cap ==
+				   DWC_OTG_CAP_PARAM_HNP_SRP_CAPABLE);
+		usbcfg.b.srpcap = (core_if->core_params->otg_cap !=
+				   DWC_OTG_CAP_PARAM_NO_HNP_SRP_CAPABLE);
+		break;
+
+	case DWC_MODE_SRP_ONLY_CAPABLE:
+		usbcfg.b.hnpcap = 0;
+		usbcfg.b.srpcap = (core_if->core_params->otg_cap !=
+				   DWC_OTG_CAP_PARAM_NO_HNP_SRP_CAPABLE);
+		break;
+
+	case DWC_MODE_NO_HNP_SRP_CAPABLE:
+		usbcfg.b.hnpcap = 0;
+		usbcfg.b.srpcap = 0;
+		break;
+
+	case DWC_MODE_SRP_CAPABLE_DEVICE:
+		usbcfg.b.hnpcap = 0;
+		usbcfg.b.srpcap = (core_if->core_params->otg_cap !=
+				   DWC_OTG_CAP_PARAM_NO_HNP_SRP_CAPABLE);
+		break;
+
+	case DWC_MODE_NO_SRP_CAPABLE_DEVICE:
+		usbcfg.b.hnpcap = 0;
+		usbcfg.b.srpcap = 0;
+		break;
+
+	case DWC_MODE_SRP_CAPABLE_HOST:
+		usbcfg.b.hnpcap = 0;
+		usbcfg.b.srpcap = (core_if->core_params->otg_cap !=
+				   DWC_OTG_CAP_PARAM_NO_HNP_SRP_CAPABLE);
+		break;
+
+	case DWC_MODE_NO_SRP_CAPABLE_HOST:
+		usbcfg.b.hnpcap = 0;
+		usbcfg.b.srpcap = 0;
+		break;
+	}
+
+	DWC_WRITE_REG32(&global_regs->gusbcfg, usbcfg.d32);
+
+#ifdef CONFIG_USB_DWC_OTG_LPM
+	if (core_if->core_params->lpm_enable) {
+		glpmcfg_data_t lpmcfg = {.d32 = 0 };
+
+		/* To enable LPM support set lpm_cap_en bit */
+		lpmcfg.b.lpm_cap_en = 1;
+
+		/* Make AppL1Res ACK */
+		lpmcfg.b.appl_resp = 1;
+
+		/* Retry 3 times */
+		lpmcfg.b.retry_count = 3;
+
+		DWC_MODIFY_REG32(&core_if->core_global_regs->glpmcfg,
+				 0, lpmcfg.d32);
+
+	}
+#endif
+	if (core_if->core_params->ic_usb_cap) {
+		gusbcfg_data_t gusbcfg = {.d32 = 0 };
+		gusbcfg.b.ic_usb_cap = 1;
+		DWC_MODIFY_REG32(&core_if->core_global_regs->gusbcfg,
+				 0, gusbcfg.d32);
+	}
+	{
+		gotgctl_data_t gotgctl = {.d32 = 0 };
+		gotgctl.b.otgver = core_if->core_params->otg_ver;
+		DWC_MODIFY_REG32(&core_if->core_global_regs->gotgctl, 0,
+				 gotgctl.d32);
+		/* Set OTG version supported */
+		core_if->otg_ver = core_if->core_params->otg_ver;
+		DWC_PRINTF("OTG VER PARAM: %d, OTG VER FLAG: %d\n",
+			   core_if->core_params->otg_ver, core_if->otg_ver);
+	}
+
+
+	/* Enable common interrupts */
+	dwc_otg_enable_common_interrupts(core_if);
+
+	/* Do device or host intialization based on mode during PCD
+	 * and HCD initialization  */
+	if (dwc_otg_is_host_mode(core_if)) {
+		DWC_DEBUGPL(DBG_ANY, "Host Mode\n");
+		core_if->op_state = A_HOST;
+	} else {
+		DWC_DEBUGPL(DBG_ANY, "Device Mode\n");
+		core_if->op_state = B_PERIPHERAL;
+#ifdef DWC_DEVICE_ONLY
+		dwc_otg_core_dev_init(core_if);
+#endif
+	}
+}
+
+/**
+ * This function enables the Device mode interrupts.
+ *
+ * @param core_if Programming view of DWC_otg controller
+ */
+void dwc_otg_enable_device_interrupts(dwc_otg_core_if_t * core_if)
+{
+	gintmsk_data_t intr_mask = {.d32 = 0 };
+	dwc_otg_core_global_regs_t *global_regs = core_if->core_global_regs;
+
+	DWC_DEBUGPL(DBG_CIL, "%s()\n", __func__);
+
+	/* Disable all interrupts. */
+	DWC_WRITE_REG32(&global_regs->gintmsk, 0);
+
+	/* Clear any pending interrupts */
+	DWC_WRITE_REG32(&global_regs->gintsts, 0xFFFFFFFF);
+
+	/* Enable the common interrupts */
+	dwc_otg_enable_common_interrupts(core_if);
+
+	/* Enable interrupts */
+	intr_mask.b.usbreset = 1;
+	intr_mask.b.enumdone = 1;
+	/* Disable Disconnect interrupt in Device mode */
+	intr_mask.b.disconnect = 0;
+
+	if (!core_if->multiproc_int_enable) {
+		intr_mask.b.inepintr = 1;
+		intr_mask.b.outepintr = 1;
+	}
+
+	intr_mask.b.erlysuspend = 1;
+
+	if (core_if->en_multiple_tx_fifo == 0) {
+		intr_mask.b.epmismatch = 1;
+	}
+
+	//intr_mask.b.incomplisoout = 1;
+	intr_mask.b.incomplisoin = 1;
+
+/* Enable the ignore frame number for ISOC xfers - MAS */
+/* Disable to support high bandwith ISOC transfers - manukz */
+#if 0
+#ifdef DWC_UTE_PER_IO
+	if (core_if->dma_enable) {
+		if (core_if->dma_desc_enable) {
+			dctl_data_t dctl1 = {.d32 = 0 };
+			dctl1.b.ifrmnum = 1;
+			DWC_MODIFY_REG32(&core_if->dev_if->dev_global_regs->
+					 dctl, 0, dctl1.d32);
+			DWC_DEBUG("----Enabled Ignore frame number (0x%08x)",
+				  DWC_READ_REG32(&core_if->dev_if->
+						 dev_global_regs->dctl));
+		}
+	}
+#endif
+#endif
+#ifdef DWC_EN_ISOC
+	if (core_if->dma_enable) {
+		if (core_if->dma_desc_enable == 0) {
+			if (core_if->pti_enh_enable) {
+				dctl_data_t dctl = {.d32 = 0 };
+				dctl.b.ifrmnum = 1;
+				DWC_MODIFY_REG32(&core_if->
+						 dev_if->dev_global_regs->dctl,
+						 0, dctl.d32);
+			} else {
+				intr_mask.b.incomplisoin = 1;
+				intr_mask.b.incomplisoout = 1;
+			}
+		}
+	} else {
+		intr_mask.b.incomplisoin = 1;
+		intr_mask.b.incomplisoout = 1;
+	}
+#endif /* DWC_EN_ISOC */
+
+	/** @todo NGS: Should this be a module parameter? */
+#ifdef USE_PERIODIC_EP
+	intr_mask.b.isooutdrop = 1;
+	intr_mask.b.eopframe = 1;
+	intr_mask.b.incomplisoin = 1;
+	intr_mask.b.incomplisoout = 1;
+#endif
+
+	DWC_MODIFY_REG32(&global_regs->gintmsk, intr_mask.d32, intr_mask.d32);
+
+	DWC_DEBUGPL(DBG_CIL, "%s() gintmsk=%0x\n", __func__,
+		    DWC_READ_REG32(&global_regs->gintmsk));
+}
+
+/**
+ * This function initializes the DWC_otg controller registers for
+ * device mode.
+ *
+ * @param core_if Programming view of DWC_otg controller
+ *
+ */
+void dwc_otg_core_dev_init(dwc_otg_core_if_t * core_if)
+{
+	int i;
+	dwc_otg_core_global_regs_t *global_regs = core_if->core_global_regs;
+	dwc_otg_dev_if_t *dev_if = core_if->dev_if;
+	dwc_otg_core_params_t *params = core_if->core_params;
+	dcfg_data_t dcfg = {.d32 = 0 };
+	depctl_data_t diepctl = {.d32 = 0 };
+	grstctl_t resetctl = {.d32 = 0 };
+	uint32_t rx_fifo_size;
+	fifosize_data_t nptxfifosize;
+	fifosize_data_t txfifosize;
+	dthrctl_data_t dthrctl;
+	fifosize_data_t ptxfifosize;
+	uint16_t rxfsiz, nptxfsiz;
+	gdfifocfg_data_t gdfifocfg = {.d32 = 0 };
+	hwcfg3_data_t hwcfg3 = {.d32 = 0 };
+
+	/* Restart the Phy Clock */
+	DWC_WRITE_REG32(core_if->pcgcctl, 0);
+
+	/* Device configuration register */
+	init_devspd(core_if);
+	dcfg.d32 = DWC_READ_REG32(&dev_if->dev_global_regs->dcfg);
+	dcfg.b.descdma = (core_if->dma_desc_enable) ? 1 : 0;
+	dcfg.b.perfrint = DWC_DCFG_FRAME_INTERVAL_80;
+	/* Enable Device OUT NAK in case of DDMA mode*/
+	if (core_if->core_params->dev_out_nak) {
+		dcfg.b.endevoutnak = 1;
+	}
+
+	if (core_if->core_params->cont_on_bna) {
+		dctl_data_t dctl = {.d32 = 0 };
+		dctl.b.encontonbna = 1;
+		DWC_MODIFY_REG32(&dev_if->dev_global_regs->dctl, 0, dctl.d32);
+	}
+
+
+	DWC_WRITE_REG32(&dev_if->dev_global_regs->dcfg, dcfg.d32);
+
+	/* Configure data FIFO sizes */
+	if (core_if->hwcfg2.b.dynamic_fifo && params->enable_dynamic_fifo) {
+		DWC_DEBUGPL(DBG_CIL, "Total FIFO Size=%d\n",
+			    core_if->total_fifo_size);
+		DWC_DEBUGPL(DBG_CIL, "Rx FIFO Size=%d\n",
+			    params->dev_rx_fifo_size);
+		DWC_DEBUGPL(DBG_CIL, "NP Tx FIFO Size=%d\n",
+			    params->dev_nperio_tx_fifo_size);
+
+		/* Rx FIFO */
+		DWC_DEBUGPL(DBG_CIL, "initial grxfsiz=%08x\n",
+			    DWC_READ_REG32(&global_regs->grxfsiz));
+
+#ifdef DWC_UTE_CFI
+		core_if->pwron_rxfsiz = DWC_READ_REG32(&global_regs->grxfsiz);
+		core_if->init_rxfsiz = params->dev_rx_fifo_size;
+#endif
+		rx_fifo_size = params->dev_rx_fifo_size;
+		DWC_WRITE_REG32(&global_regs->grxfsiz, rx_fifo_size);
+
+		DWC_DEBUGPL(DBG_CIL, "new grxfsiz=%08x\n",
+			    DWC_READ_REG32(&global_regs->grxfsiz));
+
+		/** Set Periodic Tx FIFO Mask all bits 0 */
+		core_if->p_tx_msk = 0;
+
+		/** Set Tx FIFO Mask all bits 0 */
+		core_if->tx_msk = 0;
+
+		if (core_if->en_multiple_tx_fifo == 0) {
+			/* Non-periodic Tx FIFO */
+			DWC_DEBUGPL(DBG_CIL, "initial gnptxfsiz=%08x\n",
+				    DWC_READ_REG32(&global_regs->gnptxfsiz));
+
+			nptxfifosize.b.depth = params->dev_nperio_tx_fifo_size;
+			nptxfifosize.b.startaddr = params->dev_rx_fifo_size;
+
+			DWC_WRITE_REG32(&global_regs->gnptxfsiz,
+					nptxfifosize.d32);
+
+			DWC_DEBUGPL(DBG_CIL, "new gnptxfsiz=%08x\n",
+				    DWC_READ_REG32(&global_regs->gnptxfsiz));
+
+			/**@todo NGS: Fix Periodic FIFO Sizing! */
+			/*
+			 * Periodic Tx FIFOs These FIFOs are numbered from 1 to 15.
+			 * Indexes of the FIFO size module parameters in the
+			 * dev_perio_tx_fifo_size array and the FIFO size registers in
+			 * the dptxfsiz array run from 0 to 14.
+			 */
+			/** @todo Finish debug of this */
+			ptxfifosize.b.startaddr =
+			    nptxfifosize.b.startaddr + nptxfifosize.b.depth;
+			for (i = 0; i < core_if->hwcfg4.b.num_dev_perio_in_ep; i++) {
+				ptxfifosize.b.depth =
+				    params->dev_perio_tx_fifo_size[i];
+				DWC_DEBUGPL(DBG_CIL,
+					    "initial dtxfsiz[%d]=%08x\n", i,
+					    DWC_READ_REG32(&global_regs->dtxfsiz
+							   [i]));
+				DWC_WRITE_REG32(&global_regs->dtxfsiz[i],
+						ptxfifosize.d32);
+				DWC_DEBUGPL(DBG_CIL, "new dtxfsiz[%d]=%08x\n",
+					    i,
+					    DWC_READ_REG32(&global_regs->dtxfsiz
+							   [i]));
+				ptxfifosize.b.startaddr += ptxfifosize.b.depth;
+			}
+		} else {
+			/*
+			 * Tx FIFOs These FIFOs are numbered from 1 to 15.
+			 * Indexes of the FIFO size module parameters in the
+			 * dev_tx_fifo_size array and the FIFO size registers in
+			 * the dtxfsiz array run from 0 to 14.
+			 */
+
+			/* Non-periodic Tx FIFO */
+			DWC_DEBUGPL(DBG_CIL, "initial gnptxfsiz=%08x\n",
+				    DWC_READ_REG32(&global_regs->gnptxfsiz));
+
+#ifdef DWC_UTE_CFI
+			core_if->pwron_gnptxfsiz =
+			    (DWC_READ_REG32(&global_regs->gnptxfsiz) >> 16);
+			core_if->init_gnptxfsiz =
+			    params->dev_nperio_tx_fifo_size;
+#endif
+			nptxfifosize.b.depth = params->dev_nperio_tx_fifo_size;
+			nptxfifosize.b.startaddr = params->dev_rx_fifo_size;
+
+			DWC_WRITE_REG32(&global_regs->gnptxfsiz,
+					nptxfifosize.d32);
+
+			DWC_DEBUGPL(DBG_CIL, "new gnptxfsiz=%08x\n",
+				    DWC_READ_REG32(&global_regs->gnptxfsiz));
+
+			txfifosize.b.startaddr =
+			    nptxfifosize.b.startaddr + nptxfifosize.b.depth;
+
+			for (i = 0; i < core_if->hwcfg4.b.num_in_eps; i++) {
+
+				txfifosize.b.depth =
+				    params->dev_tx_fifo_size[i];
+
+				DWC_DEBUGPL(DBG_CIL,
+					    "initial dtxfsiz[%d]=%08x\n",
+					    i,
+					    DWC_READ_REG32(&global_regs->dtxfsiz
+							   [i]));
+
+#ifdef DWC_UTE_CFI
+				core_if->pwron_txfsiz[i] =
+				    (DWC_READ_REG32
+				     (&global_regs->dtxfsiz[i]) >> 16);
+				core_if->init_txfsiz[i] =
+				    params->dev_tx_fifo_size[i];
+#endif
+				DWC_WRITE_REG32(&global_regs->dtxfsiz[i],
+						txfifosize.d32);
+
+				DWC_DEBUGPL(DBG_CIL,
+					    "new dtxfsiz[%d]=%08x\n",
+					    i,
+					    DWC_READ_REG32(&global_regs->dtxfsiz
+							   [i]));
+
+				txfifosize.b.startaddr += txfifosize.b.depth;
+			}
+			if (core_if->snpsid <= OTG_CORE_REV_2_94a) {
+				/* Calculating DFIFOCFG for Device mode to include RxFIFO and NPTXFIFO */
+				gdfifocfg.d32 = DWC_READ_REG32(&global_regs->gdfifocfg);
+				hwcfg3.d32 = DWC_READ_REG32(&global_regs->ghwcfg3);
+				gdfifocfg.b.gdfifocfg = (DWC_READ_REG32(&global_regs->ghwcfg3) >> 16);
+				DWC_WRITE_REG32(&global_regs->gdfifocfg, gdfifocfg.d32);
+				rxfsiz = (DWC_READ_REG32(&global_regs->grxfsiz) & 0x0000ffff);
+				nptxfsiz = (DWC_READ_REG32(&global_regs->gnptxfsiz) >> 16);
+				gdfifocfg.b.epinfobase = rxfsiz + nptxfsiz;
+				DWC_WRITE_REG32(&global_regs->gdfifocfg, gdfifocfg.d32);
+			}
+		}
+
+		/* Flush the FIFOs */
+		dwc_otg_flush_tx_fifo(core_if, 0x10);	/* all Tx FIFOs */
+		dwc_otg_flush_rx_fifo(core_if);
+
+		/* Flush the Learning Queue. */
+		resetctl.b.intknqflsh = 1;
+		DWC_WRITE_REG32(&core_if->core_global_regs->grstctl, resetctl.d32);
+
+		if (!core_if->core_params->en_multiple_tx_fifo && core_if->dma_enable) {
+			core_if->start_predict = 0;
+			for (i = 0; i<= core_if->dev_if->num_in_eps; ++i) {
+				core_if->nextep_seq[i] = 0xff;	// 0xff - EP not active
+			}
+			core_if->nextep_seq[0] = 0;
+			core_if->first_in_nextep_seq = 0;
+			diepctl.d32 = DWC_READ_REG32(&dev_if->in_ep_regs[0]->diepctl);
+			diepctl.b.nextep = 0;
+			DWC_WRITE_REG32(&dev_if->in_ep_regs[0]->diepctl, diepctl.d32);
+
+			/* Update IN Endpoint Mismatch Count by active IN NP EP count + 1 */
+			dcfg.d32 = DWC_READ_REG32(&dev_if->dev_global_regs->dcfg);
+			dcfg.b.epmscnt = 2;
+			DWC_WRITE_REG32(&dev_if->dev_global_regs->dcfg, dcfg.d32);
+
+			DWC_DEBUGPL(DBG_CILV,"%s first_in_nextep_seq= %2d; nextep_seq[]:\n",
+				__func__, core_if->first_in_nextep_seq);
+			for (i=0; i <= core_if->dev_if->num_in_eps; i++) {
+				DWC_DEBUGPL(DBG_CILV, "%2d ", core_if->nextep_seq[i]);
+			}
+			DWC_DEBUGPL(DBG_CILV,"\n");
+		}
+
+		/* Clear all pending Device Interrupts */
+		/** @todo - if the condition needed to be checked
+		 *  or in any case all pending interrutps should be cleared?
+	     */
+		if (core_if->multiproc_int_enable) {
+			for (i = 0; i < core_if->dev_if->num_in_eps; ++i) {
+				DWC_WRITE_REG32(&dev_if->
+						dev_global_regs->diepeachintmsk[i], 0);
+			}
+		}
+
+		for (i = 0; i < core_if->dev_if->num_out_eps; ++i) {
+			DWC_WRITE_REG32(&dev_if->
+					dev_global_regs->doepeachintmsk[i], 0);
+		}
+
+		DWC_WRITE_REG32(&dev_if->dev_global_regs->deachint, 0xFFFFFFFF);
+		DWC_WRITE_REG32(&dev_if->dev_global_regs->deachintmsk, 0);
+	} else {
+		DWC_WRITE_REG32(&dev_if->dev_global_regs->diepmsk, 0);
+		DWC_WRITE_REG32(&dev_if->dev_global_regs->doepmsk, 0);
+		DWC_WRITE_REG32(&dev_if->dev_global_regs->daint, 0xFFFFFFFF);
+		DWC_WRITE_REG32(&dev_if->dev_global_regs->daintmsk, 0);
+	}
+
+	for (i = 0; i <= dev_if->num_in_eps; i++) {
+		depctl_data_t depctl;
+		depctl.d32 = DWC_READ_REG32(&dev_if->in_ep_regs[i]->diepctl);
+		if (depctl.b.epena) {
+			depctl.d32 = 0;
+			depctl.b.epdis = 1;
+			depctl.b.snak = 1;
+		} else {
+			depctl.d32 = 0;
+		}
+
+		DWC_WRITE_REG32(&dev_if->in_ep_regs[i]->diepctl, depctl.d32);
+
+		DWC_WRITE_REG32(&dev_if->in_ep_regs[i]->dieptsiz, 0);
+		DWC_WRITE_REG32(&dev_if->in_ep_regs[i]->diepdma, 0);
+		DWC_WRITE_REG32(&dev_if->in_ep_regs[i]->diepint, 0xFF);
+	}
+
+	for (i = 0; i <= dev_if->num_out_eps; i++) {
+		depctl_data_t depctl;
+		depctl.d32 = DWC_READ_REG32(&dev_if->out_ep_regs[i]->doepctl);
+		if (depctl.b.epena) {
+			dctl_data_t dctl = {.d32 = 0 };
+			gintmsk_data_t gintsts = {.d32 = 0 };
+			doepint_data_t doepint = {.d32 = 0 };
+			dctl.b.sgoutnak = 1;
+			DWC_MODIFY_REG32(&core_if->dev_if->dev_global_regs->dctl, 0, dctl.d32);
+			do {
+				dwc_udelay(10);
+				gintsts.d32 = DWC_READ_REG32(&core_if->core_global_regs->gintsts);
+			} while (!gintsts.b.goutnakeff);
+			gintsts.d32 = 0;
+			gintsts.b.goutnakeff = 1;
+			DWC_WRITE_REG32(&core_if->core_global_regs->gintsts, gintsts.d32);
+
+			depctl.d32 = 0;
+			depctl.b.epdis = 1;
+			depctl.b.snak = 1;
+			DWC_WRITE_REG32(&core_if->dev_if->out_ep_regs[i]->doepctl, depctl.d32);
+			do {
+				dwc_udelay(10);
+				doepint.d32 = DWC_READ_REG32(&core_if->dev_if->
+					out_ep_regs[i]->doepint);
+			} while (!doepint.b.epdisabled);
+
+			doepint.b.epdisabled = 1;
+			DWC_WRITE_REG32(&core_if->dev_if->out_ep_regs[i]->doepint, doepint.d32);
+
+			dctl.d32 = 0;
+			dctl.b.cgoutnak = 1;
+			DWC_MODIFY_REG32(&core_if->dev_if->dev_global_regs->dctl, 0, dctl.d32);
+		} else {
+			depctl.d32 = 0;
+		}
+
+		DWC_WRITE_REG32(&dev_if->out_ep_regs[i]->doepctl, depctl.d32);
+
+		DWC_WRITE_REG32(&dev_if->out_ep_regs[i]->doeptsiz, 0);
+		DWC_WRITE_REG32(&dev_if->out_ep_regs[i]->doepdma, 0);
+		DWC_WRITE_REG32(&dev_if->out_ep_regs[i]->doepint, 0xFF);
+	}
+
+	if (core_if->en_multiple_tx_fifo && core_if->dma_enable) {
+		dev_if->non_iso_tx_thr_en = params->thr_ctl & 0x1;
+		dev_if->iso_tx_thr_en = (params->thr_ctl >> 1) & 0x1;
+		dev_if->rx_thr_en = (params->thr_ctl >> 2) & 0x1;
+
+		dev_if->rx_thr_length = params->rx_thr_length;
+		dev_if->tx_thr_length = params->tx_thr_length;
+
+		dev_if->setup_desc_index = 0;
+
+		dthrctl.d32 = 0;
+		dthrctl.b.non_iso_thr_en = dev_if->non_iso_tx_thr_en;
+		dthrctl.b.iso_thr_en = dev_if->iso_tx_thr_en;
+		dthrctl.b.tx_thr_len = dev_if->tx_thr_length;
+		dthrctl.b.rx_thr_en = dev_if->rx_thr_en;
+		dthrctl.b.rx_thr_len = dev_if->rx_thr_length;
+		dthrctl.b.ahb_thr_ratio = params->ahb_thr_ratio;
+
+		DWC_WRITE_REG32(&dev_if->dev_global_regs->dtknqr3_dthrctl,
+				dthrctl.d32);
+
+		DWC_DEBUGPL(DBG_CIL,
+			    "Non ISO Tx Thr - %d\nISO Tx Thr - %d\nRx Thr - %d\nTx Thr Len - %d\nRx Thr Len - %d\n",
+			    dthrctl.b.non_iso_thr_en, dthrctl.b.iso_thr_en,
+			    dthrctl.b.rx_thr_en, dthrctl.b.tx_thr_len,
+			    dthrctl.b.rx_thr_len);
+
+	}
+
+	dwc_otg_enable_device_interrupts(core_if);
+
+	{
+		diepmsk_data_t msk = {.d32 = 0 };
+		msk.b.txfifoundrn = 1;
+		if (core_if->multiproc_int_enable) {
+			DWC_MODIFY_REG32(&dev_if->dev_global_regs->
+					 diepeachintmsk[0], msk.d32, msk.d32);
+		} else {
+			DWC_MODIFY_REG32(&dev_if->dev_global_regs->diepmsk,
+					 msk.d32, msk.d32);
+		}
+	}
+
+	if (core_if->multiproc_int_enable) {
+		/* Set NAK on Babble */
+		dctl_data_t dctl = {.d32 = 0 };
+		dctl.b.nakonbble = 1;
+		DWC_MODIFY_REG32(&dev_if->dev_global_regs->dctl, 0, dctl.d32);
+	}
+
+	if (core_if->snpsid >= OTG_CORE_REV_2_94a) {
+		dctl_data_t dctl = {.d32 = 0 };
+		dctl.d32 = DWC_READ_REG32(&dev_if->dev_global_regs->dctl);
+		dctl.b.sftdiscon = 0;
+		DWC_WRITE_REG32(&dev_if->dev_global_regs->dctl, dctl.d32);
+	}
+}
+
+/**
+ * This function enables the Host mode interrupts.
+ *
+ * @param core_if Programming view of DWC_otg controller
+ */
+void dwc_otg_enable_host_interrupts(dwc_otg_core_if_t * core_if)
+{
+	dwc_otg_core_global_regs_t *global_regs = core_if->core_global_regs;
+	gintmsk_data_t intr_mask = {.d32 = 0 };
+
+	DWC_DEBUGPL(DBG_CIL, "%s(%p)\n", __func__, core_if);
+
+	/* Disable all interrupts. */
+	DWC_WRITE_REG32(&global_regs->gintmsk, 0);
+
+	/* Clear any pending interrupts. */
+	DWC_WRITE_REG32(&global_regs->gintsts, 0xFFFFFFFF);
+
+	/* Enable the common interrupts */
+	dwc_otg_enable_common_interrupts(core_if);
+
+	/*
+	 * Enable host mode interrupts without disturbing common
+	 * interrupts.
+	 */
+
+	intr_mask.b.disconnect = 1;
+	intr_mask.b.portintr = 1;
+	intr_mask.b.hcintr = 1;
+
+	DWC_MODIFY_REG32(&global_regs->gintmsk, intr_mask.d32, intr_mask.d32);
+}
+
+/**
+ * This function disables the Host Mode interrupts.
+ *
+ * @param core_if Programming view of DWC_otg controller
+ */
+void dwc_otg_disable_host_interrupts(dwc_otg_core_if_t * core_if)
+{
+	dwc_otg_core_global_regs_t *global_regs = core_if->core_global_regs;
+	gintmsk_data_t intr_mask = {.d32 = 0 };
+
+	DWC_DEBUGPL(DBG_CILV, "%s()\n", __func__);
+
+	/*
+	 * Disable host mode interrupts without disturbing common
+	 * interrupts.
+	 */
+	intr_mask.b.sofintr = 1;
+	intr_mask.b.portintr = 1;
+	intr_mask.b.hcintr = 1;
+	intr_mask.b.ptxfempty = 1;
+	intr_mask.b.nptxfempty = 1;
+
+	DWC_MODIFY_REG32(&global_regs->gintmsk, intr_mask.d32, 0);
+}
+
+/**
+ * This function initializes the DWC_otg controller registers for
+ * host mode.
+ *
+ * This function flushes the Tx and Rx FIFOs and it flushes any entries in the
+ * request queues. Host channels are reset to ensure that they are ready for
+ * performing transfers.
+ *
+ * @param core_if Programming view of DWC_otg controller
+ *
+ */
+void dwc_otg_core_host_init(dwc_otg_core_if_t * core_if)
+{
+	dwc_otg_core_global_regs_t *global_regs = core_if->core_global_regs;
+	dwc_otg_host_if_t *host_if = core_if->host_if;
+	dwc_otg_core_params_t *params = core_if->core_params;
+	hprt0_data_t hprt0 = {.d32 = 0 };
+	fifosize_data_t nptxfifosize;
+	fifosize_data_t ptxfifosize;
+	uint16_t rxfsiz, nptxfsiz, hptxfsiz;
+	gdfifocfg_data_t gdfifocfg = {.d32 = 0 };
+	int i;
+	hcchar_data_t hcchar;
+	hcfg_data_t hcfg;
+	hfir_data_t hfir;
+	dwc_otg_hc_regs_t *hc_regs;
+	int num_channels;
+	gotgctl_data_t gotgctl = {.d32 = 0 };
+
+	DWC_DEBUGPL(DBG_CILV, "%s(%p)\n", __func__, core_if);
+
+	/* Restart the Phy Clock */
+	DWC_WRITE_REG32(core_if->pcgcctl, 0);
+
+	/* Initialize Host Configuration Register */
+	init_fslspclksel(core_if);
+	if (core_if->core_params->speed == DWC_SPEED_PARAM_FULL) {
+		hcfg.d32 = DWC_READ_REG32(&host_if->host_global_regs->hcfg);
+		hcfg.b.fslssupp = 1;
+		DWC_WRITE_REG32(&host_if->host_global_regs->hcfg, hcfg.d32);
+
+	}
+
+	/* This bit allows dynamic reloading of the HFIR register
+	 * during runtime. This bit needs to be programmed during
+	 * initial configuration and its value must not be changed
+	 * during runtime.*/
+	if (core_if->core_params->reload_ctl == 1) {
+		hfir.d32 = DWC_READ_REG32(&host_if->host_global_regs->hfir);
+		hfir.b.hfirrldctrl = 1;
+		DWC_WRITE_REG32(&host_if->host_global_regs->hfir, hfir.d32);
+	}
+
+	if (core_if->core_params->dma_desc_enable) {
+		uint8_t op_mode = core_if->hwcfg2.b.op_mode;
+		if (!
+		    (core_if->hwcfg4.b.desc_dma
+		     && (core_if->snpsid >= OTG_CORE_REV_2_90a)
+		     && ((op_mode == DWC_HWCFG2_OP_MODE_HNP_SRP_CAPABLE_OTG)
+			 || (op_mode == DWC_HWCFG2_OP_MODE_SRP_ONLY_CAPABLE_OTG)
+			 || (op_mode ==
+			     DWC_HWCFG2_OP_MODE_NO_HNP_SRP_CAPABLE_OTG)
+			 || (op_mode == DWC_HWCFG2_OP_MODE_SRP_CAPABLE_HOST)
+			 || (op_mode ==
+			     DWC_HWCFG2_OP_MODE_NO_SRP_CAPABLE_HOST)))) {
+
+			DWC_ERROR("Host can't operate in Descriptor DMA mode.\n"
+				  "Either core version is below 2.90a or "
+				  "GHWCFG2, GHWCFG4 registers' values do not allow Descriptor DMA in host mode.\n"
+				  "To run the driver in Buffer DMA host mode set dma_desc_enable "
+				  "module parameter to 0.\n");
+			return;
+		}
+		hcfg.d32 = DWC_READ_REG32(&host_if->host_global_regs->hcfg);
+		hcfg.b.descdma = 1;
+		DWC_WRITE_REG32(&host_if->host_global_regs->hcfg, hcfg.d32);
+	}
+
+	/* Configure data FIFO sizes */
+	if (core_if->hwcfg2.b.dynamic_fifo && params->enable_dynamic_fifo) {
+		DWC_DEBUGPL(DBG_CIL, "Total FIFO Size=%d\n",
+			    core_if->total_fifo_size);
+		DWC_DEBUGPL(DBG_CIL, "Rx FIFO Size=%d\n",
+			    params->host_rx_fifo_size);
+		DWC_DEBUGPL(DBG_CIL, "NP Tx FIFO Size=%d\n",
+			    params->host_nperio_tx_fifo_size);
+		DWC_DEBUGPL(DBG_CIL, "P Tx FIFO Size=%d\n",
+			    params->host_perio_tx_fifo_size);
+
+		/* Rx FIFO */
+		DWC_DEBUGPL(DBG_CIL, "initial grxfsiz=%08x\n",
+			    DWC_READ_REG32(&global_regs->grxfsiz));
+		DWC_WRITE_REG32(&global_regs->grxfsiz,
+				params->host_rx_fifo_size);
+		DWC_DEBUGPL(DBG_CIL, "new grxfsiz=%08x\n",
+			    DWC_READ_REG32(&global_regs->grxfsiz));
+
+		/* Non-periodic Tx FIFO */
+		DWC_DEBUGPL(DBG_CIL, "initial gnptxfsiz=%08x\n",
+			    DWC_READ_REG32(&global_regs->gnptxfsiz));
+		nptxfifosize.b.depth = params->host_nperio_tx_fifo_size;
+		nptxfifosize.b.startaddr = params->host_rx_fifo_size;
+		DWC_WRITE_REG32(&global_regs->gnptxfsiz, nptxfifosize.d32);
+		DWC_DEBUGPL(DBG_CIL, "new gnptxfsiz=%08x\n",
+			    DWC_READ_REG32(&global_regs->gnptxfsiz));
+
+		/* Periodic Tx FIFO */
+		DWC_DEBUGPL(DBG_CIL, "initial hptxfsiz=%08x\n",
+			    DWC_READ_REG32(&global_regs->hptxfsiz));
+		ptxfifosize.b.depth = params->host_perio_tx_fifo_size;
+		ptxfifosize.b.startaddr =
+		    nptxfifosize.b.startaddr + nptxfifosize.b.depth;
+		DWC_WRITE_REG32(&global_regs->hptxfsiz, ptxfifosize.d32);
+		DWC_DEBUGPL(DBG_CIL, "new hptxfsiz=%08x\n",
+			    DWC_READ_REG32(&global_regs->hptxfsiz));
+
+		if (core_if->en_multiple_tx_fifo
+		    && core_if->snpsid <= OTG_CORE_REV_2_94a) {
+			/* Global DFIFOCFG calculation for Host mode - include RxFIFO, NPTXFIFO and HPTXFIFO */
+			gdfifocfg.d32 = DWC_READ_REG32(&global_regs->gdfifocfg);
+			rxfsiz = (DWC_READ_REG32(&global_regs->grxfsiz) & 0x0000ffff);
+			nptxfsiz = (DWC_READ_REG32(&global_regs->gnptxfsiz) >> 16);
+			hptxfsiz = (DWC_READ_REG32(&global_regs->hptxfsiz) >> 16);
+			gdfifocfg.b.epinfobase = rxfsiz + nptxfsiz + hptxfsiz;
+			DWC_WRITE_REG32(&global_regs->gdfifocfg, gdfifocfg.d32);
+		}
+	}
+
+	/* TODO - check this */
+	/* Clear Host Set HNP Enable in the OTG Control Register */
+	gotgctl.b.hstsethnpen = 1;
+	DWC_MODIFY_REG32(&global_regs->gotgctl, gotgctl.d32, 0);
+	/* Make sure the FIFOs are flushed. */
+	dwc_otg_flush_tx_fifo(core_if, 0x10 /* all TX FIFOs */ );
+	dwc_otg_flush_rx_fifo(core_if);
+
+	/* Clear Host Set HNP Enable in the OTG Control Register */
+	gotgctl.b.hstsethnpen = 1;
+	DWC_MODIFY_REG32(&global_regs->gotgctl, gotgctl.d32, 0);
+
+	if (!core_if->core_params->dma_desc_enable) {
+		/* Flush out any leftover queued requests. */
+		num_channels = core_if->core_params->host_channels;
+
+		for (i = 0; i < num_channels; i++) {
+			hc_regs = core_if->host_if->hc_regs[i];
+			hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar);
+			hcchar.b.chen = 0;
+			hcchar.b.chdis = 1;
+			hcchar.b.epdir = 0;
+			DWC_WRITE_REG32(&hc_regs->hcchar, hcchar.d32);
+		}
+
+		/* Halt all channels to put them into a known state. */
+		for (i = 0; i < num_channels; i++) {
+			int count = 0;
+			hc_regs = core_if->host_if->hc_regs[i];
+			hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar);
+			hcchar.b.chen = 1;
+			hcchar.b.chdis = 1;
+			hcchar.b.epdir = 0;
+			DWC_WRITE_REG32(&hc_regs->hcchar, hcchar.d32);
+			DWC_DEBUGPL(DBG_HCDV, "%s: Halt channel %d regs %p\n", __func__, i, hc_regs);
+			do {
+				hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar);
+				if (++count > 1000) {
+					DWC_ERROR
+					    ("%s: Unable to clear halt on channel %d (timeout HCCHAR 0x%X @%p)\n",
+					     __func__, i, hcchar.d32, &hc_regs->hcchar);
+					break;
+				}
+				dwc_udelay(1);
+			} while (hcchar.b.chen);
+		}
+	}
+
+	/* Turn on the vbus power. */
+	DWC_PRINTF("Init: Port Power? op_state=%d\n", core_if->op_state);
+	if (core_if->op_state == A_HOST) {
+		hprt0.d32 = dwc_otg_read_hprt0(core_if);
+		DWC_PRINTF("Init: Power Port (%d)\n", hprt0.b.prtpwr);
+		if (hprt0.b.prtpwr == 0) {
+			hprt0.b.prtpwr = 1;
+			DWC_WRITE_REG32(host_if->hprt0, hprt0.d32);
+		}
+	}
+
+	dwc_otg_enable_host_interrupts(core_if);
+}
+
+/**
+ * Prepares a host channel for transferring packets to/from a specific
+ * endpoint. The HCCHARn register is set up with the characteristics specified
+ * in _hc. Host channel interrupts that may need to be serviced while this
+ * transfer is in progress are enabled.
+ *
+ * @param core_if Programming view of DWC_otg controller
+ * @param hc Information needed to initialize the host channel
+ */
+void dwc_otg_hc_init(dwc_otg_core_if_t * core_if, dwc_hc_t * hc)
+{
+	hcintmsk_data_t hc_intr_mask;
+	hcchar_data_t hcchar;
+	hcsplt_data_t hcsplt;
+
+	uint8_t hc_num = hc->hc_num;
+	dwc_otg_host_if_t *host_if = core_if->host_if;
+	dwc_otg_hc_regs_t *hc_regs = host_if->hc_regs[hc_num];
+
+	/* Clear old interrupt conditions for this host channel. */
+	hc_intr_mask.d32 = 0xFFFFFFFF;
+	hc_intr_mask.b.reserved14_31 = 0;
+	DWC_WRITE_REG32(&hc_regs->hcint, hc_intr_mask.d32);
+
+	/* Enable channel interrupts required for this transfer. */
+	hc_intr_mask.d32 = 0;
+	hc_intr_mask.b.chhltd = 1;
+	if (core_if->dma_enable) {
+		/* For Descriptor DMA mode core halts the channel on AHB error. Interrupt is not required */
+		if (!core_if->dma_desc_enable)
+			hc_intr_mask.b.ahberr = 1;
+		else {
+			if (hc->ep_type == DWC_OTG_EP_TYPE_ISOC)
+				hc_intr_mask.b.xfercompl = 1;
+		}
+
+		if (hc->error_state && !hc->do_split &&
+		    hc->ep_type != DWC_OTG_EP_TYPE_ISOC) {
+			hc_intr_mask.b.ack = 1;
+			if (hc->ep_is_in) {
+				hc_intr_mask.b.datatglerr = 1;
+				if (hc->ep_type != DWC_OTG_EP_TYPE_INTR) {
+					hc_intr_mask.b.nak = 1;
+				}
+			}
+		}
+	} else {
+		switch (hc->ep_type) {
+		case DWC_OTG_EP_TYPE_CONTROL:
+		case DWC_OTG_EP_TYPE_BULK:
+			hc_intr_mask.b.xfercompl = 1;
+			hc_intr_mask.b.stall = 1;
+			hc_intr_mask.b.xacterr = 1;
+			hc_intr_mask.b.datatglerr = 1;
+			if (hc->ep_is_in) {
+				hc_intr_mask.b.bblerr = 1;
+			} else {
+				hc_intr_mask.b.nak = 1;
+				hc_intr_mask.b.nyet = 1;
+				if (hc->do_ping) {
+					hc_intr_mask.b.ack = 1;
+				}
+			}
+
+			if (hc->do_split) {
+				hc_intr_mask.b.nak = 1;
+				if (hc->complete_split) {
+					hc_intr_mask.b.nyet = 1;
+				} else {
+					hc_intr_mask.b.ack = 1;
+				}
+			}
+
+			if (hc->error_state) {
+				hc_intr_mask.b.ack = 1;
+			}
+			break;
+		case DWC_OTG_EP_TYPE_INTR:
+			hc_intr_mask.b.xfercompl = 1;
+			hc_intr_mask.b.nak = 1;
+			hc_intr_mask.b.stall = 1;
+			hc_intr_mask.b.xacterr = 1;
+			hc_intr_mask.b.datatglerr = 1;
+			hc_intr_mask.b.frmovrun = 1;
+
+			if (hc->ep_is_in) {
+				hc_intr_mask.b.bblerr = 1;
+			}
+			if (hc->error_state) {
+				hc_intr_mask.b.ack = 1;
+			}
+			if (hc->do_split) {
+				if (hc->complete_split) {
+					hc_intr_mask.b.nyet = 1;
+				} else {
+					hc_intr_mask.b.ack = 1;
+				}
+			}
+			break;
+		case DWC_OTG_EP_TYPE_ISOC:
+			hc_intr_mask.b.xfercompl = 1;
+			hc_intr_mask.b.frmovrun = 1;
+			hc_intr_mask.b.ack = 1;
+
+			if (hc->ep_is_in) {
+				hc_intr_mask.b.xacterr = 1;
+				hc_intr_mask.b.bblerr = 1;
+			}
+			break;
+		}
+	}
+	DWC_WRITE_REG32(&hc_regs->hcintmsk, hc_intr_mask.d32);
+
+	/*
+	 * Program the HCCHARn register with the endpoint characteristics for
+	 * the current transfer.
+	 */
+	hcchar.d32 = 0;
+	hcchar.b.devaddr = hc->dev_addr;
+	hcchar.b.epnum = hc->ep_num;
+	hcchar.b.epdir = hc->ep_is_in;
+	hcchar.b.lspddev = (hc->speed == DWC_OTG_EP_SPEED_LOW);
+	hcchar.b.eptype = hc->ep_type;
+	hcchar.b.mps = hc->max_packet;
+
+	DWC_WRITE_REG32(&host_if->hc_regs[hc_num]->hcchar, hcchar.d32);
+
+	DWC_DEBUGPL(DBG_HCDV, "%s: Channel %d, Dev Addr %d, EP #%d\n",
+                    __func__, hc->hc_num, hcchar.b.devaddr, hcchar.b.epnum);
+	DWC_DEBUGPL(DBG_HCDV, "	 Is In %d, Is Low Speed %d, EP Type %d, "
+                                "Max Pkt %d, Multi Cnt %d\n",
+                    hcchar.b.epdir, hcchar.b.lspddev, hcchar.b.eptype,
+                    hcchar.b.mps, hcchar.b.multicnt);
+
+	/*
+	 * Program the HCSPLIT register for SPLITs
+	 */
+	hcsplt.d32 = 0;
+	if (hc->do_split) {
+		DWC_DEBUGPL(DBG_HCDV, "Programming HC %d with split --> %s\n",
+			    hc->hc_num,
+			    hc->complete_split ? "CSPLIT" : "SSPLIT");
+		hcsplt.b.compsplt = hc->complete_split;
+		hcsplt.b.xactpos = hc->xact_pos;
+		hcsplt.b.hubaddr = hc->hub_addr;
+		hcsplt.b.prtaddr = hc->port_addr;
+		DWC_DEBUGPL(DBG_HCDV, "\t  comp split %d\n", hc->complete_split);
+		DWC_DEBUGPL(DBG_HCDV, "\t  xact pos %d\n", hc->xact_pos);
+		DWC_DEBUGPL(DBG_HCDV, "\t  hub addr %d\n", hc->hub_addr);
+		DWC_DEBUGPL(DBG_HCDV, "\t  port addr %d\n", hc->port_addr);
+		DWC_DEBUGPL(DBG_HCDV, "\t  is_in %d\n", hc->ep_is_in);
+		DWC_DEBUGPL(DBG_HCDV, "\t  Max Pkt: %d\n", hcchar.b.mps);
+		DWC_DEBUGPL(DBG_HCDV, "\t  xferlen: %d\n", hc->xfer_len);
+	}
+	DWC_WRITE_REG32(&host_if->hc_regs[hc_num]->hcsplt, hcsplt.d32);
+
+}
+
+/**
+ * Attempts to halt a host channel. This function should only be called in
+ * Slave mode or to abort a transfer in either Slave mode or DMA mode. Under
+ * normal circumstances in DMA mode, the controller halts the channel when the
+ * transfer is complete or a condition occurs that requires application
+ * intervention.
+ *
+ * In slave mode, checks for a free request queue entry, then sets the Channel
+ * Enable and Channel Disable bits of the Host Channel Characteristics
+ * register of the specified channel to intiate the halt. If there is no free
+ * request queue entry, sets only the Channel Disable bit of the HCCHARn
+ * register to flush requests for this channel. In the latter case, sets a
+ * flag to indicate that the host channel needs to be halted when a request
+ * queue slot is open.
+ *
+ * In DMA mode, always sets the Channel Enable and Channel Disable bits of the
+ * HCCHARn register. The controller ensures there is space in the request
+ * queue before submitting the halt request.
+ *
+ * Some time may elapse before the core flushes any posted requests for this
+ * host channel and halts. The Channel Halted interrupt handler completes the
+ * deactivation of the host channel.
+ *
+ * @param core_if Controller register interface.
+ * @param hc Host channel to halt.
+ * @param halt_status Reason for halting the channel.
+ */
+void dwc_otg_hc_halt(dwc_otg_core_if_t * core_if,
+		     dwc_hc_t * hc, dwc_otg_halt_status_e halt_status)
+{
+	gnptxsts_data_t nptxsts;
+	hptxsts_data_t hptxsts;
+	hcchar_data_t hcchar;
+	dwc_otg_hc_regs_t *hc_regs;
+	dwc_otg_core_global_regs_t *global_regs;
+	dwc_otg_host_global_regs_t *host_global_regs;
+
+	hc_regs = core_if->host_if->hc_regs[hc->hc_num];
+	global_regs = core_if->core_global_regs;
+	host_global_regs = core_if->host_if->host_global_regs;
+
+	DWC_ASSERT(!(halt_status == DWC_OTG_HC_XFER_NO_HALT_STATUS),
+		   "halt_status = %d\n", halt_status);
+
+	if (halt_status == DWC_OTG_HC_XFER_URB_DEQUEUE ||
+	    halt_status == DWC_OTG_HC_XFER_AHB_ERR) {
+		/*
+		 * Disable all channel interrupts except Ch Halted. The QTD
+		 * and QH state associated with this transfer has been cleared
+		 * (in the case of URB_DEQUEUE), so the channel needs to be
+		 * shut down carefully to prevent crashes.
+		 */
+		hcintmsk_data_t hcintmsk;
+		hcintmsk.d32 = 0;
+		hcintmsk.b.chhltd = 1;
+		DWC_WRITE_REG32(&hc_regs->hcintmsk, hcintmsk.d32);
+
+		/*
+		 * Make sure no other interrupts besides halt are currently
+		 * pending. Handling another interrupt could cause a crash due
+		 * to the QTD and QH state.
+		 */
+		DWC_WRITE_REG32(&hc_regs->hcint, ~hcintmsk.d32);
+
+		/*
+		 * Make sure the halt status is set to URB_DEQUEUE or AHB_ERR
+		 * even if the channel was already halted for some other
+		 * reason.
+		 */
+		hc->halt_status = halt_status;
+
+		hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar);
+		if (hcchar.b.chen == 0) {
+			/*
+			 * The channel is either already halted or it hasn't
+			 * started yet. In DMA mode, the transfer may halt if
+			 * it finishes normally or a condition occurs that
+			 * requires driver intervention. Don't want to halt
+			 * the channel again. In either Slave or DMA mode,
+			 * it's possible that the transfer has been assigned
+			 * to a channel, but not started yet when an URB is
+			 * dequeued. Don't want to halt a channel that hasn't
+			 * started yet.
+			 */
+			return;
+		}
+	}
+	if (hc->halt_pending) {
+		/*
+		 * A halt has already been issued for this channel. This might
+		 * happen when a transfer is aborted by a higher level in
+		 * the stack.
+		 */
+#ifdef DEBUG
+		DWC_PRINTF
+		    ("*** %s: Channel %d, _hc->halt_pending already set ***\n",
+		     __func__, hc->hc_num);
+
+#endif
+		return;
+	}
+
+	hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar);
+
+	/* No need to set the bit in DDMA for disabling the channel */
+	//TODO check it everywhere channel is disabled
+	if (!core_if->core_params->dma_desc_enable)
+		hcchar.b.chen = 1;
+	hcchar.b.chdis = 1;
+
+	if (!core_if->dma_enable) {
+		/* Check for space in the request queue to issue the halt. */
+		if (hc->ep_type == DWC_OTG_EP_TYPE_CONTROL ||
+		    hc->ep_type == DWC_OTG_EP_TYPE_BULK) {
+			nptxsts.d32 = DWC_READ_REG32(&global_regs->gnptxsts);
+			if (nptxsts.b.nptxqspcavail == 0) {
+				hcchar.b.chen = 0;
+			}
+		} else {
+			hptxsts.d32 =
+			    DWC_READ_REG32(&host_global_regs->hptxsts);
+			if ((hptxsts.b.ptxqspcavail == 0)
+			    || (core_if->queuing_high_bandwidth)) {
+				hcchar.b.chen = 0;
+			}
+		}
+	}
+	DWC_WRITE_REG32(&hc_regs->hcchar, hcchar.d32);
+
+	hc->halt_status = halt_status;
+
+	if (hcchar.b.chen) {
+		hc->halt_pending = 1;
+		hc->halt_on_queue = 0;
+	} else {
+		hc->halt_on_queue = 1;
+	}
+
+	DWC_DEBUGPL(DBG_HCDV, "%s: Channel %d\n", __func__, hc->hc_num);
+	DWC_DEBUGPL(DBG_HCDV, "	 hcchar: 0x%08x\n", hcchar.d32);
+	DWC_DEBUGPL(DBG_HCDV, "	 halt_pending: %d\n", hc->halt_pending);
+	DWC_DEBUGPL(DBG_HCDV, "	 halt_on_queue: %d\n", hc->halt_on_queue);
+	DWC_DEBUGPL(DBG_HCDV, "	 halt_status: %d\n", hc->halt_status);
+
+	return;
+}
+
+/**
+ * Clears the transfer state for a host channel. This function is normally
+ * called after a transfer is done and the host channel is being released.
+ *
+ * @param core_if Programming view of DWC_otg controller.
+ * @param hc Identifies the host channel to clean up.
+ */
+void dwc_otg_hc_cleanup(dwc_otg_core_if_t * core_if, dwc_hc_t * hc)
+{
+	dwc_otg_hc_regs_t *hc_regs;
+
+	hc->xfer_started = 0;
+
+	/*
+	 * Clear channel interrupt enables and any unhandled channel interrupt
+	 * conditions.
+	 */
+	hc_regs = core_if->host_if->hc_regs[hc->hc_num];
+	DWC_WRITE_REG32(&hc_regs->hcintmsk, 0);
+	DWC_WRITE_REG32(&hc_regs->hcint, 0xFFFFFFFF);
+#ifdef DEBUG
+	DWC_TIMER_CANCEL(core_if->hc_xfer_timer[hc->hc_num]);
+#endif
+}
+
+/**
+ * Sets the channel property that indicates in which frame a periodic transfer
+ * should occur. This is always set to the _next_ frame. This function has no
+ * effect on non-periodic transfers.
+ *
+ * @param core_if Programming view of DWC_otg controller.
+ * @param hc Identifies the host channel to set up and its properties.
+ * @param hcchar Current value of the HCCHAR register for the specified host
+ * channel.
+ */
+static inline void hc_set_even_odd_frame(dwc_otg_core_if_t * core_if,
+					 dwc_hc_t * hc, hcchar_data_t * hcchar)
+{
+	if (hc->ep_type == DWC_OTG_EP_TYPE_INTR ||
+	    hc->ep_type == DWC_OTG_EP_TYPE_ISOC) {
+		hfnum_data_t hfnum;
+		hfnum.d32 =
+		    DWC_READ_REG32(&core_if->host_if->host_global_regs->hfnum);
+
+		/* 1 if _next_ frame is odd, 0 if it's even */
+		hcchar->b.oddfrm = (hfnum.b.frnum & 0x1) ? 0 : 1;
+#ifdef DEBUG
+		if (hc->ep_type == DWC_OTG_EP_TYPE_INTR && hc->do_split
+		    && !hc->complete_split) {
+			switch (hfnum.b.frnum & 0x7) {
+			case 7:
+				core_if->hfnum_7_samples++;
+				core_if->hfnum_7_frrem_accum += hfnum.b.frrem;
+				break;
+			case 0:
+				core_if->hfnum_0_samples++;
+				core_if->hfnum_0_frrem_accum += hfnum.b.frrem;
+				break;
+			default:
+				core_if->hfnum_other_samples++;
+				core_if->hfnum_other_frrem_accum +=
+				    hfnum.b.frrem;
+				break;
+			}
+		}
+#endif
+	}
+}
+
+#ifdef DEBUG
+void hc_xfer_timeout(void *ptr)
+{
+	hc_xfer_info_t *xfer_info = NULL;
+	int hc_num = 0;
+
+	if (ptr)
+		xfer_info = (hc_xfer_info_t *) ptr;
+
+	if (!xfer_info->hc) {
+		DWC_ERROR("xfer_info->hc = %p\n", xfer_info->hc);
+		return;
+	}
+
+	hc_num = xfer_info->hc->hc_num;
+	DWC_WARN("%s: timeout on channel %d\n", __func__, hc_num);
+	DWC_WARN("	start_hcchar_val 0x%08x\n",
+		 xfer_info->core_if->start_hcchar_val[hc_num]);
+}
+#endif
+
+void ep_xfer_timeout(void *ptr)
+{
+	ep_xfer_info_t *xfer_info = NULL;
+	int ep_num = 0;
+	dctl_data_t dctl = {.d32 = 0 };
+	gintsts_data_t gintsts = {.d32 = 0 };
+	gintmsk_data_t gintmsk = {.d32 = 0 };
+
+	if (ptr)
+		xfer_info = (ep_xfer_info_t *) ptr;
+
+	if (!xfer_info->ep) {
+		DWC_ERROR("xfer_info->ep = %p\n", xfer_info->ep);
+		return;
+	}
+
+	ep_num = xfer_info->ep->num;
+	DWC_WARN("%s: timeout on endpoit %d\n", __func__, ep_num);
+	/* Put the sate to 2 as it was time outed */
+	xfer_info->state = 2;
+
+	dctl.d32 =
+	    DWC_READ_REG32(&xfer_info->core_if->dev_if->dev_global_regs->dctl);
+	gintsts.d32 =
+	    DWC_READ_REG32(&xfer_info->core_if->core_global_regs->gintsts);
+	gintmsk.d32 =
+	    DWC_READ_REG32(&xfer_info->core_if->core_global_regs->gintmsk);
+
+	if (!gintmsk.b.goutnakeff) {
+		/* Unmask it */
+		gintmsk.b.goutnakeff = 1;
+		DWC_WRITE_REG32(&xfer_info->core_if->core_global_regs->gintmsk,
+				gintmsk.d32);
+
+	}
+
+	if (!gintsts.b.goutnakeff) {
+		dctl.b.sgoutnak = 1;
+	}
+	DWC_WRITE_REG32(&xfer_info->core_if->dev_if->dev_global_regs->dctl,
+			dctl.d32);
+
+}
+
+void set_pid_isoc(dwc_hc_t * hc)
+{
+	/* Set up the initial PID for the transfer. */
+	if (hc->speed == DWC_OTG_EP_SPEED_HIGH) {
+		if (hc->ep_is_in) {
+			if (hc->multi_count == 1) {
+				hc->data_pid_start = DWC_OTG_HC_PID_DATA0;
+			} else if (hc->multi_count == 2) {
+				hc->data_pid_start = DWC_OTG_HC_PID_DATA1;
+			} else {
+				hc->data_pid_start = DWC_OTG_HC_PID_DATA2;
+			}
+		} else {
+			if (hc->multi_count == 1) {
+				hc->data_pid_start = DWC_OTG_HC_PID_DATA0;
+			} else {
+				hc->data_pid_start = DWC_OTG_HC_PID_MDATA;
+			}
+		}
+	} else {
+		hc->data_pid_start = DWC_OTG_HC_PID_DATA0;
+	}
+}
+
+/**
+ * This function does the setup for a data transfer for a host channel and
+ * starts the transfer. May be called in either Slave mode or DMA mode. In
+ * Slave mode, the caller must ensure that there is sufficient space in the
+ * request queue and Tx Data FIFO.
+ *
+ * For an OUT transfer in Slave mode, it loads a data packet into the
+ * appropriate FIFO. If necessary, additional data packets will be loaded in
+ * the Host ISR.
+ *
+ * For an IN transfer in Slave mode, a data packet is requested. The data
+ * packets are unloaded from the Rx FIFO in the Host ISR. If necessary,
+ * additional data packets are requested in the Host ISR.
+ *
+ * For a PING transfer in Slave mode, the Do Ping bit is set in the HCTSIZ
+ * register along with a packet count of 1 and the channel is enabled. This
+ * causes a single PING transaction to occur. Other fields in HCTSIZ are
+ * simply set to 0 since no data transfer occurs in this case.
+ *
+ * For a PING transfer in DMA mode, the HCTSIZ register is initialized with
+ * all the information required to perform the subsequent data transfer. In
+ * addition, the Do Ping bit is set in the HCTSIZ register. In this case, the
+ * controller performs the entire PING protocol, then starts the data
+ * transfer.
+ *
+ * @param core_if Programming view of DWC_otg controller.
+ * @param hc Information needed to initialize the host channel. The xfer_len
+ * value may be reduced to accommodate the max widths of the XferSize and
+ * PktCnt fields in the HCTSIZn register. The multi_count value may be changed
+ * to reflect the final xfer_len value.
+ */
+void dwc_otg_hc_start_transfer(dwc_otg_core_if_t * core_if, dwc_hc_t * hc)
+{
+	hcchar_data_t hcchar;
+	hctsiz_data_t hctsiz;
+	uint16_t num_packets;
+	uint32_t max_hc_xfer_size = core_if->core_params->max_transfer_size;
+	uint16_t max_hc_pkt_count = core_if->core_params->max_packet_count;
+	dwc_otg_hc_regs_t *hc_regs = core_if->host_if->hc_regs[hc->hc_num];
+
+	hctsiz.d32 = 0;
+
+	if (hc->do_ping) {
+		if (!core_if->dma_enable) {
+			dwc_otg_hc_do_ping(core_if, hc);
+			hc->xfer_started = 1;
+			return;
+		} else {
+			hctsiz.b.dopng = 1;
+		}
+	}
+
+	if (hc->do_split) {
+		num_packets = 1;
+
+		if (hc->complete_split && !hc->ep_is_in) {
+			/* For CSPLIT OUT Transfer, set the size to 0 so the
+			 * core doesn't expect any data written to the FIFO */
+			hc->xfer_len = 0;
+		} else if (hc->ep_is_in || (hc->xfer_len > hc->max_packet)) {
+			hc->xfer_len = hc->max_packet;
+		} else if (!hc->ep_is_in && (hc->xfer_len > 188)) {
+			hc->xfer_len = 188;
+		}
+
+		hctsiz.b.xfersize = hc->xfer_len;
+	} else {
+		/*
+		 * Ensure that the transfer length and packet count will fit
+		 * in the widths allocated for them in the HCTSIZn register.
+		 */
+		if (hc->ep_type == DWC_OTG_EP_TYPE_INTR ||
+		    hc->ep_type == DWC_OTG_EP_TYPE_ISOC) {
+			/*
+			 * Make sure the transfer size is no larger than one
+			 * (micro)frame's worth of data. (A check was done
+			 * when the periodic transfer was accepted to ensure
+			 * that a (micro)frame's worth of data can be
+			 * programmed into a channel.)
+			 */
+			uint32_t max_periodic_len =
+			    hc->multi_count * hc->max_packet;
+			if (hc->xfer_len > max_periodic_len) {
+				hc->xfer_len = max_periodic_len;
+			} else {
+			}
+		} else if (hc->xfer_len > max_hc_xfer_size) {
+			/* Make sure that xfer_len is a multiple of max packet size. */
+			hc->xfer_len = max_hc_xfer_size - hc->max_packet + 1;
+		}
+
+		if (hc->xfer_len > 0) {
+			num_packets =
+			    (hc->xfer_len + hc->max_packet -
+			     1) / hc->max_packet;
+			if (num_packets > max_hc_pkt_count) {
+				num_packets = max_hc_pkt_count;
+				hc->xfer_len = num_packets * hc->max_packet;
+			}
+		} else {
+			/* Need 1 packet for transfer length of 0. */
+			num_packets = 1;
+		}
+
+		if (hc->ep_is_in) {
+			/* Always program an integral # of max packets for IN transfers. */
+			hc->xfer_len = num_packets * hc->max_packet;
+		}
+
+		if (hc->ep_type == DWC_OTG_EP_TYPE_INTR ||
+		    hc->ep_type == DWC_OTG_EP_TYPE_ISOC) {
+			/*
+			 * Make sure that the multi_count field matches the
+			 * actual transfer length.
+			 */
+			hc->multi_count = num_packets;
+		}
+
+		if (hc->ep_type == DWC_OTG_EP_TYPE_ISOC)
+			set_pid_isoc(hc);
+
+		hctsiz.b.xfersize = hc->xfer_len;
+	}
+
+	hc->start_pkt_count = num_packets;
+	hctsiz.b.pktcnt = num_packets;
+	hctsiz.b.pid = hc->data_pid_start;
+	DWC_WRITE_REG32(&hc_regs->hctsiz, hctsiz.d32);
+
+	DWC_DEBUGPL(DBG_HCDV, "%s: Channel %d\n", __func__, hc->hc_num);
+	DWC_DEBUGPL(DBG_HCDV, "	 Xfer Size: %d\n", hctsiz.b.xfersize);
+	DWC_DEBUGPL(DBG_HCDV, "	 Num Pkts: %d\n", hctsiz.b.pktcnt);
+	DWC_DEBUGPL(DBG_HCDV, "	 Start PID: %d\n", hctsiz.b.pid);
+
+	if (core_if->dma_enable) {
+		dwc_dma_t dma_addr;
+		if (hc->align_buff) {
+			dma_addr = hc->align_buff;
+		} else {
+			dma_addr = ((unsigned long)hc->xfer_buff & 0xffffffff);
+		}
+		DWC_WRITE_REG32(&hc_regs->hcdma, dma_addr);
+	}
+
+	/* Start the split */
+	if (hc->do_split) {
+		hcsplt_data_t hcsplt;
+		hcsplt.d32 = DWC_READ_REG32(&hc_regs->hcsplt);
+		hcsplt.b.spltena = 1;
+		DWC_WRITE_REG32(&hc_regs->hcsplt, hcsplt.d32);
+	}
+
+	hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar);
+	hcchar.b.multicnt = hc->multi_count;
+	hc_set_even_odd_frame(core_if, hc, &hcchar);
+#ifdef DEBUG
+	core_if->start_hcchar_val[hc->hc_num] = hcchar.d32;
+	if (hcchar.b.chdis) {
+		DWC_WARN("%s: chdis set, channel %d, hcchar 0x%08x\n",
+			 __func__, hc->hc_num, hcchar.d32);
+	}
+#endif
+
+	/* Set host channel enable after all other setup is complete. */
+	hcchar.b.chen = 1;
+	hcchar.b.chdis = 0;
+	DWC_WRITE_REG32(&hc_regs->hcchar, hcchar.d32);
+
+	hc->xfer_started = 1;
+	hc->requests++;
+
+	if (!core_if->dma_enable && !hc->ep_is_in && hc->xfer_len > 0) {
+		/* Load OUT packet into the appropriate Tx FIFO. */
+		dwc_otg_hc_write_packet(core_if, hc);
+	}
+#ifdef DEBUG
+	if (hc->ep_type != DWC_OTG_EP_TYPE_INTR) {
+                DWC_DEBUGPL(DBG_HCDV, "transfer %d from core_if %p\n",
+                            hc->hc_num, core_if);//GRAYG
+		core_if->hc_xfer_info[hc->hc_num].core_if = core_if;
+		core_if->hc_xfer_info[hc->hc_num].hc = hc;
+
+		/* Start a timer for this transfer. */
+		DWC_TIMER_SCHEDULE(core_if->hc_xfer_timer[hc->hc_num], 10000);
+	}
+#endif
+}
+
+/**
+ * This function does the setup for a data transfer for a host channel
+ * and starts the transfer in Descriptor DMA mode.
+ *
+ * Initializes HCTSIZ register. For a PING transfer the Do Ping bit is set.
+ * Sets PID and NTD values. For periodic transfers
+ * initializes SCHED_INFO field with micro-frame bitmap.
+ *
+ * Initializes HCDMA register with descriptor list address and CTD value
+ * then starts the transfer via enabling the channel.
+ *
+ * @param core_if Programming view of DWC_otg controller.
+ * @param hc Information needed to initialize the host channel.
+ */
+void dwc_otg_hc_start_transfer_ddma(dwc_otg_core_if_t * core_if, dwc_hc_t * hc)
+{
+	dwc_otg_hc_regs_t *hc_regs = core_if->host_if->hc_regs[hc->hc_num];
+	hcchar_data_t hcchar;
+	hctsiz_data_t hctsiz;
+	hcdma_data_t hcdma;
+
+	hctsiz.d32 = 0;
+
+	if (hc->do_ping)
+		hctsiz.b_ddma.dopng = 1;
+
+	if (hc->ep_type == DWC_OTG_EP_TYPE_ISOC)
+		set_pid_isoc(hc);
+
+	/* Packet Count and Xfer Size are not used in Descriptor DMA mode */
+	hctsiz.b_ddma.pid = hc->data_pid_start;
+	hctsiz.b_ddma.ntd = hc->ntd - 1;	/* 0 - 1 descriptor, 1 - 2 descriptors, etc. */
+	hctsiz.b_ddma.schinfo = hc->schinfo;	/* Non-zero only for high-speed interrupt endpoints */
+
+	DWC_DEBUGPL(DBG_HCDV, "%s: Channel %d\n", __func__, hc->hc_num);
+	DWC_DEBUGPL(DBG_HCDV, "	 Start PID: %d\n", hctsiz.b.pid);
+	DWC_DEBUGPL(DBG_HCDV, "	 NTD: %d\n", hctsiz.b_ddma.ntd);
+
+	DWC_WRITE_REG32(&hc_regs->hctsiz, hctsiz.d32);
+
+	hcdma.d32 = 0;
+	hcdma.b.dma_addr = ((uint32_t) hc->desc_list_addr) >> 11;
+
+	/* Always start from first descriptor. */
+	hcdma.b.ctd = 0;
+	DWC_WRITE_REG32(&hc_regs->hcdma, hcdma.d32);
+
+	hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar);
+	hcchar.b.multicnt = hc->multi_count;
+
+#ifdef DEBUG
+	core_if->start_hcchar_val[hc->hc_num] = hcchar.d32;
+	if (hcchar.b.chdis) {
+		DWC_WARN("%s: chdis set, channel %d, hcchar 0x%08x\n",
+			 __func__, hc->hc_num, hcchar.d32);
+	}
+#endif
+
+	/* Set host channel enable after all other setup is complete. */
+	hcchar.b.chen = 1;
+	hcchar.b.chdis = 0;
+
+	DWC_WRITE_REG32(&hc_regs->hcchar, hcchar.d32);
+
+	hc->xfer_started = 1;
+	hc->requests++;
+
+#ifdef DEBUG
+	if ((hc->ep_type != DWC_OTG_EP_TYPE_INTR)
+	    && (hc->ep_type != DWC_OTG_EP_TYPE_ISOC)) {
+                DWC_DEBUGPL(DBG_HCDV, "DMA transfer %d from core_if %p\n",
+                            hc->hc_num, core_if);//GRAYG
+		core_if->hc_xfer_info[hc->hc_num].core_if = core_if;
+		core_if->hc_xfer_info[hc->hc_num].hc = hc;
+		/* Start a timer for this transfer. */
+		DWC_TIMER_SCHEDULE(core_if->hc_xfer_timer[hc->hc_num], 10000);
+	}
+#endif
+
+}
+
+/**
+ * This function continues a data transfer that was started by previous call
+ * to <code>dwc_otg_hc_start_transfer</code>. The caller must ensure there is
+ * sufficient space in the request queue and Tx Data FIFO. This function
+ * should only be called in Slave mode. In DMA mode, the controller acts
+ * autonomously to complete transfers programmed to a host channel.
+ *
+ * For an OUT transfer, a new data packet is loaded into the appropriate FIFO
+ * if there is any data remaining to be queued. For an IN transfer, another
+ * data packet is always requested. For the SETUP phase of a control transfer,
+ * this function does nothing.
+ *
+ * @return 1 if a new request is queued, 0 if no more requests are required
+ * for this transfer.
+ */
+int dwc_otg_hc_continue_transfer(dwc_otg_core_if_t * core_if, dwc_hc_t * hc)
+{
+	DWC_DEBUGPL(DBG_HCDV, "%s: Channel %d\n", __func__, hc->hc_num);
+
+	if (hc->do_split) {
+		/* SPLITs always queue just once per channel */
+		return 0;
+	} else if (hc->data_pid_start == DWC_OTG_HC_PID_SETUP) {
+		/* SETUPs are queued only once since they can't be NAKed. */
+		return 0;
+	} else if (hc->ep_is_in) {
+		/*
+		 * Always queue another request for other IN transfers. If
+		 * back-to-back INs are issued and NAKs are received for both,
+		 * the driver may still be processing the first NAK when the
+		 * second NAK is received. When the interrupt handler clears
+		 * the NAK interrupt for the first NAK, the second NAK will
+		 * not be seen. So we can't depend on the NAK interrupt
+		 * handler to requeue a NAKed request. Instead, IN requests
+		 * are issued each time this function is called. When the
+		 * transfer completes, the extra requests for the channel will
+		 * be flushed.
+		 */
+		hcchar_data_t hcchar;
+		dwc_otg_hc_regs_t *hc_regs =
+		    core_if->host_if->hc_regs[hc->hc_num];
+
+		hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar);
+		hc_set_even_odd_frame(core_if, hc, &hcchar);
+		hcchar.b.chen = 1;
+		hcchar.b.chdis = 0;
+		DWC_DEBUGPL(DBG_HCDV, "	 IN xfer: hcchar = 0x%08x\n",
+			    hcchar.d32);
+		DWC_WRITE_REG32(&hc_regs->hcchar, hcchar.d32);
+		hc->requests++;
+		return 1;
+	} else {
+		/* OUT transfers. */
+		if (hc->xfer_count < hc->xfer_len) {
+			if (hc->ep_type == DWC_OTG_EP_TYPE_INTR ||
+			    hc->ep_type == DWC_OTG_EP_TYPE_ISOC) {
+				hcchar_data_t hcchar;
+				dwc_otg_hc_regs_t *hc_regs;
+				hc_regs = core_if->host_if->hc_regs[hc->hc_num];
+				hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar);
+				hc_set_even_odd_frame(core_if, hc, &hcchar);
+			}
+
+			/* Load OUT packet into the appropriate Tx FIFO. */
+			dwc_otg_hc_write_packet(core_if, hc);
+			hc->requests++;
+			return 1;
+		} else {
+			return 0;
+		}
+	}
+}
+
+/**
+ * Starts a PING transfer. This function should only be called in Slave mode.
+ * The Do Ping bit is set in the HCTSIZ register, then the channel is enabled.
+ */
+void dwc_otg_hc_do_ping(dwc_otg_core_if_t * core_if, dwc_hc_t * hc)
+{
+	hcchar_data_t hcchar;
+	hctsiz_data_t hctsiz;
+	dwc_otg_hc_regs_t *hc_regs = core_if->host_if->hc_regs[hc->hc_num];
+
+	DWC_DEBUGPL(DBG_HCDV, "%s: Channel %d\n", __func__, hc->hc_num);
+
+	hctsiz.d32 = 0;
+	hctsiz.b.dopng = 1;
+	hctsiz.b.pktcnt = 1;
+	DWC_WRITE_REG32(&hc_regs->hctsiz, hctsiz.d32);
+
+	hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar);
+	hcchar.b.chen = 1;
+	hcchar.b.chdis = 0;
+	DWC_WRITE_REG32(&hc_regs->hcchar, hcchar.d32);
+}
+
+/*
+ * This function writes a packet into the Tx FIFO associated with the Host
+ * Channel. For a channel associated with a non-periodic EP, the non-periodic
+ * Tx FIFO is written. For a channel associated with a periodic EP, the
+ * periodic Tx FIFO is written. This function should only be called in Slave
+ * mode.
+ *
+ * Upon return the xfer_buff and xfer_count fields in _hc are incremented by
+ * then number of bytes written to the Tx FIFO.
+ */
+void dwc_otg_hc_write_packet(dwc_otg_core_if_t * core_if, dwc_hc_t * hc)
+{
+	uint32_t i;
+	uint32_t remaining_count;
+	uint32_t byte_count;
+	uint32_t dword_count;
+
+	uint32_t *data_buff = (uint32_t *) (hc->xfer_buff);
+	uint32_t *data_fifo = core_if->data_fifo[hc->hc_num];
+
+	remaining_count = hc->xfer_len - hc->xfer_count;
+	if (remaining_count > hc->max_packet) {
+		byte_count = hc->max_packet;
+	} else {
+		byte_count = remaining_count;
+	}
+
+	dword_count = (byte_count + 3) / 4;
+
+	if ((((unsigned long)data_buff) & 0x3) == 0) {
+		/* xfer_buff is DWORD aligned. */
+		for (i = 0; i < dword_count; i++, data_buff++) {
+			DWC_WRITE_REG32(data_fifo, *data_buff);
+		}
+	} else {
+		/* xfer_buff is not DWORD aligned. */
+		for (i = 0; i < dword_count; i++, data_buff++) {
+			uint32_t data;
+			data =
+			    (data_buff[0] | data_buff[1] << 8 | data_buff[2] <<
+			     16 | data_buff[3] << 24);
+			DWC_WRITE_REG32(data_fifo, data);
+		}
+	}
+
+	hc->xfer_count += byte_count;
+	hc->xfer_buff += byte_count;
+}
+
+/**
+ * Gets the current USB frame number. This is the frame number from the last
+ * SOF packet.
+ */
+uint32_t dwc_otg_get_frame_number(dwc_otg_core_if_t * core_if)
+{
+	dsts_data_t dsts;
+	dsts.d32 = DWC_READ_REG32(&core_if->dev_if->dev_global_regs->dsts);
+
+	/* read current frame/microframe number from DSTS register */
+	return dsts.b.soffn;
+}
+
+/**
+ * Calculates and gets the frame Interval value of HFIR register according PHY
+ * type and speed.The application can modify a value of HFIR register only after
+ * the Port Enable bit of the Host Port Control and Status register
+ * (HPRT.PrtEnaPort) has been set.
+*/
+
+uint32_t calc_frame_interval(dwc_otg_core_if_t * core_if)
+{
+	gusbcfg_data_t usbcfg;
+	hwcfg2_data_t hwcfg2;
+	hprt0_data_t hprt0;
+	int clock = 60;		// default value
+	usbcfg.d32 = DWC_READ_REG32(&core_if->core_global_regs->gusbcfg);
+	hwcfg2.d32 = DWC_READ_REG32(&core_if->core_global_regs->ghwcfg2);
+	hprt0.d32 = DWC_READ_REG32(core_if->host_if->hprt0);
+	if (!usbcfg.b.physel && usbcfg.b.ulpi_utmi_sel && !usbcfg.b.phyif)
+		clock = 60;
+	if (usbcfg.b.physel && hwcfg2.b.fs_phy_type == 3)
+		clock = 48;
+	if (!usbcfg.b.phylpwrclksel && !usbcfg.b.physel &&
+	    !usbcfg.b.ulpi_utmi_sel && usbcfg.b.phyif)
+		clock = 30;
+	if (!usbcfg.b.phylpwrclksel && !usbcfg.b.physel &&
+	    !usbcfg.b.ulpi_utmi_sel && !usbcfg.b.phyif)
+		clock = 60;
+	if (usbcfg.b.phylpwrclksel && !usbcfg.b.physel &&
+	    !usbcfg.b.ulpi_utmi_sel && usbcfg.b.phyif)
+		clock = 48;
+	if (usbcfg.b.physel && !usbcfg.b.phyif && hwcfg2.b.fs_phy_type == 2)
+		clock = 48;
+	if (usbcfg.b.physel && hwcfg2.b.fs_phy_type == 1)
+		clock = 48;
+	if (hprt0.b.prtspd == 0)
+		/* High speed case */
+		return 125 * clock - 1;
+	else
+		/* FS/LS case */
+		return 1000 * clock - 1;
+}
+
+/**
+ * This function reads a setup packet from the Rx FIFO into the destination
+ * buffer. This function is called from the Rx Status Queue Level (RxStsQLvl)
+ * Interrupt routine when a SETUP packet has been received in Slave mode.
+ *
+ * @param core_if Programming view of DWC_otg controller.
+ * @param dest Destination buffer for packet data.
+ */
+void dwc_otg_read_setup_packet(dwc_otg_core_if_t * core_if, uint32_t * dest)
+{
+	device_grxsts_data_t status;
+	/* Get the 8 bytes of a setup transaction data */
+
+	/* Pop 2 DWORDS off the receive data FIFO into memory */
+	dest[0] = DWC_READ_REG32(core_if->data_fifo[0]);
+	dest[1] = DWC_READ_REG32(core_if->data_fifo[0]);
+	if (core_if->snpsid >= OTG_CORE_REV_3_00a) {
+		status.d32 =
+		    DWC_READ_REG32(&core_if->core_global_regs->grxstsp);
+		DWC_DEBUGPL(DBG_ANY,
+			    "EP:%d BCnt:%d " "pktsts:%x Frame:%d(0x%0x)\n",
+			    status.b.epnum, status.b.bcnt, status.b.pktsts,
+			    status.b.fn, status.b.fn);
+	}
+}
+
+/**
+ * This function enables EP0 OUT to receive SETUP packets and configures EP0
+ * IN for transmitting packets. It is normally called when the
+ * "Enumeration Done" interrupt occurs.
+ *
+ * @param core_if Programming view of DWC_otg controller.
+ * @param ep The EP0 data.
+ */
+void dwc_otg_ep0_activate(dwc_otg_core_if_t * core_if, dwc_ep_t * ep)
+{
+	dwc_otg_dev_if_t *dev_if = core_if->dev_if;
+	dsts_data_t dsts;
+	depctl_data_t diepctl;
+	depctl_data_t doepctl;
+	dctl_data_t dctl = {.d32 = 0 };
+
+	ep->stp_rollover = 0;
+	/* Read the Device Status and Endpoint 0 Control registers */
+	dsts.d32 = DWC_READ_REG32(&dev_if->dev_global_regs->dsts);
+	diepctl.d32 = DWC_READ_REG32(&dev_if->in_ep_regs[0]->diepctl);
+	doepctl.d32 = DWC_READ_REG32(&dev_if->out_ep_regs[0]->doepctl);
+
+	/* Set the MPS of the IN EP based on the enumeration speed */
+	switch (dsts.b.enumspd) {
+	case DWC_DSTS_ENUMSPD_HS_PHY_30MHZ_OR_60MHZ:
+	case DWC_DSTS_ENUMSPD_FS_PHY_30MHZ_OR_60MHZ:
+	case DWC_DSTS_ENUMSPD_FS_PHY_48MHZ:
+		diepctl.b.mps = DWC_DEP0CTL_MPS_64;
+		break;
+	case DWC_DSTS_ENUMSPD_LS_PHY_6MHZ:
+		diepctl.b.mps = DWC_DEP0CTL_MPS_8;
+		break;
+	}
+
+	DWC_WRITE_REG32(&dev_if->in_ep_regs[0]->diepctl, diepctl.d32);
+
+	/* Enable OUT EP for receive */
+	if (core_if->snpsid <= OTG_CORE_REV_2_94a) {
+	doepctl.b.epena = 1;
+	DWC_WRITE_REG32(&dev_if->out_ep_regs[0]->doepctl, doepctl.d32);
+	}
+#ifdef VERBOSE
+	DWC_DEBUGPL(DBG_PCDV, "doepctl0=%0x\n",
+		    DWC_READ_REG32(&dev_if->out_ep_regs[0]->doepctl));
+	DWC_DEBUGPL(DBG_PCDV, "diepctl0=%0x\n",
+		    DWC_READ_REG32(&dev_if->in_ep_regs[0]->diepctl));
+#endif
+	dctl.b.cgnpinnak = 1;
+
+	DWC_MODIFY_REG32(&dev_if->dev_global_regs->dctl, dctl.d32, dctl.d32);
+	DWC_DEBUGPL(DBG_PCDV, "dctl=%0x\n",
+		    DWC_READ_REG32(&dev_if->dev_global_regs->dctl));
+
+}
+
+/**
+ * This function activates an EP.  The Device EP control register for
+ * the EP is configured as defined in the ep structure. Note: This
+ * function is not used for EP0.
+ *
+ * @param core_if Programming view of DWC_otg controller.
+ * @param ep The EP to activate.
+ */
+void dwc_otg_ep_activate(dwc_otg_core_if_t * core_if, dwc_ep_t * ep)
+{
+	dwc_otg_dev_if_t *dev_if = core_if->dev_if;
+	depctl_data_t depctl;
+	volatile uint32_t *addr;
+	daint_data_t daintmsk = {.d32 = 0 };
+	dcfg_data_t dcfg;
+	uint8_t i;
+
+	DWC_DEBUGPL(DBG_PCDV, "%s() EP%d-%s\n", __func__, ep->num,
+		    (ep->is_in ? "IN" : "OUT"));
+
+#ifdef DWC_UTE_PER_IO
+	ep->xiso_frame_num = 0xFFFFFFFF;
+	ep->xiso_active_xfers = 0;
+	ep->xiso_queued_xfers = 0;
+#endif
+	/* Read DEPCTLn register */
+	if (ep->is_in == 1) {
+		addr = &dev_if->in_ep_regs[ep->num]->diepctl;
+		daintmsk.ep.in = 1 << ep->num;
+	} else {
+		addr = &dev_if->out_ep_regs[ep->num]->doepctl;
+		daintmsk.ep.out = 1 << ep->num;
+	}
+
+	/* If the EP is already active don't change the EP Control
+	 * register. */
+	depctl.d32 = DWC_READ_REG32(addr);
+	if (!depctl.b.usbactep) {
+		depctl.b.mps = ep->maxpacket;
+		depctl.b.eptype = ep->type;
+		depctl.b.txfnum = ep->tx_fifo_num;
+
+		if (ep->type == DWC_OTG_EP_TYPE_ISOC) {
+			depctl.b.setd0pid = 1;	// ???
+		} else {
+			depctl.b.setd0pid = 1;
+		}
+		depctl.b.usbactep = 1;
+
+		/* Update nextep_seq array and EPMSCNT in DCFG*/
+		if (!(depctl.b.eptype & 1) && (ep->is_in == 1)) {	// NP IN EP
+			for (i = 0; i <= core_if->dev_if->num_in_eps; i++) {
+				if (core_if->nextep_seq[i] == core_if->first_in_nextep_seq)
+				break;
+			}
+			core_if->nextep_seq[i] = ep->num;
+			core_if->nextep_seq[ep->num] = core_if->first_in_nextep_seq;
+			depctl.b.nextep = core_if->nextep_seq[ep->num];
+			dcfg.d32 = DWC_READ_REG32(&dev_if->dev_global_regs->dcfg);
+			dcfg.b.epmscnt++;
+			DWC_WRITE_REG32(&dev_if->dev_global_regs->dcfg, dcfg.d32);
+
+			DWC_DEBUGPL(DBG_PCDV,
+				    "%s first_in_nextep_seq= %2d; nextep_seq[]:\n",
+				__func__, core_if->first_in_nextep_seq);
+			for (i=0; i <= core_if->dev_if->num_in_eps; i++) {
+				DWC_DEBUGPL(DBG_PCDV, "%2d\n",
+					    core_if->nextep_seq[i]);
+			}
+
+		}
+
+
+		DWC_WRITE_REG32(addr, depctl.d32);
+		DWC_DEBUGPL(DBG_PCDV, "DEPCTL=%08x\n", DWC_READ_REG32(addr));
+	}
+
+	/* Enable the Interrupt for this EP */
+	if (core_if->multiproc_int_enable) {
+		if (ep->is_in == 1) {
+			diepmsk_data_t diepmsk = {.d32 = 0 };
+			diepmsk.b.xfercompl = 1;
+			diepmsk.b.timeout = 1;
+			diepmsk.b.epdisabled = 1;
+			diepmsk.b.ahberr = 1;
+			diepmsk.b.intknepmis = 1;
+			if (!core_if->en_multiple_tx_fifo && core_if->dma_enable)
+				diepmsk.b.intknepmis = 0;
+			diepmsk.b.txfifoundrn = 1;	//?????
+			if (ep->type == DWC_OTG_EP_TYPE_ISOC) {
+				diepmsk.b.nak = 1;
+			}
+
+
+
+/*
+			if (core_if->dma_desc_enable) {
+				diepmsk.b.bna = 1;
+			}
+*/
+/*
+			if (core_if->dma_enable) {
+				doepmsk.b.nak = 1;
+			}
+*/
+			DWC_WRITE_REG32(&dev_if->dev_global_regs->
+					diepeachintmsk[ep->num], diepmsk.d32);
+
+		} else {
+			doepmsk_data_t doepmsk = {.d32 = 0 };
+			doepmsk.b.xfercompl = 1;
+			doepmsk.b.ahberr = 1;
+			doepmsk.b.epdisabled = 1;
+			if (ep->type == DWC_OTG_EP_TYPE_ISOC)
+				doepmsk.b.outtknepdis = 1;
+
+/*
+
+			if (core_if->dma_desc_enable) {
+				doepmsk.b.bna = 1;
+			}
+*/
+/*
+			doepmsk.b.babble = 1;
+			doepmsk.b.nyet = 1;
+			doepmsk.b.nak = 1;
+*/
+			DWC_WRITE_REG32(&dev_if->dev_global_regs->
+					doepeachintmsk[ep->num], doepmsk.d32);
+		}
+		DWC_MODIFY_REG32(&dev_if->dev_global_regs->deachintmsk,
+				 0, daintmsk.d32);
+	} else {
+		if (ep->type == DWC_OTG_EP_TYPE_ISOC) {
+			if (ep->is_in) {
+				diepmsk_data_t diepmsk = {.d32 = 0 };
+				diepmsk.b.nak = 1;
+				DWC_MODIFY_REG32(&dev_if->dev_global_regs->diepmsk, 0, diepmsk.d32);
+			} else {
+				doepmsk_data_t doepmsk = {.d32 = 0 };
+				doepmsk.b.outtknepdis = 1;
+				DWC_MODIFY_REG32(&dev_if->dev_global_regs->doepmsk, 0, doepmsk.d32);
+			}
+		}
+		DWC_MODIFY_REG32(&dev_if->dev_global_regs->daintmsk,
+				 0, daintmsk.d32);
+	}
+
+	DWC_DEBUGPL(DBG_PCDV, "DAINTMSK=%0x\n",
+		    DWC_READ_REG32(&dev_if->dev_global_regs->daintmsk));
+
+	ep->stall_clear_flag = 0;
+
+	return;
+}
+
+/**
+ * This function deactivates an EP. This is done by clearing the USB Active
+ * EP bit in the Device EP control register. Note: This function is not used
+ * for EP0. EP0 cannot be deactivated.
+ *
+ * @param core_if Programming view of DWC_otg controller.
+ * @param ep The EP to deactivate.
+ */
+void dwc_otg_ep_deactivate(dwc_otg_core_if_t * core_if, dwc_ep_t * ep)
+{
+	depctl_data_t depctl = {.d32 = 0 };
+	volatile uint32_t *addr;
+	daint_data_t daintmsk = {.d32 = 0 };
+	dcfg_data_t dcfg;
+	uint8_t i = 0;
+
+#ifdef DWC_UTE_PER_IO
+	ep->xiso_frame_num = 0xFFFFFFFF;
+	ep->xiso_active_xfers = 0;
+	ep->xiso_queued_xfers = 0;
+#endif
+
+	/* Read DEPCTLn register */
+	if (ep->is_in == 1) {
+		addr = &core_if->dev_if->in_ep_regs[ep->num]->diepctl;
+		daintmsk.ep.in = 1 << ep->num;
+	} else {
+		addr = &core_if->dev_if->out_ep_regs[ep->num]->doepctl;
+		daintmsk.ep.out = 1 << ep->num;
+	}
+
+	depctl.d32 = DWC_READ_REG32(addr);
+
+	depctl.b.usbactep = 0;
+
+	/* Update nextep_seq array and EPMSCNT in DCFG*/
+	if (!(depctl.b.eptype & 1) && ep->is_in == 1) {	// NP EP IN
+		for (i = 0; i <= core_if->dev_if->num_in_eps; i++) {
+			if (core_if->nextep_seq[i] == ep->num)
+			break;
+		}
+		core_if->nextep_seq[i] = core_if->nextep_seq[ep->num];
+		if (core_if->first_in_nextep_seq == ep->num)
+			core_if->first_in_nextep_seq = i;
+		core_if->nextep_seq[ep->num] = 0xff;
+		depctl.b.nextep = 0;
+		dcfg.d32 =
+		    DWC_READ_REG32(&core_if->dev_if->dev_global_regs->dcfg);
+		dcfg.b.epmscnt--;
+		DWC_WRITE_REG32(&core_if->dev_if->dev_global_regs->dcfg,
+				dcfg.d32);
+
+		DWC_DEBUGPL(DBG_PCDV,
+			    "%s first_in_nextep_seq= %2d; nextep_seq[]:\n",
+				__func__, core_if->first_in_nextep_seq);
+			for (i=0; i <= core_if->dev_if->num_in_eps; i++) {
+				DWC_DEBUGPL(DBG_PCDV, "%2d\n", core_if->nextep_seq[i]);
+			}
+	}
+
+	if (ep->is_in == 1)
+		depctl.b.txfnum = 0;
+
+	if (core_if->dma_desc_enable)
+		depctl.b.epdis = 1;
+
+	DWC_WRITE_REG32(addr, depctl.d32);
+	depctl.d32 = DWC_READ_REG32(addr);
+	if (core_if->dma_enable && ep->type == DWC_OTG_EP_TYPE_ISOC
+	    && depctl.b.epena) {
+		depctl_data_t depctl = {.d32 = 0};
+		if (ep->is_in) {
+			diepint_data_t diepint = {.d32 = 0};
+
+			depctl.b.snak = 1;
+			DWC_WRITE_REG32(&core_if->dev_if->in_ep_regs[ep->num]->
+					diepctl, depctl.d32);
+			do {
+				dwc_udelay(10);
+				diepint.d32 =
+				    DWC_READ_REG32(&core_if->
+						   dev_if->in_ep_regs[ep->num]->
+						   diepint);
+			} while (!diepint.b.inepnakeff);
+			diepint.b.inepnakeff = 1;
+			DWC_WRITE_REG32(&core_if->dev_if->in_ep_regs[ep->num]->
+					diepint, diepint.d32);
+			depctl.d32 = 0;
+			depctl.b.epdis = 1;
+			DWC_WRITE_REG32(&core_if->dev_if->in_ep_regs[ep->num]->
+					diepctl, depctl.d32);
+			do {
+				dwc_udelay(10);
+				diepint.d32 =
+				    DWC_READ_REG32(&core_if->
+						   dev_if->in_ep_regs[ep->num]->
+						   diepint);
+			} while (!diepint.b.epdisabled);
+			diepint.b.epdisabled = 1;
+			DWC_WRITE_REG32(&core_if->dev_if->in_ep_regs[ep->num]->
+					diepint, diepint.d32);
+		} else {
+			dctl_data_t dctl = {.d32 = 0};
+			gintmsk_data_t gintsts = {.d32 = 0};
+			doepint_data_t doepint = {.d32 = 0};
+			dctl.b.sgoutnak = 1;
+			DWC_MODIFY_REG32(&core_if->dev_if->dev_global_regs->
+					 dctl, 0, dctl.d32);
+			do {
+				dwc_udelay(10);
+				gintsts.d32 = DWC_READ_REG32(&core_if->core_global_regs->gintsts);
+			} while (!gintsts.b.goutnakeff);
+			gintsts.d32 = 0;
+			gintsts.b.goutnakeff = 1;
+			DWC_WRITE_REG32(&core_if->core_global_regs->gintsts, gintsts.d32);
+
+			depctl.d32 = 0;
+			depctl.b.epdis = 1;
+			depctl.b.snak = 1;
+			DWC_WRITE_REG32(&core_if->dev_if->out_ep_regs[ep->num]->doepctl, depctl.d32);
+			do
+			{
+				dwc_udelay(10);
+				doepint.d32 = DWC_READ_REG32(&core_if->dev_if->
+											out_ep_regs[ep->num]->doepint);
+			} while (!doepint.b.epdisabled);
+
+			doepint.b.epdisabled = 1;
+			DWC_WRITE_REG32(&core_if->dev_if->out_ep_regs[ep->num]->doepint, doepint.d32);
+
+			dctl.d32 = 0;
+			dctl.b.cgoutnak = 1;
+			DWC_MODIFY_REG32(&core_if->dev_if->dev_global_regs->dctl, 0, dctl.d32);
+		}
+	}
+
+	/* Disable the Interrupt for this EP */
+	if (core_if->multiproc_int_enable) {
+		DWC_MODIFY_REG32(&core_if->dev_if->dev_global_regs->deachintmsk,
+				 daintmsk.d32, 0);
+
+		if (ep->is_in == 1) {
+			DWC_WRITE_REG32(&core_if->dev_if->dev_global_regs->
+					diepeachintmsk[ep->num], 0);
+		} else {
+			DWC_WRITE_REG32(&core_if->dev_if->dev_global_regs->
+					doepeachintmsk[ep->num], 0);
+		}
+	} else {
+		DWC_MODIFY_REG32(&core_if->dev_if->dev_global_regs->daintmsk,
+				 daintmsk.d32, 0);
+	}
+
+}
+
+/**
+ * This function initializes dma descriptor chain.
+ *
+ * @param core_if Programming view of DWC_otg controller.
+ * @param ep The EP to start the transfer on.
+ */
+static void init_dma_desc_chain(dwc_otg_core_if_t * core_if, dwc_ep_t * ep)
+{
+	dwc_otg_dev_dma_desc_t *dma_desc;
+	uint32_t offset;
+	uint32_t xfer_est;
+	int i;
+	unsigned maxxfer_local, total_len;
+
+	if (!ep->is_in && ep->type == DWC_OTG_EP_TYPE_INTR &&
+					(ep->maxpacket%4)) {
+		maxxfer_local = ep->maxpacket;
+		total_len = ep->xfer_len;
+	} else {
+		maxxfer_local = ep->maxxfer;
+		total_len = ep->total_len;
+	}
+
+	ep->desc_cnt = (total_len / maxxfer_local) +
+            ((total_len % maxxfer_local) ? 1 : 0);
+
+	if (!ep->desc_cnt)
+		ep->desc_cnt = 1;
+
+	if (ep->desc_cnt > MAX_DMA_DESC_CNT)
+		ep->desc_cnt = MAX_DMA_DESC_CNT;
+
+	dma_desc = ep->desc_addr;
+	if (maxxfer_local == ep->maxpacket) {
+		if ((total_len % maxxfer_local) &&
+				(total_len/maxxfer_local < MAX_DMA_DESC_CNT)) {
+			xfer_est = (ep->desc_cnt - 1) * maxxfer_local +
+					(total_len % maxxfer_local);
+		} else
+			xfer_est = ep->desc_cnt * maxxfer_local;
+	} else
+		xfer_est = total_len;
+	offset = 0;
+	for (i = 0; i < ep->desc_cnt; ++i) {
+		/** DMA Descriptor Setup */
+		if (xfer_est > maxxfer_local) {
+			dma_desc->status.b.bs = BS_HOST_BUSY;
+			dma_desc->status.b.l = 0;
+			dma_desc->status.b.ioc = 0;
+			dma_desc->status.b.sp = 0;
+			dma_desc->status.b.bytes = maxxfer_local;
+			dma_desc->buf = ep->dma_addr + offset;
+			dma_desc->status.b.sts = 0;
+			dma_desc->status.b.bs = BS_HOST_READY;
+
+			xfer_est -= maxxfer_local;
+			offset += maxxfer_local;
+		} else {
+			dma_desc->status.b.bs = BS_HOST_BUSY;
+			dma_desc->status.b.l = 1;
+			dma_desc->status.b.ioc = 1;
+			if (ep->is_in) {
+				dma_desc->status.b.sp =
+				    (xfer_est %
+				     ep->maxpacket) ? 1 : ((ep->
+							    sent_zlp) ? 1 : 0);
+				dma_desc->status.b.bytes = xfer_est;
+			} else {
+				if (maxxfer_local == ep->maxpacket)
+					dma_desc->status.b.bytes = xfer_est;
+				else
+					dma_desc->status.b.bytes =
+						xfer_est + ((4 - (xfer_est & 0x3)) & 0x3);
+			}
+
+			dma_desc->buf = ep->dma_addr + offset;
+			dma_desc->status.b.sts = 0;
+			dma_desc->status.b.bs = BS_HOST_READY;
+		}
+		dma_desc++;
+	}
+}
+/**
+ * This function is called when to write ISOC data into appropriate dedicated
+ * periodic FIFO.
+ */
+static int32_t write_isoc_tx_fifo(dwc_otg_core_if_t * core_if, dwc_ep_t * dwc_ep)
+{
+	dwc_otg_dev_if_t *dev_if = core_if->dev_if;
+	dwc_otg_dev_in_ep_regs_t *ep_regs;
+	dtxfsts_data_t txstatus = {.d32 = 0 };
+	uint32_t len = 0;
+	int epnum = dwc_ep->num;
+	int dwords;
+
+	DWC_DEBUGPL(DBG_PCD, "Dedicated TxFifo Empty: %d \n", epnum);
+
+	ep_regs = core_if->dev_if->in_ep_regs[epnum];
+
+	len = dwc_ep->xfer_len - dwc_ep->xfer_count;
+
+	if (len > dwc_ep->maxpacket) {
+		len = dwc_ep->maxpacket;
+	}
+
+	dwords = (len + 3) / 4;
+
+	/* While there is space in the queue and space in the FIFO and
+	 * More data to tranfer, Write packets to the Tx FIFO */
+	txstatus.d32 = DWC_READ_REG32(&dev_if->in_ep_regs[epnum]->dtxfsts);
+	DWC_DEBUGPL(DBG_PCDV, "b4 dtxfsts[%d]=0x%08x\n", epnum, txstatus.d32);
+
+	while (txstatus.b.txfspcavail > dwords &&
+	       dwc_ep->xfer_count < dwc_ep->xfer_len && dwc_ep->xfer_len != 0) {
+		/* Write the FIFO */
+		dwc_otg_ep_write_packet(core_if, dwc_ep, 0);
+
+		len = dwc_ep->xfer_len - dwc_ep->xfer_count;
+		if (len > dwc_ep->maxpacket) {
+			len = dwc_ep->maxpacket;
+		}
+
+		dwords = (len + 3) / 4;
+		txstatus.d32 =
+		    DWC_READ_REG32(&dev_if->in_ep_regs[epnum]->dtxfsts);
+		DWC_DEBUGPL(DBG_PCDV, "dtxfsts[%d]=0x%08x\n", epnum,
+			    txstatus.d32);
+	}
+
+	DWC_DEBUGPL(DBG_PCDV, "b4 dtxfsts[%d]=0x%08x\n", epnum,
+		    DWC_READ_REG32(&dev_if->in_ep_regs[epnum]->dtxfsts));
+
+	return 1;
+}
+/**
+ * This function does the setup for a data transfer for an EP and
+ * starts the transfer. For an IN transfer, the packets will be
+ * loaded into the appropriate Tx FIFO in the ISR. For OUT transfers,
+ * the packets are unloaded from the Rx FIFO in the ISR.  the ISR.
+ *
+ * @param core_if Programming view of DWC_otg controller.
+ * @param ep The EP to start the transfer on.
+ */
+
+void dwc_otg_ep_start_transfer(dwc_otg_core_if_t * core_if, dwc_ep_t * ep)
+{
+	depctl_data_t depctl;
+	deptsiz_data_t deptsiz;
+	gintmsk_data_t intr_mask = {.d32 = 0 };
+
+	DWC_DEBUGPL((DBG_PCDV | DBG_CILV), "%s()\n", __func__);
+	DWC_DEBUGPL(DBG_PCD, "ep%d-%s xfer_len=%d xfer_cnt=%d "
+		    "xfer_buff=%p start_xfer_buff=%p, total_len = %d\n",
+		    ep->num, (ep->is_in ? "IN" : "OUT"), ep->xfer_len,
+		    ep->xfer_count, ep->xfer_buff, ep->start_xfer_buff,
+		    ep->total_len);
+	/* IN endpoint */
+	if (ep->is_in == 1) {
+		dwc_otg_dev_in_ep_regs_t *in_regs =
+		    core_if->dev_if->in_ep_regs[ep->num];
+
+		gnptxsts_data_t gtxstatus;
+
+		gtxstatus.d32 =
+		    DWC_READ_REG32(&core_if->core_global_regs->gnptxsts);
+
+		if (core_if->en_multiple_tx_fifo == 0
+		    && gtxstatus.b.nptxqspcavail == 0 && !core_if->dma_enable) {
+#ifdef DEBUG
+			DWC_PRINTF("TX Queue Full (0x%0x)\n", gtxstatus.d32);
+#endif
+			return;
+		}
+
+		depctl.d32 = DWC_READ_REG32(&(in_regs->diepctl));
+		deptsiz.d32 = DWC_READ_REG32(&(in_regs->dieptsiz));
+
+		if (ep->maxpacket > ep->maxxfer / MAX_PKT_CNT)
+			ep->xfer_len += (ep->maxxfer < (ep->total_len - ep->xfer_len)) ?
+				ep->maxxfer : (ep->total_len - ep->xfer_len);
+		else
+			ep->xfer_len += (MAX_PKT_CNT * ep->maxpacket < (ep->total_len - ep->xfer_len)) ?
+				 MAX_PKT_CNT * ep->maxpacket : (ep->total_len - ep->xfer_len);
+
+
+		/* Zero Length Packet? */
+		if ((ep->xfer_len - ep->xfer_count) == 0) {
+			deptsiz.b.xfersize = 0;
+			deptsiz.b.pktcnt = 1;
+		} else {
+			/* Program the transfer size and packet count
+			 *      as follows: xfersize = N * maxpacket +
+			 *      short_packet pktcnt = N + (short_packet
+			 *      exist ? 1 : 0)
+			 */
+			deptsiz.b.xfersize = ep->xfer_len - ep->xfer_count;
+			deptsiz.b.pktcnt =
+			    (ep->xfer_len - ep->xfer_count - 1 +
+			     ep->maxpacket) / ep->maxpacket;
+			if (deptsiz.b.pktcnt > MAX_PKT_CNT) {
+				deptsiz.b.pktcnt = MAX_PKT_CNT;
+				deptsiz.b.xfersize = deptsiz.b.pktcnt * ep->maxpacket;
+			}
+			if (ep->type == DWC_OTG_EP_TYPE_ISOC)
+				deptsiz.b.mc = deptsiz.b.pktcnt;
+		}
+
+		/* Write the DMA register */
+		if (core_if->dma_enable) {
+			if (core_if->dma_desc_enable == 0) {
+				if (ep->type != DWC_OTG_EP_TYPE_ISOC)
+					deptsiz.b.mc = 1;
+				DWC_WRITE_REG32(&in_regs->dieptsiz,
+						deptsiz.d32);
+				DWC_WRITE_REG32(&(in_regs->diepdma),
+						(uint32_t) ep->dma_addr);
+			} else {
+#ifdef DWC_UTE_CFI
+				/* The descriptor chain should be already initialized by now */
+				if (ep->buff_mode != BM_STANDARD) {
+					DWC_WRITE_REG32(&in_regs->diepdma,
+							ep->descs_dma_addr);
+				} else {
+#endif
+					init_dma_desc_chain(core_if, ep);
+				/** DIEPDMAn Register write */
+					DWC_WRITE_REG32(&in_regs->diepdma,
+							ep->dma_desc_addr);
+#ifdef DWC_UTE_CFI
+				}
+#endif
+			}
+		} else {
+			DWC_WRITE_REG32(&in_regs->dieptsiz, deptsiz.d32);
+			if (ep->type != DWC_OTG_EP_TYPE_ISOC) {
+				/**
+				 * Enable the Non-Periodic Tx FIFO empty interrupt,
+				 * or the Tx FIFO epmty interrupt in dedicated Tx FIFO mode,
+				 * the data will be written into the fifo by the ISR.
+				 */
+				if (core_if->en_multiple_tx_fifo == 0) {
+					intr_mask.b.nptxfempty = 1;
+					DWC_MODIFY_REG32
+					    (&core_if->core_global_regs->gintmsk,
+					     intr_mask.d32, intr_mask.d32);
+				} else {
+					/* Enable the Tx FIFO Empty Interrupt for this EP */
+					if (ep->xfer_len > 0) {
+						uint32_t fifoemptymsk = 0;
+						fifoemptymsk = 1 << ep->num;
+						DWC_MODIFY_REG32
+						    (&core_if->dev_if->dev_global_regs->dtknqr4_fifoemptymsk,
+						     0, fifoemptymsk);
+
+					}
+				}
+			}  else {
+					 write_isoc_tx_fifo(core_if, ep);
+			}
+		}
+		if (!core_if->core_params->en_multiple_tx_fifo && core_if->dma_enable)
+			depctl.b.nextep = core_if->nextep_seq[ep->num];
+
+		if (ep->type == DWC_OTG_EP_TYPE_ISOC) {
+			dsts_data_t dsts = {.d32 = 0};
+			if (ep->bInterval == 1) {
+				dsts.d32 =
+				    DWC_READ_REG32(&core_if->dev_if->
+						   dev_global_regs->dsts);
+				ep->frame_num = dsts.b.soffn + ep->bInterval;
+				if (ep->frame_num > 0x3FFF) {
+					ep->frm_overrun = 1;
+					ep->frame_num &= 0x3FFF;
+				} else
+					ep->frm_overrun = 0;
+				if (ep->frame_num & 0x1) {
+					depctl.b.setd1pid = 1;
+				} else {
+					depctl.b.setd0pid = 1;
+				}
+			}
+		}
+		/* EP enable, IN data in FIFO */
+		depctl.b.cnak = 1;
+		depctl.b.epena = 1;
+		DWC_WRITE_REG32(&in_regs->diepctl, depctl.d32);
+
+	} else {
+		/* OUT endpoint */
+		dwc_otg_dev_out_ep_regs_t *out_regs =
+		    core_if->dev_if->out_ep_regs[ep->num];
+
+		depctl.d32 = DWC_READ_REG32(&(out_regs->doepctl));
+		deptsiz.d32 = DWC_READ_REG32(&(out_regs->doeptsiz));
+
+		if (!core_if->dma_desc_enable) {
+			if (ep->maxpacket > ep->maxxfer / MAX_PKT_CNT)
+				ep->xfer_len += (ep->maxxfer < (ep->total_len - ep->xfer_len)) ?
+				ep->maxxfer : (ep->total_len - ep->xfer_len);
+                else
+					ep->xfer_len += (MAX_PKT_CNT * ep->maxpacket < (ep->total_len
+					- ep->xfer_len)) ? MAX_PKT_CNT * ep->maxpacket : (ep->total_len - ep->xfer_len);
+		}
+
+		/* Program the transfer size and packet count as follows:
+		 *
+		 *      pktcnt = N
+		 *      xfersize = N * maxpacket
+		 */
+		if ((ep->xfer_len - ep->xfer_count) == 0) {
+			/* Zero Length Packet */
+			deptsiz.b.xfersize = ep->maxpacket;
+			deptsiz.b.pktcnt = 1;
+		} else {
+			deptsiz.b.pktcnt =
+			    (ep->xfer_len - ep->xfer_count +
+			     (ep->maxpacket - 1)) / ep->maxpacket;
+			if (deptsiz.b.pktcnt > MAX_PKT_CNT) {
+				deptsiz.b.pktcnt = MAX_PKT_CNT;
+			}
+			if (!core_if->dma_desc_enable) {
+				ep->xfer_len =
+					deptsiz.b.pktcnt * ep->maxpacket + ep->xfer_count;
+			}
+			deptsiz.b.xfersize = ep->xfer_len - ep->xfer_count;
+		}
+
+		DWC_DEBUGPL(DBG_PCDV, "ep%d xfersize=%d pktcnt=%d\n",
+			    ep->num, deptsiz.b.xfersize, deptsiz.b.pktcnt);
+
+		if (core_if->dma_enable) {
+			if (!core_if->dma_desc_enable) {
+				DWC_WRITE_REG32(&out_regs->doeptsiz,
+						deptsiz.d32);
+
+				DWC_WRITE_REG32(&(out_regs->doepdma),
+						(uint32_t) ep->dma_addr);
+			} else {
+#ifdef DWC_UTE_CFI
+				/* The descriptor chain should be already initialized by now */
+				if (ep->buff_mode != BM_STANDARD) {
+					DWC_WRITE_REG32(&out_regs->doepdma,
+							ep->descs_dma_addr);
+				} else {
+#endif
+					/** This is used for interrupt out transfers*/
+					if (!ep->xfer_len)
+						ep->xfer_len = ep->total_len;
+					init_dma_desc_chain(core_if, ep);
+
+					if (core_if->core_params->dev_out_nak) {
+						if (ep->type == DWC_OTG_EP_TYPE_BULK) {
+							deptsiz.b.pktcnt = (ep->total_len +
+								(ep->maxpacket - 1)) / ep->maxpacket;
+							deptsiz.b.xfersize = ep->total_len;
+							/* Remember initial value of doeptsiz */
+							core_if->start_doeptsiz_val[ep->num] = deptsiz.d32;
+							DWC_WRITE_REG32(&out_regs->doeptsiz,
+								deptsiz.d32);
+						}
+					}
+				/** DOEPDMAn Register write */
+					DWC_WRITE_REG32(&out_regs->doepdma,
+							ep->dma_desc_addr);
+#ifdef DWC_UTE_CFI
+				}
+#endif
+			}
+		} else {
+			DWC_WRITE_REG32(&out_regs->doeptsiz, deptsiz.d32);
+		}
+
+		if (ep->type == DWC_OTG_EP_TYPE_ISOC) {
+			dsts_data_t dsts = {.d32 = 0};
+			if (ep->bInterval == 1) {
+				dsts.d32 =
+				    DWC_READ_REG32(&core_if->dev_if->
+						   dev_global_regs->dsts);
+				ep->frame_num = dsts.b.soffn + ep->bInterval;
+				if (ep->frame_num > 0x3FFF) {
+					ep->frm_overrun = 1;
+					ep->frame_num &= 0x3FFF;
+				} else
+					ep->frm_overrun = 0;
+
+				if (ep->frame_num & 0x1) {
+					depctl.b.setd1pid = 1;
+				} else {
+					depctl.b.setd0pid = 1;
+				}
+			}
+		}
+
+		/* EP enable */
+		depctl.b.cnak = 1;
+		depctl.b.epena = 1;
+
+		DWC_WRITE_REG32(&out_regs->doepctl, depctl.d32);
+
+		DWC_DEBUGPL(DBG_PCD, "DOEPCTL=%08x DOEPTSIZ=%08x\n",
+			    DWC_READ_REG32(&out_regs->doepctl),
+			    DWC_READ_REG32(&out_regs->doeptsiz));
+		DWC_DEBUGPL(DBG_PCD, "DAINTMSK=%08x GINTMSK=%08x\n",
+			    DWC_READ_REG32(&core_if->dev_if->dev_global_regs->
+					   daintmsk),
+			    DWC_READ_REG32(&core_if->core_global_regs->
+					   gintmsk));
+
+		/* Timer is scheduling only for out bulk transfers for
+		 * "Device DDMA OUT NAK Enhancement" feature to inform user
+		 * about received data payload in case of timeout
+		 */
+		if (core_if->core_params->dev_out_nak) {
+			if (ep->type == DWC_OTG_EP_TYPE_BULK) {
+				core_if->ep_xfer_info[ep->num].core_if = core_if;
+				core_if->ep_xfer_info[ep->num].ep = ep;
+				core_if->ep_xfer_info[ep->num].state = 1;
+
+				/* Start a timer for this transfer. */
+				DWC_TIMER_SCHEDULE(core_if->ep_xfer_timer[ep->num], 10000);
+			}
+		}
+	}
+}
+
+/**
+ * This function setup a zero length transfer in Buffer DMA and
+ * Slave modes for usb requests with zero field set
+ *
+ * @param core_if Programming view of DWC_otg controller.
+ * @param ep The EP to start the transfer on.
+ *
+ */
+void dwc_otg_ep_start_zl_transfer(dwc_otg_core_if_t * core_if, dwc_ep_t * ep)
+{
+
+	depctl_data_t depctl;
+	deptsiz_data_t deptsiz;
+	gintmsk_data_t intr_mask = {.d32 = 0 };
+
+	DWC_DEBUGPL((DBG_PCDV | DBG_CILV), "%s()\n", __func__);
+	DWC_PRINTF("zero length transfer is called\n");
+
+	/* IN endpoint */
+	if (ep->is_in == 1) {
+		dwc_otg_dev_in_ep_regs_t *in_regs =
+		    core_if->dev_if->in_ep_regs[ep->num];
+
+		depctl.d32 = DWC_READ_REG32(&(in_regs->diepctl));
+		deptsiz.d32 = DWC_READ_REG32(&(in_regs->dieptsiz));
+
+		deptsiz.b.xfersize = 0;
+		deptsiz.b.pktcnt = 1;
+
+		/* Write the DMA register */
+		if (core_if->dma_enable) {
+			if (core_if->dma_desc_enable == 0) {
+				deptsiz.b.mc = 1;
+				DWC_WRITE_REG32(&in_regs->dieptsiz,
+						deptsiz.d32);
+				DWC_WRITE_REG32(&(in_regs->diepdma),
+						(uint32_t) ep->dma_addr);
+			}
+		} else {
+			DWC_WRITE_REG32(&in_regs->dieptsiz, deptsiz.d32);
+			/**
+			 * Enable the Non-Periodic Tx FIFO empty interrupt,
+			 * or the Tx FIFO epmty interrupt in dedicated Tx FIFO mode,
+			 * the data will be written into the fifo by the ISR.
+			 */
+			if (core_if->en_multiple_tx_fifo == 0) {
+				intr_mask.b.nptxfempty = 1;
+				DWC_MODIFY_REG32(&core_if->
+						 core_global_regs->gintmsk,
+						 intr_mask.d32, intr_mask.d32);
+			} else {
+				/* Enable the Tx FIFO Empty Interrupt for this EP */
+				if (ep->xfer_len > 0) {
+					uint32_t fifoemptymsk = 0;
+					fifoemptymsk = 1 << ep->num;
+					DWC_MODIFY_REG32(&core_if->
+							 dev_if->dev_global_regs->dtknqr4_fifoemptymsk,
+							 0, fifoemptymsk);
+				}
+			}
+		}
+
+		if (!core_if->core_params->en_multiple_tx_fifo && core_if->dma_enable)
+			depctl.b.nextep = core_if->nextep_seq[ep->num];
+		/* EP enable, IN data in FIFO */
+		depctl.b.cnak = 1;
+		depctl.b.epena = 1;
+		DWC_WRITE_REG32(&in_regs->diepctl, depctl.d32);
+
+	} else {
+		/* OUT endpoint */
+		dwc_otg_dev_out_ep_regs_t *out_regs =
+		    core_if->dev_if->out_ep_regs[ep->num];
+
+		depctl.d32 = DWC_READ_REG32(&(out_regs->doepctl));
+		deptsiz.d32 = DWC_READ_REG32(&(out_regs->doeptsiz));
+
+		/* Zero Length Packet */
+		deptsiz.b.xfersize = ep->maxpacket;
+		deptsiz.b.pktcnt = 1;
+
+		if (core_if->dma_enable) {
+			if (!core_if->dma_desc_enable) {
+				DWC_WRITE_REG32(&out_regs->doeptsiz,
+						deptsiz.d32);
+
+				DWC_WRITE_REG32(&(out_regs->doepdma),
+						(uint32_t) ep->dma_addr);
+			}
+		} else {
+			DWC_WRITE_REG32(&out_regs->doeptsiz, deptsiz.d32);
+		}
+
+		/* EP enable */
+		depctl.b.cnak = 1;
+		depctl.b.epena = 1;
+
+		DWC_WRITE_REG32(&out_regs->doepctl, depctl.d32);
+
+	}
+}
+
+/**
+ * This function does the setup for a data transfer for EP0 and starts
+ * the transfer.  For an IN transfer, the packets will be loaded into
+ * the appropriate Tx FIFO in the ISR. For OUT transfers, the packets are
+ * unloaded from the Rx FIFO in the ISR.
+ *
+ * @param core_if Programming view of DWC_otg controller.
+ * @param ep The EP0 data.
+ */
+void dwc_otg_ep0_start_transfer(dwc_otg_core_if_t * core_if, dwc_ep_t * ep)
+{
+	depctl_data_t depctl;
+	deptsiz0_data_t deptsiz;
+	gintmsk_data_t intr_mask = {.d32 = 0 };
+	dwc_otg_dev_dma_desc_t *dma_desc;
+
+	DWC_DEBUGPL(DBG_PCD, "ep%d-%s xfer_len=%d xfer_cnt=%d "
+		    "xfer_buff=%p start_xfer_buff=%p \n",
+		    ep->num, (ep->is_in ? "IN" : "OUT"), ep->xfer_len,
+		    ep->xfer_count, ep->xfer_buff, ep->start_xfer_buff);
+
+	ep->total_len = ep->xfer_len;
+
+	/* IN endpoint */
+	if (ep->is_in == 1) {
+		dwc_otg_dev_in_ep_regs_t *in_regs =
+		    core_if->dev_if->in_ep_regs[0];
+
+		gnptxsts_data_t gtxstatus;
+
+		if (core_if->snpsid >= OTG_CORE_REV_3_00a) {
+			depctl.d32 = DWC_READ_REG32(&in_regs->diepctl);
+			if (depctl.b.epena)
+				return;
+		}
+
+		gtxstatus.d32 =
+		    DWC_READ_REG32(&core_if->core_global_regs->gnptxsts);
+
+		/* If dedicated FIFO every time flush fifo before enable ep*/
+		if (core_if->en_multiple_tx_fifo && core_if->snpsid >= OTG_CORE_REV_3_00a)
+			dwc_otg_flush_tx_fifo(core_if, ep->tx_fifo_num);
+
+		if (core_if->en_multiple_tx_fifo == 0
+		    && gtxstatus.b.nptxqspcavail == 0
+		    && !core_if->dma_enable) {
+#ifdef DEBUG
+			deptsiz.d32 = DWC_READ_REG32(&in_regs->dieptsiz);
+			DWC_DEBUGPL(DBG_PCD, "DIEPCTL0=%0x\n",
+				    DWC_READ_REG32(&in_regs->diepctl));
+			DWC_DEBUGPL(DBG_PCD, "DIEPTSIZ0=%0x (sz=%d, pcnt=%d)\n",
+				    deptsiz.d32,
+				    deptsiz.b.xfersize, deptsiz.b.pktcnt);
+			DWC_PRINTF("TX Queue or FIFO Full (0x%0x)\n",
+				   gtxstatus.d32);
+#endif
+			return;
+		}
+
+		depctl.d32 = DWC_READ_REG32(&in_regs->diepctl);
+		deptsiz.d32 = DWC_READ_REG32(&in_regs->dieptsiz);
+
+		/* Zero Length Packet? */
+		if (ep->xfer_len == 0) {
+			deptsiz.b.xfersize = 0;
+			deptsiz.b.pktcnt = 1;
+		} else {
+			/* Program the transfer size and packet count
+			 *      as follows: xfersize = N * maxpacket +
+			 *      short_packet pktcnt = N + (short_packet
+			 *      exist ? 1 : 0)
+			 */
+			if (ep->xfer_len > ep->maxpacket) {
+				ep->xfer_len = ep->maxpacket;
+				deptsiz.b.xfersize = ep->maxpacket;
+			} else {
+				deptsiz.b.xfersize = ep->xfer_len;
+			}
+			deptsiz.b.pktcnt = 1;
+
+		}
+		DWC_DEBUGPL(DBG_PCDV,
+			    "IN len=%d  xfersize=%d pktcnt=%d [%08x]\n",
+			    ep->xfer_len, deptsiz.b.xfersize, deptsiz.b.pktcnt,
+			    deptsiz.d32);
+
+		/* Write the DMA register */
+		if (core_if->dma_enable) {
+			if (core_if->dma_desc_enable == 0) {
+				DWC_WRITE_REG32(&in_regs->dieptsiz,
+						deptsiz.d32);
+
+				DWC_WRITE_REG32(&(in_regs->diepdma),
+						(uint32_t) ep->dma_addr);
+			} else {
+				dma_desc = core_if->dev_if->in_desc_addr;
+
+				/** DMA Descriptor Setup */
+				dma_desc->status.b.bs = BS_HOST_BUSY;
+				dma_desc->status.b.l = 1;
+				dma_desc->status.b.ioc = 1;
+				dma_desc->status.b.sp =
+				    (ep->xfer_len == ep->maxpacket) ? 0 : 1;
+				dma_desc->status.b.bytes = ep->xfer_len;
+				dma_desc->buf = ep->dma_addr;
+				dma_desc->status.b.sts = 0;
+				dma_desc->status.b.bs = BS_HOST_READY;
+
+				/** DIEPDMA0 Register write */
+				DWC_WRITE_REG32(&in_regs->diepdma,
+						core_if->
+						dev_if->dma_in_desc_addr);
+			}
+		} else {
+			DWC_WRITE_REG32(&in_regs->dieptsiz, deptsiz.d32);
+		}
+
+		if (!core_if->core_params->en_multiple_tx_fifo && core_if->dma_enable)
+			depctl.b.nextep = core_if->nextep_seq[ep->num];
+		/* EP enable, IN data in FIFO */
+		depctl.b.cnak = 1;
+		depctl.b.epena = 1;
+		DWC_WRITE_REG32(&in_regs->diepctl, depctl.d32);
+
+		/**
+		 * Enable the Non-Periodic Tx FIFO empty interrupt, the
+		 * data will be written into the fifo by the ISR.
+		 */
+		if (!core_if->dma_enable) {
+			if (core_if->en_multiple_tx_fifo == 0) {
+				intr_mask.b.nptxfempty = 1;
+				DWC_MODIFY_REG32(&core_if->
+						 core_global_regs->gintmsk,
+						 intr_mask.d32, intr_mask.d32);
+			} else {
+				/* Enable the Tx FIFO Empty Interrupt for this EP */
+				if (ep->xfer_len > 0) {
+					uint32_t fifoemptymsk = 0;
+					fifoemptymsk |= 1 << ep->num;
+					DWC_MODIFY_REG32(&core_if->
+							 dev_if->dev_global_regs->dtknqr4_fifoemptymsk,
+							 0, fifoemptymsk);
+				}
+			}
+		}
+	} else {
+		/* OUT endpoint */
+		dwc_otg_dev_out_ep_regs_t *out_regs =
+		    core_if->dev_if->out_ep_regs[0];
+
+		depctl.d32 = DWC_READ_REG32(&out_regs->doepctl);
+		deptsiz.d32 = DWC_READ_REG32(&out_regs->doeptsiz);
+
+		/* Program the transfer size and packet count as follows:
+		 *      xfersize = N * (maxpacket + 4 - (maxpacket % 4))
+		 *      pktcnt = N                                                                                      */
+		/* Zero Length Packet */
+		deptsiz.b.xfersize = ep->maxpacket;
+		deptsiz.b.pktcnt = 1;
+		if (core_if->snpsid >= OTG_CORE_REV_3_00a)
+			deptsiz.b.supcnt = 3;
+
+		DWC_DEBUGPL(DBG_PCDV, "len=%d  xfersize=%d pktcnt=%d\n",
+			    ep->xfer_len, deptsiz.b.xfersize, deptsiz.b.pktcnt);
+
+		if (core_if->dma_enable) {
+			if (!core_if->dma_desc_enable) {
+				DWC_WRITE_REG32(&out_regs->doeptsiz,
+						deptsiz.d32);
+
+				DWC_WRITE_REG32(&(out_regs->doepdma),
+						(uint32_t) ep->dma_addr);
+			} else {
+				dma_desc = core_if->dev_if->out_desc_addr;
+
+				/** DMA Descriptor Setup */
+				dma_desc->status.b.bs = BS_HOST_BUSY;
+				if (core_if->snpsid >= OTG_CORE_REV_3_00a) {
+					dma_desc->status.b.mtrf = 0;
+					dma_desc->status.b.sr = 0;
+				}
+				dma_desc->status.b.l = 1;
+				dma_desc->status.b.ioc = 1;
+				dma_desc->status.b.bytes = ep->maxpacket;
+				dma_desc->buf = ep->dma_addr;
+				dma_desc->status.b.sts = 0;
+				dma_desc->status.b.bs = BS_HOST_READY;
+
+				/** DOEPDMA0 Register write */
+				DWC_WRITE_REG32(&out_regs->doepdma,
+						core_if->dev_if->
+						dma_out_desc_addr);
+			}
+		} else {
+			DWC_WRITE_REG32(&out_regs->doeptsiz, deptsiz.d32);
+		}
+
+		/* EP enable */
+		depctl.b.cnak = 1;
+		depctl.b.epena = 1;
+		DWC_WRITE_REG32(&(out_regs->doepctl), depctl.d32);
+	}
+}
+
+/**
+ * This function continues control IN transfers started by
+ * dwc_otg_ep0_start_transfer, when the transfer does not fit in a
+ * single packet.  NOTE: The DIEPCTL0/DOEPCTL0 registers only have one
+ * bit for the packet count.
+ *
+ * @param core_if Programming view of DWC_otg controller.
+ * @param ep The EP0 data.
+ */
+void dwc_otg_ep0_continue_transfer(dwc_otg_core_if_t * core_if, dwc_ep_t * ep)
+{
+	depctl_data_t depctl;
+	deptsiz0_data_t deptsiz;
+	gintmsk_data_t intr_mask = {.d32 = 0 };
+	dwc_otg_dev_dma_desc_t *dma_desc;
+
+	if (ep->is_in == 1) {
+		dwc_otg_dev_in_ep_regs_t *in_regs =
+		    core_if->dev_if->in_ep_regs[0];
+		gnptxsts_data_t tx_status = {.d32 = 0 };
+
+		tx_status.d32 =
+		    DWC_READ_REG32(&core_if->core_global_regs->gnptxsts);
+		/** @todo Should there be check for room in the Tx
+		 * Status Queue.  If not remove the code above this comment. */
+
+		depctl.d32 = DWC_READ_REG32(&in_regs->diepctl);
+		deptsiz.d32 = DWC_READ_REG32(&in_regs->dieptsiz);
+
+		/* Program the transfer size and packet count
+		 *      as follows: xfersize = N * maxpacket +
+		 *      short_packet pktcnt = N + (short_packet
+		 *      exist ? 1 : 0)
+		 */
+
+		if (core_if->dma_desc_enable == 0) {
+			deptsiz.b.xfersize =
+			    (ep->total_len - ep->xfer_count) >
+			    ep->maxpacket ? ep->maxpacket : (ep->total_len -
+							     ep->xfer_count);
+			deptsiz.b.pktcnt = 1;
+			if (core_if->dma_enable == 0) {
+				ep->xfer_len += deptsiz.b.xfersize;
+			} else {
+				ep->xfer_len = deptsiz.b.xfersize;
+			}
+			DWC_WRITE_REG32(&in_regs->dieptsiz, deptsiz.d32);
+		} else {
+			ep->xfer_len =
+			    (ep->total_len - ep->xfer_count) >
+			    ep->maxpacket ? ep->maxpacket : (ep->total_len -
+							     ep->xfer_count);
+
+			dma_desc = core_if->dev_if->in_desc_addr;
+
+			/** DMA Descriptor Setup */
+			dma_desc->status.b.bs = BS_HOST_BUSY;
+			dma_desc->status.b.l = 1;
+			dma_desc->status.b.ioc = 1;
+			dma_desc->status.b.sp =
+			    (ep->xfer_len == ep->maxpacket) ? 0 : 1;
+			dma_desc->status.b.bytes = ep->xfer_len;
+			dma_desc->buf = ep->dma_addr;
+			dma_desc->status.b.sts = 0;
+			dma_desc->status.b.bs = BS_HOST_READY;
+
+			/** DIEPDMA0 Register write */
+			DWC_WRITE_REG32(&in_regs->diepdma,
+					core_if->dev_if->dma_in_desc_addr);
+		}
+
+		DWC_DEBUGPL(DBG_PCDV,
+			    "IN len=%d  xfersize=%d pktcnt=%d [%08x]\n",
+			    ep->xfer_len, deptsiz.b.xfersize, deptsiz.b.pktcnt,
+			    deptsiz.d32);
+
+		/* Write the DMA register */
+		if (core_if->hwcfg2.b.architecture == DWC_INT_DMA_ARCH) {
+			if (core_if->dma_desc_enable == 0)
+				DWC_WRITE_REG32(&(in_regs->diepdma),
+						(uint32_t) ep->dma_addr);
+		}
+		if (!core_if->core_params->en_multiple_tx_fifo && core_if->dma_enable)
+			depctl.b.nextep = core_if->nextep_seq[ep->num];
+		/* EP enable, IN data in FIFO */
+		depctl.b.cnak = 1;
+		depctl.b.epena = 1;
+		DWC_WRITE_REG32(&in_regs->diepctl, depctl.d32);
+
+		/**
+		 * Enable the Non-Periodic Tx FIFO empty interrupt, the
+		 * data will be written into the fifo by the ISR.
+		 */
+		if (!core_if->dma_enable) {
+			if (core_if->en_multiple_tx_fifo == 0) {
+				/* First clear it from GINTSTS */
+				intr_mask.b.nptxfempty = 1;
+				DWC_MODIFY_REG32(&core_if->
+						 core_global_regs->gintmsk,
+						 intr_mask.d32, intr_mask.d32);
+
+			} else {
+				/* Enable the Tx FIFO Empty Interrupt for this EP */
+				if (ep->xfer_len > 0) {
+					uint32_t fifoemptymsk = 0;
+					fifoemptymsk |= 1 << ep->num;
+					DWC_MODIFY_REG32(&core_if->
+							 dev_if->dev_global_regs->dtknqr4_fifoemptymsk,
+							 0, fifoemptymsk);
+				}
+			}
+		}
+	} else {
+		dwc_otg_dev_out_ep_regs_t *out_regs =
+		    core_if->dev_if->out_ep_regs[0];
+
+		depctl.d32 = DWC_READ_REG32(&out_regs->doepctl);
+		deptsiz.d32 = DWC_READ_REG32(&out_regs->doeptsiz);
+
+		/* Program the transfer size and packet count
+		 *      as follows: xfersize = N * maxpacket +
+		 *      short_packet pktcnt = N + (short_packet
+		 *      exist ? 1 : 0)
+		 */
+		deptsiz.b.xfersize = ep->maxpacket;
+		deptsiz.b.pktcnt = 1;
+
+		if (core_if->dma_desc_enable == 0) {
+			DWC_WRITE_REG32(&out_regs->doeptsiz, deptsiz.d32);
+		} else {
+			dma_desc = core_if->dev_if->out_desc_addr;
+
+			/** DMA Descriptor Setup */
+			dma_desc->status.b.bs = BS_HOST_BUSY;
+			dma_desc->status.b.l = 1;
+			dma_desc->status.b.ioc = 1;
+			dma_desc->status.b.bytes = ep->maxpacket;
+			dma_desc->buf = ep->dma_addr;
+			dma_desc->status.b.sts = 0;
+			dma_desc->status.b.bs = BS_HOST_READY;
+
+			/** DOEPDMA0 Register write */
+			DWC_WRITE_REG32(&out_regs->doepdma,
+					core_if->dev_if->dma_out_desc_addr);
+		}
+
+		DWC_DEBUGPL(DBG_PCDV,
+			    "IN len=%d  xfersize=%d pktcnt=%d [%08x]\n",
+			    ep->xfer_len, deptsiz.b.xfersize, deptsiz.b.pktcnt,
+			    deptsiz.d32);
+
+		/* Write the DMA register */
+		if (core_if->hwcfg2.b.architecture == DWC_INT_DMA_ARCH) {
+			if (core_if->dma_desc_enable == 0)
+				DWC_WRITE_REG32(&(out_regs->doepdma),
+						(uint32_t) ep->dma_addr);
+
+		}
+
+		/* EP enable, IN data in FIFO */
+		depctl.b.cnak = 1;
+		depctl.b.epena = 1;
+		DWC_WRITE_REG32(&out_regs->doepctl, depctl.d32);
+
+	}
+}
+
+#ifdef DEBUG
+void dump_msg(const u8 * buf, unsigned int length)
+{
+	unsigned int start, num, i;
+	char line[52], *p;
+
+	if (length >= 512)
+		return;
+	start = 0;
+	while (length > 0) {
+		num = length < 16u ? length : 16u;
+		p = line;
+		for (i = 0; i < num; ++i) {
+			if (i == 8)
+				*p++ = ' ';
+			DWC_SPRINTF(p, " %02x", buf[i]);
+			p += 3;
+		}
+		*p = 0;
+		DWC_PRINTF("%6x: %s\n", start, line);
+		buf += num;
+		start += num;
+		length -= num;
+	}
+}
+#else
+static inline void dump_msg(const u8 * buf, unsigned int length)
+{
+}
+#endif
+
+/**
+ * This function writes a packet into the Tx FIFO associated with the
+ * EP. For non-periodic EPs the non-periodic Tx FIFO is written.  For
+ * periodic EPs the periodic Tx FIFO associated with the EP is written
+ * with all packets for the next micro-frame.
+ *
+ * @param core_if Programming view of DWC_otg controller.
+ * @param ep The EP to write packet for.
+ * @param dma Indicates if DMA is being used.
+ */
+void dwc_otg_ep_write_packet(dwc_otg_core_if_t * core_if, dwc_ep_t * ep,
+			     int dma)
+{
+	/**
+	 * The buffer is padded to DWORD on a per packet basis in
+	 * slave/dma mode if the MPS is not DWORD aligned. The last
+	 * packet, if short, is also padded to a multiple of DWORD.
+	 *
+	 * ep->xfer_buff always starts DWORD aligned in memory and is a
+	 * multiple of DWORD in length
+	 *
+	 * ep->xfer_len can be any number of bytes
+	 *
+	 * ep->xfer_count is a multiple of ep->maxpacket until the last
+	 *	packet
+	 *
+	 * FIFO access is DWORD */
+
+	uint32_t i;
+	uint32_t byte_count;
+	uint32_t dword_count;
+	uint32_t *fifo;
+	uint32_t *data_buff = (uint32_t *) ep->xfer_buff;
+
+	DWC_DEBUGPL((DBG_PCDV | DBG_CILV), "%s(%p,%p)\n", __func__, core_if,
+		    ep);
+	if (ep->xfer_count >= ep->xfer_len) {
+		DWC_WARN("%s() No data for EP%d!!!\n", __func__, ep->num);
+		return;
+	}
+
+	/* Find the byte length of the packet either short packet or MPS */
+	if ((ep->xfer_len - ep->xfer_count) < ep->maxpacket) {
+		byte_count = ep->xfer_len - ep->xfer_count;
+	} else {
+		byte_count = ep->maxpacket;
+	}
+
+	/* Find the DWORD length, padded by extra bytes as neccessary if MPS
+	 * is not a multiple of DWORD */
+	dword_count = (byte_count + 3) / 4;
+
+#ifdef VERBOSE
+	dump_msg(ep->xfer_buff, byte_count);
+#endif
+
+	/**@todo NGS Where are the Periodic Tx FIFO addresses
+	 * intialized?	What should this be? */
+
+	fifo = core_if->data_fifo[ep->num];
+
+	DWC_DEBUGPL((DBG_PCDV | DBG_CILV), "fifo=%p buff=%p *p=%08x bc=%d\n",
+		    fifo, data_buff, *data_buff, byte_count);
+
+	if (!dma) {
+		for (i = 0; i < dword_count; i++, data_buff++) {
+			DWC_WRITE_REG32(fifo, *data_buff);
+		}
+	}
+
+	ep->xfer_count += byte_count;
+	ep->xfer_buff += byte_count;
+	ep->dma_addr += byte_count;
+}
+
+/**
+ * Set the EP STALL.
+ *
+ * @param core_if Programming view of DWC_otg controller.
+ * @param ep The EP to set the stall on.
+ */
+void dwc_otg_ep_set_stall(dwc_otg_core_if_t * core_if, dwc_ep_t * ep)
+{
+	depctl_data_t depctl;
+	volatile uint32_t *depctl_addr;
+
+	DWC_DEBUGPL(DBG_PCD, "%s ep%d-%s\n", __func__, ep->num,
+		    (ep->is_in ? "IN" : "OUT"));
+
+	if (ep->is_in == 1) {
+		depctl_addr = &(core_if->dev_if->in_ep_regs[ep->num]->diepctl);
+		depctl.d32 = DWC_READ_REG32(depctl_addr);
+
+		/* set the disable and stall bits */
+		if (depctl.b.epena) {
+			depctl.b.epdis = 1;
+		}
+		depctl.b.stall = 1;
+		DWC_WRITE_REG32(depctl_addr, depctl.d32);
+	} else {
+		depctl_addr = &(core_if->dev_if->out_ep_regs[ep->num]->doepctl);
+		depctl.d32 = DWC_READ_REG32(depctl_addr);
+
+		/* set the stall bit */
+		depctl.b.stall = 1;
+		DWC_WRITE_REG32(depctl_addr, depctl.d32);
+	}
+
+	DWC_DEBUGPL(DBG_PCD, "DEPCTL=%0x\n", DWC_READ_REG32(depctl_addr));
+
+	return;
+}
+
+/**
+ * Clear the EP STALL.
+ *
+ * @param core_if Programming view of DWC_otg controller.
+ * @param ep The EP to clear stall from.
+ */
+void dwc_otg_ep_clear_stall(dwc_otg_core_if_t * core_if, dwc_ep_t * ep)
+{
+	depctl_data_t depctl;
+	volatile uint32_t *depctl_addr;
+
+	DWC_DEBUGPL(DBG_PCD, "%s ep%d-%s\n", __func__, ep->num,
+		    (ep->is_in ? "IN" : "OUT"));
+
+	if (ep->is_in == 1) {
+		depctl_addr = &(core_if->dev_if->in_ep_regs[ep->num]->diepctl);
+	} else {
+		depctl_addr = &(core_if->dev_if->out_ep_regs[ep->num]->doepctl);
+	}
+
+	depctl.d32 = DWC_READ_REG32(depctl_addr);
+
+	/* clear the stall bits */
+	depctl.b.stall = 0;
+
+	/*
+	 * USB Spec 9.4.5: For endpoints using data toggle, regardless
+	 * of whether an endpoint has the Halt feature set, a
+	 * ClearFeature(ENDPOINT_HALT) request always results in the
+	 * data toggle being reinitialized to DATA0.
+	 */
+	if (ep->type == DWC_OTG_EP_TYPE_INTR ||
+	    ep->type == DWC_OTG_EP_TYPE_BULK) {
+		depctl.b.setd0pid = 1;	/* DATA0 */
+	}
+
+	DWC_WRITE_REG32(depctl_addr, depctl.d32);
+	DWC_DEBUGPL(DBG_PCD, "DEPCTL=%0x\n", DWC_READ_REG32(depctl_addr));
+	return;
+}
+
+/**
+ * This function reads a packet from the Rx FIFO into the destination
+ * buffer. To read SETUP data use dwc_otg_read_setup_packet.
+ *
+ * @param core_if Programming view of DWC_otg controller.
+ * @param dest	  Destination buffer for the packet.
+ * @param bytes  Number of bytes to copy to the destination.
+ */
+void dwc_otg_read_packet(dwc_otg_core_if_t * core_if,
+			 uint8_t * dest, uint16_t bytes)
+{
+	int i;
+	int word_count = (bytes + 3) / 4;
+
+	volatile uint32_t *fifo = core_if->data_fifo[0];
+	uint32_t *data_buff = (uint32_t *) dest;
+
+	/**
+	 * @todo Account for the case where _dest is not dword aligned. This
+	 * requires reading data from the FIFO into a uint32_t temp buffer,
+	 * then moving it into the data buffer.
+	 */
+
+	DWC_DEBUGPL((DBG_PCDV | DBG_CILV), "%s(%p,%p,%d)\n", __func__,
+		    core_if, dest, bytes);
+
+	for (i = 0; i < word_count; i++, data_buff++) {
+		*data_buff = DWC_READ_REG32(fifo);
+	}
+
+	return;
+}
+
+/**
+ * This functions reads the device registers and prints them
+ *
+ * @param core_if Programming view of DWC_otg controller.
+ */
+void dwc_otg_dump_dev_registers(dwc_otg_core_if_t * core_if)
+{
+	int i;
+	volatile uint32_t *addr;
+
+	DWC_PRINTF("Device Global Registers\n");
+	addr = &core_if->dev_if->dev_global_regs->dcfg;
+	DWC_PRINTF("DCFG		 @0x%08lX : 0x%08X\n",
+		   (unsigned long)addr, DWC_READ_REG32(addr));
+	addr = &core_if->dev_if->dev_global_regs->dctl;
+	DWC_PRINTF("DCTL		 @0x%08lX : 0x%08X\n",
+		   (unsigned long)addr, DWC_READ_REG32(addr));
+	addr = &core_if->dev_if->dev_global_regs->dsts;
+	DWC_PRINTF("DSTS		 @0x%08lX : 0x%08X\n",
+		   (unsigned long)addr, DWC_READ_REG32(addr));
+	addr = &core_if->dev_if->dev_global_regs->diepmsk;
+	DWC_PRINTF("DIEPMSK	 @0x%08lX : 0x%08X\n", (unsigned long)addr,
+		   DWC_READ_REG32(addr));
+	addr = &core_if->dev_if->dev_global_regs->doepmsk;
+	DWC_PRINTF("DOEPMSK	 @0x%08lX : 0x%08X\n", (unsigned long)addr,
+		   DWC_READ_REG32(addr));
+	addr = &core_if->dev_if->dev_global_regs->daint;
+	DWC_PRINTF("DAINT	 @0x%08lX : 0x%08X\n", (unsigned long)addr,
+		   DWC_READ_REG32(addr));
+	addr = &core_if->dev_if->dev_global_regs->daintmsk;
+	DWC_PRINTF("DAINTMSK	 @0x%08lX : 0x%08X\n", (unsigned long)addr,
+		   DWC_READ_REG32(addr));
+	addr = &core_if->dev_if->dev_global_regs->dtknqr1;
+	DWC_PRINTF("DTKNQR1	 @0x%08lX : 0x%08X\n", (unsigned long)addr,
+		   DWC_READ_REG32(addr));
+	if (core_if->hwcfg2.b.dev_token_q_depth > 6) {
+		addr = &core_if->dev_if->dev_global_regs->dtknqr2;
+		DWC_PRINTF("DTKNQR2	 @0x%08lX : 0x%08X\n",
+			   (unsigned long)addr, DWC_READ_REG32(addr));
+	}
+
+	addr = &core_if->dev_if->dev_global_regs->dvbusdis;
+	DWC_PRINTF("DVBUSID	 @0x%08lX : 0x%08X\n", (unsigned long)addr,
+		   DWC_READ_REG32(addr));
+
+	addr = &core_if->dev_if->dev_global_regs->dvbuspulse;
+	DWC_PRINTF("DVBUSPULSE	@0x%08lX : 0x%08X\n",
+		   (unsigned long)addr, DWC_READ_REG32(addr));
+
+	addr = &core_if->dev_if->dev_global_regs->dtknqr3_dthrctl;
+	DWC_PRINTF("DTKNQR3_DTHRCTL	 @0x%08lX : 0x%08X\n",
+		   (unsigned long)addr, DWC_READ_REG32(addr));
+
+	if (core_if->hwcfg2.b.dev_token_q_depth > 22) {
+		addr = &core_if->dev_if->dev_global_regs->dtknqr4_fifoemptymsk;
+		DWC_PRINTF("DTKNQR4	 @0x%08lX : 0x%08X\n",
+			   (unsigned long)addr, DWC_READ_REG32(addr));
+	}
+
+	addr = &core_if->dev_if->dev_global_regs->dtknqr4_fifoemptymsk;
+	DWC_PRINTF("FIFOEMPMSK	 @0x%08lX : 0x%08X\n", (unsigned long)addr,
+		   DWC_READ_REG32(addr));
+
+	if (core_if->hwcfg2.b.multi_proc_int) {
+
+		addr = &core_if->dev_if->dev_global_regs->deachint;
+		DWC_PRINTF("DEACHINT	 @0x%08lX : 0x%08X\n",
+			   (unsigned long)addr, DWC_READ_REG32(addr));
+		addr = &core_if->dev_if->dev_global_regs->deachintmsk;
+		DWC_PRINTF("DEACHINTMSK	 @0x%08lX : 0x%08X\n",
+			   (unsigned long)addr, DWC_READ_REG32(addr));
+
+		for (i = 0; i <= core_if->dev_if->num_in_eps; i++) {
+			addr =
+			    &core_if->dev_if->
+			    dev_global_regs->diepeachintmsk[i];
+			DWC_PRINTF("DIEPEACHINTMSK[%d]	 @0x%08lX : 0x%08X\n",
+				   i, (unsigned long)addr,
+				   DWC_READ_REG32(addr));
+		}
+
+		for (i = 0; i <= core_if->dev_if->num_out_eps; i++) {
+			addr =
+			    &core_if->dev_if->
+			    dev_global_regs->doepeachintmsk[i];
+			DWC_PRINTF("DOEPEACHINTMSK[%d]	 @0x%08lX : 0x%08X\n",
+				   i, (unsigned long)addr,
+				   DWC_READ_REG32(addr));
+		}
+	}
+
+	for (i = 0; i <= core_if->dev_if->num_in_eps; i++) {
+		DWC_PRINTF("Device IN EP %d Registers\n", i);
+		addr = &core_if->dev_if->in_ep_regs[i]->diepctl;
+		DWC_PRINTF("DIEPCTL	 @0x%08lX : 0x%08X\n",
+			   (unsigned long)addr, DWC_READ_REG32(addr));
+		addr = &core_if->dev_if->in_ep_regs[i]->diepint;
+		DWC_PRINTF("DIEPINT	 @0x%08lX : 0x%08X\n",
+			   (unsigned long)addr, DWC_READ_REG32(addr));
+		addr = &core_if->dev_if->in_ep_regs[i]->dieptsiz;
+		DWC_PRINTF("DIETSIZ	 @0x%08lX : 0x%08X\n",
+			   (unsigned long)addr, DWC_READ_REG32(addr));
+		addr = &core_if->dev_if->in_ep_regs[i]->diepdma;
+		DWC_PRINTF("DIEPDMA	 @0x%08lX : 0x%08X\n",
+			   (unsigned long)addr, DWC_READ_REG32(addr));
+		addr = &core_if->dev_if->in_ep_regs[i]->dtxfsts;
+		DWC_PRINTF("DTXFSTS	 @0x%08lX : 0x%08X\n",
+			   (unsigned long)addr, DWC_READ_REG32(addr));
+		addr = &core_if->dev_if->in_ep_regs[i]->diepdmab;
+		DWC_PRINTF("DIEPDMAB	 @0x%08lX : 0x%08X\n",
+			   (unsigned long)addr, 0 /*DWC_READ_REG32(addr) */ );
+	}
+
+	for (i = 0; i <= core_if->dev_if->num_out_eps; i++) {
+		DWC_PRINTF("Device OUT EP %d Registers\n", i);
+		addr = &core_if->dev_if->out_ep_regs[i]->doepctl;
+		DWC_PRINTF("DOEPCTL	 @0x%08lX : 0x%08X\n",
+			   (unsigned long)addr, DWC_READ_REG32(addr));
+		addr = &core_if->dev_if->out_ep_regs[i]->doepint;
+		DWC_PRINTF("DOEPINT	 @0x%08lX : 0x%08X\n",
+			   (unsigned long)addr, DWC_READ_REG32(addr));
+		addr = &core_if->dev_if->out_ep_regs[i]->doeptsiz;
+		DWC_PRINTF("DOETSIZ	 @0x%08lX : 0x%08X\n",
+			   (unsigned long)addr, DWC_READ_REG32(addr));
+		addr = &core_if->dev_if->out_ep_regs[i]->doepdma;
+		DWC_PRINTF("DOEPDMA	 @0x%08lX : 0x%08X\n",
+			   (unsigned long)addr, DWC_READ_REG32(addr));
+		if (core_if->dma_enable) {	/* Don't access this register in SLAVE mode */
+			addr = &core_if->dev_if->out_ep_regs[i]->doepdmab;
+			DWC_PRINTF("DOEPDMAB	 @0x%08lX : 0x%08X\n",
+				   (unsigned long)addr, DWC_READ_REG32(addr));
+		}
+
+	}
+}
+
+/**
+ * This functions reads the SPRAM and prints its content
+ *
+ * @param core_if Programming view of DWC_otg controller.
+ */
+void dwc_otg_dump_spram(dwc_otg_core_if_t * core_if)
+{
+	volatile uint8_t *addr, *start_addr, *end_addr;
+
+	DWC_PRINTF("SPRAM Data:\n");
+	start_addr = (void *)core_if->core_global_regs;
+	DWC_PRINTF("Base Address: 0x%8lX\n", (unsigned long)start_addr);
+	start_addr += 0x00028000;
+	end_addr = (void *)core_if->core_global_regs;
+	end_addr += 0x000280e0;
+
+	for (addr = start_addr; addr < end_addr; addr += 16) {
+		DWC_PRINTF
+		    ("0x%8lX:\t%2X %2X %2X %2X %2X %2X %2X %2X %2X %2X %2X %2X %2X %2X %2X %2X\n",
+		     (unsigned long)addr, addr[0], addr[1], addr[2], addr[3],
+		     addr[4], addr[5], addr[6], addr[7], addr[8], addr[9],
+		     addr[10], addr[11], addr[12], addr[13], addr[14], addr[15]
+		    );
+	}
+
+	return;
+}
+
+/**
+ * This function reads the host registers and prints them
+ *
+ * @param core_if Programming view of DWC_otg controller.
+ */
+void dwc_otg_dump_host_registers(dwc_otg_core_if_t * core_if)
+{
+	int i;
+	volatile uint32_t *addr;
+
+	DWC_PRINTF("Host Global Registers\n");
+	addr = &core_if->host_if->host_global_regs->hcfg;
+	DWC_PRINTF("HCFG		 @0x%08lX : 0x%08X\n",
+		   (unsigned long)addr, DWC_READ_REG32(addr));
+	addr = &core_if->host_if->host_global_regs->hfir;
+	DWC_PRINTF("HFIR		 @0x%08lX : 0x%08X\n",
+		   (unsigned long)addr, DWC_READ_REG32(addr));
+	addr = &core_if->host_if->host_global_regs->hfnum;
+	DWC_PRINTF("HFNUM	 @0x%08lX : 0x%08X\n", (unsigned long)addr,
+		   DWC_READ_REG32(addr));
+	addr = &core_if->host_if->host_global_regs->hptxsts;
+	DWC_PRINTF("HPTXSTS	 @0x%08lX : 0x%08X\n", (unsigned long)addr,
+		   DWC_READ_REG32(addr));
+	addr = &core_if->host_if->host_global_regs->haint;
+	DWC_PRINTF("HAINT	 @0x%08lX : 0x%08X\n", (unsigned long)addr,
+		   DWC_READ_REG32(addr));
+	addr = &core_if->host_if->host_global_regs->haintmsk;
+	DWC_PRINTF("HAINTMSK	 @0x%08lX : 0x%08X\n", (unsigned long)addr,
+		   DWC_READ_REG32(addr));
+	if (core_if->dma_desc_enable) {
+		addr = &core_if->host_if->host_global_regs->hflbaddr;
+		DWC_PRINTF("HFLBADDR	 @0x%08lX : 0x%08X\n",
+			   (unsigned long)addr, DWC_READ_REG32(addr));
+	}
+
+	addr = core_if->host_if->hprt0;
+	DWC_PRINTF("HPRT0	 @0x%08lX : 0x%08X\n", (unsigned long)addr,
+		   DWC_READ_REG32(addr));
+
+	for (i = 0; i < core_if->core_params->host_channels; i++) {
+		DWC_PRINTF("Host Channel %d Specific Registers\n", i);
+		addr = &core_if->host_if->hc_regs[i]->hcchar;
+		DWC_PRINTF("HCCHAR	 @0x%08lX : 0x%08X\n",
+			   (unsigned long)addr, DWC_READ_REG32(addr));
+		addr = &core_if->host_if->hc_regs[i]->hcsplt;
+		DWC_PRINTF("HCSPLT	 @0x%08lX : 0x%08X\n",
+			   (unsigned long)addr, DWC_READ_REG32(addr));
+		addr = &core_if->host_if->hc_regs[i]->hcint;
+		DWC_PRINTF("HCINT	 @0x%08lX : 0x%08X\n",
+			   (unsigned long)addr, DWC_READ_REG32(addr));
+		addr = &core_if->host_if->hc_regs[i]->hcintmsk;
+		DWC_PRINTF("HCINTMSK	 @0x%08lX : 0x%08X\n",
+			   (unsigned long)addr, DWC_READ_REG32(addr));
+		addr = &core_if->host_if->hc_regs[i]->hctsiz;
+		DWC_PRINTF("HCTSIZ	 @0x%08lX : 0x%08X\n",
+			   (unsigned long)addr, DWC_READ_REG32(addr));
+		addr = &core_if->host_if->hc_regs[i]->hcdma;
+		DWC_PRINTF("HCDMA	 @0x%08lX : 0x%08X\n",
+			   (unsigned long)addr, DWC_READ_REG32(addr));
+		if (core_if->dma_desc_enable) {
+			addr = &core_if->host_if->hc_regs[i]->hcdmab;
+			DWC_PRINTF("HCDMAB	 @0x%08lX : 0x%08X\n",
+				   (unsigned long)addr, DWC_READ_REG32(addr));
+		}
+
+	}
+	return;
+}
+
+/**
+ * This function reads the core global registers and prints them
+ *
+ * @param core_if Programming view of DWC_otg controller.
+ */
+void dwc_otg_dump_global_registers(dwc_otg_core_if_t * core_if)
+{
+	int i, ep_num;
+	volatile uint32_t *addr;
+	char *txfsiz;
+
+	DWC_PRINTF("Core Global Registers\n");
+	addr = &core_if->core_global_regs->gotgctl;
+	DWC_PRINTF("GOTGCTL	 @0x%08lX : 0x%08X\n", (unsigned long)addr,
+		   DWC_READ_REG32(addr));
+	addr = &core_if->core_global_regs->gotgint;
+	DWC_PRINTF("GOTGINT	 @0x%08lX : 0x%08X\n", (unsigned long)addr,
+		   DWC_READ_REG32(addr));
+	addr = &core_if->core_global_regs->gahbcfg;
+	DWC_PRINTF("GAHBCFG	 @0x%08lX : 0x%08X\n", (unsigned long)addr,
+		   DWC_READ_REG32(addr));
+	addr = &core_if->core_global_regs->gusbcfg;
+	DWC_PRINTF("GUSBCFG	 @0x%08lX : 0x%08X\n", (unsigned long)addr,
+		   DWC_READ_REG32(addr));
+	addr = &core_if->core_global_regs->grstctl;
+	DWC_PRINTF("GRSTCTL	 @0x%08lX : 0x%08X\n", (unsigned long)addr,
+		   DWC_READ_REG32(addr));
+	addr = &core_if->core_global_regs->gintsts;
+	DWC_PRINTF("GINTSTS	 @0x%08lX : 0x%08X\n", (unsigned long)addr,
+		   DWC_READ_REG32(addr));
+	addr = &core_if->core_global_regs->gintmsk;
+	DWC_PRINTF("GINTMSK	 @0x%08lX : 0x%08X\n", (unsigned long)addr,
+		   DWC_READ_REG32(addr));
+	addr = &core_if->core_global_regs->grxstsr;
+	DWC_PRINTF("GRXSTSR	 @0x%08lX : 0x%08X\n", (unsigned long)addr,
+		   DWC_READ_REG32(addr));
+	addr = &core_if->core_global_regs->grxfsiz;
+	DWC_PRINTF("GRXFSIZ	 @0x%08lX : 0x%08X\n", (unsigned long)addr,
+		   DWC_READ_REG32(addr));
+	addr = &core_if->core_global_regs->gnptxfsiz;
+	DWC_PRINTF("GNPTXFSIZ @0x%08lX : 0x%08X\n", (unsigned long)addr,
+		   DWC_READ_REG32(addr));
+	addr = &core_if->core_global_regs->gnptxsts;
+	DWC_PRINTF("GNPTXSTS	 @0x%08lX : 0x%08X\n", (unsigned long)addr,
+		   DWC_READ_REG32(addr));
+	addr = &core_if->core_global_regs->gi2cctl;
+	DWC_PRINTF("GI2CCTL	 @0x%08lX : 0x%08X\n", (unsigned long)addr,
+		   DWC_READ_REG32(addr));
+	addr = &core_if->core_global_regs->gpvndctl;
+	DWC_PRINTF("GPVNDCTL	 @0x%08lX : 0x%08X\n", (unsigned long)addr,
+		   DWC_READ_REG32(addr));
+	addr = &core_if->core_global_regs->ggpio;
+	DWC_PRINTF("GGPIO	 @0x%08lX : 0x%08X\n", (unsigned long)addr,
+		   DWC_READ_REG32(addr));
+	addr = &core_if->core_global_regs->guid;
+	DWC_PRINTF("GUID		 @0x%08lX : 0x%08X\n",
+		   (unsigned long)addr, DWC_READ_REG32(addr));
+	addr = &core_if->core_global_regs->gsnpsid;
+	DWC_PRINTF("GSNPSID	 @0x%08lX : 0x%08X\n", (unsigned long)addr,
+		   DWC_READ_REG32(addr));
+	addr = &core_if->core_global_regs->ghwcfg1;
+	DWC_PRINTF("GHWCFG1	 @0x%08lX : 0x%08X\n", (unsigned long)addr,
+		   DWC_READ_REG32(addr));
+	addr = &core_if->core_global_regs->ghwcfg2;
+	DWC_PRINTF("GHWCFG2	 @0x%08lX : 0x%08X\n", (unsigned long)addr,
+		   DWC_READ_REG32(addr));
+	addr = &core_if->core_global_regs->ghwcfg3;
+	DWC_PRINTF("GHWCFG3	 @0x%08lX : 0x%08X\n", (unsigned long)addr,
+		   DWC_READ_REG32(addr));
+	addr = &core_if->core_global_regs->ghwcfg4;
+	DWC_PRINTF("GHWCFG4	 @0x%08lX : 0x%08X\n", (unsigned long)addr,
+		   DWC_READ_REG32(addr));
+	addr = &core_if->core_global_regs->glpmcfg;
+	DWC_PRINTF("GLPMCFG	 @0x%08lX : 0x%08X\n", (unsigned long)addr,
+		   DWC_READ_REG32(addr));
+	addr = &core_if->core_global_regs->gpwrdn;
+	DWC_PRINTF("GPWRDN	 @0x%08lX : 0x%08X\n", (unsigned long)addr,
+		   DWC_READ_REG32(addr));
+	addr = &core_if->core_global_regs->gdfifocfg;
+	DWC_PRINTF("GDFIFOCFG	 @0x%08lX : 0x%08X\n", (unsigned long)addr,
+		   DWC_READ_REG32(addr));
+	addr = &core_if->core_global_regs->adpctl;
+	DWC_PRINTF("ADPCTL	 @0x%08lX : 0x%08X\n", (unsigned long)addr,
+		   dwc_otg_adp_read_reg(core_if));
+	addr = &core_if->core_global_regs->hptxfsiz;
+	DWC_PRINTF("HPTXFSIZ	 @0x%08lX : 0x%08X\n", (unsigned long)addr,
+		   DWC_READ_REG32(addr));
+
+	if (core_if->en_multiple_tx_fifo == 0) {
+		ep_num = core_if->hwcfg4.b.num_dev_perio_in_ep;
+		txfsiz = "DPTXFSIZ";
+	} else {
+		ep_num = core_if->hwcfg4.b.num_in_eps;
+		txfsiz = "DIENPTXF";
+	}
+	for (i = 0; i < ep_num; i++) {
+		addr = &core_if->core_global_regs->dtxfsiz[i];
+		DWC_PRINTF("%s[%d] @0x%08lX : 0x%08X\n", txfsiz, i + 1,
+			   (unsigned long)addr, DWC_READ_REG32(addr));
+	}
+	addr = core_if->pcgcctl;
+	DWC_PRINTF("PCGCCTL	 @0x%08lX : 0x%08X\n", (unsigned long)addr,
+		   DWC_READ_REG32(addr));
+}
+
+/**
+ * Flush a Tx FIFO.
+ *
+ * @param core_if Programming view of DWC_otg controller.
+ * @param num Tx FIFO to flush.
+ */
+void dwc_otg_flush_tx_fifo(dwc_otg_core_if_t * core_if, const int num)
+{
+	dwc_otg_core_global_regs_t *global_regs = core_if->core_global_regs;
+	volatile grstctl_t greset = {.d32 = 0 };
+	int count = 0;
+
+	DWC_DEBUGPL((DBG_CIL | DBG_PCDV), "Flush Tx FIFO %d\n", num);
+
+	greset.b.txfflsh = 1;
+	greset.b.txfnum = num;
+	DWC_WRITE_REG32(&global_regs->grstctl, greset.d32);
+
+	do {
+		greset.d32 = DWC_READ_REG32(&global_regs->grstctl);
+		if (++count > 10000) {
+			DWC_WARN("%s() HANG! GRSTCTL=%0x GNPTXSTS=0x%08x\n",
+				 __func__, greset.d32,
+				 DWC_READ_REG32(&global_regs->gnptxsts));
+			break;
+		}
+		dwc_udelay(1);
+	} while (greset.b.txfflsh == 1);
+
+	/* Wait for 3 PHY Clocks */
+	dwc_udelay(1);
+}
+
+/**
+ * Flush Rx FIFO.
+ *
+ * @param core_if Programming view of DWC_otg controller.
+ */
+void dwc_otg_flush_rx_fifo(dwc_otg_core_if_t * core_if)
+{
+	dwc_otg_core_global_regs_t *global_regs = core_if->core_global_regs;
+	volatile grstctl_t greset = {.d32 = 0 };
+	int count = 0;
+
+	DWC_DEBUGPL((DBG_CIL | DBG_PCDV), "%s\n", __func__);
+	/*
+	 *
+	 */
+	greset.b.rxfflsh = 1;
+	DWC_WRITE_REG32(&global_regs->grstctl, greset.d32);
+
+	do {
+		greset.d32 = DWC_READ_REG32(&global_regs->grstctl);
+		if (++count > 10000) {
+			DWC_WARN("%s() HANG! GRSTCTL=%0x\n", __func__,
+				 greset.d32);
+			break;
+		}
+		dwc_udelay(1);
+	} while (greset.b.rxfflsh == 1);
+
+	/* Wait for 3 PHY Clocks */
+	dwc_udelay(1);
+}
+
+/**
+ * Do core a soft reset of the core.  Be careful with this because it
+ * resets all the internal state machines of the core.
+ */
+void dwc_otg_core_reset(dwc_otg_core_if_t * core_if)
+{
+	dwc_otg_core_global_regs_t *global_regs = core_if->core_global_regs;
+	volatile grstctl_t greset = {.d32 = 0 };
+	int count = 0;
+
+	DWC_DEBUGPL(DBG_CILV, "%s\n", __func__);
+	/* Wait for AHB master IDLE state. */
+	do {
+		dwc_udelay(10);
+		greset.d32 = DWC_READ_REG32(&global_regs->grstctl);
+		if (++count > 100000) {
+			DWC_WARN("%s() HANG! AHB Idle GRSTCTL=%0x\n", __func__,
+				 greset.d32);
+			return;
+		}
+	}
+	while (greset.b.ahbidle == 0);
+
+	/* Core Soft Reset */
+	count = 0;
+	greset.b.csftrst = 1;
+	DWC_WRITE_REG32(&global_regs->grstctl, greset.d32);
+	do {
+		greset.d32 = DWC_READ_REG32(&global_regs->grstctl);
+		if (++count > 10000) {
+			DWC_WARN("%s() HANG! Soft Reset GRSTCTL=%0x\n",
+				 __func__, greset.d32);
+			break;
+		}
+		dwc_udelay(1);
+	}
+	while (greset.b.csftrst == 1);
+
+	/* Wait for 3 PHY Clocks */
+	dwc_mdelay(100);
+}
+
+uint8_t dwc_otg_is_device_mode(dwc_otg_core_if_t * _core_if)
+{
+	return (dwc_otg_mode(_core_if) != DWC_HOST_MODE);
+}
+
+uint8_t dwc_otg_is_host_mode(dwc_otg_core_if_t * _core_if)
+{
+	return (dwc_otg_mode(_core_if) == DWC_HOST_MODE);
+}
+
+/**
+ * Register HCD callbacks. The callbacks are used to start and stop
+ * the HCD for interrupt processing.
+ *
+ * @param core_if Programming view of DWC_otg controller.
+ * @param cb the HCD callback structure.
+ * @param p pointer to be passed to callback function (usb_hcd*).
+ */
+void dwc_otg_cil_register_hcd_callbacks(dwc_otg_core_if_t * core_if,
+					dwc_otg_cil_callbacks_t * cb, void *p)
+{
+	core_if->hcd_cb = cb;
+	cb->p = p;
+}
+
+/**
+ * Register PCD callbacks. The callbacks are used to start and stop
+ * the PCD for interrupt processing.
+ *
+ * @param core_if Programming view of DWC_otg controller.
+ * @param cb the PCD callback structure.
+ * @param p pointer to be passed to callback function (pcd*).
+ */
+void dwc_otg_cil_register_pcd_callbacks(dwc_otg_core_if_t * core_if,
+					dwc_otg_cil_callbacks_t * cb, void *p)
+{
+	core_if->pcd_cb = cb;
+	cb->p = p;
+}
+
+#ifdef DWC_EN_ISOC
+
+/**
+ * This function writes isoc data per 1 (micro)frame into tx fifo
+ *
+ * @param core_if Programming view of DWC_otg controller.
+ * @param ep The EP to start the transfer on.
+ *
+ */
+void write_isoc_frame_data(dwc_otg_core_if_t * core_if, dwc_ep_t * ep)
+{
+	dwc_otg_dev_in_ep_regs_t *ep_regs;
+	dtxfsts_data_t txstatus = {.d32 = 0 };
+	uint32_t len = 0;
+	uint32_t dwords;
+
+	ep->xfer_len = ep->data_per_frame;
+	ep->xfer_count = 0;
+
+	ep_regs = core_if->dev_if->in_ep_regs[ep->num];
+
+	len = ep->xfer_len - ep->xfer_count;
+
+	if (len > ep->maxpacket) {
+		len = ep->maxpacket;
+	}
+
+	dwords = (len + 3) / 4;
+
+	/* While there is space in the queue and space in the FIFO and
+	 * More data to tranfer, Write packets to the Tx FIFO */
+	txstatus.d32 =
+	    DWC_READ_REG32(&core_if->dev_if->in_ep_regs[ep->num]->dtxfsts);
+	DWC_DEBUGPL(DBG_PCDV, "b4 dtxfsts[%d]=0x%08x\n", ep->num, txstatus.d32);
+
+	while (txstatus.b.txfspcavail > dwords &&
+	       ep->xfer_count < ep->xfer_len && ep->xfer_len != 0) {
+		/* Write the FIFO */
+		dwc_otg_ep_write_packet(core_if, ep, 0);
+
+		len = ep->xfer_len - ep->xfer_count;
+		if (len > ep->maxpacket) {
+			len = ep->maxpacket;
+		}
+
+		dwords = (len + 3) / 4;
+		txstatus.d32 =
+		    DWC_READ_REG32(&core_if->dev_if->in_ep_regs[ep->num]->
+				   dtxfsts);
+		DWC_DEBUGPL(DBG_PCDV, "dtxfsts[%d]=0x%08x\n", ep->num,
+			    txstatus.d32);
+	}
+}
+
+/**
+ * This function initializes a descriptor chain for Isochronous transfer
+ *
+ * @param core_if Programming view of DWC_otg controller.
+ * @param ep The EP to start the transfer on.
+ *
+ */
+void dwc_otg_iso_ep_start_frm_transfer(dwc_otg_core_if_t * core_if,
+				       dwc_ep_t * ep)
+{
+	deptsiz_data_t deptsiz = {.d32 = 0 };
+	depctl_data_t depctl = {.d32 = 0 };
+	dsts_data_t dsts = {.d32 = 0 };
+	volatile uint32_t *addr;
+
+	if (ep->is_in) {
+		addr = &core_if->dev_if->in_ep_regs[ep->num]->diepctl;
+	} else {
+		addr = &core_if->dev_if->out_ep_regs[ep->num]->doepctl;
+	}
+
+	ep->xfer_len = ep->data_per_frame;
+	ep->xfer_count = 0;
+	ep->xfer_buff = ep->cur_pkt_addr;
+	ep->dma_addr = ep->cur_pkt_dma_addr;
+
+	if (ep->is_in) {
+		/* Program the transfer size and packet count
+		 *      as follows: xfersize = N * maxpacket +
+		 *      short_packet pktcnt = N + (short_packet
+		 *      exist ? 1 : 0)
+		 */
+		deptsiz.b.xfersize = ep->xfer_len;
+		deptsiz.b.pktcnt =
+		    (ep->xfer_len - 1 + ep->maxpacket) / ep->maxpacket;
+		deptsiz.b.mc = deptsiz.b.pktcnt;
+		DWC_WRITE_REG32(&core_if->dev_if->in_ep_regs[ep->num]->dieptsiz,
+				deptsiz.d32);
+
+		/* Write the DMA register */
+		if (core_if->dma_enable) {
+			DWC_WRITE_REG32(&
+					(core_if->dev_if->in_ep_regs[ep->num]->
+					 diepdma), (uint32_t) ep->dma_addr);
+		}
+	} else {
+		deptsiz.b.pktcnt =
+		    (ep->xfer_len + (ep->maxpacket - 1)) / ep->maxpacket;
+		deptsiz.b.xfersize = deptsiz.b.pktcnt * ep->maxpacket;
+
+		DWC_WRITE_REG32(&core_if->dev_if->
+				out_ep_regs[ep->num]->doeptsiz, deptsiz.d32);
+
+		if (core_if->dma_enable) {
+			DWC_WRITE_REG32(&
+					(core_if->dev_if->
+					 out_ep_regs[ep->num]->doepdma),
+					(uint32_t) ep->dma_addr);
+		}
+	}
+
+	/** Enable endpoint, clear nak  */
+
+	depctl.d32 = 0;
+	if (ep->bInterval == 1) {
+		dsts.d32 =
+		    DWC_READ_REG32(&core_if->dev_if->dev_global_regs->dsts);
+		ep->next_frame = dsts.b.soffn + ep->bInterval;
+
+		if (ep->next_frame & 0x1) {
+			depctl.b.setd1pid = 1;
+		} else {
+			depctl.b.setd0pid = 1;
+		}
+	} else {
+		ep->next_frame += ep->bInterval;
+
+		if (ep->next_frame & 0x1) {
+			depctl.b.setd1pid = 1;
+		} else {
+			depctl.b.setd0pid = 1;
+		}
+	}
+	depctl.b.epena = 1;
+	depctl.b.cnak = 1;
+
+	DWC_MODIFY_REG32(addr, 0, depctl.d32);
+	depctl.d32 = DWC_READ_REG32(addr);
+
+	if (ep->is_in && core_if->dma_enable == 0) {
+		write_isoc_frame_data(core_if, ep);
+	}
+
+}
+#endif /* DWC_EN_ISOC */
+
+static void dwc_otg_set_uninitialized(int32_t * p, int size)
+{
+	int i;
+	for (i = 0; i < size; i++) {
+		p[i] = -1;
+	}
+}
+
+static int dwc_otg_param_initialized(int32_t val)
+{
+	return val != -1;
+}
+
+static int dwc_otg_setup_params(dwc_otg_core_if_t * core_if)
+{
+	int i;
+	core_if->core_params = DWC_ALLOC(sizeof(*core_if->core_params));
+	if (!core_if->core_params) {
+		return -DWC_E_NO_MEMORY;
+	}
+	dwc_otg_set_uninitialized((int32_t *) core_if->core_params,
+				  sizeof(*core_if->core_params) /
+				  sizeof(int32_t));
+	DWC_PRINTF("Setting default values for core params\n");
+	dwc_otg_set_param_otg_cap(core_if, dwc_param_otg_cap_default);
+	dwc_otg_set_param_dma_enable(core_if, dwc_param_dma_enable_default);
+	dwc_otg_set_param_dma_desc_enable(core_if,
+					  dwc_param_dma_desc_enable_default);
+	dwc_otg_set_param_opt(core_if, dwc_param_opt_default);
+	dwc_otg_set_param_dma_burst_size(core_if,
+					 dwc_param_dma_burst_size_default);
+	dwc_otg_set_param_host_support_fs_ls_low_power(core_if,
+						       dwc_param_host_support_fs_ls_low_power_default);
+	dwc_otg_set_param_enable_dynamic_fifo(core_if,
+					      dwc_param_enable_dynamic_fifo_default);
+	dwc_otg_set_param_data_fifo_size(core_if,
+					 dwc_param_data_fifo_size_default);
+	dwc_otg_set_param_dev_rx_fifo_size(core_if,
+					   dwc_param_dev_rx_fifo_size_default);
+	dwc_otg_set_param_dev_nperio_tx_fifo_size(core_if,
+						  dwc_param_dev_nperio_tx_fifo_size_default);
+	dwc_otg_set_param_host_rx_fifo_size(core_if,
+					    dwc_param_host_rx_fifo_size_default);
+	dwc_otg_set_param_host_nperio_tx_fifo_size(core_if,
+						   dwc_param_host_nperio_tx_fifo_size_default);
+	dwc_otg_set_param_host_perio_tx_fifo_size(core_if,
+						  dwc_param_host_perio_tx_fifo_size_default);
+	dwc_otg_set_param_max_transfer_size(core_if,
+					    dwc_param_max_transfer_size_default);
+	dwc_otg_set_param_max_packet_count(core_if,
+					   dwc_param_max_packet_count_default);
+	dwc_otg_set_param_host_channels(core_if,
+					dwc_param_host_channels_default);
+	dwc_otg_set_param_dev_endpoints(core_if,
+					dwc_param_dev_endpoints_default);
+	dwc_otg_set_param_phy_type(core_if, dwc_param_phy_type_default);
+	dwc_otg_set_param_speed(core_if, dwc_param_speed_default);
+	dwc_otg_set_param_host_ls_low_power_phy_clk(core_if,
+						    dwc_param_host_ls_low_power_phy_clk_default);
+	dwc_otg_set_param_phy_ulpi_ddr(core_if, dwc_param_phy_ulpi_ddr_default);
+	dwc_otg_set_param_phy_ulpi_ext_vbus(core_if,
+					    dwc_param_phy_ulpi_ext_vbus_default);
+	dwc_otg_set_param_phy_utmi_width(core_if,
+					 dwc_param_phy_utmi_width_default);
+	dwc_otg_set_param_ts_dline(core_if, dwc_param_ts_dline_default);
+	dwc_otg_set_param_i2c_enable(core_if, dwc_param_i2c_enable_default);
+	dwc_otg_set_param_ulpi_fs_ls(core_if, dwc_param_ulpi_fs_ls_default);
+	dwc_otg_set_param_en_multiple_tx_fifo(core_if,
+					      dwc_param_en_multiple_tx_fifo_default);
+	for (i = 0; i < 15; i++) {
+		dwc_otg_set_param_dev_perio_tx_fifo_size(core_if,
+							 dwc_param_dev_perio_tx_fifo_size_default,
+							 i);
+	}
+
+	for (i = 0; i < 15; i++) {
+		dwc_otg_set_param_dev_tx_fifo_size(core_if,
+						   dwc_param_dev_tx_fifo_size_default,
+						   i);
+	}
+	dwc_otg_set_param_thr_ctl(core_if, dwc_param_thr_ctl_default);
+	dwc_otg_set_param_mpi_enable(core_if, dwc_param_mpi_enable_default);
+	dwc_otg_set_param_pti_enable(core_if, dwc_param_pti_enable_default);
+	dwc_otg_set_param_lpm_enable(core_if, dwc_param_lpm_enable_default);
+	dwc_otg_set_param_ic_usb_cap(core_if, dwc_param_ic_usb_cap_default);
+	dwc_otg_set_param_tx_thr_length(core_if,
+					dwc_param_tx_thr_length_default);
+	dwc_otg_set_param_rx_thr_length(core_if,
+					dwc_param_rx_thr_length_default);
+	dwc_otg_set_param_ahb_thr_ratio(core_if,
+					dwc_param_ahb_thr_ratio_default);
+	dwc_otg_set_param_power_down(core_if, dwc_param_power_down_default);
+	dwc_otg_set_param_reload_ctl(core_if, dwc_param_reload_ctl_default);
+	dwc_otg_set_param_dev_out_nak(core_if, dwc_param_dev_out_nak_default);
+	dwc_otg_set_param_cont_on_bna(core_if, dwc_param_cont_on_bna_default);
+	dwc_otg_set_param_ahb_single(core_if, dwc_param_ahb_single_default);
+	dwc_otg_set_param_otg_ver(core_if, dwc_param_otg_ver_default);
+	dwc_otg_set_param_adp_enable(core_if, dwc_param_adp_enable_default);
+	DWC_PRINTF("Finished setting default values for core params\n");
+
+	return 0;
+}
+
+uint8_t dwc_otg_is_dma_enable(dwc_otg_core_if_t * core_if)
+{
+	return core_if->dma_enable;
+}
+
+/* Checks if the parameter is outside of its valid range of values */
+#define DWC_OTG_PARAM_TEST(_param_, _low_, _high_) \
+		(((_param_) < (_low_)) || \
+		((_param_) > (_high_)))
+
+/* Parameter access functions */
+int dwc_otg_set_param_otg_cap(dwc_otg_core_if_t * core_if, int32_t val)
+{
+	int valid;
+	int retval = 0;
+	if (DWC_OTG_PARAM_TEST(val, 0, 2)) {
+		DWC_WARN("Wrong value for otg_cap parameter\n");
+		DWC_WARN("otg_cap parameter must be 0,1 or 2\n");
+		retval = -DWC_E_INVALID;
+		goto out;
+	}
+
+	valid = 1;
+	switch (val) {
+	case DWC_OTG_CAP_PARAM_HNP_SRP_CAPABLE:
+		if (core_if->hwcfg2.b.op_mode !=
+		    DWC_HWCFG2_OP_MODE_HNP_SRP_CAPABLE_OTG)
+			valid = 0;
+		break;
+	case DWC_OTG_CAP_PARAM_SRP_ONLY_CAPABLE:
+		if ((core_if->hwcfg2.b.op_mode !=
+		     DWC_HWCFG2_OP_MODE_HNP_SRP_CAPABLE_OTG)
+		    && (core_if->hwcfg2.b.op_mode !=
+			DWC_HWCFG2_OP_MODE_SRP_ONLY_CAPABLE_OTG)
+		    && (core_if->hwcfg2.b.op_mode !=
+			DWC_HWCFG2_OP_MODE_SRP_CAPABLE_DEVICE)
+		    && (core_if->hwcfg2.b.op_mode !=
+			DWC_HWCFG2_OP_MODE_SRP_CAPABLE_HOST)) {
+			valid = 0;
+		}
+		break;
+	case DWC_OTG_CAP_PARAM_NO_HNP_SRP_CAPABLE:
+		/* always valid */
+		break;
+	}
+	if (!valid) {
+		if (dwc_otg_param_initialized(core_if->core_params->otg_cap)) {
+			DWC_ERROR
+			    ("%d invalid for otg_cap paremter. Check HW configuration.\n",
+			     val);
+		}
+		val =
+		    (((core_if->hwcfg2.b.op_mode ==
+		       DWC_HWCFG2_OP_MODE_HNP_SRP_CAPABLE_OTG)
+		      || (core_if->hwcfg2.b.op_mode ==
+			  DWC_HWCFG2_OP_MODE_SRP_ONLY_CAPABLE_OTG)
+		      || (core_if->hwcfg2.b.op_mode ==
+			  DWC_HWCFG2_OP_MODE_SRP_CAPABLE_DEVICE)
+		      || (core_if->hwcfg2.b.op_mode ==
+			  DWC_HWCFG2_OP_MODE_SRP_CAPABLE_HOST)) ?
+		     DWC_OTG_CAP_PARAM_SRP_ONLY_CAPABLE :
+		     DWC_OTG_CAP_PARAM_NO_HNP_SRP_CAPABLE);
+		retval = -DWC_E_INVALID;
+	}
+
+	core_if->core_params->otg_cap = val;
+out:
+	return retval;
+}
+
+int32_t dwc_otg_get_param_otg_cap(dwc_otg_core_if_t * core_if)
+{
+	return core_if->core_params->otg_cap;
+}
+
+int dwc_otg_set_param_opt(dwc_otg_core_if_t * core_if, int32_t val)
+{
+	if (DWC_OTG_PARAM_TEST(val, 0, 1)) {
+		DWC_WARN("Wrong value for opt parameter\n");
+		return -DWC_E_INVALID;
+	}
+	core_if->core_params->opt = val;
+	return 0;
+}
+
+int32_t dwc_otg_get_param_opt(dwc_otg_core_if_t * core_if)
+{
+	return core_if->core_params->opt;
+}
+
+int dwc_otg_set_param_dma_enable(dwc_otg_core_if_t * core_if, int32_t val)
+{
+	int retval = 0;
+	if (DWC_OTG_PARAM_TEST(val, 0, 1)) {
+		DWC_WARN("Wrong value for dma enable\n");
+		return -DWC_E_INVALID;
+	}
+
+	if ((val == 1) && (core_if->hwcfg2.b.architecture == 0)) {
+		if (dwc_otg_param_initialized(core_if->core_params->dma_enable)) {
+			DWC_ERROR
+			    ("%d invalid for dma_enable paremter. Check HW configuration.\n",
+			     val);
+		}
+		val = 0;
+		retval = -DWC_E_INVALID;
+	}
+
+	core_if->core_params->dma_enable = val;
+	if (val == 0) {
+		dwc_otg_set_param_dma_desc_enable(core_if, 0);
+	}
+	return retval;
+}
+
+int32_t dwc_otg_get_param_dma_enable(dwc_otg_core_if_t * core_if)
+{
+	return core_if->core_params->dma_enable;
+}
+
+int dwc_otg_set_param_dma_desc_enable(dwc_otg_core_if_t * core_if, int32_t val)
+{
+	int retval = 0;
+	if (DWC_OTG_PARAM_TEST(val, 0, 1)) {
+		DWC_WARN("Wrong value for dma_enable\n");
+		DWC_WARN("dma_desc_enable must be 0 or 1\n");
+		return -DWC_E_INVALID;
+	}
+
+	if ((val == 1)
+	    && ((dwc_otg_get_param_dma_enable(core_if) == 0)
+		|| (core_if->hwcfg4.b.desc_dma == 0))) {
+		if (dwc_otg_param_initialized
+		    (core_if->core_params->dma_desc_enable)) {
+			DWC_ERROR
+			    ("%d invalid for dma_desc_enable paremter. Check HW configuration.\n",
+			     val);
+		}
+		val = 0;
+		retval = -DWC_E_INVALID;
+	}
+	core_if->core_params->dma_desc_enable = val;
+	return retval;
+}
+
+int32_t dwc_otg_get_param_dma_desc_enable(dwc_otg_core_if_t * core_if)
+{
+	return core_if->core_params->dma_desc_enable;
+}
+
+int dwc_otg_set_param_host_support_fs_ls_low_power(dwc_otg_core_if_t * core_if,
+						   int32_t val)
+{
+	if (DWC_OTG_PARAM_TEST(val, 0, 1)) {
+		DWC_WARN("Wrong value for host_support_fs_low_power\n");
+		DWC_WARN("host_support_fs_low_power must be 0 or 1\n");
+		return -DWC_E_INVALID;
+	}
+	core_if->core_params->host_support_fs_ls_low_power = val;
+	return 0;
+}
+
+int32_t dwc_otg_get_param_host_support_fs_ls_low_power(dwc_otg_core_if_t *
+						       core_if)
+{
+	return core_if->core_params->host_support_fs_ls_low_power;
+}
+
+int dwc_otg_set_param_enable_dynamic_fifo(dwc_otg_core_if_t * core_if,
+					  int32_t val)
+{
+	int retval = 0;
+	if (DWC_OTG_PARAM_TEST(val, 0, 1)) {
+		DWC_WARN("Wrong value for enable_dynamic_fifo\n");
+		DWC_WARN("enable_dynamic_fifo must be 0 or 1\n");
+		return -DWC_E_INVALID;
+	}
+
+	if ((val == 1) && (core_if->hwcfg2.b.dynamic_fifo == 0)) {
+		if (dwc_otg_param_initialized
+		    (core_if->core_params->enable_dynamic_fifo)) {
+			DWC_ERROR
+			    ("%d invalid for enable_dynamic_fifo paremter. Check HW configuration.\n",
+			     val);
+		}
+		val = 0;
+		retval = -DWC_E_INVALID;
+	}
+	core_if->core_params->enable_dynamic_fifo = val;
+	return retval;
+}
+
+int32_t dwc_otg_get_param_enable_dynamic_fifo(dwc_otg_core_if_t * core_if)
+{
+	return core_if->core_params->enable_dynamic_fifo;
+}
+
+int dwc_otg_set_param_data_fifo_size(dwc_otg_core_if_t * core_if, int32_t val)
+{
+	int retval = 0;
+	if (DWC_OTG_PARAM_TEST(val, 32, 32768)) {
+		DWC_WARN("Wrong value for data_fifo_size\n");
+		DWC_WARN("data_fifo_size must be 32-32768\n");
+		return -DWC_E_INVALID;
+	}
+
+	if (val > core_if->hwcfg3.b.dfifo_depth) {
+		if (dwc_otg_param_initialized
+		    (core_if->core_params->data_fifo_size)) {
+			DWC_ERROR
+			    ("%d invalid for data_fifo_size parameter. Check HW configuration.\n",
+			     val);
+		}
+		val = core_if->hwcfg3.b.dfifo_depth;
+		retval = -DWC_E_INVALID;
+	}
+
+	core_if->core_params->data_fifo_size = val;
+	return retval;
+}
+
+int32_t dwc_otg_get_param_data_fifo_size(dwc_otg_core_if_t * core_if)
+{
+	return core_if->core_params->data_fifo_size;
+}
+
+int dwc_otg_set_param_dev_rx_fifo_size(dwc_otg_core_if_t * core_if, int32_t val)
+{
+	int retval = 0;
+	if (DWC_OTG_PARAM_TEST(val, 16, 32768)) {
+		DWC_WARN("Wrong value for dev_rx_fifo_size\n");
+		DWC_WARN("dev_rx_fifo_size must be 16-32768\n");
+		return -DWC_E_INVALID;
+	}
+
+	if (val > DWC_READ_REG32(&core_if->core_global_regs->grxfsiz)) {
+		if (dwc_otg_param_initialized(core_if->core_params->dev_rx_fifo_size)) {
+		DWC_WARN("%d invalid for dev_rx_fifo_size parameter\n", val);
+		}
+		val = DWC_READ_REG32(&core_if->core_global_regs->grxfsiz);
+		retval = -DWC_E_INVALID;
+	}
+
+	core_if->core_params->dev_rx_fifo_size = val;
+	return retval;
+}
+
+int32_t dwc_otg_get_param_dev_rx_fifo_size(dwc_otg_core_if_t * core_if)
+{
+	return core_if->core_params->dev_rx_fifo_size;
+}
+
+int dwc_otg_set_param_dev_nperio_tx_fifo_size(dwc_otg_core_if_t * core_if,
+					      int32_t val)
+{
+	int retval = 0;
+
+	if (DWC_OTG_PARAM_TEST(val, 16, 32768)) {
+		DWC_WARN("Wrong value for dev_nperio_tx_fifo\n");
+		DWC_WARN("dev_nperio_tx_fifo must be 16-32768\n");
+		return -DWC_E_INVALID;
+	}
+
+	if (val > (DWC_READ_REG32(&core_if->core_global_regs->gnptxfsiz) >> 16)) {
+		if (dwc_otg_param_initialized
+		    (core_if->core_params->dev_nperio_tx_fifo_size)) {
+			DWC_ERROR
+			    ("%d invalid for dev_nperio_tx_fifo_size. Check HW configuration.\n",
+			     val);
+		}
+		val =
+		    (DWC_READ_REG32(&core_if->core_global_regs->gnptxfsiz) >>
+		     16);
+		retval = -DWC_E_INVALID;
+	}
+
+	core_if->core_params->dev_nperio_tx_fifo_size = val;
+	return retval;
+}
+
+int32_t dwc_otg_get_param_dev_nperio_tx_fifo_size(dwc_otg_core_if_t * core_if)
+{
+	return core_if->core_params->dev_nperio_tx_fifo_size;
+}
+
+int dwc_otg_set_param_host_rx_fifo_size(dwc_otg_core_if_t * core_if,
+					int32_t val)
+{
+	int retval = 0;
+
+	if (DWC_OTG_PARAM_TEST(val, 16, 32768)) {
+		DWC_WARN("Wrong value for host_rx_fifo_size\n");
+		DWC_WARN("host_rx_fifo_size must be 16-32768\n");
+		return -DWC_E_INVALID;
+	}
+
+	if (val > DWC_READ_REG32(&core_if->core_global_regs->grxfsiz)) {
+		if (dwc_otg_param_initialized
+		    (core_if->core_params->host_rx_fifo_size)) {
+			DWC_ERROR
+			    ("%d invalid for host_rx_fifo_size. Check HW configuration.\n",
+			     val);
+		}
+		val = DWC_READ_REG32(&core_if->core_global_regs->grxfsiz);
+		retval = -DWC_E_INVALID;
+	}
+
+	core_if->core_params->host_rx_fifo_size = val;
+	return retval;
+
+}
+
+int32_t dwc_otg_get_param_host_rx_fifo_size(dwc_otg_core_if_t * core_if)
+{
+	return core_if->core_params->host_rx_fifo_size;
+}
+
+int dwc_otg_set_param_host_nperio_tx_fifo_size(dwc_otg_core_if_t * core_if,
+					       int32_t val)
+{
+	int retval = 0;
+
+	if (DWC_OTG_PARAM_TEST(val, 16, 32768)) {
+		DWC_WARN("Wrong value for host_nperio_tx_fifo_size\n");
+		DWC_WARN("host_nperio_tx_fifo_size must be 16-32768\n");
+		return -DWC_E_INVALID;
+	}
+
+	if (val > (DWC_READ_REG32(&core_if->core_global_regs->gnptxfsiz) >> 16)) {
+		if (dwc_otg_param_initialized
+		    (core_if->core_params->host_nperio_tx_fifo_size)) {
+			DWC_ERROR
+			    ("%d invalid for host_nperio_tx_fifo_size. Check HW configuration.\n",
+			     val);
+		}
+		val =
+		    (DWC_READ_REG32(&core_if->core_global_regs->gnptxfsiz) >>
+		     16);
+		retval = -DWC_E_INVALID;
+	}
+
+	core_if->core_params->host_nperio_tx_fifo_size = val;
+	return retval;
+}
+
+int32_t dwc_otg_get_param_host_nperio_tx_fifo_size(dwc_otg_core_if_t * core_if)
+{
+	return core_if->core_params->host_nperio_tx_fifo_size;
+}
+
+int dwc_otg_set_param_host_perio_tx_fifo_size(dwc_otg_core_if_t * core_if,
+					      int32_t val)
+{
+	int retval = 0;
+	if (DWC_OTG_PARAM_TEST(val, 16, 32768)) {
+		DWC_WARN("Wrong value for host_perio_tx_fifo_size\n");
+		DWC_WARN("host_perio_tx_fifo_size must be 16-32768\n");
+		return -DWC_E_INVALID;
+	}
+
+	if (val > ((core_if->hptxfsiz.d32) >> 16)) {
+		if (dwc_otg_param_initialized
+		    (core_if->core_params->host_perio_tx_fifo_size)) {
+			DWC_ERROR
+			    ("%d invalid for host_perio_tx_fifo_size. Check HW configuration.\n",
+			     val);
+		}
+		val = (core_if->hptxfsiz.d32) >> 16;
+		retval = -DWC_E_INVALID;
+	}
+
+	core_if->core_params->host_perio_tx_fifo_size = val;
+	return retval;
+}
+
+int32_t dwc_otg_get_param_host_perio_tx_fifo_size(dwc_otg_core_if_t * core_if)
+{
+	return core_if->core_params->host_perio_tx_fifo_size;
+}
+
+int dwc_otg_set_param_max_transfer_size(dwc_otg_core_if_t * core_if,
+					int32_t val)
+{
+	int retval = 0;
+
+	if (DWC_OTG_PARAM_TEST(val, 2047, 524288)) {
+		DWC_WARN("Wrong value for max_transfer_size\n");
+		DWC_WARN("max_transfer_size must be 2047-524288\n");
+		return -DWC_E_INVALID;
+	}
+
+	if (val >= (1 << (core_if->hwcfg3.b.xfer_size_cntr_width + 11))) {
+		if (dwc_otg_param_initialized
+		    (core_if->core_params->max_transfer_size)) {
+			DWC_ERROR
+			    ("%d invalid for max_transfer_size. Check HW configuration.\n",
+			     val);
+		}
+		val =
+		    ((1 << (core_if->hwcfg3.b.packet_size_cntr_width + 11)) -
+		     1);
+		retval = -DWC_E_INVALID;
+	}
+
+	core_if->core_params->max_transfer_size = val;
+	return retval;
+}
+
+int32_t dwc_otg_get_param_max_transfer_size(dwc_otg_core_if_t * core_if)
+{
+	return core_if->core_params->max_transfer_size;
+}
+
+int dwc_otg_set_param_max_packet_count(dwc_otg_core_if_t * core_if, int32_t val)
+{
+	int retval = 0;
+
+	if (DWC_OTG_PARAM_TEST(val, 15, 511)) {
+		DWC_WARN("Wrong value for max_packet_count\n");
+		DWC_WARN("max_packet_count must be 15-511\n");
+		return -DWC_E_INVALID;
+	}
+
+	if (val > (1 << (core_if->hwcfg3.b.packet_size_cntr_width + 4))) {
+		if (dwc_otg_param_initialized
+		    (core_if->core_params->max_packet_count)) {
+			DWC_ERROR
+			    ("%d invalid for max_packet_count. Check HW configuration.\n",
+			     val);
+		}
+		val =
+		    ((1 << (core_if->hwcfg3.b.packet_size_cntr_width + 4)) - 1);
+		retval = -DWC_E_INVALID;
+	}
+
+	core_if->core_params->max_packet_count = val;
+	return retval;
+}
+
+int32_t dwc_otg_get_param_max_packet_count(dwc_otg_core_if_t * core_if)
+{
+	return core_if->core_params->max_packet_count;
+}
+
+int dwc_otg_set_param_host_channels(dwc_otg_core_if_t * core_if, int32_t val)
+{
+	int retval = 0;
+
+	if (DWC_OTG_PARAM_TEST(val, 1, 16)) {
+		DWC_WARN("Wrong value for host_channels\n");
+		DWC_WARN("host_channels must be 1-16\n");
+		return -DWC_E_INVALID;
+	}
+
+	if (val > (core_if->hwcfg2.b.num_host_chan + 1)) {
+		if (dwc_otg_param_initialized
+		    (core_if->core_params->host_channels)) {
+			DWC_ERROR
+			    ("%d invalid for host_channels. Check HW configurations.\n",
+			     val);
+		}
+		val = (core_if->hwcfg2.b.num_host_chan + 1);
+		retval = -DWC_E_INVALID;
+	}
+
+	core_if->core_params->host_channels = val;
+	return retval;
+}
+
+int32_t dwc_otg_get_param_host_channels(dwc_otg_core_if_t * core_if)
+{
+	return core_if->core_params->host_channels;
+}
+
+int dwc_otg_set_param_dev_endpoints(dwc_otg_core_if_t * core_if, int32_t val)
+{
+	int retval = 0;
+
+	if (DWC_OTG_PARAM_TEST(val, 1, 15)) {
+		DWC_WARN("Wrong value for dev_endpoints\n");
+		DWC_WARN("dev_endpoints must be 1-15\n");
+		return -DWC_E_INVALID;
+	}
+
+	if (val > (core_if->hwcfg2.b.num_dev_ep)) {
+		if (dwc_otg_param_initialized
+		    (core_if->core_params->dev_endpoints)) {
+			DWC_ERROR
+			    ("%d invalid for dev_endpoints. Check HW configurations.\n",
+			     val);
+		}
+		val = core_if->hwcfg2.b.num_dev_ep;
+		retval = -DWC_E_INVALID;
+	}
+
+	core_if->core_params->dev_endpoints = val;
+	return retval;
+}
+
+int32_t dwc_otg_get_param_dev_endpoints(dwc_otg_core_if_t * core_if)
+{
+	return core_if->core_params->dev_endpoints;
+}
+
+int dwc_otg_set_param_phy_type(dwc_otg_core_if_t * core_if, int32_t val)
+{
+	int retval = 0;
+	int valid = 0;
+
+	if (DWC_OTG_PARAM_TEST(val, 0, 2)) {
+		DWC_WARN("Wrong value for phy_type\n");
+		DWC_WARN("phy_type must be 0,1 or 2\n");
+		return -DWC_E_INVALID;
+	}
+#ifndef NO_FS_PHY_HW_CHECKS
+	if ((val == DWC_PHY_TYPE_PARAM_UTMI) &&
+	    ((core_if->hwcfg2.b.hs_phy_type == 1) ||
+	     (core_if->hwcfg2.b.hs_phy_type == 3))) {
+		valid = 1;
+	} else if ((val == DWC_PHY_TYPE_PARAM_ULPI) &&
+		   ((core_if->hwcfg2.b.hs_phy_type == 2) ||
+		    (core_if->hwcfg2.b.hs_phy_type == 3))) {
+		valid = 1;
+	} else if ((val == DWC_PHY_TYPE_PARAM_FS) &&
+		   (core_if->hwcfg2.b.fs_phy_type == 1)) {
+		valid = 1;
+	}
+	if (!valid) {
+		if (dwc_otg_param_initialized(core_if->core_params->phy_type)) {
+			DWC_ERROR
+			    ("%d invalid for phy_type. Check HW configurations.\n",
+			     val);
+		}
+		if (core_if->hwcfg2.b.hs_phy_type) {
+			if ((core_if->hwcfg2.b.hs_phy_type == 3) ||
+			    (core_if->hwcfg2.b.hs_phy_type == 1)) {
+				val = DWC_PHY_TYPE_PARAM_UTMI;
+			} else {
+				val = DWC_PHY_TYPE_PARAM_ULPI;
+			}
+		}
+		retval = -DWC_E_INVALID;
+	}
+#endif
+	core_if->core_params->phy_type = val;
+	return retval;
+}
+
+int32_t dwc_otg_get_param_phy_type(dwc_otg_core_if_t * core_if)
+{
+	return core_if->core_params->phy_type;
+}
+
+int dwc_otg_set_param_speed(dwc_otg_core_if_t * core_if, int32_t val)
+{
+	int retval = 0;
+	if (DWC_OTG_PARAM_TEST(val, 0, 1)) {
+		DWC_WARN("Wrong value for speed parameter\n");
+		DWC_WARN("max_speed parameter must be 0 or 1\n");
+		return -DWC_E_INVALID;
+	}
+	if ((val == 0)
+	    && dwc_otg_get_param_phy_type(core_if) == DWC_PHY_TYPE_PARAM_FS) {
+		if (dwc_otg_param_initialized(core_if->core_params->speed)) {
+			DWC_ERROR
+			    ("%d invalid for speed paremter. Check HW configuration.\n",
+			     val);
+		}
+		val =
+		    (dwc_otg_get_param_phy_type(core_if) ==
+		     DWC_PHY_TYPE_PARAM_FS ? 1 : 0);
+		retval = -DWC_E_INVALID;
+	}
+	core_if->core_params->speed = val;
+	return retval;
+}
+
+int32_t dwc_otg_get_param_speed(dwc_otg_core_if_t * core_if)
+{
+	return core_if->core_params->speed;
+}
+
+int dwc_otg_set_param_host_ls_low_power_phy_clk(dwc_otg_core_if_t * core_if,
+						int32_t val)
+{
+	int retval = 0;
+
+	if (DWC_OTG_PARAM_TEST(val, 0, 1)) {
+		DWC_WARN
+		    ("Wrong value for host_ls_low_power_phy_clk parameter\n");
+		DWC_WARN("host_ls_low_power_phy_clk must be 0 or 1\n");
+		return -DWC_E_INVALID;
+	}
+
+	if ((val == DWC_HOST_LS_LOW_POWER_PHY_CLK_PARAM_48MHZ)
+	    && (dwc_otg_get_param_phy_type(core_if) == DWC_PHY_TYPE_PARAM_FS)) {
+		if (dwc_otg_param_initialized
+		    (core_if->core_params->host_ls_low_power_phy_clk)) {
+			DWC_ERROR
+			    ("%d invalid for host_ls_low_power_phy_clk. Check HW configuration.\n",
+			     val);
+		}
+		val =
+		    (dwc_otg_get_param_phy_type(core_if) ==
+		     DWC_PHY_TYPE_PARAM_FS) ?
+		    DWC_HOST_LS_LOW_POWER_PHY_CLK_PARAM_6MHZ :
+		    DWC_HOST_LS_LOW_POWER_PHY_CLK_PARAM_48MHZ;
+		retval = -DWC_E_INVALID;
+	}
+
+	core_if->core_params->host_ls_low_power_phy_clk = val;
+	return retval;
+}
+
+int32_t dwc_otg_get_param_host_ls_low_power_phy_clk(dwc_otg_core_if_t * core_if)
+{
+	return core_if->core_params->host_ls_low_power_phy_clk;
+}
+
+int dwc_otg_set_param_phy_ulpi_ddr(dwc_otg_core_if_t * core_if, int32_t val)
+{
+	if (DWC_OTG_PARAM_TEST(val, 0, 1)) {
+		DWC_WARN("Wrong value for phy_ulpi_ddr\n");
+		DWC_WARN("phy_upli_ddr must be 0 or 1\n");
+		return -DWC_E_INVALID;
+	}
+
+	core_if->core_params->phy_ulpi_ddr = val;
+	return 0;
+}
+
+int32_t dwc_otg_get_param_phy_ulpi_ddr(dwc_otg_core_if_t * core_if)
+{
+	return core_if->core_params->phy_ulpi_ddr;
+}
+
+int dwc_otg_set_param_phy_ulpi_ext_vbus(dwc_otg_core_if_t * core_if,
+					int32_t val)
+{
+	if (DWC_OTG_PARAM_TEST(val, 0, 1)) {
+		DWC_WARN("Wrong valaue for phy_ulpi_ext_vbus\n");
+		DWC_WARN("phy_ulpi_ext_vbus must be 0 or 1\n");
+		return -DWC_E_INVALID;
+	}
+
+	core_if->core_params->phy_ulpi_ext_vbus = val;
+	return 0;
+}
+
+int32_t dwc_otg_get_param_phy_ulpi_ext_vbus(dwc_otg_core_if_t * core_if)
+{
+	return core_if->core_params->phy_ulpi_ext_vbus;
+}
+
+int dwc_otg_set_param_phy_utmi_width(dwc_otg_core_if_t * core_if, int32_t val)
+{
+	if (DWC_OTG_PARAM_TEST(val, 8, 8) && DWC_OTG_PARAM_TEST(val, 16, 16)) {
+		DWC_WARN("Wrong valaue for phy_utmi_width\n");
+		DWC_WARN("phy_utmi_width must be 8 or 16\n");
+		return -DWC_E_INVALID;
+	}
+
+	core_if->core_params->phy_utmi_width = val;
+	return 0;
+}
+
+int32_t dwc_otg_get_param_phy_utmi_width(dwc_otg_core_if_t * core_if)
+{
+	return core_if->core_params->phy_utmi_width;
+}
+
+int dwc_otg_set_param_ulpi_fs_ls(dwc_otg_core_if_t * core_if, int32_t val)
+{
+	if (DWC_OTG_PARAM_TEST(val, 0, 1)) {
+		DWC_WARN("Wrong valaue for ulpi_fs_ls\n");
+		DWC_WARN("ulpi_fs_ls must be 0 or 1\n");
+		return -DWC_E_INVALID;
+	}
+
+	core_if->core_params->ulpi_fs_ls = val;
+	return 0;
+}
+
+int32_t dwc_otg_get_param_ulpi_fs_ls(dwc_otg_core_if_t * core_if)
+{
+	return core_if->core_params->ulpi_fs_ls;
+}
+
+int dwc_otg_set_param_ts_dline(dwc_otg_core_if_t * core_if, int32_t val)
+{
+	if (DWC_OTG_PARAM_TEST(val, 0, 1)) {
+		DWC_WARN("Wrong valaue for ts_dline\n");
+		DWC_WARN("ts_dline must be 0 or 1\n");
+		return -DWC_E_INVALID;
+	}
+
+	core_if->core_params->ts_dline = val;
+	return 0;
+}
+
+int32_t dwc_otg_get_param_ts_dline(dwc_otg_core_if_t * core_if)
+{
+	return core_if->core_params->ts_dline;
+}
+
+int dwc_otg_set_param_i2c_enable(dwc_otg_core_if_t * core_if, int32_t val)
+{
+	int retval = 0;
+	if (DWC_OTG_PARAM_TEST(val, 0, 1)) {
+		DWC_WARN("Wrong valaue for i2c_enable\n");
+		DWC_WARN("i2c_enable must be 0 or 1\n");
+		return -DWC_E_INVALID;
+	}
+#ifndef NO_FS_PHY_HW_CHECK
+	if (val == 1 && core_if->hwcfg3.b.i2c == 0) {
+		if (dwc_otg_param_initialized(core_if->core_params->i2c_enable)) {
+			DWC_ERROR
+			    ("%d invalid for i2c_enable. Check HW configuration.\n",
+			     val);
+		}
+		val = 0;
+		retval = -DWC_E_INVALID;
+	}
+#endif
+
+	core_if->core_params->i2c_enable = val;
+	return retval;
+}
+
+int32_t dwc_otg_get_param_i2c_enable(dwc_otg_core_if_t * core_if)
+{
+	return core_if->core_params->i2c_enable;
+}
+
+int dwc_otg_set_param_dev_perio_tx_fifo_size(dwc_otg_core_if_t * core_if,
+					     int32_t val, int fifo_num)
+{
+	int retval = 0;
+
+	if (DWC_OTG_PARAM_TEST(val, 4, 768)) {
+		DWC_WARN("Wrong value for dev_perio_tx_fifo_size\n");
+		DWC_WARN("dev_perio_tx_fifo_size must be 4-768\n");
+		return -DWC_E_INVALID;
+	}
+
+	if (val >
+	    (DWC_READ_REG32(&core_if->core_global_regs->dtxfsiz[fifo_num]))) {
+		if (dwc_otg_param_initialized
+		    (core_if->core_params->dev_perio_tx_fifo_size[fifo_num])) {
+			DWC_ERROR
+			    ("`%d' invalid for parameter `dev_perio_fifo_size_%d'. Check HW configuration.\n",
+			     val, fifo_num);
+		}
+		val = (DWC_READ_REG32(&core_if->core_global_regs->dtxfsiz[fifo_num]));
+		retval = -DWC_E_INVALID;
+	}
+
+	core_if->core_params->dev_perio_tx_fifo_size[fifo_num] = val;
+	return retval;
+}
+
+int32_t dwc_otg_get_param_dev_perio_tx_fifo_size(dwc_otg_core_if_t * core_if,
+						 int fifo_num)
+{
+	return core_if->core_params->dev_perio_tx_fifo_size[fifo_num];
+}
+
+int dwc_otg_set_param_en_multiple_tx_fifo(dwc_otg_core_if_t * core_if,
+					  int32_t val)
+{
+	int retval = 0;
+	if (DWC_OTG_PARAM_TEST(val, 0, 1)) {
+		DWC_WARN("Wrong valaue for en_multiple_tx_fifo,\n");
+		DWC_WARN("en_multiple_tx_fifo must be 0 or 1\n");
+		return -DWC_E_INVALID;
+	}
+
+	if (val == 1 && core_if->hwcfg4.b.ded_fifo_en == 0) {
+		if (dwc_otg_param_initialized
+		    (core_if->core_params->en_multiple_tx_fifo)) {
+			DWC_ERROR
+			    ("%d invalid for parameter en_multiple_tx_fifo. Check HW configuration.\n",
+			     val);
+		}
+		val = 0;
+		retval = -DWC_E_INVALID;
+	}
+
+	core_if->core_params->en_multiple_tx_fifo = val;
+	return retval;
+}
+
+int32_t dwc_otg_get_param_en_multiple_tx_fifo(dwc_otg_core_if_t * core_if)
+{
+	return core_if->core_params->en_multiple_tx_fifo;
+}
+
+int dwc_otg_set_param_dev_tx_fifo_size(dwc_otg_core_if_t * core_if, int32_t val,
+				       int fifo_num)
+{
+	int retval = 0;
+
+	if (DWC_OTG_PARAM_TEST(val, 4, 768)) {
+		DWC_WARN("Wrong value for dev_tx_fifo_size\n");
+		DWC_WARN("dev_tx_fifo_size must be 4-768\n");
+		return -DWC_E_INVALID;
+	}
+
+	if (val >
+	    (DWC_READ_REG32(&core_if->core_global_regs->dtxfsiz[fifo_num]))) {
+		if (dwc_otg_param_initialized
+		    (core_if->core_params->dev_tx_fifo_size[fifo_num])) {
+			DWC_ERROR
+			    ("`%d' invalid for parameter `dev_tx_fifo_size_%d'. Check HW configuration.\n",
+			     val, fifo_num);
+		}
+		val = (DWC_READ_REG32(&core_if->core_global_regs->dtxfsiz[fifo_num]));
+		retval = -DWC_E_INVALID;
+	}
+
+	core_if->core_params->dev_tx_fifo_size[fifo_num] = val;
+	return retval;
+}
+
+int32_t dwc_otg_get_param_dev_tx_fifo_size(dwc_otg_core_if_t * core_if,
+					   int fifo_num)
+{
+	return core_if->core_params->dev_tx_fifo_size[fifo_num];
+}
+
+int dwc_otg_set_param_thr_ctl(dwc_otg_core_if_t * core_if, int32_t val)
+{
+	int retval = 0;
+
+	if (DWC_OTG_PARAM_TEST(val, 0, 7)) {
+		DWC_WARN("Wrong value for thr_ctl\n");
+		DWC_WARN("thr_ctl must be 0-7\n");
+		return -DWC_E_INVALID;
+	}
+
+	if ((val != 0) &&
+	    (!dwc_otg_get_param_dma_enable(core_if) ||
+	     !core_if->hwcfg4.b.ded_fifo_en)) {
+		if (dwc_otg_param_initialized(core_if->core_params->thr_ctl)) {
+			DWC_ERROR
+			    ("%d invalid for parameter thr_ctl. Check HW configuration.\n",
+			     val);
+		}
+		val = 0;
+		retval = -DWC_E_INVALID;
+	}
+
+	core_if->core_params->thr_ctl = val;
+	return retval;
+}
+
+int32_t dwc_otg_get_param_thr_ctl(dwc_otg_core_if_t * core_if)
+{
+	return core_if->core_params->thr_ctl;
+}
+
+int dwc_otg_set_param_lpm_enable(dwc_otg_core_if_t * core_if, int32_t val)
+{
+	int retval = 0;
+
+	if (DWC_OTG_PARAM_TEST(val, 0, 1)) {
+		DWC_WARN("Wrong value for lpm_enable\n");
+		DWC_WARN("lpm_enable must be 0 or 1\n");
+		return -DWC_E_INVALID;
+	}
+
+	if (val && !core_if->hwcfg3.b.otg_lpm_en) {
+		if (dwc_otg_param_initialized(core_if->core_params->lpm_enable)) {
+			DWC_ERROR
+			    ("%d invalid for parameter lpm_enable. Check HW configuration.\n",
+			     val);
+		}
+		val = 0;
+		retval = -DWC_E_INVALID;
+	}
+
+	core_if->core_params->lpm_enable = val;
+	return retval;
+}
+
+int32_t dwc_otg_get_param_lpm_enable(dwc_otg_core_if_t * core_if)
+{
+	return core_if->core_params->lpm_enable;
+}
+
+int dwc_otg_set_param_tx_thr_length(dwc_otg_core_if_t * core_if, int32_t val)
+{
+	if (DWC_OTG_PARAM_TEST(val, 8, 128)) {
+		DWC_WARN("Wrong valaue for tx_thr_length\n");
+		DWC_WARN("tx_thr_length must be 8 - 128\n");
+		return -DWC_E_INVALID;
+	}
+
+	core_if->core_params->tx_thr_length = val;
+	return 0;
+}
+
+int32_t dwc_otg_get_param_tx_thr_length(dwc_otg_core_if_t * core_if)
+{
+	return core_if->core_params->tx_thr_length;
+}
+
+int dwc_otg_set_param_rx_thr_length(dwc_otg_core_if_t * core_if, int32_t val)
+{
+	if (DWC_OTG_PARAM_TEST(val, 8, 128)) {
+		DWC_WARN("Wrong valaue for rx_thr_length\n");
+		DWC_WARN("rx_thr_length must be 8 - 128\n");
+		return -DWC_E_INVALID;
+	}
+
+	core_if->core_params->rx_thr_length = val;
+	return 0;
+}
+
+int32_t dwc_otg_get_param_rx_thr_length(dwc_otg_core_if_t * core_if)
+{
+	return core_if->core_params->rx_thr_length;
+}
+
+int dwc_otg_set_param_dma_burst_size(dwc_otg_core_if_t * core_if, int32_t val)
+{
+	if (DWC_OTG_PARAM_TEST(val, 1, 1) &&
+	    DWC_OTG_PARAM_TEST(val, 4, 4) &&
+	    DWC_OTG_PARAM_TEST(val, 8, 8) &&
+	    DWC_OTG_PARAM_TEST(val, 16, 16) &&
+	    DWC_OTG_PARAM_TEST(val, 32, 32) &&
+	    DWC_OTG_PARAM_TEST(val, 64, 64) &&
+	    DWC_OTG_PARAM_TEST(val, 128, 128) &&
+	    DWC_OTG_PARAM_TEST(val, 256, 256)) {
+		DWC_WARN("`%d' invalid for parameter `dma_burst_size'\n", val);
+		return -DWC_E_INVALID;
+	}
+	core_if->core_params->dma_burst_size = val;
+	return 0;
+}
+
+int32_t dwc_otg_get_param_dma_burst_size(dwc_otg_core_if_t * core_if)
+{
+	return core_if->core_params->dma_burst_size;
+}
+
+int dwc_otg_set_param_pti_enable(dwc_otg_core_if_t * core_if, int32_t val)
+{
+	int retval = 0;
+	if (DWC_OTG_PARAM_TEST(val, 0, 1)) {
+		DWC_WARN("`%d' invalid for parameter `pti_enable'\n", val);
+		return -DWC_E_INVALID;
+	}
+	if (val && (core_if->snpsid < OTG_CORE_REV_2_72a)) {
+		if (dwc_otg_param_initialized(core_if->core_params->pti_enable)) {
+			DWC_ERROR
+			    ("%d invalid for parameter pti_enable. Check HW configuration.\n",
+			     val);
+		}
+		retval = -DWC_E_INVALID;
+		val = 0;
+	}
+	core_if->core_params->pti_enable = val;
+	return retval;
+}
+
+int32_t dwc_otg_get_param_pti_enable(dwc_otg_core_if_t * core_if)
+{
+	return core_if->core_params->pti_enable;
+}
+
+int dwc_otg_set_param_mpi_enable(dwc_otg_core_if_t * core_if, int32_t val)
+{
+	int retval = 0;
+	if (DWC_OTG_PARAM_TEST(val, 0, 1)) {
+		DWC_WARN("`%d' invalid for parameter `mpi_enable'\n", val);
+		return -DWC_E_INVALID;
+	}
+	if (val && (core_if->hwcfg2.b.multi_proc_int == 0)) {
+		if (dwc_otg_param_initialized(core_if->core_params->mpi_enable)) {
+			DWC_ERROR
+			    ("%d invalid for parameter mpi_enable. Check HW configuration.\n",
+			     val);
+		}
+		retval = -DWC_E_INVALID;
+		val = 0;
+	}
+	core_if->core_params->mpi_enable = val;
+	return retval;
+}
+
+int32_t dwc_otg_get_param_mpi_enable(dwc_otg_core_if_t * core_if)
+{
+	return core_if->core_params->mpi_enable;
+}
+
+int dwc_otg_set_param_adp_enable(dwc_otg_core_if_t * core_if, int32_t val)
+{
+	int retval = 0;
+	if (DWC_OTG_PARAM_TEST(val, 0, 1)) {
+		DWC_WARN("`%d' invalid for parameter `adp_enable'\n", val);
+		return -DWC_E_INVALID;
+	}
+	if (val && (core_if->hwcfg3.b.adp_supp == 0)) {
+		if (dwc_otg_param_initialized
+		    (core_if->core_params->adp_supp_enable)) {
+			DWC_ERROR
+			    ("%d invalid for parameter adp_enable. Check HW configuration.\n",
+			     val);
+		}
+		retval = -DWC_E_INVALID;
+		val = 0;
+	}
+	core_if->core_params->adp_supp_enable = val;
+	/*Set OTG version 2.0 in case of enabling ADP*/
+	if (val)
+		dwc_otg_set_param_otg_ver(core_if, 1);
+
+	return retval;
+}
+
+int32_t dwc_otg_get_param_adp_enable(dwc_otg_core_if_t * core_if)
+{
+	return core_if->core_params->adp_supp_enable;
+}
+
+int dwc_otg_set_param_ic_usb_cap(dwc_otg_core_if_t * core_if, int32_t val)
+{
+	int retval = 0;
+	if (DWC_OTG_PARAM_TEST(val, 0, 1)) {
+		DWC_WARN("`%d' invalid for parameter `ic_usb_cap'\n", val);
+		DWC_WARN("ic_usb_cap must be 0 or 1\n");
+		return -DWC_E_INVALID;
+	}
+
+	if (val && (core_if->hwcfg2.b.otg_enable_ic_usb == 0)) {
+		if (dwc_otg_param_initialized(core_if->core_params->ic_usb_cap)) {
+			DWC_ERROR
+			    ("%d invalid for parameter ic_usb_cap. Check HW configuration.\n",
+			     val);
+		}
+		retval = -DWC_E_INVALID;
+		val = 0;
+	}
+	core_if->core_params->ic_usb_cap = val;
+	return retval;
+}
+
+int32_t dwc_otg_get_param_ic_usb_cap(dwc_otg_core_if_t * core_if)
+{
+	return core_if->core_params->ic_usb_cap;
+}
+
+int dwc_otg_set_param_ahb_thr_ratio(dwc_otg_core_if_t * core_if, int32_t val)
+{
+	int retval = 0;
+	int valid = 1;
+
+	if (DWC_OTG_PARAM_TEST(val, 0, 3)) {
+		DWC_WARN("`%d' invalid for parameter `ahb_thr_ratio'\n", val);
+		DWC_WARN("ahb_thr_ratio must be 0 - 3\n");
+		return -DWC_E_INVALID;
+	}
+
+	if (val
+	    && (core_if->snpsid < OTG_CORE_REV_2_81a
+		|| !dwc_otg_get_param_thr_ctl(core_if))) {
+		valid = 0;
+	} else if (val
+		   && ((dwc_otg_get_param_tx_thr_length(core_if) / (1 << val)) <
+		       4)) {
+		valid = 0;
+	}
+	if (valid == 0) {
+		if (dwc_otg_param_initialized
+		    (core_if->core_params->ahb_thr_ratio)) {
+			DWC_ERROR
+			    ("%d invalid for parameter ahb_thr_ratio. Check HW configuration.\n",
+			     val);
+		}
+		retval = -DWC_E_INVALID;
+		val = 0;
+	}
+
+	core_if->core_params->ahb_thr_ratio = val;
+	return retval;
+}
+
+int32_t dwc_otg_get_param_ahb_thr_ratio(dwc_otg_core_if_t * core_if)
+{
+	return core_if->core_params->ahb_thr_ratio;
+}
+
+int dwc_otg_set_param_power_down(dwc_otg_core_if_t * core_if, int32_t val)
+{
+	int retval = 0;
+	int valid = 1;
+	hwcfg4_data_t hwcfg4 = {.d32 = 0 };
+	hwcfg4.d32 = DWC_READ_REG32(&core_if->core_global_regs->ghwcfg4);
+
+	if (DWC_OTG_PARAM_TEST(val, 0, 3)) {
+		DWC_WARN("`%d' invalid for parameter `power_down'\n", val);
+		DWC_WARN("power_down must be 0 - 2\n");
+		return -DWC_E_INVALID;
+	}
+
+	if ((val == 2) && (core_if->snpsid < OTG_CORE_REV_2_91a)) {
+		valid = 0;
+	}
+	if ((val == 3)
+	    && ((core_if->snpsid < OTG_CORE_REV_3_00a)
+		|| (hwcfg4.b.xhiber == 0))) {
+		valid = 0;
+	}
+	if (valid == 0) {
+		if (dwc_otg_param_initialized(core_if->core_params->power_down)) {
+			DWC_ERROR
+			    ("%d invalid for parameter power_down. Check HW configuration.\n",
+			     val);
+		}
+		retval = -DWC_E_INVALID;
+		val = 0;
+	}
+	core_if->core_params->power_down = val;
+	return retval;
+}
+
+int32_t dwc_otg_get_param_power_down(dwc_otg_core_if_t * core_if)
+{
+	return core_if->core_params->power_down;
+}
+
+int dwc_otg_set_param_reload_ctl(dwc_otg_core_if_t * core_if, int32_t val)
+{
+	int retval = 0;
+	int valid = 1;
+
+	if (DWC_OTG_PARAM_TEST(val, 0, 1)) {
+		DWC_WARN("`%d' invalid for parameter `reload_ctl'\n", val);
+		DWC_WARN("reload_ctl must be 0 or 1\n");
+		return -DWC_E_INVALID;
+	}
+
+	if ((val == 1) && (core_if->snpsid < OTG_CORE_REV_2_92a)) {
+		valid = 0;
+	}
+	if (valid == 0) {
+		if (dwc_otg_param_initialized(core_if->core_params->reload_ctl)) {
+			DWC_ERROR("%d invalid for parameter reload_ctl."
+				  "Check HW configuration.\n", val);
+		}
+		retval = -DWC_E_INVALID;
+		val = 0;
+	}
+	core_if->core_params->reload_ctl = val;
+	return retval;
+}
+
+int32_t dwc_otg_get_param_reload_ctl(dwc_otg_core_if_t * core_if)
+{
+	return core_if->core_params->reload_ctl;
+}
+
+int dwc_otg_set_param_dev_out_nak(dwc_otg_core_if_t * core_if, int32_t val)
+{
+	int retval = 0;
+	int valid = 1;
+
+	if (DWC_OTG_PARAM_TEST(val, 0, 1)) {
+		DWC_WARN("`%d' invalid for parameter `dev_out_nak'\n", val);
+		DWC_WARN("dev_out_nak must be 0 or 1\n");
+		return -DWC_E_INVALID;
+	}
+
+	if ((val == 1) && ((core_if->snpsid < OTG_CORE_REV_2_93a) ||
+		!(core_if->core_params->dma_desc_enable))) {
+		valid = 0;
+	}
+	if (valid == 0) {
+		if (dwc_otg_param_initialized(core_if->core_params->dev_out_nak)) {
+			DWC_ERROR("%d invalid for parameter dev_out_nak."
+				"Check HW configuration.\n", val);
+		}
+		retval = -DWC_E_INVALID;
+		val = 0;
+	}
+	core_if->core_params->dev_out_nak = val;
+	return retval;
+}
+
+int32_t dwc_otg_get_param_dev_out_nak(dwc_otg_core_if_t * core_if)
+{
+	return core_if->core_params->dev_out_nak;
+}
+
+int dwc_otg_set_param_cont_on_bna(dwc_otg_core_if_t * core_if, int32_t val)
+{
+	int retval = 0;
+	int valid = 1;
+
+	if (DWC_OTG_PARAM_TEST(val, 0, 1)) {
+		DWC_WARN("`%d' invalid for parameter `cont_on_bna'\n", val);
+		DWC_WARN("cont_on_bna must be 0 or 1\n");
+		return -DWC_E_INVALID;
+	}
+
+	if ((val == 1) && ((core_if->snpsid < OTG_CORE_REV_2_94a) ||
+		!(core_if->core_params->dma_desc_enable))) {
+			valid = 0;
+	}
+	if (valid == 0) {
+		if (dwc_otg_param_initialized(core_if->core_params->cont_on_bna)) {
+			DWC_ERROR("%d invalid for parameter cont_on_bna."
+				"Check HW configuration.\n", val);
+		}
+		retval = -DWC_E_INVALID;
+		val = 0;
+	}
+	core_if->core_params->cont_on_bna = val;
+	return retval;
+}
+
+int32_t dwc_otg_get_param_cont_on_bna(dwc_otg_core_if_t * core_if)
+{
+	return core_if->core_params->cont_on_bna;
+}
+
+int dwc_otg_set_param_ahb_single(dwc_otg_core_if_t * core_if, int32_t val)
+{
+	int retval = 0;
+	int valid = 1;
+
+	if (DWC_OTG_PARAM_TEST(val, 0, 1)) {
+		DWC_WARN("`%d' invalid for parameter `ahb_single'\n", val);
+		DWC_WARN("ahb_single must be 0 or 1\n");
+		return -DWC_E_INVALID;
+	}
+
+	if ((val == 1) && (core_if->snpsid < OTG_CORE_REV_2_94a)) {
+			valid = 0;
+	}
+	if (valid == 0) {
+		if (dwc_otg_param_initialized(core_if->core_params->ahb_single)) {
+			DWC_ERROR("%d invalid for parameter ahb_single."
+				"Check HW configuration.\n", val);
+		}
+		retval = -DWC_E_INVALID;
+		val = 0;
+	}
+	core_if->core_params->ahb_single = val;
+	return retval;
+}
+
+int32_t dwc_otg_get_param_ahb_single(dwc_otg_core_if_t * core_if)
+{
+	return core_if->core_params->ahb_single;
+}
+
+int dwc_otg_set_param_otg_ver(dwc_otg_core_if_t * core_if, int32_t val)
+{
+	int retval = 0;
+
+	if (DWC_OTG_PARAM_TEST(val, 0, 1)) {
+		DWC_WARN("`%d' invalid for parameter `otg_ver'\n", val);
+		DWC_WARN
+		    ("otg_ver must be 0(for OTG 1.3 support) or 1(for OTG 2.0 support)\n");
+		return -DWC_E_INVALID;
+	}
+
+	core_if->core_params->otg_ver = val;
+	return retval;
+}
+
+int32_t dwc_otg_get_param_otg_ver(dwc_otg_core_if_t * core_if)
+{
+	return core_if->core_params->otg_ver;
+}
+
+uint32_t dwc_otg_get_hnpstatus(dwc_otg_core_if_t * core_if)
+{
+	gotgctl_data_t otgctl;
+	otgctl.d32 = DWC_READ_REG32(&core_if->core_global_regs->gotgctl);
+	return otgctl.b.hstnegscs;
+}
+
+uint32_t dwc_otg_get_srpstatus(dwc_otg_core_if_t * core_if)
+{
+	gotgctl_data_t otgctl;
+	otgctl.d32 = DWC_READ_REG32(&core_if->core_global_regs->gotgctl);
+	return otgctl.b.sesreqscs;
+}
+
+void dwc_otg_set_hnpreq(dwc_otg_core_if_t * core_if, uint32_t val)
+{
+	if(core_if->otg_ver == 0) {
+		gotgctl_data_t otgctl;
+		otgctl.d32 = DWC_READ_REG32(&core_if->core_global_regs->gotgctl);
+		otgctl.b.hnpreq = val;
+		DWC_WRITE_REG32(&core_if->core_global_regs->gotgctl, otgctl.d32);
+	} else {
+		core_if->otg_sts = val;
+	}
+}
+
+uint32_t dwc_otg_get_gsnpsid(dwc_otg_core_if_t * core_if)
+{
+	return core_if->snpsid;
+}
+
+uint32_t dwc_otg_get_mode(dwc_otg_core_if_t * core_if)
+{
+	gintsts_data_t gintsts;
+	gintsts.d32 = DWC_READ_REG32(&core_if->core_global_regs->gintsts);
+	return gintsts.b.curmode;
+}
+
+uint32_t dwc_otg_get_hnpcapable(dwc_otg_core_if_t * core_if)
+{
+	gusbcfg_data_t usbcfg;
+	usbcfg.d32 = DWC_READ_REG32(&core_if->core_global_regs->gusbcfg);
+	return usbcfg.b.hnpcap;
+}
+
+void dwc_otg_set_hnpcapable(dwc_otg_core_if_t * core_if, uint32_t val)
+{
+	gusbcfg_data_t usbcfg;
+	usbcfg.d32 = DWC_READ_REG32(&core_if->core_global_regs->gusbcfg);
+	usbcfg.b.hnpcap = val;
+	DWC_WRITE_REG32(&core_if->core_global_regs->gusbcfg, usbcfg.d32);
+}
+
+uint32_t dwc_otg_get_srpcapable(dwc_otg_core_if_t * core_if)
+{
+	gusbcfg_data_t usbcfg;
+	usbcfg.d32 = DWC_READ_REG32(&core_if->core_global_regs->gusbcfg);
+	return usbcfg.b.srpcap;
+}
+
+void dwc_otg_set_srpcapable(dwc_otg_core_if_t * core_if, uint32_t val)
+{
+	gusbcfg_data_t usbcfg;
+	usbcfg.d32 = DWC_READ_REG32(&core_if->core_global_regs->gusbcfg);
+	usbcfg.b.srpcap = val;
+	DWC_WRITE_REG32(&core_if->core_global_regs->gusbcfg, usbcfg.d32);
+}
+
+uint32_t dwc_otg_get_devspeed(dwc_otg_core_if_t * core_if)
+{
+	dcfg_data_t dcfg;
+	/* originally: dcfg.d32 = DWC_READ_REG32(&core_if->dev_if->dev_global_regs->dcfg); */
+
+        dcfg.d32 = -1; //GRAYG
+        DWC_DEBUGPL(DBG_CILV, "%s - core_if(%p)\n", __func__, core_if);
+        if (NULL == core_if)
+                DWC_ERROR("reg request with NULL core_if\n");
+        DWC_DEBUGPL(DBG_CILV, "%s - core_if(%p)->dev_if(%p)\n", __func__,
+                    core_if, core_if->dev_if);
+        if (NULL == core_if->dev_if)
+                DWC_ERROR("reg request with NULL dev_if\n");
+        DWC_DEBUGPL(DBG_CILV, "%s - core_if(%p)->dev_if(%p)->"
+                    "dev_global_regs(%p)\n", __func__,
+                    core_if, core_if->dev_if,
+                    core_if->dev_if->dev_global_regs);
+        if (NULL == core_if->dev_if->dev_global_regs)
+                DWC_ERROR("reg request with NULL dev_global_regs\n");
+        else {
+                DWC_DEBUGPL(DBG_CILV, "%s - &core_if(%p)->dev_if(%p)->"
+                            "dev_global_regs(%p)->dcfg = %p\n", __func__,
+                            core_if, core_if->dev_if,
+                            core_if->dev_if->dev_global_regs,
+                            &core_if->dev_if->dev_global_regs->dcfg);
+		dcfg.d32 = DWC_READ_REG32(&core_if->dev_if->dev_global_regs->dcfg);
+        }
+	return dcfg.b.devspd;
+}
+
+void dwc_otg_set_devspeed(dwc_otg_core_if_t * core_if, uint32_t val)
+{
+	dcfg_data_t dcfg;
+	dcfg.d32 = DWC_READ_REG32(&core_if->dev_if->dev_global_regs->dcfg);
+	dcfg.b.devspd = val;
+	DWC_WRITE_REG32(&core_if->dev_if->dev_global_regs->dcfg, dcfg.d32);
+}
+
+uint32_t dwc_otg_get_busconnected(dwc_otg_core_if_t * core_if)
+{
+	hprt0_data_t hprt0;
+	hprt0.d32 = DWC_READ_REG32(core_if->host_if->hprt0);
+	return hprt0.b.prtconnsts;
+}
+
+uint32_t dwc_otg_get_enumspeed(dwc_otg_core_if_t * core_if)
+{
+	dsts_data_t dsts;
+	dsts.d32 = DWC_READ_REG32(&core_if->dev_if->dev_global_regs->dsts);
+	return dsts.b.enumspd;
+}
+
+uint32_t dwc_otg_get_prtpower(dwc_otg_core_if_t * core_if)
+{
+	hprt0_data_t hprt0;
+	hprt0.d32 = DWC_READ_REG32(core_if->host_if->hprt0);
+	return hprt0.b.prtpwr;
+
+}
+
+uint32_t dwc_otg_get_core_state(dwc_otg_core_if_t * core_if)
+{
+	return core_if->hibernation_suspend;
+}
+
+void dwc_otg_set_prtpower(dwc_otg_core_if_t * core_if, uint32_t val)
+{
+	hprt0_data_t hprt0;
+	hprt0.d32 = dwc_otg_read_hprt0(core_if);
+	hprt0.b.prtpwr = val;
+	DWC_WRITE_REG32(core_if->host_if->hprt0, hprt0.d32);
+}
+
+uint32_t dwc_otg_get_prtsuspend(dwc_otg_core_if_t * core_if)
+{
+	hprt0_data_t hprt0;
+	hprt0.d32 = DWC_READ_REG32(core_if->host_if->hprt0);
+	return hprt0.b.prtsusp;
+
+}
+
+void dwc_otg_set_prtsuspend(dwc_otg_core_if_t * core_if, uint32_t val)
+{
+	hprt0_data_t hprt0;
+	hprt0.d32 = dwc_otg_read_hprt0(core_if);
+	hprt0.b.prtsusp = val;
+	DWC_WRITE_REG32(core_if->host_if->hprt0, hprt0.d32);
+}
+
+uint32_t dwc_otg_get_fr_interval(dwc_otg_core_if_t * core_if)
+{
+	hfir_data_t hfir;
+	hfir.d32 = DWC_READ_REG32(&core_if->host_if->host_global_regs->hfir);
+	return hfir.b.frint;
+
+}
+
+void dwc_otg_set_fr_interval(dwc_otg_core_if_t * core_if, uint32_t val)
+{
+	hfir_data_t hfir;
+	uint32_t fram_int;
+	fram_int = calc_frame_interval(core_if);
+	hfir.d32 = DWC_READ_REG32(&core_if->host_if->host_global_regs->hfir);
+	if (!core_if->core_params->reload_ctl) {
+		DWC_WARN("\nCannot reload HFIR register.HFIR.HFIRRldCtrl bit is"
+			 "not set to 1.\nShould load driver with reload_ctl=1"
+			 " module parameter\n");
+		return;
+	}
+	switch (fram_int) {
+	case 3750:
+		if ((val < 3350) || (val > 4150)) {
+			DWC_WARN("HFIR interval for HS core and 30 MHz"
+				 "clock freq should be from 3350 to 4150\n");
+			return;
+		}
+		break;
+	case 30000:
+		if ((val < 26820) || (val > 33180)) {
+			DWC_WARN("HFIR interval for FS/LS core and 30 MHz"
+				 "clock freq should be from 26820 to 33180\n");
+			return;
+		}
+		break;
+	case 6000:
+		if ((val < 5360) || (val > 6640)) {
+			DWC_WARN("HFIR interval for HS core and 48 MHz"
+				 "clock freq should be from 5360 to 6640\n");
+			return;
+		}
+		break;
+	case 48000:
+		if ((val < 42912) || (val > 53088)) {
+			DWC_WARN("HFIR interval for FS/LS core and 48 MHz"
+				 "clock freq should be from 42912 to 53088\n");
+			return;
+		}
+		break;
+	case 7500:
+		if ((val < 6700) || (val > 8300)) {
+			DWC_WARN("HFIR interval for HS core and 60 MHz"
+				 "clock freq should be from 6700 to 8300\n");
+			return;
+		}
+		break;
+	case 60000:
+		if ((val < 53640) || (val > 65536)) {
+			DWC_WARN("HFIR interval for FS/LS core and 60 MHz"
+				 "clock freq should be from 53640 to 65536\n");
+			return;
+		}
+		break;
+	default:
+		DWC_WARN("Unknown frame interval\n");
+		return;
+		break;
+
+	}
+	hfir.b.frint = val;
+	DWC_WRITE_REG32(&core_if->host_if->host_global_regs->hfir, hfir.d32);
+}
+
+uint32_t dwc_otg_get_mode_ch_tim(dwc_otg_core_if_t * core_if)
+{
+	hcfg_data_t hcfg;
+	hcfg.d32 = DWC_READ_REG32(&core_if->host_if->host_global_regs->hcfg);
+	return hcfg.b.modechtimen;
+
+}
+
+void dwc_otg_set_mode_ch_tim(dwc_otg_core_if_t * core_if, uint32_t val)
+{
+	hcfg_data_t hcfg;
+	hcfg.d32 = DWC_READ_REG32(&core_if->host_if->host_global_regs->hcfg);
+	hcfg.b.modechtimen = val;
+	DWC_WRITE_REG32(&core_if->host_if->host_global_regs->hcfg, hcfg.d32);
+}
+
+void dwc_otg_set_prtresume(dwc_otg_core_if_t * core_if, uint32_t val)
+{
+	hprt0_data_t hprt0;
+	hprt0.d32 = dwc_otg_read_hprt0(core_if);
+	hprt0.b.prtres = val;
+	DWC_WRITE_REG32(core_if->host_if->hprt0, hprt0.d32);
+}
+
+uint32_t dwc_otg_get_remotewakesig(dwc_otg_core_if_t * core_if)
+{
+	dctl_data_t dctl;
+	dctl.d32 = DWC_READ_REG32(&core_if->dev_if->dev_global_regs->dctl);
+	return dctl.b.rmtwkupsig;
+}
+
+uint32_t dwc_otg_get_lpm_portsleepstatus(dwc_otg_core_if_t * core_if)
+{
+	glpmcfg_data_t lpmcfg;
+	lpmcfg.d32 = DWC_READ_REG32(&core_if->core_global_regs->glpmcfg);
+
+	DWC_ASSERT(!
+		   ((core_if->lx_state == DWC_OTG_L1) ^ lpmcfg.b.prt_sleep_sts),
+		   "lx_state = %d, lmpcfg.prt_sleep_sts = %d\n",
+		   core_if->lx_state, lpmcfg.b.prt_sleep_sts);
+
+	return lpmcfg.b.prt_sleep_sts;
+}
+
+uint32_t dwc_otg_get_lpm_remotewakeenabled(dwc_otg_core_if_t * core_if)
+{
+	glpmcfg_data_t lpmcfg;
+	lpmcfg.d32 = DWC_READ_REG32(&core_if->core_global_regs->glpmcfg);
+	return lpmcfg.b.rem_wkup_en;
+}
+
+uint32_t dwc_otg_get_lpmresponse(dwc_otg_core_if_t * core_if)
+{
+	glpmcfg_data_t lpmcfg;
+	lpmcfg.d32 = DWC_READ_REG32(&core_if->core_global_regs->glpmcfg);
+	return lpmcfg.b.appl_resp;
+}
+
+void dwc_otg_set_lpmresponse(dwc_otg_core_if_t * core_if, uint32_t val)
+{
+	glpmcfg_data_t lpmcfg;
+	lpmcfg.d32 = DWC_READ_REG32(&core_if->core_global_regs->glpmcfg);
+	lpmcfg.b.appl_resp = val;
+	DWC_WRITE_REG32(&core_if->core_global_regs->glpmcfg, lpmcfg.d32);
+}
+
+uint32_t dwc_otg_get_hsic_connect(dwc_otg_core_if_t * core_if)
+{
+	glpmcfg_data_t lpmcfg;
+	lpmcfg.d32 = DWC_READ_REG32(&core_if->core_global_regs->glpmcfg);
+	return lpmcfg.b.hsic_connect;
+}
+
+void dwc_otg_set_hsic_connect(dwc_otg_core_if_t * core_if, uint32_t val)
+{
+	glpmcfg_data_t lpmcfg;
+	lpmcfg.d32 = DWC_READ_REG32(&core_if->core_global_regs->glpmcfg);
+	lpmcfg.b.hsic_connect = val;
+	DWC_WRITE_REG32(&core_if->core_global_regs->glpmcfg, lpmcfg.d32);
+}
+
+uint32_t dwc_otg_get_inv_sel_hsic(dwc_otg_core_if_t * core_if)
+{
+	glpmcfg_data_t lpmcfg;
+	lpmcfg.d32 = DWC_READ_REG32(&core_if->core_global_regs->glpmcfg);
+	return lpmcfg.b.inv_sel_hsic;
+
+}
+
+void dwc_otg_set_inv_sel_hsic(dwc_otg_core_if_t * core_if, uint32_t val)
+{
+	glpmcfg_data_t lpmcfg;
+	lpmcfg.d32 = DWC_READ_REG32(&core_if->core_global_regs->glpmcfg);
+	lpmcfg.b.inv_sel_hsic = val;
+	DWC_WRITE_REG32(&core_if->core_global_regs->glpmcfg, lpmcfg.d32);
+}
+
+uint32_t dwc_otg_get_gotgctl(dwc_otg_core_if_t * core_if)
+{
+	return DWC_READ_REG32(&core_if->core_global_regs->gotgctl);
+}
+
+void dwc_otg_set_gotgctl(dwc_otg_core_if_t * core_if, uint32_t val)
+{
+	DWC_WRITE_REG32(&core_if->core_global_regs->gotgctl, val);
+}
+
+uint32_t dwc_otg_get_gusbcfg(dwc_otg_core_if_t * core_if)
+{
+	return DWC_READ_REG32(&core_if->core_global_regs->gusbcfg);
+}
+
+void dwc_otg_set_gusbcfg(dwc_otg_core_if_t * core_if, uint32_t val)
+{
+	DWC_WRITE_REG32(&core_if->core_global_regs->gusbcfg, val);
+}
+
+uint32_t dwc_otg_get_grxfsiz(dwc_otg_core_if_t * core_if)
+{
+	return DWC_READ_REG32(&core_if->core_global_regs->grxfsiz);
+}
+
+void dwc_otg_set_grxfsiz(dwc_otg_core_if_t * core_if, uint32_t val)
+{
+	DWC_WRITE_REG32(&core_if->core_global_regs->grxfsiz, val);
+}
+
+uint32_t dwc_otg_get_gnptxfsiz(dwc_otg_core_if_t * core_if)
+{
+	return DWC_READ_REG32(&core_if->core_global_regs->gnptxfsiz);
+}
+
+void dwc_otg_set_gnptxfsiz(dwc_otg_core_if_t * core_if, uint32_t val)
+{
+	DWC_WRITE_REG32(&core_if->core_global_regs->gnptxfsiz, val);
+}
+
+uint32_t dwc_otg_get_gpvndctl(dwc_otg_core_if_t * core_if)
+{
+	return DWC_READ_REG32(&core_if->core_global_regs->gpvndctl);
+}
+
+void dwc_otg_set_gpvndctl(dwc_otg_core_if_t * core_if, uint32_t val)
+{
+	DWC_WRITE_REG32(&core_if->core_global_regs->gpvndctl, val);
+}
+
+uint32_t dwc_otg_get_ggpio(dwc_otg_core_if_t * core_if)
+{
+	return DWC_READ_REG32(&core_if->core_global_regs->ggpio);
+}
+
+void dwc_otg_set_ggpio(dwc_otg_core_if_t * core_if, uint32_t val)
+{
+	DWC_WRITE_REG32(&core_if->core_global_regs->ggpio, val);
+}
+
+uint32_t dwc_otg_get_hprt0(dwc_otg_core_if_t * core_if)
+{
+	return DWC_READ_REG32(core_if->host_if->hprt0);
+
+}
+
+void dwc_otg_set_hprt0(dwc_otg_core_if_t * core_if, uint32_t val)
+{
+	DWC_WRITE_REG32(core_if->host_if->hprt0, val);
+}
+
+uint32_t dwc_otg_get_guid(dwc_otg_core_if_t * core_if)
+{
+	return DWC_READ_REG32(&core_if->core_global_regs->guid);
+}
+
+void dwc_otg_set_guid(dwc_otg_core_if_t * core_if, uint32_t val)
+{
+	DWC_WRITE_REG32(&core_if->core_global_regs->guid, val);
+}
+
+uint32_t dwc_otg_get_hptxfsiz(dwc_otg_core_if_t * core_if)
+{
+	return DWC_READ_REG32(&core_if->core_global_regs->hptxfsiz);
+}
+
+uint16_t dwc_otg_get_otg_version(dwc_otg_core_if_t * core_if)
+{
+	return ((core_if->otg_ver == 1) ? (uint16_t)0x0200 : (uint16_t)0x0103);
+}
+
+/**
+ * Start the SRP timer to detect when the SRP does not complete within
+ * 6 seconds.
+ *
+ * @param core_if the pointer to core_if strucure.
+ */
+void dwc_otg_pcd_start_srp_timer(dwc_otg_core_if_t * core_if)
+{
+	core_if->srp_timer_started = 1;
+	DWC_TIMER_SCHEDULE(core_if->srp_timer, 6000 /* 6 secs */ );
+}
+
+void dwc_otg_initiate_srp(dwc_otg_core_if_t * core_if)
+{
+	uint32_t *addr = (uint32_t *) & (core_if->core_global_regs->gotgctl);
+	gotgctl_data_t mem;
+	gotgctl_data_t val;
+
+	val.d32 = DWC_READ_REG32(addr);
+	if (val.b.sesreq) {
+		DWC_ERROR("Session Request Already active!\n");
+		return;
+	}
+
+	DWC_INFO("Session Request Initated\n");	//NOTICE
+	mem.d32 = DWC_READ_REG32(addr);
+	mem.b.sesreq = 1;
+	DWC_WRITE_REG32(addr, mem.d32);
+
+	/* Start the SRP timer */
+	dwc_otg_pcd_start_srp_timer(core_if);
+	return;
+}
diff -Nur --no-dereference linux-5.10.52-orig/drivers/usb/host/dwc_otg/dwc_otg_cil.h linux-5.10.52-v7l+/drivers/usb/host/dwc_otg/dwc_otg_cil.h
--- linux-5.10.52-orig/drivers/usb/host/dwc_otg/dwc_otg_cil.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/usb/host/dwc_otg/dwc_otg_cil.h	2021-07-25 16:46:28.677910113 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/* ==========================================================================
+ * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_cil.h $
+ * $Revision: #123 $
+ * $Date: 2012/08/10 $
+ * $Change: 2047372 $
+ *
+ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter,
+ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless
+ * otherwise expressly agreed to in writing between Synopsys and you.
+ *
+ * The Software IS NOT an item of Licensed Software or Licensed Product under
+ * any End User Software License Agreement or Agreement for Licensed Product
+ * with Synopsys or any supplement thereto. You are permitted to use and
+ * redistribute this Software in source and binary forms, with or without
+ * modification, provided that redistributions of source code must retain this
+ * notice. You may not view, use, disclose, copy or distribute this file or
+ * any information contained herein except pursuant to this license grant from
+ * Synopsys. If you do not agree with this notice, including the disclaimer
+ * below, then you are not authorized to use the Software.
+ *
+ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * ========================================================================== */
+
+#if !defined(__DWC_CIL_H__)
+#define __DWC_CIL_H__
+
+#include "dwc_list.h"
+#include "dwc_otg_dbg.h"
+#include "dwc_otg_regs.h"
+
+#include "dwc_otg_core_if.h"
+#include "dwc_otg_adp.h"
+
+/**
+ * @file
+ * This file contains the interface to the Core Interface Layer.
+ */
+
+#ifdef DWC_UTE_CFI
+
+#define MAX_DMA_DESCS_PER_EP	256
+
+/**
+ * Enumeration for the data buffer mode
+ */
+typedef enum _data_buffer_mode {
+	BM_STANDARD = 0,	/* data buffer is in normal mode */
+	BM_SG = 1,		/* data buffer uses the scatter/gather mode */
+	BM_CONCAT = 2,		/* data buffer uses the concatenation mode */
+	BM_CIRCULAR = 3,	/* data buffer uses the circular DMA mode */
+	BM_ALIGN = 4		/* data buffer is in buffer alignment mode */
+} data_buffer_mode_e;
+#endif //DWC_UTE_CFI
+
+/** Macros defined for DWC OTG HW Release version */
+
+#define OTG_CORE_REV_2_60a	0x4F54260A
+#define OTG_CORE_REV_2_71a	0x4F54271A
+#define OTG_CORE_REV_2_72a	0x4F54272A
+#define OTG_CORE_REV_2_80a	0x4F54280A
+#define OTG_CORE_REV_2_81a	0x4F54281A
+#define OTG_CORE_REV_2_90a	0x4F54290A
+#define OTG_CORE_REV_2_91a	0x4F54291A
+#define OTG_CORE_REV_2_92a	0x4F54292A
+#define OTG_CORE_REV_2_93a	0x4F54293A
+#define OTG_CORE_REV_2_94a	0x4F54294A
+#define OTG_CORE_REV_3_00a	0x4F54300A
+
+/**
+ * Information for each ISOC packet.
+ */
+typedef struct iso_pkt_info {
+	uint32_t offset;
+	uint32_t length;
+	int32_t status;
+} iso_pkt_info_t;
+
+/**
+ * The <code>dwc_ep</code> structure represents the state of a single
+ * endpoint when acting in device mode. It contains the data items
+ * needed for an endpoint to be activated and transfer packets.
+ */
+typedef struct dwc_ep {
+	/** EP number used for register address lookup */
+	uint8_t num;
+	/** EP direction 0 = OUT */
+	unsigned is_in:1;
+	/** EP active. */
+	unsigned active:1;
+
+	/**
+	 * Periodic Tx FIFO # for IN EPs For INTR EP set to 0 to use non-periodic
+	 * Tx FIFO. If dedicated Tx FIFOs are enabled Tx FIFO # FOR IN EPs*/
+	unsigned tx_fifo_num:4;
+	/** EP type: 0 - Control, 1 - ISOC,	 2 - BULK,	3 - INTR */
+	unsigned type:2;
+#define DWC_OTG_EP_TYPE_CONTROL	   0
+#define DWC_OTG_EP_TYPE_ISOC	   1
+#define DWC_OTG_EP_TYPE_BULK	   2
+#define DWC_OTG_EP_TYPE_INTR	   3
+
+	/** DATA start PID for INTR and BULK EP */
+	unsigned data_pid_start:1;
+	/** Frame (even/odd) for ISOC EP */
+	unsigned even_odd_frame:1;
+	/** Max Packet bytes */
+	unsigned maxpacket:11;
+
+	/** Max Transfer size */
+	uint32_t maxxfer;
+
+	/** @name Transfer state */
+	/** @{ */
+
+	/**
+	 * Pointer to the beginning of the transfer buffer -- do not modify
+	 * during transfer.
+	 */
+
+	dwc_dma_t dma_addr;
+
+	dwc_dma_t dma_desc_addr;
+	dwc_otg_dev_dma_desc_t *desc_addr;
+
+	uint8_t *start_xfer_buff;
+	/** pointer to the transfer buffer */
+	uint8_t *xfer_buff;
+	/** Number of bytes to transfer */
+	unsigned xfer_len:19;
+	/** Number of bytes transferred. */
+	unsigned xfer_count:19;
+	/** Sent ZLP */
+	unsigned sent_zlp:1;
+	/** Total len for control transfer */
+	unsigned total_len:19;
+
+	/** stall clear flag */
+	unsigned stall_clear_flag:1;
+
+	/** SETUP pkt cnt rollover flag for EP0 out*/
+	unsigned stp_rollover;
+
+#ifdef DWC_UTE_CFI
+	/* The buffer mode */
+	data_buffer_mode_e buff_mode;
+
+	/* The chain of DMA descriptors.
+	 * MAX_DMA_DESCS_PER_EP will be allocated for each active EP.
+	 */
+	dwc_otg_dma_desc_t *descs;
+
+	/* The DMA address of the descriptors chain start */
+	dma_addr_t descs_dma_addr;
+	/** This variable stores the length of the last enqueued request */
+	uint32_t cfi_req_len;
+#endif				//DWC_UTE_CFI
+
+/** Max DMA Descriptor count for any EP */
+#define MAX_DMA_DESC_CNT 256
+	/** Allocated DMA Desc count */
+	uint32_t desc_cnt;
+
+	/** bInterval */
+	uint32_t bInterval;
+	/** Next frame num to setup next ISOC transfer */
+	uint32_t frame_num;
+	/** Indicates SOF number overrun in DSTS */
+	uint8_t frm_overrun;
+
+#ifdef DWC_UTE_PER_IO
+	/** Next frame num for which will be setup DMA Desc */
+	uint32_t xiso_frame_num;
+	/** bInterval */
+	uint32_t xiso_bInterval;
+	/** Count of currently active transfers - shall be either 0 or 1 */
+	int xiso_active_xfers;
+	int xiso_queued_xfers;
+#endif
+#ifdef DWC_EN_ISOC
+	/**
+	 * Variables specific for ISOC EPs
+	 *
+	 */
+	/** DMA addresses of ISOC buffers */
+	dwc_dma_t dma_addr0;
+	dwc_dma_t dma_addr1;
+
+	dwc_dma_t iso_dma_desc_addr;
+	dwc_otg_dev_dma_desc_t *iso_desc_addr;
+
+	/** pointer to the transfer buffers */
+	uint8_t *xfer_buff0;
+	uint8_t *xfer_buff1;
+
+	/** number of ISOC Buffer is processing */
+	uint32_t proc_buf_num;
+	/** Interval of ISOC Buffer processing */
+	uint32_t buf_proc_intrvl;
+	/** Data size for regular frame */
+	uint32_t data_per_frame;
+
+	/* todo - pattern data support is to be implemented in the future */
+	/** Data size for pattern frame */
+	uint32_t data_pattern_frame;
+	/** Frame number of pattern data */
+	uint32_t sync_frame;
+
+	/** bInterval */
+	uint32_t bInterval;
+	/** ISO Packet number per frame */
+	uint32_t pkt_per_frm;
+	/** Next frame num for which will be setup DMA Desc */
+	uint32_t next_frame;
+	/** Number of packets per buffer processing */
+	uint32_t pkt_cnt;
+	/** Info for all isoc packets */
+	iso_pkt_info_t *pkt_info;
+	/** current pkt number */
+	uint32_t cur_pkt;
+	/** current pkt number */
+	uint8_t *cur_pkt_addr;
+	/** current pkt number */
+	uint32_t cur_pkt_dma_addr;
+#endif				/* DWC_EN_ISOC */
+
+/** @} */
+} dwc_ep_t;
+
+/*
+ * Reasons for halting a host channel.
+ */
+typedef enum dwc_otg_halt_status {
+	DWC_OTG_HC_XFER_NO_HALT_STATUS,
+	DWC_OTG_HC_XFER_COMPLETE,
+	DWC_OTG_HC_XFER_URB_COMPLETE,
+	DWC_OTG_HC_XFER_ACK,
+	DWC_OTG_HC_XFER_NAK,
+	DWC_OTG_HC_XFER_NYET,
+	DWC_OTG_HC_XFER_STALL,
+	DWC_OTG_HC_XFER_XACT_ERR,
+	DWC_OTG_HC_XFER_FRAME_OVERRUN,
+	DWC_OTG_HC_XFER_BABBLE_ERR,
+	DWC_OTG_HC_XFER_DATA_TOGGLE_ERR,
+	DWC_OTG_HC_XFER_AHB_ERR,
+	DWC_OTG_HC_XFER_PERIODIC_INCOMPLETE,
+	DWC_OTG_HC_XFER_URB_DEQUEUE
+} dwc_otg_halt_status_e;
+
+/**
+ * Host channel descriptor. This structure represents the state of a single
+ * host channel when acting in host mode. It contains the data items needed to
+ * transfer packets to an endpoint via a host channel.
+ */
+typedef struct dwc_hc {
+	/** Host channel number used for register address lookup */
+	uint8_t hc_num;
+
+	/** Device to access */
+	unsigned dev_addr:7;
+
+	/** EP to access */
+	unsigned ep_num:4;
+
+	/** EP direction. 0: OUT, 1: IN */
+	unsigned ep_is_in:1;
+
+	/**
+	 * EP speed.
+	 * One of the following values:
+	 *	- DWC_OTG_EP_SPEED_LOW
+	 *	- DWC_OTG_EP_SPEED_FULL
+	 *	- DWC_OTG_EP_SPEED_HIGH
+	 */
+	unsigned speed:2;
+#define DWC_OTG_EP_SPEED_LOW	0
+#define DWC_OTG_EP_SPEED_FULL	1
+#define DWC_OTG_EP_SPEED_HIGH	2
+
+	/**
+	 * Endpoint type.
+	 * One of the following values:
+	 *	- DWC_OTG_EP_TYPE_CONTROL: 0
+	 *	- DWC_OTG_EP_TYPE_ISOC: 1
+	 *	- DWC_OTG_EP_TYPE_BULK: 2
+	 *	- DWC_OTG_EP_TYPE_INTR: 3
+	 */
+	unsigned ep_type:2;
+
+	/** Max packet size in bytes */
+	unsigned max_packet:11;
+
+	/**
+	 * PID for initial transaction.
+	 * 0: DATA0,<br>
+	 * 1: DATA2,<br>
+	 * 2: DATA1,<br>
+	 * 3: MDATA (non-Control EP),
+	 *	  SETUP (Control EP)
+	 */
+	unsigned data_pid_start:2;
+#define DWC_OTG_HC_PID_DATA0 0
+#define DWC_OTG_HC_PID_DATA2 1
+#define DWC_OTG_HC_PID_DATA1 2
+#define DWC_OTG_HC_PID_MDATA 3
+#define DWC_OTG_HC_PID_SETUP 3
+
+	/** Number of periodic transactions per (micro)frame */
+	unsigned multi_count:2;
+
+	/** @name Transfer State */
+	/** @{ */
+
+	/** Pointer to the current transfer buffer position. */
+	uint8_t *xfer_buff;
+	/**
+	 * In Buffer DMA mode this buffer will be used
+	 * if xfer_buff is not DWORD aligned.
+	 */
+	dwc_dma_t align_buff;
+	/** Total number of bytes to transfer. */
+	uint32_t xfer_len;
+	/** Number of bytes transferred so far. */
+	uint32_t xfer_count;
+	/** Packet count at start of transfer.*/
+	uint16_t start_pkt_count;
+
+	/**
+	 * Flag to indicate whether the transfer has been started. Set to 1 if
+	 * it has been started, 0 otherwise.
+	 */
+	uint8_t xfer_started;
+
+	/**
+	 * Set to 1 to indicate that a PING request should be issued on this
+	 * channel. If 0, process normally.
+	 */
+	uint8_t do_ping;
+
+	/**
+	 * Set to 1 to indicate that the error count for this transaction is
+	 * non-zero. Set to 0 if the error count is 0.
+	 */
+	uint8_t error_state;
+
+	/**
+	 * Set to 1 to indicate that this channel should be halted the next
+	 * time a request is queued for the channel. This is necessary in
+	 * slave mode if no request queue space is available when an attempt
+	 * is made to halt the channel.
+	 */
+	uint8_t halt_on_queue;
+
+	/**
+	 * Set to 1 if the host channel has been halted, but the core is not
+	 * finished flushing queued requests. Otherwise 0.
+	 */
+	uint8_t halt_pending;
+
+	/**
+	 * Reason for halting the host channel.
+	 */
+	dwc_otg_halt_status_e halt_status;
+
+	/*
+	 * Split settings for the host channel
+	 */
+	uint8_t do_split;		   /**< Enable split for the channel */
+	uint8_t complete_split;	   /**< Enable complete split */
+	uint8_t hub_addr;		   /**< Address of high speed hub */
+
+	uint8_t port_addr;		   /**< Port of the low/full speed device */
+	/** Split transaction position
+	 * One of the following values:
+	 *	  - DWC_HCSPLIT_XACTPOS_MID
+	 *	  - DWC_HCSPLIT_XACTPOS_BEGIN
+	 *	  - DWC_HCSPLIT_XACTPOS_END
+	 *	  - DWC_HCSPLIT_XACTPOS_ALL */
+	uint8_t xact_pos;
+
+	/** Set when the host channel does a short read. */
+	uint8_t short_read;
+
+	/**
+	 * Number of requests issued for this channel since it was assigned to
+	 * the current transfer (not counting PINGs).
+	 */
+	uint8_t requests;
+
+	/**
+	 * Queue Head for the transfer being processed by this channel.
+	 */
+	struct dwc_otg_qh *qh;
+
+	/** @} */
+
+	/** Entry in list of host channels. */
+	 DWC_CIRCLEQ_ENTRY(dwc_hc) hc_list_entry;
+
+	/** @name Descriptor DMA support */
+	/** @{ */
+
+	/** Number of Transfer Descriptors */
+	uint16_t ntd;
+
+	/** Descriptor List DMA address */
+	dwc_dma_t desc_list_addr;
+
+	/** Scheduling micro-frame bitmap. */
+	uint8_t schinfo;
+
+	/** @} */
+} dwc_hc_t;
+
+/**
+ * The following parameters may be specified when starting the module. These
+ * parameters define how the DWC_otg controller should be configured.
+ */
+typedef struct dwc_otg_core_params {
+	int32_t opt;
+
+	/**
+	 * Specifies the OTG capabilities. The driver will automatically
+	 * detect the value for this parameter if none is specified.
+	 * 0 - HNP and SRP capable (default)
+	 * 1 - SRP Only capable
+	 * 2 - No HNP/SRP capable
+	 */
+	int32_t otg_cap;
+
+	/**
+	 * Specifies whether to use slave or DMA mode for accessing the data
+	 * FIFOs. The driver will automatically detect the value for this
+	 * parameter if none is specified.
+	 * 0 - Slave
+	 * 1 - DMA (default, if available)
+	 */
+	int32_t dma_enable;
+
+	/**
+	 * When DMA mode is enabled specifies whether to use address DMA or DMA
+	 * Descriptor mode for accessing the data FIFOs in device mode. The driver
+	 * will automatically detect the value for this if none is specified.
+	 * 0 - address DMA
+	 * 1 - DMA Descriptor(default, if available)
+	 */
+	int32_t dma_desc_enable;
+	/** The DMA Burst size (applicable only for External DMA
+	 * Mode). 1, 4, 8 16, 32, 64, 128, 256 (default 32)
+	 */
+	int32_t dma_burst_size;	/* Translate this to GAHBCFG values */
+
+	/**
+	 * Specifies the maximum speed of operation in host and device mode.
+	 * The actual speed depends on the speed of the attached device and
+	 * the value of phy_type. The actual speed depends on the speed of the
+	 * attached device.
+	 * 0 - High Speed (default)
+	 * 1 - Full Speed
+	 */
+	int32_t speed;
+	/** Specifies whether low power mode is supported when attached
+	 *	to a Full Speed or Low Speed device in host mode.
+	 * 0 - Don't support low power mode (default)
+	 * 1 - Support low power mode
+	 */
+	int32_t host_support_fs_ls_low_power;
+
+	/** Specifies the PHY clock rate in low power mode when connected to a
+	 * Low Speed device in host mode. This parameter is applicable only if
+	 * HOST_SUPPORT_FS_LS_LOW_POWER is enabled. If PHY_TYPE is set to FS
+	 * then defaults to 6 MHZ otherwise 48 MHZ.
+	 *
+	 * 0 - 48 MHz
+	 * 1 - 6 MHz
+	 */
+	int32_t host_ls_low_power_phy_clk;
+
+	/**
+	 * 0 - Use cC FIFO size parameters
+	 * 1 - Allow dynamic FIFO sizing (default)
+	 */
+	int32_t enable_dynamic_fifo;
+
+	/** Total number of 4-byte words in the data FIFO memory. This
+	 * memory includes the Rx FIFO, non-periodic Tx FIFO, and periodic
+	 * Tx FIFOs.
+	 * 32 to 32768 (default 8192)
+	 * Note: The total FIFO memory depth in the FPGA configuration is 8192.
+	 */
+	int32_t data_fifo_size;
+
+	/** Number of 4-byte words in the Rx FIFO in device mode when dynamic
+	 * FIFO sizing is enabled.
+	 * 16 to 32768 (default 1064)
+	 */
+	int32_t dev_rx_fifo_size;
+
+	/** Number of 4-byte words in the non-periodic Tx FIFO in device mode
+	 * when dynamic FIFO sizing is enabled.
+	 * 16 to 32768 (default 1024)
+	 */
+	int32_t dev_nperio_tx_fifo_size;
+
+	/** Number of 4-byte words in each of the periodic Tx FIFOs in device
+	 * mode when dynamic FIFO sizing is enabled.
+	 * 4 to 768 (default 256)
+	 */
+	uint32_t dev_perio_tx_fifo_size[MAX_PERIO_FIFOS];
+
+	/** Number of 4-byte words in the Rx FIFO in host mode when dynamic
+	 * FIFO sizing is enabled.
+	 * 16 to 32768 (default 1024)
+	 */
+	int32_t host_rx_fifo_size;
+
+	/** Number of 4-byte words in the non-periodic Tx FIFO in host mode
+	 * when Dynamic FIFO sizing is enabled in the core.
+	 * 16 to 32768 (default 1024)
+	 */
+	int32_t host_nperio_tx_fifo_size;
+
+	/** Number of 4-byte words in the host periodic Tx FIFO when dynamic
+	 * FIFO sizing is enabled.
+	 * 16 to 32768 (default 1024)
+	 */
+	int32_t host_perio_tx_fifo_size;
+
+	/** The maximum transfer size supported in bytes.
+	 * 2047 to 65,535  (default 65,535)
+	 */
+	int32_t max_transfer_size;
+
+	/** The maximum number of packets in a transfer.
+	 * 15 to 511  (default 511)
+	 */
+	int32_t max_packet_count;
+
+	/** The number of host channel registers to use.
+	 * 1 to 16 (default 12)
+	 * Note: The FPGA configuration supports a maximum of 12 host channels.
+	 */
+	int32_t host_channels;
+
+	/** The number of endpoints in addition to EP0 available for device
+	 * mode operations.
+	 * 1 to 15 (default 6 IN and OUT)
+	 * Note: The FPGA configuration supports a maximum of 6 IN and OUT
+	 * endpoints in addition to EP0.
+	 */
+	int32_t dev_endpoints;
+
+		/**
+		 * Specifies the type of PHY interface to use. By default, the driver
+		 * will automatically detect the phy_type.
+		 *
+		 * 0 - Full Speed PHY
+		 * 1 - UTMI+ (default)
+		 * 2 - ULPI
+		 */
+	int32_t phy_type;
+
+	/**
+	 * Specifies the UTMI+ Data Width. This parameter is
+	 * applicable for a PHY_TYPE of UTMI+ or ULPI. (For a ULPI
+	 * PHY_TYPE, this parameter indicates the data width between
+	 * the MAC and the ULPI Wrapper.) Also, this parameter is
+	 * applicable only if the OTG_HSPHY_WIDTH cC parameter was set
+	 * to "8 and 16 bits", meaning that the core has been
+	 * configured to work at either data path width.
+	 *
+	 * 8 or 16 bits (default 16)
+	 */
+	int32_t phy_utmi_width;
+
+	/**
+	 * Specifies whether the ULPI operates at double or single
+	 * data rate. This parameter is only applicable if PHY_TYPE is
+	 * ULPI.
+	 *
+	 * 0 - single data rate ULPI interface with 8 bit wide data
+	 * bus (default)
+	 * 1 - double data rate ULPI interface with 4 bit wide data
+	 * bus
+	 */
+	int32_t phy_ulpi_ddr;
+
+	/**
+	 * Specifies whether to use the internal or external supply to
+	 * drive the vbus with a ULPI phy.
+	 */
+	int32_t phy_ulpi_ext_vbus;
+
+	/**
+	 * Specifies whether to use the I2Cinterface for full speed PHY. This
+	 * parameter is only applicable if PHY_TYPE is FS.
+	 * 0 - No (default)
+	 * 1 - Yes
+	 */
+	int32_t i2c_enable;
+
+	int32_t ulpi_fs_ls;
+
+	int32_t ts_dline;
+
+	/**
+	 * Specifies whether dedicated transmit FIFOs are
+	 * enabled for non periodic IN endpoints in device mode
+	 * 0 - No
+	 * 1 - Yes
+	 */
+	int32_t en_multiple_tx_fifo;
+
+	/** Number of 4-byte words in each of the Tx FIFOs in device
+	 * mode when dynamic FIFO sizing is enabled.
+	 * 4 to 768 (default 256)
+	 */
+	uint32_t dev_tx_fifo_size[MAX_TX_FIFOS];
+
+	/** Thresholding enable flag-
+	 * bit 0 - enable non-ISO Tx thresholding
+	 * bit 1 - enable ISO Tx thresholding
+	 * bit 2 - enable Rx thresholding
+	 */
+	uint32_t thr_ctl;
+
+	/** Thresholding length for Tx
+	 *	FIFOs in 32 bit DWORDs
+	 */
+	uint32_t tx_thr_length;
+
+	/** Thresholding length for Rx
+	 *	FIFOs in 32 bit DWORDs
+	 */
+	uint32_t rx_thr_length;
+
+	/**
+	 * Specifies whether LPM (Link Power Management) support is enabled
+	 */
+	int32_t lpm_enable;
+
+	/** Per Transfer Interrupt
+	 *	mode enable flag
+	 * 1 - Enabled
+	 * 0 - Disabled
+	 */
+	int32_t pti_enable;
+
+	/** Multi Processor Interrupt
+	 *	mode enable flag
+	 * 1 - Enabled
+	 * 0 - Disabled
+	 */
+	int32_t mpi_enable;
+
+	/** IS_USB Capability
+	 * 1 - Enabled
+	 * 0 - Disabled
+	 */
+	int32_t ic_usb_cap;
+
+	/** AHB Threshold Ratio
+	 * 2'b00 AHB Threshold = 	MAC Threshold
+	 * 2'b01 AHB Threshold = 1/2 	MAC Threshold
+	 * 2'b10 AHB Threshold = 1/4	MAC Threshold
+	 * 2'b11 AHB Threshold = 1/8	MAC Threshold
+	 */
+	int32_t ahb_thr_ratio;
+
+	/** ADP Support
+	 * 1 - Enabled
+	 * 0 - Disabled
+	 */
+	int32_t adp_supp_enable;
+
+	/** HFIR Reload Control
+	 * 0 - The HFIR cannot be reloaded dynamically.
+	 * 1 - Allow dynamic reloading of the HFIR register during runtime.
+	 */
+	int32_t reload_ctl;
+
+	/** DCFG: Enable device Out NAK
+	 * 0 - The core does not set NAK after Bulk Out transfer complete.
+	 * 1 - The core sets NAK after Bulk OUT transfer complete.
+	 */
+	int32_t dev_out_nak;
+
+	/** DCFG: Enable Continue on BNA
+	 * After receiving BNA interrupt the core disables the endpoint,when the
+	 * endpoint is re-enabled by the application the core starts processing
+	 * 0 - from the DOEPDMA descriptor
+	 * 1 - from the descriptor which received the BNA.
+	 */
+	int32_t cont_on_bna;
+
+	/** GAHBCFG: AHB Single Support
+	 * This bit when programmed supports SINGLE transfers for remainder
+	 * data in a transfer for DMA mode of operation.
+	 * 0 - in this case the remainder data will be sent using INCR burst size.
+	 * 1 - in this case the remainder data will be sent using SINGLE burst size.
+	 */
+	int32_t ahb_single;
+
+	/** Core Power down mode
+	 * 0 - No Power Down is enabled
+	 * 1 - Reserved
+	 * 2 - Complete Power Down (Hibernation)
+	 */
+	int32_t power_down;
+
+	/** OTG revision supported
+	 * 0 - OTG 1.3 revision
+	 * 1 - OTG 2.0 revision
+	 */
+	int32_t otg_ver;
+
+} dwc_otg_core_params_t;
+
+#ifdef DEBUG
+struct dwc_otg_core_if;
+typedef struct hc_xfer_info {
+	struct dwc_otg_core_if *core_if;
+	dwc_hc_t *hc;
+} hc_xfer_info_t;
+#endif
+
+typedef struct ep_xfer_info {
+	struct dwc_otg_core_if *core_if;
+	dwc_ep_t *ep;
+	uint8_t state;
+} ep_xfer_info_t;
+/*
+ * Device States
+ */
+typedef enum dwc_otg_lx_state {
+	/** On state */
+	DWC_OTG_L0,
+	/** LPM sleep state*/
+	DWC_OTG_L1,
+	/** USB suspend state*/
+	DWC_OTG_L2,
+	/** Off state*/
+	DWC_OTG_L3
+} dwc_otg_lx_state_e;
+
+struct dwc_otg_global_regs_backup {
+	uint32_t gotgctl_local;
+	uint32_t gintmsk_local;
+	uint32_t gahbcfg_local;
+	uint32_t gusbcfg_local;
+	uint32_t grxfsiz_local;
+	uint32_t gnptxfsiz_local;
+#ifdef CONFIG_USB_DWC_OTG_LPM
+	uint32_t glpmcfg_local;
+#endif
+	uint32_t gi2cctl_local;
+	uint32_t hptxfsiz_local;
+	uint32_t pcgcctl_local;
+	uint32_t gdfifocfg_local;
+	uint32_t dtxfsiz_local[MAX_EPS_CHANNELS];
+	uint32_t gpwrdn_local;
+	uint32_t xhib_pcgcctl;
+	uint32_t xhib_gpwrdn;
+};
+
+struct dwc_otg_host_regs_backup {
+	uint32_t hcfg_local;
+	uint32_t haintmsk_local;
+	uint32_t hcintmsk_local[MAX_EPS_CHANNELS];
+	uint32_t hprt0_local;
+	uint32_t hfir_local;
+};
+
+struct dwc_otg_dev_regs_backup {
+	uint32_t dcfg;
+	uint32_t dctl;
+	uint32_t daintmsk;
+	uint32_t diepmsk;
+	uint32_t doepmsk;
+	uint32_t diepctl[MAX_EPS_CHANNELS];
+	uint32_t dieptsiz[MAX_EPS_CHANNELS];
+	uint32_t diepdma[MAX_EPS_CHANNELS];
+};
+/**
+ * The <code>dwc_otg_core_if</code> structure contains information needed to manage
+ * the DWC_otg controller acting in either host or device mode. It
+ * represents the programming view of the controller as a whole.
+ */
+struct dwc_otg_core_if {
+	/** Parameters that define how the core should be configured.*/
+	dwc_otg_core_params_t *core_params;
+
+	/** Core Global registers starting at offset 000h. */
+	dwc_otg_core_global_regs_t *core_global_regs;
+
+	/** Device-specific information */
+	dwc_otg_dev_if_t *dev_if;
+	/** Host-specific information */
+	dwc_otg_host_if_t *host_if;
+
+	/** Value from SNPSID register */
+	uint32_t snpsid;
+
+	/*
+	 * Set to 1 if the core PHY interface bits in USBCFG have been
+	 * initialized.
+	 */
+	uint8_t phy_init_done;
+
+	/*
+	 * SRP Success flag, set by srp success interrupt in FS I2C mode
+	 */
+	uint8_t srp_success;
+	uint8_t srp_timer_started;
+	/** Timer for SRP. If it expires before SRP is successful
+	 * clear the SRP. */
+	dwc_timer_t *srp_timer;
+
+#ifdef DWC_DEV_SRPCAP
+	/* This timer is needed to power on the hibernated host core if SRP is not
+	 * initiated on connected SRP capable device for limited period of time
+	 */
+	uint8_t pwron_timer_started;
+	dwc_timer_t *pwron_timer;
+#endif
+	/* Common configuration information */
+	/** Power and Clock Gating Control Register */
+	volatile uint32_t *pcgcctl;
+#define DWC_OTG_PCGCCTL_OFFSET 0xE00
+
+	/** Push/pop addresses for endpoints or host channels.*/
+	uint32_t *data_fifo[MAX_EPS_CHANNELS];
+#define DWC_OTG_DATA_FIFO_OFFSET 0x1000
+#define DWC_OTG_DATA_FIFO_SIZE 0x1000
+
+	/** Total RAM for FIFOs (Bytes) */
+	uint16_t total_fifo_size;
+	/** Size of Rx FIFO (Bytes) */
+	uint16_t rx_fifo_size;
+	/** Size of Non-periodic Tx FIFO (Bytes) */
+	uint16_t nperio_tx_fifo_size;
+
+	/** 1 if DMA is enabled, 0 otherwise. */
+	uint8_t dma_enable;
+
+	/** 1 if DMA descriptor is enabled, 0 otherwise. */
+	uint8_t dma_desc_enable;
+
+	/** 1 if PTI Enhancement mode is enabled, 0 otherwise. */
+	uint8_t pti_enh_enable;
+
+	/** 1 if MPI Enhancement mode is enabled, 0 otherwise. */
+	uint8_t multiproc_int_enable;
+
+	/** 1 if dedicated Tx FIFOs are enabled, 0 otherwise. */
+	uint8_t en_multiple_tx_fifo;
+
+	/** Set to 1 if multiple packets of a high-bandwidth transfer is in
+	 * process of being queued */
+	uint8_t queuing_high_bandwidth;
+
+	/** Hardware Configuration -- stored here for convenience.*/
+	hwcfg1_data_t hwcfg1;
+	hwcfg2_data_t hwcfg2;
+	hwcfg3_data_t hwcfg3;
+	hwcfg4_data_t hwcfg4;
+	fifosize_data_t hptxfsiz;
+
+	/** Host and Device Configuration -- stored here for convenience.*/
+	hcfg_data_t hcfg;
+	dcfg_data_t dcfg;
+
+	/** The operational State, during transations
+	 * (a_host>>a_peripherial and b_device=>b_host) this may not
+	 * match the core but allows the software to determine
+	 * transitions.
+	 */
+	uint8_t op_state;
+
+	/**
+	 * Set to 1 if the HCD needs to be restarted on a session request
+	 * interrupt. This is required if no connector ID status change has
+	 * occurred since the HCD was last disconnected.
+	 */
+	uint8_t restart_hcd_on_session_req;
+
+	/** HCD callbacks */
+	/** A-Device is a_host */
+#define A_HOST		(1)
+	/** A-Device is a_suspend */
+#define A_SUSPEND	(2)
+	/** A-Device is a_peripherial */
+#define A_PERIPHERAL	(3)
+	/** B-Device is operating as a Peripheral. */
+#define B_PERIPHERAL	(4)
+	/** B-Device is operating as a Host. */
+#define B_HOST		(5)
+
+	/** HCD callbacks */
+	struct dwc_otg_cil_callbacks *hcd_cb;
+	/** PCD callbacks */
+	struct dwc_otg_cil_callbacks *pcd_cb;
+
+	/** Device mode Periodic Tx FIFO Mask */
+	uint32_t p_tx_msk;
+	/** Device mode Periodic Tx FIFO Mask */
+	uint32_t tx_msk;
+
+	/** Workqueue object used for handling several interrupts */
+	dwc_workq_t *wq_otg;
+
+	/** Timer object used for handling "Wakeup Detected" Interrupt */
+	dwc_timer_t *wkp_timer;
+	/** This arrays used for debug purposes for DEV OUT NAK enhancement */
+	uint32_t start_doeptsiz_val[MAX_EPS_CHANNELS];
+	ep_xfer_info_t ep_xfer_info[MAX_EPS_CHANNELS];
+	dwc_timer_t *ep_xfer_timer[MAX_EPS_CHANNELS];
+#ifdef DEBUG
+	uint32_t start_hcchar_val[MAX_EPS_CHANNELS];
+
+	hc_xfer_info_t hc_xfer_info[MAX_EPS_CHANNELS];
+	dwc_timer_t *hc_xfer_timer[MAX_EPS_CHANNELS];
+
+	uint32_t hfnum_7_samples;
+	uint64_t hfnum_7_frrem_accum;
+	uint32_t hfnum_0_samples;
+	uint64_t hfnum_0_frrem_accum;
+	uint32_t hfnum_other_samples;
+	uint64_t hfnum_other_frrem_accum;
+#endif
+
+#ifdef DWC_UTE_CFI
+	uint16_t pwron_rxfsiz;
+	uint16_t pwron_gnptxfsiz;
+	uint16_t pwron_txfsiz[15];
+
+	uint16_t init_rxfsiz;
+	uint16_t init_gnptxfsiz;
+	uint16_t init_txfsiz[15];
+#endif
+
+	/** Lx state of device */
+	dwc_otg_lx_state_e lx_state;
+
+	/** Saved Core Global registers */
+	struct dwc_otg_global_regs_backup *gr_backup;
+	/** Saved Host registers */
+	struct dwc_otg_host_regs_backup *hr_backup;
+	/** Saved Device registers */
+	struct dwc_otg_dev_regs_backup *dr_backup;
+
+	/** Power Down Enable */
+	uint32_t power_down;
+
+	/** ADP support Enable */
+	uint32_t adp_enable;
+
+	/** ADP structure object */
+	dwc_otg_adp_t adp;
+
+	/** hibernation/suspend flag */
+	int hibernation_suspend;
+
+	/** Device mode extended hibernation flag */
+	int xhib;
+
+	/** OTG revision supported */
+	uint32_t otg_ver;
+
+	/** OTG status flag used for HNP polling */
+	uint8_t otg_sts;
+
+	/** Pointer to either hcd->lock or pcd->lock */
+	dwc_spinlock_t *lock;
+
+	/** Start predict NextEP based on Learning Queue if equal 1,
+	 * also used as counter of disabled NP IN EP's */
+	uint8_t start_predict;
+
+	/** NextEp sequence, including EP0: nextep_seq[] = EP if non-periodic and
+	 * active, 0xff otherwise */
+	uint8_t nextep_seq[MAX_EPS_CHANNELS];
+
+	/** Index of fisrt EP in nextep_seq array which should be re-enabled **/
+	uint8_t first_in_nextep_seq;
+
+	/** Frame number while entering to ISR - needed for ISOCs **/
+	uint32_t frame_num;
+
+};
+
+#ifdef DEBUG
+/*
+ * This function is called when transfer is timed out.
+ */
+extern void hc_xfer_timeout(void *ptr);
+#endif
+
+/*
+ * This function is called when transfer is timed out on endpoint.
+ */
+extern void ep_xfer_timeout(void *ptr);
+
+/*
+ * The following functions are functions for works
+ * using during handling some interrupts
+ */
+extern void w_conn_id_status_change(void *p);
+
+extern void w_wakeup_detected(void *p);
+
+/** Saves global register values into system memory. */
+extern int dwc_otg_save_global_regs(dwc_otg_core_if_t * core_if);
+/** Saves device register values into system memory. */
+extern int dwc_otg_save_dev_regs(dwc_otg_core_if_t * core_if);
+/** Saves host register values into system memory. */
+extern int dwc_otg_save_host_regs(dwc_otg_core_if_t * core_if);
+/** Restore global register values. */
+extern int dwc_otg_restore_global_regs(dwc_otg_core_if_t * core_if);
+/** Restore host register values. */
+extern int dwc_otg_restore_host_regs(dwc_otg_core_if_t * core_if, int reset);
+/** Restore device register values. */
+extern int dwc_otg_restore_dev_regs(dwc_otg_core_if_t * core_if,
+				    int rem_wakeup);
+extern int restore_lpm_i2c_regs(dwc_otg_core_if_t * core_if);
+extern int restore_essential_regs(dwc_otg_core_if_t * core_if, int rmode,
+				  int is_host);
+
+extern int dwc_otg_host_hibernation_restore(dwc_otg_core_if_t * core_if,
+					    int restore_mode, int reset);
+extern int dwc_otg_device_hibernation_restore(dwc_otg_core_if_t * core_if,
+					      int rem_wakeup, int reset);
+
+/*
+ * The following functions support initialization of the CIL driver component
+ * and the DWC_otg controller.
+ */
+extern void dwc_otg_core_host_init(dwc_otg_core_if_t * _core_if);
+extern void dwc_otg_core_dev_init(dwc_otg_core_if_t * _core_if);
+
+/** @name Device CIL Functions
+ * The following functions support managing the DWC_otg controller in device
+ * mode.
+ */
+/**@{*/
+extern void dwc_otg_wakeup(dwc_otg_core_if_t * _core_if);
+extern void dwc_otg_read_setup_packet(dwc_otg_core_if_t * _core_if,
+				      uint32_t * _dest);
+extern uint32_t dwc_otg_get_frame_number(dwc_otg_core_if_t * _core_if);
+extern void dwc_otg_ep0_activate(dwc_otg_core_if_t * _core_if, dwc_ep_t * _ep);
+extern void dwc_otg_ep_activate(dwc_otg_core_if_t * _core_if, dwc_ep_t * _ep);
+extern void dwc_otg_ep_deactivate(dwc_otg_core_if_t * _core_if, dwc_ep_t * _ep);
+extern void dwc_otg_ep_start_transfer(dwc_otg_core_if_t * _core_if,
+				      dwc_ep_t * _ep);
+extern void dwc_otg_ep_start_zl_transfer(dwc_otg_core_if_t * _core_if,
+					 dwc_ep_t * _ep);
+extern void dwc_otg_ep0_start_transfer(dwc_otg_core_if_t * _core_if,
+				       dwc_ep_t * _ep);
+extern void dwc_otg_ep0_continue_transfer(dwc_otg_core_if_t * _core_if,
+					  dwc_ep_t * _ep);
+extern void dwc_otg_ep_write_packet(dwc_otg_core_if_t * _core_if,
+				    dwc_ep_t * _ep, int _dma);
+extern void dwc_otg_ep_set_stall(dwc_otg_core_if_t * _core_if, dwc_ep_t * _ep);
+extern void dwc_otg_ep_clear_stall(dwc_otg_core_if_t * _core_if,
+				   dwc_ep_t * _ep);
+extern void dwc_otg_enable_device_interrupts(dwc_otg_core_if_t * _core_if);
+
+#ifdef DWC_EN_ISOC
+extern void dwc_otg_iso_ep_start_frm_transfer(dwc_otg_core_if_t * core_if,
+					      dwc_ep_t * ep);
+extern void dwc_otg_iso_ep_start_buf_transfer(dwc_otg_core_if_t * core_if,
+					      dwc_ep_t * ep);
+#endif /* DWC_EN_ISOC */
+/**@}*/
+
+/** @name Host CIL Functions
+ * The following functions support managing the DWC_otg controller in host
+ * mode.
+ */
+/**@{*/
+extern void dwc_otg_hc_init(dwc_otg_core_if_t * _core_if, dwc_hc_t * _hc);
+extern void dwc_otg_hc_halt(dwc_otg_core_if_t * _core_if,
+			    dwc_hc_t * _hc, dwc_otg_halt_status_e _halt_status);
+extern void dwc_otg_hc_cleanup(dwc_otg_core_if_t * _core_if, dwc_hc_t * _hc);
+extern void dwc_otg_hc_start_transfer(dwc_otg_core_if_t * _core_if,
+				      dwc_hc_t * _hc);
+extern int dwc_otg_hc_continue_transfer(dwc_otg_core_if_t * _core_if,
+					dwc_hc_t * _hc);
+extern void dwc_otg_hc_do_ping(dwc_otg_core_if_t * _core_if, dwc_hc_t * _hc);
+extern void dwc_otg_hc_write_packet(dwc_otg_core_if_t * _core_if,
+				    dwc_hc_t * _hc);
+extern void dwc_otg_enable_host_interrupts(dwc_otg_core_if_t * _core_if);
+extern void dwc_otg_disable_host_interrupts(dwc_otg_core_if_t * _core_if);
+
+extern void dwc_otg_hc_start_transfer_ddma(dwc_otg_core_if_t * core_if,
+					   dwc_hc_t * hc);
+
+extern uint32_t calc_frame_interval(dwc_otg_core_if_t * core_if);
+
+/* Macro used to clear one channel interrupt */
+#define clear_hc_int(_hc_regs_, _intr_) \
+do { \
+	hcint_data_t hcint_clear = {.d32 = 0}; \
+	hcint_clear.b._intr_ = 1; \
+	DWC_WRITE_REG32(&(_hc_regs_)->hcint, hcint_clear.d32); \
+} while (0)
+
+/*
+ * Macro used to disable one channel interrupt. Channel interrupts are
+ * disabled when the channel is halted or released by the interrupt handler.
+ * There is no need to handle further interrupts of that type until the
+ * channel is re-assigned. In fact, subsequent handling may cause crashes
+ * because the channel structures are cleaned up when the channel is released.
+ */
+#define disable_hc_int(_hc_regs_, _intr_) \
+do { \
+	hcintmsk_data_t hcintmsk = {.d32 = 0}; \
+	hcintmsk.b._intr_ = 1; \
+	DWC_MODIFY_REG32(&(_hc_regs_)->hcintmsk, hcintmsk.d32, 0); \
+} while (0)
+
+/**
+ * This function Reads HPRT0 in preparation to modify. It keeps the
+ * WC bits 0 so that if they are read as 1, they won't clear when you
+ * write it back
+ */
+static inline uint32_t dwc_otg_read_hprt0(dwc_otg_core_if_t * _core_if)
+{
+	hprt0_data_t hprt0;
+	hprt0.d32 = DWC_READ_REG32(_core_if->host_if->hprt0);
+	hprt0.b.prtena = 0;
+	hprt0.b.prtconndet = 0;
+	hprt0.b.prtenchng = 0;
+	hprt0.b.prtovrcurrchng = 0;
+	return hprt0.d32;
+}
+
+/**@}*/
+
+/** @name Common CIL Functions
+ * The following functions support managing the DWC_otg controller in either
+ * device or host mode.
+ */
+/**@{*/
+
+extern void dwc_otg_read_packet(dwc_otg_core_if_t * core_if,
+				uint8_t * dest, uint16_t bytes);
+
+extern void dwc_otg_flush_tx_fifo(dwc_otg_core_if_t * _core_if, const int _num);
+extern void dwc_otg_flush_rx_fifo(dwc_otg_core_if_t * _core_if);
+extern void dwc_otg_core_reset(dwc_otg_core_if_t * _core_if);
+
+/**
+ * This function returns the Core Interrupt register.
+ */
+static inline uint32_t dwc_otg_read_core_intr(dwc_otg_core_if_t * core_if)
+{
+	return (DWC_READ_REG32(&core_if->core_global_regs->gintsts) &
+		DWC_READ_REG32(&core_if->core_global_regs->gintmsk));
+}
+
+/**
+ * This function returns the OTG Interrupt register.
+ */
+static inline uint32_t dwc_otg_read_otg_intr(dwc_otg_core_if_t * core_if)
+{
+	return (DWC_READ_REG32(&core_if->core_global_regs->gotgint));
+}
+
+/**
+ * This function reads the Device All Endpoints Interrupt register and
+ * returns the IN endpoint interrupt bits.
+ */
+static inline uint32_t dwc_otg_read_dev_all_in_ep_intr(dwc_otg_core_if_t *
+						       core_if)
+{
+
+	uint32_t v;
+
+	if (core_if->multiproc_int_enable) {
+		v = DWC_READ_REG32(&core_if->dev_if->
+				   dev_global_regs->deachint) &
+		    DWC_READ_REG32(&core_if->
+				   dev_if->dev_global_regs->deachintmsk);
+	} else {
+		v = DWC_READ_REG32(&core_if->dev_if->dev_global_regs->daint) &
+		    DWC_READ_REG32(&core_if->dev_if->dev_global_regs->daintmsk);
+	}
+	return (v & 0xffff);
+}
+
+/**
+ * This function reads the Device All Endpoints Interrupt register and
+ * returns the OUT endpoint interrupt bits.
+ */
+static inline uint32_t dwc_otg_read_dev_all_out_ep_intr(dwc_otg_core_if_t *
+							core_if)
+{
+	uint32_t v;
+
+	if (core_if->multiproc_int_enable) {
+		v = DWC_READ_REG32(&core_if->dev_if->
+				   dev_global_regs->deachint) &
+		    DWC_READ_REG32(&core_if->
+				   dev_if->dev_global_regs->deachintmsk);
+	} else {
+		v = DWC_READ_REG32(&core_if->dev_if->dev_global_regs->daint) &
+		    DWC_READ_REG32(&core_if->dev_if->dev_global_regs->daintmsk);
+	}
+
+	return ((v & 0xffff0000) >> 16);
+}
+
+/**
+ * This function returns the Device IN EP Interrupt register
+ */
+static inline uint32_t dwc_otg_read_dev_in_ep_intr(dwc_otg_core_if_t * core_if,
+						   dwc_ep_t * ep)
+{
+	dwc_otg_dev_if_t *dev_if = core_if->dev_if;
+	uint32_t v, msk, emp;
+
+	if (core_if->multiproc_int_enable) {
+		msk =
+		    DWC_READ_REG32(&dev_if->
+				   dev_global_regs->diepeachintmsk[ep->num]);
+		emp =
+		    DWC_READ_REG32(&dev_if->
+				   dev_global_regs->dtknqr4_fifoemptymsk);
+		msk |= ((emp >> ep->num) & 0x1) << 7;
+		v = DWC_READ_REG32(&dev_if->in_ep_regs[ep->num]->diepint) & msk;
+	} else {
+		msk = DWC_READ_REG32(&dev_if->dev_global_regs->diepmsk);
+		emp =
+		    DWC_READ_REG32(&dev_if->
+				   dev_global_regs->dtknqr4_fifoemptymsk);
+		msk |= ((emp >> ep->num) & 0x1) << 7;
+		v = DWC_READ_REG32(&dev_if->in_ep_regs[ep->num]->diepint) & msk;
+	}
+
+	return v;
+}
+
+/**
+ * This function returns the Device OUT EP Interrupt register
+ */
+static inline uint32_t dwc_otg_read_dev_out_ep_intr(dwc_otg_core_if_t *
+						    _core_if, dwc_ep_t * _ep)
+{
+	dwc_otg_dev_if_t *dev_if = _core_if->dev_if;
+	uint32_t v;
+	doepmsk_data_t msk = {.d32 = 0 };
+
+	if (_core_if->multiproc_int_enable) {
+		msk.d32 =
+		    DWC_READ_REG32(&dev_if->
+				   dev_global_regs->doepeachintmsk[_ep->num]);
+		if (_core_if->pti_enh_enable) {
+			msk.b.pktdrpsts = 1;
+		}
+		v = DWC_READ_REG32(&dev_if->
+				   out_ep_regs[_ep->num]->doepint) & msk.d32;
+	} else {
+		msk.d32 = DWC_READ_REG32(&dev_if->dev_global_regs->doepmsk);
+		if (_core_if->pti_enh_enable) {
+			msk.b.pktdrpsts = 1;
+		}
+		v = DWC_READ_REG32(&dev_if->
+				   out_ep_regs[_ep->num]->doepint) & msk.d32;
+	}
+	return v;
+}
+
+/**
+ * This function returns the Host All Channel Interrupt register
+ */
+static inline uint32_t dwc_otg_read_host_all_channels_intr(dwc_otg_core_if_t *
+							   _core_if)
+{
+	return (DWC_READ_REG32(&_core_if->host_if->host_global_regs->haint));
+}
+
+static inline uint32_t dwc_otg_read_host_channel_intr(dwc_otg_core_if_t *
+						      _core_if, dwc_hc_t * _hc)
+{
+	return (DWC_READ_REG32
+		(&_core_if->host_if->hc_regs[_hc->hc_num]->hcint));
+}
+
+/**
+ * This function returns the mode of the operation, host or device.
+ *
+ * @return 0 - Device Mode, 1 - Host Mode
+ */
+static inline uint32_t dwc_otg_mode(dwc_otg_core_if_t * _core_if)
+{
+	return (DWC_READ_REG32(&_core_if->core_global_regs->gintsts) & 0x1);
+}
+
+/**@}*/
+
+/**
+ * DWC_otg CIL callback structure. This structure allows the HCD and
+ * PCD to register functions used for starting and stopping the PCD
+ * and HCD for role change on for a DRD.
+ */
+typedef struct dwc_otg_cil_callbacks {
+	/** Start function for role change */
+	int (*start) (void *_p);
+	/** Stop Function for role change */
+	int (*stop) (void *_p);
+	/** Disconnect Function for role change */
+	int (*disconnect) (void *_p);
+	/** Resume/Remote wakeup Function */
+	int (*resume_wakeup) (void *_p);
+	/** Suspend function */
+	int (*suspend) (void *_p);
+	/** Session Start (SRP) */
+	int (*session_start) (void *_p);
+#ifdef CONFIG_USB_DWC_OTG_LPM
+	/** Sleep (switch to L0 state) */
+	int (*sleep) (void *_p);
+#endif
+	/** Pointer passed to start() and stop() */
+	void *p;
+} dwc_otg_cil_callbacks_t;
+
+extern void dwc_otg_cil_register_pcd_callbacks(dwc_otg_core_if_t * _core_if,
+					       dwc_otg_cil_callbacks_t * _cb,
+					       void *_p);
+extern void dwc_otg_cil_register_hcd_callbacks(dwc_otg_core_if_t * _core_if,
+					       dwc_otg_cil_callbacks_t * _cb,
+					       void *_p);
+
+void dwc_otg_initiate_srp(dwc_otg_core_if_t * core_if);
+
+//////////////////////////////////////////////////////////////////////
+/** Start the HCD.  Helper function for using the HCD callbacks.
+ *
+ * @param core_if Programming view of DWC_otg controller.
+ */
+static inline void cil_hcd_start(dwc_otg_core_if_t * core_if)
+{
+	if (core_if->hcd_cb && core_if->hcd_cb->start) {
+		core_if->hcd_cb->start(core_if->hcd_cb->p);
+	}
+}
+
+/** Stop the HCD.  Helper function for using the HCD callbacks.
+ *
+ * @param core_if Programming view of DWC_otg controller.
+ */
+static inline void cil_hcd_stop(dwc_otg_core_if_t * core_if)
+{
+	if (core_if->hcd_cb && core_if->hcd_cb->stop) {
+		core_if->hcd_cb->stop(core_if->hcd_cb->p);
+	}
+}
+
+/** Disconnect the HCD.  Helper function for using the HCD callbacks.
+ *
+ * @param core_if Programming view of DWC_otg controller.
+ */
+static inline void cil_hcd_disconnect(dwc_otg_core_if_t * core_if)
+{
+	if (core_if->hcd_cb && core_if->hcd_cb->disconnect) {
+		core_if->hcd_cb->disconnect(core_if->hcd_cb->p);
+	}
+}
+
+/** Inform the HCD the a New Session has begun.  Helper function for
+ * using the HCD callbacks.
+ *
+ * @param core_if Programming view of DWC_otg controller.
+ */
+static inline void cil_hcd_session_start(dwc_otg_core_if_t * core_if)
+{
+	if (core_if->hcd_cb && core_if->hcd_cb->session_start) {
+		core_if->hcd_cb->session_start(core_if->hcd_cb->p);
+	}
+}
+
+#ifdef CONFIG_USB_DWC_OTG_LPM
+/**
+ * Inform the HCD about LPM sleep.
+ * Helper function for using the HCD callbacks.
+ *
+ * @param core_if Programming view of DWC_otg controller.
+ */
+static inline void cil_hcd_sleep(dwc_otg_core_if_t * core_if)
+{
+	if (core_if->hcd_cb && core_if->hcd_cb->sleep) {
+		core_if->hcd_cb->sleep(core_if->hcd_cb->p);
+	}
+}
+#endif
+
+/** Resume the HCD.  Helper function for using the HCD callbacks.
+ *
+ * @param core_if Programming view of DWC_otg controller.
+ */
+static inline void cil_hcd_resume(dwc_otg_core_if_t * core_if)
+{
+	if (core_if->hcd_cb && core_if->hcd_cb->resume_wakeup) {
+		core_if->hcd_cb->resume_wakeup(core_if->hcd_cb->p);
+	}
+}
+
+/** Start the PCD.  Helper function for using the PCD callbacks.
+ *
+ * @param core_if Programming view of DWC_otg controller.
+ */
+static inline void cil_pcd_start(dwc_otg_core_if_t * core_if)
+{
+	if (core_if->pcd_cb && core_if->pcd_cb->start) {
+		core_if->pcd_cb->start(core_if->pcd_cb->p);
+	}
+}
+
+/** Stop the PCD.  Helper function for using the PCD callbacks.
+ *
+ * @param core_if Programming view of DWC_otg controller.
+ */
+static inline void cil_pcd_stop(dwc_otg_core_if_t * core_if)
+{
+	if (core_if->pcd_cb && core_if->pcd_cb->stop) {
+		core_if->pcd_cb->stop(core_if->pcd_cb->p);
+	}
+}
+
+/** Suspend the PCD.  Helper function for using the PCD callbacks.
+ *
+ * @param core_if Programming view of DWC_otg controller.
+ */
+static inline void cil_pcd_suspend(dwc_otg_core_if_t * core_if)
+{
+	if (core_if->pcd_cb && core_if->pcd_cb->suspend) {
+		core_if->pcd_cb->suspend(core_if->pcd_cb->p);
+	}
+}
+
+/** Resume the PCD.  Helper function for using the PCD callbacks.
+ *
+ * @param core_if Programming view of DWC_otg controller.
+ */
+static inline void cil_pcd_resume(dwc_otg_core_if_t * core_if)
+{
+	if (core_if->pcd_cb && core_if->pcd_cb->resume_wakeup) {
+		core_if->pcd_cb->resume_wakeup(core_if->pcd_cb->p);
+	}
+}
+
+//////////////////////////////////////////////////////////////////////
+
+#endif
diff -Nur --no-dereference linux-5.10.52-orig/drivers/usb/host/dwc_otg/dwc_otg_cil_intr.c linux-5.10.52-v7l+/drivers/usb/host/dwc_otg/dwc_otg_cil_intr.c
--- linux-5.10.52-orig/drivers/usb/host/dwc_otg/dwc_otg_cil_intr.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/usb/host/dwc_otg/dwc_otg_cil_intr.c	2021-07-25 16:46:28.677910113 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/* ==========================================================================
+ * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_cil_intr.c $
+ * $Revision: #32 $
+ * $Date: 2012/08/10 $
+ * $Change: 2047372 $
+ *
+ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter,
+ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless
+ * otherwise expressly agreed to in writing between Synopsys and you.
+ *
+ * The Software IS NOT an item of Licensed Software or Licensed Product under
+ * any End User Software License Agreement or Agreement for Licensed Product
+ * with Synopsys or any supplement thereto. You are permitted to use and
+ * redistribute this Software in source and binary forms, with or without
+ * modification, provided that redistributions of source code must retain this
+ * notice. You may not view, use, disclose, copy or distribute this file or
+ * any information contained herein except pursuant to this license grant from
+ * Synopsys. If you do not agree with this notice, including the disclaimer
+ * below, then you are not authorized to use the Software.
+ *
+ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * ========================================================================== */
+
+/** @file
+ *
+ * The Core Interface Layer provides basic services for accessing and
+ * managing the DWC_otg hardware. These services are used by both the
+ * Host Controller Driver and the Peripheral Controller Driver.
+ *
+ * This file contains the Common Interrupt handlers.
+ */
+#include "dwc_os.h"
+#include "dwc_otg_regs.h"
+#include "dwc_otg_cil.h"
+#include "dwc_otg_driver.h"
+#include "dwc_otg_pcd.h"
+#include "dwc_otg_hcd.h"
+
+#ifdef DEBUG
+inline const char *op_state_str(dwc_otg_core_if_t * core_if)
+{
+	return (core_if->op_state == A_HOST ? "a_host" :
+		(core_if->op_state == A_SUSPEND ? "a_suspend" :
+		 (core_if->op_state == A_PERIPHERAL ? "a_peripheral" :
+		  (core_if->op_state == B_PERIPHERAL ? "b_peripheral" :
+		   (core_if->op_state == B_HOST ? "b_host" : "unknown")))));
+}
+#endif
+
+/** This function will log a debug message
+ *
+ * @param core_if Programming view of DWC_otg controller.
+ */
+int32_t dwc_otg_handle_mode_mismatch_intr(dwc_otg_core_if_t * core_if)
+{
+	gintsts_data_t gintsts;
+	DWC_WARN("Mode Mismatch Interrupt: currently in %s mode\n",
+		 dwc_otg_mode(core_if) ? "Host" : "Device");
+
+	/* Clear interrupt */
+	gintsts.d32 = 0;
+	gintsts.b.modemismatch = 1;
+	DWC_WRITE_REG32(&core_if->core_global_regs->gintsts, gintsts.d32);
+	return 1;
+}
+
+/**
+ * This function handles the OTG Interrupts. It reads the OTG
+ * Interrupt Register (GOTGINT) to determine what interrupt has
+ * occurred.
+ *
+ * @param core_if Programming view of DWC_otg controller.
+ */
+int32_t dwc_otg_handle_otg_intr(dwc_otg_core_if_t * core_if)
+{
+	dwc_otg_core_global_regs_t *global_regs = core_if->core_global_regs;
+	gotgint_data_t gotgint;
+	gotgctl_data_t gotgctl;
+	gintmsk_data_t gintmsk;
+	gpwrdn_data_t gpwrdn;
+
+	gotgint.d32 = DWC_READ_REG32(&global_regs->gotgint);
+	gotgctl.d32 = DWC_READ_REG32(&global_regs->gotgctl);
+	DWC_DEBUGPL(DBG_CIL, "++OTG Interrupt gotgint=%0x [%s]\n", gotgint.d32,
+		    op_state_str(core_if));
+
+	if (gotgint.b.sesenddet) {
+		DWC_DEBUGPL(DBG_ANY, " ++OTG Interrupt: "
+			    "Session End Detected++ (%s)\n",
+			    op_state_str(core_if));
+		gotgctl.d32 = DWC_READ_REG32(&global_regs->gotgctl);
+
+		if (core_if->op_state == B_HOST) {
+			cil_pcd_start(core_if);
+			core_if->op_state = B_PERIPHERAL;
+		} else {
+			/* If not B_HOST and Device HNP still set. HNP
+			 * Did not succeed!*/
+			if (gotgctl.b.devhnpen) {
+				DWC_DEBUGPL(DBG_ANY, "Session End Detected\n");
+				__DWC_ERROR("Device Not Connected/Responding!\n");
+			}
+
+			/* If Session End Detected the B-Cable has
+			 * been disconnected. */
+			/* Reset PCD and Gadget driver to a
+			 * clean state. */
+			core_if->lx_state = DWC_OTG_L0;
+			DWC_SPINUNLOCK(core_if->lock);
+			cil_pcd_stop(core_if);
+			DWC_SPINLOCK(core_if->lock);
+
+			if (core_if->adp_enable) {
+				if (core_if->power_down == 2) {
+					gpwrdn.d32 = 0;
+					gpwrdn.b.pwrdnswtch = 1;
+					DWC_MODIFY_REG32(&core_if->
+							 core_global_regs->
+							 gpwrdn, gpwrdn.d32, 0);
+				}
+
+				gpwrdn.d32 = 0;
+				gpwrdn.b.pmuintsel = 1;
+				gpwrdn.b.pmuactv = 1;
+				DWC_MODIFY_REG32(&core_if->core_global_regs->
+						 gpwrdn, 0, gpwrdn.d32);
+
+				dwc_otg_adp_sense_start(core_if);
+			}
+		}
+
+		gotgctl.d32 = 0;
+		gotgctl.b.devhnpen = 1;
+		DWC_MODIFY_REG32(&global_regs->gotgctl, gotgctl.d32, 0);
+	}
+	if (gotgint.b.sesreqsucstschng) {
+		DWC_DEBUGPL(DBG_ANY, " ++OTG Interrupt: "
+			    "Session Reqeust Success Status Change++\n");
+		gotgctl.d32 = DWC_READ_REG32(&global_regs->gotgctl);
+		if (gotgctl.b.sesreqscs) {
+
+			if ((core_if->core_params->phy_type ==
+			     DWC_PHY_TYPE_PARAM_FS) && (core_if->core_params->i2c_enable)) {
+				core_if->srp_success = 1;
+			} else {
+				DWC_SPINUNLOCK(core_if->lock);
+				cil_pcd_resume(core_if);
+				DWC_SPINLOCK(core_if->lock);
+				/* Clear Session Request */
+				gotgctl.d32 = 0;
+				gotgctl.b.sesreq = 1;
+				DWC_MODIFY_REG32(&global_regs->gotgctl,
+						 gotgctl.d32, 0);
+			}
+		}
+	}
+	if (gotgint.b.hstnegsucstschng) {
+		/* Print statements during the HNP interrupt handling
+		 * can cause it to fail.*/
+		gotgctl.d32 = DWC_READ_REG32(&global_regs->gotgctl);
+		/* WA for 3.00a- HW is not setting cur_mode, even sometimes
+		 * this does not help*/
+		if (core_if->snpsid >= OTG_CORE_REV_3_00a)
+			dwc_udelay(100);
+		if (gotgctl.b.hstnegscs) {
+			if (dwc_otg_is_host_mode(core_if)) {
+				core_if->op_state = B_HOST;
+				/*
+				 * Need to disable SOF interrupt immediately.
+				 * When switching from device to host, the PCD
+				 * interrupt handler won't handle the
+				 * interrupt if host mode is already set. The
+				 * HCD interrupt handler won't get called if
+				 * the HCD state is HALT. This means that the
+				 * interrupt does not get handled and Linux
+				 * complains loudly.
+				 */
+				gintmsk.d32 = 0;
+				gintmsk.b.sofintr = 1;
+				DWC_MODIFY_REG32(&global_regs->gintmsk,
+						 gintmsk.d32, 0);
+				/* Call callback function with spin lock released */
+				DWC_SPINUNLOCK(core_if->lock);
+				cil_pcd_stop(core_if);
+				/*
+				 * Initialize the Core for Host mode.
+				 */
+				cil_hcd_start(core_if);
+				DWC_SPINLOCK(core_if->lock);
+				core_if->op_state = B_HOST;
+			}
+		} else {
+			gotgctl.d32 = 0;
+			gotgctl.b.hnpreq = 1;
+			gotgctl.b.devhnpen = 1;
+			DWC_MODIFY_REG32(&global_regs->gotgctl, gotgctl.d32, 0);
+			DWC_DEBUGPL(DBG_ANY, "HNP Failed\n");
+			__DWC_ERROR("Device Not Connected/Responding\n");
+		}
+	}
+	if (gotgint.b.hstnegdet) {
+		/* The disconnect interrupt is set at the same time as
+		 * Host Negotiation Detected.  During the mode
+		 * switch all interrupts are cleared so the disconnect
+		 * interrupt handler will not get executed.
+		 */
+		DWC_DEBUGPL(DBG_ANY, " ++OTG Interrupt: "
+			    "Host Negotiation Detected++ (%s)\n",
+			    (dwc_otg_is_host_mode(core_if) ? "Host" :
+			     "Device"));
+		if (dwc_otg_is_device_mode(core_if)) {
+			DWC_DEBUGPL(DBG_ANY, "a_suspend->a_peripheral (%d)\n",
+				    core_if->op_state);
+			DWC_SPINUNLOCK(core_if->lock);
+			cil_hcd_disconnect(core_if);
+			cil_pcd_start(core_if);
+			DWC_SPINLOCK(core_if->lock);
+			core_if->op_state = A_PERIPHERAL;
+		} else {
+			/*
+			 * Need to disable SOF interrupt immediately. When
+			 * switching from device to host, the PCD interrupt
+			 * handler won't handle the interrupt if host mode is
+			 * already set. The HCD interrupt handler won't get
+			 * called if the HCD state is HALT. This means that
+			 * the interrupt does not get handled and Linux
+			 * complains loudly.
+			 */
+			gintmsk.d32 = 0;
+			gintmsk.b.sofintr = 1;
+			DWC_MODIFY_REG32(&global_regs->gintmsk, gintmsk.d32, 0);
+			DWC_SPINUNLOCK(core_if->lock);
+			cil_pcd_stop(core_if);
+			cil_hcd_start(core_if);
+			DWC_SPINLOCK(core_if->lock);
+			core_if->op_state = A_HOST;
+		}
+	}
+	if (gotgint.b.adevtoutchng) {
+		DWC_DEBUGPL(DBG_ANY, " ++OTG Interrupt: "
+			    "A-Device Timeout Change++\n");
+	}
+	if (gotgint.b.debdone) {
+		DWC_DEBUGPL(DBG_ANY, " ++OTG Interrupt: " "Debounce Done++\n");
+	}
+
+	/* Clear GOTGINT */
+	DWC_WRITE_REG32(&core_if->core_global_regs->gotgint, gotgint.d32);
+
+	return 1;
+}
+
+void w_conn_id_status_change(void *p)
+{
+	dwc_otg_core_if_t *core_if = p;
+	uint32_t count = 0;
+	gotgctl_data_t gotgctl = {.d32 = 0 };
+
+	gotgctl.d32 = DWC_READ_REG32(&core_if->core_global_regs->gotgctl);
+	DWC_DEBUGPL(DBG_CIL, "gotgctl=%0x\n", gotgctl.d32);
+	DWC_DEBUGPL(DBG_CIL, "gotgctl.b.conidsts=%d\n", gotgctl.b.conidsts);
+
+	/* B-Device connector (Device Mode) */
+	if (gotgctl.b.conidsts) {
+		/* Wait for switch to device mode. */
+		while (!dwc_otg_is_device_mode(core_if)) {
+			DWC_PRINTF("Waiting for Peripheral Mode, Mode=%s\n",
+				   (dwc_otg_is_host_mode(core_if) ? "Host" :
+				    "Peripheral"));
+			dwc_mdelay(100);
+			if (++count > 10000)
+				break;
+		}
+		DWC_ASSERT(++count < 10000,
+			   "Connection id status change timed out");
+		core_if->op_state = B_PERIPHERAL;
+		dwc_otg_core_init(core_if);
+		dwc_otg_enable_global_interrupts(core_if);
+		cil_pcd_start(core_if);
+	} else {
+		/* A-Device connector (Host Mode) */
+		while (!dwc_otg_is_host_mode(core_if)) {
+			DWC_PRINTF("Waiting for Host Mode, Mode=%s\n",
+				   (dwc_otg_is_host_mode(core_if) ? "Host" :
+				    "Peripheral"));
+			dwc_mdelay(100);
+			if (++count > 10000)
+				break;
+		}
+		DWC_ASSERT(++count < 10000,
+			   "Connection id status change timed out");
+		core_if->op_state = A_HOST;
+		/*
+		 * Initialize the Core for Host mode.
+		 */
+		dwc_otg_core_init(core_if);
+		dwc_otg_enable_global_interrupts(core_if);
+		cil_hcd_start(core_if);
+	}
+}
+
+/**
+ * This function handles the Connector ID Status Change Interrupt.  It
+ * reads the OTG Interrupt Register (GOTCTL) to determine whether this
+ * is a Device to Host Mode transition or a Host Mode to Device
+ * Transition.
+ *
+ * This only occurs when the cable is connected/removed from the PHY
+ * connector.
+ *
+ * @param core_if Programming view of DWC_otg controller.
+ */
+int32_t dwc_otg_handle_conn_id_status_change_intr(dwc_otg_core_if_t * core_if)
+{
+
+	/*
+	 * Need to disable SOF interrupt immediately. If switching from device
+	 * to host, the PCD interrupt handler won't handle the interrupt if
+	 * host mode is already set. The HCD interrupt handler won't get
+	 * called if the HCD state is HALT. This means that the interrupt does
+	 * not get handled and Linux complains loudly.
+	 */
+	gintmsk_data_t gintmsk = {.d32 = 0 };
+	gintsts_data_t gintsts = {.d32 = 0 };
+
+	gintmsk.b.sofintr = 1;
+	DWC_MODIFY_REG32(&core_if->core_global_regs->gintmsk, gintmsk.d32, 0);
+
+	DWC_DEBUGPL(DBG_CIL,
+		    " ++Connector ID Status Change Interrupt++  (%s)\n",
+		    (dwc_otg_is_host_mode(core_if) ? "Host" : "Device"));
+
+	DWC_SPINUNLOCK(core_if->lock);
+
+	/*
+	 * Need to schedule a work, as there are possible DELAY function calls
+	 * Release lock before scheduling workq as it holds spinlock during scheduling
+	 */
+
+	DWC_WORKQ_SCHEDULE(core_if->wq_otg, w_conn_id_status_change,
+			   core_if, "connection id status change");
+	DWC_SPINLOCK(core_if->lock);
+
+	/* Set flag and clear interrupt */
+	gintsts.b.conidstschng = 1;
+	DWC_WRITE_REG32(&core_if->core_global_regs->gintsts, gintsts.d32);
+
+	return 1;
+}
+
+/**
+ * This interrupt indicates that a device is initiating the Session
+ * Request Protocol to request the host to turn on bus power so a new
+ * session can begin. The handler responds by turning on bus power. If
+ * the DWC_otg controller is in low power mode, the handler brings the
+ * controller out of low power mode before turning on bus power.
+ *
+ * @param core_if Programming view of DWC_otg controller.
+ */
+int32_t dwc_otg_handle_session_req_intr(dwc_otg_core_if_t * core_if)
+{
+	gintsts_data_t gintsts;
+
+#ifndef DWC_HOST_ONLY
+	DWC_DEBUGPL(DBG_ANY, "++Session Request Interrupt++\n");
+
+	if (dwc_otg_is_device_mode(core_if)) {
+		DWC_PRINTF("SRP: Device mode\n");
+	} else {
+		hprt0_data_t hprt0;
+		DWC_PRINTF("SRP: Host mode\n");
+
+		/* Turn on the port power bit. */
+		hprt0.d32 = dwc_otg_read_hprt0(core_if);
+		hprt0.b.prtpwr = 1;
+		DWC_WRITE_REG32(core_if->host_if->hprt0, hprt0.d32);
+
+		/* Start the Connection timer. So a message can be displayed
+		 * if connect does not occur within 10 seconds. */
+		cil_hcd_session_start(core_if);
+	}
+#endif
+
+	/* Clear interrupt */
+	gintsts.d32 = 0;
+	gintsts.b.sessreqintr = 1;
+	DWC_WRITE_REG32(&core_if->core_global_regs->gintsts, gintsts.d32);
+
+	return 1;
+}
+
+void w_wakeup_detected(void *p)
+{
+	dwc_otg_core_if_t *core_if = (dwc_otg_core_if_t *) p;
+	/*
+	 * Clear the Resume after 70ms. (Need 20 ms minimum. Use 70 ms
+	 * so that OPT tests pass with all PHYs).
+	 */
+	hprt0_data_t hprt0 = {.d32 = 0 };
+#if 0
+	pcgcctl_data_t pcgcctl = {.d32 = 0 };
+	/* Restart the Phy Clock */
+	pcgcctl.b.stoppclk = 1;
+	DWC_MODIFY_REG32(core_if->pcgcctl, pcgcctl.d32, 0);
+	dwc_udelay(10);
+#endif //0
+	hprt0.d32 = dwc_otg_read_hprt0(core_if);
+	DWC_DEBUGPL(DBG_ANY, "Resume: HPRT0=%0x\n", hprt0.d32);
+//      dwc_mdelay(70);
+	hprt0.b.prtres = 0;	/* Resume */
+	DWC_WRITE_REG32(core_if->host_if->hprt0, hprt0.d32);
+	DWC_DEBUGPL(DBG_ANY, "Clear Resume: HPRT0=%0x\n",
+		    DWC_READ_REG32(core_if->host_if->hprt0));
+
+	cil_hcd_resume(core_if);
+
+	/** Change to L0 state*/
+	core_if->lx_state = DWC_OTG_L0;
+}
+
+/**
+ * This interrupt indicates that the DWC_otg controller has detected a
+ * resume or remote wakeup sequence. If the DWC_otg controller is in
+ * low power mode, the handler must brings the controller out of low
+ * power mode. The controller automatically begins resume
+ * signaling. The handler schedules a time to stop resume signaling.
+ */
+int32_t dwc_otg_handle_wakeup_detected_intr(dwc_otg_core_if_t * core_if)
+{
+	gintsts_data_t gintsts;
+
+	DWC_DEBUGPL(DBG_ANY,
+		    "++Resume and Remote Wakeup Detected Interrupt++\n");
+
+	DWC_PRINTF("%s lxstate = %d\n", __func__, core_if->lx_state);
+
+	if (dwc_otg_is_device_mode(core_if)) {
+		dctl_data_t dctl = {.d32 = 0 };
+		DWC_DEBUGPL(DBG_PCD, "DSTS=0x%0x\n",
+			    DWC_READ_REG32(&core_if->dev_if->dev_global_regs->
+					   dsts));
+		if (core_if->lx_state == DWC_OTG_L2) {
+#ifdef PARTIAL_POWER_DOWN
+			if (core_if->hwcfg4.b.power_optimiz) {
+				pcgcctl_data_t power = {.d32 = 0 };
+
+				power.d32 = DWC_READ_REG32(core_if->pcgcctl);
+				DWC_DEBUGPL(DBG_CIL, "PCGCCTL=%0x\n",
+					    power.d32);
+
+				power.b.stoppclk = 0;
+				DWC_WRITE_REG32(core_if->pcgcctl, power.d32);
+
+				power.b.pwrclmp = 0;
+				DWC_WRITE_REG32(core_if->pcgcctl, power.d32);
+
+				power.b.rstpdwnmodule = 0;
+				DWC_WRITE_REG32(core_if->pcgcctl, power.d32);
+			}
+#endif
+			/* Clear the Remote Wakeup Signaling */
+			dctl.b.rmtwkupsig = 1;
+			DWC_MODIFY_REG32(&core_if->dev_if->dev_global_regs->
+					 dctl, dctl.d32, 0);
+
+			DWC_SPINUNLOCK(core_if->lock);
+			if (core_if->pcd_cb && core_if->pcd_cb->resume_wakeup) {
+				core_if->pcd_cb->resume_wakeup(core_if->pcd_cb->p);
+			}
+			DWC_SPINLOCK(core_if->lock);
+		} else {
+			glpmcfg_data_t lpmcfg;
+			lpmcfg.d32 =
+			    DWC_READ_REG32(&core_if->core_global_regs->glpmcfg);
+			lpmcfg.b.hird_thres &= (~(1 << 4));
+			DWC_WRITE_REG32(&core_if->core_global_regs->glpmcfg,
+					lpmcfg.d32);
+		}
+		/** Change to L0 state*/
+		core_if->lx_state = DWC_OTG_L0;
+	} else {
+		if (core_if->lx_state != DWC_OTG_L1) {
+			pcgcctl_data_t pcgcctl = {.d32 = 0 };
+
+			/* Restart the Phy Clock */
+			pcgcctl.b.stoppclk = 1;
+			DWC_MODIFY_REG32(core_if->pcgcctl, pcgcctl.d32, 0);
+			DWC_TIMER_SCHEDULE(core_if->wkp_timer, 71);
+		} else {
+			/** Change to L0 state*/
+			core_if->lx_state = DWC_OTG_L0;
+		}
+	}
+
+	/* Clear interrupt */
+	gintsts.d32 = 0;
+	gintsts.b.wkupintr = 1;
+	DWC_WRITE_REG32(&core_if->core_global_regs->gintsts, gintsts.d32);
+
+	return 1;
+}
+
+/**
+ * This interrupt indicates that the Wakeup Logic has detected a
+ * Device disconnect.
+ */
+static int32_t dwc_otg_handle_pwrdn_disconnect_intr(dwc_otg_core_if_t *core_if)
+{
+	gpwrdn_data_t gpwrdn = { .d32 = 0 };
+	gpwrdn_data_t gpwrdn_temp = { .d32 = 0 };
+	gpwrdn_temp.d32 = DWC_READ_REG32(&core_if->core_global_regs->gpwrdn);
+
+	DWC_PRINTF("%s called\n", __FUNCTION__);
+
+	if (!core_if->hibernation_suspend) {
+		DWC_PRINTF("Already exited from Hibernation\n");
+		return 1;
+	}
+
+	/* Switch on the voltage to the core */
+	gpwrdn.b.pwrdnswtch = 1;
+	DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
+	dwc_udelay(10);
+
+	/* Reset the core */
+	gpwrdn.d32 = 0;
+	gpwrdn.b.pwrdnrstn = 1;
+	DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
+	dwc_udelay(10);
+
+	/* Disable power clamps*/
+	gpwrdn.d32 = 0;
+	gpwrdn.b.pwrdnclmp = 1;
+	DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
+
+	/* Remove reset the core signal */
+	gpwrdn.d32 = 0;
+	gpwrdn.b.pwrdnrstn = 1;
+	DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, 0, gpwrdn.d32);
+	dwc_udelay(10);
+
+	/* Disable PMU interrupt */
+	gpwrdn.d32 = 0;
+	gpwrdn.b.pmuintsel = 1;
+	DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
+
+	core_if->hibernation_suspend = 0;
+
+	/* Disable PMU */
+	gpwrdn.d32 = 0;
+	gpwrdn.b.pmuactv = 1;
+	DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
+	dwc_udelay(10);
+
+	if (gpwrdn_temp.b.idsts) {
+		core_if->op_state = B_PERIPHERAL;
+		dwc_otg_core_init(core_if);
+		dwc_otg_enable_global_interrupts(core_if);
+		cil_pcd_start(core_if);
+	} else {
+		core_if->op_state = A_HOST;
+		dwc_otg_core_init(core_if);
+		dwc_otg_enable_global_interrupts(core_if);
+		cil_hcd_start(core_if);
+	}
+
+	return 1;
+}
+
+/**
+ * This interrupt indicates that the Wakeup Logic has detected a
+ * remote wakeup sequence.
+ */
+static int32_t dwc_otg_handle_pwrdn_wakeup_detected_intr(dwc_otg_core_if_t * core_if)
+{
+	gpwrdn_data_t gpwrdn = {.d32 = 0 };
+	DWC_DEBUGPL(DBG_ANY,
+		    "++Powerdown Remote Wakeup Detected Interrupt++\n");
+
+	if (!core_if->hibernation_suspend) {
+		DWC_PRINTF("Already exited from Hibernation\n");
+		return 1;
+	}
+
+	gpwrdn.d32 = DWC_READ_REG32(&core_if->core_global_regs->gpwrdn);
+	if (gpwrdn.b.idsts) {	// Device Mode
+		if ((core_if->power_down == 2)
+		    && (core_if->hibernation_suspend == 1)) {
+			dwc_otg_device_hibernation_restore(core_if, 0, 0);
+		}
+	} else {
+		if ((core_if->power_down == 2)
+		    && (core_if->hibernation_suspend == 1)) {
+			dwc_otg_host_hibernation_restore(core_if, 1, 0);
+		}
+	}
+	return 1;
+}
+
+static int32_t dwc_otg_handle_pwrdn_idsts_change(dwc_otg_device_t *otg_dev)
+{
+	gpwrdn_data_t gpwrdn = {.d32 = 0 };
+	gpwrdn_data_t gpwrdn_temp = {.d32 = 0 };
+	dwc_otg_core_if_t *core_if = otg_dev->core_if;
+
+	DWC_DEBUGPL(DBG_ANY, "%s called\n", __FUNCTION__);
+	gpwrdn_temp.d32 = DWC_READ_REG32(&core_if->core_global_regs->gpwrdn);
+	if (core_if->power_down == 2) {
+		if (!core_if->hibernation_suspend) {
+			DWC_PRINTF("Already exited from Hibernation\n");
+			return 1;
+		}
+		DWC_DEBUGPL(DBG_ANY, "Exit from hibernation on ID sts change\n");
+		/* Switch on the voltage to the core */
+		gpwrdn.b.pwrdnswtch = 1;
+		DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
+		dwc_udelay(10);
+
+		/* Reset the core */
+		gpwrdn.d32 = 0;
+		gpwrdn.b.pwrdnrstn = 1;
+		DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
+		dwc_udelay(10);
+
+		/* Disable power clamps */
+		gpwrdn.d32 = 0;
+		gpwrdn.b.pwrdnclmp = 1;
+		DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
+
+		/* Remove reset the core signal */
+		gpwrdn.d32 = 0;
+		gpwrdn.b.pwrdnrstn = 1;
+		DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, 0, gpwrdn.d32);
+		dwc_udelay(10);
+
+		/* Disable PMU interrupt */
+		gpwrdn.d32 = 0;
+		gpwrdn.b.pmuintsel = 1;
+		DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
+
+		/*Indicates that we are exiting from hibernation */
+		core_if->hibernation_suspend = 0;
+
+		/* Disable PMU */
+		gpwrdn.d32 = 0;
+		gpwrdn.b.pmuactv = 1;
+		DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
+		dwc_udelay(10);
+
+		gpwrdn.d32 = core_if->gr_backup->gpwrdn_local;
+		if (gpwrdn.b.dis_vbus == 1) {
+			gpwrdn.d32 = 0;
+			gpwrdn.b.dis_vbus = 1;
+			DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
+		}
+
+		if (gpwrdn_temp.b.idsts) {
+			core_if->op_state = B_PERIPHERAL;
+			dwc_otg_core_init(core_if);
+			dwc_otg_enable_global_interrupts(core_if);
+			cil_pcd_start(core_if);
+		} else {
+			core_if->op_state = A_HOST;
+			dwc_otg_core_init(core_if);
+			dwc_otg_enable_global_interrupts(core_if);
+			cil_hcd_start(core_if);
+		}
+	}
+
+	if (core_if->adp_enable) {
+		uint8_t is_host = 0;
+		DWC_SPINUNLOCK(core_if->lock);
+		/* Change the core_if's lock to hcd/pcd lock depend on mode? */
+#ifndef DWC_HOST_ONLY
+		if (gpwrdn_temp.b.idsts)
+			core_if->lock = otg_dev->pcd->lock;
+#endif
+#ifndef DWC_DEVICE_ONLY
+		if (!gpwrdn_temp.b.idsts) {
+				core_if->lock = otg_dev->hcd->lock;
+				is_host = 1;
+		}
+#endif
+		DWC_PRINTF("RESTART ADP\n");
+		if (core_if->adp.probe_enabled)
+			dwc_otg_adp_probe_stop(core_if);
+		if (core_if->adp.sense_enabled)
+			dwc_otg_adp_sense_stop(core_if);
+		if (core_if->adp.sense_timer_started)
+			DWC_TIMER_CANCEL(core_if->adp.sense_timer);
+		if (core_if->adp.vbuson_timer_started)
+			DWC_TIMER_CANCEL(core_if->adp.vbuson_timer);
+		core_if->adp.probe_timer_values[0] = -1;
+		core_if->adp.probe_timer_values[1] = -1;
+		core_if->adp.sense_timer_started = 0;
+		core_if->adp.vbuson_timer_started = 0;
+		core_if->adp.probe_counter = 0;
+		core_if->adp.gpwrdn = 0;
+
+		/* Disable PMU and restart ADP */
+		gpwrdn_temp.d32 = 0;
+		gpwrdn_temp.b.pmuactv = 1;
+		gpwrdn_temp.b.pmuintsel = 1;
+		DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
+		DWC_PRINTF("Check point 1\n");
+		dwc_mdelay(110);
+		dwc_otg_adp_start(core_if, is_host);
+		DWC_SPINLOCK(core_if->lock);
+	}
+
+
+	return 1;
+}
+
+static int32_t dwc_otg_handle_pwrdn_session_change(dwc_otg_core_if_t * core_if)
+{
+	gpwrdn_data_t gpwrdn = {.d32 = 0 };
+	int32_t otg_cap_param = core_if->core_params->otg_cap;
+	DWC_DEBUGPL(DBG_ANY, "%s called\n", __FUNCTION__);
+
+	gpwrdn.d32 = DWC_READ_REG32(&core_if->core_global_regs->gpwrdn);
+	if (core_if->power_down == 2) {
+		if (!core_if->hibernation_suspend) {
+			DWC_PRINTF("Already exited from Hibernation\n");
+			return 1;
+		}
+
+		if ((otg_cap_param != DWC_OTG_CAP_PARAM_HNP_SRP_CAPABLE ||
+			 otg_cap_param != DWC_OTG_CAP_PARAM_SRP_ONLY_CAPABLE) &&
+			gpwrdn.b.bsessvld == 0) {
+			/* Save gpwrdn register for further usage if stschng interrupt */
+			core_if->gr_backup->gpwrdn_local =
+				DWC_READ_REG32(&core_if->core_global_regs->gpwrdn);
+			/*Exit from ISR and wait for stschng interrupt with bsessvld = 1 */
+			return 1;
+		}
+
+		/* Switch on the voltage to the core */
+		gpwrdn.d32 = 0;
+		gpwrdn.b.pwrdnswtch = 1;
+		DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
+		dwc_udelay(10);
+
+		/* Reset the core */
+		gpwrdn.d32 = 0;
+		gpwrdn.b.pwrdnrstn = 1;
+		DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
+		dwc_udelay(10);
+
+		/* Disable power clamps */
+		gpwrdn.d32 = 0;
+		gpwrdn.b.pwrdnclmp = 1;
+		DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
+
+		/* Remove reset the core signal */
+		gpwrdn.d32 = 0;
+		gpwrdn.b.pwrdnrstn = 1;
+		DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, 0, gpwrdn.d32);
+		dwc_udelay(10);
+
+		/* Disable PMU interrupt */
+		gpwrdn.d32 = 0;
+		gpwrdn.b.pmuintsel = 1;
+		DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
+		dwc_udelay(10);
+
+		/*Indicates that we are exiting from hibernation */
+		core_if->hibernation_suspend = 0;
+
+		/* Disable PMU */
+		gpwrdn.d32 = 0;
+		gpwrdn.b.pmuactv = 1;
+		DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
+		dwc_udelay(10);
+
+		core_if->op_state = B_PERIPHERAL;
+		dwc_otg_core_init(core_if);
+		dwc_otg_enable_global_interrupts(core_if);
+		cil_pcd_start(core_if);
+
+		if (otg_cap_param == DWC_OTG_CAP_PARAM_HNP_SRP_CAPABLE ||
+			otg_cap_param == DWC_OTG_CAP_PARAM_SRP_ONLY_CAPABLE) {
+			/*
+			 * Initiate SRP after initial ADP probe.
+			 */
+			dwc_otg_initiate_srp(core_if);
+		}
+	}
+
+	return 1;
+}
+/**
+ * This interrupt indicates that the Wakeup Logic has detected a
+ * status change either on IDDIG or BSessVld.
+ */
+static uint32_t dwc_otg_handle_pwrdn_stschng_intr(dwc_otg_device_t *otg_dev)
+{
+	uint32_t retval = 0;
+	gpwrdn_data_t gpwrdn = {.d32 = 0 };
+	gpwrdn_data_t gpwrdn_temp = {.d32 = 0 };
+	dwc_otg_core_if_t *core_if = otg_dev->core_if;
+
+	DWC_PRINTF("%s called\n", __FUNCTION__);
+
+	if (core_if->power_down == 2) {
+		if (core_if->hibernation_suspend <= 0) {
+			DWC_PRINTF("Already exited from Hibernation\n");
+			return 1;
+		} else
+			gpwrdn_temp.d32 = core_if->gr_backup->gpwrdn_local;
+
+	} else {
+		gpwrdn_temp.d32 = core_if->adp.gpwrdn;
+	}
+
+	gpwrdn.d32 = DWC_READ_REG32(&core_if->core_global_regs->gpwrdn);
+
+	if (gpwrdn.b.idsts ^ gpwrdn_temp.b.idsts) {
+		retval = dwc_otg_handle_pwrdn_idsts_change(otg_dev);
+	} else if (gpwrdn.b.bsessvld ^ gpwrdn_temp.b.bsessvld) {
+		retval = dwc_otg_handle_pwrdn_session_change(core_if);
+	}
+
+	return retval;
+}
+
+/**
+ * This interrupt indicates that the Wakeup Logic has detected a
+ * SRP.
+ */
+static int32_t dwc_otg_handle_pwrdn_srp_intr(dwc_otg_core_if_t * core_if)
+{
+	gpwrdn_data_t gpwrdn = {.d32 = 0 };
+
+	DWC_PRINTF("%s called\n", __FUNCTION__);
+
+	if (!core_if->hibernation_suspend) {
+		DWC_PRINTF("Already exited from Hibernation\n");
+		return 1;
+	}
+#ifdef DWC_DEV_SRPCAP
+	if (core_if->pwron_timer_started) {
+		core_if->pwron_timer_started = 0;
+		DWC_TIMER_CANCEL(core_if->pwron_timer);
+	}
+#endif
+
+	/* Switch on the voltage to the core */
+	gpwrdn.b.pwrdnswtch = 1;
+	DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
+	dwc_udelay(10);
+
+	/* Reset the core */
+	gpwrdn.d32 = 0;
+	gpwrdn.b.pwrdnrstn = 1;
+	DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
+	dwc_udelay(10);
+
+	/* Disable power clamps */
+	gpwrdn.d32 = 0;
+	gpwrdn.b.pwrdnclmp = 1;
+	DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
+
+	/* Remove reset the core signal */
+	gpwrdn.d32 = 0;
+	gpwrdn.b.pwrdnrstn = 1;
+	DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, 0, gpwrdn.d32);
+	dwc_udelay(10);
+
+	/* Disable PMU interrupt */
+	gpwrdn.d32 = 0;
+	gpwrdn.b.pmuintsel = 1;
+	DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
+
+	/* Indicates that we are exiting from hibernation */
+	core_if->hibernation_suspend = 0;
+
+	/* Disable PMU */
+	gpwrdn.d32 = 0;
+	gpwrdn.b.pmuactv = 1;
+	DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
+	dwc_udelay(10);
+
+	/* Programm Disable VBUS to 0 */
+	gpwrdn.d32 = 0;
+	gpwrdn.b.dis_vbus = 1;
+	DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
+
+	/*Initialize the core as Host */
+	core_if->op_state = A_HOST;
+	dwc_otg_core_init(core_if);
+	dwc_otg_enable_global_interrupts(core_if);
+	cil_hcd_start(core_if);
+
+	return 1;
+}
+
+/** This interrupt indicates that restore command after Hibernation
+ * was completed by the core. */
+int32_t dwc_otg_handle_restore_done_intr(dwc_otg_core_if_t * core_if)
+{
+	pcgcctl_data_t pcgcctl;
+	DWC_DEBUGPL(DBG_ANY, "++Restore Done Interrupt++\n");
+
+	//TODO De-assert restore signal. 8.a
+	pcgcctl.d32 = DWC_READ_REG32(core_if->pcgcctl);
+	if (pcgcctl.b.restoremode == 1) {
+		gintmsk_data_t gintmsk = {.d32 = 0 };
+		/*
+		 * If restore mode is Remote Wakeup,
+		 * unmask Remote Wakeup interrupt.
+		 */
+		gintmsk.b.wkupintr = 1;
+		DWC_MODIFY_REG32(&core_if->core_global_regs->gintmsk,
+				 0, gintmsk.d32);
+	}
+
+	return 1;
+}
+
+/**
+ * This interrupt indicates that a device has been disconnected from
+ * the root port.
+ */
+int32_t dwc_otg_handle_disconnect_intr(dwc_otg_core_if_t * core_if)
+{
+	gintsts_data_t gintsts;
+
+	DWC_DEBUGPL(DBG_ANY, "++Disconnect Detected Interrupt++ (%s) %s\n",
+		    (dwc_otg_is_host_mode(core_if) ? "Host" : "Device"),
+		    op_state_str(core_if));
+
+/** @todo Consolidate this if statement. */
+#ifndef DWC_HOST_ONLY
+	if (core_if->op_state == B_HOST) {
+		/* If in device mode Disconnect and stop the HCD, then
+		 * start the PCD. */
+		DWC_SPINUNLOCK(core_if->lock);
+		cil_hcd_disconnect(core_if);
+		cil_pcd_start(core_if);
+		DWC_SPINLOCK(core_if->lock);
+		core_if->op_state = B_PERIPHERAL;
+	} else if (dwc_otg_is_device_mode(core_if)) {
+		gotgctl_data_t gotgctl = {.d32 = 0 };
+		gotgctl.d32 =
+		    DWC_READ_REG32(&core_if->core_global_regs->gotgctl);
+		if (gotgctl.b.hstsethnpen == 1) {
+			/* Do nothing, if HNP in process the OTG
+			 * interrupt "Host Negotiation Detected"
+			 * interrupt will do the mode switch.
+			 */
+		} else if (gotgctl.b.devhnpen == 0) {
+			/* If in device mode Disconnect and stop the HCD, then
+			 * start the PCD. */
+			DWC_SPINUNLOCK(core_if->lock);
+			cil_hcd_disconnect(core_if);
+			cil_pcd_start(core_if);
+			DWC_SPINLOCK(core_if->lock);
+			core_if->op_state = B_PERIPHERAL;
+		} else {
+			DWC_DEBUGPL(DBG_ANY, "!a_peripheral && !devhnpen\n");
+		}
+	} else {
+		if (core_if->op_state == A_HOST) {
+			/* A-Cable still connected but device disconnected. */
+			DWC_SPINUNLOCK(core_if->lock);
+			cil_hcd_disconnect(core_if);
+			DWC_SPINLOCK(core_if->lock);
+			if (core_if->adp_enable) {
+				gpwrdn_data_t gpwrdn = { .d32 = 0 };
+				cil_hcd_stop(core_if);
+				/* Enable Power Down Logic */
+				gpwrdn.b.pmuintsel = 1;
+				gpwrdn.b.pmuactv = 1;
+				DWC_MODIFY_REG32(&core_if->core_global_regs->
+						 gpwrdn, 0, gpwrdn.d32);
+				dwc_otg_adp_probe_start(core_if);
+
+				/* Power off the core */
+				if (core_if->power_down == 2) {
+					gpwrdn.d32 = 0;
+					gpwrdn.b.pwrdnswtch = 1;
+					DWC_MODIFY_REG32
+					    (&core_if->core_global_regs->gpwrdn,
+					     gpwrdn.d32, 0);
+				}
+			}
+		}
+	}
+#endif
+	/* Change to L3(OFF) state */
+	core_if->lx_state = DWC_OTG_L3;
+
+	gintsts.d32 = 0;
+	gintsts.b.disconnect = 1;
+	DWC_WRITE_REG32(&core_if->core_global_regs->gintsts, gintsts.d32);
+	return 1;
+}
+
+/**
+ * This interrupt indicates that SUSPEND state has been detected on
+ * the USB.
+ *
+ * For HNP the USB Suspend interrupt signals the change from
+ * "a_peripheral" to "a_host".
+ *
+ * When power management is enabled the core will be put in low power
+ * mode.
+ */
+int32_t dwc_otg_handle_usb_suspend_intr(dwc_otg_core_if_t * core_if)
+{
+	dsts_data_t dsts;
+	gintsts_data_t gintsts;
+	dcfg_data_t dcfg;
+
+	DWC_DEBUGPL(DBG_ANY, "USB SUSPEND\n");
+
+	if (dwc_otg_is_device_mode(core_if)) {
+		/* Check the Device status register to determine if the Suspend
+		 * state is active. */
+		dsts.d32 =
+		    DWC_READ_REG32(&core_if->dev_if->dev_global_regs->dsts);
+		DWC_DEBUGPL(DBG_PCD, "DSTS=0x%0x\n", dsts.d32);
+		DWC_DEBUGPL(DBG_PCD, "DSTS.Suspend Status=%d "
+			    "HWCFG4.power Optimize=%d\n",
+			    dsts.b.suspsts, core_if->hwcfg4.b.power_optimiz);
+
+#ifdef PARTIAL_POWER_DOWN
+/** @todo Add a module parameter for power management. */
+
+		if (dsts.b.suspsts && core_if->hwcfg4.b.power_optimiz) {
+			pcgcctl_data_t power = {.d32 = 0 };
+			DWC_DEBUGPL(DBG_CIL, "suspend\n");
+
+			power.b.pwrclmp = 1;
+			DWC_WRITE_REG32(core_if->pcgcctl, power.d32);
+
+			power.b.rstpdwnmodule = 1;
+			DWC_MODIFY_REG32(core_if->pcgcctl, 0, power.d32);
+
+			power.b.stoppclk = 1;
+			DWC_MODIFY_REG32(core_if->pcgcctl, 0, power.d32);
+
+		} else {
+			DWC_DEBUGPL(DBG_ANY, "disconnect?\n");
+		}
+#endif
+		/* PCD callback for suspend. Release the lock inside of callback function */
+		cil_pcd_suspend(core_if);
+		if (core_if->power_down == 2)
+		{
+			dcfg.d32 = DWC_READ_REG32(&core_if->dev_if->dev_global_regs->dcfg);
+			DWC_DEBUGPL(DBG_ANY,"lx_state = %08x\n",core_if->lx_state);
+			DWC_DEBUGPL(DBG_ANY," device address = %08d\n",dcfg.b.devaddr);
+
+			if (core_if->lx_state != DWC_OTG_L3 && dcfg.b.devaddr) {
+				pcgcctl_data_t pcgcctl = {.d32 = 0 };
+				gpwrdn_data_t gpwrdn = {.d32 = 0 };
+				gusbcfg_data_t gusbcfg = {.d32 = 0 };
+
+				/* Change to L2(suspend) state */
+				core_if->lx_state = DWC_OTG_L2;
+
+				/* Clear interrupt in gintsts */
+				gintsts.d32 = 0;
+				gintsts.b.usbsuspend = 1;
+				DWC_WRITE_REG32(&core_if->core_global_regs->
+						gintsts, gintsts.d32);
+				DWC_PRINTF("Start of hibernation completed\n");
+				dwc_otg_save_global_regs(core_if);
+				dwc_otg_save_dev_regs(core_if);
+
+				gusbcfg.d32 =
+				    DWC_READ_REG32(&core_if->core_global_regs->
+						   gusbcfg);
+				if (gusbcfg.b.ulpi_utmi_sel == 1) {
+					/* ULPI interface */
+					/* Suspend the Phy Clock */
+					pcgcctl.d32 = 0;
+					pcgcctl.b.stoppclk = 1;
+					DWC_MODIFY_REG32(core_if->pcgcctl, 0,
+							 pcgcctl.d32);
+					dwc_udelay(10);
+					gpwrdn.b.pmuactv = 1;
+					DWC_MODIFY_REG32(&core_if->
+							 core_global_regs->
+							 gpwrdn, 0, gpwrdn.d32);
+				} else {
+					/* UTMI+ Interface */
+					gpwrdn.b.pmuactv = 1;
+					DWC_MODIFY_REG32(&core_if->
+							 core_global_regs->
+							 gpwrdn, 0, gpwrdn.d32);
+					dwc_udelay(10);
+					pcgcctl.b.stoppclk = 1;
+					DWC_MODIFY_REG32(core_if->pcgcctl, 0,
+							 pcgcctl.d32);
+					dwc_udelay(10);
+				}
+
+				/* Set flag to indicate that we are in hibernation */
+				core_if->hibernation_suspend = 1;
+				/* Enable interrupts from wake up logic */
+				gpwrdn.d32 = 0;
+				gpwrdn.b.pmuintsel = 1;
+				DWC_MODIFY_REG32(&core_if->core_global_regs->
+						 gpwrdn, 0, gpwrdn.d32);
+				dwc_udelay(10);
+
+				/* Unmask device mode interrupts in GPWRDN */
+				gpwrdn.d32 = 0;
+				gpwrdn.b.rst_det_msk = 1;
+				gpwrdn.b.lnstchng_msk = 1;
+				gpwrdn.b.sts_chngint_msk = 1;
+				DWC_MODIFY_REG32(&core_if->core_global_regs->
+						 gpwrdn, 0, gpwrdn.d32);
+				dwc_udelay(10);
+
+				/* Enable Power Down Clamp */
+				gpwrdn.d32 = 0;
+				gpwrdn.b.pwrdnclmp = 1;
+				DWC_MODIFY_REG32(&core_if->core_global_regs->
+						 gpwrdn, 0, gpwrdn.d32);
+				dwc_udelay(10);
+
+				/* Switch off VDD */
+				gpwrdn.d32 = 0;
+				gpwrdn.b.pwrdnswtch = 1;
+				DWC_MODIFY_REG32(&core_if->core_global_regs->
+						 gpwrdn, 0, gpwrdn.d32);
+
+				/* Save gpwrdn register for further usage if stschng interrupt */
+				core_if->gr_backup->gpwrdn_local =
+							DWC_READ_REG32(&core_if->core_global_regs->gpwrdn);
+				DWC_PRINTF("Hibernation completed\n");
+
+				return 1;
+			}
+		} else if (core_if->power_down == 3) {
+			pcgcctl_data_t pcgcctl = {.d32 = 0 };
+			dcfg.d32 = DWC_READ_REG32(&core_if->dev_if->dev_global_regs->dcfg);
+			DWC_DEBUGPL(DBG_ANY, "lx_state = %08x\n",core_if->lx_state);
+			DWC_DEBUGPL(DBG_ANY, " device address = %08d\n",dcfg.b.devaddr);
+
+			if (core_if->lx_state != DWC_OTG_L3 && dcfg.b.devaddr) {
+				DWC_DEBUGPL(DBG_ANY, "Start entering to extended hibernation\n");
+				core_if->xhib = 1;
+
+				/* Clear interrupt in gintsts */
+				gintsts.d32 = 0;
+				gintsts.b.usbsuspend = 1;
+				DWC_WRITE_REG32(&core_if->core_global_regs->
+					gintsts, gintsts.d32);
+
+				dwc_otg_save_global_regs(core_if);
+				dwc_otg_save_dev_regs(core_if);
+
+				/* Wait for 10 PHY clocks */
+				dwc_udelay(10);
+
+				/* Program GPIO register while entering to xHib */
+				DWC_WRITE_REG32(&core_if->core_global_regs->ggpio, 0x1);
+
+				pcgcctl.b.enbl_extnd_hiber = 1;
+				DWC_MODIFY_REG32(core_if->pcgcctl, 0, pcgcctl.d32);
+				DWC_MODIFY_REG32(core_if->pcgcctl, 0, pcgcctl.d32);
+
+				pcgcctl.d32 = 0;
+				pcgcctl.b.extnd_hiber_pwrclmp = 1;
+				DWC_MODIFY_REG32(core_if->pcgcctl, 0, pcgcctl.d32);
+
+				pcgcctl.d32 = 0;
+				pcgcctl.b.extnd_hiber_switch = 1;
+				core_if->gr_backup->xhib_gpwrdn = DWC_READ_REG32(&core_if->core_global_regs->gpwrdn);
+				core_if->gr_backup->xhib_pcgcctl = DWC_READ_REG32(core_if->pcgcctl) | pcgcctl.d32;
+				DWC_MODIFY_REG32(core_if->pcgcctl, 0, pcgcctl.d32);
+
+				DWC_DEBUGPL(DBG_ANY, "Finished entering to extended hibernation\n");
+
+				return 1;
+			}
+		}
+	} else {
+		if (core_if->op_state == A_PERIPHERAL) {
+			DWC_DEBUGPL(DBG_ANY, "a_peripheral->a_host\n");
+			/* Clear the a_peripheral flag, back to a_host. */
+			DWC_SPINUNLOCK(core_if->lock);
+			cil_pcd_stop(core_if);
+			cil_hcd_start(core_if);
+			DWC_SPINLOCK(core_if->lock);
+			core_if->op_state = A_HOST;
+		}
+	}
+
+	/* Change to L2(suspend) state */
+	core_if->lx_state = DWC_OTG_L2;
+
+	/* Clear interrupt */
+	gintsts.d32 = 0;
+	gintsts.b.usbsuspend = 1;
+	DWC_WRITE_REG32(&core_if->core_global_regs->gintsts, gintsts.d32);
+
+	return 1;
+}
+
+static int32_t dwc_otg_handle_xhib_exit_intr(dwc_otg_core_if_t * core_if)
+{
+	gpwrdn_data_t gpwrdn = {.d32 = 0 };
+	pcgcctl_data_t pcgcctl = {.d32 = 0 };
+	gahbcfg_data_t gahbcfg = {.d32 = 0 };
+
+	dwc_udelay(10);
+
+	/* Program GPIO register while entering to xHib */
+	DWC_WRITE_REG32(&core_if->core_global_regs->ggpio, 0x0);
+
+	pcgcctl.d32 = core_if->gr_backup->xhib_pcgcctl;
+	pcgcctl.b.extnd_hiber_pwrclmp = 0;
+	DWC_WRITE_REG32(core_if->pcgcctl, pcgcctl.d32);
+	dwc_udelay(10);
+
+	gpwrdn.d32 = core_if->gr_backup->xhib_gpwrdn;
+	gpwrdn.b.restore = 1;
+	DWC_WRITE_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32);
+	dwc_udelay(10);
+
+	restore_lpm_i2c_regs(core_if);
+
+	pcgcctl.d32 = core_if->gr_backup->pcgcctl_local & (0x3FFFF << 14);
+	pcgcctl.b.max_xcvrselect = 1;
+	pcgcctl.b.ess_reg_restored = 0;
+	pcgcctl.b.extnd_hiber_switch = 0;
+	pcgcctl.b.extnd_hiber_pwrclmp = 0;
+	pcgcctl.b.enbl_extnd_hiber = 1;
+	DWC_WRITE_REG32(core_if->pcgcctl, pcgcctl.d32);
+
+	gahbcfg.d32 = core_if->gr_backup->gahbcfg_local;
+	gahbcfg.b.glblintrmsk = 1;
+	DWC_WRITE_REG32(&core_if->core_global_regs->gahbcfg, gahbcfg.d32);
+
+	DWC_WRITE_REG32(&core_if->core_global_regs->gintsts, 0xFFFFFFFF);
+	DWC_WRITE_REG32(&core_if->core_global_regs->gintmsk, 0x1 << 16);
+
+	DWC_WRITE_REG32(&core_if->core_global_regs->gusbcfg,
+			core_if->gr_backup->gusbcfg_local);
+	DWC_WRITE_REG32(&core_if->dev_if->dev_global_regs->dcfg,
+			core_if->dr_backup->dcfg);
+
+	pcgcctl.d32 = 0;
+	pcgcctl.d32 = core_if->gr_backup->pcgcctl_local & (0x3FFFF << 14);
+	pcgcctl.b.max_xcvrselect = 1;
+	pcgcctl.d32 |= 0x608;
+	DWC_WRITE_REG32(core_if->pcgcctl, pcgcctl.d32);
+	dwc_udelay(10);
+
+	pcgcctl.d32 = 0;
+	pcgcctl.d32 = core_if->gr_backup->pcgcctl_local & (0x3FFFF << 14);
+	pcgcctl.b.max_xcvrselect = 1;
+	pcgcctl.b.ess_reg_restored = 1;
+	pcgcctl.b.enbl_extnd_hiber = 1;
+	pcgcctl.b.rstpdwnmodule = 1;
+	pcgcctl.b.restoremode = 1;
+	DWC_WRITE_REG32(core_if->pcgcctl, pcgcctl.d32);
+
+	DWC_DEBUGPL(DBG_ANY, "%s called\n", __FUNCTION__);
+
+	return 1;
+}
+
+#ifdef CONFIG_USB_DWC_OTG_LPM
+/**
+ * This function hadles LPM transaction received interrupt.
+ */
+static int32_t dwc_otg_handle_lpm_intr(dwc_otg_core_if_t * core_if)
+{
+	glpmcfg_data_t lpmcfg;
+	gintsts_data_t gintsts;
+
+	if (!core_if->core_params->lpm_enable) {
+		DWC_PRINTF("Unexpected LPM interrupt\n");
+	}
+
+	lpmcfg.d32 = DWC_READ_REG32(&core_if->core_global_regs->glpmcfg);
+	DWC_PRINTF("LPM config register = 0x%08x\n", lpmcfg.d32);
+
+	if (dwc_otg_is_host_mode(core_if)) {
+		cil_hcd_sleep(core_if);
+	} else {
+		lpmcfg.b.hird_thres |= (1 << 4);
+		DWC_WRITE_REG32(&core_if->core_global_regs->glpmcfg,
+				lpmcfg.d32);
+	}
+
+	/* Examine prt_sleep_sts after TL1TokenTetry period max (10 us) */
+	dwc_udelay(10);
+	lpmcfg.d32 = DWC_READ_REG32(&core_if->core_global_regs->glpmcfg);
+	if (lpmcfg.b.prt_sleep_sts) {
+		/* Save the current state */
+		core_if->lx_state = DWC_OTG_L1;
+	}
+
+	/* Clear interrupt  */
+	gintsts.d32 = 0;
+	gintsts.b.lpmtranrcvd = 1;
+	DWC_WRITE_REG32(&core_if->core_global_regs->gintsts, gintsts.d32);
+	return 1;
+}
+#endif /* CONFIG_USB_DWC_OTG_LPM */
+
+/**
+ * This function returns the Core Interrupt register.
+ */
+static inline uint32_t dwc_otg_read_common_intr(dwc_otg_core_if_t * core_if, gintmsk_data_t *reenable_gintmsk, dwc_otg_hcd_t *hcd)
+{
+	gahbcfg_data_t gahbcfg = {.d32 = 0 };
+	gintsts_data_t gintsts;
+	gintmsk_data_t gintmsk;
+	gintmsk_data_t gintmsk_common = {.d32 = 0 };
+	gintmsk_common.b.wkupintr = 1;
+	gintmsk_common.b.sessreqintr = 1;
+	gintmsk_common.b.conidstschng = 1;
+	gintmsk_common.b.otgintr = 1;
+	gintmsk_common.b.modemismatch = 1;
+	gintmsk_common.b.disconnect = 1;
+	gintmsk_common.b.usbsuspend = 1;
+#ifdef CONFIG_USB_DWC_OTG_LPM
+	gintmsk_common.b.lpmtranrcvd = 1;
+#endif
+	gintmsk_common.b.restoredone = 1;
+	if(dwc_otg_is_device_mode(core_if))
+	{
+		/** @todo: The port interrupt occurs while in device
+		 * mode. Added code to CIL to clear the interrupt for now!
+		 */
+		gintmsk_common.b.portintr = 1;
+	}
+	if(fiq_enable) {
+		local_fiq_disable();
+		fiq_fsm_spin_lock(&hcd->fiq_state->lock);
+		gintsts.d32 = DWC_READ_REG32(&core_if->core_global_regs->gintsts);
+		gintmsk.d32 = DWC_READ_REG32(&core_if->core_global_regs->gintmsk);
+		/* Pull in the interrupts that the FIQ has masked */
+		gintmsk.d32 |= ~(hcd->fiq_state->gintmsk_saved.d32);
+		gintmsk.d32 |= gintmsk_common.d32;
+		/* for the upstairs function to reenable - have to read it here in case FIQ triggers again */
+		reenable_gintmsk->d32 = gintmsk.d32;
+		fiq_fsm_spin_unlock(&hcd->fiq_state->lock);
+		local_fiq_enable();
+	} else {
+		gintsts.d32 = DWC_READ_REG32(&core_if->core_global_regs->gintsts);
+		gintmsk.d32 = DWC_READ_REG32(&core_if->core_global_regs->gintmsk);
+	}
+
+	gahbcfg.d32 = DWC_READ_REG32(&core_if->core_global_regs->gahbcfg);
+
+#ifdef DEBUG
+	/* if any common interrupts set */
+	if (gintsts.d32 & gintmsk_common.d32) {
+		DWC_DEBUGPL(DBG_ANY, "common_intr: gintsts=%08x  gintmsk=%08x\n",
+			    gintsts.d32, gintmsk.d32);
+	}
+#endif
+	if (!fiq_enable){
+		if (gahbcfg.b.glblintrmsk)
+			return ((gintsts.d32 & gintmsk.d32) & gintmsk_common.d32);
+		else
+			return 0;
+	} else {
+		/* Our IRQ kicker is no longer the USB hardware, it's the MPHI interface.
+		 * Can't trust the global interrupt mask bit in this case.
+		 */
+		return ((gintsts.d32 & gintmsk.d32) & gintmsk_common.d32);
+	}
+
+}
+
+/* MACRO for clearing interupt bits in GPWRDN register */
+#define CLEAR_GPWRDN_INTR(__core_if,__intr) \
+do { \
+		gpwrdn_data_t gpwrdn = {.d32=0}; \
+		gpwrdn.b.__intr = 1; \
+		DWC_MODIFY_REG32(&__core_if->core_global_regs->gpwrdn, \
+		0, gpwrdn.d32); \
+} while (0)
+
+/**
+ * Common interrupt handler.
+ *
+ * The common interrupts are those that occur in both Host and Device mode.
+ * This handler handles the following interrupts:
+ * - Mode Mismatch Interrupt
+ * - Disconnect Interrupt
+ * - OTG Interrupt
+ * - Connector ID Status Change Interrupt
+ * - Session Request Interrupt.
+ * - Resume / Remote Wakeup Detected Interrupt.
+ * - LPM Transaction Received Interrupt
+ * - ADP Transaction Received Interrupt
+ *
+ */
+int32_t dwc_otg_handle_common_intr(void *dev)
+{
+	int retval = 0;
+	gintsts_data_t gintsts;
+	gintmsk_data_t gintmsk_reenable = { .d32 = 0 };
+	gpwrdn_data_t gpwrdn = {.d32 = 0 };
+	dwc_otg_device_t *otg_dev = dev;
+	dwc_otg_core_if_t *core_if = otg_dev->core_if;
+	gpwrdn.d32 = DWC_READ_REG32(&core_if->core_global_regs->gpwrdn);
+	if (dwc_otg_is_device_mode(core_if))
+		core_if->frame_num = dwc_otg_get_frame_number(core_if);
+
+	if (core_if->lock)
+		DWC_SPINLOCK(core_if->lock);
+
+	if (core_if->power_down == 3 && core_if->xhib == 1) {
+		DWC_DEBUGPL(DBG_ANY, "Exiting from xHIB state\n");
+		retval |= dwc_otg_handle_xhib_exit_intr(core_if);
+		core_if->xhib = 2;
+		if (core_if->lock)
+			DWC_SPINUNLOCK(core_if->lock);
+
+		return retval;
+	}
+
+	if (core_if->hibernation_suspend <= 0) {
+		/* read_common will have to poke the FIQ's saved mask. We must then clear this mask at the end
+		 * of this handler - god only knows why it's done like this
+		 */
+		gintsts.d32 = dwc_otg_read_common_intr(core_if, &gintmsk_reenable, otg_dev->hcd);
+
+		if (gintsts.b.modemismatch) {
+			retval |= dwc_otg_handle_mode_mismatch_intr(core_if);
+		}
+		if (gintsts.b.otgintr) {
+			retval |= dwc_otg_handle_otg_intr(core_if);
+		}
+		if (gintsts.b.conidstschng) {
+			retval |=
+			    dwc_otg_handle_conn_id_status_change_intr(core_if);
+		}
+		if (gintsts.b.disconnect) {
+			retval |= dwc_otg_handle_disconnect_intr(core_if);
+		}
+		if (gintsts.b.sessreqintr) {
+			retval |= dwc_otg_handle_session_req_intr(core_if);
+		}
+		if (gintsts.b.wkupintr) {
+			retval |= dwc_otg_handle_wakeup_detected_intr(core_if);
+		}
+		if (gintsts.b.usbsuspend) {
+			retval |= dwc_otg_handle_usb_suspend_intr(core_if);
+		}
+#ifdef CONFIG_USB_DWC_OTG_LPM
+		if (gintsts.b.lpmtranrcvd) {
+			retval |= dwc_otg_handle_lpm_intr(core_if);
+		}
+#endif
+		if (gintsts.b.restoredone) {
+			gintsts.d32 = 0;
+	                if (core_if->power_down == 2)
+				core_if->hibernation_suspend = -1;
+			else if (core_if->power_down == 3 && core_if->xhib == 2) {
+				gpwrdn_data_t gpwrdn = {.d32 = 0 };
+				pcgcctl_data_t pcgcctl = {.d32 = 0 };
+				dctl_data_t dctl = {.d32 = 0 };
+
+				DWC_WRITE_REG32(&core_if->core_global_regs->
+						gintsts, 0xFFFFFFFF);
+
+				DWC_DEBUGPL(DBG_ANY,
+					    "RESTORE DONE generated\n");
+
+				gpwrdn.b.restore = 1;
+				DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
+				dwc_udelay(10);
+
+				pcgcctl.b.rstpdwnmodule = 1;
+				DWC_MODIFY_REG32(core_if->pcgcctl, pcgcctl.d32, 0);
+
+				DWC_WRITE_REG32(&core_if->core_global_regs->gusbcfg, core_if->gr_backup->gusbcfg_local);
+				DWC_WRITE_REG32(&core_if->dev_if->dev_global_regs->dcfg, core_if->dr_backup->dcfg);
+				DWC_WRITE_REG32(&core_if->dev_if->dev_global_regs->dctl, core_if->dr_backup->dctl);
+				dwc_udelay(50);
+
+				dctl.b.pwronprgdone = 1;
+				DWC_MODIFY_REG32(&core_if->dev_if->dev_global_regs->dctl, 0, dctl.d32);
+				dwc_udelay(10);
+
+				dwc_otg_restore_global_regs(core_if);
+				dwc_otg_restore_dev_regs(core_if, 0);
+
+				dctl.d32 = 0;
+				dctl.b.pwronprgdone = 1;
+				DWC_MODIFY_REG32(&core_if->dev_if->dev_global_regs->dctl, dctl.d32, 0);
+				dwc_udelay(10);
+
+				pcgcctl.d32 = 0;
+				pcgcctl.b.enbl_extnd_hiber = 1;
+				DWC_MODIFY_REG32(core_if->pcgcctl, pcgcctl.d32, 0);
+
+				/* The core will be in ON STATE */
+				core_if->lx_state = DWC_OTG_L0;
+				core_if->xhib = 0;
+
+				DWC_SPINUNLOCK(core_if->lock);
+				if (core_if->pcd_cb && core_if->pcd_cb->resume_wakeup) {
+					core_if->pcd_cb->resume_wakeup(core_if->pcd_cb->p);
+				}
+				DWC_SPINLOCK(core_if->lock);
+
+			}
+
+			gintsts.b.restoredone = 1;
+			DWC_WRITE_REG32(&core_if->core_global_regs->gintsts,gintsts.d32);
+			DWC_PRINTF(" --Restore done interrupt received-- \n");
+			retval |= 1;
+		}
+		if (gintsts.b.portintr && dwc_otg_is_device_mode(core_if)) {
+			/* The port interrupt occurs while in device mode with HPRT0
+			 * Port Enable/Disable.
+			 */
+			gintsts.d32 = 0;
+			gintsts.b.portintr = 1;
+			DWC_WRITE_REG32(&core_if->core_global_regs->gintsts,gintsts.d32);
+			retval |= 1;
+			gintmsk_reenable.b.portintr = 1;
+
+		}
+		/* Did we actually handle anything? if so, unmask the interrupt */
+//		fiq_print(FIQDBG_INT, otg_dev->hcd->fiq_state, "CILOUT %1d", retval);
+//		fiq_print(FIQDBG_INT, otg_dev->hcd->fiq_state, "%08x", gintsts.d32);
+//		fiq_print(FIQDBG_INT, otg_dev->hcd->fiq_state, "%08x", gintmsk_reenable.d32);
+		if (retval && fiq_enable) {
+			DWC_WRITE_REG32(&core_if->core_global_regs->gintmsk, gintmsk_reenable.d32);
+		}
+
+	} else {
+		DWC_DEBUGPL(DBG_ANY, "gpwrdn=%08x\n", gpwrdn.d32);
+
+		if (gpwrdn.b.disconn_det && gpwrdn.b.disconn_det_msk) {
+			CLEAR_GPWRDN_INTR(core_if, disconn_det);
+			if (gpwrdn.b.linestate == 0) {
+				dwc_otg_handle_pwrdn_disconnect_intr(core_if);
+			} else {
+				DWC_PRINTF("Disconnect detected while linestate is not 0\n");
+			}
+
+			retval |= 1;
+		}
+		if (gpwrdn.b.lnstschng && gpwrdn.b.lnstchng_msk) {
+			CLEAR_GPWRDN_INTR(core_if, lnstschng);
+			/* remote wakeup from hibernation */
+			if (gpwrdn.b.linestate == 2 || gpwrdn.b.linestate == 1) {
+				dwc_otg_handle_pwrdn_wakeup_detected_intr(core_if);
+			} else {
+				DWC_PRINTF("gpwrdn.linestate = %d\n", gpwrdn.b.linestate);
+			}
+			retval |= 1;
+		}
+		if (gpwrdn.b.rst_det && gpwrdn.b.rst_det_msk) {
+			CLEAR_GPWRDN_INTR(core_if, rst_det);
+			if (gpwrdn.b.linestate == 0) {
+				DWC_PRINTF("Reset detected\n");
+				retval |= dwc_otg_device_hibernation_restore(core_if, 0, 1);
+			}
+		}
+		if (gpwrdn.b.srp_det && gpwrdn.b.srp_det_msk) {
+			CLEAR_GPWRDN_INTR(core_if, srp_det);
+			dwc_otg_handle_pwrdn_srp_intr(core_if);
+			retval |= 1;
+		}
+	}
+	/* Handle ADP interrupt here */
+	if (gpwrdn.b.adp_int) {
+		DWC_PRINTF("ADP interrupt\n");
+		CLEAR_GPWRDN_INTR(core_if, adp_int);
+		dwc_otg_adp_handle_intr(core_if);
+		retval |= 1;
+	}
+	if (gpwrdn.b.sts_chngint && gpwrdn.b.sts_chngint_msk) {
+		DWC_PRINTF("STS CHNG interrupt asserted\n");
+		CLEAR_GPWRDN_INTR(core_if, sts_chngint);
+		dwc_otg_handle_pwrdn_stschng_intr(otg_dev);
+
+		retval |= 1;
+	}
+	if (core_if->lock)
+		DWC_SPINUNLOCK(core_if->lock);
+	return retval;
+}
diff -Nur --no-dereference linux-5.10.52-orig/drivers/usb/host/dwc_otg/dwc_otg_core_if.h linux-5.10.52-v7l+/drivers/usb/host/dwc_otg/dwc_otg_core_if.h
--- linux-5.10.52-orig/drivers/usb/host/dwc_otg/dwc_otg_core_if.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/usb/host/dwc_otg/dwc_otg_core_if.h	2021-07-25 16:46:28.677910113 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/* ==========================================================================
+ * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_core_if.h $
+ * $Revision: #13 $
+ * $Date: 2012/08/10 $
+ * $Change: 2047372 $
+ *
+ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter,
+ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless
+ * otherwise expressly agreed to in writing between Synopsys and you.
+ *
+ * The Software IS NOT an item of Licensed Software or Licensed Product under
+ * any End User Software License Agreement or Agreement for Licensed Product
+ * with Synopsys or any supplement thereto. You are permitted to use and
+ * redistribute this Software in source and binary forms, with or without
+ * modification, provided that redistributions of source code must retain this
+ * notice. You may not view, use, disclose, copy or distribute this file or
+ * any information contained herein except pursuant to this license grant from
+ * Synopsys. If you do not agree with this notice, including the disclaimer
+ * below, then you are not authorized to use the Software.
+ *
+ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * ========================================================================== */
+#if !defined(__DWC_CORE_IF_H__)
+#define __DWC_CORE_IF_H__
+
+#include "dwc_os.h"
+
+/** @file
+ * This file defines DWC_OTG Core API
+ */
+
+struct dwc_otg_core_if;
+typedef struct dwc_otg_core_if dwc_otg_core_if_t;
+
+/** Maximum number of Periodic FIFOs */
+#define MAX_PERIO_FIFOS 15
+/** Maximum number of Periodic FIFOs */
+#define MAX_TX_FIFOS 15
+
+/** Maximum number of Endpoints/HostChannels */
+#define MAX_EPS_CHANNELS 16
+
+extern dwc_otg_core_if_t *dwc_otg_cil_init(const uint32_t * _reg_base_addr);
+extern void dwc_otg_core_init(dwc_otg_core_if_t * _core_if);
+extern void dwc_otg_cil_remove(dwc_otg_core_if_t * _core_if);
+
+extern void dwc_otg_enable_global_interrupts(dwc_otg_core_if_t * _core_if);
+extern void dwc_otg_disable_global_interrupts(dwc_otg_core_if_t * _core_if);
+
+extern uint8_t dwc_otg_is_device_mode(dwc_otg_core_if_t * _core_if);
+extern uint8_t dwc_otg_is_host_mode(dwc_otg_core_if_t * _core_if);
+
+extern uint8_t dwc_otg_is_dma_enable(dwc_otg_core_if_t * core_if);
+
+/** This function should be called on every hardware interrupt. */
+extern int32_t dwc_otg_handle_common_intr(void *otg_dev);
+
+/** @name OTG Core Parameters */
+/** @{ */
+
+/**
+ * Specifies the OTG capabilities. The driver will automatically
+ * detect the value for this parameter if none is specified.
+ * 0 - HNP and SRP capable (default)
+ * 1 - SRP Only capable
+ * 2 - No HNP/SRP capable
+ */
+extern int dwc_otg_set_param_otg_cap(dwc_otg_core_if_t * core_if, int32_t val);
+extern int32_t dwc_otg_get_param_otg_cap(dwc_otg_core_if_t * core_if);
+#define DWC_OTG_CAP_PARAM_HNP_SRP_CAPABLE 0
+#define DWC_OTG_CAP_PARAM_SRP_ONLY_CAPABLE 1
+#define DWC_OTG_CAP_PARAM_NO_HNP_SRP_CAPABLE 2
+#define dwc_param_otg_cap_default DWC_OTG_CAP_PARAM_HNP_SRP_CAPABLE
+
+extern int dwc_otg_set_param_opt(dwc_otg_core_if_t * core_if, int32_t val);
+extern int32_t dwc_otg_get_param_opt(dwc_otg_core_if_t * core_if);
+#define dwc_param_opt_default 1
+
+/**
+ * Specifies whether to use slave or DMA mode for accessing the data
+ * FIFOs. The driver will automatically detect the value for this
+ * parameter if none is specified.
+ * 0 - Slave
+ * 1 - DMA (default, if available)
+ */
+extern int dwc_otg_set_param_dma_enable(dwc_otg_core_if_t * core_if,
+					int32_t val);
+extern int32_t dwc_otg_get_param_dma_enable(dwc_otg_core_if_t * core_if);
+#define dwc_param_dma_enable_default 1
+
+/**
+ * When DMA mode is enabled specifies whether to use
+ * address DMA or DMA Descritor mode for accessing the data
+ * FIFOs in device mode. The driver will automatically detect
+ * the value for this parameter if none is specified.
+ * 0 - address DMA
+ * 1 - DMA Descriptor(default, if available)
+ */
+extern int dwc_otg_set_param_dma_desc_enable(dwc_otg_core_if_t * core_if,
+					     int32_t val);
+extern int32_t dwc_otg_get_param_dma_desc_enable(dwc_otg_core_if_t * core_if);
+//#define dwc_param_dma_desc_enable_default 1
+#define dwc_param_dma_desc_enable_default 0 // Broadcom BCM2708
+
+/** The DMA Burst size (applicable only for External DMA
+ * Mode). 1, 4, 8 16, 32, 64, 128, 256 (default 32)
+ */
+extern int dwc_otg_set_param_dma_burst_size(dwc_otg_core_if_t * core_if,
+					    int32_t val);
+extern int32_t dwc_otg_get_param_dma_burst_size(dwc_otg_core_if_t * core_if);
+#define dwc_param_dma_burst_size_default 32
+
+/**
+ * Specifies the maximum speed of operation in host and device mode.
+ * The actual speed depends on the speed of the attached device and
+ * the value of phy_type. The actual speed depends on the speed of the
+ * attached device.
+ * 0 - High Speed (default)
+ * 1 - Full Speed
+ */
+extern int dwc_otg_set_param_speed(dwc_otg_core_if_t * core_if, int32_t val);
+extern int32_t dwc_otg_get_param_speed(dwc_otg_core_if_t * core_if);
+#define dwc_param_speed_default 0
+#define DWC_SPEED_PARAM_HIGH 0
+#define DWC_SPEED_PARAM_FULL 1
+
+/** Specifies whether low power mode is supported when attached
+ *	to a Full Speed or Low Speed device in host mode.
+ * 0 - Don't support low power mode (default)
+ * 1 - Support low power mode
+ */
+extern int dwc_otg_set_param_host_support_fs_ls_low_power(dwc_otg_core_if_t *
+							  core_if, int32_t val);
+extern int32_t dwc_otg_get_param_host_support_fs_ls_low_power(dwc_otg_core_if_t
+							      * core_if);
+#define dwc_param_host_support_fs_ls_low_power_default 0
+
+/** Specifies the PHY clock rate in low power mode when connected to a
+ * Low Speed device in host mode. This parameter is applicable only if
+ * HOST_SUPPORT_FS_LS_LOW_POWER is enabled. If PHY_TYPE is set to FS
+ * then defaults to 6 MHZ otherwise 48 MHZ.
+ *
+ * 0 - 48 MHz
+ * 1 - 6 MHz
+ */
+extern int dwc_otg_set_param_host_ls_low_power_phy_clk(dwc_otg_core_if_t *
+						       core_if, int32_t val);
+extern int32_t dwc_otg_get_param_host_ls_low_power_phy_clk(dwc_otg_core_if_t *
+							   core_if);
+#define dwc_param_host_ls_low_power_phy_clk_default 0
+#define DWC_HOST_LS_LOW_POWER_PHY_CLK_PARAM_48MHZ 0
+#define DWC_HOST_LS_LOW_POWER_PHY_CLK_PARAM_6MHZ 1
+
+/**
+ * 0 - Use cC FIFO size parameters
+ * 1 - Allow dynamic FIFO sizing (default)
+ */
+extern int dwc_otg_set_param_enable_dynamic_fifo(dwc_otg_core_if_t * core_if,
+						 int32_t val);
+extern int32_t dwc_otg_get_param_enable_dynamic_fifo(dwc_otg_core_if_t *
+						     core_if);
+#define dwc_param_enable_dynamic_fifo_default 1
+
+/** Total number of 4-byte words in the data FIFO memory. This
+ * memory includes the Rx FIFO, non-periodic Tx FIFO, and periodic
+ * Tx FIFOs.
+ * 32 to 32768 (default 8192)
+ * Note: The total FIFO memory depth in the FPGA configuration is 8192.
+ */
+extern int dwc_otg_set_param_data_fifo_size(dwc_otg_core_if_t * core_if,
+					    int32_t val);
+extern int32_t dwc_otg_get_param_data_fifo_size(dwc_otg_core_if_t * core_if);
+//#define dwc_param_data_fifo_size_default 8192
+#define dwc_param_data_fifo_size_default 0xFF0 // Broadcom BCM2708
+
+/** Number of 4-byte words in the Rx FIFO in device mode when dynamic
+ * FIFO sizing is enabled.
+ * 16 to 32768 (default 1064)
+ */
+extern int dwc_otg_set_param_dev_rx_fifo_size(dwc_otg_core_if_t * core_if,
+					      int32_t val);
+extern int32_t dwc_otg_get_param_dev_rx_fifo_size(dwc_otg_core_if_t * core_if);
+#define dwc_param_dev_rx_fifo_size_default 1064
+
+/** Number of 4-byte words in the non-periodic Tx FIFO in device mode
+ * when dynamic FIFO sizing is enabled.
+ * 16 to 32768 (default 1024)
+ */
+extern int dwc_otg_set_param_dev_nperio_tx_fifo_size(dwc_otg_core_if_t *
+						     core_if, int32_t val);
+extern int32_t dwc_otg_get_param_dev_nperio_tx_fifo_size(dwc_otg_core_if_t *
+							 core_if);
+#define dwc_param_dev_nperio_tx_fifo_size_default 1024
+
+/** Number of 4-byte words in each of the periodic Tx FIFOs in device
+ * mode when dynamic FIFO sizing is enabled.
+ * 4 to 768 (default 256)
+ */
+extern int dwc_otg_set_param_dev_perio_tx_fifo_size(dwc_otg_core_if_t * core_if,
+						    int32_t val, int fifo_num);
+extern int32_t dwc_otg_get_param_dev_perio_tx_fifo_size(dwc_otg_core_if_t *
+							core_if, int fifo_num);
+#define dwc_param_dev_perio_tx_fifo_size_default 256
+
+/** Number of 4-byte words in the Rx FIFO in host mode when dynamic
+ * FIFO sizing is enabled.
+ * 16 to 32768 (default 1024)
+ */
+extern int dwc_otg_set_param_host_rx_fifo_size(dwc_otg_core_if_t * core_if,
+					       int32_t val);
+extern int32_t dwc_otg_get_param_host_rx_fifo_size(dwc_otg_core_if_t * core_if);
+//#define dwc_param_host_rx_fifo_size_default 1024
+#define dwc_param_host_rx_fifo_size_default 774 // Broadcom BCM2708
+
+/** Number of 4-byte words in the non-periodic Tx FIFO in host mode
+ * when Dynamic FIFO sizing is enabled in the core.
+ * 16 to 32768 (default 1024)
+ */
+extern int dwc_otg_set_param_host_nperio_tx_fifo_size(dwc_otg_core_if_t *
+						      core_if, int32_t val);
+extern int32_t dwc_otg_get_param_host_nperio_tx_fifo_size(dwc_otg_core_if_t *
+							  core_if);
+//#define dwc_param_host_nperio_tx_fifo_size_default 1024
+#define dwc_param_host_nperio_tx_fifo_size_default 0x100 // Broadcom BCM2708
+
+/** Number of 4-byte words in the host periodic Tx FIFO when dynamic
+ * FIFO sizing is enabled.
+ * 16 to 32768 (default 1024)
+ */
+extern int dwc_otg_set_param_host_perio_tx_fifo_size(dwc_otg_core_if_t *
+						     core_if, int32_t val);
+extern int32_t dwc_otg_get_param_host_perio_tx_fifo_size(dwc_otg_core_if_t *
+							 core_if);
+//#define dwc_param_host_perio_tx_fifo_size_default 1024
+#define dwc_param_host_perio_tx_fifo_size_default 0x200 // Broadcom BCM2708
+
+/** The maximum transfer size supported in bytes.
+ * 2047 to 65,535  (default 65,535)
+ */
+extern int dwc_otg_set_param_max_transfer_size(dwc_otg_core_if_t * core_if,
+					       int32_t val);
+extern int32_t dwc_otg_get_param_max_transfer_size(dwc_otg_core_if_t * core_if);
+#define dwc_param_max_transfer_size_default 65535
+
+/** The maximum number of packets in a transfer.
+ * 15 to 511  (default 511)
+ */
+extern int dwc_otg_set_param_max_packet_count(dwc_otg_core_if_t * core_if,
+					      int32_t val);
+extern int32_t dwc_otg_get_param_max_packet_count(dwc_otg_core_if_t * core_if);
+#define dwc_param_max_packet_count_default 511
+
+/** The number of host channel registers to use.
+ * 1 to 16 (default 12)
+ * Note: The FPGA configuration supports a maximum of 12 host channels.
+ */
+extern int dwc_otg_set_param_host_channels(dwc_otg_core_if_t * core_if,
+					   int32_t val);
+extern int32_t dwc_otg_get_param_host_channels(dwc_otg_core_if_t * core_if);
+//#define dwc_param_host_channels_default 12
+#define dwc_param_host_channels_default 8 // Broadcom BCM2708
+
+/** The number of endpoints in addition to EP0 available for device
+ * mode operations.
+ * 1 to 15 (default 6 IN and OUT)
+ * Note: The FPGA configuration supports a maximum of 6 IN and OUT
+ * endpoints in addition to EP0.
+ */
+extern int dwc_otg_set_param_dev_endpoints(dwc_otg_core_if_t * core_if,
+					   int32_t val);
+extern int32_t dwc_otg_get_param_dev_endpoints(dwc_otg_core_if_t * core_if);
+#define dwc_param_dev_endpoints_default 6
+
+/**
+ * Specifies the type of PHY interface to use. By default, the driver
+ * will automatically detect the phy_type.
+ *
+ * 0 - Full Speed PHY
+ * 1 - UTMI+ (default)
+ * 2 - ULPI
+ */
+extern int dwc_otg_set_param_phy_type(dwc_otg_core_if_t * core_if, int32_t val);
+extern int32_t dwc_otg_get_param_phy_type(dwc_otg_core_if_t * core_if);
+#define DWC_PHY_TYPE_PARAM_FS 0
+#define DWC_PHY_TYPE_PARAM_UTMI 1
+#define DWC_PHY_TYPE_PARAM_ULPI 2
+#define dwc_param_phy_type_default DWC_PHY_TYPE_PARAM_UTMI
+
+/**
+ * Specifies the UTMI+ Data Width. This parameter is
+ * applicable for a PHY_TYPE of UTMI+ or ULPI. (For a ULPI
+ * PHY_TYPE, this parameter indicates the data width between
+ * the MAC and the ULPI Wrapper.) Also, this parameter is
+ * applicable only if the OTG_HSPHY_WIDTH cC parameter was set
+ * to "8 and 16 bits", meaning that the core has been
+ * configured to work at either data path width.
+ *
+ * 8 or 16 bits (default 16)
+ */
+extern int dwc_otg_set_param_phy_utmi_width(dwc_otg_core_if_t * core_if,
+					    int32_t val);
+extern int32_t dwc_otg_get_param_phy_utmi_width(dwc_otg_core_if_t * core_if);
+//#define dwc_param_phy_utmi_width_default 16
+#define dwc_param_phy_utmi_width_default 8 // Broadcom BCM2708
+
+/**
+ * Specifies whether the ULPI operates at double or single
+ * data rate. This parameter is only applicable if PHY_TYPE is
+ * ULPI.
+ *
+ * 0 - single data rate ULPI interface with 8 bit wide data
+ * bus (default)
+ * 1 - double data rate ULPI interface with 4 bit wide data
+ * bus
+ */
+extern int dwc_otg_set_param_phy_ulpi_ddr(dwc_otg_core_if_t * core_if,
+					  int32_t val);
+extern int32_t dwc_otg_get_param_phy_ulpi_ddr(dwc_otg_core_if_t * core_if);
+#define dwc_param_phy_ulpi_ddr_default 0
+
+/**
+ * Specifies whether to use the internal or external supply to
+ * drive the vbus with a ULPI phy.
+ */
+extern int dwc_otg_set_param_phy_ulpi_ext_vbus(dwc_otg_core_if_t * core_if,
+					       int32_t val);
+extern int32_t dwc_otg_get_param_phy_ulpi_ext_vbus(dwc_otg_core_if_t * core_if);
+#define DWC_PHY_ULPI_INTERNAL_VBUS 0
+#define DWC_PHY_ULPI_EXTERNAL_VBUS 1
+#define dwc_param_phy_ulpi_ext_vbus_default DWC_PHY_ULPI_INTERNAL_VBUS
+
+/**
+ * Specifies whether to use the I2Cinterface for full speed PHY. This
+ * parameter is only applicable if PHY_TYPE is FS.
+ * 0 - No (default)
+ * 1 - Yes
+ */
+extern int dwc_otg_set_param_i2c_enable(dwc_otg_core_if_t * core_if,
+					int32_t val);
+extern int32_t dwc_otg_get_param_i2c_enable(dwc_otg_core_if_t * core_if);
+#define dwc_param_i2c_enable_default 0
+
+extern int dwc_otg_set_param_ulpi_fs_ls(dwc_otg_core_if_t * core_if,
+					int32_t val);
+extern int32_t dwc_otg_get_param_ulpi_fs_ls(dwc_otg_core_if_t * core_if);
+#define dwc_param_ulpi_fs_ls_default 0
+
+extern int dwc_otg_set_param_ts_dline(dwc_otg_core_if_t * core_if, int32_t val);
+extern int32_t dwc_otg_get_param_ts_dline(dwc_otg_core_if_t * core_if);
+#define dwc_param_ts_dline_default 0
+
+/**
+ * Specifies whether dedicated transmit FIFOs are
+ * enabled for non periodic IN endpoints in device mode
+ * 0 - No
+ * 1 - Yes
+ */
+extern int dwc_otg_set_param_en_multiple_tx_fifo(dwc_otg_core_if_t * core_if,
+						 int32_t val);
+extern int32_t dwc_otg_get_param_en_multiple_tx_fifo(dwc_otg_core_if_t *
+						     core_if);
+#define dwc_param_en_multiple_tx_fifo_default 1
+
+/** Number of 4-byte words in each of the Tx FIFOs in device
+ * mode when dynamic FIFO sizing is enabled.
+ * 4 to 768 (default 256)
+ */
+extern int dwc_otg_set_param_dev_tx_fifo_size(dwc_otg_core_if_t * core_if,
+					      int fifo_num, int32_t val);
+extern int32_t dwc_otg_get_param_dev_tx_fifo_size(dwc_otg_core_if_t * core_if,
+						  int fifo_num);
+#define dwc_param_dev_tx_fifo_size_default 768
+
+/** Thresholding enable flag-
+ * bit 0 - enable non-ISO Tx thresholding
+ * bit 1 - enable ISO Tx thresholding
+ * bit 2 - enable Rx thresholding
+ */
+extern int dwc_otg_set_param_thr_ctl(dwc_otg_core_if_t * core_if, int32_t val);
+extern int32_t dwc_otg_get_thr_ctl(dwc_otg_core_if_t * core_if, int fifo_num);
+#define dwc_param_thr_ctl_default 0
+
+/** Thresholding length for Tx
+ * FIFOs in 32 bit DWORDs
+ */
+extern int dwc_otg_set_param_tx_thr_length(dwc_otg_core_if_t * core_if,
+					   int32_t val);
+extern int32_t dwc_otg_get_tx_thr_length(dwc_otg_core_if_t * core_if);
+#define dwc_param_tx_thr_length_default 64
+
+/** Thresholding length for Rx
+ *	FIFOs in 32 bit DWORDs
+ */
+extern int dwc_otg_set_param_rx_thr_length(dwc_otg_core_if_t * core_if,
+					   int32_t val);
+extern int32_t dwc_otg_get_rx_thr_length(dwc_otg_core_if_t * core_if);
+#define dwc_param_rx_thr_length_default 64
+
+/**
+ * Specifies whether LPM (Link Power Management) support is enabled
+ */
+extern int dwc_otg_set_param_lpm_enable(dwc_otg_core_if_t * core_if,
+					int32_t val);
+extern int32_t dwc_otg_get_param_lpm_enable(dwc_otg_core_if_t * core_if);
+#define dwc_param_lpm_enable_default 1
+
+/**
+ * Specifies whether PTI enhancement is enabled
+ */
+extern int dwc_otg_set_param_pti_enable(dwc_otg_core_if_t * core_if,
+					int32_t val);
+extern int32_t dwc_otg_get_param_pti_enable(dwc_otg_core_if_t * core_if);
+#define dwc_param_pti_enable_default 0
+
+/**
+ * Specifies whether MPI enhancement is enabled
+ */
+extern int dwc_otg_set_param_mpi_enable(dwc_otg_core_if_t * core_if,
+					int32_t val);
+extern int32_t dwc_otg_get_param_mpi_enable(dwc_otg_core_if_t * core_if);
+#define dwc_param_mpi_enable_default 0
+
+/**
+ * Specifies whether ADP capability is enabled
+ */
+extern int dwc_otg_set_param_adp_enable(dwc_otg_core_if_t * core_if,
+					int32_t val);
+extern int32_t dwc_otg_get_param_adp_enable(dwc_otg_core_if_t * core_if);
+#define dwc_param_adp_enable_default 0
+
+/**
+ * Specifies whether IC_USB capability is enabled
+ */
+
+extern int dwc_otg_set_param_ic_usb_cap(dwc_otg_core_if_t * core_if,
+					int32_t val);
+extern int32_t dwc_otg_get_param_ic_usb_cap(dwc_otg_core_if_t * core_if);
+#define dwc_param_ic_usb_cap_default 0
+
+extern int dwc_otg_set_param_ahb_thr_ratio(dwc_otg_core_if_t * core_if,
+					   int32_t val);
+extern int32_t dwc_otg_get_param_ahb_thr_ratio(dwc_otg_core_if_t * core_if);
+#define dwc_param_ahb_thr_ratio_default 0
+
+extern int dwc_otg_set_param_power_down(dwc_otg_core_if_t * core_if,
+					int32_t val);
+extern int32_t dwc_otg_get_param_power_down(dwc_otg_core_if_t * core_if);
+#define dwc_param_power_down_default 0
+
+extern int dwc_otg_set_param_reload_ctl(dwc_otg_core_if_t * core_if,
+					int32_t val);
+extern int32_t dwc_otg_get_param_reload_ctl(dwc_otg_core_if_t * core_if);
+#define dwc_param_reload_ctl_default 0
+
+extern int dwc_otg_set_param_dev_out_nak(dwc_otg_core_if_t * core_if,
+										int32_t val);
+extern int32_t dwc_otg_get_param_dev_out_nak(dwc_otg_core_if_t * core_if);
+#define dwc_param_dev_out_nak_default 0
+
+extern int dwc_otg_set_param_cont_on_bna(dwc_otg_core_if_t * core_if,
+										 int32_t val);
+extern int32_t dwc_otg_get_param_cont_on_bna(dwc_otg_core_if_t * core_if);
+#define dwc_param_cont_on_bna_default 0
+
+extern int dwc_otg_set_param_ahb_single(dwc_otg_core_if_t * core_if,
+										 int32_t val);
+extern int32_t dwc_otg_get_param_ahb_single(dwc_otg_core_if_t * core_if);
+#define dwc_param_ahb_single_default 0
+
+extern int dwc_otg_set_param_otg_ver(dwc_otg_core_if_t * core_if, int32_t val);
+extern int32_t dwc_otg_get_param_otg_ver(dwc_otg_core_if_t * core_if);
+#define dwc_param_otg_ver_default 0
+
+/** @} */
+
+/** @name Access to registers and bit-fields */
+
+/**
+ * Dump core registers and SPRAM
+ */
+extern void dwc_otg_dump_dev_registers(dwc_otg_core_if_t * _core_if);
+extern void dwc_otg_dump_spram(dwc_otg_core_if_t * _core_if);
+extern void dwc_otg_dump_host_registers(dwc_otg_core_if_t * _core_if);
+extern void dwc_otg_dump_global_registers(dwc_otg_core_if_t * _core_if);
+
+/**
+ * Get host negotiation status.
+ */
+extern uint32_t dwc_otg_get_hnpstatus(dwc_otg_core_if_t * core_if);
+
+/**
+ * Get srp status
+ */
+extern uint32_t dwc_otg_get_srpstatus(dwc_otg_core_if_t * core_if);
+
+/**
+ * Set hnpreq bit in the GOTGCTL register.
+ */
+extern void dwc_otg_set_hnpreq(dwc_otg_core_if_t * core_if, uint32_t val);
+
+/**
+ * Get Content of SNPSID register.
+ */
+extern uint32_t dwc_otg_get_gsnpsid(dwc_otg_core_if_t * core_if);
+
+/**
+ * Get current mode.
+ * Returns 0 if in device mode, and 1 if in host mode.
+ */
+extern uint32_t dwc_otg_get_mode(dwc_otg_core_if_t * core_if);
+
+/**
+ * Get value of hnpcapable field in the GUSBCFG register
+ */
+extern uint32_t dwc_otg_get_hnpcapable(dwc_otg_core_if_t * core_if);
+/**
+ * Set value of hnpcapable field in the GUSBCFG register
+ */
+extern void dwc_otg_set_hnpcapable(dwc_otg_core_if_t * core_if, uint32_t val);
+
+/**
+ * Get value of srpcapable field in the GUSBCFG register
+ */
+extern uint32_t dwc_otg_get_srpcapable(dwc_otg_core_if_t * core_if);
+/**
+ * Set value of srpcapable field in the GUSBCFG register
+ */
+extern void dwc_otg_set_srpcapable(dwc_otg_core_if_t * core_if, uint32_t val);
+
+/**
+ * Get value of devspeed field in the DCFG register
+ */
+extern uint32_t dwc_otg_get_devspeed(dwc_otg_core_if_t * core_if);
+/**
+ * Set value of devspeed field in the DCFG register
+ */
+extern void dwc_otg_set_devspeed(dwc_otg_core_if_t * core_if, uint32_t val);
+
+/**
+ * Get the value of busconnected field from the HPRT0 register
+ */
+extern uint32_t dwc_otg_get_busconnected(dwc_otg_core_if_t * core_if);
+
+/**
+ * Gets the device enumeration Speed.
+ */
+extern uint32_t dwc_otg_get_enumspeed(dwc_otg_core_if_t * core_if);
+
+/**
+ * Get value of prtpwr field from the HPRT0 register
+ */
+extern uint32_t dwc_otg_get_prtpower(dwc_otg_core_if_t * core_if);
+
+/**
+ * Get value of flag indicating core state - hibernated or not
+ */
+extern uint32_t dwc_otg_get_core_state(dwc_otg_core_if_t * core_if);
+
+/**
+ * Set value of prtpwr field from the HPRT0 register
+ */
+extern void dwc_otg_set_prtpower(dwc_otg_core_if_t * core_if, uint32_t val);
+
+/**
+ * Get value of prtsusp field from the HPRT0 regsiter
+ */
+extern uint32_t dwc_otg_get_prtsuspend(dwc_otg_core_if_t * core_if);
+/**
+ * Set value of prtpwr field from the HPRT0 register
+ */
+extern void dwc_otg_set_prtsuspend(dwc_otg_core_if_t * core_if, uint32_t val);
+
+/**
+ * Get value of ModeChTimEn field from the HCFG regsiter
+ */
+extern uint32_t dwc_otg_get_mode_ch_tim(dwc_otg_core_if_t * core_if);
+/**
+ * Set value of ModeChTimEn field from the HCFG regsiter
+ */
+extern void dwc_otg_set_mode_ch_tim(dwc_otg_core_if_t * core_if, uint32_t val);
+
+/**
+ * Get value of Fram Interval field from the HFIR regsiter
+ */
+extern uint32_t dwc_otg_get_fr_interval(dwc_otg_core_if_t * core_if);
+/**
+ * Set value of Frame Interval field from the HFIR regsiter
+ */
+extern void dwc_otg_set_fr_interval(dwc_otg_core_if_t * core_if, uint32_t val);
+
+/**
+ * Set value of prtres field from the HPRT0 register
+ *FIXME Remove?
+ */
+extern void dwc_otg_set_prtresume(dwc_otg_core_if_t * core_if, uint32_t val);
+
+/**
+ * Get value of rmtwkupsig bit in DCTL register
+ */
+extern uint32_t dwc_otg_get_remotewakesig(dwc_otg_core_if_t * core_if);
+
+/**
+ * Get value of prt_sleep_sts field from the GLPMCFG register
+ */
+extern uint32_t dwc_otg_get_lpm_portsleepstatus(dwc_otg_core_if_t * core_if);
+
+/**
+ * Get value of rem_wkup_en field from the GLPMCFG register
+ */
+extern uint32_t dwc_otg_get_lpm_remotewakeenabled(dwc_otg_core_if_t * core_if);
+
+/**
+ * Get value of appl_resp field from the GLPMCFG register
+ */
+extern uint32_t dwc_otg_get_lpmresponse(dwc_otg_core_if_t * core_if);
+/**
+ * Set value of appl_resp field from the GLPMCFG register
+ */
+extern void dwc_otg_set_lpmresponse(dwc_otg_core_if_t * core_if, uint32_t val);
+
+/**
+ * Get value of hsic_connect field from the GLPMCFG register
+ */
+extern uint32_t dwc_otg_get_hsic_connect(dwc_otg_core_if_t * core_if);
+/**
+ * Set value of hsic_connect field from the GLPMCFG register
+ */
+extern void dwc_otg_set_hsic_connect(dwc_otg_core_if_t * core_if, uint32_t val);
+
+/**
+ * Get value of inv_sel_hsic field from the GLPMCFG register.
+ */
+extern uint32_t dwc_otg_get_inv_sel_hsic(dwc_otg_core_if_t * core_if);
+/**
+ * Set value of inv_sel_hsic field from the GLPMFG register.
+ */
+extern void dwc_otg_set_inv_sel_hsic(dwc_otg_core_if_t * core_if, uint32_t val);
+
+/*
+ * Some functions for accessing registers
+ */
+
+/**
+ *  GOTGCTL register
+ */
+extern uint32_t dwc_otg_get_gotgctl(dwc_otg_core_if_t * core_if);
+extern void dwc_otg_set_gotgctl(dwc_otg_core_if_t * core_if, uint32_t val);
+
+/**
+ * GUSBCFG register
+ */
+extern uint32_t dwc_otg_get_gusbcfg(dwc_otg_core_if_t * core_if);
+extern void dwc_otg_set_gusbcfg(dwc_otg_core_if_t * core_if, uint32_t val);
+
+/**
+ * GRXFSIZ register
+ */
+extern uint32_t dwc_otg_get_grxfsiz(dwc_otg_core_if_t * core_if);
+extern void dwc_otg_set_grxfsiz(dwc_otg_core_if_t * core_if, uint32_t val);
+
+/**
+ * GNPTXFSIZ register
+ */
+extern uint32_t dwc_otg_get_gnptxfsiz(dwc_otg_core_if_t * core_if);
+extern void dwc_otg_set_gnptxfsiz(dwc_otg_core_if_t * core_if, uint32_t val);
+
+extern uint32_t dwc_otg_get_gpvndctl(dwc_otg_core_if_t * core_if);
+extern void dwc_otg_set_gpvndctl(dwc_otg_core_if_t * core_if, uint32_t val);
+
+/**
+ * GGPIO register
+ */
+extern uint32_t dwc_otg_get_ggpio(dwc_otg_core_if_t * core_if);
+extern void dwc_otg_set_ggpio(dwc_otg_core_if_t * core_if, uint32_t val);
+
+/**
+ * GUID register
+ */
+extern uint32_t dwc_otg_get_guid(dwc_otg_core_if_t * core_if);
+extern void dwc_otg_set_guid(dwc_otg_core_if_t * core_if, uint32_t val);
+
+/**
+ * HPRT0 register
+ */
+extern uint32_t dwc_otg_get_hprt0(dwc_otg_core_if_t * core_if);
+extern void dwc_otg_set_hprt0(dwc_otg_core_if_t * core_if, uint32_t val);
+
+/**
+ * GHPTXFSIZE
+ */
+extern uint32_t dwc_otg_get_hptxfsiz(dwc_otg_core_if_t * core_if);
+
+/** @} */
+
+#endif				/* __DWC_CORE_IF_H__ */
diff -Nur --no-dereference linux-5.10.52-orig/drivers/usb/host/dwc_otg/dwc_otg_dbg.h linux-5.10.52-v7l+/drivers/usb/host/dwc_otg/dwc_otg_dbg.h
--- linux-5.10.52-orig/drivers/usb/host/dwc_otg/dwc_otg_dbg.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/usb/host/dwc_otg/dwc_otg_dbg.h	2021-07-25 16:46:28.677910113 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/* ==========================================================================
+ *
+ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter,
+ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless
+ * otherwise expressly agreed to in writing between Synopsys and you.
+ *
+ * The Software IS NOT an item of Licensed Software or Licensed Product under
+ * any End User Software License Agreement or Agreement for Licensed Product
+ * with Synopsys or any supplement thereto. You are permitted to use and
+ * redistribute this Software in source and binary forms, with or without
+ * modification, provided that redistributions of source code must retain this
+ * notice. You may not view, use, disclose, copy or distribute this file or
+ * any information contained herein except pursuant to this license grant from
+ * Synopsys. If you do not agree with this notice, including the disclaimer
+ * below, then you are not authorized to use the Software.
+ *
+ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * ========================================================================== */
+
+#ifndef __DWC_OTG_DBG_H__
+#define __DWC_OTG_DBG_H__
+
+/** @file
+ * This file defines debug levels.
+ * Debugging support vanishes in non-debug builds.
+ */
+
+/**
+ * The Debug Level bit-mask variable.
+ */
+extern uint32_t g_dbg_lvl;
+/**
+ * Set the Debug Level variable.
+ */
+static inline uint32_t SET_DEBUG_LEVEL(const uint32_t new)
+{
+	uint32_t old = g_dbg_lvl;
+	g_dbg_lvl = new;
+	return old;
+}
+
+#define DBG_USER	(0x1)
+/** When debug level has the DBG_CIL bit set, display CIL Debug messages. */
+#define DBG_CIL		(0x2)
+/** When debug level has the DBG_CILV bit set, display CIL Verbose debug
+ * messages */
+#define DBG_CILV	(0x20)
+/**  When debug level has the DBG_PCD bit set, display PCD (Device) debug
+ *  messages */
+#define DBG_PCD		(0x4)
+/** When debug level has the DBG_PCDV set, display PCD (Device) Verbose debug
+ * messages */
+#define DBG_PCDV	(0x40)
+/** When debug level has the DBG_HCD bit set, display Host debug messages */
+#define DBG_HCD		(0x8)
+/** When debug level has the DBG_HCDV bit set, display Verbose Host debug
+ * messages */
+#define DBG_HCDV	(0x80)
+/** When debug level has the DBG_HCD_URB bit set, display enqueued URBs in host
+ *  mode. */
+#define DBG_HCD_URB	(0x800)
+/** When debug level has the DBG_HCDI bit set, display host interrupt
+ *  messages. */
+#define DBG_HCDI	(0x1000)
+
+/** When debug level has any bit set, display debug messages */
+#define DBG_ANY		(0xFF)
+
+/** All debug messages off */
+#define DBG_OFF		0
+
+/** Prefix string for DWC_DEBUG print macros. */
+#define USB_DWC "DWC_otg: "
+
+/**
+ * Print a debug message when the Global debug level variable contains
+ * the bit defined in <code>lvl</code>.
+ *
+ * @param[in] lvl - Debug level, use one of the DBG_ constants above.
+ * @param[in] x - like printf
+ *
+ *    Example:<p>
+ * <code>
+ *      DWC_DEBUGPL( DBG_ANY, "%s(%p)\n", __func__, _reg_base_addr);
+ * </code>
+ * <br>
+ * results in:<br>
+ * <code>
+ * usb-DWC_otg: dwc_otg_cil_init(ca867000)
+ * </code>
+ */
+#ifdef DEBUG
+
+# define DWC_DEBUGPL(lvl, x...) do{ if ((lvl)&g_dbg_lvl)__DWC_DEBUG(USB_DWC x ); }while(0)
+# define DWC_DEBUGP(x...)	DWC_DEBUGPL(DBG_ANY, x )
+
+# define CHK_DEBUG_LEVEL(level) ((level) & g_dbg_lvl)
+
+#else
+
+# define DWC_DEBUGPL(lvl, x...) do{}while(0)
+# define DWC_DEBUGP(x...)
+
+# define CHK_DEBUG_LEVEL(level) (0)
+
+#endif /*DEBUG*/
+#endif
diff -Nur --no-dereference linux-5.10.52-orig/drivers/usb/host/dwc_otg/dwc_otg_driver.c linux-5.10.52-v7l+/drivers/usb/host/dwc_otg/dwc_otg_driver.c
--- linux-5.10.52-orig/drivers/usb/host/dwc_otg/dwc_otg_driver.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/usb/host/dwc_otg/dwc_otg_driver.c	2021-07-25 16:46:28.677910113 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/* ==========================================================================
+ * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_driver.c $
+ * $Revision: #92 $
+ * $Date: 2012/08/10 $
+ * $Change: 2047372 $
+ *
+ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter,
+ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless
+ * otherwise expressly agreed to in writing between Synopsys and you.
+ *
+ * The Software IS NOT an item of Licensed Software or Licensed Product under
+ * any End User Software License Agreement or Agreement for Licensed Product
+ * with Synopsys or any supplement thereto. You are permitted to use and
+ * redistribute this Software in source and binary forms, with or without
+ * modification, provided that redistributions of source code must retain this
+ * notice. You may not view, use, disclose, copy or distribute this file or
+ * any information contained herein except pursuant to this license grant from
+ * Synopsys. If you do not agree with this notice, including the disclaimer
+ * below, then you are not authorized to use the Software.
+ *
+ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * ========================================================================== */
+
+/** @file
+ * The dwc_otg_driver module provides the initialization and cleanup entry
+ * points for the DWC_otg driver. This module will be dynamically installed
+ * after Linux is booted using the insmod command. When the module is
+ * installed, the dwc_otg_driver_init function is called. When the module is
+ * removed (using rmmod), the dwc_otg_driver_cleanup function is called.
+ *
+ * This module also defines a data structure for the dwc_otg_driver, which is
+ * used in conjunction with the standard ARM lm_device structure. These
+ * structures allow the OTG driver to comply with the standard Linux driver
+ * model in which devices and drivers are registered with a bus driver. This
+ * has the benefit that Linux can expose attributes of the driver and device
+ * in its special sysfs file system. Users can then read or write files in
+ * this file system to perform diagnostics on the driver components or the
+ * device.
+ */
+
+#include "dwc_otg_os_dep.h"
+#include "dwc_os.h"
+#include "dwc_otg_dbg.h"
+#include "dwc_otg_driver.h"
+#include "dwc_otg_attr.h"
+#include "dwc_otg_core_if.h"
+#include "dwc_otg_pcd_if.h"
+#include "dwc_otg_hcd_if.h"
+#include "dwc_otg_fiq_fsm.h"
+
+#define DWC_DRIVER_VERSION	"3.00a 10-AUG-2012"
+#define DWC_DRIVER_DESC		"HS OTG USB Controller driver"
+
+bool microframe_schedule=true;
+
+static const char dwc_driver_name[] = "dwc_otg";
+
+
+extern int pcd_init(
+#ifdef LM_INTERFACE
+			   struct lm_device *_dev
+#elif  defined(PCI_INTERFACE)
+			   struct pci_dev *_dev
+#elif  defined(PLATFORM_INTERFACE)
+	struct platform_device *dev
+#endif
+    );
+extern int hcd_init(
+#ifdef LM_INTERFACE
+			   struct lm_device *_dev
+#elif  defined(PCI_INTERFACE)
+			   struct pci_dev *_dev
+#elif  defined(PLATFORM_INTERFACE)
+	struct platform_device *dev
+#endif
+    );
+
+extern int pcd_remove(
+#ifdef LM_INTERFACE
+			     struct lm_device *_dev
+#elif  defined(PCI_INTERFACE)
+			     struct pci_dev *_dev
+#elif  defined(PLATFORM_INTERFACE)
+	struct platform_device *_dev
+#endif
+    );
+
+extern void hcd_remove(
+#ifdef LM_INTERFACE
+			      struct lm_device *_dev
+#elif  defined(PCI_INTERFACE)
+			      struct pci_dev *_dev
+#elif  defined(PLATFORM_INTERFACE)
+	struct platform_device *_dev
+#endif
+    );
+
+extern void dwc_otg_adp_start(dwc_otg_core_if_t * core_if, uint8_t is_host);
+
+/*-------------------------------------------------------------------------*/
+/* Encapsulate the module parameter settings */
+
+struct dwc_otg_driver_module_params {
+	int32_t opt;
+	int32_t otg_cap;
+	int32_t dma_enable;
+	int32_t dma_desc_enable;
+	int32_t dma_burst_size;
+	int32_t speed;
+	int32_t host_support_fs_ls_low_power;
+	int32_t host_ls_low_power_phy_clk;
+	int32_t enable_dynamic_fifo;
+	int32_t data_fifo_size;
+	int32_t dev_rx_fifo_size;
+	int32_t dev_nperio_tx_fifo_size;
+	uint32_t dev_perio_tx_fifo_size[MAX_PERIO_FIFOS];
+	int32_t host_rx_fifo_size;
+	int32_t host_nperio_tx_fifo_size;
+	int32_t host_perio_tx_fifo_size;
+	int32_t max_transfer_size;
+	int32_t max_packet_count;
+	int32_t host_channels;
+	int32_t dev_endpoints;
+	int32_t phy_type;
+	int32_t phy_utmi_width;
+	int32_t phy_ulpi_ddr;
+	int32_t phy_ulpi_ext_vbus;
+	int32_t i2c_enable;
+	int32_t ulpi_fs_ls;
+	int32_t ts_dline;
+	int32_t en_multiple_tx_fifo;
+	uint32_t dev_tx_fifo_size[MAX_TX_FIFOS];
+	uint32_t thr_ctl;
+	uint32_t tx_thr_length;
+	uint32_t rx_thr_length;
+	int32_t pti_enable;
+	int32_t mpi_enable;
+	int32_t lpm_enable;
+	int32_t ic_usb_cap;
+	int32_t ahb_thr_ratio;
+	int32_t power_down;
+	int32_t reload_ctl;
+	int32_t dev_out_nak;
+	int32_t cont_on_bna;
+	int32_t ahb_single;
+	int32_t otg_ver;
+	int32_t adp_enable;
+};
+
+static struct dwc_otg_driver_module_params dwc_otg_module_params = {
+	.opt = -1,
+	.otg_cap = -1,
+	.dma_enable = -1,
+	.dma_desc_enable = -1,
+	.dma_burst_size = -1,
+	.speed = -1,
+	.host_support_fs_ls_low_power = -1,
+	.host_ls_low_power_phy_clk = -1,
+	.enable_dynamic_fifo = -1,
+	.data_fifo_size = -1,
+	.dev_rx_fifo_size = -1,
+	.dev_nperio_tx_fifo_size = -1,
+	.dev_perio_tx_fifo_size = {
+				   /* dev_perio_tx_fifo_size_1 */
+				   -1,
+				   -1,
+				   -1,
+				   -1,
+				   -1,
+				   -1,
+				   -1,
+				   -1,
+				   -1,
+				   -1,
+				   -1,
+				   -1,
+				   -1,
+				   -1,
+				   -1
+				   /* 15 */
+				   },
+	.host_rx_fifo_size = -1,
+	.host_nperio_tx_fifo_size = -1,
+	.host_perio_tx_fifo_size = -1,
+	.max_transfer_size = -1,
+	.max_packet_count = -1,
+	.host_channels = -1,
+	.dev_endpoints = -1,
+	.phy_type = -1,
+	.phy_utmi_width = -1,
+	.phy_ulpi_ddr = -1,
+	.phy_ulpi_ext_vbus = -1,
+	.i2c_enable = -1,
+	.ulpi_fs_ls = -1,
+	.ts_dline = -1,
+	.en_multiple_tx_fifo = -1,
+	.dev_tx_fifo_size = {
+			     /* dev_tx_fifo_size */
+			     -1,
+			     -1,
+			     -1,
+			     -1,
+			     -1,
+			     -1,
+			     -1,
+			     -1,
+			     -1,
+			     -1,
+			     -1,
+			     -1,
+			     -1,
+			     -1,
+			     -1
+			     /* 15 */
+			     },
+	.thr_ctl = -1,
+	.tx_thr_length = -1,
+	.rx_thr_length = -1,
+	.pti_enable = -1,
+	.mpi_enable = -1,
+	.lpm_enable = 0,
+	.ic_usb_cap = -1,
+	.ahb_thr_ratio = -1,
+	.power_down = -1,
+	.reload_ctl = -1,
+	.dev_out_nak = -1,
+	.cont_on_bna = -1,
+	.ahb_single = -1,
+	.otg_ver = -1,
+	.adp_enable = -1,
+};
+
+//Global variable to switch the fiq fix on or off
+bool fiq_enable = 1;
+// Global variable to enable the split transaction fix
+bool fiq_fsm_enable = true;
+//Bulk split-transaction NAK holdoff in microframes
+uint16_t nak_holdoff = 8;
+
+//Force host mode during CIL re-init
+bool cil_force_host = true;
+
+unsigned short fiq_fsm_mask = 0x0F;
+
+unsigned short int_ep_interval_min = 0;
+/**
+ * This function shows the Driver Version.
+ */
+static ssize_t version_show(struct device_driver *dev, char *buf)
+{
+	return snprintf(buf, sizeof(DWC_DRIVER_VERSION) + 2, "%s\n",
+			DWC_DRIVER_VERSION);
+}
+
+static DRIVER_ATTR_RO(version);
+
+/**
+ * Global Debug Level Mask.
+ */
+uint32_t g_dbg_lvl = 0;		/* OFF */
+
+/**
+ * This function shows the driver Debug Level.
+ */
+static ssize_t debuglevel_show(struct device_driver *drv, char *buf)
+{
+	return sprintf(buf, "0x%0x\n", g_dbg_lvl);
+}
+
+/**
+ * This function stores the driver Debug Level.
+ */
+static ssize_t debuglevel_store(struct device_driver *drv, const char *buf,
+			       size_t count)
+{
+	g_dbg_lvl = simple_strtoul(buf, NULL, 16);
+	return count;
+}
+
+static DRIVER_ATTR_RW(debuglevel);
+
+/**
+ * This function is called during module intialization
+ * to pass module parameters to the DWC_OTG CORE.
+ */
+static int set_parameters(dwc_otg_core_if_t * core_if)
+{
+	int retval = 0;
+	int i;
+
+	if (dwc_otg_module_params.otg_cap != -1) {
+		retval +=
+		    dwc_otg_set_param_otg_cap(core_if,
+					      dwc_otg_module_params.otg_cap);
+	}
+	if (dwc_otg_module_params.dma_enable != -1) {
+		retval +=
+		    dwc_otg_set_param_dma_enable(core_if,
+						 dwc_otg_module_params.
+						 dma_enable);
+	}
+	if (dwc_otg_module_params.dma_desc_enable != -1) {
+		retval +=
+		    dwc_otg_set_param_dma_desc_enable(core_if,
+						      dwc_otg_module_params.
+						      dma_desc_enable);
+	}
+	if (dwc_otg_module_params.opt != -1) {
+		retval +=
+		    dwc_otg_set_param_opt(core_if, dwc_otg_module_params.opt);
+	}
+	if (dwc_otg_module_params.dma_burst_size != -1) {
+		retval +=
+		    dwc_otg_set_param_dma_burst_size(core_if,
+						     dwc_otg_module_params.
+						     dma_burst_size);
+	}
+	if (dwc_otg_module_params.host_support_fs_ls_low_power != -1) {
+		retval +=
+		    dwc_otg_set_param_host_support_fs_ls_low_power(core_if,
+								   dwc_otg_module_params.
+								   host_support_fs_ls_low_power);
+	}
+	if (dwc_otg_module_params.enable_dynamic_fifo != -1) {
+		retval +=
+		    dwc_otg_set_param_enable_dynamic_fifo(core_if,
+							  dwc_otg_module_params.
+							  enable_dynamic_fifo);
+	}
+	if (dwc_otg_module_params.data_fifo_size != -1) {
+		retval +=
+		    dwc_otg_set_param_data_fifo_size(core_if,
+						     dwc_otg_module_params.
+						     data_fifo_size);
+	}
+	if (dwc_otg_module_params.dev_rx_fifo_size != -1) {
+		retval +=
+		    dwc_otg_set_param_dev_rx_fifo_size(core_if,
+						       dwc_otg_module_params.
+						       dev_rx_fifo_size);
+	}
+	if (dwc_otg_module_params.dev_nperio_tx_fifo_size != -1) {
+		retval +=
+		    dwc_otg_set_param_dev_nperio_tx_fifo_size(core_if,
+							      dwc_otg_module_params.
+							      dev_nperio_tx_fifo_size);
+	}
+	if (dwc_otg_module_params.host_rx_fifo_size != -1) {
+		retval +=
+		    dwc_otg_set_param_host_rx_fifo_size(core_if,
+							dwc_otg_module_params.host_rx_fifo_size);
+	}
+	if (dwc_otg_module_params.host_nperio_tx_fifo_size != -1) {
+		retval +=
+		    dwc_otg_set_param_host_nperio_tx_fifo_size(core_if,
+							       dwc_otg_module_params.
+							       host_nperio_tx_fifo_size);
+	}
+	if (dwc_otg_module_params.host_perio_tx_fifo_size != -1) {
+		retval +=
+		    dwc_otg_set_param_host_perio_tx_fifo_size(core_if,
+							      dwc_otg_module_params.
+							      host_perio_tx_fifo_size);
+	}
+	if (dwc_otg_module_params.max_transfer_size != -1) {
+		retval +=
+		    dwc_otg_set_param_max_transfer_size(core_if,
+							dwc_otg_module_params.
+							max_transfer_size);
+	}
+	if (dwc_otg_module_params.max_packet_count != -1) {
+		retval +=
+		    dwc_otg_set_param_max_packet_count(core_if,
+						       dwc_otg_module_params.
+						       max_packet_count);
+	}
+	if (dwc_otg_module_params.host_channels != -1) {
+		retval +=
+		    dwc_otg_set_param_host_channels(core_if,
+						    dwc_otg_module_params.
+						    host_channels);
+	}
+	if (dwc_otg_module_params.dev_endpoints != -1) {
+		retval +=
+		    dwc_otg_set_param_dev_endpoints(core_if,
+						    dwc_otg_module_params.
+						    dev_endpoints);
+	}
+	if (dwc_otg_module_params.phy_type != -1) {
+		retval +=
+		    dwc_otg_set_param_phy_type(core_if,
+					       dwc_otg_module_params.phy_type);
+	}
+	if (dwc_otg_module_params.speed != -1) {
+		retval +=
+		    dwc_otg_set_param_speed(core_if,
+					    dwc_otg_module_params.speed);
+	}
+	if (dwc_otg_module_params.host_ls_low_power_phy_clk != -1) {
+		retval +=
+		    dwc_otg_set_param_host_ls_low_power_phy_clk(core_if,
+								dwc_otg_module_params.
+								host_ls_low_power_phy_clk);
+	}
+	if (dwc_otg_module_params.phy_ulpi_ddr != -1) {
+		retval +=
+		    dwc_otg_set_param_phy_ulpi_ddr(core_if,
+						   dwc_otg_module_params.
+						   phy_ulpi_ddr);
+	}
+	if (dwc_otg_module_params.phy_ulpi_ext_vbus != -1) {
+		retval +=
+		    dwc_otg_set_param_phy_ulpi_ext_vbus(core_if,
+							dwc_otg_module_params.
+							phy_ulpi_ext_vbus);
+	}
+	if (dwc_otg_module_params.phy_utmi_width != -1) {
+		retval +=
+		    dwc_otg_set_param_phy_utmi_width(core_if,
+						     dwc_otg_module_params.
+						     phy_utmi_width);
+	}
+	if (dwc_otg_module_params.ulpi_fs_ls != -1) {
+		retval +=
+		    dwc_otg_set_param_ulpi_fs_ls(core_if,
+						 dwc_otg_module_params.ulpi_fs_ls);
+	}
+	if (dwc_otg_module_params.ts_dline != -1) {
+		retval +=
+		    dwc_otg_set_param_ts_dline(core_if,
+					       dwc_otg_module_params.ts_dline);
+	}
+	if (dwc_otg_module_params.i2c_enable != -1) {
+		retval +=
+		    dwc_otg_set_param_i2c_enable(core_if,
+						 dwc_otg_module_params.
+						 i2c_enable);
+	}
+	if (dwc_otg_module_params.en_multiple_tx_fifo != -1) {
+		retval +=
+		    dwc_otg_set_param_en_multiple_tx_fifo(core_if,
+							  dwc_otg_module_params.
+							  en_multiple_tx_fifo);
+	}
+	for (i = 0; i < 15; i++) {
+		if (dwc_otg_module_params.dev_perio_tx_fifo_size[i] != -1) {
+			retval +=
+			    dwc_otg_set_param_dev_perio_tx_fifo_size(core_if,
+								     dwc_otg_module_params.
+								     dev_perio_tx_fifo_size
+								     [i], i);
+		}
+	}
+
+	for (i = 0; i < 15; i++) {
+		if (dwc_otg_module_params.dev_tx_fifo_size[i] != -1) {
+			retval += dwc_otg_set_param_dev_tx_fifo_size(core_if,
+								     dwc_otg_module_params.
+								     dev_tx_fifo_size
+								     [i], i);
+		}
+	}
+	if (dwc_otg_module_params.thr_ctl != -1) {
+		retval +=
+		    dwc_otg_set_param_thr_ctl(core_if,
+					      dwc_otg_module_params.thr_ctl);
+	}
+	if (dwc_otg_module_params.mpi_enable != -1) {
+		retval +=
+		    dwc_otg_set_param_mpi_enable(core_if,
+						 dwc_otg_module_params.
+						 mpi_enable);
+	}
+	if (dwc_otg_module_params.pti_enable != -1) {
+		retval +=
+		    dwc_otg_set_param_pti_enable(core_if,
+						 dwc_otg_module_params.
+						 pti_enable);
+	}
+	if (dwc_otg_module_params.lpm_enable != -1) {
+		retval +=
+		    dwc_otg_set_param_lpm_enable(core_if,
+						 dwc_otg_module_params.
+						 lpm_enable);
+	}
+	if (dwc_otg_module_params.ic_usb_cap != -1) {
+		retval +=
+		    dwc_otg_set_param_ic_usb_cap(core_if,
+						 dwc_otg_module_params.
+						 ic_usb_cap);
+	}
+	if (dwc_otg_module_params.tx_thr_length != -1) {
+		retval +=
+		    dwc_otg_set_param_tx_thr_length(core_if,
+						    dwc_otg_module_params.tx_thr_length);
+	}
+	if (dwc_otg_module_params.rx_thr_length != -1) {
+		retval +=
+		    dwc_otg_set_param_rx_thr_length(core_if,
+						    dwc_otg_module_params.
+						    rx_thr_length);
+	}
+	if (dwc_otg_module_params.ahb_thr_ratio != -1) {
+		retval +=
+		    dwc_otg_set_param_ahb_thr_ratio(core_if,
+						    dwc_otg_module_params.ahb_thr_ratio);
+	}
+	if (dwc_otg_module_params.power_down != -1) {
+		retval +=
+		    dwc_otg_set_param_power_down(core_if,
+						 dwc_otg_module_params.power_down);
+	}
+	if (dwc_otg_module_params.reload_ctl != -1) {
+		retval +=
+		    dwc_otg_set_param_reload_ctl(core_if,
+						 dwc_otg_module_params.reload_ctl);
+	}
+
+	if (dwc_otg_module_params.dev_out_nak != -1) {
+		retval +=
+			dwc_otg_set_param_dev_out_nak(core_if,
+			dwc_otg_module_params.dev_out_nak);
+	}
+
+	if (dwc_otg_module_params.cont_on_bna != -1) {
+		retval +=
+			dwc_otg_set_param_cont_on_bna(core_if,
+			dwc_otg_module_params.cont_on_bna);
+	}
+
+	if (dwc_otg_module_params.ahb_single != -1) {
+		retval +=
+			dwc_otg_set_param_ahb_single(core_if,
+			dwc_otg_module_params.ahb_single);
+	}
+
+	if (dwc_otg_module_params.otg_ver != -1) {
+		retval +=
+		    dwc_otg_set_param_otg_ver(core_if,
+					      dwc_otg_module_params.otg_ver);
+	}
+	if (dwc_otg_module_params.adp_enable != -1) {
+		retval +=
+		    dwc_otg_set_param_adp_enable(core_if,
+						 dwc_otg_module_params.
+						 adp_enable);
+	}
+	return retval;
+}
+
+/**
+ * This function is the top level interrupt handler for the Common
+ * (Device and host modes) interrupts.
+ */
+static irqreturn_t dwc_otg_common_irq(int irq, void *dev)
+{
+	int32_t retval = IRQ_NONE;
+
+	retval = dwc_otg_handle_common_intr(dev);
+	if (retval != 0) {
+		S3C2410X_CLEAR_EINTPEND();
+	}
+	return IRQ_RETVAL(retval);
+}
+
+/**
+ * This function is called when a lm_device is unregistered with the
+ * dwc_otg_driver. This happens, for example, when the rmmod command is
+ * executed. The device may or may not be electrically present. If it is
+ * present, the driver stops device processing. Any resources used on behalf
+ * of this device are freed.
+ *
+ * @param _dev
+ */
+#ifdef LM_INTERFACE
+#define REM_RETVAL(n)
+static void dwc_otg_driver_remove(	 struct lm_device *_dev )
+{       dwc_otg_device_t *otg_dev = lm_get_drvdata(_dev);
+#elif  defined(PCI_INTERFACE)
+#define REM_RETVAL(n)
+static void dwc_otg_driver_remove(	 struct pci_dev *_dev )
+{	dwc_otg_device_t *otg_dev = pci_get_drvdata(_dev);
+#elif  defined(PLATFORM_INTERFACE)
+#define REM_RETVAL(n) n
+static int dwc_otg_driver_remove(        struct platform_device *_dev )
+{       dwc_otg_device_t *otg_dev = platform_get_drvdata(_dev);
+#endif
+
+	DWC_DEBUGPL(DBG_ANY, "%s(%p) otg_dev %p\n", __func__, _dev, otg_dev);
+
+	if (!otg_dev) {
+		/* Memory allocation for the dwc_otg_device failed. */
+		DWC_DEBUGPL(DBG_ANY, "%s: otg_dev NULL!\n", __func__);
+                return REM_RETVAL(-ENOMEM);
+	}
+#ifndef DWC_DEVICE_ONLY
+	if (otg_dev->hcd) {
+		hcd_remove(_dev);
+	} else {
+		DWC_DEBUGPL(DBG_ANY, "%s: otg_dev->hcd NULL!\n", __func__);
+                return REM_RETVAL(-EINVAL);
+	}
+#endif
+
+#ifndef DWC_HOST_ONLY
+	if (otg_dev->pcd) {
+		pcd_remove(_dev);
+	} else {
+		DWC_DEBUGPL(DBG_ANY, "%s: otg_dev->pcd NULL!\n", __func__);
+                return REM_RETVAL(-EINVAL);
+	}
+#endif
+	/*
+	 * Free the IRQ
+	 */
+	if (otg_dev->common_irq_installed) {
+		free_irq(otg_dev->os_dep.irq_num, otg_dev);
+        } else {
+		DWC_DEBUGPL(DBG_ANY, "%s: There is no installed irq!\n", __func__);
+		return REM_RETVAL(-ENXIO);
+	}
+
+	if (otg_dev->core_if) {
+		dwc_otg_cil_remove(otg_dev->core_if);
+	} else {
+		DWC_DEBUGPL(DBG_ANY, "%s: otg_dev->core_if NULL!\n", __func__);
+		return REM_RETVAL(-ENXIO);
+	}
+
+	/*
+	 * Remove the device attributes
+	 */
+	dwc_otg_attr_remove(_dev);
+
+	/*
+	 * Return the memory.
+	 */
+	if (otg_dev->os_dep.base) {
+		iounmap(otg_dev->os_dep.base);
+	}
+	DWC_FREE(otg_dev);
+
+	/*
+	 * Clear the drvdata pointer.
+	 */
+#ifdef LM_INTERFACE
+	lm_set_drvdata(_dev, 0);
+#elif defined(PCI_INTERFACE)
+        release_mem_region(otg_dev->os_dep.rsrc_start,
+                           otg_dev->os_dep.rsrc_len);
+	pci_set_drvdata(_dev, 0);
+#elif  defined(PLATFORM_INTERFACE)
+        platform_set_drvdata(_dev, 0);
+#endif
+        return REM_RETVAL(0);
+}
+
+/**
+ * This function is called when an lm_device is bound to a
+ * dwc_otg_driver. It creates the driver components required to
+ * control the device (CIL, HCD, and PCD) and it initializes the
+ * device. The driver components are stored in a dwc_otg_device
+ * structure. A reference to the dwc_otg_device is saved in the
+ * lm_device. This allows the driver to access the dwc_otg_device
+ * structure on subsequent calls to driver methods for this device.
+ *
+ * @param _dev Bus device
+ */
+static int dwc_otg_driver_probe(
+#ifdef LM_INTERFACE
+				       struct lm_device *_dev
+#elif defined(PCI_INTERFACE)
+				       struct pci_dev *_dev,
+				       const struct pci_device_id *id
+#elif  defined(PLATFORM_INTERFACE)
+                                       struct platform_device *_dev
+#endif
+    )
+{
+	int retval = 0;
+	dwc_otg_device_t *dwc_otg_device;
+        int devirq;
+
+	dev_dbg(&_dev->dev, "dwc_otg_driver_probe(%p)\n", _dev);
+#ifdef LM_INTERFACE
+	dev_dbg(&_dev->dev, "start=0x%08x\n", (unsigned)_dev->resource.start);
+#elif defined(PCI_INTERFACE)
+	if (!id) {
+		DWC_ERROR("Invalid pci_device_id %p", id);
+		return -EINVAL;
+	}
+
+	if (!_dev || (pci_enable_device(_dev) < 0)) {
+		DWC_ERROR("Invalid pci_device %p", _dev);
+		return -ENODEV;
+	}
+	dev_dbg(&_dev->dev, "start=0x%08x\n", (unsigned)pci_resource_start(_dev,0));
+	/* other stuff needed as well? */
+
+#elif  defined(PLATFORM_INTERFACE)
+	dev_dbg(&_dev->dev, "start=0x%08x (len 0x%x)\n",
+                (unsigned)_dev->resource->start,
+                (unsigned)(_dev->resource->end - _dev->resource->start));
+#endif
+
+	dwc_otg_device = DWC_ALLOC(sizeof(dwc_otg_device_t));
+
+	if (!dwc_otg_device) {
+		dev_err(&_dev->dev, "kmalloc of dwc_otg_device failed\n");
+		return -ENOMEM;
+	}
+
+	memset(dwc_otg_device, 0, sizeof(*dwc_otg_device));
+	dwc_otg_device->os_dep.reg_offset = 0xFFFFFFFF;
+	dwc_otg_device->os_dep.platformdev = _dev;
+
+	/*
+	 * Map the DWC_otg Core memory into virtual address space.
+	 */
+#ifdef LM_INTERFACE
+	dwc_otg_device->os_dep.base = ioremap(_dev->resource.start, SZ_256K);
+
+	if (!dwc_otg_device->os_dep.base) {
+		dev_err(&_dev->dev, "ioremap() failed\n");
+		DWC_FREE(dwc_otg_device);
+		return -ENOMEM;
+	}
+	dev_dbg(&_dev->dev, "base=0x%08x\n",
+		(unsigned)dwc_otg_device->os_dep.base);
+#elif defined(PCI_INTERFACE)
+	_dev->current_state = PCI_D0;
+	_dev->dev.power.power_state = PMSG_ON;
+
+	if (!_dev->irq) {
+		DWC_ERROR("Found HC with no IRQ. Check BIOS/PCI %s setup!",
+			  pci_name(_dev));
+		iounmap(dwc_otg_device->os_dep.base);
+		DWC_FREE(dwc_otg_device);
+		return -ENODEV;
+	}
+
+	dwc_otg_device->os_dep.rsrc_start = pci_resource_start(_dev, 0);
+	dwc_otg_device->os_dep.rsrc_len = pci_resource_len(_dev, 0);
+	DWC_DEBUGPL(DBG_ANY, "PCI resource: start=%08x, len=%08x\n",
+		    (unsigned)dwc_otg_device->os_dep.rsrc_start,
+		    (unsigned)dwc_otg_device->os_dep.rsrc_len);
+	if (!request_mem_region
+	    (dwc_otg_device->os_dep.rsrc_start, dwc_otg_device->os_dep.rsrc_len,
+	     "dwc_otg")) {
+		dev_dbg(&_dev->dev, "error requesting memory\n");
+		iounmap(dwc_otg_device->os_dep.base);
+		DWC_FREE(dwc_otg_device);
+		return -EFAULT;
+	}
+
+	dwc_otg_device->os_dep.base =
+	    ioremap(dwc_otg_device->os_dep.rsrc_start,
+			    dwc_otg_device->os_dep.rsrc_len);
+	if (dwc_otg_device->os_dep.base == NULL) {
+		dev_dbg(&_dev->dev, "error mapping memory\n");
+		release_mem_region(dwc_otg_device->os_dep.rsrc_start,
+				   dwc_otg_device->os_dep.rsrc_len);
+		iounmap(dwc_otg_device->os_dep.base);
+		DWC_FREE(dwc_otg_device);
+		return -EFAULT;
+	}
+	dev_dbg(&_dev->dev, "base=0x%p (before adjust) \n",
+		dwc_otg_device->os_dep.base);
+	dwc_otg_device->os_dep.base = (char *)dwc_otg_device->os_dep.base;
+	dev_dbg(&_dev->dev, "base=0x%p (after adjust) \n",
+		dwc_otg_device->os_dep.base);
+	dev_dbg(&_dev->dev, "%s: mapped PA 0x%x to VA 0x%p\n", __func__,
+		(unsigned)dwc_otg_device->os_dep.rsrc_start,
+		dwc_otg_device->os_dep.base);
+
+	pci_set_master(_dev);
+	pci_set_drvdata(_dev, dwc_otg_device);
+#elif defined(PLATFORM_INTERFACE)
+        DWC_DEBUGPL(DBG_ANY,"Platform resource: start=%08x, len=%08x\n",
+                    _dev->resource->start,
+                    _dev->resource->end - _dev->resource->start + 1);
+#if 1
+        if (!request_mem_region(_dev->resource[0].start,
+                                _dev->resource[0].end - _dev->resource[0].start + 1,
+                                "dwc_otg")) {
+          dev_dbg(&_dev->dev, "error reserving mapped memory\n");
+          retval = -EFAULT;
+          goto fail;
+        }
+
+	dwc_otg_device->os_dep.base = ioremap(_dev->resource[0].start,
+                                                      _dev->resource[0].end -
+                                                      _dev->resource[0].start+1);
+	if (fiq_enable)
+	{
+		if (!request_mem_region(_dev->resource[1].start,
+	                                _dev->resource[1].end - _dev->resource[1].start + 1,
+	                                "dwc_otg")) {
+			dev_dbg(&_dev->dev, "error reserving mapped memory\n");
+			retval = -EFAULT;
+			goto fail;
+		}
+
+		dwc_otg_device->os_dep.mphi_base = ioremap(_dev->resource[1].start,
+							    _dev->resource[1].end -
+							    _dev->resource[1].start + 1);
+		dwc_otg_device->os_dep.use_swirq = (_dev->resource[1].end - _dev->resource[1].start) == 0x200;
+	}
+
+#else
+        {
+                struct map_desc desc = {
+                    .virtual = IO_ADDRESS((unsigned)_dev->resource->start),
+                    .pfn     = __phys_to_pfn((unsigned)_dev->resource->start),
+                    .length  = SZ_128K,
+                    .type    = MT_DEVICE
+                };
+                iotable_init(&desc, 1);
+                dwc_otg_device->os_dep.base = (void *)desc.virtual;
+        }
+#endif
+	if (!dwc_otg_device->os_dep.base) {
+		dev_err(&_dev->dev, "ioremap() failed\n");
+		retval = -ENOMEM;
+		goto fail;
+	}
+#endif
+
+	/*
+	 * Initialize driver data to point to the global DWC_otg
+	 * Device structure.
+	 */
+#ifdef LM_INTERFACE
+	lm_set_drvdata(_dev, dwc_otg_device);
+#elif defined(PLATFORM_INTERFACE)
+	platform_set_drvdata(_dev, dwc_otg_device);
+#endif
+	dev_dbg(&_dev->dev, "dwc_otg_device=0x%p\n", dwc_otg_device);
+
+	dwc_otg_device->core_if = dwc_otg_cil_init(dwc_otg_device->os_dep.base);
+        DWC_DEBUGPL(DBG_HCDV, "probe of device %p given core_if %p\n",
+                    dwc_otg_device, dwc_otg_device->core_if);//GRAYG
+
+	if (!dwc_otg_device->core_if) {
+		dev_err(&_dev->dev, "CIL initialization failed!\n");
+		retval = -ENOMEM;
+		goto fail;
+	}
+
+	dev_dbg(&_dev->dev, "Calling get_gsnpsid\n");
+	/*
+	 * Attempt to ensure this device is really a DWC_otg Controller.
+	 * Read and verify the SNPSID register contents. The value should be
+	 * 0x45F42XXX or 0x45F42XXX, which corresponds to either "OT2" or "OTG3",
+	 * as in "OTG version 2.XX" or "OTG version 3.XX".
+	 */
+
+	if (((dwc_otg_get_gsnpsid(dwc_otg_device->core_if) & 0xFFFFF000) !=	0x4F542000) &&
+		((dwc_otg_get_gsnpsid(dwc_otg_device->core_if) & 0xFFFFF000) != 0x4F543000)) {
+		dev_err(&_dev->dev, "Bad value for SNPSID: 0x%08x\n",
+			dwc_otg_get_gsnpsid(dwc_otg_device->core_if));
+		retval = -EINVAL;
+		goto fail;
+	}
+
+	/*
+	 * Validate parameter values.
+	 */
+	dev_dbg(&_dev->dev, "Calling set_parameters\n");
+	if (set_parameters(dwc_otg_device->core_if)) {
+		retval = -EINVAL;
+		goto fail;
+	}
+
+	/*
+	 * Create Device Attributes in sysfs
+	 */
+	dev_dbg(&_dev->dev, "Calling attr_create\n");
+	dwc_otg_attr_create(_dev);
+
+	/*
+	 * Disable the global interrupt until all the interrupt
+	 * handlers are installed.
+	 */
+	dev_dbg(&_dev->dev, "Calling disable_global_interrupts\n");
+	dwc_otg_disable_global_interrupts(dwc_otg_device->core_if);
+
+	/*
+	 * Install the interrupt handler for the common interrupts before
+	 * enabling common interrupts in core_init below.
+	 */
+
+#if defined(PLATFORM_INTERFACE)
+	devirq = platform_get_irq_byname(_dev, fiq_enable ? "soft" : "usb");
+	if (devirq < 0)
+	    devirq = platform_get_irq(_dev, fiq_enable ? 0 : 1);
+#else
+	devirq = _dev->irq;
+#endif
+	DWC_DEBUGPL(DBG_CIL, "registering (common) handler for irq%d\n",
+		    devirq);
+	dev_dbg(&_dev->dev, "Calling request_irq(%d)\n", devirq);
+	retval = request_irq(devirq, dwc_otg_common_irq,
+                             IRQF_SHARED,
+                             "dwc_otg", dwc_otg_device);
+	if (retval) {
+		DWC_ERROR("request of irq%d failed\n", devirq);
+		retval = -EBUSY;
+		goto fail;
+	} else {
+		dwc_otg_device->common_irq_installed = 1;
+	}
+	dwc_otg_device->os_dep.irq_num = devirq;
+	dwc_otg_device->os_dep.fiq_num = -EINVAL;
+	if (fiq_enable) {
+		int devfiq = platform_get_irq_byname(_dev, "usb");
+		if (devfiq < 0)
+			devfiq = platform_get_irq(_dev, 1);
+		dwc_otg_device->os_dep.fiq_num = devfiq;
+	}
+
+#ifndef IRQF_TRIGGER_LOW
+#if defined(LM_INTERFACE) || defined(PLATFORM_INTERFACE)
+	dev_dbg(&_dev->dev, "Calling set_irq_type\n");
+	set_irq_type(devirq,
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,30))
+                     IRQT_LOW
+#else
+                     IRQ_TYPE_LEVEL_LOW
+#endif
+                    );
+#endif
+#endif /*IRQF_TRIGGER_LOW*/
+
+	/*
+	 * Initialize the DWC_otg core.
+	 */
+	dev_dbg(&_dev->dev, "Calling dwc_otg_core_init\n");
+	dwc_otg_core_init(dwc_otg_device->core_if);
+
+#ifndef DWC_HOST_ONLY
+	/*
+	 * Initialize the PCD
+	 */
+	dev_dbg(&_dev->dev, "Calling pcd_init\n");
+	retval = pcd_init(_dev);
+	if (retval != 0) {
+		DWC_ERROR("pcd_init failed\n");
+		dwc_otg_device->pcd = NULL;
+		goto fail;
+	}
+#endif
+#ifndef DWC_DEVICE_ONLY
+	/*
+	 * Initialize the HCD
+	 */
+	dev_dbg(&_dev->dev, "Calling hcd_init\n");
+	retval = hcd_init(_dev);
+	if (retval != 0) {
+		DWC_ERROR("hcd_init failed\n");
+		dwc_otg_device->hcd = NULL;
+		goto fail;
+	}
+#endif
+        /* Recover from drvdata having been overwritten by hcd_init() */
+#ifdef LM_INTERFACE
+	lm_set_drvdata(_dev, dwc_otg_device);
+#elif defined(PLATFORM_INTERFACE)
+	platform_set_drvdata(_dev, dwc_otg_device);
+#elif defined(PCI_INTERFACE)
+	pci_set_drvdata(_dev, dwc_otg_device);
+	dwc_otg_device->os_dep.pcidev = _dev;
+#endif
+
+	/*
+	 * Enable the global interrupt after all the interrupt
+	 * handlers are installed if there is no ADP support else
+	 * perform initial actions required for Internal ADP logic.
+	 */
+	if (!dwc_otg_get_param_adp_enable(dwc_otg_device->core_if)) {
+	        dev_dbg(&_dev->dev, "Calling enable_global_interrupts\n");
+		dwc_otg_enable_global_interrupts(dwc_otg_device->core_if);
+	        dev_dbg(&_dev->dev, "Done\n");
+	} else
+		dwc_otg_adp_start(dwc_otg_device->core_if,
+							dwc_otg_is_host_mode(dwc_otg_device->core_if));
+
+	return 0;
+
+fail:
+	dwc_otg_driver_remove(_dev);
+	return retval;
+}
+
+/**
+ * This structure defines the methods to be called by a bus driver
+ * during the lifecycle of a device on that bus. Both drivers and
+ * devices are registered with a bus driver. The bus driver matches
+ * devices to drivers based on information in the device and driver
+ * structures.
+ *
+ * The probe function is called when the bus driver matches a device
+ * to this driver. The remove function is called when a device is
+ * unregistered with the bus driver.
+ */
+#ifdef LM_INTERFACE
+static struct lm_driver dwc_otg_driver = {
+	.drv = {.name = (char *)dwc_driver_name,},
+	.probe = dwc_otg_driver_probe,
+	.remove = dwc_otg_driver_remove,
+        // 'suspend' and 'resume' absent
+};
+#elif defined(PCI_INTERFACE)
+static const struct pci_device_id pci_ids[] = { {
+						 PCI_DEVICE(0x16c3, 0xabcd),
+						 .driver_data =
+						 (unsigned long)0xdeadbeef,
+						 }, { /* end: all zeroes */ }
+};
+
+MODULE_DEVICE_TABLE(pci, pci_ids);
+
+/* pci driver glue; this is a "new style" PCI driver module */
+static struct pci_driver dwc_otg_driver = {
+	.name = "dwc_otg",
+	.id_table = pci_ids,
+
+	.probe = dwc_otg_driver_probe,
+	.remove = dwc_otg_driver_remove,
+
+	.driver = {
+		   .name = (char *)dwc_driver_name,
+		   },
+};
+#elif defined(PLATFORM_INTERFACE)
+static struct platform_device_id platform_ids[] = {
+        {
+              .name = "bcm2708_usb",
+              .driver_data = (kernel_ulong_t) 0xdeadbeef,
+        },
+        { /* end: all zeroes */ }
+};
+MODULE_DEVICE_TABLE(platform, platform_ids);
+
+static const struct of_device_id dwc_otg_of_match_table[] = {
+	{ .compatible = "brcm,bcm2708-usb", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, dwc_otg_of_match_table);
+
+static struct platform_driver dwc_otg_driver = {
+	.driver = {
+		.name = (char *)dwc_driver_name,
+		.of_match_table = dwc_otg_of_match_table,
+		},
+        .id_table = platform_ids,
+
+	.probe = dwc_otg_driver_probe,
+	.remove = dwc_otg_driver_remove,
+        // no 'shutdown', 'suspend', 'resume', 'suspend_late' or 'resume_early'
+};
+#endif
+
+/**
+ * This function is called when the dwc_otg_driver is installed with the
+ * insmod command. It registers the dwc_otg_driver structure with the
+ * appropriate bus driver. This will cause the dwc_otg_driver_probe function
+ * to be called. In addition, the bus driver will automatically expose
+ * attributes defined for the device and driver in the special sysfs file
+ * system.
+ *
+ * @return
+ */
+static int __init dwc_otg_driver_init(void)
+{
+	int retval = 0;
+	int error;
+        struct device_driver *drv;
+
+	if(fiq_fsm_enable && !fiq_enable) {
+		printk(KERN_WARNING "dwc_otg: fiq_fsm_enable was set without fiq_enable! Correcting.\n");
+		fiq_enable = 1;
+	}
+
+	printk(KERN_INFO "%s: version %s (%s bus)\n", dwc_driver_name,
+	       DWC_DRIVER_VERSION,
+#ifdef LM_INTERFACE
+               "logicmodule");
+	retval = lm_driver_register(&dwc_otg_driver);
+        drv = &dwc_otg_driver.drv;
+#elif defined(PCI_INTERFACE)
+               "pci");
+	retval = pci_register_driver(&dwc_otg_driver);
+        drv = &dwc_otg_driver.driver;
+#elif defined(PLATFORM_INTERFACE)
+               "platform");
+	retval = platform_driver_register(&dwc_otg_driver);
+        drv = &dwc_otg_driver.driver;
+#endif
+	if (retval < 0) {
+		printk(KERN_ERR "%s retval=%d\n", __func__, retval);
+		return retval;
+	}
+	printk(KERN_DEBUG "dwc_otg: FIQ %s\n", fiq_enable ? "enabled":"disabled");
+	printk(KERN_DEBUG "dwc_otg: NAK holdoff %s\n", nak_holdoff ? "enabled":"disabled");
+	printk(KERN_DEBUG "dwc_otg: FIQ split-transaction FSM %s\n", fiq_fsm_enable ? "enabled":"disabled");
+
+	error = driver_create_file(drv, &driver_attr_version);
+#ifdef DEBUG
+	error = driver_create_file(drv, &driver_attr_debuglevel);
+#endif
+	return retval;
+}
+
+module_init(dwc_otg_driver_init);
+
+/**
+ * This function is called when the driver is removed from the kernel
+ * with the rmmod command. The driver unregisters itself with its bus
+ * driver.
+ *
+ */
+static void __exit dwc_otg_driver_cleanup(void)
+{
+	printk(KERN_DEBUG "dwc_otg_driver_cleanup()\n");
+
+#ifdef LM_INTERFACE
+	driver_remove_file(&dwc_otg_driver.drv, &driver_attr_debuglevel);
+	driver_remove_file(&dwc_otg_driver.drv, &driver_attr_version);
+	lm_driver_unregister(&dwc_otg_driver);
+#elif defined(PCI_INTERFACE)
+	driver_remove_file(&dwc_otg_driver.driver, &driver_attr_debuglevel);
+	driver_remove_file(&dwc_otg_driver.driver, &driver_attr_version);
+	pci_unregister_driver(&dwc_otg_driver);
+#elif defined(PLATFORM_INTERFACE)
+	driver_remove_file(&dwc_otg_driver.driver, &driver_attr_debuglevel);
+	driver_remove_file(&dwc_otg_driver.driver, &driver_attr_version);
+	platform_driver_unregister(&dwc_otg_driver);
+#endif
+
+	printk(KERN_INFO "%s module removed\n", dwc_driver_name);
+}
+
+module_exit(dwc_otg_driver_cleanup);
+
+MODULE_DESCRIPTION(DWC_DRIVER_DESC);
+MODULE_AUTHOR("Synopsys Inc.");
+MODULE_LICENSE("GPL");
+
+module_param_named(otg_cap, dwc_otg_module_params.otg_cap, int, 0444);
+MODULE_PARM_DESC(otg_cap, "OTG Capabilities 0=HNP&SRP 1=SRP Only 2=None");
+module_param_named(opt, dwc_otg_module_params.opt, int, 0444);
+MODULE_PARM_DESC(opt, "OPT Mode");
+module_param_named(dma_enable, dwc_otg_module_params.dma_enable, int, 0444);
+MODULE_PARM_DESC(dma_enable, "DMA Mode 0=Slave 1=DMA enabled");
+
+module_param_named(dma_desc_enable, dwc_otg_module_params.dma_desc_enable, int,
+		   0444);
+MODULE_PARM_DESC(dma_desc_enable,
+		 "DMA Desc Mode 0=Address DMA 1=DMA Descriptor enabled");
+
+module_param_named(dma_burst_size, dwc_otg_module_params.dma_burst_size, int,
+		   0444);
+MODULE_PARM_DESC(dma_burst_size,
+		 "DMA Burst Size 1, 4, 8, 16, 32, 64, 128, 256");
+module_param_named(speed, dwc_otg_module_params.speed, int, 0444);
+MODULE_PARM_DESC(speed, "Speed 0=High Speed 1=Full Speed");
+module_param_named(host_support_fs_ls_low_power,
+		   dwc_otg_module_params.host_support_fs_ls_low_power, int,
+		   0444);
+MODULE_PARM_DESC(host_support_fs_ls_low_power,
+		 "Support Low Power w/FS or LS 0=Support 1=Don't Support");
+module_param_named(host_ls_low_power_phy_clk,
+		   dwc_otg_module_params.host_ls_low_power_phy_clk, int, 0444);
+MODULE_PARM_DESC(host_ls_low_power_phy_clk,
+		 "Low Speed Low Power Clock 0=48Mhz 1=6Mhz");
+module_param_named(enable_dynamic_fifo,
+		   dwc_otg_module_params.enable_dynamic_fifo, int, 0444);
+MODULE_PARM_DESC(enable_dynamic_fifo, "0=cC Setting 1=Allow Dynamic Sizing");
+module_param_named(data_fifo_size, dwc_otg_module_params.data_fifo_size, int,
+		   0444);
+MODULE_PARM_DESC(data_fifo_size,
+		 "Total number of words in the data FIFO memory 32-32768");
+module_param_named(dev_rx_fifo_size, dwc_otg_module_params.dev_rx_fifo_size,
+		   int, 0444);
+MODULE_PARM_DESC(dev_rx_fifo_size, "Number of words in the Rx FIFO 16-32768");
+module_param_named(dev_nperio_tx_fifo_size,
+		   dwc_otg_module_params.dev_nperio_tx_fifo_size, int, 0444);
+MODULE_PARM_DESC(dev_nperio_tx_fifo_size,
+		 "Number of words in the non-periodic Tx FIFO 16-32768");
+module_param_named(dev_perio_tx_fifo_size_1,
+		   dwc_otg_module_params.dev_perio_tx_fifo_size[0], int, 0444);
+MODULE_PARM_DESC(dev_perio_tx_fifo_size_1,
+		 "Number of words in the periodic Tx FIFO 4-768");
+module_param_named(dev_perio_tx_fifo_size_2,
+		   dwc_otg_module_params.dev_perio_tx_fifo_size[1], int, 0444);
+MODULE_PARM_DESC(dev_perio_tx_fifo_size_2,
+		 "Number of words in the periodic Tx FIFO 4-768");
+module_param_named(dev_perio_tx_fifo_size_3,
+		   dwc_otg_module_params.dev_perio_tx_fifo_size[2], int, 0444);
+MODULE_PARM_DESC(dev_perio_tx_fifo_size_3,
+		 "Number of words in the periodic Tx FIFO 4-768");
+module_param_named(dev_perio_tx_fifo_size_4,
+		   dwc_otg_module_params.dev_perio_tx_fifo_size[3], int, 0444);
+MODULE_PARM_DESC(dev_perio_tx_fifo_size_4,
+		 "Number of words in the periodic Tx FIFO 4-768");
+module_param_named(dev_perio_tx_fifo_size_5,
+		   dwc_otg_module_params.dev_perio_tx_fifo_size[4], int, 0444);
+MODULE_PARM_DESC(dev_perio_tx_fifo_size_5,
+		 "Number of words in the periodic Tx FIFO 4-768");
+module_param_named(dev_perio_tx_fifo_size_6,
+		   dwc_otg_module_params.dev_perio_tx_fifo_size[5], int, 0444);
+MODULE_PARM_DESC(dev_perio_tx_fifo_size_6,
+		 "Number of words in the periodic Tx FIFO 4-768");
+module_param_named(dev_perio_tx_fifo_size_7,
+		   dwc_otg_module_params.dev_perio_tx_fifo_size[6], int, 0444);
+MODULE_PARM_DESC(dev_perio_tx_fifo_size_7,
+		 "Number of words in the periodic Tx FIFO 4-768");
+module_param_named(dev_perio_tx_fifo_size_8,
+		   dwc_otg_module_params.dev_perio_tx_fifo_size[7], int, 0444);
+MODULE_PARM_DESC(dev_perio_tx_fifo_size_8,
+		 "Number of words in the periodic Tx FIFO 4-768");
+module_param_named(dev_perio_tx_fifo_size_9,
+		   dwc_otg_module_params.dev_perio_tx_fifo_size[8], int, 0444);
+MODULE_PARM_DESC(dev_perio_tx_fifo_size_9,
+		 "Number of words in the periodic Tx FIFO 4-768");
+module_param_named(dev_perio_tx_fifo_size_10,
+		   dwc_otg_module_params.dev_perio_tx_fifo_size[9], int, 0444);
+MODULE_PARM_DESC(dev_perio_tx_fifo_size_10,
+		 "Number of words in the periodic Tx FIFO 4-768");
+module_param_named(dev_perio_tx_fifo_size_11,
+		   dwc_otg_module_params.dev_perio_tx_fifo_size[10], int, 0444);
+MODULE_PARM_DESC(dev_perio_tx_fifo_size_11,
+		 "Number of words in the periodic Tx FIFO 4-768");
+module_param_named(dev_perio_tx_fifo_size_12,
+		   dwc_otg_module_params.dev_perio_tx_fifo_size[11], int, 0444);
+MODULE_PARM_DESC(dev_perio_tx_fifo_size_12,
+		 "Number of words in the periodic Tx FIFO 4-768");
+module_param_named(dev_perio_tx_fifo_size_13,
+		   dwc_otg_module_params.dev_perio_tx_fifo_size[12], int, 0444);
+MODULE_PARM_DESC(dev_perio_tx_fifo_size_13,
+		 "Number of words in the periodic Tx FIFO 4-768");
+module_param_named(dev_perio_tx_fifo_size_14,
+		   dwc_otg_module_params.dev_perio_tx_fifo_size[13], int, 0444);
+MODULE_PARM_DESC(dev_perio_tx_fifo_size_14,
+		 "Number of words in the periodic Tx FIFO 4-768");
+module_param_named(dev_perio_tx_fifo_size_15,
+		   dwc_otg_module_params.dev_perio_tx_fifo_size[14], int, 0444);
+MODULE_PARM_DESC(dev_perio_tx_fifo_size_15,
+		 "Number of words in the periodic Tx FIFO 4-768");
+module_param_named(host_rx_fifo_size, dwc_otg_module_params.host_rx_fifo_size,
+		   int, 0444);
+MODULE_PARM_DESC(host_rx_fifo_size, "Number of words in the Rx FIFO 16-32768");
+module_param_named(host_nperio_tx_fifo_size,
+		   dwc_otg_module_params.host_nperio_tx_fifo_size, int, 0444);
+MODULE_PARM_DESC(host_nperio_tx_fifo_size,
+		 "Number of words in the non-periodic Tx FIFO 16-32768");
+module_param_named(host_perio_tx_fifo_size,
+		   dwc_otg_module_params.host_perio_tx_fifo_size, int, 0444);
+MODULE_PARM_DESC(host_perio_tx_fifo_size,
+		 "Number of words in the host periodic Tx FIFO 16-32768");
+module_param_named(max_transfer_size, dwc_otg_module_params.max_transfer_size,
+		   int, 0444);
+/** @todo Set the max to 512K, modify checks */
+MODULE_PARM_DESC(max_transfer_size,
+		 "The maximum transfer size supported in bytes 2047-65535");
+module_param_named(max_packet_count, dwc_otg_module_params.max_packet_count,
+		   int, 0444);
+MODULE_PARM_DESC(max_packet_count,
+		 "The maximum number of packets in a transfer 15-511");
+module_param_named(host_channels, dwc_otg_module_params.host_channels, int,
+		   0444);
+MODULE_PARM_DESC(host_channels,
+		 "The number of host channel registers to use 1-16");
+module_param_named(dev_endpoints, dwc_otg_module_params.dev_endpoints, int,
+		   0444);
+MODULE_PARM_DESC(dev_endpoints,
+		 "The number of endpoints in addition to EP0 available for device mode 1-15");
+module_param_named(phy_type, dwc_otg_module_params.phy_type, int, 0444);
+MODULE_PARM_DESC(phy_type, "0=Reserved 1=UTMI+ 2=ULPI");
+module_param_named(phy_utmi_width, dwc_otg_module_params.phy_utmi_width, int,
+		   0444);
+MODULE_PARM_DESC(phy_utmi_width, "Specifies the UTMI+ Data Width 8 or 16 bits");
+module_param_named(phy_ulpi_ddr, dwc_otg_module_params.phy_ulpi_ddr, int, 0444);
+MODULE_PARM_DESC(phy_ulpi_ddr,
+		 "ULPI at double or single data rate 0=Single 1=Double");
+module_param_named(phy_ulpi_ext_vbus, dwc_otg_module_params.phy_ulpi_ext_vbus,
+		   int, 0444);
+MODULE_PARM_DESC(phy_ulpi_ext_vbus,
+		 "ULPI PHY using internal or external vbus 0=Internal");
+module_param_named(i2c_enable, dwc_otg_module_params.i2c_enable, int, 0444);
+MODULE_PARM_DESC(i2c_enable, "FS PHY Interface");
+module_param_named(ulpi_fs_ls, dwc_otg_module_params.ulpi_fs_ls, int, 0444);
+MODULE_PARM_DESC(ulpi_fs_ls, "ULPI PHY FS/LS mode only");
+module_param_named(ts_dline, dwc_otg_module_params.ts_dline, int, 0444);
+MODULE_PARM_DESC(ts_dline, "Term select Dline pulsing for all PHYs");
+module_param_named(debug, g_dbg_lvl, int, 0444);
+MODULE_PARM_DESC(debug, "");
+
+module_param_named(en_multiple_tx_fifo,
+		   dwc_otg_module_params.en_multiple_tx_fifo, int, 0444);
+MODULE_PARM_DESC(en_multiple_tx_fifo,
+		 "Dedicated Non Periodic Tx FIFOs 0=disabled 1=enabled");
+module_param_named(dev_tx_fifo_size_1,
+		   dwc_otg_module_params.dev_tx_fifo_size[0], int, 0444);
+MODULE_PARM_DESC(dev_tx_fifo_size_1, "Number of words in the Tx FIFO 4-768");
+module_param_named(dev_tx_fifo_size_2,
+		   dwc_otg_module_params.dev_tx_fifo_size[1], int, 0444);
+MODULE_PARM_DESC(dev_tx_fifo_size_2, "Number of words in the Tx FIFO 4-768");
+module_param_named(dev_tx_fifo_size_3,
+		   dwc_otg_module_params.dev_tx_fifo_size[2], int, 0444);
+MODULE_PARM_DESC(dev_tx_fifo_size_3, "Number of words in the Tx FIFO 4-768");
+module_param_named(dev_tx_fifo_size_4,
+		   dwc_otg_module_params.dev_tx_fifo_size[3], int, 0444);
+MODULE_PARM_DESC(dev_tx_fifo_size_4, "Number of words in the Tx FIFO 4-768");
+module_param_named(dev_tx_fifo_size_5,
+		   dwc_otg_module_params.dev_tx_fifo_size[4], int, 0444);
+MODULE_PARM_DESC(dev_tx_fifo_size_5, "Number of words in the Tx FIFO 4-768");
+module_param_named(dev_tx_fifo_size_6,
+		   dwc_otg_module_params.dev_tx_fifo_size[5], int, 0444);
+MODULE_PARM_DESC(dev_tx_fifo_size_6, "Number of words in the Tx FIFO 4-768");
+module_param_named(dev_tx_fifo_size_7,
+		   dwc_otg_module_params.dev_tx_fifo_size[6], int, 0444);
+MODULE_PARM_DESC(dev_tx_fifo_size_7, "Number of words in the Tx FIFO 4-768");
+module_param_named(dev_tx_fifo_size_8,
+		   dwc_otg_module_params.dev_tx_fifo_size[7], int, 0444);
+MODULE_PARM_DESC(dev_tx_fifo_size_8, "Number of words in the Tx FIFO 4-768");
+module_param_named(dev_tx_fifo_size_9,
+		   dwc_otg_module_params.dev_tx_fifo_size[8], int, 0444);
+MODULE_PARM_DESC(dev_tx_fifo_size_9, "Number of words in the Tx FIFO 4-768");
+module_param_named(dev_tx_fifo_size_10,
+		   dwc_otg_module_params.dev_tx_fifo_size[9], int, 0444);
+MODULE_PARM_DESC(dev_tx_fifo_size_10, "Number of words in the Tx FIFO 4-768");
+module_param_named(dev_tx_fifo_size_11,
+		   dwc_otg_module_params.dev_tx_fifo_size[10], int, 0444);
+MODULE_PARM_DESC(dev_tx_fifo_size_11, "Number of words in the Tx FIFO 4-768");
+module_param_named(dev_tx_fifo_size_12,
+		   dwc_otg_module_params.dev_tx_fifo_size[11], int, 0444);
+MODULE_PARM_DESC(dev_tx_fifo_size_12, "Number of words in the Tx FIFO 4-768");
+module_param_named(dev_tx_fifo_size_13,
+		   dwc_otg_module_params.dev_tx_fifo_size[12], int, 0444);
+MODULE_PARM_DESC(dev_tx_fifo_size_13, "Number of words in the Tx FIFO 4-768");
+module_param_named(dev_tx_fifo_size_14,
+		   dwc_otg_module_params.dev_tx_fifo_size[13], int, 0444);
+MODULE_PARM_DESC(dev_tx_fifo_size_14, "Number of words in the Tx FIFO 4-768");
+module_param_named(dev_tx_fifo_size_15,
+		   dwc_otg_module_params.dev_tx_fifo_size[14], int, 0444);
+MODULE_PARM_DESC(dev_tx_fifo_size_15, "Number of words in the Tx FIFO 4-768");
+
+module_param_named(thr_ctl, dwc_otg_module_params.thr_ctl, int, 0444);
+MODULE_PARM_DESC(thr_ctl,
+		 "Thresholding enable flag bit 0 - non ISO Tx thr., 1 - ISO Tx thr., 2 - Rx thr.- bit 0=disabled 1=enabled");
+module_param_named(tx_thr_length, dwc_otg_module_params.tx_thr_length, int,
+		   0444);
+MODULE_PARM_DESC(tx_thr_length, "Tx Threshold length in 32 bit DWORDs");
+module_param_named(rx_thr_length, dwc_otg_module_params.rx_thr_length, int,
+		   0444);
+MODULE_PARM_DESC(rx_thr_length, "Rx Threshold length in 32 bit DWORDs");
+
+module_param_named(pti_enable, dwc_otg_module_params.pti_enable, int, 0444);
+module_param_named(mpi_enable, dwc_otg_module_params.mpi_enable, int, 0444);
+module_param_named(lpm_enable, dwc_otg_module_params.lpm_enable, int, 0444);
+MODULE_PARM_DESC(lpm_enable, "LPM Enable 0=LPM Disabled 1=LPM Enabled");
+module_param_named(ic_usb_cap, dwc_otg_module_params.ic_usb_cap, int, 0444);
+MODULE_PARM_DESC(ic_usb_cap,
+		 "IC_USB Capability 0=IC_USB Disabled 1=IC_USB Enabled");
+module_param_named(ahb_thr_ratio, dwc_otg_module_params.ahb_thr_ratio, int,
+		   0444);
+MODULE_PARM_DESC(ahb_thr_ratio, "AHB Threshold Ratio");
+module_param_named(power_down, dwc_otg_module_params.power_down, int, 0444);
+MODULE_PARM_DESC(power_down, "Power Down Mode");
+module_param_named(reload_ctl, dwc_otg_module_params.reload_ctl, int, 0444);
+MODULE_PARM_DESC(reload_ctl, "HFIR Reload Control");
+module_param_named(dev_out_nak, dwc_otg_module_params.dev_out_nak, int, 0444);
+MODULE_PARM_DESC(dev_out_nak, "Enable Device OUT NAK");
+module_param_named(cont_on_bna, dwc_otg_module_params.cont_on_bna, int, 0444);
+MODULE_PARM_DESC(cont_on_bna, "Enable Enable Continue on BNA");
+module_param_named(ahb_single, dwc_otg_module_params.ahb_single, int, 0444);
+MODULE_PARM_DESC(ahb_single, "Enable AHB Single Support");
+module_param_named(adp_enable, dwc_otg_module_params.adp_enable, int, 0444);
+MODULE_PARM_DESC(adp_enable, "ADP Enable 0=ADP Disabled 1=ADP Enabled");
+module_param_named(otg_ver, dwc_otg_module_params.otg_ver, int, 0444);
+MODULE_PARM_DESC(otg_ver, "OTG revision supported 0=OTG 1.3 1=OTG 2.0");
+module_param(microframe_schedule, bool, 0444);
+MODULE_PARM_DESC(microframe_schedule, "Enable the microframe scheduler");
+
+module_param(fiq_enable, bool, 0444);
+MODULE_PARM_DESC(fiq_enable, "Enable the FIQ");
+module_param(nak_holdoff, ushort, 0644);
+MODULE_PARM_DESC(nak_holdoff, "Throttle duration for bulk split-transaction endpoints on a NAK. Default 8");
+module_param(fiq_fsm_enable, bool, 0444);
+MODULE_PARM_DESC(fiq_fsm_enable, "Enable the FIQ to perform split transactions as defined by fiq_fsm_mask");
+module_param(fiq_fsm_mask, ushort, 0444);
+MODULE_PARM_DESC(fiq_fsm_mask, "Bitmask of transactions to perform in the FIQ.\n"
+					"Bit 0 : Non-periodic split transactions\n"
+					"Bit 1 : Periodic split transactions\n"
+					"Bit 2 : High-speed multi-transfer isochronous\n"
+					"All other bits should be set 0.");
+module_param(int_ep_interval_min, ushort, 0644);
+MODULE_PARM_DESC(int_ep_interval_min, "Clamp high-speed Interrupt endpoints to a minimum polling interval.\n"
+					"0..1 = Use endpoint default\n"
+					"2..n = Minimum interval n microframes. Use powers of 2.\n");
+
+module_param(cil_force_host, bool, 0644);
+MODULE_PARM_DESC(cil_force_host, "On a connector-ID status change, "
+					"force Host Mode regardless of OTG state.");
+
+/** @page "Module Parameters"
+ *
+ * The following parameters may be specified when starting the module.
+ * These parameters define how the DWC_otg controller should be
+ * configured. Parameter values are passed to the CIL initialization
+ * function dwc_otg_cil_init
+ *
+ * Example: <code>modprobe dwc_otg speed=1 otg_cap=1</code>
+ *
+
+ <table>
+ <tr><td>Parameter Name</td><td>Meaning</td></tr>
+
+ <tr>
+ <td>otg_cap</td>
+ <td>Specifies the OTG capabilities. The driver will automatically detect the
+ value for this parameter if none is specified.
+ - 0: HNP and SRP capable (default, if available)
+ - 1: SRP Only capable
+ - 2: No HNP/SRP capable
+ </td></tr>
+
+ <tr>
+ <td>dma_enable</td>
+ <td>Specifies whether to use slave or DMA mode for accessing the data FIFOs.
+ The driver will automatically detect the value for this parameter if none is
+ specified.
+ - 0: Slave
+ - 1: DMA (default, if available)
+ </td></tr>
+
+ <tr>
+ <td>dma_burst_size</td>
+ <td>The DMA Burst size (applicable only for External DMA Mode).
+ - Values: 1, 4, 8 16, 32, 64, 128, 256 (default 32)
+ </td></tr>
+
+ <tr>
+ <td>speed</td>
+ <td>Specifies the maximum speed of operation in host and device mode. The
+ actual speed depends on the speed of the attached device and the value of
+ phy_type.
+ - 0: High Speed (default)
+ - 1: Full Speed
+ </td></tr>
+
+ <tr>
+ <td>host_support_fs_ls_low_power</td>
+ <td>Specifies whether low power mode is supported when attached to a Full
+ Speed or Low Speed device in host mode.
+ - 0: Don't support low power mode (default)
+ - 1: Support low power mode
+ </td></tr>
+
+ <tr>
+ <td>host_ls_low_power_phy_clk</td>
+ <td>Specifies the PHY clock rate in low power mode when connected to a Low
+ Speed device in host mode. This parameter is applicable only if
+ HOST_SUPPORT_FS_LS_LOW_POWER is enabled.
+ - 0: 48 MHz (default)
+ - 1: 6 MHz
+ </td></tr>
+
+ <tr>
+ <td>enable_dynamic_fifo</td>
+ <td> Specifies whether FIFOs may be resized by the driver software.
+ - 0: Use cC FIFO size parameters
+ - 1: Allow dynamic FIFO sizing (default)
+ </td></tr>
+
+ <tr>
+ <td>data_fifo_size</td>
+ <td>Total number of 4-byte words in the data FIFO memory. This memory
+ includes the Rx FIFO, non-periodic Tx FIFO, and periodic Tx FIFOs.
+ - Values: 32 to 32768 (default 8192)
+
+ Note: The total FIFO memory depth in the FPGA configuration is 8192.
+ </td></tr>
+
+ <tr>
+ <td>dev_rx_fifo_size</td>
+ <td>Number of 4-byte words in the Rx FIFO in device mode when dynamic
+ FIFO sizing is enabled.
+ - Values: 16 to 32768 (default 1064)
+ </td></tr>
+
+ <tr>
+ <td>dev_nperio_tx_fifo_size</td>
+ <td>Number of 4-byte words in the non-periodic Tx FIFO in device mode when
+ dynamic FIFO sizing is enabled.
+ - Values: 16 to 32768 (default 1024)
+ </td></tr>
+
+ <tr>
+ <td>dev_perio_tx_fifo_size_n (n = 1 to 15)</td>
+ <td>Number of 4-byte words in each of the periodic Tx FIFOs in device mode
+ when dynamic FIFO sizing is enabled.
+ - Values: 4 to 768 (default 256)
+ </td></tr>
+
+ <tr>
+ <td>host_rx_fifo_size</td>
+ <td>Number of 4-byte words in the Rx FIFO in host mode when dynamic FIFO
+ sizing is enabled.
+ - Values: 16 to 32768 (default 1024)
+ </td></tr>
+
+ <tr>
+ <td>host_nperio_tx_fifo_size</td>
+ <td>Number of 4-byte words in the non-periodic Tx FIFO in host mode when
+ dynamic FIFO sizing is enabled in the core.
+ - Values: 16 to 32768 (default 1024)
+ </td></tr>
+
+ <tr>
+ <td>host_perio_tx_fifo_size</td>
+ <td>Number of 4-byte words in the host periodic Tx FIFO when dynamic FIFO
+ sizing is enabled.
+ - Values: 16 to 32768 (default 1024)
+ </td></tr>
+
+ <tr>
+ <td>max_transfer_size</td>
+ <td>The maximum transfer size supported in bytes.
+ - Values: 2047 to 65,535 (default 65,535)
+ </td></tr>
+
+ <tr>
+ <td>max_packet_count</td>
+ <td>The maximum number of packets in a transfer.
+ - Values: 15 to 511 (default 511)
+ </td></tr>
+
+ <tr>
+ <td>host_channels</td>
+ <td>The number of host channel registers to use.
+ - Values: 1 to 16 (default 12)
+
+ Note: The FPGA configuration supports a maximum of 12 host channels.
+ </td></tr>
+
+ <tr>
+ <td>dev_endpoints</td>
+ <td>The number of endpoints in addition to EP0 available for device mode
+ operations.
+ - Values: 1 to 15 (default 6 IN and OUT)
+
+ Note: The FPGA configuration supports a maximum of 6 IN and OUT endpoints in
+ addition to EP0.
+ </td></tr>
+
+ <tr>
+ <td>phy_type</td>
+ <td>Specifies the type of PHY interface to use. By default, the driver will
+ automatically detect the phy_type.
+ - 0: Full Speed
+ - 1: UTMI+ (default, if available)
+ - 2: ULPI
+ </td></tr>
+
+ <tr>
+ <td>phy_utmi_width</td>
+ <td>Specifies the UTMI+ Data Width. This parameter is applicable for a
+ phy_type of UTMI+. Also, this parameter is applicable only if the
+ OTG_HSPHY_WIDTH cC parameter was set to "8 and 16 bits", meaning that the
+ core has been configured to work at either data path width.
+ - Values: 8 or 16 bits (default 16)
+ </td></tr>
+
+ <tr>
+ <td>phy_ulpi_ddr</td>
+ <td>Specifies whether the ULPI operates at double or single data rate. This
+ parameter is only applicable if phy_type is ULPI.
+ - 0: single data rate ULPI interface with 8 bit wide data bus (default)
+ - 1: double data rate ULPI interface with 4 bit wide data bus
+ </td></tr>
+
+ <tr>
+ <td>i2c_enable</td>
+ <td>Specifies whether to use the I2C interface for full speed PHY. This
+ parameter is only applicable if PHY_TYPE is FS.
+ - 0: Disabled (default)
+ - 1: Enabled
+ </td></tr>
+
+ <tr>
+ <td>ulpi_fs_ls</td>
+ <td>Specifies whether to use ULPI FS/LS mode only.
+ - 0: Disabled (default)
+ - 1: Enabled
+ </td></tr>
+
+ <tr>
+ <td>ts_dline</td>
+ <td>Specifies whether term select D-Line pulsing for all PHYs is enabled.
+ - 0: Disabled (default)
+ - 1: Enabled
+ </td></tr>
+
+ <tr>
+ <td>en_multiple_tx_fifo</td>
+ <td>Specifies whether dedicatedto tx fifos are enabled for non periodic IN EPs.
+ The driver will automatically detect the value for this parameter if none is
+ specified.
+ - 0: Disabled
+ - 1: Enabled (default, if available)
+ </td></tr>
+
+ <tr>
+ <td>dev_tx_fifo_size_n (n = 1 to 15)</td>
+ <td>Number of 4-byte words in each of the Tx FIFOs in device mode
+ when dynamic FIFO sizing is enabled.
+ - Values: 4 to 768 (default 256)
+ </td></tr>
+
+ <tr>
+ <td>tx_thr_length</td>
+ <td>Transmit Threshold length in 32 bit double words
+ - Values: 8 to 128 (default 64)
+ </td></tr>
+
+ <tr>
+ <td>rx_thr_length</td>
+ <td>Receive Threshold length in 32 bit double words
+ - Values: 8 to 128 (default 64)
+ </td></tr>
+
+<tr>
+ <td>thr_ctl</td>
+ <td>Specifies whether to enable Thresholding for Device mode. Bits 0, 1, 2 of
+ this parmater specifies if thresholding is enabled for non-Iso Tx, Iso Tx and
+ Rx transfers accordingly.
+ The driver will automatically detect the value for this parameter if none is
+ specified.
+ - Values: 0 to 7 (default 0)
+ Bit values indicate:
+ - 0: Thresholding disabled
+ - 1: Thresholding enabled
+ </td></tr>
+
+<tr>
+ <td>dma_desc_enable</td>
+ <td>Specifies whether to enable Descriptor DMA mode.
+ The driver will automatically detect the value for this parameter if none is
+ specified.
+ - 0: Descriptor DMA disabled
+ - 1: Descriptor DMA (default, if available)
+ </td></tr>
+
+<tr>
+ <td>mpi_enable</td>
+ <td>Specifies whether to enable MPI enhancement mode.
+ The driver will automatically detect the value for this parameter if none is
+ specified.
+ - 0: MPI disabled (default)
+ - 1: MPI enable
+ </td></tr>
+
+<tr>
+ <td>pti_enable</td>
+ <td>Specifies whether to enable PTI enhancement support.
+ The driver will automatically detect the value for this parameter if none is
+ specified.
+ - 0: PTI disabled (default)
+ - 1: PTI enable
+ </td></tr>
+
+<tr>
+ <td>lpm_enable</td>
+ <td>Specifies whether to enable LPM support.
+ The driver will automatically detect the value for this parameter if none is
+ specified.
+ - 0: LPM disabled
+ - 1: LPM enable (default, if available)
+ </td></tr>
+
+<tr>
+ <td>ic_usb_cap</td>
+ <td>Specifies whether to enable IC_USB capability.
+ The driver will automatically detect the value for this parameter if none is
+ specified.
+ - 0: IC_USB disabled (default, if available)
+ - 1: IC_USB enable
+ </td></tr>
+
+<tr>
+ <td>ahb_thr_ratio</td>
+ <td>Specifies AHB Threshold ratio.
+ - Values: 0 to 3 (default 0)
+ </td></tr>
+
+<tr>
+ <td>power_down</td>
+ <td>Specifies Power Down(Hibernation) Mode.
+ The driver will automatically detect the value for this parameter if none is
+ specified.
+ - 0: Power Down disabled (default)
+ - 2: Power Down enabled
+ </td></tr>
+
+ <tr>
+ <td>reload_ctl</td>
+ <td>Specifies whether dynamic reloading of the HFIR register is allowed during
+ run time. The driver will automatically detect the value for this parameter if
+ none is specified. In case the HFIR value is reloaded when HFIR.RldCtrl == 1'b0
+ the core might misbehave.
+ - 0: Reload Control disabled (default)
+ - 1: Reload Control enabled
+ </td></tr>
+
+ <tr>
+ <td>dev_out_nak</td>
+ <td>Specifies whether  Device OUT NAK enhancement enabled or no.
+ The driver will automatically detect the value for this parameter if
+ none is specified. This parameter is valid only when OTG_EN_DESC_DMA == 1b1.
+ - 0: The core does not set NAK after Bulk OUT transfer complete (default)
+ - 1: The core sets NAK after Bulk OUT transfer complete
+ </td></tr>
+
+ <tr>
+ <td>cont_on_bna</td>
+ <td>Specifies whether Enable Continue on BNA enabled or no.
+ After receiving BNA interrupt the core disables the endpoint,when the
+ endpoint is re-enabled by the application the
+ - 0: Core starts processing from the DOEPDMA descriptor (default)
+ - 1: Core starts processing from the descriptor which received the BNA.
+ This parameter is valid only when OTG_EN_DESC_DMA == 1b1.
+ </td></tr>
+
+ <tr>
+ <td>ahb_single</td>
+ <td>This bit when programmed supports SINGLE transfers for remainder data
+ in a transfer for DMA mode of operation.
+ - 0: The remainder data will be sent using INCR burst size (default)
+ - 1: The remainder data will be sent using SINGLE burst size.
+ </td></tr>
+
+<tr>
+ <td>adp_enable</td>
+ <td>Specifies whether ADP feature is enabled.
+ The driver will automatically detect the value for this parameter if none is
+ specified.
+ - 0: ADP feature disabled (default)
+ - 1: ADP feature enabled
+ </td></tr>
+
+  <tr>
+ <td>otg_ver</td>
+ <td>Specifies whether OTG is performing as USB OTG Revision 2.0 or Revision 1.3
+ USB OTG device.
+ - 0: OTG 2.0 support disabled (default)
+ - 1: OTG 2.0 support enabled
+ </td></tr>
+
+*/
diff -Nur --no-dereference linux-5.10.52-orig/drivers/usb/host/dwc_otg/dwc_otg_driver.h linux-5.10.52-v7l+/drivers/usb/host/dwc_otg/dwc_otg_driver.h
--- linux-5.10.52-orig/drivers/usb/host/dwc_otg/dwc_otg_driver.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/usb/host/dwc_otg/dwc_otg_driver.h	2021-07-25 16:46:28.677910113 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/* ==========================================================================
+ * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_driver.h $
+ * $Revision: #19 $
+ * $Date: 2010/11/15 $
+ * $Change: 1627671 $
+ *
+ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter,
+ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless
+ * otherwise expressly agreed to in writing between Synopsys and you.
+ *
+ * The Software IS NOT an item of Licensed Software or Licensed Product under
+ * any End User Software License Agreement or Agreement for Licensed Product
+ * with Synopsys or any supplement thereto. You are permitted to use and
+ * redistribute this Software in source and binary forms, with or without
+ * modification, provided that redistributions of source code must retain this
+ * notice. You may not view, use, disclose, copy or distribute this file or
+ * any information contained herein except pursuant to this license grant from
+ * Synopsys. If you do not agree with this notice, including the disclaimer
+ * below, then you are not authorized to use the Software.
+ *
+ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * ========================================================================== */
+
+#ifndef __DWC_OTG_DRIVER_H__
+#define __DWC_OTG_DRIVER_H__
+
+/** @file
+ * This file contains the interface to the Linux driver.
+ */
+#include "dwc_otg_os_dep.h"
+#include "dwc_otg_core_if.h"
+
+/* Type declarations */
+struct dwc_otg_pcd;
+struct dwc_otg_hcd;
+
+/**
+ * This structure is a wrapper that encapsulates the driver components used to
+ * manage a single DWC_otg controller.
+ */
+typedef struct dwc_otg_device {
+	/** Structure containing OS-dependent stuff. KEEP THIS STRUCT AT THE
+	 * VERY BEGINNING OF THE DEVICE STRUCT. OSes such as FreeBSD and NetBSD
+	 * require this. */
+	struct os_dependent os_dep;
+
+	/** Pointer to the core interface structure. */
+	dwc_otg_core_if_t *core_if;
+
+	/** Pointer to the PCD structure. */
+	struct dwc_otg_pcd *pcd;
+
+	/** Pointer to the HCD structure. */
+	struct dwc_otg_hcd *hcd;
+
+	/** Flag to indicate whether the common IRQ handler is installed. */
+	uint8_t common_irq_installed;
+
+} dwc_otg_device_t;
+
+/*We must clear S3C24XX_EINTPEND external interrupt register
+ * because after clearing in this register trigerred IRQ from
+ * H/W core in kernel interrupt can be occured again before OTG
+ * handlers clear all IRQ sources of Core registers because of
+ * timing latencies and Low Level IRQ Type.
+ */
+#ifdef CONFIG_MACH_IPMATE
+#define  S3C2410X_CLEAR_EINTPEND()   \
+do { \
+	__raw_writel(1UL << 11,S3C24XX_EINTPEND); \
+} while (0)
+#else
+#define  S3C2410X_CLEAR_EINTPEND()   do { } while (0)
+#endif
+
+#endif
diff -Nur --no-dereference linux-5.10.52-orig/drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.c linux-5.10.52-v7l+/drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.c
--- linux-5.10.52-orig/drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.c	2021-07-25 16:46:28.677910113 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/*
+ * dwc_otg_fiq_fsm.c - The finite state machine FIQ
+ *
+ * Copyright (c) 2013 Raspberry Pi Foundation
+ *
+ * Author: Jonathan Bell <jonathan@raspberrypi.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *	* Redistributions of source code must retain the above copyright
+ *	  notice, this list of conditions and the following disclaimer.
+ *	* Redistributions in binary form must reproduce the above copyright
+ *	  notice, this list of conditions and the following disclaimer in the
+ *	  documentation and/or other materials provided with the distribution.
+ *	* Neither the name of Raspberry Pi nor the
+ *	  names of its contributors may be used to endorse or promote products
+ *	  derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * This FIQ implements functionality that performs split transactions on
+ * the dwc_otg hardware without any outside intervention. A split transaction
+ * is "queued" by nominating a specific host channel to perform the entirety
+ * of a split transaction. This FIQ will then perform the microframe-precise
+ * scheduling required in each phase of the transaction until completion.
+ *
+ * The FIQ functionality is glued into the Synopsys driver via the entry point
+ * in the FSM enqueue function, and at the exit point in handling a HC interrupt
+ * for a FSM-enabled channel.
+ *
+ * NB: Large parts of this implementation have architecture-specific code.
+ * For porting this functionality to other ARM machines, the minimum is required:
+ * - An interrupt controller allowing the top-level dwc USB interrupt to be routed
+ *   to the FIQ
+ * - A method of forcing a software generated interrupt from FIQ mode that then
+ *   triggers an IRQ entry (with the dwc USB handler called by this IRQ number)
+ * - Guaranteed interrupt routing such that both the FIQ and SGI occur on the same
+ *   processor core - there is no locking between the FIQ and IRQ (aside from
+ *   local_fiq_disable)
+ *
+ */
+
+#include "dwc_otg_fiq_fsm.h"
+
+
+char buffer[1000*16];
+int wptr;
+void notrace _fiq_print(enum fiq_debug_level dbg_lvl, volatile struct fiq_state *state, char *fmt, ...)
+{
+	enum fiq_debug_level dbg_lvl_req = FIQDBG_ERR;
+	va_list args;
+	char text[17];
+	hfnum_data_t hfnum = { .d32 = FIQ_READ(state->dwc_regs_base + 0x408) };
+
+	if((dbg_lvl & dbg_lvl_req) || dbg_lvl == FIQDBG_ERR)
+	{
+		snprintf(text, 9, " %4d:%1u  ", hfnum.b.frnum/8, hfnum.b.frnum & 7);
+		va_start(args, fmt);
+		vsnprintf(text+8, 9, fmt, args);
+		va_end(args);
+
+		memcpy(buffer + wptr, text, 16);
+		wptr = (wptr + 16) % sizeof(buffer);
+	}
+}
+
+
+#ifdef CONFIG_ARM64
+
+inline void fiq_fsm_spin_lock(fiq_lock_t *lock)
+{
+	spin_lock((spinlock_t *)lock);
+}
+
+inline void fiq_fsm_spin_unlock(fiq_lock_t *lock)
+{
+	spin_unlock((spinlock_t *)lock);
+}
+
+#else
+
+/**
+ * fiq_fsm_spin_lock() - ARMv6+ bare bones spinlock
+ * Must be called with local interrupts and FIQ disabled.
+ */
+#if defined(CONFIG_ARCH_BCM2835) && defined(CONFIG_SMP)
+inline void fiq_fsm_spin_lock(fiq_lock_t *lock)
+{
+	unsigned long tmp;
+	uint32_t newval;
+	fiq_lock_t lockval;
+	/* Nested locking, yay. If we are on the same CPU as the fiq, then the disable
+	 * will be sufficient. If we are on a different CPU, then the lock protects us. */
+	prefetchw(&lock->slock);
+	asm volatile (
+	"1:     ldrex   %0, [%3]\n"
+	"       add     %1, %0, %4\n"
+	"       strex   %2, %1, [%3]\n"
+	"       teq     %2, #0\n"
+	"       bne     1b"
+	: "=&r" (lockval), "=&r" (newval), "=&r" (tmp)
+	: "r" (&lock->slock), "I" (1 << 16)
+	: "cc");
+
+	while (lockval.tickets.next != lockval.tickets.owner) {
+		wfe();
+		lockval.tickets.owner = READ_ONCE(lock->tickets.owner);
+	}
+	smp_mb();
+}
+#else
+inline void fiq_fsm_spin_lock(fiq_lock_t *lock) { }
+#endif
+
+/**
+ * fiq_fsm_spin_unlock() - ARMv6+ bare bones spinunlock
+ */
+#if defined(CONFIG_ARCH_BCM2835) && defined(CONFIG_SMP)
+inline void fiq_fsm_spin_unlock(fiq_lock_t *lock)
+{
+	smp_mb();
+	lock->tickets.owner++;
+	dsb_sev();
+}
+#else
+inline void fiq_fsm_spin_unlock(fiq_lock_t *lock) { }
+#endif
+
+#endif
+
+/**
+ * fiq_fsm_restart_channel() - Poke channel enable bit for a split transaction
+ * @channel: channel to re-enable
+ */
+static void fiq_fsm_restart_channel(struct fiq_state *st, int n, int force)
+{
+	hcchar_data_t hcchar = { .d32 = FIQ_READ(st->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCCHAR) };
+
+	hcchar.b.chen = 0;
+	if (st->channel[n].hcchar_copy.b.eptype & 0x1) {
+		hfnum_data_t hfnum = { .d32 = FIQ_READ(st->dwc_regs_base + HFNUM) };
+		/* Hardware bug workaround: update the ssplit index */
+		if (st->channel[n].hcsplt_copy.b.spltena)
+			st->channel[n].expected_uframe = (hfnum.b.frnum + 1) & 0x3FFF;
+
+		hcchar.b.oddfrm = (hfnum.b.frnum & 0x1) ? 0	: 1;
+	}
+
+	FIQ_WRITE(st->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCCHAR, hcchar.d32);
+	hcchar.d32 = FIQ_READ(st->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCCHAR);
+	hcchar.b.chen = 1;
+
+	FIQ_WRITE(st->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCCHAR, hcchar.d32);
+	fiq_print(FIQDBG_INT, st, "HCGO %01d %01d", n, force);
+}
+
+/**
+ * fiq_fsm_setup_csplit() - Prepare a host channel for a CSplit transaction stage
+ * @st: Pointer to the channel's state
+ * @n : channel number
+ *
+ * Change host channel registers to perform a complete-split transaction. Being mindful of the
+ * endpoint direction, set control regs up correctly.
+ */
+static void notrace fiq_fsm_setup_csplit(struct fiq_state *st, int n)
+{
+	hcsplt_data_t hcsplt = { .d32 = FIQ_READ(st->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCSPLT) };
+	hctsiz_data_t hctsiz = { .d32 = FIQ_READ(st->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCTSIZ) };
+
+	hcsplt.b.compsplt = 1;
+	if (st->channel[n].hcchar_copy.b.epdir == 1) {
+		// If IN, the CSPLIT result contains the data or a hub handshake. hctsiz = maxpacket.
+		hctsiz.b.xfersize = st->channel[n].hctsiz_copy.b.xfersize;
+	} else {
+		// If OUT, the CSPLIT result contains handshake only.
+		hctsiz.b.xfersize = 0;
+	}
+	FIQ_WRITE(st->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCSPLT, hcsplt.d32);
+	FIQ_WRITE(st->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCTSIZ, hctsiz.d32);
+	mb();
+}
+
+/**
+ * fiq_fsm_restart_np_pending() - Restart a single non-periodic contended transfer
+ * @st: Pointer to the channel's state
+ * @num_channels: Total number of host channels
+ * @orig_channel: Channel index of completed transfer
+ *
+ * In the case where an IN and OUT transfer are simultaneously scheduled to the
+ * same device/EP, inadequate hub implementations will misbehave. Once the first
+ * transfer is complete, a pending non-periodic split can then be issued.
+ */
+static void notrace fiq_fsm_restart_np_pending(struct fiq_state *st, int num_channels, int orig_channel)
+{
+	int i;
+	int dev_addr = st->channel[orig_channel].hcchar_copy.b.devaddr;
+	int ep_num = st->channel[orig_channel].hcchar_copy.b.epnum;
+	for (i = 0; i < num_channels; i++) {
+		if (st->channel[i].fsm == FIQ_NP_SSPLIT_PENDING &&
+			st->channel[i].hcchar_copy.b.devaddr == dev_addr &&
+			st->channel[i].hcchar_copy.b.epnum == ep_num) {
+			st->channel[i].fsm = FIQ_NP_SSPLIT_STARTED;
+			fiq_fsm_restart_channel(st, i, 0);
+			break;
+		}
+	}
+}
+
+static inline int notrace fiq_get_xfer_len(struct fiq_state *st, int n)
+{
+	/* The xfersize register is a bit wonky. For IN transfers, it decrements by the packet size. */
+	hctsiz_data_t hctsiz = { .d32 = FIQ_READ(st->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCTSIZ) };
+
+	if (st->channel[n].hcchar_copy.b.epdir == 0) {
+		return st->channel[n].hctsiz_copy.b.xfersize;
+	} else {
+		return st->channel[n].hctsiz_copy.b.xfersize - hctsiz.b.xfersize;
+	}
+
+}
+
+
+/**
+ * fiq_increment_dma_buf() - update DMA address for bounce buffers after a CSPLIT
+ *
+ * Of use only for IN periodic transfers.
+ */
+static int notrace fiq_increment_dma_buf(struct fiq_state *st, int num_channels, int n)
+{
+	hcdma_data_t hcdma;
+	int i = st->channel[n].dma_info.index;
+	int len;
+	struct fiq_dma_blob *blob =
+		(struct fiq_dma_blob *)(uintptr_t)st->dma_base;
+
+	len = fiq_get_xfer_len(st, n);
+	fiq_print(FIQDBG_INT, st, "LEN: %03d", len);
+	st->channel[n].dma_info.slot_len[i] = len;
+	i++;
+	if (i > 6)
+		BUG();
+
+	hcdma.d32 = (u32)(uintptr_t)&blob->channel[n].index[i].buf[0];
+	FIQ_WRITE(st->dwc_regs_base + HC_START + (HC_OFFSET * n) + HC_DMA, hcdma.d32);
+	st->channel[n].dma_info.index = i;
+	return 0;
+}
+
+/**
+ * fiq_reload_hctsiz() - for IN transactions, reset HCTSIZ
+ */
+static void notrace fiq_fsm_reload_hctsiz(struct fiq_state *st, int n)
+{
+	hctsiz_data_t hctsiz = { .d32 = FIQ_READ(st->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCTSIZ) };
+	hctsiz.b.xfersize = st->channel[n].hctsiz_copy.b.xfersize;
+	hctsiz.b.pktcnt = 1;
+	FIQ_WRITE(st->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCTSIZ, hctsiz.d32);
+}
+
+/**
+ * fiq_fsm_reload_hcdma() - for OUT transactions, rewind DMA pointer
+ */
+static void notrace fiq_fsm_reload_hcdma(struct fiq_state *st, int n)
+{
+	hcdma_data_t hcdma = st->channel[n].hcdma_copy;
+	FIQ_WRITE(st->dwc_regs_base + HC_START + (HC_OFFSET * n) + HC_DMA, hcdma.d32);
+}
+
+/**
+ * fiq_iso_out_advance() - update DMA address and split position bits
+ * for isochronous OUT transactions.
+ *
+ * Returns 1 if this is the last packet queued, 0 otherwise. Split-ALL and
+ * Split-BEGIN states are not handled - this is done when the transaction was queued.
+ *
+ * This function must only be called from the FIQ_ISO_OUT_ACTIVE state.
+ */
+static int notrace fiq_iso_out_advance(struct fiq_state *st, int num_channels, int n)
+{
+	hcsplt_data_t hcsplt;
+	hctsiz_data_t hctsiz;
+	hcdma_data_t hcdma;
+	struct fiq_dma_blob *blob =
+		(struct fiq_dma_blob *)(uintptr_t)st->dma_base;
+	int last = 0;
+	int i = st->channel[n].dma_info.index;
+
+	fiq_print(FIQDBG_INT, st, "ADV %01d %01d ", n, i);
+	i++;
+	if (i == 4)
+		last = 1;
+	if (st->channel[n].dma_info.slot_len[i+1] == 255)
+		last = 1;
+
+	/* New DMA address - address of bounce buffer referred to in index */
+	hcdma.d32 = (u32)(uintptr_t)blob->channel[n].index[i].buf;
+	//hcdma.d32 = FIQ_READ(st->dwc_regs_base + HC_START + (HC_OFFSET * n) + HC_DMA);
+	//hcdma.d32 += st->channel[n].dma_info.slot_len[i];
+	fiq_print(FIQDBG_INT, st, "LAST: %01d ", last);
+	fiq_print(FIQDBG_INT, st, "LEN: %03d", st->channel[n].dma_info.slot_len[i]);
+	hcsplt.d32 = FIQ_READ(st->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCSPLT);
+	hctsiz.d32 = FIQ_READ(st->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCTSIZ);
+	hcsplt.b.xactpos = (last) ? ISOC_XACTPOS_END : ISOC_XACTPOS_MID;
+	/* Set up new packet length */
+	hctsiz.b.pktcnt = 1;
+	hctsiz.b.xfersize = st->channel[n].dma_info.slot_len[i];
+	fiq_print(FIQDBG_INT, st, "%08x", hctsiz.d32);
+
+	st->channel[n].dma_info.index++;
+	FIQ_WRITE(st->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCSPLT, hcsplt.d32);
+	FIQ_WRITE(st->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCTSIZ, hctsiz.d32);
+	FIQ_WRITE(st->dwc_regs_base + HC_START + (HC_OFFSET * n) + HC_DMA, hcdma.d32);
+	return last;
+}
+
+/**
+ * fiq_fsm_tt_next_isoc() - queue next pending isochronous out start-split on a TT
+ *
+ * Despite the limitations of the DWC core, we can force a microframe pipeline of
+ * isochronous OUT start-split transactions while waiting for a corresponding other-type
+ * of endpoint to finish its CSPLITs. TTs have big periodic buffers therefore it
+ * is very unlikely that filling the start-split FIFO will cause data loss.
+ * This allows much better interleaving of transactions in an order-independent way-
+ * there is no requirement to prioritise isochronous, just a state-space search has
+ * to be performed on each periodic start-split complete interrupt.
+ */
+static int notrace fiq_fsm_tt_next_isoc(struct fiq_state *st, int num_channels, int n)
+{
+	int hub_addr = st->channel[n].hub_addr;
+	int port_addr = st->channel[n].port_addr;
+	int i, poked = 0;
+	for (i = 0; i < num_channels; i++) {
+		if (i == n || st->channel[i].fsm == FIQ_PASSTHROUGH)
+			continue;
+		if (st->channel[i].hub_addr == hub_addr &&
+			st->channel[i].port_addr == port_addr) {
+			switch (st->channel[i].fsm) {
+			case FIQ_PER_ISO_OUT_PENDING:
+				if (st->channel[i].nrpackets == 1) {
+					st->channel[i].fsm = FIQ_PER_ISO_OUT_LAST;
+				} else {
+					st->channel[i].fsm = FIQ_PER_ISO_OUT_ACTIVE;
+				}
+				fiq_fsm_restart_channel(st, i, 0);
+				poked = 1;
+				break;
+
+			default:
+				break;
+			}
+		}
+		if (poked)
+			break;
+	}
+	return poked;
+}
+
+/**
+ * fiq_fsm_tt_in_use() - search for host channels using this TT
+ * @n: Channel to use as reference
+ *
+ */
+int notrace noinline fiq_fsm_tt_in_use(struct fiq_state *st, int num_channels, int n)
+{
+	int hub_addr = st->channel[n].hub_addr;
+	int port_addr = st->channel[n].port_addr;
+	int i, in_use = 0;
+	for (i = 0; i < num_channels; i++) {
+		if (i == n || st->channel[i].fsm == FIQ_PASSTHROUGH)
+			continue;
+		switch (st->channel[i].fsm) {
+		/* TT is reserved for channels that are in the middle of a periodic
+		 * split transaction.
+		 */
+		case FIQ_PER_SSPLIT_STARTED:
+		case FIQ_PER_CSPLIT_WAIT:
+		case FIQ_PER_CSPLIT_NYET1:
+		//case FIQ_PER_CSPLIT_POLL:
+		case FIQ_PER_ISO_OUT_ACTIVE:
+		case FIQ_PER_ISO_OUT_LAST:
+			if (st->channel[i].hub_addr == hub_addr &&
+				st->channel[i].port_addr == port_addr) {
+				in_use = 1;
+			}
+			break;
+		default:
+			break;
+		}
+		if (in_use)
+			break;
+	}
+	return in_use;
+}
+
+/**
+ * fiq_fsm_more_csplits() - determine whether additional CSPLITs need
+ * 			to be issued for this IN transaction.
+ *
+ * We cannot tell the inbound PID of a data packet due to hardware limitations.
+ * we need to make an educated guess as to whether we need to queue another CSPLIT
+ * or not. A no-brainer is when we have received enough data to fill the endpoint
+ * size, but for endpoints that give variable-length data then we have to resort
+ * to heuristics.
+ *
+ * We also return whether this is the last CSPLIT to be queued, again based on
+ * heuristics. This is to allow a 1-uframe overlap of periodic split transactions.
+ * Note: requires at least 1 CSPLIT to have been performed prior to being called.
+ */
+
+/*
+ * We need some way of guaranteeing if a returned periodic packet of size X
+ * has a DATA0 PID.
+ * The heuristic value of 144 bytes assumes that the received data has maximal
+ * bit-stuffing and the clock frequency of the transmitting device is at the lowest
+ * permissible limit. If the transfer length results in a final packet size
+ * 144 < p <= 188, then an erroneous CSPLIT will be issued.
+ * Also used to ensure that an endpoint will nominally only return a single
+ * complete-split worth of data.
+ */
+#define DATA0_PID_HEURISTIC 144
+
+static int notrace noinline fiq_fsm_more_csplits(struct fiq_state *state, int n, int *probably_last)
+{
+
+	int i;
+	int total_len = 0;
+	int more_needed = 1;
+	struct fiq_channel_state *st = &state->channel[n];
+
+	for (i = 0; i < st->dma_info.index; i++) {
+			total_len += st->dma_info.slot_len[i];
+	}
+
+	*probably_last = 0;
+
+	if (st->hcchar_copy.b.eptype == 0x3) {
+		/*
+		 * An interrupt endpoint will take max 2 CSPLITs. if we are receiving data
+		 * then this is definitely the last CSPLIT.
+		 */
+		*probably_last = 1;
+	} else {
+		/* Isoc IN. This is a bit risky if we are the first transaction:
+		 * we may have been held off slightly. */
+		if (i > 1 && st->dma_info.slot_len[st->dma_info.index-1] <= DATA0_PID_HEURISTIC) {
+			more_needed = 0;
+		}
+		/* If in the next uframe we will receive enough data to fill the endpoint,
+		 * then only issue 1 more csplit.
+		 */
+		if (st->hctsiz_copy.b.xfersize - total_len <= DATA0_PID_HEURISTIC)
+			*probably_last = 1;
+	}
+
+	if (total_len >= st->hctsiz_copy.b.xfersize ||
+		i == 6 || total_len == 0)
+		/* Note: due to bit stuffing it is possible to have > 6 CSPLITs for
+		 * a single endpoint. Accepting more would completely break our scheduling mechanism though
+		 * - in these extreme cases we will pass through a truncated packet.
+		 */
+		more_needed = 0;
+
+	return more_needed;
+}
+
+/**
+ * fiq_fsm_too_late() - Test transaction for lateness
+ *
+ * If a SSPLIT for a large IN transaction is issued too late in a frame,
+ * the hub will disable the port to the device and respond with ERR handshakes.
+ * The hub status endpoint will not reflect this change.
+ * Returns 1 if we will issue a SSPLIT that will result in a device babble.
+ */
+int notrace fiq_fsm_too_late(struct fiq_state *st, int n)
+{
+	int uframe;
+	hfnum_data_t hfnum = { .d32 = FIQ_READ(st->dwc_regs_base + HFNUM) };
+	uframe = hfnum.b.frnum & 0x7;
+	if ((uframe < 6) && (st->channel[n].nrpackets + 1 + uframe > 7)) {
+		return 1;
+	} else {
+		return 0;
+	}
+}
+
+
+/**
+ * fiq_fsm_start_next_periodic() - A half-arsed attempt at a microframe pipeline
+ *
+ * Search pending transactions in the start-split pending state and queue them.
+ * Don't queue packets in uframe .5 (comes out in .6) (USB2.0 11.18.4).
+ * Note: we specifically don't do isochronous OUT transactions first because better
+ * use of the TT's start-split fifo can be achieved by pipelining an IN before an OUT.
+ */
+static void notrace noinline fiq_fsm_start_next_periodic(struct fiq_state *st, int num_channels)
+{
+	int n;
+	hfnum_data_t hfnum = { .d32 = FIQ_READ(st->dwc_regs_base + HFNUM) };
+	if ((hfnum.b.frnum & 0x7) == 5)
+		return;
+	for (n = 0; n < num_channels; n++) {
+		if (st->channel[n].fsm == FIQ_PER_SSPLIT_QUEUED) {
+			/* Check to see if any other transactions are using this TT */
+			if(!fiq_fsm_tt_in_use(st, num_channels, n)) {
+				if (!fiq_fsm_too_late(st, n)) {
+					st->channel[n].fsm = FIQ_PER_SSPLIT_STARTED;
+					fiq_print(FIQDBG_INT, st, "NEXTPER ");
+					fiq_fsm_restart_channel(st, n, 0);
+				} else {
+					st->channel[n].fsm = FIQ_PER_SPLIT_TIMEOUT;
+				}
+				break;
+			}
+		}
+	}
+	for (n = 0; n < num_channels; n++) {
+		if (st->channel[n].fsm == FIQ_PER_ISO_OUT_PENDING) {
+			if (!fiq_fsm_tt_in_use(st, num_channels, n)) {
+				fiq_print(FIQDBG_INT, st, "NEXTISO ");
+				if (st->channel[n].nrpackets == 1)
+					st->channel[n].fsm = FIQ_PER_ISO_OUT_LAST;
+				else
+					st->channel[n].fsm = FIQ_PER_ISO_OUT_ACTIVE;
+				fiq_fsm_restart_channel(st, n, 0);
+				break;
+			}
+		}
+	}
+}
+
+/**
+ * fiq_fsm_update_hs_isoc() - update isochronous frame and transfer data
+ * @state:	Pointer to fiq_state
+ * @n:		Channel transaction is active on
+ * @hcint:	Copy of host channel interrupt register
+ *
+ * Returns 0 if there are no more transactions for this HC to do, 1
+ * otherwise.
+ */
+static int notrace noinline fiq_fsm_update_hs_isoc(struct fiq_state *state, int n, hcint_data_t hcint)
+{
+	struct fiq_channel_state *st = &state->channel[n];
+	int xfer_len = 0, nrpackets = 0;
+	hcdma_data_t hcdma;
+	fiq_print(FIQDBG_INT, state, "HSISO %02d", n);
+
+	xfer_len = fiq_get_xfer_len(state, n);
+	st->hs_isoc_info.iso_desc[st->hs_isoc_info.index].actual_length = xfer_len;
+
+	st->hs_isoc_info.iso_desc[st->hs_isoc_info.index].status = hcint.d32;
+
+	st->hs_isoc_info.index++;
+	if (st->hs_isoc_info.index == st->hs_isoc_info.nrframes) {
+		return 0;
+	}
+
+	/* grab the next DMA address offset from the array */
+	hcdma.d32 = st->hcdma_copy.d32 + st->hs_isoc_info.iso_desc[st->hs_isoc_info.index].offset;
+	FIQ_WRITE(state->dwc_regs_base + HC_START + (HC_OFFSET * n) + HC_DMA, hcdma.d32);
+
+	/* We need to set multi_count. This is a bit tricky - has to be set per-transaction as
+	 * the core needs to be told to send the correct number. Caution: for IN transfers,
+	 * this is always set to the maximum size of the endpoint. */
+	xfer_len = st->hs_isoc_info.iso_desc[st->hs_isoc_info.index].length;
+	/* Integer divide in a FIQ: fun. FIXME: make this not suck */
+	nrpackets = (xfer_len + st->hcchar_copy.b.mps - 1) / st->hcchar_copy.b.mps;
+	if (nrpackets == 0)
+		nrpackets = 1;
+	st->hcchar_copy.b.multicnt = nrpackets;
+	st->hctsiz_copy.b.pktcnt = nrpackets;
+
+	/* Initial PID also needs to be set */
+	if (st->hcchar_copy.b.epdir == 0) {
+		st->hctsiz_copy.b.xfersize = xfer_len;
+		switch (st->hcchar_copy.b.multicnt) {
+		case 1:
+			st->hctsiz_copy.b.pid = DWC_PID_DATA0;
+			break;
+		case 2:
+		case 3:
+			st->hctsiz_copy.b.pid = DWC_PID_MDATA;
+			break;
+		}
+
+	} else {
+		st->hctsiz_copy.b.xfersize = nrpackets * st->hcchar_copy.b.mps;
+		switch (st->hcchar_copy.b.multicnt) {
+		case 1:
+			st->hctsiz_copy.b.pid = DWC_PID_DATA0;
+			break;
+		case 2:
+			st->hctsiz_copy.b.pid = DWC_PID_DATA1;
+			break;
+		case 3:
+			st->hctsiz_copy.b.pid = DWC_PID_DATA2;
+			break;
+		}
+	}
+	FIQ_WRITE(state->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCTSIZ, st->hctsiz_copy.d32);
+	FIQ_WRITE(state->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCCHAR, st->hcchar_copy.d32);
+	/* Channel is enabled on hcint handler exit */
+	fiq_print(FIQDBG_INT, state, "HSISOOUT");
+	return 1;
+}
+
+
+/**
+ * fiq_fsm_do_sof() - FSM start-of-frame interrupt handler
+ * @state:	Pointer to the state struct passed from banked FIQ mode registers.
+ * @num_channels:	set according to the DWC hardware configuration
+ *
+ * The SOF handler in FSM mode has two functions
+ * 1. Hold off SOF from causing schedule advancement in IRQ context if there's
+ *    nothing to do
+ * 2. Advance certain FSM states that require either a microframe delay, or a microframe
+ *    of holdoff.
+ *
+ * The second part is architecture-specific to mach-bcm2835 -
+ * a sane interrupt controller would have a mask register for ARM interrupt sources
+ * to be promoted to the nFIQ line, but it doesn't. Instead a single interrupt
+ * number (USB) can be enabled. This means that certain parts of the USB specification
+ * that require "wait a little while, then issue another packet" cannot be fulfilled with
+ * the timing granularity required to achieve optimal throughout. The workaround is to use
+ * the SOF "timer" (125uS) to perform this task.
+ */
+static int notrace noinline fiq_fsm_do_sof(struct fiq_state *state, int num_channels)
+{
+	hfnum_data_t hfnum = { .d32 = FIQ_READ(state->dwc_regs_base + HFNUM) };
+	int n;
+	int kick_irq = 0;
+
+	if ((hfnum.b.frnum & 0x7) == 1) {
+		/* We cannot issue csplits for transactions in the last frame past (n+1).1
+		 * Check to see if there are any transactions that are stale.
+		 * Boot them out.
+		 */
+		for (n = 0; n < num_channels; n++) {
+			switch (state->channel[n].fsm) {
+			case FIQ_PER_CSPLIT_WAIT:
+			case FIQ_PER_CSPLIT_NYET1:
+			case FIQ_PER_CSPLIT_POLL:
+			case FIQ_PER_CSPLIT_LAST:
+				/* Check if we are no longer in the same full-speed frame. */
+				if (((state->channel[n].expected_uframe & 0x3FFF) & ~0x7) <
+						(hfnum.b.frnum & ~0x7))
+					state->channel[n].fsm = FIQ_PER_SPLIT_TIMEOUT;
+				break;
+			default:
+				break;
+			}
+		}
+	}
+
+	for (n = 0; n < num_channels; n++) {
+		switch (state->channel[n].fsm) {
+
+		case FIQ_NP_SSPLIT_RETRY:
+		case FIQ_NP_IN_CSPLIT_RETRY:
+		case FIQ_NP_OUT_CSPLIT_RETRY:
+			fiq_fsm_restart_channel(state, n, 0);
+			break;
+
+		case FIQ_HS_ISOC_SLEEPING:
+			/* Is it time to wake this channel yet? */
+			if (--state->channel[n].uframe_sleeps == 0) {
+				state->channel[n].fsm = FIQ_HS_ISOC_TURBO;
+				fiq_fsm_restart_channel(state, n, 0);
+			}
+			break;
+
+		case FIQ_PER_SSPLIT_QUEUED:
+			if ((hfnum.b.frnum & 0x7) == 5)
+				break;
+			if(!fiq_fsm_tt_in_use(state, num_channels, n)) {
+				if (!fiq_fsm_too_late(state, n)) {
+					fiq_print(FIQDBG_INT, state, "SOF GO %01d", n);
+					fiq_fsm_restart_channel(state, n, 0);
+					state->channel[n].fsm = FIQ_PER_SSPLIT_STARTED;
+				} else {
+					/* Transaction cannot be started without risking a device babble error */
+					state->channel[n].fsm = FIQ_PER_SPLIT_TIMEOUT;
+					state->haintmsk_saved.b2.chint &= ~(1 << n);
+					FIQ_WRITE(state->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCINTMSK, 0);
+					kick_irq |= 1;
+				}
+			}
+			break;
+
+		case FIQ_PER_ISO_OUT_PENDING:
+			/* Ordinarily, this should be poked after the SSPLIT
+			 * complete interrupt for a competing transfer on the same
+			 * TT. Doesn't happen for aborted transactions though.
+			 */
+			if ((hfnum.b.frnum & 0x7) >= 5)
+				break;
+			if (!fiq_fsm_tt_in_use(state, num_channels, n)) {
+				/* Hardware bug. SOF can sometimes occur after the channel halt interrupt
+				 * that caused this.
+				 */
+					fiq_fsm_restart_channel(state, n, 0);
+					fiq_print(FIQDBG_INT, state, "SOF ISOC");
+					if (state->channel[n].nrpackets == 1) {
+						state->channel[n].fsm = FIQ_PER_ISO_OUT_LAST;
+					} else {
+						state->channel[n].fsm = FIQ_PER_ISO_OUT_ACTIVE;
+					}
+			}
+			break;
+
+		case FIQ_PER_CSPLIT_WAIT:
+			/* we are guaranteed to be in this state if and only if the SSPLIT interrupt
+			 * occurred when the bus transaction occurred. The SOF interrupt reversal bug
+			 * will utterly bugger this up though.
+			 */
+			if (hfnum.b.frnum != state->channel[n].expected_uframe) {
+				fiq_print(FIQDBG_INT, state, "SOFCS %d ", n);
+				state->channel[n].fsm = FIQ_PER_CSPLIT_POLL;
+				fiq_fsm_restart_channel(state, n, 0);
+				fiq_fsm_start_next_periodic(state, num_channels);
+
+			}
+			break;
+
+		case FIQ_PER_SPLIT_TIMEOUT:
+		case FIQ_DEQUEUE_ISSUED:
+			/* Ugly: we have to force a HCD interrupt.
+			 * Poke the mask for the channel in question.
+			 * We will take a fake SOF because of this, but
+			 * that's OK.
+			 */
+			state->haintmsk_saved.b2.chint &= ~(1 << n);
+			FIQ_WRITE(state->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCINTMSK, 0);
+			kick_irq |= 1;
+			break;
+
+		default:
+			break;
+		}
+	}
+
+	if (state->kick_np_queues ||
+			dwc_frame_num_le(state->next_sched_frame, hfnum.b.frnum))
+		kick_irq |= 1;
+
+	return !kick_irq;
+}
+
+
+/**
+ * fiq_fsm_do_hcintr() - FSM host channel interrupt handler
+ * @state: Pointer to the FIQ state struct
+ * @num_channels: Number of channels as per hardware config
+ * @n: channel for which HAINT(i) was raised
+ *
+ * An important property is that only the CHHLT interrupt is unmasked. Unfortunately, AHBerr is as well.
+ */
+static int notrace noinline fiq_fsm_do_hcintr(struct fiq_state *state, int num_channels, int n)
+{
+	hcint_data_t hcint;
+	hcintmsk_data_t hcintmsk;
+	hcint_data_t hcint_probe;
+	hcchar_data_t hcchar;
+	int handled = 0;
+	int restart = 0;
+	int last_csplit = 0;
+	int start_next_periodic = 0;
+	struct fiq_channel_state *st = &state->channel[n];
+	hfnum_data_t hfnum;
+
+	hcint.d32 = FIQ_READ(state->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCINT);
+	hcintmsk.d32 = FIQ_READ(state->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCINTMSK);
+	hcint_probe.d32 = hcint.d32 & hcintmsk.d32;
+
+	if (st->fsm != FIQ_PASSTHROUGH) {
+		fiq_print(FIQDBG_INT, state, "HC%01d ST%02d", n, st->fsm);
+		fiq_print(FIQDBG_INT, state, "%08x", hcint.d32);
+	}
+
+	switch (st->fsm) {
+
+	case FIQ_PASSTHROUGH:
+	case FIQ_DEQUEUE_ISSUED:
+		/* doesn't belong to us, kick it upstairs */
+		break;
+
+	case FIQ_PASSTHROUGH_ERRORSTATE:
+		/* We are here to emulate the error recovery mechanism of the dwc HCD.
+		 * Several interrupts are unmasked if a previous transaction failed - it's
+		 * death for the FIQ to attempt to handle them as the channel isn't halted.
+		 * Emulate what the HCD does in this situation: mask and continue.
+		 * The FSM has no other state setup so this has to be handled out-of-band.
+		 */
+		fiq_print(FIQDBG_ERR, state, "ERRST %02d", n);
+		if (hcint_probe.b.nak || hcint_probe.b.ack || hcint_probe.b.datatglerr) {
+			fiq_print(FIQDBG_ERR, state, "RESET %02d", n);
+			/* In some random cases we can get a NAK interrupt coincident with a Xacterr
+			 * interrupt, after the device has disappeared.
+			 */
+			if (!hcint.b.xacterr)
+				st->nr_errors = 0;
+			hcintmsk.b.nak = 0;
+			hcintmsk.b.ack = 0;
+			hcintmsk.b.datatglerr = 0;
+			FIQ_WRITE(state->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCINTMSK, hcintmsk.d32);
+			return 1;
+		}
+		if (hcint_probe.b.chhltd) {
+			fiq_print(FIQDBG_ERR, state, "CHHLT %02d", n);
+			fiq_print(FIQDBG_ERR, state, "%08x", hcint.d32);
+			return 0;
+		}
+		break;
+
+	/* Non-periodic state groups */
+	case FIQ_NP_SSPLIT_STARTED:
+	case FIQ_NP_SSPLIT_RETRY:
+		/* Got a HCINT for a NP SSPLIT. Expected ACK / NAK / fail */
+		if (hcint.b.ack) {
+			/* SSPLIT complete. For OUT, the data has been sent. For IN, the LS transaction
+			 * will start shortly. SOF needs to kick the transaction to prevent a NYET flood.
+			 */
+			if(st->hcchar_copy.b.epdir == 1)
+				st->fsm = FIQ_NP_IN_CSPLIT_RETRY;
+			else
+				st->fsm = FIQ_NP_OUT_CSPLIT_RETRY;
+			st->nr_errors = 0;
+			handled = 1;
+			fiq_fsm_setup_csplit(state, n);
+		} else if (hcint.b.nak) {
+			// No buffer space in TT. Retry on a uframe boundary.
+			fiq_fsm_reload_hcdma(state, n);
+			st->fsm = FIQ_NP_SSPLIT_RETRY;
+			handled = 1;
+		} else if (hcint.b.xacterr) {
+			// The only other one we care about is xacterr. This implies HS bus error - retry.
+			st->nr_errors++;
+			if(st->hcchar_copy.b.epdir == 0)
+				fiq_fsm_reload_hcdma(state, n);
+			st->fsm = FIQ_NP_SSPLIT_RETRY;
+			if (st->nr_errors >= 3) {
+				st->fsm = FIQ_NP_SPLIT_HS_ABORTED;
+			} else {
+				handled = 1;
+				restart = 1;
+			}
+		} else {
+			st->fsm = FIQ_NP_SPLIT_LS_ABORTED;
+			handled = 0;
+			restart = 0;
+		}
+		break;
+
+	case FIQ_NP_IN_CSPLIT_RETRY:
+		/* Received a CSPLIT done interrupt.
+		 * Expected Data/NAK/STALL/NYET for IN.
+		 */
+		if (hcint.b.xfercomp) {
+			/* For IN, data is present. */
+			st->fsm = FIQ_NP_SPLIT_DONE;
+		} else if (hcint.b.nak) {
+			/* no endpoint data. Punt it upstairs */
+			st->fsm = FIQ_NP_SPLIT_DONE;
+		} else if (hcint.b.nyet) {
+			/* CSPLIT NYET - retry on a uframe boundary. */
+			handled = 1;
+			st->nr_errors = 0;
+		} else if (hcint.b.datatglerr) {
+			/* data toggle errors do not set the xfercomp bit. */
+			st->fsm = FIQ_NP_SPLIT_LS_ABORTED;
+		} else if (hcint.b.xacterr) {
+			/* HS error. Retry immediate */
+			st->fsm = FIQ_NP_IN_CSPLIT_RETRY;
+			st->nr_errors++;
+			if (st->nr_errors >= 3) {
+				st->fsm = FIQ_NP_SPLIT_HS_ABORTED;
+			} else {
+				handled = 1;
+				restart = 1;
+			}
+		} else if (hcint.b.stall || hcint.b.bblerr) {
+			/* A STALL implies either a LS bus error or a genuine STALL. */
+			st->fsm = FIQ_NP_SPLIT_LS_ABORTED;
+		} else {
+			/*  Hardware bug. It's possible in some cases to
+			 *  get a channel halt with nothing else set when
+			 *  the response was a NYET. Treat as local 3-strikes retry.
+			 */
+			hcint_data_t hcint_test = hcint;
+			hcint_test.b.chhltd = 0;
+			if (!hcint_test.d32) {
+				st->nr_errors++;
+				if (st->nr_errors >= 3) {
+					st->fsm = FIQ_NP_SPLIT_HS_ABORTED;
+				} else {
+					handled = 1;
+				}
+			} else {
+				/* Bail out if something unexpected happened */
+				st->fsm = FIQ_NP_SPLIT_HS_ABORTED;
+			}
+		}
+		if (st->fsm != FIQ_NP_IN_CSPLIT_RETRY) {
+			fiq_fsm_restart_np_pending(state, num_channels, n);
+		}
+		break;
+
+	case FIQ_NP_OUT_CSPLIT_RETRY:
+		/* Received a CSPLIT done interrupt.
+		 * Expected ACK/NAK/STALL/NYET/XFERCOMP for OUT.*/
+		if (hcint.b.xfercomp) {
+			st->fsm = FIQ_NP_SPLIT_DONE;
+		} else if (hcint.b.nak) {
+			// The HCD will implement the holdoff on frame boundaries.
+			st->fsm = FIQ_NP_SPLIT_DONE;
+		} else if (hcint.b.nyet) {
+			// Hub still processing.
+			st->fsm = FIQ_NP_OUT_CSPLIT_RETRY;
+			handled = 1;
+			st->nr_errors = 0;
+			//restart = 1;
+		} else if (hcint.b.xacterr) {
+			/* HS error. retry immediate */
+			st->fsm = FIQ_NP_OUT_CSPLIT_RETRY;
+			st->nr_errors++;
+			if (st->nr_errors >= 3) {
+				st->fsm = FIQ_NP_SPLIT_HS_ABORTED;
+			} else {
+				handled = 1;
+				restart = 1;
+			}
+		} else if (hcint.b.stall) {
+			/* LS bus error or genuine stall */
+			st->fsm = FIQ_NP_SPLIT_LS_ABORTED;
+		} else {
+			/*
+			 * Hardware bug. It's possible in some cases to get a
+			 * channel halt with nothing else set when the response was a NYET.
+			 * Treat as local 3-strikes retry.
+			 */
+			hcint_data_t hcint_test = hcint;
+			hcint_test.b.chhltd = 0;
+			if (!hcint_test.d32) {
+				st->nr_errors++;
+				if (st->nr_errors >= 3) {
+					st->fsm = FIQ_NP_SPLIT_HS_ABORTED;
+				} else {
+					handled = 1;
+				}
+			} else {
+				// Something unexpected happened. AHBerror or babble perhaps. Let the IRQ deal with it.
+				st->fsm = FIQ_NP_SPLIT_HS_ABORTED;
+			}
+		}
+		if (st->fsm != FIQ_NP_OUT_CSPLIT_RETRY) {
+			fiq_fsm_restart_np_pending(state, num_channels, n);
+		}
+		break;
+
+	/* Periodic split states (except isoc out) */
+	case FIQ_PER_SSPLIT_STARTED:
+		/* Expect an ACK or failure for SSPLIT */
+		if (hcint.b.ack) {
+			/*
+			 * SSPLIT transfer complete interrupt - the generation of this interrupt is fraught with bugs.
+			 * For a packet queued in microframe n-3 to appear in n-2, if the channel is enabled near the EOF1
+			 * point for microframe n-3, the packet will not appear on the bus until microframe n.
+			 * Additionally, the generation of the actual interrupt is dodgy. For a packet appearing on the bus
+			 * in microframe n, sometimes the interrupt is generated immediately. Sometimes, it appears in n+1
+			 * coincident with SOF for n+1.
+			 * SOF is also buggy. It can sometimes be raised AFTER the first bus transaction has taken place.
+			 * These appear to be caused by timing/clock crossing bugs within the core itself.
+			 * State machine workaround.
+			 */
+			hfnum.d32 = FIQ_READ(state->dwc_regs_base + HFNUM);
+			hcchar.d32 = FIQ_READ(state->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCCHAR);
+			fiq_fsm_setup_csplit(state, n);
+			/* Poke the oddfrm bit. If we are equivalent, we received the interrupt at the correct
+			 * time. If not, then we're in the next SOF.
+			 */
+			if ((hfnum.b.frnum & 0x1) == hcchar.b.oddfrm) {
+				fiq_print(FIQDBG_INT, state, "CSWAIT %01d", n);
+				st->expected_uframe = hfnum.b.frnum;
+				st->fsm = FIQ_PER_CSPLIT_WAIT;
+			} else {
+				fiq_print(FIQDBG_INT, state, "CSPOL  %01d", n);
+				/* For isochronous IN endpoints,
+				 * we need to hold off if we are expecting a lot of data */
+				if (st->hcchar_copy.b.mps < DATA0_PID_HEURISTIC) {
+					start_next_periodic = 1;
+				}
+				/* Danger will robinson: we are in a broken state. If our first interrupt after
+				 * this is a NYET, it will be delayed by 1 uframe and result in an unrecoverable
+				 * lag. Unmask the NYET interrupt.
+				 */
+				st->expected_uframe = (hfnum.b.frnum + 1) & 0x3FFF;
+				st->fsm = FIQ_PER_CSPLIT_BROKEN_NYET1;
+				restart = 1;
+			}
+			handled = 1;
+		} else if (hcint.b.xacterr) {
+			/* 3-strikes retry is enabled, we have hit our max nr_errors */
+			st->fsm = FIQ_PER_SPLIT_HS_ABORTED;
+			start_next_periodic = 1;
+		} else {
+			st->fsm = FIQ_PER_SPLIT_HS_ABORTED;
+			start_next_periodic = 1;
+		}
+		/* We can now queue the next isochronous OUT transaction, if one is pending. */
+		if(fiq_fsm_tt_next_isoc(state, num_channels, n)) {
+			fiq_print(FIQDBG_INT, state, "NEXTISO ");
+		}
+		break;
+
+	case FIQ_PER_CSPLIT_NYET1:
+		/* First CSPLIT attempt was a NYET. If we get a subsequent NYET,
+		 * we are too late and the TT has dropped its CSPLIT fifo.
+		 */
+		hfnum.d32 = FIQ_READ(state->dwc_regs_base + HFNUM);
+		hcchar.d32 = FIQ_READ(state->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCCHAR);
+		start_next_periodic = 1;
+		if (hcint.b.nak) {
+			st->fsm = FIQ_PER_SPLIT_DONE;
+		} else if (hcint.b.xfercomp) {
+			fiq_increment_dma_buf(state, num_channels, n);
+			st->fsm = FIQ_PER_CSPLIT_POLL;
+			st->nr_errors = 0;
+			if (fiq_fsm_more_csplits(state, n, &last_csplit)) {
+				handled = 1;
+				restart = 1;
+				if (!last_csplit)
+					start_next_periodic = 0;
+			} else {
+				st->fsm = FIQ_PER_SPLIT_DONE;
+			}
+		} else if (hcint.b.nyet) {
+			/* Doh. Data lost. */
+			st->fsm = FIQ_PER_SPLIT_NYET_ABORTED;
+		} else if (hcint.b.xacterr || hcint.b.stall || hcint.b.bblerr) {
+			st->fsm = FIQ_PER_SPLIT_LS_ABORTED;
+		} else {
+			st->fsm = FIQ_PER_SPLIT_HS_ABORTED;
+		}
+		break;
+
+	case FIQ_PER_CSPLIT_BROKEN_NYET1:
+		/*
+		 * we got here because our host channel is in the delayed-interrupt
+		 * state and we cannot take a NYET interrupt any later than when it
+		 * occurred. Disable then re-enable the channel if this happens to force
+		 * CSPLITs to occur at the right time.
+		 */
+		hfnum.d32 = FIQ_READ(state->dwc_regs_base + HFNUM);
+		hcchar.d32 = FIQ_READ(state->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCCHAR);
+		fiq_print(FIQDBG_INT, state, "BROK: %01d ", n);
+		if (hcint.b.nak) {
+			st->fsm = FIQ_PER_SPLIT_DONE;
+			start_next_periodic = 1;
+		} else if (hcint.b.xfercomp) {
+			fiq_increment_dma_buf(state, num_channels, n);
+			if (fiq_fsm_more_csplits(state, n, &last_csplit)) {
+				st->fsm = FIQ_PER_CSPLIT_POLL;
+				handled = 1;
+				restart = 1;
+				start_next_periodic = 1;
+				/* Reload HCTSIZ for the next transfer */
+				fiq_fsm_reload_hctsiz(state, n);
+				if (!last_csplit)
+					start_next_periodic = 0;
+			} else {
+				st->fsm = FIQ_PER_SPLIT_DONE;
+			}
+		} else if (hcint.b.nyet) {
+			st->fsm = FIQ_PER_SPLIT_NYET_ABORTED;
+			start_next_periodic = 1;
+		} else if (hcint.b.xacterr || hcint.b.stall || hcint.b.bblerr) {
+			/* Local 3-strikes retry is handled by the core. This is a ERR response.*/
+			st->fsm = FIQ_PER_SPLIT_LS_ABORTED;
+		} else {
+			st->fsm = FIQ_PER_SPLIT_HS_ABORTED;
+		}
+		break;
+
+	case FIQ_PER_CSPLIT_POLL:
+		hfnum.d32 = FIQ_READ(state->dwc_regs_base + HFNUM);
+		hcchar.d32 = FIQ_READ(state->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCCHAR);
+		start_next_periodic = 1;
+		if (hcint.b.nak) {
+			st->fsm = FIQ_PER_SPLIT_DONE;
+		} else if (hcint.b.xfercomp) {
+			fiq_increment_dma_buf(state, num_channels, n);
+			if (fiq_fsm_more_csplits(state, n, &last_csplit)) {
+				handled = 1;
+				restart = 1;
+				/* Reload HCTSIZ for the next transfer */
+				fiq_fsm_reload_hctsiz(state, n);
+				if (!last_csplit)
+					start_next_periodic = 0;
+			} else {
+				st->fsm = FIQ_PER_SPLIT_DONE;
+			}
+		} else if (hcint.b.nyet) {
+			/* Are we a NYET after the first data packet? */
+			if (st->nrpackets == 0) {
+				st->fsm = FIQ_PER_CSPLIT_NYET1;
+				handled = 1;
+				restart = 1;
+			} else {
+				/* We got a NYET when polling CSPLITs. Can happen
+				 * if our heuristic fails, or if someone disables us
+				 * for any significant length of time.
+				 */
+				if (st->nr_errors >= 3) {
+					st->fsm = FIQ_PER_SPLIT_NYET_ABORTED;
+				} else {
+					st->fsm = FIQ_PER_SPLIT_DONE;
+				}
+			}
+		} else if (hcint.b.xacterr || hcint.b.stall || hcint.b.bblerr) {
+			/* For xacterr, Local 3-strikes retry is handled by the core. This is a ERR response.*/
+			st->fsm = FIQ_PER_SPLIT_LS_ABORTED;
+		} else {
+			st->fsm = FIQ_PER_SPLIT_HS_ABORTED;
+		}
+		break;
+
+	case FIQ_HS_ISOC_TURBO:
+		if (fiq_fsm_update_hs_isoc(state, n, hcint)) {
+			/* more transactions to come */
+			handled = 1;
+			fiq_print(FIQDBG_INT, state, "HSISO M ");
+			/* For strided transfers, put ourselves to sleep */
+			if (st->hs_isoc_info.stride > 1) {
+				st->uframe_sleeps = st->hs_isoc_info.stride - 1;
+				st->fsm = FIQ_HS_ISOC_SLEEPING;
+			} else {
+				restart = 1;
+			}
+		} else {
+			st->fsm = FIQ_HS_ISOC_DONE;
+			fiq_print(FIQDBG_INT, state, "HSISO F ");
+		}
+		break;
+
+	case FIQ_HS_ISOC_ABORTED:
+		/* This abort is called by the driver rewriting the state mid-transaction
+		 * which allows the dequeue mechanism to work more effectively.
+		 */
+		break;
+
+	case FIQ_PER_ISO_OUT_ACTIVE:
+		if (hcint.b.ack) {
+			if(fiq_iso_out_advance(state, num_channels, n)) {
+				/* last OUT transfer */
+				st->fsm = FIQ_PER_ISO_OUT_LAST;
+				/*
+				 * Assuming the periodic FIFO in the dwc core
+				 * actually does its job properly, we can queue
+				 * the next ssplit now and in theory, the wire
+				 * transactions will be in-order.
+				 */
+				// No it doesn't. It appears to process requests in host channel order.
+				//start_next_periodic = 1;
+			}
+			handled = 1;
+			restart = 1;
+		} else {
+			/*
+			 * Isochronous transactions carry on regardless. Log the error
+			 * and continue.
+			 */
+			//explode += 1;
+			st->nr_errors++;
+			if(fiq_iso_out_advance(state, num_channels, n)) {
+				st->fsm = FIQ_PER_ISO_OUT_LAST;
+				//start_next_periodic = 1;
+			}
+			handled = 1;
+			restart = 1;
+		}
+		break;
+
+	case FIQ_PER_ISO_OUT_LAST:
+		if (hcint.b.ack) {
+			/* All done here */
+			st->fsm = FIQ_PER_ISO_OUT_DONE;
+		} else {
+			st->fsm = FIQ_PER_ISO_OUT_DONE;
+			st->nr_errors++;
+		}
+		start_next_periodic = 1;
+		break;
+
+	case FIQ_PER_SPLIT_TIMEOUT:
+		/* SOF kicked us because we overran. */
+		start_next_periodic = 1;
+		break;
+
+	default:
+		break;
+	}
+
+	if (handled) {
+		FIQ_WRITE(state->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCINT, hcint.d32);
+	} else {
+		/* Copy the regs into the state so the IRQ knows what to do */
+		st->hcint_copy.d32 = hcint.d32;
+	}
+
+	if (restart) {
+		/* Restart always implies handled. */
+		if (restart == 2) {
+			/* For complete-split INs, the show must go on.
+			 * Force a channel restart */
+			fiq_fsm_restart_channel(state, n, 1);
+		} else {
+			fiq_fsm_restart_channel(state, n, 0);
+		}
+	}
+	if (start_next_periodic) {
+		fiq_fsm_start_next_periodic(state, num_channels);
+	}
+	if (st->fsm != FIQ_PASSTHROUGH)
+		fiq_print(FIQDBG_INT, state, "FSMOUT%02d", st->fsm);
+
+	return handled;
+}
+
+
+/**
+ * dwc_otg_fiq_fsm() - Flying State Machine (monster) FIQ
+ * @state:		pointer to state struct passed from the banked FIQ mode registers.
+ * @num_channels:	set according to the DWC hardware configuration
+ * @dma:		pointer to DMA bounce buffers for split transaction slots
+ *
+ * The FSM FIQ performs the low-level tasks that normally would be performed by the microcode
+ * inside an EHCI or similar host controller regarding split transactions. The DWC core
+ * interrupts each and every time a split transaction packet is received or sent successfully.
+ * This results in either an interrupt storm when everything is working "properly", or
+ * the interrupt latency of the system in general breaks time-sensitive periodic split
+ * transactions. Pushing the low-level, but relatively easy state machine work into the FIQ
+ * solves these problems.
+ *
+ * Return: void
+ */
+void notrace dwc_otg_fiq_fsm(struct fiq_state *state, int num_channels)
+{
+	gintsts_data_t gintsts, gintsts_handled;
+	gintmsk_data_t gintmsk;
+	//hfnum_data_t hfnum;
+	haint_data_t haint, haint_handled;
+	haintmsk_data_t haintmsk;
+	int kick_irq = 0;
+
+	/* Ensure peripheral reads issued prior to FIQ entry are complete */
+	dsb(sy);
+
+	gintsts_handled.d32 = 0;
+	haint_handled.d32 = 0;
+
+	fiq_fsm_spin_lock(&state->lock);
+	gintsts.d32 = FIQ_READ(state->dwc_regs_base + GINTSTS);
+	gintmsk.d32 = FIQ_READ(state->dwc_regs_base + GINTMSK);
+	gintsts.d32 &= gintmsk.d32;
+
+	if (gintsts.b.sofintr) {
+		/* For FSM mode, SOF is required to keep the state machine advance for
+		 * certain stages of the periodic pipeline. It's death to mask this
+		 * interrupt in that case.
+		 */
+
+		if (!fiq_fsm_do_sof(state, num_channels)) {
+			/* Kick IRQ once. Queue advancement means that all pending transactions
+			 * will get serviced when the IRQ finally executes.
+			 */
+			if (state->gintmsk_saved.b.sofintr == 1)
+				kick_irq |= 1;
+			state->gintmsk_saved.b.sofintr = 0;
+		}
+		gintsts_handled.b.sofintr = 1;
+	}
+
+	if (gintsts.b.hcintr) {
+		int i;
+		haint.d32 = FIQ_READ(state->dwc_regs_base + HAINT);
+		haintmsk.d32 = FIQ_READ(state->dwc_regs_base + HAINTMSK);
+		haint.d32 &= haintmsk.d32;
+		haint_handled.d32 = 0;
+		for (i=0; i<num_channels; i++) {
+			if (haint.b2.chint & (1 << i)) {
+				if(!fiq_fsm_do_hcintr(state, num_channels, i)) {
+					/* HCINT was not handled in FIQ
+					 * HAINT is level-sensitive, leading to level-sensitive ginststs.b.hcint bit.
+					 * Mask HAINT(i) but keep top-level hcint unmasked.
+					 */
+					state->haintmsk_saved.b2.chint &= ~(1 << i);
+				} else {
+					/* do_hcintr cleaned up after itself, but clear haint */
+					haint_handled.b2.chint |= (1 << i);
+				}
+			}
+		}
+
+		if (haint_handled.b2.chint) {
+			FIQ_WRITE(state->dwc_regs_base + HAINT, haint_handled.d32);
+		}
+
+		if (haintmsk.d32 != (haintmsk.d32 & state->haintmsk_saved.d32)) {
+			/*
+			 * This is necessary to avoid multiple retriggers of the MPHI in the case
+			 * where interrupts are held off and HCINTs start to pile up.
+			 * Only wake up the IRQ if a new interrupt came in, was not handled and was
+			 * masked.
+			 */
+			haintmsk.d32 &= state->haintmsk_saved.d32;
+			FIQ_WRITE(state->dwc_regs_base + HAINTMSK, haintmsk.d32);
+			kick_irq |= 1;
+		}
+		/* Top-Level interrupt - always handled because it's level-sensitive */
+		gintsts_handled.b.hcintr = 1;
+	}
+
+
+	/* Clear the bits in the saved register that were not handled but were triggered. */
+	state->gintmsk_saved.d32 &= ~(gintsts.d32 & ~gintsts_handled.d32);
+
+	/* FIQ didn't handle something - mask has changed - write new mask */
+	if (gintmsk.d32 != (gintmsk.d32 & state->gintmsk_saved.d32)) {
+		gintmsk.d32 &= state->gintmsk_saved.d32;
+		gintmsk.b.sofintr = 1;
+		FIQ_WRITE(state->dwc_regs_base + GINTMSK, gintmsk.d32);
+//		fiq_print(FIQDBG_INT, state, "KICKGINT");
+//		fiq_print(FIQDBG_INT, state, "%08x", gintmsk.d32);
+//		fiq_print(FIQDBG_INT, state, "%08x", state->gintmsk_saved.d32);
+		kick_irq |= 1;
+	}
+
+	if (gintsts_handled.d32) {
+		/* Only applies to edge-sensitive bits in GINTSTS */
+		FIQ_WRITE(state->dwc_regs_base + GINTSTS, gintsts_handled.d32);
+	}
+
+	/* We got an interrupt, didn't handle it. */
+	if (kick_irq) {
+		state->mphi_int_count++;
+		if (state->mphi_regs.swirq_set) {
+			FIQ_WRITE(state->mphi_regs.swirq_set, 1);
+		} else {
+			FIQ_WRITE(state->mphi_regs.outdda, state->dummy_send_dma);
+			FIQ_WRITE(state->mphi_regs.outddb, (1<<29));
+		}
+
+	}
+	state->fiq_done++;
+	mb();
+	fiq_fsm_spin_unlock(&state->lock);
+}
+
+
+/**
+ * dwc_otg_fiq_nop() - FIQ "lite"
+ * @state:	pointer to state struct passed from the banked FIQ mode registers.
+ *
+ * The "nop" handler does not intervene on any interrupts other than SOF.
+ * It is limited in scope to deciding at each SOF if the IRQ SOF handler (which deals
+ * with non-periodic/periodic queues) needs to be kicked.
+ *
+ * This is done to hold off the SOF interrupt, which occurs at a rate of 8000 per second.
+ *
+ * Return: void
+ */
+void notrace dwc_otg_fiq_nop(struct fiq_state *state)
+{
+	gintsts_data_t gintsts, gintsts_handled;
+	gintmsk_data_t gintmsk;
+	hfnum_data_t hfnum;
+
+	/* Ensure peripheral reads issued prior to FIQ entry are complete */
+	dsb(sy);
+
+	fiq_fsm_spin_lock(&state->lock);
+	hfnum.d32 = FIQ_READ(state->dwc_regs_base + HFNUM);
+	gintsts.d32 = FIQ_READ(state->dwc_regs_base + GINTSTS);
+	gintmsk.d32 = FIQ_READ(state->dwc_regs_base + GINTMSK);
+	gintsts.d32 &= gintmsk.d32;
+	gintsts_handled.d32 = 0;
+
+	if (gintsts.b.sofintr) {
+		if (!state->kick_np_queues &&
+				dwc_frame_num_gt(state->next_sched_frame, hfnum.b.frnum)) {
+			/* SOF handled, no work to do, just ACK interrupt */
+			gintsts_handled.b.sofintr = 1;
+		} else {
+			/* Kick IRQ */
+			state->gintmsk_saved.b.sofintr = 0;
+		}
+	}
+
+	/* Reset handled interrupts */
+	if(gintsts_handled.d32) {
+		FIQ_WRITE(state->dwc_regs_base + GINTSTS, gintsts_handled.d32);
+	}
+
+	/* Clear the bits in the saved register that were not handled but were triggered. */
+	state->gintmsk_saved.d32 &= ~(gintsts.d32 & ~gintsts_handled.d32);
+
+	/* We got an interrupt, didn't handle it and want to mask it */
+	if (~(state->gintmsk_saved.d32)) {
+		state->mphi_int_count++;
+		gintmsk.d32 &= state->gintmsk_saved.d32;
+		FIQ_WRITE(state->dwc_regs_base + GINTMSK, gintmsk.d32);
+		if (state->mphi_regs.swirq_set) {
+			FIQ_WRITE(state->mphi_regs.swirq_set, 1);
+		} else {
+			/* Force a clear before another dummy send */
+			FIQ_WRITE(state->mphi_regs.intstat, (1<<29));
+			FIQ_WRITE(state->mphi_regs.outdda, state->dummy_send_dma);
+			FIQ_WRITE(state->mphi_regs.outddb, (1<<29));
+		}
+	}
+	state->fiq_done++;
+	mb();
+	fiq_fsm_spin_unlock(&state->lock);
+}
diff -Nur --no-dereference linux-5.10.52-orig/drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.h linux-5.10.52-v7l+/drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.h
--- linux-5.10.52-orig/drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.h	2021-07-25 16:46:28.677910113 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/*
+ * dwc_otg_fiq_fsm.h - Finite state machine FIQ header definitions
+ *
+ * Copyright (c) 2013 Raspberry Pi Foundation
+ *
+ * Author: Jonathan Bell <jonathan@raspberrypi.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *	* Redistributions of source code must retain the above copyright
+ *	  notice, this list of conditions and the following disclaimer.
+ *	* Redistributions in binary form must reproduce the above copyright
+ *	  notice, this list of conditions and the following disclaimer in the
+ *	  documentation and/or other materials provided with the distribution.
+ *	* Neither the name of Raspberry Pi nor the
+ *	  names of its contributors may be used to endorse or promote products
+ *	  derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * This FIQ implements functionality that performs split transactions on
+ * the dwc_otg hardware without any outside intervention. A split transaction
+ * is "queued" by nominating a specific host channel to perform the entirety
+ * of a split transaction. This FIQ will then perform the microframe-precise
+ * scheduling required in each phase of the transaction until completion.
+ *
+ * The FIQ functionality has been surgically implanted into the Synopsys
+ * vendor-provided driver.
+ *
+ */
+
+#ifndef DWC_OTG_FIQ_FSM_H_
+#define DWC_OTG_FIQ_FSM_H_
+
+#include "dwc_otg_regs.h"
+#include "dwc_otg_cil.h"
+#include "dwc_otg_hcd.h"
+#include <linux/kernel.h>
+#include <linux/irqflags.h>
+#include <linux/string.h>
+#include <asm/barrier.h>
+
+#if 0
+#define FLAME_ON(x)					\
+do {							\
+	int gpioreg;                                    \
+							\
+	gpioreg = readl(__io_address(0x20200000+0x8));	\
+	gpioreg &= ~(7 << (x-20)*3);			\
+	gpioreg |= 0x1 << (x-20)*3;			\
+	writel(gpioreg, __io_address(0x20200000+0x8));	\
+							\
+	writel(1<<x, __io_address(0x20200000+(0x1C)));	\
+} while (0)
+
+#define FLAME_OFF(x)					\
+do {							\
+	writel(1<<x, __io_address(0x20200000+(0x28)));	\
+} while (0)
+#else
+#define FLAME_ON(x) do { } while (0)
+#define FLAME_OFF(X) do { } while (0)
+#endif
+
+/* This is a quick-and-dirty arch-specific register read/write. We know that
+ * writes to a peripheral on BCM2835 will always arrive in-order, also that
+ * reads and writes are executed in-order therefore the need for memory barriers
+ * is obviated if we're only talking to USB.
+ */
+#define FIQ_WRITE(_addr_,_data_) (*(volatile unsigned int *) (_addr_) = (_data_))
+#define FIQ_READ(_addr_) (*(volatile unsigned int *) (_addr_))
+
+/* FIQ-ified register definitions. Offsets are from dwc_regs_base. */
+#define GINTSTS		0x014
+#define GINTMSK		0x018
+/* Debug register. Poll the top of the received packets FIFO. */
+#define GRXSTSR		0x01C
+#define HFNUM		0x408
+#define HAINT		0x414
+#define HAINTMSK	0x418
+#define HPRT0		0x440
+
+/* HC_regs start from an offset of 0x500 */
+#define HC_START	0x500
+#define HC_OFFSET	0x020
+
+#define HC_DMA		0x14
+
+#define HCCHAR		0x00
+#define HCSPLT		0x04
+#define HCINT		0x08
+#define HCINTMSK	0x0C
+#define HCTSIZ		0x10
+
+#define ISOC_XACTPOS_ALL 	0b11
+#define ISOC_XACTPOS_BEGIN	0b10
+#define ISOC_XACTPOS_MID	0b00
+#define ISOC_XACTPOS_END	0b01
+
+#define DWC_PID_DATA2	0b01
+#define DWC_PID_MDATA	0b11
+#define DWC_PID_DATA1	0b10
+#define DWC_PID_DATA0	0b00
+
+typedef struct {
+	volatile void* base;
+	volatile void* ctrl;
+	volatile void* outdda;
+	volatile void* outddb;
+	volatile void* intstat;
+	volatile void* swirq_set;
+	volatile void* swirq_clr;
+} mphi_regs_t;
+
+enum fiq_debug_level {
+	FIQDBG_SCHED = (1 << 0),
+	FIQDBG_INT   = (1 << 1),
+	FIQDBG_ERR   = (1 << 2),
+	FIQDBG_PORTHUB = (1 << 3),
+};
+
+#ifdef CONFIG_ARM64
+
+typedef spinlock_t fiq_lock_t;
+
+#else
+
+typedef struct {
+	union {
+		uint32_t slock;
+		struct _tickets {
+			uint16_t owner;
+			uint16_t next;
+		} tickets;
+	};
+} fiq_lock_t;
+
+#endif
+
+struct fiq_state;
+
+extern void _fiq_print (enum fiq_debug_level dbg_lvl, volatile struct fiq_state *state, char *fmt, ...);
+#if 0
+#define fiq_print _fiq_print
+#else
+#define fiq_print(x, y, ...)
+#endif
+
+extern bool fiq_enable, fiq_fsm_enable;
+extern ushort nak_holdoff;
+
+/**
+ * enum fiq_fsm_state - The FIQ FSM states.
+ *
+ * This is the "core" of the FIQ FSM. Broadly, the FSM states follow the
+ * USB2.0 specification for host responses to various transaction states.
+ * There are modifications to this host state machine because of a variety of
+ * quirks and limitations in the dwc_otg hardware.
+ *
+ * The fsm state is also used to communicate back to the driver on completion of
+ * a split transaction. The end states are used in conjunction with the interrupts
+ * raised by the final transaction.
+ */
+enum fiq_fsm_state {
+	/* FIQ isn't enabled for this host channel */
+	FIQ_PASSTHROUGH = 0,
+	/* For the first interrupt received for this channel,
+	 * the FIQ has to ack any interrupts indicating success. */
+	FIQ_PASSTHROUGH_ERRORSTATE = 31,
+	/* Nonperiodic state groups */
+	FIQ_NP_SSPLIT_STARTED = 1,
+	FIQ_NP_SSPLIT_RETRY = 2,
+	/* TT contention - working around hub bugs */
+	FIQ_NP_SSPLIT_PENDING = 33,
+	FIQ_NP_OUT_CSPLIT_RETRY = 3,
+	FIQ_NP_IN_CSPLIT_RETRY = 4,
+	FIQ_NP_SPLIT_DONE = 5,
+	FIQ_NP_SPLIT_LS_ABORTED = 6,
+	/* This differentiates a HS transaction error from a LS one
+	 * (handling the hub state is different) */
+	FIQ_NP_SPLIT_HS_ABORTED = 7,
+
+	/* Periodic state groups */
+	/* Periodic transactions are either started directly by the IRQ handler
+	 * or deferred if the TT is already in use.
+	 */
+	FIQ_PER_SSPLIT_QUEUED = 8,
+	FIQ_PER_SSPLIT_STARTED = 9,
+	FIQ_PER_SSPLIT_LAST = 10,
+
+
+	FIQ_PER_ISO_OUT_PENDING = 11,
+	FIQ_PER_ISO_OUT_ACTIVE = 12,
+	FIQ_PER_ISO_OUT_LAST = 13,
+	FIQ_PER_ISO_OUT_DONE = 27,
+
+	FIQ_PER_CSPLIT_WAIT = 14,
+	FIQ_PER_CSPLIT_NYET1 = 15,
+	FIQ_PER_CSPLIT_BROKEN_NYET1 = 28,
+	FIQ_PER_CSPLIT_NYET_FAFF = 29,
+	/* For multiple CSPLITs (large isoc IN, or delayed interrupt) */
+	FIQ_PER_CSPLIT_POLL = 16,
+	/* The last CSPLIT for a transaction has been issued, differentiates
+	 * for the state machine to queue the next packet.
+	 */
+	FIQ_PER_CSPLIT_LAST = 17,
+
+	FIQ_PER_SPLIT_DONE = 18,
+	FIQ_PER_SPLIT_LS_ABORTED = 19,
+	FIQ_PER_SPLIT_HS_ABORTED = 20,
+	FIQ_PER_SPLIT_NYET_ABORTED = 21,
+	/* Frame rollover has occurred without the transaction finishing. */
+	FIQ_PER_SPLIT_TIMEOUT = 22,
+
+	/* FIQ-accelerated HS Isochronous state groups */
+	FIQ_HS_ISOC_TURBO = 23,
+	/* For interval > 1, SOF wakes up the isochronous FSM */
+	FIQ_HS_ISOC_SLEEPING = 24,
+	FIQ_HS_ISOC_DONE = 25,
+	FIQ_HS_ISOC_ABORTED = 26,
+	FIQ_DEQUEUE_ISSUED = 30,
+	FIQ_TEST = 32,
+};
+
+struct fiq_stack {
+	int magic1;
+	uint8_t stack[2048];
+	int magic2;
+};
+
+
+/**
+ * struct fiq_dma_info - DMA bounce buffer utilisation information (per-channel)
+ * @index:	Number of slots reported used for IN transactions / number of slots
+ *			transmitted for an OUT transaction
+ * @slot_len[6]: Number of actual transfer bytes in each slot (255 if unused)
+ *
+ * Split transaction transfers can have variable length depending on other bus
+ * traffic. The OTG core DMA engine requires 4-byte aligned addresses therefore
+ * each transaction needs a guaranteed aligned address. A maximum of 6 split transfers
+ * can happen per-frame.
+ */
+struct fiq_dma_info {
+	u8 index;
+	u8 slot_len[6];
+};
+
+struct fiq_split_dma_slot {
+	u8 buf[188];
+} __attribute__((packed));
+
+struct fiq_dma_channel {
+	struct fiq_split_dma_slot index[6];
+} __attribute__((packed));
+
+struct fiq_dma_blob {
+	struct fiq_dma_channel channel[0];
+} __attribute__((packed));
+
+/**
+ * struct fiq_hs_isoc_info - USB2.0 isochronous data
+ * @iso_frame:	Pointer to the array of OTG URB iso_frame_descs.
+ * @nrframes:	Total length of iso_frame_desc array
+ * @index:	Current index (FIQ-maintained)
+ * @stride:	Interval in uframes between HS isoc transactions
+ */
+struct fiq_hs_isoc_info {
+	struct dwc_otg_hcd_iso_packet_desc *iso_desc;
+	unsigned int nrframes;
+	unsigned int index;
+	unsigned int stride;
+};
+
+/**
+ * struct fiq_channel_state - FIQ state machine storage
+ * @fsm:	Current state of the channel as understood by the FIQ
+ * @nr_errors:	Number of transaction errors on this split-transaction
+ * @hub_addr:   SSPLIT/CSPLIT destination hub
+ * @port_addr:  SSPLIT/CSPLIT destination port - always 1 if single TT hub
+ * @nrpackets:  For isoc OUT, the number of split-OUT packets to transmit. For
+ * 		split-IN, number of CSPLIT data packets that were received.
+ * @hcchar_copy:
+ * @hcsplt_copy:
+ * @hcintmsk_copy:
+ * @hctsiz_copy:	Copies of the host channel registers.
+ * 			For use as scratch, or for returning state.
+ *
+ * The fiq_channel_state is state storage between interrupts for a host channel. The
+ * FSM state is stored here. Members of this structure must only be set up by the
+ * driver prior to enabling the FIQ for this host channel, and not touched until the FIQ
+ * has updated the state to either a COMPLETE state group or ABORT state group.
+ */
+
+struct fiq_channel_state {
+	enum fiq_fsm_state fsm;
+	unsigned int nr_errors;
+	unsigned int hub_addr;
+	unsigned int port_addr;
+	/* Hardware bug workaround: sometimes channel halt interrupts are
+	 * delayed until the next SOF. Keep track of when we expected to get interrupted. */
+	unsigned int expected_uframe;
+	/* number of uframes remaining (for interval > 1 HS isoc transfers) before next transfer */
+	unsigned int uframe_sleeps;
+	/* in/out for communicating number of dma buffers used, or number of ISOC to do */
+	unsigned int nrpackets;
+	struct fiq_dma_info dma_info;
+	struct fiq_hs_isoc_info hs_isoc_info;
+	/* Copies of HC registers - in/out communication from/to IRQ handler
+	 * and for ease of channel setup. A bit of mungeing is performed - for
+	 * example the hctsiz.b.maxp is _always_ the max packet size of the endpoint.
+	 */
+	hcchar_data_t hcchar_copy;
+	hcsplt_data_t hcsplt_copy;
+	hcint_data_t hcint_copy;
+	hcintmsk_data_t hcintmsk_copy;
+	hctsiz_data_t hctsiz_copy;
+	hcdma_data_t hcdma_copy;
+};
+
+/**
+ * struct fiq_state - top-level FIQ state machine storage
+ * @mphi_regs:		virtual address of the MPHI peripheral register file
+ * @dwc_regs_base:	virtual address of the base of the DWC core register file
+ * @dma_base:		physical address for the base of the DMA bounce buffers
+ * @dummy_send:		Scratch area for sending a fake message to the MPHI peripheral
+ * @gintmsk_saved:	Top-level mask of interrupts that the FIQ has not handled.
+ * 			Used for determining which interrupts fired to set off the IRQ handler.
+ * @haintmsk_saved:	Mask of interrupts from host channels that the FIQ did not handle internally.
+ * @np_count:		Non-periodic transactions in the active queue
+ * @np_sent:		Count of non-periodic transactions that have completed
+ * @next_sched_frame:	For periodic transactions handled by the driver's SOF-driven queuing mechanism,
+ * 			this is the next frame on which a SOF interrupt is required. Used to hold off
+ * 			passing SOF through to the driver until necessary.
+ * @channel[n]:		Per-channel FIQ state. Allocated during init depending on the number of host
+ * 			channels configured into the core logic.
+ *
+ * This is passed as the first argument to the dwc_otg_fiq_fsm top-level FIQ handler from the asm stub.
+ * It contains top-level state information.
+ */
+struct fiq_state {
+	fiq_lock_t lock;
+	mphi_regs_t mphi_regs;
+	void *dwc_regs_base;
+	dma_addr_t dma_base;
+	struct fiq_dma_blob *fiq_dmab;
+	void *dummy_send;
+	dma_addr_t dummy_send_dma;
+	gintmsk_data_t gintmsk_saved;
+	haintmsk_data_t haintmsk_saved;
+	int mphi_int_count;
+	unsigned int fiq_done;
+	unsigned int kick_np_queues;
+	unsigned int next_sched_frame;
+#ifdef FIQ_DEBUG
+	char * buffer;
+	unsigned int bufsiz;
+#endif
+	struct fiq_channel_state channel[0];
+};
+
+#ifdef CONFIG_ARM64
+
+#ifdef local_fiq_enable
+#undef local_fiq_enable
+#endif
+
+#ifdef local_fiq_disable
+#undef local_fiq_disable
+#endif
+
+extern void local_fiq_enable(void);
+
+extern void local_fiq_disable(void);
+
+#endif
+
+extern void fiq_fsm_spin_lock(fiq_lock_t *lock);
+
+extern void fiq_fsm_spin_unlock(fiq_lock_t *lock);
+
+extern int fiq_fsm_too_late(struct fiq_state *st, int n);
+
+extern int fiq_fsm_tt_in_use(struct fiq_state *st, int num_channels, int n);
+
+extern void dwc_otg_fiq_fsm(struct fiq_state *state, int num_channels);
+
+extern void dwc_otg_fiq_nop(struct fiq_state *state);
+
+#endif /* DWC_OTG_FIQ_FSM_H_ */
diff -Nur --no-dereference linux-5.10.52-orig/drivers/usb/host/dwc_otg/dwc_otg_fiq_stub.S linux-5.10.52-v7l+/drivers/usb/host/dwc_otg/dwc_otg_fiq_stub.S
--- linux-5.10.52-orig/drivers/usb/host/dwc_otg/dwc_otg_fiq_stub.S	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/usb/host/dwc_otg/dwc_otg_fiq_stub.S	2021-07-25 16:46:28.677910113 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/*
+ * dwc_otg_fiq_fsm.S - assembly stub for the FSM FIQ
+ *
+ * Copyright (c) 2013 Raspberry Pi Foundation
+ *
+ * Author: Jonathan Bell <jonathan@raspberrypi.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *	* Redistributions of source code must retain the above copyright
+ *	  notice, this list of conditions and the following disclaimer.
+ *	* Redistributions in binary form must reproduce the above copyright
+ *	  notice, this list of conditions and the following disclaimer in the
+ *	  documentation and/or other materials provided with the distribution.
+ *	* Neither the name of Raspberry Pi nor the
+ *	  names of its contributors may be used to endorse or promote products
+ *	  derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+#include <asm/assembler.h>
+#include <linux/linkage.h>
+
+
+.text
+
+.global _dwc_otg_fiq_stub_end;
+
+/**
+  * _dwc_otg_fiq_stub() - entry copied to the FIQ vector page to allow
+  * a C-style function call with arguments from the FIQ banked registers.
+  * r0 = &hcd->fiq_state
+  * r1 = &hcd->num_channels
+  * r2 = &hcd->dma_buffers
+  * Tramples: r0, r1, r2, r4, fp, ip
+  */
+
+ENTRY(_dwc_otg_fiq_stub)
+	/* Stash unbanked regs - SP will have been set up for us */
+	mov ip, sp;
+	stmdb sp!, {r0-r12, lr};
+#ifdef FIQ_DEBUG
+	// Cycle profiling - read cycle counter at start
+	mrc p15, 0, r5, c15, c12, 1;
+#endif
+	/* r11 = fp, don't trample it */
+	mov r4, fp;
+	/* set EABI frame size */
+	sub fp, ip, #512;
+
+	/* for fiq NOP mode - just need state */
+	mov r0, r8;
+	/* r9 = num_channels */
+	mov r1, r9;
+	/* r10 = struct *dma_bufs */
+//	mov r2, r10;
+
+	/* r4 = &fiq_c_function */
+	blx r4;
+#ifdef FIQ_DEBUG
+	mrc p15, 0, r4, c15, c12, 1;
+	subs r5, r5, r4;
+	// r5 is now the cycle count time for executing the FIQ. Store it somewhere?
+#endif
+	ldmia sp!, {r0-r12, lr};
+	subs pc, lr, #4;
+_dwc_otg_fiq_stub_end:
+END(_dwc_otg_fiq_stub)
diff -Nur --no-dereference linux-5.10.52-orig/drivers/usb/host/dwc_otg/dwc_otg_hcd.c linux-5.10.52-v7l+/drivers/usb/host/dwc_otg/dwc_otg_hcd.c
--- linux-5.10.52-orig/drivers/usb/host/dwc_otg/dwc_otg_hcd.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/usb/host/dwc_otg/dwc_otg_hcd.c	2021-07-25 16:46:28.697909777 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+
+/* ==========================================================================
+ * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_hcd.c $
+ * $Revision: #104 $
+ * $Date: 2011/10/24 $
+ * $Change: 1871159 $
+ *
+ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter,
+ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless
+ * otherwise expressly agreed to in writing between Synopsys and you.
+ *
+ * The Software IS NOT an item of Licensed Software or Licensed Product under
+ * any End User Software License Agreement or Agreement for Licensed Product
+ * with Synopsys or any supplement thereto. You are permitted to use and
+ * redistribute this Software in source and binary forms, with or without
+ * modification, provided that redistributions of source code must retain this
+ * notice. You may not view, use, disclose, copy or distribute this file or
+ * any information contained herein except pursuant to this license grant from
+ * Synopsys. If you do not agree with this notice, including the disclaimer
+ * below, then you are not authorized to use the Software.
+ *
+ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * ========================================================================== */
+#ifndef DWC_DEVICE_ONLY
+
+/** @file
+ * This file implements HCD Core. All code in this file is portable and doesn't
+ * use any OS specific functions.
+ * Interface provided by HCD Core is defined in <code><hcd_if.h></code>
+ * header file.
+ */
+
+#include <linux/usb.h>
+#include <linux/usb/hcd.h>
+
+#include "dwc_otg_hcd.h"
+#include "dwc_otg_regs.h"
+#include "dwc_otg_fiq_fsm.h"
+
+extern bool microframe_schedule;
+extern uint16_t fiq_fsm_mask, nak_holdoff;
+
+//#define DEBUG_HOST_CHANNELS
+#ifdef DEBUG_HOST_CHANNELS
+static int last_sel_trans_num_per_scheduled = 0;
+static int last_sel_trans_num_nonper_scheduled = 0;
+static int last_sel_trans_num_avail_hc_at_start = 0;
+static int last_sel_trans_num_avail_hc_at_end = 0;
+#endif /* DEBUG_HOST_CHANNELS */
+
+
+dwc_otg_hcd_t *dwc_otg_hcd_alloc_hcd(void)
+{
+	return DWC_ALLOC(sizeof(dwc_otg_hcd_t));
+}
+
+/**
+ * Connection timeout function.  An OTG host is required to display a
+ * message if the device does not connect within 10 seconds.
+ */
+void dwc_otg_hcd_connect_timeout(void *ptr)
+{
+	DWC_DEBUGPL(DBG_HCDV, "%s(%p)\n", __func__, ptr);
+	DWC_PRINTF("Connect Timeout\n");
+	__DWC_ERROR("Device Not Connected/Responding\n");
+}
+
+#if defined(DEBUG)
+static void dump_channel_info(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh)
+{
+	if (qh->channel != NULL) {
+		dwc_hc_t *hc = qh->channel;
+		dwc_list_link_t *item;
+		dwc_otg_qh_t *qh_item;
+		int num_channels = hcd->core_if->core_params->host_channels;
+		int i;
+
+		dwc_otg_hc_regs_t *hc_regs;
+		hcchar_data_t hcchar;
+		hcsplt_data_t hcsplt;
+		hctsiz_data_t hctsiz;
+		uint32_t hcdma;
+
+		hc_regs = hcd->core_if->host_if->hc_regs[hc->hc_num];
+		hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar);
+		hcsplt.d32 = DWC_READ_REG32(&hc_regs->hcsplt);
+		hctsiz.d32 = DWC_READ_REG32(&hc_regs->hctsiz);
+		hcdma = DWC_READ_REG32(&hc_regs->hcdma);
+
+		DWC_PRINTF("  Assigned to channel %p:\n", hc);
+		DWC_PRINTF("    hcchar 0x%08x, hcsplt 0x%08x\n", hcchar.d32,
+			   hcsplt.d32);
+		DWC_PRINTF("    hctsiz 0x%08x, hcdma 0x%08x\n", hctsiz.d32,
+			   hcdma);
+		DWC_PRINTF("    dev_addr: %d, ep_num: %d, ep_is_in: %d\n",
+			   hc->dev_addr, hc->ep_num, hc->ep_is_in);
+		DWC_PRINTF("    ep_type: %d\n", hc->ep_type);
+		DWC_PRINTF("    max_packet: %d\n", hc->max_packet);
+		DWC_PRINTF("    data_pid_start: %d\n", hc->data_pid_start);
+		DWC_PRINTF("    xfer_started: %d\n", hc->xfer_started);
+		DWC_PRINTF("    halt_status: %d\n", hc->halt_status);
+		DWC_PRINTF("    xfer_buff: %p\n", hc->xfer_buff);
+		DWC_PRINTF("    xfer_len: %d\n", hc->xfer_len);
+		DWC_PRINTF("    qh: %p\n", hc->qh);
+		DWC_PRINTF("  NP inactive sched:\n");
+		DWC_LIST_FOREACH(item, &hcd->non_periodic_sched_inactive) {
+			qh_item =
+			    DWC_LIST_ENTRY(item, dwc_otg_qh_t, qh_list_entry);
+			DWC_PRINTF("    %p\n", qh_item);
+		}
+		DWC_PRINTF("  NP active sched:\n");
+		DWC_LIST_FOREACH(item, &hcd->non_periodic_sched_active) {
+			qh_item =
+			    DWC_LIST_ENTRY(item, dwc_otg_qh_t, qh_list_entry);
+			DWC_PRINTF("    %p\n", qh_item);
+		}
+		DWC_PRINTF("  Channels: \n");
+		for (i = 0; i < num_channels; i++) {
+			dwc_hc_t *hc = hcd->hc_ptr_array[i];
+			DWC_PRINTF("    %2d: %p\n", i, hc);
+		}
+	}
+}
+#else
+#define dump_channel_info(hcd, qh)
+#endif /* DEBUG */
+
+/**
+ * Work queue function for starting the HCD when A-Cable is connected.
+ * The hcd_start() must be called in a process context.
+ */
+static void hcd_start_func(void *_vp)
+{
+	dwc_otg_hcd_t *hcd = (dwc_otg_hcd_t *) _vp;
+
+	DWC_DEBUGPL(DBG_HCDV, "%s() %p\n", __func__, hcd);
+	if (hcd) {
+		hcd->fops->start(hcd);
+	}
+}
+
+static void del_xfer_timers(dwc_otg_hcd_t * hcd)
+{
+#ifdef DEBUG
+	int i;
+	int num_channels = hcd->core_if->core_params->host_channels;
+	for (i = 0; i < num_channels; i++) {
+		DWC_TIMER_CANCEL(hcd->core_if->hc_xfer_timer[i]);
+	}
+#endif
+}
+
+static void del_timers(dwc_otg_hcd_t * hcd)
+{
+	del_xfer_timers(hcd);
+	DWC_TIMER_CANCEL(hcd->conn_timer);
+}
+
+/**
+ * Processes all the URBs in a single list of QHs. Completes them with
+ * -ESHUTDOWN and frees the QTD.
+ */
+static void kill_urbs_in_qh_list(dwc_otg_hcd_t * hcd, dwc_list_link_t * qh_list)
+{
+	dwc_list_link_t *qh_item, *qh_tmp;
+	dwc_otg_qh_t *qh;
+	dwc_otg_qtd_t *qtd, *qtd_tmp;
+	int quiesced = 0;
+
+	DWC_LIST_FOREACH_SAFE(qh_item, qh_tmp, qh_list) {
+		qh = DWC_LIST_ENTRY(qh_item, dwc_otg_qh_t, qh_list_entry);
+		DWC_CIRCLEQ_FOREACH_SAFE(qtd, qtd_tmp,
+					 &qh->qtd_list, qtd_list_entry) {
+			qtd = DWC_CIRCLEQ_FIRST(&qh->qtd_list);
+			if (qtd->urb != NULL) {
+				hcd->fops->complete(hcd, qtd->urb->priv,
+						    qtd->urb, -DWC_E_SHUTDOWN);
+				dwc_otg_hcd_qtd_remove_and_free(hcd, qtd, qh);
+			}
+
+		}
+		if(qh->channel) {
+			int n = qh->channel->hc_num;
+			/* Using hcchar.chen == 1 is not a reliable test.
+			 * It is possible that the channel has already halted
+			 * but not yet been through the IRQ handler.
+			 */
+			if (fiq_fsm_enable && (hcd->fiq_state->channel[qh->channel->hc_num].fsm != FIQ_PASSTHROUGH)) {
+				qh->channel->halt_status = DWC_OTG_HC_XFER_URB_DEQUEUE;
+				qh->channel->halt_pending = 1;
+				if (hcd->fiq_state->channel[n].fsm == FIQ_HS_ISOC_TURBO ||
+				    hcd->fiq_state->channel[n].fsm == FIQ_HS_ISOC_SLEEPING)
+					hcd->fiq_state->channel[n].fsm = FIQ_HS_ISOC_ABORTED;
+				/* We're called from disconnect callback or in the middle of freeing the HCD here,
+				 * so FIQ is disabled, top-level interrupts masked and we're holding the spinlock.
+				 * No further URBs will be submitted, but wait 1 microframe for any previously
+				 * submitted periodic DMA to finish.
+				 */
+				if (!quiesced) {
+					udelay(125);
+					quiesced = 1;
+				}
+			} else {
+				dwc_otg_hc_halt(hcd->core_if, qh->channel,
+						DWC_OTG_HC_XFER_URB_DEQUEUE);
+			}
+			qh->channel = NULL;
+		}
+		dwc_otg_hcd_qh_remove(hcd, qh);
+	}
+}
+
+/**
+ * Responds with an error status of ESHUTDOWN to all URBs in the non-periodic
+ * and periodic schedules. The QTD associated with each URB is removed from
+ * the schedule and freed. This function may be called when a disconnect is
+ * detected or when the HCD is being stopped.
+ */
+static void kill_all_urbs(dwc_otg_hcd_t * hcd)
+{
+	kill_urbs_in_qh_list(hcd, &hcd->non_periodic_sched_inactive);
+	kill_urbs_in_qh_list(hcd, &hcd->non_periodic_sched_active);
+	kill_urbs_in_qh_list(hcd, &hcd->periodic_sched_inactive);
+	kill_urbs_in_qh_list(hcd, &hcd->periodic_sched_ready);
+	kill_urbs_in_qh_list(hcd, &hcd->periodic_sched_assigned);
+	kill_urbs_in_qh_list(hcd, &hcd->periodic_sched_queued);
+}
+
+/**
+ * Start the connection timer.  An OTG host is required to display a
+ * message if the device does not connect within 10 seconds.  The
+ * timer is deleted if a port connect interrupt occurs before the
+ * timer expires.
+ */
+static void dwc_otg_hcd_start_connect_timer(dwc_otg_hcd_t * hcd)
+{
+	DWC_TIMER_SCHEDULE(hcd->conn_timer, 10000 /* 10 secs */ );
+}
+
+/**
+ * HCD Callback function for disconnect of the HCD.
+ *
+ * @param p void pointer to the <code>struct usb_hcd</code>
+ */
+static int32_t dwc_otg_hcd_session_start_cb(void *p)
+{
+	dwc_otg_hcd_t *dwc_otg_hcd;
+	DWC_DEBUGPL(DBG_HCDV, "%s(%p)\n", __func__, p);
+	dwc_otg_hcd = p;
+	dwc_otg_hcd_start_connect_timer(dwc_otg_hcd);
+	return 1;
+}
+
+/**
+ * HCD Callback function for starting the HCD when A-Cable is
+ * connected.
+ *
+ * @param p void pointer to the <code>struct usb_hcd</code>
+ */
+static int32_t dwc_otg_hcd_start_cb(void *p)
+{
+	dwc_otg_hcd_t *dwc_otg_hcd = p;
+	dwc_otg_core_if_t *core_if;
+	hprt0_data_t hprt0;
+
+	core_if = dwc_otg_hcd->core_if;
+
+	if (core_if->op_state == B_HOST) {
+		/*
+		 * Reset the port.  During a HNP mode switch the reset
+		 * needs to occur within 1ms and have a duration of at
+		 * least 50ms.
+		 */
+		hprt0.d32 = dwc_otg_read_hprt0(core_if);
+		hprt0.b.prtrst = 1;
+		DWC_WRITE_REG32(core_if->host_if->hprt0, hprt0.d32);
+	}
+	DWC_WORKQ_SCHEDULE_DELAYED(core_if->wq_otg,
+				   hcd_start_func, dwc_otg_hcd, 50,
+				   "start hcd");
+
+	return 1;
+}
+
+/**
+ * HCD Callback function for disconnect of the HCD.
+ *
+ * @param p void pointer to the <code>struct usb_hcd</code>
+ */
+static int32_t dwc_otg_hcd_disconnect_cb(void *p)
+{
+	gintsts_data_t intr;
+	dwc_otg_hcd_t *dwc_otg_hcd = p;
+
+	DWC_SPINLOCK(dwc_otg_hcd->lock);
+	/*
+	 * Set status flags for the hub driver.
+	 */
+	dwc_otg_hcd->flags.b.port_connect_status_change = 1;
+	dwc_otg_hcd->flags.b.port_connect_status = 0;
+	if(fiq_enable) {
+		local_fiq_disable();
+		fiq_fsm_spin_lock(&dwc_otg_hcd->fiq_state->lock);
+	}
+	/*
+	 * Shutdown any transfers in process by clearing the Tx FIFO Empty
+	 * interrupt mask and status bits and disabling subsequent host
+	 * channel interrupts.
+	 */
+	intr.d32 = 0;
+	intr.b.nptxfempty = 1;
+	intr.b.ptxfempty = 1;
+	intr.b.hcintr = 1;
+	DWC_MODIFY_REG32(&dwc_otg_hcd->core_if->core_global_regs->gintmsk,
+			 intr.d32, 0);
+	DWC_MODIFY_REG32(&dwc_otg_hcd->core_if->core_global_regs->gintsts,
+			 intr.d32, 0);
+
+	del_timers(dwc_otg_hcd);
+
+	/*
+	 * Turn off the vbus power only if the core has transitioned to device
+	 * mode. If still in host mode, need to keep power on to detect a
+	 * reconnection.
+	 */
+	if (dwc_otg_is_device_mode(dwc_otg_hcd->core_if)) {
+		if (dwc_otg_hcd->core_if->op_state != A_SUSPEND) {
+			hprt0_data_t hprt0 = {.d32 = 0 };
+			DWC_PRINTF("Disconnect: PortPower off\n");
+			hprt0.b.prtpwr = 0;
+			DWC_WRITE_REG32(dwc_otg_hcd->core_if->host_if->hprt0,
+					hprt0.d32);
+		}
+
+		dwc_otg_disable_host_interrupts(dwc_otg_hcd->core_if);
+	}
+
+	/* Respond with an error status to all URBs in the schedule. */
+	kill_all_urbs(dwc_otg_hcd);
+
+	if (dwc_otg_is_host_mode(dwc_otg_hcd->core_if)) {
+		/* Clean up any host channels that were in use. */
+		int num_channels;
+		int i;
+		dwc_hc_t *channel;
+		dwc_otg_hc_regs_t *hc_regs;
+		hcchar_data_t hcchar;
+
+		num_channels = dwc_otg_hcd->core_if->core_params->host_channels;
+
+		if (!dwc_otg_hcd->core_if->dma_enable) {
+			/* Flush out any channel requests in slave mode. */
+			for (i = 0; i < num_channels; i++) {
+				channel = dwc_otg_hcd->hc_ptr_array[i];
+				if (DWC_CIRCLEQ_EMPTY_ENTRY
+				    (channel, hc_list_entry)) {
+					hc_regs =
+					    dwc_otg_hcd->core_if->
+					    host_if->hc_regs[i];
+					hcchar.d32 =
+					    DWC_READ_REG32(&hc_regs->hcchar);
+					if (hcchar.b.chen) {
+						hcchar.b.chen = 0;
+						hcchar.b.chdis = 1;
+						hcchar.b.epdir = 0;
+						DWC_WRITE_REG32
+						    (&hc_regs->hcchar,
+						     hcchar.d32);
+					}
+				}
+			}
+		}
+
+		if(fiq_fsm_enable) {
+			for(i=0; i < 128; i++) {
+				dwc_otg_hcd->hub_port[i] = 0;
+			}
+		}
+	}
+
+	if(fiq_enable) {
+		fiq_fsm_spin_unlock(&dwc_otg_hcd->fiq_state->lock);
+		local_fiq_enable();
+	}
+
+	if (dwc_otg_hcd->fops->disconnect) {
+		dwc_otg_hcd->fops->disconnect(dwc_otg_hcd);
+	}
+
+	DWC_SPINUNLOCK(dwc_otg_hcd->lock);
+	return 1;
+}
+
+/**
+ * HCD Callback function for stopping the HCD.
+ *
+ * @param p void pointer to the <code>struct usb_hcd</code>
+ */
+static int32_t dwc_otg_hcd_stop_cb(void *p)
+{
+	dwc_otg_hcd_t *dwc_otg_hcd = p;
+
+	DWC_DEBUGPL(DBG_HCDV, "%s(%p)\n", __func__, p);
+	dwc_otg_hcd_stop(dwc_otg_hcd);
+	return 1;
+}
+
+#ifdef CONFIG_USB_DWC_OTG_LPM
+/**
+ * HCD Callback function for sleep of HCD.
+ *
+ * @param p void pointer to the <code>struct usb_hcd</code>
+ */
+static int dwc_otg_hcd_sleep_cb(void *p)
+{
+	dwc_otg_hcd_t *hcd = p;
+
+	dwc_otg_hcd_free_hc_from_lpm(hcd);
+
+	return 0;
+}
+#endif
+
+
+/**
+ * HCD Callback function for Remote Wakeup.
+ *
+ * @param p void pointer to the <code>struct usb_hcd</code>
+ */
+static int dwc_otg_hcd_rem_wakeup_cb(void *p)
+{
+	dwc_otg_hcd_t *hcd = p;
+
+	if (hcd->core_if->lx_state == DWC_OTG_L2) {
+		hcd->flags.b.port_suspend_change = 1;
+	}
+#ifdef CONFIG_USB_DWC_OTG_LPM
+	else {
+		hcd->flags.b.port_l1_change = 1;
+	}
+#endif
+	return 0;
+}
+
+/**
+ * Halts the DWC_otg host mode operations in a clean manner. USB transfers are
+ * stopped.
+ */
+void dwc_otg_hcd_stop(dwc_otg_hcd_t * hcd)
+{
+	hprt0_data_t hprt0 = {.d32 = 0 };
+
+	DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD STOP\n");
+
+	/*
+	 * The root hub should be disconnected before this function is called.
+	 * The disconnect will clear the QTD lists (via ..._hcd_urb_dequeue)
+	 * and the QH lists (via ..._hcd_endpoint_disable).
+	 */
+
+	/* Turn off all host-specific interrupts. */
+	dwc_otg_disable_host_interrupts(hcd->core_if);
+
+	/* Turn off the vbus power */
+	DWC_PRINTF("PortPower off\n");
+	hprt0.b.prtpwr = 0;
+	DWC_WRITE_REG32(hcd->core_if->host_if->hprt0, hprt0.d32);
+	dwc_mdelay(1);
+}
+
+int dwc_otg_hcd_urb_enqueue(dwc_otg_hcd_t * hcd,
+			    dwc_otg_hcd_urb_t * dwc_otg_urb, void **ep_handle,
+			    int atomic_alloc)
+{
+	int retval = 0;
+	uint8_t needs_scheduling = 0;
+	dwc_otg_transaction_type_e tr_type;
+	dwc_otg_qtd_t *qtd;
+	gintmsk_data_t intr_mask = {.d32 = 0 };
+	hprt0_data_t hprt0 = { .d32 = 0 };
+
+#ifdef DEBUG /* integrity checks (Broadcom) */
+	if (NULL == hcd->core_if) {
+		DWC_ERROR("**** DWC OTG HCD URB Enqueue - HCD has NULL core_if\n");
+		/* No longer connected. */
+		return -DWC_E_INVALID;
+	}
+#endif
+	if (!hcd->flags.b.port_connect_status) {
+		/* No longer connected. */
+		DWC_ERROR("Not connected\n");
+		return -DWC_E_NO_DEVICE;
+	}
+
+	/* Some core configurations cannot support LS traffic on a FS root port */
+	if ((hcd->fops->speed(hcd, dwc_otg_urb->priv) == USB_SPEED_LOW) &&
+		(hcd->core_if->hwcfg2.b.fs_phy_type == 1) &&
+		(hcd->core_if->hwcfg2.b.hs_phy_type == 1)) {
+			hprt0.d32 = DWC_READ_REG32(hcd->core_if->host_if->hprt0);
+			if (hprt0.b.prtspd == DWC_HPRT0_PRTSPD_FULL_SPEED) {
+				return -DWC_E_NO_DEVICE;
+			}
+	}
+
+	qtd = dwc_otg_hcd_qtd_create(dwc_otg_urb, atomic_alloc);
+	if (qtd == NULL) {
+		DWC_ERROR("DWC OTG HCD URB Enqueue failed creating QTD\n");
+		return -DWC_E_NO_MEMORY;
+	}
+#ifdef DEBUG /* integrity checks (Broadcom) */
+	if (qtd->urb == NULL) {
+		DWC_ERROR("**** DWC OTG HCD URB Enqueue created QTD with no URBs\n");
+		return -DWC_E_NO_MEMORY;
+	}
+	if (qtd->urb->priv == NULL) {
+		DWC_ERROR("**** DWC OTG HCD URB Enqueue created QTD URB with no URB handle\n");
+		return -DWC_E_NO_MEMORY;
+	}
+#endif
+	intr_mask.d32 = DWC_READ_REG32(&hcd->core_if->core_global_regs->gintmsk);
+	if(!intr_mask.b.sofintr || fiq_enable) needs_scheduling = 1;
+	if((((dwc_otg_qh_t *)ep_handle)->ep_type == UE_BULK) && !(qtd->urb->flags & URB_GIVEBACK_ASAP))
+		/* Do not schedule SG transactions until qtd has URB_GIVEBACK_ASAP set */
+		needs_scheduling = 0;
+
+	retval = dwc_otg_hcd_qtd_add(qtd, hcd, (dwc_otg_qh_t **) ep_handle, atomic_alloc);
+            // creates a new queue in ep_handle if it doesn't exist already
+	if (retval < 0) {
+		DWC_ERROR("DWC OTG HCD URB Enqueue failed adding QTD. "
+			  "Error status %d\n", retval);
+		dwc_otg_hcd_qtd_free(qtd);
+		return retval;
+	}
+
+	if(needs_scheduling) {
+		tr_type = dwc_otg_hcd_select_transactions(hcd);
+		if (tr_type != DWC_OTG_TRANSACTION_NONE) {
+			dwc_otg_hcd_queue_transactions(hcd, tr_type);
+		}
+	}
+	return retval;
+}
+
+int dwc_otg_hcd_urb_dequeue(dwc_otg_hcd_t * hcd,
+			    dwc_otg_hcd_urb_t * dwc_otg_urb)
+{
+	dwc_otg_qh_t *qh;
+	dwc_otg_qtd_t *urb_qtd;
+	BUG_ON(!hcd);
+	BUG_ON(!dwc_otg_urb);
+
+#ifdef DEBUG /* integrity checks (Broadcom) */
+
+	if (hcd == NULL) {
+		DWC_ERROR("**** DWC OTG HCD URB Dequeue has NULL HCD\n");
+		return -DWC_E_INVALID;
+	}
+	if (dwc_otg_urb == NULL) {
+		DWC_ERROR("**** DWC OTG HCD URB Dequeue has NULL URB\n");
+		return -DWC_E_INVALID;
+	}
+	if (dwc_otg_urb->qtd == NULL) {
+		DWC_ERROR("**** DWC OTG HCD URB Dequeue with NULL QTD\n");
+		return -DWC_E_INVALID;
+	}
+	urb_qtd = dwc_otg_urb->qtd;
+	BUG_ON(!urb_qtd);
+	if (urb_qtd->qh == NULL) {
+		DWC_ERROR("**** DWC OTG HCD URB Dequeue with QTD with NULL Q handler\n");
+		return -DWC_E_INVALID;
+	}
+#else
+	urb_qtd = dwc_otg_urb->qtd;
+	BUG_ON(!urb_qtd);
+#endif
+	qh = urb_qtd->qh;
+	BUG_ON(!qh);
+	if (CHK_DEBUG_LEVEL(DBG_HCDV | DBG_HCD_URB)) {
+		if (urb_qtd->in_process) {
+			dump_channel_info(hcd, qh);
+		}
+	}
+#ifdef DEBUG /* integrity checks (Broadcom) */
+	if (hcd->core_if == NULL) {
+		DWC_ERROR("**** DWC OTG HCD URB Dequeue HCD has NULL core_if\n");
+		return -DWC_E_INVALID;
+	}
+#endif
+	if (urb_qtd->in_process && qh->channel) {
+		/* The QTD is in process (it has been assigned to a channel). */
+		if (hcd->flags.b.port_connect_status) {
+			int n = qh->channel->hc_num;
+			/*
+			 * If still connected (i.e. in host mode), halt the
+			 * channel so it can be used for other transfers. If
+			 * no longer connected, the host registers can't be
+			 * written to halt the channel since the core is in
+			 * device mode.
+			 */
+			/* In FIQ FSM mode, we need to shut down carefully.
+			 * The FIQ may attempt to restart a disabled channel */
+			if (fiq_fsm_enable && (hcd->fiq_state->channel[n].fsm != FIQ_PASSTHROUGH)) {
+				int retries = 3;
+				int running = 0;
+				enum fiq_fsm_state state;
+
+				local_fiq_disable();
+				fiq_fsm_spin_lock(&hcd->fiq_state->lock);
+				qh->channel->halt_status = DWC_OTG_HC_XFER_URB_DEQUEUE;
+				qh->channel->halt_pending = 1;
+				if (hcd->fiq_state->channel[n].fsm == FIQ_HS_ISOC_TURBO ||
+				    hcd->fiq_state->channel[n].fsm == FIQ_HS_ISOC_SLEEPING)
+					hcd->fiq_state->channel[n].fsm = FIQ_HS_ISOC_ABORTED;
+				fiq_fsm_spin_unlock(&hcd->fiq_state->lock);
+				local_fiq_enable();
+
+				if (dwc_qh_is_non_per(qh)) {
+					do {
+						state = READ_ONCE(hcd->fiq_state->channel[n].fsm);
+						running = (state != FIQ_NP_SPLIT_DONE) &&
+							  (state != FIQ_NP_SPLIT_LS_ABORTED) &&
+							  (state != FIQ_NP_SPLIT_HS_ABORTED);
+						if (!running)
+							break;
+						udelay(125);
+					} while(--retries);
+					if (!retries)
+						DWC_WARN("Timed out waiting for FSM NP transfer to complete on %d",
+							 qh->channel->hc_num);
+				}
+			} else {
+				dwc_otg_hc_halt(hcd->core_if, qh->channel,
+						DWC_OTG_HC_XFER_URB_DEQUEUE);
+			}
+		}
+	}
+
+	/*
+	 * Free the QTD and clean up the associated QH. Leave the QH in the
+	 * schedule if it has any remaining QTDs.
+	 */
+
+	DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD URB Dequeue - "
+                    "delete %sQueue handler\n",
+                    hcd->core_if->dma_desc_enable?"DMA ":"");
+	if (!hcd->core_if->dma_desc_enable) {
+		uint8_t b = urb_qtd->in_process;
+		if (nak_holdoff && qh->do_split && dwc_qh_is_non_per(qh))
+			qh->nak_frame = 0xFFFF;
+		dwc_otg_hcd_qtd_remove_and_free(hcd, urb_qtd, qh);
+		if (b) {
+			dwc_otg_hcd_qh_deactivate(hcd, qh, 0);
+			qh->channel = NULL;
+		} else if (DWC_CIRCLEQ_EMPTY(&qh->qtd_list)) {
+			dwc_otg_hcd_qh_remove(hcd, qh);
+		}
+	} else {
+		dwc_otg_hcd_qtd_remove_and_free(hcd, urb_qtd, qh);
+	}
+	return 0;
+}
+
+int dwc_otg_hcd_endpoint_disable(dwc_otg_hcd_t * hcd, void *ep_handle,
+				 int retry)
+{
+	dwc_otg_qh_t *qh = (dwc_otg_qh_t *) ep_handle;
+	int retval = 0;
+	dwc_irqflags_t flags;
+
+	if (retry < 0) {
+		retval = -DWC_E_INVALID;
+		goto done;
+	}
+
+	if (!qh) {
+		retval = -DWC_E_INVALID;
+		goto done;
+	}
+
+	DWC_SPINLOCK_IRQSAVE(hcd->lock, &flags);
+
+	while (!DWC_CIRCLEQ_EMPTY(&qh->qtd_list) && retry) {
+		DWC_SPINUNLOCK_IRQRESTORE(hcd->lock, flags);
+		retry--;
+		dwc_msleep(5);
+		DWC_SPINLOCK_IRQSAVE(hcd->lock, &flags);
+	}
+
+	dwc_otg_hcd_qh_remove(hcd, qh);
+
+	DWC_SPINUNLOCK_IRQRESTORE(hcd->lock, flags);
+	/*
+	 * Split dwc_otg_hcd_qh_remove_and_free() into qh_remove
+	 * and qh_free to prevent stack dump on DWC_DMA_FREE() with
+	 * irq_disabled (spinlock_irqsave) in dwc_otg_hcd_desc_list_free()
+	 * and dwc_otg_hcd_frame_list_alloc().
+	 */
+	dwc_otg_hcd_qh_free(hcd, qh);
+
+done:
+	return retval;
+}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,30)
+int dwc_otg_hcd_endpoint_reset(dwc_otg_hcd_t * hcd, void *ep_handle)
+{
+	int retval = 0;
+	dwc_otg_qh_t *qh = (dwc_otg_qh_t *) ep_handle;
+	if (!qh)
+		return -DWC_E_INVALID;
+
+	qh->data_toggle = DWC_OTG_HC_PID_DATA0;
+	return retval;
+}
+#endif
+
+/**
+ * HCD Callback structure for handling mode switching.
+ */
+static dwc_otg_cil_callbacks_t hcd_cil_callbacks = {
+	.start = dwc_otg_hcd_start_cb,
+	.stop = dwc_otg_hcd_stop_cb,
+	.disconnect = dwc_otg_hcd_disconnect_cb,
+	.session_start = dwc_otg_hcd_session_start_cb,
+	.resume_wakeup = dwc_otg_hcd_rem_wakeup_cb,
+#ifdef CONFIG_USB_DWC_OTG_LPM
+	.sleep = dwc_otg_hcd_sleep_cb,
+#endif
+	.p = 0,
+};
+
+/**
+ * Reset tasklet function
+ */
+static void reset_tasklet_func(void *data)
+{
+	dwc_otg_hcd_t *dwc_otg_hcd = (dwc_otg_hcd_t *) data;
+	dwc_otg_core_if_t *core_if = dwc_otg_hcd->core_if;
+	hprt0_data_t hprt0;
+
+	DWC_DEBUGPL(DBG_HCDV, "USB RESET tasklet called\n");
+
+	hprt0.d32 = dwc_otg_read_hprt0(core_if);
+	hprt0.b.prtrst = 1;
+	DWC_WRITE_REG32(core_if->host_if->hprt0, hprt0.d32);
+	dwc_mdelay(60);
+
+	hprt0.b.prtrst = 0;
+	DWC_WRITE_REG32(core_if->host_if->hprt0, hprt0.d32);
+	dwc_otg_hcd->flags.b.port_reset_change = 1;
+}
+
+static void completion_tasklet_func(void *ptr)
+{
+	dwc_otg_hcd_t *hcd = (dwc_otg_hcd_t *) ptr;
+	struct urb *urb;
+	urb_tq_entry_t *item;
+	dwc_irqflags_t flags;
+
+	/* This could just be spin_lock_irq */
+	DWC_SPINLOCK_IRQSAVE(hcd->lock, &flags);
+	while (!DWC_TAILQ_EMPTY(&hcd->completed_urb_list)) {
+		item = DWC_TAILQ_FIRST(&hcd->completed_urb_list);
+		urb = item->urb;
+		DWC_TAILQ_REMOVE(&hcd->completed_urb_list, item,
+				urb_tq_entries);
+		DWC_SPINUNLOCK_IRQRESTORE(hcd->lock, flags);
+		DWC_FREE(item);
+
+		usb_hcd_giveback_urb(hcd->priv, urb, urb->status);
+
+
+		DWC_SPINLOCK_IRQSAVE(hcd->lock, &flags);
+	}
+	DWC_SPINUNLOCK_IRQRESTORE(hcd->lock, flags);
+	return;
+}
+
+static void qh_list_free(dwc_otg_hcd_t * hcd, dwc_list_link_t * qh_list)
+{
+	dwc_list_link_t *item;
+	dwc_otg_qh_t *qh;
+	dwc_irqflags_t flags;
+
+	if (!qh_list->next) {
+		/* The list hasn't been initialized yet. */
+		return;
+	}
+	/*
+	 * Hold spinlock here. Not needed in that case if bellow
+	 * function is being called from ISR
+	 */
+	DWC_SPINLOCK_IRQSAVE(hcd->lock, &flags);
+	/* Ensure there are no QTDs or URBs left. */
+	kill_urbs_in_qh_list(hcd, qh_list);
+	DWC_SPINUNLOCK_IRQRESTORE(hcd->lock, flags);
+
+	DWC_LIST_FOREACH(item, qh_list) {
+		qh = DWC_LIST_ENTRY(item, dwc_otg_qh_t, qh_list_entry);
+		dwc_otg_hcd_qh_remove_and_free(hcd, qh);
+	}
+}
+
+/**
+ * Exit from Hibernation if Host did not detect SRP from connected SRP capable
+ * Device during SRP time by host power up.
+ */
+void dwc_otg_hcd_power_up(void *ptr)
+{
+	gpwrdn_data_t gpwrdn = {.d32 = 0 };
+	dwc_otg_core_if_t *core_if = (dwc_otg_core_if_t *) ptr;
+
+	DWC_PRINTF("%s called\n", __FUNCTION__);
+
+	if (!core_if->hibernation_suspend) {
+		DWC_PRINTF("Already exited from Hibernation\n");
+		return;
+	}
+
+	/* Switch on the voltage to the core */
+	gpwrdn.b.pwrdnswtch = 1;
+	DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
+	dwc_udelay(10);
+
+	/* Reset the core */
+	gpwrdn.d32 = 0;
+	gpwrdn.b.pwrdnrstn = 1;
+	DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
+	dwc_udelay(10);
+
+	/* Disable power clamps */
+	gpwrdn.d32 = 0;
+	gpwrdn.b.pwrdnclmp = 1;
+	DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
+
+	/* Remove reset the core signal */
+	gpwrdn.d32 = 0;
+	gpwrdn.b.pwrdnrstn = 1;
+	DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, 0, gpwrdn.d32);
+	dwc_udelay(10);
+
+	/* Disable PMU interrupt */
+	gpwrdn.d32 = 0;
+	gpwrdn.b.pmuintsel = 1;
+	DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
+
+	core_if->hibernation_suspend = 0;
+
+	/* Disable PMU */
+	gpwrdn.d32 = 0;
+	gpwrdn.b.pmuactv = 1;
+	DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
+	dwc_udelay(10);
+
+	/* Enable VBUS */
+	gpwrdn.d32 = 0;
+	gpwrdn.b.dis_vbus = 1;
+	DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
+
+	core_if->op_state = A_HOST;
+	dwc_otg_core_init(core_if);
+	dwc_otg_enable_global_interrupts(core_if);
+	cil_hcd_start(core_if);
+}
+
+void dwc_otg_cleanup_fiq_channel(dwc_otg_hcd_t *hcd, uint32_t num)
+{
+	struct fiq_channel_state *st = &hcd->fiq_state->channel[num];
+	struct fiq_dma_blob *blob = hcd->fiq_dmab;
+	int i;
+
+	st->fsm = FIQ_PASSTHROUGH;
+	st->hcchar_copy.d32 = 0;
+	st->hcsplt_copy.d32 = 0;
+	st->hcint_copy.d32 = 0;
+	st->hcintmsk_copy.d32 = 0;
+	st->hctsiz_copy.d32 = 0;
+	st->hcdma_copy.d32 = 0;
+	st->nr_errors = 0;
+	st->hub_addr = 0;
+	st->port_addr = 0;
+	st->expected_uframe = 0;
+	st->nrpackets = 0;
+	st->dma_info.index = 0;
+	for (i = 0; i < 6; i++)
+		st->dma_info.slot_len[i] = 255;
+	st->hs_isoc_info.index = 0;
+	st->hs_isoc_info.iso_desc = NULL;
+	st->hs_isoc_info.nrframes = 0;
+
+	DWC_MEMSET(&blob->channel[num].index[0], 0x6b, 1128);
+}
+
+/**
+ * Frees secondary storage associated with the dwc_otg_hcd structure contained
+ * in the struct usb_hcd field.
+ */
+static void dwc_otg_hcd_free(dwc_otg_hcd_t * dwc_otg_hcd)
+{
+	struct device *dev = dwc_otg_hcd_to_dev(dwc_otg_hcd);
+	int i;
+
+	DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD FREE\n");
+
+	del_timers(dwc_otg_hcd);
+
+	/* Free memory for QH/QTD lists */
+	qh_list_free(dwc_otg_hcd, &dwc_otg_hcd->non_periodic_sched_inactive);
+	qh_list_free(dwc_otg_hcd, &dwc_otg_hcd->non_periodic_sched_active);
+	qh_list_free(dwc_otg_hcd, &dwc_otg_hcd->periodic_sched_inactive);
+	qh_list_free(dwc_otg_hcd, &dwc_otg_hcd->periodic_sched_ready);
+	qh_list_free(dwc_otg_hcd, &dwc_otg_hcd->periodic_sched_assigned);
+	qh_list_free(dwc_otg_hcd, &dwc_otg_hcd->periodic_sched_queued);
+
+	/* Free memory for the host channels. */
+	for (i = 0; i < MAX_EPS_CHANNELS; i++) {
+		dwc_hc_t *hc = dwc_otg_hcd->hc_ptr_array[i];
+
+#ifdef DEBUG
+		if (dwc_otg_hcd->core_if->hc_xfer_timer[i]) {
+			DWC_TIMER_FREE(dwc_otg_hcd->core_if->hc_xfer_timer[i]);
+		}
+#endif
+		if (hc != NULL) {
+			DWC_DEBUGPL(DBG_HCDV, "HCD Free channel #%i, hc=%p\n",
+				    i, hc);
+			DWC_FREE(hc);
+		}
+	}
+
+	if (dwc_otg_hcd->core_if->dma_enable) {
+		if (dwc_otg_hcd->status_buf_dma) {
+			DWC_DMA_FREE(dev, DWC_OTG_HCD_STATUS_BUF_SIZE,
+				     dwc_otg_hcd->status_buf,
+				     dwc_otg_hcd->status_buf_dma);
+		}
+	} else if (dwc_otg_hcd->status_buf != NULL) {
+		DWC_FREE(dwc_otg_hcd->status_buf);
+	}
+	DWC_SPINLOCK_FREE(dwc_otg_hcd->lock);
+	/* Set core_if's lock pointer to NULL */
+	dwc_otg_hcd->core_if->lock = NULL;
+
+	DWC_TIMER_FREE(dwc_otg_hcd->conn_timer);
+	DWC_TASK_FREE(dwc_otg_hcd->reset_tasklet);
+	DWC_TASK_FREE(dwc_otg_hcd->completion_tasklet);
+	DWC_DMA_FREE(dev, 16, dwc_otg_hcd->fiq_state->dummy_send,
+		     dwc_otg_hcd->fiq_state->dummy_send_dma);
+	DWC_FREE(dwc_otg_hcd->fiq_state);
+
+#ifdef DWC_DEV_SRPCAP
+	if (dwc_otg_hcd->core_if->power_down == 2 &&
+	    dwc_otg_hcd->core_if->pwron_timer) {
+		DWC_TIMER_FREE(dwc_otg_hcd->core_if->pwron_timer);
+	}
+#endif
+	DWC_FREE(dwc_otg_hcd);
+}
+
+int dwc_otg_hcd_init(dwc_otg_hcd_t * hcd, dwc_otg_core_if_t * core_if)
+{
+	struct device *dev = dwc_otg_hcd_to_dev(hcd);
+	int retval = 0;
+	int num_channels;
+	int i;
+	dwc_hc_t *channel;
+
+#if (defined(DWC_LINUX) && defined(CONFIG_DEBUG_SPINLOCK))
+	DWC_SPINLOCK_ALLOC_LINUX_DEBUG(hcd->lock);
+#else
+	hcd->lock = DWC_SPINLOCK_ALLOC();
+#endif
+        DWC_DEBUGPL(DBG_HCDV, "init of HCD %p given core_if %p\n",
+                    hcd, core_if);
+	if (!hcd->lock) {
+		DWC_ERROR("Could not allocate lock for pcd");
+		DWC_FREE(hcd);
+		retval = -DWC_E_NO_MEMORY;
+		goto out;
+	}
+	hcd->core_if = core_if;
+
+	/* Register the HCD CIL Callbacks */
+	dwc_otg_cil_register_hcd_callbacks(hcd->core_if,
+					   &hcd_cil_callbacks, hcd);
+
+	/* Initialize the non-periodic schedule. */
+	DWC_LIST_INIT(&hcd->non_periodic_sched_inactive);
+	DWC_LIST_INIT(&hcd->non_periodic_sched_active);
+
+	/* Initialize the periodic schedule. */
+	DWC_LIST_INIT(&hcd->periodic_sched_inactive);
+	DWC_LIST_INIT(&hcd->periodic_sched_ready);
+	DWC_LIST_INIT(&hcd->periodic_sched_assigned);
+	DWC_LIST_INIT(&hcd->periodic_sched_queued);
+	DWC_TAILQ_INIT(&hcd->completed_urb_list);
+	/*
+	 * Create a host channel descriptor for each host channel implemented
+	 * in the controller. Initialize the channel descriptor array.
+	 */
+	DWC_CIRCLEQ_INIT(&hcd->free_hc_list);
+	num_channels = hcd->core_if->core_params->host_channels;
+	DWC_MEMSET(hcd->hc_ptr_array, 0, sizeof(hcd->hc_ptr_array));
+	for (i = 0; i < num_channels; i++) {
+		channel = DWC_ALLOC(sizeof(dwc_hc_t));
+		if (channel == NULL) {
+			retval = -DWC_E_NO_MEMORY;
+			DWC_ERROR("%s: host channel allocation failed\n",
+				  __func__);
+			dwc_otg_hcd_free(hcd);
+			goto out;
+		}
+		channel->hc_num = i;
+		hcd->hc_ptr_array[i] = channel;
+#ifdef DEBUG
+		hcd->core_if->hc_xfer_timer[i] =
+		    DWC_TIMER_ALLOC("hc timer", hc_xfer_timeout,
+				    &hcd->core_if->hc_xfer_info[i]);
+#endif
+		DWC_DEBUGPL(DBG_HCDV, "HCD Added channel #%d, hc=%p\n", i,
+			    channel);
+	}
+
+	if (fiq_enable) {
+		hcd->fiq_state = DWC_ALLOC(sizeof(struct fiq_state) + (sizeof(struct fiq_channel_state) * num_channels));
+		if (!hcd->fiq_state) {
+			retval = -DWC_E_NO_MEMORY;
+			DWC_ERROR("%s: cannot allocate fiq_state structure\n", __func__);
+			dwc_otg_hcd_free(hcd);
+			goto out;
+		}
+		DWC_MEMSET(hcd->fiq_state, 0, (sizeof(struct fiq_state) + (sizeof(struct fiq_channel_state) * num_channels)));
+
+#ifdef CONFIG_ARM64
+		spin_lock_init(&hcd->fiq_state->lock);
+#endif
+
+		for (i = 0; i < num_channels; i++) {
+			hcd->fiq_state->channel[i].fsm = FIQ_PASSTHROUGH;
+		}
+		hcd->fiq_state->dummy_send = DWC_DMA_ALLOC_ATOMIC(dev, 16,
+							 &hcd->fiq_state->dummy_send_dma);
+
+		hcd->fiq_stack = DWC_ALLOC(sizeof(struct fiq_stack));
+		if (!hcd->fiq_stack) {
+			retval = -DWC_E_NO_MEMORY;
+			DWC_ERROR("%s: cannot allocate fiq_stack structure\n", __func__);
+			dwc_otg_hcd_free(hcd);
+			goto out;
+		}
+		hcd->fiq_stack->magic1 = 0xDEADBEEF;
+		hcd->fiq_stack->magic2 = 0xD00DFEED;
+		hcd->fiq_state->gintmsk_saved.d32 = ~0;
+		hcd->fiq_state->haintmsk_saved.b2.chint = ~0;
+
+		/* This bit is terrible and uses no API, but necessary. The FIQ has no concept of DMA pools
+		 * (and if it did, would be a lot slower). This allocates a chunk of memory (~9kiB for 8 host channels)
+		 * for use as transaction bounce buffers in a 2-D array. Our access into this chunk is done by some
+		 * moderately readable array casts.
+		 */
+		hcd->fiq_dmab = DWC_DMA_ALLOC(dev, (sizeof(struct fiq_dma_channel) * num_channels), &hcd->fiq_state->dma_base);
+		DWC_WARN("FIQ DMA bounce buffers: virt = %px dma = %pad len=%zu",
+				hcd->fiq_dmab, &hcd->fiq_state->dma_base,
+				sizeof(struct fiq_dma_channel) * num_channels);
+
+		DWC_MEMSET(hcd->fiq_dmab, 0x6b, 9024);
+
+		/* pointer for debug in fiq_print */
+		hcd->fiq_state->fiq_dmab = hcd->fiq_dmab;
+		if (fiq_fsm_enable) {
+			int i;
+			for (i=0; i < hcd->core_if->core_params->host_channels; i++) {
+				dwc_otg_cleanup_fiq_channel(hcd, i);
+			}
+			DWC_PRINTF("FIQ FSM acceleration enabled for :\n%s%s%s%s",
+				(fiq_fsm_mask & 0x1) ? "Non-periodic Split Transactions\n" : "",
+				(fiq_fsm_mask & 0x2) ? "Periodic Split Transactions\n" : "",
+				(fiq_fsm_mask & 0x4) ? "High-Speed Isochronous Endpoints\n" : "",
+				(fiq_fsm_mask & 0x8) ? "Interrupt/Control Split Transaction hack enabled\n" : "");
+		}
+	}
+
+	/* Initialize the Connection timeout timer. */
+	hcd->conn_timer = DWC_TIMER_ALLOC("Connection timer",
+					  dwc_otg_hcd_connect_timeout, 0);
+
+	printk(KERN_DEBUG "dwc_otg: Microframe scheduler %s\n", microframe_schedule ? "enabled":"disabled");
+	if (microframe_schedule)
+		init_hcd_usecs(hcd);
+
+	/* Initialize reset tasklet. */
+	hcd->reset_tasklet = DWC_TASK_ALLOC("reset_tasklet", reset_tasklet_func, hcd);
+
+	hcd->completion_tasklet = DWC_TASK_ALLOC("completion_tasklet",
+						completion_tasklet_func, hcd);
+#ifdef DWC_DEV_SRPCAP
+	if (hcd->core_if->power_down == 2) {
+		/* Initialize Power on timer for Host power up in case hibernation */
+		hcd->core_if->pwron_timer = DWC_TIMER_ALLOC("PWRON TIMER",
+									dwc_otg_hcd_power_up, core_if);
+	}
+#endif
+
+	/*
+	 * Allocate space for storing data on status transactions. Normally no
+	 * data is sent, but this space acts as a bit bucket. This must be
+	 * done after usb_add_hcd since that function allocates the DMA buffer
+	 * pool.
+	 */
+	if (hcd->core_if->dma_enable) {
+		hcd->status_buf =
+		    DWC_DMA_ALLOC(dev, DWC_OTG_HCD_STATUS_BUF_SIZE,
+				  &hcd->status_buf_dma);
+	} else {
+		hcd->status_buf = DWC_ALLOC(DWC_OTG_HCD_STATUS_BUF_SIZE);
+	}
+	if (!hcd->status_buf) {
+		retval = -DWC_E_NO_MEMORY;
+		DWC_ERROR("%s: status_buf allocation failed\n", __func__);
+		dwc_otg_hcd_free(hcd);
+		goto out;
+	}
+
+	hcd->otg_port = 1;
+	hcd->frame_list = NULL;
+	hcd->frame_list_dma = 0;
+	hcd->periodic_qh_count = 0;
+
+	DWC_MEMSET(hcd->hub_port, 0, sizeof(hcd->hub_port));
+#ifdef FIQ_DEBUG
+	DWC_MEMSET(hcd->hub_port_alloc, -1, sizeof(hcd->hub_port_alloc));
+#endif
+
+out:
+	return retval;
+}
+
+void dwc_otg_hcd_remove(dwc_otg_hcd_t * hcd)
+{
+	/* Turn off all host-specific interrupts. */
+	dwc_otg_disable_host_interrupts(hcd->core_if);
+
+	dwc_otg_hcd_free(hcd);
+}
+
+/**
+ * Initializes dynamic portions of the DWC_otg HCD state.
+ */
+static void dwc_otg_hcd_reinit(dwc_otg_hcd_t * hcd)
+{
+	int num_channels;
+	int i;
+	dwc_hc_t *channel;
+	dwc_hc_t *channel_tmp;
+
+	hcd->flags.d32 = 0;
+
+	hcd->non_periodic_qh_ptr = &hcd->non_periodic_sched_active;
+	if (!microframe_schedule) {
+		hcd->non_periodic_channels = 0;
+		hcd->periodic_channels = 0;
+	} else {
+		hcd->available_host_channels = hcd->core_if->core_params->host_channels;
+	}
+	/*
+	 * Put all channels in the free channel list and clean up channel
+	 * states.
+	 */
+	DWC_CIRCLEQ_FOREACH_SAFE(channel, channel_tmp,
+				 &hcd->free_hc_list, hc_list_entry) {
+		DWC_CIRCLEQ_REMOVE(&hcd->free_hc_list, channel, hc_list_entry);
+	}
+
+	num_channels = hcd->core_if->core_params->host_channels;
+	for (i = 0; i < num_channels; i++) {
+		channel = hcd->hc_ptr_array[i];
+		DWC_CIRCLEQ_INSERT_TAIL(&hcd->free_hc_list, channel,
+					hc_list_entry);
+		dwc_otg_hc_cleanup(hcd->core_if, channel);
+	}
+
+	/* Initialize the DWC core for host mode operation. */
+	dwc_otg_core_host_init(hcd->core_if);
+
+	/* Set core_if's lock pointer to the hcd->lock */
+	hcd->core_if->lock = hcd->lock;
+}
+
+/**
+ * Assigns transactions from a QTD to a free host channel and initializes the
+ * host channel to perform the transactions. The host channel is removed from
+ * the free list.
+ *
+ * @param hcd The HCD state structure.
+ * @param qh Transactions from the first QTD for this QH are selected and
+ * assigned to a free host channel.
+ */
+static void assign_and_init_hc(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh)
+{
+	dwc_hc_t *hc;
+	dwc_otg_qtd_t *qtd;
+	dwc_otg_hcd_urb_t *urb;
+	void* ptr = NULL;
+	uint16_t wLength;
+	uint32_t intr_enable;
+	unsigned long flags;
+	gintmsk_data_t gintmsk = { .d32 = 0, };
+	struct device *dev = dwc_otg_hcd_to_dev(hcd);
+
+	qtd = DWC_CIRCLEQ_FIRST(&qh->qtd_list);
+
+	urb = qtd->urb;
+
+	DWC_DEBUGPL(DBG_HCDV, "%s(%p,%p) - urb %x, actual_length %d\n", __func__, hcd, qh, (unsigned int)urb, urb->actual_length);
+
+	if (((urb->actual_length < 0) || (urb->actual_length > urb->length)) && !dwc_otg_hcd_is_pipe_in(&urb->pipe_info))
+		urb->actual_length = urb->length;
+
+
+	hc = DWC_CIRCLEQ_FIRST(&hcd->free_hc_list);
+
+	/* Remove the host channel from the free list. */
+	DWC_CIRCLEQ_REMOVE_INIT(&hcd->free_hc_list, hc, hc_list_entry);
+
+	qh->channel = hc;
+
+	qtd->in_process = 1;
+
+	/*
+	 * Use usb_pipedevice to determine device address. This address is
+	 * 0 before the SET_ADDRESS command and the correct address afterward.
+	 */
+	hc->dev_addr = dwc_otg_hcd_get_dev_addr(&urb->pipe_info);
+	hc->ep_num = dwc_otg_hcd_get_ep_num(&urb->pipe_info);
+	hc->speed = qh->dev_speed;
+	hc->max_packet = dwc_max_packet(qh->maxp);
+
+	hc->xfer_started = 0;
+	hc->halt_status = DWC_OTG_HC_XFER_NO_HALT_STATUS;
+	hc->error_state = (qtd->error_count > 0);
+	hc->halt_on_queue = 0;
+	hc->halt_pending = 0;
+	hc->requests = 0;
+
+	/*
+	 * The following values may be modified in the transfer type section
+	 * below. The xfer_len value may be reduced when the transfer is
+	 * started to accommodate the max widths of the XferSize and PktCnt
+	 * fields in the HCTSIZn register.
+	 */
+
+	hc->ep_is_in = (dwc_otg_hcd_is_pipe_in(&urb->pipe_info) != 0);
+	if (hc->ep_is_in) {
+		hc->do_ping = 0;
+	} else {
+		hc->do_ping = qh->ping_state;
+	}
+
+	hc->data_pid_start = qh->data_toggle;
+	hc->multi_count = 1;
+
+	if (hcd->core_if->dma_enable) {
+		hc->xfer_buff =
+		    (uint8_t *)(uintptr_t)urb->dma + urb->actual_length;
+
+		/* For non-dword aligned case */
+		if (((unsigned long)hc->xfer_buff & 0x3)
+		    && !hcd->core_if->dma_desc_enable) {
+			ptr = (uint8_t *) urb->buf + urb->actual_length;
+		}
+	} else {
+		hc->xfer_buff = (uint8_t *) urb->buf + urb->actual_length;
+	}
+	hc->xfer_len = urb->length - urb->actual_length;
+	hc->xfer_count = 0;
+
+	/*
+	 * Set the split attributes
+	 */
+	hc->do_split = 0;
+	if (qh->do_split) {
+		uint32_t hub_addr, port_addr;
+		hc->do_split = 1;
+		hc->start_pkt_count = 1;
+		hc->xact_pos = qtd->isoc_split_pos;
+		/* We don't need to do complete splits anymore */
+//		if(fiq_fsm_enable)
+		if (0)
+			hc->complete_split = qtd->complete_split = 0;
+		else
+			hc->complete_split = qtd->complete_split;
+
+		hcd->fops->hub_info(hcd, urb->priv, &hub_addr, &port_addr);
+		hc->hub_addr = (uint8_t) hub_addr;
+		hc->port_addr = (uint8_t) port_addr;
+	}
+
+	switch (dwc_otg_hcd_get_pipe_type(&urb->pipe_info)) {
+	case UE_CONTROL:
+		hc->ep_type = DWC_OTG_EP_TYPE_CONTROL;
+		switch (qtd->control_phase) {
+		case DWC_OTG_CONTROL_SETUP:
+			DWC_DEBUGPL(DBG_HCDV, "  Control setup transaction\n");
+			hc->do_ping = 0;
+			hc->ep_is_in = 0;
+			hc->data_pid_start = DWC_OTG_HC_PID_SETUP;
+			if (hcd->core_if->dma_enable) {
+				hc->xfer_buff =
+					(uint8_t *)(uintptr_t)urb->setup_dma;
+			} else {
+				hc->xfer_buff = (uint8_t *) urb->setup_packet;
+			}
+			hc->xfer_len = 8;
+			ptr = NULL;
+			break;
+		case DWC_OTG_CONTROL_DATA:
+			DWC_DEBUGPL(DBG_HCDV, "  Control data transaction\n");
+			/*
+			 * Hardware bug: small IN packets with length < 4
+			 * cause a 4-byte write to memory. We can only catch
+			 * the case where we know a short packet is going to be
+			 * returned in a control transfer, as the length is
+			 * specified in the setup packet. This is only an issue
+			 * for drivers that insist on packing a device's various
+			 * properties into a struct and querying them one at a
+			 * time (uvcvideo).
+			 * Force the use of align_buf so that the subsequent
+			 * memcpy puts the right number of bytes in the URB's
+			 * buffer.
+			 */
+			wLength = ((uint16_t *)urb->setup_packet)[3];
+			if (hc->ep_is_in && wLength < 4)
+				ptr = hc->xfer_buff;
+
+			hc->data_pid_start = qtd->data_toggle;
+			break;
+		case DWC_OTG_CONTROL_STATUS:
+			/*
+			 * Direction is opposite of data direction or IN if no
+			 * data.
+			 */
+			DWC_DEBUGPL(DBG_HCDV, "  Control status transaction\n");
+			if (urb->length == 0) {
+				hc->ep_is_in = 1;
+			} else {
+				hc->ep_is_in =
+				    dwc_otg_hcd_is_pipe_out(&urb->pipe_info);
+			}
+			if (hc->ep_is_in) {
+				hc->do_ping = 0;
+			}
+
+			hc->data_pid_start = DWC_OTG_HC_PID_DATA1;
+
+			hc->xfer_len = 0;
+			if (hcd->core_if->dma_enable) {
+				hc->xfer_buff = (uint8_t *)
+					(uintptr_t)hcd->status_buf_dma;
+			} else {
+				hc->xfer_buff = (uint8_t *) hcd->status_buf;
+			}
+			ptr = NULL;
+			break;
+		}
+		break;
+	case UE_BULK:
+		hc->ep_type = DWC_OTG_EP_TYPE_BULK;
+		break;
+	case UE_INTERRUPT:
+		hc->ep_type = DWC_OTG_EP_TYPE_INTR;
+		break;
+	case UE_ISOCHRONOUS:
+		{
+			struct dwc_otg_hcd_iso_packet_desc *frame_desc;
+
+			hc->ep_type = DWC_OTG_EP_TYPE_ISOC;
+
+			if (hcd->core_if->dma_desc_enable)
+				break;
+
+			frame_desc = &urb->iso_descs[qtd->isoc_frame_index];
+
+			frame_desc->status = 0;
+
+			if (hcd->core_if->dma_enable) {
+				hc->xfer_buff = (uint8_t *)(uintptr_t)urb->dma;
+			} else {
+				hc->xfer_buff = (uint8_t *) urb->buf;
+			}
+			hc->xfer_buff +=
+			    frame_desc->offset + qtd->isoc_split_offset;
+			hc->xfer_len =
+			    frame_desc->length - qtd->isoc_split_offset;
+
+			/* For non-dword aligned buffers */
+			if (((unsigned long)hc->xfer_buff & 0x3)
+			    && hcd->core_if->dma_enable) {
+				ptr =
+				    (uint8_t *) urb->buf + frame_desc->offset +
+				    qtd->isoc_split_offset;
+			} else
+				ptr = NULL;
+
+			if (hc->xact_pos == DWC_HCSPLIT_XACTPOS_ALL) {
+				if (hc->xfer_len <= 188) {
+					hc->xact_pos = DWC_HCSPLIT_XACTPOS_ALL;
+				} else {
+					hc->xact_pos =
+					    DWC_HCSPLIT_XACTPOS_BEGIN;
+				}
+			}
+		}
+		break;
+	}
+	/* non DWORD-aligned buffer case */
+	if (ptr) {
+		uint32_t buf_size;
+		if (hc->ep_type != DWC_OTG_EP_TYPE_ISOC) {
+			buf_size = hcd->core_if->core_params->max_transfer_size;
+		} else {
+			buf_size = 4096;
+		}
+		if (!qh->dw_align_buf) {
+			qh->dw_align_buf = DWC_DMA_ALLOC_ATOMIC(dev, buf_size,
+							 &qh->dw_align_buf_dma);
+			if (!qh->dw_align_buf) {
+				DWC_ERROR
+				    ("%s: Failed to allocate memory to handle "
+				     "non-dword aligned buffer case\n",
+				     __func__);
+				return;
+			}
+		}
+		if (!hc->ep_is_in) {
+			dwc_memcpy(qh->dw_align_buf, ptr, hc->xfer_len);
+		}
+		hc->align_buff = qh->dw_align_buf_dma;
+	} else {
+		hc->align_buff = 0;
+	}
+
+	if (hc->ep_type == DWC_OTG_EP_TYPE_INTR ||
+	    hc->ep_type == DWC_OTG_EP_TYPE_ISOC) {
+		/*
+		 * This value may be modified when the transfer is started to
+		 * reflect the actual transfer length.
+		 */
+		hc->multi_count = dwc_hb_mult(qh->maxp);
+	}
+
+	if (hcd->core_if->dma_desc_enable)
+		hc->desc_list_addr = qh->desc_list_dma;
+
+	dwc_otg_hc_init(hcd->core_if, hc);
+
+	local_irq_save(flags);
+
+	if (fiq_enable) {
+		local_fiq_disable();
+		fiq_fsm_spin_lock(&hcd->fiq_state->lock);
+	}
+
+	/* Enable the top level host channel interrupt. */
+	intr_enable = (1 << hc->hc_num);
+	DWC_MODIFY_REG32(&hcd->core_if->host_if->host_global_regs->haintmsk, 0, intr_enable);
+
+	/* Make sure host channel interrupts are enabled. */
+	gintmsk.b.hcintr = 1;
+	DWC_MODIFY_REG32(&hcd->core_if->core_global_regs->gintmsk, 0, gintmsk.d32);
+
+	if (fiq_enable) {
+		fiq_fsm_spin_unlock(&hcd->fiq_state->lock);
+		local_fiq_enable();
+	}
+
+	local_irq_restore(flags);
+	hc->qh = qh;
+}
+
+
+/**
+ * fiq_fsm_transaction_suitable() - Test a QH for compatibility with the FIQ
+ * @hcd:	Pointer to the dwc_otg_hcd struct
+ * @qh:	pointer to the endpoint's queue head
+ *
+ * Transaction start/end control flow is grafted onto the existing dwc_otg
+ * mechanisms, to avoid spaghettifying the functions more than they already are.
+ * This function's eligibility check is altered by debug parameter.
+ *
+ * Returns: 0 for unsuitable, 1 implies the FIQ can be enabled for this transaction.
+ */
+
+int fiq_fsm_transaction_suitable(dwc_otg_hcd_t *hcd, dwc_otg_qh_t *qh)
+{
+	if (qh->do_split) {
+		switch (qh->ep_type) {
+		case UE_CONTROL:
+		case UE_BULK:
+			if (fiq_fsm_mask & (1 << 0))
+				return 1;
+			break;
+		case UE_INTERRUPT:
+		case UE_ISOCHRONOUS:
+			if (fiq_fsm_mask & (1 << 1))
+				return 1;
+			break;
+		default:
+			break;
+		}
+	} else if (qh->ep_type == UE_ISOCHRONOUS) {
+		if (fiq_fsm_mask & (1 << 2)) {
+			/* ISOCH support. We test for compatibility:
+			 * - DWORD aligned buffers
+			 * - Must be at least 2 transfers (otherwise pointless to use the FIQ)
+			 * If yes, then the fsm enqueue function will handle the state machine setup.
+			 */
+			dwc_otg_qtd_t *qtd = DWC_CIRCLEQ_FIRST(&qh->qtd_list);
+			dwc_otg_hcd_urb_t *urb = qtd->urb;
+			dwc_dma_t ptr;
+			int i;
+
+			if (urb->packet_count < 2)
+				return 0;
+			for (i = 0; i < urb->packet_count; i++) {
+				ptr = urb->dma + urb->iso_descs[i].offset;
+				if (ptr & 0x3)
+					return 0;
+			}
+			return 1;
+		}
+	}
+	return 0;
+}
+
+/**
+ * fiq_fsm_setup_periodic_dma() - Set up DMA bounce buffers
+ * @hcd: Pointer to the dwc_otg_hcd struct
+ * @qh: Pointer to the endpoint's queue head
+ *
+ * Periodic split transactions are transmitted modulo 188 bytes.
+ * This necessitates slicing data up into buckets for isochronous out
+ * and fixing up the DMA address for all IN transfers.
+ *
+ * Returns 1 if the DMA bounce buffers have been used, 0 if the default
+ * HC buffer has been used.
+ */
+int fiq_fsm_setup_periodic_dma(dwc_otg_hcd_t *hcd, struct fiq_channel_state *st, dwc_otg_qh_t *qh)
+ {
+	int frame_length, i = 0;
+	uint8_t *ptr = NULL;
+	dwc_hc_t *hc = qh->channel;
+	struct fiq_dma_blob *blob;
+	struct dwc_otg_hcd_iso_packet_desc *frame_desc;
+
+	for (i = 0; i < 6; i++) {
+		st->dma_info.slot_len[i] = 255;
+	}
+	st->dma_info.index = 0;
+	i = 0;
+	if (hc->ep_is_in) {
+		/*
+		 * Set dma_regs to bounce buffer. FIQ will update the
+		 * state depending on transaction progress.
+		 * Pointer arithmetic on hcd->fiq_state->dma_base (a dma_addr_t)
+		 * to point it to the correct offset in the allocated buffers.
+		 */
+		blob = (struct fiq_dma_blob *)
+			(uintptr_t)hcd->fiq_state->dma_base;
+		st->hcdma_copy.d32 =(u32)(uintptr_t)
+			blob->channel[hc->hc_num].index[0].buf;
+
+		/* Calculate the max number of CSPLITS such that the FIQ can time out
+		 * a transaction if it fails.
+		 */
+		frame_length = st->hcchar_copy.b.mps;
+		do {
+			i++;
+			frame_length -= 188;
+		} while (frame_length >= 0);
+		st->nrpackets = i;
+		return 1;
+	} else {
+		if (qh->ep_type == UE_ISOCHRONOUS) {
+
+			dwc_otg_qtd_t *qtd = DWC_CIRCLEQ_FIRST(&qh->qtd_list);
+
+			frame_desc = &qtd->urb->iso_descs[qtd->isoc_frame_index];
+			frame_length = frame_desc->length;
+
+			/* Virtual address for bounce buffers */
+			blob = hcd->fiq_dmab;
+
+			ptr = qtd->urb->buf + frame_desc->offset;
+			if (frame_length == 0) {
+				/*
+				 * for isochronous transactions, we must still transmit a packet
+				 * even if the length is zero.
+				 */
+				st->dma_info.slot_len[0] = 0;
+				st->nrpackets = 1;
+			} else {
+				do {
+					if (frame_length <= 188) {
+						dwc_memcpy(&blob->channel[hc->hc_num].index[i].buf[0], ptr, frame_length);
+						st->dma_info.slot_len[i] = frame_length;
+						ptr += frame_length;
+					} else {
+						dwc_memcpy(&blob->channel[hc->hc_num].index[i].buf[0], ptr, 188);
+						st->dma_info.slot_len[i] = 188;
+						ptr += 188;
+					}
+					i++;
+					frame_length -= 188;
+				} while (frame_length > 0);
+				st->nrpackets = i;
+			}
+			ptr = qtd->urb->buf + frame_desc->offset;
+			/*
+			 * Point the HC at the DMA address of the bounce buffers
+			 *
+			 * Pointer arithmetic on hcd->fiq_state->dma_base (a
+			 * dma_addr_t) to point it to the correct offset in the
+			 * allocated buffers.
+			 */
+			blob = (struct fiq_dma_blob *)
+				(uintptr_t)hcd->fiq_state->dma_base;
+			st->hcdma_copy.d32 = (u32)(uintptr_t)
+				blob->channel[hc->hc_num].index[0].buf;
+
+			/* fixup xfersize to the actual packet size */
+			st->hctsiz_copy.b.pid = 0;
+			st->hctsiz_copy.b.xfersize = st->dma_info.slot_len[0];
+			return 1;
+		} else {
+			/* For interrupt, single OUT packet required, goes in the SSPLIT from hc_buff. */
+			return 0;
+		}
+	}
+}
+
+/**
+ * fiq_fsm_np_tt_contended() - Avoid performing contended non-periodic transfers
+ * @hcd: Pointer to the dwc_otg_hcd struct
+ * @qh: Pointer to the endpoint's queue head
+ *
+ * Certain hub chips don't differentiate between IN and OUT non-periodic pipes
+ * with the same endpoint number. If transfers get completed out of order
+ * (disregarding the direction token) then the hub can lock up
+ * or return erroneous responses.
+ *
+ * Returns 1 if initiating the transfer would cause contention, 0 otherwise.
+ */
+int fiq_fsm_np_tt_contended(dwc_otg_hcd_t *hcd, dwc_otg_qh_t *qh)
+{
+	int i;
+	struct fiq_channel_state *st;
+	int dev_addr = qh->channel->dev_addr;
+	int ep_num = qh->channel->ep_num;
+	for (i = 0; i < hcd->core_if->core_params->host_channels; i++) {
+		if (i == qh->channel->hc_num)
+			continue;
+		st = &hcd->fiq_state->channel[i];
+		switch (st->fsm) {
+		case FIQ_NP_SSPLIT_STARTED:
+		case FIQ_NP_SSPLIT_RETRY:
+		case FIQ_NP_SSPLIT_PENDING:
+		case FIQ_NP_OUT_CSPLIT_RETRY:
+		case FIQ_NP_IN_CSPLIT_RETRY:
+			if (st->hcchar_copy.b.devaddr == dev_addr &&
+				st->hcchar_copy.b.epnum == ep_num)
+				return 1;
+			break;
+		default:
+			break;
+		}
+	}
+	return 0;
+}
+
+/*
+ * Pushing a periodic request into the queue near the EOF1 point
+ * in a microframe causes erroneous behaviour (frmovrun) interrupt.
+ * Usually, the request goes out on the bus causing a transfer but
+ * the core does not transfer the data to memory.
+ * This guard interval (in number of 60MHz clocks) is required which
+ * must cater for CPU latency between reading the value and enabling
+ * the channel.
+ */
+#define PERIODIC_FRREM_BACKOFF 1000
+
+int fiq_fsm_queue_isoc_transaction(dwc_otg_hcd_t *hcd, dwc_otg_qh_t *qh)
+{
+	dwc_hc_t *hc = qh->channel;
+	dwc_otg_hc_regs_t *hc_regs = hcd->core_if->host_if->hc_regs[hc->hc_num];
+	dwc_otg_qtd_t *qtd = DWC_CIRCLEQ_FIRST(&qh->qtd_list);
+	int frame;
+	struct fiq_channel_state *st = &hcd->fiq_state->channel[hc->hc_num];
+	int xfer_len, nrpackets;
+	hcdma_data_t hcdma;
+	hfnum_data_t hfnum;
+
+	if (st->fsm != FIQ_PASSTHROUGH)
+		return 0;
+
+	st->nr_errors = 0;
+
+	st->hcchar_copy.d32 = 0;
+	st->hcchar_copy.b.mps = hc->max_packet;
+	st->hcchar_copy.b.epdir = hc->ep_is_in;
+	st->hcchar_copy.b.devaddr = hc->dev_addr;
+	st->hcchar_copy.b.epnum = hc->ep_num;
+	st->hcchar_copy.b.eptype = hc->ep_type;
+
+	st->hcintmsk_copy.b.chhltd = 1;
+
+	frame = dwc_otg_hcd_get_frame_number(hcd);
+	st->hcchar_copy.b.oddfrm = (frame & 0x1) ? 0 : 1;
+
+	st->hcchar_copy.b.lspddev = 0;
+	/* Enable the channel later as a final register write. */
+
+	st->hcsplt_copy.d32 = 0;
+
+	st->hs_isoc_info.iso_desc = (struct dwc_otg_hcd_iso_packet_desc *) &qtd->urb->iso_descs;
+	st->hs_isoc_info.nrframes = qtd->urb->packet_count;
+	/* grab the next DMA address offset from the array */
+	st->hcdma_copy.d32 = qtd->urb->dma;
+	hcdma.d32 = st->hcdma_copy.d32 + st->hs_isoc_info.iso_desc[0].offset;
+
+	/* We need to set multi_count. This is a bit tricky - has to be set per-transaction as
+	 * the core needs to be told to send the correct number. Caution: for IN transfers,
+	 * this is always set to the maximum size of the endpoint. */
+	xfer_len = st->hs_isoc_info.iso_desc[0].length;
+	nrpackets = (xfer_len + st->hcchar_copy.b.mps - 1) / st->hcchar_copy.b.mps;
+	if (nrpackets == 0)
+		nrpackets = 1;
+	st->hcchar_copy.b.multicnt = nrpackets;
+	st->hctsiz_copy.b.pktcnt = nrpackets;
+
+	/* Initial PID also needs to be set */
+	if (st->hcchar_copy.b.epdir == 0) {
+		st->hctsiz_copy.b.xfersize = xfer_len;
+		switch (st->hcchar_copy.b.multicnt) {
+		case 1:
+			st->hctsiz_copy.b.pid = DWC_PID_DATA0;
+			break;
+		case 2:
+		case 3:
+			st->hctsiz_copy.b.pid = DWC_PID_MDATA;
+			break;
+		}
+
+	} else {
+		st->hctsiz_copy.b.xfersize = nrpackets * st->hcchar_copy.b.mps;
+		switch (st->hcchar_copy.b.multicnt) {
+		case 1:
+			st->hctsiz_copy.b.pid = DWC_PID_DATA0;
+			break;
+		case 2:
+			st->hctsiz_copy.b.pid = DWC_PID_DATA1;
+			break;
+		case 3:
+			st->hctsiz_copy.b.pid = DWC_PID_DATA2;
+			break;
+		}
+	}
+
+	st->hs_isoc_info.stride = qh->interval;
+	st->uframe_sleeps = 0;
+
+	fiq_print(FIQDBG_INT, hcd->fiq_state, "FSMQ  %01d ", hc->hc_num);
+	fiq_print(FIQDBG_INT, hcd->fiq_state, "%08x", st->hcchar_copy.d32);
+	fiq_print(FIQDBG_INT, hcd->fiq_state, "%08x", st->hctsiz_copy.d32);
+	fiq_print(FIQDBG_INT, hcd->fiq_state, "%08x", st->hcdma_copy.d32);
+	hfnum.d32 = DWC_READ_REG32(&hcd->core_if->host_if->host_global_regs->hfnum);
+	local_fiq_disable();
+	fiq_fsm_spin_lock(&hcd->fiq_state->lock);
+	DWC_WRITE_REG32(&hc_regs->hctsiz, st->hctsiz_copy.d32);
+	DWC_WRITE_REG32(&hc_regs->hcsplt, st->hcsplt_copy.d32);
+	DWC_WRITE_REG32(&hc_regs->hcdma, st->hcdma_copy.d32);
+	DWC_WRITE_REG32(&hc_regs->hcchar, st->hcchar_copy.d32);
+	DWC_WRITE_REG32(&hc_regs->hcintmsk, st->hcintmsk_copy.d32);
+	if (hfnum.b.frrem < PERIODIC_FRREM_BACKOFF) {
+		/* Prevent queueing near EOF1. Bad things happen if a periodic
+		 * split transaction is queued very close to EOF. SOF interrupt handler
+		 * will wake this channel at the next interrupt.
+		 */
+		st->fsm = FIQ_HS_ISOC_SLEEPING;
+		st->uframe_sleeps = 1;
+	} else {
+		st->fsm = FIQ_HS_ISOC_TURBO;
+		st->hcchar_copy.b.chen = 1;
+		DWC_WRITE_REG32(&hc_regs->hcchar, st->hcchar_copy.d32);
+	}
+	mb();
+	st->hcchar_copy.b.chen = 0;
+	fiq_fsm_spin_unlock(&hcd->fiq_state->lock);
+	local_fiq_enable();
+	return 0;
+}
+
+
+/**
+ * fiq_fsm_queue_split_transaction() - Set up a host channel and FIQ state
+ * @hcd: Pointer to the dwc_otg_hcd struct
+ * @qh: Pointer to the endpoint's queue head
+ *
+ * This overrides the dwc_otg driver's normal method of queueing a transaction.
+ * Called from dwc_otg_hcd_queue_transactions(), this performs specific setup
+ * for the nominated host channel.
+ *
+ * For periodic transfers, it also peeks at the FIQ state to see if an immediate
+ * start is possible. If not, then the FIQ is left to start the transfer.
+ */
+int fiq_fsm_queue_split_transaction(dwc_otg_hcd_t *hcd, dwc_otg_qh_t *qh)
+{
+	int start_immediate = 1, i;
+	hfnum_data_t hfnum;
+	dwc_hc_t *hc = qh->channel;
+	dwc_otg_hc_regs_t *hc_regs = hcd->core_if->host_if->hc_regs[hc->hc_num];
+	/* Program HC registers, setup FIQ_state, examine FIQ if periodic, start transfer (not if uframe 5) */
+	int hub_addr, port_addr, frame, uframe;
+	struct fiq_channel_state *st = &hcd->fiq_state->channel[hc->hc_num];
+
+	/*
+	 * Non-periodic channel assignments stay in the non_periodic_active queue.
+	 * Therefore we get repeatedly called until the FIQ's done processing this channel.
+	 */
+	if (qh->channel->xfer_started == 1)
+		return 0;
+
+	if (st->fsm != FIQ_PASSTHROUGH) {
+		pr_warn_ratelimited("%s:%d: Queue called for an active channel\n", __func__, __LINE__);
+		return 0;
+	}
+
+	qh->channel->xfer_started = 1;
+
+	st->nr_errors = 0;
+
+	st->hcchar_copy.d32 = 0;
+	st->hcchar_copy.b.mps = min_t(uint32_t, hc->xfer_len, hc->max_packet);
+	st->hcchar_copy.b.epdir = hc->ep_is_in;
+	st->hcchar_copy.b.devaddr = hc->dev_addr;
+	st->hcchar_copy.b.epnum = hc->ep_num;
+	st->hcchar_copy.b.eptype = hc->ep_type;
+	if (hc->ep_type & 0x1) {
+		if (hc->ep_is_in)
+			st->hcchar_copy.b.multicnt = 3;
+		else
+			/* Docs say set this to 1, but driver sets to 0! */
+			st->hcchar_copy.b.multicnt = 0;
+	} else {
+		st->hcchar_copy.b.multicnt = 1;
+		st->hcchar_copy.b.oddfrm = 0;
+	}
+	st->hcchar_copy.b.lspddev = (hc->speed == DWC_OTG_EP_SPEED_LOW) ? 1 : 0;
+	/* Enable the channel later as a final register write. */
+
+	st->hcsplt_copy.d32 = 0;
+	if(qh->do_split) {
+		hcd->fops->hub_info(hcd, DWC_CIRCLEQ_FIRST(&qh->qtd_list)->urb->priv, &hub_addr, &port_addr);
+		st->hcsplt_copy.b.compsplt = 0;
+		st->hcsplt_copy.b.spltena = 1;
+		// XACTPOS is for isoc-out only but needs initialising anyway.
+		st->hcsplt_copy.b.xactpos = ISOC_XACTPOS_ALL;
+		if((qh->ep_type == DWC_OTG_EP_TYPE_ISOC) && (!qh->ep_is_in)) {
+			/* For packetsize 0 < L < 188, ISOC_XACTPOS_ALL.
+			 * for longer than this, ISOC_XACTPOS_BEGIN and the FIQ
+			 * will update as necessary.
+			 */
+			if (hc->xfer_len > 188) {
+				st->hcsplt_copy.b.xactpos = ISOC_XACTPOS_BEGIN;
+			}
+		}
+		st->hcsplt_copy.b.hubaddr = (uint8_t) hub_addr;
+		st->hcsplt_copy.b.prtaddr = (uint8_t) port_addr;
+		st->hub_addr = hub_addr;
+		st->port_addr = port_addr;
+	}
+
+	st->hctsiz_copy.d32 = 0;
+	st->hctsiz_copy.b.dopng = 0;
+	st->hctsiz_copy.b.pid = hc->data_pid_start;
+
+	if (hc->ep_is_in || (hc->xfer_len > hc->max_packet)) {
+		hc->xfer_len = min_t(uint32_t, hc->xfer_len, hc->max_packet);
+	} else if (!hc->ep_is_in && (hc->xfer_len > 188)) {
+		hc->xfer_len = 188;
+	}
+	st->hctsiz_copy.b.xfersize = hc->xfer_len;
+
+	st->hctsiz_copy.b.pktcnt = 1;
+
+	if (hc->ep_type & 0x1) {
+		/*
+		 * For potentially multi-packet transfers, must use the DMA bounce buffers. For IN transfers,
+		 * the DMA address is the address of the first 188byte slot buffer in the bounce buffer array.
+		 * For multi-packet OUT transfers, we need to copy the data into the bounce buffer array so the FIQ can punt
+		 * the right address out as necessary. hc->xfer_buff and hc->xfer_len have already been set
+		 * in assign_and_init_hc(), but this is for the eventual transaction completion only. The FIQ
+		 * must not touch internal driver state.
+		 */
+		if(!fiq_fsm_setup_periodic_dma(hcd, st, qh)) {
+			if (hc->align_buff) {
+				st->hcdma_copy.d32 = hc->align_buff;
+			} else {
+				st->hcdma_copy.d32 = ((unsigned long) hc->xfer_buff & 0xFFFFFFFF);
+			}
+		}
+	} else {
+		if (hc->align_buff) {
+			st->hcdma_copy.d32 = hc->align_buff;
+		} else {
+			st->hcdma_copy.d32 = ((unsigned long) hc->xfer_buff & 0xFFFFFFFF);
+		}
+	}
+	/* The FIQ depends upon no other interrupts being enabled except channel halt.
+	 * Fixup channel interrupt mask. */
+	st->hcintmsk_copy.d32 = 0;
+	st->hcintmsk_copy.b.chhltd = 1;
+	st->hcintmsk_copy.b.ahberr = 1;
+
+	/* Hack courtesy of FreeBSD: apparently forcing Interrupt Split transactions
+	 * as Control puts the transfer into the non-periodic request queue and the
+	 * non-periodic handler in the hub. Makes things lots easier.
+	 */
+	if ((fiq_fsm_mask & 0x8) && hc->ep_type == UE_INTERRUPT) {
+		st->hcchar_copy.b.multicnt = 0;
+		st->hcchar_copy.b.oddfrm = 0;
+		st->hcchar_copy.b.eptype = UE_CONTROL;
+		if (hc->align_buff) {
+			st->hcdma_copy.d32 = hc->align_buff;
+		} else {
+			st->hcdma_copy.d32 = ((unsigned long) hc->xfer_buff & 0xFFFFFFFF);
+		}
+	}
+	DWC_WRITE_REG32(&hc_regs->hcdma, st->hcdma_copy.d32);
+	DWC_WRITE_REG32(&hc_regs->hctsiz, st->hctsiz_copy.d32);
+	DWC_WRITE_REG32(&hc_regs->hcsplt, st->hcsplt_copy.d32);
+	DWC_WRITE_REG32(&hc_regs->hcchar, st->hcchar_copy.d32);
+	DWC_WRITE_REG32(&hc_regs->hcintmsk, st->hcintmsk_copy.d32);
+
+	local_fiq_disable();
+	fiq_fsm_spin_lock(&hcd->fiq_state->lock);
+
+	if (hc->ep_type & 0x1) {
+		hfnum.d32 = DWC_READ_REG32(&hcd->core_if->host_if->host_global_regs->hfnum);
+		frame = (hfnum.b.frnum & ~0x7) >> 3;
+		uframe = hfnum.b.frnum & 0x7;
+		if (hfnum.b.frrem < PERIODIC_FRREM_BACKOFF) {
+			/* Prevent queueing near EOF1. Bad things happen if a periodic
+			 * split transaction is queued very close to EOF.
+			 */
+			start_immediate = 0;
+		} else if (uframe == 5) {
+			start_immediate = 0;
+		} else if (hc->ep_type == UE_ISOCHRONOUS && !hc->ep_is_in) {
+			start_immediate = 0;
+		} else if (hc->ep_is_in && fiq_fsm_too_late(hcd->fiq_state, hc->hc_num)) {
+			start_immediate = 0;
+		} else {
+			/* Search through all host channels to determine if a transaction
+			 * is currently in progress */
+			for (i = 0; i < hcd->core_if->core_params->host_channels; i++) {
+				if (i == hc->hc_num || hcd->fiq_state->channel[i].fsm == FIQ_PASSTHROUGH)
+					continue;
+				switch (hcd->fiq_state->channel[i].fsm) {
+				/* TT is reserved for channels that are in the middle of a periodic
+				 * split transaction.
+				 */
+				case FIQ_PER_SSPLIT_STARTED:
+				case FIQ_PER_CSPLIT_WAIT:
+				case FIQ_PER_CSPLIT_NYET1:
+				case FIQ_PER_CSPLIT_POLL:
+				case FIQ_PER_ISO_OUT_ACTIVE:
+				case FIQ_PER_ISO_OUT_LAST:
+					if (hcd->fiq_state->channel[i].hub_addr == hub_addr &&
+							hcd->fiq_state->channel[i].port_addr == port_addr) {
+						start_immediate = 0;
+					}
+					break;
+				default:
+					break;
+				}
+				if (!start_immediate)
+					break;
+			}
+		}
+	}
+	if ((fiq_fsm_mask & 0x8) && hc->ep_type == UE_INTERRUPT)
+		start_immediate = 1;
+
+	fiq_print(FIQDBG_INT, hcd->fiq_state, "FSMQ %01d %01d", hc->hc_num, start_immediate);
+	fiq_print(FIQDBG_INT, hcd->fiq_state, "%08d", hfnum.b.frrem);
+	//fiq_print(FIQDBG_INT, hcd->fiq_state, "H:%02dP:%02d", hub_addr, port_addr);
+	//fiq_print(FIQDBG_INT, hcd->fiq_state, "%08x", st->hctsiz_copy.d32);
+	//fiq_print(FIQDBG_INT, hcd->fiq_state, "%08x", st->hcdma_copy.d32);
+	switch (hc->ep_type) {
+		case UE_CONTROL:
+		case UE_BULK:
+			if (fiq_fsm_np_tt_contended(hcd, qh)) {
+				st->fsm = FIQ_NP_SSPLIT_PENDING;
+				start_immediate = 0;
+			} else {
+				st->fsm = FIQ_NP_SSPLIT_STARTED;
+			}
+			break;
+		case UE_ISOCHRONOUS:
+			if (hc->ep_is_in) {
+				if (start_immediate) {
+					st->fsm = FIQ_PER_SSPLIT_STARTED;
+				} else {
+					st->fsm = FIQ_PER_SSPLIT_QUEUED;
+				}
+			} else {
+				if (start_immediate) {
+					/* Single-isoc OUT packets don't require FIQ involvement */
+					if (st->nrpackets == 1) {
+						st->fsm = FIQ_PER_ISO_OUT_LAST;
+					} else {
+						st->fsm = FIQ_PER_ISO_OUT_ACTIVE;
+					}
+				} else {
+					st->fsm = FIQ_PER_ISO_OUT_PENDING;
+				}
+			}
+			break;
+		case UE_INTERRUPT:
+			if (fiq_fsm_mask & 0x8) {
+				if (fiq_fsm_np_tt_contended(hcd, qh)) {
+					st->fsm = FIQ_NP_SSPLIT_PENDING;
+					start_immediate = 0;
+				} else {
+					st->fsm = FIQ_NP_SSPLIT_STARTED;
+				}
+			} else if (start_immediate) {
+					st->fsm = FIQ_PER_SSPLIT_STARTED;
+			} else {
+				st->fsm = FIQ_PER_SSPLIT_QUEUED;
+			}
+		default:
+			break;
+	}
+	if (start_immediate) {
+		/* Set the oddfrm bit as close as possible to actual queueing */
+		frame = dwc_otg_hcd_get_frame_number(hcd);
+		st->expected_uframe = (frame + 1) & 0x3FFF;
+		st->hcchar_copy.b.oddfrm = (frame & 0x1) ? 0 : 1;
+		st->hcchar_copy.b.chen = 1;
+		DWC_WRITE_REG32(&hc_regs->hcchar, st->hcchar_copy.d32);
+	}
+	mb();
+	fiq_fsm_spin_unlock(&hcd->fiq_state->lock);
+	local_fiq_enable();
+	return 0;
+}
+
+
+/**
+ * This function selects transactions from the HCD transfer schedule and
+ * assigns them to available host channels. It is called from HCD interrupt
+ * handler functions.
+ *
+ * @param hcd The HCD state structure.
+ *
+ * @return The types of new transactions that were assigned to host channels.
+ */
+dwc_otg_transaction_type_e dwc_otg_hcd_select_transactions(dwc_otg_hcd_t * hcd)
+{
+	dwc_list_link_t *qh_ptr;
+	dwc_otg_qh_t *qh;
+	int num_channels;
+	dwc_otg_transaction_type_e ret_val = DWC_OTG_TRANSACTION_NONE;
+
+#ifdef DEBUG_HOST_CHANNELS
+	last_sel_trans_num_per_scheduled = 0;
+	last_sel_trans_num_nonper_scheduled = 0;
+	last_sel_trans_num_avail_hc_at_start = hcd->available_host_channels;
+#endif /* DEBUG_HOST_CHANNELS */
+
+	/* Process entries in the periodic ready list. */
+	qh_ptr = DWC_LIST_FIRST(&hcd->periodic_sched_ready);
+
+	while (qh_ptr != &hcd->periodic_sched_ready &&
+	       !DWC_CIRCLEQ_EMPTY(&hcd->free_hc_list)) {
+
+		qh = DWC_LIST_ENTRY(qh_ptr, dwc_otg_qh_t, qh_list_entry);
+
+		if (microframe_schedule) {
+			// Make sure we leave one channel for non periodic transactions.
+			if (hcd->available_host_channels <= 1) {
+				break;
+			}
+			hcd->available_host_channels--;
+#ifdef DEBUG_HOST_CHANNELS
+			last_sel_trans_num_per_scheduled++;
+#endif /* DEBUG_HOST_CHANNELS */
+		}
+		qh = DWC_LIST_ENTRY(qh_ptr, dwc_otg_qh_t, qh_list_entry);
+		assign_and_init_hc(hcd, qh);
+
+		/*
+		 * Move the QH from the periodic ready schedule to the
+		 * periodic assigned schedule.
+		 */
+		qh_ptr = DWC_LIST_NEXT(qh_ptr);
+		DWC_LIST_MOVE_HEAD(&hcd->periodic_sched_assigned,
+				   &qh->qh_list_entry);
+	}
+
+	/*
+	 * Process entries in the inactive portion of the non-periodic
+	 * schedule. Some free host channels may not be used if they are
+	 * reserved for periodic transfers.
+	 */
+	qh_ptr = hcd->non_periodic_sched_inactive.next;
+	num_channels = hcd->core_if->core_params->host_channels;
+	while (qh_ptr != &hcd->non_periodic_sched_inactive &&
+	       (microframe_schedule || hcd->non_periodic_channels <
+		num_channels - hcd->periodic_channels) &&
+	       !DWC_CIRCLEQ_EMPTY(&hcd->free_hc_list)) {
+
+		qh = DWC_LIST_ENTRY(qh_ptr, dwc_otg_qh_t, qh_list_entry);
+		/*
+		 * Check to see if this is a NAK'd retransmit, in which case ignore for retransmission
+		 * we hold off on bulk retransmissions to reduce NAK interrupt overhead for full-speed
+		 * cheeky devices that just hold off using NAKs
+		 */
+		if (fiq_enable && nak_holdoff && qh->do_split) {
+			if (qh->nak_frame != 0xffff) {
+				uint16_t next_frame = dwc_frame_num_inc(qh->nak_frame, (qh->ep_type == UE_BULK) ? nak_holdoff : 8);
+				uint16_t frame = dwc_otg_hcd_get_frame_number(hcd);
+				if (dwc_frame_num_le(frame, next_frame)) {
+					if(dwc_frame_num_le(next_frame, hcd->fiq_state->next_sched_frame)) {
+						hcd->fiq_state->next_sched_frame = next_frame;
+					}
+					qh_ptr = DWC_LIST_NEXT(qh_ptr);
+					continue;
+				} else {
+					qh->nak_frame = 0xFFFF;
+				}
+			}
+		}
+
+		if (microframe_schedule) {
+				if (hcd->available_host_channels < 1) {
+					break;
+				}
+				hcd->available_host_channels--;
+#ifdef DEBUG_HOST_CHANNELS
+				last_sel_trans_num_nonper_scheduled++;
+#endif /* DEBUG_HOST_CHANNELS */
+		}
+
+		assign_and_init_hc(hcd, qh);
+
+		/*
+		 * Move the QH from the non-periodic inactive schedule to the
+		 * non-periodic active schedule.
+		 */
+		qh_ptr = DWC_LIST_NEXT(qh_ptr);
+		DWC_LIST_MOVE_HEAD(&hcd->non_periodic_sched_active,
+				   &qh->qh_list_entry);
+
+		if (!microframe_schedule)
+			hcd->non_periodic_channels++;
+	}
+	/* we moved a non-periodic QH to the active schedule. If the inactive queue is empty,
+	 * stop the FIQ from kicking us. We could potentially still have elements here if we
+	 * ran out of host channels.
+	 */
+	if (fiq_enable) {
+		if (DWC_LIST_EMPTY(&hcd->non_periodic_sched_inactive)) {
+			hcd->fiq_state->kick_np_queues = 0;
+		} else {
+			/* For each entry remaining in the NP inactive queue,
+			* if this a NAK'd retransmit then don't set the kick flag.
+			*/
+			if(nak_holdoff) {
+				DWC_LIST_FOREACH(qh_ptr, &hcd->non_periodic_sched_inactive) {
+					qh = DWC_LIST_ENTRY(qh_ptr, dwc_otg_qh_t, qh_list_entry);
+					if (qh->nak_frame == 0xFFFF) {
+						hcd->fiq_state->kick_np_queues = 1;
+					}
+				}
+			}
+		}
+	}
+	if(!DWC_LIST_EMPTY(&hcd->periodic_sched_assigned))
+		ret_val |= DWC_OTG_TRANSACTION_PERIODIC;
+
+	if(!DWC_LIST_EMPTY(&hcd->non_periodic_sched_active))
+		ret_val |= DWC_OTG_TRANSACTION_NON_PERIODIC;
+
+
+#ifdef DEBUG_HOST_CHANNELS
+	last_sel_trans_num_avail_hc_at_end = hcd->available_host_channels;
+#endif /* DEBUG_HOST_CHANNELS */
+	return ret_val;
+}
+
+/**
+ * Attempts to queue a single transaction request for a host channel
+ * associated with either a periodic or non-periodic transfer. This function
+ * assumes that there is space available in the appropriate request queue. For
+ * an OUT transfer or SETUP transaction in Slave mode, it checks whether space
+ * is available in the appropriate Tx FIFO.
+ *
+ * @param hcd The HCD state structure.
+ * @param hc Host channel descriptor associated with either a periodic or
+ * non-periodic transfer.
+ * @param fifo_dwords_avail Number of DWORDs available in the periodic Tx
+ * FIFO for periodic transfers or the non-periodic Tx FIFO for non-periodic
+ * transfers.
+ *
+ * @return 1 if a request is queued and more requests may be needed to
+ * complete the transfer, 0 if no more requests are required for this
+ * transfer, -1 if there is insufficient space in the Tx FIFO.
+ */
+static int queue_transaction(dwc_otg_hcd_t * hcd,
+			     dwc_hc_t * hc, uint16_t fifo_dwords_avail)
+{
+	int retval;
+
+	if (hcd->core_if->dma_enable) {
+		if (hcd->core_if->dma_desc_enable) {
+			if (!hc->xfer_started
+			    || (hc->ep_type == DWC_OTG_EP_TYPE_ISOC)) {
+				dwc_otg_hcd_start_xfer_ddma(hcd, hc->qh);
+				hc->qh->ping_state = 0;
+			}
+		} else if (!hc->xfer_started) {
+			if (fiq_fsm_enable && hc->error_state) {
+				hcd->fiq_state->channel[hc->hc_num].nr_errors =
+					DWC_CIRCLEQ_FIRST(&hc->qh->qtd_list)->error_count;
+				hcd->fiq_state->channel[hc->hc_num].fsm =
+					FIQ_PASSTHROUGH_ERRORSTATE;
+			}
+			dwc_otg_hc_start_transfer(hcd->core_if, hc);
+			hc->qh->ping_state = 0;
+		}
+		retval = 0;
+	} else if (hc->halt_pending) {
+		/* Don't queue a request if the channel has been halted. */
+		retval = 0;
+	} else if (hc->halt_on_queue) {
+		dwc_otg_hc_halt(hcd->core_if, hc, hc->halt_status);
+		retval = 0;
+	} else if (hc->do_ping) {
+		if (!hc->xfer_started) {
+			dwc_otg_hc_start_transfer(hcd->core_if, hc);
+		}
+		retval = 0;
+	} else if (!hc->ep_is_in || hc->data_pid_start == DWC_OTG_HC_PID_SETUP) {
+		if ((fifo_dwords_avail * 4) >= hc->max_packet) {
+			if (!hc->xfer_started) {
+				dwc_otg_hc_start_transfer(hcd->core_if, hc);
+				retval = 1;
+			} else {
+				retval =
+				    dwc_otg_hc_continue_transfer(hcd->core_if,
+								 hc);
+			}
+		} else {
+			retval = -1;
+		}
+	} else {
+		if (!hc->xfer_started) {
+			dwc_otg_hc_start_transfer(hcd->core_if, hc);
+			retval = 1;
+		} else {
+			retval = dwc_otg_hc_continue_transfer(hcd->core_if, hc);
+		}
+	}
+
+	return retval;
+}
+
+/**
+ * Processes periodic channels for the next frame and queues transactions for
+ * these channels to the DWC_otg controller. After queueing transactions, the
+ * Periodic Tx FIFO Empty interrupt is enabled if there are more transactions
+ * to queue as Periodic Tx FIFO or request queue space becomes available.
+ * Otherwise, the Periodic Tx FIFO Empty interrupt is disabled.
+ */
+static void process_periodic_channels(dwc_otg_hcd_t * hcd)
+{
+	hptxsts_data_t tx_status;
+	dwc_list_link_t *qh_ptr;
+	dwc_otg_qh_t *qh;
+	int status = 0;
+	int no_queue_space = 0;
+	int no_fifo_space = 0;
+
+	dwc_otg_host_global_regs_t *host_regs;
+	host_regs = hcd->core_if->host_if->host_global_regs;
+
+	DWC_DEBUGPL(DBG_HCDV, "Queue periodic transactions\n");
+#ifdef DEBUG
+	tx_status.d32 = DWC_READ_REG32(&host_regs->hptxsts);
+	DWC_DEBUGPL(DBG_HCDV,
+		    "  P Tx Req Queue Space Avail (before queue): %d\n",
+		    tx_status.b.ptxqspcavail);
+	DWC_DEBUGPL(DBG_HCDV, "  P Tx FIFO Space Avail (before queue): %d\n",
+		    tx_status.b.ptxfspcavail);
+#endif
+
+	qh_ptr = hcd->periodic_sched_assigned.next;
+	while (qh_ptr != &hcd->periodic_sched_assigned) {
+		tx_status.d32 = DWC_READ_REG32(&host_regs->hptxsts);
+		if (tx_status.b.ptxqspcavail == 0) {
+			no_queue_space = 1;
+			break;
+		}
+
+		qh = DWC_LIST_ENTRY(qh_ptr, dwc_otg_qh_t, qh_list_entry);
+
+		// Do not send a split start transaction any later than frame .6
+		// Note, we have to schedule a periodic in .5 to make it go in .6
+		if(fiq_fsm_enable && qh->do_split && ((dwc_otg_hcd_get_frame_number(hcd) + 1) & 7) > 6)
+		{
+			qh_ptr = qh_ptr->next;
+			hcd->fiq_state->next_sched_frame = dwc_otg_hcd_get_frame_number(hcd) | 7;
+			continue;
+		}
+
+		if (fiq_fsm_enable && fiq_fsm_transaction_suitable(hcd, qh)) {
+			if (qh->do_split)
+				fiq_fsm_queue_split_transaction(hcd, qh);
+			else
+				fiq_fsm_queue_isoc_transaction(hcd, qh);
+		} else {
+
+			/*
+			 * Set a flag if we're queueing high-bandwidth in slave mode.
+			 * The flag prevents any halts to get into the request queue in
+			 * the middle of multiple high-bandwidth packets getting queued.
+			 */
+			if (!hcd->core_if->dma_enable && qh->channel->multi_count > 1) {
+				hcd->core_if->queuing_high_bandwidth = 1;
+			}
+			status = queue_transaction(hcd, qh->channel,
+							tx_status.b.ptxfspcavail);
+			if (status < 0) {
+				no_fifo_space = 1;
+				break;
+			}
+		}
+
+		/*
+		 * In Slave mode, stay on the current transfer until there is
+		 * nothing more to do or the high-bandwidth request count is
+		 * reached. In DMA mode, only need to queue one request. The
+		 * controller automatically handles multiple packets for
+		 * high-bandwidth transfers.
+		 */
+		if (hcd->core_if->dma_enable || status == 0 ||
+		    qh->channel->requests == qh->channel->multi_count) {
+			qh_ptr = qh_ptr->next;
+			/*
+			 * Move the QH from the periodic assigned schedule to
+			 * the periodic queued schedule.
+			 */
+			DWC_LIST_MOVE_HEAD(&hcd->periodic_sched_queued,
+					   &qh->qh_list_entry);
+
+			/* done queuing high bandwidth */
+			hcd->core_if->queuing_high_bandwidth = 0;
+		}
+	}
+
+	if (!hcd->core_if->dma_enable) {
+		dwc_otg_core_global_regs_t *global_regs;
+		gintmsk_data_t intr_mask = {.d32 = 0 };
+
+		global_regs = hcd->core_if->core_global_regs;
+		intr_mask.b.ptxfempty = 1;
+#ifdef DEBUG
+		tx_status.d32 = DWC_READ_REG32(&host_regs->hptxsts);
+		DWC_DEBUGPL(DBG_HCDV,
+			    "  P Tx Req Queue Space Avail (after queue): %d\n",
+			    tx_status.b.ptxqspcavail);
+		DWC_DEBUGPL(DBG_HCDV,
+			    "  P Tx FIFO Space Avail (after queue): %d\n",
+			    tx_status.b.ptxfspcavail);
+#endif
+		if (!DWC_LIST_EMPTY(&hcd->periodic_sched_assigned) ||
+		    no_queue_space || no_fifo_space) {
+			/*
+			 * May need to queue more transactions as the request
+			 * queue or Tx FIFO empties. Enable the periodic Tx
+			 * FIFO empty interrupt. (Always use the half-empty
+			 * level to ensure that new requests are loaded as
+			 * soon as possible.)
+			 */
+			DWC_MODIFY_REG32(&global_regs->gintmsk, 0,
+					 intr_mask.d32);
+		} else {
+			/*
+			 * Disable the Tx FIFO empty interrupt since there are
+			 * no more transactions that need to be queued right
+			 * now. This function is called from interrupt
+			 * handlers to queue more transactions as transfer
+			 * states change.
+			 */
+			DWC_MODIFY_REG32(&global_regs->gintmsk, intr_mask.d32,
+					 0);
+		}
+	}
+}
+
+/**
+ * Processes active non-periodic channels and queues transactions for these
+ * channels to the DWC_otg controller. After queueing transactions, the NP Tx
+ * FIFO Empty interrupt is enabled if there are more transactions to queue as
+ * NP Tx FIFO or request queue space becomes available. Otherwise, the NP Tx
+ * FIFO Empty interrupt is disabled.
+ */
+static void process_non_periodic_channels(dwc_otg_hcd_t * hcd)
+{
+	gnptxsts_data_t tx_status;
+	dwc_list_link_t *orig_qh_ptr;
+	dwc_otg_qh_t *qh;
+	int status;
+	int no_queue_space = 0;
+	int no_fifo_space = 0;
+	int more_to_do = 0;
+
+	dwc_otg_core_global_regs_t *global_regs =
+	    hcd->core_if->core_global_regs;
+
+	DWC_DEBUGPL(DBG_HCDV, "Queue non-periodic transactions\n");
+#ifdef DEBUG
+	tx_status.d32 = DWC_READ_REG32(&global_regs->gnptxsts);
+	DWC_DEBUGPL(DBG_HCDV,
+		    "  NP Tx Req Queue Space Avail (before queue): %d\n",
+		    tx_status.b.nptxqspcavail);
+	DWC_DEBUGPL(DBG_HCDV, "  NP Tx FIFO Space Avail (before queue): %d\n",
+		    tx_status.b.nptxfspcavail);
+#endif
+	/*
+	 * Keep track of the starting point. Skip over the start-of-list
+	 * entry.
+	 */
+	if (hcd->non_periodic_qh_ptr == &hcd->non_periodic_sched_active) {
+		hcd->non_periodic_qh_ptr = hcd->non_periodic_qh_ptr->next;
+	}
+	orig_qh_ptr = hcd->non_periodic_qh_ptr;
+
+	/*
+	 * Process once through the active list or until no more space is
+	 * available in the request queue or the Tx FIFO.
+	 */
+	do {
+		tx_status.d32 = DWC_READ_REG32(&global_regs->gnptxsts);
+		if (!hcd->core_if->dma_enable && tx_status.b.nptxqspcavail == 0) {
+			no_queue_space = 1;
+			break;
+		}
+
+		qh = DWC_LIST_ENTRY(hcd->non_periodic_qh_ptr, dwc_otg_qh_t,
+				    qh_list_entry);
+
+		if(fiq_fsm_enable && fiq_fsm_transaction_suitable(hcd, qh)) {
+			fiq_fsm_queue_split_transaction(hcd, qh);
+		} else {
+			status = queue_transaction(hcd, qh->channel,
+						tx_status.b.nptxfspcavail);
+
+			if (status > 0) {
+				more_to_do = 1;
+			} else if (status < 0) {
+				no_fifo_space = 1;
+				break;
+			}
+		}
+		/* Advance to next QH, skipping start-of-list entry. */
+		hcd->non_periodic_qh_ptr = hcd->non_periodic_qh_ptr->next;
+		if (hcd->non_periodic_qh_ptr == &hcd->non_periodic_sched_active) {
+			hcd->non_periodic_qh_ptr =
+			    hcd->non_periodic_qh_ptr->next;
+		}
+
+	} while (hcd->non_periodic_qh_ptr != orig_qh_ptr);
+
+	if (!hcd->core_if->dma_enable) {
+		gintmsk_data_t intr_mask = {.d32 = 0 };
+		intr_mask.b.nptxfempty = 1;
+
+#ifdef DEBUG
+		tx_status.d32 = DWC_READ_REG32(&global_regs->gnptxsts);
+		DWC_DEBUGPL(DBG_HCDV,
+			    "  NP Tx Req Queue Space Avail (after queue): %d\n",
+			    tx_status.b.nptxqspcavail);
+		DWC_DEBUGPL(DBG_HCDV,
+			    "  NP Tx FIFO Space Avail (after queue): %d\n",
+			    tx_status.b.nptxfspcavail);
+#endif
+		if (more_to_do || no_queue_space || no_fifo_space) {
+			/*
+			 * May need to queue more transactions as the request
+			 * queue or Tx FIFO empties. Enable the non-periodic
+			 * Tx FIFO empty interrupt. (Always use the half-empty
+			 * level to ensure that new requests are loaded as
+			 * soon as possible.)
+			 */
+			DWC_MODIFY_REG32(&global_regs->gintmsk, 0,
+					 intr_mask.d32);
+		} else {
+			/*
+			 * Disable the Tx FIFO empty interrupt since there are
+			 * no more transactions that need to be queued right
+			 * now. This function is called from interrupt
+			 * handlers to queue more transactions as transfer
+			 * states change.
+			 */
+			DWC_MODIFY_REG32(&global_regs->gintmsk, intr_mask.d32,
+					 0);
+		}
+	}
+}
+
+/**
+ * This function processes the currently active host channels and queues
+ * transactions for these channels to the DWC_otg controller. It is called
+ * from HCD interrupt handler functions.
+ *
+ * @param hcd The HCD state structure.
+ * @param tr_type The type(s) of transactions to queue (non-periodic,
+ * periodic, or both).
+ */
+void dwc_otg_hcd_queue_transactions(dwc_otg_hcd_t * hcd,
+				    dwc_otg_transaction_type_e tr_type)
+{
+#ifdef DEBUG_SOF
+	DWC_DEBUGPL(DBG_HCD, "Queue Transactions\n");
+#endif
+	/* Process host channels associated with periodic transfers. */
+	if ((tr_type == DWC_OTG_TRANSACTION_PERIODIC ||
+	     tr_type == DWC_OTG_TRANSACTION_ALL) &&
+	    !DWC_LIST_EMPTY(&hcd->periodic_sched_assigned)) {
+
+		process_periodic_channels(hcd);
+	}
+
+	/* Process host channels associated with non-periodic transfers. */
+	if (tr_type == DWC_OTG_TRANSACTION_NON_PERIODIC ||
+	    tr_type == DWC_OTG_TRANSACTION_ALL) {
+		if (!DWC_LIST_EMPTY(&hcd->non_periodic_sched_active)) {
+			process_non_periodic_channels(hcd);
+		} else {
+			/*
+			 * Ensure NP Tx FIFO empty interrupt is disabled when
+			 * there are no non-periodic transfers to process.
+			 */
+			gintmsk_data_t gintmsk = {.d32 = 0 };
+			gintmsk.b.nptxfempty = 1;
+
+			if (fiq_enable) {
+				local_fiq_disable();
+				fiq_fsm_spin_lock(&hcd->fiq_state->lock);
+				DWC_MODIFY_REG32(&hcd->core_if->core_global_regs->gintmsk, gintmsk.d32, 0);
+				fiq_fsm_spin_unlock(&hcd->fiq_state->lock);
+				local_fiq_enable();
+			} else {
+				DWC_MODIFY_REG32(&hcd->core_if->core_global_regs->gintmsk, gintmsk.d32, 0);
+			}
+		}
+	}
+}
+
+#ifdef DWC_HS_ELECT_TST
+/*
+ * Quick and dirty hack to implement the HS Electrical Test
+ * SINGLE_STEP_GET_DEVICE_DESCRIPTOR feature.
+ *
+ * This code was copied from our userspace app "hset". It sends a
+ * Get Device Descriptor control sequence in two parts, first the
+ * Setup packet by itself, followed some time later by the In and
+ * Ack packets. Rather than trying to figure out how to add this
+ * functionality to the normal driver code, we just hijack the
+ * hardware, using these two function to drive the hardware
+ * directly.
+ */
+
+static dwc_otg_core_global_regs_t *global_regs;
+static dwc_otg_host_global_regs_t *hc_global_regs;
+static dwc_otg_hc_regs_t *hc_regs;
+static uint32_t *data_fifo;
+
+static void do_setup(void)
+{
+	gintsts_data_t gintsts;
+	hctsiz_data_t hctsiz;
+	hcchar_data_t hcchar;
+	haint_data_t haint;
+	hcint_data_t hcint;
+
+	/* Enable HAINTs */
+	DWC_WRITE_REG32(&hc_global_regs->haintmsk, 0x0001);
+
+	/* Enable HCINTs */
+	DWC_WRITE_REG32(&hc_regs->hcintmsk, 0x04a3);
+
+	/* Read GINTSTS */
+	gintsts.d32 = DWC_READ_REG32(&global_regs->gintsts);
+
+	/* Read HAINT */
+	haint.d32 = DWC_READ_REG32(&hc_global_regs->haint);
+
+	/* Read HCINT */
+	hcint.d32 = DWC_READ_REG32(&hc_regs->hcint);
+
+	/* Read HCCHAR */
+	hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar);
+
+	/* Clear HCINT */
+	DWC_WRITE_REG32(&hc_regs->hcint, hcint.d32);
+
+	/* Clear HAINT */
+	DWC_WRITE_REG32(&hc_global_regs->haint, haint.d32);
+
+	/* Clear GINTSTS */
+	DWC_WRITE_REG32(&global_regs->gintsts, gintsts.d32);
+
+	/* Read GINTSTS */
+	gintsts.d32 = DWC_READ_REG32(&global_regs->gintsts);
+
+	/*
+	 * Send Setup packet (Get Device Descriptor)
+	 */
+
+	/* Make sure channel is disabled */
+	hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar);
+	if (hcchar.b.chen) {
+		hcchar.b.chdis = 1;
+//              hcchar.b.chen = 1;
+		DWC_WRITE_REG32(&hc_regs->hcchar, hcchar.d32);
+		//sleep(1);
+		dwc_mdelay(1000);
+
+		/* Read GINTSTS */
+		gintsts.d32 = DWC_READ_REG32(&global_regs->gintsts);
+
+		/* Read HAINT */
+		haint.d32 = DWC_READ_REG32(&hc_global_regs->haint);
+
+		/* Read HCINT */
+		hcint.d32 = DWC_READ_REG32(&hc_regs->hcint);
+
+		/* Read HCCHAR */
+		hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar);
+
+		/* Clear HCINT */
+		DWC_WRITE_REG32(&hc_regs->hcint, hcint.d32);
+
+		/* Clear HAINT */
+		DWC_WRITE_REG32(&hc_global_regs->haint, haint.d32);
+
+		/* Clear GINTSTS */
+		DWC_WRITE_REG32(&global_regs->gintsts, gintsts.d32);
+
+		hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar);
+	}
+
+	/* Set HCTSIZ */
+	hctsiz.d32 = 0;
+	hctsiz.b.xfersize = 8;
+	hctsiz.b.pktcnt = 1;
+	hctsiz.b.pid = DWC_OTG_HC_PID_SETUP;
+	DWC_WRITE_REG32(&hc_regs->hctsiz, hctsiz.d32);
+
+	/* Set HCCHAR */
+	hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar);
+	hcchar.b.eptype = DWC_OTG_EP_TYPE_CONTROL;
+	hcchar.b.epdir = 0;
+	hcchar.b.epnum = 0;
+	hcchar.b.mps = 8;
+	hcchar.b.chen = 1;
+	DWC_WRITE_REG32(&hc_regs->hcchar, hcchar.d32);
+
+	/* Fill FIFO with Setup data for Get Device Descriptor */
+	data_fifo = (uint32_t *) ((char *)global_regs + 0x1000);
+	DWC_WRITE_REG32(data_fifo++, 0x01000680);
+	DWC_WRITE_REG32(data_fifo++, 0x00080000);
+
+	gintsts.d32 = DWC_READ_REG32(&global_regs->gintsts);
+
+	/* Wait for host channel interrupt */
+	do {
+		gintsts.d32 = DWC_READ_REG32(&global_regs->gintsts);
+	} while (gintsts.b.hcintr == 0);
+
+	/* Disable HCINTs */
+	DWC_WRITE_REG32(&hc_regs->hcintmsk, 0x0000);
+
+	/* Disable HAINTs */
+	DWC_WRITE_REG32(&hc_global_regs->haintmsk, 0x0000);
+
+	/* Read HAINT */
+	haint.d32 = DWC_READ_REG32(&hc_global_regs->haint);
+
+	/* Read HCINT */
+	hcint.d32 = DWC_READ_REG32(&hc_regs->hcint);
+
+	/* Read HCCHAR */
+	hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar);
+
+	/* Clear HCINT */
+	DWC_WRITE_REG32(&hc_regs->hcint, hcint.d32);
+
+	/* Clear HAINT */
+	DWC_WRITE_REG32(&hc_global_regs->haint, haint.d32);
+
+	/* Clear GINTSTS */
+	DWC_WRITE_REG32(&global_regs->gintsts, gintsts.d32);
+
+	/* Read GINTSTS */
+	gintsts.d32 = DWC_READ_REG32(&global_regs->gintsts);
+}
+
+static void do_in_ack(void)
+{
+	gintsts_data_t gintsts;
+	hctsiz_data_t hctsiz;
+	hcchar_data_t hcchar;
+	haint_data_t haint;
+	hcint_data_t hcint;
+	host_grxsts_data_t grxsts;
+
+	/* Enable HAINTs */
+	DWC_WRITE_REG32(&hc_global_regs->haintmsk, 0x0001);
+
+	/* Enable HCINTs */
+	DWC_WRITE_REG32(&hc_regs->hcintmsk, 0x04a3);
+
+	/* Read GINTSTS */
+	gintsts.d32 = DWC_READ_REG32(&global_regs->gintsts);
+
+	/* Read HAINT */
+	haint.d32 = DWC_READ_REG32(&hc_global_regs->haint);
+
+	/* Read HCINT */
+	hcint.d32 = DWC_READ_REG32(&hc_regs->hcint);
+
+	/* Read HCCHAR */
+	hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar);
+
+	/* Clear HCINT */
+	DWC_WRITE_REG32(&hc_regs->hcint, hcint.d32);
+
+	/* Clear HAINT */
+	DWC_WRITE_REG32(&hc_global_regs->haint, haint.d32);
+
+	/* Clear GINTSTS */
+	DWC_WRITE_REG32(&global_regs->gintsts, gintsts.d32);
+
+	/* Read GINTSTS */
+	gintsts.d32 = DWC_READ_REG32(&global_regs->gintsts);
+
+	/*
+	 * Receive Control In packet
+	 */
+
+	/* Make sure channel is disabled */
+	hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar);
+	if (hcchar.b.chen) {
+		hcchar.b.chdis = 1;
+		hcchar.b.chen = 1;
+		DWC_WRITE_REG32(&hc_regs->hcchar, hcchar.d32);
+		//sleep(1);
+		dwc_mdelay(1000);
+
+		/* Read GINTSTS */
+		gintsts.d32 = DWC_READ_REG32(&global_regs->gintsts);
+
+		/* Read HAINT */
+		haint.d32 = DWC_READ_REG32(&hc_global_regs->haint);
+
+		/* Read HCINT */
+		hcint.d32 = DWC_READ_REG32(&hc_regs->hcint);
+
+		/* Read HCCHAR */
+		hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar);
+
+		/* Clear HCINT */
+		DWC_WRITE_REG32(&hc_regs->hcint, hcint.d32);
+
+		/* Clear HAINT */
+		DWC_WRITE_REG32(&hc_global_regs->haint, haint.d32);
+
+		/* Clear GINTSTS */
+		DWC_WRITE_REG32(&global_regs->gintsts, gintsts.d32);
+
+		hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar);
+	}
+
+	/* Set HCTSIZ */
+	hctsiz.d32 = 0;
+	hctsiz.b.xfersize = 8;
+	hctsiz.b.pktcnt = 1;
+	hctsiz.b.pid = DWC_OTG_HC_PID_DATA1;
+	DWC_WRITE_REG32(&hc_regs->hctsiz, hctsiz.d32);
+
+	/* Set HCCHAR */
+	hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar);
+	hcchar.b.eptype = DWC_OTG_EP_TYPE_CONTROL;
+	hcchar.b.epdir = 1;
+	hcchar.b.epnum = 0;
+	hcchar.b.mps = 8;
+	hcchar.b.chen = 1;
+	DWC_WRITE_REG32(&hc_regs->hcchar, hcchar.d32);
+
+	gintsts.d32 = DWC_READ_REG32(&global_regs->gintsts);
+
+	/* Wait for receive status queue interrupt */
+	do {
+		gintsts.d32 = DWC_READ_REG32(&global_regs->gintsts);
+	} while (gintsts.b.rxstsqlvl == 0);
+
+	/* Read RXSTS */
+	grxsts.d32 = DWC_READ_REG32(&global_regs->grxstsp);
+
+	/* Clear RXSTSQLVL in GINTSTS */
+	gintsts.d32 = 0;
+	gintsts.b.rxstsqlvl = 1;
+	DWC_WRITE_REG32(&global_regs->gintsts, gintsts.d32);
+
+	switch (grxsts.b.pktsts) {
+	case DWC_GRXSTS_PKTSTS_IN:
+		/* Read the data into the host buffer */
+		if (grxsts.b.bcnt > 0) {
+			int i;
+			int word_count = (grxsts.b.bcnt + 3) / 4;
+
+			data_fifo = (uint32_t *) ((char *)global_regs + 0x1000);
+
+			for (i = 0; i < word_count; i++) {
+				(void)DWC_READ_REG32(data_fifo++);
+			}
+		}
+		break;
+
+	default:
+		break;
+	}
+
+	gintsts.d32 = DWC_READ_REG32(&global_regs->gintsts);
+
+	/* Wait for receive status queue interrupt */
+	do {
+		gintsts.d32 = DWC_READ_REG32(&global_regs->gintsts);
+	} while (gintsts.b.rxstsqlvl == 0);
+
+	/* Read RXSTS */
+	grxsts.d32 = DWC_READ_REG32(&global_regs->grxstsp);
+
+	/* Clear RXSTSQLVL in GINTSTS */
+	gintsts.d32 = 0;
+	gintsts.b.rxstsqlvl = 1;
+	DWC_WRITE_REG32(&global_regs->gintsts, gintsts.d32);
+
+	switch (grxsts.b.pktsts) {
+	case DWC_GRXSTS_PKTSTS_IN_XFER_COMP:
+		break;
+
+	default:
+		break;
+	}
+
+	gintsts.d32 = DWC_READ_REG32(&global_regs->gintsts);
+
+	/* Wait for host channel interrupt */
+	do {
+		gintsts.d32 = DWC_READ_REG32(&global_regs->gintsts);
+	} while (gintsts.b.hcintr == 0);
+
+	/* Read HAINT */
+	haint.d32 = DWC_READ_REG32(&hc_global_regs->haint);
+
+	/* Read HCINT */
+	hcint.d32 = DWC_READ_REG32(&hc_regs->hcint);
+
+	/* Read HCCHAR */
+	hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar);
+
+	/* Clear HCINT */
+	DWC_WRITE_REG32(&hc_regs->hcint, hcint.d32);
+
+	/* Clear HAINT */
+	DWC_WRITE_REG32(&hc_global_regs->haint, haint.d32);
+
+	/* Clear GINTSTS */
+	DWC_WRITE_REG32(&global_regs->gintsts, gintsts.d32);
+
+	/* Read GINTSTS */
+	gintsts.d32 = DWC_READ_REG32(&global_regs->gintsts);
+
+//      usleep(100000);
+//      mdelay(100);
+	dwc_mdelay(1);
+
+	/*
+	 * Send handshake packet
+	 */
+
+	/* Read HAINT */
+	haint.d32 = DWC_READ_REG32(&hc_global_regs->haint);
+
+	/* Read HCINT */
+	hcint.d32 = DWC_READ_REG32(&hc_regs->hcint);
+
+	/* Read HCCHAR */
+	hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar);
+
+	/* Clear HCINT */
+	DWC_WRITE_REG32(&hc_regs->hcint, hcint.d32);
+
+	/* Clear HAINT */
+	DWC_WRITE_REG32(&hc_global_regs->haint, haint.d32);
+
+	/* Clear GINTSTS */
+	DWC_WRITE_REG32(&global_regs->gintsts, gintsts.d32);
+
+	/* Read GINTSTS */
+	gintsts.d32 = DWC_READ_REG32(&global_regs->gintsts);
+
+	/* Make sure channel is disabled */
+	hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar);
+	if (hcchar.b.chen) {
+		hcchar.b.chdis = 1;
+		hcchar.b.chen = 1;
+		DWC_WRITE_REG32(&hc_regs->hcchar, hcchar.d32);
+		//sleep(1);
+		dwc_mdelay(1000);
+
+		/* Read GINTSTS */
+		gintsts.d32 = DWC_READ_REG32(&global_regs->gintsts);
+
+		/* Read HAINT */
+		haint.d32 = DWC_READ_REG32(&hc_global_regs->haint);
+
+		/* Read HCINT */
+		hcint.d32 = DWC_READ_REG32(&hc_regs->hcint);
+
+		/* Read HCCHAR */
+		hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar);
+
+		/* Clear HCINT */
+		DWC_WRITE_REG32(&hc_regs->hcint, hcint.d32);
+
+		/* Clear HAINT */
+		DWC_WRITE_REG32(&hc_global_regs->haint, haint.d32);
+
+		/* Clear GINTSTS */
+		DWC_WRITE_REG32(&global_regs->gintsts, gintsts.d32);
+
+		hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar);
+	}
+
+	/* Set HCTSIZ */
+	hctsiz.d32 = 0;
+	hctsiz.b.xfersize = 0;
+	hctsiz.b.pktcnt = 1;
+	hctsiz.b.pid = DWC_OTG_HC_PID_DATA1;
+	DWC_WRITE_REG32(&hc_regs->hctsiz, hctsiz.d32);
+
+	/* Set HCCHAR */
+	hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar);
+	hcchar.b.eptype = DWC_OTG_EP_TYPE_CONTROL;
+	hcchar.b.epdir = 0;
+	hcchar.b.epnum = 0;
+	hcchar.b.mps = 8;
+	hcchar.b.chen = 1;
+	DWC_WRITE_REG32(&hc_regs->hcchar, hcchar.d32);
+
+	gintsts.d32 = DWC_READ_REG32(&global_regs->gintsts);
+
+	/* Wait for host channel interrupt */
+	do {
+		gintsts.d32 = DWC_READ_REG32(&global_regs->gintsts);
+	} while (gintsts.b.hcintr == 0);
+
+	/* Disable HCINTs */
+	DWC_WRITE_REG32(&hc_regs->hcintmsk, 0x0000);
+
+	/* Disable HAINTs */
+	DWC_WRITE_REG32(&hc_global_regs->haintmsk, 0x0000);
+
+	/* Read HAINT */
+	haint.d32 = DWC_READ_REG32(&hc_global_regs->haint);
+
+	/* Read HCINT */
+	hcint.d32 = DWC_READ_REG32(&hc_regs->hcint);
+
+	/* Read HCCHAR */
+	hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar);
+
+	/* Clear HCINT */
+	DWC_WRITE_REG32(&hc_regs->hcint, hcint.d32);
+
+	/* Clear HAINT */
+	DWC_WRITE_REG32(&hc_global_regs->haint, haint.d32);
+
+	/* Clear GINTSTS */
+	DWC_WRITE_REG32(&global_regs->gintsts, gintsts.d32);
+
+	/* Read GINTSTS */
+	gintsts.d32 = DWC_READ_REG32(&global_regs->gintsts);
+}
+#endif
+
+/** Handles hub class-specific requests. */
+int dwc_otg_hcd_hub_control(dwc_otg_hcd_t * dwc_otg_hcd,
+			    uint16_t typeReq,
+			    uint16_t wValue,
+			    uint16_t wIndex, uint8_t * buf, uint16_t wLength)
+{
+	int retval = 0;
+
+	dwc_otg_core_if_t *core_if = dwc_otg_hcd->core_if;
+	usb_hub_descriptor_t *hub_desc;
+	hprt0_data_t hprt0 = {.d32 = 0 };
+
+	uint32_t port_status;
+
+	switch (typeReq) {
+	case UCR_CLEAR_HUB_FEATURE:
+		DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD HUB CONTROL - "
+			    "ClearHubFeature 0x%x\n", wValue);
+		switch (wValue) {
+		case UHF_C_HUB_LOCAL_POWER:
+		case UHF_C_HUB_OVER_CURRENT:
+			/* Nothing required here */
+			break;
+		default:
+			retval = -DWC_E_INVALID;
+			DWC_ERROR("DWC OTG HCD - "
+				  "ClearHubFeature request %xh unknown\n",
+				  wValue);
+		}
+		break;
+	case UCR_CLEAR_PORT_FEATURE:
+#ifdef CONFIG_USB_DWC_OTG_LPM
+		if (wValue != UHF_PORT_L1)
+#endif
+			if (!wIndex || wIndex > 1)
+				goto error;
+
+		switch (wValue) {
+		case UHF_PORT_ENABLE:
+			DWC_DEBUGPL(DBG_ANY, "DWC OTG HCD HUB CONTROL - "
+				    "ClearPortFeature USB_PORT_FEAT_ENABLE\n");
+			hprt0.d32 = dwc_otg_read_hprt0(core_if);
+			hprt0.b.prtena = 1;
+			DWC_WRITE_REG32(core_if->host_if->hprt0, hprt0.d32);
+			break;
+		case UHF_PORT_SUSPEND:
+			DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD HUB CONTROL - "
+				    "ClearPortFeature USB_PORT_FEAT_SUSPEND\n");
+
+			if (core_if->power_down == 2) {
+				dwc_otg_host_hibernation_restore(core_if, 0, 0);
+			} else {
+				DWC_WRITE_REG32(core_if->pcgcctl, 0);
+				dwc_mdelay(5);
+
+				hprt0.d32 = dwc_otg_read_hprt0(core_if);
+				hprt0.b.prtres = 1;
+				DWC_WRITE_REG32(core_if->host_if->hprt0, hprt0.d32);
+				hprt0.b.prtsusp = 0;
+				/* Clear Resume bit */
+				dwc_mdelay(100);
+				hprt0.b.prtres = 0;
+				DWC_WRITE_REG32(core_if->host_if->hprt0, hprt0.d32);
+			}
+			break;
+#ifdef CONFIG_USB_DWC_OTG_LPM
+		case UHF_PORT_L1:
+			{
+				pcgcctl_data_t pcgcctl = {.d32 = 0 };
+				glpmcfg_data_t lpmcfg = {.d32 = 0 };
+
+				lpmcfg.d32 =
+				    DWC_READ_REG32(&core_if->
+						   core_global_regs->glpmcfg);
+				lpmcfg.b.en_utmi_sleep = 0;
+				lpmcfg.b.hird_thres &= (~(1 << 4));
+				lpmcfg.b.prt_sleep_sts = 1;
+				DWC_WRITE_REG32(&core_if->
+						core_global_regs->glpmcfg,
+						lpmcfg.d32);
+
+				/* Clear Enbl_L1Gating bit. */
+				pcgcctl.b.enbl_sleep_gating = 1;
+				DWC_MODIFY_REG32(core_if->pcgcctl, pcgcctl.d32,
+						 0);
+
+				dwc_mdelay(5);
+
+				hprt0.d32 = dwc_otg_read_hprt0(core_if);
+				hprt0.b.prtres = 1;
+				DWC_WRITE_REG32(core_if->host_if->hprt0,
+						hprt0.d32);
+				/* This bit will be cleared in wakeup interrupt handle */
+				break;
+			}
+#endif
+		case UHF_PORT_POWER:
+			DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD HUB CONTROL - "
+				    "ClearPortFeature USB_PORT_FEAT_POWER\n");
+			hprt0.d32 = dwc_otg_read_hprt0(core_if);
+			hprt0.b.prtpwr = 0;
+			DWC_WRITE_REG32(core_if->host_if->hprt0, hprt0.d32);
+			break;
+		case UHF_PORT_INDICATOR:
+			DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD HUB CONTROL - "
+				    "ClearPortFeature USB_PORT_FEAT_INDICATOR\n");
+			/* Port inidicator not supported */
+			break;
+		case UHF_C_PORT_CONNECTION:
+			/* Clears drivers internal connect status change
+			 * flag */
+			DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD HUB CONTROL - "
+				    "ClearPortFeature USB_PORT_FEAT_C_CONNECTION\n");
+			dwc_otg_hcd->flags.b.port_connect_status_change = 0;
+			break;
+		case UHF_C_PORT_RESET:
+			/* Clears the driver's internal Port Reset Change
+			 * flag */
+			DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD HUB CONTROL - "
+				    "ClearPortFeature USB_PORT_FEAT_C_RESET\n");
+			dwc_otg_hcd->flags.b.port_reset_change = 0;
+			break;
+		case UHF_C_PORT_ENABLE:
+			/* Clears the driver's internal Port
+			 * Enable/Disable Change flag */
+			DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD HUB CONTROL - "
+				    "ClearPortFeature USB_PORT_FEAT_C_ENABLE\n");
+			dwc_otg_hcd->flags.b.port_enable_change = 0;
+			break;
+		case UHF_C_PORT_SUSPEND:
+			/* Clears the driver's internal Port Suspend
+			 * Change flag, which is set when resume signaling on
+			 * the host port is complete */
+			DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD HUB CONTROL - "
+				    "ClearPortFeature USB_PORT_FEAT_C_SUSPEND\n");
+			dwc_otg_hcd->flags.b.port_suspend_change = 0;
+			break;
+#ifdef CONFIG_USB_DWC_OTG_LPM
+		case UHF_C_PORT_L1:
+			dwc_otg_hcd->flags.b.port_l1_change = 0;
+			break;
+#endif
+		case UHF_C_PORT_OVER_CURRENT:
+			DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD HUB CONTROL - "
+				    "ClearPortFeature USB_PORT_FEAT_C_OVER_CURRENT\n");
+			dwc_otg_hcd->flags.b.port_over_current_change = 0;
+			break;
+		default:
+			retval = -DWC_E_INVALID;
+			DWC_ERROR("DWC OTG HCD - "
+				  "ClearPortFeature request %xh "
+				  "unknown or unsupported\n", wValue);
+		}
+		break;
+	case UCR_GET_HUB_DESCRIPTOR:
+		DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD HUB CONTROL - "
+			    "GetHubDescriptor\n");
+		hub_desc = (usb_hub_descriptor_t *) buf;
+		hub_desc->bDescLength = 9;
+		hub_desc->bDescriptorType = 0x29;
+		hub_desc->bNbrPorts = 1;
+		USETW(hub_desc->wHubCharacteristics, 0x08);
+		hub_desc->bPwrOn2PwrGood = 1;
+		hub_desc->bHubContrCurrent = 0;
+		hub_desc->DeviceRemovable[0] = 0;
+		hub_desc->DeviceRemovable[1] = 0xff;
+		break;
+	case UCR_GET_HUB_STATUS:
+		DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD HUB CONTROL - "
+			    "GetHubStatus\n");
+		DWC_MEMSET(buf, 0, 4);
+		break;
+	case UCR_GET_PORT_STATUS:
+		DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD HUB CONTROL - "
+			    "GetPortStatus wIndex = 0x%04x FLAGS=0x%08x\n",
+			    wIndex, dwc_otg_hcd->flags.d32);
+		if (!wIndex || wIndex > 1)
+			goto error;
+
+		port_status = 0;
+
+		if (dwc_otg_hcd->flags.b.port_connect_status_change)
+			port_status |= (1 << UHF_C_PORT_CONNECTION);
+
+		if (dwc_otg_hcd->flags.b.port_enable_change)
+			port_status |= (1 << UHF_C_PORT_ENABLE);
+
+		if (dwc_otg_hcd->flags.b.port_suspend_change)
+			port_status |= (1 << UHF_C_PORT_SUSPEND);
+
+		if (dwc_otg_hcd->flags.b.port_l1_change)
+			port_status |= (1 << UHF_C_PORT_L1);
+
+		if (dwc_otg_hcd->flags.b.port_reset_change) {
+			port_status |= (1 << UHF_C_PORT_RESET);
+		}
+
+		if (dwc_otg_hcd->flags.b.port_over_current_change) {
+			DWC_WARN("Overcurrent change detected\n");
+			port_status |= (1 << UHF_C_PORT_OVER_CURRENT);
+		}
+
+		if (!dwc_otg_hcd->flags.b.port_connect_status) {
+			/*
+			 * The port is disconnected, which means the core is
+			 * either in device mode or it soon will be. Just
+			 * return 0's for the remainder of the port status
+			 * since the port register can't be read if the core
+			 * is in device mode.
+			 */
+			*((__le32 *) buf) = dwc_cpu_to_le32(&port_status);
+			break;
+		}
+
+		hprt0.d32 = DWC_READ_REG32(core_if->host_if->hprt0);
+		DWC_DEBUGPL(DBG_HCDV, "  HPRT0: 0x%08x\n", hprt0.d32);
+
+		if (hprt0.b.prtconnsts)
+			port_status |= (1 << UHF_PORT_CONNECTION);
+
+		if (hprt0.b.prtena)
+			port_status |= (1 << UHF_PORT_ENABLE);
+
+		if (hprt0.b.prtsusp)
+			port_status |= (1 << UHF_PORT_SUSPEND);
+
+		if (hprt0.b.prtovrcurract)
+			port_status |= (1 << UHF_PORT_OVER_CURRENT);
+
+		if (hprt0.b.prtrst)
+			port_status |= (1 << UHF_PORT_RESET);
+
+		if (hprt0.b.prtpwr)
+			port_status |= (1 << UHF_PORT_POWER);
+
+		if (hprt0.b.prtspd == DWC_HPRT0_PRTSPD_HIGH_SPEED)
+			port_status |= (1 << UHF_PORT_HIGH_SPEED);
+		else if (hprt0.b.prtspd == DWC_HPRT0_PRTSPD_LOW_SPEED)
+			port_status |= (1 << UHF_PORT_LOW_SPEED);
+
+		if (hprt0.b.prttstctl)
+			port_status |= (1 << UHF_PORT_TEST);
+		if (dwc_otg_get_lpm_portsleepstatus(dwc_otg_hcd->core_if)) {
+			port_status |= (1 << UHF_PORT_L1);
+		}
+		/*
+		   For Synopsys HW emulation of Power down wkup_control asserts the
+		   hreset_n and prst_n on suspned. This causes the HPRT0 to be zero.
+		   We intentionally tell the software that port is in L2Suspend state.
+		   Only for STE.
+		*/
+		if ((core_if->power_down == 2)
+		    && (core_if->hibernation_suspend == 1)) {
+			port_status |= (1 << UHF_PORT_SUSPEND);
+		}
+		/* USB_PORT_FEAT_INDICATOR unsupported always 0 */
+
+		*((__le32 *) buf) = dwc_cpu_to_le32(&port_status);
+
+		break;
+	case UCR_SET_HUB_FEATURE:
+		DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD HUB CONTROL - "
+			    "SetHubFeature\n");
+		/* No HUB features supported */
+		break;
+	case UCR_SET_PORT_FEATURE:
+		if (wValue != UHF_PORT_TEST && (!wIndex || wIndex > 1))
+			goto error;
+
+		if (!dwc_otg_hcd->flags.b.port_connect_status) {
+			/*
+			 * The port is disconnected, which means the core is
+			 * either in device mode or it soon will be. Just
+			 * return without doing anything since the port
+			 * register can't be written if the core is in device
+			 * mode.
+			 */
+			break;
+		}
+
+		switch (wValue) {
+		case UHF_PORT_SUSPEND:
+			DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD HUB CONTROL - "
+				    "SetPortFeature - USB_PORT_FEAT_SUSPEND\n");
+			if (dwc_otg_hcd_otg_port(dwc_otg_hcd) != wIndex) {
+				goto error;
+			}
+			if (core_if->power_down == 2) {
+				int timeout = 300;
+				dwc_irqflags_t flags;
+				pcgcctl_data_t pcgcctl = {.d32 = 0 };
+				gpwrdn_data_t gpwrdn = {.d32 = 0 };
+				gusbcfg_data_t gusbcfg = {.d32 = 0 };
+#ifdef DWC_DEV_SRPCAP
+				int32_t otg_cap_param = core_if->core_params->otg_cap;
+#endif
+				DWC_PRINTF("Preparing for complete power-off\n");
+
+				/* Save registers before hibernation */
+				dwc_otg_save_global_regs(core_if);
+				dwc_otg_save_host_regs(core_if);
+
+				hprt0.d32 = dwc_otg_read_hprt0(core_if);
+				hprt0.b.prtsusp = 1;
+				hprt0.b.prtena = 0;
+				DWC_WRITE_REG32(core_if->host_if->hprt0, hprt0.d32);
+				/* Spin hprt0.b.prtsusp to became 1 */
+				do {
+					hprt0.d32 = dwc_otg_read_hprt0(core_if);
+					if (hprt0.b.prtsusp) {
+						break;
+					}
+					dwc_mdelay(1);
+				} while (--timeout);
+				if (!timeout) {
+					DWC_WARN("Suspend wasn't genereted\n");
+				}
+				dwc_udelay(10);
+
+				/*
+				 * We need to disable interrupts to prevent servicing of any IRQ
+				 * during going to hibernation
+				 */
+				DWC_SPINLOCK_IRQSAVE(dwc_otg_hcd->lock, &flags);
+				core_if->lx_state = DWC_OTG_L2;
+#ifdef DWC_DEV_SRPCAP
+				hprt0.d32 = dwc_otg_read_hprt0(core_if);
+				hprt0.b.prtpwr = 0;
+				hprt0.b.prtena = 0;
+				DWC_WRITE_REG32(core_if->host_if->hprt0,
+						hprt0.d32);
+#endif
+				gusbcfg.d32 =
+				    DWC_READ_REG32(&core_if->core_global_regs->
+						   gusbcfg);
+				if (gusbcfg.b.ulpi_utmi_sel == 1) {
+					/* ULPI interface */
+					/* Suspend the Phy Clock */
+					pcgcctl.d32 = 0;
+					pcgcctl.b.stoppclk = 1;
+					DWC_MODIFY_REG32(core_if->pcgcctl, 0,
+							 pcgcctl.d32);
+					dwc_udelay(10);
+					gpwrdn.b.pmuactv = 1;
+					DWC_MODIFY_REG32(&core_if->
+							 core_global_regs->
+							 gpwrdn, 0, gpwrdn.d32);
+				} else {
+					/* UTMI+ Interface */
+					gpwrdn.b.pmuactv = 1;
+					DWC_MODIFY_REG32(&core_if->
+							 core_global_regs->
+							 gpwrdn, 0, gpwrdn.d32);
+					dwc_udelay(10);
+					pcgcctl.b.stoppclk = 1;
+					DWC_MODIFY_REG32(core_if->pcgcctl, 0, pcgcctl.d32);
+					dwc_udelay(10);
+				}
+#ifdef DWC_DEV_SRPCAP
+				gpwrdn.d32 = 0;
+				gpwrdn.b.dis_vbus = 1;
+				DWC_MODIFY_REG32(&core_if->core_global_regs->
+						 gpwrdn, 0, gpwrdn.d32);
+#endif
+				gpwrdn.d32 = 0;
+				gpwrdn.b.pmuintsel = 1;
+				DWC_MODIFY_REG32(&core_if->core_global_regs->
+						 gpwrdn, 0, gpwrdn.d32);
+				dwc_udelay(10);
+
+				gpwrdn.d32 = 0;
+#ifdef DWC_DEV_SRPCAP
+				gpwrdn.b.srp_det_msk = 1;
+#endif
+				gpwrdn.b.disconn_det_msk = 1;
+				gpwrdn.b.lnstchng_msk = 1;
+				gpwrdn.b.sts_chngint_msk = 1;
+				DWC_MODIFY_REG32(&core_if->core_global_regs->
+						 gpwrdn, 0, gpwrdn.d32);
+				dwc_udelay(10);
+
+				/* Enable Power Down Clamp and all interrupts in GPWRDN */
+				gpwrdn.d32 = 0;
+				gpwrdn.b.pwrdnclmp = 1;
+				DWC_MODIFY_REG32(&core_if->core_global_regs->
+						 gpwrdn, 0, gpwrdn.d32);
+				dwc_udelay(10);
+
+				/* Switch off VDD */
+				gpwrdn.d32 = 0;
+				gpwrdn.b.pwrdnswtch = 1;
+				DWC_MODIFY_REG32(&core_if->core_global_regs->
+						 gpwrdn, 0, gpwrdn.d32);
+
+#ifdef DWC_DEV_SRPCAP
+				if (otg_cap_param == DWC_OTG_CAP_PARAM_HNP_SRP_CAPABLE)
+				{
+					core_if->pwron_timer_started = 1;
+					DWC_TIMER_SCHEDULE(core_if->pwron_timer, 6000 /* 6 secs */ );
+				}
+#endif
+				/* Save gpwrdn register for further usage if stschng interrupt */
+				core_if->gr_backup->gpwrdn_local =
+						DWC_READ_REG32(&core_if->core_global_regs->gpwrdn);
+
+				/* Set flag to indicate that we are in hibernation */
+				core_if->hibernation_suspend = 1;
+				DWC_SPINUNLOCK_IRQRESTORE(dwc_otg_hcd->lock,flags);
+
+				DWC_PRINTF("Host hibernation completed\n");
+				// Exit from case statement
+				break;
+
+			}
+			if (dwc_otg_hcd_otg_port(dwc_otg_hcd) == wIndex &&
+			    dwc_otg_hcd->fops->get_b_hnp_enable(dwc_otg_hcd)) {
+				gotgctl_data_t gotgctl = {.d32 = 0 };
+				gotgctl.b.hstsethnpen = 1;
+				DWC_MODIFY_REG32(&core_if->core_global_regs->
+						 gotgctl, 0, gotgctl.d32);
+				core_if->op_state = A_SUSPEND;
+			}
+			hprt0.d32 = dwc_otg_read_hprt0(core_if);
+			hprt0.b.prtsusp = 1;
+			DWC_WRITE_REG32(core_if->host_if->hprt0, hprt0.d32);
+			{
+				dwc_irqflags_t flags;
+				/* Update lx_state */
+				DWC_SPINLOCK_IRQSAVE(dwc_otg_hcd->lock, &flags);
+				core_if->lx_state = DWC_OTG_L2;
+				DWC_SPINUNLOCK_IRQRESTORE(dwc_otg_hcd->lock, flags);
+			}
+			/* Suspend the Phy Clock */
+			{
+				pcgcctl_data_t pcgcctl = {.d32 = 0 };
+				pcgcctl.b.stoppclk = 1;
+				DWC_MODIFY_REG32(core_if->pcgcctl, 0,
+						 pcgcctl.d32);
+				dwc_udelay(10);
+			}
+
+			/* For HNP the bus must be suspended for at least 200ms. */
+			if (dwc_otg_hcd->fops->get_b_hnp_enable(dwc_otg_hcd)) {
+				pcgcctl_data_t pcgcctl = {.d32 = 0 };
+				pcgcctl.b.stoppclk = 1;
+                DWC_MODIFY_REG32(core_if->pcgcctl, pcgcctl.d32, 0);
+				dwc_mdelay(200);
+			}
+
+			/** @todo - check how sw can wait for 1 sec to check asesvld??? */
+#if 0 //vahrama !!!!!!!!!!!!!!!!!!
+			if (core_if->adp_enable) {
+				gotgctl_data_t gotgctl = {.d32 = 0 };
+				gpwrdn_data_t gpwrdn;
+
+				while (gotgctl.b.asesvld == 1) {
+					gotgctl.d32 =
+					    DWC_READ_REG32(&core_if->
+							   core_global_regs->
+							   gotgctl);
+					dwc_mdelay(100);
+				}
+
+				/* Enable Power Down Logic */
+				gpwrdn.d32 = 0;
+				gpwrdn.b.pmuactv = 1;
+				DWC_MODIFY_REG32(&core_if->core_global_regs->
+						 gpwrdn, 0, gpwrdn.d32);
+
+				/* Unmask SRP detected interrupt from Power Down Logic */
+				gpwrdn.d32 = 0;
+				gpwrdn.b.srp_det_msk = 1;
+				DWC_MODIFY_REG32(&core_if->core_global_regs->
+						 gpwrdn, 0, gpwrdn.d32);
+
+				dwc_otg_adp_probe_start(core_if);
+			}
+#endif
+			break;
+		case UHF_PORT_POWER:
+			DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD HUB CONTROL - "
+				    "SetPortFeature - USB_PORT_FEAT_POWER\n");
+			hprt0.d32 = dwc_otg_read_hprt0(core_if);
+			hprt0.b.prtpwr = 1;
+			DWC_WRITE_REG32(core_if->host_if->hprt0, hprt0.d32);
+			break;
+		case UHF_PORT_RESET:
+			if ((core_if->power_down == 2)
+			    && (core_if->hibernation_suspend == 1)) {
+				/* If we are going to exit from Hibernated
+				 * state via USB RESET.
+				 */
+				dwc_otg_host_hibernation_restore(core_if, 0, 1);
+			} else {
+				hprt0.d32 = dwc_otg_read_hprt0(core_if);
+
+				DWC_DEBUGPL(DBG_HCD,
+					    "DWC OTG HCD HUB CONTROL - "
+					    "SetPortFeature - USB_PORT_FEAT_RESET\n");
+				{
+					pcgcctl_data_t pcgcctl = {.d32 = 0 };
+					pcgcctl.b.enbl_sleep_gating = 1;
+					pcgcctl.b.stoppclk = 1;
+					DWC_MODIFY_REG32(core_if->pcgcctl, pcgcctl.d32, 0);
+					DWC_WRITE_REG32(core_if->pcgcctl, 0);
+				}
+#ifdef CONFIG_USB_DWC_OTG_LPM
+				{
+					glpmcfg_data_t lpmcfg;
+					lpmcfg.d32 =
+						DWC_READ_REG32(&core_if->core_global_regs->glpmcfg);
+					if (lpmcfg.b.prt_sleep_sts) {
+						lpmcfg.b.en_utmi_sleep = 0;
+						lpmcfg.b.hird_thres &= (~(1 << 4));
+						DWC_WRITE_REG32
+						    (&core_if->core_global_regs->glpmcfg,
+						     lpmcfg.d32);
+						dwc_mdelay(1);
+					}
+				}
+#endif
+				hprt0.d32 = dwc_otg_read_hprt0(core_if);
+				/* Clear suspend bit if resetting from suspended state. */
+				hprt0.b.prtsusp = 0;
+				/* When B-Host the Port reset bit is set in
+				 * the Start HCD Callback function, so that
+				 * the reset is started within 1ms of the HNP
+				 * success interrupt. */
+				if (!dwc_otg_hcd_is_b_host(dwc_otg_hcd)) {
+					hprt0.b.prtpwr = 1;
+					hprt0.b.prtrst = 1;
+					DWC_PRINTF("Indeed it is in host mode hprt0 = %08x\n",hprt0.d32);
+					DWC_WRITE_REG32(core_if->host_if->hprt0,
+							hprt0.d32);
+				}
+				/* Clear reset bit in 10ms (FS/LS) or 50ms (HS) */
+				dwc_mdelay(60);
+				hprt0.b.prtrst = 0;
+				DWC_WRITE_REG32(core_if->host_if->hprt0, hprt0.d32);
+				core_if->lx_state = DWC_OTG_L0;	/* Now back to the on state */
+			}
+			break;
+#ifdef DWC_HS_ELECT_TST
+		case UHF_PORT_TEST:
+			{
+				uint32_t t;
+				gintmsk_data_t gintmsk;
+
+				t = (wIndex >> 8);	/* MSB wIndex USB */
+				DWC_DEBUGPL(DBG_HCD,
+					    "DWC OTG HCD HUB CONTROL - "
+					    "SetPortFeature - USB_PORT_FEAT_TEST %d\n",
+					    t);
+				DWC_WARN("USB_PORT_FEAT_TEST %d\n", t);
+				if (t < 6) {
+					hprt0.d32 = dwc_otg_read_hprt0(core_if);
+					hprt0.b.prttstctl = t;
+					DWC_WRITE_REG32(core_if->host_if->hprt0,
+							hprt0.d32);
+				} else {
+					/* Setup global vars with reg addresses (quick and
+					 * dirty hack, should be cleaned up)
+					 */
+					global_regs = core_if->core_global_regs;
+					hc_global_regs =
+					    core_if->host_if->host_global_regs;
+					hc_regs =
+					    (dwc_otg_hc_regs_t *) ((char *)
+								   global_regs +
+								   0x500);
+					data_fifo =
+					    (uint32_t *) ((char *)global_regs +
+							  0x1000);
+
+					if (t == 6) {	/* HS_HOST_PORT_SUSPEND_RESUME */
+						/* Save current interrupt mask */
+						gintmsk.d32 =
+						    DWC_READ_REG32
+						    (&global_regs->gintmsk);
+
+						/* Disable all interrupts while we muck with
+						 * the hardware directly
+						 */
+						DWC_WRITE_REG32(&global_regs->gintmsk, 0);
+
+						/* 15 second delay per the test spec */
+						dwc_mdelay(15000);
+
+						/* Drive suspend on the root port */
+						hprt0.d32 =
+						    dwc_otg_read_hprt0(core_if);
+						hprt0.b.prtsusp = 1;
+						hprt0.b.prtres = 0;
+						DWC_WRITE_REG32(core_if->host_if->hprt0, hprt0.d32);
+
+						/* 15 second delay per the test spec */
+						dwc_mdelay(15000);
+
+						/* Drive resume on the root port */
+						hprt0.d32 =
+						    dwc_otg_read_hprt0(core_if);
+						hprt0.b.prtsusp = 0;
+						hprt0.b.prtres = 1;
+						DWC_WRITE_REG32(core_if->host_if->hprt0, hprt0.d32);
+						dwc_mdelay(100);
+
+						/* Clear the resume bit */
+						hprt0.b.prtres = 0;
+						DWC_WRITE_REG32(core_if->host_if->hprt0, hprt0.d32);
+
+						/* Restore interrupts */
+						DWC_WRITE_REG32(&global_regs->gintmsk, gintmsk.d32);
+					} else if (t == 7) {	/* SINGLE_STEP_GET_DEVICE_DESCRIPTOR setup */
+						/* Save current interrupt mask */
+						gintmsk.d32 =
+						    DWC_READ_REG32
+						    (&global_regs->gintmsk);
+
+						/* Disable all interrupts while we muck with
+						 * the hardware directly
+						 */
+						DWC_WRITE_REG32(&global_regs->gintmsk, 0);
+
+						/* 15 second delay per the test spec */
+						dwc_mdelay(15000);
+
+						/* Send the Setup packet */
+						do_setup();
+
+						/* 15 second delay so nothing else happens for awhile */
+						dwc_mdelay(15000);
+
+						/* Restore interrupts */
+						DWC_WRITE_REG32(&global_regs->gintmsk, gintmsk.d32);
+					} else if (t == 8) {	/* SINGLE_STEP_GET_DEVICE_DESCRIPTOR execute */
+						/* Save current interrupt mask */
+						gintmsk.d32 =
+						    DWC_READ_REG32
+						    (&global_regs->gintmsk);
+
+						/* Disable all interrupts while we muck with
+						 * the hardware directly
+						 */
+						DWC_WRITE_REG32(&global_regs->gintmsk, 0);
+
+						/* Send the Setup packet */
+						do_setup();
+
+						/* 15 second delay so nothing else happens for awhile */
+						dwc_mdelay(15000);
+
+						/* Send the In and Ack packets */
+						do_in_ack();
+
+						/* 15 second delay so nothing else happens for awhile */
+						dwc_mdelay(15000);
+
+						/* Restore interrupts */
+						DWC_WRITE_REG32(&global_regs->gintmsk, gintmsk.d32);
+					}
+				}
+				break;
+			}
+#endif /* DWC_HS_ELECT_TST */
+
+		case UHF_PORT_INDICATOR:
+			DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD HUB CONTROL - "
+				    "SetPortFeature - USB_PORT_FEAT_INDICATOR\n");
+			/* Not supported */
+			break;
+		default:
+			retval = -DWC_E_INVALID;
+			DWC_ERROR("DWC OTG HCD - "
+				  "SetPortFeature request %xh "
+				  "unknown or unsupported\n", wValue);
+			break;
+		}
+		break;
+#ifdef CONFIG_USB_DWC_OTG_LPM
+	case UCR_SET_AND_TEST_PORT_FEATURE:
+		if (wValue != UHF_PORT_L1) {
+			goto error;
+		}
+		{
+			int portnum, hird, devaddr, remwake;
+			glpmcfg_data_t lpmcfg;
+			uint32_t time_usecs;
+			gintsts_data_t gintsts;
+			gintmsk_data_t gintmsk;
+
+			if (!dwc_otg_get_param_lpm_enable(core_if)) {
+				goto error;
+			}
+			if (wValue != UHF_PORT_L1 || wLength != 1) {
+				goto error;
+			}
+			/* Check if the port currently is in SLEEP state */
+			lpmcfg.d32 =
+			    DWC_READ_REG32(&core_if->core_global_regs->glpmcfg);
+			if (lpmcfg.b.prt_sleep_sts) {
+				DWC_INFO("Port is already in sleep mode\n");
+				buf[0] = 0;	/* Return success */
+				break;
+			}
+
+			portnum = wIndex & 0xf;
+			hird = (wIndex >> 4) & 0xf;
+			devaddr = (wIndex >> 8) & 0x7f;
+			remwake = (wIndex >> 15);
+
+			if (portnum != 1) {
+				retval = -DWC_E_INVALID;
+				DWC_WARN
+				    ("Wrong port number(%d) in SetandTestPortFeature request\n",
+				     portnum);
+				break;
+			}
+
+			DWC_PRINTF
+			    ("SetandTestPortFeature request: portnum = %d, hird = %d, devaddr = %d, rewake = %d\n",
+			     portnum, hird, devaddr, remwake);
+			/* Disable LPM interrupt */
+			gintmsk.d32 = 0;
+			gintmsk.b.lpmtranrcvd = 1;
+			DWC_MODIFY_REG32(&core_if->core_global_regs->gintmsk,
+					 gintmsk.d32, 0);
+
+			if (dwc_otg_hcd_send_lpm
+			    (dwc_otg_hcd, devaddr, hird, remwake)) {
+				retval = -DWC_E_INVALID;
+				break;
+			}
+
+			time_usecs = 10 * (lpmcfg.b.retry_count + 1);
+			/* We will consider timeout if time_usecs microseconds pass,
+			 * and we don't receive LPM transaction status.
+			 * After receiving non-error responce(ACK/NYET/STALL) from device,
+			 *  core will set lpmtranrcvd bit.
+			 */
+			do {
+				gintsts.d32 =
+				    DWC_READ_REG32(&core_if->core_global_regs->gintsts);
+				if (gintsts.b.lpmtranrcvd) {
+					break;
+				}
+				dwc_udelay(1);
+			} while (--time_usecs);
+			/* lpm_int bit will be cleared in LPM interrupt handler */
+
+			/* Now fill status
+			 * 0x00 - Success
+			 * 0x10 - NYET
+			 * 0x11 - Timeout
+			 */
+			if (!gintsts.b.lpmtranrcvd) {
+				buf[0] = 0x3;	/* Completion code is Timeout */
+				dwc_otg_hcd_free_hc_from_lpm(dwc_otg_hcd);
+			} else {
+				lpmcfg.d32 =
+				    DWC_READ_REG32(&core_if->core_global_regs->glpmcfg);
+				if (lpmcfg.b.lpm_resp == 0x3) {
+					/* ACK responce from the device */
+					buf[0] = 0x00;	/* Success */
+				} else if (lpmcfg.b.lpm_resp == 0x2) {
+					/* NYET responce from the device */
+					buf[0] = 0x2;
+				} else {
+					/* Otherwise responce with Timeout */
+					buf[0] = 0x3;
+				}
+			}
+			DWC_PRINTF("Device responce to LPM trans is %x\n",
+				   lpmcfg.b.lpm_resp);
+			DWC_MODIFY_REG32(&core_if->core_global_regs->gintmsk, 0,
+					 gintmsk.d32);
+
+			break;
+		}
+#endif /* CONFIG_USB_DWC_OTG_LPM */
+	default:
+error:
+		retval = -DWC_E_INVALID;
+		DWC_WARN("DWC OTG HCD - "
+			 "Unknown hub control request type or invalid typeReq: %xh wIndex: %xh wValue: %xh\n",
+			 typeReq, wIndex, wValue);
+		break;
+	}
+
+	return retval;
+}
+
+#ifdef CONFIG_USB_DWC_OTG_LPM
+/** Returns index of host channel to perform LPM transaction. */
+int dwc_otg_hcd_get_hc_for_lpm_tran(dwc_otg_hcd_t * hcd, uint8_t devaddr)
+{
+	dwc_otg_core_if_t *core_if = hcd->core_if;
+	dwc_hc_t *hc;
+	hcchar_data_t hcchar;
+	gintmsk_data_t gintmsk = {.d32 = 0 };
+
+	if (DWC_CIRCLEQ_EMPTY(&hcd->free_hc_list)) {
+		DWC_PRINTF("No free channel to select for LPM transaction\n");
+		return -1;
+	}
+
+	hc = DWC_CIRCLEQ_FIRST(&hcd->free_hc_list);
+
+	/* Mask host channel interrupts. */
+	gintmsk.b.hcintr = 1;
+	DWC_MODIFY_REG32(&core_if->core_global_regs->gintmsk, gintmsk.d32, 0);
+
+	/* Fill fields that core needs for LPM transaction */
+	hcchar.b.devaddr = devaddr;
+	hcchar.b.epnum = 0;
+	hcchar.b.eptype = DWC_OTG_EP_TYPE_CONTROL;
+	hcchar.b.mps = 64;
+	hcchar.b.lspddev = (hc->speed == DWC_OTG_EP_SPEED_LOW);
+	hcchar.b.epdir = 0;	/* OUT */
+	DWC_WRITE_REG32(&core_if->host_if->hc_regs[hc->hc_num]->hcchar,
+			hcchar.d32);
+
+	/* Remove the host channel from the free list. */
+	DWC_CIRCLEQ_REMOVE_INIT(&hcd->free_hc_list, hc, hc_list_entry);
+
+	DWC_PRINTF("hcnum = %d devaddr = %d\n", hc->hc_num, devaddr);
+
+	return hc->hc_num;
+}
+
+/** Release hc after performing LPM transaction */
+void dwc_otg_hcd_free_hc_from_lpm(dwc_otg_hcd_t * hcd)
+{
+	dwc_hc_t *hc;
+	glpmcfg_data_t lpmcfg;
+	uint8_t hc_num;
+
+	lpmcfg.d32 = DWC_READ_REG32(&hcd->core_if->core_global_regs->glpmcfg);
+	hc_num = lpmcfg.b.lpm_chan_index;
+
+	hc = hcd->hc_ptr_array[hc_num];
+
+	DWC_PRINTF("Freeing channel %d after LPM\n", hc_num);
+	/* Return host channel to free list */
+	DWC_CIRCLEQ_INSERT_TAIL(&hcd->free_hc_list, hc, hc_list_entry);
+}
+
+int dwc_otg_hcd_send_lpm(dwc_otg_hcd_t * hcd, uint8_t devaddr, uint8_t hird,
+			 uint8_t bRemoteWake)
+{
+	glpmcfg_data_t lpmcfg;
+	pcgcctl_data_t pcgcctl = {.d32 = 0 };
+	int channel;
+
+	channel = dwc_otg_hcd_get_hc_for_lpm_tran(hcd, devaddr);
+	if (channel < 0) {
+		return channel;
+	}
+
+	pcgcctl.b.enbl_sleep_gating = 1;
+	DWC_MODIFY_REG32(hcd->core_if->pcgcctl, 0, pcgcctl.d32);
+
+	/* Read LPM config register */
+	lpmcfg.d32 = DWC_READ_REG32(&hcd->core_if->core_global_regs->glpmcfg);
+
+	/* Program LPM transaction fields */
+	lpmcfg.b.rem_wkup_en = bRemoteWake;
+	lpmcfg.b.hird = hird;
+	lpmcfg.b.hird_thres = 0x1c;
+	lpmcfg.b.lpm_chan_index = channel;
+	lpmcfg.b.en_utmi_sleep = 1;
+	/* Program LPM config register */
+	DWC_WRITE_REG32(&hcd->core_if->core_global_regs->glpmcfg, lpmcfg.d32);
+
+	/* Send LPM transaction */
+	lpmcfg.b.send_lpm = 1;
+	DWC_WRITE_REG32(&hcd->core_if->core_global_regs->glpmcfg, lpmcfg.d32);
+
+	return 0;
+}
+
+#endif /* CONFIG_USB_DWC_OTG_LPM */
+
+int dwc_otg_hcd_is_status_changed(dwc_otg_hcd_t * hcd, int port)
+{
+	int retval;
+
+	if (port != 1) {
+		return -DWC_E_INVALID;
+	}
+
+	retval = (hcd->flags.b.port_connect_status_change ||
+		  hcd->flags.b.port_reset_change ||
+		  hcd->flags.b.port_enable_change ||
+		  hcd->flags.b.port_suspend_change ||
+		  hcd->flags.b.port_over_current_change);
+#ifdef DEBUG
+	if (retval) {
+		DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD HUB STATUS DATA:"
+			    " Root port status changed\n");
+		DWC_DEBUGPL(DBG_HCDV, "  port_connect_status_change: %d\n",
+			    hcd->flags.b.port_connect_status_change);
+		DWC_DEBUGPL(DBG_HCDV, "  port_reset_change: %d\n",
+			    hcd->flags.b.port_reset_change);
+		DWC_DEBUGPL(DBG_HCDV, "  port_enable_change: %d\n",
+			    hcd->flags.b.port_enable_change);
+		DWC_DEBUGPL(DBG_HCDV, "  port_suspend_change: %d\n",
+			    hcd->flags.b.port_suspend_change);
+		DWC_DEBUGPL(DBG_HCDV, "  port_over_current_change: %d\n",
+			    hcd->flags.b.port_over_current_change);
+	}
+#endif
+	return retval;
+}
+
+int dwc_otg_hcd_get_frame_number(dwc_otg_hcd_t * dwc_otg_hcd)
+{
+	hfnum_data_t hfnum;
+	hfnum.d32 =
+	    DWC_READ_REG32(&dwc_otg_hcd->core_if->host_if->host_global_regs->
+			   hfnum);
+
+#ifdef DEBUG_SOF
+	DWC_DEBUGPL(DBG_HCDV, "DWC OTG HCD GET FRAME NUMBER %d\n",
+		    hfnum.b.frnum);
+#endif
+	return hfnum.b.frnum;
+}
+
+int dwc_otg_hcd_start(dwc_otg_hcd_t * hcd,
+		      struct dwc_otg_hcd_function_ops *fops)
+{
+	int retval = 0;
+
+	hcd->fops = fops;
+	if (!dwc_otg_is_device_mode(hcd->core_if) &&
+		(!hcd->core_if->adp_enable || hcd->core_if->adp.adp_started)) {
+		dwc_otg_hcd_reinit(hcd);
+	} else {
+		retval = -DWC_E_NO_DEVICE;
+	}
+
+	return retval;
+}
+
+void *dwc_otg_hcd_get_priv_data(dwc_otg_hcd_t * hcd)
+{
+	return hcd->priv;
+}
+
+void dwc_otg_hcd_set_priv_data(dwc_otg_hcd_t * hcd, void *priv_data)
+{
+	hcd->priv = priv_data;
+}
+
+uint32_t dwc_otg_hcd_otg_port(dwc_otg_hcd_t * hcd)
+{
+	return hcd->otg_port;
+}
+
+uint32_t dwc_otg_hcd_is_b_host(dwc_otg_hcd_t * hcd)
+{
+	uint32_t is_b_host;
+	if (hcd->core_if->op_state == B_HOST) {
+		is_b_host = 1;
+	} else {
+		is_b_host = 0;
+	}
+
+	return is_b_host;
+}
+
+dwc_otg_hcd_urb_t *dwc_otg_hcd_urb_alloc(dwc_otg_hcd_t * hcd,
+					 int iso_desc_count, int atomic_alloc)
+{
+	dwc_otg_hcd_urb_t *dwc_otg_urb;
+	uint32_t size;
+
+	size =
+	    sizeof(*dwc_otg_urb) +
+	    iso_desc_count * sizeof(struct dwc_otg_hcd_iso_packet_desc);
+	if (atomic_alloc)
+		dwc_otg_urb = DWC_ALLOC_ATOMIC(size);
+	else
+		dwc_otg_urb = DWC_ALLOC(size);
+
+        if (dwc_otg_urb)
+		dwc_otg_urb->packet_count = iso_desc_count;
+        else {
+		DWC_ERROR("**** DWC OTG HCD URB alloc - "
+			"%salloc of %db failed\n",
+			atomic_alloc?"atomic ":"", size);
+	}
+	return dwc_otg_urb;
+}
+
+void dwc_otg_hcd_urb_set_pipeinfo(dwc_otg_hcd_urb_t * dwc_otg_urb,
+				  uint8_t dev_addr, uint8_t ep_num,
+				  uint8_t ep_type, uint8_t ep_dir, uint16_t mps)
+{
+	dwc_otg_hcd_fill_pipe(&dwc_otg_urb->pipe_info, dev_addr, ep_num,
+			      ep_type, ep_dir, mps);
+#if 0
+	DWC_PRINTF
+	    ("addr = %d, ep_num = %d, ep_dir = 0x%x, ep_type = 0x%x, mps = %d\n",
+	     dev_addr, ep_num, ep_dir, ep_type, mps);
+#endif
+}
+
+void dwc_otg_hcd_urb_set_params(dwc_otg_hcd_urb_t * dwc_otg_urb,
+				void *urb_handle, void *buf, dwc_dma_t dma,
+				uint32_t buflen, void *setup_packet,
+				dwc_dma_t setup_dma, uint32_t flags,
+				uint16_t interval)
+{
+	dwc_otg_urb->priv = urb_handle;
+	dwc_otg_urb->buf = buf;
+	dwc_otg_urb->dma = dma;
+	dwc_otg_urb->length = buflen;
+	dwc_otg_urb->setup_packet = setup_packet;
+	dwc_otg_urb->setup_dma = setup_dma;
+	dwc_otg_urb->flags = flags;
+	dwc_otg_urb->interval = interval;
+	dwc_otg_urb->status = -DWC_E_IN_PROGRESS;
+}
+
+uint32_t dwc_otg_hcd_urb_get_status(dwc_otg_hcd_urb_t * dwc_otg_urb)
+{
+	return dwc_otg_urb->status;
+}
+
+uint32_t dwc_otg_hcd_urb_get_actual_length(dwc_otg_hcd_urb_t * dwc_otg_urb)
+{
+	return dwc_otg_urb->actual_length;
+}
+
+uint32_t dwc_otg_hcd_urb_get_error_count(dwc_otg_hcd_urb_t * dwc_otg_urb)
+{
+	return dwc_otg_urb->error_count;
+}
+
+void dwc_otg_hcd_urb_set_iso_desc_params(dwc_otg_hcd_urb_t * dwc_otg_urb,
+					 int desc_num, uint32_t offset,
+					 uint32_t length)
+{
+	dwc_otg_urb->iso_descs[desc_num].offset = offset;
+	dwc_otg_urb->iso_descs[desc_num].length = length;
+}
+
+uint32_t dwc_otg_hcd_urb_get_iso_desc_status(dwc_otg_hcd_urb_t * dwc_otg_urb,
+					     int desc_num)
+{
+	return dwc_otg_urb->iso_descs[desc_num].status;
+}
+
+uint32_t dwc_otg_hcd_urb_get_iso_desc_actual_length(dwc_otg_hcd_urb_t *
+						    dwc_otg_urb, int desc_num)
+{
+	return dwc_otg_urb->iso_descs[desc_num].actual_length;
+}
+
+int dwc_otg_hcd_is_bandwidth_allocated(dwc_otg_hcd_t * hcd, void *ep_handle)
+{
+	int allocated = 0;
+	dwc_otg_qh_t *qh = (dwc_otg_qh_t *) ep_handle;
+
+	if (qh) {
+		if (!DWC_LIST_EMPTY(&qh->qh_list_entry)) {
+			allocated = 1;
+		}
+	}
+	return allocated;
+}
+
+int dwc_otg_hcd_is_bandwidth_freed(dwc_otg_hcd_t * hcd, void *ep_handle)
+{
+	dwc_otg_qh_t *qh = (dwc_otg_qh_t *) ep_handle;
+	int freed = 0;
+	DWC_ASSERT(qh, "qh is not allocated\n");
+
+	if (DWC_LIST_EMPTY(&qh->qh_list_entry)) {
+		freed = 1;
+	}
+
+	return freed;
+}
+
+uint8_t dwc_otg_hcd_get_ep_bandwidth(dwc_otg_hcd_t * hcd, void *ep_handle)
+{
+	dwc_otg_qh_t *qh = (dwc_otg_qh_t *) ep_handle;
+	DWC_ASSERT(qh, "qh is not allocated\n");
+	return qh->usecs;
+}
+
+void dwc_otg_hcd_dump_state(dwc_otg_hcd_t * hcd)
+{
+#ifdef DEBUG
+	int num_channels;
+	int i;
+	gnptxsts_data_t np_tx_status;
+	hptxsts_data_t p_tx_status;
+
+	num_channels = hcd->core_if->core_params->host_channels;
+	DWC_PRINTF("\n");
+	DWC_PRINTF
+	    ("************************************************************\n");
+	DWC_PRINTF("HCD State:\n");
+	DWC_PRINTF("  Num channels: %d\n", num_channels);
+	for (i = 0; i < num_channels; i++) {
+		dwc_hc_t *hc = hcd->hc_ptr_array[i];
+		DWC_PRINTF("  Channel %d:\n", i);
+		DWC_PRINTF("    dev_addr: %d, ep_num: %d, ep_is_in: %d\n",
+			   hc->dev_addr, hc->ep_num, hc->ep_is_in);
+		DWC_PRINTF("    speed: %d\n", hc->speed);
+		DWC_PRINTF("    ep_type: %d\n", hc->ep_type);
+		DWC_PRINTF("    max_packet: %d\n", hc->max_packet);
+		DWC_PRINTF("    data_pid_start: %d\n", hc->data_pid_start);
+		DWC_PRINTF("    multi_count: %d\n", hc->multi_count);
+		DWC_PRINTF("    xfer_started: %d\n", hc->xfer_started);
+		DWC_PRINTF("    xfer_buff: %p\n", hc->xfer_buff);
+		DWC_PRINTF("    xfer_len: %d\n", hc->xfer_len);
+		DWC_PRINTF("    xfer_count: %d\n", hc->xfer_count);
+		DWC_PRINTF("    halt_on_queue: %d\n", hc->halt_on_queue);
+		DWC_PRINTF("    halt_pending: %d\n", hc->halt_pending);
+		DWC_PRINTF("    halt_status: %d\n", hc->halt_status);
+		DWC_PRINTF("    do_split: %d\n", hc->do_split);
+		DWC_PRINTF("    complete_split: %d\n", hc->complete_split);
+		DWC_PRINTF("    hub_addr: %d\n", hc->hub_addr);
+		DWC_PRINTF("    port_addr: %d\n", hc->port_addr);
+		DWC_PRINTF("    xact_pos: %d\n", hc->xact_pos);
+		DWC_PRINTF("    requests: %d\n", hc->requests);
+		DWC_PRINTF("    qh: %p\n", hc->qh);
+		if (hc->xfer_started) {
+			hfnum_data_t hfnum;
+			hcchar_data_t hcchar;
+			hctsiz_data_t hctsiz;
+			hcint_data_t hcint;
+			hcintmsk_data_t hcintmsk;
+			hfnum.d32 =
+			    DWC_READ_REG32(&hcd->core_if->
+					   host_if->host_global_regs->hfnum);
+			hcchar.d32 =
+			    DWC_READ_REG32(&hcd->core_if->host_if->
+					   hc_regs[i]->hcchar);
+			hctsiz.d32 =
+			    DWC_READ_REG32(&hcd->core_if->host_if->
+					   hc_regs[i]->hctsiz);
+			hcint.d32 =
+			    DWC_READ_REG32(&hcd->core_if->host_if->
+					   hc_regs[i]->hcint);
+			hcintmsk.d32 =
+			    DWC_READ_REG32(&hcd->core_if->host_if->
+					   hc_regs[i]->hcintmsk);
+			DWC_PRINTF("    hfnum: 0x%08x\n", hfnum.d32);
+			DWC_PRINTF("    hcchar: 0x%08x\n", hcchar.d32);
+			DWC_PRINTF("    hctsiz: 0x%08x\n", hctsiz.d32);
+			DWC_PRINTF("    hcint: 0x%08x\n", hcint.d32);
+			DWC_PRINTF("    hcintmsk: 0x%08x\n", hcintmsk.d32);
+		}
+		if (hc->xfer_started && hc->qh) {
+			dwc_otg_qtd_t *qtd;
+			dwc_otg_hcd_urb_t *urb;
+
+			DWC_CIRCLEQ_FOREACH(qtd, &hc->qh->qtd_list, qtd_list_entry) {
+				if (!qtd->in_process)
+					break;
+
+				urb = qtd->urb;
+			DWC_PRINTF("    URB Info:\n");
+			DWC_PRINTF("      qtd: %p, urb: %p\n", qtd, urb);
+			if (urb) {
+				DWC_PRINTF("      Dev: %d, EP: %d %s\n",
+					   dwc_otg_hcd_get_dev_addr(&urb->
+								    pipe_info),
+					   dwc_otg_hcd_get_ep_num(&urb->
+								  pipe_info),
+					   dwc_otg_hcd_is_pipe_in(&urb->
+								  pipe_info) ?
+					   "IN" : "OUT");
+				DWC_PRINTF("      Max packet size: %d\n",
+					   dwc_otg_hcd_get_mps(&urb->
+							       pipe_info));
+				DWC_PRINTF("      transfer_buffer: %p\n",
+					   urb->buf);
+				DWC_PRINTF("      transfer_dma: %p\n",
+					   (void *)urb->dma);
+				DWC_PRINTF("      transfer_buffer_length: %d\n",
+					   urb->length);
+					DWC_PRINTF("      actual_length: %d\n",
+						   urb->actual_length);
+				}
+			}
+		}
+	}
+	DWC_PRINTF("  non_periodic_channels: %d\n", hcd->non_periodic_channels);
+	DWC_PRINTF("  periodic_channels: %d\n", hcd->periodic_channels);
+	DWC_PRINTF("  periodic_usecs: %d\n", hcd->periodic_usecs);
+	np_tx_status.d32 =
+	    DWC_READ_REG32(&hcd->core_if->core_global_regs->gnptxsts);
+	DWC_PRINTF("  NP Tx Req Queue Space Avail: %d\n",
+		   np_tx_status.b.nptxqspcavail);
+	DWC_PRINTF("  NP Tx FIFO Space Avail: %d\n",
+		   np_tx_status.b.nptxfspcavail);
+	p_tx_status.d32 =
+	    DWC_READ_REG32(&hcd->core_if->host_if->host_global_regs->hptxsts);
+	DWC_PRINTF("  P Tx Req Queue Space Avail: %d\n",
+		   p_tx_status.b.ptxqspcavail);
+	DWC_PRINTF("  P Tx FIFO Space Avail: %d\n", p_tx_status.b.ptxfspcavail);
+	dwc_otg_hcd_dump_frrem(hcd);
+	dwc_otg_dump_global_registers(hcd->core_if);
+	dwc_otg_dump_host_registers(hcd->core_if);
+	DWC_PRINTF
+	    ("************************************************************\n");
+	DWC_PRINTF("\n");
+#endif
+}
+
+#ifdef DEBUG
+void dwc_print_setup_data(uint8_t * setup)
+{
+	int i;
+	if (CHK_DEBUG_LEVEL(DBG_HCD)) {
+		DWC_PRINTF("Setup Data = MSB ");
+		for (i = 7; i >= 0; i--)
+			DWC_PRINTF("%02x ", setup[i]);
+		DWC_PRINTF("\n");
+		DWC_PRINTF("  bmRequestType Tranfer = %s\n",
+			   (setup[0] & 0x80) ? "Device-to-Host" :
+			   "Host-to-Device");
+		DWC_PRINTF("  bmRequestType Type = ");
+		switch ((setup[0] & 0x60) >> 5) {
+		case 0:
+			DWC_PRINTF("Standard\n");
+			break;
+		case 1:
+			DWC_PRINTF("Class\n");
+			break;
+		case 2:
+			DWC_PRINTF("Vendor\n");
+			break;
+		case 3:
+			DWC_PRINTF("Reserved\n");
+			break;
+		}
+		DWC_PRINTF("  bmRequestType Recipient = ");
+		switch (setup[0] & 0x1f) {
+		case 0:
+			DWC_PRINTF("Device\n");
+			break;
+		case 1:
+			DWC_PRINTF("Interface\n");
+			break;
+		case 2:
+			DWC_PRINTF("Endpoint\n");
+			break;
+		case 3:
+			DWC_PRINTF("Other\n");
+			break;
+		default:
+			DWC_PRINTF("Reserved\n");
+			break;
+		}
+		DWC_PRINTF("  bRequest = 0x%0x\n", setup[1]);
+		DWC_PRINTF("  wValue = 0x%0x\n", *((uint16_t *) & setup[2]));
+		DWC_PRINTF("  wIndex = 0x%0x\n", *((uint16_t *) & setup[4]));
+		DWC_PRINTF("  wLength = 0x%0x\n\n", *((uint16_t *) & setup[6]));
+	}
+}
+#endif
+
+void dwc_otg_hcd_dump_frrem(dwc_otg_hcd_t * hcd)
+{
+#if 0
+	DWC_PRINTF("Frame remaining at SOF:\n");
+	DWC_PRINTF("  samples %u, accum %llu, avg %llu\n",
+		   hcd->frrem_samples, hcd->frrem_accum,
+		   (hcd->frrem_samples > 0) ?
+		   hcd->frrem_accum / hcd->frrem_samples : 0);
+
+	DWC_PRINTF("\n");
+	DWC_PRINTF("Frame remaining at start_transfer (uframe 7):\n");
+	DWC_PRINTF("  samples %u, accum %llu, avg %llu\n",
+		   hcd->core_if->hfnum_7_samples,
+		   hcd->core_if->hfnum_7_frrem_accum,
+		   (hcd->core_if->hfnum_7_samples >
+		    0) ? hcd->core_if->hfnum_7_frrem_accum /
+		   hcd->core_if->hfnum_7_samples : 0);
+	DWC_PRINTF("Frame remaining at start_transfer (uframe 0):\n");
+	DWC_PRINTF("  samples %u, accum %llu, avg %llu\n",
+		   hcd->core_if->hfnum_0_samples,
+		   hcd->core_if->hfnum_0_frrem_accum,
+		   (hcd->core_if->hfnum_0_samples >
+		    0) ? hcd->core_if->hfnum_0_frrem_accum /
+		   hcd->core_if->hfnum_0_samples : 0);
+	DWC_PRINTF("Frame remaining at start_transfer (uframe 1-6):\n");
+	DWC_PRINTF("  samples %u, accum %llu, avg %llu\n",
+		   hcd->core_if->hfnum_other_samples,
+		   hcd->core_if->hfnum_other_frrem_accum,
+		   (hcd->core_if->hfnum_other_samples >
+		    0) ? hcd->core_if->hfnum_other_frrem_accum /
+		   hcd->core_if->hfnum_other_samples : 0);
+
+	DWC_PRINTF("\n");
+	DWC_PRINTF("Frame remaining at sample point A (uframe 7):\n");
+	DWC_PRINTF("  samples %u, accum %llu, avg %llu\n",
+		   hcd->hfnum_7_samples_a, hcd->hfnum_7_frrem_accum_a,
+		   (hcd->hfnum_7_samples_a > 0) ?
+		   hcd->hfnum_7_frrem_accum_a / hcd->hfnum_7_samples_a : 0);
+	DWC_PRINTF("Frame remaining at sample point A (uframe 0):\n");
+	DWC_PRINTF("  samples %u, accum %llu, avg %llu\n",
+		   hcd->hfnum_0_samples_a, hcd->hfnum_0_frrem_accum_a,
+		   (hcd->hfnum_0_samples_a > 0) ?
+		   hcd->hfnum_0_frrem_accum_a / hcd->hfnum_0_samples_a : 0);
+	DWC_PRINTF("Frame remaining at sample point A (uframe 1-6):\n");
+	DWC_PRINTF("  samples %u, accum %llu, avg %llu\n",
+		   hcd->hfnum_other_samples_a, hcd->hfnum_other_frrem_accum_a,
+		   (hcd->hfnum_other_samples_a > 0) ?
+		   hcd->hfnum_other_frrem_accum_a /
+		   hcd->hfnum_other_samples_a : 0);
+
+	DWC_PRINTF("\n");
+	DWC_PRINTF("Frame remaining at sample point B (uframe 7):\n");
+	DWC_PRINTF("  samples %u, accum %llu, avg %llu\n",
+		   hcd->hfnum_7_samples_b, hcd->hfnum_7_frrem_accum_b,
+		   (hcd->hfnum_7_samples_b > 0) ?
+		   hcd->hfnum_7_frrem_accum_b / hcd->hfnum_7_samples_b : 0);
+	DWC_PRINTF("Frame remaining at sample point B (uframe 0):\n");
+	DWC_PRINTF("  samples %u, accum %llu, avg %llu\n",
+		   hcd->hfnum_0_samples_b, hcd->hfnum_0_frrem_accum_b,
+		   (hcd->hfnum_0_samples_b > 0) ?
+		   hcd->hfnum_0_frrem_accum_b / hcd->hfnum_0_samples_b : 0);
+	DWC_PRINTF("Frame remaining at sample point B (uframe 1-6):\n");
+	DWC_PRINTF("  samples %u, accum %llu, avg %llu\n",
+		   hcd->hfnum_other_samples_b, hcd->hfnum_other_frrem_accum_b,
+		   (hcd->hfnum_other_samples_b > 0) ?
+		   hcd->hfnum_other_frrem_accum_b /
+		   hcd->hfnum_other_samples_b : 0);
+#endif
+}
+
+#endif /* DWC_DEVICE_ONLY */
diff -Nur --no-dereference linux-5.10.52-orig/drivers/usb/host/dwc_otg/dwc_otg_hcd_ddma.c linux-5.10.52-v7l+/drivers/usb/host/dwc_otg/dwc_otg_hcd_ddma.c
--- linux-5.10.52-orig/drivers/usb/host/dwc_otg/dwc_otg_hcd_ddma.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/usb/host/dwc_otg/dwc_otg_hcd_ddma.c	2021-07-25 16:46:28.697909777 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/*==========================================================================
+ * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_hcd_ddma.c $
+ * $Revision: #10 $
+ * $Date: 2011/10/20 $
+ * $Change: 1869464 $
+ *
+ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter,
+ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless
+ * otherwise expressly agreed to in writing between Synopsys and you.
+ *
+ * The Software IS NOT an item of Licensed Software or Licensed Product under
+ * any End User Software License Agreement or Agreement for Licensed Product
+ * with Synopsys or any supplement thereto. You are permitted to use and
+ * redistribute this Software in source and binary forms, with or without
+ * modification, provided that redistributions of source code must retain this
+ * notice. You may not view, use, disclose, copy or distribute this file or
+ * any information contained herein except pursuant to this license grant from
+ * Synopsys. If you do not agree with this notice, including the disclaimer
+ * below, then you are not authorized to use the Software.
+ *
+ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * ========================================================================== */
+#ifndef DWC_DEVICE_ONLY
+
+/** @file
+ * This file contains Descriptor DMA support implementation for host mode.
+ */
+
+#include "dwc_otg_hcd.h"
+#include "dwc_otg_regs.h"
+
+extern bool microframe_schedule;
+
+static inline uint8_t frame_list_idx(uint16_t frame)
+{
+	return (frame & (MAX_FRLIST_EN_NUM - 1));
+}
+
+static inline uint16_t desclist_idx_inc(uint16_t idx, uint16_t inc, uint8_t speed)
+{
+	return (idx + inc) &
+	    (((speed ==
+	       DWC_OTG_EP_SPEED_HIGH) ? MAX_DMA_DESC_NUM_HS_ISOC :
+	      MAX_DMA_DESC_NUM_GENERIC) - 1);
+}
+
+static inline uint16_t desclist_idx_dec(uint16_t idx, uint16_t inc, uint8_t speed)
+{
+	return (idx - inc) &
+	    (((speed ==
+	       DWC_OTG_EP_SPEED_HIGH) ? MAX_DMA_DESC_NUM_HS_ISOC :
+	      MAX_DMA_DESC_NUM_GENERIC) - 1);
+}
+
+static inline uint16_t max_desc_num(dwc_otg_qh_t * qh)
+{
+	return (((qh->ep_type == UE_ISOCHRONOUS)
+		 && (qh->dev_speed == DWC_OTG_EP_SPEED_HIGH))
+		? MAX_DMA_DESC_NUM_HS_ISOC : MAX_DMA_DESC_NUM_GENERIC);
+}
+static inline uint16_t frame_incr_val(dwc_otg_qh_t * qh)
+{
+	return ((qh->dev_speed == DWC_OTG_EP_SPEED_HIGH)
+		? ((qh->interval + 8 - 1) / 8)
+		: qh->interval);
+}
+
+static int desc_list_alloc(struct device *dev, dwc_otg_qh_t * qh)
+{
+	int retval = 0;
+
+	qh->desc_list = (dwc_otg_host_dma_desc_t *)
+	    DWC_DMA_ALLOC(dev, sizeof(dwc_otg_host_dma_desc_t) * max_desc_num(qh),
+			  &qh->desc_list_dma);
+
+	if (!qh->desc_list) {
+		retval = -DWC_E_NO_MEMORY;
+		DWC_ERROR("%s: DMA descriptor list allocation failed\n", __func__);
+
+	}
+
+	dwc_memset(qh->desc_list, 0x00,
+		   sizeof(dwc_otg_host_dma_desc_t) * max_desc_num(qh));
+
+	qh->n_bytes =
+	    (uint32_t *) DWC_ALLOC(sizeof(uint32_t) * max_desc_num(qh));
+
+	if (!qh->n_bytes) {
+		retval = -DWC_E_NO_MEMORY;
+		DWC_ERROR
+		    ("%s: Failed to allocate array for descriptors' size actual values\n",
+		     __func__);
+
+	}
+	return retval;
+
+}
+
+static void desc_list_free(struct device *dev, dwc_otg_qh_t * qh)
+{
+	if (qh->desc_list) {
+		DWC_DMA_FREE(dev, max_desc_num(qh), qh->desc_list,
+			     qh->desc_list_dma);
+		qh->desc_list = NULL;
+	}
+
+	if (qh->n_bytes) {
+		DWC_FREE(qh->n_bytes);
+		qh->n_bytes = NULL;
+	}
+}
+
+static int frame_list_alloc(dwc_otg_hcd_t * hcd)
+{
+	struct device *dev = dwc_otg_hcd_to_dev(hcd);
+	int retval = 0;
+
+	if (hcd->frame_list)
+		return 0;
+
+	hcd->frame_list = DWC_DMA_ALLOC(dev, 4 * MAX_FRLIST_EN_NUM,
+					&hcd->frame_list_dma);
+	if (!hcd->frame_list) {
+		retval = -DWC_E_NO_MEMORY;
+		DWC_ERROR("%s: Frame List allocation failed\n", __func__);
+	}
+
+	dwc_memset(hcd->frame_list, 0x00, 4 * MAX_FRLIST_EN_NUM);
+
+	return retval;
+}
+
+static void frame_list_free(dwc_otg_hcd_t * hcd)
+{
+	struct device *dev = dwc_otg_hcd_to_dev(hcd);
+
+	if (!hcd->frame_list)
+		return;
+
+	DWC_DMA_FREE(dev, 4 * MAX_FRLIST_EN_NUM, hcd->frame_list, hcd->frame_list_dma);
+	hcd->frame_list = NULL;
+}
+
+static void per_sched_enable(dwc_otg_hcd_t * hcd, uint16_t fr_list_en)
+{
+
+	hcfg_data_t hcfg;
+
+	hcfg.d32 = DWC_READ_REG32(&hcd->core_if->host_if->host_global_regs->hcfg);
+
+	if (hcfg.b.perschedena) {
+		/* already enabled */
+		return;
+	}
+
+	DWC_WRITE_REG32(&hcd->core_if->host_if->host_global_regs->hflbaddr,
+			hcd->frame_list_dma);
+
+	switch (fr_list_en) {
+	case 64:
+		hcfg.b.frlisten = 3;
+		break;
+	case 32:
+		hcfg.b.frlisten = 2;
+		break;
+	case 16:
+		hcfg.b.frlisten = 1;
+		break;
+	case 8:
+		hcfg.b.frlisten = 0;
+		break;
+	default:
+		break;
+	}
+
+	hcfg.b.perschedena = 1;
+
+	DWC_DEBUGPL(DBG_HCD, "Enabling Periodic schedule\n");
+	DWC_WRITE_REG32(&hcd->core_if->host_if->host_global_regs->hcfg, hcfg.d32);
+
+}
+
+static void per_sched_disable(dwc_otg_hcd_t * hcd)
+{
+	hcfg_data_t hcfg;
+
+	hcfg.d32 = DWC_READ_REG32(&hcd->core_if->host_if->host_global_regs->hcfg);
+
+	if (!hcfg.b.perschedena) {
+		/* already disabled */
+		return;
+	}
+	hcfg.b.perschedena = 0;
+
+	DWC_DEBUGPL(DBG_HCD, "Disabling Periodic schedule\n");
+	DWC_WRITE_REG32(&hcd->core_if->host_if->host_global_regs->hcfg, hcfg.d32);
+}
+
+/*
+ * Activates/Deactivates FrameList entries for the channel
+ * based on endpoint servicing period.
+ */
+void update_frame_list(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh, uint8_t enable)
+{
+	uint16_t i, j, inc;
+	dwc_hc_t *hc = NULL;
+
+	if (!qh->channel) {
+		DWC_ERROR("qh->channel = %p", qh->channel);
+		return;
+	}
+
+	if (!hcd) {
+		DWC_ERROR("------hcd = %p", hcd);
+		return;
+	}
+
+	if (!hcd->frame_list) {
+		DWC_ERROR("-------hcd->frame_list = %p", hcd->frame_list);
+		return;
+	}
+
+	hc = qh->channel;
+	inc = frame_incr_val(qh);
+	if (qh->ep_type == UE_ISOCHRONOUS)
+		i = frame_list_idx(qh->sched_frame);
+	else
+		i = 0;
+
+	j = i;
+	do {
+		if (enable)
+			hcd->frame_list[j] |= (1 << hc->hc_num);
+		else
+			hcd->frame_list[j] &= ~(1 << hc->hc_num);
+		j = (j + inc) & (MAX_FRLIST_EN_NUM - 1);
+	}
+	while (j != i);
+	if (!enable)
+		return;
+	hc->schinfo = 0;
+	if (qh->channel->speed == DWC_OTG_EP_SPEED_HIGH) {
+		j = 1;
+		/* TODO - check this */
+		inc = (8 + qh->interval - 1) / qh->interval;
+		for (i = 0; i < inc; i++) {
+			hc->schinfo |= j;
+			j = j << qh->interval;
+		}
+	} else {
+		hc->schinfo = 0xff;
+	}
+}
+
+#if 1
+void dump_frame_list(dwc_otg_hcd_t * hcd)
+{
+	int i = 0;
+	DWC_PRINTF("--FRAME LIST (hex) --\n");
+	for (i = 0; i < MAX_FRLIST_EN_NUM; i++) {
+		DWC_PRINTF("%x\t", hcd->frame_list[i]);
+		if (!(i % 8) && i)
+			DWC_PRINTF("\n");
+	}
+	DWC_PRINTF("\n----\n");
+
+}
+#endif
+
+static void release_channel_ddma(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh)
+{
+	dwc_hc_t *hc = qh->channel;
+	if (dwc_qh_is_non_per(qh)) {
+		if (!microframe_schedule)
+			hcd->non_periodic_channels--;
+		else
+			hcd->available_host_channels++;
+	} else
+		update_frame_list(hcd, qh, 0);
+
+	/*
+	 * The condition is added to prevent double cleanup try in case of device
+	 * disconnect. See channel cleanup in dwc_otg_hcd_disconnect_cb().
+	 */
+	if (hc->qh) {
+		dwc_otg_hc_cleanup(hcd->core_if, hc);
+		DWC_CIRCLEQ_INSERT_TAIL(&hcd->free_hc_list, hc, hc_list_entry);
+		hc->qh = NULL;
+	}
+
+	qh->channel = NULL;
+	qh->ntd = 0;
+
+	if (qh->desc_list) {
+		dwc_memset(qh->desc_list, 0x00,
+			   sizeof(dwc_otg_host_dma_desc_t) * max_desc_num(qh));
+	}
+}
+
+/**
+ * Initializes a QH structure's Descriptor DMA related members.
+ * Allocates memory for descriptor list.
+ * On first periodic QH, allocates memory for FrameList
+ * and enables periodic scheduling.
+ *
+ * @param hcd The HCD state structure for the DWC OTG controller.
+ * @param qh The QH to init.
+ *
+ * @return 0 if successful, negative error code otherwise.
+ */
+int dwc_otg_hcd_qh_init_ddma(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh)
+{
+	struct device *dev = dwc_otg_hcd_to_dev(hcd);
+	int retval = 0;
+
+	if (qh->do_split) {
+		DWC_ERROR("SPLIT Transfers are not supported in Descriptor DMA.\n");
+		return -1;
+	}
+
+	retval = desc_list_alloc(dev, qh);
+
+	if ((retval == 0)
+	    && (qh->ep_type == UE_ISOCHRONOUS || qh->ep_type == UE_INTERRUPT)) {
+		if (!hcd->frame_list) {
+			retval = frame_list_alloc(hcd);
+			/* Enable periodic schedule on first periodic QH */
+			if (retval == 0)
+				per_sched_enable(hcd, MAX_FRLIST_EN_NUM);
+		}
+	}
+
+	qh->ntd = 0;
+
+	return retval;
+}
+
+/**
+ * Frees descriptor list memory associated with the QH.
+ * If QH is periodic and the last, frees FrameList memory
+ * and disables periodic scheduling.
+ *
+ * @param hcd The HCD state structure for the DWC OTG controller.
+ * @param qh The QH to init.
+ */
+void dwc_otg_hcd_qh_free_ddma(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh)
+{
+	struct device *dev = dwc_otg_hcd_to_dev(hcd);
+
+	desc_list_free(dev, qh);
+
+	/*
+	 * Channel still assigned due to some reasons.
+	 * Seen on Isoc URB dequeue. Channel halted but no subsequent
+	 * ChHalted interrupt to release the channel. Afterwards
+	 * when it comes here from endpoint disable routine
+	 * channel remains assigned.
+	 */
+	if (qh->channel)
+		release_channel_ddma(hcd, qh);
+
+	if ((qh->ep_type == UE_ISOCHRONOUS || qh->ep_type == UE_INTERRUPT)
+	    && (microframe_schedule || !hcd->periodic_channels) && hcd->frame_list) {
+
+		per_sched_disable(hcd);
+		frame_list_free(hcd);
+	}
+}
+
+static uint8_t frame_to_desc_idx(dwc_otg_qh_t * qh, uint16_t frame_idx)
+{
+	if (qh->dev_speed == DWC_OTG_EP_SPEED_HIGH) {
+		/*
+		 * Descriptor set(8 descriptors) index
+		 * which is 8-aligned.
+		 */
+		return (frame_idx & ((MAX_DMA_DESC_NUM_HS_ISOC / 8) - 1)) * 8;
+	} else {
+		return (frame_idx & (MAX_DMA_DESC_NUM_GENERIC - 1));
+	}
+}
+
+/*
+ * Determine starting frame for Isochronous transfer.
+ * Few frames skipped to prevent race condition with HC.
+ */
+static uint8_t calc_starting_frame(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh,
+				   uint8_t * skip_frames)
+{
+	uint16_t frame = 0;
+	hcd->frame_number = dwc_otg_hcd_get_frame_number(hcd);
+
+	/* sched_frame is always frame number(not uFrame) both in FS and HS !! */
+
+	/*
+	 * skip_frames is used to limit activated descriptors number
+	 * to avoid the situation when HC services the last activated
+	 * descriptor firstly.
+	 * Example for FS:
+	 * Current frame is 1, scheduled frame is 3. Since HC always fetches the descriptor
+	 * corresponding to curr_frame+1, the descriptor corresponding to frame 2
+	 * will be fetched. If the number of descriptors is max=64 (or greather) the
+	 * list will be fully programmed with Active descriptors and it is possible
+	 * case(rare) that the latest descriptor(considering rollback) corresponding
+	 * to frame 2 will be serviced first. HS case is more probable because, in fact,
+	 * up to 11 uframes(16 in the code) may be skipped.
+	 */
+	if (qh->dev_speed == DWC_OTG_EP_SPEED_HIGH) {
+		/*
+		 * Consider uframe counter also, to start xfer asap.
+		 * If half of the frame elapsed skip 2 frames otherwise
+		 * just 1 frame.
+		 * Starting descriptor index must be 8-aligned, so
+		 * if the current frame is near to complete the next one
+		 * is skipped as well.
+		 */
+
+		if (dwc_micro_frame_num(hcd->frame_number) >= 5) {
+			*skip_frames = 2 * 8;
+			frame = dwc_frame_num_inc(hcd->frame_number, *skip_frames);
+		} else {
+			*skip_frames = 1 * 8;
+			frame = dwc_frame_num_inc(hcd->frame_number, *skip_frames);
+		}
+
+		frame = dwc_full_frame_num(frame);
+	} else {
+		/*
+		 * Two frames are skipped for FS - the current and the next.
+		 * But for descriptor programming, 1 frame(descriptor) is enough,
+		 * see example above.
+		 */
+		*skip_frames = 1;
+		frame = dwc_frame_num_inc(hcd->frame_number, 2);
+	}
+
+	return frame;
+}
+
+/*
+ * Calculate initial descriptor index for isochronous transfer
+ * based on scheduled frame.
+ */
+static uint8_t recalc_initial_desc_idx(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh)
+{
+	uint16_t frame = 0, fr_idx, fr_idx_tmp;
+	uint8_t skip_frames = 0;
+	/*
+	 * With current ISOC processing algorithm the channel is being
+	 * released when no more QTDs in the list(qh->ntd == 0).
+	 * Thus this function is called only when qh->ntd == 0 and qh->channel == 0.
+	 *
+	 * So qh->channel != NULL branch is not used and just not removed from the
+	 * source file. It is required for another possible approach which is,
+	 * do not disable and release the channel when ISOC session completed,
+	 * just move QH to inactive schedule until new QTD arrives.
+	 * On new QTD, the QH moved back to 'ready' schedule,
+	 * starting frame and therefore starting desc_index are recalculated.
+	 * In this case channel is released only on ep_disable.
+	 */
+
+	/* Calculate starting descriptor index. For INTERRUPT endpoint it is always 0. */
+	if (qh->channel) {
+		frame = calc_starting_frame(hcd, qh, &skip_frames);
+		/*
+		 * Calculate initial descriptor index based on FrameList current bitmap
+		 * and servicing period.
+		 */
+		fr_idx_tmp = frame_list_idx(frame);
+		fr_idx =
+		    (MAX_FRLIST_EN_NUM + frame_list_idx(qh->sched_frame) -
+		     fr_idx_tmp)
+		    % frame_incr_val(qh);
+		fr_idx = (fr_idx + fr_idx_tmp) % MAX_FRLIST_EN_NUM;
+	} else {
+		qh->sched_frame = calc_starting_frame(hcd, qh, &skip_frames);
+		fr_idx = frame_list_idx(qh->sched_frame);
+	}
+
+	qh->td_first = qh->td_last = frame_to_desc_idx(qh, fr_idx);
+
+	return skip_frames;
+}
+
+#define	ISOC_URB_GIVEBACK_ASAP
+
+#define MAX_ISOC_XFER_SIZE_FS 1023
+#define MAX_ISOC_XFER_SIZE_HS 3072
+#define DESCNUM_THRESHOLD 4
+
+static void init_isoc_dma_desc(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh,
+			       uint8_t skip_frames)
+{
+	struct dwc_otg_hcd_iso_packet_desc *frame_desc;
+	dwc_otg_qtd_t *qtd;
+	dwc_otg_host_dma_desc_t *dma_desc;
+	uint16_t idx, inc, n_desc, ntd_max, max_xfer_size;
+
+	idx = qh->td_last;
+	inc = qh->interval;
+	n_desc = 0;
+
+	ntd_max = (max_desc_num(qh) + qh->interval - 1) / qh->interval;
+	if (skip_frames && !qh->channel)
+		ntd_max = ntd_max - skip_frames / qh->interval;
+
+	max_xfer_size =
+	    (qh->dev_speed ==
+	     DWC_OTG_EP_SPEED_HIGH) ? MAX_ISOC_XFER_SIZE_HS :
+	    MAX_ISOC_XFER_SIZE_FS;
+
+	DWC_CIRCLEQ_FOREACH(qtd, &qh->qtd_list, qtd_list_entry) {
+		while ((qh->ntd < ntd_max)
+		       && (qtd->isoc_frame_index_last <
+			   qtd->urb->packet_count)) {
+
+			dma_desc = &qh->desc_list[idx];
+			dwc_memset(dma_desc, 0x00, sizeof(dwc_otg_host_dma_desc_t));
+
+			frame_desc = &qtd->urb->iso_descs[qtd->isoc_frame_index_last];
+
+			if (frame_desc->length > max_xfer_size)
+				qh->n_bytes[idx] = max_xfer_size;
+			else
+				qh->n_bytes[idx] = frame_desc->length;
+			dma_desc->status.b_isoc.n_bytes = qh->n_bytes[idx];
+			dma_desc->status.b_isoc.a = 1;
+			dma_desc->status.b_isoc.sts = 0;
+
+			dma_desc->buf = qtd->urb->dma + frame_desc->offset;
+
+			qh->ntd++;
+
+			qtd->isoc_frame_index_last++;
+
+#ifdef	ISOC_URB_GIVEBACK_ASAP
+			/*
+			 * Set IOC for each descriptor corresponding to the
+			 * last frame of the URB.
+			 */
+			if (qtd->isoc_frame_index_last ==
+			    qtd->urb->packet_count)
+				dma_desc->status.b_isoc.ioc = 1;
+
+#endif
+			idx = desclist_idx_inc(idx, inc, qh->dev_speed);
+			n_desc++;
+
+		}
+		qtd->in_process = 1;
+	}
+
+	qh->td_last = idx;
+
+#ifdef	ISOC_URB_GIVEBACK_ASAP
+	/* Set IOC for the last descriptor if descriptor list is full */
+	if (qh->ntd == ntd_max) {
+		idx = desclist_idx_dec(qh->td_last, inc, qh->dev_speed);
+		qh->desc_list[idx].status.b_isoc.ioc = 1;
+	}
+#else
+	/*
+	 * Set IOC bit only for one descriptor.
+	 * Always try to be ahead of HW processing,
+	 * i.e. on IOC generation driver activates next descriptors but
+	 * core continues to process descriptors followed the one with IOC set.
+	 */
+
+	if (n_desc > DESCNUM_THRESHOLD) {
+		/*
+		 * Move IOC "up". Required even if there is only one QTD
+		 * in the list, cause QTDs migth continue to be queued,
+		 * but during the activation it was only one queued.
+		 * Actually more than one QTD might be in the list if this function called
+		 * from XferCompletion - QTDs was queued during HW processing of the previous
+		 * descriptor chunk.
+		 */
+		idx = dwc_desclist_idx_dec(idx, inc * ((qh->ntd + 1) / 2), qh->dev_speed);
+	} else {
+		/*
+		 * Set the IOC for the latest descriptor
+		 * if either number of descriptor is not greather than threshold
+		 * or no more new descriptors activated.
+		 */
+		idx = dwc_desclist_idx_dec(qh->td_last, inc, qh->dev_speed);
+	}
+
+	qh->desc_list[idx].status.b_isoc.ioc = 1;
+#endif
+}
+
+static void init_non_isoc_dma_desc(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh)
+{
+
+	dwc_hc_t *hc;
+	dwc_otg_host_dma_desc_t *dma_desc;
+	dwc_otg_qtd_t *qtd;
+	int num_packets, len, n_desc = 0;
+
+	hc = qh->channel;
+
+	/*
+	 * Start with hc->xfer_buff initialized in
+	 * assign_and_init_hc(), then if SG transfer consists of multiple URBs,
+	 * this pointer re-assigned to the buffer of the currently processed QTD.
+	 * For non-SG request there is always one QTD active.
+	 */
+
+	DWC_CIRCLEQ_FOREACH(qtd, &qh->qtd_list, qtd_list_entry) {
+
+		if (n_desc) {
+			/* SG request - more than 1 QTDs */
+			hc->xfer_buff = (uint8_t *)(uintptr_t)qtd->urb->dma +
+					qtd->urb->actual_length;
+			hc->xfer_len = qtd->urb->length - qtd->urb->actual_length;
+		}
+
+		qtd->n_desc = 0;
+
+		do {
+			dma_desc = &qh->desc_list[n_desc];
+			len = hc->xfer_len;
+
+			if (len > MAX_DMA_DESC_SIZE)
+				len = MAX_DMA_DESC_SIZE - hc->max_packet + 1;
+
+			if (hc->ep_is_in) {
+				if (len > 0) {
+					num_packets = (len + hc->max_packet - 1) / hc->max_packet;
+				} else {
+					/* Need 1 packet for transfer length of 0. */
+					num_packets = 1;
+				}
+				/* Always program an integral # of max packets for IN transfers. */
+				len = num_packets * hc->max_packet;
+			}
+
+			dma_desc->status.b.n_bytes = len;
+
+			qh->n_bytes[n_desc] = len;
+
+			if ((qh->ep_type == UE_CONTROL)
+			    && (qtd->control_phase == DWC_OTG_CONTROL_SETUP))
+				dma_desc->status.b.sup = 1;	/* Setup Packet */
+
+			dma_desc->status.b.a = 1;	/* Active descriptor */
+			dma_desc->status.b.sts = 0;
+
+			dma_desc->buf =
+			    ((unsigned long)hc->xfer_buff & 0xffffffff);
+
+			/*
+			 * Last descriptor(or single) of IN transfer
+			 * with actual size less than MaxPacket.
+			 */
+			if (len > hc->xfer_len) {
+				hc->xfer_len = 0;
+			} else {
+				hc->xfer_buff += len;
+				hc->xfer_len -= len;
+			}
+
+			qtd->n_desc++;
+			n_desc++;
+		}
+		while ((hc->xfer_len > 0) && (n_desc != MAX_DMA_DESC_NUM_GENERIC));
+
+
+		qtd->in_process = 1;
+
+		if (qh->ep_type == UE_CONTROL)
+			break;
+
+		if (n_desc == MAX_DMA_DESC_NUM_GENERIC)
+			break;
+	}
+
+	if (n_desc) {
+		/* Request Transfer Complete interrupt for the last descriptor */
+		qh->desc_list[n_desc - 1].status.b.ioc = 1;
+		/* End of List indicator */
+		qh->desc_list[n_desc - 1].status.b.eol = 1;
+
+		hc->ntd = n_desc;
+	}
+}
+
+/**
+ * For Control and Bulk endpoints initializes descriptor list
+ * and starts the transfer.
+ *
+ * For Interrupt and Isochronous endpoints initializes descriptor list
+ * then updates FrameList, marking appropriate entries as active.
+ * In case of Isochronous, the starting descriptor index is calculated based
+ * on the scheduled frame, but only on the first transfer descriptor within a session.
+ * Then starts the transfer via enabling the channel.
+ * For Isochronous endpoint the channel is not halted on XferComplete
+ * interrupt so remains assigned to the endpoint(QH) until session is done.
+ *
+ * @param hcd The HCD state structure for the DWC OTG controller.
+ * @param qh The QH to init.
+ *
+ * @return 0 if successful, negative error code otherwise.
+ */
+void dwc_otg_hcd_start_xfer_ddma(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh)
+{
+	/* Channel is already assigned */
+	dwc_hc_t *hc = qh->channel;
+	uint8_t skip_frames = 0;
+
+	switch (hc->ep_type) {
+	case DWC_OTG_EP_TYPE_CONTROL:
+	case DWC_OTG_EP_TYPE_BULK:
+		init_non_isoc_dma_desc(hcd, qh);
+
+		dwc_otg_hc_start_transfer_ddma(hcd->core_if, hc);
+		break;
+	case DWC_OTG_EP_TYPE_INTR:
+		init_non_isoc_dma_desc(hcd, qh);
+
+		update_frame_list(hcd, qh, 1);
+
+		dwc_otg_hc_start_transfer_ddma(hcd->core_if, hc);
+		break;
+	case DWC_OTG_EP_TYPE_ISOC:
+
+		if (!qh->ntd)
+			skip_frames = recalc_initial_desc_idx(hcd, qh);
+
+		init_isoc_dma_desc(hcd, qh, skip_frames);
+
+		if (!hc->xfer_started) {
+
+			update_frame_list(hcd, qh, 1);
+
+			/*
+			 * Always set to max, instead of actual size.
+			 * Otherwise ntd will be changed with
+			 * channel being enabled. Not recommended.
+			 *
+			 */
+			hc->ntd = max_desc_num(qh);
+			/* Enable channel only once for ISOC */
+			dwc_otg_hc_start_transfer_ddma(hcd->core_if, hc);
+		}
+
+		break;
+	default:
+
+		break;
+	}
+}
+
+static void complete_isoc_xfer_ddma(dwc_otg_hcd_t * hcd,
+				    dwc_hc_t * hc,
+				    dwc_otg_hc_regs_t * hc_regs,
+				    dwc_otg_halt_status_e halt_status)
+{
+	struct dwc_otg_hcd_iso_packet_desc *frame_desc;
+	dwc_otg_qtd_t *qtd, *qtd_tmp;
+	dwc_otg_qh_t *qh;
+	dwc_otg_host_dma_desc_t *dma_desc;
+	uint16_t idx, remain;
+	uint8_t urb_compl;
+
+	qh = hc->qh;
+	idx = qh->td_first;
+
+	if (hc->halt_status == DWC_OTG_HC_XFER_URB_DEQUEUE) {
+		DWC_CIRCLEQ_FOREACH_SAFE(qtd, qtd_tmp, &hc->qh->qtd_list, qtd_list_entry)
+		    qtd->in_process = 0;
+		return;
+	} else if ((halt_status == DWC_OTG_HC_XFER_AHB_ERR) ||
+		   (halt_status == DWC_OTG_HC_XFER_BABBLE_ERR)) {
+		/*
+		 * Channel is halted in these error cases.
+		 * Considered as serious issues.
+		 * Complete all URBs marking all frames as failed,
+		 * irrespective whether some of the descriptors(frames) succeeded or no.
+		 * Pass error code to completion routine as well, to
+		 * update urb->status, some of class drivers might use it to stop
+		 * queing transfer requests.
+		 */
+		int err = (halt_status == DWC_OTG_HC_XFER_AHB_ERR)
+		    ? (-DWC_E_IO)
+		    : (-DWC_E_OVERFLOW);
+
+		DWC_CIRCLEQ_FOREACH_SAFE(qtd, qtd_tmp, &hc->qh->qtd_list, qtd_list_entry) {
+			for (idx = 0; idx < qtd->urb->packet_count; idx++) {
+				frame_desc = &qtd->urb->iso_descs[idx];
+				frame_desc->status = err;
+			}
+			hcd->fops->complete(hcd, qtd->urb->priv, qtd->urb, err);
+			dwc_otg_hcd_qtd_remove_and_free(hcd, qtd, qh);
+		}
+		return;
+	}
+
+	DWC_CIRCLEQ_FOREACH_SAFE(qtd, qtd_tmp, &hc->qh->qtd_list, qtd_list_entry) {
+
+		if (!qtd->in_process)
+			break;
+
+		urb_compl = 0;
+
+		do {
+
+			dma_desc = &qh->desc_list[idx];
+
+			frame_desc = &qtd->urb->iso_descs[qtd->isoc_frame_index];
+			remain = hc->ep_is_in ? dma_desc->status.b_isoc.n_bytes : 0;
+
+			if (dma_desc->status.b_isoc.sts == DMA_DESC_STS_PKTERR) {
+				/*
+				 * XactError or, unable to complete all the transactions
+				 * in the scheduled micro-frame/frame,
+				 * both indicated by DMA_DESC_STS_PKTERR.
+				 */
+				qtd->urb->error_count++;
+				frame_desc->actual_length = qh->n_bytes[idx] - remain;
+				frame_desc->status = -DWC_E_PROTOCOL;
+			} else {
+				/* Success */
+
+				frame_desc->actual_length = qh->n_bytes[idx] - remain;
+				frame_desc->status = 0;
+			}
+
+			if (++qtd->isoc_frame_index == qtd->urb->packet_count) {
+				/*
+				 * urb->status is not used for isoc transfers here.
+				 * The individual frame_desc status are used instead.
+				 */
+
+				hcd->fops->complete(hcd, qtd->urb->priv, qtd->urb, 0);
+				dwc_otg_hcd_qtd_remove_and_free(hcd, qtd, qh);
+
+				/*
+				 * This check is necessary because urb_dequeue can be called
+				 * from urb complete callback(sound driver example).
+				 * All pending URBs are dequeued there, so no need for
+				 * further processing.
+				 */
+				if (hc->halt_status == DWC_OTG_HC_XFER_URB_DEQUEUE) {
+					return;
+				}
+
+				urb_compl = 1;
+
+			}
+
+			qh->ntd--;
+
+			/* Stop if IOC requested descriptor reached */
+			if (dma_desc->status.b_isoc.ioc) {
+				idx = desclist_idx_inc(idx, qh->interval, hc->speed);
+				goto stop_scan;
+			}
+
+			idx = desclist_idx_inc(idx, qh->interval, hc->speed);
+
+			if (urb_compl)
+				break;
+		}
+		while (idx != qh->td_first);
+	}
+stop_scan:
+	qh->td_first = idx;
+}
+
+uint8_t update_non_isoc_urb_state_ddma(dwc_otg_hcd_t * hcd,
+				       dwc_hc_t * hc,
+				       dwc_otg_qtd_t * qtd,
+				       dwc_otg_host_dma_desc_t * dma_desc,
+				       dwc_otg_halt_status_e halt_status,
+				       uint32_t n_bytes, uint8_t * xfer_done)
+{
+
+	uint16_t remain = hc->ep_is_in ? dma_desc->status.b.n_bytes : 0;
+	dwc_otg_hcd_urb_t *urb = qtd->urb;
+
+	if (halt_status == DWC_OTG_HC_XFER_AHB_ERR) {
+		urb->status = -DWC_E_IO;
+		return 1;
+	}
+	if (dma_desc->status.b.sts == DMA_DESC_STS_PKTERR) {
+		switch (halt_status) {
+		case DWC_OTG_HC_XFER_STALL:
+			urb->status = -DWC_E_PIPE;
+			break;
+		case DWC_OTG_HC_XFER_BABBLE_ERR:
+			urb->status = -DWC_E_OVERFLOW;
+			break;
+		case DWC_OTG_HC_XFER_XACT_ERR:
+			urb->status = -DWC_E_PROTOCOL;
+			break;
+		default:
+			DWC_ERROR("%s: Unhandled descriptor error status (%d)\n", __func__,
+				  halt_status);
+			break;
+		}
+		return 1;
+	}
+
+	if (dma_desc->status.b.a == 1) {
+		DWC_DEBUGPL(DBG_HCDV,
+			    "Active descriptor encountered on channel %d\n",
+			    hc->hc_num);
+		return 0;
+	}
+
+	if (hc->ep_type == DWC_OTG_EP_TYPE_CONTROL) {
+		if (qtd->control_phase == DWC_OTG_CONTROL_DATA) {
+			urb->actual_length += n_bytes - remain;
+			if (remain || urb->actual_length == urb->length) {
+				/*
+				 * For Control Data stage do not set urb->status=0 to prevent
+				 * URB callback. Set it when Status phase done. See below.
+				 */
+				*xfer_done = 1;
+			}
+
+		} else if (qtd->control_phase == DWC_OTG_CONTROL_STATUS) {
+			urb->status = 0;
+			*xfer_done = 1;
+		}
+		/* No handling for SETUP stage */
+	} else {
+		/* BULK and INTR */
+		urb->actual_length += n_bytes - remain;
+		if (remain || urb->actual_length == urb->length) {
+			urb->status = 0;
+			*xfer_done = 1;
+		}
+	}
+
+	return 0;
+}
+
+static void complete_non_isoc_xfer_ddma(dwc_otg_hcd_t * hcd,
+					dwc_hc_t * hc,
+					dwc_otg_hc_regs_t * hc_regs,
+					dwc_otg_halt_status_e halt_status)
+{
+	dwc_otg_hcd_urb_t *urb = NULL;
+	dwc_otg_qtd_t *qtd, *qtd_tmp;
+	dwc_otg_qh_t *qh;
+	dwc_otg_host_dma_desc_t *dma_desc;
+	uint32_t n_bytes, n_desc, i;
+	uint8_t failed = 0, xfer_done;
+
+	n_desc = 0;
+
+	qh = hc->qh;
+
+	if (hc->halt_status == DWC_OTG_HC_XFER_URB_DEQUEUE) {
+		DWC_CIRCLEQ_FOREACH_SAFE(qtd, qtd_tmp, &hc->qh->qtd_list, qtd_list_entry) {
+			qtd->in_process = 0;
+		}
+		return;
+	}
+
+	DWC_CIRCLEQ_FOREACH_SAFE(qtd, qtd_tmp, &qh->qtd_list, qtd_list_entry) {
+
+		urb = qtd->urb;
+
+		n_bytes = 0;
+		xfer_done = 0;
+
+		for (i = 0; i < qtd->n_desc; i++) {
+			dma_desc = &qh->desc_list[n_desc];
+
+			n_bytes = qh->n_bytes[n_desc];
+
+			failed =
+			    update_non_isoc_urb_state_ddma(hcd, hc, qtd,
+							   dma_desc,
+							   halt_status, n_bytes,
+							   &xfer_done);
+
+			if (failed
+			    || (xfer_done
+				&& (urb->status != -DWC_E_IN_PROGRESS))) {
+
+				hcd->fops->complete(hcd, urb->priv, urb,
+						    urb->status);
+				dwc_otg_hcd_qtd_remove_and_free(hcd, qtd, qh);
+
+				if (failed)
+					goto stop_scan;
+			} else if (qh->ep_type == UE_CONTROL) {
+				if (qtd->control_phase == DWC_OTG_CONTROL_SETUP) {
+					if (urb->length > 0) {
+						qtd->control_phase = DWC_OTG_CONTROL_DATA;
+					} else {
+						qtd->control_phase = DWC_OTG_CONTROL_STATUS;
+					}
+					DWC_DEBUGPL(DBG_HCDV, "  Control setup transaction done\n");
+				} else if (qtd->control_phase == DWC_OTG_CONTROL_DATA) {
+					if (xfer_done) {
+						qtd->control_phase = DWC_OTG_CONTROL_STATUS;
+						DWC_DEBUGPL(DBG_HCDV, "  Control data transfer done\n");
+					} else if (i + 1 == qtd->n_desc) {
+						/*
+						 * Last descriptor for Control data stage which is
+						 * not completed yet.
+						 */
+						dwc_otg_hcd_save_data_toggle(hc, hc_regs, qtd);
+					}
+				}
+			}
+
+			n_desc++;
+		}
+
+	}
+
+stop_scan:
+
+	if (qh->ep_type != UE_CONTROL) {
+		/*
+		 * Resetting the data toggle for bulk
+		 * and interrupt endpoints in case of stall. See handle_hc_stall_intr()
+		 */
+		if (halt_status == DWC_OTG_HC_XFER_STALL)
+			qh->data_toggle = DWC_OTG_HC_PID_DATA0;
+		else
+			dwc_otg_hcd_save_data_toggle(hc, hc_regs, qtd);
+	}
+
+	if (halt_status == DWC_OTG_HC_XFER_COMPLETE) {
+		hcint_data_t hcint;
+		hcint.d32 = DWC_READ_REG32(&hc_regs->hcint);
+		if (hcint.b.nyet) {
+			/*
+			 * Got a NYET on the last transaction of the transfer. It
+			 * means that the endpoint should be in the PING state at the
+			 * beginning of the next transfer.
+			 */
+			qh->ping_state = 1;
+			clear_hc_int(hc_regs, nyet);
+		}
+
+	}
+
+}
+
+/**
+ * This function is called from interrupt handlers.
+ * Scans the descriptor list, updates URB's status and
+ * calls completion routine for the URB if it's done.
+ * Releases the channel to be used by other transfers.
+ * In case of Isochronous endpoint the channel is not halted until
+ * the end of the session, i.e. QTD list is empty.
+ * If periodic channel released the FrameList is updated accordingly.
+ *
+ * Calls transaction selection routines to activate pending transfers.
+ *
+ * @param hcd The HCD state structure for the DWC OTG controller.
+ * @param hc Host channel, the transfer is completed on.
+ * @param hc_regs Host channel registers.
+ * @param halt_status Reason the channel is being halted,
+ *		      or just XferComplete for isochronous transfer
+ */
+void dwc_otg_hcd_complete_xfer_ddma(dwc_otg_hcd_t * hcd,
+				    dwc_hc_t * hc,
+				    dwc_otg_hc_regs_t * hc_regs,
+				    dwc_otg_halt_status_e halt_status)
+{
+	uint8_t continue_isoc_xfer = 0;
+	dwc_otg_transaction_type_e tr_type;
+	dwc_otg_qh_t *qh = hc->qh;
+
+	if (hc->ep_type == DWC_OTG_EP_TYPE_ISOC) {
+
+		complete_isoc_xfer_ddma(hcd, hc, hc_regs, halt_status);
+
+		/* Release the channel if halted or session completed */
+		if (halt_status != DWC_OTG_HC_XFER_COMPLETE ||
+		    DWC_CIRCLEQ_EMPTY(&qh->qtd_list)) {
+
+			/* Halt the channel if session completed */
+			if (halt_status == DWC_OTG_HC_XFER_COMPLETE) {
+				dwc_otg_hc_halt(hcd->core_if, hc, halt_status);
+			}
+
+			release_channel_ddma(hcd, qh);
+			dwc_otg_hcd_qh_remove(hcd, qh);
+		} else {
+			/* Keep in assigned schedule to continue transfer */
+			DWC_LIST_MOVE_HEAD(&hcd->periodic_sched_assigned,
+					   &qh->qh_list_entry);
+			continue_isoc_xfer = 1;
+
+		}
+		/** @todo Consider the case when period exceeds FrameList size.
+		 *  Frame Rollover interrupt should be used.
+		 */
+	} else {
+		/* Scan descriptor list to complete the URB(s), then release the channel */
+		complete_non_isoc_xfer_ddma(hcd, hc, hc_regs, halt_status);
+
+		release_channel_ddma(hcd, qh);
+		dwc_otg_hcd_qh_remove(hcd, qh);
+
+		if (!DWC_CIRCLEQ_EMPTY(&qh->qtd_list)) {
+			/* Add back to inactive non-periodic schedule on normal completion */
+			dwc_otg_hcd_qh_add(hcd, qh);
+		}
+
+	}
+	tr_type = dwc_otg_hcd_select_transactions(hcd);
+	if (tr_type != DWC_OTG_TRANSACTION_NONE || continue_isoc_xfer) {
+		if (continue_isoc_xfer) {
+			if (tr_type == DWC_OTG_TRANSACTION_NONE) {
+				tr_type = DWC_OTG_TRANSACTION_PERIODIC;
+			} else if (tr_type == DWC_OTG_TRANSACTION_NON_PERIODIC) {
+				tr_type = DWC_OTG_TRANSACTION_ALL;
+			}
+		}
+		dwc_otg_hcd_queue_transactions(hcd, tr_type);
+	}
+}
+
+#endif /* DWC_DEVICE_ONLY */
diff -Nur --no-dereference linux-5.10.52-orig/drivers/usb/host/dwc_otg/dwc_otg_hcd.h linux-5.10.52-v7l+/drivers/usb/host/dwc_otg/dwc_otg_hcd.h
--- linux-5.10.52-orig/drivers/usb/host/dwc_otg/dwc_otg_hcd.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/usb/host/dwc_otg/dwc_otg_hcd.h	2021-07-25 16:46:28.697909777 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/* ==========================================================================
+ * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_hcd.h $
+ * $Revision: #58 $
+ * $Date: 2011/09/15 $
+ * $Change: 1846647 $
+ *
+ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter,
+ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless
+ * otherwise expressly agreed to in writing between Synopsys and you.
+ *
+ * The Software IS NOT an item of Licensed Software or Licensed Product under
+ * any End User Software License Agreement or Agreement for Licensed Product
+ * with Synopsys or any supplement thereto. You are permitted to use and
+ * redistribute this Software in source and binary forms, with or without
+ * modification, provided that redistributions of source code must retain this
+ * notice. You may not view, use, disclose, copy or distribute this file or
+ * any information contained herein except pursuant to this license grant from
+ * Synopsys. If you do not agree with this notice, including the disclaimer
+ * below, then you are not authorized to use the Software.
+ *
+ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * ========================================================================== */
+#ifndef DWC_DEVICE_ONLY
+#ifndef __DWC_HCD_H__
+#define __DWC_HCD_H__
+
+#include "dwc_otg_os_dep.h"
+#include "usb.h"
+#include "dwc_otg_hcd_if.h"
+#include "dwc_otg_core_if.h"
+#include "dwc_list.h"
+#include "dwc_otg_cil.h"
+#include "dwc_otg_fiq_fsm.h"
+#include "dwc_otg_driver.h"
+
+
+/**
+ * @file
+ *
+ * This file contains the structures, constants, and interfaces for
+ * the Host Contoller Driver (HCD).
+ *
+ * The Host Controller Driver (HCD) is responsible for translating requests
+ * from the USB Driver into the appropriate actions on the DWC_otg controller.
+ * It isolates the USBD from the specifics of the controller by providing an
+ * API to the USBD.
+ */
+
+struct dwc_otg_hcd_pipe_info {
+	uint8_t dev_addr;
+	uint8_t ep_num;
+	uint8_t pipe_type;
+	uint8_t pipe_dir;
+	uint16_t mps;
+};
+
+struct dwc_otg_hcd_iso_packet_desc {
+	uint32_t offset;
+	uint32_t length;
+	uint32_t actual_length;
+	uint32_t status;
+};
+
+struct dwc_otg_qtd;
+
+struct dwc_otg_hcd_urb {
+	void *priv;
+	struct dwc_otg_qtd *qtd;
+	void *buf;
+	dwc_dma_t dma;
+	void *setup_packet;
+	dwc_dma_t setup_dma;
+	uint32_t length;
+	uint32_t actual_length;
+	uint32_t status;
+	uint32_t error_count;
+	uint32_t packet_count;
+	uint32_t flags;
+	uint16_t interval;
+	struct dwc_otg_hcd_pipe_info pipe_info;
+	struct dwc_otg_hcd_iso_packet_desc iso_descs[0];
+};
+
+static inline uint8_t dwc_otg_hcd_get_ep_num(struct dwc_otg_hcd_pipe_info *pipe)
+{
+	return pipe->ep_num;
+}
+
+static inline uint8_t dwc_otg_hcd_get_pipe_type(struct dwc_otg_hcd_pipe_info
+						*pipe)
+{
+	return pipe->pipe_type;
+}
+
+static inline uint16_t dwc_otg_hcd_get_mps(struct dwc_otg_hcd_pipe_info *pipe)
+{
+	return pipe->mps;
+}
+
+static inline uint8_t dwc_otg_hcd_get_dev_addr(struct dwc_otg_hcd_pipe_info
+					       *pipe)
+{
+	return pipe->dev_addr;
+}
+
+static inline uint8_t dwc_otg_hcd_is_pipe_isoc(struct dwc_otg_hcd_pipe_info
+					       *pipe)
+{
+	return (pipe->pipe_type == UE_ISOCHRONOUS);
+}
+
+static inline uint8_t dwc_otg_hcd_is_pipe_int(struct dwc_otg_hcd_pipe_info
+					      *pipe)
+{
+	return (pipe->pipe_type == UE_INTERRUPT);
+}
+
+static inline uint8_t dwc_otg_hcd_is_pipe_bulk(struct dwc_otg_hcd_pipe_info
+					       *pipe)
+{
+	return (pipe->pipe_type == UE_BULK);
+}
+
+static inline uint8_t dwc_otg_hcd_is_pipe_control(struct dwc_otg_hcd_pipe_info
+						  *pipe)
+{
+	return (pipe->pipe_type == UE_CONTROL);
+}
+
+static inline uint8_t dwc_otg_hcd_is_pipe_in(struct dwc_otg_hcd_pipe_info *pipe)
+{
+	return (pipe->pipe_dir == UE_DIR_IN);
+}
+
+static inline uint8_t dwc_otg_hcd_is_pipe_out(struct dwc_otg_hcd_pipe_info
+					      *pipe)
+{
+	return (!dwc_otg_hcd_is_pipe_in(pipe));
+}
+
+static inline void dwc_otg_hcd_fill_pipe(struct dwc_otg_hcd_pipe_info *pipe,
+					 uint8_t devaddr, uint8_t ep_num,
+					 uint8_t pipe_type, uint8_t pipe_dir,
+					 uint16_t mps)
+{
+	pipe->dev_addr = devaddr;
+	pipe->ep_num = ep_num;
+	pipe->pipe_type = pipe_type;
+	pipe->pipe_dir = pipe_dir;
+	pipe->mps = mps;
+}
+
+/**
+ * Phases for control transfers.
+ */
+typedef enum dwc_otg_control_phase {
+	DWC_OTG_CONTROL_SETUP,
+	DWC_OTG_CONTROL_DATA,
+	DWC_OTG_CONTROL_STATUS
+} dwc_otg_control_phase_e;
+
+/** Transaction types. */
+typedef enum dwc_otg_transaction_type {
+	DWC_OTG_TRANSACTION_NONE          = 0,
+	DWC_OTG_TRANSACTION_PERIODIC      = 1,
+	DWC_OTG_TRANSACTION_NON_PERIODIC  = 2,
+	DWC_OTG_TRANSACTION_ALL           = DWC_OTG_TRANSACTION_PERIODIC + DWC_OTG_TRANSACTION_NON_PERIODIC
+} dwc_otg_transaction_type_e;
+
+struct dwc_otg_qh;
+
+/**
+ * A Queue Transfer Descriptor (QTD) holds the state of a bulk, control,
+ * interrupt, or isochronous transfer. A single QTD is created for each URB
+ * (of one of these types) submitted to the HCD. The transfer associated with
+ * a QTD may require one or multiple transactions.
+ *
+ * A QTD is linked to a Queue Head, which is entered in either the
+ * non-periodic or periodic schedule for execution. When a QTD is chosen for
+ * execution, some or all of its transactions may be executed. After
+ * execution, the state of the QTD is updated. The QTD may be retired if all
+ * its transactions are complete or if an error occurred. Otherwise, it
+ * remains in the schedule so more transactions can be executed later.
+ */
+typedef struct dwc_otg_qtd {
+	/**
+	 * Determines the PID of the next data packet for the data phase of
+	 * control transfers. Ignored for other transfer types.<br>
+	 * One of the following values:
+	 *	- DWC_OTG_HC_PID_DATA0
+	 *	- DWC_OTG_HC_PID_DATA1
+	 */
+	uint8_t data_toggle;
+
+	/** Current phase for control transfers (Setup, Data, or Status). */
+	dwc_otg_control_phase_e control_phase;
+
+	/** Keep track of the current split type
+	 * for FS/LS endpoints on a HS Hub */
+	uint8_t complete_split;
+
+	/** How many bytes transferred during SSPLIT OUT */
+	uint32_t ssplit_out_xfer_count;
+
+	/**
+	 * Holds the number of bus errors that have occurred for a transaction
+	 * within this transfer.
+	 */
+	uint8_t error_count;
+
+	/**
+	 * Index of the next frame descriptor for an isochronous transfer. A
+	 * frame descriptor describes the buffer position and length of the
+	 * data to be transferred in the next scheduled (micro)frame of an
+	 * isochronous transfer. It also holds status for that transaction.
+	 * The frame index starts at 0.
+	 */
+	uint16_t isoc_frame_index;
+
+	/** Position of the ISOC split on full/low speed */
+	uint8_t isoc_split_pos;
+
+	/** Position of the ISOC split in the buffer for the current frame */
+	uint16_t isoc_split_offset;
+
+	/** URB for this transfer */
+	struct dwc_otg_hcd_urb *urb;
+
+	struct dwc_otg_qh *qh;
+
+	/** This list of QTDs */
+	 DWC_CIRCLEQ_ENTRY(dwc_otg_qtd) qtd_list_entry;
+
+	/** Indicates if this QTD is currently processed by HW. */
+	uint8_t in_process;
+
+	/** Number of DMA descriptors for this QTD */
+	uint8_t n_desc;
+
+	/**
+	 * Last activated frame(packet) index.
+	 * Used in Descriptor DMA mode only.
+	 */
+	uint16_t isoc_frame_index_last;
+
+} dwc_otg_qtd_t;
+
+DWC_CIRCLEQ_HEAD(dwc_otg_qtd_list, dwc_otg_qtd);
+
+/**
+ * A Queue Head (QH) holds the static characteristics of an endpoint and
+ * maintains a list of transfers (QTDs) for that endpoint. A QH structure may
+ * be entered in either the non-periodic or periodic schedule.
+ */
+typedef struct dwc_otg_qh {
+	/**
+	 * Endpoint type.
+	 * One of the following values:
+	 *	- UE_CONTROL
+	 *	- UE_BULK
+	 *	- UE_INTERRUPT
+	 *	- UE_ISOCHRONOUS
+	 */
+	uint8_t ep_type;
+	uint8_t ep_is_in;
+
+	/** wMaxPacketSize Field of Endpoint Descriptor. */
+	uint16_t maxp;
+
+	/**
+	 * Device speed.
+	 * One of the following values:
+	 *	- DWC_OTG_EP_SPEED_LOW
+	 *	- DWC_OTG_EP_SPEED_FULL
+	 *	- DWC_OTG_EP_SPEED_HIGH
+	 */
+	uint8_t dev_speed;
+
+	/**
+	 * Determines the PID of the next data packet for non-control
+	 * transfers. Ignored for control transfers.<br>
+	 * One of the following values:
+	 *	- DWC_OTG_HC_PID_DATA0
+	 *	- DWC_OTG_HC_PID_DATA1
+	 */
+	uint8_t data_toggle;
+
+	/** Ping state if 1. */
+	uint8_t ping_state;
+
+	/**
+	 * List of QTDs for this QH.
+	 */
+	struct dwc_otg_qtd_list qtd_list;
+
+	/** Host channel currently processing transfers for this QH. */
+	struct dwc_hc *channel;
+
+	/** Full/low speed endpoint on high-speed hub requires split. */
+	uint8_t do_split;
+
+	/** @name Periodic schedule information */
+	/** @{ */
+
+	/** Bandwidth in microseconds per (micro)frame. */
+	uint16_t usecs;
+
+	/** Interval between transfers in (micro)frames. */
+	uint16_t interval;
+
+	/**
+	 * (micro)frame to initialize a periodic transfer. The transfer
+	 * executes in the following (micro)frame.
+	 */
+	uint16_t sched_frame;
+
+	/*
+	** Frame a NAK was received on this queue head, used to minimise NAK retransmission
+	*/
+	uint16_t nak_frame;
+
+	/** (micro)frame at which last start split was initialized. */
+	uint16_t start_split_frame;
+
+	/** @} */
+
+	/**
+	 * Used instead of original buffer if
+	 * it(physical address) is not dword-aligned.
+	 */
+	uint8_t *dw_align_buf;
+	dwc_dma_t dw_align_buf_dma;
+
+	/** Entry for QH in either the periodic or non-periodic schedule. */
+	dwc_list_link_t qh_list_entry;
+
+	/** @name Descriptor DMA support */
+	/** @{ */
+
+	/** Descriptor List. */
+	dwc_otg_host_dma_desc_t *desc_list;
+
+	/** Descriptor List physical address. */
+	dwc_dma_t desc_list_dma;
+
+	/**
+	 * Xfer Bytes array.
+	 * Each element corresponds to a descriptor and indicates
+	 * original XferSize size value for the descriptor.
+	 */
+	uint32_t *n_bytes;
+
+	/** Actual number of transfer descriptors in a list. */
+	uint16_t ntd;
+
+	/** First activated isochronous transfer descriptor index. */
+	uint8_t td_first;
+	/** Last activated isochronous transfer descriptor index. */
+	uint8_t td_last;
+
+	/** @} */
+
+
+	uint16_t speed;
+	uint16_t frame_usecs[8];
+
+	uint32_t skip_count;
+} dwc_otg_qh_t;
+
+DWC_CIRCLEQ_HEAD(hc_list, dwc_hc);
+
+typedef struct urb_tq_entry {
+	struct urb *urb;
+	DWC_TAILQ_ENTRY(urb_tq_entry) urb_tq_entries;
+} urb_tq_entry_t;
+
+DWC_TAILQ_HEAD(urb_list, urb_tq_entry);
+
+/**
+ * This structure holds the state of the HCD, including the non-periodic and
+ * periodic schedules.
+ */
+struct dwc_otg_hcd {
+	/** The DWC otg device pointer */
+	struct dwc_otg_device *otg_dev;
+	/** DWC OTG Core Interface Layer */
+	dwc_otg_core_if_t *core_if;
+
+	/** Function HCD driver callbacks */
+	struct dwc_otg_hcd_function_ops *fops;
+
+	/** Internal DWC HCD Flags */
+	volatile union dwc_otg_hcd_internal_flags {
+		uint32_t d32;
+		struct {
+			unsigned port_connect_status_change:1;
+			unsigned port_connect_status:1;
+			unsigned port_reset_change:1;
+			unsigned port_enable_change:1;
+			unsigned port_suspend_change:1;
+			unsigned port_over_current_change:1;
+			unsigned port_l1_change:1;
+			unsigned port_speed:2;
+			unsigned reserved:24;
+		} b;
+	} flags;
+
+	/**
+	 * Inactive items in the non-periodic schedule. This is a list of
+	 * Queue Heads. Transfers associated with these Queue Heads are not
+	 * currently assigned to a host channel.
+	 */
+	dwc_list_link_t non_periodic_sched_inactive;
+
+	/**
+	 * Active items in the non-periodic schedule. This is a list of
+	 * Queue Heads. Transfers associated with these Queue Heads are
+	 * currently assigned to a host channel.
+	 */
+	dwc_list_link_t non_periodic_sched_active;
+
+	/**
+	 * Pointer to the next Queue Head to process in the active
+	 * non-periodic schedule.
+	 */
+	dwc_list_link_t *non_periodic_qh_ptr;
+
+	/**
+	 * Inactive items in the periodic schedule. This is a list of QHs for
+	 * periodic transfers that are _not_ scheduled for the next frame.
+	 * Each QH in the list has an interval counter that determines when it
+	 * needs to be scheduled for execution. This scheduling mechanism
+	 * allows only a simple calculation for periodic bandwidth used (i.e.
+	 * must assume that all periodic transfers may need to execute in the
+	 * same frame). However, it greatly simplifies scheduling and should
+	 * be sufficient for the vast majority of OTG hosts, which need to
+	 * connect to a small number of peripherals at one time.
+	 *
+	 * Items move from this list to periodic_sched_ready when the QH
+	 * interval counter is 0 at SOF.
+	 */
+	dwc_list_link_t periodic_sched_inactive;
+
+	/**
+	 * List of periodic QHs that are ready for execution in the next
+	 * frame, but have not yet been assigned to host channels.
+	 *
+	 * Items move from this list to periodic_sched_assigned as host
+	 * channels become available during the current frame.
+	 */
+	dwc_list_link_t periodic_sched_ready;
+
+	/**
+	 * List of periodic QHs to be executed in the next frame that are
+	 * assigned to host channels.
+	 *
+	 * Items move from this list to periodic_sched_queued as the
+	 * transactions for the QH are queued to the DWC_otg controller.
+	 */
+	dwc_list_link_t periodic_sched_assigned;
+
+	/**
+	 * List of periodic QHs that have been queued for execution.
+	 *
+	 * Items move from this list to either periodic_sched_inactive or
+	 * periodic_sched_ready when the channel associated with the transfer
+	 * is released. If the interval for the QH is 1, the item moves to
+	 * periodic_sched_ready because it must be rescheduled for the next
+	 * frame. Otherwise, the item moves to periodic_sched_inactive.
+	 */
+	dwc_list_link_t periodic_sched_queued;
+
+	/**
+	 * Total bandwidth claimed so far for periodic transfers. This value
+	 * is in microseconds per (micro)frame. The assumption is that all
+	 * periodic transfers may occur in the same (micro)frame.
+	 */
+	uint16_t periodic_usecs;
+
+	/**
+	 * Total bandwidth claimed so far for all periodic transfers
+	 * in a frame.
+	 * This will include a mixture of HS and FS transfers.
+	 * Units are microseconds per (micro)frame.
+	 * We have a budget per frame and have to schedule
+	 * transactions accordingly.
+	 * Watch out for the fact that things are actually scheduled for the
+	 * "next frame".
+	 */
+	uint16_t                frame_usecs[8];
+
+
+	/**
+	 * Frame number read from the core at SOF. The value ranges from 0 to
+	 * DWC_HFNUM_MAX_FRNUM.
+	 */
+	uint16_t frame_number;
+
+	/**
+	 * Count of periodic QHs, if using several eps. For SOF enable/disable.
+	 */
+	uint16_t periodic_qh_count;
+
+	/**
+	 * Free host channels in the controller. This is a list of
+	 * dwc_hc_t items.
+	 */
+	struct hc_list free_hc_list;
+	/**
+	 * Number of host channels assigned to periodic transfers. Currently
+	 * assuming that there is a dedicated host channel for each periodic
+	 * transaction and at least one host channel available for
+	 * non-periodic transactions.
+	 */
+	int periodic_channels; /* microframe_schedule==0 */
+
+	/**
+	 * Number of host channels assigned to non-periodic transfers.
+	 */
+	int non_periodic_channels; /* microframe_schedule==0 */
+
+	/**
+	 * Number of host channels assigned to non-periodic transfers.
+	 */
+	int available_host_channels;
+
+	/**
+	 * Array of pointers to the host channel descriptors. Allows accessing
+	 * a host channel descriptor given the host channel number. This is
+	 * useful in interrupt handlers.
+	 */
+	struct dwc_hc *hc_ptr_array[MAX_EPS_CHANNELS];
+
+	/**
+	 * Buffer to use for any data received during the status phase of a
+	 * control transfer. Normally no data is transferred during the status
+	 * phase. This buffer is used as a bit bucket.
+	 */
+	uint8_t *status_buf;
+
+	/**
+	 * DMA address for status_buf.
+	 */
+	dma_addr_t status_buf_dma;
+#define DWC_OTG_HCD_STATUS_BUF_SIZE 64
+
+	/**
+	 * Connection timer. An OTG host must display a message if the device
+	 * does not connect. Started when the VBus power is turned on via
+	 * sysfs attribute "buspower".
+	 */
+	dwc_timer_t *conn_timer;
+
+	/* Tasket to do a reset */
+	dwc_tasklet_t *reset_tasklet;
+
+	dwc_tasklet_t *completion_tasklet;
+	struct urb_list completed_urb_list;
+
+	/*  */
+	dwc_spinlock_t *lock;
+	/**
+	 * Private data that could be used by OS wrapper.
+	 */
+	void *priv;
+
+	uint8_t otg_port;
+
+	/** Frame List */
+	uint32_t *frame_list;
+
+	/** Hub - Port assignment */
+	int hub_port[128];
+#ifdef FIQ_DEBUG
+	int hub_port_alloc[2048];
+#endif
+
+	/** Frame List DMA address */
+	dma_addr_t frame_list_dma;
+
+	struct fiq_stack *fiq_stack;
+	struct fiq_state *fiq_state;
+
+	/** Virtual address for split transaction DMA bounce buffers */
+	struct fiq_dma_blob *fiq_dmab;
+
+#ifdef DEBUG
+	uint32_t frrem_samples;
+	uint64_t frrem_accum;
+
+	uint32_t hfnum_7_samples_a;
+	uint64_t hfnum_7_frrem_accum_a;
+	uint32_t hfnum_0_samples_a;
+	uint64_t hfnum_0_frrem_accum_a;
+	uint32_t hfnum_other_samples_a;
+	uint64_t hfnum_other_frrem_accum_a;
+
+	uint32_t hfnum_7_samples_b;
+	uint64_t hfnum_7_frrem_accum_b;
+	uint32_t hfnum_0_samples_b;
+	uint64_t hfnum_0_frrem_accum_b;
+	uint32_t hfnum_other_samples_b;
+	uint64_t hfnum_other_frrem_accum_b;
+#endif
+};
+
+static inline struct device *dwc_otg_hcd_to_dev(struct dwc_otg_hcd *hcd)
+{
+	return &hcd->otg_dev->os_dep.platformdev->dev;
+}
+
+/** @name Transaction Execution Functions */
+/** @{ */
+extern dwc_otg_transaction_type_e dwc_otg_hcd_select_transactions(dwc_otg_hcd_t
+								  * hcd);
+extern void dwc_otg_hcd_queue_transactions(dwc_otg_hcd_t * hcd,
+					   dwc_otg_transaction_type_e tr_type);
+
+int dwc_otg_hcd_allocate_port(dwc_otg_hcd_t * hcd, dwc_otg_qh_t *qh);
+void dwc_otg_hcd_release_port(dwc_otg_hcd_t * dwc_otg_hcd, dwc_otg_qh_t *qh);
+
+extern int fiq_fsm_queue_transaction(dwc_otg_hcd_t *hcd, dwc_otg_qh_t *qh);
+extern int fiq_fsm_transaction_suitable(dwc_otg_hcd_t *hcd, dwc_otg_qh_t *qh);
+extern void dwc_otg_cleanup_fiq_channel(dwc_otg_hcd_t *hcd, uint32_t num);
+
+/** @} */
+
+/** @name Interrupt Handler Functions */
+/** @{ */
+extern int32_t dwc_otg_hcd_handle_intr(dwc_otg_hcd_t * dwc_otg_hcd);
+extern int32_t dwc_otg_hcd_handle_sof_intr(dwc_otg_hcd_t * dwc_otg_hcd);
+extern int32_t dwc_otg_hcd_handle_rx_status_q_level_intr(dwc_otg_hcd_t *
+							 dwc_otg_hcd);
+extern int32_t dwc_otg_hcd_handle_np_tx_fifo_empty_intr(dwc_otg_hcd_t *
+							dwc_otg_hcd);
+extern int32_t dwc_otg_hcd_handle_perio_tx_fifo_empty_intr(dwc_otg_hcd_t *
+							   dwc_otg_hcd);
+extern int32_t dwc_otg_hcd_handle_incomplete_periodic_intr(dwc_otg_hcd_t *
+							   dwc_otg_hcd);
+extern int32_t dwc_otg_hcd_handle_port_intr(dwc_otg_hcd_t * dwc_otg_hcd);
+extern int32_t dwc_otg_hcd_handle_conn_id_status_change_intr(dwc_otg_hcd_t *
+							     dwc_otg_hcd);
+extern int32_t dwc_otg_hcd_handle_disconnect_intr(dwc_otg_hcd_t * dwc_otg_hcd);
+extern int32_t dwc_otg_hcd_handle_hc_intr(dwc_otg_hcd_t * dwc_otg_hcd);
+extern int32_t dwc_otg_hcd_handle_hc_n_intr(dwc_otg_hcd_t * dwc_otg_hcd,
+					    uint32_t num);
+extern int32_t dwc_otg_hcd_handle_session_req_intr(dwc_otg_hcd_t * dwc_otg_hcd);
+extern int32_t dwc_otg_hcd_handle_wakeup_detected_intr(dwc_otg_hcd_t *
+						       dwc_otg_hcd);
+/** @} */
+
+/** @name Schedule Queue Functions */
+/** @{ */
+
+/* Implemented in dwc_otg_hcd_queue.c */
+extern dwc_otg_qh_t *dwc_otg_hcd_qh_create(dwc_otg_hcd_t * hcd,
+					   dwc_otg_hcd_urb_t * urb, int atomic_alloc);
+extern void dwc_otg_hcd_qh_free(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh);
+extern int dwc_otg_hcd_qh_add(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh);
+extern void dwc_otg_hcd_qh_remove(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh);
+extern void dwc_otg_hcd_qh_deactivate(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh,
+				      int sched_csplit);
+
+/** Remove and free a QH */
+static inline void dwc_otg_hcd_qh_remove_and_free(dwc_otg_hcd_t * hcd,
+						  dwc_otg_qh_t * qh)
+{
+	dwc_irqflags_t flags;
+	DWC_SPINLOCK_IRQSAVE(hcd->lock, &flags);
+	dwc_otg_hcd_qh_remove(hcd, qh);
+	DWC_SPINUNLOCK_IRQRESTORE(hcd->lock, flags);
+	dwc_otg_hcd_qh_free(hcd, qh);
+}
+
+/** Allocates memory for a QH structure.
+ * @return Returns the memory allocate or NULL on error. */
+static inline dwc_otg_qh_t *dwc_otg_hcd_qh_alloc(int atomic_alloc)
+{
+	if (atomic_alloc)
+		return (dwc_otg_qh_t *) DWC_ALLOC_ATOMIC(sizeof(dwc_otg_qh_t));
+	else
+		return (dwc_otg_qh_t *) DWC_ALLOC(sizeof(dwc_otg_qh_t));
+}
+
+extern dwc_otg_qtd_t *dwc_otg_hcd_qtd_create(dwc_otg_hcd_urb_t * urb,
+					     int atomic_alloc);
+extern void dwc_otg_hcd_qtd_init(dwc_otg_qtd_t * qtd, dwc_otg_hcd_urb_t * urb);
+extern int dwc_otg_hcd_qtd_add(dwc_otg_qtd_t * qtd, dwc_otg_hcd_t * dwc_otg_hcd,
+			       dwc_otg_qh_t ** qh, int atomic_alloc);
+
+/** Allocates memory for a QTD structure.
+ * @return Returns the memory allocate or NULL on error. */
+static inline dwc_otg_qtd_t *dwc_otg_hcd_qtd_alloc(int atomic_alloc)
+{
+	if (atomic_alloc)
+		return (dwc_otg_qtd_t *) DWC_ALLOC_ATOMIC(sizeof(dwc_otg_qtd_t));
+	else
+		return (dwc_otg_qtd_t *) DWC_ALLOC(sizeof(dwc_otg_qtd_t));
+}
+
+/** Frees the memory for a QTD structure.  QTD should already be removed from
+ * list.
+ * @param qtd QTD to free.*/
+static inline void dwc_otg_hcd_qtd_free(dwc_otg_qtd_t * qtd)
+{
+	DWC_FREE(qtd);
+}
+
+/** Removes a QTD from list.
+ * @param hcd HCD instance.
+ * @param qtd QTD to remove from list.
+ * @param qh QTD belongs to.
+ */
+static inline void dwc_otg_hcd_qtd_remove(dwc_otg_hcd_t * hcd,
+					  dwc_otg_qtd_t * qtd,
+					  dwc_otg_qh_t * qh)
+{
+	DWC_CIRCLEQ_REMOVE(&qh->qtd_list, qtd, qtd_list_entry);
+}
+
+/** Remove and free a QTD
+  * Need to disable IRQ and hold hcd lock while calling this function out of
+  * interrupt servicing chain */
+static inline void dwc_otg_hcd_qtd_remove_and_free(dwc_otg_hcd_t * hcd,
+						   dwc_otg_qtd_t * qtd,
+						   dwc_otg_qh_t * qh)
+{
+	dwc_otg_hcd_qtd_remove(hcd, qtd, qh);
+	dwc_otg_hcd_qtd_free(qtd);
+}
+
+/** @} */
+
+/** @name Descriptor DMA Supporting Functions */
+/** @{ */
+
+extern void dwc_otg_hcd_start_xfer_ddma(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh);
+extern void dwc_otg_hcd_complete_xfer_ddma(dwc_otg_hcd_t * hcd,
+					   dwc_hc_t * hc,
+					   dwc_otg_hc_regs_t * hc_regs,
+					   dwc_otg_halt_status_e halt_status);
+
+extern int dwc_otg_hcd_qh_init_ddma(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh);
+extern void dwc_otg_hcd_qh_free_ddma(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh);
+
+/** @} */
+
+/** @name Internal Functions */
+/** @{ */
+dwc_otg_qh_t *dwc_urb_to_qh(dwc_otg_hcd_urb_t * urb);
+/** @} */
+
+#ifdef CONFIG_USB_DWC_OTG_LPM
+extern int dwc_otg_hcd_get_hc_for_lpm_tran(dwc_otg_hcd_t * hcd,
+					   uint8_t devaddr);
+extern void dwc_otg_hcd_free_hc_from_lpm(dwc_otg_hcd_t * hcd);
+#endif
+
+/** Gets the QH that contains the list_head */
+#define dwc_list_to_qh(_list_head_ptr_) container_of(_list_head_ptr_, dwc_otg_qh_t, qh_list_entry)
+
+/** Gets the QTD that contains the list_head */
+#define dwc_list_to_qtd(_list_head_ptr_) container_of(_list_head_ptr_, dwc_otg_qtd_t, qtd_list_entry)
+
+/** Check if QH is non-periodic  */
+#define dwc_qh_is_non_per(_qh_ptr_) ((_qh_ptr_->ep_type == UE_BULK) || \
+				     (_qh_ptr_->ep_type == UE_CONTROL))
+
+/** High bandwidth multiplier as encoded in highspeed endpoint descriptors */
+#define dwc_hb_mult(wMaxPacketSize) (1 + (((wMaxPacketSize) >> 11) & 0x03))
+
+/** Packet size for any kind of endpoint descriptor */
+#define dwc_max_packet(wMaxPacketSize) ((wMaxPacketSize) & 0x07ff)
+
+/**
+ * Returns true if _frame1 is less than or equal to _frame2. The comparison is
+ * done modulo DWC_HFNUM_MAX_FRNUM. This accounts for the rollover of the
+ * frame number when the max frame number is reached.
+ */
+static inline int dwc_frame_num_le(uint16_t frame1, uint16_t frame2)
+{
+	return ((frame2 - frame1) & DWC_HFNUM_MAX_FRNUM) <=
+	    (DWC_HFNUM_MAX_FRNUM >> 1);
+}
+
+/**
+ * Returns true if _frame1 is greater than _frame2. The comparison is done
+ * modulo DWC_HFNUM_MAX_FRNUM. This accounts for the rollover of the frame
+ * number when the max frame number is reached.
+ */
+static inline int dwc_frame_num_gt(uint16_t frame1, uint16_t frame2)
+{
+	return (frame1 != frame2) &&
+	    (((frame1 - frame2) & DWC_HFNUM_MAX_FRNUM) <
+	     (DWC_HFNUM_MAX_FRNUM >> 1));
+}
+
+/**
+ * Increments _frame by the amount specified by _inc. The addition is done
+ * modulo DWC_HFNUM_MAX_FRNUM. Returns the incremented value.
+ */
+static inline uint16_t dwc_frame_num_inc(uint16_t frame, uint16_t inc)
+{
+	return (frame + inc) & DWC_HFNUM_MAX_FRNUM;
+}
+
+static inline uint16_t dwc_full_frame_num(uint16_t frame)
+{
+	return (frame & DWC_HFNUM_MAX_FRNUM) >> 3;
+}
+
+static inline uint16_t dwc_micro_frame_num(uint16_t frame)
+{
+	return frame & 0x7;
+}
+
+extern void init_hcd_usecs(dwc_otg_hcd_t *_hcd);
+
+void dwc_otg_hcd_save_data_toggle(dwc_hc_t * hc,
+				  dwc_otg_hc_regs_t * hc_regs,
+				  dwc_otg_qtd_t * qtd);
+
+#ifdef DEBUG
+/**
+ * Macro to sample the remaining PHY clocks left in the current frame. This
+ * may be used during debugging to determine the average time it takes to
+ * execute sections of code. There are two possible sample points, "a" and
+ * "b", so the _letter argument must be one of these values.
+ *
+ * To dump the average sample times, read the "hcd_frrem" sysfs attribute. For
+ * example, "cat /sys/devices/lm0/hcd_frrem".
+ */
+#define dwc_sample_frrem(_hcd, _qh, _letter) \
+{ \
+	hfnum_data_t hfnum; \
+	dwc_otg_qtd_t *qtd; \
+	qtd = list_entry(_qh->qtd_list.next, dwc_otg_qtd_t, qtd_list_entry); \
+	if (usb_pipeint(qtd->urb->pipe) && _qh->start_split_frame != 0 && !qtd->complete_split) { \
+		hfnum.d32 = DWC_READ_REG32(&_hcd->core_if->host_if->host_global_regs->hfnum); \
+		switch (hfnum.b.frnum & 0x7) { \
+		case 7: \
+			_hcd->hfnum_7_samples_##_letter++; \
+			_hcd->hfnum_7_frrem_accum_##_letter += hfnum.b.frrem; \
+			break; \
+		case 0: \
+			_hcd->hfnum_0_samples_##_letter++; \
+			_hcd->hfnum_0_frrem_accum_##_letter += hfnum.b.frrem; \
+			break; \
+		default: \
+			_hcd->hfnum_other_samples_##_letter++; \
+			_hcd->hfnum_other_frrem_accum_##_letter += hfnum.b.frrem; \
+			break; \
+		} \
+	} \
+}
+#else
+#define dwc_sample_frrem(_hcd, _qh, _letter)
+#endif
+#endif
+#endif /* DWC_DEVICE_ONLY */
diff -Nur --no-dereference linux-5.10.52-orig/drivers/usb/host/dwc_otg/dwc_otg_hcd_if.h linux-5.10.52-v7l+/drivers/usb/host/dwc_otg/dwc_otg_hcd_if.h
--- linux-5.10.52-orig/drivers/usb/host/dwc_otg/dwc_otg_hcd_if.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/usb/host/dwc_otg/dwc_otg_hcd_if.h	2021-07-25 16:46:28.697909777 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/* ==========================================================================
+ * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_hcd_if.h $
+ * $Revision: #12 $
+ * $Date: 2011/10/26 $
+ * $Change: 1873028 $
+ *
+ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter,
+ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless
+ * otherwise expressly agreed to in writing between Synopsys and you.
+ *
+ * The Software IS NOT an item of Licensed Software or Licensed Product under
+ * any End User Software License Agreement or Agreement for Licensed Product
+ * with Synopsys or any supplement thereto. You are permitted to use and
+ * redistribute this Software in source and binary forms, with or without
+ * modification, provided that redistributions of source code must retain this
+ * notice. You may not view, use, disclose, copy or distribute this file or
+ * any information contained herein except pursuant to this license grant from
+ * Synopsys. If you do not agree with this notice, including the disclaimer
+ * below, then you are not authorized to use the Software.
+ *
+ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * ========================================================================== */
+#ifndef DWC_DEVICE_ONLY
+#ifndef __DWC_HCD_IF_H__
+#define __DWC_HCD_IF_H__
+
+#include "dwc_otg_core_if.h"
+
+/** @file
+ * This file defines DWC_OTG HCD Core API.
+ */
+
+struct dwc_otg_hcd;
+typedef struct dwc_otg_hcd dwc_otg_hcd_t;
+
+struct dwc_otg_hcd_urb;
+typedef struct dwc_otg_hcd_urb dwc_otg_hcd_urb_t;
+
+/** @name HCD Function Driver Callbacks */
+/** @{ */
+
+/** This function is called whenever core switches to host mode. */
+typedef int (*dwc_otg_hcd_start_cb_t) (dwc_otg_hcd_t * hcd);
+
+/** This function is called when device has been disconnected */
+typedef int (*dwc_otg_hcd_disconnect_cb_t) (dwc_otg_hcd_t * hcd);
+
+/** Wrapper provides this function to HCD to core, so it can get hub information to which device is connected */
+typedef int (*dwc_otg_hcd_hub_info_from_urb_cb_t) (dwc_otg_hcd_t * hcd,
+						   void *urb_handle,
+						   uint32_t * hub_addr,
+						   uint32_t * port_addr);
+/** Via this function HCD core gets device speed */
+typedef int (*dwc_otg_hcd_speed_from_urb_cb_t) (dwc_otg_hcd_t * hcd,
+						void *urb_handle);
+
+/** This function is called when urb is completed */
+typedef int (*dwc_otg_hcd_complete_urb_cb_t) (dwc_otg_hcd_t * hcd,
+					      void *urb_handle,
+					      dwc_otg_hcd_urb_t * dwc_otg_urb,
+					      int32_t status);
+
+/** Via this function HCD core gets b_hnp_enable parameter */
+typedef int (*dwc_otg_hcd_get_b_hnp_enable) (dwc_otg_hcd_t * hcd);
+
+struct dwc_otg_hcd_function_ops {
+	dwc_otg_hcd_start_cb_t start;
+	dwc_otg_hcd_disconnect_cb_t disconnect;
+	dwc_otg_hcd_hub_info_from_urb_cb_t hub_info;
+	dwc_otg_hcd_speed_from_urb_cb_t speed;
+	dwc_otg_hcd_complete_urb_cb_t complete;
+	dwc_otg_hcd_get_b_hnp_enable get_b_hnp_enable;
+};
+/** @} */
+
+/** @name HCD Core API */
+/** @{ */
+/** This function allocates dwc_otg_hcd structure and returns pointer on it. */
+extern dwc_otg_hcd_t *dwc_otg_hcd_alloc_hcd(void);
+
+/** This function should be called to initiate HCD Core.
+ *
+ * @param hcd The HCD
+ * @param core_if The DWC_OTG Core
+ *
+ * Returns -DWC_E_NO_MEMORY if no enough memory.
+ * Returns 0 on success
+ */
+extern int dwc_otg_hcd_init(dwc_otg_hcd_t * hcd, dwc_otg_core_if_t * core_if);
+
+/** Frees HCD
+ *
+ * @param hcd The HCD
+ */
+extern void dwc_otg_hcd_remove(dwc_otg_hcd_t * hcd);
+
+/** This function should be called on every hardware interrupt.
+ *
+ * @param dwc_otg_hcd The HCD
+ *
+ * Returns non zero if interrupt is handled
+ * Return 0 if interrupt is not handled
+ */
+extern int32_t dwc_otg_hcd_handle_intr(dwc_otg_hcd_t * dwc_otg_hcd);
+
+/** This function is used to handle the fast interrupt
+ *
+ */
+#ifdef CONFIG_ARM64
+extern void dwc_otg_hcd_handle_fiq(void);
+#else
+extern void __attribute__ ((naked)) dwc_otg_hcd_handle_fiq(void);
+#endif
+
+/**
+ * Returns private data set by
+ * dwc_otg_hcd_set_priv_data function.
+ *
+ * @param hcd The HCD
+ */
+extern void *dwc_otg_hcd_get_priv_data(dwc_otg_hcd_t * hcd);
+
+/**
+ * Set private data.
+ *
+ * @param hcd The HCD
+ * @param priv_data pointer to be stored in private data
+ */
+extern void dwc_otg_hcd_set_priv_data(dwc_otg_hcd_t * hcd, void *priv_data);
+
+/**
+ * This function initializes the HCD Core.
+ *
+ * @param hcd The HCD
+ * @param fops The Function Driver Operations data structure containing pointers to all callbacks.
+ *
+ * Returns -DWC_E_NO_DEVICE if Core is currently is in device mode.
+ * Returns 0 on success
+ */
+extern int dwc_otg_hcd_start(dwc_otg_hcd_t * hcd,
+			     struct dwc_otg_hcd_function_ops *fops);
+
+/**
+ * Halts the DWC_otg host mode operations in a clean manner. USB transfers are
+ * stopped.
+ *
+ * @param hcd The HCD
+ */
+extern void dwc_otg_hcd_stop(dwc_otg_hcd_t * hcd);
+
+/**
+ * Handles hub class-specific requests.
+ *
+ * @param dwc_otg_hcd The HCD
+ * @param typeReq Request Type
+ * @param wValue wValue from control request
+ * @param wIndex wIndex from control request
+ * @param buf data buffer
+ * @param wLength data buffer length
+ *
+ * Returns -DWC_E_INVALID if invalid argument is passed
+ * Returns 0 on success
+ */
+extern int dwc_otg_hcd_hub_control(dwc_otg_hcd_t * dwc_otg_hcd,
+				   uint16_t typeReq, uint16_t wValue,
+				   uint16_t wIndex, uint8_t * buf,
+				   uint16_t wLength);
+
+/**
+ * Returns otg port number.
+ *
+ * @param hcd The HCD
+ */
+extern uint32_t dwc_otg_hcd_otg_port(dwc_otg_hcd_t * hcd);
+
+/**
+ * Returns OTG version - either 1.3 or 2.0.
+ *
+ * @param core_if The core_if structure pointer
+ */
+extern uint16_t dwc_otg_get_otg_version(dwc_otg_core_if_t * core_if);
+
+/**
+ * Returns 1 if currently core is acting as B host, and 0 otherwise.
+ *
+ * @param hcd The HCD
+ */
+extern uint32_t dwc_otg_hcd_is_b_host(dwc_otg_hcd_t * hcd);
+
+/**
+ * Returns current frame number.
+ *
+ * @param hcd The HCD
+ */
+extern int dwc_otg_hcd_get_frame_number(dwc_otg_hcd_t * hcd);
+
+/**
+ * Dumps hcd state.
+ *
+ * @param hcd The HCD
+ */
+extern void dwc_otg_hcd_dump_state(dwc_otg_hcd_t * hcd);
+
+/**
+ * Dump the average frame remaining at SOF. This can be used to
+ * determine average interrupt latency. Frame remaining is also shown for
+ * start transfer and two additional sample points.
+ * Currently this function is not implemented.
+ *
+ * @param hcd The HCD
+ */
+extern void dwc_otg_hcd_dump_frrem(dwc_otg_hcd_t * hcd);
+
+/**
+ * Sends LPM transaction to the local device.
+ *
+ * @param hcd The HCD
+ * @param devaddr Device Address
+ * @param hird Host initiated resume duration
+ * @param bRemoteWake Value of bRemoteWake field in LPM transaction
+ *
+ * Returns negative value if sending LPM transaction was not succeeded.
+ * Returns 0 on success.
+ */
+extern int dwc_otg_hcd_send_lpm(dwc_otg_hcd_t * hcd, uint8_t devaddr,
+				uint8_t hird, uint8_t bRemoteWake);
+
+/* URB interface */
+
+/**
+ * Allocates memory for dwc_otg_hcd_urb structure.
+ * Allocated memory should be freed by call of DWC_FREE.
+ *
+ * @param hcd The HCD
+ * @param iso_desc_count Count of ISOC descriptors
+ * @param atomic_alloc Specefies whether to perform atomic allocation.
+ */
+extern dwc_otg_hcd_urb_t *dwc_otg_hcd_urb_alloc(dwc_otg_hcd_t * hcd,
+						int iso_desc_count,
+						int atomic_alloc);
+
+/**
+ * Set pipe information in URB.
+ *
+ * @param hcd_urb DWC_OTG URB
+ * @param devaddr Device Address
+ * @param ep_num Endpoint Number
+ * @param ep_type Endpoint Type
+ * @param ep_dir Endpoint Direction
+ * @param mps Max Packet Size
+ */
+extern void dwc_otg_hcd_urb_set_pipeinfo(dwc_otg_hcd_urb_t * hcd_urb,
+					 uint8_t devaddr, uint8_t ep_num,
+					 uint8_t ep_type, uint8_t ep_dir,
+					 uint16_t mps);
+
+/* Transfer flags */
+#define URB_GIVEBACK_ASAP 0x1
+#define URB_SEND_ZERO_PACKET 0x2
+
+/**
+ * Sets dwc_otg_hcd_urb parameters.
+ *
+ * @param urb DWC_OTG URB allocated by dwc_otg_hcd_urb_alloc function.
+ * @param urb_handle Unique handle for request, this will be passed back
+ * to function driver in completion callback.
+ * @param buf The buffer for the data
+ * @param dma The DMA buffer for the data
+ * @param buflen Transfer length
+ * @param sp Buffer for setup data
+ * @param sp_dma DMA address of setup data buffer
+ * @param flags Transfer flags
+ * @param interval Polling interval for interrupt or isochronous transfers.
+ */
+extern void dwc_otg_hcd_urb_set_params(dwc_otg_hcd_urb_t * urb,
+				       void *urb_handle, void *buf,
+				       dwc_dma_t dma, uint32_t buflen, void *sp,
+				       dwc_dma_t sp_dma, uint32_t flags,
+				       uint16_t interval);
+
+/** Gets status from dwc_otg_hcd_urb
+ *
+ * @param dwc_otg_urb DWC_OTG URB
+ */
+extern uint32_t dwc_otg_hcd_urb_get_status(dwc_otg_hcd_urb_t * dwc_otg_urb);
+
+/** Gets actual length from dwc_otg_hcd_urb
+ *
+ * @param dwc_otg_urb DWC_OTG URB
+ */
+extern uint32_t dwc_otg_hcd_urb_get_actual_length(dwc_otg_hcd_urb_t *
+						  dwc_otg_urb);
+
+/** Gets error count from dwc_otg_hcd_urb. Only for ISOC URBs
+ *
+ * @param dwc_otg_urb DWC_OTG URB
+ */
+extern uint32_t dwc_otg_hcd_urb_get_error_count(dwc_otg_hcd_urb_t *
+						dwc_otg_urb);
+
+/** Set ISOC descriptor offset and length
+ *
+ * @param dwc_otg_urb DWC_OTG URB
+ * @param desc_num ISOC descriptor number
+ * @param offset Offset from beginig of buffer.
+ * @param length Transaction length
+ */
+extern void dwc_otg_hcd_urb_set_iso_desc_params(dwc_otg_hcd_urb_t * dwc_otg_urb,
+						int desc_num, uint32_t offset,
+						uint32_t length);
+
+/** Get status of ISOC descriptor, specified by desc_num
+ *
+ * @param dwc_otg_urb DWC_OTG URB
+ * @param desc_num ISOC descriptor number
+ */
+extern uint32_t dwc_otg_hcd_urb_get_iso_desc_status(dwc_otg_hcd_urb_t *
+						    dwc_otg_urb, int desc_num);
+
+/** Get actual length of ISOC descriptor, specified by desc_num
+ *
+ * @param dwc_otg_urb DWC_OTG URB
+ * @param desc_num ISOC descriptor number
+ */
+extern uint32_t dwc_otg_hcd_urb_get_iso_desc_actual_length(dwc_otg_hcd_urb_t *
+							   dwc_otg_urb,
+							   int desc_num);
+
+/** Queue URB. After transfer is completes, the complete callback will be called with the URB status
+ *
+ * @param dwc_otg_hcd The HCD
+ * @param dwc_otg_urb DWC_OTG URB
+ * @param ep_handle Out parameter for returning endpoint handle
+ * @param atomic_alloc Flag to do atomic allocation if needed
+ *
+ * Returns -DWC_E_NO_DEVICE if no device is connected.
+ * Returns -DWC_E_NO_MEMORY if there is no enough memory.
+ * Returns 0 on success.
+ */
+extern int dwc_otg_hcd_urb_enqueue(dwc_otg_hcd_t * dwc_otg_hcd,
+				   dwc_otg_hcd_urb_t * dwc_otg_urb,
+				   void **ep_handle, int atomic_alloc);
+
+/** De-queue the specified URB
+ *
+ * @param dwc_otg_hcd The HCD
+ * @param dwc_otg_urb DWC_OTG URB
+ */
+extern int dwc_otg_hcd_urb_dequeue(dwc_otg_hcd_t * dwc_otg_hcd,
+				   dwc_otg_hcd_urb_t * dwc_otg_urb);
+
+/** Frees resources in the DWC_otg controller related to a given endpoint.
+ * Any URBs for the endpoint must already be dequeued.
+ *
+ * @param hcd The HCD
+ * @param ep_handle Endpoint handle, returned by dwc_otg_hcd_urb_enqueue function
+ * @param retry Number of retries if there are queued transfers.
+ *
+ * Returns -DWC_E_INVALID if invalid arguments are passed.
+ * Returns 0 on success
+ */
+extern int dwc_otg_hcd_endpoint_disable(dwc_otg_hcd_t * hcd, void *ep_handle,
+					int retry);
+
+/* Resets the data toggle in qh structure. This function can be called from
+ * usb_clear_halt routine.
+ *
+ * @param hcd The HCD
+ * @param ep_handle Endpoint handle, returned by dwc_otg_hcd_urb_enqueue function
+ *
+ * Returns -DWC_E_INVALID if invalid arguments are passed.
+ * Returns 0 on success
+ */
+extern int dwc_otg_hcd_endpoint_reset(dwc_otg_hcd_t * hcd, void *ep_handle);
+
+/** Returns 1 if status of specified port is changed and 0 otherwise.
+ *
+ * @param hcd The HCD
+ * @param port Port number
+ */
+extern int dwc_otg_hcd_is_status_changed(dwc_otg_hcd_t * hcd, int port);
+
+/** Call this function to check if bandwidth was allocated for specified endpoint.
+ * Only for ISOC and INTERRUPT endpoints.
+ *
+ * @param hcd The HCD
+ * @param ep_handle Endpoint handle
+ */
+extern int dwc_otg_hcd_is_bandwidth_allocated(dwc_otg_hcd_t * hcd,
+					      void *ep_handle);
+
+/** Call this function to check if bandwidth was freed for specified endpoint.
+ *
+ * @param hcd The HCD
+ * @param ep_handle Endpoint handle
+ */
+extern int dwc_otg_hcd_is_bandwidth_freed(dwc_otg_hcd_t * hcd, void *ep_handle);
+
+/** Returns bandwidth allocated for specified endpoint in microseconds.
+ * Only for ISOC and INTERRUPT endpoints.
+ *
+ * @param hcd The HCD
+ * @param ep_handle Endpoint handle
+ */
+extern uint8_t dwc_otg_hcd_get_ep_bandwidth(dwc_otg_hcd_t * hcd,
+					    void *ep_handle);
+
+/** @} */
+
+#endif /* __DWC_HCD_IF_H__ */
+#endif /* DWC_DEVICE_ONLY */
diff -Nur --no-dereference linux-5.10.52-orig/drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c linux-5.10.52-v7l+/drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c
--- linux-5.10.52-orig/drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c	2021-07-25 16:46:28.697909777 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/* ==========================================================================
+ * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_hcd_intr.c $
+ * $Revision: #89 $
+ * $Date: 2011/10/20 $
+ * $Change: 1869487 $
+ *
+ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter,
+ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless
+ * otherwise expressly agreed to in writing between Synopsys and you.
+ *
+ * The Software IS NOT an item of Licensed Software or Licensed Product under
+ * any End User Software License Agreement or Agreement for Licensed Product
+ * with Synopsys or any supplement thereto. You are permitted to use and
+ * redistribute this Software in source and binary forms, with or without
+ * modification, provided that redistributions of source code must retain this
+ * notice. You may not view, use, disclose, copy or distribute this file or
+ * any information contained herein except pursuant to this license grant from
+ * Synopsys. If you do not agree with this notice, including the disclaimer
+ * below, then you are not authorized to use the Software.
+ *
+ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * ========================================================================== */
+#ifndef DWC_DEVICE_ONLY
+
+#include "dwc_otg_hcd.h"
+#include "dwc_otg_regs.h"
+
+#include <linux/jiffies.h>
+#ifdef CONFIG_ARM
+#include <asm/fiq.h>
+#endif
+
+extern bool microframe_schedule;
+
+/** @file
+ * This file contains the implementation of the HCD Interrupt handlers.
+ */
+
+int fiq_done, int_done;
+
+#ifdef FIQ_DEBUG
+char buffer[1000*16];
+int wptr;
+void notrace _fiq_print(FIQDBG_T dbg_lvl, char *fmt, ...)
+{
+	FIQDBG_T dbg_lvl_req = FIQDBG_PORTHUB;
+	va_list args;
+	char text[17];
+	hfnum_data_t hfnum = { .d32 = FIQ_READ(dwc_regs_base + 0x408) };
+
+	if(dbg_lvl & dbg_lvl_req || dbg_lvl == FIQDBG_ERR)
+	{
+		local_fiq_disable();
+		snprintf(text, 9, "%4d%d:%d ", hfnum.b.frnum/8, hfnum.b.frnum%8, 8 - hfnum.b.frrem/937);
+		va_start(args, fmt);
+		vsnprintf(text+8, 9, fmt, args);
+		va_end(args);
+
+		memcpy(buffer + wptr, text, 16);
+		wptr = (wptr + 16) % sizeof(buffer);
+		local_fiq_enable();
+	}
+}
+#endif
+
+/** This function handles interrupts for the HCD. */
+int32_t dwc_otg_hcd_handle_intr(dwc_otg_hcd_t * dwc_otg_hcd)
+{
+	int retval = 0;
+	static int last_time;
+	dwc_otg_core_if_t *core_if = dwc_otg_hcd->core_if;
+	gintsts_data_t gintsts;
+	gintmsk_data_t gintmsk;
+	hfnum_data_t hfnum;
+	haintmsk_data_t haintmsk;
+
+#ifdef DEBUG
+	dwc_otg_core_global_regs_t *global_regs = core_if->core_global_regs;
+
+#endif
+
+	gintsts.d32 = DWC_READ_REG32(&core_if->core_global_regs->gintsts);
+	gintmsk.d32 = DWC_READ_REG32(&core_if->core_global_regs->gintmsk);
+
+	/* Exit from ISR if core is hibernated */
+	if (core_if->hibernation_suspend == 1) {
+		goto exit_handler_routine;
+	}
+	DWC_SPINLOCK(dwc_otg_hcd->lock);
+	/* Check if HOST Mode */
+	if (dwc_otg_is_host_mode(core_if)) {
+		if (fiq_enable) {
+			local_fiq_disable();
+			fiq_fsm_spin_lock(&dwc_otg_hcd->fiq_state->lock);
+			/* Pull in from the FIQ's disabled mask */
+			gintmsk.d32 = gintmsk.d32 | ~(dwc_otg_hcd->fiq_state->gintmsk_saved.d32);
+			dwc_otg_hcd->fiq_state->gintmsk_saved.d32 = ~0;
+		}
+
+		if (fiq_fsm_enable && ( 0x0000FFFF & ~(dwc_otg_hcd->fiq_state->haintmsk_saved.b2.chint))) {
+			gintsts.b.hcintr = 1;
+		}
+
+		/* Danger will robinson: fake a SOF if necessary */
+		if (fiq_fsm_enable && (dwc_otg_hcd->fiq_state->gintmsk_saved.b.sofintr == 1)) {
+			gintsts.b.sofintr = 1;
+		}
+		gintsts.d32 &= gintmsk.d32;
+
+		if (fiq_enable) {
+			fiq_fsm_spin_unlock(&dwc_otg_hcd->fiq_state->lock);
+			local_fiq_enable();
+		}
+
+		if (!gintsts.d32) {
+			goto exit_handler_routine;
+		}
+
+#ifdef DEBUG
+		// We should be OK doing this because the common interrupts should already have been serviced
+		/* Don't print debug message in the interrupt handler on SOF */
+#ifndef DEBUG_SOF
+		if (gintsts.d32 != DWC_SOF_INTR_MASK)
+#endif
+			DWC_DEBUGPL(DBG_HCDI, "\n");
+#endif
+
+#ifdef DEBUG
+#ifndef DEBUG_SOF
+		if (gintsts.d32 != DWC_SOF_INTR_MASK)
+#endif
+			DWC_DEBUGPL(DBG_HCDI,
+				    "DWC OTG HCD Interrupt Detected gintsts&gintmsk=0x%08x core_if=%p\n",
+				    gintsts.d32, core_if);
+#endif
+		hfnum.d32 = DWC_READ_REG32(&dwc_otg_hcd->core_if->host_if->host_global_regs->hfnum);
+		if (gintsts.b.sofintr) {
+			retval |= dwc_otg_hcd_handle_sof_intr(dwc_otg_hcd);
+		}
+
+		if (gintsts.b.rxstsqlvl) {
+			retval |=
+			    dwc_otg_hcd_handle_rx_status_q_level_intr
+			    (dwc_otg_hcd);
+		}
+		if (gintsts.b.nptxfempty) {
+			retval |=
+			    dwc_otg_hcd_handle_np_tx_fifo_empty_intr
+			    (dwc_otg_hcd);
+		}
+		if (gintsts.b.i2cintr) {
+			/** @todo Implement i2cintr handler. */
+		}
+		if (gintsts.b.portintr) {
+
+			gintmsk_data_t gintmsk = { .b.portintr = 1};
+			retval |= dwc_otg_hcd_handle_port_intr(dwc_otg_hcd);
+			if (fiq_enable) {
+				local_fiq_disable();
+				fiq_fsm_spin_lock(&dwc_otg_hcd->fiq_state->lock);
+				DWC_MODIFY_REG32(&dwc_otg_hcd->core_if->core_global_regs->gintmsk, 0, gintmsk.d32);
+				fiq_fsm_spin_unlock(&dwc_otg_hcd->fiq_state->lock);
+				local_fiq_enable();
+			} else {
+				DWC_MODIFY_REG32(&dwc_otg_hcd->core_if->core_global_regs->gintmsk, 0, gintmsk.d32);
+			}
+		}
+		if (gintsts.b.hcintr) {
+			retval |= dwc_otg_hcd_handle_hc_intr(dwc_otg_hcd);
+		}
+		if (gintsts.b.ptxfempty) {
+			retval |=
+			    dwc_otg_hcd_handle_perio_tx_fifo_empty_intr
+			    (dwc_otg_hcd);
+		}
+#ifdef DEBUG
+#ifndef DEBUG_SOF
+		if (gintsts.d32 != DWC_SOF_INTR_MASK)
+#endif
+		{
+			DWC_DEBUGPL(DBG_HCDI,
+				    "DWC OTG HCD Finished Servicing Interrupts\n");
+			DWC_DEBUGPL(DBG_HCDV, "DWC OTG HCD gintsts=0x%08x\n",
+				    DWC_READ_REG32(&global_regs->gintsts));
+			DWC_DEBUGPL(DBG_HCDV, "DWC OTG HCD gintmsk=0x%08x\n",
+				    DWC_READ_REG32(&global_regs->gintmsk));
+		}
+#endif
+
+#ifdef DEBUG
+#ifndef DEBUG_SOF
+		if (gintsts.d32 != DWC_SOF_INTR_MASK)
+#endif
+			DWC_DEBUGPL(DBG_HCDI, "\n");
+#endif
+
+	}
+
+exit_handler_routine:
+	if (fiq_enable)	{
+		gintmsk_data_t gintmsk_new;
+		haintmsk_data_t haintmsk_new;
+		local_fiq_disable();
+		fiq_fsm_spin_lock(&dwc_otg_hcd->fiq_state->lock);
+		gintmsk_new.d32 = *(volatile uint32_t *)&dwc_otg_hcd->fiq_state->gintmsk_saved.d32;
+		if(fiq_fsm_enable)
+			haintmsk_new.d32 = *(volatile uint32_t *)&dwc_otg_hcd->fiq_state->haintmsk_saved.d32;
+		else
+			haintmsk_new.d32 = 0x0000FFFF;
+
+		/* The FIQ could have sneaked another interrupt in. If so, don't clear MPHI */
+		if ((gintmsk_new.d32 == ~0) && (haintmsk_new.d32 == 0x0000FFFF)) {
+			if (dwc_otg_hcd->fiq_state->mphi_regs.swirq_clr) {
+				DWC_WRITE_REG32(dwc_otg_hcd->fiq_state->mphi_regs.swirq_clr, 1);
+			} else {
+				DWC_WRITE_REG32(dwc_otg_hcd->fiq_state->mphi_regs.intstat, (1<<16));
+			}
+			if (dwc_otg_hcd->fiq_state->mphi_int_count >= 50) {
+				fiq_print(FIQDBG_INT, dwc_otg_hcd->fiq_state, "MPHI CLR");
+					DWC_WRITE_REG32(dwc_otg_hcd->fiq_state->mphi_regs.ctrl, ((1<<31) + (1<<16)));
+					while (!(DWC_READ_REG32(dwc_otg_hcd->fiq_state->mphi_regs.ctrl) & (1 << 17)))
+						;
+					DWC_WRITE_REG32(dwc_otg_hcd->fiq_state->mphi_regs.ctrl, (1<<31));
+					dwc_otg_hcd->fiq_state->mphi_int_count = 0;
+			}
+			int_done++;
+		}
+		haintmsk.d32 = DWC_READ_REG32(&core_if->host_if->host_global_regs->haintmsk);
+		/* Re-enable interrupts that the FIQ masked (first time round) */
+		FIQ_WRITE(dwc_otg_hcd->fiq_state->dwc_regs_base + GINTMSK, gintmsk.d32);
+		fiq_fsm_spin_unlock(&dwc_otg_hcd->fiq_state->lock);
+		local_fiq_enable();
+
+		if ((jiffies / HZ) > last_time) {
+			//dwc_otg_qh_t *qh;
+			//dwc_list_link_t *cur;
+			/* Once a second output the fiq and irq numbers, useful for debug */
+			last_time = jiffies / HZ;
+		//	 DWC_WARN("np_kick=%d AHC=%d sched_frame=%d cur_frame=%d int_done=%d fiq_done=%d",
+		//	dwc_otg_hcd->fiq_state->kick_np_queues, dwc_otg_hcd->available_host_channels,
+		//	dwc_otg_hcd->fiq_state->next_sched_frame, hfnum.b.frnum, int_done, dwc_otg_hcd->fiq_state->fiq_done);
+			 //printk(KERN_WARNING "Periodic queues:\n");
+		}
+	}
+
+	DWC_SPINUNLOCK(dwc_otg_hcd->lock);
+	return retval;
+}
+
+#ifdef DWC_TRACK_MISSED_SOFS
+
+#warning Compiling code to track missed SOFs
+#define FRAME_NUM_ARRAY_SIZE 1000
+/**
+ * This function is for debug only.
+ */
+static inline void track_missed_sofs(uint16_t curr_frame_number)
+{
+	static uint16_t frame_num_array[FRAME_NUM_ARRAY_SIZE];
+	static uint16_t last_frame_num_array[FRAME_NUM_ARRAY_SIZE];
+	static int frame_num_idx = 0;
+	static uint16_t last_frame_num = DWC_HFNUM_MAX_FRNUM;
+	static int dumped_frame_num_array = 0;
+
+	if (frame_num_idx < FRAME_NUM_ARRAY_SIZE) {
+		if (((last_frame_num + 1) & DWC_HFNUM_MAX_FRNUM) !=
+		    curr_frame_number) {
+			frame_num_array[frame_num_idx] = curr_frame_number;
+			last_frame_num_array[frame_num_idx++] = last_frame_num;
+		}
+	} else if (!dumped_frame_num_array) {
+		int i;
+		DWC_PRINTF("Frame     Last Frame\n");
+		DWC_PRINTF("-----     ----------\n");
+		for (i = 0; i < FRAME_NUM_ARRAY_SIZE; i++) {
+			DWC_PRINTF("0x%04x    0x%04x\n",
+				   frame_num_array[i], last_frame_num_array[i]);
+		}
+		dumped_frame_num_array = 1;
+	}
+	last_frame_num = curr_frame_number;
+}
+#endif
+
+/**
+ * Handles the start-of-frame interrupt in host mode. Non-periodic
+ * transactions may be queued to the DWC_otg controller for the current
+ * (micro)frame. Periodic transactions may be queued to the controller for the
+ * next (micro)frame.
+ */
+int32_t dwc_otg_hcd_handle_sof_intr(dwc_otg_hcd_t * hcd)
+{
+	hfnum_data_t hfnum;
+	gintsts_data_t gintsts = { .d32 = 0 };
+	dwc_list_link_t *qh_entry;
+	dwc_otg_qh_t *qh;
+	dwc_otg_transaction_type_e tr_type;
+	int did_something = 0;
+	int32_t next_sched_frame = -1;
+
+	hfnum.d32 =
+	    DWC_READ_REG32(&hcd->core_if->host_if->host_global_regs->hfnum);
+
+#ifdef DEBUG_SOF
+	DWC_DEBUGPL(DBG_HCD, "--Start of Frame Interrupt--\n");
+#endif
+	hcd->frame_number = hfnum.b.frnum;
+
+#ifdef DEBUG
+	hcd->frrem_accum += hfnum.b.frrem;
+	hcd->frrem_samples++;
+#endif
+
+#ifdef DWC_TRACK_MISSED_SOFS
+	track_missed_sofs(hcd->frame_number);
+#endif
+	/* Determine whether any periodic QHs should be executed. */
+	qh_entry = DWC_LIST_FIRST(&hcd->periodic_sched_inactive);
+	while (qh_entry != &hcd->periodic_sched_inactive) {
+		qh = DWC_LIST_ENTRY(qh_entry, dwc_otg_qh_t, qh_list_entry);
+		qh_entry = qh_entry->next;
+		if (dwc_frame_num_le(qh->sched_frame, hcd->frame_number)) {
+
+			/*
+			 * Move QH to the ready list to be executed next
+			 * (micro)frame.
+			 */
+			DWC_LIST_MOVE_HEAD(&hcd->periodic_sched_ready,
+					   &qh->qh_list_entry);
+
+			did_something = 1;
+		}
+		else
+		{
+			if(next_sched_frame < 0 || dwc_frame_num_le(qh->sched_frame, next_sched_frame))
+			{
+				next_sched_frame = qh->sched_frame;
+			}
+		}
+	}
+	if (fiq_enable)
+		hcd->fiq_state->next_sched_frame = next_sched_frame;
+
+	tr_type = dwc_otg_hcd_select_transactions(hcd);
+	if (tr_type != DWC_OTG_TRANSACTION_NONE) {
+		dwc_otg_hcd_queue_transactions(hcd, tr_type);
+		did_something = 1;
+	}
+
+	/* Clear interrupt - but do not trample on the FIQ sof */
+	if (!fiq_fsm_enable) {
+		gintsts.b.sofintr = 1;
+		DWC_WRITE_REG32(&hcd->core_if->core_global_regs->gintsts, gintsts.d32);
+	}
+	return 1;
+}
+
+/** Handles the Rx Status Queue Level Interrupt, which indicates that there is at
+ * least one packet in the Rx FIFO.  The packets are moved from the FIFO to
+ * memory if the DWC_otg controller is operating in Slave mode. */
+int32_t dwc_otg_hcd_handle_rx_status_q_level_intr(dwc_otg_hcd_t * dwc_otg_hcd)
+{
+	host_grxsts_data_t grxsts;
+	dwc_hc_t *hc = NULL;
+
+	DWC_DEBUGPL(DBG_HCD, "--RxStsQ Level Interrupt--\n");
+
+	grxsts.d32 =
+	    DWC_READ_REG32(&dwc_otg_hcd->core_if->core_global_regs->grxstsp);
+
+	hc = dwc_otg_hcd->hc_ptr_array[grxsts.b.chnum];
+	if (!hc) {
+		DWC_ERROR("Unable to get corresponding channel\n");
+		return 0;
+	}
+
+	/* Packet Status */
+	DWC_DEBUGPL(DBG_HCDV, "    Ch num = %d\n", grxsts.b.chnum);
+	DWC_DEBUGPL(DBG_HCDV, "    Count = %d\n", grxsts.b.bcnt);
+	DWC_DEBUGPL(DBG_HCDV, "    DPID = %d, hc.dpid = %d\n", grxsts.b.dpid,
+		    hc->data_pid_start);
+	DWC_DEBUGPL(DBG_HCDV, "    PStatus = %d\n", grxsts.b.pktsts);
+
+	switch (grxsts.b.pktsts) {
+	case DWC_GRXSTS_PKTSTS_IN:
+		/* Read the data into the host buffer. */
+		if (grxsts.b.bcnt > 0) {
+			dwc_otg_read_packet(dwc_otg_hcd->core_if,
+					    hc->xfer_buff, grxsts.b.bcnt);
+
+			/* Update the HC fields for the next packet received. */
+			hc->xfer_count += grxsts.b.bcnt;
+			hc->xfer_buff += grxsts.b.bcnt;
+		}
+
+	case DWC_GRXSTS_PKTSTS_IN_XFER_COMP:
+	case DWC_GRXSTS_PKTSTS_DATA_TOGGLE_ERR:
+	case DWC_GRXSTS_PKTSTS_CH_HALTED:
+		/* Handled in interrupt, just ignore data */
+		break;
+	default:
+		DWC_ERROR("RX_STS_Q Interrupt: Unknown status %d\n",
+			  grxsts.b.pktsts);
+		break;
+	}
+
+	return 1;
+}
+
+/** This interrupt occurs when the non-periodic Tx FIFO is half-empty. More
+ * data packets may be written to the FIFO for OUT transfers. More requests
+ * may be written to the non-periodic request queue for IN transfers. This
+ * interrupt is enabled only in Slave mode. */
+int32_t dwc_otg_hcd_handle_np_tx_fifo_empty_intr(dwc_otg_hcd_t * dwc_otg_hcd)
+{
+	DWC_DEBUGPL(DBG_HCD, "--Non-Periodic TxFIFO Empty Interrupt--\n");
+	dwc_otg_hcd_queue_transactions(dwc_otg_hcd,
+				       DWC_OTG_TRANSACTION_NON_PERIODIC);
+	return 1;
+}
+
+/** This interrupt occurs when the periodic Tx FIFO is half-empty. More data
+ * packets may be written to the FIFO for OUT transfers. More requests may be
+ * written to the periodic request queue for IN transfers. This interrupt is
+ * enabled only in Slave mode. */
+int32_t dwc_otg_hcd_handle_perio_tx_fifo_empty_intr(dwc_otg_hcd_t * dwc_otg_hcd)
+{
+	DWC_DEBUGPL(DBG_HCD, "--Periodic TxFIFO Empty Interrupt--\n");
+	dwc_otg_hcd_queue_transactions(dwc_otg_hcd,
+				       DWC_OTG_TRANSACTION_PERIODIC);
+	return 1;
+}
+
+/** There are multiple conditions that can cause a port interrupt. This function
+ * determines which interrupt conditions have occurred and handles them
+ * appropriately. */
+int32_t dwc_otg_hcd_handle_port_intr(dwc_otg_hcd_t * dwc_otg_hcd)
+{
+	int retval = 0;
+	hprt0_data_t hprt0;
+	hprt0_data_t hprt0_modify;
+
+	hprt0.d32 = DWC_READ_REG32(dwc_otg_hcd->core_if->host_if->hprt0);
+	hprt0_modify.d32 = DWC_READ_REG32(dwc_otg_hcd->core_if->host_if->hprt0);
+
+	/* Clear appropriate bits in HPRT0 to clear the interrupt bit in
+	 * GINTSTS */
+
+	hprt0_modify.b.prtena = 0;
+	hprt0_modify.b.prtconndet = 0;
+	hprt0_modify.b.prtenchng = 0;
+	hprt0_modify.b.prtovrcurrchng = 0;
+
+	/* Port Connect Detected
+	 * Set flag and clear if detected */
+	if (dwc_otg_hcd->core_if->hibernation_suspend == 1) {
+		// Dont modify port status if we are in hibernation state
+		hprt0_modify.b.prtconndet = 1;
+		hprt0_modify.b.prtenchng = 1;
+		DWC_WRITE_REG32(dwc_otg_hcd->core_if->host_if->hprt0, hprt0_modify.d32);
+		hprt0.d32 = DWC_READ_REG32(dwc_otg_hcd->core_if->host_if->hprt0);
+		return retval;
+	}
+
+	if (hprt0.b.prtconndet) {
+		/** @todo - check if steps performed in 'else' block should be perfromed regardles adp */
+		if (dwc_otg_hcd->core_if->adp_enable &&
+				dwc_otg_hcd->core_if->adp.vbuson_timer_started == 1) {
+			DWC_PRINTF("PORT CONNECT DETECTED ----------------\n");
+			DWC_TIMER_CANCEL(dwc_otg_hcd->core_if->adp.vbuson_timer);
+			dwc_otg_hcd->core_if->adp.vbuson_timer_started = 0;
+			/* TODO - check if this is required, as
+			 * host initialization was already performed
+			 * after initial ADP probing
+			 */
+			/*dwc_otg_hcd->core_if->adp.vbuson_timer_started = 0;
+			dwc_otg_core_init(dwc_otg_hcd->core_if);
+			dwc_otg_enable_global_interrupts(dwc_otg_hcd->core_if);
+			cil_hcd_start(dwc_otg_hcd->core_if);*/
+		} else {
+
+			DWC_DEBUGPL(DBG_HCD, "--Port Interrupt HPRT0=0x%08x "
+				    "Port Connect Detected--\n", hprt0.d32);
+			dwc_otg_hcd->flags.b.port_connect_status_change = 1;
+			dwc_otg_hcd->flags.b.port_connect_status = 1;
+			hprt0_modify.b.prtconndet = 1;
+
+			/* B-Device has connected, Delete the connection timer. */
+			DWC_TIMER_CANCEL(dwc_otg_hcd->conn_timer);
+		}
+		/* The Hub driver asserts a reset when it sees port connect
+		 * status change flag */
+		retval |= 1;
+	}
+
+	/* Port Enable Changed
+	 * Clear if detected - Set internal flag if disabled */
+	if (hprt0.b.prtenchng) {
+		DWC_DEBUGPL(DBG_HCD, "  --Port Interrupt HPRT0=0x%08x "
+			    "Port Enable Changed--\n", hprt0.d32);
+		hprt0_modify.b.prtenchng = 1;
+		if (hprt0.b.prtena == 1) {
+			hfir_data_t hfir;
+			int do_reset = 0;
+			dwc_otg_core_params_t *params =
+			    dwc_otg_hcd->core_if->core_params;
+			dwc_otg_core_global_regs_t *global_regs =
+			    dwc_otg_hcd->core_if->core_global_regs;
+			dwc_otg_host_if_t *host_if =
+			    dwc_otg_hcd->core_if->host_if;
+
+			dwc_otg_hcd->flags.b.port_speed = hprt0.b.prtspd;
+			if (microframe_schedule)
+				init_hcd_usecs(dwc_otg_hcd);
+
+			/* Every time when port enables calculate
+			 * HFIR.FrInterval
+			 */
+			hfir.d32 = DWC_READ_REG32(&host_if->host_global_regs->hfir);
+			hfir.b.frint = calc_frame_interval(dwc_otg_hcd->core_if);
+			DWC_WRITE_REG32(&host_if->host_global_regs->hfir, hfir.d32);
+
+			/* Check if we need to adjust the PHY clock speed for
+			 * low power and adjust it */
+			if (params->host_support_fs_ls_low_power) {
+				gusbcfg_data_t usbcfg;
+
+				usbcfg.d32 =
+				    DWC_READ_REG32(&global_regs->gusbcfg);
+
+				if (hprt0.b.prtspd == DWC_HPRT0_PRTSPD_LOW_SPEED
+				    || hprt0.b.prtspd ==
+				    DWC_HPRT0_PRTSPD_FULL_SPEED) {
+					/*
+					 * Low power
+					 */
+					hcfg_data_t hcfg;
+					if (usbcfg.b.phylpwrclksel == 0) {
+						/* Set PHY low power clock select for FS/LS devices */
+						usbcfg.b.phylpwrclksel = 1;
+						DWC_WRITE_REG32
+						    (&global_regs->gusbcfg,
+						     usbcfg.d32);
+						do_reset = 1;
+					}
+
+					hcfg.d32 =
+					    DWC_READ_REG32
+					    (&host_if->host_global_regs->hcfg);
+
+					if (hprt0.b.prtspd ==
+					    DWC_HPRT0_PRTSPD_LOW_SPEED
+					    && params->host_ls_low_power_phy_clk
+					    ==
+					    DWC_HOST_LS_LOW_POWER_PHY_CLK_PARAM_6MHZ)
+					{
+						/* 6 MHZ */
+						DWC_DEBUGPL(DBG_CIL,
+							    "FS_PHY programming HCFG to 6 MHz (Low Power)\n");
+						if (hcfg.b.fslspclksel !=
+						    DWC_HCFG_6_MHZ) {
+							hcfg.b.fslspclksel =
+							    DWC_HCFG_6_MHZ;
+							DWC_WRITE_REG32
+							    (&host_if->host_global_regs->hcfg,
+							     hcfg.d32);
+							do_reset = 1;
+						}
+					} else {
+						/* 48 MHZ */
+						DWC_DEBUGPL(DBG_CIL,
+							    "FS_PHY programming HCFG to 48 MHz ()\n");
+						if (hcfg.b.fslspclksel !=
+						    DWC_HCFG_48_MHZ) {
+							hcfg.b.fslspclksel =
+							    DWC_HCFG_48_MHZ;
+							DWC_WRITE_REG32
+							    (&host_if->host_global_regs->hcfg,
+							     hcfg.d32);
+							do_reset = 1;
+						}
+					}
+				} else {
+					/*
+					 * Not low power
+					 */
+					if (usbcfg.b.phylpwrclksel == 1) {
+						usbcfg.b.phylpwrclksel = 0;
+						DWC_WRITE_REG32
+						    (&global_regs->gusbcfg,
+						     usbcfg.d32);
+						do_reset = 1;
+					}
+				}
+
+				if (do_reset) {
+					DWC_TASK_SCHEDULE(dwc_otg_hcd->reset_tasklet);
+				}
+			}
+
+			if (!do_reset) {
+				/* Port has been enabled set the reset change flag */
+				dwc_otg_hcd->flags.b.port_reset_change = 1;
+			}
+		} else {
+			dwc_otg_hcd->flags.b.port_enable_change = 1;
+		}
+		retval |= 1;
+	}
+
+	/** Overcurrent Change Interrupt */
+	if (hprt0.b.prtovrcurrchng) {
+		DWC_DEBUGPL(DBG_HCD, "  --Port Interrupt HPRT0=0x%08x "
+			    "Port Overcurrent Changed--\n", hprt0.d32);
+		dwc_otg_hcd->flags.b.port_over_current_change = 1;
+		hprt0_modify.b.prtovrcurrchng = 1;
+		retval |= 1;
+	}
+
+	/* Clear Port Interrupts */
+	DWC_WRITE_REG32(dwc_otg_hcd->core_if->host_if->hprt0, hprt0_modify.d32);
+
+	return retval;
+}
+
+/** This interrupt indicates that one or more host channels has a pending
+ * interrupt. There are multiple conditions that can cause each host channel
+ * interrupt. This function determines which conditions have occurred for each
+ * host channel interrupt and handles them appropriately. */
+int32_t dwc_otg_hcd_handle_hc_intr(dwc_otg_hcd_t * dwc_otg_hcd)
+{
+	int i;
+	int retval = 0;
+	haint_data_t haint = { .d32 = 0 } ;
+
+	/* Clear appropriate bits in HCINTn to clear the interrupt bit in
+	 * GINTSTS */
+
+	if (!fiq_fsm_enable)
+		haint.d32 = dwc_otg_read_host_all_channels_intr(dwc_otg_hcd->core_if);
+
+	// Overwrite with saved interrupts from fiq handler
+	if(fiq_fsm_enable)
+	{
+		/* check the mask? */
+		local_fiq_disable();
+		fiq_fsm_spin_lock(&dwc_otg_hcd->fiq_state->lock);
+		haint.b2.chint |= ~(dwc_otg_hcd->fiq_state->haintmsk_saved.b2.chint);
+		dwc_otg_hcd->fiq_state->haintmsk_saved.b2.chint = ~0;
+		fiq_fsm_spin_unlock(&dwc_otg_hcd->fiq_state->lock);
+		local_fiq_enable();
+	}
+
+	for (i = 0; i < dwc_otg_hcd->core_if->core_params->host_channels; i++) {
+		if (haint.b2.chint & (1 << i)) {
+			retval |= dwc_otg_hcd_handle_hc_n_intr(dwc_otg_hcd, i);
+		}
+	}
+
+	return retval;
+}
+
+/**
+ * Gets the actual length of a transfer after the transfer halts. _halt_status
+ * holds the reason for the halt.
+ *
+ * For IN transfers where halt_status is DWC_OTG_HC_XFER_COMPLETE,
+ * *short_read is set to 1 upon return if less than the requested
+ * number of bytes were transferred. Otherwise, *short_read is set to 0 upon
+ * return. short_read may also be NULL on entry, in which case it remains
+ * unchanged.
+ */
+static uint32_t get_actual_xfer_length(dwc_hc_t * hc,
+				       dwc_otg_hc_regs_t * hc_regs,
+				       dwc_otg_qtd_t * qtd,
+				       dwc_otg_halt_status_e halt_status,
+				       int *short_read)
+{
+	hctsiz_data_t hctsiz;
+	uint32_t length;
+
+	if (short_read != NULL) {
+		*short_read = 0;
+	}
+	hctsiz.d32 = DWC_READ_REG32(&hc_regs->hctsiz);
+
+	if (halt_status == DWC_OTG_HC_XFER_COMPLETE) {
+		if (hc->ep_is_in) {
+			length = hc->xfer_len - hctsiz.b.xfersize;
+			if (short_read != NULL) {
+				*short_read = (hctsiz.b.xfersize != 0);
+			}
+		} else if (hc->qh->do_split) {
+				//length = split_out_xfersize[hc->hc_num];
+				length = qtd->ssplit_out_xfer_count;
+		} else {
+			length = hc->xfer_len;
+		}
+	} else {
+		/*
+		 * Must use the hctsiz.pktcnt field to determine how much data
+		 * has been transferred. This field reflects the number of
+		 * packets that have been transferred via the USB. This is
+		 * always an integral number of packets if the transfer was
+		 * halted before its normal completion. (Can't use the
+		 * hctsiz.xfersize field because that reflects the number of
+		 * bytes transferred via the AHB, not the USB).
+		 */
+		length =
+		    (hc->start_pkt_count - hctsiz.b.pktcnt) * hc->max_packet;
+	}
+
+	return length;
+}
+
+/**
+ * Updates the state of the URB after a Transfer Complete interrupt on the
+ * host channel. Updates the actual_length field of the URB based on the
+ * number of bytes transferred via the host channel. Sets the URB status
+ * if the data transfer is finished.
+ *
+ * @return 1 if the data transfer specified by the URB is completely finished,
+ * 0 otherwise.
+ */
+static int update_urb_state_xfer_comp(dwc_hc_t * hc,
+				      dwc_otg_hc_regs_t * hc_regs,
+				      dwc_otg_hcd_urb_t * urb,
+				      dwc_otg_qtd_t * qtd)
+{
+	int xfer_done = 0;
+	int short_read = 0;
+
+	int xfer_length;
+
+	xfer_length = get_actual_xfer_length(hc, hc_regs, qtd,
+					     DWC_OTG_HC_XFER_COMPLETE,
+					     &short_read);
+
+	if (urb->actual_length + xfer_length > urb->length) {
+		printk_once(KERN_DEBUG "dwc_otg: DEVICE:%03d : %s:%d:trimming xfer length\n",
+			hc->dev_addr, __func__, __LINE__);
+		xfer_length = urb->length - urb->actual_length;
+	}
+
+	/* non DWORD-aligned buffer case handling. */
+	if (hc->align_buff && xfer_length && hc->ep_is_in) {
+		dwc_memcpy(urb->buf + urb->actual_length, hc->qh->dw_align_buf,
+			   xfer_length);
+	}
+
+	urb->actual_length += xfer_length;
+
+	if (xfer_length && (hc->ep_type == DWC_OTG_EP_TYPE_BULK) &&
+	    (urb->flags & URB_SEND_ZERO_PACKET)
+	    && (urb->actual_length == urb->length)
+	    && !(urb->length % hc->max_packet)) {
+		xfer_done = 0;
+	} else if (short_read || urb->actual_length >= urb->length) {
+		xfer_done = 1;
+		urb->status = 0;
+	}
+
+#ifdef DEBUG
+	{
+		hctsiz_data_t hctsiz;
+		hctsiz.d32 = DWC_READ_REG32(&hc_regs->hctsiz);
+		DWC_DEBUGPL(DBG_HCDV, "DWC_otg: %s: %s, channel %d\n",
+			    __func__, (hc->ep_is_in ? "IN" : "OUT"),
+			    hc->hc_num);
+		DWC_DEBUGPL(DBG_HCDV, "  hc->xfer_len %d\n", hc->xfer_len);
+		DWC_DEBUGPL(DBG_HCDV, "  hctsiz.xfersize %d\n",
+			    hctsiz.b.xfersize);
+		DWC_DEBUGPL(DBG_HCDV, "  urb->transfer_buffer_length %d\n",
+			    urb->length);
+		DWC_DEBUGPL(DBG_HCDV, "  urb->actual_length %d\n",
+			    urb->actual_length);
+		DWC_DEBUGPL(DBG_HCDV, "  short_read %d, xfer_done %d\n",
+			    short_read, xfer_done);
+	}
+#endif
+
+	return xfer_done;
+}
+
+/*
+ * Save the starting data toggle for the next transfer. The data toggle is
+ * saved in the QH for non-control transfers and it's saved in the QTD for
+ * control transfers.
+ */
+void dwc_otg_hcd_save_data_toggle(dwc_hc_t * hc,
+			     dwc_otg_hc_regs_t * hc_regs, dwc_otg_qtd_t * qtd)
+{
+	hctsiz_data_t hctsiz;
+	hctsiz.d32 = DWC_READ_REG32(&hc_regs->hctsiz);
+
+	if (hc->ep_type != DWC_OTG_EP_TYPE_CONTROL) {
+		dwc_otg_qh_t *qh = hc->qh;
+		if (hctsiz.b.pid == DWC_HCTSIZ_DATA0) {
+			qh->data_toggle = DWC_OTG_HC_PID_DATA0;
+		} else {
+			qh->data_toggle = DWC_OTG_HC_PID_DATA1;
+		}
+	} else {
+		if (hctsiz.b.pid == DWC_HCTSIZ_DATA0) {
+			qtd->data_toggle = DWC_OTG_HC_PID_DATA0;
+		} else {
+			qtd->data_toggle = DWC_OTG_HC_PID_DATA1;
+		}
+	}
+}
+
+/**
+ * Updates the state of an Isochronous URB when the transfer is stopped for
+ * any reason. The fields of the current entry in the frame descriptor array
+ * are set based on the transfer state and the input _halt_status. Completes
+ * the Isochronous URB if all the URB frames have been completed.
+ *
+ * @return DWC_OTG_HC_XFER_COMPLETE if there are more frames remaining to be
+ * transferred in the URB. Otherwise return DWC_OTG_HC_XFER_URB_COMPLETE.
+ */
+static dwc_otg_halt_status_e
+update_isoc_urb_state(dwc_otg_hcd_t * hcd,
+		      dwc_hc_t * hc,
+		      dwc_otg_hc_regs_t * hc_regs,
+		      dwc_otg_qtd_t * qtd, dwc_otg_halt_status_e halt_status)
+{
+	dwc_otg_hcd_urb_t *urb = qtd->urb;
+	dwc_otg_halt_status_e ret_val = halt_status;
+	struct dwc_otg_hcd_iso_packet_desc *frame_desc;
+
+	frame_desc = &urb->iso_descs[qtd->isoc_frame_index];
+	switch (halt_status) {
+	case DWC_OTG_HC_XFER_COMPLETE:
+		frame_desc->status = 0;
+		frame_desc->actual_length =
+		    get_actual_xfer_length(hc, hc_regs, qtd, halt_status, NULL);
+
+		/* non DWORD-aligned buffer case handling. */
+		if (hc->align_buff && frame_desc->actual_length && hc->ep_is_in) {
+			dwc_memcpy(urb->buf + frame_desc->offset + qtd->isoc_split_offset,
+				   hc->qh->dw_align_buf, frame_desc->actual_length);
+		}
+
+		break;
+	case DWC_OTG_HC_XFER_FRAME_OVERRUN:
+		urb->error_count++;
+		if (hc->ep_is_in) {
+			frame_desc->status = -DWC_E_NO_STREAM_RES;
+		} else {
+			frame_desc->status = -DWC_E_COMMUNICATION;
+		}
+		frame_desc->actual_length = 0;
+		break;
+	case DWC_OTG_HC_XFER_BABBLE_ERR:
+		urb->error_count++;
+		frame_desc->status = -DWC_E_OVERFLOW;
+		/* Don't need to update actual_length in this case. */
+		break;
+	case DWC_OTG_HC_XFER_XACT_ERR:
+		urb->error_count++;
+		frame_desc->status = -DWC_E_PROTOCOL;
+		frame_desc->actual_length =
+		    get_actual_xfer_length(hc, hc_regs, qtd, halt_status, NULL);
+
+		/* non DWORD-aligned buffer case handling. */
+		if (hc->align_buff && frame_desc->actual_length && hc->ep_is_in) {
+			dwc_memcpy(urb->buf + frame_desc->offset + qtd->isoc_split_offset,
+				   hc->qh->dw_align_buf, frame_desc->actual_length);
+		}
+		/* Skip whole frame */
+		if (hc->qh->do_split && (hc->ep_type == DWC_OTG_EP_TYPE_ISOC) &&
+		    hc->ep_is_in && hcd->core_if->dma_enable) {
+			qtd->complete_split = 0;
+			qtd->isoc_split_offset = 0;
+		}
+
+		break;
+	default:
+		DWC_ASSERT(1, "Unhandled _halt_status (%d)\n", halt_status);
+		break;
+	}
+	if (++qtd->isoc_frame_index == urb->packet_count) {
+		/*
+		 * urb->status is not used for isoc transfers.
+		 * The individual frame_desc statuses are used instead.
+		 */
+		hcd->fops->complete(hcd, urb->priv, urb, 0);
+		ret_val = DWC_OTG_HC_XFER_URB_COMPLETE;
+	} else {
+		ret_val = DWC_OTG_HC_XFER_COMPLETE;
+	}
+	return ret_val;
+}
+
+/**
+ * Frees the first QTD in the QH's list if free_qtd is 1. For non-periodic
+ * QHs, removes the QH from the active non-periodic schedule. If any QTDs are
+ * still linked to the QH, the QH is added to the end of the inactive
+ * non-periodic schedule. For periodic QHs, removes the QH from the periodic
+ * schedule if no more QTDs are linked to the QH.
+ */
+static void deactivate_qh(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh, int free_qtd)
+{
+	int continue_split = 0;
+	dwc_otg_qtd_t *qtd;
+
+	DWC_DEBUGPL(DBG_HCDV, "  %s(%p,%p,%d)\n", __func__, hcd, qh, free_qtd);
+
+	qtd = DWC_CIRCLEQ_FIRST(&qh->qtd_list);
+
+	if (qtd->complete_split) {
+		continue_split = 1;
+	} else if (qtd->isoc_split_pos == DWC_HCSPLIT_XACTPOS_MID ||
+		   qtd->isoc_split_pos == DWC_HCSPLIT_XACTPOS_END) {
+		continue_split = 1;
+	}
+
+	if (free_qtd) {
+		dwc_otg_hcd_qtd_remove_and_free(hcd, qtd, qh);
+		continue_split = 0;
+	}
+
+	qh->channel = NULL;
+	dwc_otg_hcd_qh_deactivate(hcd, qh, continue_split);
+}
+
+/**
+ * Releases a host channel for use by other transfers. Attempts to select and
+ * queue more transactions since at least one host channel is available.
+ *
+ * @param hcd The HCD state structure.
+ * @param hc The host channel to release.
+ * @param qtd The QTD associated with the host channel. This QTD may be freed
+ * if the transfer is complete or an error has occurred.
+ * @param halt_status Reason the channel is being released. This status
+ * determines the actions taken by this function.
+ */
+static void release_channel(dwc_otg_hcd_t * hcd,
+			    dwc_hc_t * hc,
+			    dwc_otg_qtd_t * qtd,
+			    dwc_otg_halt_status_e halt_status)
+{
+	dwc_otg_transaction_type_e tr_type;
+	int free_qtd;
+
+	int hog_port = 0;
+
+	DWC_DEBUGPL(DBG_HCDV, "  %s: channel %d, halt_status %d, xfer_len %d\n",
+		    __func__, hc->hc_num, halt_status, hc->xfer_len);
+
+	if(fiq_fsm_enable && hc->do_split) {
+		if(!hc->ep_is_in && hc->ep_type == UE_ISOCHRONOUS) {
+			if(hc->xact_pos == DWC_HCSPLIT_XACTPOS_MID ||
+					hc->xact_pos == DWC_HCSPLIT_XACTPOS_BEGIN) {
+				hog_port = 0;
+			}
+		}
+	}
+
+	switch (halt_status) {
+	case DWC_OTG_HC_XFER_URB_COMPLETE:
+		free_qtd = 1;
+		break;
+	case DWC_OTG_HC_XFER_AHB_ERR:
+	case DWC_OTG_HC_XFER_STALL:
+	case DWC_OTG_HC_XFER_BABBLE_ERR:
+		free_qtd = 1;
+		break;
+	case DWC_OTG_HC_XFER_XACT_ERR:
+		if (qtd->error_count >= 3) {
+			DWC_DEBUGPL(DBG_HCDV,
+				    "  Complete URB with transaction error\n");
+			free_qtd = 1;
+			qtd->urb->status = -DWC_E_PROTOCOL;
+			hcd->fops->complete(hcd, qtd->urb->priv,
+					    qtd->urb, -DWC_E_PROTOCOL);
+		} else {
+			free_qtd = 0;
+		}
+		break;
+	case DWC_OTG_HC_XFER_URB_DEQUEUE:
+		/*
+		 * The QTD has already been removed and the QH has been
+		 * deactivated. Don't want to do anything except release the
+		 * host channel and try to queue more transfers.
+		 */
+		goto cleanup;
+	case DWC_OTG_HC_XFER_NO_HALT_STATUS:
+		free_qtd = 0;
+		break;
+	case DWC_OTG_HC_XFER_PERIODIC_INCOMPLETE:
+		DWC_DEBUGPL(DBG_HCDV,
+			"  Complete URB with I/O error\n");
+		free_qtd = 1;
+		qtd->urb->status = -DWC_E_IO;
+		hcd->fops->complete(hcd, qtd->urb->priv,
+			qtd->urb, -DWC_E_IO);
+		break;
+	default:
+		free_qtd = 0;
+		break;
+	}
+
+	deactivate_qh(hcd, hc->qh, free_qtd);
+
+cleanup:
+	/*
+	 * Release the host channel for use by other transfers. The cleanup
+	 * function clears the channel interrupt enables and conditions, so
+	 * there's no need to clear the Channel Halted interrupt separately.
+	 */
+	if (fiq_fsm_enable && hcd->fiq_state->channel[hc->hc_num].fsm != FIQ_PASSTHROUGH)
+		dwc_otg_cleanup_fiq_channel(hcd, hc->hc_num);
+	dwc_otg_hc_cleanup(hcd->core_if, hc);
+	DWC_CIRCLEQ_INSERT_TAIL(&hcd->free_hc_list, hc, hc_list_entry);
+
+	if (!microframe_schedule) {
+		switch (hc->ep_type) {
+		case DWC_OTG_EP_TYPE_CONTROL:
+		case DWC_OTG_EP_TYPE_BULK:
+			hcd->non_periodic_channels--;
+			break;
+
+		default:
+			/*
+			 * Don't release reservations for periodic channels here.
+			 * That's done when a periodic transfer is descheduled (i.e.
+			 * when the QH is removed from the periodic schedule).
+			 */
+			break;
+		}
+	} else {
+		hcd->available_host_channels++;
+		fiq_print(FIQDBG_INT, hcd->fiq_state, "AHC = %d ", hcd->available_host_channels);
+	}
+
+	/* Try to queue more transfers now that there's a free channel. */
+	tr_type = dwc_otg_hcd_select_transactions(hcd);
+	if (tr_type != DWC_OTG_TRANSACTION_NONE) {
+		dwc_otg_hcd_queue_transactions(hcd, tr_type);
+	}
+}
+
+/**
+ * Halts a host channel. If the channel cannot be halted immediately because
+ * the request queue is full, this function ensures that the FIFO empty
+ * interrupt for the appropriate queue is enabled so that the halt request can
+ * be queued when there is space in the request queue.
+ *
+ * This function may also be called in DMA mode. In that case, the channel is
+ * simply released since the core always halts the channel automatically in
+ * DMA mode.
+ */
+static void halt_channel(dwc_otg_hcd_t * hcd,
+			 dwc_hc_t * hc,
+			 dwc_otg_qtd_t * qtd, dwc_otg_halt_status_e halt_status)
+{
+	if (hcd->core_if->dma_enable) {
+		release_channel(hcd, hc, qtd, halt_status);
+		return;
+	}
+
+	/* Slave mode processing... */
+	dwc_otg_hc_halt(hcd->core_if, hc, halt_status);
+
+	if (hc->halt_on_queue) {
+		gintmsk_data_t gintmsk = {.d32 = 0 };
+		dwc_otg_core_global_regs_t *global_regs;
+		global_regs = hcd->core_if->core_global_regs;
+
+		if (hc->ep_type == DWC_OTG_EP_TYPE_CONTROL ||
+		    hc->ep_type == DWC_OTG_EP_TYPE_BULK) {
+			/*
+			 * Make sure the Non-periodic Tx FIFO empty interrupt
+			 * is enabled so that the non-periodic schedule will
+			 * be processed.
+			 */
+			gintmsk.b.nptxfempty = 1;
+			if (fiq_enable) {
+				local_fiq_disable();
+				fiq_fsm_spin_lock(&hcd->fiq_state->lock);
+				DWC_MODIFY_REG32(&global_regs->gintmsk, 0, gintmsk.d32);
+				fiq_fsm_spin_unlock(&hcd->fiq_state->lock);
+				local_fiq_enable();
+			} else {
+				DWC_MODIFY_REG32(&global_regs->gintmsk, 0, gintmsk.d32);
+			}
+		} else {
+			/*
+			 * Move the QH from the periodic queued schedule to
+			 * the periodic assigned schedule. This allows the
+			 * halt to be queued when the periodic schedule is
+			 * processed.
+			 */
+			DWC_LIST_MOVE_HEAD(&hcd->periodic_sched_assigned,
+					   &hc->qh->qh_list_entry);
+
+			/*
+			 * Make sure the Periodic Tx FIFO Empty interrupt is
+			 * enabled so that the periodic schedule will be
+			 * processed.
+			 */
+			gintmsk.b.ptxfempty = 1;
+			if (fiq_enable) {
+				local_fiq_disable();
+				fiq_fsm_spin_lock(&hcd->fiq_state->lock);
+				DWC_MODIFY_REG32(&global_regs->gintmsk, 0, gintmsk.d32);
+				fiq_fsm_spin_unlock(&hcd->fiq_state->lock);
+				local_fiq_enable();
+			} else {
+				DWC_MODIFY_REG32(&global_regs->gintmsk, 0, gintmsk.d32);
+			}
+		}
+	}
+}
+
+/**
+ * Performs common cleanup for non-periodic transfers after a Transfer
+ * Complete interrupt. This function should be called after any endpoint type
+ * specific handling is finished to release the host channel.
+ */
+static void complete_non_periodic_xfer(dwc_otg_hcd_t * hcd,
+				       dwc_hc_t * hc,
+				       dwc_otg_hc_regs_t * hc_regs,
+				       dwc_otg_qtd_t * qtd,
+				       dwc_otg_halt_status_e halt_status)
+{
+	hcint_data_t hcint;
+
+	qtd->error_count = 0;
+
+	hcint.d32 = DWC_READ_REG32(&hc_regs->hcint);
+	if (hcint.b.nyet) {
+		/*
+		 * Got a NYET on the last transaction of the transfer. This
+		 * means that the endpoint should be in the PING state at the
+		 * beginning of the next transfer.
+		 */
+		hc->qh->ping_state = 1;
+		clear_hc_int(hc_regs, nyet);
+	}
+
+	/*
+	 * Always halt and release the host channel to make it available for
+	 * more transfers. There may still be more phases for a control
+	 * transfer or more data packets for a bulk transfer at this point,
+	 * but the host channel is still halted. A channel will be reassigned
+	 * to the transfer when the non-periodic schedule is processed after
+	 * the channel is released. This allows transactions to be queued
+	 * properly via dwc_otg_hcd_queue_transactions, which also enables the
+	 * Tx FIFO Empty interrupt if necessary.
+	 */
+	if (hc->ep_is_in) {
+		/*
+		 * IN transfers in Slave mode require an explicit disable to
+		 * halt the channel. (In DMA mode, this call simply releases
+		 * the channel.)
+		 */
+		halt_channel(hcd, hc, qtd, halt_status);
+	} else {
+		/*
+		 * The channel is automatically disabled by the core for OUT
+		 * transfers in Slave mode.
+		 */
+		release_channel(hcd, hc, qtd, halt_status);
+	}
+}
+
+/**
+ * Performs common cleanup for periodic transfers after a Transfer Complete
+ * interrupt. This function should be called after any endpoint type specific
+ * handling is finished to release the host channel.
+ */
+static void complete_periodic_xfer(dwc_otg_hcd_t * hcd,
+				   dwc_hc_t * hc,
+				   dwc_otg_hc_regs_t * hc_regs,
+				   dwc_otg_qtd_t * qtd,
+				   dwc_otg_halt_status_e halt_status)
+{
+	hctsiz_data_t hctsiz;
+	qtd->error_count = 0;
+
+	hctsiz.d32 = DWC_READ_REG32(&hc_regs->hctsiz);
+	if (!hc->ep_is_in || hctsiz.b.pktcnt == 0) {
+		/* Core halts channel in these cases. */
+		release_channel(hcd, hc, qtd, halt_status);
+	} else {
+		/* Flush any outstanding requests from the Tx queue. */
+		halt_channel(hcd, hc, qtd, halt_status);
+	}
+}
+
+static int32_t handle_xfercomp_isoc_split_in(dwc_otg_hcd_t * hcd,
+					     dwc_hc_t * hc,
+					     dwc_otg_hc_regs_t * hc_regs,
+					     dwc_otg_qtd_t * qtd)
+{
+	uint32_t len;
+	struct dwc_otg_hcd_iso_packet_desc *frame_desc;
+	frame_desc = &qtd->urb->iso_descs[qtd->isoc_frame_index];
+
+	len = get_actual_xfer_length(hc, hc_regs, qtd,
+				     DWC_OTG_HC_XFER_COMPLETE, NULL);
+
+	if (!len) {
+		qtd->complete_split = 0;
+		qtd->isoc_split_offset = 0;
+		return 0;
+	}
+	frame_desc->actual_length += len;
+
+	if (hc->align_buff && len)
+		dwc_memcpy(qtd->urb->buf + frame_desc->offset +
+			   qtd->isoc_split_offset, hc->qh->dw_align_buf, len);
+	qtd->isoc_split_offset += len;
+
+	if (frame_desc->length == frame_desc->actual_length) {
+		frame_desc->status = 0;
+		qtd->isoc_frame_index++;
+		qtd->complete_split = 0;
+		qtd->isoc_split_offset = 0;
+	}
+
+	if (qtd->isoc_frame_index == qtd->urb->packet_count) {
+		hcd->fops->complete(hcd, qtd->urb->priv, qtd->urb, 0);
+		release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_URB_COMPLETE);
+	} else {
+		release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_NO_HALT_STATUS);
+	}
+
+	return 1;		/* Indicates that channel released */
+}
+
+/**
+ * Handles a host channel Transfer Complete interrupt. This handler may be
+ * called in either DMA mode or Slave mode.
+ */
+static int32_t handle_hc_xfercomp_intr(dwc_otg_hcd_t * hcd,
+				       dwc_hc_t * hc,
+				       dwc_otg_hc_regs_t * hc_regs,
+				       dwc_otg_qtd_t * qtd)
+{
+	int urb_xfer_done;
+	dwc_otg_halt_status_e halt_status = DWC_OTG_HC_XFER_COMPLETE;
+	dwc_otg_hcd_urb_t *urb = qtd->urb;
+	int pipe_type = dwc_otg_hcd_get_pipe_type(&urb->pipe_info);
+
+	DWC_DEBUGPL(DBG_HCDI, "--Host Channel %d Interrupt: "
+		    "Transfer Complete--\n", hc->hc_num);
+
+	if (hcd->core_if->dma_desc_enable) {
+		dwc_otg_hcd_complete_xfer_ddma(hcd, hc, hc_regs, halt_status);
+		if (pipe_type == UE_ISOCHRONOUS) {
+			/* Do not disable the interrupt, just clear it */
+			clear_hc_int(hc_regs, xfercomp);
+			return 1;
+		}
+		goto handle_xfercomp_done;
+	}
+
+	/*
+	 * Handle xfer complete on CSPLIT.
+	 */
+
+	if (hc->qh->do_split) {
+		if ((hc->ep_type == DWC_OTG_EP_TYPE_ISOC) && hc->ep_is_in
+		    && hcd->core_if->dma_enable) {
+			if (qtd->complete_split
+			    && handle_xfercomp_isoc_split_in(hcd, hc, hc_regs,
+							     qtd))
+				goto handle_xfercomp_done;
+		} else {
+			qtd->complete_split = 0;
+		}
+	}
+
+	/* Update the QTD and URB states. */
+	switch (pipe_type) {
+	case UE_CONTROL:
+		switch (qtd->control_phase) {
+		case DWC_OTG_CONTROL_SETUP:
+			if (urb->length > 0) {
+				qtd->control_phase = DWC_OTG_CONTROL_DATA;
+			} else {
+				qtd->control_phase = DWC_OTG_CONTROL_STATUS;
+			}
+			DWC_DEBUGPL(DBG_HCDV,
+				    "  Control setup transaction done\n");
+			halt_status = DWC_OTG_HC_XFER_COMPLETE;
+			break;
+		case DWC_OTG_CONTROL_DATA:{
+				urb_xfer_done =
+				    update_urb_state_xfer_comp(hc, hc_regs, urb,
+							       qtd);
+				if (urb_xfer_done) {
+					qtd->control_phase =
+					    DWC_OTG_CONTROL_STATUS;
+					DWC_DEBUGPL(DBG_HCDV,
+						    "  Control data transfer done\n");
+				} else {
+					dwc_otg_hcd_save_data_toggle(hc, hc_regs, qtd);
+				}
+				halt_status = DWC_OTG_HC_XFER_COMPLETE;
+				break;
+			}
+		case DWC_OTG_CONTROL_STATUS:
+			DWC_DEBUGPL(DBG_HCDV, "  Control transfer complete\n");
+			if (urb->status == -DWC_E_IN_PROGRESS) {
+				urb->status = 0;
+			}
+			hcd->fops->complete(hcd, urb->priv, urb, urb->status);
+			halt_status = DWC_OTG_HC_XFER_URB_COMPLETE;
+			break;
+		}
+
+		complete_non_periodic_xfer(hcd, hc, hc_regs, qtd, halt_status);
+		break;
+	case UE_BULK:
+		DWC_DEBUGPL(DBG_HCDV, "  Bulk transfer complete\n");
+		urb_xfer_done =
+		    update_urb_state_xfer_comp(hc, hc_regs, urb, qtd);
+		if (urb_xfer_done) {
+			hcd->fops->complete(hcd, urb->priv, urb, urb->status);
+			halt_status = DWC_OTG_HC_XFER_URB_COMPLETE;
+		} else {
+			halt_status = DWC_OTG_HC_XFER_COMPLETE;
+		}
+
+		dwc_otg_hcd_save_data_toggle(hc, hc_regs, qtd);
+		complete_non_periodic_xfer(hcd, hc, hc_regs, qtd, halt_status);
+		break;
+	case UE_INTERRUPT:
+		DWC_DEBUGPL(DBG_HCDV, "  Interrupt transfer complete\n");
+		urb_xfer_done =
+			update_urb_state_xfer_comp(hc, hc_regs, urb, qtd);
+
+		/*
+		 * Interrupt URB is done on the first transfer complete
+		 * interrupt.
+		 */
+		if (urb_xfer_done) {
+				hcd->fops->complete(hcd, urb->priv, urb, urb->status);
+				halt_status = DWC_OTG_HC_XFER_URB_COMPLETE;
+		} else {
+				halt_status = DWC_OTG_HC_XFER_COMPLETE;
+		}
+
+		dwc_otg_hcd_save_data_toggle(hc, hc_regs, qtd);
+		complete_periodic_xfer(hcd, hc, hc_regs, qtd, halt_status);
+		break;
+	case UE_ISOCHRONOUS:
+		DWC_DEBUGPL(DBG_HCDV, "  Isochronous transfer complete\n");
+		if (qtd->isoc_split_pos == DWC_HCSPLIT_XACTPOS_ALL) {
+			halt_status =
+			    update_isoc_urb_state(hcd, hc, hc_regs, qtd,
+						  DWC_OTG_HC_XFER_COMPLETE);
+		}
+		complete_periodic_xfer(hcd, hc, hc_regs, qtd, halt_status);
+		break;
+	}
+
+handle_xfercomp_done:
+	disable_hc_int(hc_regs, xfercompl);
+
+	return 1;
+}
+
+/**
+ * Handles a host channel STALL interrupt. This handler may be called in
+ * either DMA mode or Slave mode.
+ */
+static int32_t handle_hc_stall_intr(dwc_otg_hcd_t * hcd,
+				    dwc_hc_t * hc,
+				    dwc_otg_hc_regs_t * hc_regs,
+				    dwc_otg_qtd_t * qtd)
+{
+	dwc_otg_hcd_urb_t *urb = qtd->urb;
+	int pipe_type = dwc_otg_hcd_get_pipe_type(&urb->pipe_info);
+
+	DWC_DEBUGPL(DBG_HCD, "--Host Channel %d Interrupt: "
+		    "STALL Received--\n", hc->hc_num);
+
+	if (hcd->core_if->dma_desc_enable) {
+		dwc_otg_hcd_complete_xfer_ddma(hcd, hc, hc_regs, DWC_OTG_HC_XFER_STALL);
+		goto handle_stall_done;
+	}
+
+	if (pipe_type == UE_CONTROL) {
+		hcd->fops->complete(hcd, urb->priv, urb, -DWC_E_PIPE);
+	}
+
+	if (pipe_type == UE_BULK || pipe_type == UE_INTERRUPT) {
+		hcd->fops->complete(hcd, urb->priv, urb, -DWC_E_PIPE);
+		/*
+		 * USB protocol requires resetting the data toggle for bulk
+		 * and interrupt endpoints when a CLEAR_FEATURE(ENDPOINT_HALT)
+		 * setup command is issued to the endpoint. Anticipate the
+		 * CLEAR_FEATURE command since a STALL has occurred and reset
+		 * the data toggle now.
+		 */
+		hc->qh->data_toggle = 0;
+	}
+
+	halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_STALL);
+
+handle_stall_done:
+	disable_hc_int(hc_regs, stall);
+
+	return 1;
+}
+
+/*
+ * Updates the state of the URB when a transfer has been stopped due to an
+ * abnormal condition before the transfer completes. Modifies the
+ * actual_length field of the URB to reflect the number of bytes that have
+ * actually been transferred via the host channel.
+ */
+static void update_urb_state_xfer_intr(dwc_hc_t * hc,
+				       dwc_otg_hc_regs_t * hc_regs,
+				       dwc_otg_hcd_urb_t * urb,
+				       dwc_otg_qtd_t * qtd,
+				       dwc_otg_halt_status_e halt_status)
+{
+	uint32_t bytes_transferred = get_actual_xfer_length(hc, hc_regs, qtd,
+							    halt_status, NULL);
+
+	if (urb->actual_length + bytes_transferred > urb->length) {
+		printk_once(KERN_DEBUG "dwc_otg: DEVICE:%03d : %s:%d:trimming xfer length\n",
+			hc->dev_addr, __func__, __LINE__);
+		bytes_transferred = urb->length - urb->actual_length;
+	}
+
+	/* non DWORD-aligned buffer case handling. */
+	if (hc->align_buff && bytes_transferred && hc->ep_is_in) {
+		dwc_memcpy(urb->buf + urb->actual_length, hc->qh->dw_align_buf,
+			   bytes_transferred);
+	}
+
+	urb->actual_length += bytes_transferred;
+
+#ifdef DEBUG
+	{
+		hctsiz_data_t hctsiz;
+		hctsiz.d32 = DWC_READ_REG32(&hc_regs->hctsiz);
+		DWC_DEBUGPL(DBG_HCDV, "DWC_otg: %s: %s, channel %d\n",
+			    __func__, (hc->ep_is_in ? "IN" : "OUT"),
+			    hc->hc_num);
+		DWC_DEBUGPL(DBG_HCDV, "  hc->start_pkt_count %d\n",
+			    hc->start_pkt_count);
+		DWC_DEBUGPL(DBG_HCDV, "  hctsiz.pktcnt %d\n", hctsiz.b.pktcnt);
+		DWC_DEBUGPL(DBG_HCDV, "  hc->max_packet %d\n", hc->max_packet);
+		DWC_DEBUGPL(DBG_HCDV, "  bytes_transferred %d\n",
+			    bytes_transferred);
+		DWC_DEBUGPL(DBG_HCDV, "  urb->actual_length %d\n",
+			    urb->actual_length);
+		DWC_DEBUGPL(DBG_HCDV, "  urb->transfer_buffer_length %d\n",
+			    urb->length);
+	}
+#endif
+}
+
+/**
+ * Handles a host channel NAK interrupt. This handler may be called in either
+ * DMA mode or Slave mode.
+ */
+static int32_t handle_hc_nak_intr(dwc_otg_hcd_t * hcd,
+				  dwc_hc_t * hc,
+				  dwc_otg_hc_regs_t * hc_regs,
+				  dwc_otg_qtd_t * qtd)
+{
+	DWC_DEBUGPL(DBG_HCDI, "--Host Channel %d Interrupt: "
+		    "NAK Received--\n", hc->hc_num);
+
+	/*
+	 * When we get bulk NAKs then remember this so we holdoff on this qh until
+	 * the beginning of the next frame
+	 */
+	switch(dwc_otg_hcd_get_pipe_type(&qtd->urb->pipe_info)) {
+		case UE_BULK:
+		case UE_CONTROL:
+		if (nak_holdoff && qtd->qh->do_split)
+			hc->qh->nak_frame = dwc_otg_hcd_get_frame_number(hcd);
+	}
+
+	/*
+	 * Handle NAK for IN/OUT SSPLIT/CSPLIT transfers, bulk, control, and
+	 * interrupt.  Re-start the SSPLIT transfer.
+	 */
+	if (hc->do_split) {
+		if (hc->complete_split) {
+			qtd->error_count = 0;
+		}
+		qtd->complete_split = 0;
+		halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_NAK);
+		goto handle_nak_done;
+	}
+
+	switch (dwc_otg_hcd_get_pipe_type(&qtd->urb->pipe_info)) {
+	case UE_CONTROL:
+	case UE_BULK:
+		if (hcd->core_if->dma_enable && hc->ep_is_in) {
+			/*
+			 * NAK interrupts are enabled on bulk/control IN
+			 * transfers in DMA mode for the sole purpose of
+			 * resetting the error count after a transaction error
+			 * occurs. The core will continue transferring data.
+			 * Disable other interrupts unmasked for the same
+			 * reason.
+			 */
+			disable_hc_int(hc_regs, datatglerr);
+			disable_hc_int(hc_regs, ack);
+			qtd->error_count = 0;
+			goto handle_nak_done;
+		}
+
+		/*
+		 * NAK interrupts normally occur during OUT transfers in DMA
+		 * or Slave mode. For IN transfers, more requests will be
+		 * queued as request queue space is available.
+		 */
+		qtd->error_count = 0;
+
+		if (!hc->qh->ping_state) {
+			update_urb_state_xfer_intr(hc, hc_regs,
+						   qtd->urb, qtd,
+						   DWC_OTG_HC_XFER_NAK);
+			dwc_otg_hcd_save_data_toggle(hc, hc_regs, qtd);
+
+			if (hc->speed == DWC_OTG_EP_SPEED_HIGH)
+				hc->qh->ping_state = 1;
+		}
+
+		/*
+		 * Halt the channel so the transfer can be re-started from
+		 * the appropriate point or the PING protocol will
+		 * start/continue.
+		 */
+		halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_NAK);
+		break;
+	case UE_INTERRUPT:
+		qtd->error_count = 0;
+		halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_NAK);
+		break;
+	case UE_ISOCHRONOUS:
+		/* Should never get called for isochronous transfers. */
+		DWC_ASSERT(1, "NACK interrupt for ISOC transfer\n");
+		break;
+	}
+
+handle_nak_done:
+	disable_hc_int(hc_regs, nak);
+
+	return 1;
+}
+
+/**
+ * Handles a host channel ACK interrupt. This interrupt is enabled when
+ * performing the PING protocol in Slave mode, when errors occur during
+ * either Slave mode or DMA mode, and during Start Split transactions.
+ */
+static int32_t handle_hc_ack_intr(dwc_otg_hcd_t * hcd,
+				  dwc_hc_t * hc,
+				  dwc_otg_hc_regs_t * hc_regs,
+				  dwc_otg_qtd_t * qtd)
+{
+	DWC_DEBUGPL(DBG_HCDI, "--Host Channel %d Interrupt: "
+		    "ACK Received--\n", hc->hc_num);
+
+	if (hc->do_split) {
+		/*
+		 * Handle ACK on SSPLIT.
+		 * ACK should not occur in CSPLIT.
+		 */
+		if (!hc->ep_is_in && hc->data_pid_start != DWC_OTG_HC_PID_SETUP) {
+			qtd->ssplit_out_xfer_count = hc->xfer_len;
+		}
+		if (!(hc->ep_type == DWC_OTG_EP_TYPE_ISOC && !hc->ep_is_in)) {
+			/* Don't need complete for isochronous out transfers. */
+			qtd->complete_split = 1;
+		}
+
+		/* ISOC OUT */
+		if (hc->ep_type == DWC_OTG_EP_TYPE_ISOC && !hc->ep_is_in) {
+			switch (hc->xact_pos) {
+			case DWC_HCSPLIT_XACTPOS_ALL:
+				break;
+			case DWC_HCSPLIT_XACTPOS_END:
+				qtd->isoc_split_pos = DWC_HCSPLIT_XACTPOS_ALL;
+				qtd->isoc_split_offset = 0;
+				break;
+			case DWC_HCSPLIT_XACTPOS_BEGIN:
+			case DWC_HCSPLIT_XACTPOS_MID:
+				/*
+				 * For BEGIN or MID, calculate the length for
+				 * the next microframe to determine the correct
+				 * SSPLIT token, either MID or END.
+				 */
+				{
+					struct dwc_otg_hcd_iso_packet_desc
+					*frame_desc;
+
+					frame_desc =
+					    &qtd->urb->
+					    iso_descs[qtd->isoc_frame_index];
+					qtd->isoc_split_offset += 188;
+
+					if ((frame_desc->length -
+					     qtd->isoc_split_offset) <= 188) {
+						qtd->isoc_split_pos =
+						    DWC_HCSPLIT_XACTPOS_END;
+					} else {
+						qtd->isoc_split_pos =
+						    DWC_HCSPLIT_XACTPOS_MID;
+					}
+
+				}
+				break;
+			}
+		} else {
+			halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_ACK);
+		}
+	} else {
+		/*
+		 * An unmasked ACK on a non-split DMA transaction is
+		 * for the sole purpose of resetting error counts. Disable other
+		 * interrupts unmasked for the same reason.
+		 */
+		if(hcd->core_if->dma_enable) {
+			disable_hc_int(hc_regs, datatglerr);
+			disable_hc_int(hc_regs, nak);
+		}
+		qtd->error_count = 0;
+
+		if (hc->qh->ping_state) {
+			hc->qh->ping_state = 0;
+			/*
+			 * Halt the channel so the transfer can be re-started
+			 * from the appropriate point. This only happens in
+			 * Slave mode. In DMA mode, the ping_state is cleared
+			 * when the transfer is started because the core
+			 * automatically executes the PING, then the transfer.
+			 */
+			halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_ACK);
+		}
+	}
+
+	/*
+	 * If the ACK occurred when _not_ in the PING state, let the channel
+	 * continue transferring data after clearing the error count.
+	 */
+
+	disable_hc_int(hc_regs, ack);
+
+	return 1;
+}
+
+/**
+ * Handles a host channel NYET interrupt. This interrupt should only occur on
+ * Bulk and Control OUT endpoints and for complete split transactions. If a
+ * NYET occurs at the same time as a Transfer Complete interrupt, it is
+ * handled in the xfercomp interrupt handler, not here. This handler may be
+ * called in either DMA mode or Slave mode.
+ */
+static int32_t handle_hc_nyet_intr(dwc_otg_hcd_t * hcd,
+				   dwc_hc_t * hc,
+				   dwc_otg_hc_regs_t * hc_regs,
+				   dwc_otg_qtd_t * qtd)
+{
+	DWC_DEBUGPL(DBG_HCDI, "--Host Channel %d Interrupt: "
+		    "NYET Received--\n", hc->hc_num);
+
+	/*
+	 * NYET on CSPLIT
+	 * re-do the CSPLIT immediately on non-periodic
+	 */
+	if (hc->do_split && hc->complete_split) {
+		if (hc->ep_is_in && (hc->ep_type == DWC_OTG_EP_TYPE_ISOC)
+		    && hcd->core_if->dma_enable) {
+			qtd->complete_split = 0;
+			qtd->isoc_split_offset = 0;
+			if (++qtd->isoc_frame_index == qtd->urb->packet_count) {
+				hcd->fops->complete(hcd, qtd->urb->priv, qtd->urb, 0);
+				release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_URB_COMPLETE);
+			}
+			else
+				release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_NO_HALT_STATUS);
+			goto handle_nyet_done;
+		}
+
+		if (hc->ep_type == DWC_OTG_EP_TYPE_INTR ||
+		    hc->ep_type == DWC_OTG_EP_TYPE_ISOC) {
+			int frnum = dwc_otg_hcd_get_frame_number(hcd);
+
+			// With the FIQ running we only ever see the failed NYET
+			if (dwc_full_frame_num(frnum) !=
+			    dwc_full_frame_num(hc->qh->sched_frame) ||
+			    fiq_fsm_enable) {
+				/*
+				 * No longer in the same full speed frame.
+				 * Treat this as a transaction error.
+				 */
+#if 0
+				/** @todo Fix system performance so this can
+				 * be treated as an error. Right now complete
+				 * splits cannot be scheduled precisely enough
+				 * due to other system activity, so this error
+				 * occurs regularly in Slave mode.
+				 */
+				qtd->error_count++;
+#endif
+				qtd->complete_split = 0;
+				halt_channel(hcd, hc, qtd,
+					     DWC_OTG_HC_XFER_XACT_ERR);
+				/** @todo add support for isoc release */
+				goto handle_nyet_done;
+			}
+		}
+
+		halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_NYET);
+		goto handle_nyet_done;
+	}
+
+	hc->qh->ping_state = 1;
+	qtd->error_count = 0;
+
+	update_urb_state_xfer_intr(hc, hc_regs, qtd->urb, qtd,
+				   DWC_OTG_HC_XFER_NYET);
+	dwc_otg_hcd_save_data_toggle(hc, hc_regs, qtd);
+
+	/*
+	 * Halt the channel and re-start the transfer so the PING
+	 * protocol will start.
+	 */
+	halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_NYET);
+
+handle_nyet_done:
+	disable_hc_int(hc_regs, nyet);
+	return 1;
+}
+
+/**
+ * Handles a host channel babble interrupt. This handler may be called in
+ * either DMA mode or Slave mode.
+ */
+static int32_t handle_hc_babble_intr(dwc_otg_hcd_t * hcd,
+				     dwc_hc_t * hc,
+				     dwc_otg_hc_regs_t * hc_regs,
+				     dwc_otg_qtd_t * qtd)
+{
+	DWC_DEBUGPL(DBG_HCDI, "--Host Channel %d Interrupt: "
+		    "Babble Error--\n", hc->hc_num);
+
+	if (hcd->core_if->dma_desc_enable) {
+		dwc_otg_hcd_complete_xfer_ddma(hcd, hc, hc_regs,
+					       DWC_OTG_HC_XFER_BABBLE_ERR);
+		goto handle_babble_done;
+	}
+
+	if (hc->ep_type != DWC_OTG_EP_TYPE_ISOC) {
+		hcd->fops->complete(hcd, qtd->urb->priv,
+				    qtd->urb, -DWC_E_OVERFLOW);
+		halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_BABBLE_ERR);
+	} else {
+		dwc_otg_halt_status_e halt_status;
+		halt_status = update_isoc_urb_state(hcd, hc, hc_regs, qtd,
+						    DWC_OTG_HC_XFER_BABBLE_ERR);
+		halt_channel(hcd, hc, qtd, halt_status);
+	}
+
+handle_babble_done:
+	disable_hc_int(hc_regs, bblerr);
+	return 1;
+}
+
+/**
+ * Handles a host channel AHB error interrupt. This handler is only called in
+ * DMA mode.
+ */
+static int32_t handle_hc_ahberr_intr(dwc_otg_hcd_t * hcd,
+				     dwc_hc_t * hc,
+				     dwc_otg_hc_regs_t * hc_regs,
+				     dwc_otg_qtd_t * qtd)
+{
+	hcchar_data_t hcchar;
+	hcsplt_data_t hcsplt;
+	hctsiz_data_t hctsiz;
+	uint32_t hcdma;
+	char *pipetype, *speed;
+
+	dwc_otg_hcd_urb_t *urb = qtd->urb;
+
+	DWC_DEBUGPL(DBG_HCDI, "--Host Channel %d Interrupt: "
+		    "AHB Error--\n", hc->hc_num);
+
+	hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar);
+	hcsplt.d32 = DWC_READ_REG32(&hc_regs->hcsplt);
+	hctsiz.d32 = DWC_READ_REG32(&hc_regs->hctsiz);
+	hcdma = DWC_READ_REG32(&hc_regs->hcdma);
+
+	DWC_ERROR("AHB ERROR, Channel %d\n", hc->hc_num);
+	DWC_ERROR("  hcchar 0x%08x, hcsplt 0x%08x\n", hcchar.d32, hcsplt.d32);
+	DWC_ERROR("  hctsiz 0x%08x, hcdma 0x%08x\n", hctsiz.d32, hcdma);
+	DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD URB Enqueue\n");
+	DWC_ERROR("  Device address: %d\n",
+		  dwc_otg_hcd_get_dev_addr(&urb->pipe_info));
+	DWC_ERROR("  Endpoint: %d, %s\n",
+		  dwc_otg_hcd_get_ep_num(&urb->pipe_info),
+		  (dwc_otg_hcd_is_pipe_in(&urb->pipe_info) ? "IN" : "OUT"));
+
+	switch (dwc_otg_hcd_get_pipe_type(&urb->pipe_info)) {
+	case UE_CONTROL:
+		pipetype = "CONTROL";
+		break;
+	case UE_BULK:
+		pipetype = "BULK";
+		break;
+	case UE_INTERRUPT:
+		pipetype = "INTERRUPT";
+		break;
+	case UE_ISOCHRONOUS:
+		pipetype = "ISOCHRONOUS";
+		break;
+	default:
+		pipetype = "UNKNOWN";
+		break;
+	}
+
+	DWC_ERROR("  Endpoint type: %s\n", pipetype);
+
+	switch (hc->speed) {
+	case DWC_OTG_EP_SPEED_HIGH:
+		speed = "HIGH";
+		break;
+	case DWC_OTG_EP_SPEED_FULL:
+		speed = "FULL";
+		break;
+	case DWC_OTG_EP_SPEED_LOW:
+		speed = "LOW";
+		break;
+	default:
+		speed = "UNKNOWN";
+		break;
+	};
+
+	DWC_ERROR("  Speed: %s\n", speed);
+
+	DWC_ERROR("  Max packet size: %d\n",
+		  dwc_otg_hcd_get_mps(&urb->pipe_info));
+	DWC_ERROR("  Data buffer length: %d\n", urb->length);
+	DWC_ERROR("  Transfer buffer: %p, Transfer DMA: %pad\n",
+		  urb->buf, &urb->dma);
+	DWC_ERROR("  Setup buffer: %p, Setup DMA: %pad\n",
+		  urb->setup_packet, &urb->setup_dma);
+	DWC_ERROR("  Interval: %d\n", urb->interval);
+
+	/* Core haltes the channel for Descriptor DMA mode */
+	if (hcd->core_if->dma_desc_enable) {
+		dwc_otg_hcd_complete_xfer_ddma(hcd, hc, hc_regs,
+					       DWC_OTG_HC_XFER_AHB_ERR);
+		goto handle_ahberr_done;
+	}
+
+	hcd->fops->complete(hcd, urb->priv, urb, -DWC_E_IO);
+
+	/*
+	 * Force a channel halt. Don't call halt_channel because that won't
+	 * write to the HCCHARn register in DMA mode to force the halt.
+	 */
+	dwc_otg_hc_halt(hcd->core_if, hc, DWC_OTG_HC_XFER_AHB_ERR);
+handle_ahberr_done:
+	disable_hc_int(hc_regs, ahberr);
+	return 1;
+}
+
+/**
+ * Handles a host channel transaction error interrupt. This handler may be
+ * called in either DMA mode or Slave mode.
+ */
+static int32_t handle_hc_xacterr_intr(dwc_otg_hcd_t * hcd,
+				      dwc_hc_t * hc,
+				      dwc_otg_hc_regs_t * hc_regs,
+				      dwc_otg_qtd_t * qtd)
+{
+	DWC_DEBUGPL(DBG_HCDI, "--Host Channel %d Interrupt: "
+		    "Transaction Error--\n", hc->hc_num);
+
+	if (hcd->core_if->dma_desc_enable) {
+		dwc_otg_hcd_complete_xfer_ddma(hcd, hc, hc_regs,
+					       DWC_OTG_HC_XFER_XACT_ERR);
+		goto handle_xacterr_done;
+	}
+
+	switch (dwc_otg_hcd_get_pipe_type(&qtd->urb->pipe_info)) {
+	case UE_CONTROL:
+	case UE_BULK:
+		qtd->error_count++;
+		if (!hc->qh->ping_state) {
+
+			update_urb_state_xfer_intr(hc, hc_regs,
+						   qtd->urb, qtd,
+						   DWC_OTG_HC_XFER_XACT_ERR);
+			dwc_otg_hcd_save_data_toggle(hc, hc_regs, qtd);
+			if (!hc->ep_is_in && hc->speed == DWC_OTG_EP_SPEED_HIGH) {
+				hc->qh->ping_state = 1;
+			}
+		}
+
+		/*
+		 * Halt the channel so the transfer can be re-started from
+		 * the appropriate point or the PING protocol will start.
+		 */
+		halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_XACT_ERR);
+		break;
+	case UE_INTERRUPT:
+		qtd->error_count++;
+		if (hc->do_split && hc->complete_split) {
+			qtd->complete_split = 0;
+		}
+		halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_XACT_ERR);
+		break;
+	case UE_ISOCHRONOUS:
+		{
+			dwc_otg_halt_status_e halt_status;
+			halt_status =
+			    update_isoc_urb_state(hcd, hc, hc_regs, qtd,
+						  DWC_OTG_HC_XFER_XACT_ERR);
+
+			halt_channel(hcd, hc, qtd, halt_status);
+		}
+		break;
+	}
+handle_xacterr_done:
+	disable_hc_int(hc_regs, xacterr);
+
+	return 1;
+}
+
+/**
+ * Handles a host channel frame overrun interrupt. This handler may be called
+ * in either DMA mode or Slave mode.
+ */
+static int32_t handle_hc_frmovrun_intr(dwc_otg_hcd_t * hcd,
+				       dwc_hc_t * hc,
+				       dwc_otg_hc_regs_t * hc_regs,
+				       dwc_otg_qtd_t * qtd)
+{
+	DWC_DEBUGPL(DBG_HCDI, "--Host Channel %d Interrupt: "
+		    "Frame Overrun--\n", hc->hc_num);
+
+	switch (dwc_otg_hcd_get_pipe_type(&qtd->urb->pipe_info)) {
+	case UE_CONTROL:
+	case UE_BULK:
+		break;
+	case UE_INTERRUPT:
+		halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_FRAME_OVERRUN);
+		break;
+	case UE_ISOCHRONOUS:
+		{
+			dwc_otg_halt_status_e halt_status;
+			halt_status =
+			    update_isoc_urb_state(hcd, hc, hc_regs, qtd,
+						  DWC_OTG_HC_XFER_FRAME_OVERRUN);
+
+			halt_channel(hcd, hc, qtd, halt_status);
+		}
+		break;
+	}
+
+	disable_hc_int(hc_regs, frmovrun);
+
+	return 1;
+}
+
+/**
+ * Handles a host channel data toggle error interrupt. This handler may be
+ * called in either DMA mode or Slave mode.
+ */
+static int32_t handle_hc_datatglerr_intr(dwc_otg_hcd_t * hcd,
+					 dwc_hc_t * hc,
+					 dwc_otg_hc_regs_t * hc_regs,
+					 dwc_otg_qtd_t * qtd)
+{
+	DWC_DEBUGPL(DBG_HCDI, "--Host Channel %d Interrupt: "
+		"Data Toggle Error on %s transfer--\n",
+		hc->hc_num, (hc->ep_is_in ? "IN" : "OUT"));
+
+	/* Data toggles on split transactions cause the hc to halt.
+	 * restart transfer */
+	if(hc->qh->do_split)
+	{
+		qtd->error_count++;
+		dwc_otg_hcd_save_data_toggle(hc, hc_regs, qtd);
+		update_urb_state_xfer_intr(hc, hc_regs,
+			qtd->urb, qtd, DWC_OTG_HC_XFER_XACT_ERR);
+		halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_XACT_ERR);
+	} else if (hc->ep_is_in) {
+		/* An unmasked data toggle error on a non-split DMA transaction is
+		 * for the sole purpose of resetting error counts. Disable other
+		 * interrupts unmasked for the same reason.
+		 */
+		if(hcd->core_if->dma_enable) {
+			disable_hc_int(hc_regs, ack);
+			disable_hc_int(hc_regs, nak);
+		}
+		qtd->error_count = 0;
+	}
+
+	disable_hc_int(hc_regs, datatglerr);
+
+	return 1;
+}
+
+#ifdef DEBUG
+/**
+ * This function is for debug only. It checks that a valid halt status is set
+ * and that HCCHARn.chdis is clear. If there's a problem, corrective action is
+ * taken and a warning is issued.
+ * @return 1 if halt status is ok, 0 otherwise.
+ */
+static inline int halt_status_ok(dwc_otg_hcd_t * hcd,
+				 dwc_hc_t * hc,
+				 dwc_otg_hc_regs_t * hc_regs,
+				 dwc_otg_qtd_t * qtd)
+{
+	hcchar_data_t hcchar;
+	hctsiz_data_t hctsiz;
+	hcint_data_t hcint;
+	hcintmsk_data_t hcintmsk;
+	hcsplt_data_t hcsplt;
+
+	if (hc->halt_status == DWC_OTG_HC_XFER_NO_HALT_STATUS) {
+		/*
+		 * This code is here only as a check. This condition should
+		 * never happen. Ignore the halt if it does occur.
+		 */
+		hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar);
+		hctsiz.d32 = DWC_READ_REG32(&hc_regs->hctsiz);
+		hcint.d32 = DWC_READ_REG32(&hc_regs->hcint);
+		hcintmsk.d32 = DWC_READ_REG32(&hc_regs->hcintmsk);
+		hcsplt.d32 = DWC_READ_REG32(&hc_regs->hcsplt);
+		DWC_WARN
+		    ("%s: hc->halt_status == DWC_OTG_HC_XFER_NO_HALT_STATUS, "
+		     "channel %d, hcchar 0x%08x, hctsiz 0x%08x, "
+		     "hcint 0x%08x, hcintmsk 0x%08x, "
+		     "hcsplt 0x%08x, qtd->complete_split %d\n", __func__,
+		     hc->hc_num, hcchar.d32, hctsiz.d32, hcint.d32,
+		     hcintmsk.d32, hcsplt.d32, qtd->complete_split);
+
+		DWC_WARN("%s: no halt status, channel %d, ignoring interrupt\n",
+			 __func__, hc->hc_num);
+		DWC_WARN("\n");
+		clear_hc_int(hc_regs, chhltd);
+		return 0;
+	}
+
+	/*
+	 * This code is here only as a check. hcchar.chdis should
+	 * never be set when the halt interrupt occurs. Halt the
+	 * channel again if it does occur.
+	 */
+	hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar);
+	if (hcchar.b.chdis) {
+		DWC_WARN("%s: hcchar.chdis set unexpectedly, "
+			 "hcchar 0x%08x, trying to halt again\n",
+			 __func__, hcchar.d32);
+		clear_hc_int(hc_regs, chhltd);
+		hc->halt_pending = 0;
+		halt_channel(hcd, hc, qtd, hc->halt_status);
+		return 0;
+	}
+
+	return 1;
+}
+#endif
+
+/**
+ * Handles a host Channel Halted interrupt in DMA mode. This handler
+ * determines the reason the channel halted and proceeds accordingly.
+ */
+static void handle_hc_chhltd_intr_dma(dwc_otg_hcd_t * hcd,
+				      dwc_hc_t * hc,
+				      dwc_otg_hc_regs_t * hc_regs,
+				      dwc_otg_qtd_t * qtd)
+{
+	int out_nak_enh = 0;
+	hcint_data_t hcint;
+	hcintmsk_data_t hcintmsk;
+	/* For core with OUT NAK enhancement, the flow for high-
+	 * speed CONTROL/BULK OUT is handled a little differently.
+	 */
+	if (hcd->core_if->snpsid >= OTG_CORE_REV_2_71a) {
+		if (hc->speed == DWC_OTG_EP_SPEED_HIGH && !hc->ep_is_in &&
+		    (hc->ep_type == DWC_OTG_EP_TYPE_CONTROL ||
+		     hc->ep_type == DWC_OTG_EP_TYPE_BULK)) {
+			out_nak_enh = 1;
+		}
+	}
+
+	if (hc->halt_status == DWC_OTG_HC_XFER_URB_DEQUEUE ||
+	    (hc->halt_status == DWC_OTG_HC_XFER_AHB_ERR
+	     && !hcd->core_if->dma_desc_enable)) {
+		/*
+		 * Just release the channel. A dequeue can happen on a
+		 * transfer timeout. In the case of an AHB Error, the channel
+		 * was forced to halt because there's no way to gracefully
+		 * recover.
+		 */
+		if (hcd->core_if->dma_desc_enable)
+			dwc_otg_hcd_complete_xfer_ddma(hcd, hc, hc_regs,
+						       hc->halt_status);
+		else
+			release_channel(hcd, hc, qtd, hc->halt_status);
+		return;
+	}
+
+	/* Read the HCINTn register to determine the cause for the halt. */
+
+	hcint.d32 = DWC_READ_REG32(&hc_regs->hcint);
+	hcintmsk.d32 = DWC_READ_REG32(&hc_regs->hcintmsk);
+
+	if (hcint.b.xfercomp) {
+		/** @todo This is here because of a possible hardware bug.  Spec
+		 * says that on SPLIT-ISOC OUT transfers in DMA mode that a HALT
+		 * interrupt w/ACK bit set should occur, but I only see the
+		 * XFERCOMP bit, even with it masked out.  This is a workaround
+		 * for that behavior.  Should fix this when hardware is fixed.
+		 */
+		if (hc->ep_type == DWC_OTG_EP_TYPE_ISOC && !hc->ep_is_in) {
+			handle_hc_ack_intr(hcd, hc, hc_regs, qtd);
+		}
+		handle_hc_xfercomp_intr(hcd, hc, hc_regs, qtd);
+	} else if (hcint.b.stall) {
+		handle_hc_stall_intr(hcd, hc, hc_regs, qtd);
+	} else if (hcint.b.xacterr && !hcd->core_if->dma_desc_enable) {
+		if (out_nak_enh) {
+			if (hcint.b.nyet || hcint.b.nak || hcint.b.ack) {
+				DWC_DEBUGPL(DBG_HCD, "XactErr with NYET/NAK/ACK\n");
+				qtd->error_count = 0;
+			} else {
+				DWC_DEBUGPL(DBG_HCD, "XactErr without NYET/NAK/ACK\n");
+			}
+		}
+
+		/*
+		 * Must handle xacterr before nak or ack. Could get a xacterr
+		 * at the same time as either of these on a BULK/CONTROL OUT
+		 * that started with a PING. The xacterr takes precedence.
+		 */
+		handle_hc_xacterr_intr(hcd, hc, hc_regs, qtd);
+	} else if (hcint.b.xcs_xact && hcd->core_if->dma_desc_enable) {
+		handle_hc_xacterr_intr(hcd, hc, hc_regs, qtd);
+	} else if (hcint.b.ahberr && hcd->core_if->dma_desc_enable) {
+		handle_hc_ahberr_intr(hcd, hc, hc_regs, qtd);
+	} else if (hcint.b.bblerr) {
+		handle_hc_babble_intr(hcd, hc, hc_regs, qtd);
+	} else if (hcint.b.frmovrun) {
+		handle_hc_frmovrun_intr(hcd, hc, hc_regs, qtd);
+	} else if (hcint.b.datatglerr) {
+		handle_hc_datatglerr_intr(hcd, hc, hc_regs, qtd);
+	} else if (!out_nak_enh) {
+		if (hcint.b.nyet) {
+			/*
+			 * Must handle nyet before nak or ack. Could get a nyet at the
+			 * same time as either of those on a BULK/CONTROL OUT that
+			 * started with a PING. The nyet takes precedence.
+			 */
+			handle_hc_nyet_intr(hcd, hc, hc_regs, qtd);
+		} else if (hcint.b.nak && !hcintmsk.b.nak) {
+			/*
+			 * If nak is not masked, it's because a non-split IN transfer
+			 * is in an error state. In that case, the nak is handled by
+			 * the nak interrupt handler, not here. Handle nak here for
+			 * BULK/CONTROL OUT transfers, which halt on a NAK to allow
+			 * rewinding the buffer pointer.
+			 */
+			handle_hc_nak_intr(hcd, hc, hc_regs, qtd);
+		} else if (hcint.b.ack && !hcintmsk.b.ack) {
+			/*
+			 * If ack is not masked, it's because a non-split IN transfer
+			 * is in an error state. In that case, the ack is handled by
+			 * the ack interrupt handler, not here. Handle ack here for
+			 * split transfers. Start splits halt on ACK.
+			 */
+			handle_hc_ack_intr(hcd, hc, hc_regs, qtd);
+		} else {
+			if (hc->ep_type == DWC_OTG_EP_TYPE_INTR ||
+			    hc->ep_type == DWC_OTG_EP_TYPE_ISOC) {
+				/*
+				 * A periodic transfer halted with no other channel
+				 * interrupts set. Assume it was halted by the core
+				 * because it could not be completed in its scheduled
+				 * (micro)frame.
+				 */
+#ifdef DEBUG
+				DWC_PRINTF
+				    ("%s: Halt channel %d (assume incomplete periodic transfer)\n",
+				     __func__, hc->hc_num);
+#endif
+				halt_channel(hcd, hc, qtd,
+					     DWC_OTG_HC_XFER_PERIODIC_INCOMPLETE);
+			} else {
+				DWC_ERROR
+				    ("%s: Channel %d, DMA Mode -- ChHltd set, but reason "
+				     "for halting is unknown, hcint 0x%08x, intsts 0x%08x\n",
+				     __func__, hc->hc_num, hcint.d32,
+				     DWC_READ_REG32(&hcd->
+						    core_if->core_global_regs->
+						    gintsts));
+				/* Failthrough: use 3-strikes rule */
+				qtd->error_count++;
+				dwc_otg_hcd_save_data_toggle(hc, hc_regs, qtd);
+				update_urb_state_xfer_intr(hc, hc_regs,
+					   qtd->urb, qtd, DWC_OTG_HC_XFER_XACT_ERR);
+				halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_XACT_ERR);
+			}
+
+		}
+	} else {
+		DWC_PRINTF("NYET/NAK/ACK/other in non-error case, 0x%08x\n",
+			   hcint.d32);
+		/* Failthrough: use 3-strikes rule */
+		qtd->error_count++;
+		dwc_otg_hcd_save_data_toggle(hc, hc_regs, qtd);
+		update_urb_state_xfer_intr(hc, hc_regs,
+			   qtd->urb, qtd, DWC_OTG_HC_XFER_XACT_ERR);
+		halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_XACT_ERR);
+	}
+}
+
+/**
+ * Handles a host channel Channel Halted interrupt.
+ *
+ * In slave mode, this handler is called only when the driver specifically
+ * requests a halt. This occurs during handling other host channel interrupts
+ * (e.g. nak, xacterr, stall, nyet, etc.).
+ *
+ * In DMA mode, this is the interrupt that occurs when the core has finished
+ * processing a transfer on a channel. Other host channel interrupts (except
+ * ahberr) are disabled in DMA mode.
+ */
+static int32_t handle_hc_chhltd_intr(dwc_otg_hcd_t * hcd,
+				     dwc_hc_t * hc,
+				     dwc_otg_hc_regs_t * hc_regs,
+				     dwc_otg_qtd_t * qtd)
+{
+	DWC_DEBUGPL(DBG_HCDI, "--Host Channel %d Interrupt: "
+		    "Channel Halted--\n", hc->hc_num);
+
+	if (hcd->core_if->dma_enable) {
+		handle_hc_chhltd_intr_dma(hcd, hc, hc_regs, qtd);
+	} else {
+#ifdef DEBUG
+		if (!halt_status_ok(hcd, hc, hc_regs, qtd)) {
+			return 1;
+		}
+#endif
+		release_channel(hcd, hc, qtd, hc->halt_status);
+	}
+
+	return 1;
+}
+
+
+/**
+ * dwc_otg_fiq_unmangle_isoc() - Update the iso_frame_desc structure on
+ * FIQ transfer completion
+ * @hcd:	Pointer to dwc_otg_hcd struct
+ * @num:	Host channel number
+ *
+ * 1. Un-mangle the status as recorded in each iso_frame_desc status
+ * 2. Copy it from the dwc_otg_urb into the real URB
+ */
+void dwc_otg_fiq_unmangle_isoc(dwc_otg_hcd_t *hcd, dwc_otg_qh_t *qh, dwc_otg_qtd_t *qtd, uint32_t num)
+{
+	struct dwc_otg_hcd_urb *dwc_urb = qtd->urb;
+	int nr_frames = dwc_urb->packet_count;
+	int i;
+	hcint_data_t frame_hcint;
+
+	for (i = 0; i < nr_frames; i++) {
+		frame_hcint.d32 = dwc_urb->iso_descs[i].status;
+		if (frame_hcint.b.xfercomp) {
+			dwc_urb->iso_descs[i].status = 0;
+			dwc_urb->actual_length += dwc_urb->iso_descs[i].actual_length;
+		} else if (frame_hcint.b.frmovrun) {
+			if (qh->ep_is_in)
+				dwc_urb->iso_descs[i].status = -DWC_E_NO_STREAM_RES;
+			else
+				dwc_urb->iso_descs[i].status = -DWC_E_COMMUNICATION;
+			dwc_urb->error_count++;
+			dwc_urb->iso_descs[i].actual_length = 0;
+		} else if (frame_hcint.b.xacterr) {
+			dwc_urb->iso_descs[i].status = -DWC_E_PROTOCOL;
+			dwc_urb->error_count++;
+			dwc_urb->iso_descs[i].actual_length = 0;
+		} else if (frame_hcint.b.bblerr) {
+			dwc_urb->iso_descs[i].status = -DWC_E_OVERFLOW;
+			dwc_urb->error_count++;
+			dwc_urb->iso_descs[i].actual_length = 0;
+		} else {
+			/* Something went wrong */
+			dwc_urb->iso_descs[i].status = -1;
+			dwc_urb->iso_descs[i].actual_length = 0;
+			dwc_urb->error_count++;
+		}
+	}
+	qh->sched_frame = dwc_frame_num_inc(qh->sched_frame, qh->interval * (nr_frames - 1));
+
+	//printk_ratelimited(KERN_INFO "%s: HS isochronous of %d/%d frames with %d errors complete\n",
+	//			__FUNCTION__, i, dwc_urb->packet_count, dwc_urb->error_count);
+}
+
+/**
+ * dwc_otg_fiq_unsetup_per_dma() - Remove data from bounce buffers for split transactions
+ * @hcd:	Pointer to dwc_otg_hcd struct
+ * @num:	Host channel number
+ *
+ * Copies data from the FIQ bounce buffers into the URB's transfer buffer. Does not modify URB state.
+ * Returns total length of data or -1 if the buffers were not used.
+ *
+ */
+int dwc_otg_fiq_unsetup_per_dma(dwc_otg_hcd_t *hcd, dwc_otg_qh_t *qh, dwc_otg_qtd_t *qtd, uint32_t num)
+{
+	dwc_hc_t *hc = qh->channel;
+	struct fiq_dma_blob *blob = hcd->fiq_dmab;
+	struct fiq_channel_state *st = &hcd->fiq_state->channel[num];
+	uint8_t *ptr = NULL;
+	int index = 0, len = 0;
+	int i = 0;
+	if (hc->ep_is_in) {
+		/* Copy data out of the DMA bounce buffers to the URB's buffer.
+		 * The align_buf is ignored as this is ignored on FSM enqueue. */
+		ptr = qtd->urb->buf;
+		if (qh->ep_type == UE_ISOCHRONOUS) {
+			/* Isoc IN transactions - grab the offset of the iso_frame_desc into the URB transfer buffer */
+			index = qtd->isoc_frame_index;
+			ptr += qtd->urb->iso_descs[index].offset;
+		} else {
+			/* Need to increment by actual_length for interrupt IN */
+			ptr += qtd->urb->actual_length;
+		}
+
+		for (i = 0; i < st->dma_info.index; i++) {
+			len += st->dma_info.slot_len[i];
+			dwc_memcpy(ptr, &blob->channel[num].index[i].buf[0], st->dma_info.slot_len[i]);
+			ptr += st->dma_info.slot_len[i];
+		}
+		return len;
+	} else {
+		/* OUT endpoints - nothing to do. */
+		return -1;
+	}
+
+}
+/**
+ * dwc_otg_hcd_handle_hc_fsm() - handle an unmasked channel interrupt
+ * 				 from a channel handled in the FIQ
+ * @hcd:	Pointer to dwc_otg_hcd struct
+ * @num:	Host channel number
+ *
+ * If a host channel interrupt was received by the IRQ and this was a channel
+ * used by the FIQ, the execution flow for transfer completion is substantially
+ * different from the normal (messy) path. This function and its friends handles
+ * channel cleanup and transaction completion from a FIQ transaction.
+ */
+void dwc_otg_hcd_handle_hc_fsm(dwc_otg_hcd_t *hcd, uint32_t num)
+{
+	struct fiq_channel_state *st = &hcd->fiq_state->channel[num];
+	dwc_hc_t *hc = hcd->hc_ptr_array[num];
+	dwc_otg_qtd_t *qtd;
+	dwc_otg_hc_regs_t *hc_regs = hcd->core_if->host_if->hc_regs[num];
+	hcint_data_t hcint = hcd->fiq_state->channel[num].hcint_copy;
+	hctsiz_data_t hctsiz = hcd->fiq_state->channel[num].hctsiz_copy;
+	int hostchannels  = 0;
+	fiq_print(FIQDBG_INT, hcd->fiq_state, "OUT %01d %01d ", num , st->fsm);
+
+	hostchannels = hcd->available_host_channels;
+	if (hc->halt_pending) {
+		/* Dequeue: The FIQ was allowed to complete the transfer but state has been cleared. */
+		if (hc->qh && st->fsm == FIQ_NP_SPLIT_DONE &&
+				hcint.b.xfercomp && hc->qh->ep_type == UE_BULK) {
+			if (hctsiz.b.pid == DWC_HCTSIZ_DATA0) {
+				hc->qh->data_toggle = DWC_OTG_HC_PID_DATA1;
+			} else {
+				hc->qh->data_toggle = DWC_OTG_HC_PID_DATA0;
+			}
+		}
+		release_channel(hcd, hc, NULL, hc->halt_status);
+		return;
+	}
+
+	qtd = DWC_CIRCLEQ_FIRST(&hc->qh->qtd_list);
+	switch (st->fsm) {
+	case FIQ_TEST:
+		break;
+
+	case FIQ_DEQUEUE_ISSUED:
+		/* Handled above, but keep for posterity */
+		release_channel(hcd, hc, NULL, hc->halt_status);
+		break;
+
+	case FIQ_NP_SPLIT_DONE:
+		/* Nonperiodic transaction complete. */
+		if (!hc->ep_is_in) {
+			qtd->ssplit_out_xfer_count = hc->xfer_len;
+		}
+		if (hcint.b.xfercomp) {
+			handle_hc_xfercomp_intr(hcd, hc, hc_regs, qtd);
+		} else if (hcint.b.nak) {
+			handle_hc_nak_intr(hcd, hc, hc_regs, qtd);
+		} else {
+			DWC_WARN("Unexpected IRQ state on FSM transaction:"
+					"dev_addr=%d ep=%d fsm=%d, hcint=0x%08x\n",
+				hc->dev_addr, hc->ep_num, st->fsm, hcint.d32);
+			release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_NO_HALT_STATUS);
+		}
+		break;
+
+	case FIQ_NP_SPLIT_HS_ABORTED:
+		/* A HS abort is a 3-strikes on the HS bus at any point in the transaction.
+		 * Normally a CLEAR_TT_BUFFER hub command would be required: we can't do that
+		 * because there's no guarantee which order a non-periodic split happened in.
+		 * We could end up clearing a perfectly good transaction out of the buffer.
+		 */
+		if (hcint.b.xacterr) {
+			qtd->error_count += st->nr_errors;
+			handle_hc_xacterr_intr(hcd, hc, hc_regs, qtd);
+		} else if (hcint.b.ahberr) {
+			handle_hc_ahberr_intr(hcd, hc, hc_regs, qtd);
+		} else {
+			DWC_WARN("Unexpected IRQ state on FSM transaction:"
+					"dev_addr=%d ep=%d fsm=%d, hcint=0x%08x\n",
+				hc->dev_addr, hc->ep_num, st->fsm, hcint.d32);
+			release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_NO_HALT_STATUS);
+		}
+		break;
+
+	case FIQ_NP_SPLIT_LS_ABORTED:
+		/* A few cases can cause this - either an unknown state on a SSPLIT or
+		 * STALL/data toggle error response on a CSPLIT */
+		if (hcint.b.stall) {
+			handle_hc_stall_intr(hcd, hc, hc_regs, qtd);
+		} else if (hcint.b.datatglerr) {
+			handle_hc_datatglerr_intr(hcd, hc, hc_regs, qtd);
+		} else if (hcint.b.bblerr) {
+			handle_hc_babble_intr(hcd, hc, hc_regs, qtd);
+		} else if (hcint.b.ahberr) {
+			handle_hc_ahberr_intr(hcd, hc, hc_regs, qtd);
+		} else {
+			DWC_WARN("Unexpected IRQ state on FSM transaction:"
+					"dev_addr=%d ep=%d fsm=%d, hcint=0x%08x\n",
+				hc->dev_addr, hc->ep_num, st->fsm, hcint.d32);
+			release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_NO_HALT_STATUS);
+		}
+		break;
+
+	case FIQ_PER_SPLIT_DONE:
+		/* Isoc IN or Interrupt IN/OUT */
+
+		/* Flow control here is different from the normal execution by the driver.
+		* We need to completely ignore most of the driver's method of handling
+		* split transactions and do it ourselves.
+		*/
+		if (hc->ep_type == UE_INTERRUPT) {
+			if (hcint.b.nak) {
+					handle_hc_nak_intr(hcd, hc, hc_regs, qtd);
+			} else if (hc->ep_is_in) {
+				int len;
+				len = dwc_otg_fiq_unsetup_per_dma(hcd, hc->qh, qtd, num);
+				//printk(KERN_NOTICE "FIQ Transaction: hc=%d len=%d urb_len = %d\n", num, len, qtd->urb->length);
+				qtd->urb->actual_length += len;
+				if (qtd->urb->actual_length >= qtd->urb->length) {
+					qtd->urb->status = 0;
+					hcd->fops->complete(hcd, qtd->urb->priv, qtd->urb, qtd->urb->status);
+					release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_URB_COMPLETE);
+				} else {
+					/* Interrupt transfer not complete yet - is it a short read? */
+					if (len < hc->max_packet) {
+						/* Interrupt transaction complete */
+						qtd->urb->status = 0;
+						hcd->fops->complete(hcd, qtd->urb->priv, qtd->urb, qtd->urb->status);
+						release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_URB_COMPLETE);
+					} else {
+						/* Further transactions required */
+						release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_COMPLETE);
+					}
+				}
+			} else {
+				/* Interrupt OUT complete. */
+				dwc_otg_hcd_save_data_toggle(hc, hc_regs, qtd);
+				qtd->urb->actual_length += hc->xfer_len;
+				if (qtd->urb->actual_length >= qtd->urb->length) {
+					qtd->urb->status = 0;
+					hcd->fops->complete(hcd, qtd->urb->priv, qtd->urb, qtd->urb->status);
+					release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_URB_COMPLETE);
+				} else {
+					release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_COMPLETE);
+				}
+			}
+		} else {
+			/* ISOC IN complete. */
+			struct dwc_otg_hcd_iso_packet_desc *frame_desc = &qtd->urb->iso_descs[qtd->isoc_frame_index];
+			int len = 0;
+			/* Record errors, update qtd. */
+			if (st->nr_errors) {
+				frame_desc->actual_length = 0;
+				frame_desc->status = -DWC_E_PROTOCOL;
+			} else {
+				frame_desc->status = 0;
+				/* Unswizzle dma */
+				len = dwc_otg_fiq_unsetup_per_dma(hcd, hc->qh, qtd, num);
+				frame_desc->actual_length = len;
+			}
+			qtd->isoc_frame_index++;
+			if (qtd->isoc_frame_index == qtd->urb->packet_count) {
+				hcd->fops->complete(hcd, qtd->urb->priv, qtd->urb, 0);
+				release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_URB_COMPLETE);
+			} else {
+				release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_COMPLETE);
+			}
+		}
+		break;
+
+	case FIQ_PER_ISO_OUT_DONE: {
+			struct dwc_otg_hcd_iso_packet_desc *frame_desc = &qtd->urb->iso_descs[qtd->isoc_frame_index];
+			/* Record errors, update qtd. */
+			if (st->nr_errors) {
+				frame_desc->actual_length = 0;
+				frame_desc->status = -DWC_E_PROTOCOL;
+			} else {
+				frame_desc->status = 0;
+				frame_desc->actual_length = frame_desc->length;
+			}
+			qtd->isoc_frame_index++;
+			qtd->isoc_split_offset = 0;
+			if (qtd->isoc_frame_index == qtd->urb->packet_count) {
+				hcd->fops->complete(hcd, qtd->urb->priv, qtd->urb, 0);
+				release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_URB_COMPLETE);
+			} else {
+				release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_COMPLETE);
+			}
+		}
+		break;
+
+	case FIQ_PER_SPLIT_NYET_ABORTED:
+		/* Doh. lost the data. */
+		printk_ratelimited(KERN_INFO "Transfer to device %d endpoint 0x%x frame %d failed "
+				"- FIQ reported NYET. Data may have been lost.\n",
+				hc->dev_addr, hc->ep_num, dwc_otg_hcd_get_frame_number(hcd) >> 3);
+		if (hc->ep_type == UE_ISOCHRONOUS) {
+			struct dwc_otg_hcd_iso_packet_desc *frame_desc = &qtd->urb->iso_descs[qtd->isoc_frame_index];
+			/* Record errors, update qtd. */
+			frame_desc->actual_length = 0;
+			frame_desc->status = -DWC_E_PROTOCOL;
+			qtd->isoc_frame_index++;
+			qtd->isoc_split_offset = 0;
+			if (qtd->isoc_frame_index == qtd->urb->packet_count) {
+				hcd->fops->complete(hcd, qtd->urb->priv, qtd->urb, 0);
+				release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_URB_COMPLETE);
+			} else {
+				release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_COMPLETE);
+			}
+		} else {
+			release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_NO_HALT_STATUS);
+		}
+		break;
+
+	case FIQ_HS_ISOC_DONE:
+		/* The FIQ has performed a whole pile of isochronous transactions.
+		 * The status is recorded as the interrupt state should the transaction
+		 * fail.
+		 */
+		dwc_otg_fiq_unmangle_isoc(hcd, hc->qh, qtd, num);
+		hcd->fops->complete(hcd, qtd->urb->priv, qtd->urb, 0);
+		release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_URB_COMPLETE);
+		break;
+
+	case FIQ_PER_SPLIT_LS_ABORTED:
+		if (hcint.b.xacterr) {
+			/* Hub has responded with an ERR packet. Device
+			 * has been unplugged or the port has been disabled.
+			 * TODO: need to issue a reset to the hub port. */
+			qtd->error_count += 3;
+			handle_hc_xacterr_intr(hcd, hc, hc_regs, qtd);
+		} else if (hcint.b.stall) {
+			handle_hc_stall_intr(hcd, hc, hc_regs, qtd);
+		} else if (hcint.b.bblerr) {
+			handle_hc_babble_intr(hcd, hc, hc_regs, qtd);
+		} else {
+			printk_ratelimited(KERN_INFO "Transfer to device %d endpoint 0x%x failed "
+				"- FIQ reported FSM=%d. Data may have been lost.\n",
+				st->fsm, hc->dev_addr, hc->ep_num);
+			release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_NO_HALT_STATUS);
+		}
+		break;
+
+	case FIQ_PER_SPLIT_HS_ABORTED:
+		/* Either the SSPLIT phase suffered transaction errors or something
+		 * unexpected happened.
+		 */
+		qtd->error_count += 3;
+		handle_hc_xacterr_intr(hcd, hc, hc_regs, qtd);
+		release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_NO_HALT_STATUS);
+		break;
+
+	case FIQ_PER_SPLIT_TIMEOUT:
+		/* Couldn't complete in the nominated frame */
+		printk(KERN_INFO "Transfer to device %d endpoint 0x%x frame %d failed "
+				"- FIQ timed out. Data may have been lost.\n",
+				hc->dev_addr, hc->ep_num, dwc_otg_hcd_get_frame_number(hcd) >> 3);
+		if (hc->ep_type == UE_ISOCHRONOUS) {
+			struct dwc_otg_hcd_iso_packet_desc *frame_desc = &qtd->urb->iso_descs[qtd->isoc_frame_index];
+			/* Record errors, update qtd. */
+			frame_desc->actual_length = 0;
+			if (hc->ep_is_in) {
+				frame_desc->status = -DWC_E_NO_STREAM_RES;
+			} else {
+				frame_desc->status = -DWC_E_COMMUNICATION;
+			}
+			qtd->isoc_frame_index++;
+			if (qtd->isoc_frame_index == qtd->urb->packet_count) {
+				hcd->fops->complete(hcd, qtd->urb->priv, qtd->urb, 0);
+				release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_URB_COMPLETE);
+			} else {
+				release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_COMPLETE);
+			}
+		} else {
+			release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_NO_HALT_STATUS);
+		}
+		break;
+
+	default:
+		DWC_WARN("Unexpected state received on hc=%d fsm=%d on transfer to device %d ep 0x%x", 
+					hc->hc_num, st->fsm, hc->dev_addr, hc->ep_num);
+		qtd->error_count++;
+		release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_NO_HALT_STATUS);
+	}
+	return;
+}
+
+/** Handles interrupt for a specific Host Channel */
+int32_t dwc_otg_hcd_handle_hc_n_intr(dwc_otg_hcd_t * dwc_otg_hcd, uint32_t num)
+{
+	int retval = 0;
+	hcint_data_t hcint;
+	hcintmsk_data_t hcintmsk;
+	dwc_hc_t *hc;
+	dwc_otg_hc_regs_t *hc_regs;
+	dwc_otg_qtd_t *qtd;
+
+	DWC_DEBUGPL(DBG_HCDV, "--Host Channel Interrupt--, Channel %d\n", num);
+
+	hc = dwc_otg_hcd->hc_ptr_array[num];
+	hc_regs = dwc_otg_hcd->core_if->host_if->hc_regs[num];
+	if(hc->halt_status == DWC_OTG_HC_XFER_URB_DEQUEUE) {
+		/* A dequeue was issued for this transfer. Our QTD has gone away
+		 * but in the case of a FIQ transfer, the transfer would have run
+		 * to completion.
+		 */
+		if (fiq_fsm_enable && dwc_otg_hcd->fiq_state->channel[num].fsm != FIQ_PASSTHROUGH) {
+			dwc_otg_hcd_handle_hc_fsm(dwc_otg_hcd, num);
+		} else {
+			release_channel(dwc_otg_hcd, hc, NULL, hc->halt_status);
+		}
+		return 1;
+	}
+	qtd = DWC_CIRCLEQ_FIRST(&hc->qh->qtd_list);
+
+	/*
+	 * FSM mode: Check to see if this is a HC interrupt from a channel handled by the FIQ.
+	 * Execution path is fundamentally different for the channels after a FIQ has completed
+	 * a split transaction.
+	 */
+	if (fiq_fsm_enable) {
+		switch (dwc_otg_hcd->fiq_state->channel[num].fsm) {
+			case FIQ_PASSTHROUGH:
+				break;
+			case FIQ_PASSTHROUGH_ERRORSTATE:
+				/* Hook into the error count */
+				fiq_print(FIQDBG_ERR, dwc_otg_hcd->fiq_state, "HCDERR%02d", num);
+				if (!dwc_otg_hcd->fiq_state->channel[num].nr_errors) {
+					qtd->error_count = 0;
+					fiq_print(FIQDBG_ERR, dwc_otg_hcd->fiq_state, "RESET   ");
+				}
+				break;
+			default:
+				dwc_otg_hcd_handle_hc_fsm(dwc_otg_hcd, num);
+				return 1;
+		}
+	}
+
+	hcint.d32 = DWC_READ_REG32(&hc_regs->hcint);
+	hcintmsk.d32 = DWC_READ_REG32(&hc_regs->hcintmsk);
+	hcint.d32 = hcint.d32 & hcintmsk.d32;
+	if (!dwc_otg_hcd->core_if->dma_enable) {
+		if (hcint.b.chhltd && hcint.d32 != 0x2) {
+			hcint.b.chhltd = 0;
+		}
+	}
+
+	if (hcint.b.xfercomp) {
+		retval |=
+		    handle_hc_xfercomp_intr(dwc_otg_hcd, hc, hc_regs, qtd);
+		/*
+		 * If NYET occurred at same time as Xfer Complete, the NYET is
+		 * handled by the Xfer Complete interrupt handler. Don't want
+		 * to call the NYET interrupt handler in this case.
+		 */
+		hcint.b.nyet = 0;
+	}
+	if (hcint.b.chhltd) {
+		retval |= handle_hc_chhltd_intr(dwc_otg_hcd, hc, hc_regs, qtd);
+	}
+	if (hcint.b.ahberr) {
+		retval |= handle_hc_ahberr_intr(dwc_otg_hcd, hc, hc_regs, qtd);
+	}
+	if (hcint.b.stall) {
+		retval |= handle_hc_stall_intr(dwc_otg_hcd, hc, hc_regs, qtd);
+	}
+	if (hcint.b.nak) {
+		retval |= handle_hc_nak_intr(dwc_otg_hcd, hc, hc_regs, qtd);
+	}
+	if (hcint.b.ack) {
+		if(!hcint.b.chhltd)
+			retval |= handle_hc_ack_intr(dwc_otg_hcd, hc, hc_regs, qtd);
+	}
+	if (hcint.b.nyet) {
+		retval |= handle_hc_nyet_intr(dwc_otg_hcd, hc, hc_regs, qtd);
+	}
+	if (hcint.b.xacterr) {
+		retval |= handle_hc_xacterr_intr(dwc_otg_hcd, hc, hc_regs, qtd);
+	}
+	if (hcint.b.bblerr) {
+		retval |= handle_hc_babble_intr(dwc_otg_hcd, hc, hc_regs, qtd);
+	}
+	if (hcint.b.frmovrun) {
+		retval |=
+		    handle_hc_frmovrun_intr(dwc_otg_hcd, hc, hc_regs, qtd);
+	}
+	if (hcint.b.datatglerr) {
+		retval |=
+		    handle_hc_datatglerr_intr(dwc_otg_hcd, hc, hc_regs, qtd);
+	}
+
+	return retval;
+}
+#endif /* DWC_DEVICE_ONLY */
diff -Nur --no-dereference linux-5.10.52-orig/drivers/usb/host/dwc_otg/dwc_otg_hcd_linux.c linux-5.10.52-v7l+/drivers/usb/host/dwc_otg/dwc_otg_hcd_linux.c
--- linux-5.10.52-orig/drivers/usb/host/dwc_otg/dwc_otg_hcd_linux.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/usb/host/dwc_otg/dwc_otg_hcd_linux.c	2021-07-25 16:46:28.697909777 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+
+/* ==========================================================================
+ * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_hcd_linux.c $
+ * $Revision: #20 $
+ * $Date: 2011/10/26 $
+ * $Change: 1872981 $
+ *
+ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter,
+ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless
+ * otherwise expressly agreed to in writing between Synopsys and you.
+ *
+ * The Software IS NOT an item of Licensed Software or Licensed Product under
+ * any End User Software License Agreement or Agreement for Licensed Product
+ * with Synopsys or any supplement thereto. You are permitted to use and
+ * redistribute this Software in source and binary forms, with or without
+ * modification, provided that redistributions of source code must retain this
+ * notice. You may not view, use, disclose, copy or distribute this file or
+ * any information contained herein except pursuant to this license grant from
+ * Synopsys. If you do not agree with this notice, including the disclaimer
+ * below, then you are not authorized to use the Software.
+ *
+ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * ========================================================================== */
+#ifndef DWC_DEVICE_ONLY
+
+/**
+ * @file
+ *
+ * This file contains the implementation of the HCD. In Linux, the HCD
+ * implements the hc_driver API.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/string.h>
+#include <linux/dma-mapping.h>
+#include <linux/version.h>
+#include <asm/io.h>
+#ifdef CONFIG_ARM
+#include <asm/fiq.h>
+#endif
+#include <linux/usb.h>
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,35)
+#include <../drivers/usb/core/hcd.h>
+#else
+#include <linux/usb/hcd.h>
+#endif
+#include <asm/bug.h>
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,30))
+#define USB_URB_EP_LINKING 1
+#else
+#define USB_URB_EP_LINKING 0
+#endif
+
+#include "dwc_otg_hcd_if.h"
+#include "dwc_otg_dbg.h"
+#include "dwc_otg_driver.h"
+#include "dwc_otg_hcd.h"
+
+#ifndef __virt_to_bus
+#define __virt_to_bus	__virt_to_phys
+#define __bus_to_virt	__phys_to_virt
+#define __pfn_to_bus(x)	__pfn_to_phys(x)
+#define __bus_to_pfn(x)	__phys_to_pfn(x)
+#endif
+
+extern unsigned char  _dwc_otg_fiq_stub, _dwc_otg_fiq_stub_end;
+
+/**
+ * Gets the endpoint number from a _bEndpointAddress argument. The endpoint is
+ * qualified with its direction (possible 32 endpoints per device).
+ */
+#define dwc_ep_addr_to_endpoint(_bEndpointAddress_) ((_bEndpointAddress_ & USB_ENDPOINT_NUMBER_MASK) | \
+						     ((_bEndpointAddress_ & USB_DIR_IN) != 0) << 4)
+
+static const char dwc_otg_hcd_name[] = "dwc_otg_hcd";
+
+extern bool fiq_enable;
+
+/** @name Linux HC Driver API Functions */
+/** @{ */
+/* manage i/o requests, device state */
+static int dwc_otg_urb_enqueue(struct usb_hcd *hcd,
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28)
+		       struct usb_host_endpoint *ep,
+#endif
+		       struct urb *urb, gfp_t mem_flags);
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,30)
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28)
+static int dwc_otg_urb_dequeue(struct usb_hcd *hcd, struct urb *urb);
+#endif
+#else /* kernels at or post 2.6.30 */
+static int dwc_otg_urb_dequeue(struct usb_hcd *hcd,
+                               struct urb *urb, int status);
+#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2,6,30) */
+
+static void endpoint_disable(struct usb_hcd *hcd, struct usb_host_endpoint *ep);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,30)
+static void endpoint_reset(struct usb_hcd *hcd, struct usb_host_endpoint *ep);
+#endif
+static irqreturn_t dwc_otg_hcd_irq(struct usb_hcd *hcd);
+extern int hcd_start(struct usb_hcd *hcd);
+extern void hcd_stop(struct usb_hcd *hcd);
+static int get_frame_number(struct usb_hcd *hcd);
+extern int hub_status_data(struct usb_hcd *hcd, char *buf);
+extern int hub_control(struct usb_hcd *hcd,
+		       u16 typeReq,
+		       u16 wValue, u16 wIndex, char *buf, u16 wLength);
+
+struct wrapper_priv_data {
+	dwc_otg_hcd_t *dwc_otg_hcd;
+};
+
+/** @} */
+
+static struct hc_driver dwc_otg_hc_driver = {
+
+	.description = dwc_otg_hcd_name,
+	.product_desc = "DWC OTG Controller",
+	.hcd_priv_size = sizeof(struct wrapper_priv_data),
+
+	.irq = dwc_otg_hcd_irq,
+
+	.flags = HCD_MEMORY | HCD_DMA | HCD_USB2,
+
+	//.reset =
+	.start = hcd_start,
+	//.suspend =
+	//.resume =
+	.stop = hcd_stop,
+
+	.urb_enqueue = dwc_otg_urb_enqueue,
+	.urb_dequeue = dwc_otg_urb_dequeue,
+	.endpoint_disable = endpoint_disable,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,30)
+	.endpoint_reset = endpoint_reset,
+#endif
+	.get_frame_number = get_frame_number,
+
+	.hub_status_data = hub_status_data,
+	.hub_control = hub_control,
+	//.bus_suspend =
+	//.bus_resume =
+};
+
+/** Gets the dwc_otg_hcd from a struct usb_hcd */
+static inline dwc_otg_hcd_t *hcd_to_dwc_otg_hcd(struct usb_hcd *hcd)
+{
+	struct wrapper_priv_data *p;
+	p = (struct wrapper_priv_data *)(hcd->hcd_priv);
+	return p->dwc_otg_hcd;
+}
+
+/** Gets the struct usb_hcd that contains a dwc_otg_hcd_t. */
+static inline struct usb_hcd *dwc_otg_hcd_to_hcd(dwc_otg_hcd_t * dwc_otg_hcd)
+{
+	return dwc_otg_hcd_get_priv_data(dwc_otg_hcd);
+}
+
+/** Gets the usb_host_endpoint associated with an URB. */
+inline struct usb_host_endpoint *dwc_urb_to_endpoint(struct urb *urb)
+{
+	struct usb_device *dev = urb->dev;
+	int ep_num = usb_pipeendpoint(urb->pipe);
+
+	if (usb_pipein(urb->pipe))
+		return dev->ep_in[ep_num];
+	else
+		return dev->ep_out[ep_num];
+}
+
+static int _disconnect(dwc_otg_hcd_t * hcd)
+{
+	struct usb_hcd *usb_hcd = dwc_otg_hcd_to_hcd(hcd);
+
+	usb_hcd->self.is_b_host = 0;
+	return 0;
+}
+
+static int _start(dwc_otg_hcd_t * hcd)
+{
+	struct usb_hcd *usb_hcd = dwc_otg_hcd_to_hcd(hcd);
+
+	usb_hcd->self.is_b_host = dwc_otg_hcd_is_b_host(hcd);
+	hcd_start(usb_hcd);
+
+	return 0;
+}
+
+static int _hub_info(dwc_otg_hcd_t * hcd, void *urb_handle, uint32_t * hub_addr,
+		     uint32_t * port_addr)
+{
+   struct urb *urb = (struct urb *)urb_handle;
+   struct usb_bus *bus;
+#if 1 //GRAYG - temporary
+   if (NULL == urb_handle)
+      DWC_ERROR("**** %s - NULL URB handle\n", __func__);//GRAYG
+   if (NULL == urb->dev)
+      DWC_ERROR("**** %s - URB has no device\n", __func__);//GRAYG
+   if (NULL == port_addr)
+      DWC_ERROR("**** %s - NULL port_address\n", __func__);//GRAYG
+#endif
+   if (urb->dev->tt) {
+        if (NULL == urb->dev->tt->hub) {
+                DWC_ERROR("**** %s - (URB's transactor has no TT - giving no hub)\n",
+                           __func__); //GRAYG
+                //*hub_addr = (u8)usb_pipedevice(urb->pipe); //GRAYG
+                *hub_addr = 0; //GRAYG
+                // we probably shouldn't have a transaction translator if
+                // there's no associated hub?
+        } else {
+		bus = hcd_to_bus(dwc_otg_hcd_to_hcd(hcd));
+		if (urb->dev->tt->hub == bus->root_hub)
+			*hub_addr = 0;
+		else
+			*hub_addr = urb->dev->tt->hub->devnum;
+	}
+	*port_addr = urb->dev->ttport;
+   } else {
+        *hub_addr = 0;
+	*port_addr = urb->dev->ttport;
+   }
+   return 0;
+}
+
+static int _speed(dwc_otg_hcd_t * hcd, void *urb_handle)
+{
+	struct urb *urb = (struct urb *)urb_handle;
+	return urb->dev->speed;
+}
+
+static int _get_b_hnp_enable(dwc_otg_hcd_t * hcd)
+{
+	struct usb_hcd *usb_hcd = dwc_otg_hcd_to_hcd(hcd);
+	return usb_hcd->self.b_hnp_enable;
+}
+
+static void allocate_bus_bandwidth(struct usb_hcd *hcd, uint32_t bw,
+				   struct urb *urb)
+{
+	hcd_to_bus(hcd)->bandwidth_allocated += bw / urb->interval;
+	if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
+		hcd_to_bus(hcd)->bandwidth_isoc_reqs++;
+	} else {
+		hcd_to_bus(hcd)->bandwidth_int_reqs++;
+	}
+}
+
+static void free_bus_bandwidth(struct usb_hcd *hcd, uint32_t bw,
+			       struct urb *urb)
+{
+	hcd_to_bus(hcd)->bandwidth_allocated -= bw / urb->interval;
+	if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
+		hcd_to_bus(hcd)->bandwidth_isoc_reqs--;
+	} else {
+		hcd_to_bus(hcd)->bandwidth_int_reqs--;
+	}
+}
+
+/**
+ * Sets the final status of an URB and returns it to the device driver. Any
+ * required cleanup of the URB is performed.  The HCD lock should be held on
+ * entry.
+ */
+static int _complete(dwc_otg_hcd_t * hcd, void *urb_handle,
+		     dwc_otg_hcd_urb_t * dwc_otg_urb, int32_t status)
+{
+	struct urb *urb = (struct urb *)urb_handle;
+	urb_tq_entry_t *new_entry;
+	int rc = 0;
+	if (CHK_DEBUG_LEVEL(DBG_HCDV | DBG_HCD_URB)) {
+		DWC_PRINTF("%s: urb %p, device %d, ep %d %s, status=%d\n",
+			   __func__, urb, usb_pipedevice(urb->pipe),
+			   usb_pipeendpoint(urb->pipe),
+			   usb_pipein(urb->pipe) ? "IN" : "OUT", status);
+		if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
+			int i;
+			for (i = 0; i < urb->number_of_packets; i++) {
+				DWC_PRINTF("  ISO Desc %d status: %d\n",
+					   i, urb->iso_frame_desc[i].status);
+			}
+		}
+	}
+	new_entry = DWC_ALLOC_ATOMIC(sizeof(urb_tq_entry_t));
+	urb->actual_length = dwc_otg_hcd_urb_get_actual_length(dwc_otg_urb);
+	/* Convert status value. */
+	switch (status) {
+	case -DWC_E_PROTOCOL:
+		status = -EPROTO;
+		break;
+	case -DWC_E_IN_PROGRESS:
+		status = -EINPROGRESS;
+		break;
+	case -DWC_E_PIPE:
+		status = -EPIPE;
+		break;
+	case -DWC_E_IO:
+		status = -EIO;
+		break;
+	case -DWC_E_TIMEOUT:
+		status = -ETIMEDOUT;
+		break;
+	case -DWC_E_OVERFLOW:
+		status = -EOVERFLOW;
+		break;
+	case -DWC_E_SHUTDOWN:
+		status = -ESHUTDOWN;
+		break;
+	default:
+		if (status) {
+			DWC_PRINTF("Uknown urb status %d\n", status);
+
+		}
+	}
+
+	if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
+		int i;
+
+		urb->error_count = dwc_otg_hcd_urb_get_error_count(dwc_otg_urb);
+		urb->actual_length = 0;
+		for (i = 0; i < urb->number_of_packets; ++i) {
+			urb->iso_frame_desc[i].actual_length =
+			    dwc_otg_hcd_urb_get_iso_desc_actual_length
+			    (dwc_otg_urb, i);
+			urb->actual_length += urb->iso_frame_desc[i].actual_length;
+			urb->iso_frame_desc[i].status =
+			    dwc_otg_hcd_urb_get_iso_desc_status(dwc_otg_urb, i);
+		}
+	}
+
+	urb->status = status;
+	urb->hcpriv = NULL;
+	if (!status) {
+		if ((urb->transfer_flags & URB_SHORT_NOT_OK) &&
+		    (urb->actual_length < urb->transfer_buffer_length)) {
+			urb->status = -EREMOTEIO;
+		}
+	}
+
+	if ((usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) ||
+	    (usb_pipetype(urb->pipe) == PIPE_INTERRUPT)) {
+		struct usb_host_endpoint *ep = dwc_urb_to_endpoint(urb);
+		if (ep) {
+			free_bus_bandwidth(dwc_otg_hcd_to_hcd(hcd),
+					   dwc_otg_hcd_get_ep_bandwidth(hcd,
+									ep->hcpriv),
+					   urb);
+		}
+	}
+	DWC_FREE(dwc_otg_urb);
+	if (!new_entry) {
+		DWC_ERROR("dwc_otg_hcd: complete: cannot allocate URB TQ entry\n");
+		urb->status = -EPROTO;
+		/* don't schedule the tasklet -
+		 * directly return the packet here with error. */
+#if USB_URB_EP_LINKING
+		usb_hcd_unlink_urb_from_ep(dwc_otg_hcd_to_hcd(hcd), urb);
+#endif
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28)
+		usb_hcd_giveback_urb(dwc_otg_hcd_to_hcd(hcd), urb);
+#else
+		usb_hcd_giveback_urb(dwc_otg_hcd_to_hcd(hcd), urb, urb->status);
+#endif
+	} else {
+		new_entry->urb = urb;
+#if USB_URB_EP_LINKING
+		rc = usb_hcd_check_unlink_urb(dwc_otg_hcd_to_hcd(hcd), urb, urb->status);
+		if(0 == rc) {
+			usb_hcd_unlink_urb_from_ep(dwc_otg_hcd_to_hcd(hcd), urb);
+		}
+#endif
+		if(0 == rc) {
+			DWC_TAILQ_INSERT_TAIL(&hcd->completed_urb_list, new_entry,
+						urb_tq_entries);
+			DWC_TASK_HI_SCHEDULE(hcd->completion_tasklet);
+		}
+	}
+	return 0;
+}
+
+static struct dwc_otg_hcd_function_ops hcd_fops = {
+	.start = _start,
+	.disconnect = _disconnect,
+	.hub_info = _hub_info,
+	.speed = _speed,
+	.complete = _complete,
+	.get_b_hnp_enable = _get_b_hnp_enable,
+};
+
+#ifdef CONFIG_ARM64
+
+static int simfiq_irq = -1;
+
+void local_fiq_enable(void)
+{
+	if (simfiq_irq >= 0)
+		enable_irq(simfiq_irq);
+}
+
+void local_fiq_disable(void)
+{
+	if (simfiq_irq >= 0)
+		disable_irq(simfiq_irq);
+}
+
+irqreturn_t fiq_irq_handler(int irq, void *dev_id)
+{
+	dwc_otg_hcd_t *dwc_otg_hcd = (dwc_otg_hcd_t *)dev_id;
+
+	if (fiq_fsm_enable)
+		dwc_otg_fiq_fsm(dwc_otg_hcd->fiq_state, dwc_otg_hcd->core_if->core_params->host_channels);
+	else
+		dwc_otg_fiq_nop(dwc_otg_hcd->fiq_state);
+
+	return IRQ_HANDLED;
+}
+
+#else
+static struct fiq_handler fh = {
+  .name = "usb_fiq",
+};
+
+#endif
+
+static void hcd_init_fiq(void *cookie)
+{
+	dwc_otg_device_t *otg_dev = cookie;
+	dwc_otg_hcd_t *dwc_otg_hcd = otg_dev->hcd;
+#ifdef CONFIG_ARM64
+	int retval = 0;
+	int irq;
+#else
+	struct pt_regs regs;
+	int irq;
+
+	if (claim_fiq(&fh)) {
+		DWC_ERROR("Can't claim FIQ");
+		BUG();
+	}
+	DWC_WARN("FIQ on core %d", smp_processor_id());
+	DWC_WARN("FIQ ASM at %px length %d", &_dwc_otg_fiq_stub, (int)(&_dwc_otg_fiq_stub_end - &_dwc_otg_fiq_stub));
+	set_fiq_handler((void *) &_dwc_otg_fiq_stub, &_dwc_otg_fiq_stub_end - &_dwc_otg_fiq_stub);
+	memset(&regs,0,sizeof(regs));
+
+	regs.ARM_r8 = (long) dwc_otg_hcd->fiq_state;
+	if (fiq_fsm_enable) {
+		regs.ARM_r9 = dwc_otg_hcd->core_if->core_params->host_channels;
+		//regs.ARM_r10 = dwc_otg_hcd->dma;
+		regs.ARM_fp = (long) dwc_otg_fiq_fsm;
+	} else {
+		regs.ARM_fp = (long) dwc_otg_fiq_nop;
+	}
+
+	regs.ARM_sp = (long) dwc_otg_hcd->fiq_stack + (sizeof(struct fiq_stack) - 4);
+
+//		__show_regs(&regs);
+	set_fiq_regs(&regs);
+#endif
+
+	dwc_otg_hcd->fiq_state->dwc_regs_base = otg_dev->os_dep.base;
+	//Set the mphi periph to the required registers
+	dwc_otg_hcd->fiq_state->mphi_regs.base    = otg_dev->os_dep.mphi_base;
+	if (otg_dev->os_dep.use_swirq) {
+		dwc_otg_hcd->fiq_state->mphi_regs.swirq_set =
+			otg_dev->os_dep.mphi_base + 0x1f0;
+		dwc_otg_hcd->fiq_state->mphi_regs.swirq_clr =
+			otg_dev->os_dep.mphi_base + 0x1f4;
+		DWC_WARN("Fake MPHI regs_base at %px",
+			 dwc_otg_hcd->fiq_state->mphi_regs.base);
+	} else {
+		dwc_otg_hcd->fiq_state->mphi_regs.ctrl =
+			otg_dev->os_dep.mphi_base + 0x4c;
+		dwc_otg_hcd->fiq_state->mphi_regs.outdda
+			= otg_dev->os_dep.mphi_base + 0x28;
+		dwc_otg_hcd->fiq_state->mphi_regs.outddb
+			= otg_dev->os_dep.mphi_base + 0x2c;
+		dwc_otg_hcd->fiq_state->mphi_regs.intstat
+			= otg_dev->os_dep.mphi_base + 0x50;
+		DWC_WARN("MPHI regs_base at %px",
+			 dwc_otg_hcd->fiq_state->mphi_regs.base);
+
+		//Enable mphi peripheral
+		writel((1<<31),dwc_otg_hcd->fiq_state->mphi_regs.ctrl);
+#ifdef DEBUG
+		if (readl(dwc_otg_hcd->fiq_state->mphi_regs.ctrl) & 0x80000000)
+			DWC_WARN("MPHI periph has been enabled");
+		else
+			DWC_WARN("MPHI periph has NOT been enabled");
+#endif
+	}
+	// Enable FIQ interrupt from USB peripheral
+#ifdef CONFIG_ARM64
+	irq = otg_dev->os_dep.fiq_num;
+
+	if (irq < 0) {
+		DWC_ERROR("Can't get SIM-FIQ irq");
+		return;
+	}
+
+	retval = request_irq(irq, fiq_irq_handler, 0, "dwc_otg_sim-fiq", dwc_otg_hcd);
+
+	if (retval < 0) {
+		DWC_ERROR("Unable to request SIM-FIQ irq\n");
+		return;
+	}
+
+	simfiq_irq = irq;
+#else
+#ifdef CONFIG_GENERIC_IRQ_MULTI_HANDLER
+	irq = otg_dev->os_dep.fiq_num;
+#else
+	irq = INTERRUPT_VC_USB;
+#endif
+	if (irq < 0) {
+		DWC_ERROR("Can't get FIQ irq");
+		return;
+	}
+	/*
+	 * We could take an interrupt immediately after enabling the FIQ.
+	 * Ensure coherency of hcd->fiq_state.
+	 */
+	smp_mb();
+	enable_fiq(irq);
+	local_fiq_enable();
+#endif
+
+}
+
+/**
+ * Initializes the HCD. This function allocates memory for and initializes the
+ * static parts of the usb_hcd and dwc_otg_hcd structures. It also registers the
+ * USB bus with the core and calls the hc_driver->start() function. It returns
+ * a negative error on failure.
+ */
+int hcd_init(dwc_bus_dev_t *_dev)
+{
+	struct usb_hcd *hcd = NULL;
+	dwc_otg_hcd_t *dwc_otg_hcd = NULL;
+	dwc_otg_device_t *otg_dev = DWC_OTG_BUSDRVDATA(_dev);
+	int retval = 0;
+        u64 dmamask;
+
+	DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD INIT otg_dev=%p\n", otg_dev);
+
+	/* Set device flags indicating whether the HCD supports DMA. */
+	if (dwc_otg_is_dma_enable(otg_dev->core_if))
+                dmamask = DMA_BIT_MASK(32);
+        else
+                dmamask = 0;
+
+#if    defined(LM_INTERFACE) || defined(PLATFORM_INTERFACE)
+        dma_set_mask(&_dev->dev, dmamask);
+        dma_set_coherent_mask(&_dev->dev, dmamask);
+#elif  defined(PCI_INTERFACE)
+        pci_set_dma_mask(_dev, dmamask);
+        pci_set_consistent_dma_mask(_dev, dmamask);
+#endif
+
+	/*
+	 * Allocate memory for the base HCD plus the DWC OTG HCD.
+	 * Initialize the base HCD.
+	 */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,30)
+	hcd = usb_create_hcd(&dwc_otg_hc_driver, &_dev->dev, _dev->dev.bus_id);
+#else
+	hcd = usb_create_hcd(&dwc_otg_hc_driver, &_dev->dev, dev_name(&_dev->dev));
+	hcd->has_tt = 1;
+//      hcd->uses_new_polling = 1;
+//      hcd->poll_rh = 0;
+#endif
+	if (!hcd) {
+		retval = -ENOMEM;
+		goto error1;
+	}
+
+	hcd->regs = otg_dev->os_dep.base;
+
+
+	/* Initialize the DWC OTG HCD. */
+	dwc_otg_hcd = dwc_otg_hcd_alloc_hcd();
+	if (!dwc_otg_hcd) {
+		goto error2;
+	}
+	((struct wrapper_priv_data *)(hcd->hcd_priv))->dwc_otg_hcd =
+	    dwc_otg_hcd;
+	otg_dev->hcd = dwc_otg_hcd;
+	otg_dev->hcd->otg_dev = otg_dev;
+
+#ifdef CONFIG_ARM64
+	if (dwc_otg_hcd_init(dwc_otg_hcd, otg_dev->core_if))
+		goto error2;
+
+	if (fiq_enable)
+		hcd_init_fiq(otg_dev);
+#else
+	if (dwc_otg_hcd_init(dwc_otg_hcd, otg_dev->core_if)) {
+		goto error2;
+	}
+
+	if (fiq_enable) {
+		if (num_online_cpus() > 1) {
+			/*
+			 * bcm2709: can run the FIQ on a separate core to IRQs.
+			 * Ensure driver state is visible to other cores before setting up the FIQ.
+			 */
+			smp_mb();
+			smp_call_function_single(1, hcd_init_fiq, otg_dev, 1);
+		} else {
+			smp_call_function_single(0, hcd_init_fiq, otg_dev, 1);
+		}
+	}
+#endif
+
+	hcd->self.otg_port = dwc_otg_hcd_otg_port(dwc_otg_hcd);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,33) //don't support for LM(with 2.6.20.1 kernel)
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,35) //version field absent later
+	hcd->self.otg_version = dwc_otg_get_otg_version(otg_dev->core_if);
+#endif
+	/* Don't support SG list at this point */
+	hcd->self.sg_tablesize = 0;
+#endif
+	/*
+	 * Finish generic HCD initialization and start the HCD. This function
+	 * allocates the DMA buffer pool, registers the USB bus, requests the
+	 * IRQ line, and calls hcd_start method.
+	 */
+	retval = usb_add_hcd(hcd, otg_dev->os_dep.irq_num, IRQF_SHARED);
+	if (retval < 0) {
+		goto error2;
+	}
+
+	dwc_otg_hcd_set_priv_data(dwc_otg_hcd, hcd);
+	return 0;
+
+error2:
+	usb_put_hcd(hcd);
+error1:
+	return retval;
+}
+
+/**
+ * Removes the HCD.
+ * Frees memory and resources associated with the HCD and deregisters the bus.
+ */
+void hcd_remove(dwc_bus_dev_t *_dev)
+{
+	dwc_otg_device_t *otg_dev = DWC_OTG_BUSDRVDATA(_dev);
+	dwc_otg_hcd_t *dwc_otg_hcd;
+	struct usb_hcd *hcd;
+
+	DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD REMOVE otg_dev=%p\n", otg_dev);
+
+	if (!otg_dev) {
+		DWC_DEBUGPL(DBG_ANY, "%s: otg_dev NULL!\n", __func__);
+		return;
+	}
+
+	dwc_otg_hcd = otg_dev->hcd;
+
+	if (!dwc_otg_hcd) {
+		DWC_DEBUGPL(DBG_ANY, "%s: otg_dev->hcd NULL!\n", __func__);
+		return;
+	}
+
+	hcd = dwc_otg_hcd_to_hcd(dwc_otg_hcd);
+
+	if (!hcd) {
+		DWC_DEBUGPL(DBG_ANY,
+			    "%s: dwc_otg_hcd_to_hcd(dwc_otg_hcd) NULL!\n",
+			    __func__);
+		return;
+	}
+	usb_remove_hcd(hcd);
+	dwc_otg_hcd_set_priv_data(dwc_otg_hcd, NULL);
+	dwc_otg_hcd_remove(dwc_otg_hcd);
+	usb_put_hcd(hcd);
+}
+
+/* =========================================================================
+ *  Linux HC Driver Functions
+ * ========================================================================= */
+
+/** Initializes the DWC_otg controller and its root hub and prepares it for host
+ * mode operation. Activates the root port. Returns 0 on success and a negative
+ * error code on failure. */
+int hcd_start(struct usb_hcd *hcd)
+{
+	dwc_otg_hcd_t *dwc_otg_hcd = hcd_to_dwc_otg_hcd(hcd);
+	struct usb_bus *bus;
+
+	DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD START\n");
+	bus = hcd_to_bus(hcd);
+
+	hcd->state = HC_STATE_RUNNING;
+	if (dwc_otg_hcd_start(dwc_otg_hcd, &hcd_fops)) {
+		return 0;
+	}
+
+	/* Initialize and connect root hub if one is not already attached */
+	if (bus->root_hub) {
+		DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD Has Root Hub\n");
+		/* Inform the HUB driver to resume. */
+		usb_hcd_resume_root_hub(hcd);
+	}
+
+	return 0;
+}
+
+/**
+ * Halts the DWC_otg host mode operations in a clean manner. USB transfers are
+ * stopped.
+ */
+void hcd_stop(struct usb_hcd *hcd)
+{
+	dwc_otg_hcd_t *dwc_otg_hcd = hcd_to_dwc_otg_hcd(hcd);
+
+	dwc_otg_hcd_stop(dwc_otg_hcd);
+}
+
+/** Returns the current frame number. */
+static int get_frame_number(struct usb_hcd *hcd)
+{
+	hprt0_data_t hprt0;
+	dwc_otg_hcd_t *dwc_otg_hcd = hcd_to_dwc_otg_hcd(hcd);
+	hprt0.d32 = DWC_READ_REG32(dwc_otg_hcd->core_if->host_if->hprt0);
+	if (hprt0.b.prtspd == DWC_HPRT0_PRTSPD_HIGH_SPEED)
+		return dwc_otg_hcd_get_frame_number(dwc_otg_hcd) >> 3;
+	else
+		return dwc_otg_hcd_get_frame_number(dwc_otg_hcd);
+}
+
+#ifdef DEBUG
+static void dump_urb_info(struct urb *urb, char *fn_name)
+{
+	DWC_PRINTF("%s, urb %p\n", fn_name, urb);
+	DWC_PRINTF("  Device address: %d\n", usb_pipedevice(urb->pipe));
+	DWC_PRINTF("  Endpoint: %d, %s\n", usb_pipeendpoint(urb->pipe),
+		   (usb_pipein(urb->pipe) ? "IN" : "OUT"));
+	DWC_PRINTF("  Endpoint type: %s\n", ( {
+					     char *pipetype;
+					     switch (usb_pipetype(urb->pipe)) {
+case PIPE_CONTROL:
+pipetype = "CONTROL"; break; case PIPE_BULK:
+pipetype = "BULK"; break; case PIPE_INTERRUPT:
+pipetype = "INTERRUPT"; break; case PIPE_ISOCHRONOUS:
+pipetype = "ISOCHRONOUS"; break; default:
+					     pipetype = "UNKNOWN"; break;};
+					     pipetype;}
+		   )) ;
+	DWC_PRINTF("  Speed: %s\n", ( {
+				     char *speed; switch (urb->dev->speed) {
+case USB_SPEED_HIGH:
+speed = "HIGH"; break; case USB_SPEED_FULL:
+speed = "FULL"; break; case USB_SPEED_LOW:
+speed = "LOW"; break; default:
+				     speed = "UNKNOWN"; break;};
+				     speed;}
+		   )) ;
+	DWC_PRINTF("  Max packet size: %d\n",
+		   usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe)));
+	DWC_PRINTF("  Data buffer length: %d\n", urb->transfer_buffer_length);
+	DWC_PRINTF("  Transfer buffer: %p, Transfer DMA: %p\n",
+		   urb->transfer_buffer, (void *)urb->transfer_dma);
+	DWC_PRINTF("  Setup buffer: %p, Setup DMA: %p\n",
+		   urb->setup_packet, (void *)urb->setup_dma);
+	DWC_PRINTF("  Interval: %d\n", urb->interval);
+	if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
+		int i;
+		for (i = 0; i < urb->number_of_packets; i++) {
+			DWC_PRINTF("  ISO Desc %d:\n", i);
+			DWC_PRINTF("    offset: %d, length %d\n",
+				   urb->iso_frame_desc[i].offset,
+				   urb->iso_frame_desc[i].length);
+		}
+	}
+}
+#endif
+
+/** Starts processing a USB transfer request specified by a USB Request Block
+ * (URB). mem_flags indicates the type of memory allocation to use while
+ * processing this URB. */
+static int dwc_otg_urb_enqueue(struct usb_hcd *hcd,
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28)
+		       struct usb_host_endpoint *ep,
+#endif
+		       struct urb *urb, gfp_t mem_flags)
+{
+	int retval = 0;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28)
+	struct usb_host_endpoint *ep = urb->ep;
+#endif
+	dwc_irqflags_t irqflags;
+        void **ref_ep_hcpriv = &ep->hcpriv;
+	dwc_otg_hcd_t *dwc_otg_hcd = hcd_to_dwc_otg_hcd(hcd);
+	dwc_otg_hcd_urb_t *dwc_otg_urb;
+	int i;
+	int alloc_bandwidth = 0;
+	uint8_t ep_type = 0;
+	uint32_t flags = 0;
+	void *buf;
+
+#ifdef DEBUG
+	if (CHK_DEBUG_LEVEL(DBG_HCDV | DBG_HCD_URB)) {
+		dump_urb_info(urb, "dwc_otg_urb_enqueue");
+	}
+#endif
+	if ((usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS)
+	    || (usb_pipetype(urb->pipe) == PIPE_INTERRUPT)) {
+		if (!dwc_otg_hcd_is_bandwidth_allocated
+		    (dwc_otg_hcd, ref_ep_hcpriv)) {
+			alloc_bandwidth = 1;
+		}
+	}
+
+	switch (usb_pipetype(urb->pipe)) {
+	case PIPE_CONTROL:
+		ep_type = USB_ENDPOINT_XFER_CONTROL;
+		break;
+	case PIPE_ISOCHRONOUS:
+		ep_type = USB_ENDPOINT_XFER_ISOC;
+		break;
+	case PIPE_BULK:
+		ep_type = USB_ENDPOINT_XFER_BULK;
+		break;
+	case PIPE_INTERRUPT:
+		ep_type = USB_ENDPOINT_XFER_INT;
+		break;
+	default:
+                DWC_WARN("Wrong EP type - %d\n", usb_pipetype(urb->pipe));
+	}
+
+        /* # of packets is often 0 - do we really need to call this then? */
+	dwc_otg_urb = dwc_otg_hcd_urb_alloc(dwc_otg_hcd,
+					    urb->number_of_packets,
+					    mem_flags == GFP_ATOMIC ? 1 : 0);
+
+	if(dwc_otg_urb == NULL)
+		return -ENOMEM;
+
+	if (!dwc_otg_urb && urb->number_of_packets)
+		return -ENOMEM;
+
+	dwc_otg_hcd_urb_set_pipeinfo(dwc_otg_urb, usb_pipedevice(urb->pipe),
+				     usb_pipeendpoint(urb->pipe), ep_type,
+				     usb_pipein(urb->pipe),
+				     usb_maxpacket(urb->dev, urb->pipe,
+						   !(usb_pipein(urb->pipe))));
+
+	buf = urb->transfer_buffer;
+	if (hcd_uses_dma(hcd) && !buf && urb->transfer_buffer_length) {
+		/*
+		 * Calculate virtual address from physical address,
+		 * because some class driver may not fill transfer_buffer.
+		 * In Buffer DMA mode virual address is used,
+		 * when handling non DWORD aligned buffers.
+		 */
+		buf = (void *)__bus_to_virt((unsigned long)urb->transfer_dma);
+		dev_warn_once(&urb->dev->dev,
+			      "USB transfer_buffer was NULL, will use __bus_to_virt(%pad)=%p\n",
+			      &urb->transfer_dma, buf);
+	}
+
+	if (!buf && urb->transfer_buffer_length) {
+		DWC_FREE(dwc_otg_urb);
+		DWC_ERROR("transfer_buffer is NULL in PIO mode or both "
+			   "transfer_buffer and transfer_dma are NULL in DMA mode\n");
+		return -EINVAL;
+	}
+
+	if (!(urb->transfer_flags & URB_NO_INTERRUPT))
+		flags |= URB_GIVEBACK_ASAP;
+	if (urb->transfer_flags & URB_ZERO_PACKET)
+		flags |= URB_SEND_ZERO_PACKET;
+
+	dwc_otg_hcd_urb_set_params(dwc_otg_urb, urb, buf,
+				   urb->transfer_dma,
+				   urb->transfer_buffer_length,
+				   urb->setup_packet,
+				   urb->setup_dma, flags, urb->interval);
+
+	for (i = 0; i < urb->number_of_packets; ++i) {
+		dwc_otg_hcd_urb_set_iso_desc_params(dwc_otg_urb, i,
+						    urb->
+						    iso_frame_desc[i].offset,
+						    urb->
+						    iso_frame_desc[i].length);
+	}
+
+	DWC_SPINLOCK_IRQSAVE(dwc_otg_hcd->lock, &irqflags);
+	urb->hcpriv = dwc_otg_urb;
+#if USB_URB_EP_LINKING
+	retval = usb_hcd_link_urb_to_ep(hcd, urb);
+	if (0 == retval)
+#endif
+	{
+		retval = dwc_otg_hcd_urb_enqueue(dwc_otg_hcd, dwc_otg_urb,
+						/*(dwc_otg_qh_t **)*/
+						ref_ep_hcpriv, 1);
+		if (0 == retval) {
+			if (alloc_bandwidth) {
+				allocate_bus_bandwidth(hcd,
+						dwc_otg_hcd_get_ep_bandwidth(
+							dwc_otg_hcd, *ref_ep_hcpriv),
+						urb);
+			}
+		} else {
+			DWC_DEBUGPL(DBG_HCD, "DWC OTG dwc_otg_hcd_urb_enqueue failed rc %d\n", retval);
+#if USB_URB_EP_LINKING
+			usb_hcd_unlink_urb_from_ep(hcd, urb);
+#endif
+			DWC_FREE(dwc_otg_urb);
+			urb->hcpriv = NULL;
+			if (retval == -DWC_E_NO_DEVICE)
+				retval = -ENODEV;
+		}
+	}
+#if USB_URB_EP_LINKING
+	else
+	{
+		DWC_FREE(dwc_otg_urb);
+		urb->hcpriv = NULL;
+	}
+#endif
+	DWC_SPINUNLOCK_IRQRESTORE(dwc_otg_hcd->lock, irqflags);
+	return retval;
+}
+
+/** Aborts/cancels a USB transfer request. Always returns 0 to indicate
+ * success.  */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28)
+static int dwc_otg_urb_dequeue(struct usb_hcd *hcd, struct urb *urb)
+#else
+static int dwc_otg_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
+#endif
+{
+	dwc_irqflags_t flags;
+	dwc_otg_hcd_t *dwc_otg_hcd;
+        int rc;
+
+	DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD URB Dequeue\n");
+
+	dwc_otg_hcd = hcd_to_dwc_otg_hcd(hcd);
+
+#ifdef DEBUG
+	if (CHK_DEBUG_LEVEL(DBG_HCDV | DBG_HCD_URB)) {
+		dump_urb_info(urb, "dwc_otg_urb_dequeue");
+	}
+#endif
+
+	DWC_SPINLOCK_IRQSAVE(dwc_otg_hcd->lock, &flags);
+	rc = usb_hcd_check_unlink_urb(hcd, urb, status);
+	if (0 == rc) {
+		if(urb->hcpriv != NULL) {
+	                dwc_otg_hcd_urb_dequeue(dwc_otg_hcd,
+	                                    (dwc_otg_hcd_urb_t *)urb->hcpriv);
+
+		        DWC_FREE(urb->hcpriv);
+			urb->hcpriv = NULL;
+		}
+        }
+
+        if (0 == rc) {
+		/* Higher layer software sets URB status. */
+#if USB_URB_EP_LINKING
+                usb_hcd_unlink_urb_from_ep(hcd, urb);
+#endif
+		DWC_SPINUNLOCK_IRQRESTORE(dwc_otg_hcd->lock, flags);
+
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28)
+                usb_hcd_giveback_urb(hcd, urb);
+#else
+                usb_hcd_giveback_urb(hcd, urb, status);
+#endif
+                if (CHK_DEBUG_LEVEL(DBG_HCDV | DBG_HCD_URB)) {
+                        DWC_PRINTF("Called usb_hcd_giveback_urb() \n");
+                        DWC_PRINTF("  1urb->status = %d\n", urb->status);
+                }
+                DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD URB Dequeue OK\n");
+        } else {
+		DWC_SPINUNLOCK_IRQRESTORE(dwc_otg_hcd->lock, flags);
+                DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD URB Dequeue failed - rc %d\n",
+                            rc);
+        }
+
+	return rc;
+}
+
+/* Frees resources in the DWC_otg controller related to a given endpoint. Also
+ * clears state in the HCD related to the endpoint. Any URBs for the endpoint
+ * must already be dequeued. */
+static void endpoint_disable(struct usb_hcd *hcd, struct usb_host_endpoint *ep)
+{
+	dwc_otg_hcd_t *dwc_otg_hcd = hcd_to_dwc_otg_hcd(hcd);
+
+	DWC_DEBUGPL(DBG_HCD,
+		    "DWC OTG HCD EP DISABLE: _bEndpointAddress=0x%02x, "
+		    "endpoint=%d\n", ep->desc.bEndpointAddress,
+		    dwc_ep_addr_to_endpoint(ep->desc.bEndpointAddress));
+	dwc_otg_hcd_endpoint_disable(dwc_otg_hcd, ep->hcpriv, 250);
+	ep->hcpriv = NULL;
+}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,30)
+/* Resets endpoint specific parameter values, in current version used to reset
+ * the data toggle(as a WA). This function can be called from usb_clear_halt routine */
+static void endpoint_reset(struct usb_hcd *hcd, struct usb_host_endpoint *ep)
+{
+	dwc_irqflags_t flags;
+	dwc_otg_hcd_t *dwc_otg_hcd = hcd_to_dwc_otg_hcd(hcd);
+
+	DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD EP RESET: Endpoint Num=0x%02d\n",
+		    ep->desc.bEndpointAddress);
+
+	DWC_SPINLOCK_IRQSAVE(dwc_otg_hcd->lock, &flags);
+	if (ep->hcpriv) {
+		dwc_otg_hcd_endpoint_reset(dwc_otg_hcd, ep->hcpriv);
+	}
+	DWC_SPINUNLOCK_IRQRESTORE(dwc_otg_hcd->lock, flags);
+}
+#endif
+
+/** Handles host mode interrupts for the DWC_otg controller. Returns IRQ_NONE if
+ * there was no interrupt to handle. Returns IRQ_HANDLED if there was a valid
+ * interrupt.
+ *
+ * This function is called by the USB core when an interrupt occurs */
+static irqreturn_t dwc_otg_hcd_irq(struct usb_hcd *hcd)
+{
+	dwc_otg_hcd_t *dwc_otg_hcd = hcd_to_dwc_otg_hcd(hcd);
+	int32_t retval = dwc_otg_hcd_handle_intr(dwc_otg_hcd);
+	if (retval != 0) {
+		S3C2410X_CLEAR_EINTPEND();
+	}
+	return IRQ_RETVAL(retval);
+}
+
+/** Creates Status Change bitmap for the root hub and root port. The bitmap is
+ * returned in buf. Bit 0 is the status change indicator for the root hub. Bit 1
+ * is the status change indicator for the single root port. Returns 1 if either
+ * change indicator is 1, otherwise returns 0. */
+int hub_status_data(struct usb_hcd *hcd, char *buf)
+{
+	dwc_otg_hcd_t *dwc_otg_hcd = hcd_to_dwc_otg_hcd(hcd);
+
+	buf[0] = 0;
+	buf[0] |= (dwc_otg_hcd_is_status_changed(dwc_otg_hcd, 1)) << 1;
+
+	return (buf[0] != 0);
+}
+
+/** Handles hub class-specific requests. */
+int hub_control(struct usb_hcd *hcd,
+		u16 typeReq, u16 wValue, u16 wIndex, char *buf, u16 wLength)
+{
+	int retval;
+
+	retval = dwc_otg_hcd_hub_control(hcd_to_dwc_otg_hcd(hcd),
+					 typeReq, wValue, wIndex, buf, wLength);
+
+	switch (retval) {
+	case -DWC_E_INVALID:
+		retval = -EINVAL;
+		break;
+	}
+
+	return retval;
+}
+
+#endif /* DWC_DEVICE_ONLY */
diff -Nur --no-dereference linux-5.10.52-orig/drivers/usb/host/dwc_otg/dwc_otg_hcd_queue.c linux-5.10.52-v7l+/drivers/usb/host/dwc_otg/dwc_otg_hcd_queue.c
--- linux-5.10.52-orig/drivers/usb/host/dwc_otg/dwc_otg_hcd_queue.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/usb/host/dwc_otg/dwc_otg_hcd_queue.c	2021-07-25 16:46:28.697909777 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/* ==========================================================================
+ * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_hcd_queue.c $
+ * $Revision: #44 $
+ * $Date: 2011/10/26 $
+ * $Change: 1873028 $
+ *
+ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter,
+ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless
+ * otherwise expressly agreed to in writing between Synopsys and you.
+ *
+ * The Software IS NOT an item of Licensed Software or Licensed Product under
+ * any End User Software License Agreement or Agreement for Licensed Product
+ * with Synopsys or any supplement thereto. You are permitted to use and
+ * redistribute this Software in source and binary forms, with or without
+ * modification, provided that redistributions of source code must retain this
+ * notice. You may not view, use, disclose, copy or distribute this file or
+ * any information contained herein except pursuant to this license grant from
+ * Synopsys. If you do not agree with this notice, including the disclaimer
+ * below, then you are not authorized to use the Software.
+ *
+ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * ========================================================================== */
+#ifndef DWC_DEVICE_ONLY
+
+/**
+ * @file
+ *
+ * This file contains the functions to manage Queue Heads and Queue
+ * Transfer Descriptors.
+ */
+
+#include "dwc_otg_hcd.h"
+#include "dwc_otg_regs.h"
+
+extern bool microframe_schedule;
+extern unsigned short int_ep_interval_min;
+
+/**
+ * Free each QTD in the QH's QTD-list then free the QH.  QH should already be
+ * removed from a list.  QTD list should already be empty if called from URB
+ * Dequeue.
+ *
+ * @param hcd HCD instance.
+ * @param qh The QH to free.
+ */
+void dwc_otg_hcd_qh_free(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh)
+{
+	dwc_otg_qtd_t *qtd, *qtd_tmp;
+	dwc_irqflags_t flags;
+	uint32_t buf_size = 0;
+	uint8_t *align_buf_virt = NULL;
+	dwc_dma_t align_buf_dma;
+	struct device *dev = dwc_otg_hcd_to_dev(hcd);
+
+	/* Free each QTD in the QTD list */
+	DWC_SPINLOCK_IRQSAVE(hcd->lock, &flags);
+	DWC_CIRCLEQ_FOREACH_SAFE(qtd, qtd_tmp, &qh->qtd_list, qtd_list_entry) {
+		DWC_CIRCLEQ_REMOVE(&qh->qtd_list, qtd, qtd_list_entry);
+		dwc_otg_hcd_qtd_free(qtd);
+	}
+
+	if (hcd->core_if->dma_desc_enable) {
+		dwc_otg_hcd_qh_free_ddma(hcd, qh);
+	} else if (qh->dw_align_buf) {
+		if (qh->ep_type == UE_ISOCHRONOUS) {
+			buf_size = 4096;
+		} else {
+			buf_size = hcd->core_if->core_params->max_transfer_size;
+		}
+		align_buf_virt = qh->dw_align_buf;
+		align_buf_dma = qh->dw_align_buf_dma;
+	}
+
+	DWC_FREE(qh);
+	DWC_SPINUNLOCK_IRQRESTORE(hcd->lock, flags);
+	if (align_buf_virt)
+		DWC_DMA_FREE(dev, buf_size, align_buf_virt, align_buf_dma);
+	return;
+}
+
+#define BitStuffTime(bytecount)  ((8 * 7* bytecount) / 6)
+#define HS_HOST_DELAY		5	/* nanoseconds */
+#define FS_LS_HOST_DELAY	1000	/* nanoseconds */
+#define HUB_LS_SETUP		333	/* nanoseconds */
+#define NS_TO_US(ns)		((ns + 500) / 1000)
+				/* convert & round nanoseconds to microseconds */
+
+static uint32_t calc_bus_time(int speed, int is_in, int is_isoc, int bytecount)
+{
+	unsigned long retval;
+
+	switch (speed) {
+	case USB_SPEED_HIGH:
+		if (is_isoc) {
+			retval =
+			    ((38 * 8 * 2083) +
+			     (2083 * (3 + BitStuffTime(bytecount)))) / 1000 +
+			    HS_HOST_DELAY;
+		} else {
+			retval =
+			    ((55 * 8 * 2083) +
+			     (2083 * (3 + BitStuffTime(bytecount)))) / 1000 +
+			    HS_HOST_DELAY;
+		}
+		break;
+	case USB_SPEED_FULL:
+		if (is_isoc) {
+			retval =
+			    (8354 * (31 + 10 * BitStuffTime(bytecount))) / 1000;
+			if (is_in) {
+				retval = 7268 + FS_LS_HOST_DELAY + retval;
+			} else {
+				retval = 6265 + FS_LS_HOST_DELAY + retval;
+			}
+		} else {
+			retval =
+			    (8354 * (31 + 10 * BitStuffTime(bytecount))) / 1000;
+			retval = 9107 + FS_LS_HOST_DELAY + retval;
+		}
+		break;
+	case USB_SPEED_LOW:
+		if (is_in) {
+			retval =
+			    (67667 * (31 + 10 * BitStuffTime(bytecount))) /
+			    1000;
+			retval =
+			    64060 + (2 * HUB_LS_SETUP) + FS_LS_HOST_DELAY +
+			    retval;
+		} else {
+			retval =
+			    (66700 * (31 + 10 * BitStuffTime(bytecount))) /
+			    1000;
+			retval =
+			    64107 + (2 * HUB_LS_SETUP) + FS_LS_HOST_DELAY +
+			    retval;
+		}
+		break;
+	default:
+		DWC_WARN("Unknown device speed\n");
+		retval = -1;
+	}
+
+	return NS_TO_US(retval);
+}
+
+/**
+ * Initializes a QH structure.
+ *
+ * @param hcd The HCD state structure for the DWC OTG controller.
+ * @param qh  The QH to init.
+ * @param urb Holds the information about the device/endpoint that we need
+ * 	      to initialize the QH.
+ */
+#define SCHEDULE_SLOP 10
+void qh_init(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh, dwc_otg_hcd_urb_t * urb)
+{
+	char *speed, *type;
+	int dev_speed;
+	uint32_t hub_addr, hub_port;
+	hprt0_data_t hprt;
+
+	dwc_memset(qh, 0, sizeof(dwc_otg_qh_t));
+	hprt.d32 = DWC_READ_REG32(hcd->core_if->host_if->hprt0);
+
+	/* Initialize QH */
+	qh->ep_type = dwc_otg_hcd_get_pipe_type(&urb->pipe_info);
+	qh->ep_is_in = dwc_otg_hcd_is_pipe_in(&urb->pipe_info) ? 1 : 0;
+
+	qh->data_toggle = DWC_OTG_HC_PID_DATA0;
+	qh->maxp = dwc_otg_hcd_get_mps(&urb->pipe_info);
+	DWC_CIRCLEQ_INIT(&qh->qtd_list);
+	DWC_LIST_INIT(&qh->qh_list_entry);
+	qh->channel = NULL;
+
+	/* FS/LS Enpoint on HS Hub
+	 * NOT virtual root hub */
+	dev_speed = hcd->fops->speed(hcd, urb->priv);
+
+	hcd->fops->hub_info(hcd, urb->priv, &hub_addr, &hub_port);
+	qh->do_split = 0;
+	if (microframe_schedule)
+		qh->speed = dev_speed;
+
+	qh->nak_frame = 0xffff;
+
+	if (hprt.b.prtspd == DWC_HPRT0_PRTSPD_HIGH_SPEED &&
+			dev_speed != USB_SPEED_HIGH) {
+		DWC_DEBUGPL(DBG_HCD,
+			    "QH init: EP %d: TT found at hub addr %d, for port %d\n",
+			    dwc_otg_hcd_get_ep_num(&urb->pipe_info), hub_addr,
+			    hub_port);
+		qh->do_split = 1;
+		qh->skip_count = 0;
+	}
+
+	if (qh->ep_type == UE_INTERRUPT || qh->ep_type == UE_ISOCHRONOUS) {
+		/* Compute scheduling parameters once and save them. */
+
+		/** @todo Account for split transfers in the bus time. */
+		int bytecount =
+		    dwc_hb_mult(qh->maxp) * dwc_max_packet(qh->maxp);
+
+		qh->usecs =
+		    calc_bus_time((qh->do_split ? USB_SPEED_HIGH : dev_speed),
+				  qh->ep_is_in, (qh->ep_type == UE_ISOCHRONOUS),
+				  bytecount);
+		/* Start in a slightly future (micro)frame. */
+		qh->sched_frame = dwc_frame_num_inc(hcd->frame_number,
+						    SCHEDULE_SLOP);
+		qh->interval = urb->interval;
+
+		if (hprt.b.prtspd == DWC_HPRT0_PRTSPD_HIGH_SPEED) {
+			if (dev_speed == USB_SPEED_LOW ||
+					dev_speed == USB_SPEED_FULL) {
+				qh->interval *= 8;
+				qh->sched_frame |= 0x7;
+				qh->start_split_frame = qh->sched_frame;
+			} else if (int_ep_interval_min >= 2 &&
+					qh->interval < int_ep_interval_min &&
+					qh->ep_type == UE_INTERRUPT) {
+				qh->interval = int_ep_interval_min;
+			}
+		}
+	}
+
+	DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD QH Initialized\n");
+	DWC_DEBUGPL(DBG_HCDV, "DWC OTG HCD QH  - qh = %p\n", qh);
+	DWC_DEBUGPL(DBG_HCDV, "DWC OTG HCD QH  - Device Address = %d\n",
+		    dwc_otg_hcd_get_dev_addr(&urb->pipe_info));
+	DWC_DEBUGPL(DBG_HCDV, "DWC OTG HCD QH  - Endpoint %d, %s\n",
+		    dwc_otg_hcd_get_ep_num(&urb->pipe_info),
+		    dwc_otg_hcd_is_pipe_in(&urb->pipe_info) ? "IN" : "OUT");
+	switch (dev_speed) {
+	case USB_SPEED_LOW:
+		qh->dev_speed = DWC_OTG_EP_SPEED_LOW;
+		speed = "low";
+		break;
+	case USB_SPEED_FULL:
+		qh->dev_speed = DWC_OTG_EP_SPEED_FULL;
+		speed = "full";
+		break;
+	case USB_SPEED_HIGH:
+		qh->dev_speed = DWC_OTG_EP_SPEED_HIGH;
+		speed = "high";
+		break;
+	default:
+		speed = "?";
+		break;
+	}
+	DWC_DEBUGPL(DBG_HCDV, "DWC OTG HCD QH  - Speed = %s\n", speed);
+
+	switch (qh->ep_type) {
+	case UE_ISOCHRONOUS:
+		type = "isochronous";
+		break;
+	case UE_INTERRUPT:
+		type = "interrupt";
+		break;
+	case UE_CONTROL:
+		type = "control";
+		break;
+	case UE_BULK:
+		type = "bulk";
+		break;
+	default:
+		type = "?";
+		break;
+	}
+
+	DWC_DEBUGPL(DBG_HCDV, "DWC OTG HCD QH  - Type = %s\n", type);
+
+#ifdef DEBUG
+	if (qh->ep_type == UE_INTERRUPT) {
+		DWC_DEBUGPL(DBG_HCDV, "DWC OTG HCD QH - usecs = %d\n",
+			    qh->usecs);
+		DWC_DEBUGPL(DBG_HCDV, "DWC OTG HCD QH - interval = %d\n",
+			    qh->interval);
+	}
+#endif
+
+}
+
+/**
+ * This function allocates and initializes a QH.
+ *
+ * @param hcd The HCD state structure for the DWC OTG controller.
+ * @param urb Holds the information about the device/endpoint that we need
+ * 	      to initialize the QH.
+ * @param atomic_alloc Flag to do atomic allocation if needed
+ *
+ * @return Returns pointer to the newly allocated QH, or NULL on error. */
+dwc_otg_qh_t *dwc_otg_hcd_qh_create(dwc_otg_hcd_t * hcd,
+				    dwc_otg_hcd_urb_t * urb, int atomic_alloc)
+{
+	dwc_otg_qh_t *qh;
+
+	/* Allocate memory */
+	/** @todo add memflags argument */
+	qh = dwc_otg_hcd_qh_alloc(atomic_alloc);
+	if (qh == NULL) {
+		DWC_ERROR("qh allocation failed");
+		return NULL;
+	}
+
+	qh_init(hcd, qh, urb);
+
+	if (hcd->core_if->dma_desc_enable
+	    && (dwc_otg_hcd_qh_init_ddma(hcd, qh) < 0)) {
+		dwc_otg_hcd_qh_free(hcd, qh);
+		return NULL;
+	}
+
+	return qh;
+}
+
+/* microframe_schedule=0 start */
+
+/**
+ * Checks that a channel is available for a periodic transfer.
+ *
+ * @return 0 if successful, negative error code otherise.
+ */
+static int periodic_channel_available(dwc_otg_hcd_t * hcd)
+{
+	/*
+	 * Currently assuming that there is a dedicated host channnel for each
+	 * periodic transaction plus at least one host channel for
+	 * non-periodic transactions.
+	 */
+	int status;
+	int num_channels;
+
+	num_channels = hcd->core_if->core_params->host_channels;
+	if ((hcd->periodic_channels + hcd->non_periodic_channels < num_channels)
+	    && (hcd->periodic_channels < num_channels - 1)) {
+		status = 0;
+	} else {
+		DWC_INFO("%s: Total channels: %d, Periodic: %d, Non-periodic: %d\n",
+			__func__, num_channels, hcd->periodic_channels, hcd->non_periodic_channels);	//NOTICE
+		status = -DWC_E_NO_SPACE;
+	}
+
+	return status;
+}
+
+/**
+ * Checks that there is sufficient bandwidth for the specified QH in the
+ * periodic schedule. For simplicity, this calculation assumes that all the
+ * transfers in the periodic schedule may occur in the same (micro)frame.
+ *
+ * @param hcd The HCD state structure for the DWC OTG controller.
+ * @param qh QH containing periodic bandwidth required.
+ *
+ * @return 0 if successful, negative error code otherwise.
+ */
+static int check_periodic_bandwidth(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh)
+{
+	int status;
+	int16_t max_claimed_usecs;
+
+	status = 0;
+
+	if ((qh->dev_speed == DWC_OTG_EP_SPEED_HIGH) || qh->do_split) {
+		/*
+		 * High speed mode.
+		 * Max periodic usecs is 80% x 125 usec = 100 usec.
+		 */
+
+		max_claimed_usecs = 100 - qh->usecs;
+	} else {
+		/*
+		 * Full speed mode.
+		 * Max periodic usecs is 90% x 1000 usec = 900 usec.
+		 */
+		max_claimed_usecs = 900 - qh->usecs;
+	}
+
+	if (hcd->periodic_usecs > max_claimed_usecs) {
+		DWC_INFO("%s: already claimed usecs %d, required usecs %d\n", __func__, hcd->periodic_usecs, qh->usecs);	//NOTICE
+		status = -DWC_E_NO_SPACE;
+	}
+
+	return status;
+}
+
+/* microframe_schedule=0 end */
+
+/**
+ * Microframe scheduler
+ * track the total use in hcd->frame_usecs
+ * keep each qh use in qh->frame_usecs
+ * when surrendering the qh then donate the time back
+ */
+const unsigned short max_uframe_usecs[]={ 100, 100, 100, 100, 100, 100, 30, 0 };
+
+/*
+ * called from dwc_otg_hcd.c:dwc_otg_hcd_init
+ */
+void init_hcd_usecs(dwc_otg_hcd_t *_hcd)
+{
+	int i;
+	if (_hcd->flags.b.port_speed == DWC_HPRT0_PRTSPD_FULL_SPEED) {
+		_hcd->frame_usecs[0] = 900;
+		for (i = 1; i < 8; i++)
+			_hcd->frame_usecs[i] = 0;
+	} else {
+		for (i = 0; i < 8; i++)
+			_hcd->frame_usecs[i] = max_uframe_usecs[i];
+	}
+}
+
+static int find_single_uframe(dwc_otg_hcd_t * _hcd, dwc_otg_qh_t * _qh)
+{
+	int i;
+	unsigned short utime;
+	int t_left;
+	int ret;
+	int done;
+
+	ret = -1;
+	utime = _qh->usecs;
+	t_left = utime;
+	i = 0;
+	done = 0;
+	while (done == 0) {
+		/* At the start _hcd->frame_usecs[i] = max_uframe_usecs[i]; */
+		if (utime <= _hcd->frame_usecs[i]) {
+			_hcd->frame_usecs[i] -= utime;
+			_qh->frame_usecs[i] += utime;
+			t_left -= utime;
+			ret = i;
+			done = 1;
+			return ret;
+		} else {
+			i++;
+			if (i == 8) {
+				done = 1;
+				ret = -1;
+			}
+		}
+	}
+	return ret;
+ }
+
+/*
+ * use this for FS apps that can span multiple uframes
+  */
+static int find_multi_uframe(dwc_otg_hcd_t * _hcd, dwc_otg_qh_t * _qh)
+{
+	int i;
+	int j;
+	unsigned short utime;
+	int t_left;
+	int ret;
+	int done;
+	unsigned short xtime;
+
+	ret = -1;
+	utime = _qh->usecs;
+	t_left = utime;
+	i = 0;
+	done = 0;
+loop:
+	while (done == 0) {
+		if(_hcd->frame_usecs[i] <= 0) {
+			i++;
+			if (i == 8) {
+				done = 1;
+				ret = -1;
+			}
+			goto loop;
+		}
+
+		/*
+		 * we need n consecutive slots
+		 * so use j as a start slot j plus j+1 must be enough time (for now)
+		 */
+		xtime= _hcd->frame_usecs[i];
+		for (j = i+1 ; j < 8 ; j++ ) {
+                       /*
+                        * if we add this frame remaining time to xtime we may
+                        * be OK, if not we need to test j for a complete frame
+                        */
+                       if ((xtime+_hcd->frame_usecs[j]) < utime) {
+                               if (_hcd->frame_usecs[j] < max_uframe_usecs[j]) {
+                                       j = 8;
+                                       ret = -1;
+                                       continue;
+                               }
+                       }
+                       if (xtime >= utime) {
+                               ret = i;
+                               j = 8;  /* stop loop with a good value ret */
+                               continue;
+                       }
+                       /* add the frame time to x time */
+                       xtime += _hcd->frame_usecs[j];
+		       /* we must have a fully available next frame or break */
+		       if ((xtime < utime)
+				       && (_hcd->frame_usecs[j] == max_uframe_usecs[j])) {
+			       ret = -1;
+			       j = 8;  /* stop loop with a bad value ret */
+			       continue;
+		       }
+		}
+		if (ret >= 0) {
+			t_left = utime;
+			for (j = i; (t_left>0) && (j < 8); j++ ) {
+				t_left -= _hcd->frame_usecs[j];
+				if ( t_left <= 0 ) {
+					_qh->frame_usecs[j] += _hcd->frame_usecs[j] + t_left;
+					_hcd->frame_usecs[j]= -t_left;
+					ret = i;
+					done = 1;
+				} else {
+					_qh->frame_usecs[j] += _hcd->frame_usecs[j];
+					_hcd->frame_usecs[j] = 0;
+				}
+			}
+		} else {
+			i++;
+			if (i == 8) {
+				done = 1;
+				ret = -1;
+			}
+		}
+	}
+	return ret;
+}
+
+static int find_uframe(dwc_otg_hcd_t * _hcd, dwc_otg_qh_t * _qh)
+{
+	int ret;
+	ret = -1;
+
+	if (_qh->speed == USB_SPEED_HIGH ||
+		_hcd->flags.b.port_speed == DWC_HPRT0_PRTSPD_FULL_SPEED) {
+		/* if this is a hs transaction we need a full frame - or account for FS usecs */
+		ret = find_single_uframe(_hcd, _qh);
+	} else {
+		/* if this is a fs transaction we may need a sequence of frames */
+		ret = find_multi_uframe(_hcd, _qh);
+	}
+	return ret;
+}
+
+/**
+ * Checks that the max transfer size allowed in a host channel is large enough
+ * to handle the maximum data transfer in a single (micro)frame for a periodic
+ * transfer.
+ *
+ * @param hcd The HCD state structure for the DWC OTG controller.
+ * @param qh QH for a periodic endpoint.
+ *
+ * @return 0 if successful, negative error code otherwise.
+ */
+static int check_max_xfer_size(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh)
+{
+	int status;
+	uint32_t max_xfer_size;
+	uint32_t max_channel_xfer_size;
+
+	status = 0;
+
+	max_xfer_size = dwc_max_packet(qh->maxp) * dwc_hb_mult(qh->maxp);
+	max_channel_xfer_size = hcd->core_if->core_params->max_transfer_size;
+
+	if (max_xfer_size > max_channel_xfer_size) {
+		DWC_INFO("%s: Periodic xfer length %d > " "max xfer length for channel %d\n",
+				__func__, max_xfer_size, max_channel_xfer_size);	//NOTICE
+		status = -DWC_E_NO_SPACE;
+	}
+
+	return status;
+}
+
+
+
+/**
+ * Schedules an interrupt or isochronous transfer in the periodic schedule.
+ *
+ * @param hcd The HCD state structure for the DWC OTG controller.
+ * @param qh QH for the periodic transfer. The QH should already contain the
+ * scheduling information.
+ *
+ * @return 0 if successful, negative error code otherwise.
+ */
+static int schedule_periodic(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh)
+{
+	int status = 0;
+
+	if (microframe_schedule) {
+		int frame;
+		status = find_uframe(hcd, qh);
+		frame = -1;
+		if (status == 0) {
+			frame = 7;
+		} else {
+			if (status > 0 )
+				frame = status-1;
+		}
+
+		/* Set the new frame up */
+		if (frame > -1) {
+			qh->sched_frame &= ~0x7;
+			qh->sched_frame |= (frame & 7);
+		}
+
+		if (status != -1)
+			status = 0;
+	} else {
+		status = periodic_channel_available(hcd);
+		if (status) {
+			DWC_INFO("%s: No host channel available for periodic " "transfer.\n", __func__);	//NOTICE
+			return status;
+		}
+
+		status = check_periodic_bandwidth(hcd, qh);
+	}
+	if (status) {
+		DWC_INFO("%s: Insufficient periodic bandwidth for "
+			    "periodic transfer.\n", __func__);
+		return -DWC_E_NO_SPACE;
+	}
+	status = check_max_xfer_size(hcd, qh);
+	if (status) {
+		DWC_INFO("%s: Channel max transfer size too small "
+			    "for periodic transfer.\n", __func__);
+		return status;
+	}
+
+	if (hcd->core_if->dma_desc_enable) {
+		/* Don't rely on SOF and start in ready schedule */
+		DWC_LIST_INSERT_TAIL(&hcd->periodic_sched_ready, &qh->qh_list_entry);
+	}
+	else {
+		if(fiq_enable && (DWC_LIST_EMPTY(&hcd->periodic_sched_inactive) || dwc_frame_num_le(qh->sched_frame, hcd->fiq_state->next_sched_frame)))
+		{
+			hcd->fiq_state->next_sched_frame = qh->sched_frame;
+
+		}
+		/* Always start in the inactive schedule. */
+		DWC_LIST_INSERT_TAIL(&hcd->periodic_sched_inactive, &qh->qh_list_entry);
+	}
+
+	if (!microframe_schedule) {
+		/* Reserve the periodic channel. */
+		hcd->periodic_channels++;
+	}
+
+	/* Update claimed usecs per (micro)frame. */
+	hcd->periodic_usecs += qh->usecs;
+
+	return status;
+}
+
+
+/**
+ * This function adds a QH to either the non periodic or periodic schedule if
+ * it is not already in the schedule. If the QH is already in the schedule, no
+ * action is taken.
+ *
+ * @return 0 if successful, negative error code otherwise.
+ */
+int dwc_otg_hcd_qh_add(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh)
+{
+	int status = 0;
+	gintmsk_data_t intr_mask = {.d32 = 0 };
+
+	if (!DWC_LIST_EMPTY(&qh->qh_list_entry)) {
+		/* QH already in a schedule. */
+		return status;
+	}
+
+	/* Add the new QH to the appropriate schedule */
+	if (dwc_qh_is_non_per(qh)) {
+		/* Always start in the inactive schedule. */
+		DWC_LIST_INSERT_TAIL(&hcd->non_periodic_sched_inactive,
+				     &qh->qh_list_entry);
+		//hcd->fiq_state->kick_np_queues = 1;
+	} else {
+		/* If the QH wasn't in a schedule, then sched_frame is stale. */
+		qh->sched_frame = dwc_frame_num_inc(dwc_otg_hcd_get_frame_number(hcd),
+							SCHEDULE_SLOP);
+		status = schedule_periodic(hcd, qh);
+		qh->start_split_frame = qh->sched_frame;
+		if ( !hcd->periodic_qh_count ) {
+			intr_mask.b.sofintr = 1;
+			if (fiq_enable) {
+				local_fiq_disable();
+				fiq_fsm_spin_lock(&hcd->fiq_state->lock);
+				DWC_MODIFY_REG32(&hcd->core_if->core_global_regs->gintmsk, intr_mask.d32, intr_mask.d32);
+				fiq_fsm_spin_unlock(&hcd->fiq_state->lock);
+				local_fiq_enable();
+			} else {
+				DWC_MODIFY_REG32(&hcd->core_if->core_global_regs->gintmsk, intr_mask.d32, intr_mask.d32);
+			}
+		}
+		hcd->periodic_qh_count++;
+	}
+
+	return status;
+}
+
+/**
+ * Removes an interrupt or isochronous transfer from the periodic schedule.
+ *
+ * @param hcd The HCD state structure for the DWC OTG controller.
+ * @param qh QH for the periodic transfer.
+ */
+static void deschedule_periodic(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh)
+{
+	int i;
+	DWC_LIST_REMOVE_INIT(&qh->qh_list_entry);
+
+	/* Update claimed usecs per (micro)frame. */
+	hcd->periodic_usecs -= qh->usecs;
+
+	if (!microframe_schedule) {
+		/* Release the periodic channel reservation. */
+		hcd->periodic_channels--;
+	} else {
+		for (i = 0; i < 8; i++) {
+			hcd->frame_usecs[i] += qh->frame_usecs[i];
+			qh->frame_usecs[i] = 0;
+		}
+	}
+}
+
+/**
+ * Removes a QH from either the non-periodic or periodic schedule.  Memory is
+ * not freed.
+ *
+ * @param hcd The HCD state structure.
+ * @param qh QH to remove from schedule. */
+void dwc_otg_hcd_qh_remove(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh)
+{
+	gintmsk_data_t intr_mask = {.d32 = 0 };
+
+	if (DWC_LIST_EMPTY(&qh->qh_list_entry)) {
+		/* QH is not in a schedule. */
+		return;
+	}
+
+	if (dwc_qh_is_non_per(qh)) {
+		if (hcd->non_periodic_qh_ptr == &qh->qh_list_entry) {
+			hcd->non_periodic_qh_ptr =
+			    hcd->non_periodic_qh_ptr->next;
+		}
+		DWC_LIST_REMOVE_INIT(&qh->qh_list_entry);
+		//if (!DWC_LIST_EMPTY(&hcd->non_periodic_sched_inactive))
+		//	hcd->fiq_state->kick_np_queues = 1;
+	} else {
+		deschedule_periodic(hcd, qh);
+		hcd->periodic_qh_count--;
+		if( !hcd->periodic_qh_count && !fiq_fsm_enable ) {
+			intr_mask.b.sofintr = 1;
+			if (fiq_enable) {
+				local_fiq_disable();
+				fiq_fsm_spin_lock(&hcd->fiq_state->lock);
+				DWC_MODIFY_REG32(&hcd->core_if->core_global_regs->gintmsk, intr_mask.d32, 0);
+				fiq_fsm_spin_unlock(&hcd->fiq_state->lock);
+				local_fiq_enable();
+			} else {
+				DWC_MODIFY_REG32(&hcd->core_if->core_global_regs->gintmsk, intr_mask.d32, 0);
+			}
+		}
+	}
+}
+
+/**
+ * Deactivates a QH. For non-periodic QHs, removes the QH from the active
+ * non-periodic schedule. The QH is added to the inactive non-periodic
+ * schedule if any QTDs are still attached to the QH.
+ *
+ * For periodic QHs, the QH is removed from the periodic queued schedule. If
+ * there are any QTDs still attached to the QH, the QH is added to either the
+ * periodic inactive schedule or the periodic ready schedule and its next
+ * scheduled frame is calculated. The QH is placed in the ready schedule if
+ * the scheduled frame has been reached already. Otherwise it's placed in the
+ * inactive schedule. If there are no QTDs attached to the QH, the QH is
+ * completely removed from the periodic schedule.
+ */
+void dwc_otg_hcd_qh_deactivate(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh,
+			       int sched_next_periodic_split)
+{
+	if (dwc_qh_is_non_per(qh)) {
+		dwc_otg_hcd_qh_remove(hcd, qh);
+		if (!DWC_CIRCLEQ_EMPTY(&qh->qtd_list)) {
+			/* Add back to inactive non-periodic schedule. */
+			dwc_otg_hcd_qh_add(hcd, qh);
+			//hcd->fiq_state->kick_np_queues = 1;
+		} else {
+			if(nak_holdoff && qh->do_split) {
+				qh->nak_frame = 0xFFFF;
+			}
+		}
+	} else {
+		uint16_t frame_number = dwc_otg_hcd_get_frame_number(hcd);
+
+		if (qh->do_split) {
+			/* Schedule the next continuing periodic split transfer */
+			if (sched_next_periodic_split) {
+
+				qh->sched_frame = frame_number;
+
+				if (dwc_frame_num_le(frame_number,
+						     dwc_frame_num_inc
+						     (qh->start_split_frame,
+						      1))) {
+					/*
+					 * Allow one frame to elapse after start
+					 * split microframe before scheduling
+					 * complete split, but DONT if we are
+					 * doing the next start split in the
+					 * same frame for an ISOC out.
+					 */
+					if ((qh->ep_type != UE_ISOCHRONOUS) ||
+					    (qh->ep_is_in != 0)) {
+						qh->sched_frame =
+						    dwc_frame_num_inc(qh->sched_frame, 1);
+					}
+				}
+			} else {
+				qh->sched_frame =
+				    dwc_frame_num_inc(qh->start_split_frame,
+						      qh->interval);
+				if (dwc_frame_num_le
+				    (qh->sched_frame, frame_number)) {
+					qh->sched_frame = frame_number;
+				}
+				qh->sched_frame |= 0x7;
+				qh->start_split_frame = qh->sched_frame;
+			}
+		} else {
+			qh->sched_frame =
+			    dwc_frame_num_inc(qh->sched_frame, qh->interval);
+			if (dwc_frame_num_le(qh->sched_frame, frame_number)) {
+				qh->sched_frame = frame_number;
+			}
+		}
+
+		if (DWC_CIRCLEQ_EMPTY(&qh->qtd_list)) {
+			dwc_otg_hcd_qh_remove(hcd, qh);
+		} else {
+			/*
+			 * Remove from periodic_sched_queued and move to
+			 * appropriate queue.
+			 */
+			if ((microframe_schedule && dwc_frame_num_le(qh->sched_frame, frame_number)) ||
+			(!microframe_schedule && qh->sched_frame == frame_number)) {
+				DWC_LIST_MOVE_HEAD(&hcd->periodic_sched_ready,
+						   &qh->qh_list_entry);
+			} else {
+				if(fiq_enable && !dwc_frame_num_le(hcd->fiq_state->next_sched_frame, qh->sched_frame))
+				{
+					hcd->fiq_state->next_sched_frame = qh->sched_frame;
+				}
+
+				DWC_LIST_MOVE_HEAD
+				    (&hcd->periodic_sched_inactive,
+				     &qh->qh_list_entry);
+			}
+		}
+	}
+}
+
+/**
+ * This function allocates and initializes a QTD.
+ *
+ * @param urb The URB to create a QTD from.  Each URB-QTD pair will end up
+ * 	      pointing to each other so each pair should have a unique correlation.
+ * @param atomic_alloc Flag to do atomic alloc if needed
+ *
+ * @return Returns pointer to the newly allocated QTD, or NULL on error. */
+dwc_otg_qtd_t *dwc_otg_hcd_qtd_create(dwc_otg_hcd_urb_t * urb, int atomic_alloc)
+{
+	dwc_otg_qtd_t *qtd;
+
+	qtd = dwc_otg_hcd_qtd_alloc(atomic_alloc);
+	if (qtd == NULL) {
+		return NULL;
+	}
+
+	dwc_otg_hcd_qtd_init(qtd, urb);
+	return qtd;
+}
+
+/**
+ * Initializes a QTD structure.
+ *
+ * @param qtd The QTD to initialize.
+ * @param urb The URB to use for initialization.  */
+void dwc_otg_hcd_qtd_init(dwc_otg_qtd_t * qtd, dwc_otg_hcd_urb_t * urb)
+{
+	dwc_memset(qtd, 0, sizeof(dwc_otg_qtd_t));
+	qtd->urb = urb;
+	if (dwc_otg_hcd_get_pipe_type(&urb->pipe_info) == UE_CONTROL) {
+		/*
+		 * The only time the QTD data toggle is used is on the data
+		 * phase of control transfers. This phase always starts with
+		 * DATA1.
+		 */
+		qtd->data_toggle = DWC_OTG_HC_PID_DATA1;
+		qtd->control_phase = DWC_OTG_CONTROL_SETUP;
+	}
+
+	/* start split */
+	qtd->complete_split = 0;
+	qtd->isoc_split_pos = DWC_HCSPLIT_XACTPOS_ALL;
+	qtd->isoc_split_offset = 0;
+	qtd->in_process = 0;
+
+	/* Store the qtd ptr in the urb to reference what QTD. */
+	urb->qtd = qtd;
+	return;
+}
+
+/**
+ * This function adds a QTD to the QTD-list of a QH.  It will find the correct
+ * QH to place the QTD into.  If it does not find a QH, then it will create a
+ * new QH. If the QH to which the QTD is added is not currently scheduled, it
+ * is placed into the proper schedule based on its EP type.
+ * HCD lock must be held and interrupts must be disabled on entry
+ *
+ * @param[in] qtd The QTD to add
+ * @param[in] hcd The DWC HCD structure
+ * @param[out] qh out parameter to return queue head
+ * @param atomic_alloc Flag to do atomic alloc if needed
+ *
+ * @return 0 if successful, negative error code otherwise.
+ */
+int dwc_otg_hcd_qtd_add(dwc_otg_qtd_t * qtd,
+			dwc_otg_hcd_t * hcd, dwc_otg_qh_t ** qh, int atomic_alloc)
+{
+	int retval = 0;
+	dwc_otg_hcd_urb_t *urb = qtd->urb;
+
+	/*
+	 * Get the QH which holds the QTD-list to insert to. Create QH if it
+	 * doesn't exist.
+	 */
+	if (*qh == NULL) {
+		*qh = dwc_otg_hcd_qh_create(hcd, urb, atomic_alloc);
+		if (*qh == NULL) {
+			retval = -DWC_E_NO_MEMORY;
+			goto done;
+		} else {
+			if (fiq_enable)
+				hcd->fiq_state->kick_np_queues = 1;
+		}
+	}
+	retval = dwc_otg_hcd_qh_add(hcd, *qh);
+	if (retval == 0) {
+		DWC_CIRCLEQ_INSERT_TAIL(&((*qh)->qtd_list), qtd,
+					qtd_list_entry);
+		qtd->qh = *qh;
+	}
+done:
+
+	return retval;
+}
+
+#endif /* DWC_DEVICE_ONLY */
diff -Nur --no-dereference linux-5.10.52-orig/drivers/usb/host/dwc_otg/dwc_otg_os_dep.h linux-5.10.52-v7l+/drivers/usb/host/dwc_otg/dwc_otg_os_dep.h
--- linux-5.10.52-orig/drivers/usb/host/dwc_otg/dwc_otg_os_dep.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/usb/host/dwc_otg/dwc_otg_os_dep.h	2021-07-25 16:46:28.697909777 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+#ifndef _DWC_OS_DEP_H_
+#define _DWC_OS_DEP_H_
+
+/**
+ * @file
+ *
+ * This file contains OS dependent structures.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/ctype.h>
+#include <linux/string.h>
+#include <linux/dma-mapping.h>
+#include <linux/jiffies.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/workqueue.h>
+#include <linux/stat.h>
+#include <linux/pci.h>
+#include <linux/compiler.h>
+
+#include <linux/version.h>
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20)
+# include <linux/irq.h>
+#endif
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,21)
+# include <linux/usb/ch9.h>
+#else
+# include <linux/usb_ch9.h>
+#endif
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
+# include <linux/usb/gadget.h>
+#else
+# include <linux/usb_gadget.h>
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
+# include <asm/irq.h>
+#endif
+
+#ifdef PCI_INTERFACE
+# include <asm/io.h>
+#endif
+
+#ifdef LM_INTERFACE
+# include <asm/unaligned.h>
+# include <asm/sizes.h>
+# include <asm/param.h>
+# include <asm/io.h>
+# if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,30))
+#  include <asm/arch/hardware.h>
+#  include <asm/arch/lm.h>
+#  include <asm/arch/irqs.h>
+#  include <asm/arch/regs-irq.h>
+# else
+/* in 2.6.31, at least, we seem to have lost the generic LM infrastructure -
+   here we assume that the machine architecture provides definitions
+   in its own header
+*/
+#  include <mach/lm.h>
+#  include <mach/hardware.h>
+# endif
+#endif
+
+#ifdef PLATFORM_INTERFACE
+#include <linux/platform_device.h>
+#ifdef CONFIG_ARM
+#include <asm/mach/map.h>
+#endif
+#endif
+
+/** The OS page size */
+#define DWC_OS_PAGE_SIZE	PAGE_SIZE
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,14)
+typedef int gfp_t;
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)
+# define IRQF_SHARED SA_SHIRQ
+#endif
+
+typedef struct os_dependent {
+	/** Base address returned from ioremap() */
+	void *base;
+
+	/** Register offset for Diagnostic API */
+	uint32_t reg_offset;
+
+	/** Base address for MPHI peripheral */
+	void *mphi_base;
+
+	/** mphi_base actually points to the SWIRQ block */
+	bool use_swirq;
+
+	/** IRQ number (<0 if not valid) */
+	int irq_num;
+
+	/** FIQ number (<0 if not valid) */
+	int fiq_num;
+
+#ifdef LM_INTERFACE
+	struct lm_device *lmdev;
+#elif  defined(PCI_INTERFACE)
+	struct pci_dev *pcidev;
+
+	/** Start address of a PCI region */
+	resource_size_t rsrc_start;
+
+	/** Length address of a PCI region */
+	resource_size_t rsrc_len;
+#elif  defined(PLATFORM_INTERFACE)
+	struct platform_device *platformdev;
+#endif
+
+} os_dependent_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+
+
+/* Type for the our device on the chosen bus */
+#if   defined(LM_INTERFACE)
+typedef struct lm_device       dwc_bus_dev_t;
+#elif defined(PCI_INTERFACE)
+typedef struct pci_dev         dwc_bus_dev_t;
+#elif defined(PLATFORM_INTERFACE)
+typedef struct platform_device dwc_bus_dev_t;
+#endif
+
+/* Helper macro to retrieve drvdata from the device on the chosen bus */
+#if    defined(LM_INTERFACE)
+#define DWC_OTG_BUSDRVDATA(_dev) lm_get_drvdata(_dev)
+#elif  defined(PCI_INTERFACE)
+#define DWC_OTG_BUSDRVDATA(_dev) pci_get_drvdata(_dev)
+#elif  defined(PLATFORM_INTERFACE)
+#define DWC_OTG_BUSDRVDATA(_dev) platform_get_drvdata(_dev)
+#endif
+
+/**
+ * Helper macro returning the otg_device structure of a given struct device
+ *
+ * c.f. static dwc_otg_device_t *dwc_otg_drvdev(struct device *_dev)
+ */
+#ifdef LM_INTERFACE
+#define DWC_OTG_GETDRVDEV(_var, _dev) do { \
+                struct lm_device *lm_dev = \
+                        container_of(_dev, struct lm_device, dev); \
+                _var = lm_get_drvdata(lm_dev); \
+        } while (0)
+
+#elif defined(PCI_INTERFACE)
+#define DWC_OTG_GETDRVDEV(_var, _dev) do { \
+                _var = dev_get_drvdata(_dev); \
+        } while (0)
+
+#elif defined(PLATFORM_INTERFACE)
+#define DWC_OTG_GETDRVDEV(_var, _dev) do { \
+                struct platform_device *platform_dev = \
+                        container_of(_dev, struct platform_device, dev); \
+                _var = platform_get_drvdata(platform_dev); \
+        } while (0)
+#endif
+
+
+/**
+ * Helper macro returning the struct dev of the given struct os_dependent
+ *
+ * c.f. static struct device *dwc_otg_getdev(struct os_dependent *osdep)
+ */
+#ifdef LM_INTERFACE
+#define DWC_OTG_OS_GETDEV(_osdep) \
+        ((_osdep).lmdev == NULL? NULL: &(_osdep).lmdev->dev)
+#elif defined(PCI_INTERFACE)
+#define DWC_OTG_OS_GETDEV(_osdep) \
+        ((_osdep).pci_dev == NULL? NULL: &(_osdep).pci_dev->dev)
+#elif defined(PLATFORM_INTERFACE)
+#define DWC_OTG_OS_GETDEV(_osdep) \
+        ((_osdep).platformdev == NULL? NULL: &(_osdep).platformdev->dev)
+#endif
+
+
+
+
+#endif /* _DWC_OS_DEP_H_ */
diff -Nur --no-dereference linux-5.10.52-orig/drivers/usb/host/dwc_otg/dwc_otg_pcd.c linux-5.10.52-v7l+/drivers/usb/host/dwc_otg/dwc_otg_pcd.c
--- linux-5.10.52-orig/drivers/usb/host/dwc_otg/dwc_otg_pcd.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/usb/host/dwc_otg/dwc_otg_pcd.c	2021-07-25 16:46:28.697909777 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/* ==========================================================================
+ * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_pcd.c $
+ * $Revision: #101 $
+ * $Date: 2012/08/10 $
+ * $Change: 2047372 $
+ *
+ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter,
+ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless
+ * otherwise expressly agreed to in writing between Synopsys and you.
+ *
+ * The Software IS NOT an item of Licensed Software or Licensed Product under
+ * any End User Software License Agreement or Agreement for Licensed Product
+ * with Synopsys or any supplement thereto. You are permitted to use and
+ * redistribute this Software in source and binary forms, with or without
+ * modification, provided that redistributions of source code must retain this
+ * notice. You may not view, use, disclose, copy or distribute this file or
+ * any information contained herein except pursuant to this license grant from
+ * Synopsys. If you do not agree with this notice, including the disclaimer
+ * below, then you are not authorized to use the Software.
+ *
+ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * ========================================================================== */
+#ifndef DWC_HOST_ONLY
+
+/** @file
+ * This file implements PCD Core. All code in this file is portable and doesn't
+ * use any OS specific functions.
+ * PCD Core provides Interface, defined in <code><dwc_otg_pcd_if.h></code>
+ * header file, which can be used to implement OS specific PCD interface.
+ *
+ * An important function of the PCD is managing interrupts generated
+ * by the DWC_otg controller. The implementation of the DWC_otg device
+ * mode interrupt service routines is in dwc_otg_pcd_intr.c.
+ *
+ * @todo Add Device Mode test modes (Test J mode, Test K mode, etc).
+ * @todo Does it work when the request size is greater than DEPTSIZ
+ * transfer size
+ *
+ */
+
+#include "dwc_otg_pcd.h"
+
+#ifdef DWC_UTE_CFI
+#include "dwc_otg_cfi.h"
+
+extern int init_cfi(cfiobject_t * cfiobj);
+#endif
+
+/**
+ * Choose endpoint from ep arrays using usb_ep structure.
+ */
+static dwc_otg_pcd_ep_t *get_ep_from_handle(dwc_otg_pcd_t * pcd, void *handle)
+{
+	int i;
+	if (pcd->ep0.priv == handle) {
+		return &pcd->ep0;
+	}
+	for (i = 0; i < MAX_EPS_CHANNELS - 1; i++) {
+		if (pcd->in_ep[i].priv == handle)
+			return &pcd->in_ep[i];
+		if (pcd->out_ep[i].priv == handle)
+			return &pcd->out_ep[i];
+	}
+
+	return NULL;
+}
+
+/**
+ * This function completes a request.  It call's the request call back.
+ */
+void dwc_otg_request_done(dwc_otg_pcd_ep_t * ep, dwc_otg_pcd_request_t * req,
+			  int32_t status)
+{
+	unsigned stopped = ep->stopped;
+
+	DWC_DEBUGPL(DBG_PCDV, "%s(ep %p req %p)\n", __func__, ep, req);
+	DWC_CIRCLEQ_REMOVE_INIT(&ep->queue, req, queue_entry);
+
+	/* don't modify queue heads during completion callback */
+	ep->stopped = 1;
+	/* spin_unlock/spin_lock now done in fops->complete() */
+	ep->pcd->fops->complete(ep->pcd, ep->priv, req->priv, status,
+				req->actual);
+
+	if (ep->pcd->request_pending > 0) {
+		--ep->pcd->request_pending;
+	}
+
+	ep->stopped = stopped;
+	DWC_FREE(req);
+}
+
+/**
+ * This function terminates all the requsts in the EP request queue.
+ */
+void dwc_otg_request_nuke(dwc_otg_pcd_ep_t * ep)
+{
+	dwc_otg_pcd_request_t *req;
+
+	ep->stopped = 1;
+
+	/* called with irqs blocked?? */
+	while (!DWC_CIRCLEQ_EMPTY(&ep->queue)) {
+		req = DWC_CIRCLEQ_FIRST(&ep->queue);
+		dwc_otg_request_done(ep, req, -DWC_E_SHUTDOWN);
+	}
+}
+
+void dwc_otg_pcd_start(dwc_otg_pcd_t * pcd,
+		       const struct dwc_otg_pcd_function_ops *fops)
+{
+	pcd->fops = fops;
+}
+
+/**
+ * PCD Callback function for initializing the PCD when switching to
+ * device mode.
+ *
+ * @param p void pointer to the <code>dwc_otg_pcd_t</code>
+ */
+static int32_t dwc_otg_pcd_start_cb(void *p)
+{
+	dwc_otg_pcd_t *pcd = (dwc_otg_pcd_t *) p;
+	dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd);
+
+	/*
+	 * Initialized the Core for Device mode.
+	 */
+	if (dwc_otg_is_device_mode(core_if)) {
+		dwc_otg_core_dev_init(core_if);
+		/* Set core_if's lock pointer to the pcd->lock */
+		core_if->lock = pcd->lock;
+	}
+	return 1;
+}
+
+/** CFI-specific buffer allocation function for EP */
+#ifdef DWC_UTE_CFI
+uint8_t *cfiw_ep_alloc_buffer(dwc_otg_pcd_t * pcd, void *pep, dwc_dma_t * addr,
+			      size_t buflen, int flags)
+{
+	dwc_otg_pcd_ep_t *ep;
+	ep = get_ep_from_handle(pcd, pep);
+	if (!ep) {
+		DWC_WARN("bad ep\n");
+		return -DWC_E_INVALID;
+	}
+
+	return pcd->cfi->ops.ep_alloc_buf(pcd->cfi, pcd, ep, addr, buflen,
+					  flags);
+}
+#else
+uint8_t *cfiw_ep_alloc_buffer(dwc_otg_pcd_t * pcd, void *pep, dwc_dma_t * addr,
+			      size_t buflen, int flags);
+#endif
+
+/**
+ * PCD Callback function for notifying the PCD when resuming from
+ * suspend.
+ *
+ * @param p void pointer to the <code>dwc_otg_pcd_t</code>
+ */
+static int32_t dwc_otg_pcd_resume_cb(void *p)
+{
+	dwc_otg_pcd_t *pcd = (dwc_otg_pcd_t *) p;
+
+	if (pcd->fops->resume) {
+		pcd->fops->resume(pcd);
+	}
+
+	/* Stop the SRP timeout timer. */
+	if ((GET_CORE_IF(pcd)->core_params->phy_type != DWC_PHY_TYPE_PARAM_FS)
+	    || (!GET_CORE_IF(pcd)->core_params->i2c_enable)) {
+		if (GET_CORE_IF(pcd)->srp_timer_started) {
+			GET_CORE_IF(pcd)->srp_timer_started = 0;
+			DWC_TIMER_CANCEL(GET_CORE_IF(pcd)->srp_timer);
+		}
+	}
+	return 1;
+}
+
+/**
+ * PCD Callback function for notifying the PCD device is suspended.
+ *
+ * @param p void pointer to the <code>dwc_otg_pcd_t</code>
+ */
+static int32_t dwc_otg_pcd_suspend_cb(void *p)
+{
+	dwc_otg_pcd_t *pcd = (dwc_otg_pcd_t *) p;
+
+	if (pcd->fops->suspend) {
+		DWC_SPINUNLOCK(pcd->lock);
+		pcd->fops->suspend(pcd);
+		DWC_SPINLOCK(pcd->lock);
+	}
+
+	return 1;
+}
+
+/**
+ * PCD Callback function for stopping the PCD when switching to Host
+ * mode.
+ *
+ * @param p void pointer to the <code>dwc_otg_pcd_t</code>
+ */
+static int32_t dwc_otg_pcd_stop_cb(void *p)
+{
+	dwc_otg_pcd_t *pcd = (dwc_otg_pcd_t *) p;
+	extern void dwc_otg_pcd_stop(dwc_otg_pcd_t * _pcd);
+
+	dwc_otg_pcd_stop(pcd);
+	return 1;
+}
+
+/**
+ * PCD Callback structure for handling mode switching.
+ */
+static dwc_otg_cil_callbacks_t pcd_callbacks = {
+	.start = dwc_otg_pcd_start_cb,
+	.stop = dwc_otg_pcd_stop_cb,
+	.suspend = dwc_otg_pcd_suspend_cb,
+	.resume_wakeup = dwc_otg_pcd_resume_cb,
+	.p = 0,			/* Set at registration */
+};
+
+/**
+ * This function allocates a DMA Descriptor chain for the Endpoint
+ * buffer to be used for a transfer to/from the specified endpoint.
+ */
+dwc_otg_dev_dma_desc_t *dwc_otg_ep_alloc_desc_chain(struct device *dev,
+						    dwc_dma_t * dma_desc_addr,
+						    uint32_t count)
+{
+	return DWC_DMA_ALLOC_ATOMIC(dev, count * sizeof(dwc_otg_dev_dma_desc_t),
+							dma_desc_addr);
+}
+
+/**
+ * This function frees a DMA Descriptor chain that was allocated by ep_alloc_desc.
+ */
+void dwc_otg_ep_free_desc_chain(struct device *dev,
+				dwc_otg_dev_dma_desc_t * desc_addr,
+				uint32_t dma_desc_addr, uint32_t count)
+{
+	DWC_DMA_FREE(dev, count * sizeof(dwc_otg_dev_dma_desc_t), desc_addr,
+		     dma_desc_addr);
+}
+
+#ifdef DWC_EN_ISOC
+
+/**
+ * This function initializes a descriptor chain for Isochronous transfer
+ *
+ * @param core_if Programming view of DWC_otg controller.
+ * @param dwc_ep The EP to start the transfer on.
+ *
+ */
+void dwc_otg_iso_ep_start_ddma_transfer(dwc_otg_core_if_t * core_if,
+					dwc_ep_t * dwc_ep)
+{
+
+	dsts_data_t dsts = {.d32 = 0 };
+	depctl_data_t depctl = {.d32 = 0 };
+	volatile uint32_t *addr;
+	int i, j;
+	uint32_t len;
+
+	if (dwc_ep->is_in)
+		dwc_ep->desc_cnt = dwc_ep->buf_proc_intrvl / dwc_ep->bInterval;
+	else
+		dwc_ep->desc_cnt =
+		    dwc_ep->buf_proc_intrvl * dwc_ep->pkt_per_frm /
+		    dwc_ep->bInterval;
+
+	/** Allocate descriptors for double buffering */
+	dwc_ep->iso_desc_addr =
+	    dwc_otg_ep_alloc_desc_chain(&dwc_ep->iso_dma_desc_addr,
+					dwc_ep->desc_cnt * 2);
+	if (dwc_ep->desc_addr) {
+		DWC_WARN("%s, can't allocate DMA descriptor chain\n", __func__);
+		return;
+	}
+
+	dsts.d32 = DWC_READ_REG32(&core_if->dev_if->dev_global_regs->dsts);
+
+	/** ISO OUT EP */
+	if (dwc_ep->is_in == 0) {
+		dev_dma_desc_sts_t sts = {.d32 = 0 };
+		dwc_otg_dev_dma_desc_t *dma_desc = dwc_ep->iso_desc_addr;
+		dma_addr_t dma_ad;
+		uint32_t data_per_desc;
+		dwc_otg_dev_out_ep_regs_t *out_regs =
+		    core_if->dev_if->out_ep_regs[dwc_ep->num];
+		int offset;
+
+		addr = &core_if->dev_if->out_ep_regs[dwc_ep->num]->doepctl;
+		dma_ad = (dma_addr_t) DWC_READ_REG32(&(out_regs->doepdma));
+
+		/** Buffer 0 descriptors setup */
+		dma_ad = dwc_ep->dma_addr0;
+
+		sts.b_iso_out.bs = BS_HOST_READY;
+		sts.b_iso_out.rxsts = 0;
+		sts.b_iso_out.l = 0;
+		sts.b_iso_out.sp = 0;
+		sts.b_iso_out.ioc = 0;
+		sts.b_iso_out.pid = 0;
+		sts.b_iso_out.framenum = 0;
+
+		offset = 0;
+		for (i = 0; i < dwc_ep->desc_cnt - dwc_ep->pkt_per_frm;
+		     i += dwc_ep->pkt_per_frm) {
+
+			for (j = 0; j < dwc_ep->pkt_per_frm; ++j) {
+				uint32_t len = (j + 1) * dwc_ep->maxpacket;
+				if (len > dwc_ep->data_per_frame)
+					data_per_desc =
+					    dwc_ep->data_per_frame -
+					    j * dwc_ep->maxpacket;
+				else
+					data_per_desc = dwc_ep->maxpacket;
+				len = data_per_desc % 4;
+				if (len)
+					data_per_desc += 4 - len;
+
+				sts.b_iso_out.rxbytes = data_per_desc;
+				dma_desc->buf = dma_ad;
+				dma_desc->status.d32 = sts.d32;
+
+				offset += data_per_desc;
+				dma_desc++;
+				dma_ad += data_per_desc;
+			}
+		}
+
+		for (j = 0; j < dwc_ep->pkt_per_frm - 1; ++j) {
+			uint32_t len = (j + 1) * dwc_ep->maxpacket;
+			if (len > dwc_ep->data_per_frame)
+				data_per_desc =
+				    dwc_ep->data_per_frame -
+				    j * dwc_ep->maxpacket;
+			else
+				data_per_desc = dwc_ep->maxpacket;
+			len = data_per_desc % 4;
+			if (len)
+				data_per_desc += 4 - len;
+			sts.b_iso_out.rxbytes = data_per_desc;
+			dma_desc->buf = dma_ad;
+			dma_desc->status.d32 = sts.d32;
+
+			offset += data_per_desc;
+			dma_desc++;
+			dma_ad += data_per_desc;
+		}
+
+		sts.b_iso_out.ioc = 1;
+		len = (j + 1) * dwc_ep->maxpacket;
+		if (len > dwc_ep->data_per_frame)
+			data_per_desc =
+			    dwc_ep->data_per_frame - j * dwc_ep->maxpacket;
+		else
+			data_per_desc = dwc_ep->maxpacket;
+		len = data_per_desc % 4;
+		if (len)
+			data_per_desc += 4 - len;
+		sts.b_iso_out.rxbytes = data_per_desc;
+
+		dma_desc->buf = dma_ad;
+		dma_desc->status.d32 = sts.d32;
+		dma_desc++;
+
+		/** Buffer 1 descriptors setup */
+		sts.b_iso_out.ioc = 0;
+		dma_ad = dwc_ep->dma_addr1;
+
+		offset = 0;
+		for (i = 0; i < dwc_ep->desc_cnt - dwc_ep->pkt_per_frm;
+		     i += dwc_ep->pkt_per_frm) {
+			for (j = 0; j < dwc_ep->pkt_per_frm; ++j) {
+				uint32_t len = (j + 1) * dwc_ep->maxpacket;
+				if (len > dwc_ep->data_per_frame)
+					data_per_desc =
+					    dwc_ep->data_per_frame -
+					    j * dwc_ep->maxpacket;
+				else
+					data_per_desc = dwc_ep->maxpacket;
+				len = data_per_desc % 4;
+				if (len)
+					data_per_desc += 4 - len;
+
+				data_per_desc =
+				    sts.b_iso_out.rxbytes = data_per_desc;
+				dma_desc->buf = dma_ad;
+				dma_desc->status.d32 = sts.d32;
+
+				offset += data_per_desc;
+				dma_desc++;
+				dma_ad += data_per_desc;
+			}
+		}
+		for (j = 0; j < dwc_ep->pkt_per_frm - 1; ++j) {
+			data_per_desc =
+			    ((j + 1) * dwc_ep->maxpacket >
+			     dwc_ep->data_per_frame) ? dwc_ep->data_per_frame -
+			    j * dwc_ep->maxpacket : dwc_ep->maxpacket;
+			data_per_desc +=
+			    (data_per_desc % 4) ? (4 - data_per_desc % 4) : 0;
+			sts.b_iso_out.rxbytes = data_per_desc;
+			dma_desc->buf = dma_ad;
+			dma_desc->status.d32 = sts.d32;
+
+			offset += data_per_desc;
+			dma_desc++;
+			dma_ad += data_per_desc;
+		}
+
+		sts.b_iso_out.ioc = 1;
+		sts.b_iso_out.l = 1;
+		data_per_desc =
+		    ((j + 1) * dwc_ep->maxpacket >
+		     dwc_ep->data_per_frame) ? dwc_ep->data_per_frame -
+		    j * dwc_ep->maxpacket : dwc_ep->maxpacket;
+		data_per_desc +=
+		    (data_per_desc % 4) ? (4 - data_per_desc % 4) : 0;
+		sts.b_iso_out.rxbytes = data_per_desc;
+
+		dma_desc->buf = dma_ad;
+		dma_desc->status.d32 = sts.d32;
+
+		dwc_ep->next_frame = 0;
+
+		/** Write dma_ad into DOEPDMA register */
+		DWC_WRITE_REG32(&(out_regs->doepdma),
+				(uint32_t) dwc_ep->iso_dma_desc_addr);
+
+	}
+	/** ISO IN EP */
+	else {
+		dev_dma_desc_sts_t sts = {.d32 = 0 };
+		dwc_otg_dev_dma_desc_t *dma_desc = dwc_ep->iso_desc_addr;
+		dma_addr_t dma_ad;
+		dwc_otg_dev_in_ep_regs_t *in_regs =
+		    core_if->dev_if->in_ep_regs[dwc_ep->num];
+		unsigned int frmnumber;
+		fifosize_data_t txfifosize, rxfifosize;
+
+		txfifosize.d32 =
+		    DWC_READ_REG32(&core_if->dev_if->in_ep_regs[dwc_ep->num]->
+				   dtxfsts);
+		rxfifosize.d32 =
+		    DWC_READ_REG32(&core_if->core_global_regs->grxfsiz);
+
+		addr = &core_if->dev_if->in_ep_regs[dwc_ep->num]->diepctl;
+
+		dma_ad = dwc_ep->dma_addr0;
+
+		dsts.d32 =
+		    DWC_READ_REG32(&core_if->dev_if->dev_global_regs->dsts);
+
+		sts.b_iso_in.bs = BS_HOST_READY;
+		sts.b_iso_in.txsts = 0;
+		sts.b_iso_in.sp =
+		    (dwc_ep->data_per_frame % dwc_ep->maxpacket) ? 1 : 0;
+		sts.b_iso_in.ioc = 0;
+		sts.b_iso_in.pid = dwc_ep->pkt_per_frm;
+
+		frmnumber = dwc_ep->next_frame;
+
+		sts.b_iso_in.framenum = frmnumber;
+		sts.b_iso_in.txbytes = dwc_ep->data_per_frame;
+		sts.b_iso_in.l = 0;
+
+		/** Buffer 0 descriptors setup */
+		for (i = 0; i < dwc_ep->desc_cnt - 1; i++) {
+			dma_desc->buf = dma_ad;
+			dma_desc->status.d32 = sts.d32;
+			dma_desc++;
+
+			dma_ad += dwc_ep->data_per_frame;
+			sts.b_iso_in.framenum += dwc_ep->bInterval;
+		}
+
+		sts.b_iso_in.ioc = 1;
+		dma_desc->buf = dma_ad;
+		dma_desc->status.d32 = sts.d32;
+		++dma_desc;
+
+		/** Buffer 1 descriptors setup */
+		sts.b_iso_in.ioc = 0;
+		dma_ad = dwc_ep->dma_addr1;
+
+		for (i = 0; i < dwc_ep->desc_cnt - dwc_ep->pkt_per_frm;
+		     i += dwc_ep->pkt_per_frm) {
+			dma_desc->buf = dma_ad;
+			dma_desc->status.d32 = sts.d32;
+			dma_desc++;
+
+			dma_ad += dwc_ep->data_per_frame;
+			sts.b_iso_in.framenum += dwc_ep->bInterval;
+
+			sts.b_iso_in.ioc = 0;
+		}
+		sts.b_iso_in.ioc = 1;
+		sts.b_iso_in.l = 1;
+
+		dma_desc->buf = dma_ad;
+		dma_desc->status.d32 = sts.d32;
+
+		dwc_ep->next_frame = sts.b_iso_in.framenum + dwc_ep->bInterval;
+
+		/** Write dma_ad into diepdma register */
+		DWC_WRITE_REG32(&(in_regs->diepdma),
+				(uint32_t) dwc_ep->iso_dma_desc_addr);
+	}
+	/** Enable endpoint, clear nak  */
+	depctl.d32 = 0;
+	depctl.b.epena = 1;
+	depctl.b.usbactep = 1;
+	depctl.b.cnak = 1;
+
+	DWC_MODIFY_REG32(addr, depctl.d32, depctl.d32);
+	depctl.d32 = DWC_READ_REG32(addr);
+}
+
+/**
+ * This function initializes a descriptor chain for Isochronous transfer
+ *
+ * @param core_if Programming view of DWC_otg controller.
+ * @param ep The EP to start the transfer on.
+ *
+ */
+void dwc_otg_iso_ep_start_buf_transfer(dwc_otg_core_if_t * core_if,
+				       dwc_ep_t * ep)
+{
+	depctl_data_t depctl = {.d32 = 0 };
+	volatile uint32_t *addr;
+
+	if (ep->is_in) {
+		addr = &core_if->dev_if->in_ep_regs[ep->num]->diepctl;
+	} else {
+		addr = &core_if->dev_if->out_ep_regs[ep->num]->doepctl;
+	}
+
+	if (core_if->dma_enable == 0 || core_if->dma_desc_enable != 0) {
+		return;
+	} else {
+		deptsiz_data_t deptsiz = {.d32 = 0 };
+
+		ep->xfer_len =
+		    ep->data_per_frame * ep->buf_proc_intrvl / ep->bInterval;
+		ep->pkt_cnt =
+		    (ep->xfer_len - 1 + ep->maxpacket) / ep->maxpacket;
+		ep->xfer_count = 0;
+		ep->xfer_buff =
+		    (ep->proc_buf_num) ? ep->xfer_buff1 : ep->xfer_buff0;
+		ep->dma_addr =
+		    (ep->proc_buf_num) ? ep->dma_addr1 : ep->dma_addr0;
+
+		if (ep->is_in) {
+			/* Program the transfer size and packet count
+			 *      as follows: xfersize = N * maxpacket +
+			 *      short_packet pktcnt = N + (short_packet
+			 *      exist ? 1 : 0)
+			 */
+			deptsiz.b.mc = ep->pkt_per_frm;
+			deptsiz.b.xfersize = ep->xfer_len;
+			deptsiz.b.pktcnt =
+			    (ep->xfer_len - 1 + ep->maxpacket) / ep->maxpacket;
+			DWC_WRITE_REG32(&core_if->dev_if->in_ep_regs[ep->num]->
+					dieptsiz, deptsiz.d32);
+
+			/* Write the DMA register */
+			DWC_WRITE_REG32(&
+					(core_if->dev_if->in_ep_regs[ep->num]->
+					 diepdma), (uint32_t) ep->dma_addr);
+
+		} else {
+			deptsiz.b.pktcnt =
+			    (ep->xfer_len + (ep->maxpacket - 1)) /
+			    ep->maxpacket;
+			deptsiz.b.xfersize = deptsiz.b.pktcnt * ep->maxpacket;
+
+			DWC_WRITE_REG32(&core_if->dev_if->out_ep_regs[ep->num]->
+					doeptsiz, deptsiz.d32);
+
+			/* Write the DMA register */
+			DWC_WRITE_REG32(&
+					(core_if->dev_if->out_ep_regs[ep->num]->
+					 doepdma), (uint32_t) ep->dma_addr);
+
+		}
+		/** Enable endpoint, clear nak  */
+		depctl.d32 = 0;
+		depctl.b.epena = 1;
+		depctl.b.cnak = 1;
+
+		DWC_MODIFY_REG32(addr, depctl.d32, depctl.d32);
+	}
+}
+
+/**
+ * This function does the setup for a data transfer for an EP and
+ * starts the transfer. For an IN transfer, the packets will be
+ * loaded into the appropriate Tx FIFO in the ISR. For OUT transfers,
+ * the packets are unloaded from the Rx FIFO in the ISR.
+ *
+ * @param core_if Programming view of DWC_otg controller.
+ * @param ep The EP to start the transfer on.
+ */
+
+static void dwc_otg_iso_ep_start_transfer(dwc_otg_core_if_t * core_if,
+					  dwc_ep_t * ep)
+{
+	if (core_if->dma_enable) {
+		if (core_if->dma_desc_enable) {
+			if (ep->is_in) {
+				ep->desc_cnt = ep->pkt_cnt / ep->pkt_per_frm;
+			} else {
+				ep->desc_cnt = ep->pkt_cnt;
+			}
+			dwc_otg_iso_ep_start_ddma_transfer(core_if, ep);
+		} else {
+			if (core_if->pti_enh_enable) {
+				dwc_otg_iso_ep_start_buf_transfer(core_if, ep);
+			} else {
+				ep->cur_pkt_addr =
+				    (ep->proc_buf_num) ? ep->xfer_buff1 : ep->
+				    xfer_buff0;
+				ep->cur_pkt_dma_addr =
+				    (ep->proc_buf_num) ? ep->dma_addr1 : ep->
+				    dma_addr0;
+				dwc_otg_iso_ep_start_frm_transfer(core_if, ep);
+			}
+		}
+	} else {
+		ep->cur_pkt_addr =
+		    (ep->proc_buf_num) ? ep->xfer_buff1 : ep->xfer_buff0;
+		ep->cur_pkt_dma_addr =
+		    (ep->proc_buf_num) ? ep->dma_addr1 : ep->dma_addr0;
+		dwc_otg_iso_ep_start_frm_transfer(core_if, ep);
+	}
+}
+
+/**
+ * This function stops transfer for an EP and
+ * resets the ep's variables.
+ *
+ * @param core_if Programming view of DWC_otg controller.
+ * @param ep The EP to start the transfer on.
+ */
+
+void dwc_otg_iso_ep_stop_transfer(dwc_otg_core_if_t * core_if, dwc_ep_t * ep)
+{
+	depctl_data_t depctl = {.d32 = 0 };
+	volatile uint32_t *addr;
+
+	if (ep->is_in == 1) {
+		addr = &core_if->dev_if->in_ep_regs[ep->num]->diepctl;
+	} else {
+		addr = &core_if->dev_if->out_ep_regs[ep->num]->doepctl;
+	}
+
+	/* disable the ep */
+	depctl.d32 = DWC_READ_REG32(addr);
+
+	depctl.b.epdis = 1;
+	depctl.b.snak = 1;
+
+	DWC_WRITE_REG32(addr, depctl.d32);
+
+	if (core_if->dma_desc_enable &&
+	    ep->iso_desc_addr && ep->iso_dma_desc_addr) {
+		dwc_otg_ep_free_desc_chain(ep->iso_desc_addr,
+					   ep->iso_dma_desc_addr,
+					   ep->desc_cnt * 2);
+	}
+
+	/* reset varibales */
+	ep->dma_addr0 = 0;
+	ep->dma_addr1 = 0;
+	ep->xfer_buff0 = 0;
+	ep->xfer_buff1 = 0;
+	ep->data_per_frame = 0;
+	ep->data_pattern_frame = 0;
+	ep->sync_frame = 0;
+	ep->buf_proc_intrvl = 0;
+	ep->bInterval = 0;
+	ep->proc_buf_num = 0;
+	ep->pkt_per_frm = 0;
+	ep->pkt_per_frm = 0;
+	ep->desc_cnt = 0;
+	ep->iso_desc_addr = 0;
+	ep->iso_dma_desc_addr = 0;
+}
+
+int dwc_otg_pcd_iso_ep_start(dwc_otg_pcd_t * pcd, void *ep_handle,
+			     uint8_t * buf0, uint8_t * buf1, dwc_dma_t dma0,
+			     dwc_dma_t dma1, int sync_frame, int dp_frame,
+			     int data_per_frame, int start_frame,
+			     int buf_proc_intrvl, void *req_handle,
+			     int atomic_alloc)
+{
+	dwc_otg_pcd_ep_t *ep;
+	dwc_irqflags_t flags = 0;
+	dwc_ep_t *dwc_ep;
+	int32_t frm_data;
+	dsts_data_t dsts;
+	dwc_otg_core_if_t *core_if;
+
+	ep = get_ep_from_handle(pcd, ep_handle);
+
+	if (!ep || !ep->desc || ep->dwc_ep.num == 0) {
+		DWC_WARN("bad ep\n");
+		return -DWC_E_INVALID;
+	}
+
+	DWC_SPINLOCK_IRQSAVE(pcd->lock, &flags);
+	core_if = GET_CORE_IF(pcd);
+	dwc_ep = &ep->dwc_ep;
+
+	if (ep->iso_req_handle) {
+		DWC_WARN("ISO request in progress\n");
+	}
+
+	dwc_ep->dma_addr0 = dma0;
+	dwc_ep->dma_addr1 = dma1;
+
+	dwc_ep->xfer_buff0 = buf0;
+	dwc_ep->xfer_buff1 = buf1;
+
+	dwc_ep->data_per_frame = data_per_frame;
+
+	/** @todo - pattern data support is to be implemented in the future */
+	dwc_ep->data_pattern_frame = dp_frame;
+	dwc_ep->sync_frame = sync_frame;
+
+	dwc_ep->buf_proc_intrvl = buf_proc_intrvl;
+
+	dwc_ep->bInterval = 1 << (ep->desc->bInterval - 1);
+
+	dwc_ep->proc_buf_num = 0;
+
+	dwc_ep->pkt_per_frm = 0;
+	frm_data = ep->dwc_ep.data_per_frame;
+	while (frm_data > 0) {
+		dwc_ep->pkt_per_frm++;
+		frm_data -= ep->dwc_ep.maxpacket;
+	}
+
+	dsts.d32 = DWC_READ_REG32(&core_if->dev_if->dev_global_regs->dsts);
+
+	if (start_frame == -1) {
+		dwc_ep->next_frame = dsts.b.soffn + 1;
+		if (dwc_ep->bInterval != 1) {
+			dwc_ep->next_frame =
+			    dwc_ep->next_frame + (dwc_ep->bInterval - 1 -
+						  dwc_ep->next_frame %
+						  dwc_ep->bInterval);
+		}
+	} else {
+		dwc_ep->next_frame = start_frame;
+	}
+
+	if (!core_if->pti_enh_enable) {
+		dwc_ep->pkt_cnt =
+		    dwc_ep->buf_proc_intrvl * dwc_ep->pkt_per_frm /
+		    dwc_ep->bInterval;
+	} else {
+		dwc_ep->pkt_cnt =
+		    (dwc_ep->data_per_frame *
+		     (dwc_ep->buf_proc_intrvl / dwc_ep->bInterval)
+		     - 1 + dwc_ep->maxpacket) / dwc_ep->maxpacket;
+	}
+
+	if (core_if->dma_desc_enable) {
+		dwc_ep->desc_cnt =
+		    dwc_ep->buf_proc_intrvl * dwc_ep->pkt_per_frm /
+		    dwc_ep->bInterval;
+	}
+
+	if (atomic_alloc) {
+		dwc_ep->pkt_info =
+		    DWC_ALLOC_ATOMIC(sizeof(iso_pkt_info_t) * dwc_ep->pkt_cnt);
+	} else {
+		dwc_ep->pkt_info =
+		    DWC_ALLOC(sizeof(iso_pkt_info_t) * dwc_ep->pkt_cnt);
+	}
+	if (!dwc_ep->pkt_info) {
+		DWC_SPINUNLOCK_IRQRESTORE(pcd->lock, flags);
+		return -DWC_E_NO_MEMORY;
+	}
+	if (core_if->pti_enh_enable) {
+		dwc_memset(dwc_ep->pkt_info, 0,
+			   sizeof(iso_pkt_info_t) * dwc_ep->pkt_cnt);
+	}
+
+	dwc_ep->cur_pkt = 0;
+	ep->iso_req_handle = req_handle;
+
+	DWC_SPINUNLOCK_IRQRESTORE(pcd->lock, flags);
+	dwc_otg_iso_ep_start_transfer(core_if, dwc_ep);
+	return 0;
+}
+
+int dwc_otg_pcd_iso_ep_stop(dwc_otg_pcd_t * pcd, void *ep_handle,
+			    void *req_handle)
+{
+	dwc_irqflags_t flags = 0;
+	dwc_otg_pcd_ep_t *ep;
+	dwc_ep_t *dwc_ep;
+
+	ep = get_ep_from_handle(pcd, ep_handle);
+	if (!ep || !ep->desc || ep->dwc_ep.num == 0) {
+		DWC_WARN("bad ep\n");
+		return -DWC_E_INVALID;
+	}
+	dwc_ep = &ep->dwc_ep;
+
+	dwc_otg_iso_ep_stop_transfer(GET_CORE_IF(pcd), dwc_ep);
+
+	DWC_FREE(dwc_ep->pkt_info);
+	DWC_SPINLOCK_IRQSAVE(pcd->lock, &flags);
+	if (ep->iso_req_handle != req_handle) {
+		DWC_SPINUNLOCK_IRQRESTORE(pcd->lock, flags);
+		return -DWC_E_INVALID;
+	}
+
+	DWC_SPINUNLOCK_IRQRESTORE(pcd->lock, flags);
+
+	ep->iso_req_handle = 0;
+	return 0;
+}
+
+/**
+ * This function is used for perodical data exchnage between PCD and gadget drivers.
+ * for Isochronous EPs
+ *
+ *	- Every time a sync period completes this function is called to
+ *	  perform data exchange between PCD and gadget
+ */
+void dwc_otg_iso_buffer_done(dwc_otg_pcd_t * pcd, dwc_otg_pcd_ep_t * ep,
+			     void *req_handle)
+{
+	int i;
+	dwc_ep_t *dwc_ep;
+
+	dwc_ep = &ep->dwc_ep;
+
+	DWC_SPINUNLOCK(ep->pcd->lock);
+	pcd->fops->isoc_complete(pcd, ep->priv, ep->iso_req_handle,
+				 dwc_ep->proc_buf_num ^ 0x1);
+	DWC_SPINLOCK(ep->pcd->lock);
+
+	for (i = 0; i < dwc_ep->pkt_cnt; ++i) {
+		dwc_ep->pkt_info[i].status = 0;
+		dwc_ep->pkt_info[i].offset = 0;
+		dwc_ep->pkt_info[i].length = 0;
+	}
+}
+
+int dwc_otg_pcd_get_iso_packet_count(dwc_otg_pcd_t * pcd, void *ep_handle,
+				     void *iso_req_handle)
+{
+	dwc_otg_pcd_ep_t *ep;
+	dwc_ep_t *dwc_ep;
+
+	ep = get_ep_from_handle(pcd, ep_handle);
+	if (!ep->desc || ep->dwc_ep.num == 0) {
+		DWC_WARN("bad ep\n");
+		return -DWC_E_INVALID;
+	}
+	dwc_ep = &ep->dwc_ep;
+
+	return dwc_ep->pkt_cnt;
+}
+
+void dwc_otg_pcd_get_iso_packet_params(dwc_otg_pcd_t * pcd, void *ep_handle,
+				       void *iso_req_handle, int packet,
+				       int *status, int *actual, int *offset)
+{
+	dwc_otg_pcd_ep_t *ep;
+	dwc_ep_t *dwc_ep;
+
+	ep = get_ep_from_handle(pcd, ep_handle);
+	if (!ep)
+		DWC_WARN("bad ep\n");
+
+	dwc_ep = &ep->dwc_ep;
+
+	*status = dwc_ep->pkt_info[packet].status;
+	*actual = dwc_ep->pkt_info[packet].length;
+	*offset = dwc_ep->pkt_info[packet].offset;
+}
+
+#endif /* DWC_EN_ISOC */
+
+static void dwc_otg_pcd_init_ep(dwc_otg_pcd_t * pcd, dwc_otg_pcd_ep_t * pcd_ep,
+				uint32_t is_in, uint32_t ep_num)
+{
+	/* Init EP structure */
+	pcd_ep->desc = 0;
+	pcd_ep->pcd = pcd;
+	pcd_ep->stopped = 1;
+	pcd_ep->queue_sof = 0;
+
+	/* Init DWC ep structure */
+	pcd_ep->dwc_ep.is_in = is_in;
+	pcd_ep->dwc_ep.num = ep_num;
+	pcd_ep->dwc_ep.active = 0;
+	pcd_ep->dwc_ep.tx_fifo_num = 0;
+	/* Control until ep is actvated */
+	pcd_ep->dwc_ep.type = DWC_OTG_EP_TYPE_CONTROL;
+	pcd_ep->dwc_ep.maxpacket = MAX_PACKET_SIZE;
+	pcd_ep->dwc_ep.dma_addr = 0;
+	pcd_ep->dwc_ep.start_xfer_buff = 0;
+	pcd_ep->dwc_ep.xfer_buff = 0;
+	pcd_ep->dwc_ep.xfer_len = 0;
+	pcd_ep->dwc_ep.xfer_count = 0;
+	pcd_ep->dwc_ep.sent_zlp = 0;
+	pcd_ep->dwc_ep.total_len = 0;
+	pcd_ep->dwc_ep.desc_addr = 0;
+	pcd_ep->dwc_ep.dma_desc_addr = 0;
+	DWC_CIRCLEQ_INIT(&pcd_ep->queue);
+}
+
+/**
+ * Initialize ep's
+ */
+static void dwc_otg_pcd_reinit(dwc_otg_pcd_t * pcd)
+{
+	int i;
+	uint32_t hwcfg1;
+	dwc_otg_pcd_ep_t *ep;
+	int in_ep_cntr, out_ep_cntr;
+	uint32_t num_in_eps = (GET_CORE_IF(pcd))->dev_if->num_in_eps;
+	uint32_t num_out_eps = (GET_CORE_IF(pcd))->dev_if->num_out_eps;
+
+	/**
+	 * Initialize the EP0 structure.
+	 */
+	ep = &pcd->ep0;
+	dwc_otg_pcd_init_ep(pcd, ep, 0, 0);
+
+	in_ep_cntr = 0;
+	hwcfg1 = (GET_CORE_IF(pcd))->hwcfg1.d32 >> 3;
+	for (i = 1; in_ep_cntr < num_in_eps; i++) {
+		if ((hwcfg1 & 0x1) == 0) {
+			dwc_otg_pcd_ep_t *ep = &pcd->in_ep[in_ep_cntr];
+			in_ep_cntr++;
+			/**
+			 * @todo NGS: Add direction to EP, based on contents
+			 * of HWCFG1.  Need a copy of HWCFG1 in pcd structure?
+			 * sprintf(";r
+			 */
+			dwc_otg_pcd_init_ep(pcd, ep, 1 /* IN */ , i);
+
+			DWC_CIRCLEQ_INIT(&ep->queue);
+		}
+		hwcfg1 >>= 2;
+	}
+
+	out_ep_cntr = 0;
+	hwcfg1 = (GET_CORE_IF(pcd))->hwcfg1.d32 >> 2;
+	for (i = 1; out_ep_cntr < num_out_eps; i++) {
+		if ((hwcfg1 & 0x1) == 0) {
+			dwc_otg_pcd_ep_t *ep = &pcd->out_ep[out_ep_cntr];
+			out_ep_cntr++;
+			/**
+			 * @todo NGS: Add direction to EP, based on contents
+			 * of HWCFG1.  Need a copy of HWCFG1 in pcd structure?
+			 * sprintf(";r
+			 */
+			dwc_otg_pcd_init_ep(pcd, ep, 0 /* OUT */ , i);
+			DWC_CIRCLEQ_INIT(&ep->queue);
+		}
+		hwcfg1 >>= 2;
+	}
+
+	pcd->ep0state = EP0_DISCONNECT;
+	pcd->ep0.dwc_ep.maxpacket = MAX_EP0_SIZE;
+	pcd->ep0.dwc_ep.type = DWC_OTG_EP_TYPE_CONTROL;
+}
+
+/**
+ * This function is called when the SRP timer expires. The SRP should
+ * complete within 6 seconds.
+ */
+static void srp_timeout(void *ptr)
+{
+	gotgctl_data_t gotgctl;
+	dwc_otg_core_if_t *core_if = (dwc_otg_core_if_t *) ptr;
+	volatile uint32_t *addr = &core_if->core_global_regs->gotgctl;
+
+	gotgctl.d32 = DWC_READ_REG32(addr);
+
+	core_if->srp_timer_started = 0;
+
+	if (core_if->adp_enable) {
+		if (gotgctl.b.bsesvld == 0) {
+			gpwrdn_data_t gpwrdn = {.d32 = 0 };
+			DWC_PRINTF("SRP Timeout BSESSVLD = 0\n");
+			/* Power off the core */
+			if (core_if->power_down == 2) {
+				gpwrdn.b.pwrdnswtch = 1;
+				DWC_MODIFY_REG32(&core_if->
+						 core_global_regs->gpwrdn,
+						 gpwrdn.d32, 0);
+			}
+
+			gpwrdn.d32 = 0;
+			gpwrdn.b.pmuintsel = 1;
+			gpwrdn.b.pmuactv = 1;
+			DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, 0,
+					 gpwrdn.d32);
+			dwc_otg_adp_probe_start(core_if);
+		} else {
+			DWC_PRINTF("SRP Timeout BSESSVLD = 1\n");
+			core_if->op_state = B_PERIPHERAL;
+			dwc_otg_core_init(core_if);
+			dwc_otg_enable_global_interrupts(core_if);
+			cil_pcd_start(core_if);
+		}
+	}
+
+	if ((core_if->core_params->phy_type == DWC_PHY_TYPE_PARAM_FS) &&
+	    (core_if->core_params->i2c_enable)) {
+		DWC_PRINTF("SRP Timeout\n");
+
+		if ((core_if->srp_success) && (gotgctl.b.bsesvld)) {
+			if (core_if->pcd_cb && core_if->pcd_cb->resume_wakeup) {
+				core_if->pcd_cb->resume_wakeup(core_if->pcd_cb->p);
+			}
+
+			/* Clear Session Request */
+			gotgctl.d32 = 0;
+			gotgctl.b.sesreq = 1;
+			DWC_MODIFY_REG32(&core_if->core_global_regs->gotgctl,
+					 gotgctl.d32, 0);
+
+			core_if->srp_success = 0;
+		} else {
+			__DWC_ERROR("Device not connected/responding\n");
+			gotgctl.b.sesreq = 0;
+			DWC_WRITE_REG32(addr, gotgctl.d32);
+		}
+	} else if (gotgctl.b.sesreq) {
+		DWC_PRINTF("SRP Timeout\n");
+
+		__DWC_ERROR("Device not connected/responding\n");
+		gotgctl.b.sesreq = 0;
+		DWC_WRITE_REG32(addr, gotgctl.d32);
+	} else {
+		DWC_PRINTF(" SRP GOTGCTL=%0x\n", gotgctl.d32);
+	}
+}
+
+/**
+ * Tasklet
+ *
+ */
+extern void start_next_request(dwc_otg_pcd_ep_t * ep);
+
+static void start_xfer_tasklet_func(void *data)
+{
+	dwc_otg_pcd_t *pcd = (dwc_otg_pcd_t *) data;
+	dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd);
+
+	int i;
+	depctl_data_t diepctl;
+
+	DWC_DEBUGPL(DBG_PCDV, "Start xfer tasklet\n");
+
+	diepctl.d32 = DWC_READ_REG32(&core_if->dev_if->in_ep_regs[0]->diepctl);
+
+	if (pcd->ep0.queue_sof) {
+		pcd->ep0.queue_sof = 0;
+		start_next_request(&pcd->ep0);
+		// break;
+	}
+
+	for (i = 0; i < core_if->dev_if->num_in_eps; i++) {
+		depctl_data_t diepctl;
+		diepctl.d32 =
+		    DWC_READ_REG32(&core_if->dev_if->in_ep_regs[i]->diepctl);
+
+		if (pcd->in_ep[i].queue_sof) {
+			pcd->in_ep[i].queue_sof = 0;
+			start_next_request(&pcd->in_ep[i]);
+			// break;
+		}
+	}
+
+	return;
+}
+
+/**
+ * This function initialized the PCD portion of the driver.
+ *
+ */
+dwc_otg_pcd_t *dwc_otg_pcd_init(dwc_otg_device_t *otg_dev)
+{
+	struct device *dev = &otg_dev->os_dep.platformdev->dev;
+	dwc_otg_core_if_t *core_if = otg_dev->core_if;
+	dwc_otg_pcd_t *pcd = NULL;
+	dwc_otg_dev_if_t *dev_if;
+	int i;
+
+	/*
+	 * Allocate PCD structure
+	 */
+	pcd = DWC_ALLOC(sizeof(dwc_otg_pcd_t));
+
+	if (pcd == NULL) {
+		return NULL;
+	}
+
+#if (defined(DWC_LINUX) && defined(CONFIG_DEBUG_SPINLOCK))
+	DWC_SPINLOCK_ALLOC_LINUX_DEBUG(pcd->lock);
+#else
+	pcd->lock = DWC_SPINLOCK_ALLOC();
+#endif
+        DWC_DEBUGPL(DBG_HCDV, "Init of PCD %p given core_if %p\n",
+                    pcd, core_if);//GRAYG
+	if (!pcd->lock) {
+		DWC_ERROR("Could not allocate lock for pcd");
+		DWC_FREE(pcd);
+		return NULL;
+	}
+	/* Set core_if's lock pointer to hcd->lock */
+	core_if->lock = pcd->lock;
+	pcd->core_if = core_if;
+
+	dev_if = core_if->dev_if;
+	dev_if->isoc_ep = NULL;
+
+	if (core_if->hwcfg4.b.ded_fifo_en) {
+		DWC_PRINTF("Dedicated Tx FIFOs mode\n");
+	} else {
+		DWC_PRINTF("Shared Tx FIFO mode\n");
+	}
+
+	/*
+	 * Initialized the Core for Device mode here if there is nod ADP support.
+	 * Otherwise it will be done later in dwc_otg_adp_start routine.
+	 */
+	if (dwc_otg_is_device_mode(core_if) /*&& !core_if->adp_enable*/) {
+		dwc_otg_core_dev_init(core_if);
+	}
+
+	/*
+	 * Register the PCD Callbacks.
+	 */
+	dwc_otg_cil_register_pcd_callbacks(core_if, &pcd_callbacks, pcd);
+
+	/*
+	 * Initialize the DMA buffer for SETUP packets
+	 */
+	if (GET_CORE_IF(pcd)->dma_enable) {
+		pcd->setup_pkt =
+		    DWC_DMA_ALLOC(dev, sizeof(*pcd->setup_pkt) * 5,
+				  &pcd->setup_pkt_dma_handle);
+		if (pcd->setup_pkt == NULL) {
+			DWC_FREE(pcd);
+			return NULL;
+		}
+
+		pcd->status_buf =
+		    DWC_DMA_ALLOC(dev, sizeof(uint16_t),
+				  &pcd->status_buf_dma_handle);
+		if (pcd->status_buf == NULL) {
+			DWC_DMA_FREE(dev, sizeof(*pcd->setup_pkt) * 5,
+				     pcd->setup_pkt, pcd->setup_pkt_dma_handle);
+			DWC_FREE(pcd);
+			return NULL;
+		}
+
+		if (GET_CORE_IF(pcd)->dma_desc_enable) {
+			dev_if->setup_desc_addr[0] =
+			    dwc_otg_ep_alloc_desc_chain(dev,
+				&dev_if->dma_setup_desc_addr[0], 1);
+			dev_if->setup_desc_addr[1] =
+			    dwc_otg_ep_alloc_desc_chain(dev,
+				&dev_if->dma_setup_desc_addr[1], 1);
+			dev_if->in_desc_addr =
+			    dwc_otg_ep_alloc_desc_chain(dev,
+				&dev_if->dma_in_desc_addr, 1);
+			dev_if->out_desc_addr =
+			    dwc_otg_ep_alloc_desc_chain(dev,
+				&dev_if->dma_out_desc_addr, 1);
+			pcd->data_terminated = 0;
+
+			if (dev_if->setup_desc_addr[0] == 0
+			    || dev_if->setup_desc_addr[1] == 0
+			    || dev_if->in_desc_addr == 0
+			    || dev_if->out_desc_addr == 0) {
+
+				if (dev_if->out_desc_addr)
+					dwc_otg_ep_free_desc_chain(dev,
+					     dev_if->out_desc_addr,
+					     dev_if->dma_out_desc_addr, 1);
+				if (dev_if->in_desc_addr)
+					dwc_otg_ep_free_desc_chain(dev,
+					     dev_if->in_desc_addr,
+					     dev_if->dma_in_desc_addr, 1);
+				if (dev_if->setup_desc_addr[1])
+					dwc_otg_ep_free_desc_chain(dev,
+					     dev_if->setup_desc_addr[1],
+					     dev_if->dma_setup_desc_addr[1], 1);
+				if (dev_if->setup_desc_addr[0])
+					dwc_otg_ep_free_desc_chain(dev,
+					     dev_if->setup_desc_addr[0],
+					     dev_if->dma_setup_desc_addr[0], 1);
+
+				DWC_DMA_FREE(dev, sizeof(*pcd->setup_pkt) * 5,
+					     pcd->setup_pkt,
+					     pcd->setup_pkt_dma_handle);
+				DWC_DMA_FREE(dev, sizeof(*pcd->status_buf),
+					     pcd->status_buf,
+					     pcd->status_buf_dma_handle);
+
+				DWC_FREE(pcd);
+
+				return NULL;
+			}
+		}
+	} else {
+		pcd->setup_pkt = DWC_ALLOC(sizeof(*pcd->setup_pkt) * 5);
+		if (pcd->setup_pkt == NULL) {
+			DWC_FREE(pcd);
+			return NULL;
+		}
+
+		pcd->status_buf = DWC_ALLOC(sizeof(uint16_t));
+		if (pcd->status_buf == NULL) {
+			DWC_FREE(pcd->setup_pkt);
+			DWC_FREE(pcd);
+			return NULL;
+		}
+	}
+
+	dwc_otg_pcd_reinit(pcd);
+
+	/* Allocate the cfi object for the PCD */
+#ifdef DWC_UTE_CFI
+	pcd->cfi = DWC_ALLOC(sizeof(cfiobject_t));
+	if (NULL == pcd->cfi)
+		goto fail;
+	if (init_cfi(pcd->cfi)) {
+		CFI_INFO("%s: Failed to init the CFI object\n", __func__);
+		goto fail;
+	}
+#endif
+
+	/* Initialize tasklets */
+	pcd->start_xfer_tasklet = DWC_TASK_ALLOC("xfer_tasklet",
+						 start_xfer_tasklet_func, pcd);
+	pcd->test_mode_tasklet = DWC_TASK_ALLOC("test_mode_tasklet",
+						do_test_mode, pcd);
+
+	/* Initialize SRP timer */
+	core_if->srp_timer = DWC_TIMER_ALLOC("SRP TIMER", srp_timeout, core_if);
+
+	if (core_if->core_params->dev_out_nak) {
+		/**
+		* Initialize xfer timeout timer. Implemented for
+		* 2.93a feature "Device DDMA OUT NAK Enhancement"
+		*/
+		for(i = 0; i < MAX_EPS_CHANNELS; i++) {
+			pcd->core_if->ep_xfer_timer[i] =
+				DWC_TIMER_ALLOC("ep timer", ep_xfer_timeout,
+				&pcd->core_if->ep_xfer_info[i]);
+		}
+	}
+
+	return pcd;
+#ifdef DWC_UTE_CFI
+fail:
+#endif
+	if (pcd->setup_pkt)
+		DWC_FREE(pcd->setup_pkt);
+	if (pcd->status_buf)
+		DWC_FREE(pcd->status_buf);
+#ifdef DWC_UTE_CFI
+	if (pcd->cfi)
+		DWC_FREE(pcd->cfi);
+#endif
+	if (pcd)
+		DWC_FREE(pcd);
+	return NULL;
+
+}
+
+/**
+ * Remove PCD specific data
+ */
+void dwc_otg_pcd_remove(dwc_otg_pcd_t * pcd)
+{
+	dwc_otg_dev_if_t *dev_if = GET_CORE_IF(pcd)->dev_if;
+	struct device *dev = dwc_otg_pcd_to_dev(pcd);
+	int i;
+
+	if (pcd->core_if->core_params->dev_out_nak) {
+		for (i = 0; i < MAX_EPS_CHANNELS; i++) {
+			DWC_TIMER_CANCEL(pcd->core_if->ep_xfer_timer[i]);
+			pcd->core_if->ep_xfer_info[i].state = 0;
+		}
+	}
+
+	if (GET_CORE_IF(pcd)->dma_enable) {
+		DWC_DMA_FREE(dev, sizeof(*pcd->setup_pkt) * 5, pcd->setup_pkt,
+			     pcd->setup_pkt_dma_handle);
+		DWC_DMA_FREE(dev, sizeof(uint16_t), pcd->status_buf,
+			     pcd->status_buf_dma_handle);
+		if (GET_CORE_IF(pcd)->dma_desc_enable) {
+			dwc_otg_ep_free_desc_chain(dev,
+						   dev_if->setup_desc_addr[0],
+						   dev_if->dma_setup_desc_addr
+						   [0], 1);
+			dwc_otg_ep_free_desc_chain(dev,
+						   dev_if->setup_desc_addr[1],
+						   dev_if->dma_setup_desc_addr
+						   [1], 1);
+			dwc_otg_ep_free_desc_chain(dev,
+						   dev_if->in_desc_addr,
+						   dev_if->dma_in_desc_addr, 1);
+			dwc_otg_ep_free_desc_chain(dev,
+						   dev_if->out_desc_addr,
+						   dev_if->dma_out_desc_addr,
+						   1);
+		}
+	} else {
+		DWC_FREE(pcd->setup_pkt);
+		DWC_FREE(pcd->status_buf);
+	}
+	DWC_SPINLOCK_FREE(pcd->lock);
+	/* Set core_if's lock pointer to NULL */
+	pcd->core_if->lock = NULL;
+
+	DWC_TASK_FREE(pcd->start_xfer_tasklet);
+	DWC_TASK_FREE(pcd->test_mode_tasklet);
+	if (pcd->core_if->core_params->dev_out_nak) {
+		for (i = 0; i < MAX_EPS_CHANNELS; i++) {
+			if (pcd->core_if->ep_xfer_timer[i]) {
+					DWC_TIMER_FREE(pcd->core_if->ep_xfer_timer[i]);
+			}
+		}
+	}
+
+/* Release the CFI object's dynamic memory */
+#ifdef DWC_UTE_CFI
+	if (pcd->cfi->ops.release) {
+		pcd->cfi->ops.release(pcd->cfi);
+	}
+#endif
+
+	DWC_FREE(pcd);
+}
+
+/**
+ * Returns whether registered pcd is dual speed or not
+ */
+uint32_t dwc_otg_pcd_is_dualspeed(dwc_otg_pcd_t * pcd)
+{
+	dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd);
+
+	if ((core_if->core_params->speed == DWC_SPEED_PARAM_FULL) ||
+	    ((core_if->hwcfg2.b.hs_phy_type == 2) &&
+	     (core_if->hwcfg2.b.fs_phy_type == 1) &&
+	     (core_if->core_params->ulpi_fs_ls))) {
+		return 0;
+	}
+
+	return 1;
+}
+
+/**
+ * Returns whether registered pcd is OTG capable or not
+ */
+uint32_t dwc_otg_pcd_is_otg(dwc_otg_pcd_t * pcd)
+{
+	dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd);
+	gusbcfg_data_t usbcfg = {.d32 = 0 };
+
+	usbcfg.d32 = DWC_READ_REG32(&core_if->core_global_regs->gusbcfg);
+	if (!usbcfg.b.srpcap || !usbcfg.b.hnpcap) {
+		return 0;
+	}
+
+	return 1;
+}
+
+/**
+ * This function assigns periodic Tx FIFO to an periodic EP
+ * in shared Tx FIFO mode
+ */
+static uint32_t assign_tx_fifo(dwc_otg_core_if_t * core_if)
+{
+	uint32_t TxMsk = 1;
+	int i;
+
+	for (i = 0; i < core_if->hwcfg4.b.num_in_eps; ++i) {
+		if ((TxMsk & core_if->tx_msk) == 0) {
+			core_if->tx_msk |= TxMsk;
+			return i + 1;
+		}
+		TxMsk <<= 1;
+	}
+	return 0;
+}
+
+/**
+ * This function assigns periodic Tx FIFO to an periodic EP
+ * in shared Tx FIFO mode
+ */
+static uint32_t assign_perio_tx_fifo(dwc_otg_core_if_t * core_if)
+{
+	uint32_t PerTxMsk = 1;
+	int i;
+	for (i = 0; i < core_if->hwcfg4.b.num_dev_perio_in_ep; ++i) {
+		if ((PerTxMsk & core_if->p_tx_msk) == 0) {
+			core_if->p_tx_msk |= PerTxMsk;
+			return i + 1;
+		}
+		PerTxMsk <<= 1;
+	}
+	return 0;
+}
+
+/**
+ * This function releases periodic Tx FIFO
+ * in shared Tx FIFO mode
+ */
+static void release_perio_tx_fifo(dwc_otg_core_if_t * core_if,
+				  uint32_t fifo_num)
+{
+	core_if->p_tx_msk =
+	    (core_if->p_tx_msk & (1 << (fifo_num - 1))) ^ core_if->p_tx_msk;
+}
+
+/**
+ * This function releases periodic Tx FIFO
+ * in shared Tx FIFO mode
+ */
+static void release_tx_fifo(dwc_otg_core_if_t * core_if, uint32_t fifo_num)
+{
+	core_if->tx_msk =
+	    (core_if->tx_msk & (1 << (fifo_num - 1))) ^ core_if->tx_msk;
+}
+
+/**
+ * This function is being called from gadget
+ * to enable PCD endpoint.
+ */
+int dwc_otg_pcd_ep_enable(dwc_otg_pcd_t * pcd,
+			  const uint8_t * ep_desc, void *usb_ep)
+{
+	int num, dir;
+	dwc_otg_pcd_ep_t *ep = NULL;
+	const usb_endpoint_descriptor_t *desc;
+	dwc_irqflags_t flags;
+	fifosize_data_t dptxfsiz = {.d32 = 0 };
+	gdfifocfg_data_t gdfifocfg = {.d32 = 0 };
+	gdfifocfg_data_t gdfifocfgbase = {.d32 = 0 };
+	int retval = 0;
+	int i, epcount;
+	struct device *dev = dwc_otg_pcd_to_dev(pcd);
+
+	desc = (const usb_endpoint_descriptor_t *)ep_desc;
+
+	if (!desc) {
+		pcd->ep0.priv = usb_ep;
+		ep = &pcd->ep0;
+		retval = -DWC_E_INVALID;
+		goto out;
+	}
+
+	num = UE_GET_ADDR(desc->bEndpointAddress);
+	dir = UE_GET_DIR(desc->bEndpointAddress);
+
+	if (!UGETW(desc->wMaxPacketSize)) {
+		DWC_WARN("bad maxpacketsize\n");
+		retval = -DWC_E_INVALID;
+		goto out;
+	}
+
+	if (dir == UE_DIR_IN) {
+		epcount = pcd->core_if->dev_if->num_in_eps;
+		for (i = 0; i < epcount; i++) {
+			if (num == pcd->in_ep[i].dwc_ep.num) {
+				ep = &pcd->in_ep[i];
+				break;
+			}
+		}
+	} else {
+		epcount = pcd->core_if->dev_if->num_out_eps;
+		for (i = 0; i < epcount; i++) {
+			if (num == pcd->out_ep[i].dwc_ep.num) {
+				ep = &pcd->out_ep[i];
+				break;
+			}
+		}
+	}
+
+	if (!ep) {
+		DWC_WARN("bad address\n");
+		retval = -DWC_E_INVALID;
+		goto out;
+	}
+
+	DWC_SPINLOCK_IRQSAVE(pcd->lock, &flags);
+
+	ep->desc = desc;
+	ep->priv = usb_ep;
+
+	/*
+	 * Activate the EP
+	 */
+	ep->stopped = 0;
+
+	ep->dwc_ep.is_in = (dir == UE_DIR_IN);
+	ep->dwc_ep.maxpacket = UGETW(desc->wMaxPacketSize);
+
+	ep->dwc_ep.type = desc->bmAttributes & UE_XFERTYPE;
+
+	if (ep->dwc_ep.is_in) {
+		if (!GET_CORE_IF(pcd)->en_multiple_tx_fifo) {
+			ep->dwc_ep.tx_fifo_num = 0;
+
+			if (ep->dwc_ep.type == UE_ISOCHRONOUS) {
+				/*
+				 * if ISOC EP then assign a Periodic Tx FIFO.
+				 */
+				ep->dwc_ep.tx_fifo_num =
+				    assign_perio_tx_fifo(GET_CORE_IF(pcd));
+			}
+		} else {
+			/*
+			 * if Dedicated FIFOs mode is on then assign a Tx FIFO.
+			 */
+			ep->dwc_ep.tx_fifo_num =
+			    assign_tx_fifo(GET_CORE_IF(pcd));
+		}
+
+		/* Calculating EP info controller base address */
+		if (ep->dwc_ep.tx_fifo_num
+		    && GET_CORE_IF(pcd)->en_multiple_tx_fifo) {
+			gdfifocfg.d32 =
+			    DWC_READ_REG32(&GET_CORE_IF(pcd)->
+					   core_global_regs->gdfifocfg);
+			gdfifocfgbase.d32 = gdfifocfg.d32 >> 16;
+			dptxfsiz.d32 =
+			    (DWC_READ_REG32
+			     (&GET_CORE_IF(pcd)->core_global_regs->
+			      dtxfsiz[ep->dwc_ep.tx_fifo_num - 1]) >> 16);
+			gdfifocfg.b.epinfobase =
+			    gdfifocfgbase.d32 + dptxfsiz.d32;
+			if (GET_CORE_IF(pcd)->snpsid <= OTG_CORE_REV_2_94a) {
+				DWC_WRITE_REG32(&GET_CORE_IF(pcd)->
+						core_global_regs->gdfifocfg,
+						gdfifocfg.d32);
+			}
+		}
+	}
+	/* Set initial data PID. */
+	if (ep->dwc_ep.type == UE_BULK) {
+		ep->dwc_ep.data_pid_start = 0;
+	}
+
+	/* Alloc DMA Descriptors */
+	if (GET_CORE_IF(pcd)->dma_desc_enable) {
+#ifndef DWC_UTE_PER_IO
+		if (ep->dwc_ep.type != UE_ISOCHRONOUS) {
+#endif
+			ep->dwc_ep.desc_addr =
+			    dwc_otg_ep_alloc_desc_chain(dev,
+						&ep->dwc_ep.dma_desc_addr,
+						MAX_DMA_DESC_CNT);
+			if (!ep->dwc_ep.desc_addr) {
+				DWC_WARN("%s, can't allocate DMA descriptor\n",
+					 __func__);
+				retval = -DWC_E_SHUTDOWN;
+				DWC_SPINUNLOCK_IRQRESTORE(pcd->lock, flags);
+				goto out;
+			}
+#ifndef DWC_UTE_PER_IO
+		}
+#endif
+	}
+
+	DWC_DEBUGPL(DBG_PCD, "Activate %s: type=%d, mps=%d desc=%p\n",
+		    (ep->dwc_ep.is_in ? "IN" : "OUT"),
+		    ep->dwc_ep.type, ep->dwc_ep.maxpacket, ep->desc);
+#ifdef DWC_UTE_PER_IO
+	ep->dwc_ep.xiso_bInterval = 1 << (ep->desc->bInterval - 1);
+#endif
+	if (ep->dwc_ep.type == DWC_OTG_EP_TYPE_ISOC) {
+		ep->dwc_ep.bInterval = 1 << (ep->desc->bInterval - 1);
+		ep->dwc_ep.frame_num = 0xFFFFFFFF;
+	}
+
+	dwc_otg_ep_activate(GET_CORE_IF(pcd), &ep->dwc_ep);
+
+#ifdef DWC_UTE_CFI
+	if (pcd->cfi->ops.ep_enable) {
+		pcd->cfi->ops.ep_enable(pcd->cfi, pcd, ep);
+	}
+#endif
+
+	DWC_SPINUNLOCK_IRQRESTORE(pcd->lock, flags);
+
+out:
+	return retval;
+}
+
+/**
+ * This function is being called from gadget
+ * to disable PCD endpoint.
+ */
+int dwc_otg_pcd_ep_disable(dwc_otg_pcd_t * pcd, void *ep_handle)
+{
+	dwc_otg_pcd_ep_t *ep;
+	dwc_irqflags_t flags;
+	dwc_otg_dev_dma_desc_t *desc_addr;
+	dwc_dma_t dma_desc_addr;
+	gdfifocfg_data_t gdfifocfgbase = {.d32 = 0 };
+	gdfifocfg_data_t gdfifocfg = {.d32 = 0 };
+	fifosize_data_t dptxfsiz = {.d32 = 0 };
+	struct device *dev = dwc_otg_pcd_to_dev(pcd);
+
+	ep = get_ep_from_handle(pcd, ep_handle);
+
+	if (!ep || !ep->desc) {
+		DWC_DEBUGPL(DBG_PCD, "bad ep address\n");
+		return -DWC_E_INVALID;
+	}
+
+	DWC_SPINLOCK_IRQSAVE(pcd->lock, &flags);
+
+	dwc_otg_request_nuke(ep);
+
+	dwc_otg_ep_deactivate(GET_CORE_IF(pcd), &ep->dwc_ep);
+	if (pcd->core_if->core_params->dev_out_nak) {
+		DWC_TIMER_CANCEL(pcd->core_if->ep_xfer_timer[ep->dwc_ep.num]);
+		pcd->core_if->ep_xfer_info[ep->dwc_ep.num].state = 0;
+	}
+	ep->desc = NULL;
+	ep->stopped = 1;
+
+	gdfifocfg.d32 =
+	    DWC_READ_REG32(&GET_CORE_IF(pcd)->core_global_regs->gdfifocfg);
+	gdfifocfgbase.d32 = gdfifocfg.d32 >> 16;
+
+	if (ep->dwc_ep.is_in) {
+		if (GET_CORE_IF(pcd)->en_multiple_tx_fifo) {
+			/* Flush the Tx FIFO */
+			dwc_otg_flush_tx_fifo(GET_CORE_IF(pcd),
+					      ep->dwc_ep.tx_fifo_num);
+		}
+		release_perio_tx_fifo(GET_CORE_IF(pcd), ep->dwc_ep.tx_fifo_num);
+		release_tx_fifo(GET_CORE_IF(pcd), ep->dwc_ep.tx_fifo_num);
+		if (GET_CORE_IF(pcd)->en_multiple_tx_fifo) {
+			/* Decreasing EPinfo Base Addr */
+			dptxfsiz.d32 =
+			    (DWC_READ_REG32
+			     (&GET_CORE_IF(pcd)->
+				core_global_regs->dtxfsiz[ep->dwc_ep.tx_fifo_num-1]) >> 16);
+			gdfifocfg.b.epinfobase = gdfifocfgbase.d32 - dptxfsiz.d32;
+			if (GET_CORE_IF(pcd)->snpsid <= OTG_CORE_REV_2_94a) {
+				DWC_WRITE_REG32(&GET_CORE_IF(pcd)->core_global_regs->gdfifocfg,
+					gdfifocfg.d32);
+			}
+		}
+	}
+
+	/* Free DMA Descriptors */
+	if (GET_CORE_IF(pcd)->dma_desc_enable) {
+		if (ep->dwc_ep.type != UE_ISOCHRONOUS) {
+			desc_addr = ep->dwc_ep.desc_addr;
+			dma_desc_addr = ep->dwc_ep.dma_desc_addr;
+
+			/* Cannot call dma_free_coherent() with IRQs disabled */
+			DWC_SPINUNLOCK_IRQRESTORE(pcd->lock, flags);
+			dwc_otg_ep_free_desc_chain(dev, desc_addr, dma_desc_addr,
+						   MAX_DMA_DESC_CNT);
+
+			goto out_unlocked;
+		}
+	}
+	DWC_SPINUNLOCK_IRQRESTORE(pcd->lock, flags);
+
+out_unlocked:
+	DWC_DEBUGPL(DBG_PCD, "%d %s disabled\n", ep->dwc_ep.num,
+		    ep->dwc_ep.is_in ? "IN" : "OUT");
+	return 0;
+
+}
+
+/******************************************************************************/
+#ifdef DWC_UTE_PER_IO
+
+/**
+ * Free the request and its extended parts
+ *
+ */
+void dwc_pcd_xiso_ereq_free(dwc_otg_pcd_ep_t * ep, dwc_otg_pcd_request_t * req)
+{
+	DWC_FREE(req->ext_req.per_io_frame_descs);
+	DWC_FREE(req);
+}
+
+/**
+ * Start the next request in the endpoint's queue.
+ *
+ */
+int dwc_otg_pcd_xiso_start_next_request(dwc_otg_pcd_t * pcd,
+					dwc_otg_pcd_ep_t * ep)
+{
+	int i;
+	dwc_otg_pcd_request_t *req = NULL;
+	dwc_ep_t *dwcep = NULL;
+	struct dwc_iso_xreq_port *ereq = NULL;
+	struct dwc_iso_pkt_desc_port *ddesc_iso;
+	uint16_t nat;
+	depctl_data_t diepctl;
+
+	dwcep = &ep->dwc_ep;
+
+	if (dwcep->xiso_active_xfers > 0) {
+#if 0	//Disable this to decrease s/w overhead that is crucial for Isoc transfers
+		DWC_WARN("There are currently active transfers for EP%d \
+				(active=%d; queued=%d)", dwcep->num, dwcep->xiso_active_xfers,
+				dwcep->xiso_queued_xfers);
+#endif
+		return 0;
+	}
+
+	nat = UGETW(ep->desc->wMaxPacketSize);
+	nat = (nat >> 11) & 0x03;
+
+	if (!DWC_CIRCLEQ_EMPTY(&ep->queue)) {
+		req = DWC_CIRCLEQ_FIRST(&ep->queue);
+		ereq = &req->ext_req;
+		ep->stopped = 0;
+
+		/* Get the frame number */
+		dwcep->xiso_frame_num =
+		    dwc_otg_get_frame_number(GET_CORE_IF(pcd));
+		DWC_DEBUG("FRM_NUM=%d", dwcep->xiso_frame_num);
+
+		ddesc_iso = ereq->per_io_frame_descs;
+
+		if (dwcep->is_in) {
+			/* Setup DMA Descriptor chain for IN Isoc request */
+			for (i = 0; i < ereq->pio_pkt_count; i++) {
+				//if ((i % (nat + 1)) == 0)
+				if ( i > 0 )
+					dwcep->xiso_frame_num =
+					    (dwcep->xiso_bInterval +
+										dwcep->xiso_frame_num) & 0x3FFF;
+				dwcep->desc_addr[i].buf =
+				    req->dma + ddesc_iso[i].offset;
+				dwcep->desc_addr[i].status.b_iso_in.txbytes =
+				    ddesc_iso[i].length;
+				dwcep->desc_addr[i].status.b_iso_in.framenum =
+				    dwcep->xiso_frame_num;
+				dwcep->desc_addr[i].status.b_iso_in.bs =
+				    BS_HOST_READY;
+				dwcep->desc_addr[i].status.b_iso_in.txsts = 0;
+				dwcep->desc_addr[i].status.b_iso_in.sp =
+				    (ddesc_iso[i].length %
+				     dwcep->maxpacket) ? 1 : 0;
+				dwcep->desc_addr[i].status.b_iso_in.ioc = 0;
+				dwcep->desc_addr[i].status.b_iso_in.pid = nat + 1;
+				dwcep->desc_addr[i].status.b_iso_in.l = 0;
+
+				/* Process the last descriptor */
+				if (i == ereq->pio_pkt_count - 1) {
+					dwcep->desc_addr[i].status.b_iso_in.ioc = 1;
+					dwcep->desc_addr[i].status.b_iso_in.l = 1;
+				}
+			}
+
+			/* Setup and start the transfer for this endpoint */
+			dwcep->xiso_active_xfers++;
+			DWC_WRITE_REG32(&GET_CORE_IF(pcd)->dev_if->
+					in_ep_regs[dwcep->num]->diepdma,
+					dwcep->dma_desc_addr);
+			diepctl.d32 = 0;
+			diepctl.b.epena = 1;
+			diepctl.b.cnak = 1;
+			DWC_MODIFY_REG32(&GET_CORE_IF(pcd)->dev_if->
+					 in_ep_regs[dwcep->num]->diepctl, 0,
+					 diepctl.d32);
+		} else {
+			/* Setup DMA Descriptor chain for OUT Isoc request */
+			for (i = 0; i < ereq->pio_pkt_count; i++) {
+				//if ((i % (nat + 1)) == 0)
+				dwcep->xiso_frame_num = (dwcep->xiso_bInterval +
+										dwcep->xiso_frame_num) & 0x3FFF;
+				dwcep->desc_addr[i].buf =
+				    req->dma + ddesc_iso[i].offset;
+				dwcep->desc_addr[i].status.b_iso_out.rxbytes =
+				    ddesc_iso[i].length;
+				dwcep->desc_addr[i].status.b_iso_out.framenum =
+				    dwcep->xiso_frame_num;
+				dwcep->desc_addr[i].status.b_iso_out.bs =
+				    BS_HOST_READY;
+				dwcep->desc_addr[i].status.b_iso_out.rxsts = 0;
+				dwcep->desc_addr[i].status.b_iso_out.sp =
+				    (ddesc_iso[i].length %
+				     dwcep->maxpacket) ? 1 : 0;
+				dwcep->desc_addr[i].status.b_iso_out.ioc = 0;
+				dwcep->desc_addr[i].status.b_iso_out.pid = nat + 1;
+				dwcep->desc_addr[i].status.b_iso_out.l = 0;
+
+				/* Process the last descriptor */
+				if (i == ereq->pio_pkt_count - 1) {
+					dwcep->desc_addr[i].status.b_iso_out.ioc = 1;
+					dwcep->desc_addr[i].status.b_iso_out.l = 1;
+				}
+			}
+
+			/* Setup and start the transfer for this endpoint */
+			dwcep->xiso_active_xfers++;
+			DWC_WRITE_REG32(&GET_CORE_IF(pcd)->
+					dev_if->out_ep_regs[dwcep->num]->
+					doepdma, dwcep->dma_desc_addr);
+			diepctl.d32 = 0;
+			diepctl.b.epena = 1;
+			diepctl.b.cnak = 1;
+			DWC_MODIFY_REG32(&GET_CORE_IF(pcd)->
+					 dev_if->out_ep_regs[dwcep->num]->
+					 doepctl, 0, diepctl.d32);
+		}
+
+	} else {
+		ep->stopped = 1;
+	}
+
+	return 0;
+}
+
+/**
+ *	- Remove the request from the queue
+ */
+void complete_xiso_ep(dwc_otg_pcd_ep_t * ep)
+{
+	dwc_otg_pcd_request_t *req = NULL;
+	struct dwc_iso_xreq_port *ereq = NULL;
+	struct dwc_iso_pkt_desc_port *ddesc_iso = NULL;
+	dwc_ep_t *dwcep = NULL;
+	int i;
+
+	//DWC_DEBUG();
+	dwcep = &ep->dwc_ep;
+
+	/* Get the first pending request from the queue */
+	if (!DWC_CIRCLEQ_EMPTY(&ep->queue)) {
+		req = DWC_CIRCLEQ_FIRST(&ep->queue);
+		if (!req) {
+			DWC_PRINTF("complete_ep 0x%p, req = NULL!\n", ep);
+			return;
+		}
+		dwcep->xiso_active_xfers--;
+		dwcep->xiso_queued_xfers--;
+		/* Remove this request from the queue */
+		DWC_CIRCLEQ_REMOVE_INIT(&ep->queue, req, queue_entry);
+	} else {
+		DWC_PRINTF("complete_ep 0x%p, ep->queue empty!\n", ep);
+		return;
+	}
+
+	ep->stopped = 1;
+	ereq = &req->ext_req;
+	ddesc_iso = ereq->per_io_frame_descs;
+
+	if (dwcep->xiso_active_xfers < 0) {
+		DWC_WARN("EP#%d (xiso_active_xfers=%d)", dwcep->num,
+			 dwcep->xiso_active_xfers);
+	}
+
+	/* Fill the Isoc descs of portable extended req from dma descriptors */
+	for (i = 0; i < ereq->pio_pkt_count; i++) {
+		if (dwcep->is_in) {	/* IN endpoints */
+			ddesc_iso[i].actual_length = ddesc_iso[i].length -
+			    dwcep->desc_addr[i].status.b_iso_in.txbytes;
+			ddesc_iso[i].status =
+			    dwcep->desc_addr[i].status.b_iso_in.txsts;
+		} else {	/* OUT endpoints */
+			ddesc_iso[i].actual_length = ddesc_iso[i].length -
+			    dwcep->desc_addr[i].status.b_iso_out.rxbytes;
+			ddesc_iso[i].status =
+			    dwcep->desc_addr[i].status.b_iso_out.rxsts;
+		}
+	}
+
+	DWC_SPINUNLOCK(ep->pcd->lock);
+
+	/* Call the completion function in the non-portable logic */
+	ep->pcd->fops->xisoc_complete(ep->pcd, ep->priv, req->priv, 0,
+				      &req->ext_req);
+
+	DWC_SPINLOCK(ep->pcd->lock);
+
+	/* Free the request - specific freeing needed for extended request object */
+	dwc_pcd_xiso_ereq_free(ep, req);
+
+	/* Start the next request */
+	dwc_otg_pcd_xiso_start_next_request(ep->pcd, ep);
+
+	return;
+}
+
+/**
+ * Create and initialize the Isoc pkt descriptors of the extended request.
+ *
+ */
+static int dwc_otg_pcd_xiso_create_pkt_descs(dwc_otg_pcd_request_t * req,
+					     void *ereq_nonport,
+					     int atomic_alloc)
+{
+	struct dwc_iso_xreq_port *ereq = NULL;
+	struct dwc_iso_xreq_port *req_mapped = NULL;
+	struct dwc_iso_pkt_desc_port *ipds = NULL;	/* To be created in this function */
+	uint32_t pkt_count;
+	int i;
+
+	ereq = &req->ext_req;
+	req_mapped = (struct dwc_iso_xreq_port *)ereq_nonport;
+	pkt_count = req_mapped->pio_pkt_count;
+
+	/* Create the isoc descs */
+	if (atomic_alloc) {
+		ipds = DWC_ALLOC_ATOMIC(sizeof(*ipds) * pkt_count);
+	} else {
+		ipds = DWC_ALLOC(sizeof(*ipds) * pkt_count);
+	}
+
+	if (!ipds) {
+		DWC_ERROR("Failed to allocate isoc descriptors");
+		return -DWC_E_NO_MEMORY;
+	}
+
+	/* Initialize the extended request fields */
+	ereq->per_io_frame_descs = ipds;
+	ereq->error_count = 0;
+	ereq->pio_alloc_pkt_count = pkt_count;
+	ereq->pio_pkt_count = pkt_count;
+	ereq->tr_sub_flags = req_mapped->tr_sub_flags;
+
+	/* Init the Isoc descriptors */
+	for (i = 0; i < pkt_count; i++) {
+		ipds[i].length = req_mapped->per_io_frame_descs[i].length;
+		ipds[i].offset = req_mapped->per_io_frame_descs[i].offset;
+		ipds[i].status = req_mapped->per_io_frame_descs[i].status;	/* 0 */
+		ipds[i].actual_length =
+		    req_mapped->per_io_frame_descs[i].actual_length;
+	}
+
+	return 0;
+}
+
+static void prn_ext_request(struct dwc_iso_xreq_port *ereq)
+{
+	struct dwc_iso_pkt_desc_port *xfd = NULL;
+	int i;
+
+	DWC_DEBUG("per_io_frame_descs=%p", ereq->per_io_frame_descs);
+	DWC_DEBUG("tr_sub_flags=%d", ereq->tr_sub_flags);
+	DWC_DEBUG("error_count=%d", ereq->error_count);
+	DWC_DEBUG("pio_alloc_pkt_count=%d", ereq->pio_alloc_pkt_count);
+	DWC_DEBUG("pio_pkt_count=%d", ereq->pio_pkt_count);
+	DWC_DEBUG("res=%d", ereq->res);
+
+	for (i = 0; i < ereq->pio_pkt_count; i++) {
+		xfd = &ereq->per_io_frame_descs[0];
+		DWC_DEBUG("FD #%d", i);
+
+		DWC_DEBUG("xfd->actual_length=%d", xfd->actual_length);
+		DWC_DEBUG("xfd->length=%d", xfd->length);
+		DWC_DEBUG("xfd->offset=%d", xfd->offset);
+		DWC_DEBUG("xfd->status=%d", xfd->status);
+	}
+}
+
+/**
+ *
+ */
+int dwc_otg_pcd_xiso_ep_queue(dwc_otg_pcd_t * pcd, void *ep_handle,
+			      uint8_t * buf, dwc_dma_t dma_buf, uint32_t buflen,
+			      int zero, void *req_handle, int atomic_alloc,
+			      void *ereq_nonport)
+{
+	dwc_otg_pcd_request_t *req = NULL;
+	dwc_otg_pcd_ep_t *ep;
+	dwc_irqflags_t flags;
+	int res;
+
+	ep = get_ep_from_handle(pcd, ep_handle);
+	if (!ep) {
+		DWC_WARN("bad ep\n");
+		return -DWC_E_INVALID;
+	}
+
+	/* We support this extension only for DDMA mode */
+	if (ep->dwc_ep.type == DWC_OTG_EP_TYPE_ISOC)
+		if (!GET_CORE_IF(pcd)->dma_desc_enable)
+			return -DWC_E_INVALID;
+
+	/* Create a dwc_otg_pcd_request_t object */
+	if (atomic_alloc) {
+		req = DWC_ALLOC_ATOMIC(sizeof(*req));
+	} else {
+		req = DWC_ALLOC(sizeof(*req));
+	}
+
+	if (!req) {
+		return -DWC_E_NO_MEMORY;
+	}
+
+	/* Create the Isoc descs for this request which shall be the exact match
+	 * of the structure sent to us from the non-portable logic */
+	res =
+	    dwc_otg_pcd_xiso_create_pkt_descs(req, ereq_nonport, atomic_alloc);
+	if (res) {
+		DWC_WARN("Failed to init the Isoc descriptors");
+		DWC_FREE(req);
+		return res;
+	}
+
+	DWC_SPINLOCK_IRQSAVE(pcd->lock, &flags);
+
+	DWC_CIRCLEQ_INIT_ENTRY(req, queue_entry);
+	req->buf = buf;
+	req->dma = dma_buf;
+	req->length = buflen;
+	req->sent_zlp = zero;
+	req->priv = req_handle;
+
+	//DWC_SPINLOCK_IRQSAVE(pcd->lock, &flags);
+	ep->dwc_ep.dma_addr = dma_buf;
+	ep->dwc_ep.start_xfer_buff = buf;
+	ep->dwc_ep.xfer_buff = buf;
+	ep->dwc_ep.xfer_len = 0;
+	ep->dwc_ep.xfer_count = 0;
+	ep->dwc_ep.sent_zlp = 0;
+	ep->dwc_ep.total_len = buflen;
+
+	/* Add this request to the tail */
+	DWC_CIRCLEQ_INSERT_TAIL(&ep->queue, req, queue_entry);
+	ep->dwc_ep.xiso_queued_xfers++;
+
+//DWC_DEBUG("CP_0");
+//DWC_DEBUG("req->ext_req.tr_sub_flags=%d", req->ext_req.tr_sub_flags);
+//prn_ext_request((struct dwc_iso_xreq_port *) ereq_nonport);
+//prn_ext_request(&req->ext_req);
+
+	//DWC_SPINUNLOCK_IRQRESTORE(pcd->lock, flags);
+
+	/* If the req->status == ASAP  then check if there is any active transfer
+	 * for this endpoint. If no active transfers, then get the first entry
+	 * from the queue and start that transfer
+	 */
+	if (req->ext_req.tr_sub_flags == DWC_EREQ_TF_ASAP) {
+		res = dwc_otg_pcd_xiso_start_next_request(pcd, ep);
+		if (res) {
+			DWC_WARN("Failed to start the next Isoc transfer");
+			DWC_SPINUNLOCK_IRQRESTORE(pcd->lock, flags);
+			DWC_FREE(req);
+			return res;
+		}
+	}
+
+	DWC_SPINUNLOCK_IRQRESTORE(pcd->lock, flags);
+	return 0;
+}
+
+#endif
+/* END ifdef DWC_UTE_PER_IO ***************************************************/
+int dwc_otg_pcd_ep_queue(dwc_otg_pcd_t * pcd, void *ep_handle,
+			 uint8_t * buf, dwc_dma_t dma_buf, uint32_t buflen,
+			 int zero, void *req_handle, int atomic_alloc)
+{
+	struct device *dev = dwc_otg_pcd_to_dev(pcd);
+	dwc_irqflags_t flags;
+	dwc_otg_pcd_request_t *req;
+	dwc_otg_pcd_ep_t *ep;
+	uint32_t max_transfer;
+
+	ep = get_ep_from_handle(pcd, ep_handle);
+	if (!ep || (!ep->desc && ep->dwc_ep.num != 0)) {
+		DWC_WARN("bad ep\n");
+		return -DWC_E_INVALID;
+	}
+
+	if (atomic_alloc) {
+		req = DWC_ALLOC_ATOMIC(sizeof(*req));
+	} else {
+		req = DWC_ALLOC(sizeof(*req));
+	}
+
+	if (!req) {
+		return -DWC_E_NO_MEMORY;
+	}
+	DWC_CIRCLEQ_INIT_ENTRY(req, queue_entry);
+	if (!GET_CORE_IF(pcd)->core_params->opt) {
+		if (ep->dwc_ep.num != 0) {
+			DWC_ERROR("queue req %p, len %d buf %p\n",
+				  req_handle, buflen, buf);
+		}
+	}
+
+	req->buf = buf;
+	req->dma = dma_buf;
+	req->length = buflen;
+	req->sent_zlp = zero;
+	req->priv = req_handle;
+	req->dw_align_buf = NULL;
+	if ((dma_buf & 0x3) && GET_CORE_IF(pcd)->dma_enable
+			&& !GET_CORE_IF(pcd)->dma_desc_enable)
+		req->dw_align_buf = DWC_DMA_ALLOC(dev, buflen,
+				 &req->dw_align_buf_dma);
+	DWC_SPINLOCK_IRQSAVE(pcd->lock, &flags);
+
+	/*
+	 * After adding request to the queue for IN ISOC wait for In Token Received
+	 * when TX FIFO is empty interrupt and for OUT ISOC wait for OUT Token
+	 * Received when EP is disabled interrupt to obtain starting microframe
+	 * (odd/even) start transfer
+	 */
+	if (ep->dwc_ep.type == DWC_OTG_EP_TYPE_ISOC) {
+		if (req != 0) {
+			depctl_data_t depctl = {.d32 =
+				    DWC_READ_REG32(&pcd->core_if->dev_if->
+						   in_ep_regs[ep->dwc_ep.num]->
+						   diepctl) };
+			++pcd->request_pending;
+
+			DWC_CIRCLEQ_INSERT_TAIL(&ep->queue, req, queue_entry);
+			if (ep->dwc_ep.is_in) {
+				depctl.b.cnak = 1;
+				DWC_WRITE_REG32(&pcd->core_if->dev_if->
+						in_ep_regs[ep->dwc_ep.num]->
+						diepctl, depctl.d32);
+			}
+
+			DWC_SPINUNLOCK_IRQRESTORE(pcd->lock, flags);
+		}
+		return 0;
+	}
+
+	/*
+	 * For EP0 IN without premature status, zlp is required?
+	 */
+	if (ep->dwc_ep.num == 0 && ep->dwc_ep.is_in) {
+		DWC_DEBUGPL(DBG_PCDV, "%d-OUT ZLP\n", ep->dwc_ep.num);
+		//_req->zero = 1;
+	}
+
+	/* Start the transfer */
+	if (DWC_CIRCLEQ_EMPTY(&ep->queue) && !ep->stopped) {
+		/* EP0 Transfer? */
+		if (ep->dwc_ep.num == 0) {
+			switch (pcd->ep0state) {
+			case EP0_IN_DATA_PHASE:
+				DWC_DEBUGPL(DBG_PCD,
+					    "%s ep0: EP0_IN_DATA_PHASE\n",
+					    __func__);
+				break;
+
+			case EP0_OUT_DATA_PHASE:
+				DWC_DEBUGPL(DBG_PCD,
+					    "%s ep0: EP0_OUT_DATA_PHASE\n",
+					    __func__);
+				if (pcd->request_config) {
+					/* Complete STATUS PHASE */
+					ep->dwc_ep.is_in = 1;
+					pcd->ep0state = EP0_IN_STATUS_PHASE;
+				}
+				break;
+
+			case EP0_IN_STATUS_PHASE:
+				DWC_DEBUGPL(DBG_PCD,
+					    "%s ep0: EP0_IN_STATUS_PHASE\n",
+					    __func__);
+				break;
+
+			default:
+				DWC_DEBUGPL(DBG_ANY, "ep0: odd state %d\n",
+					    pcd->ep0state);
+				DWC_SPINUNLOCK_IRQRESTORE(pcd->lock, flags);
+				return -DWC_E_SHUTDOWN;
+			}
+
+			ep->dwc_ep.dma_addr = dma_buf;
+			ep->dwc_ep.start_xfer_buff = buf;
+			ep->dwc_ep.xfer_buff = buf;
+			ep->dwc_ep.xfer_len = buflen;
+			ep->dwc_ep.xfer_count = 0;
+			ep->dwc_ep.sent_zlp = 0;
+			ep->dwc_ep.total_len = ep->dwc_ep.xfer_len;
+
+			if (zero) {
+				if ((ep->dwc_ep.xfer_len %
+				     ep->dwc_ep.maxpacket == 0)
+				    && (ep->dwc_ep.xfer_len != 0)) {
+					ep->dwc_ep.sent_zlp = 1;
+				}
+
+			}
+
+			dwc_otg_ep0_start_transfer(GET_CORE_IF(pcd),
+						   &ep->dwc_ep);
+		}		// non-ep0 endpoints
+		else {
+#ifdef DWC_UTE_CFI
+			if (ep->dwc_ep.buff_mode != BM_STANDARD) {
+				/* store the request length */
+				ep->dwc_ep.cfi_req_len = buflen;
+				pcd->cfi->ops.build_descriptors(pcd->cfi, pcd,
+								ep, req);
+			} else {
+#endif
+				max_transfer =
+				    GET_CORE_IF(ep->pcd)->core_params->
+				    max_transfer_size;
+
+				/* Setup and start the Transfer */
+				if (req->dw_align_buf){
+					if (ep->dwc_ep.is_in)
+						dwc_memcpy(req->dw_align_buf,
+							   buf, buflen);
+					ep->dwc_ep.dma_addr =
+					    req->dw_align_buf_dma;
+					ep->dwc_ep.start_xfer_buff =
+					    req->dw_align_buf;
+					ep->dwc_ep.xfer_buff =
+					    req->dw_align_buf;
+				} else {
+					ep->dwc_ep.dma_addr = dma_buf;
+					ep->dwc_ep.start_xfer_buff = buf;
+                                        ep->dwc_ep.xfer_buff = buf;
+				}
+				ep->dwc_ep.xfer_len = 0;
+				ep->dwc_ep.xfer_count = 0;
+				ep->dwc_ep.sent_zlp = 0;
+				ep->dwc_ep.total_len = buflen;
+
+				ep->dwc_ep.maxxfer = max_transfer;
+				if (GET_CORE_IF(pcd)->dma_desc_enable) {
+					uint32_t out_max_xfer =
+					    DDMA_MAX_TRANSFER_SIZE -
+					    (DDMA_MAX_TRANSFER_SIZE % 4);
+					if (ep->dwc_ep.is_in) {
+						if (ep->dwc_ep.maxxfer >
+						    DDMA_MAX_TRANSFER_SIZE) {
+							ep->dwc_ep.maxxfer =
+							    DDMA_MAX_TRANSFER_SIZE;
+						}
+					} else {
+						if (ep->dwc_ep.maxxfer >
+						    out_max_xfer) {
+							ep->dwc_ep.maxxfer =
+							    out_max_xfer;
+						}
+					}
+				}
+				if (ep->dwc_ep.maxxfer < ep->dwc_ep.total_len) {
+					ep->dwc_ep.maxxfer -=
+					    (ep->dwc_ep.maxxfer %
+					     ep->dwc_ep.maxpacket);
+				}
+
+				if (zero) {
+					if ((ep->dwc_ep.total_len %
+					     ep->dwc_ep.maxpacket == 0)
+					    && (ep->dwc_ep.total_len != 0)) {
+						ep->dwc_ep.sent_zlp = 1;
+					}
+				}
+#ifdef DWC_UTE_CFI
+			}
+#endif
+			dwc_otg_ep_start_transfer(GET_CORE_IF(pcd),
+						  &ep->dwc_ep);
+		}
+	}
+
+	if (req != 0) {
+		++pcd->request_pending;
+		DWC_CIRCLEQ_INSERT_TAIL(&ep->queue, req, queue_entry);
+		if (ep->dwc_ep.is_in && ep->stopped
+		    && !(GET_CORE_IF(pcd)->dma_enable)) {
+			/** @todo NGS Create a function for this. */
+			diepmsk_data_t diepmsk = {.d32 = 0 };
+			diepmsk.b.intktxfemp = 1;
+			if (GET_CORE_IF(pcd)->multiproc_int_enable) {
+				DWC_MODIFY_REG32(&GET_CORE_IF(pcd)->
+						 dev_if->dev_global_regs->diepeachintmsk
+						 [ep->dwc_ep.num], 0,
+						 diepmsk.d32);
+			} else {
+				DWC_MODIFY_REG32(&GET_CORE_IF(pcd)->
+						 dev_if->dev_global_regs->
+						 diepmsk, 0, diepmsk.d32);
+			}
+
+		}
+	}
+	DWC_SPINUNLOCK_IRQRESTORE(pcd->lock, flags);
+
+	return 0;
+}
+
+int dwc_otg_pcd_ep_dequeue(dwc_otg_pcd_t * pcd, void *ep_handle,
+			   void *req_handle)
+{
+	dwc_irqflags_t flags;
+	dwc_otg_pcd_request_t *req;
+	dwc_otg_pcd_ep_t *ep;
+
+	ep = get_ep_from_handle(pcd, ep_handle);
+	if (!ep || (!ep->desc && ep->dwc_ep.num != 0)) {
+		DWC_WARN("bad argument\n");
+		return -DWC_E_INVALID;
+	}
+
+	DWC_SPINLOCK_IRQSAVE(pcd->lock, &flags);
+
+	/* make sure it's actually queued on this endpoint */
+	DWC_CIRCLEQ_FOREACH(req, &ep->queue, queue_entry) {
+		if (req->priv == (void *)req_handle) {
+			break;
+		}
+	}
+
+	if (req->priv != (void *)req_handle) {
+		DWC_SPINUNLOCK_IRQRESTORE(pcd->lock, flags);
+		return -DWC_E_INVALID;
+	}
+
+	if (!DWC_CIRCLEQ_EMPTY_ENTRY(req, queue_entry)) {
+		dwc_otg_request_done(ep, req, -DWC_E_RESTART);
+	} else {
+		req = NULL;
+	}
+
+	DWC_SPINUNLOCK_IRQRESTORE(pcd->lock, flags);
+
+	return req ? 0 : -DWC_E_SHUTDOWN;
+
+}
+
+/**
+ * dwc_otg_pcd_ep_wedge - sets the halt feature and ignores clear requests
+ *
+ * Use this to stall an endpoint and ignore CLEAR_FEATURE(HALT_ENDPOINT)
+ * requests. If the gadget driver clears the halt status, it will
+ * automatically unwedge the endpoint.
+ *
+ * Returns zero on success, else negative DWC error code.
+ */
+int dwc_otg_pcd_ep_wedge(dwc_otg_pcd_t * pcd, void *ep_handle)
+{
+	dwc_otg_pcd_ep_t *ep;
+	dwc_irqflags_t flags;
+	int retval = 0;
+
+	ep = get_ep_from_handle(pcd, ep_handle);
+
+	if ((!ep->desc && ep != &pcd->ep0) ||
+	    (ep->desc && (ep->desc->bmAttributes == UE_ISOCHRONOUS))) {
+		DWC_WARN("%s, bad ep\n", __func__);
+		return -DWC_E_INVALID;
+	}
+
+	DWC_SPINLOCK_IRQSAVE(pcd->lock, &flags);
+	if (!DWC_CIRCLEQ_EMPTY(&ep->queue)) {
+		DWC_WARN("%d %s XFer In process\n", ep->dwc_ep.num,
+			 ep->dwc_ep.is_in ? "IN" : "OUT");
+		retval = -DWC_E_AGAIN;
+	} else {
+                /* This code needs to be reviewed */
+		if (ep->dwc_ep.is_in == 1 && GET_CORE_IF(pcd)->dma_desc_enable) {
+			dtxfsts_data_t txstatus;
+			fifosize_data_t txfifosize;
+
+			txfifosize.d32 =
+			    DWC_READ_REG32(&GET_CORE_IF(pcd)->
+					   core_global_regs->dtxfsiz[ep->dwc_ep.
+								     tx_fifo_num]);
+			txstatus.d32 =
+			    DWC_READ_REG32(&GET_CORE_IF(pcd)->
+					   dev_if->in_ep_regs[ep->dwc_ep.num]->
+					   dtxfsts);
+
+			if (txstatus.b.txfspcavail < txfifosize.b.depth) {
+				DWC_WARN("%s() Data In Tx Fifo\n", __func__);
+				retval = -DWC_E_AGAIN;
+			} else {
+				if (ep->dwc_ep.num == 0) {
+					pcd->ep0state = EP0_STALL;
+				}
+
+				ep->stopped = 1;
+				dwc_otg_ep_set_stall(GET_CORE_IF(pcd),
+						     &ep->dwc_ep);
+			}
+		} else {
+			if (ep->dwc_ep.num == 0) {
+				pcd->ep0state = EP0_STALL;
+			}
+
+			ep->stopped = 1;
+			dwc_otg_ep_set_stall(GET_CORE_IF(pcd), &ep->dwc_ep);
+		}
+	}
+
+	DWC_SPINUNLOCK_IRQRESTORE(pcd->lock, flags);
+
+	return retval;
+}
+
+int dwc_otg_pcd_ep_halt(dwc_otg_pcd_t * pcd, void *ep_handle, int value)
+{
+	dwc_otg_pcd_ep_t *ep;
+	dwc_irqflags_t flags;
+	int retval = 0;
+
+	ep = get_ep_from_handle(pcd, ep_handle);
+
+	if (!ep || (!ep->desc && ep != &pcd->ep0) ||
+	    (ep->desc && (ep->desc->bmAttributes == UE_ISOCHRONOUS))) {
+		DWC_WARN("%s, bad ep\n", __func__);
+		return -DWC_E_INVALID;
+	}
+
+	DWC_SPINLOCK_IRQSAVE(pcd->lock, &flags);
+	if (!DWC_CIRCLEQ_EMPTY(&ep->queue)) {
+		DWC_WARN("%d %s XFer In process\n", ep->dwc_ep.num,
+			 ep->dwc_ep.is_in ? "IN" : "OUT");
+		retval = -DWC_E_AGAIN;
+	} else if (value == 0) {
+		dwc_otg_ep_clear_stall(GET_CORE_IF(pcd), &ep->dwc_ep);
+	} else if (value == 1) {
+		if (ep->dwc_ep.is_in == 1 && GET_CORE_IF(pcd)->dma_desc_enable) {
+			dtxfsts_data_t txstatus;
+			fifosize_data_t txfifosize;
+
+			txfifosize.d32 =
+			    DWC_READ_REG32(&GET_CORE_IF(pcd)->core_global_regs->
+					   dtxfsiz[ep->dwc_ep.tx_fifo_num]);
+			txstatus.d32 =
+			    DWC_READ_REG32(&GET_CORE_IF(pcd)->dev_if->
+					   in_ep_regs[ep->dwc_ep.num]->dtxfsts);
+
+			if (txstatus.b.txfspcavail < txfifosize.b.depth) {
+				DWC_WARN("%s() Data In Tx Fifo\n", __func__);
+				retval = -DWC_E_AGAIN;
+			} else {
+				if (ep->dwc_ep.num == 0) {
+					pcd->ep0state = EP0_STALL;
+				}
+
+				ep->stopped = 1;
+				dwc_otg_ep_set_stall(GET_CORE_IF(pcd),
+						     &ep->dwc_ep);
+			}
+		} else {
+			if (ep->dwc_ep.num == 0) {
+				pcd->ep0state = EP0_STALL;
+			}
+
+			ep->stopped = 1;
+			dwc_otg_ep_set_stall(GET_CORE_IF(pcd), &ep->dwc_ep);
+		}
+	} else if (value == 2) {
+		ep->dwc_ep.stall_clear_flag = 0;
+	} else if (value == 3) {
+		ep->dwc_ep.stall_clear_flag = 1;
+	}
+
+	DWC_SPINUNLOCK_IRQRESTORE(pcd->lock, flags);
+
+	return retval;
+}
+
+/**
+ * This function initiates remote wakeup of the host from suspend state.
+ */
+void dwc_otg_pcd_rem_wkup_from_suspend(dwc_otg_pcd_t * pcd, int set)
+{
+	dctl_data_t dctl = { 0 };
+	dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd);
+	dsts_data_t dsts;
+
+	dsts.d32 = DWC_READ_REG32(&core_if->dev_if->dev_global_regs->dsts);
+	if (!dsts.b.suspsts) {
+		DWC_WARN("Remote wakeup while is not in suspend state\n");
+	}
+	/* Check if DEVICE_REMOTE_WAKEUP feature enabled */
+	if (pcd->remote_wakeup_enable) {
+		if (set) {
+
+			if (core_if->adp_enable) {
+				gpwrdn_data_t gpwrdn;
+
+				dwc_otg_adp_probe_stop(core_if);
+
+				/* Mask SRP detected interrupt from Power Down Logic */
+				gpwrdn.d32 = 0;
+				gpwrdn.b.srp_det_msk = 1;
+				DWC_MODIFY_REG32(&core_if->
+						 core_global_regs->gpwrdn,
+						 gpwrdn.d32, 0);
+
+				/* Disable Power Down Logic */
+				gpwrdn.d32 = 0;
+				gpwrdn.b.pmuactv = 1;
+				DWC_MODIFY_REG32(&core_if->
+						 core_global_regs->gpwrdn,
+						 gpwrdn.d32, 0);
+
+				/*
+				 * Initialize the Core for Device mode.
+				 */
+				core_if->op_state = B_PERIPHERAL;
+				dwc_otg_core_init(core_if);
+				dwc_otg_enable_global_interrupts(core_if);
+				cil_pcd_start(core_if);
+
+				dwc_otg_initiate_srp(core_if);
+			}
+
+			dctl.b.rmtwkupsig = 1;
+			DWC_MODIFY_REG32(&core_if->dev_if->dev_global_regs->
+					 dctl, 0, dctl.d32);
+			DWC_DEBUGPL(DBG_PCD, "Set Remote Wakeup\n");
+
+			dwc_mdelay(2);
+			DWC_MODIFY_REG32(&core_if->dev_if->dev_global_regs->
+					 dctl, dctl.d32, 0);
+			DWC_DEBUGPL(DBG_PCD, "Clear Remote Wakeup\n");
+		}
+	} else {
+		DWC_DEBUGPL(DBG_PCD, "Remote Wakeup is disabled\n");
+	}
+}
+
+#ifdef CONFIG_USB_DWC_OTG_LPM
+/**
+ * This function initiates remote wakeup of the host from L1 sleep state.
+ */
+void dwc_otg_pcd_rem_wkup_from_sleep(dwc_otg_pcd_t * pcd, int set)
+{
+	glpmcfg_data_t lpmcfg;
+	dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd);
+
+	lpmcfg.d32 = DWC_READ_REG32(&core_if->core_global_regs->glpmcfg);
+
+	/* Check if we are in L1 state */
+	if (!lpmcfg.b.prt_sleep_sts) {
+		DWC_DEBUGPL(DBG_PCD, "Device is not in sleep state\n");
+		return;
+	}
+
+	/* Check if host allows remote wakeup */
+	if (!lpmcfg.b.rem_wkup_en) {
+		DWC_DEBUGPL(DBG_PCD, "Host does not allow remote wakeup\n");
+		return;
+	}
+
+	/* Check if Resume OK */
+	if (!lpmcfg.b.sleep_state_resumeok) {
+		DWC_DEBUGPL(DBG_PCD, "Sleep state resume is not OK\n");
+		return;
+	}
+
+	lpmcfg.d32 = DWC_READ_REG32(&core_if->core_global_regs->glpmcfg);
+	lpmcfg.b.en_utmi_sleep = 0;
+	lpmcfg.b.hird_thres &= (~(1 << 4));
+	DWC_WRITE_REG32(&core_if->core_global_regs->glpmcfg, lpmcfg.d32);
+
+	if (set) {
+		dctl_data_t dctl = {.d32 = 0 };
+		dctl.b.rmtwkupsig = 1;
+		/* Set RmtWkUpSig bit to start remote wakup signaling.
+		 * Hardware will automatically clear this bit.
+		 */
+		DWC_MODIFY_REG32(&core_if->dev_if->dev_global_regs->dctl,
+				 0, dctl.d32);
+		DWC_DEBUGPL(DBG_PCD, "Set Remote Wakeup\n");
+	}
+
+}
+#endif
+
+/**
+ * Performs remote wakeup.
+ */
+void dwc_otg_pcd_remote_wakeup(dwc_otg_pcd_t * pcd, int set)
+{
+	dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd);
+	dwc_irqflags_t flags;
+	if (dwc_otg_is_device_mode(core_if)) {
+		DWC_SPINLOCK_IRQSAVE(pcd->lock, &flags);
+#ifdef CONFIG_USB_DWC_OTG_LPM
+		if (core_if->lx_state == DWC_OTG_L1) {
+			dwc_otg_pcd_rem_wkup_from_sleep(pcd, set);
+		} else {
+#endif
+			dwc_otg_pcd_rem_wkup_from_suspend(pcd, set);
+#ifdef CONFIG_USB_DWC_OTG_LPM
+		}
+#endif
+		DWC_SPINUNLOCK_IRQRESTORE(pcd->lock, flags);
+	}
+	return;
+}
+
+void dwc_otg_pcd_disconnect_us(dwc_otg_pcd_t * pcd, int no_of_usecs)
+{
+	dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd);
+	dctl_data_t dctl = { 0 };
+
+	if (dwc_otg_is_device_mode(core_if)) {
+		dctl.b.sftdiscon = 1;
+		DWC_PRINTF("Soft disconnect for %d useconds\n",no_of_usecs);
+		DWC_MODIFY_REG32(&core_if->dev_if->dev_global_regs->dctl, 0, dctl.d32);
+		dwc_udelay(no_of_usecs);
+		DWC_MODIFY_REG32(&core_if->dev_if->dev_global_regs->dctl, dctl.d32,0);
+
+	} else{
+		DWC_PRINTF("NOT SUPPORTED IN HOST MODE\n");
+	}
+	return;
+
+}
+
+int dwc_otg_pcd_wakeup(dwc_otg_pcd_t * pcd)
+{
+	dsts_data_t dsts;
+	gotgctl_data_t gotgctl;
+
+	/*
+	 * This function starts the Protocol if no session is in progress. If
+	 * a session is already in progress, but the device is suspended,
+	 * remote wakeup signaling is started.
+	 */
+
+	/* Check if valid session */
+	gotgctl.d32 =
+	    DWC_READ_REG32(&(GET_CORE_IF(pcd)->core_global_regs->gotgctl));
+	if (gotgctl.b.bsesvld) {
+		/* Check if suspend state */
+		dsts.d32 =
+		    DWC_READ_REG32(&
+				   (GET_CORE_IF(pcd)->dev_if->
+				    dev_global_regs->dsts));
+		if (dsts.b.suspsts) {
+			dwc_otg_pcd_remote_wakeup(pcd, 1);
+		}
+	} else {
+		dwc_otg_pcd_initiate_srp(pcd);
+	}
+
+	return 0;
+
+}
+
+/**
+ * Start the SRP timer to detect when the SRP does not complete within
+ * 6 seconds.
+ *
+ * @param pcd the pcd structure.
+ */
+void dwc_otg_pcd_initiate_srp(dwc_otg_pcd_t * pcd)
+{
+	dwc_irqflags_t flags;
+	DWC_SPINLOCK_IRQSAVE(pcd->lock, &flags);
+	dwc_otg_initiate_srp(GET_CORE_IF(pcd));
+	DWC_SPINUNLOCK_IRQRESTORE(pcd->lock, flags);
+}
+
+int dwc_otg_pcd_get_frame_number(dwc_otg_pcd_t * pcd)
+{
+	return dwc_otg_get_frame_number(GET_CORE_IF(pcd));
+}
+
+int dwc_otg_pcd_is_lpm_enabled(dwc_otg_pcd_t * pcd)
+{
+	return GET_CORE_IF(pcd)->core_params->lpm_enable;
+}
+
+uint32_t get_b_hnp_enable(dwc_otg_pcd_t * pcd)
+{
+	return pcd->b_hnp_enable;
+}
+
+uint32_t get_a_hnp_support(dwc_otg_pcd_t * pcd)
+{
+	return pcd->a_hnp_support;
+}
+
+uint32_t get_a_alt_hnp_support(dwc_otg_pcd_t * pcd)
+{
+	return pcd->a_alt_hnp_support;
+}
+
+int dwc_otg_pcd_get_rmwkup_enable(dwc_otg_pcd_t * pcd)
+{
+	return pcd->remote_wakeup_enable;
+}
+
+#endif /* DWC_HOST_ONLY */
diff -Nur --no-dereference linux-5.10.52-orig/drivers/usb/host/dwc_otg/dwc_otg_pcd.h linux-5.10.52-v7l+/drivers/usb/host/dwc_otg/dwc_otg_pcd.h
--- linux-5.10.52-orig/drivers/usb/host/dwc_otg/dwc_otg_pcd.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/usb/host/dwc_otg/dwc_otg_pcd.h	2021-07-25 16:46:28.697909777 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/* ==========================================================================
+ * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_pcd.h $
+ * $Revision: #48 $
+ * $Date: 2012/08/10 $
+ * $Change: 2047372 $
+ *
+ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter,
+ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless
+ * otherwise expressly agreed to in writing between Synopsys and you.
+ *
+ * The Software IS NOT an item of Licensed Software or Licensed Product under
+ * any End User Software License Agreement or Agreement for Licensed Product
+ * with Synopsys or any supplement thereto. You are permitted to use and
+ * redistribute this Software in source and binary forms, with or without
+ * modification, provided that redistributions of source code must retain this
+ * notice. You may not view, use, disclose, copy or distribute this file or
+ * any information contained herein except pursuant to this license grant from
+ * Synopsys. If you do not agree with this notice, including the disclaimer
+ * below, then you are not authorized to use the Software.
+ *
+ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * ========================================================================== */
+#ifndef DWC_HOST_ONLY
+#if !defined(__DWC_PCD_H__)
+#define __DWC_PCD_H__
+
+#include "dwc_otg_os_dep.h"
+#include "usb.h"
+#include "dwc_otg_cil.h"
+#include "dwc_otg_pcd_if.h"
+#include "dwc_otg_driver.h"
+
+struct cfiobject;
+
+/**
+ * @file
+ *
+ * This file contains the structures, constants, and interfaces for
+ * the Perpherial Contoller Driver (PCD).
+ *
+ * The Peripheral Controller Driver (PCD) for Linux will implement the
+ * Gadget API, so that the existing Gadget drivers can be used. For
+ * the Mass Storage Function driver the File-backed USB Storage Gadget
+ * (FBS) driver will be used.  The FBS driver supports the
+ * Control-Bulk (CB), Control-Bulk-Interrupt (CBI), and Bulk-Only
+ * transports.
+ *
+ */
+
+/** Invalid DMA Address */
+#define DWC_DMA_ADDR_INVALID	(~(dwc_dma_t)0)
+
+/** Max Transfer size for any EP */
+#define DDMA_MAX_TRANSFER_SIZE 65535
+
+/**
+ * Get the pointer to the core_if from the pcd pointer.
+ */
+#define GET_CORE_IF( _pcd ) (_pcd->core_if)
+
+/**
+ * States of EP0.
+ */
+typedef enum ep0_state {
+	EP0_DISCONNECT,		/* no host */
+	EP0_IDLE,
+	EP0_IN_DATA_PHASE,
+	EP0_OUT_DATA_PHASE,
+	EP0_IN_STATUS_PHASE,
+	EP0_OUT_STATUS_PHASE,
+	EP0_STALL,
+} ep0state_e;
+
+/** Fordward declaration.*/
+struct dwc_otg_pcd;
+
+/** DWC_otg iso request structure.
+ *
+ */
+typedef struct usb_iso_request dwc_otg_pcd_iso_request_t;
+
+#ifdef DWC_UTE_PER_IO
+
+/**
+ * This shall be the exact analogy of the same type structure defined in the
+ * usb_gadget.h. Each descriptor contains
+ */
+struct dwc_iso_pkt_desc_port {
+	uint32_t offset;
+	uint32_t length;	/* expected length */
+	uint32_t actual_length;
+	uint32_t status;
+};
+
+struct dwc_iso_xreq_port {
+	/** transfer/submission flag */
+	uint32_t tr_sub_flags;
+	/** Start the request ASAP */
+#define DWC_EREQ_TF_ASAP		0x00000002
+	/** Just enqueue the request w/o initiating a transfer */
+#define DWC_EREQ_TF_ENQUEUE		0x00000004
+
+	/**
+	* count of ISO packets attached to this request - shall
+	* not exceed the pio_alloc_pkt_count
+	*/
+	uint32_t pio_pkt_count;
+	/** count of ISO packets allocated for this request */
+	uint32_t pio_alloc_pkt_count;
+	/** number of ISO packet errors */
+	uint32_t error_count;
+	/** reserved for future extension */
+	uint32_t res;
+	/** Will be allocated and freed in the UTE gadget and based on the CFC value */
+	struct dwc_iso_pkt_desc_port *per_io_frame_descs;
+};
+#endif
+/** DWC_otg request structure.
+ * This structure is a list of requests.
+ */
+typedef struct dwc_otg_pcd_request {
+	void *priv;
+	void *buf;
+	dwc_dma_t dma;
+	uint32_t length;
+	uint32_t actual;
+	unsigned sent_zlp:1;
+    /**
+     * Used instead of original buffer if
+     * it(physical address) is not dword-aligned.
+     **/
+     uint8_t *dw_align_buf;
+     dwc_dma_t dw_align_buf_dma;
+
+	 DWC_CIRCLEQ_ENTRY(dwc_otg_pcd_request) queue_entry;
+#ifdef DWC_UTE_PER_IO
+	struct dwc_iso_xreq_port ext_req;
+	//void *priv_ereq_nport; /*  */
+#endif
+} dwc_otg_pcd_request_t;
+
+DWC_CIRCLEQ_HEAD(req_list, dwc_otg_pcd_request);
+
+/**	  PCD EP structure.
+ * This structure describes an EP, there is an array of EPs in the PCD
+ * structure.
+ */
+typedef struct dwc_otg_pcd_ep {
+	/** USB EP Descriptor */
+	const usb_endpoint_descriptor_t *desc;
+
+	/** queue of dwc_otg_pcd_requests. */
+	struct req_list queue;
+	unsigned stopped:1;
+	unsigned disabling:1;
+	unsigned dma:1;
+	unsigned queue_sof:1;
+
+#ifdef DWC_EN_ISOC
+	/** ISOC req handle passed */
+	void *iso_req_handle;
+#endif				//_EN_ISOC_
+
+	/** DWC_otg ep data. */
+	dwc_ep_t dwc_ep;
+
+	/** Pointer to PCD */
+	struct dwc_otg_pcd *pcd;
+
+	void *priv;
+} dwc_otg_pcd_ep_t;
+
+/** DWC_otg PCD Structure.
+ * This structure encapsulates the data for the dwc_otg PCD.
+ */
+struct dwc_otg_pcd {
+	const struct dwc_otg_pcd_function_ops *fops;
+	/** The DWC otg device pointer */
+	struct dwc_otg_device *otg_dev;
+	/** Core Interface */
+	dwc_otg_core_if_t *core_if;
+	/** State of EP0 */
+	ep0state_e ep0state;
+	/** EP0 Request is pending */
+	unsigned ep0_pending:1;
+	/** Indicates when SET CONFIGURATION Request is in process */
+	unsigned request_config:1;
+	/** The state of the Remote Wakeup Enable. */
+	unsigned remote_wakeup_enable:1;
+	/** The state of the B-Device HNP Enable. */
+	unsigned b_hnp_enable:1;
+	/** The state of A-Device HNP Support. */
+	unsigned a_hnp_support:1;
+	/** The state of the A-Device Alt HNP support. */
+	unsigned a_alt_hnp_support:1;
+	/** Count of pending Requests */
+	unsigned request_pending;
+
+	/** SETUP packet for EP0
+	 * This structure is allocated as a DMA buffer on PCD initialization
+	 * with enough space for up to 3 setup packets.
+	 */
+	union {
+		usb_device_request_t req;
+		uint32_t d32[2];
+	} *setup_pkt;
+
+	dwc_dma_t setup_pkt_dma_handle;
+
+	/* Additional buffer and flag for CTRL_WR premature case */
+	uint8_t *backup_buf;
+	unsigned data_terminated;
+
+	/** 2-byte dma buffer used to return status from GET_STATUS */
+	uint16_t *status_buf;
+	dwc_dma_t status_buf_dma_handle;
+
+	/** EP0 */
+	dwc_otg_pcd_ep_t ep0;
+
+	/** Array of IN EPs. */
+	dwc_otg_pcd_ep_t in_ep[MAX_EPS_CHANNELS - 1];
+	/** Array of OUT EPs. */
+	dwc_otg_pcd_ep_t out_ep[MAX_EPS_CHANNELS - 1];
+	/** number of valid EPs in the above array. */
+//        unsigned      num_eps : 4;
+	dwc_spinlock_t *lock;
+
+	/** Tasklet to defer starting of TEST mode transmissions until
+	 *	Status Phase has been completed.
+	 */
+	dwc_tasklet_t *test_mode_tasklet;
+
+	/** Tasklet to delay starting of xfer in DMA mode */
+	dwc_tasklet_t *start_xfer_tasklet;
+
+	/** The test mode to enter when the tasklet is executed. */
+	unsigned test_mode;
+	/** The cfi_api structure that implements most of the CFI API
+	 * and OTG specific core configuration functionality
+	 */
+#ifdef DWC_UTE_CFI
+	struct cfiobject *cfi;
+#endif
+
+};
+
+static inline struct device *dwc_otg_pcd_to_dev(struct dwc_otg_pcd *pcd)
+{
+	return &pcd->otg_dev->os_dep.platformdev->dev;
+}
+
+//FIXME this functions should be static, and this prototypes should be removed
+extern void dwc_otg_request_nuke(dwc_otg_pcd_ep_t * ep);
+extern void dwc_otg_request_done(dwc_otg_pcd_ep_t * ep,
+				 dwc_otg_pcd_request_t * req, int32_t status);
+
+void dwc_otg_iso_buffer_done(dwc_otg_pcd_t * pcd, dwc_otg_pcd_ep_t * ep,
+			     void *req_handle);
+
+extern void do_test_mode(void *data);
+#endif
+#endif /* DWC_HOST_ONLY */
diff -Nur --no-dereference linux-5.10.52-orig/drivers/usb/host/dwc_otg/dwc_otg_pcd_if.h linux-5.10.52-v7l+/drivers/usb/host/dwc_otg/dwc_otg_pcd_if.h
--- linux-5.10.52-orig/drivers/usb/host/dwc_otg/dwc_otg_pcd_if.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/usb/host/dwc_otg/dwc_otg_pcd_if.h	2021-07-25 16:46:28.697909777 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/* ==========================================================================
+ * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_pcd_if.h $
+ * $Revision: #11 $
+ * $Date: 2011/10/26 $
+ * $Change: 1873028 $
+ *
+ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter,
+ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless
+ * otherwise expressly agreed to in writing between Synopsys and you.
+ *
+ * The Software IS NOT an item of Licensed Software or Licensed Product under
+ * any End User Software License Agreement or Agreement for Licensed Product
+ * with Synopsys or any supplement thereto. You are permitted to use and
+ * redistribute this Software in source and binary forms, with or without
+ * modification, provided that redistributions of source code must retain this
+ * notice. You may not view, use, disclose, copy or distribute this file or
+ * any information contained herein except pursuant to this license grant from
+ * Synopsys. If you do not agree with this notice, including the disclaimer
+ * below, then you are not authorized to use the Software.
+ *
+ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * ========================================================================== */
+#ifndef DWC_HOST_ONLY
+
+#if !defined(__DWC_PCD_IF_H__)
+#define __DWC_PCD_IF_H__
+
+//#include "dwc_os.h"
+#include "dwc_otg_core_if.h"
+#include "dwc_otg_driver.h"
+
+/** @file
+ * This file defines DWC_OTG PCD Core API.
+ */
+
+struct dwc_otg_pcd;
+typedef struct dwc_otg_pcd dwc_otg_pcd_t;
+
+/** Maxpacket size for EP0 */
+#define MAX_EP0_SIZE	64
+/** Maxpacket size for any EP */
+#define MAX_PACKET_SIZE 1024
+
+/** @name Function Driver Callbacks */
+/** @{ */
+
+/** This function will be called whenever a previously queued request has
+ * completed.  The status value will be set to -DWC_E_SHUTDOWN to indicated a
+ * failed or aborted transfer, or -DWC_E_RESTART to indicate the device was reset,
+ * or -DWC_E_TIMEOUT to indicate it timed out, or -DWC_E_INVALID to indicate invalid
+ * parameters. */
+typedef int (*dwc_completion_cb_t) (dwc_otg_pcd_t * pcd, void *ep_handle,
+				    void *req_handle, int32_t status,
+				    uint32_t actual);
+/**
+ * This function will be called whenever a previousle queued ISOC request has
+ * completed. Count of ISOC packets could be read using dwc_otg_pcd_get_iso_packet_count
+ * function.
+ * The status of each ISOC packet could be read using dwc_otg_pcd_get_iso_packet_*
+ * functions.
+ */
+typedef int (*dwc_isoc_completion_cb_t) (dwc_otg_pcd_t * pcd, void *ep_handle,
+					 void *req_handle, int proc_buf_num);
+/** This function should handle any SETUP request that cannot be handled by the
+ * PCD Core.  This includes most GET_DESCRIPTORs, SET_CONFIGS, Any
+ * class-specific requests, etc.  The function must non-blocking.
+ *
+ * Returns 0 on success.
+ * Returns -DWC_E_NOT_SUPPORTED if the request is not supported.
+ * Returns -DWC_E_INVALID if the setup request had invalid parameters or bytes.
+ * Returns -DWC_E_SHUTDOWN on any other error. */
+typedef int (*dwc_setup_cb_t) (dwc_otg_pcd_t * pcd, uint8_t * bytes);
+/** This is called whenever the device has been disconnected.  The function
+ * driver should take appropriate action to clean up all pending requests in the
+ * PCD Core, remove all endpoints (except ep0), and initialize back to reset
+ * state. */
+typedef int (*dwc_disconnect_cb_t) (dwc_otg_pcd_t * pcd);
+/** This function is called when device has been connected. */
+typedef int (*dwc_connect_cb_t) (dwc_otg_pcd_t * pcd, int speed);
+/** This function is called when device has been suspended */
+typedef int (*dwc_suspend_cb_t) (dwc_otg_pcd_t * pcd);
+/** This function is called when device has received LPM tokens, i.e.
+ * device has been sent to sleep state. */
+typedef int (*dwc_sleep_cb_t) (dwc_otg_pcd_t * pcd);
+/** This function is called when device has been resumed
+ * from suspend(L2) or L1 sleep state. */
+typedef int (*dwc_resume_cb_t) (dwc_otg_pcd_t * pcd);
+/** This function is called whenever hnp params has been changed.
+ * User can call get_b_hnp_enable, get_a_hnp_support, get_a_alt_hnp_support functions
+ * to get hnp parameters. */
+typedef int (*dwc_hnp_params_changed_cb_t) (dwc_otg_pcd_t * pcd);
+/** This function is called whenever USB RESET is detected. */
+typedef int (*dwc_reset_cb_t) (dwc_otg_pcd_t * pcd);
+
+typedef int (*cfi_setup_cb_t) (dwc_otg_pcd_t * pcd, void *ctrl_req_bytes);
+
+/**
+ *
+ * @param ep_handle	Void pointer to the usb_ep structure
+ * @param ereq_port Pointer to the extended request structure created in the
+ *					portable part.
+ */
+typedef int (*xiso_completion_cb_t) (dwc_otg_pcd_t * pcd, void *ep_handle,
+				     void *req_handle, int32_t status,
+				     void *ereq_port);
+/** Function Driver Ops Data Structure */
+struct dwc_otg_pcd_function_ops {
+	dwc_connect_cb_t connect;
+	dwc_disconnect_cb_t disconnect;
+	dwc_setup_cb_t setup;
+	dwc_completion_cb_t complete;
+	dwc_isoc_completion_cb_t isoc_complete;
+	dwc_suspend_cb_t suspend;
+	dwc_sleep_cb_t sleep;
+	dwc_resume_cb_t resume;
+	dwc_reset_cb_t reset;
+	dwc_hnp_params_changed_cb_t hnp_changed;
+	cfi_setup_cb_t cfi_setup;
+#ifdef DWC_UTE_PER_IO
+	xiso_completion_cb_t xisoc_complete;
+#endif
+};
+/** @} */
+
+/** @name Function Driver Functions */
+/** @{ */
+
+/** Call this function to get pointer on dwc_otg_pcd_t,
+ * this pointer will be used for all PCD API functions.
+ *
+ * @param core_if The DWC_OTG Core
+ */
+extern dwc_otg_pcd_t *dwc_otg_pcd_init(dwc_otg_device_t *otg_dev);
+
+/** Frees PCD allocated by dwc_otg_pcd_init
+ *
+ * @param pcd The PCD
+ */
+extern void dwc_otg_pcd_remove(dwc_otg_pcd_t * pcd);
+
+/** Call this to bind the function driver to the PCD Core.
+ *
+ * @param pcd Pointer on dwc_otg_pcd_t returned by dwc_otg_pcd_init function.
+ * @param fops The Function Driver Ops data structure containing pointers to all callbacks.
+ */
+extern void dwc_otg_pcd_start(dwc_otg_pcd_t * pcd,
+			      const struct dwc_otg_pcd_function_ops *fops);
+
+/** Enables an endpoint for use.  This function enables an endpoint in
+ * the PCD.  The endpoint is described by the ep_desc which has the
+ * same format as a USB ep descriptor.  The ep_handle parameter is used to refer
+ * to the endpoint from other API functions and in callbacks.  Normally this
+ * should be called after a SET_CONFIGURATION/SET_INTERFACE to configure the
+ * core for that interface.
+ *
+ * Returns -DWC_E_INVALID if invalid parameters were passed.
+ * Returns -DWC_E_SHUTDOWN if any other error ocurred.
+ * Returns 0 on success.
+ *
+ * @param pcd The PCD
+ * @param ep_desc Endpoint descriptor
+ * @param usb_ep Handle on endpoint, that will be used to identify endpoint.
+ */
+extern int dwc_otg_pcd_ep_enable(dwc_otg_pcd_t * pcd,
+				 const uint8_t * ep_desc, void *usb_ep);
+
+/** Disable the endpoint referenced by ep_handle.
+ *
+ * Returns -DWC_E_INVALID if invalid parameters were passed.
+ * Returns -DWC_E_SHUTDOWN if any other error occurred.
+ * Returns 0 on success. */
+extern int dwc_otg_pcd_ep_disable(dwc_otg_pcd_t * pcd, void *ep_handle);
+
+/** Queue a data transfer request on the endpoint referenced by ep_handle.
+ * After the transfer is completes, the complete callback will be called with
+ * the request status.
+ *
+ * @param pcd The PCD
+ * @param ep_handle The handle of the endpoint
+ * @param buf The buffer for the data
+ * @param dma_buf The DMA buffer for the data
+ * @param buflen The length of the data transfer
+ * @param zero Specifies whether to send zero length last packet.
+ * @param req_handle Set this handle to any value to use to reference this
+ * request in the ep_dequeue function or from the complete callback
+ * @param atomic_alloc If driver need to perform atomic allocations
+ * for internal data structures.
+ *
+ * Returns -DWC_E_INVALID if invalid parameters were passed.
+ * Returns -DWC_E_SHUTDOWN if any other error ocurred.
+ * Returns 0 on success. */
+extern int dwc_otg_pcd_ep_queue(dwc_otg_pcd_t * pcd, void *ep_handle,
+				uint8_t * buf, dwc_dma_t dma_buf,
+				uint32_t buflen, int zero, void *req_handle,
+				int atomic_alloc);
+#ifdef DWC_UTE_PER_IO
+/**
+ *
+ * @param ereq_nonport	Pointer to the extended request part of the
+ *						usb_request structure defined in usb_gadget.h file.
+ */
+extern int dwc_otg_pcd_xiso_ep_queue(dwc_otg_pcd_t * pcd, void *ep_handle,
+				     uint8_t * buf, dwc_dma_t dma_buf,
+				     uint32_t buflen, int zero,
+				     void *req_handle, int atomic_alloc,
+				     void *ereq_nonport);
+
+#endif
+
+/** De-queue the specified data transfer that has not yet completed.
+ *
+ * Returns -DWC_E_INVALID if invalid parameters were passed.
+ * Returns -DWC_E_SHUTDOWN if any other error ocurred.
+ * Returns 0 on success. */
+extern int dwc_otg_pcd_ep_dequeue(dwc_otg_pcd_t * pcd, void *ep_handle,
+				  void *req_handle);
+
+/** Halt (STALL) an endpoint or clear it.
+ *
+ * Returns -DWC_E_INVALID if invalid parameters were passed.
+ * Returns -DWC_E_SHUTDOWN if any other error ocurred.
+ * Returns -DWC_E_AGAIN if the STALL cannot be sent and must be tried again later
+ * Returns 0 on success. */
+extern int dwc_otg_pcd_ep_halt(dwc_otg_pcd_t * pcd, void *ep_handle, int value);
+
+/** This function */
+extern int dwc_otg_pcd_ep_wedge(dwc_otg_pcd_t * pcd, void *ep_handle);
+
+/** This function should be called on every hardware interrupt */
+extern int32_t dwc_otg_pcd_handle_intr(dwc_otg_pcd_t * pcd);
+
+/** This function returns current frame number */
+extern int dwc_otg_pcd_get_frame_number(dwc_otg_pcd_t * pcd);
+
+/**
+ * Start isochronous transfers on the endpoint referenced by ep_handle.
+ * For isochronous transfers duble buffering is used.
+ * After processing each of buffers comlete callback will be called with
+ * status for each transaction.
+ *
+ * @param pcd The PCD
+ * @param ep_handle The handle of the endpoint
+ * @param buf0 The virtual address of first data buffer
+ * @param buf1 The virtual address of second data buffer
+ * @param dma0 The DMA address of first data buffer
+ * @param dma1 The DMA address of second data buffer
+ * @param sync_frame Data pattern frame number
+ * @param dp_frame Data size for pattern frame
+ * @param data_per_frame Data size for regular frame
+ * @param start_frame Frame number to start transfers, if -1 then start transfers ASAP.
+ * @param buf_proc_intrvl Interval of ISOC Buffer processing
+ * @param req_handle Handle of ISOC request
+ * @param atomic_alloc Specefies whether to perform atomic allocation for
+ * 			internal data structures.
+ *
+ * Returns -DWC_E_NO_MEMORY if there is no enough memory.
+ * Returns -DWC_E_INVALID if incorrect arguments are passed to the function.
+ * Returns -DW_E_SHUTDOWN for any other error.
+ * Returns 0 on success
+ */
+extern int dwc_otg_pcd_iso_ep_start(dwc_otg_pcd_t * pcd, void *ep_handle,
+				    uint8_t * buf0, uint8_t * buf1,
+				    dwc_dma_t dma0, dwc_dma_t dma1,
+				    int sync_frame, int dp_frame,
+				    int data_per_frame, int start_frame,
+				    int buf_proc_intrvl, void *req_handle,
+				    int atomic_alloc);
+
+/** Stop ISOC transfers on endpoint referenced by ep_handle.
+ *
+ * @param pcd The PCD
+ * @param ep_handle The handle of the endpoint
+ * @param req_handle Handle of ISOC request
+ *
+ * Returns -DWC_E_INVALID if incorrect arguments are passed to the function
+ * Returns 0 on success
+ */
+int dwc_otg_pcd_iso_ep_stop(dwc_otg_pcd_t * pcd, void *ep_handle,
+			    void *req_handle);
+
+/** Get ISOC packet status.
+ *
+ * @param pcd The PCD
+ * @param ep_handle The handle of the endpoint
+ * @param iso_req_handle Isochronoush request handle
+ * @param packet Number of packet
+ * @param status Out parameter for returning status
+ * @param actual Out parameter for returning actual length
+ * @param offset Out parameter for returning offset
+ *
+ */
+extern void dwc_otg_pcd_get_iso_packet_params(dwc_otg_pcd_t * pcd,
+					      void *ep_handle,
+					      void *iso_req_handle, int packet,
+					      int *status, int *actual,
+					      int *offset);
+
+/** Get ISOC packet count.
+ *
+ * @param pcd The PCD
+ * @param ep_handle The handle of the endpoint
+ * @param iso_req_handle
+ */
+extern int dwc_otg_pcd_get_iso_packet_count(dwc_otg_pcd_t * pcd,
+					    void *ep_handle,
+					    void *iso_req_handle);
+
+/** This function starts the SRP Protocol if no session is in progress. If
+ * a session is already in progress, but the device is suspended,
+ * remote wakeup signaling is started.
+ */
+extern int dwc_otg_pcd_wakeup(dwc_otg_pcd_t * pcd);
+
+/** This function returns 1 if LPM support is enabled, and 0 otherwise. */
+extern int dwc_otg_pcd_is_lpm_enabled(dwc_otg_pcd_t * pcd);
+
+/** This function returns 1 if remote wakeup is allowed and 0, otherwise. */
+extern int dwc_otg_pcd_get_rmwkup_enable(dwc_otg_pcd_t * pcd);
+
+/** Initiate SRP */
+extern void dwc_otg_pcd_initiate_srp(dwc_otg_pcd_t * pcd);
+
+/** Starts remote wakeup signaling. */
+extern void dwc_otg_pcd_remote_wakeup(dwc_otg_pcd_t * pcd, int set);
+
+/** Starts micorsecond soft disconnect. */
+extern void dwc_otg_pcd_disconnect_us(dwc_otg_pcd_t * pcd, int no_of_usecs);
+/** This function returns whether device is dualspeed.*/
+extern uint32_t dwc_otg_pcd_is_dualspeed(dwc_otg_pcd_t * pcd);
+
+/** This function returns whether device is otg. */
+extern uint32_t dwc_otg_pcd_is_otg(dwc_otg_pcd_t * pcd);
+
+/** These functions allow to get hnp parameters */
+extern uint32_t get_b_hnp_enable(dwc_otg_pcd_t * pcd);
+extern uint32_t get_a_hnp_support(dwc_otg_pcd_t * pcd);
+extern uint32_t get_a_alt_hnp_support(dwc_otg_pcd_t * pcd);
+
+/** CFI specific Interface functions */
+/** Allocate a cfi buffer */
+extern uint8_t *cfiw_ep_alloc_buffer(dwc_otg_pcd_t * pcd, void *pep,
+				     dwc_dma_t * addr, size_t buflen,
+				     int flags);
+
+/******************************************************************************/
+
+/** @} */
+
+#endif				/* __DWC_PCD_IF_H__ */
+
+#endif				/* DWC_HOST_ONLY */
diff -Nur --no-dereference linux-5.10.52-orig/drivers/usb/host/dwc_otg/dwc_otg_pcd_intr.c linux-5.10.52-v7l+/drivers/usb/host/dwc_otg/dwc_otg_pcd_intr.c
--- linux-5.10.52-orig/drivers/usb/host/dwc_otg/dwc_otg_pcd_intr.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/usb/host/dwc_otg/dwc_otg_pcd_intr.c	2021-07-25 16:46:28.717909442 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/* ==========================================================================
+ * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_pcd_intr.c $
+ * $Revision: #116 $
+ * $Date: 2012/08/10 $
+ * $Change: 2047372 $
+ *
+ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter,
+ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless
+ * otherwise expressly agreed to in writing between Synopsys and you.
+ *
+ * The Software IS NOT an item of Licensed Software or Licensed Product under
+ * any End User Software License Agreement or Agreement for Licensed Product
+ * with Synopsys or any supplement thereto. You are permitted to use and
+ * redistribute this Software in source and binary forms, with or without
+ * modification, provided that redistributions of source code must retain this
+ * notice. You may not view, use, disclose, copy or distribute this file or
+ * any information contained herein except pursuant to this license grant from
+ * Synopsys. If you do not agree with this notice, including the disclaimer
+ * below, then you are not authorized to use the Software.
+ *
+ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * ========================================================================== */
+#ifndef DWC_HOST_ONLY
+
+#include "dwc_otg_pcd.h"
+
+#ifdef DWC_UTE_CFI
+#include "dwc_otg_cfi.h"
+#endif
+
+#ifdef DWC_UTE_PER_IO
+extern void complete_xiso_ep(dwc_otg_pcd_ep_t * ep);
+#endif
+//#define PRINT_CFI_DMA_DESCS
+
+#define DEBUG_EP0
+
+/**
+ * This function updates OTG.
+ */
+static void dwc_otg_pcd_update_otg(dwc_otg_pcd_t * pcd, const unsigned reset)
+{
+
+	if (reset) {
+		pcd->b_hnp_enable = 0;
+		pcd->a_hnp_support = 0;
+		pcd->a_alt_hnp_support = 0;
+	}
+
+	if (pcd->fops->hnp_changed) {
+		pcd->fops->hnp_changed(pcd);
+	}
+}
+
+/** @file
+ * This file contains the implementation of the PCD Interrupt handlers.
+ *
+ * The PCD handles the device interrupts.  Many conditions can cause a
+ * device interrupt. When an interrupt occurs, the device interrupt
+ * service routine determines the cause of the interrupt and
+ * dispatches handling to the appropriate function. These interrupt
+ * handling functions are described below.
+ * All interrupt registers are processed from LSB to MSB.
+ */
+
+/**
+ * This function prints the ep0 state for debug purposes.
+ */
+static inline void print_ep0_state(dwc_otg_pcd_t * pcd)
+{
+#ifdef DEBUG
+	char str[40];
+
+	switch (pcd->ep0state) {
+	case EP0_DISCONNECT:
+		dwc_strcpy(str, "EP0_DISCONNECT");
+		break;
+	case EP0_IDLE:
+		dwc_strcpy(str, "EP0_IDLE");
+		break;
+	case EP0_IN_DATA_PHASE:
+		dwc_strcpy(str, "EP0_IN_DATA_PHASE");
+		break;
+	case EP0_OUT_DATA_PHASE:
+		dwc_strcpy(str, "EP0_OUT_DATA_PHASE");
+		break;
+	case EP0_IN_STATUS_PHASE:
+		dwc_strcpy(str, "EP0_IN_STATUS_PHASE");
+		break;
+	case EP0_OUT_STATUS_PHASE:
+		dwc_strcpy(str, "EP0_OUT_STATUS_PHASE");
+		break;
+	case EP0_STALL:
+		dwc_strcpy(str, "EP0_STALL");
+		break;
+	default:
+		dwc_strcpy(str, "EP0_INVALID");
+	}
+
+	DWC_DEBUGPL(DBG_ANY, "%s(%d)\n", str, pcd->ep0state);
+#endif
+}
+
+/**
+ * This function calculate the size of the payload in the memory
+ * for out endpoints and prints size for debug purposes(used in
+ * 2.93a DevOutNak feature).
+ */
+static inline void print_memory_payload(dwc_otg_pcd_t * pcd,  dwc_ep_t * ep)
+{
+#ifdef DEBUG
+	deptsiz_data_t deptsiz_init = {.d32 = 0 };
+	deptsiz_data_t deptsiz_updt = {.d32 = 0 };
+	int pack_num;
+	unsigned payload;
+
+	deptsiz_init.d32 = pcd->core_if->start_doeptsiz_val[ep->num];
+	deptsiz_updt.d32 =
+		DWC_READ_REG32(&pcd->core_if->dev_if->
+						out_ep_regs[ep->num]->doeptsiz);
+	/* Payload will be */
+	payload = deptsiz_init.b.xfersize - deptsiz_updt.b.xfersize;
+	/* Packet count is decremented every time a packet
+	 * is written to the RxFIFO not in to the external memory
+	 * So, if payload == 0, then it means no packet was sent to ext memory*/
+	pack_num = (!payload) ? 0 : (deptsiz_init.b.pktcnt - deptsiz_updt.b.pktcnt);
+	DWC_DEBUGPL(DBG_PCDV,
+		"Payload for EP%d-%s\n",
+		ep->num, (ep->is_in ? "IN" : "OUT"));
+	DWC_DEBUGPL(DBG_PCDV,
+		"Number of transfered bytes = 0x%08x\n", payload);
+	DWC_DEBUGPL(DBG_PCDV,
+		"Number of transfered packets = %d\n", pack_num);
+#endif
+}
+
+
+#ifdef DWC_UTE_CFI
+static inline void print_desc(struct dwc_otg_dma_desc *ddesc,
+			      const uint8_t * epname, int descnum)
+{
+	CFI_INFO
+	    ("%s DMA_DESC(%d) buf=0x%08x bytes=0x%04x; sp=0x%x; l=0x%x; sts=0x%02x; bs=0x%02x\n",
+	     epname, descnum, ddesc->buf, ddesc->status.b.bytes,
+	     ddesc->status.b.sp, ddesc->status.b.l, ddesc->status.b.sts,
+	     ddesc->status.b.bs);
+}
+#endif
+
+/**
+ * This function returns pointer to in ep struct with number ep_num
+ */
+static inline dwc_otg_pcd_ep_t *get_in_ep(dwc_otg_pcd_t * pcd, uint32_t ep_num)
+{
+	int i;
+	int num_in_eps = GET_CORE_IF(pcd)->dev_if->num_in_eps;
+	if (ep_num == 0) {
+		return &pcd->ep0;
+	} else {
+		for (i = 0; i < num_in_eps; ++i) {
+			if (pcd->in_ep[i].dwc_ep.num == ep_num)
+				return &pcd->in_ep[i];
+		}
+		return 0;
+	}
+}
+
+/**
+ * This function returns pointer to out ep struct with number ep_num
+ */
+static inline dwc_otg_pcd_ep_t *get_out_ep(dwc_otg_pcd_t * pcd, uint32_t ep_num)
+{
+	int i;
+	int num_out_eps = GET_CORE_IF(pcd)->dev_if->num_out_eps;
+	if (ep_num == 0) {
+		return &pcd->ep0;
+	} else {
+		for (i = 0; i < num_out_eps; ++i) {
+			if (pcd->out_ep[i].dwc_ep.num == ep_num)
+				return &pcd->out_ep[i];
+		}
+		return 0;
+	}
+}
+
+/**
+ * This functions gets a pointer to an EP from the wIndex address
+ * value of the control request.
+ */
+dwc_otg_pcd_ep_t *get_ep_by_addr(dwc_otg_pcd_t * pcd, u16 wIndex)
+{
+	dwc_otg_pcd_ep_t *ep;
+	uint32_t ep_num = UE_GET_ADDR(wIndex);
+
+	if (ep_num == 0) {
+		ep = &pcd->ep0;
+	} else if (UE_GET_DIR(wIndex) == UE_DIR_IN) {	/* in ep */
+		ep = &pcd->in_ep[ep_num - 1];
+	} else {
+		ep = &pcd->out_ep[ep_num - 1];
+	}
+
+	return ep;
+}
+
+/**
+ * This function checks the EP request queue, if the queue is not
+ * empty the next request is started.
+ */
+void start_next_request(dwc_otg_pcd_ep_t * ep)
+{
+	dwc_otg_pcd_request_t *req = 0;
+	uint32_t max_transfer =
+	    GET_CORE_IF(ep->pcd)->core_params->max_transfer_size;
+
+#ifdef DWC_UTE_CFI
+	struct dwc_otg_pcd *pcd;
+	pcd = ep->pcd;
+#endif
+
+	if (!DWC_CIRCLEQ_EMPTY(&ep->queue)) {
+		req = DWC_CIRCLEQ_FIRST(&ep->queue);
+
+#ifdef DWC_UTE_CFI
+		if (ep->dwc_ep.buff_mode != BM_STANDARD) {
+			ep->dwc_ep.cfi_req_len = req->length;
+			pcd->cfi->ops.build_descriptors(pcd->cfi, pcd, ep, req);
+		} else {
+#endif
+			/* Setup and start the Transfer */
+			if (req->dw_align_buf) {
+				ep->dwc_ep.dma_addr = req->dw_align_buf_dma;
+				ep->dwc_ep.start_xfer_buff = req->dw_align_buf;
+				ep->dwc_ep.xfer_buff = req->dw_align_buf;
+			} else {
+				ep->dwc_ep.dma_addr = req->dma;
+				ep->dwc_ep.start_xfer_buff = req->buf;
+				ep->dwc_ep.xfer_buff = req->buf;
+			}
+			ep->dwc_ep.sent_zlp = 0;
+			ep->dwc_ep.total_len = req->length;
+			ep->dwc_ep.xfer_len = 0;
+			ep->dwc_ep.xfer_count = 0;
+
+			ep->dwc_ep.maxxfer = max_transfer;
+			if (GET_CORE_IF(ep->pcd)->dma_desc_enable) {
+				uint32_t out_max_xfer = DDMA_MAX_TRANSFER_SIZE
+				    - (DDMA_MAX_TRANSFER_SIZE % 4);
+				if (ep->dwc_ep.is_in) {
+					if (ep->dwc_ep.maxxfer >
+					    DDMA_MAX_TRANSFER_SIZE) {
+						ep->dwc_ep.maxxfer =
+						    DDMA_MAX_TRANSFER_SIZE;
+					}
+				} else {
+					if (ep->dwc_ep.maxxfer > out_max_xfer) {
+						ep->dwc_ep.maxxfer =
+						    out_max_xfer;
+					}
+				}
+			}
+			if (ep->dwc_ep.maxxfer < ep->dwc_ep.total_len) {
+				ep->dwc_ep.maxxfer -=
+				    (ep->dwc_ep.maxxfer % ep->dwc_ep.maxpacket);
+			}
+			if (req->sent_zlp) {
+				if ((ep->dwc_ep.total_len %
+				     ep->dwc_ep.maxpacket == 0)
+				    && (ep->dwc_ep.total_len != 0)) {
+					ep->dwc_ep.sent_zlp = 1;
+				}
+
+			}
+#ifdef DWC_UTE_CFI
+		}
+#endif
+		dwc_otg_ep_start_transfer(GET_CORE_IF(ep->pcd), &ep->dwc_ep);
+	} else if (ep->dwc_ep.type == DWC_OTG_EP_TYPE_ISOC) {
+		DWC_PRINTF("There are no more ISOC requests \n");
+		ep->dwc_ep.frame_num = 0xFFFFFFFF;
+	}
+}
+
+/**
+ * This function handles the SOF Interrupts. At this time the SOF
+ * Interrupt is disabled.
+ */
+int32_t dwc_otg_pcd_handle_sof_intr(dwc_otg_pcd_t * pcd)
+{
+	dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd);
+
+	gintsts_data_t gintsts;
+
+	DWC_DEBUGPL(DBG_PCD, "SOF\n");
+
+	/* Clear interrupt */
+	gintsts.d32 = 0;
+	gintsts.b.sofintr = 1;
+	DWC_WRITE_REG32(&core_if->core_global_regs->gintsts, gintsts.d32);
+
+	return 1;
+}
+
+/**
+ * This function handles the Rx Status Queue Level Interrupt, which
+ * indicates that there is a least one packet in the Rx FIFO.  The
+ * packets are moved from the FIFO to memory, where they will be
+ * processed when the Endpoint Interrupt Register indicates Transfer
+ * Complete or SETUP Phase Done.
+ *
+ * Repeat the following until the Rx Status Queue is empty:
+ *	 -# Read the Receive Status Pop Register (GRXSTSP) to get Packet
+ *		info
+ *	 -# If Receive FIFO is empty then skip to step Clear the interrupt
+ *		and exit
+ *	 -# If SETUP Packet call dwc_otg_read_setup_packet to copy the
+ *		SETUP data to the buffer
+ *	 -# If OUT Data Packet call dwc_otg_read_packet to copy the data
+ *		to the destination buffer
+ */
+int32_t dwc_otg_pcd_handle_rx_status_q_level_intr(dwc_otg_pcd_t * pcd)
+{
+	dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd);
+	dwc_otg_core_global_regs_t *global_regs = core_if->core_global_regs;
+	gintmsk_data_t gintmask = {.d32 = 0 };
+	device_grxsts_data_t status;
+	dwc_otg_pcd_ep_t *ep;
+	gintsts_data_t gintsts;
+#ifdef DEBUG
+	static char *dpid_str[] = { "D0", "D2", "D1", "MDATA" };
+#endif
+
+	//DWC_DEBUGPL(DBG_PCDV, "%s(%p)\n", __func__, _pcd);
+	/* Disable the Rx Status Queue Level interrupt */
+	gintmask.b.rxstsqlvl = 1;
+	DWC_MODIFY_REG32(&global_regs->gintmsk, gintmask.d32, 0);
+
+	/* Get the Status from the top of the FIFO */
+	status.d32 = DWC_READ_REG32(&global_regs->grxstsp);
+
+	DWC_DEBUGPL(DBG_PCD, "EP:%d BCnt:%d DPID:%s "
+		    "pktsts:%x Frame:%d(0x%0x)\n",
+		    status.b.epnum, status.b.bcnt,
+		    dpid_str[status.b.dpid],
+		    status.b.pktsts, status.b.fn, status.b.fn);
+	/* Get pointer to EP structure */
+	ep = get_out_ep(pcd, status.b.epnum);
+
+	switch (status.b.pktsts) {
+	case DWC_DSTS_GOUT_NAK:
+		DWC_DEBUGPL(DBG_PCDV, "Global OUT NAK\n");
+		break;
+	case DWC_STS_DATA_UPDT:
+		DWC_DEBUGPL(DBG_PCDV, "OUT Data Packet\n");
+		if (status.b.bcnt && ep->dwc_ep.xfer_buff) {
+			/** @todo NGS Check for buffer overflow? */
+			dwc_otg_read_packet(core_if,
+					    ep->dwc_ep.xfer_buff,
+					    status.b.bcnt);
+			ep->dwc_ep.xfer_count += status.b.bcnt;
+			ep->dwc_ep.xfer_buff += status.b.bcnt;
+		}
+		break;
+	case DWC_STS_XFER_COMP:
+		DWC_DEBUGPL(DBG_PCDV, "OUT Complete\n");
+		break;
+	case DWC_DSTS_SETUP_COMP:
+#ifdef DEBUG_EP0
+		DWC_DEBUGPL(DBG_PCDV, "Setup Complete\n");
+#endif
+		break;
+	case DWC_DSTS_SETUP_UPDT:
+		dwc_otg_read_setup_packet(core_if, pcd->setup_pkt->d32);
+#ifdef DEBUG_EP0
+		DWC_DEBUGPL(DBG_PCD,
+			    "SETUP PKT: %02x.%02x v%04x i%04x l%04x\n",
+			    pcd->setup_pkt->req.bmRequestType,
+			    pcd->setup_pkt->req.bRequest,
+			    UGETW(pcd->setup_pkt->req.wValue),
+			    UGETW(pcd->setup_pkt->req.wIndex),
+			    UGETW(pcd->setup_pkt->req.wLength));
+#endif
+		ep->dwc_ep.xfer_count += status.b.bcnt;
+		break;
+	default:
+		DWC_DEBUGPL(DBG_PCDV, "Invalid Packet Status (0x%0x)\n",
+			    status.b.pktsts);
+		break;
+	}
+
+	/* Enable the Rx Status Queue Level interrupt */
+	DWC_MODIFY_REG32(&global_regs->gintmsk, 0, gintmask.d32);
+	/* Clear interrupt */
+	gintsts.d32 = 0;
+	gintsts.b.rxstsqlvl = 1;
+	DWC_WRITE_REG32(&global_regs->gintsts, gintsts.d32);
+
+	//DWC_DEBUGPL(DBG_PCDV, "EXIT: %s\n", __func__);
+	return 1;
+}
+
+/**
+ * This function examines the Device IN Token Learning Queue to
+ * determine the EP number of the last IN token received.  This
+ * implementation is for the Mass Storage device where there are only
+ * 2 IN EPs (Control-IN and BULK-IN).
+ *
+ * The EP numbers for the first six IN Tokens are in DTKNQR1 and there
+ * are 8 EP Numbers in each of the other possible DTKNQ Registers.
+ *
+ * @param core_if Programming view of DWC_otg controller.
+ *
+ */
+static inline int get_ep_of_last_in_token(dwc_otg_core_if_t * core_if)
+{
+	dwc_otg_device_global_regs_t *dev_global_regs =
+	    core_if->dev_if->dev_global_regs;
+	const uint32_t TOKEN_Q_DEPTH = core_if->hwcfg2.b.dev_token_q_depth;
+	/* Number of Token Queue Registers */
+	const int DTKNQ_REG_CNT = (TOKEN_Q_DEPTH + 7) / 8;
+	dtknq1_data_t dtknqr1;
+	uint32_t in_tkn_epnums[4];
+	int ndx = 0;
+	int i = 0;
+	volatile uint32_t *addr = &dev_global_regs->dtknqr1;
+	int epnum = 0;
+
+	//DWC_DEBUGPL(DBG_PCD,"dev_token_q_depth=%d\n",TOKEN_Q_DEPTH);
+
+	/* Read the DTKNQ Registers */
+	for (i = 0; i < DTKNQ_REG_CNT; i++) {
+		in_tkn_epnums[i] = DWC_READ_REG32(addr);
+		DWC_DEBUGPL(DBG_PCDV, "DTKNQR%d=0x%08x\n", i + 1,
+			    in_tkn_epnums[i]);
+		if (addr == &dev_global_regs->dvbusdis) {
+			addr = &dev_global_regs->dtknqr3_dthrctl;
+		} else {
+			++addr;
+		}
+
+	}
+
+	/* Copy the DTKNQR1 data to the bit field. */
+	dtknqr1.d32 = in_tkn_epnums[0];
+	/* Get the EP numbers */
+	in_tkn_epnums[0] = dtknqr1.b.epnums0_5;
+	ndx = dtknqr1.b.intknwptr - 1;
+
+	//DWC_DEBUGPL(DBG_PCDV,"ndx=%d\n",ndx);
+	if (ndx == -1) {
+		/** @todo Find a simpler way to calculate the max
+		 * queue position.*/
+		int cnt = TOKEN_Q_DEPTH;
+		if (TOKEN_Q_DEPTH <= 6) {
+			cnt = TOKEN_Q_DEPTH - 1;
+		} else if (TOKEN_Q_DEPTH <= 14) {
+			cnt = TOKEN_Q_DEPTH - 7;
+		} else if (TOKEN_Q_DEPTH <= 22) {
+			cnt = TOKEN_Q_DEPTH - 15;
+		} else {
+			cnt = TOKEN_Q_DEPTH - 23;
+		}
+		epnum = (in_tkn_epnums[DTKNQ_REG_CNT - 1] >> (cnt * 4)) & 0xF;
+	} else {
+		if (ndx <= 5) {
+			epnum = (in_tkn_epnums[0] >> (ndx * 4)) & 0xF;
+		} else if (ndx <= 13) {
+			ndx -= 6;
+			epnum = (in_tkn_epnums[1] >> (ndx * 4)) & 0xF;
+		} else if (ndx <= 21) {
+			ndx -= 14;
+			epnum = (in_tkn_epnums[2] >> (ndx * 4)) & 0xF;
+		} else if (ndx <= 29) {
+			ndx -= 22;
+			epnum = (in_tkn_epnums[3] >> (ndx * 4)) & 0xF;
+		}
+	}
+	//DWC_DEBUGPL(DBG_PCD,"epnum=%d\n",epnum);
+	return epnum;
+}
+
+/**
+ * This interrupt occurs when the non-periodic Tx FIFO is half-empty.
+ * The active request is checked for the next packet to be loaded into
+ * the non-periodic Tx FIFO.
+ */
+int32_t dwc_otg_pcd_handle_np_tx_fifo_empty_intr(dwc_otg_pcd_t * pcd)
+{
+	dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd);
+	dwc_otg_core_global_regs_t *global_regs = core_if->core_global_regs;
+	dwc_otg_dev_in_ep_regs_t *ep_regs;
+	gnptxsts_data_t txstatus = {.d32 = 0 };
+	gintsts_data_t gintsts;
+
+	int epnum = 0;
+	dwc_otg_pcd_ep_t *ep = 0;
+	uint32_t len = 0;
+	int dwords;
+
+	/* Get the epnum from the IN Token Learning Queue. */
+	epnum = get_ep_of_last_in_token(core_if);
+	ep = get_in_ep(pcd, epnum);
+
+	DWC_DEBUGPL(DBG_PCD, "NP TxFifo Empty: %d \n", epnum);
+
+	ep_regs = core_if->dev_if->in_ep_regs[epnum];
+
+	len = ep->dwc_ep.xfer_len - ep->dwc_ep.xfer_count;
+	if (len > ep->dwc_ep.maxpacket) {
+		len = ep->dwc_ep.maxpacket;
+	}
+	dwords = (len + 3) / 4;
+
+	/* While there is space in the queue and space in the FIFO and
+	 * More data to tranfer, Write packets to the Tx FIFO */
+	txstatus.d32 = DWC_READ_REG32(&global_regs->gnptxsts);
+	DWC_DEBUGPL(DBG_PCDV, "b4 GNPTXSTS=0x%08x\n", txstatus.d32);
+
+	while (txstatus.b.nptxqspcavail > 0 &&
+	       txstatus.b.nptxfspcavail > dwords &&
+	       ep->dwc_ep.xfer_count < ep->dwc_ep.xfer_len) {
+		/* Write the FIFO */
+		dwc_otg_ep_write_packet(core_if, &ep->dwc_ep, 0);
+		len = ep->dwc_ep.xfer_len - ep->dwc_ep.xfer_count;
+
+		if (len > ep->dwc_ep.maxpacket) {
+			len = ep->dwc_ep.maxpacket;
+		}
+
+		dwords = (len + 3) / 4;
+		txstatus.d32 = DWC_READ_REG32(&global_regs->gnptxsts);
+		DWC_DEBUGPL(DBG_PCDV, "GNPTXSTS=0x%08x\n", txstatus.d32);
+	}
+
+	DWC_DEBUGPL(DBG_PCDV, "GNPTXSTS=0x%08x\n",
+		    DWC_READ_REG32(&global_regs->gnptxsts));
+
+	/* Clear interrupt */
+	gintsts.d32 = 0;
+	gintsts.b.nptxfempty = 1;
+	DWC_WRITE_REG32(&global_regs->gintsts, gintsts.d32);
+
+	return 1;
+}
+
+/**
+ * This function is called when dedicated Tx FIFO Empty interrupt occurs.
+ * The active request is checked for the next packet to be loaded into
+ * apropriate Tx FIFO.
+ */
+static int32_t write_empty_tx_fifo(dwc_otg_pcd_t * pcd, uint32_t epnum)
+{
+	dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd);
+	dwc_otg_dev_if_t *dev_if = core_if->dev_if;
+	dwc_otg_dev_in_ep_regs_t *ep_regs;
+	dtxfsts_data_t txstatus = {.d32 = 0 };
+	dwc_otg_pcd_ep_t *ep = 0;
+	uint32_t len = 0;
+	int dwords;
+
+	ep = get_in_ep(pcd, epnum);
+
+	DWC_DEBUGPL(DBG_PCD, "Dedicated TxFifo Empty: %d \n", epnum);
+
+	ep_regs = core_if->dev_if->in_ep_regs[epnum];
+
+	len = ep->dwc_ep.xfer_len - ep->dwc_ep.xfer_count;
+
+	if (len > ep->dwc_ep.maxpacket) {
+		len = ep->dwc_ep.maxpacket;
+	}
+
+	dwords = (len + 3) / 4;
+
+	/* While there is space in the queue and space in the FIFO and
+	 * More data to tranfer, Write packets to the Tx FIFO */
+	txstatus.d32 = DWC_READ_REG32(&dev_if->in_ep_regs[epnum]->dtxfsts);
+	DWC_DEBUGPL(DBG_PCDV, "b4 dtxfsts[%d]=0x%08x\n", epnum, txstatus.d32);
+
+	while (txstatus.b.txfspcavail > dwords &&
+	       ep->dwc_ep.xfer_count < ep->dwc_ep.xfer_len &&
+	       ep->dwc_ep.xfer_len != 0) {
+		/* Write the FIFO */
+		dwc_otg_ep_write_packet(core_if, &ep->dwc_ep, 0);
+
+		len = ep->dwc_ep.xfer_len - ep->dwc_ep.xfer_count;
+		if (len > ep->dwc_ep.maxpacket) {
+			len = ep->dwc_ep.maxpacket;
+		}
+
+		dwords = (len + 3) / 4;
+		txstatus.d32 =
+		    DWC_READ_REG32(&dev_if->in_ep_regs[epnum]->dtxfsts);
+		DWC_DEBUGPL(DBG_PCDV, "dtxfsts[%d]=0x%08x\n", epnum,
+			    txstatus.d32);
+	}
+
+	DWC_DEBUGPL(DBG_PCDV, "b4 dtxfsts[%d]=0x%08x\n", epnum,
+		    DWC_READ_REG32(&dev_if->in_ep_regs[epnum]->dtxfsts));
+
+	return 1;
+}
+
+/**
+ * This function is called when the Device is disconnected. It stops
+ * any active requests and informs the Gadget driver of the
+ * disconnect.
+ */
+void dwc_otg_pcd_stop(dwc_otg_pcd_t * pcd)
+{
+	int i, num_in_eps, num_out_eps;
+	dwc_otg_pcd_ep_t *ep;
+
+	gintmsk_data_t intr_mask = {.d32 = 0 };
+
+	DWC_SPINLOCK(pcd->lock);
+
+	num_in_eps = GET_CORE_IF(pcd)->dev_if->num_in_eps;
+	num_out_eps = GET_CORE_IF(pcd)->dev_if->num_out_eps;
+
+	DWC_DEBUGPL(DBG_PCDV, "%s() \n", __func__);
+	/* don't disconnect drivers more than once */
+	if (pcd->ep0state == EP0_DISCONNECT) {
+		DWC_DEBUGPL(DBG_ANY, "%s() Already Disconnected\n", __func__);
+		DWC_SPINUNLOCK(pcd->lock);
+		return;
+	}
+	pcd->ep0state = EP0_DISCONNECT;
+
+	/* Reset the OTG state. */
+	dwc_otg_pcd_update_otg(pcd, 1);
+
+	/* Disable the NP Tx Fifo Empty Interrupt. */
+	intr_mask.b.nptxfempty = 1;
+	DWC_MODIFY_REG32(&GET_CORE_IF(pcd)->core_global_regs->gintmsk,
+			 intr_mask.d32, 0);
+
+	/* Flush the FIFOs */
+	/**@todo NGS Flush Periodic FIFOs */
+	dwc_otg_flush_tx_fifo(GET_CORE_IF(pcd), 0x10);
+	dwc_otg_flush_rx_fifo(GET_CORE_IF(pcd));
+
+	/* prevent new request submissions, kill any outstanding requests  */
+	ep = &pcd->ep0;
+	dwc_otg_request_nuke(ep);
+	/* prevent new request submissions, kill any outstanding requests  */
+	for (i = 0; i < num_in_eps; i++) {
+		dwc_otg_pcd_ep_t *ep = &pcd->in_ep[i];
+		dwc_otg_request_nuke(ep);
+	}
+	/* prevent new request submissions, kill any outstanding requests  */
+	for (i = 0; i < num_out_eps; i++) {
+		dwc_otg_pcd_ep_t *ep = &pcd->out_ep[i];
+		dwc_otg_request_nuke(ep);
+	}
+
+	/* report disconnect; the driver is already quiesced */
+	if (pcd->fops->disconnect) {
+		DWC_SPINUNLOCK(pcd->lock);
+		pcd->fops->disconnect(pcd);
+		DWC_SPINLOCK(pcd->lock);
+	}
+	DWC_SPINUNLOCK(pcd->lock);
+}
+
+/**
+ * This interrupt indicates that ...
+ */
+int32_t dwc_otg_pcd_handle_i2c_intr(dwc_otg_pcd_t * pcd)
+{
+	gintmsk_data_t intr_mask = {.d32 = 0 };
+	gintsts_data_t gintsts;
+
+	DWC_PRINTF("INTERRUPT Handler not implemented for %s\n", "i2cintr");
+	intr_mask.b.i2cintr = 1;
+	DWC_MODIFY_REG32(&GET_CORE_IF(pcd)->core_global_regs->gintmsk,
+			 intr_mask.d32, 0);
+
+	/* Clear interrupt */
+	gintsts.d32 = 0;
+	gintsts.b.i2cintr = 1;
+	DWC_WRITE_REG32(&GET_CORE_IF(pcd)->core_global_regs->gintsts,
+			gintsts.d32);
+	return 1;
+}
+
+/**
+ * This interrupt indicates that ...
+ */
+int32_t dwc_otg_pcd_handle_early_suspend_intr(dwc_otg_pcd_t * pcd)
+{
+	gintsts_data_t gintsts;
+#if defined(VERBOSE)
+	DWC_PRINTF("Early Suspend Detected\n");
+#endif
+
+	/* Clear interrupt */
+	gintsts.d32 = 0;
+	gintsts.b.erlysuspend = 1;
+	DWC_WRITE_REG32(&GET_CORE_IF(pcd)->core_global_regs->gintsts,
+			gintsts.d32);
+	return 1;
+}
+
+/**
+ * This function configures EPO to receive SETUP packets.
+ *
+ * @todo NGS: Update the comments from the HW FS.
+ *
+ *	-# Program the following fields in the endpoint specific registers
+ *	for Control OUT EP 0, in order to receive a setup packet
+ *	- DOEPTSIZ0.Packet Count = 3 (To receive up to 3 back to back
+ *	  setup packets)
+ *	- DOEPTSIZE0.Transfer Size = 24 Bytes (To receive up to 3 back
+ *	  to back setup packets)
+ *		- In DMA mode, DOEPDMA0 Register with a memory address to
+ *		  store any setup packets received
+ *
+ * @param core_if Programming view of DWC_otg controller.
+ * @param pcd	  Programming view of the PCD.
+ */
+static inline void ep0_out_start(dwc_otg_core_if_t * core_if,
+				 dwc_otg_pcd_t * pcd)
+{
+	dwc_otg_dev_if_t *dev_if = core_if->dev_if;
+	deptsiz0_data_t doeptsize0 = {.d32 = 0 };
+	dwc_otg_dev_dma_desc_t *dma_desc;
+	depctl_data_t doepctl = {.d32 = 0 };
+
+#ifdef VERBOSE
+	DWC_DEBUGPL(DBG_PCDV, "%s() doepctl0=%0x\n", __func__,
+		    DWC_READ_REG32(&dev_if->out_ep_regs[0]->doepctl));
+#endif
+	if (core_if->snpsid >= OTG_CORE_REV_3_00a) {
+		doepctl.d32 = DWC_READ_REG32(&dev_if->out_ep_regs[0]->doepctl);
+		if (doepctl.b.epena) {
+			return;
+		}
+	}
+
+	doeptsize0.b.supcnt = 3;
+	doeptsize0.b.pktcnt = 1;
+	doeptsize0.b.xfersize = 8 * 3;
+
+	if (core_if->dma_enable) {
+		if (!core_if->dma_desc_enable) {
+			/** put here as for Hermes mode deptisz register should not be written */
+			DWC_WRITE_REG32(&dev_if->out_ep_regs[0]->doeptsiz,
+					doeptsize0.d32);
+
+			/** @todo dma needs to handle multiple setup packets (up to 3) */
+			DWC_WRITE_REG32(&dev_if->out_ep_regs[0]->doepdma,
+					pcd->setup_pkt_dma_handle);
+		} else {
+			dev_if->setup_desc_index =
+			    (dev_if->setup_desc_index + 1) & 1;
+			dma_desc =
+			    dev_if->setup_desc_addr[dev_if->setup_desc_index];
+
+			/** DMA Descriptor Setup */
+			dma_desc->status.b.bs = BS_HOST_BUSY;
+			if (core_if->snpsid >= OTG_CORE_REV_3_00a) {
+				dma_desc->status.b.sr = 0;
+				dma_desc->status.b.mtrf = 0;
+			}
+			dma_desc->status.b.l = 1;
+			dma_desc->status.b.ioc = 1;
+			dma_desc->status.b.bytes = pcd->ep0.dwc_ep.maxpacket;
+			dma_desc->buf = pcd->setup_pkt_dma_handle;
+			dma_desc->status.b.sts = 0;
+			dma_desc->status.b.bs = BS_HOST_READY;
+
+			/** DOEPDMA0 Register write */
+			DWC_WRITE_REG32(&dev_if->out_ep_regs[0]->doepdma,
+					dev_if->dma_setup_desc_addr
+					[dev_if->setup_desc_index]);
+		}
+
+	} else {
+		/** put here as for Hermes mode deptisz register should not be written */
+		DWC_WRITE_REG32(&dev_if->out_ep_regs[0]->doeptsiz,
+				doeptsize0.d32);
+	}
+
+	/** DOEPCTL0 Register write cnak will be set after setup interrupt */
+	doepctl.d32 = 0;
+	doepctl.b.epena = 1;
+	if (core_if->snpsid <= OTG_CORE_REV_2_94a) {
+	doepctl.b.cnak = 1;
+	DWC_WRITE_REG32(&dev_if->out_ep_regs[0]->doepctl, doepctl.d32);
+	} else {
+		DWC_MODIFY_REG32(&dev_if->out_ep_regs[0]->doepctl, 0, doepctl.d32);
+	}
+
+#ifdef VERBOSE
+	DWC_DEBUGPL(DBG_PCDV, "doepctl0=%0x\n",
+		    DWC_READ_REG32(&dev_if->out_ep_regs[0]->doepctl));
+	DWC_DEBUGPL(DBG_PCDV, "diepctl0=%0x\n",
+		    DWC_READ_REG32(&dev_if->in_ep_regs[0]->diepctl));
+#endif
+}
+
+/**
+ * This interrupt occurs when a USB Reset is detected. When the USB
+ * Reset Interrupt occurs the device state is set to DEFAULT and the
+ * EP0 state is set to IDLE.
+ *	-#	Set the NAK bit for all OUT endpoints (DOEPCTLn.SNAK = 1)
+ *	-#	Unmask the following interrupt bits
+ *		- DAINTMSK.INEP0 = 1 (Control 0 IN endpoint)
+ *	- DAINTMSK.OUTEP0 = 1 (Control 0 OUT endpoint)
+ *	- DOEPMSK.SETUP = 1
+ *	- DOEPMSK.XferCompl = 1
+ *	- DIEPMSK.XferCompl = 1
+ *	- DIEPMSK.TimeOut = 1
+ *	-# Program the following fields in the endpoint specific registers
+ *	for Control OUT EP 0, in order to receive a setup packet
+ *	- DOEPTSIZ0.Packet Count = 3 (To receive up to 3 back to back
+ *	  setup packets)
+ *	- DOEPTSIZE0.Transfer Size = 24 Bytes (To receive up to 3 back
+ *	  to back setup packets)
+ *		- In DMA mode, DOEPDMA0 Register with a memory address to
+ *		  store any setup packets received
+ * At this point, all the required initialization, except for enabling
+ * the control 0 OUT endpoint is done, for receiving SETUP packets.
+ */
+int32_t dwc_otg_pcd_handle_usb_reset_intr(dwc_otg_pcd_t * pcd)
+{
+	dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd);
+	dwc_otg_dev_if_t *dev_if = core_if->dev_if;
+	depctl_data_t doepctl = {.d32 = 0 };
+	depctl_data_t diepctl = {.d32 = 0 };
+	daint_data_t daintmsk = {.d32 = 0 };
+	doepmsk_data_t doepmsk = {.d32 = 0 };
+	diepmsk_data_t diepmsk = {.d32 = 0 };
+	dcfg_data_t dcfg = {.d32 = 0 };
+	grstctl_t resetctl = {.d32 = 0 };
+	dctl_data_t dctl = {.d32 = 0 };
+	int i = 0;
+	gintsts_data_t gintsts;
+	pcgcctl_data_t power = {.d32 = 0 };
+
+	power.d32 = DWC_READ_REG32(core_if->pcgcctl);
+	if (power.b.stoppclk) {
+		power.d32 = 0;
+		power.b.stoppclk = 1;
+		DWC_MODIFY_REG32(core_if->pcgcctl, power.d32, 0);
+
+		power.b.pwrclmp = 1;
+		DWC_MODIFY_REG32(core_if->pcgcctl, power.d32, 0);
+
+		power.b.rstpdwnmodule = 1;
+		DWC_MODIFY_REG32(core_if->pcgcctl, power.d32, 0);
+	}
+
+	core_if->lx_state = DWC_OTG_L0;
+
+	DWC_PRINTF("USB RESET\n");
+#ifdef DWC_EN_ISOC
+	for (i = 1; i < 16; ++i) {
+		dwc_otg_pcd_ep_t *ep;
+		dwc_ep_t *dwc_ep;
+		ep = get_in_ep(pcd, i);
+		if (ep != 0) {
+			dwc_ep = &ep->dwc_ep;
+			dwc_ep->next_frame = 0xffffffff;
+		}
+	}
+#endif /* DWC_EN_ISOC */
+
+	/* reset the HNP settings */
+	dwc_otg_pcd_update_otg(pcd, 1);
+
+	/* Clear the Remote Wakeup Signalling */
+	dctl.b.rmtwkupsig = 1;
+	DWC_MODIFY_REG32(&core_if->dev_if->dev_global_regs->dctl, dctl.d32, 0);
+
+	/* Set NAK for all OUT EPs */
+	doepctl.b.snak = 1;
+	for (i = 0; i <= dev_if->num_out_eps; i++) {
+		DWC_WRITE_REG32(&dev_if->out_ep_regs[i]->doepctl, doepctl.d32);
+	}
+
+	/* Flush the NP Tx FIFO */
+	dwc_otg_flush_tx_fifo(core_if, 0x10);
+	/* Flush the Learning Queue */
+	resetctl.b.intknqflsh = 1;
+	DWC_WRITE_REG32(&core_if->core_global_regs->grstctl, resetctl.d32);
+
+	if (!core_if->core_params->en_multiple_tx_fifo && core_if->dma_enable) {
+		core_if->start_predict = 0;
+		for (i = 0; i<= core_if->dev_if->num_in_eps; ++i) {
+			core_if->nextep_seq[i] = 0xff;	// 0xff - EP not active
+		}
+		core_if->nextep_seq[0] = 0;
+		core_if->first_in_nextep_seq = 0;
+		diepctl.d32 = DWC_READ_REG32(&dev_if->in_ep_regs[0]->diepctl);
+		diepctl.b.nextep = 0;
+		DWC_WRITE_REG32(&dev_if->in_ep_regs[0]->diepctl, diepctl.d32);
+
+		/* Update IN Endpoint Mismatch Count by active IN NP EP count + 1 */
+		dcfg.d32 = DWC_READ_REG32(&dev_if->dev_global_regs->dcfg);
+		dcfg.b.epmscnt = 2;
+		DWC_WRITE_REG32(&dev_if->dev_global_regs->dcfg, dcfg.d32);
+
+		DWC_DEBUGPL(DBG_PCDV,
+			    "%s first_in_nextep_seq= %2d; nextep_seq[]:\n",
+			__func__, core_if->first_in_nextep_seq);
+		for (i=0; i <= core_if->dev_if->num_in_eps; i++) {
+			DWC_DEBUGPL(DBG_PCDV, "%2d\n", core_if->nextep_seq[i]);
+		}
+	}
+
+	if (core_if->multiproc_int_enable) {
+		daintmsk.b.inep0 = 1;
+		daintmsk.b.outep0 = 1;
+		DWC_WRITE_REG32(&dev_if->dev_global_regs->deachintmsk,
+				daintmsk.d32);
+
+		doepmsk.b.setup = 1;
+		doepmsk.b.xfercompl = 1;
+		doepmsk.b.ahberr = 1;
+		doepmsk.b.epdisabled = 1;
+
+		if ((core_if->dma_desc_enable) ||
+		    (core_if->dma_enable
+		     && core_if->snpsid >= OTG_CORE_REV_3_00a)) {
+			doepmsk.b.stsphsercvd = 1;
+		}
+		if (core_if->dma_desc_enable)
+			doepmsk.b.bna = 1;
+/*
+		doepmsk.b.babble = 1;
+		doepmsk.b.nyet = 1;
+
+		if (core_if->dma_enable) {
+			doepmsk.b.nak = 1;
+		}
+*/
+		DWC_WRITE_REG32(&dev_if->dev_global_regs->doepeachintmsk[0],
+				doepmsk.d32);
+
+		diepmsk.b.xfercompl = 1;
+		diepmsk.b.timeout = 1;
+		diepmsk.b.epdisabled = 1;
+		diepmsk.b.ahberr = 1;
+		diepmsk.b.intknepmis = 1;
+		if (!core_if->en_multiple_tx_fifo && core_if->dma_enable)
+			diepmsk.b.intknepmis = 0;
+
+/*		if (core_if->dma_desc_enable) {
+			diepmsk.b.bna = 1;
+		}
+*/
+/*
+		if (core_if->dma_enable) {
+			diepmsk.b.nak = 1;
+		}
+*/
+		DWC_WRITE_REG32(&dev_if->dev_global_regs->diepeachintmsk[0],
+				diepmsk.d32);
+	} else {
+		daintmsk.b.inep0 = 1;
+		daintmsk.b.outep0 = 1;
+		DWC_WRITE_REG32(&dev_if->dev_global_regs->daintmsk,
+				daintmsk.d32);
+
+		doepmsk.b.setup = 1;
+		doepmsk.b.xfercompl = 1;
+		doepmsk.b.ahberr = 1;
+		doepmsk.b.epdisabled = 1;
+
+		if ((core_if->dma_desc_enable) ||
+		    (core_if->dma_enable
+		     && core_if->snpsid >= OTG_CORE_REV_3_00a)) {
+			doepmsk.b.stsphsercvd = 1;
+		}
+		if (core_if->dma_desc_enable)
+			doepmsk.b.bna = 1;
+		DWC_WRITE_REG32(&dev_if->dev_global_regs->doepmsk, doepmsk.d32);
+
+		diepmsk.b.xfercompl = 1;
+		diepmsk.b.timeout = 1;
+		diepmsk.b.epdisabled = 1;
+		diepmsk.b.ahberr = 1;
+		if (!core_if->en_multiple_tx_fifo && core_if->dma_enable)
+			diepmsk.b.intknepmis = 0;
+/*
+		if (core_if->dma_desc_enable) {
+			diepmsk.b.bna = 1;
+		}
+*/
+
+		DWC_WRITE_REG32(&dev_if->dev_global_regs->diepmsk, diepmsk.d32);
+	}
+
+	/* Reset Device Address */
+	dcfg.d32 = DWC_READ_REG32(&dev_if->dev_global_regs->dcfg);
+	dcfg.b.devaddr = 0;
+	DWC_WRITE_REG32(&dev_if->dev_global_regs->dcfg, dcfg.d32);
+
+	/* setup EP0 to receive SETUP packets */
+	if (core_if->snpsid <= OTG_CORE_REV_2_94a)
+		ep0_out_start(core_if, pcd);
+
+	/* Clear interrupt */
+	gintsts.d32 = 0;
+	gintsts.b.usbreset = 1;
+	DWC_WRITE_REG32(&core_if->core_global_regs->gintsts, gintsts.d32);
+
+	return 1;
+}
+
+/**
+ * Get the device speed from the device status register and convert it
+ * to USB speed constant.
+ *
+ * @param core_if Programming view of DWC_otg controller.
+ */
+static int get_device_speed(dwc_otg_core_if_t * core_if)
+{
+	dsts_data_t dsts;
+	int speed = 0;
+	dsts.d32 = DWC_READ_REG32(&core_if->dev_if->dev_global_regs->dsts);
+
+	switch (dsts.b.enumspd) {
+	case DWC_DSTS_ENUMSPD_HS_PHY_30MHZ_OR_60MHZ:
+		speed = USB_SPEED_HIGH;
+		break;
+	case DWC_DSTS_ENUMSPD_FS_PHY_30MHZ_OR_60MHZ:
+	case DWC_DSTS_ENUMSPD_FS_PHY_48MHZ:
+		speed = USB_SPEED_FULL;
+		break;
+
+	case DWC_DSTS_ENUMSPD_LS_PHY_6MHZ:
+		speed = USB_SPEED_LOW;
+		break;
+	}
+
+	return speed;
+}
+
+/**
+ * Read the device status register and set the device speed in the
+ * data structure.
+ * Set up EP0 to receive SETUP packets by calling dwc_ep0_activate.
+ */
+int32_t dwc_otg_pcd_handle_enum_done_intr(dwc_otg_pcd_t * pcd)
+{
+	dwc_otg_pcd_ep_t *ep0 = &pcd->ep0;
+	gintsts_data_t gintsts;
+	gusbcfg_data_t gusbcfg;
+	dwc_otg_core_global_regs_t *global_regs =
+	    GET_CORE_IF(pcd)->core_global_regs;
+	uint8_t utmi16b, utmi8b;
+	int speed;
+	DWC_DEBUGPL(DBG_PCD, "SPEED ENUM\n");
+
+	if (GET_CORE_IF(pcd)->snpsid >= OTG_CORE_REV_2_60a) {
+		utmi16b = 6;	//vahrama old value was 6;
+		utmi8b = 9;
+	} else {
+		utmi16b = 4;
+		utmi8b = 8;
+	}
+	dwc_otg_ep0_activate(GET_CORE_IF(pcd), &ep0->dwc_ep);
+	if (GET_CORE_IF(pcd)->snpsid >= OTG_CORE_REV_3_00a) {
+		ep0_out_start(GET_CORE_IF(pcd), pcd);
+	}
+
+#ifdef DEBUG_EP0
+	print_ep0_state(pcd);
+#endif
+
+	if (pcd->ep0state == EP0_DISCONNECT) {
+		pcd->ep0state = EP0_IDLE;
+	} else if (pcd->ep0state == EP0_STALL) {
+		pcd->ep0state = EP0_IDLE;
+	}
+
+	pcd->ep0state = EP0_IDLE;
+
+	ep0->stopped = 0;
+
+	speed = get_device_speed(GET_CORE_IF(pcd));
+	pcd->fops->connect(pcd, speed);
+
+	/* Set USB turnaround time based on device speed and PHY interface. */
+	gusbcfg.d32 = DWC_READ_REG32(&global_regs->gusbcfg);
+	if (speed == USB_SPEED_HIGH) {
+		if (GET_CORE_IF(pcd)->hwcfg2.b.hs_phy_type ==
+		    DWC_HWCFG2_HS_PHY_TYPE_ULPI) {
+			/* ULPI interface */
+			gusbcfg.b.usbtrdtim = 9;
+		}
+		if (GET_CORE_IF(pcd)->hwcfg2.b.hs_phy_type ==
+		    DWC_HWCFG2_HS_PHY_TYPE_UTMI) {
+			/* UTMI+ interface */
+			if (GET_CORE_IF(pcd)->hwcfg4.b.utmi_phy_data_width == 0) {
+				gusbcfg.b.usbtrdtim = utmi8b;
+			} else if (GET_CORE_IF(pcd)->hwcfg4.
+				   b.utmi_phy_data_width == 1) {
+				gusbcfg.b.usbtrdtim = utmi16b;
+			} else if (GET_CORE_IF(pcd)->
+				   core_params->phy_utmi_width == 8) {
+				gusbcfg.b.usbtrdtim = utmi8b;
+			} else {
+				gusbcfg.b.usbtrdtim = utmi16b;
+			}
+		}
+		if (GET_CORE_IF(pcd)->hwcfg2.b.hs_phy_type ==
+		    DWC_HWCFG2_HS_PHY_TYPE_UTMI_ULPI) {
+			/* UTMI+  OR  ULPI interface */
+			if (gusbcfg.b.ulpi_utmi_sel == 1) {
+				/* ULPI interface */
+				gusbcfg.b.usbtrdtim = 9;
+			} else {
+				/* UTMI+ interface */
+				if (GET_CORE_IF(pcd)->
+				    core_params->phy_utmi_width == 16) {
+					gusbcfg.b.usbtrdtim = utmi16b;
+				} else {
+					gusbcfg.b.usbtrdtim = utmi8b;
+				}
+			}
+		}
+	} else {
+		/* Full or low speed */
+		gusbcfg.b.usbtrdtim = 9;
+	}
+	DWC_WRITE_REG32(&global_regs->gusbcfg, gusbcfg.d32);
+
+	/* Clear interrupt */
+	gintsts.d32 = 0;
+	gintsts.b.enumdone = 1;
+	DWC_WRITE_REG32(&GET_CORE_IF(pcd)->core_global_regs->gintsts,
+			gintsts.d32);
+	return 1;
+}
+
+/**
+ * This interrupt indicates that the ISO OUT Packet was dropped due to
+ * Rx FIFO full or Rx Status Queue Full.  If this interrupt occurs
+ * read all the data from the Rx FIFO.
+ */
+int32_t dwc_otg_pcd_handle_isoc_out_packet_dropped_intr(dwc_otg_pcd_t * pcd)
+{
+	gintmsk_data_t intr_mask = {.d32 = 0 };
+	gintsts_data_t gintsts;
+
+	DWC_WARN("INTERRUPT Handler not implemented for %s\n",
+		 "ISOC Out Dropped");
+
+	intr_mask.b.isooutdrop = 1;
+	DWC_MODIFY_REG32(&GET_CORE_IF(pcd)->core_global_regs->gintmsk,
+			 intr_mask.d32, 0);
+
+	/* Clear interrupt */
+	gintsts.d32 = 0;
+	gintsts.b.isooutdrop = 1;
+	DWC_WRITE_REG32(&GET_CORE_IF(pcd)->core_global_regs->gintsts,
+			gintsts.d32);
+
+	return 1;
+}
+
+/**
+ * This interrupt indicates the end of the portion of the micro-frame
+ * for periodic transactions.  If there is a periodic transaction for
+ * the next frame, load the packets into the EP periodic Tx FIFO.
+ */
+int32_t dwc_otg_pcd_handle_end_periodic_frame_intr(dwc_otg_pcd_t * pcd)
+{
+	gintmsk_data_t intr_mask = {.d32 = 0 };
+	gintsts_data_t gintsts;
+	DWC_PRINTF("INTERRUPT Handler not implemented for %s\n", "EOP");
+
+	intr_mask.b.eopframe = 1;
+	DWC_MODIFY_REG32(&GET_CORE_IF(pcd)->core_global_regs->gintmsk,
+			 intr_mask.d32, 0);
+
+	/* Clear interrupt */
+	gintsts.d32 = 0;
+	gintsts.b.eopframe = 1;
+	DWC_WRITE_REG32(&GET_CORE_IF(pcd)->core_global_regs->gintsts,
+			gintsts.d32);
+
+	return 1;
+}
+
+/**
+ * This interrupt indicates that EP of the packet on the top of the
+ * non-periodic Tx FIFO does not match EP of the IN Token received.
+ *
+ * The "Device IN Token Queue" Registers are read to determine the
+ * order the IN Tokens have been received. The non-periodic Tx FIFO
+ * is flushed, so it can be reloaded in the order seen in the IN Token
+ * Queue.
+ */
+int32_t dwc_otg_pcd_handle_ep_mismatch_intr(dwc_otg_pcd_t * pcd)
+{
+	gintsts_data_t gintsts;
+	dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd);
+	dctl_data_t dctl;
+	gintmsk_data_t intr_mask = {.d32 = 0 };
+
+	if (!core_if->en_multiple_tx_fifo && core_if->dma_enable) {
+		core_if->start_predict = 1;
+
+		DWC_DEBUGPL(DBG_PCDV, "%s(%p)\n", __func__, core_if);
+
+		gintsts.d32 = DWC_READ_REG32(&core_if->core_global_regs->gintsts);
+		if (!gintsts.b.ginnakeff) {
+			/* Disable EP Mismatch interrupt */
+			intr_mask.d32 = 0;
+			intr_mask.b.epmismatch = 1;
+			DWC_MODIFY_REG32(&core_if->core_global_regs->gintmsk, intr_mask.d32, 0);
+			/* Enable the Global IN NAK Effective Interrupt */
+			intr_mask.d32 = 0;
+			intr_mask.b.ginnakeff = 1;
+			DWC_MODIFY_REG32(&core_if->core_global_regs->gintmsk, 0, intr_mask.d32);
+			/* Set the global non-periodic IN NAK handshake */
+			dctl.d32 = DWC_READ_REG32(&core_if->dev_if->dev_global_regs->dctl);
+			dctl.b.sgnpinnak = 1;
+			DWC_WRITE_REG32(&core_if->dev_if->dev_global_regs->dctl, dctl.d32);
+		} else {
+			DWC_PRINTF("gintsts.b.ginnakeff = 1! dctl.b.sgnpinnak not set\n");
+		}
+		/* Disabling of all EP's will be done in dwc_otg_pcd_handle_in_nak_effective()
+		 * handler after Global IN NAK Effective interrupt will be asserted */
+	}
+	/* Clear interrupt */
+	gintsts.d32 = 0;
+	gintsts.b.epmismatch = 1;
+	DWC_WRITE_REG32(&core_if->core_global_regs->gintsts, gintsts.d32);
+
+	return 1;
+}
+
+/**
+ * This interrupt is valid only in DMA mode. This interrupt indicates that the
+ * core has stopped fetching data for IN endpoints due to the unavailability of
+ * TxFIFO space or Request Queue space. This interrupt is used by the
+ * application for an endpoint mismatch algorithm.
+ *
+ * @param pcd The PCD
+ */
+int32_t dwc_otg_pcd_handle_ep_fetsusp_intr(dwc_otg_pcd_t * pcd)
+{
+	gintsts_data_t gintsts;
+	gintmsk_data_t gintmsk_data;
+	dctl_data_t dctl;
+	dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd);
+	DWC_DEBUGPL(DBG_PCDV, "%s(%p)\n", __func__, core_if);
+
+	/* Clear the global non-periodic IN NAK handshake */
+	dctl.d32 = 0;
+	dctl.b.cgnpinnak = 1;
+	DWC_MODIFY_REG32(&core_if->dev_if->dev_global_regs->dctl, dctl.d32, dctl.d32);
+
+	/* Mask GINTSTS.FETSUSP interrupt */
+	gintmsk_data.d32 = DWC_READ_REG32(&core_if->core_global_regs->gintmsk);
+	gintmsk_data.b.fetsusp = 0;
+	DWC_WRITE_REG32(&core_if->core_global_regs->gintmsk, gintmsk_data.d32);
+
+	/* Clear interrupt */
+	gintsts.d32 = 0;
+	gintsts.b.fetsusp = 1;
+	DWC_WRITE_REG32(&core_if->core_global_regs->gintsts, gintsts.d32);
+
+	return 1;
+}
+/**
+ * This funcion stalls EP0.
+ */
+static inline void ep0_do_stall(dwc_otg_pcd_t * pcd, const int err_val)
+{
+	dwc_otg_pcd_ep_t *ep0 = &pcd->ep0;
+	usb_device_request_t *ctrl = &pcd->setup_pkt->req;
+	DWC_WARN("req %02x.%02x protocol STALL; err %d\n",
+		 ctrl->bmRequestType, ctrl->bRequest, err_val);
+
+	ep0->dwc_ep.is_in = 1;
+	dwc_otg_ep_set_stall(GET_CORE_IF(pcd), &ep0->dwc_ep);
+	pcd->ep0.stopped = 1;
+	pcd->ep0state = EP0_IDLE;
+	ep0_out_start(GET_CORE_IF(pcd), pcd);
+}
+
+/**
+ * This functions delegates the setup command to the gadget driver.
+ */
+static inline void do_gadget_setup(dwc_otg_pcd_t * pcd,
+				   usb_device_request_t * ctrl)
+{
+	int ret = 0;
+	DWC_SPINUNLOCK(pcd->lock);
+	ret = pcd->fops->setup(pcd, (uint8_t *) ctrl);
+	DWC_SPINLOCK(pcd->lock);
+	if (ret < 0) {
+		ep0_do_stall(pcd, ret);
+	}
+
+	/** @todo This is a g_file_storage gadget driver specific
+	 * workaround: a DELAYED_STATUS result from the fsg_setup
+	 * routine will result in the gadget queueing a EP0 IN status
+	 * phase for a two-stage control transfer. Exactly the same as
+	 * a SET_CONFIGURATION/SET_INTERFACE except that this is a class
+	 * specific request.  Need a generic way to know when the gadget
+	 * driver will queue the status phase. Can we assume when we
+	 * call the gadget driver setup() function that it will always
+	 * queue and require the following flag? Need to look into
+	 * this.
+	 */
+
+	if (ret == 256 + 999) {
+		pcd->request_config = 1;
+	}
+}
+
+#ifdef DWC_UTE_CFI
+/**
+ * This functions delegates the CFI setup commands to the gadget driver.
+ * This function will return a negative value to indicate a failure.
+ */
+static inline int cfi_gadget_setup(dwc_otg_pcd_t * pcd,
+				   struct cfi_usb_ctrlrequest *ctrl_req)
+{
+	int ret = 0;
+
+	if (pcd->fops && pcd->fops->cfi_setup) {
+		DWC_SPINUNLOCK(pcd->lock);
+		ret = pcd->fops->cfi_setup(pcd, ctrl_req);
+		DWC_SPINLOCK(pcd->lock);
+		if (ret < 0) {
+			ep0_do_stall(pcd, ret);
+			return ret;
+		}
+	}
+
+	return ret;
+}
+#endif
+
+/**
+ * This function starts the Zero-Length Packet for the IN status phase
+ * of a 2 stage control transfer.
+ */
+static inline void do_setup_in_status_phase(dwc_otg_pcd_t * pcd)
+{
+	dwc_otg_pcd_ep_t *ep0 = &pcd->ep0;
+	if (pcd->ep0state == EP0_STALL) {
+		return;
+	}
+
+	pcd->ep0state = EP0_IN_STATUS_PHASE;
+
+	/* Prepare for more SETUP Packets */
+	DWC_DEBUGPL(DBG_PCD, "EP0 IN ZLP\n");
+	if ((GET_CORE_IF(pcd)->snpsid >= OTG_CORE_REV_3_00a)
+	    && (pcd->core_if->dma_desc_enable)
+	    && (ep0->dwc_ep.xfer_count < ep0->dwc_ep.total_len)) {
+		DWC_DEBUGPL(DBG_PCDV,
+			    "Data terminated wait next packet in out_desc_addr\n");
+		pcd->backup_buf = phys_to_virt(ep0->dwc_ep.dma_addr);
+		pcd->data_terminated = 1;
+	}
+	ep0->dwc_ep.xfer_len = 0;
+	ep0->dwc_ep.xfer_count = 0;
+	ep0->dwc_ep.is_in = 1;
+	ep0->dwc_ep.dma_addr = pcd->setup_pkt_dma_handle;
+	dwc_otg_ep0_start_transfer(GET_CORE_IF(pcd), &ep0->dwc_ep);
+
+	/* Prepare for more SETUP Packets */
+	//ep0_out_start(GET_CORE_IF(pcd), pcd);
+}
+
+/**
+ * This function starts the Zero-Length Packet for the OUT status phase
+ * of a 2 stage control transfer.
+ */
+static inline void do_setup_out_status_phase(dwc_otg_pcd_t * pcd)
+{
+	dwc_otg_pcd_ep_t *ep0 = &pcd->ep0;
+	if (pcd->ep0state == EP0_STALL) {
+		DWC_DEBUGPL(DBG_PCD, "EP0 STALLED\n");
+		return;
+	}
+	pcd->ep0state = EP0_OUT_STATUS_PHASE;
+
+	DWC_DEBUGPL(DBG_PCD, "EP0 OUT ZLP\n");
+	ep0->dwc_ep.xfer_len = 0;
+	ep0->dwc_ep.xfer_count = 0;
+	ep0->dwc_ep.is_in = 0;
+	ep0->dwc_ep.dma_addr = pcd->setup_pkt_dma_handle;
+	dwc_otg_ep0_start_transfer(GET_CORE_IF(pcd), &ep0->dwc_ep);
+
+	/* Prepare for more SETUP Packets */
+	if (GET_CORE_IF(pcd)->dma_enable == 0) {
+		ep0_out_start(GET_CORE_IF(pcd), pcd);
+	}
+}
+
+/**
+ * Clear the EP halt (STALL) and if pending requests start the
+ * transfer.
+ */
+static inline void pcd_clear_halt(dwc_otg_pcd_t * pcd, dwc_otg_pcd_ep_t * ep)
+{
+	if (ep->dwc_ep.stall_clear_flag == 0)
+		dwc_otg_ep_clear_stall(GET_CORE_IF(pcd), &ep->dwc_ep);
+
+	/* Reactive the EP */
+	dwc_otg_ep_activate(GET_CORE_IF(pcd), &ep->dwc_ep);
+	if (ep->stopped) {
+		ep->stopped = 0;
+		/* If there is a request in the EP queue start it */
+
+		/** @todo FIXME: this causes an EP mismatch in DMA mode.
+		 * epmismatch not yet implemented. */
+
+		/*
+		 * Above fixme is solved by implmenting a tasklet to call the
+		 * start_next_request(), outside of interrupt context at some
+		 * time after the current time, after a clear-halt setup packet.
+		 * Still need to implement ep mismatch in the future if a gadget
+		 * ever uses more than one endpoint at once
+		 */
+		ep->queue_sof = 1;
+		DWC_TASK_SCHEDULE(pcd->start_xfer_tasklet);
+	}
+	/* Start Control Status Phase */
+	do_setup_in_status_phase(pcd);
+}
+
+/**
+ * This function is called when the SET_FEATURE TEST_MODE Setup packet
+ * is sent from the host.  The Device Control register is written with
+ * the Test Mode bits set to the specified Test Mode.  This is done as
+ * a tasklet so that the "Status" phase of the control transfer
+ * completes before transmitting the TEST packets.
+ *
+ * @todo This has not been tested since the tasklet struct was put
+ * into the PCD struct!
+ *
+ */
+void do_test_mode(void *data)
+{
+	dctl_data_t dctl;
+	dwc_otg_pcd_t *pcd = (dwc_otg_pcd_t *) data;
+	dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd);
+	int test_mode = pcd->test_mode;
+
+//        DWC_WARN("%s() has not been tested since being rewritten!\n", __func__);
+
+	dctl.d32 = DWC_READ_REG32(&core_if->dev_if->dev_global_regs->dctl);
+	switch (test_mode) {
+	case 1:		// TEST_J
+		dctl.b.tstctl = 1;
+		break;
+
+	case 2:		// TEST_K
+		dctl.b.tstctl = 2;
+		break;
+
+	case 3:		// TEST_SE0_NAK
+		dctl.b.tstctl = 3;
+		break;
+
+	case 4:		// TEST_PACKET
+		dctl.b.tstctl = 4;
+		break;
+
+	case 5:		// TEST_FORCE_ENABLE
+		dctl.b.tstctl = 5;
+		break;
+	}
+	DWC_WRITE_REG32(&core_if->dev_if->dev_global_regs->dctl, dctl.d32);
+}
+
+/**
+ * This function process the GET_STATUS Setup Commands.
+ */
+static inline void do_get_status(dwc_otg_pcd_t * pcd)
+{
+	usb_device_request_t ctrl = pcd->setup_pkt->req;
+	dwc_otg_pcd_ep_t *ep;
+	dwc_otg_pcd_ep_t *ep0 = &pcd->ep0;
+	uint16_t *status = pcd->status_buf;
+	dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd);
+
+#ifdef DEBUG_EP0
+	DWC_DEBUGPL(DBG_PCD,
+		    "GET_STATUS %02x.%02x v%04x i%04x l%04x\n",
+		    ctrl.bmRequestType, ctrl.bRequest,
+		    UGETW(ctrl.wValue), UGETW(ctrl.wIndex),
+		    UGETW(ctrl.wLength));
+#endif
+
+	switch (UT_GET_RECIPIENT(ctrl.bmRequestType)) {
+	case UT_DEVICE:
+		if(UGETW(ctrl.wIndex) == 0xF000) { /* OTG Status selector */
+			DWC_PRINTF("wIndex - %d\n", UGETW(ctrl.wIndex));
+			DWC_PRINTF("OTG VERSION - %d\n", core_if->otg_ver);
+			DWC_PRINTF("OTG CAP - %d, %d\n",
+				   core_if->core_params->otg_cap,
+						DWC_OTG_CAP_PARAM_HNP_SRP_CAPABLE);
+			if (core_if->otg_ver == 1
+			    && core_if->core_params->otg_cap ==
+			    DWC_OTG_CAP_PARAM_HNP_SRP_CAPABLE) {
+				uint8_t *otgsts = (uint8_t*)pcd->status_buf;
+				*otgsts = (core_if->otg_sts & 0x1);
+				pcd->ep0_pending = 1;
+				ep0->dwc_ep.start_xfer_buff =
+				    (uint8_t *) otgsts;
+				ep0->dwc_ep.xfer_buff = (uint8_t *) otgsts;
+				ep0->dwc_ep.dma_addr =
+				    pcd->status_buf_dma_handle;
+				ep0->dwc_ep.xfer_len = 1;
+				ep0->dwc_ep.xfer_count = 0;
+				ep0->dwc_ep.total_len = ep0->dwc_ep.xfer_len;
+				dwc_otg_ep0_start_transfer(GET_CORE_IF(pcd),
+							   &ep0->dwc_ep);
+				return;
+			} else {
+				ep0_do_stall(pcd, -DWC_E_NOT_SUPPORTED);
+				return;
+			}
+			break;
+		} else {
+			*status = 0x1;	/* Self powered */
+			*status |= pcd->remote_wakeup_enable << 1;
+			break;
+		}
+	case UT_INTERFACE:
+		*status = 0;
+		break;
+
+	case UT_ENDPOINT:
+		ep = get_ep_by_addr(pcd, UGETW(ctrl.wIndex));
+		if (ep == 0 || UGETW(ctrl.wLength) > 2) {
+			ep0_do_stall(pcd, -DWC_E_NOT_SUPPORTED);
+			return;
+		}
+		/** @todo check for EP stall */
+		*status = ep->stopped;
+		break;
+	}
+	pcd->ep0_pending = 1;
+	ep0->dwc_ep.start_xfer_buff = (uint8_t *) status;
+	ep0->dwc_ep.xfer_buff = (uint8_t *) status;
+	ep0->dwc_ep.dma_addr = pcd->status_buf_dma_handle;
+	ep0->dwc_ep.xfer_len = 2;
+	ep0->dwc_ep.xfer_count = 0;
+	ep0->dwc_ep.total_len = ep0->dwc_ep.xfer_len;
+	dwc_otg_ep0_start_transfer(GET_CORE_IF(pcd), &ep0->dwc_ep);
+}
+
+/**
+ * This function process the SET_FEATURE Setup Commands.
+ */
+static inline void do_set_feature(dwc_otg_pcd_t * pcd)
+{
+	dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd);
+	dwc_otg_core_global_regs_t *global_regs = core_if->core_global_regs;
+	usb_device_request_t ctrl = pcd->setup_pkt->req;
+	dwc_otg_pcd_ep_t *ep = 0;
+	int32_t otg_cap_param = core_if->core_params->otg_cap;
+	gotgctl_data_t gotgctl = {.d32 = 0 };
+
+	DWC_DEBUGPL(DBG_PCD, "SET_FEATURE:%02x.%02x v%04x i%04x l%04x\n",
+		    ctrl.bmRequestType, ctrl.bRequest,
+		    UGETW(ctrl.wValue), UGETW(ctrl.wIndex),
+		    UGETW(ctrl.wLength));
+	DWC_DEBUGPL(DBG_PCD, "otg_cap=%d\n", otg_cap_param);
+
+	switch (UT_GET_RECIPIENT(ctrl.bmRequestType)) {
+	case UT_DEVICE:
+		switch (UGETW(ctrl.wValue)) {
+		case UF_DEVICE_REMOTE_WAKEUP:
+			pcd->remote_wakeup_enable = 1;
+			break;
+
+		case UF_TEST_MODE:
+			/* Setup the Test Mode tasklet to do the Test
+			 * Packet generation after the SETUP Status
+			 * phase has completed. */
+
+			/** @todo This has not been tested since the
+			 * tasklet struct was put into the PCD
+			 * struct! */
+			pcd->test_mode = UGETW(ctrl.wIndex) >> 8;
+			DWC_TASK_SCHEDULE(pcd->test_mode_tasklet);
+			break;
+
+		case UF_DEVICE_B_HNP_ENABLE:
+			DWC_DEBUGPL(DBG_PCDV,
+				    "SET_FEATURE: USB_DEVICE_B_HNP_ENABLE\n");
+
+			/* dev may initiate HNP */
+			if (otg_cap_param == DWC_OTG_CAP_PARAM_HNP_SRP_CAPABLE) {
+				pcd->b_hnp_enable = 1;
+				dwc_otg_pcd_update_otg(pcd, 0);
+				DWC_DEBUGPL(DBG_PCD, "Request B HNP\n");
+				/**@todo Is the gotgctl.devhnpen cleared
+				 * by a USB Reset? */
+				gotgctl.b.devhnpen = 1;
+				gotgctl.b.hnpreq = 1;
+				DWC_WRITE_REG32(&global_regs->gotgctl,
+						gotgctl.d32);
+			} else {
+				ep0_do_stall(pcd, -DWC_E_NOT_SUPPORTED);
+				return;
+			}
+			break;
+
+		case UF_DEVICE_A_HNP_SUPPORT:
+			/* RH port supports HNP */
+			DWC_DEBUGPL(DBG_PCDV,
+				    "SET_FEATURE: USB_DEVICE_A_HNP_SUPPORT\n");
+			if (otg_cap_param == DWC_OTG_CAP_PARAM_HNP_SRP_CAPABLE) {
+				pcd->a_hnp_support = 1;
+				dwc_otg_pcd_update_otg(pcd, 0);
+			} else {
+				ep0_do_stall(pcd, -DWC_E_NOT_SUPPORTED);
+				return;
+			}
+			break;
+
+		case UF_DEVICE_A_ALT_HNP_SUPPORT:
+			/* other RH port does */
+			DWC_DEBUGPL(DBG_PCDV,
+				    "SET_FEATURE: USB_DEVICE_A_ALT_HNP_SUPPORT\n");
+			if (otg_cap_param == DWC_OTG_CAP_PARAM_HNP_SRP_CAPABLE) {
+				pcd->a_alt_hnp_support = 1;
+				dwc_otg_pcd_update_otg(pcd, 0);
+			} else {
+				ep0_do_stall(pcd, -DWC_E_NOT_SUPPORTED);
+				return;
+			}
+			break;
+
+		default:
+			ep0_do_stall(pcd, -DWC_E_NOT_SUPPORTED);
+			return;
+
+		}
+		do_setup_in_status_phase(pcd);
+		break;
+
+	case UT_INTERFACE:
+		do_gadget_setup(pcd, &ctrl);
+		break;
+
+	case UT_ENDPOINT:
+		if (UGETW(ctrl.wValue) == UF_ENDPOINT_HALT) {
+			ep = get_ep_by_addr(pcd, UGETW(ctrl.wIndex));
+			if (ep == 0) {
+				ep0_do_stall(pcd, -DWC_E_NOT_SUPPORTED);
+				return;
+			}
+			ep->stopped = 1;
+			dwc_otg_ep_set_stall(core_if, &ep->dwc_ep);
+		}
+		do_setup_in_status_phase(pcd);
+		break;
+	}
+}
+
+/**
+ * This function process the CLEAR_FEATURE Setup Commands.
+ */
+static inline void do_clear_feature(dwc_otg_pcd_t * pcd)
+{
+	usb_device_request_t ctrl = pcd->setup_pkt->req;
+	dwc_otg_pcd_ep_t *ep = 0;
+
+	DWC_DEBUGPL(DBG_PCD,
+		    "CLEAR_FEATURE:%02x.%02x v%04x i%04x l%04x\n",
+		    ctrl.bmRequestType, ctrl.bRequest,
+		    UGETW(ctrl.wValue), UGETW(ctrl.wIndex),
+		    UGETW(ctrl.wLength));
+
+	switch (UT_GET_RECIPIENT(ctrl.bmRequestType)) {
+	case UT_DEVICE:
+		switch (UGETW(ctrl.wValue)) {
+		case UF_DEVICE_REMOTE_WAKEUP:
+			pcd->remote_wakeup_enable = 0;
+			break;
+
+		case UF_TEST_MODE:
+			/** @todo Add CLEAR_FEATURE for TEST modes. */
+			break;
+
+		default:
+			ep0_do_stall(pcd, -DWC_E_NOT_SUPPORTED);
+			return;
+		}
+		do_setup_in_status_phase(pcd);
+		break;
+
+	case UT_ENDPOINT:
+		ep = get_ep_by_addr(pcd, UGETW(ctrl.wIndex));
+		if (ep == 0) {
+			ep0_do_stall(pcd, -DWC_E_NOT_SUPPORTED);
+			return;
+		}
+
+		pcd_clear_halt(pcd, ep);
+
+		break;
+	}
+}
+
+/**
+ * This function process the SET_ADDRESS Setup Commands.
+ */
+static inline void do_set_address(dwc_otg_pcd_t * pcd)
+{
+	dwc_otg_dev_if_t *dev_if = GET_CORE_IF(pcd)->dev_if;
+	usb_device_request_t ctrl = pcd->setup_pkt->req;
+
+	if (ctrl.bmRequestType == UT_DEVICE) {
+		dcfg_data_t dcfg = {.d32 = 0 };
+
+#ifdef DEBUG_EP0
+//                      DWC_DEBUGPL(DBG_PCDV, "SET_ADDRESS:%d\n", ctrl.wValue);
+#endif
+		dcfg.b.devaddr = UGETW(ctrl.wValue);
+		DWC_MODIFY_REG32(&dev_if->dev_global_regs->dcfg, 0, dcfg.d32);
+		do_setup_in_status_phase(pcd);
+	}
+}
+
+/**
+ *	This function processes SETUP commands. In Linux, the USB Command
+ *	processing is done in two places - the first being the PCD and the
+ *	second in the Gadget Driver (for example, the File-Backed Storage
+ *	Gadget Driver).
+ *
+ * <table>
+ * <tr><td>Command	</td><td>Driver </td><td>Description</td></tr>
+ *
+ * <tr><td>GET_STATUS </td><td>PCD </td><td>Command is processed as
+ * defined in chapter 9 of the USB 2.0 Specification chapter 9
+ * </td></tr>
+ *
+ * <tr><td>CLEAR_FEATURE </td><td>PCD </td><td>The Device and Endpoint
+ * requests are the ENDPOINT_HALT feature is procesed, all others the
+ * interface requests are ignored.</td></tr>
+ *
+ * <tr><td>SET_FEATURE </td><td>PCD </td><td>The Device and Endpoint
+ * requests are processed by the PCD.  Interface requests are passed
+ * to the Gadget Driver.</td></tr>
+ *
+ * <tr><td>SET_ADDRESS </td><td>PCD </td><td>Program the DCFG reg,
+ * with device address received </td></tr>
+ *
+ * <tr><td>GET_DESCRIPTOR </td><td>Gadget Driver </td><td>Return the
+ * requested descriptor</td></tr>
+ *
+ * <tr><td>SET_DESCRIPTOR </td><td>Gadget Driver </td><td>Optional -
+ * not implemented by any of the existing Gadget Drivers.</td></tr>
+ *
+ * <tr><td>SET_CONFIGURATION </td><td>Gadget Driver </td><td>Disable
+ * all EPs and enable EPs for new configuration.</td></tr>
+ *
+ * <tr><td>GET_CONFIGURATION </td><td>Gadget Driver </td><td>Return
+ * the current configuration</td></tr>
+ *
+ * <tr><td>SET_INTERFACE </td><td>Gadget Driver </td><td>Disable all
+ * EPs and enable EPs for new configuration.</td></tr>
+ *
+ * <tr><td>GET_INTERFACE </td><td>Gadget Driver </td><td>Return the
+ * current interface.</td></tr>
+ *
+ * <tr><td>SYNC_FRAME </td><td>PCD </td><td>Display debug
+ * message.</td></tr>
+ * </table>
+ *
+ * When the SETUP Phase Done interrupt occurs, the PCD SETUP commands are
+ * processed by pcd_setup. Calling the Function Driver's setup function from
+ * pcd_setup processes the gadget SETUP commands.
+ */
+static inline void pcd_setup(dwc_otg_pcd_t * pcd)
+{
+	dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd);
+	dwc_otg_dev_if_t *dev_if = core_if->dev_if;
+	usb_device_request_t ctrl = pcd->setup_pkt->req;
+	dwc_otg_pcd_ep_t *ep0 = &pcd->ep0;
+
+	deptsiz0_data_t doeptsize0 = {.d32 = 0 };
+
+#ifdef DWC_UTE_CFI
+	int retval = 0;
+	struct cfi_usb_ctrlrequest cfi_req;
+#endif
+
+	doeptsize0.d32 = DWC_READ_REG32(&dev_if->out_ep_regs[0]->doeptsiz);
+
+	/** In BDMA more then 1 setup packet is not supported till 3.00a */
+	if (core_if->dma_enable && core_if->dma_desc_enable == 0
+	    && (doeptsize0.b.supcnt < 2)
+	    && (core_if->snpsid < OTG_CORE_REV_2_94a)) {
+		DWC_ERROR
+		    ("\n\n-----------	 CANNOT handle > 1 setup packet in DMA mode\n\n");
+	}
+	if ((core_if->snpsid >= OTG_CORE_REV_3_00a)
+	    && (core_if->dma_enable == 1) && (core_if->dma_desc_enable == 0)) {
+		ctrl =
+		    (pcd->setup_pkt +
+		     (3 - doeptsize0.b.supcnt - 1 +
+		      ep0->dwc_ep.stp_rollover))->req;
+	}
+#ifdef DEBUG_EP0
+	DWC_DEBUGPL(DBG_PCD, "SETUP %02x.%02x v%04x i%04x l%04x\n",
+		    ctrl.bmRequestType, ctrl.bRequest,
+		    UGETW(ctrl.wValue), UGETW(ctrl.wIndex),
+		    UGETW(ctrl.wLength));
+#endif
+
+	/* Clean up the request queue */
+	dwc_otg_request_nuke(ep0);
+	ep0->stopped = 0;
+
+	if (ctrl.bmRequestType & UE_DIR_IN) {
+		ep0->dwc_ep.is_in = 1;
+		pcd->ep0state = EP0_IN_DATA_PHASE;
+	} else {
+		ep0->dwc_ep.is_in = 0;
+		pcd->ep0state = EP0_OUT_DATA_PHASE;
+	}
+
+	if (UGETW(ctrl.wLength) == 0) {
+		ep0->dwc_ep.is_in = 1;
+		pcd->ep0state = EP0_IN_STATUS_PHASE;
+	}
+
+	if (UT_GET_TYPE(ctrl.bmRequestType) != UT_STANDARD) {
+
+#ifdef DWC_UTE_CFI
+		DWC_MEMCPY(&cfi_req, &ctrl, sizeof(usb_device_request_t));
+
+		//printk(KERN_ALERT "CFI: req_type=0x%02x; req=0x%02x\n",
+				ctrl.bRequestType, ctrl.bRequest);
+		if (UT_GET_TYPE(cfi_req.bRequestType) == UT_VENDOR) {
+			if (cfi_req.bRequest > 0xB0 && cfi_req.bRequest < 0xBF) {
+				retval = cfi_setup(pcd, &cfi_req);
+				if (retval < 0) {
+					ep0_do_stall(pcd, retval);
+					pcd->ep0_pending = 0;
+					return;
+				}
+
+				/* if need gadget setup then call it and check the retval */
+				if (pcd->cfi->need_gadget_att) {
+					retval =
+					    cfi_gadget_setup(pcd,
+							     &pcd->
+							     cfi->ctrl_req);
+					if (retval < 0) {
+						pcd->ep0_pending = 0;
+						return;
+					}
+				}
+
+				if (pcd->cfi->need_status_in_complete) {
+					do_setup_in_status_phase(pcd);
+				}
+				return;
+			}
+		}
+#endif
+
+		/* handle non-standard (class/vendor) requests in the gadget driver */
+		do_gadget_setup(pcd, &ctrl);
+		return;
+	}
+
+	/** @todo NGS: Handle bad setup packet? */
+
+///////////////////////////////////////////
+//// --- Standard Request handling --- ////
+
+	switch (ctrl.bRequest) {
+	case UR_GET_STATUS:
+		do_get_status(pcd);
+		break;
+
+	case UR_CLEAR_FEATURE:
+		do_clear_feature(pcd);
+		break;
+
+	case UR_SET_FEATURE:
+		do_set_feature(pcd);
+		break;
+
+	case UR_SET_ADDRESS:
+		do_set_address(pcd);
+		break;
+
+	case UR_SET_INTERFACE:
+	case UR_SET_CONFIG:
+//              _pcd->request_config = 1;       /* Configuration changed */
+		do_gadget_setup(pcd, &ctrl);
+		break;
+
+	case UR_SYNCH_FRAME:
+		do_gadget_setup(pcd, &ctrl);
+		break;
+
+	default:
+		/* Call the Gadget Driver's setup functions */
+		do_gadget_setup(pcd, &ctrl);
+		break;
+	}
+}
+
+/**
+ * This function completes the ep0 control transfer.
+ */
+static int32_t ep0_complete_request(dwc_otg_pcd_ep_t * ep)
+{
+	dwc_otg_core_if_t *core_if = GET_CORE_IF(ep->pcd);
+	dwc_otg_dev_if_t *dev_if = core_if->dev_if;
+	dwc_otg_dev_in_ep_regs_t *in_ep_regs =
+	    dev_if->in_ep_regs[ep->dwc_ep.num];
+#ifdef DEBUG_EP0
+	dwc_otg_dev_out_ep_regs_t *out_ep_regs =
+	    dev_if->out_ep_regs[ep->dwc_ep.num];
+#endif
+	deptsiz0_data_t deptsiz;
+	dev_dma_desc_sts_t desc_sts;
+	dwc_otg_pcd_request_t *req;
+	int is_last = 0;
+	dwc_otg_pcd_t *pcd = ep->pcd;
+
+#ifdef DWC_UTE_CFI
+	struct cfi_usb_ctrlrequest *ctrlreq;
+	int retval = -DWC_E_NOT_SUPPORTED;
+#endif
+
+        desc_sts.b.bytes = 0;
+
+	if (pcd->ep0_pending && DWC_CIRCLEQ_EMPTY(&ep->queue)) {
+		if (ep->dwc_ep.is_in) {
+#ifdef DEBUG_EP0
+			DWC_DEBUGPL(DBG_PCDV, "Do setup OUT status phase\n");
+#endif
+			do_setup_out_status_phase(pcd);
+		} else {
+#ifdef DEBUG_EP0
+			DWC_DEBUGPL(DBG_PCDV, "Do setup IN status phase\n");
+#endif
+
+#ifdef DWC_UTE_CFI
+			ctrlreq = &pcd->cfi->ctrl_req;
+
+			if (UT_GET_TYPE(ctrlreq->bRequestType) == UT_VENDOR) {
+				if (ctrlreq->bRequest > 0xB0
+				    && ctrlreq->bRequest < 0xBF) {
+
+					/* Return if the PCD failed to handle the request */
+					if ((retval =
+					     pcd->cfi->ops.
+					     ctrl_write_complete(pcd->cfi,
+								 pcd)) < 0) {
+						CFI_INFO
+						    ("ERROR setting a new value in the PCD(%d)\n",
+						     retval);
+						ep0_do_stall(pcd, retval);
+						pcd->ep0_pending = 0;
+						return 0;
+					}
+
+					/* If the gadget needs to be notified on the request */
+					if (pcd->cfi->need_gadget_att == 1) {
+						//retval = do_gadget_setup(pcd, &pcd->cfi->ctrl_req);
+						retval =
+						    cfi_gadget_setup(pcd,
+								     &pcd->cfi->
+								     ctrl_req);
+
+						/* Return from the function if the gadget failed to process
+						 * the request properly - this should never happen !!!
+						 */
+						if (retval < 0) {
+							CFI_INFO
+							    ("ERROR setting a new value in the gadget(%d)\n",
+							     retval);
+							pcd->ep0_pending = 0;
+							return 0;
+						}
+					}
+
+					CFI_INFO("%s: RETVAL=%d\n", __func__,
+						 retval);
+					/* If we hit here then the PCD and the gadget has properly
+					 * handled the request - so send the ZLP IN to the host.
+					 */
+					/* @todo: MAS - decide whether we need to start the setup
+					 * stage based on the need_setup value of the cfi object
+					 */
+					do_setup_in_status_phase(pcd);
+					pcd->ep0_pending = 0;
+					return 1;
+				}
+			}
+#endif
+
+			do_setup_in_status_phase(pcd);
+		}
+		pcd->ep0_pending = 0;
+		return 1;
+	}
+
+	if (DWC_CIRCLEQ_EMPTY(&ep->queue)) {
+		return 0;
+	}
+	req = DWC_CIRCLEQ_FIRST(&ep->queue);
+
+	if (pcd->ep0state == EP0_OUT_STATUS_PHASE
+	    || pcd->ep0state == EP0_IN_STATUS_PHASE) {
+		is_last = 1;
+	} else if (ep->dwc_ep.is_in) {
+		deptsiz.d32 = DWC_READ_REG32(&in_ep_regs->dieptsiz);
+		if (core_if->dma_desc_enable != 0)
+			desc_sts = dev_if->in_desc_addr->status;
+#ifdef DEBUG_EP0
+		DWC_DEBUGPL(DBG_PCDV, "%d len=%d  xfersize=%d pktcnt=%d\n",
+			    ep->dwc_ep.num, ep->dwc_ep.xfer_len,
+			    deptsiz.b.xfersize, deptsiz.b.pktcnt);
+#endif
+
+		if (((core_if->dma_desc_enable == 0)
+		     && (deptsiz.b.xfersize == 0))
+		    || ((core_if->dma_desc_enable != 0)
+			&& (desc_sts.b.bytes == 0))) {
+			req->actual = ep->dwc_ep.xfer_count;
+			/* Is a Zero Len Packet needed? */
+			if (req->sent_zlp) {
+#ifdef DEBUG_EP0
+				DWC_DEBUGPL(DBG_PCD, "Setup Rx ZLP\n");
+#endif
+				req->sent_zlp = 0;
+			}
+			do_setup_out_status_phase(pcd);
+		}
+	} else {
+		/* ep0-OUT */
+#ifdef DEBUG_EP0
+		deptsiz.d32 = DWC_READ_REG32(&out_ep_regs->doeptsiz);
+		DWC_DEBUGPL(DBG_PCDV, "%d len=%d xsize=%d pktcnt=%d\n",
+			    ep->dwc_ep.num, ep->dwc_ep.xfer_len,
+			    deptsiz.b.xfersize, deptsiz.b.pktcnt);
+#endif
+		req->actual = ep->dwc_ep.xfer_count;
+
+		/* Is a Zero Len Packet needed? */
+		if (req->sent_zlp) {
+#ifdef DEBUG_EP0
+			DWC_DEBUGPL(DBG_PCDV, "Setup Tx ZLP\n");
+#endif
+			req->sent_zlp = 0;
+		}
+		/* For older cores do setup in status phase in Slave/BDMA modes,
+		 * starting from 3.00 do that only in slave, and for DMA modes
+		 * just re-enable ep 0 OUT here*/
+		if (core_if->dma_enable == 0
+		    || (core_if->dma_desc_enable == 0
+			&& core_if->snpsid <= OTG_CORE_REV_2_94a)) {
+			do_setup_in_status_phase(pcd);
+		} else if (core_if->snpsid >= OTG_CORE_REV_3_00a) {
+			DWC_DEBUGPL(DBG_PCDV,
+				    "Enable out ep before in status phase\n");
+			ep0_out_start(core_if, pcd);
+		}
+	}
+
+	/* Complete the request */
+	if (is_last) {
+		dwc_otg_request_done(ep, req, 0);
+		ep->dwc_ep.start_xfer_buff = 0;
+		ep->dwc_ep.xfer_buff = 0;
+		ep->dwc_ep.xfer_len = 0;
+		return 1;
+	}
+	return 0;
+}
+
+#ifdef DWC_UTE_CFI
+/**
+ * This function calculates traverses all the CFI DMA descriptors and
+ * and accumulates the bytes that are left to be transfered.
+ *
+ * @return The total bytes left to transfered, or a negative value as failure
+ */
+static inline int cfi_calc_desc_residue(dwc_otg_pcd_ep_t * ep)
+{
+	int32_t ret = 0;
+	int i;
+	struct dwc_otg_dma_desc *ddesc = NULL;
+	struct cfi_ep *cfiep;
+
+	/* See if the pcd_ep has its respective cfi_ep mapped */
+	cfiep = get_cfi_ep_by_pcd_ep(ep->pcd->cfi, ep);
+	if (!cfiep) {
+		CFI_INFO("%s: Failed to find ep\n", __func__);
+		return -1;
+	}
+
+	ddesc = ep->dwc_ep.descs;
+
+	for (i = 0; (i < cfiep->desc_count) && (i < MAX_DMA_DESCS_PER_EP); i++) {
+
+#if defined(PRINT_CFI_DMA_DESCS)
+		print_desc(ddesc, ep->ep.name, i);
+#endif
+		ret += ddesc->status.b.bytes;
+		ddesc++;
+	}
+
+	if (ret)
+		CFI_INFO("!!!!!!!!!! WARNING (%s) - residue=%d\n", __func__,
+			 ret);
+
+	return ret;
+}
+#endif
+
+/**
+ * This function completes the request for the EP. If there are
+ * additional requests for the EP in the queue they will be started.
+ */
+static void complete_ep(dwc_otg_pcd_ep_t * ep)
+{
+	dwc_otg_core_if_t *core_if = GET_CORE_IF(ep->pcd);
+	struct device *dev = dwc_otg_pcd_to_dev(ep->pcd);
+	dwc_otg_dev_if_t *dev_if = core_if->dev_if;
+	dwc_otg_dev_in_ep_regs_t *in_ep_regs =
+	    dev_if->in_ep_regs[ep->dwc_ep.num];
+	deptsiz_data_t deptsiz;
+	dev_dma_desc_sts_t desc_sts;
+	dwc_otg_pcd_request_t *req = 0;
+	dwc_otg_dev_dma_desc_t *dma_desc;
+	uint32_t byte_count = 0;
+	int is_last = 0;
+	int i;
+
+	DWC_DEBUGPL(DBG_PCDV, "%s() %d-%s\n", __func__, ep->dwc_ep.num,
+		    (ep->dwc_ep.is_in ? "IN" : "OUT"));
+
+	/* Get any pending requests */
+	if (!DWC_CIRCLEQ_EMPTY(&ep->queue)) {
+		req = DWC_CIRCLEQ_FIRST(&ep->queue);
+		if (!req) {
+			DWC_PRINTF("complete_ep 0x%p, req = NULL!\n", ep);
+			return;
+		}
+	} else {
+		DWC_PRINTF("complete_ep 0x%p, ep->queue empty!\n", ep);
+		return;
+	}
+
+	DWC_DEBUGPL(DBG_PCD, "Requests %d\n", ep->pcd->request_pending);
+
+	if (ep->dwc_ep.is_in) {
+		deptsiz.d32 = DWC_READ_REG32(&in_ep_regs->dieptsiz);
+
+		if (core_if->dma_enable) {
+			if (core_if->dma_desc_enable == 0) {
+				if (deptsiz.b.xfersize == 0
+				    && deptsiz.b.pktcnt == 0) {
+					byte_count =
+					    ep->dwc_ep.xfer_len -
+					    ep->dwc_ep.xfer_count;
+
+					ep->dwc_ep.xfer_buff += byte_count;
+					ep->dwc_ep.dma_addr += byte_count;
+					ep->dwc_ep.xfer_count += byte_count;
+
+					DWC_DEBUGPL(DBG_PCDV,
+						    "%d-%s len=%d  xfersize=%d pktcnt=%d\n",
+						    ep->dwc_ep.num,
+						    (ep->dwc_ep.
+						     is_in ? "IN" : "OUT"),
+						    ep->dwc_ep.xfer_len,
+						    deptsiz.b.xfersize,
+						    deptsiz.b.pktcnt);
+
+					if (ep->dwc_ep.xfer_len <
+					    ep->dwc_ep.total_len) {
+						dwc_otg_ep_start_transfer
+						    (core_if, &ep->dwc_ep);
+					} else if (ep->dwc_ep.sent_zlp) {
+						/*
+						 * This fragment of code should initiate 0
+						 * length transfer in case if it is queued
+						 * a transfer with size divisible to EPs max
+						 * packet size and with usb_request zero field
+						 * is set, which means that after data is transfered,
+						 * it is also should be transfered
+						 * a 0 length packet at the end. For Slave and
+						 * Buffer DMA modes in this case SW has
+						 * to initiate 2 transfers one with transfer size,
+						 * and the second with 0 size. For Descriptor
+						 * DMA mode SW is able to initiate a transfer,
+						 * which will handle all the packets including
+						 * the last  0 length.
+						 */
+						ep->dwc_ep.sent_zlp = 0;
+						dwc_otg_ep_start_zl_transfer
+						    (core_if, &ep->dwc_ep);
+					} else {
+						is_last = 1;
+					}
+				} else {
+					if (ep->dwc_ep.type ==
+					    DWC_OTG_EP_TYPE_ISOC) {
+						req->actual = 0;
+						dwc_otg_request_done(ep, req, 0);
+
+						ep->dwc_ep.start_xfer_buff = 0;
+						ep->dwc_ep.xfer_buff = 0;
+						ep->dwc_ep.xfer_len = 0;
+
+						/* If there is a request in the queue start it. */
+						start_next_request(ep);
+					} else
+						DWC_WARN
+						("Incomplete transfer (%d - %s [siz=%d pkt=%d])\n",
+						ep->dwc_ep.num,
+						(ep->dwc_ep.is_in ? "IN" : "OUT"),
+						deptsiz.b.xfersize,
+						deptsiz.b.pktcnt);
+				}
+			} else {
+				dma_desc = ep->dwc_ep.desc_addr;
+				byte_count = 0;
+				ep->dwc_ep.sent_zlp = 0;
+
+#ifdef DWC_UTE_CFI
+				CFI_INFO("%s: BUFFER_MODE=%d\n", __func__,
+					 ep->dwc_ep.buff_mode);
+				if (ep->dwc_ep.buff_mode != BM_STANDARD) {
+					int residue;
+
+					residue = cfi_calc_desc_residue(ep);
+					if (residue < 0)
+						return;
+
+					byte_count = residue;
+				} else {
+#endif
+					for (i = 0; i < ep->dwc_ep.desc_cnt;
+					     ++i) {
+					desc_sts = dma_desc->status;
+					byte_count += desc_sts.b.bytes;
+					dma_desc++;
+				}
+#ifdef DWC_UTE_CFI
+				}
+#endif
+				if (byte_count == 0) {
+					ep->dwc_ep.xfer_count =
+					    ep->dwc_ep.total_len;
+					is_last = 1;
+				} else {
+					DWC_WARN("Incomplete transfer\n");
+				}
+			}
+		} else {
+			if (deptsiz.b.xfersize == 0 && deptsiz.b.pktcnt == 0) {
+				DWC_DEBUGPL(DBG_PCDV,
+					    "%d-%s len=%d  xfersize=%d pktcnt=%d\n",
+					    ep->dwc_ep.num,
+					    ep->dwc_ep.is_in ? "IN" : "OUT",
+					    ep->dwc_ep.xfer_len,
+					    deptsiz.b.xfersize,
+					    deptsiz.b.pktcnt);
+
+				/*      Check if the whole transfer was completed,
+				 *      if no, setup transfer for next portion of data
+				 */
+				if (ep->dwc_ep.xfer_len < ep->dwc_ep.total_len) {
+					dwc_otg_ep_start_transfer(core_if,
+								  &ep->dwc_ep);
+				} else if (ep->dwc_ep.sent_zlp) {
+					/*
+					 * This fragment of code should initiate 0
+					 * length trasfer in case if it is queued
+					 * a trasfer with size divisible to EPs max
+					 * packet size and with usb_request zero field
+					 * is set, which means that after data is transfered,
+					 * it is also should be transfered
+					 * a 0 length packet at the end. For Slave and
+					 * Buffer DMA modes in this case SW has
+					 * to initiate 2 transfers one with transfer size,
+					 * and the second with 0 size. For Desriptor
+					 * DMA mode SW is able to initiate a transfer,
+					 * which will handle all the packets including
+					 * the last  0 legth.
+					 */
+					ep->dwc_ep.sent_zlp = 0;
+					dwc_otg_ep_start_zl_transfer(core_if,
+								     &ep->dwc_ep);
+				} else {
+					is_last = 1;
+				}
+			} else {
+				DWC_WARN
+				    ("Incomplete transfer (%d-%s [siz=%d pkt=%d])\n",
+				     ep->dwc_ep.num,
+				     (ep->dwc_ep.is_in ? "IN" : "OUT"),
+				     deptsiz.b.xfersize, deptsiz.b.pktcnt);
+			}
+		}
+	} else {
+		dwc_otg_dev_out_ep_regs_t *out_ep_regs =
+		    dev_if->out_ep_regs[ep->dwc_ep.num];
+		desc_sts.d32 = 0;
+		if (core_if->dma_enable) {
+			if (core_if->dma_desc_enable) {
+				dma_desc = ep->dwc_ep.desc_addr;
+				byte_count = 0;
+				ep->dwc_ep.sent_zlp = 0;
+
+#ifdef DWC_UTE_CFI
+				CFI_INFO("%s: BUFFER_MODE=%d\n", __func__,
+					 ep->dwc_ep.buff_mode);
+				if (ep->dwc_ep.buff_mode != BM_STANDARD) {
+					int residue;
+					residue = cfi_calc_desc_residue(ep);
+					if (residue < 0)
+						return;
+					byte_count = residue;
+				} else {
+#endif
+
+					for (i = 0; i < ep->dwc_ep.desc_cnt;
+					     ++i) {
+						desc_sts = dma_desc->status;
+						byte_count += desc_sts.b.bytes;
+						dma_desc++;
+					}
+
+#ifdef DWC_UTE_CFI
+				}
+#endif
+				/* Checking for interrupt Out transfers with not
+				 * dword aligned mps sizes
+				 */
+				if (ep->dwc_ep.type == DWC_OTG_EP_TYPE_INTR &&
+							(ep->dwc_ep.maxpacket%4)) {
+					ep->dwc_ep.xfer_count =
+					    ep->dwc_ep.total_len - byte_count;
+					if ((ep->dwc_ep.xfer_len %
+					     ep->dwc_ep.maxpacket)
+					    && (ep->dwc_ep.xfer_len /
+						ep->dwc_ep.maxpacket <
+						MAX_DMA_DESC_CNT))
+						ep->dwc_ep.xfer_len -=
+						    (ep->dwc_ep.desc_cnt -
+						     1) * ep->dwc_ep.maxpacket +
+						    ep->dwc_ep.xfer_len %
+						    ep->dwc_ep.maxpacket;
+					else
+						ep->dwc_ep.xfer_len -=
+						    ep->dwc_ep.desc_cnt *
+						    ep->dwc_ep.maxpacket;
+					if (ep->dwc_ep.xfer_len > 0) {
+						dwc_otg_ep_start_transfer
+						    (core_if, &ep->dwc_ep);
+					} else {
+						is_last = 1;
+					}
+				} else {
+					ep->dwc_ep.xfer_count =
+					    ep->dwc_ep.total_len - byte_count +
+					    ((4 -
+					      (ep->dwc_ep.
+					       total_len & 0x3)) & 0x3);
+					is_last = 1;
+				}
+			} else {
+				deptsiz.d32 = 0;
+				deptsiz.d32 =
+				    DWC_READ_REG32(&out_ep_regs->doeptsiz);
+
+				byte_count = (ep->dwc_ep.xfer_len -
+					      ep->dwc_ep.xfer_count -
+					      deptsiz.b.xfersize);
+				ep->dwc_ep.xfer_buff += byte_count;
+				ep->dwc_ep.dma_addr += byte_count;
+				ep->dwc_ep.xfer_count += byte_count;
+
+				/*      Check if the whole transfer was completed,
+				 *      if no, setup transfer for next portion of data
+				 */
+				if (ep->dwc_ep.xfer_len < ep->dwc_ep.total_len) {
+					dwc_otg_ep_start_transfer(core_if,
+								  &ep->dwc_ep);
+				} else if (ep->dwc_ep.sent_zlp) {
+					/*
+					 * This fragment of code should initiate 0
+					 * length trasfer in case if it is queued
+					 * a trasfer with size divisible to EPs max
+					 * packet size and with usb_request zero field
+					 * is set, which means that after data is transfered,
+					 * it is also should be transfered
+					 * a 0 length packet at the end. For Slave and
+					 * Buffer DMA modes in this case SW has
+					 * to initiate 2 transfers one with transfer size,
+					 * and the second with 0 size. For Desriptor
+					 * DMA mode SW is able to initiate a transfer,
+					 * which will handle all the packets including
+					 * the last  0 legth.
+					 */
+					ep->dwc_ep.sent_zlp = 0;
+					dwc_otg_ep_start_zl_transfer(core_if,
+								     &ep->dwc_ep);
+				} else {
+					is_last = 1;
+				}
+			}
+		} else {
+			/*      Check if the whole transfer was completed,
+			 *      if no, setup transfer for next portion of data
+			 */
+			if (ep->dwc_ep.xfer_len < ep->dwc_ep.total_len) {
+				dwc_otg_ep_start_transfer(core_if, &ep->dwc_ep);
+			} else if (ep->dwc_ep.sent_zlp) {
+				/*
+				 * This fragment of code should initiate 0
+				 * length transfer in case if it is queued
+				 * a transfer with size divisible to EPs max
+				 * packet size and with usb_request zero field
+				 * is set, which means that after data is transfered,
+				 * it is also should be transfered
+				 * a 0 length packet at the end. For Slave and
+				 * Buffer DMA modes in this case SW has
+				 * to initiate 2 transfers one with transfer size,
+				 * and the second with 0 size. For Descriptor
+				 * DMA mode SW is able to initiate a transfer,
+				 * which will handle all the packets including
+				 * the last  0 length.
+				 */
+				ep->dwc_ep.sent_zlp = 0;
+				dwc_otg_ep_start_zl_transfer(core_if,
+							     &ep->dwc_ep);
+			} else {
+				is_last = 1;
+			}
+		}
+
+		DWC_DEBUGPL(DBG_PCDV,
+			    "addr %p,	 %d-%s len=%d cnt=%d xsize=%d pktcnt=%d\n",
+			    &out_ep_regs->doeptsiz, ep->dwc_ep.num,
+			    ep->dwc_ep.is_in ? "IN" : "OUT",
+			    ep->dwc_ep.xfer_len, ep->dwc_ep.xfer_count,
+			    deptsiz.b.xfersize, deptsiz.b.pktcnt);
+	}
+
+	/* Complete the request */
+	if (is_last) {
+#ifdef DWC_UTE_CFI
+		if (ep->dwc_ep.buff_mode != BM_STANDARD) {
+			req->actual = ep->dwc_ep.cfi_req_len - byte_count;
+		} else {
+#endif
+			req->actual = ep->dwc_ep.xfer_count;
+#ifdef DWC_UTE_CFI
+		}
+#endif
+		if (req->dw_align_buf) {
+			if (!ep->dwc_ep.is_in) {
+				dwc_memcpy(req->buf, req->dw_align_buf, req->length);
+			}
+			DWC_DMA_FREE(dev, req->length, req->dw_align_buf,
+				     req->dw_align_buf_dma);
+		}
+
+		dwc_otg_request_done(ep, req, 0);
+
+		ep->dwc_ep.start_xfer_buff = 0;
+		ep->dwc_ep.xfer_buff = 0;
+		ep->dwc_ep.xfer_len = 0;
+
+		/* If there is a request in the queue start it. */
+		start_next_request(ep);
+	}
+}
+
+#ifdef DWC_EN_ISOC
+
+/**
+ * This function BNA interrupt for Isochronous EPs
+ *
+ */
+static void dwc_otg_pcd_handle_iso_bna(dwc_otg_pcd_ep_t * ep)
+{
+	dwc_ep_t *dwc_ep = &ep->dwc_ep;
+	volatile uint32_t *addr;
+	depctl_data_t depctl = {.d32 = 0 };
+	dwc_otg_pcd_t *pcd = ep->pcd;
+	dwc_otg_dev_dma_desc_t *dma_desc;
+	int i;
+
+	dma_desc =
+	    dwc_ep->iso_desc_addr + dwc_ep->desc_cnt * (dwc_ep->proc_buf_num);
+
+	if (dwc_ep->is_in) {
+		dev_dma_desc_sts_t sts = {.d32 = 0 };
+		for (i = 0; i < dwc_ep->desc_cnt; ++i, ++dma_desc) {
+			sts.d32 = dma_desc->status.d32;
+			sts.b_iso_in.bs = BS_HOST_READY;
+			dma_desc->status.d32 = sts.d32;
+		}
+	} else {
+		dev_dma_desc_sts_t sts = {.d32 = 0 };
+		for (i = 0; i < dwc_ep->desc_cnt; ++i, ++dma_desc) {
+			sts.d32 = dma_desc->status.d32;
+			sts.b_iso_out.bs = BS_HOST_READY;
+			dma_desc->status.d32 = sts.d32;
+		}
+	}
+
+	if (dwc_ep->is_in == 0) {
+		addr =
+		    &GET_CORE_IF(pcd)->dev_if->out_ep_regs[dwc_ep->
+							   num]->doepctl;
+	} else {
+		addr =
+		    &GET_CORE_IF(pcd)->dev_if->in_ep_regs[dwc_ep->num]->diepctl;
+	}
+	depctl.b.epena = 1;
+	DWC_MODIFY_REG32(addr, depctl.d32, depctl.d32);
+}
+
+/**
+ * This function sets latest iso packet information(non-PTI mode)
+ *
+ * @param core_if Programming view of DWC_otg controller.
+ * @param ep The EP to start the transfer on.
+ *
+ */
+void set_current_pkt_info(dwc_otg_core_if_t * core_if, dwc_ep_t * ep)
+{
+	deptsiz_data_t deptsiz = {.d32 = 0 };
+	dma_addr_t dma_addr;
+	uint32_t offset;
+
+	if (ep->proc_buf_num)
+		dma_addr = ep->dma_addr1;
+	else
+		dma_addr = ep->dma_addr0;
+
+	if (ep->is_in) {
+		deptsiz.d32 =
+		    DWC_READ_REG32(&core_if->dev_if->
+				   in_ep_regs[ep->num]->dieptsiz);
+		offset = ep->data_per_frame;
+	} else {
+		deptsiz.d32 =
+		    DWC_READ_REG32(&core_if->dev_if->
+				   out_ep_regs[ep->num]->doeptsiz);
+		offset =
+		    ep->data_per_frame +
+		    (0x4 & (0x4 - (ep->data_per_frame & 0x3)));
+	}
+
+	if (!deptsiz.b.xfersize) {
+		ep->pkt_info[ep->cur_pkt].length = ep->data_per_frame;
+		ep->pkt_info[ep->cur_pkt].offset =
+		    ep->cur_pkt_dma_addr - dma_addr;
+		ep->pkt_info[ep->cur_pkt].status = 0;
+	} else {
+		ep->pkt_info[ep->cur_pkt].length = ep->data_per_frame;
+		ep->pkt_info[ep->cur_pkt].offset =
+		    ep->cur_pkt_dma_addr - dma_addr;
+		ep->pkt_info[ep->cur_pkt].status = -DWC_E_NO_DATA;
+	}
+	ep->cur_pkt_addr += offset;
+	ep->cur_pkt_dma_addr += offset;
+	ep->cur_pkt++;
+}
+
+/**
+ * This function sets latest iso packet information(DDMA mode)
+ *
+ * @param core_if Programming view of DWC_otg controller.
+ * @param dwc_ep The EP to start the transfer on.
+ *
+ */
+static void set_ddma_iso_pkts_info(dwc_otg_core_if_t * core_if,
+				   dwc_ep_t * dwc_ep)
+{
+	dwc_otg_dev_dma_desc_t *dma_desc;
+	dev_dma_desc_sts_t sts = {.d32 = 0 };
+	iso_pkt_info_t *iso_packet;
+	uint32_t data_per_desc;
+	uint32_t offset;
+	int i, j;
+
+	iso_packet = dwc_ep->pkt_info;
+
+	/** Reinit closed DMA Descriptors*/
+	/** ISO OUT EP */
+	if (dwc_ep->is_in == 0) {
+		dma_desc =
+		    dwc_ep->iso_desc_addr +
+		    dwc_ep->desc_cnt * dwc_ep->proc_buf_num;
+		offset = 0;
+
+		for (i = 0; i < dwc_ep->desc_cnt - dwc_ep->pkt_per_frm;
+		     i += dwc_ep->pkt_per_frm) {
+			for (j = 0; j < dwc_ep->pkt_per_frm; ++j) {
+				data_per_desc =
+				    ((j + 1) * dwc_ep->maxpacket >
+				     dwc_ep->
+				     data_per_frame) ? dwc_ep->data_per_frame -
+				    j * dwc_ep->maxpacket : dwc_ep->maxpacket;
+				data_per_desc +=
+				    (data_per_desc % 4) ? (4 -
+							   data_per_desc %
+							   4) : 0;
+
+				sts.d32 = dma_desc->status.d32;
+
+				/* Write status in iso_packet_decsriptor  */
+				iso_packet->status =
+				    sts.b_iso_out.rxsts +
+				    (sts.b_iso_out.bs ^ BS_DMA_DONE);
+				if (iso_packet->status) {
+					iso_packet->status = -DWC_E_NO_DATA;
+				}
+
+				/* Received data length */
+				if (!sts.b_iso_out.rxbytes) {
+					iso_packet->length =
+					    data_per_desc -
+					    sts.b_iso_out.rxbytes;
+				} else {
+					iso_packet->length =
+					    data_per_desc -
+					    sts.b_iso_out.rxbytes + (4 -
+								     dwc_ep->data_per_frame
+								     % 4);
+				}
+
+				iso_packet->offset = offset;
+
+				offset += data_per_desc;
+				dma_desc++;
+				iso_packet++;
+			}
+		}
+
+		for (j = 0; j < dwc_ep->pkt_per_frm - 1; ++j) {
+			data_per_desc =
+			    ((j + 1) * dwc_ep->maxpacket >
+			     dwc_ep->data_per_frame) ? dwc_ep->data_per_frame -
+			    j * dwc_ep->maxpacket : dwc_ep->maxpacket;
+			data_per_desc +=
+			    (data_per_desc % 4) ? (4 - data_per_desc % 4) : 0;
+
+			sts.d32 = dma_desc->status.d32;
+
+			/* Write status in iso_packet_decsriptor  */
+			iso_packet->status =
+			    sts.b_iso_out.rxsts +
+			    (sts.b_iso_out.bs ^ BS_DMA_DONE);
+			if (iso_packet->status) {
+				iso_packet->status = -DWC_E_NO_DATA;
+			}
+
+			/* Received data length */
+			iso_packet->length =
+			    dwc_ep->data_per_frame - sts.b_iso_out.rxbytes;
+
+			iso_packet->offset = offset;
+
+			offset += data_per_desc;
+			iso_packet++;
+			dma_desc++;
+		}
+
+		sts.d32 = dma_desc->status.d32;
+
+		/* Write status in iso_packet_decsriptor  */
+		iso_packet->status =
+		    sts.b_iso_out.rxsts + (sts.b_iso_out.bs ^ BS_DMA_DONE);
+		if (iso_packet->status) {
+			iso_packet->status = -DWC_E_NO_DATA;
+		}
+		/* Received data length */
+		if (!sts.b_iso_out.rxbytes) {
+			iso_packet->length =
+			    dwc_ep->data_per_frame - sts.b_iso_out.rxbytes;
+		} else {
+			iso_packet->length =
+			    dwc_ep->data_per_frame - sts.b_iso_out.rxbytes +
+			    (4 - dwc_ep->data_per_frame % 4);
+		}
+
+		iso_packet->offset = offset;
+	} else {
+/** ISO IN EP */
+
+		dma_desc =
+		    dwc_ep->iso_desc_addr +
+		    dwc_ep->desc_cnt * dwc_ep->proc_buf_num;
+
+		for (i = 0; i < dwc_ep->desc_cnt - 1; i++) {
+			sts.d32 = dma_desc->status.d32;
+
+			/* Write status in iso packet descriptor */
+			iso_packet->status =
+			    sts.b_iso_in.txsts +
+			    (sts.b_iso_in.bs ^ BS_DMA_DONE);
+			if (iso_packet->status != 0) {
+				iso_packet->status = -DWC_E_NO_DATA;
+
+			}
+			/* Bytes has been transfered */
+			iso_packet->length =
+			    dwc_ep->data_per_frame - sts.b_iso_in.txbytes;
+
+			dma_desc++;
+			iso_packet++;
+		}
+
+		sts.d32 = dma_desc->status.d32;
+		while (sts.b_iso_in.bs == BS_DMA_BUSY) {
+			sts.d32 = dma_desc->status.d32;
+		}
+
+		/* Write status in iso packet descriptor ??? do be done with ERROR codes */
+		iso_packet->status =
+		    sts.b_iso_in.txsts + (sts.b_iso_in.bs ^ BS_DMA_DONE);
+		if (iso_packet->status != 0) {
+			iso_packet->status = -DWC_E_NO_DATA;
+		}
+
+		/* Bytes has been transfered */
+		iso_packet->length =
+		    dwc_ep->data_per_frame - sts.b_iso_in.txbytes;
+	}
+}
+
+/**
+ * This function reinitialize DMA Descriptors for Isochronous transfer
+ *
+ * @param core_if Programming view of DWC_otg controller.
+ * @param dwc_ep The EP to start the transfer on.
+ *
+ */
+static void reinit_ddma_iso_xfer(dwc_otg_core_if_t * core_if, dwc_ep_t * dwc_ep)
+{
+	int i, j;
+	dwc_otg_dev_dma_desc_t *dma_desc;
+	dma_addr_t dma_ad;
+	volatile uint32_t *addr;
+	dev_dma_desc_sts_t sts = {.d32 = 0 };
+	uint32_t data_per_desc;
+
+	if (dwc_ep->is_in == 0) {
+		addr = &core_if->dev_if->out_ep_regs[dwc_ep->num]->doepctl;
+	} else {
+		addr = &core_if->dev_if->in_ep_regs[dwc_ep->num]->diepctl;
+	}
+
+	if (dwc_ep->proc_buf_num == 0) {
+		/** Buffer 0 descriptors setup */
+		dma_ad = dwc_ep->dma_addr0;
+	} else {
+		/** Buffer 1 descriptors setup */
+		dma_ad = dwc_ep->dma_addr1;
+	}
+
+	/** Reinit closed DMA Descriptors*/
+	/** ISO OUT EP */
+	if (dwc_ep->is_in == 0) {
+		dma_desc =
+		    dwc_ep->iso_desc_addr +
+		    dwc_ep->desc_cnt * dwc_ep->proc_buf_num;
+
+		sts.b_iso_out.bs = BS_HOST_READY;
+		sts.b_iso_out.rxsts = 0;
+		sts.b_iso_out.l = 0;
+		sts.b_iso_out.sp = 0;
+		sts.b_iso_out.ioc = 0;
+		sts.b_iso_out.pid = 0;
+		sts.b_iso_out.framenum = 0;
+
+		for (i = 0; i < dwc_ep->desc_cnt - dwc_ep->pkt_per_frm;
+		     i += dwc_ep->pkt_per_frm) {
+			for (j = 0; j < dwc_ep->pkt_per_frm; ++j) {
+				data_per_desc =
+				    ((j + 1) * dwc_ep->maxpacket >
+				     dwc_ep->
+				     data_per_frame) ? dwc_ep->data_per_frame -
+				    j * dwc_ep->maxpacket : dwc_ep->maxpacket;
+				data_per_desc +=
+				    (data_per_desc % 4) ? (4 -
+							   data_per_desc %
+							   4) : 0;
+				sts.b_iso_out.rxbytes = data_per_desc;
+				dma_desc->buf = dma_ad;
+				dma_desc->status.d32 = sts.d32;
+
+				dma_ad += data_per_desc;
+				dma_desc++;
+			}
+		}
+
+		for (j = 0; j < dwc_ep->pkt_per_frm - 1; ++j) {
+
+			data_per_desc =
+			    ((j + 1) * dwc_ep->maxpacket >
+			     dwc_ep->data_per_frame) ? dwc_ep->data_per_frame -
+			    j * dwc_ep->maxpacket : dwc_ep->maxpacket;
+			data_per_desc +=
+			    (data_per_desc % 4) ? (4 - data_per_desc % 4) : 0;
+			sts.b_iso_out.rxbytes = data_per_desc;
+
+			dma_desc->buf = dma_ad;
+			dma_desc->status.d32 = sts.d32;
+
+			dma_desc++;
+			dma_ad += data_per_desc;
+		}
+
+		sts.b_iso_out.ioc = 1;
+		sts.b_iso_out.l = dwc_ep->proc_buf_num;
+
+		data_per_desc =
+		    ((j + 1) * dwc_ep->maxpacket >
+		     dwc_ep->data_per_frame) ? dwc_ep->data_per_frame -
+		    j * dwc_ep->maxpacket : dwc_ep->maxpacket;
+		data_per_desc +=
+		    (data_per_desc % 4) ? (4 - data_per_desc % 4) : 0;
+		sts.b_iso_out.rxbytes = data_per_desc;
+
+		dma_desc->buf = dma_ad;
+		dma_desc->status.d32 = sts.d32;
+	} else {
+/** ISO IN EP */
+
+		dma_desc =
+		    dwc_ep->iso_desc_addr +
+		    dwc_ep->desc_cnt * dwc_ep->proc_buf_num;
+
+		sts.b_iso_in.bs = BS_HOST_READY;
+		sts.b_iso_in.txsts = 0;
+		sts.b_iso_in.sp = 0;
+		sts.b_iso_in.ioc = 0;
+		sts.b_iso_in.pid = dwc_ep->pkt_per_frm;
+		sts.b_iso_in.framenum = dwc_ep->next_frame;
+		sts.b_iso_in.txbytes = dwc_ep->data_per_frame;
+		sts.b_iso_in.l = 0;
+
+		for (i = 0; i < dwc_ep->desc_cnt - 1; i++) {
+			dma_desc->buf = dma_ad;
+			dma_desc->status.d32 = sts.d32;
+
+			sts.b_iso_in.framenum += dwc_ep->bInterval;
+			dma_ad += dwc_ep->data_per_frame;
+			dma_desc++;
+		}
+
+		sts.b_iso_in.ioc = 1;
+		sts.b_iso_in.l = dwc_ep->proc_buf_num;
+
+		dma_desc->buf = dma_ad;
+		dma_desc->status.d32 = sts.d32;
+
+		dwc_ep->next_frame =
+		    sts.b_iso_in.framenum + dwc_ep->bInterval * 1;
+	}
+	dwc_ep->proc_buf_num = (dwc_ep->proc_buf_num ^ 1) & 0x1;
+}
+
+/**
+ * This function is to handle Iso EP transfer complete interrupt
+ * in case Iso out packet was dropped
+ *
+ * @param core_if Programming view of DWC_otg controller.
+ * @param dwc_ep The EP for wihich transfer complete was asserted
+ *
+ */
+static uint32_t handle_iso_out_pkt_dropped(dwc_otg_core_if_t * core_if,
+					   dwc_ep_t * dwc_ep)
+{
+	uint32_t dma_addr;
+	uint32_t drp_pkt;
+	uint32_t drp_pkt_cnt;
+	deptsiz_data_t deptsiz = {.d32 = 0 };
+	depctl_data_t depctl = {.d32 = 0 };
+	int i;
+
+	deptsiz.d32 =
+	    DWC_READ_REG32(&core_if->dev_if->
+			   out_ep_regs[dwc_ep->num]->doeptsiz);
+
+	drp_pkt = dwc_ep->pkt_cnt - deptsiz.b.pktcnt;
+	drp_pkt_cnt = dwc_ep->pkt_per_frm - (drp_pkt % dwc_ep->pkt_per_frm);
+
+	/* Setting dropped packets status */
+	for (i = 0; i < drp_pkt_cnt; ++i) {
+		dwc_ep->pkt_info[drp_pkt].status = -DWC_E_NO_DATA;
+		drp_pkt++;
+		deptsiz.b.pktcnt--;
+	}
+
+	if (deptsiz.b.pktcnt > 0) {
+		deptsiz.b.xfersize =
+		    dwc_ep->xfer_len - (dwc_ep->pkt_cnt -
+					deptsiz.b.pktcnt) * dwc_ep->maxpacket;
+	} else {
+		deptsiz.b.xfersize = 0;
+		deptsiz.b.pktcnt = 0;
+	}
+
+	DWC_WRITE_REG32(&core_if->dev_if->out_ep_regs[dwc_ep->num]->doeptsiz,
+			deptsiz.d32);
+
+	if (deptsiz.b.pktcnt > 0) {
+		if (dwc_ep->proc_buf_num) {
+			dma_addr =
+			    dwc_ep->dma_addr1 + dwc_ep->xfer_len -
+			    deptsiz.b.xfersize;
+		} else {
+			dma_addr =
+			    dwc_ep->dma_addr0 + dwc_ep->xfer_len -
+			    deptsiz.b.xfersize;;
+		}
+
+		DWC_WRITE_REG32(&core_if->dev_if->
+				out_ep_regs[dwc_ep->num]->doepdma, dma_addr);
+
+		/** Re-enable endpoint, clear nak  */
+		depctl.d32 = 0;
+		depctl.b.epena = 1;
+		depctl.b.cnak = 1;
+
+		DWC_MODIFY_REG32(&core_if->dev_if->
+				 out_ep_regs[dwc_ep->num]->doepctl, depctl.d32,
+				 depctl.d32);
+		return 0;
+	} else {
+		return 1;
+	}
+}
+
+/**
+ * This function sets iso packets information(PTI mode)
+ *
+ * @param core_if Programming view of DWC_otg controller.
+ * @param ep The EP to start the transfer on.
+ *
+ */
+static uint32_t set_iso_pkts_info(dwc_otg_core_if_t * core_if, dwc_ep_t * ep)
+{
+	int i, j;
+	dma_addr_t dma_ad;
+	iso_pkt_info_t *packet_info = ep->pkt_info;
+	uint32_t offset;
+	uint32_t frame_data;
+	deptsiz_data_t deptsiz;
+
+	if (ep->proc_buf_num == 0) {
+		/** Buffer 0 descriptors setup */
+		dma_ad = ep->dma_addr0;
+	} else {
+		/** Buffer 1 descriptors setup */
+		dma_ad = ep->dma_addr1;
+	}
+
+	if (ep->is_in) {
+		deptsiz.d32 =
+		    DWC_READ_REG32(&core_if->dev_if->in_ep_regs[ep->num]->
+				   dieptsiz);
+	} else {
+		deptsiz.d32 =
+		    DWC_READ_REG32(&core_if->dev_if->out_ep_regs[ep->num]->
+				   doeptsiz);
+	}
+
+	if (!deptsiz.b.xfersize) {
+		offset = 0;
+		for (i = 0; i < ep->pkt_cnt; i += ep->pkt_per_frm) {
+			frame_data = ep->data_per_frame;
+			for (j = 0; j < ep->pkt_per_frm; ++j) {
+
+				/* Packet status - is not set as initially
+				 * it is set to 0 and if packet was sent
+				 successfully, status field will remain 0*/
+
+				/* Bytes has been transfered */
+				packet_info->length =
+				    (ep->maxpacket <
+				     frame_data) ? ep->maxpacket : frame_data;
+
+				/* Received packet offset */
+				packet_info->offset = offset;
+				offset += packet_info->length;
+				frame_data -= packet_info->length;
+
+				packet_info++;
+			}
+		}
+		return 1;
+	} else {
+		/* This is a workaround for in case of Transfer Complete with
+		 * PktDrpSts interrupts merging - in this case Transfer complete
+		 * interrupt for Isoc Out Endpoint is asserted without PktDrpSts
+		 * set and with DOEPTSIZ register non zero. Investigations showed,
+		 * that this happens when Out packet is dropped, but because of
+		 * interrupts merging during first interrupt handling PktDrpSts
+		 * bit is cleared and for next merged interrupts it is not reset.
+		 * In this case SW hadles the interrupt as if PktDrpSts bit is set.
+		 */
+		if (ep->is_in) {
+			return 1;
+		} else {
+			return handle_iso_out_pkt_dropped(core_if, ep);
+		}
+	}
+}
+
+/**
+ * This function is to handle Iso EP transfer complete interrupt
+ *
+ * @param pcd The PCD
+ * @param ep The EP for which transfer complete was asserted
+ *
+ */
+static void complete_iso_ep(dwc_otg_pcd_t * pcd, dwc_otg_pcd_ep_t * ep)
+{
+	dwc_otg_core_if_t *core_if = GET_CORE_IF(ep->pcd);
+	dwc_ep_t *dwc_ep = &ep->dwc_ep;
+	uint8_t is_last = 0;
+
+	if (ep->dwc_ep.next_frame == 0xffffffff) {
+		DWC_WARN("Next frame is not set!\n");
+		return;
+	}
+
+	if (core_if->dma_enable) {
+		if (core_if->dma_desc_enable) {
+			set_ddma_iso_pkts_info(core_if, dwc_ep);
+			reinit_ddma_iso_xfer(core_if, dwc_ep);
+			is_last = 1;
+		} else {
+			if (core_if->pti_enh_enable) {
+				if (set_iso_pkts_info(core_if, dwc_ep)) {
+					dwc_ep->proc_buf_num =
+					    (dwc_ep->proc_buf_num ^ 1) & 0x1;
+					dwc_otg_iso_ep_start_buf_transfer
+					    (core_if, dwc_ep);
+					is_last = 1;
+				}
+			} else {
+				set_current_pkt_info(core_if, dwc_ep);
+				if (dwc_ep->cur_pkt >= dwc_ep->pkt_cnt) {
+					is_last = 1;
+					dwc_ep->cur_pkt = 0;
+					dwc_ep->proc_buf_num =
+					    (dwc_ep->proc_buf_num ^ 1) & 0x1;
+					if (dwc_ep->proc_buf_num) {
+						dwc_ep->cur_pkt_addr =
+						    dwc_ep->xfer_buff1;
+						dwc_ep->cur_pkt_dma_addr =
+						    dwc_ep->dma_addr1;
+					} else {
+						dwc_ep->cur_pkt_addr =
+						    dwc_ep->xfer_buff0;
+						dwc_ep->cur_pkt_dma_addr =
+						    dwc_ep->dma_addr0;
+					}
+
+				}
+				dwc_otg_iso_ep_start_frm_transfer(core_if,
+								  dwc_ep);
+			}
+		}
+	} else {
+		set_current_pkt_info(core_if, dwc_ep);
+		if (dwc_ep->cur_pkt >= dwc_ep->pkt_cnt) {
+			is_last = 1;
+			dwc_ep->cur_pkt = 0;
+			dwc_ep->proc_buf_num = (dwc_ep->proc_buf_num ^ 1) & 0x1;
+			if (dwc_ep->proc_buf_num) {
+				dwc_ep->cur_pkt_addr = dwc_ep->xfer_buff1;
+				dwc_ep->cur_pkt_dma_addr = dwc_ep->dma_addr1;
+			} else {
+				dwc_ep->cur_pkt_addr = dwc_ep->xfer_buff0;
+				dwc_ep->cur_pkt_dma_addr = dwc_ep->dma_addr0;
+			}
+
+		}
+		dwc_otg_iso_ep_start_frm_transfer(core_if, dwc_ep);
+	}
+	if (is_last)
+		dwc_otg_iso_buffer_done(pcd, ep, ep->iso_req_handle);
+}
+#endif /* DWC_EN_ISOC */
+
+/**
+ * This function handle BNA interrupt for Non Isochronous EPs
+ *
+ */
+static void dwc_otg_pcd_handle_noniso_bna(dwc_otg_pcd_ep_t * ep)
+{
+	dwc_ep_t *dwc_ep = &ep->dwc_ep;
+	volatile uint32_t *addr;
+	depctl_data_t depctl = {.d32 = 0 };
+	dwc_otg_pcd_t *pcd = ep->pcd;
+	dwc_otg_dev_dma_desc_t *dma_desc;
+	dev_dma_desc_sts_t sts = {.d32 = 0 };
+	dwc_otg_core_if_t *core_if = ep->pcd->core_if;
+	int i, start;
+
+	if (!dwc_ep->desc_cnt)
+		DWC_WARN("Ep%d %s Descriptor count = %d \n", dwc_ep->num,
+			 (dwc_ep->is_in ? "IN" : "OUT"), dwc_ep->desc_cnt);
+
+	if (core_if->core_params->cont_on_bna && !dwc_ep->is_in
+							&& dwc_ep->type != DWC_OTG_EP_TYPE_CONTROL) {
+		uint32_t doepdma;
+		dwc_otg_dev_out_ep_regs_t *out_regs =
+			core_if->dev_if->out_ep_regs[dwc_ep->num];
+		doepdma = DWC_READ_REG32(&(out_regs->doepdma));
+		start = (doepdma - dwc_ep->dma_desc_addr)/sizeof(dwc_otg_dev_dma_desc_t);
+		dma_desc = &(dwc_ep->desc_addr[start]);
+	} else {
+		start = 0;
+		dma_desc = dwc_ep->desc_addr;
+	}
+
+
+	for (i = start; i < dwc_ep->desc_cnt; ++i, ++dma_desc) {
+		sts.d32 = dma_desc->status.d32;
+		sts.b.bs = BS_HOST_READY;
+		dma_desc->status.d32 = sts.d32;
+	}
+
+	if (dwc_ep->is_in == 0) {
+		addr =
+		    &GET_CORE_IF(pcd)->dev_if->out_ep_regs[dwc_ep->num]->
+		    doepctl;
+	} else {
+		addr =
+		    &GET_CORE_IF(pcd)->dev_if->in_ep_regs[dwc_ep->num]->diepctl;
+	}
+	depctl.b.epena = 1;
+	depctl.b.cnak = 1;
+	DWC_MODIFY_REG32(addr, 0, depctl.d32);
+}
+
+/**
+ * This function handles EP0 Control transfers.
+ *
+ * The state of the control transfers are tracked in
+ * <code>ep0state</code>.
+ */
+static void handle_ep0(dwc_otg_pcd_t * pcd)
+{
+	dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd);
+	dwc_otg_pcd_ep_t *ep0 = &pcd->ep0;
+	dev_dma_desc_sts_t desc_sts;
+	deptsiz0_data_t deptsiz;
+	uint32_t byte_count;
+
+#ifdef DEBUG_EP0
+	DWC_DEBUGPL(DBG_PCDV, "%s()\n", __func__);
+	print_ep0_state(pcd);
+#endif
+
+//      DWC_PRINTF("HANDLE EP0\n");
+
+	switch (pcd->ep0state) {
+	case EP0_DISCONNECT:
+		break;
+
+	case EP0_IDLE:
+		pcd->request_config = 0;
+
+		pcd_setup(pcd);
+		break;
+
+	case EP0_IN_DATA_PHASE:
+#ifdef DEBUG_EP0
+		DWC_DEBUGPL(DBG_PCD, "DATA_IN EP%d-%s: type=%d, mps=%d\n",
+			    ep0->dwc_ep.num, (ep0->dwc_ep.is_in ? "IN" : "OUT"),
+			    ep0->dwc_ep.type, ep0->dwc_ep.maxpacket);
+#endif
+
+		if (core_if->dma_enable != 0) {
+			/*
+			 * For EP0 we can only program 1 packet at a time so we
+			 * need to do the make calculations after each complete.
+			 * Call write_packet to make the calculations, as in
+			 * slave mode, and use those values to determine if we
+			 * can complete.
+			 */
+			if (core_if->dma_desc_enable == 0) {
+				deptsiz.d32 =
+				    DWC_READ_REG32(&core_if->
+						   dev_if->in_ep_regs[0]->
+						   dieptsiz);
+				byte_count =
+				    ep0->dwc_ep.xfer_len - deptsiz.b.xfersize;
+			} else {
+				desc_sts =
+				    core_if->dev_if->in_desc_addr->status;
+				byte_count =
+				    ep0->dwc_ep.xfer_len - desc_sts.b.bytes;
+			}
+			ep0->dwc_ep.xfer_count += byte_count;
+			ep0->dwc_ep.xfer_buff += byte_count;
+			ep0->dwc_ep.dma_addr += byte_count;
+		}
+		if (ep0->dwc_ep.xfer_count < ep0->dwc_ep.total_len) {
+			dwc_otg_ep0_continue_transfer(GET_CORE_IF(pcd),
+						      &ep0->dwc_ep);
+			DWC_DEBUGPL(DBG_PCD, "CONTINUE TRANSFER\n");
+		} else if (ep0->dwc_ep.sent_zlp) {
+			dwc_otg_ep0_continue_transfer(GET_CORE_IF(pcd),
+						      &ep0->dwc_ep);
+			ep0->dwc_ep.sent_zlp = 0;
+			DWC_DEBUGPL(DBG_PCD, "CONTINUE TRANSFER sent zlp\n");
+		} else {
+			ep0_complete_request(ep0);
+			DWC_DEBUGPL(DBG_PCD, "COMPLETE TRANSFER\n");
+		}
+		break;
+	case EP0_OUT_DATA_PHASE:
+#ifdef DEBUG_EP0
+		DWC_DEBUGPL(DBG_PCD, "DATA_OUT EP%d-%s: type=%d, mps=%d\n",
+			    ep0->dwc_ep.num, (ep0->dwc_ep.is_in ? "IN" : "OUT"),
+			    ep0->dwc_ep.type, ep0->dwc_ep.maxpacket);
+#endif
+		if (core_if->dma_enable != 0) {
+			if (core_if->dma_desc_enable == 0) {
+				deptsiz.d32 =
+				    DWC_READ_REG32(&core_if->
+						   dev_if->out_ep_regs[0]->
+						   doeptsiz);
+				byte_count =
+				    ep0->dwc_ep.maxpacket - deptsiz.b.xfersize;
+			} else {
+				desc_sts =
+				    core_if->dev_if->out_desc_addr->status;
+				byte_count =
+				    ep0->dwc_ep.maxpacket - desc_sts.b.bytes;
+			}
+			ep0->dwc_ep.xfer_count += byte_count;
+			ep0->dwc_ep.xfer_buff += byte_count;
+			ep0->dwc_ep.dma_addr += byte_count;
+		}
+		if (ep0->dwc_ep.xfer_count < ep0->dwc_ep.total_len) {
+			dwc_otg_ep0_continue_transfer(GET_CORE_IF(pcd),
+						      &ep0->dwc_ep);
+			DWC_DEBUGPL(DBG_PCD, "CONTINUE TRANSFER\n");
+		} else if (ep0->dwc_ep.sent_zlp) {
+			dwc_otg_ep0_continue_transfer(GET_CORE_IF(pcd),
+						      &ep0->dwc_ep);
+			ep0->dwc_ep.sent_zlp = 0;
+			DWC_DEBUGPL(DBG_PCD, "CONTINUE TRANSFER sent zlp\n");
+		} else {
+			ep0_complete_request(ep0);
+			DWC_DEBUGPL(DBG_PCD, "COMPLETE TRANSFER\n");
+		}
+		break;
+
+	case EP0_IN_STATUS_PHASE:
+	case EP0_OUT_STATUS_PHASE:
+		DWC_DEBUGPL(DBG_PCD, "CASE: EP0_STATUS\n");
+		ep0_complete_request(ep0);
+		pcd->ep0state = EP0_IDLE;
+		ep0->stopped = 1;
+		ep0->dwc_ep.is_in = 0;	/* OUT for next SETUP */
+
+		/* Prepare for more SETUP Packets */
+		if (core_if->dma_enable) {
+			ep0_out_start(core_if, pcd);
+		}
+		break;
+
+	case EP0_STALL:
+		DWC_ERROR("EP0 STALLed, should not get here pcd_setup()\n");
+		break;
+	}
+#ifdef DEBUG_EP0
+	print_ep0_state(pcd);
+#endif
+}
+
+/**
+ * Restart transfer
+ */
+static void restart_transfer(dwc_otg_pcd_t * pcd, const uint32_t epnum)
+{
+	dwc_otg_core_if_t *core_if;
+	dwc_otg_dev_if_t *dev_if;
+	deptsiz_data_t dieptsiz = {.d32 = 0 };
+	dwc_otg_pcd_ep_t *ep;
+
+	ep = get_in_ep(pcd, epnum);
+
+#ifdef DWC_EN_ISOC
+	if (ep->dwc_ep.type == DWC_OTG_EP_TYPE_ISOC) {
+		return;
+	}
+#endif /* DWC_EN_ISOC  */
+
+	core_if = GET_CORE_IF(pcd);
+	dev_if = core_if->dev_if;
+
+	dieptsiz.d32 = DWC_READ_REG32(&dev_if->in_ep_regs[epnum]->dieptsiz);
+
+	DWC_DEBUGPL(DBG_PCD, "xfer_buff=%p xfer_count=%0x xfer_len=%0x"
+		    " stopped=%d\n", ep->dwc_ep.xfer_buff,
+		    ep->dwc_ep.xfer_count, ep->dwc_ep.xfer_len, ep->stopped);
+	/*
+	 * If xfersize is 0 and pktcnt in not 0, resend the last packet.
+	 */
+	if (dieptsiz.b.pktcnt && dieptsiz.b.xfersize == 0 &&
+	    ep->dwc_ep.start_xfer_buff != 0) {
+		if (ep->dwc_ep.total_len <= ep->dwc_ep.maxpacket) {
+			ep->dwc_ep.xfer_count = 0;
+			ep->dwc_ep.xfer_buff = ep->dwc_ep.start_xfer_buff;
+			ep->dwc_ep.xfer_len = ep->dwc_ep.xfer_count;
+		} else {
+			ep->dwc_ep.xfer_count -= ep->dwc_ep.maxpacket;
+			/* convert packet size to dwords. */
+			ep->dwc_ep.xfer_buff -= ep->dwc_ep.maxpacket;
+			ep->dwc_ep.xfer_len = ep->dwc_ep.xfer_count;
+		}
+		ep->stopped = 0;
+		DWC_DEBUGPL(DBG_PCD, "xfer_buff=%p xfer_count=%0x "
+			    "xfer_len=%0x stopped=%d\n",
+			    ep->dwc_ep.xfer_buff,
+			    ep->dwc_ep.xfer_count, ep->dwc_ep.xfer_len,
+			    ep->stopped);
+		if (epnum == 0) {
+			dwc_otg_ep0_start_transfer(core_if, &ep->dwc_ep);
+		} else {
+			dwc_otg_ep_start_transfer(core_if, &ep->dwc_ep);
+		}
+	}
+}
+
+/*
+ * This function create new nextep sequnce based on Learn Queue.
+ *
+ * @param core_if Programming view of DWC_otg controller
+ */
+void predict_nextep_seq( dwc_otg_core_if_t * core_if)
+{
+	dwc_otg_device_global_regs_t *dev_global_regs =
+	    core_if->dev_if->dev_global_regs;
+	const uint32_t TOKEN_Q_DEPTH = core_if->hwcfg2.b.dev_token_q_depth;
+	/* Number of Token Queue Registers */
+	const int DTKNQ_REG_CNT = (TOKEN_Q_DEPTH + 7) / 8;
+	dtknq1_data_t dtknqr1;
+	uint32_t in_tkn_epnums[4];
+	uint8_t seqnum[MAX_EPS_CHANNELS];
+	uint8_t intkn_seq[1 << 5];
+	grstctl_t resetctl = {.d32 = 0 };
+	uint8_t temp;
+	int ndx = 0;
+	int start = 0;
+	int end = 0;
+	int sort_done = 0;
+	int i = 0;
+	volatile uint32_t *addr = &dev_global_regs->dtknqr1;
+
+
+	DWC_DEBUGPL(DBG_PCD,"dev_token_q_depth=%d\n",TOKEN_Q_DEPTH);
+
+	/* Read the DTKNQ Registers */
+	for (i = 0; i < DTKNQ_REG_CNT; i++) {
+		in_tkn_epnums[i] = DWC_READ_REG32(addr);
+		DWC_DEBUGPL(DBG_PCDV, "DTKNQR%d=0x%08x\n", i + 1,
+			    in_tkn_epnums[i]);
+		if (addr == &dev_global_regs->dvbusdis) {
+			addr = &dev_global_regs->dtknqr3_dthrctl;
+		} else {
+			++addr;
+		}
+
+	}
+
+	/* Copy the DTKNQR1 data to the bit field. */
+	dtknqr1.d32 = in_tkn_epnums[0];
+	if (dtknqr1.b.wrap_bit) {
+		ndx = dtknqr1.b.intknwptr;
+		end = ndx -1;
+		if (end < 0)
+			end = TOKEN_Q_DEPTH -1;
+	} else {
+		ndx = 0;
+		end = dtknqr1.b.intknwptr -1;
+		if (end < 0)
+			end = 0;
+	}
+	start = ndx;
+
+	/* Fill seqnum[] by initial values: EP number + 31 */
+	for (i=0; i <= core_if->dev_if->num_in_eps; i++) {
+		seqnum[i] = i +31;
+	}
+
+	/* Fill intkn_seq[] from in_tkn_epnums[0] */
+	for (i=0; i < 6; i++)
+		intkn_seq[i] = (in_tkn_epnums[0] >> ((7-i) * 4)) & 0xf;
+
+	if (TOKEN_Q_DEPTH > 6) {
+		/* Fill intkn_seq[] from in_tkn_epnums[1] */
+		for (i=6; i < 14; i++)
+			intkn_seq[i] =
+			    (in_tkn_epnums[1] >> ((7 - (i - 6)) * 4)) & 0xf;
+	}
+
+	if (TOKEN_Q_DEPTH > 14) {
+		/* Fill intkn_seq[] from in_tkn_epnums[1] */
+		for (i=14; i < 22; i++)
+			intkn_seq[i] =
+			    (in_tkn_epnums[2] >> ((7 - (i - 14)) * 4)) & 0xf;
+	}
+
+	if (TOKEN_Q_DEPTH > 22) {
+		/* Fill intkn_seq[] from in_tkn_epnums[1] */
+		for (i=22; i < 30; i++)
+			intkn_seq[i] =
+			    (in_tkn_epnums[3] >> ((7 - (i - 22)) * 4)) & 0xf;
+	}
+
+	DWC_DEBUGPL(DBG_PCDV, "%s start=%d end=%d intkn_seq[]:\n", __func__,
+		    start, end);
+	for (i=0; i<TOKEN_Q_DEPTH; i++)
+		DWC_DEBUGPL(DBG_PCDV,"%d\n", intkn_seq[i]);
+
+	/* Update seqnum based on intkn_seq[] */
+	i = 0;
+	do {
+		seqnum[intkn_seq[ndx]] = i;
+		ndx++;
+		i++;
+		if (ndx == TOKEN_Q_DEPTH)
+			ndx = 0;
+	} while ( i < TOKEN_Q_DEPTH );
+
+	/* Mark non active EP's in seqnum[] by 0xff */
+	for (i=0; i<=core_if->dev_if->num_in_eps; i++) {
+		if (core_if->nextep_seq[i] == 0xff )
+			seqnum[i] = 0xff;
+	}
+
+	/* Sort seqnum[] */
+	sort_done = 0;
+	while (!sort_done) {
+		sort_done = 1;
+		for (i=0; i<core_if->dev_if->num_in_eps; i++) {
+			if (seqnum[i] > seqnum[i+1]) {
+				temp = seqnum[i];
+				seqnum[i] = seqnum[i+1];
+				seqnum[i+1] = temp;
+				sort_done = 0;
+			}
+		}
+	}
+
+	ndx = start + seqnum[0];
+	if (ndx >= TOKEN_Q_DEPTH)
+		ndx = ndx % TOKEN_Q_DEPTH;
+	core_if->first_in_nextep_seq = intkn_seq[ndx];
+
+	/* Update seqnum[] by EP numbers  */
+	for (i=0; i<=core_if->dev_if->num_in_eps; i++) {
+		ndx = start + i;
+		if (seqnum[i] < 31) {
+			ndx = start + seqnum[i];
+			if (ndx >= TOKEN_Q_DEPTH)
+				ndx = ndx % TOKEN_Q_DEPTH;
+			seqnum[i] = intkn_seq[ndx];
+		} else {
+			if (seqnum[i] < 0xff) {
+				seqnum[i] = seqnum[i] - 31;
+			} else {
+				break;
+			}
+		}
+	}
+
+	/* Update nextep_seq[] based on seqnum[] */
+	for (i=0; i<core_if->dev_if->num_in_eps; i++) {
+		if (seqnum[i] != 0xff) {
+			if (seqnum[i+1] != 0xff) {
+				core_if->nextep_seq[seqnum[i]] = seqnum[i+1];
+			} else {
+				core_if->nextep_seq[seqnum[i]] = core_if->first_in_nextep_seq;
+				break;
+			}
+		} else {
+			break;
+		}
+	}
+
+	DWC_DEBUGPL(DBG_PCDV, "%s first_in_nextep_seq= %2d; nextep_seq[]:\n",
+		__func__, core_if->first_in_nextep_seq);
+	for (i=0; i <= core_if->dev_if->num_in_eps; i++) {
+		DWC_DEBUGPL(DBG_PCDV,"%2d\n", core_if->nextep_seq[i]);
+	}
+
+	/* Flush the Learning Queue */
+	resetctl.d32 = DWC_READ_REG32(&core_if->core_global_regs->grstctl);
+	resetctl.b.intknqflsh = 1;
+	DWC_WRITE_REG32(&core_if->core_global_regs->grstctl, resetctl.d32);
+
+
+}
+
+/**
+ * handle the IN EP disable interrupt.
+ */
+static inline void handle_in_ep_disable_intr(dwc_otg_pcd_t * pcd,
+					     const uint32_t epnum)
+{
+	dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd);
+	dwc_otg_dev_if_t *dev_if = core_if->dev_if;
+	deptsiz_data_t dieptsiz = {.d32 = 0 };
+	dctl_data_t dctl = {.d32 = 0 };
+	dwc_otg_pcd_ep_t *ep;
+	dwc_ep_t *dwc_ep;
+	gintmsk_data_t gintmsk_data;
+	depctl_data_t depctl;
+	uint32_t diepdma;
+	uint32_t remain_to_transfer = 0;
+	uint8_t i;
+	uint32_t xfer_size;
+
+	ep = get_in_ep(pcd, epnum);
+	dwc_ep = &ep->dwc_ep;
+
+	if (dwc_ep->type == DWC_OTG_EP_TYPE_ISOC) {
+		dwc_otg_flush_tx_fifo(core_if, dwc_ep->tx_fifo_num);
+		complete_ep(ep);
+		return;
+	}
+
+	DWC_DEBUGPL(DBG_PCD, "diepctl%d=%0x\n", epnum,
+		    DWC_READ_REG32(&dev_if->in_ep_regs[epnum]->diepctl));
+	dieptsiz.d32 = DWC_READ_REG32(&dev_if->in_ep_regs[epnum]->dieptsiz);
+	depctl.d32 = DWC_READ_REG32(&dev_if->in_ep_regs[epnum]->diepctl);
+
+	DWC_DEBUGPL(DBG_ANY, "pktcnt=%d size=%d\n",
+		    dieptsiz.b.pktcnt, dieptsiz.b.xfersize);
+
+	if ((core_if->start_predict == 0) || (depctl.b.eptype & 1)) {
+		if (ep->stopped) {
+			if (core_if->en_multiple_tx_fifo)
+				/* Flush the Tx FIFO */
+				dwc_otg_flush_tx_fifo(core_if, dwc_ep->tx_fifo_num);
+			/* Clear the Global IN NP NAK */
+			dctl.d32 = 0;
+			dctl.b.cgnpinnak = 1;
+			DWC_MODIFY_REG32(&dev_if->dev_global_regs->dctl, dctl.d32, dctl.d32);
+			/* Restart the transaction */
+			if (dieptsiz.b.pktcnt != 0 || dieptsiz.b.xfersize != 0) {
+				restart_transfer(pcd, epnum);
+			}
+		} else {
+			/* Restart the transaction */
+			if (dieptsiz.b.pktcnt != 0 || dieptsiz.b.xfersize != 0) {
+				restart_transfer(pcd, epnum);
+			}
+			DWC_DEBUGPL(DBG_ANY, "STOPPED!!!\n");
+		}
+		return;
+	}
+
+	if (core_if->start_predict > 2) {	// NP IN EP
+		core_if->start_predict--;
+		return;
+	}
+
+	core_if->start_predict--;
+
+	if (core_if->start_predict == 1) {	// All NP IN Ep's disabled now
+
+		predict_nextep_seq(core_if);
+
+		/* Update all active IN EP's NextEP field based of nextep_seq[] */
+		for ( i = 0; i <= core_if->dev_if->num_in_eps; i++) {
+			depctl.d32 =
+			    DWC_READ_REG32(&dev_if->in_ep_regs[i]->diepctl);
+			if (core_if->nextep_seq[i] != 0xff) {	// Active NP IN EP
+				depctl.b.nextep = core_if->nextep_seq[i];
+				DWC_WRITE_REG32(&dev_if->in_ep_regs[i]->diepctl, depctl.d32);
+			}
+		}
+		/* Flush Shared NP TxFIFO */
+		dwc_otg_flush_tx_fifo(core_if, 0);
+		/* Rewind buffers */
+		if (!core_if->dma_desc_enable) {
+			i = core_if->first_in_nextep_seq;
+			do {
+				ep = get_in_ep(pcd, i);
+				dieptsiz.d32 = DWC_READ_REG32(&dev_if->in_ep_regs[i]->dieptsiz);
+				xfer_size = ep->dwc_ep.total_len - ep->dwc_ep.xfer_count;
+				if (xfer_size > ep->dwc_ep.maxxfer)
+					xfer_size = ep->dwc_ep.maxxfer;
+				depctl.d32 = DWC_READ_REG32(&dev_if->in_ep_regs[i]->diepctl);
+				if (dieptsiz.b.pktcnt != 0) {
+					if (xfer_size == 0) {
+						remain_to_transfer = 0;
+					} else {
+						if ((xfer_size % ep->dwc_ep.maxpacket) == 0) {
+							remain_to_transfer =
+								dieptsiz.b.pktcnt * ep->dwc_ep.maxpacket;
+						} else {
+							remain_to_transfer = ((dieptsiz.b.pktcnt -1) * ep->dwc_ep.maxpacket)
+								+ (xfer_size % ep->dwc_ep.maxpacket);
+						}
+					}
+					diepdma = DWC_READ_REG32(&dev_if->in_ep_regs[i]->diepdma);
+					dieptsiz.b.xfersize = remain_to_transfer;
+					DWC_WRITE_REG32(&dev_if->in_ep_regs[i]->dieptsiz, dieptsiz.d32);
+					diepdma = ep->dwc_ep.dma_addr + (xfer_size - remain_to_transfer);
+					DWC_WRITE_REG32(&dev_if->in_ep_regs[i]->diepdma, diepdma);
+				}
+				i = core_if->nextep_seq[i];
+			} while (i != core_if->first_in_nextep_seq);
+		} else { // dma_desc_enable
+				DWC_PRINTF("%s Learning Queue not supported in DDMA\n", __func__);
+		}
+
+		/* Restart transfers in predicted sequences */
+		i = core_if->first_in_nextep_seq;
+		do {
+			dieptsiz.d32 = DWC_READ_REG32(&dev_if->in_ep_regs[i]->dieptsiz);
+			depctl.d32 = DWC_READ_REG32(&dev_if->in_ep_regs[i]->diepctl);
+			if (dieptsiz.b.pktcnt != 0) {
+				depctl.d32 = DWC_READ_REG32(&dev_if->in_ep_regs[i]->diepctl);
+				depctl.b.epena = 1;
+				depctl.b.cnak = 1;
+				DWC_WRITE_REG32(&dev_if->in_ep_regs[i]->diepctl, depctl.d32);
+			}
+			i = core_if->nextep_seq[i];
+		} while (i != core_if->first_in_nextep_seq);
+
+		/* Clear the global non-periodic IN NAK handshake */
+		dctl.d32 = 0;
+		dctl.b.cgnpinnak = 1;
+		DWC_MODIFY_REG32(&dev_if->dev_global_regs->dctl, dctl.d32, dctl.d32);
+
+		/* Unmask EP Mismatch interrupt */
+		gintmsk_data.d32 = 0;
+		gintmsk_data.b.epmismatch = 1;
+		DWC_MODIFY_REG32(&core_if->core_global_regs->gintmsk, 0, gintmsk_data.d32);
+
+		core_if->start_predict = 0;
+
+	}
+}
+
+/**
+ * Handler for the IN EP timeout handshake interrupt.
+ */
+static inline void handle_in_ep_timeout_intr(dwc_otg_pcd_t * pcd,
+					     const uint32_t epnum)
+{
+	dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd);
+	dwc_otg_dev_if_t *dev_if = core_if->dev_if;
+
+#ifdef DEBUG
+	deptsiz_data_t dieptsiz = {.d32 = 0 };
+	uint32_t num = 0;
+#endif
+	dctl_data_t dctl = {.d32 = 0 };
+	dwc_otg_pcd_ep_t *ep;
+
+	gintmsk_data_t intr_mask = {.d32 = 0 };
+
+	ep = get_in_ep(pcd, epnum);
+
+	/* Disable the NP Tx Fifo Empty Interrrupt */
+	if (!core_if->dma_enable) {
+		intr_mask.b.nptxfempty = 1;
+		DWC_MODIFY_REG32(&core_if->core_global_regs->gintmsk,
+				 intr_mask.d32, 0);
+	}
+	/** @todo NGS Check EP type.
+	 * Implement for Periodic EPs */
+	/*
+	 * Non-periodic EP
+	 */
+	/* Enable the Global IN NAK Effective Interrupt */
+	intr_mask.b.ginnakeff = 1;
+	DWC_MODIFY_REG32(&core_if->core_global_regs->gintmsk, 0, intr_mask.d32);
+
+	/* Set Global IN NAK */
+	dctl.b.sgnpinnak = 1;
+	DWC_MODIFY_REG32(&dev_if->dev_global_regs->dctl, dctl.d32, dctl.d32);
+
+	ep->stopped = 1;
+
+#ifdef DEBUG
+	dieptsiz.d32 = DWC_READ_REG32(&dev_if->in_ep_regs[num]->dieptsiz);
+	DWC_DEBUGPL(DBG_ANY, "pktcnt=%d size=%d\n",
+		    dieptsiz.b.pktcnt, dieptsiz.b.xfersize);
+#endif
+
+#ifdef DISABLE_PERIODIC_EP
+	/*
+	 * Set the NAK bit for this EP to
+	 * start the disable process.
+	 */
+	diepctl.d32 = 0;
+	diepctl.b.snak = 1;
+	DWC_MODIFY_REG32(&dev_if->in_ep_regs[num]->diepctl, diepctl.d32,
+			 diepctl.d32);
+	ep->disabling = 1;
+	ep->stopped = 1;
+#endif
+}
+
+/**
+ * Handler for the IN EP NAK interrupt.
+ */
+static inline int32_t handle_in_ep_nak_intr(dwc_otg_pcd_t * pcd,
+					    const uint32_t epnum)
+{
+	/** @todo implement ISR */
+	dwc_otg_core_if_t *core_if;
+	diepmsk_data_t intr_mask = {.d32 = 0 };
+
+	DWC_PRINTF("INTERRUPT Handler not implemented for %s\n", "IN EP NAK");
+	core_if = GET_CORE_IF(pcd);
+	intr_mask.b.nak = 1;
+
+	if (core_if->multiproc_int_enable) {
+		DWC_MODIFY_REG32(&core_if->dev_if->dev_global_regs->
+				 diepeachintmsk[epnum], intr_mask.d32, 0);
+	} else {
+		DWC_MODIFY_REG32(&core_if->dev_if->dev_global_regs->diepmsk,
+				 intr_mask.d32, 0);
+	}
+
+	return 1;
+}
+
+/**
+ * Handler for the OUT EP Babble interrupt.
+ */
+static inline int32_t handle_out_ep_babble_intr(dwc_otg_pcd_t * pcd,
+						const uint32_t epnum)
+{
+	/** @todo implement ISR */
+	dwc_otg_core_if_t *core_if;
+	doepmsk_data_t intr_mask = {.d32 = 0 };
+
+	DWC_PRINTF("INTERRUPT Handler not implemented for %s\n",
+		   "OUT EP Babble");
+	core_if = GET_CORE_IF(pcd);
+	intr_mask.b.babble = 1;
+
+	if (core_if->multiproc_int_enable) {
+		DWC_MODIFY_REG32(&core_if->dev_if->dev_global_regs->
+				 doepeachintmsk[epnum], intr_mask.d32, 0);
+	} else {
+		DWC_MODIFY_REG32(&core_if->dev_if->dev_global_regs->doepmsk,
+				 intr_mask.d32, 0);
+	}
+
+	return 1;
+}
+
+/**
+ * Handler for the OUT EP NAK interrupt.
+ */
+static inline int32_t handle_out_ep_nak_intr(dwc_otg_pcd_t * pcd,
+					     const uint32_t epnum)
+{
+	/** @todo implement ISR */
+	dwc_otg_core_if_t *core_if;
+	doepmsk_data_t intr_mask = {.d32 = 0 };
+
+	DWC_DEBUGPL(DBG_ANY, "INTERRUPT Handler not implemented for %s\n", "OUT EP NAK");
+	core_if = GET_CORE_IF(pcd);
+	intr_mask.b.nak = 1;
+
+	if (core_if->multiproc_int_enable) {
+		DWC_MODIFY_REG32(&core_if->dev_if->dev_global_regs->
+				 doepeachintmsk[epnum], intr_mask.d32, 0);
+	} else {
+		DWC_MODIFY_REG32(&core_if->dev_if->dev_global_regs->doepmsk,
+				 intr_mask.d32, 0);
+	}
+
+	return 1;
+}
+
+/**
+ * Handler for the OUT EP NYET interrupt.
+ */
+static inline int32_t handle_out_ep_nyet_intr(dwc_otg_pcd_t * pcd,
+					      const uint32_t epnum)
+{
+	/** @todo implement ISR */
+	dwc_otg_core_if_t *core_if;
+	doepmsk_data_t intr_mask = {.d32 = 0 };
+
+	DWC_PRINTF("INTERRUPT Handler not implemented for %s\n", "OUT EP NYET");
+	core_if = GET_CORE_IF(pcd);
+	intr_mask.b.nyet = 1;
+
+	if (core_if->multiproc_int_enable) {
+		DWC_MODIFY_REG32(&core_if->dev_if->dev_global_regs->
+				 doepeachintmsk[epnum], intr_mask.d32, 0);
+	} else {
+		DWC_MODIFY_REG32(&core_if->dev_if->dev_global_regs->doepmsk,
+				 intr_mask.d32, 0);
+	}
+
+	return 1;
+}
+
+/**
+ * This interrupt indicates that an IN EP has a pending Interrupt.
+ * The sequence for handling the IN EP interrupt is shown below:
+ * -#	Read the Device All Endpoint Interrupt register
+ * -#	Repeat the following for each IN EP interrupt bit set (from
+ *		LSB to MSB).
+ * -#	Read the Device Endpoint Interrupt (DIEPINTn) register
+ * -#	If "Transfer Complete" call the request complete function
+ * -#	If "Endpoint Disabled" complete the EP disable procedure.
+ * -#	If "AHB Error Interrupt" log error
+ * -#	If "Time-out Handshake" log error
+ * -#	If "IN Token Received when TxFIFO Empty" write packet to Tx
+ *		FIFO.
+ * -#	If "IN Token EP Mismatch" (disable, this is handled by EP
+ *		Mismatch Interrupt)
+ */
+static int32_t dwc_otg_pcd_handle_in_ep_intr(dwc_otg_pcd_t * pcd)
+{
+#define CLEAR_IN_EP_INTR(__core_if,__epnum,__intr) \
+do { \
+		diepint_data_t diepint = {.d32=0}; \
+		diepint.b.__intr = 1; \
+		DWC_WRITE_REG32(&__core_if->dev_if->in_ep_regs[__epnum]->diepint, \
+		diepint.d32); \
+} while (0)
+
+	dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd);
+	dwc_otg_dev_if_t *dev_if = core_if->dev_if;
+	diepint_data_t diepint = {.d32 = 0 };
+	depctl_data_t depctl = {.d32 = 0 };
+	uint32_t ep_intr;
+	uint32_t epnum = 0;
+	dwc_otg_pcd_ep_t *ep;
+	dwc_ep_t *dwc_ep;
+	gintmsk_data_t intr_mask = {.d32 = 0 };
+
+	DWC_DEBUGPL(DBG_PCDV, "%s(%p)\n", __func__, pcd);
+
+	/* Read in the device interrupt bits */
+	ep_intr = dwc_otg_read_dev_all_in_ep_intr(core_if);
+
+	/* Service the Device IN interrupts for each endpoint */
+	while (ep_intr) {
+		if (ep_intr & 0x1) {
+			uint32_t empty_msk;
+			/* Get EP pointer */
+			ep = get_in_ep(pcd, epnum);
+			dwc_ep = &ep->dwc_ep;
+
+			depctl.d32 =
+			    DWC_READ_REG32(&dev_if->in_ep_regs[epnum]->diepctl);
+			empty_msk =
+			    DWC_READ_REG32(&dev_if->
+					   dev_global_regs->dtknqr4_fifoemptymsk);
+
+			DWC_DEBUGPL(DBG_PCDV,
+				    "IN EP INTERRUPT - %d\nepmty_msk - %8x  diepctl - %8x\n",
+				    epnum, empty_msk, depctl.d32);
+
+			DWC_DEBUGPL(DBG_PCD,
+				    "EP%d-%s: type=%d, mps=%d\n",
+				    dwc_ep->num, (dwc_ep->is_in ? "IN" : "OUT"),
+				    dwc_ep->type, dwc_ep->maxpacket);
+
+			diepint.d32 =
+			    dwc_otg_read_dev_in_ep_intr(core_if, dwc_ep);
+
+			DWC_DEBUGPL(DBG_PCDV,
+				    "EP %d Interrupt Register - 0x%x\n", epnum,
+				    diepint.d32);
+			/* Transfer complete */
+			if (diepint.b.xfercompl) {
+				/* Disable the NP Tx FIFO Empty
+				 * Interrupt */
+				if (core_if->en_multiple_tx_fifo == 0) {
+					intr_mask.b.nptxfempty = 1;
+					DWC_MODIFY_REG32
+					    (&core_if->core_global_regs->gintmsk,
+					     intr_mask.d32, 0);
+				} else {
+					/* Disable the Tx FIFO Empty Interrupt for this EP */
+					uint32_t fifoemptymsk =
+					    0x1 << dwc_ep->num;
+					DWC_MODIFY_REG32(&core_if->
+							 dev_if->dev_global_regs->dtknqr4_fifoemptymsk,
+							 fifoemptymsk, 0);
+				}
+				/* Clear the bit in DIEPINTn for this interrupt */
+				CLEAR_IN_EP_INTR(core_if, epnum, xfercompl);
+
+				/* Complete the transfer */
+				if (epnum == 0) {
+					handle_ep0(pcd);
+				}
+#ifdef DWC_EN_ISOC
+				else if (dwc_ep->type == DWC_OTG_EP_TYPE_ISOC) {
+					if (!ep->stopped)
+						complete_iso_ep(pcd, ep);
+				}
+#endif /* DWC_EN_ISOC */
+#ifdef DWC_UTE_PER_IO
+				else if (dwc_ep->type == DWC_OTG_EP_TYPE_ISOC) {
+					if (!ep->stopped)
+						complete_xiso_ep(ep);
+				}
+#endif /* DWC_UTE_PER_IO */
+				else {
+					if (dwc_ep->type == DWC_OTG_EP_TYPE_ISOC &&
+							dwc_ep->bInterval > 1) {
+						dwc_ep->frame_num += dwc_ep->bInterval;
+						if (dwc_ep->frame_num > 0x3FFF)
+						{
+							dwc_ep->frm_overrun = 1;
+							dwc_ep->frame_num &= 0x3FFF;
+						} else
+							dwc_ep->frm_overrun = 0;
+					}
+					complete_ep(ep);
+					if(diepint.b.nak)
+						CLEAR_IN_EP_INTR(core_if, epnum, nak);
+				}
+			}
+			/* Endpoint disable      */
+			if (diepint.b.epdisabled) {
+				DWC_DEBUGPL(DBG_ANY, "EP%d IN disabled\n",
+					    epnum);
+				handle_in_ep_disable_intr(pcd, epnum);
+
+				/* Clear the bit in DIEPINTn for this interrupt */
+				CLEAR_IN_EP_INTR(core_if, epnum, epdisabled);
+			}
+			/* AHB Error */
+			if (diepint.b.ahberr) {
+				DWC_ERROR("EP%d IN AHB Error\n", epnum);
+				/* Clear the bit in DIEPINTn for this interrupt */
+				CLEAR_IN_EP_INTR(core_if, epnum, ahberr);
+			}
+			/* TimeOUT Handshake (non-ISOC IN EPs) */
+			if (diepint.b.timeout) {
+				DWC_ERROR("EP%d IN Time-out\n", epnum);
+				handle_in_ep_timeout_intr(pcd, epnum);
+
+				CLEAR_IN_EP_INTR(core_if, epnum, timeout);
+			}
+			/** IN Token received with TxF Empty */
+			if (diepint.b.intktxfemp) {
+				DWC_DEBUGPL(DBG_ANY,
+					    "EP%d IN TKN TxFifo Empty\n",
+					    epnum);
+				if (!ep->stopped && epnum != 0) {
+
+					diepmsk_data_t diepmsk = {.d32 = 0 };
+					diepmsk.b.intktxfemp = 1;
+
+					if (core_if->multiproc_int_enable) {
+						DWC_MODIFY_REG32
+						    (&dev_if->dev_global_regs->diepeachintmsk
+						     [epnum], diepmsk.d32, 0);
+					} else {
+						DWC_MODIFY_REG32
+						    (&dev_if->dev_global_regs->diepmsk,
+						     diepmsk.d32, 0);
+					}
+				} else if (core_if->dma_desc_enable
+					   && epnum == 0
+					   && pcd->ep0state ==
+					   EP0_OUT_STATUS_PHASE) {
+					// EP0 IN set STALL
+					depctl.d32 =
+					    DWC_READ_REG32(&dev_if->in_ep_regs
+							   [epnum]->diepctl);
+
+					/* set the disable and stall bits */
+					if (depctl.b.epena) {
+						depctl.b.epdis = 1;
+					}
+					depctl.b.stall = 1;
+					DWC_WRITE_REG32(&dev_if->in_ep_regs
+							[epnum]->diepctl,
+							depctl.d32);
+				}
+				CLEAR_IN_EP_INTR(core_if, epnum, intktxfemp);
+			}
+			/** IN Token Received with EP mismatch */
+			if (diepint.b.intknepmis) {
+				DWC_DEBUGPL(DBG_ANY,
+					    "EP%d IN TKN EP Mismatch\n", epnum);
+				CLEAR_IN_EP_INTR(core_if, epnum, intknepmis);
+			}
+			/** IN Endpoint NAK Effective */
+			if (diepint.b.inepnakeff) {
+				DWC_DEBUGPL(DBG_ANY,
+					    "EP%d IN EP NAK Effective\n",
+					    epnum);
+				/* Periodic EP */
+				if (ep->disabling) {
+					depctl.d32 = 0;
+					depctl.b.snak = 1;
+					depctl.b.epdis = 1;
+					DWC_MODIFY_REG32(&dev_if->in_ep_regs
+							 [epnum]->diepctl,
+							 depctl.d32,
+							 depctl.d32);
+				}
+				CLEAR_IN_EP_INTR(core_if, epnum, inepnakeff);
+
+			}
+
+			/** IN EP Tx FIFO Empty Intr */
+			if (diepint.b.emptyintr) {
+				DWC_DEBUGPL(DBG_ANY,
+					    "EP%d Tx FIFO Empty Intr \n",
+					    epnum);
+				write_empty_tx_fifo(pcd, epnum);
+
+				CLEAR_IN_EP_INTR(core_if, epnum, emptyintr);
+
+			}
+
+			/** IN EP BNA Intr */
+			if (diepint.b.bna) {
+				CLEAR_IN_EP_INTR(core_if, epnum, bna);
+				if (core_if->dma_desc_enable) {
+#ifdef DWC_EN_ISOC
+					if (dwc_ep->type ==
+					    DWC_OTG_EP_TYPE_ISOC) {
+						/*
+						 * This checking is performed to prevent first "false" BNA
+						 * handling occuring right after reconnect
+						 */
+						if (dwc_ep->next_frame !=
+						    0xffffffff)
+							dwc_otg_pcd_handle_iso_bna(ep);
+					} else
+#endif				/* DWC_EN_ISOC */
+					{
+						dwc_otg_pcd_handle_noniso_bna(ep);
+					}
+				}
+			}
+			/* NAK Interrutp */
+			if (diepint.b.nak) {
+				DWC_DEBUGPL(DBG_ANY, "EP%d IN NAK Interrupt\n",
+					    epnum);
+				if (ep->dwc_ep.type == DWC_OTG_EP_TYPE_ISOC) {
+					depctl_data_t depctl;
+					if (ep->dwc_ep.frame_num == 0xFFFFFFFF) {
+						ep->dwc_ep.frame_num = core_if->frame_num;
+						if (ep->dwc_ep.bInterval > 1) {
+							depctl.d32 = 0;
+							depctl.d32 = DWC_READ_REG32(&dev_if->in_ep_regs[epnum]->diepctl);
+							if (ep->dwc_ep.frame_num & 0x1) {
+								depctl.b.setd1pid = 1;
+								depctl.b.setd0pid = 0;
+							} else {
+								depctl.b.setd0pid = 1;
+								depctl.b.setd1pid = 0;
+							}
+							DWC_WRITE_REG32(&dev_if->in_ep_regs[epnum]->diepctl, depctl.d32);
+						}
+						start_next_request(ep);
+					}
+					ep->dwc_ep.frame_num += ep->dwc_ep.bInterval;
+					if (dwc_ep->frame_num > 0x3FFF)	{
+						dwc_ep->frm_overrun = 1;
+						dwc_ep->frame_num &= 0x3FFF;
+					} else
+						dwc_ep->frm_overrun = 0;
+				}
+
+				CLEAR_IN_EP_INTR(core_if, epnum, nak);
+			}
+		}
+		epnum++;
+		ep_intr >>= 1;
+	}
+
+	return 1;
+#undef CLEAR_IN_EP_INTR
+}
+
+/**
+ * This interrupt indicates that an OUT EP has a pending Interrupt.
+ * The sequence for handling the OUT EP interrupt is shown below:
+ * -#	Read the Device All Endpoint Interrupt register
+ * -#	Repeat the following for each OUT EP interrupt bit set (from
+ *		LSB to MSB).
+ * -#	Read the Device Endpoint Interrupt (DOEPINTn) register
+ * -#	If "Transfer Complete" call the request complete function
+ * -#	If "Endpoint Disabled" complete the EP disable procedure.
+ * -#	If "AHB Error Interrupt" log error
+ * -#	If "Setup Phase Done" process Setup Packet (See Standard USB
+ *		Command Processing)
+ */
+static int32_t dwc_otg_pcd_handle_out_ep_intr(dwc_otg_pcd_t * pcd)
+{
+#define CLEAR_OUT_EP_INTR(__core_if,__epnum,__intr) \
+do { \
+		doepint_data_t doepint = {.d32=0}; \
+		doepint.b.__intr = 1; \
+		DWC_WRITE_REG32(&__core_if->dev_if->out_ep_regs[__epnum]->doepint, \
+		doepint.d32); \
+} while (0)
+
+	dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd);
+	uint32_t ep_intr;
+	doepint_data_t doepint = {.d32 = 0 };
+	uint32_t epnum = 0;
+	dwc_otg_pcd_ep_t *ep;
+	dwc_ep_t *dwc_ep;
+	dctl_data_t dctl = {.d32 = 0 };
+	gintmsk_data_t gintmsk = {.d32 = 0 };
+
+
+	DWC_DEBUGPL(DBG_PCDV, "%s()\n", __func__);
+
+	/* Read in the device interrupt bits */
+	ep_intr = dwc_otg_read_dev_all_out_ep_intr(core_if);
+
+	while (ep_intr) {
+		if (ep_intr & 0x1) {
+			/* Get EP pointer */
+			ep = get_out_ep(pcd, epnum);
+			dwc_ep = &ep->dwc_ep;
+
+#ifdef VERBOSE
+			DWC_DEBUGPL(DBG_PCDV,
+				    "EP%d-%s: type=%d, mps=%d\n",
+				    dwc_ep->num, (dwc_ep->is_in ? "IN" : "OUT"),
+				    dwc_ep->type, dwc_ep->maxpacket);
+#endif
+			doepint.d32 =
+			    dwc_otg_read_dev_out_ep_intr(core_if, dwc_ep);
+			/* Moved this interrupt upper due to core deffect of asserting
+			 * OUT EP 0 xfercompl along with stsphsrcvd in BDMA */
+			if (doepint.b.stsphsercvd) {
+				deptsiz0_data_t deptsiz;
+				CLEAR_OUT_EP_INTR(core_if, epnum, stsphsercvd);
+				deptsiz.d32 =
+				    DWC_READ_REG32(&core_if->dev_if->
+						   out_ep_regs[0]->doeptsiz);
+				if (core_if->snpsid >= OTG_CORE_REV_3_00a
+				    && core_if->dma_enable
+				    && core_if->dma_desc_enable == 0
+				    && doepint.b.xfercompl
+				    && deptsiz.b.xfersize == 24) {
+					CLEAR_OUT_EP_INTR(core_if, epnum,
+							  xfercompl);
+					doepint.b.xfercompl = 0;
+					ep0_out_start(core_if, pcd);
+				}
+				if ((core_if->dma_desc_enable) ||
+				    (core_if->dma_enable
+				     && core_if->snpsid >=
+				     OTG_CORE_REV_3_00a)) {
+					do_setup_in_status_phase(pcd);
+				}
+			}
+			/* Transfer complete */
+			if (doepint.b.xfercompl) {
+
+				if (epnum == 0) {
+					/* Clear the bit in DOEPINTn for this interrupt */
+					CLEAR_OUT_EP_INTR(core_if, epnum, xfercompl);
+					if (core_if->snpsid >= OTG_CORE_REV_3_00a) {
+						DWC_DEBUGPL(DBG_PCDV, "DOEPINT=%x doepint=%x\n",
+							DWC_READ_REG32(&core_if->dev_if->out_ep_regs[0]->doepint),
+							doepint.d32);
+						DWC_DEBUGPL(DBG_PCDV, "DOEPCTL=%x \n",
+							DWC_READ_REG32(&core_if->dev_if->out_ep_regs[0]->doepctl));
+
+						if (core_if->snpsid >= OTG_CORE_REV_3_00a
+							&& core_if->dma_enable == 0) {
+							doepint_data_t doepint;
+							doepint.d32 = DWC_READ_REG32(&core_if->dev_if->
+														out_ep_regs[0]->doepint);
+							if (pcd->ep0state == EP0_IDLE && doepint.b.sr) {
+								CLEAR_OUT_EP_INTR(core_if, epnum, sr);
+								goto exit_xfercompl;
+							}
+						}
+						/* In case of DDMA  look at SR bit to go to the Data Stage */
+						if (core_if->dma_desc_enable) {
+							dev_dma_desc_sts_t status = {.d32 = 0};
+							if (pcd->ep0state == EP0_IDLE) {
+								status.d32 = core_if->dev_if->setup_desc_addr[core_if->
+											dev_if->setup_desc_index]->status.d32;
+								if(pcd->data_terminated) {
+									 pcd->data_terminated = 0;
+									 status.d32 = core_if->dev_if->out_desc_addr->status.d32;
+									 dwc_memcpy(&pcd->setup_pkt->req, pcd->backup_buf, 8);
+								}
+								if (status.b.sr) {
+									if (doepint.b.setup) {
+										DWC_DEBUGPL(DBG_PCDV, "DMA DESC EP0_IDLE SR=1 setup=1\n");
+										/* Already started data stage, clear setup */
+										CLEAR_OUT_EP_INTR(core_if, epnum, setup);
+										doepint.b.setup = 0;
+										handle_ep0(pcd);
+										/* Prepare for more setup packets */
+										if (pcd->ep0state == EP0_IN_STATUS_PHASE ||
+											pcd->ep0state == EP0_IN_DATA_PHASE) {
+											ep0_out_start(core_if, pcd);
+										}
+
+										goto exit_xfercompl;
+									} else {
+										/* Prepare for more setup packets */
+										DWC_DEBUGPL(DBG_PCDV,
+											"EP0_IDLE SR=1 setup=0 new setup comes\n");
+										ep0_out_start(core_if, pcd);
+									}
+								}
+							} else {
+								dwc_otg_pcd_request_t *req;
+								dev_dma_desc_sts_t status = {.d32 = 0};
+								diepint_data_t diepint0;
+								diepint0.d32 = DWC_READ_REG32(&core_if->dev_if->
+															in_ep_regs[0]->diepint);
+
+								if (pcd->ep0state == EP0_STALL || pcd->ep0state == EP0_DISCONNECT) {
+									DWC_ERROR("EP0 is stalled/disconnected\n");
+								}
+
+								/* Clear IN xfercompl if set */
+								if (diepint0.b.xfercompl && (pcd->ep0state == EP0_IN_STATUS_PHASE
+									|| pcd->ep0state == EP0_IN_DATA_PHASE)) {
+									DWC_WRITE_REG32(&core_if->dev_if->
+										in_ep_regs[0]->diepint, diepint0.d32);
+								}
+
+								status.d32 = core_if->dev_if->setup_desc_addr[core_if->
+									dev_if->setup_desc_index]->status.d32;
+
+								if (ep->dwc_ep.xfer_count != ep->dwc_ep.total_len
+									&& (pcd->ep0state == EP0_OUT_DATA_PHASE))
+									status.d32 = core_if->dev_if->out_desc_addr->status.d32;
+								if (pcd->ep0state == EP0_OUT_STATUS_PHASE)
+									status.d32 = core_if->dev_if->
+									out_desc_addr->status.d32;
+
+								if (status.b.sr) {
+									if (DWC_CIRCLEQ_EMPTY(&ep->queue)) {
+										DWC_DEBUGPL(DBG_PCDV, "Request queue empty!!\n");
+									} else {
+										DWC_DEBUGPL(DBG_PCDV, "complete req!!\n");
+										req = DWC_CIRCLEQ_FIRST(&ep->queue);
+										if (ep->dwc_ep.xfer_count != ep->dwc_ep.total_len &&
+											pcd->ep0state == EP0_OUT_DATA_PHASE) {
+												/* Read arrived setup packet from req->buf */
+												dwc_memcpy(&pcd->setup_pkt->req,
+													req->buf + ep->dwc_ep.xfer_count, 8);
+										}
+										req->actual = ep->dwc_ep.xfer_count;
+										dwc_otg_request_done(ep, req, -ECONNRESET);
+										ep->dwc_ep.start_xfer_buff = 0;
+										ep->dwc_ep.xfer_buff = 0;
+										ep->dwc_ep.xfer_len = 0;
+									}
+									pcd->ep0state = EP0_IDLE;
+									if (doepint.b.setup) {
+										DWC_DEBUGPL(DBG_PCDV, "EP0_IDLE SR=1 setup=1\n");
+										/* Data stage started, clear setup */
+										CLEAR_OUT_EP_INTR(core_if, epnum, setup);
+										doepint.b.setup = 0;
+										handle_ep0(pcd);
+										/* Prepare for setup packets if ep0in was enabled*/
+										if (pcd->ep0state == EP0_IN_STATUS_PHASE) {
+											ep0_out_start(core_if, pcd);
+										}
+
+										goto exit_xfercompl;
+									} else {
+										/* Prepare for more setup packets */
+										DWC_DEBUGPL(DBG_PCDV,
+											"EP0_IDLE SR=1 setup=0 new setup comes 2\n");
+										ep0_out_start(core_if, pcd);
+									}
+								}
+							}
+						}
+						if (core_if->snpsid >= OTG_CORE_REV_2_94a && core_if->dma_enable
+							&& core_if->dma_desc_enable == 0) {
+							doepint_data_t doepint_temp = {.d32 = 0};
+							deptsiz0_data_t doeptsize0 = {.d32 = 0 };
+							doepint_temp.d32 = DWC_READ_REG32(&core_if->dev_if->
+															out_ep_regs[ep->dwc_ep.num]->doepint);
+							doeptsize0.d32 = DWC_READ_REG32(&core_if->dev_if->
+															out_ep_regs[ep->dwc_ep.num]->doeptsiz);
+							if (pcd->ep0state == EP0_IDLE) {
+								if (doepint_temp.b.sr) {
+									CLEAR_OUT_EP_INTR(core_if, epnum, sr);
+								}
+									doepint.d32 = DWC_READ_REG32(&core_if->dev_if->
+																	out_ep_regs[0]->doepint);
+									if (doeptsize0.b.supcnt == 3) {
+										DWC_DEBUGPL(DBG_ANY, "Rolling over!!!!!!!\n");
+										ep->dwc_ep.stp_rollover = 1;
+									}
+									if (doepint.b.setup) {
+retry:
+										/* Already started data stage, clear setup */
+										CLEAR_OUT_EP_INTR(core_if, epnum, setup);
+										doepint.b.setup = 0;
+										handle_ep0(pcd);
+										ep->dwc_ep.stp_rollover = 0;
+										/* Prepare for more setup packets */
+										if (pcd->ep0state == EP0_IN_STATUS_PHASE ||
+											pcd->ep0state == EP0_IN_DATA_PHASE) {
+											ep0_out_start(core_if, pcd);
+										}
+										goto exit_xfercompl;
+									} else {
+										/* Prepare for more setup packets */
+										DWC_DEBUGPL(DBG_ANY,
+											"EP0_IDLE SR=1 setup=0 new setup comes\n");
+										doepint.d32 = DWC_READ_REG32(&core_if->dev_if->
+																	out_ep_regs[0]->doepint);
+										if(doepint.b.setup)
+											goto retry;
+										ep0_out_start(core_if, pcd);
+									}
+							} else {
+								dwc_otg_pcd_request_t *req;
+								diepint_data_t diepint0 = {.d32 = 0};
+								doepint_data_t doepint_temp = {.d32 = 0};
+								depctl_data_t diepctl0;
+								diepint0.d32 = DWC_READ_REG32(&core_if->dev_if->
+																in_ep_regs[0]->diepint);
+								diepctl0.d32 = DWC_READ_REG32(&core_if->dev_if->
+																in_ep_regs[0]->diepctl);
+
+								if (pcd->ep0state == EP0_IN_DATA_PHASE
+									|| pcd->ep0state == EP0_IN_STATUS_PHASE) {
+									if (diepint0.b.xfercompl) {
+										DWC_WRITE_REG32(&core_if->dev_if->
+											in_ep_regs[0]->diepint, diepint0.d32);
+									}
+									if (diepctl0.b.epena) {
+										diepint_data_t diepint = {.d32 = 0};
+										diepctl0.b.snak = 1;
+										DWC_WRITE_REG32(&core_if->dev_if->
+														in_ep_regs[0]->diepctl, diepctl0.d32);
+										do {
+											dwc_udelay(10);
+											diepint.d32 = DWC_READ_REG32(&core_if->dev_if->
+												in_ep_regs[0]->diepint);
+										} while (!diepint.b.inepnakeff);
+										diepint.b.inepnakeff = 1;
+										DWC_WRITE_REG32(&core_if->dev_if->
+											in_ep_regs[0]->diepint, diepint.d32);
+										diepctl0.d32 = 0;
+										diepctl0.b.epdis = 1;
+										DWC_WRITE_REG32(&core_if->dev_if->in_ep_regs[0]->diepctl,
+														diepctl0.d32);
+										do {
+											dwc_udelay(10);
+											diepint.d32 = DWC_READ_REG32(&core_if->dev_if->
+												in_ep_regs[0]->diepint);
+										} while (!diepint.b.epdisabled);
+										diepint.b.epdisabled = 1;
+										DWC_WRITE_REG32(&core_if->dev_if->in_ep_regs[0]->diepint,
+															diepint.d32);
+									}
+								}
+								doepint_temp.d32 = DWC_READ_REG32(&core_if->dev_if->
+																out_ep_regs[ep->dwc_ep.num]->doepint);
+								if (doepint_temp.b.sr) {
+									CLEAR_OUT_EP_INTR(core_if, epnum, sr);
+									if (DWC_CIRCLEQ_EMPTY(&ep->queue)) {
+										DWC_DEBUGPL(DBG_PCDV, "Request queue empty!!\n");
+									} else {
+										DWC_DEBUGPL(DBG_PCDV, "complete req!!\n");
+										req = DWC_CIRCLEQ_FIRST(&ep->queue);
+										if (ep->dwc_ep.xfer_count != ep->dwc_ep.total_len &&
+											pcd->ep0state == EP0_OUT_DATA_PHASE) {
+												/* Read arrived setup packet from req->buf */
+												dwc_memcpy(&pcd->setup_pkt->req,
+													req->buf + ep->dwc_ep.xfer_count, 8);
+										}
+										req->actual = ep->dwc_ep.xfer_count;
+										dwc_otg_request_done(ep, req, -ECONNRESET);
+										ep->dwc_ep.start_xfer_buff = 0;
+										ep->dwc_ep.xfer_buff = 0;
+										ep->dwc_ep.xfer_len = 0;
+									}
+									pcd->ep0state = EP0_IDLE;
+									if (doepint.b.setup) {
+										DWC_DEBUGPL(DBG_PCDV, "EP0_IDLE SR=1 setup=1\n");
+										/* Data stage started, clear setup */
+										CLEAR_OUT_EP_INTR(core_if, epnum, setup);
+										doepint.b.setup = 0;
+										handle_ep0(pcd);
+										/* Prepare for setup packets if ep0in was enabled*/
+										if (pcd->ep0state == EP0_IN_STATUS_PHASE) {
+											ep0_out_start(core_if, pcd);
+										}
+										goto exit_xfercompl;
+									} else {
+										/* Prepare for more setup packets */
+										DWC_DEBUGPL(DBG_PCDV,
+											"EP0_IDLE SR=1 setup=0 new setup comes 2\n");
+										ep0_out_start(core_if, pcd);
+									}
+								}
+							}
+						}
+						if (core_if->dma_enable == 0 || pcd->ep0state != EP0_IDLE)
+							handle_ep0(pcd);
+exit_xfercompl:
+						DWC_DEBUGPL(DBG_PCDV, "DOEPINT=%x doepint=%x\n",
+							dwc_otg_read_dev_out_ep_intr(core_if, dwc_ep), doepint.d32);
+					} else {
+					if (core_if->dma_desc_enable == 0
+					    || pcd->ep0state != EP0_IDLE)
+						handle_ep0(pcd);
+					}
+#ifdef DWC_EN_ISOC
+				} else if (dwc_ep->type == DWC_OTG_EP_TYPE_ISOC) {
+					if (doepint.b.pktdrpsts == 0) {
+						/* Clear the bit in DOEPINTn for this interrupt */
+						CLEAR_OUT_EP_INTR(core_if,
+								  epnum,
+								  xfercompl);
+						complete_iso_ep(pcd, ep);
+					} else {
+
+						doepint_data_t doepint = {.d32 = 0 };
+						doepint.b.xfercompl = 1;
+						doepint.b.pktdrpsts = 1;
+						DWC_WRITE_REG32
+						    (&core_if->dev_if->out_ep_regs
+						     [epnum]->doepint,
+						     doepint.d32);
+						if (handle_iso_out_pkt_dropped
+						    (core_if, dwc_ep)) {
+							complete_iso_ep(pcd,
+									ep);
+						}
+					}
+#endif /* DWC_EN_ISOC */
+#ifdef DWC_UTE_PER_IO
+				} else if (dwc_ep->type == DWC_OTG_EP_TYPE_ISOC) {
+					CLEAR_OUT_EP_INTR(core_if, epnum, xfercompl);
+					if (!ep->stopped)
+						complete_xiso_ep(ep);
+#endif /* DWC_UTE_PER_IO */
+				} else {
+					/* Clear the bit in DOEPINTn for this interrupt */
+					CLEAR_OUT_EP_INTR(core_if, epnum,
+							  xfercompl);
+
+					if (core_if->core_params->dev_out_nak) {
+						DWC_TIMER_CANCEL(pcd->core_if->ep_xfer_timer[epnum]);
+						pcd->core_if->ep_xfer_info[epnum].state = 0;
+#ifdef DEBUG
+						print_memory_payload(pcd, dwc_ep);
+#endif
+					}
+					complete_ep(ep);
+				}
+
+			}
+
+			/* Endpoint disable      */
+			if (doepint.b.epdisabled) {
+
+				/* Clear the bit in DOEPINTn for this interrupt */
+				CLEAR_OUT_EP_INTR(core_if, epnum, epdisabled);
+				if (core_if->core_params->dev_out_nak) {
+#ifdef DEBUG
+					print_memory_payload(pcd, dwc_ep);
+#endif
+					/* In case of timeout condition */
+					if (core_if->ep_xfer_info[epnum].state == 2) {
+						dctl.d32 = DWC_READ_REG32(&core_if->dev_if->
+										dev_global_regs->dctl);
+						dctl.b.cgoutnak = 1;
+						DWC_WRITE_REG32(&core_if->dev_if->dev_global_regs->dctl,
+																dctl.d32);
+						/* Unmask goutnakeff interrupt which was masked
+						 * during handle nak out interrupt */
+						gintmsk.b.goutnakeff = 1;
+						DWC_MODIFY_REG32(&core_if->core_global_regs->gintmsk,
+																0, gintmsk.d32);
+
+						complete_ep(ep);
+					}
+				}
+				if (ep->dwc_ep.type == DWC_OTG_EP_TYPE_ISOC)
+				{
+					dctl_data_t dctl;
+					gintmsk_data_t intr_mask = {.d32 = 0};
+					dwc_otg_pcd_request_t *req = 0;
+
+					dctl.d32 = DWC_READ_REG32(&core_if->dev_if->
+						dev_global_regs->dctl);
+					dctl.b.cgoutnak = 1;
+					DWC_WRITE_REG32(&core_if->dev_if->dev_global_regs->dctl,
+						dctl.d32);
+
+					intr_mask.d32 = 0;
+					intr_mask.b.incomplisoout = 1;
+
+					/* Get any pending requests */
+					if (!DWC_CIRCLEQ_EMPTY(&ep->queue)) {
+						req = DWC_CIRCLEQ_FIRST(&ep->queue);
+						if (!req) {
+							DWC_PRINTF("complete_ep 0x%p, req = NULL!\n", ep);
+						} else {
+							dwc_otg_request_done(ep, req, 0);
+							start_next_request(ep);
+						}
+					} else {
+						DWC_PRINTF("complete_ep 0x%p, ep->queue empty!\n", ep);
+					}
+				}
+			}
+			/* AHB Error */
+			if (doepint.b.ahberr) {
+				DWC_ERROR("EP%d OUT AHB Error\n", epnum);
+				DWC_ERROR("EP%d DEPDMA=0x%08x \n",
+					  epnum, core_if->dev_if->out_ep_regs[epnum]->doepdma);
+				CLEAR_OUT_EP_INTR(core_if, epnum, ahberr);
+			}
+			/* Setup Phase Done (contorl EPs) */
+			if (doepint.b.setup) {
+#ifdef DEBUG_EP0
+				DWC_DEBUGPL(DBG_PCD, "EP%d SETUP Done\n", epnum);
+#endif
+				CLEAR_OUT_EP_INTR(core_if, epnum, setup);
+
+				handle_ep0(pcd);
+			}
+
+			/** OUT EP BNA Intr */
+			if (doepint.b.bna) {
+				CLEAR_OUT_EP_INTR(core_if, epnum, bna);
+				if (core_if->dma_desc_enable) {
+#ifdef DWC_EN_ISOC
+					if (dwc_ep->type ==
+					    DWC_OTG_EP_TYPE_ISOC) {
+						/*
+						 * This checking is performed to prevent first "false" BNA
+						 * handling occuring right after reconnect
+						 */
+						if (dwc_ep->next_frame !=
+						    0xffffffff)
+							dwc_otg_pcd_handle_iso_bna(ep);
+					} else
+#endif				/* DWC_EN_ISOC */
+					{
+						dwc_otg_pcd_handle_noniso_bna(ep);
+					}
+				}
+			}
+			/* Babble Interrupt */
+			if (doepint.b.babble) {
+				DWC_DEBUGPL(DBG_ANY, "EP%d OUT Babble\n",
+					    epnum);
+				handle_out_ep_babble_intr(pcd, epnum);
+
+				CLEAR_OUT_EP_INTR(core_if, epnum, babble);
+			}
+			if (doepint.b.outtknepdis) {
+				DWC_DEBUGPL(DBG_ANY, "EP%d OUT Token received when EP is \
+					disabled\n",epnum);
+				if (ep->dwc_ep.type == DWC_OTG_EP_TYPE_ISOC) {
+					doepmsk_data_t doepmsk = {.d32 = 0};
+					ep->dwc_ep.frame_num = core_if->frame_num;
+					if (ep->dwc_ep.bInterval > 1) {
+						depctl_data_t depctl;
+						depctl.d32 = DWC_READ_REG32(&core_if->dev_if->
+													out_ep_regs[epnum]->doepctl);
+						if (ep->dwc_ep.frame_num & 0x1) {
+							depctl.b.setd1pid = 1;
+							depctl.b.setd0pid = 0;
+						} else {
+							depctl.b.setd0pid = 1;
+							depctl.b.setd1pid = 0;
+						}
+						DWC_WRITE_REG32(&core_if->dev_if->
+										out_ep_regs[epnum]->doepctl, depctl.d32);
+					}
+					start_next_request(ep);
+					doepmsk.b.outtknepdis = 1;
+					DWC_MODIFY_REG32(&core_if->dev_if->dev_global_regs->doepmsk,
+								 doepmsk.d32, 0);
+				}
+				CLEAR_OUT_EP_INTR(core_if, epnum, outtknepdis);
+			}
+
+			/* NAK Interrutp */
+			if (doepint.b.nak) {
+				DWC_DEBUGPL(DBG_ANY, "EP%d OUT NAK\n", epnum);
+				handle_out_ep_nak_intr(pcd, epnum);
+
+				CLEAR_OUT_EP_INTR(core_if, epnum, nak);
+			}
+			/* NYET Interrutp */
+			if (doepint.b.nyet) {
+				DWC_DEBUGPL(DBG_ANY, "EP%d OUT NYET\n", epnum);
+				handle_out_ep_nyet_intr(pcd, epnum);
+
+				CLEAR_OUT_EP_INTR(core_if, epnum, nyet);
+			}
+		}
+
+		epnum++;
+		ep_intr >>= 1;
+	}
+
+	return 1;
+
+#undef CLEAR_OUT_EP_INTR
+}
+static int drop_transfer(uint32_t trgt_fr, uint32_t curr_fr, uint8_t frm_overrun)
+{
+	int retval = 0;
+	if(!frm_overrun && curr_fr >= trgt_fr)
+		retval = 1;
+	else if (frm_overrun
+		 && (curr_fr >= trgt_fr && ((curr_fr - trgt_fr) < 0x3FFF / 2)))
+		retval = 1;
+	return retval;
+}
+/**
+ * Incomplete ISO IN Transfer Interrupt.
+ * This interrupt indicates one of the following conditions occurred
+ * while transmitting an ISOC transaction.
+ * - Corrupted IN Token for ISOC EP.
+ * - Packet not complete in FIFO.
+ * The follow actions will be taken:
+ *	-#	Determine the EP
+ *	-#	Set incomplete flag in dwc_ep structure
+ *	-#	Disable EP; when "Endpoint Disabled" interrupt is received
+ *		Flush FIFO
+ */
+int32_t dwc_otg_pcd_handle_incomplete_isoc_in_intr(dwc_otg_pcd_t * pcd)
+{
+	gintsts_data_t gintsts;
+
+#ifdef DWC_EN_ISOC
+	dwc_otg_dev_if_t *dev_if;
+	deptsiz_data_t deptsiz = {.d32 = 0 };
+	depctl_data_t depctl = {.d32 = 0 };
+	dsts_data_t dsts = {.d32 = 0 };
+	dwc_ep_t *dwc_ep;
+	int i;
+
+	dev_if = GET_CORE_IF(pcd)->dev_if;
+
+	for (i = 1; i <= dev_if->num_in_eps; ++i) {
+		dwc_ep = &pcd->in_ep[i].dwc_ep;
+		if (dwc_ep->active && dwc_ep->type == DWC_OTG_EP_TYPE_ISOC) {
+			deptsiz.d32 =
+			    DWC_READ_REG32(&dev_if->in_ep_regs[i]->dieptsiz);
+			depctl.d32 =
+			    DWC_READ_REG32(&dev_if->in_ep_regs[i]->diepctl);
+
+			if (depctl.b.epdis && deptsiz.d32) {
+				set_current_pkt_info(GET_CORE_IF(pcd), dwc_ep);
+				if (dwc_ep->cur_pkt >= dwc_ep->pkt_cnt) {
+					dwc_ep->cur_pkt = 0;
+					dwc_ep->proc_buf_num =
+					    (dwc_ep->proc_buf_num ^ 1) & 0x1;
+
+					if (dwc_ep->proc_buf_num) {
+						dwc_ep->cur_pkt_addr =
+						    dwc_ep->xfer_buff1;
+						dwc_ep->cur_pkt_dma_addr =
+						    dwc_ep->dma_addr1;
+					} else {
+						dwc_ep->cur_pkt_addr =
+						    dwc_ep->xfer_buff0;
+						dwc_ep->cur_pkt_dma_addr =
+						    dwc_ep->dma_addr0;
+					}
+
+				}
+
+				dsts.d32 =
+				    DWC_READ_REG32(&GET_CORE_IF(pcd)->dev_if->
+						   dev_global_regs->dsts);
+				dwc_ep->next_frame = dsts.b.soffn;
+
+				dwc_otg_iso_ep_start_frm_transfer(GET_CORE_IF
+								  (pcd),
+								  dwc_ep);
+			}
+		}
+	}
+
+#else
+	depctl_data_t depctl = {.d32 = 0 };
+	dwc_ep_t *dwc_ep;
+	dwc_otg_dev_if_t *dev_if;
+	int i;
+	dev_if = GET_CORE_IF(pcd)->dev_if;
+
+	DWC_DEBUGPL(DBG_PCD,"Incomplete ISO IN \n");
+
+	for (i = 1; i <= dev_if->num_in_eps; ++i) {
+		dwc_ep = &pcd->in_ep[i-1].dwc_ep;
+		depctl.d32 =
+			DWC_READ_REG32(&dev_if->in_ep_regs[i]->diepctl);
+		if (depctl.b.epena && dwc_ep->type == DWC_OTG_EP_TYPE_ISOC) {
+			if (drop_transfer(dwc_ep->frame_num, GET_CORE_IF(pcd)->frame_num,
+							dwc_ep->frm_overrun))
+			{
+				depctl.d32 =
+					DWC_READ_REG32(&dev_if->in_ep_regs[i]->diepctl);
+				depctl.b.snak = 1;
+				depctl.b.epdis = 1;
+				DWC_MODIFY_REG32(&dev_if->in_ep_regs[i]->diepctl, depctl.d32, depctl.d32);
+			}
+		}
+	}
+
+	/*intr_mask.b.incomplisoin = 1;
+	DWC_MODIFY_REG32(&GET_CORE_IF(pcd)->core_global_regs->gintmsk,
+			 intr_mask.d32, 0);	 */
+#endif				//DWC_EN_ISOC
+
+	/* Clear interrupt */
+	gintsts.d32 = 0;
+	gintsts.b.incomplisoin = 1;
+	DWC_WRITE_REG32(&GET_CORE_IF(pcd)->core_global_regs->gintsts,
+			gintsts.d32);
+
+	return 1;
+}
+
+/**
+ * Incomplete ISO OUT Transfer Interrupt.
+ *
+ * This interrupt indicates that the core has dropped an ISO OUT
+ * packet. The following conditions can be the cause:
+ * - FIFO Full, the entire packet would not fit in the FIFO.
+ * - CRC Error
+ * - Corrupted Token
+ * The follow actions will be taken:
+ *	-#	Determine the EP
+ *	-#	Set incomplete flag in dwc_ep structure
+ *	-#	Read any data from the FIFO
+ *	-#	Disable EP. When "Endpoint Disabled" interrupt is received
+ *		re-enable EP.
+ */
+int32_t dwc_otg_pcd_handle_incomplete_isoc_out_intr(dwc_otg_pcd_t * pcd)
+{
+
+	gintsts_data_t gintsts;
+
+#ifdef DWC_EN_ISOC
+	dwc_otg_dev_if_t *dev_if;
+	deptsiz_data_t deptsiz = {.d32 = 0 };
+	depctl_data_t depctl = {.d32 = 0 };
+	dsts_data_t dsts = {.d32 = 0 };
+	dwc_ep_t *dwc_ep;
+	int i;
+
+	dev_if = GET_CORE_IF(pcd)->dev_if;
+
+	for (i = 1; i <= dev_if->num_out_eps; ++i) {
+		dwc_ep = &pcd->in_ep[i].dwc_ep;
+		if (pcd->out_ep[i].dwc_ep.active &&
+		    pcd->out_ep[i].dwc_ep.type == DWC_OTG_EP_TYPE_ISOC) {
+			deptsiz.d32 =
+			    DWC_READ_REG32(&dev_if->out_ep_regs[i]->doeptsiz);
+			depctl.d32 =
+			    DWC_READ_REG32(&dev_if->out_ep_regs[i]->doepctl);
+
+			if (depctl.b.epdis && deptsiz.d32) {
+				set_current_pkt_info(GET_CORE_IF(pcd),
+						     &pcd->out_ep[i].dwc_ep);
+				if (dwc_ep->cur_pkt >= dwc_ep->pkt_cnt) {
+					dwc_ep->cur_pkt = 0;
+					dwc_ep->proc_buf_num =
+					    (dwc_ep->proc_buf_num ^ 1) & 0x1;
+
+					if (dwc_ep->proc_buf_num) {
+						dwc_ep->cur_pkt_addr =
+						    dwc_ep->xfer_buff1;
+						dwc_ep->cur_pkt_dma_addr =
+						    dwc_ep->dma_addr1;
+					} else {
+						dwc_ep->cur_pkt_addr =
+						    dwc_ep->xfer_buff0;
+						dwc_ep->cur_pkt_dma_addr =
+						    dwc_ep->dma_addr0;
+					}
+
+				}
+
+				dsts.d32 =
+				    DWC_READ_REG32(&GET_CORE_IF(pcd)->dev_if->
+						   dev_global_regs->dsts);
+				dwc_ep->next_frame = dsts.b.soffn;
+
+				dwc_otg_iso_ep_start_frm_transfer(GET_CORE_IF
+								  (pcd),
+								  dwc_ep);
+			}
+		}
+	}
+#else
+	/** @todo implement ISR */
+	gintmsk_data_t intr_mask = {.d32 = 0 };
+	dwc_otg_core_if_t *core_if;
+	deptsiz_data_t deptsiz = {.d32 = 0 };
+	depctl_data_t depctl = {.d32 = 0 };
+	dctl_data_t dctl = {.d32 = 0 };
+	dwc_ep_t *dwc_ep = NULL;
+	int i;
+	core_if = GET_CORE_IF(pcd);
+
+	for (i = 0; i < core_if->dev_if->num_out_eps; ++i) {
+		dwc_ep = &pcd->out_ep[i].dwc_ep;
+		depctl.d32 =
+			DWC_READ_REG32(&core_if->dev_if->out_ep_regs[dwc_ep->num]->doepctl);
+		if (depctl.b.epena && depctl.b.dpid == (core_if->frame_num & 0x1)) {
+			core_if->dev_if->isoc_ep = dwc_ep;
+			deptsiz.d32 =
+					DWC_READ_REG32(&core_if->dev_if->out_ep_regs[dwc_ep->num]->doeptsiz);
+				break;
+		}
+	}
+	dctl.d32 = DWC_READ_REG32(&core_if->dev_if->dev_global_regs->dctl);
+	gintsts.d32 = DWC_READ_REG32(&core_if->core_global_regs->gintsts);
+	intr_mask.d32 = DWC_READ_REG32(&core_if->core_global_regs->gintmsk);
+
+	if (!intr_mask.b.goutnakeff) {
+		/* Unmask it */
+		intr_mask.b.goutnakeff = 1;
+		DWC_WRITE_REG32(&core_if->core_global_regs->gintmsk, intr_mask.d32);
+	}
+	if (!gintsts.b.goutnakeff) {
+		dctl.b.sgoutnak = 1;
+	}
+	DWC_WRITE_REG32(&core_if->dev_if->dev_global_regs->dctl, dctl.d32);
+
+	depctl.d32 = DWC_READ_REG32(&core_if->dev_if->out_ep_regs[dwc_ep->num]->doepctl);
+	if (depctl.b.epena) {
+		depctl.b.epdis = 1;
+		depctl.b.snak = 1;
+	}
+	DWC_WRITE_REG32(&core_if->dev_if->out_ep_regs[dwc_ep->num]->doepctl, depctl.d32);
+
+	intr_mask.d32 = 0;
+	intr_mask.b.incomplisoout = 1;
+
+#endif /* DWC_EN_ISOC */
+
+	/* Clear interrupt */
+	gintsts.d32 = 0;
+	gintsts.b.incomplisoout = 1;
+	DWC_WRITE_REG32(&GET_CORE_IF(pcd)->core_global_regs->gintsts,
+			gintsts.d32);
+
+	return 1;
+}
+
+/**
+ * This function handles the Global IN NAK Effective interrupt.
+ *
+ */
+int32_t dwc_otg_pcd_handle_in_nak_effective(dwc_otg_pcd_t * pcd)
+{
+	dwc_otg_dev_if_t *dev_if = GET_CORE_IF(pcd)->dev_if;
+	depctl_data_t diepctl = {.d32 = 0 };
+	gintmsk_data_t intr_mask = {.d32 = 0 };
+	gintsts_data_t gintsts;
+	dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd);
+	int i;
+
+	DWC_DEBUGPL(DBG_PCD, "Global IN NAK Effective\n");
+
+	/* Disable all active IN EPs */
+	for (i = 0; i <= dev_if->num_in_eps; i++) {
+		diepctl.d32 = DWC_READ_REG32(&dev_if->in_ep_regs[i]->diepctl);
+		if (!(diepctl.b.eptype & 1) && diepctl.b.epena) {
+			if (core_if->start_predict > 0)
+				core_if->start_predict++;
+			diepctl.b.epdis = 1;
+			diepctl.b.snak = 1;
+			DWC_WRITE_REG32(&dev_if->in_ep_regs[i]->diepctl, diepctl.d32);
+		}
+	}
+
+
+	/* Disable the Global IN NAK Effective Interrupt */
+	intr_mask.b.ginnakeff = 1;
+	DWC_MODIFY_REG32(&GET_CORE_IF(pcd)->core_global_regs->gintmsk,
+			 intr_mask.d32, 0);
+
+	/* Clear interrupt */
+	gintsts.d32 = 0;
+	gintsts.b.ginnakeff = 1;
+	DWC_WRITE_REG32(&GET_CORE_IF(pcd)->core_global_regs->gintsts,
+			gintsts.d32);
+
+	return 1;
+}
+
+/**
+ * OUT NAK Effective.
+ *
+ */
+int32_t dwc_otg_pcd_handle_out_nak_effective(dwc_otg_pcd_t * pcd)
+{
+	dwc_otg_dev_if_t *dev_if = GET_CORE_IF(pcd)->dev_if;
+	gintmsk_data_t intr_mask = {.d32 = 0 };
+	gintsts_data_t gintsts;
+	depctl_data_t doepctl;
+	int i;
+
+	/* Disable the Global OUT NAK Effective Interrupt */
+	intr_mask.b.goutnakeff = 1;
+	DWC_MODIFY_REG32(&GET_CORE_IF(pcd)->core_global_regs->gintmsk,
+		intr_mask.d32, 0);
+
+	/* If DEV OUT NAK enabled*/
+	if (pcd->core_if->core_params->dev_out_nak) {
+		/* Run over all out endpoints to determine the ep number on
+		 * which the timeout has happened
+		 */
+		for (i = 0; i <= dev_if->num_out_eps; i++) {
+			if ( pcd->core_if->ep_xfer_info[i].state == 2 )
+				break;
+		}
+		if (i > dev_if->num_out_eps) {
+			dctl_data_t dctl;
+			dctl.d32 =
+			    DWC_READ_REG32(&dev_if->dev_global_regs->dctl);
+			dctl.b.cgoutnak = 1;
+			DWC_WRITE_REG32(&dev_if->dev_global_regs->dctl,
+				dctl.d32);
+			goto out;
+		}
+
+		/* Disable the endpoint */
+		doepctl.d32 = DWC_READ_REG32(&dev_if->out_ep_regs[i]->doepctl);
+		if (doepctl.b.epena) {
+			doepctl.b.epdis = 1;
+			doepctl.b.snak = 1;
+		}
+		DWC_WRITE_REG32(&dev_if->out_ep_regs[i]->doepctl, doepctl.d32);
+		return 1;
+	}
+	/* We come here from Incomplete ISO OUT handler */
+	if (dev_if->isoc_ep) {
+		dwc_ep_t *dwc_ep = (dwc_ep_t *)dev_if->isoc_ep;
+		uint32_t epnum = dwc_ep->num;
+		doepint_data_t doepint;
+		doepint.d32 =
+		    DWC_READ_REG32(&dev_if->out_ep_regs[dwc_ep->num]->doepint);
+		dev_if->isoc_ep = NULL;
+		doepctl.d32 =
+		    DWC_READ_REG32(&dev_if->out_ep_regs[epnum]->doepctl);
+		DWC_PRINTF("Before disable DOEPCTL = %08x\n", doepctl.d32);
+		if (doepctl.b.epena) {
+			doepctl.b.epdis = 1;
+			doepctl.b.snak = 1;
+		}
+		DWC_WRITE_REG32(&dev_if->out_ep_regs[epnum]->doepctl,
+				doepctl.d32);
+		return 1;
+	} else
+		DWC_PRINTF("INTERRUPT Handler not implemented for %s\n",
+			   "Global OUT NAK Effective\n");
+
+out:
+	/* Clear interrupt */
+	gintsts.d32 = 0;
+	gintsts.b.goutnakeff = 1;
+	DWC_WRITE_REG32(&GET_CORE_IF(pcd)->core_global_regs->gintsts,
+			gintsts.d32);
+
+	return 1;
+}
+
+/**
+ * PCD interrupt handler.
+ *
+ * The PCD handles the device interrupts.  Many conditions can cause a
+ * device interrupt. When an interrupt occurs, the device interrupt
+ * service routine determines the cause of the interrupt and
+ * dispatches handling to the appropriate function. These interrupt
+ * handling functions are described below.
+ *
+ * All interrupt registers are processed from LSB to MSB.
+ *
+ */
+int32_t dwc_otg_pcd_handle_intr(dwc_otg_pcd_t * pcd)
+{
+	dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd);
+#ifdef VERBOSE
+	dwc_otg_core_global_regs_t *global_regs = core_if->core_global_regs;
+#endif
+	gintsts_data_t gintr_status;
+	int32_t retval = 0;
+
+	/* Exit from ISR if core is hibernated */
+	if (core_if->hibernation_suspend == 1) {
+		return retval;
+	}
+#ifdef VERBOSE
+	DWC_DEBUGPL(DBG_ANY, "%s() gintsts=%08x	 gintmsk=%08x\n",
+		    __func__,
+		    DWC_READ_REG32(&global_regs->gintsts),
+		    DWC_READ_REG32(&global_regs->gintmsk));
+#endif
+
+	if (dwc_otg_is_device_mode(core_if)) {
+		DWC_SPINLOCK(pcd->lock);
+#ifdef VERBOSE
+		DWC_DEBUGPL(DBG_PCDV, "%s() gintsts=%08x  gintmsk=%08x\n",
+			    __func__,
+			    DWC_READ_REG32(&global_regs->gintsts),
+			    DWC_READ_REG32(&global_regs->gintmsk));
+#endif
+
+		gintr_status.d32 = dwc_otg_read_core_intr(core_if);
+
+		DWC_DEBUGPL(DBG_PCDV, "%s: gintsts&gintmsk=%08x\n",
+			    __func__, gintr_status.d32);
+
+		if (gintr_status.b.sofintr) {
+			retval |= dwc_otg_pcd_handle_sof_intr(pcd);
+		}
+		if (gintr_status.b.rxstsqlvl) {
+			retval |=
+			    dwc_otg_pcd_handle_rx_status_q_level_intr(pcd);
+		}
+		if (gintr_status.b.nptxfempty) {
+			retval |= dwc_otg_pcd_handle_np_tx_fifo_empty_intr(pcd);
+		}
+		if (gintr_status.b.goutnakeff) {
+			retval |= dwc_otg_pcd_handle_out_nak_effective(pcd);
+		}
+		if (gintr_status.b.i2cintr) {
+			retval |= dwc_otg_pcd_handle_i2c_intr(pcd);
+		}
+		if (gintr_status.b.erlysuspend) {
+			retval |= dwc_otg_pcd_handle_early_suspend_intr(pcd);
+		}
+		if (gintr_status.b.usbreset) {
+			retval |= dwc_otg_pcd_handle_usb_reset_intr(pcd);
+		}
+		if (gintr_status.b.enumdone) {
+			retval |= dwc_otg_pcd_handle_enum_done_intr(pcd);
+		}
+		if (gintr_status.b.isooutdrop) {
+			retval |=
+			    dwc_otg_pcd_handle_isoc_out_packet_dropped_intr
+			    (pcd);
+		}
+		if (gintr_status.b.eopframe) {
+			retval |=
+			    dwc_otg_pcd_handle_end_periodic_frame_intr(pcd);
+		}
+		if (gintr_status.b.inepint) {
+			if (!core_if->multiproc_int_enable) {
+				retval |= dwc_otg_pcd_handle_in_ep_intr(pcd);
+			}
+		}
+		if (gintr_status.b.outepintr) {
+			if (!core_if->multiproc_int_enable) {
+				retval |= dwc_otg_pcd_handle_out_ep_intr(pcd);
+			}
+		}
+		if (gintr_status.b.epmismatch) {
+			retval |= dwc_otg_pcd_handle_ep_mismatch_intr(pcd);
+		}
+		if (gintr_status.b.fetsusp) {
+			retval |= dwc_otg_pcd_handle_ep_fetsusp_intr(pcd);
+		}
+		if (gintr_status.b.ginnakeff) {
+			retval |= dwc_otg_pcd_handle_in_nak_effective(pcd);
+		}
+		if (gintr_status.b.incomplisoin) {
+			retval |=
+			    dwc_otg_pcd_handle_incomplete_isoc_in_intr(pcd);
+		}
+		if (gintr_status.b.incomplisoout) {
+			retval |=
+			    dwc_otg_pcd_handle_incomplete_isoc_out_intr(pcd);
+		}
+
+		/* In MPI mode Device Endpoints interrupts are asserted
+		 * without setting outepintr and inepint bits set, so these
+		 * Interrupt handlers are called without checking these bit-fields
+		 */
+		if (core_if->multiproc_int_enable) {
+			retval |= dwc_otg_pcd_handle_in_ep_intr(pcd);
+			retval |= dwc_otg_pcd_handle_out_ep_intr(pcd);
+		}
+#ifdef VERBOSE
+		DWC_DEBUGPL(DBG_PCDV, "%s() gintsts=%0x\n", __func__,
+			    DWC_READ_REG32(&global_regs->gintsts));
+#endif
+		DWC_SPINUNLOCK(pcd->lock);
+	}
+	return retval;
+}
+
+#endif /* DWC_HOST_ONLY */
diff -Nur --no-dereference linux-5.10.52-orig/drivers/usb/host/dwc_otg/dwc_otg_pcd_linux.c linux-5.10.52-v7l+/drivers/usb/host/dwc_otg/dwc_otg_pcd_linux.c
--- linux-5.10.52-orig/drivers/usb/host/dwc_otg/dwc_otg_pcd_linux.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/usb/host/dwc_otg/dwc_otg_pcd_linux.c	2021-07-25 16:46:28.717909442 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+ /* ==========================================================================
+  * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_pcd_linux.c $
+  * $Revision: #21 $
+  * $Date: 2012/08/10 $
+  * $Change: 2047372 $
+  *
+  * Synopsys HS OTG Linux Software Driver and documentation (hereinafter,
+  * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless
+  * otherwise expressly agreed to in writing between Synopsys and you.
+  *
+  * The Software IS NOT an item of Licensed Software or Licensed Product under
+  * any End User Software License Agreement or Agreement for Licensed Product
+  * with Synopsys or any supplement thereto. You are permitted to use and
+  * redistribute this Software in source and binary forms, with or without
+  * modification, provided that redistributions of source code must retain this
+  * notice. You may not view, use, disclose, copy or distribute this file or
+  * any information contained herein except pursuant to this license grant from
+  * Synopsys. If you do not agree with this notice, including the disclaimer
+  * below, then you are not authorized to use the Software.
+  *
+  * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS
+  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+  * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT,
+  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+  * DAMAGE.
+  * ========================================================================== */
+#ifndef DWC_HOST_ONLY
+
+/** @file
+ * This file implements the Peripheral Controller Driver.
+ *
+ * The Peripheral Controller Driver (PCD) is responsible for
+ * translating requests from the Function Driver into the appropriate
+ * actions on the DWC_otg controller. It isolates the Function Driver
+ * from the specifics of the controller by providing an API to the
+ * Function Driver.
+ *
+ * The Peripheral Controller Driver for Linux will implement the
+ * Gadget API, so that the existing Gadget drivers can be used.
+ * (Gadget Driver is the Linux terminology for a Function Driver.)
+ *
+ * The Linux Gadget API is defined in the header file
+ * <code><linux/usb_gadget.h></code>.  The USB EP operations API is
+ * defined in the structure <code>usb_ep_ops</code> and the USB
+ * Controller API is defined in the structure
+ * <code>usb_gadget_ops</code>.
+ *
+ */
+
+#include "dwc_otg_os_dep.h"
+#include "dwc_otg_pcd_if.h"
+#include "dwc_otg_pcd.h"
+#include "dwc_otg_driver.h"
+#include "dwc_otg_dbg.h"
+
+extern bool fiq_enable;
+
+static struct gadget_wrapper {
+	dwc_otg_pcd_t *pcd;
+
+	struct usb_gadget gadget;
+	struct usb_gadget_driver *driver;
+
+	struct usb_ep ep0;
+	struct usb_ep in_ep[16];
+	struct usb_ep out_ep[16];
+
+} *gadget_wrapper;
+
+/* Display the contents of the buffer */
+extern void dump_msg(const u8 * buf, unsigned int length);
+/**
+ * Get the dwc_otg_pcd_ep_t* from usb_ep* pointer - NULL in case
+ * if the endpoint is not found
+ */
+static struct dwc_otg_pcd_ep *ep_from_handle(dwc_otg_pcd_t * pcd, void *handle)
+{
+	int i;
+	if (pcd->ep0.priv == handle) {
+		return &pcd->ep0;
+	}
+
+	for (i = 0; i < MAX_EPS_CHANNELS - 1; i++) {
+		if (pcd->in_ep[i].priv == handle)
+			return &pcd->in_ep[i];
+		if (pcd->out_ep[i].priv == handle)
+			return &pcd->out_ep[i];
+	}
+
+	return NULL;
+}
+
+/* USB Endpoint Operations */
+/*
+ * The following sections briefly describe the behavior of the Gadget
+ * API endpoint operations implemented in the DWC_otg driver
+ * software. Detailed descriptions of the generic behavior of each of
+ * these functions can be found in the Linux header file
+ * include/linux/usb_gadget.h.
+ *
+ * The Gadget API provides wrapper functions for each of the function
+ * pointers defined in usb_ep_ops. The Gadget Driver calls the wrapper
+ * function, which then calls the underlying PCD function. The
+ * following sections are named according to the wrapper
+ * functions. Within each section, the corresponding DWC_otg PCD
+ * function name is specified.
+ *
+ */
+
+/**
+ * This function is called by the Gadget Driver for each EP to be
+ * configured for the current configuration (SET_CONFIGURATION).
+ *
+ * This function initializes the dwc_otg_ep_t data structure, and then
+ * calls dwc_otg_ep_activate.
+ */
+static int ep_enable(struct usb_ep *usb_ep,
+		     const struct usb_endpoint_descriptor *ep_desc)
+{
+	int retval;
+
+	DWC_DEBUGPL(DBG_PCDV, "%s(%p,%p)\n", __func__, usb_ep, ep_desc);
+
+	if (!usb_ep || !ep_desc || ep_desc->bDescriptorType != USB_DT_ENDPOINT) {
+		DWC_WARN("%s, bad ep or descriptor\n", __func__);
+		return -EINVAL;
+	}
+	if (usb_ep == &gadget_wrapper->ep0) {
+		DWC_WARN("%s, bad ep(0)\n", __func__);
+		return -EINVAL;
+	}
+
+	/* Check FIFO size? */
+	if (!ep_desc->wMaxPacketSize) {
+		DWC_WARN("%s, bad %s maxpacket\n", __func__, usb_ep->name);
+		return -ERANGE;
+	}
+
+	if (!gadget_wrapper->driver ||
+	    gadget_wrapper->gadget.speed == USB_SPEED_UNKNOWN) {
+		DWC_WARN("%s, bogus device state\n", __func__);
+		return -ESHUTDOWN;
+	}
+
+	/* Delete after check - MAS */
+#if 0
+	nat = (uint32_t) ep_desc->wMaxPacketSize;
+	printk(KERN_ALERT "%s: nat (before) =%d\n", __func__, nat);
+	nat = (nat >> 11) & 0x03;
+	printk(KERN_ALERT "%s: nat (after) =%d\n", __func__, nat);
+#endif
+	retval = dwc_otg_pcd_ep_enable(gadget_wrapper->pcd,
+				       (const uint8_t *)ep_desc,
+				       (void *)usb_ep);
+	if (retval) {
+		DWC_WARN("dwc_otg_pcd_ep_enable failed\n");
+		return -EINVAL;
+	}
+
+	usb_ep->maxpacket = le16_to_cpu(ep_desc->wMaxPacketSize);
+
+	return 0;
+}
+
+/**
+ * This function is called when an EP is disabled due to disconnect or
+ * change in configuration. Any pending requests will terminate with a
+ * status of -ESHUTDOWN.
+ *
+ * This function modifies the dwc_otg_ep_t data structure for this EP,
+ * and then calls dwc_otg_ep_deactivate.
+ */
+static int ep_disable(struct usb_ep *usb_ep)
+{
+	int retval;
+
+	DWC_DEBUGPL(DBG_PCDV, "%s(%p)\n", __func__, usb_ep);
+	if (!usb_ep) {
+		DWC_DEBUGPL(DBG_PCD, "%s, %s not enabled\n", __func__,
+			    usb_ep ? usb_ep->name : NULL);
+		return -EINVAL;
+	}
+
+	retval = dwc_otg_pcd_ep_disable(gadget_wrapper->pcd, usb_ep);
+	if (retval) {
+		retval = -EINVAL;
+	}
+
+	return retval;
+}
+
+/**
+ * This function allocates a request object to use with the specified
+ * endpoint.
+ *
+ * @param ep The endpoint to be used with with the request
+ * @param gfp_flags the GFP_* flags to use.
+ */
+static struct usb_request *dwc_otg_pcd_alloc_request(struct usb_ep *ep,
+						     gfp_t gfp_flags)
+{
+	struct usb_request *usb_req;
+
+	DWC_DEBUGPL(DBG_PCDV, "%s(%p,%d)\n", __func__, ep, gfp_flags);
+	if (0 == ep) {
+		DWC_WARN("%s() %s\n", __func__, "Invalid EP!\n");
+		return 0;
+	}
+	usb_req = kzalloc(sizeof(*usb_req), gfp_flags);
+	if (0 == usb_req) {
+		DWC_WARN("%s() %s\n", __func__, "request allocation failed!\n");
+		return 0;
+	}
+	usb_req->dma = DWC_DMA_ADDR_INVALID;
+
+	return usb_req;
+}
+
+/**
+ * This function frees a request object.
+ *
+ * @param ep The endpoint associated with the request
+ * @param req The request being freed
+ */
+static void dwc_otg_pcd_free_request(struct usb_ep *ep, struct usb_request *req)
+{
+	DWC_DEBUGPL(DBG_PCDV, "%s(%p,%p)\n", __func__, ep, req);
+
+	if (0 == ep || 0 == req) {
+		DWC_WARN("%s() %s\n", __func__,
+			 "Invalid ep or req argument!\n");
+		return;
+	}
+
+	kfree(req);
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28)
+/**
+ * This function allocates an I/O buffer to be used for a transfer
+ * to/from the specified endpoint.
+ *
+ * @param usb_ep The endpoint to be used with with the request
+ * @param bytes The desired number of bytes for the buffer
+ * @param dma Pointer to the buffer's DMA address; must be valid
+ * @param gfp_flags the GFP_* flags to use.
+ * @return address of a new buffer or null is buffer could not be allocated.
+ */
+static void *dwc_otg_pcd_alloc_buffer(struct usb_ep *usb_ep, unsigned bytes,
+				      dma_addr_t * dma, gfp_t gfp_flags)
+{
+	void *buf;
+	dwc_otg_pcd_t *pcd = 0;
+
+	pcd = gadget_wrapper->pcd;
+
+	DWC_DEBUGPL(DBG_PCDV, "%s(%p,%d,%p,%0x)\n", __func__, usb_ep, bytes,
+		    dma, gfp_flags);
+
+	/* Check dword alignment */
+	if ((bytes & 0x3UL) != 0) {
+		DWC_WARN("%s() Buffer size is not a multiple of"
+			 "DWORD size (%d)", __func__, bytes);
+	}
+
+	buf = dma_alloc_coherent(NULL, bytes, dma, gfp_flags);
+	WARN_ON(!buf);
+
+	/* Check dword alignment */
+	if (((int)buf & 0x3UL) != 0) {
+		DWC_WARN("%s() Buffer is not DWORD aligned (%p)",
+			 __func__, buf);
+	}
+
+	return buf;
+}
+
+/**
+ * This function frees an I/O buffer that was allocated by alloc_buffer.
+ *
+ * @param usb_ep the endpoint associated with the buffer
+ * @param buf address of the buffer
+ * @param dma The buffer's DMA address
+ * @param bytes The number of bytes of the buffer
+ */
+static void dwc_otg_pcd_free_buffer(struct usb_ep *usb_ep, void *buf,
+				    dma_addr_t dma, unsigned bytes)
+{
+	dwc_otg_pcd_t *pcd = 0;
+
+	pcd = gadget_wrapper->pcd;
+
+	DWC_DEBUGPL(DBG_PCDV, "%s(%p,%0x,%d)\n", __func__, buf, dma, bytes);
+
+	dma_free_coherent(NULL, bytes, buf, dma);
+}
+#endif
+
+/**
+ * This function is used to submit an I/O Request to an EP.
+ *
+ *	- When the request completes the request's completion callback
+ *	  is called to return the request to the driver.
+ *	- An EP, except control EPs, may have multiple requests
+ *	  pending.
+ *	- Once submitted the request cannot be examined or modified.
+ *	- Each request is turned into one or more packets.
+ *	- A BULK EP can queue any amount of data; the transfer is
+ *	  packetized.
+ *	- Zero length Packets are specified with the request 'zero'
+ *	  flag.
+ */
+static int ep_queue(struct usb_ep *usb_ep, struct usb_request *usb_req,
+		    gfp_t gfp_flags)
+{
+	dwc_otg_pcd_t *pcd;
+	struct dwc_otg_pcd_ep *ep = NULL;
+	int retval = 0, is_isoc_ep = 0;
+	dma_addr_t dma_addr = DWC_DMA_ADDR_INVALID;
+
+	DWC_DEBUGPL(DBG_PCDV, "%s(%p,%p,%d)\n",
+		    __func__, usb_ep, usb_req, gfp_flags);
+
+	if (!usb_req || !usb_req->complete || !usb_req->buf) {
+		DWC_WARN("bad params\n");
+		return -EINVAL;
+	}
+
+	if (!usb_ep) {
+		DWC_WARN("bad ep\n");
+		return -EINVAL;
+	}
+
+	pcd = gadget_wrapper->pcd;
+	if (!gadget_wrapper->driver ||
+	    gadget_wrapper->gadget.speed == USB_SPEED_UNKNOWN) {
+		DWC_DEBUGPL(DBG_PCDV, "gadget.speed=%d\n",
+			    gadget_wrapper->gadget.speed);
+		DWC_WARN("bogus device state\n");
+		return -ESHUTDOWN;
+	}
+
+	DWC_DEBUGPL(DBG_PCD, "%s queue req %p, len %d buf %p\n",
+		    usb_ep->name, usb_req, usb_req->length, usb_req->buf);
+
+	usb_req->status = -EINPROGRESS;
+	usb_req->actual = 0;
+
+	ep = ep_from_handle(pcd, usb_ep);
+	if (ep == NULL)
+		is_isoc_ep = 0;
+	else
+		is_isoc_ep = (ep->dwc_ep.type == DWC_OTG_EP_TYPE_ISOC) ? 1 : 0;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28)
+	dma_addr = usb_req->dma;
+#else
+	if (GET_CORE_IF(pcd)->dma_enable) {
+                dwc_otg_device_t *otg_dev = gadget_wrapper->pcd->otg_dev;
+                struct device *dev = NULL;
+
+                if (otg_dev != NULL)
+                        dev = DWC_OTG_OS_GETDEV(otg_dev->os_dep);
+
+		if (usb_req->length != 0 &&
+                    usb_req->dma == DWC_DMA_ADDR_INVALID) {
+                        dma_addr = dma_map_single(dev, usb_req->buf,
+                                                  usb_req->length,
+                                                  ep->dwc_ep.is_in ?
+                                                        DMA_TO_DEVICE:
+                                                        DMA_FROM_DEVICE);
+		}
+	}
+#endif
+
+#ifdef DWC_UTE_PER_IO
+	if (is_isoc_ep == 1) {
+		retval = dwc_otg_pcd_xiso_ep_queue(pcd, usb_ep, usb_req->buf, dma_addr,
+			usb_req->length, usb_req->zero, usb_req,
+			gfp_flags == GFP_ATOMIC ? 1 : 0, &usb_req->ext_req);
+		if (retval)
+			return -EINVAL;
+
+		return 0;
+	}
+#endif
+	retval = dwc_otg_pcd_ep_queue(pcd, usb_ep, usb_req->buf, dma_addr,
+				      usb_req->length, usb_req->zero, usb_req,
+				      gfp_flags == GFP_ATOMIC ? 1 : 0);
+	if (retval) {
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/**
+ * This function cancels an I/O request from an EP.
+ */
+static int ep_dequeue(struct usb_ep *usb_ep, struct usb_request *usb_req)
+{
+	DWC_DEBUGPL(DBG_PCDV, "%s(%p,%p)\n", __func__, usb_ep, usb_req);
+
+	if (!usb_ep || !usb_req) {
+		DWC_WARN("bad argument\n");
+		return -EINVAL;
+	}
+	if (!gadget_wrapper->driver ||
+	    gadget_wrapper->gadget.speed == USB_SPEED_UNKNOWN) {
+		DWC_WARN("bogus device state\n");
+		return -ESHUTDOWN;
+	}
+	if (dwc_otg_pcd_ep_dequeue(gadget_wrapper->pcd, usb_ep, usb_req)) {
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/**
+ * usb_ep_set_halt stalls an endpoint.
+ *
+ * usb_ep_clear_halt clears an endpoint halt and resets its data
+ * toggle.
+ *
+ * Both of these functions are implemented with the same underlying
+ * function. The behavior depends on the value argument.
+ *
+ * @param[in] usb_ep the Endpoint to halt or clear halt.
+ * @param[in] value
+ *	- 0 means clear_halt.
+ *	- 1 means set_halt,
+ *	- 2 means clear stall lock flag.
+ *	- 3 means set  stall lock flag.
+ */
+static int ep_halt(struct usb_ep *usb_ep, int value)
+{
+	int retval = 0;
+
+	DWC_DEBUGPL(DBG_PCD, "HALT %s %d\n", usb_ep->name, value);
+
+	if (!usb_ep) {
+		DWC_WARN("bad ep\n");
+		return -EINVAL;
+	}
+
+	retval = dwc_otg_pcd_ep_halt(gadget_wrapper->pcd, usb_ep, value);
+	if (retval == -DWC_E_AGAIN) {
+		return -EAGAIN;
+	} else if (retval) {
+		retval = -EINVAL;
+	}
+
+	return retval;
+}
+
+//#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,30))
+#if 0
+/**
+ * ep_wedge: sets the halt feature and ignores clear requests
+ *
+ * @usb_ep: the endpoint being wedged
+ *
+ * Use this to stall an endpoint and ignore CLEAR_FEATURE(HALT_ENDPOINT)
+ * requests. If the gadget driver clears the halt status, it will
+ * automatically unwedge the endpoint.
+ *
+ * Returns zero on success, else negative errno. *
+ * Check usb_ep_set_wedge() at "usb_gadget.h" for details
+ */
+static int ep_wedge(struct usb_ep *usb_ep)
+{
+	int retval = 0;
+
+	DWC_DEBUGPL(DBG_PCD, "WEDGE %s\n", usb_ep->name);
+
+	if (!usb_ep) {
+		DWC_WARN("bad ep\n");
+		return -EINVAL;
+	}
+
+	retval = dwc_otg_pcd_ep_wedge(gadget_wrapper->pcd, usb_ep);
+	if (retval == -DWC_E_AGAIN) {
+		retval = -EAGAIN;
+	} else if (retval) {
+		retval = -EINVAL;
+	}
+
+	return retval;
+}
+#endif
+
+#ifdef DWC_EN_ISOC
+/**
+ * This function is used to submit an ISOC Transfer Request to an EP.
+ *
+ *	- Every time a sync period completes the request's completion callback
+ *	  is called to provide data to the gadget driver.
+ *	- Once submitted the request cannot be modified.
+ *	- Each request is turned into periodic data packets untill ISO
+ *	  Transfer is stopped..
+ */
+static int iso_ep_start(struct usb_ep *usb_ep, struct usb_iso_request *req,
+			gfp_t gfp_flags)
+{
+	int retval = 0;
+
+	if (!req || !req->process_buffer || !req->buf0 || !req->buf1) {
+		DWC_WARN("bad params\n");
+		return -EINVAL;
+	}
+
+	if (!usb_ep) {
+		DWC_PRINTF("bad params\n");
+		return -EINVAL;
+	}
+
+	req->status = -EINPROGRESS;
+
+	retval =
+	    dwc_otg_pcd_iso_ep_start(gadget_wrapper->pcd, usb_ep, req->buf0,
+				     req->buf1, req->dma0, req->dma1,
+				     req->sync_frame, req->data_pattern_frame,
+				     req->data_per_frame,
+				     req->
+				     flags & USB_REQ_ISO_ASAP ? -1 :
+				     req->start_frame, req->buf_proc_intrvl,
+				     req, gfp_flags == GFP_ATOMIC ? 1 : 0);
+
+	if (retval) {
+		return -EINVAL;
+	}
+
+	return retval;
+}
+
+/**
+ * This function stops ISO EP Periodic Data Transfer.
+ */
+static int iso_ep_stop(struct usb_ep *usb_ep, struct usb_iso_request *req)
+{
+	int retval = 0;
+	if (!usb_ep) {
+		DWC_WARN("bad ep\n");
+	}
+
+	if (!gadget_wrapper->driver ||
+	    gadget_wrapper->gadget.speed == USB_SPEED_UNKNOWN) {
+		DWC_DEBUGPL(DBG_PCDV, "gadget.speed=%d\n",
+			    gadget_wrapper->gadget.speed);
+		DWC_WARN("bogus device state\n");
+	}
+
+	dwc_otg_pcd_iso_ep_stop(gadget_wrapper->pcd, usb_ep, req);
+	if (retval) {
+		retval = -EINVAL;
+	}
+
+	return retval;
+}
+
+static struct usb_iso_request *alloc_iso_request(struct usb_ep *ep,
+						 int packets, gfp_t gfp_flags)
+{
+	struct usb_iso_request *pReq = NULL;
+	uint32_t req_size;
+
+	req_size = sizeof(struct usb_iso_request);
+	req_size +=
+	    (2 * packets * (sizeof(struct usb_gadget_iso_packet_descriptor)));
+
+	pReq = kmalloc(req_size, gfp_flags);
+	if (!pReq) {
+		DWC_WARN("Can't allocate Iso Request\n");
+		return 0;
+	}
+	pReq->iso_packet_desc0 = (void *)(pReq + 1);
+
+	pReq->iso_packet_desc1 = pReq->iso_packet_desc0 + packets;
+
+	return pReq;
+}
+
+static void free_iso_request(struct usb_ep *ep, struct usb_iso_request *req)
+{
+	kfree(req);
+}
+
+static struct usb_isoc_ep_ops dwc_otg_pcd_ep_ops = {
+	.ep_ops = {
+		   .enable = ep_enable,
+		   .disable = ep_disable,
+
+		   .alloc_request = dwc_otg_pcd_alloc_request,
+		   .free_request = dwc_otg_pcd_free_request,
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28)
+		   .alloc_buffer = dwc_otg_pcd_alloc_buffer,
+		   .free_buffer = dwc_otg_pcd_free_buffer,
+#endif
+
+		   .queue = ep_queue,
+		   .dequeue = ep_dequeue,
+
+		   .set_halt = ep_halt,
+		   .fifo_status = 0,
+		   .fifo_flush = 0,
+		   },
+	.iso_ep_start = iso_ep_start,
+	.iso_ep_stop = iso_ep_stop,
+	.alloc_iso_request = alloc_iso_request,
+	.free_iso_request = free_iso_request,
+};
+
+#else
+
+	int (*enable) (struct usb_ep *ep,
+		const struct usb_endpoint_descriptor *desc);
+	int (*disable) (struct usb_ep *ep);
+
+	struct usb_request *(*alloc_request) (struct usb_ep *ep,
+		gfp_t gfp_flags);
+	void (*free_request) (struct usb_ep *ep, struct usb_request *req);
+
+	int (*queue) (struct usb_ep *ep, struct usb_request *req,
+		gfp_t gfp_flags);
+	int (*dequeue) (struct usb_ep *ep, struct usb_request *req);
+
+	int (*set_halt) (struct usb_ep *ep, int value);
+	int (*set_wedge) (struct usb_ep *ep);
+
+	int (*fifo_status) (struct usb_ep *ep);
+	void (*fifo_flush) (struct usb_ep *ep);
+static struct usb_ep_ops dwc_otg_pcd_ep_ops = {
+	.enable = ep_enable,
+	.disable = ep_disable,
+
+	.alloc_request = dwc_otg_pcd_alloc_request,
+	.free_request = dwc_otg_pcd_free_request,
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28)
+	.alloc_buffer = dwc_otg_pcd_alloc_buffer,
+	.free_buffer = dwc_otg_pcd_free_buffer,
+#else
+	/* .set_wedge = ep_wedge, */
+        .set_wedge = NULL, /* uses set_halt instead */
+#endif
+
+	.queue = ep_queue,
+	.dequeue = ep_dequeue,
+
+	.set_halt = ep_halt,
+	.fifo_status = 0,
+	.fifo_flush = 0,
+
+};
+
+#endif /* _EN_ISOC_ */
+/*	Gadget Operations */
+/**
+ * The following gadget operations will be implemented in the DWC_otg
+ * PCD. Functions in the API that are not described below are not
+ * implemented.
+ *
+ * The Gadget API provides wrapper functions for each of the function
+ * pointers defined in usb_gadget_ops. The Gadget Driver calls the
+ * wrapper function, which then calls the underlying PCD function. The
+ * following sections are named according to the wrapper functions
+ * (except for ioctl, which doesn't have a wrapper function). Within
+ * each section, the corresponding DWC_otg PCD function name is
+ * specified.
+ *
+ */
+
+/**
+ *Gets the USB Frame number of the last SOF.
+ */
+static int get_frame_number(struct usb_gadget *gadget)
+{
+	struct gadget_wrapper *d;
+
+	DWC_DEBUGPL(DBG_PCDV, "%s(%p)\n", __func__, gadget);
+
+	if (gadget == 0) {
+		return -ENODEV;
+	}
+
+	d = container_of(gadget, struct gadget_wrapper, gadget);
+	return dwc_otg_pcd_get_frame_number(d->pcd);
+}
+
+#ifdef CONFIG_USB_DWC_OTG_LPM
+static int test_lpm_enabled(struct usb_gadget *gadget)
+{
+	struct gadget_wrapper *d;
+
+	d = container_of(gadget, struct gadget_wrapper, gadget);
+
+	return dwc_otg_pcd_is_lpm_enabled(d->pcd);
+}
+#endif
+
+/**
+ * Initiates Session Request Protocol (SRP) to wakeup the host if no
+ * session is in progress. If a session is already in progress, but
+ * the device is suspended, remote wakeup signaling is started.
+ *
+ */
+static int wakeup(struct usb_gadget *gadget)
+{
+	struct gadget_wrapper *d;
+
+	DWC_DEBUGPL(DBG_PCDV, "%s(%p)\n", __func__, gadget);
+
+	if (gadget == 0) {
+		return -ENODEV;
+	} else {
+		d = container_of(gadget, struct gadget_wrapper, gadget);
+	}
+	dwc_otg_pcd_wakeup(d->pcd);
+	return 0;
+}
+
+static const struct usb_gadget_ops dwc_otg_pcd_ops = {
+	.get_frame = get_frame_number,
+	.wakeup = wakeup,
+#ifdef CONFIG_USB_DWC_OTG_LPM
+	.lpm_support = test_lpm_enabled,
+#endif
+	// current versions must always be self-powered
+};
+
+static int _setup(dwc_otg_pcd_t * pcd, uint8_t * bytes)
+{
+	int retval = -DWC_E_NOT_SUPPORTED;
+	if (gadget_wrapper->driver && gadget_wrapper->driver->setup) {
+		retval = gadget_wrapper->driver->setup(&gadget_wrapper->gadget,
+						       (struct usb_ctrlrequest
+							*)bytes);
+	}
+
+	if (retval == -ENOTSUPP) {
+		retval = -DWC_E_NOT_SUPPORTED;
+	} else if (retval < 0) {
+		retval = -DWC_E_INVALID;
+	}
+
+	return retval;
+}
+
+#ifdef DWC_EN_ISOC
+static int _isoc_complete(dwc_otg_pcd_t * pcd, void *ep_handle,
+			  void *req_handle, int proc_buf_num)
+{
+	int i, packet_count;
+	struct usb_gadget_iso_packet_descriptor *iso_packet = 0;
+	struct usb_iso_request *iso_req = req_handle;
+
+	if (proc_buf_num) {
+		iso_packet = iso_req->iso_packet_desc1;
+	} else {
+		iso_packet = iso_req->iso_packet_desc0;
+	}
+	packet_count =
+	    dwc_otg_pcd_get_iso_packet_count(pcd, ep_handle, req_handle);
+	for (i = 0; i < packet_count; ++i) {
+		int status;
+		int actual;
+		int offset;
+		dwc_otg_pcd_get_iso_packet_params(pcd, ep_handle, req_handle,
+						  i, &status, &actual, &offset);
+		switch (status) {
+		case -DWC_E_NO_DATA:
+			status = -ENODATA;
+			break;
+		default:
+			if (status) {
+				DWC_PRINTF("unknown status in isoc packet\n");
+			}
+
+		}
+		iso_packet[i].status = status;
+		iso_packet[i].offset = offset;
+		iso_packet[i].actual_length = actual;
+	}
+
+	iso_req->status = 0;
+	iso_req->process_buffer(ep_handle, iso_req);
+
+	return 0;
+}
+#endif /* DWC_EN_ISOC */
+
+#ifdef DWC_UTE_PER_IO
+/**
+ * Copy the contents of the extended request to the Linux usb_request's
+ * extended part and call the gadget's completion.
+ *
+ * @param pcd			Pointer to the pcd structure
+ * @param ep_handle		Void pointer to the usb_ep structure
+ * @param req_handle	Void pointer to the usb_request structure
+ * @param status		Request status returned from the portable logic
+ * @param ereq_port		Void pointer to the extended request structure
+ *						created in the the portable part that contains the
+ *						results of the processed iso packets.
+ */
+static int _xisoc_complete(dwc_otg_pcd_t * pcd, void *ep_handle,
+			   void *req_handle, int32_t status, void *ereq_port)
+{
+	struct dwc_ute_iso_req_ext *ereqorg = NULL;
+	struct dwc_iso_xreq_port *ereqport = NULL;
+	struct dwc_ute_iso_packet_descriptor *desc_org = NULL;
+	int i;
+	struct usb_request *req;
+	//struct dwc_ute_iso_packet_descriptor *
+	//int status = 0;
+
+	req = (struct usb_request *)req_handle;
+	ereqorg = &req->ext_req;
+	ereqport = (struct dwc_iso_xreq_port *)ereq_port;
+	desc_org = ereqorg->per_io_frame_descs;
+
+	if (req && req->complete) {
+		/* Copy the request data from the portable logic to our request */
+		for (i = 0; i < ereqport->pio_pkt_count; i++) {
+			desc_org[i].actual_length =
+			    ereqport->per_io_frame_descs[i].actual_length;
+			desc_org[i].status =
+			    ereqport->per_io_frame_descs[i].status;
+		}
+
+		switch (status) {
+		case -DWC_E_SHUTDOWN:
+			req->status = -ESHUTDOWN;
+			break;
+		case -DWC_E_RESTART:
+			req->status = -ECONNRESET;
+			break;
+		case -DWC_E_INVALID:
+			req->status = -EINVAL;
+			break;
+		case -DWC_E_TIMEOUT:
+			req->status = -ETIMEDOUT;
+			break;
+		default:
+			req->status = status;
+		}
+
+		/* And call the gadget's completion */
+		req->complete(ep_handle, req);
+	}
+
+	return 0;
+}
+#endif /* DWC_UTE_PER_IO */
+
+static int _complete(dwc_otg_pcd_t * pcd, void *ep_handle,
+		     void *req_handle, int32_t status, uint32_t actual)
+{
+	struct usb_request *req = (struct usb_request *)req_handle;
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,27)
+	struct dwc_otg_pcd_ep *ep = NULL;
+#endif
+
+	if (req && req->complete) {
+		switch (status) {
+		case -DWC_E_SHUTDOWN:
+			req->status = -ESHUTDOWN;
+			break;
+		case -DWC_E_RESTART:
+			req->status = -ECONNRESET;
+			break;
+		case -DWC_E_INVALID:
+			req->status = -EINVAL;
+			break;
+		case -DWC_E_TIMEOUT:
+			req->status = -ETIMEDOUT;
+			break;
+		default:
+			req->status = status;
+
+		}
+
+		req->actual = actual;
+		DWC_SPINUNLOCK(pcd->lock);
+		req->complete(ep_handle, req);
+		DWC_SPINLOCK(pcd->lock);
+	}
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,27)
+	ep = ep_from_handle(pcd, ep_handle);
+	if (GET_CORE_IF(pcd)->dma_enable) {
+                if (req->length != 0) {
+                        dwc_otg_device_t *otg_dev = gadget_wrapper->pcd->otg_dev;
+                        struct device *dev = NULL;
+
+                        if (otg_dev != NULL)
+                                  dev = DWC_OTG_OS_GETDEV(otg_dev->os_dep);
+
+			dma_unmap_single(dev, req->dma, req->length,
+                                         ep->dwc_ep.is_in ?
+                                                DMA_TO_DEVICE: DMA_FROM_DEVICE);
+                }
+	}
+#endif
+
+	return 0;
+}
+
+static int _connect(dwc_otg_pcd_t * pcd, int speed)
+{
+	gadget_wrapper->gadget.speed = speed;
+	return 0;
+}
+
+static int _disconnect(dwc_otg_pcd_t * pcd)
+{
+	if (gadget_wrapper->driver && gadget_wrapper->driver->disconnect) {
+		gadget_wrapper->driver->disconnect(&gadget_wrapper->gadget);
+	}
+	return 0;
+}
+
+static int _resume(dwc_otg_pcd_t * pcd)
+{
+	if (gadget_wrapper->driver && gadget_wrapper->driver->resume) {
+		gadget_wrapper->driver->resume(&gadget_wrapper->gadget);
+	}
+
+	return 0;
+}
+
+static int _suspend(dwc_otg_pcd_t * pcd)
+{
+	if (gadget_wrapper->driver && gadget_wrapper->driver->suspend) {
+		gadget_wrapper->driver->suspend(&gadget_wrapper->gadget);
+	}
+	return 0;
+}
+
+/**
+ * This function updates the otg values in the gadget structure.
+ */
+static int _hnp_changed(dwc_otg_pcd_t * pcd)
+{
+
+	if (!gadget_wrapper->gadget.is_otg)
+		return 0;
+
+	gadget_wrapper->gadget.b_hnp_enable = get_b_hnp_enable(pcd);
+	gadget_wrapper->gadget.a_hnp_support = get_a_hnp_support(pcd);
+	gadget_wrapper->gadget.a_alt_hnp_support = get_a_alt_hnp_support(pcd);
+	return 0;
+}
+
+static int _reset(dwc_otg_pcd_t * pcd)
+{
+	return 0;
+}
+
+#ifdef DWC_UTE_CFI
+static int _cfi_setup(dwc_otg_pcd_t * pcd, void *cfi_req)
+{
+	int retval = -DWC_E_INVALID;
+	if (gadget_wrapper->driver->cfi_feature_setup) {
+		retval =
+		    gadget_wrapper->driver->
+		    cfi_feature_setup(&gadget_wrapper->gadget,
+				      (struct cfi_usb_ctrlrequest *)cfi_req);
+	}
+
+	return retval;
+}
+#endif
+
+static const struct dwc_otg_pcd_function_ops fops = {
+	.complete = _complete,
+#ifdef DWC_EN_ISOC
+	.isoc_complete = _isoc_complete,
+#endif
+	.setup = _setup,
+	.disconnect = _disconnect,
+	.connect = _connect,
+	.resume = _resume,
+	.suspend = _suspend,
+	.hnp_changed = _hnp_changed,
+	.reset = _reset,
+#ifdef DWC_UTE_CFI
+	.cfi_setup = _cfi_setup,
+#endif
+#ifdef DWC_UTE_PER_IO
+	.xisoc_complete = _xisoc_complete,
+#endif
+};
+
+/**
+ * This function is the top level PCD interrupt handler.
+ */
+static irqreturn_t dwc_otg_pcd_irq(int irq, void *dev)
+{
+	dwc_otg_pcd_t *pcd = dev;
+	int32_t retval = IRQ_NONE;
+
+	retval = dwc_otg_pcd_handle_intr(pcd);
+	if (retval != 0) {
+		S3C2410X_CLEAR_EINTPEND();
+	}
+	return IRQ_RETVAL(retval);
+}
+
+/**
+ * This function initialized the usb_ep structures to there default
+ * state.
+ *
+ * @param d Pointer on gadget_wrapper.
+ */
+void gadget_add_eps(struct gadget_wrapper *d)
+{
+	static const char *names[] = {
+
+		"ep0",
+		"ep1in",
+		"ep2in",
+		"ep3in",
+		"ep4in",
+		"ep5in",
+		"ep6in",
+		"ep7in",
+		"ep8in",
+		"ep9in",
+		"ep10in",
+		"ep11in",
+		"ep12in",
+		"ep13in",
+		"ep14in",
+		"ep15in",
+		"ep1out",
+		"ep2out",
+		"ep3out",
+		"ep4out",
+		"ep5out",
+		"ep6out",
+		"ep7out",
+		"ep8out",
+		"ep9out",
+		"ep10out",
+		"ep11out",
+		"ep12out",
+		"ep13out",
+		"ep14out",
+		"ep15out"
+	};
+
+	int i;
+	struct usb_ep *ep;
+	int8_t dev_endpoints;
+
+	DWC_DEBUGPL(DBG_PCDV, "%s\n", __func__);
+
+	INIT_LIST_HEAD(&d->gadget.ep_list);
+	d->gadget.ep0 = &d->ep0;
+	d->gadget.speed = USB_SPEED_UNKNOWN;
+
+	INIT_LIST_HEAD(&d->gadget.ep0->ep_list);
+
+	/**
+	 * Initialize the EP0 structure.
+	 */
+	ep = &d->ep0;
+
+	/* Init the usb_ep structure. */
+	ep->name = names[0];
+	ep->ops = (struct usb_ep_ops *)&dwc_otg_pcd_ep_ops;
+
+	/**
+	 * @todo NGS: What should the max packet size be set to
+	 * here?  Before EP type is set?
+	 */
+	ep->maxpacket = MAX_PACKET_SIZE;
+	dwc_otg_pcd_ep_enable(d->pcd, NULL, ep);
+
+	list_add_tail(&ep->ep_list, &d->gadget.ep_list);
+
+	/**
+	 * Initialize the EP structures.
+	 */
+	dev_endpoints = d->pcd->core_if->dev_if->num_in_eps;
+
+	for (i = 0; i < dev_endpoints; i++) {
+		ep = &d->in_ep[i];
+
+		/* Init the usb_ep structure. */
+		ep->name = names[d->pcd->in_ep[i].dwc_ep.num];
+		ep->ops = (struct usb_ep_ops *)&dwc_otg_pcd_ep_ops;
+
+		/**
+		 * @todo NGS: What should the max packet size be set to
+		 * here?  Before EP type is set?
+		 */
+		ep->maxpacket = MAX_PACKET_SIZE;
+		list_add_tail(&ep->ep_list, &d->gadget.ep_list);
+	}
+
+	dev_endpoints = d->pcd->core_if->dev_if->num_out_eps;
+
+	for (i = 0; i < dev_endpoints; i++) {
+		ep = &d->out_ep[i];
+
+		/* Init the usb_ep structure. */
+		ep->name = names[15 + d->pcd->out_ep[i].dwc_ep.num];
+		ep->ops = (struct usb_ep_ops *)&dwc_otg_pcd_ep_ops;
+
+		/**
+		 * @todo NGS: What should the max packet size be set to
+		 * here?  Before EP type is set?
+		 */
+		ep->maxpacket = MAX_PACKET_SIZE;
+
+		list_add_tail(&ep->ep_list, &d->gadget.ep_list);
+	}
+
+	/* remove ep0 from the list.  There is a ep0 pointer. */
+	list_del_init(&d->ep0.ep_list);
+
+	d->ep0.maxpacket = MAX_EP0_SIZE;
+}
+
+/**
+ * This function releases the Gadget device.
+ * required by device_unregister().
+ *
+ * @todo Should this do something?	Should it free the PCD?
+ */
+static void dwc_otg_pcd_gadget_release(struct device *dev)
+{
+	DWC_DEBUGPL(DBG_PCDV, "%s(%p)\n", __func__, dev);
+}
+
+static struct gadget_wrapper *alloc_wrapper(dwc_bus_dev_t *_dev)
+{
+	static char pcd_name[] = "dwc_otg_pcd";
+	dwc_otg_device_t *otg_dev = DWC_OTG_BUSDRVDATA(_dev);
+	struct gadget_wrapper *d;
+	int retval;
+
+	d = DWC_ALLOC(sizeof(*d));
+	if (d == NULL) {
+		return NULL;
+	}
+
+	memset(d, 0, sizeof(*d));
+
+	d->gadget.name = pcd_name;
+	d->pcd = otg_dev->pcd;
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,30)
+	strcpy(d->gadget.dev.bus_id, "gadget");
+#else
+	dev_set_name(&d->gadget.dev, "%s", "gadget");
+#endif
+
+	d->gadget.dev.parent = &_dev->dev;
+	d->gadget.dev.release = dwc_otg_pcd_gadget_release;
+	d->gadget.ops = &dwc_otg_pcd_ops;
+	d->gadget.max_speed = dwc_otg_pcd_is_dualspeed(otg_dev->pcd) ? USB_SPEED_HIGH:USB_SPEED_FULL;
+	d->gadget.is_otg = dwc_otg_pcd_is_otg(otg_dev->pcd);
+
+	d->driver = 0;
+	/* Register the gadget device */
+	retval = device_register(&d->gadget.dev);
+	if (retval != 0) {
+		DWC_ERROR("device_register failed\n");
+		DWC_FREE(d);
+		return NULL;
+	}
+
+	return d;
+}
+
+static void free_wrapper(struct gadget_wrapper *d)
+{
+	if (d->driver) {
+		/* should have been done already by driver model core */
+		DWC_WARN("driver '%s' is still registered\n",
+			 d->driver->driver.name);
+#ifdef CONFIG_USB_GADGET
+		usb_gadget_unregister_driver(d->driver);
+#endif
+	}
+
+	device_unregister(&d->gadget.dev);
+	DWC_FREE(d);
+}
+
+/**
+ * This function initialized the PCD portion of the driver.
+ *
+ */
+int pcd_init(dwc_bus_dev_t *_dev)
+{
+	dwc_otg_device_t *otg_dev = DWC_OTG_BUSDRVDATA(_dev);
+	int retval = 0;
+
+	DWC_DEBUGPL(DBG_PCDV, "%s(%p) otg_dev=%p\n", __func__, _dev, otg_dev);
+
+	otg_dev->pcd = dwc_otg_pcd_init(otg_dev);
+
+	if (!otg_dev->pcd) {
+		DWC_ERROR("dwc_otg_pcd_init failed\n");
+		return -ENOMEM;
+	}
+
+	otg_dev->pcd->otg_dev = otg_dev;
+	gadget_wrapper = alloc_wrapper(_dev);
+
+	/*
+	 * Initialize EP structures
+	 */
+	gadget_add_eps(gadget_wrapper);
+	/*
+	 * Setup interupt handler
+	 */
+	DWC_DEBUGPL(DBG_ANY, "registering handler for irq%d\n",
+                    otg_dev->os_dep.irq_num);
+	retval = request_irq(otg_dev->os_dep.irq_num, dwc_otg_pcd_irq,
+			     IRQF_SHARED, gadget_wrapper->gadget.name,
+			     otg_dev->pcd);
+	if (retval != 0) {
+		DWC_ERROR("request of irq%d failed\n", otg_dev->os_dep.irq_num);
+		free_wrapper(gadget_wrapper);
+		return -EBUSY;
+	}
+
+	dwc_otg_pcd_start(gadget_wrapper->pcd, &fops);
+
+	return retval;
+}
+
+/**
+ * Cleanup the PCD.
+ */
+void pcd_remove(dwc_bus_dev_t *_dev)
+{
+	dwc_otg_device_t *otg_dev = DWC_OTG_BUSDRVDATA(_dev);
+	dwc_otg_pcd_t *pcd = otg_dev->pcd;
+
+	DWC_DEBUGPL(DBG_PCDV, "%s(%p) otg_dev %p\n", __func__, _dev, otg_dev);
+
+	/*
+	 * Free the IRQ
+	 */
+	free_irq(otg_dev->os_dep.irq_num, pcd);
+	dwc_otg_pcd_remove(otg_dev->pcd);
+	free_wrapper(gadget_wrapper);
+	otg_dev->pcd = 0;
+}
+
+#endif /* DWC_HOST_ONLY */
diff -Nur --no-dereference linux-5.10.52-orig/drivers/usb/host/dwc_otg/dwc_otg_regs.h linux-5.10.52-v7l+/drivers/usb/host/dwc_otg/dwc_otg_regs.h
--- linux-5.10.52-orig/drivers/usb/host/dwc_otg/dwc_otg_regs.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/usb/host/dwc_otg/dwc_otg_regs.h	2021-07-25 16:46:28.717909442 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/* ==========================================================================
+ * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_regs.h $
+ * $Revision: #98 $
+ * $Date: 2012/08/10 $
+ * $Change: 2047372 $
+ *
+ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter,
+ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless
+ * otherwise expressly agreed to in writing between Synopsys and you.
+ *
+ * The Software IS NOT an item of Licensed Software or Licensed Product under
+ * any End User Software License Agreement or Agreement for Licensed Product
+ * with Synopsys or any supplement thereto. You are permitted to use and
+ * redistribute this Software in source and binary forms, with or without
+ * modification, provided that redistributions of source code must retain this
+ * notice. You may not view, use, disclose, copy or distribute this file or
+ * any information contained herein except pursuant to this license grant from
+ * Synopsys. If you do not agree with this notice, including the disclaimer
+ * below, then you are not authorized to use the Software.
+ *
+ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * ========================================================================== */
+
+#ifndef __DWC_OTG_REGS_H__
+#define __DWC_OTG_REGS_H__
+
+#include "dwc_otg_core_if.h"
+
+/**
+ * @file
+ *
+ * This file contains the data structures for accessing the DWC_otg core registers.
+ *
+ * The application interfaces with the HS OTG core by reading from and
+ * writing to the Control and Status Register (CSR) space through the
+ * AHB Slave interface. These registers are 32 bits wide, and the
+ * addresses are 32-bit-block aligned.
+ * CSRs are classified as follows:
+ * - Core Global Registers
+ * - Device Mode Registers
+ * - Device Global Registers
+ * - Device Endpoint Specific Registers
+ * - Host Mode Registers
+ * - Host Global Registers
+ * - Host Port CSRs
+ * - Host Channel Specific Registers
+ *
+ * Only the Core Global registers can be accessed in both Device and
+ * Host modes. When the HS OTG core is operating in one mode, either
+ * Device or Host, the application must not access registers from the
+ * other mode. When the core switches from one mode to another, the
+ * registers in the new mode of operation must be reprogrammed as they
+ * would be after a power-on reset.
+ */
+
+/****************************************************************************/
+/** DWC_otg Core registers .
+ * The dwc_otg_core_global_regs structure defines the size
+ * and relative field offsets for the Core Global registers.
+ */
+typedef struct dwc_otg_core_global_regs {
+	/** OTG Control and Status Register.  <i>Offset: 000h</i> */
+	volatile uint32_t gotgctl;
+	/** OTG Interrupt Register.	 <i>Offset: 004h</i> */
+	volatile uint32_t gotgint;
+	/**Core AHB Configuration Register.	 <i>Offset: 008h</i> */
+	volatile uint32_t gahbcfg;
+
+#define DWC_GLBINTRMASK		0x0001
+#define DWC_DMAENABLE		0x0020
+#define DWC_NPTXEMPTYLVL_EMPTY	0x0080
+#define DWC_NPTXEMPTYLVL_HALFEMPTY	0x0000
+#define DWC_PTXEMPTYLVL_EMPTY	0x0100
+#define DWC_PTXEMPTYLVL_HALFEMPTY	0x0000
+
+	/**Core USB Configuration Register.	 <i>Offset: 00Ch</i> */
+	volatile uint32_t gusbcfg;
+	/**Core Reset Register.	 <i>Offset: 010h</i> */
+	volatile uint32_t grstctl;
+	/**Core Interrupt Register.	 <i>Offset: 014h</i> */
+	volatile uint32_t gintsts;
+	/**Core Interrupt Mask Register.  <i>Offset: 018h</i> */
+	volatile uint32_t gintmsk;
+	/**Receive Status Queue Read Register (Read Only).	<i>Offset: 01Ch</i> */
+	volatile uint32_t grxstsr;
+	/**Receive Status Queue Read & POP Register (Read Only).  <i>Offset: 020h</i>*/
+	volatile uint32_t grxstsp;
+	/**Receive FIFO Size Register.	<i>Offset: 024h</i> */
+	volatile uint32_t grxfsiz;
+	/**Non Periodic Transmit FIFO Size Register.  <i>Offset: 028h</i> */
+	volatile uint32_t gnptxfsiz;
+	/**Non Periodic Transmit FIFO/Queue Status Register (Read
+	 * Only). <i>Offset: 02Ch</i> */
+	volatile uint32_t gnptxsts;
+	/**I2C Access Register.	 <i>Offset: 030h</i> */
+	volatile uint32_t gi2cctl;
+	/**PHY Vendor Control Register.	 <i>Offset: 034h</i> */
+	volatile uint32_t gpvndctl;
+	/**General Purpose Input/Output Register.  <i>Offset: 038h</i> */
+	volatile uint32_t ggpio;
+	/**User ID Register.  <i>Offset: 03Ch</i> */
+	volatile uint32_t guid;
+	/**Synopsys ID Register (Read Only).  <i>Offset: 040h</i> */
+	volatile uint32_t gsnpsid;
+	/**User HW Config1 Register (Read Only).  <i>Offset: 044h</i> */
+	volatile uint32_t ghwcfg1;
+	/**User HW Config2 Register (Read Only).  <i>Offset: 048h</i> */
+	volatile uint32_t ghwcfg2;
+#define DWC_SLAVE_ONLY_ARCH 0
+#define DWC_EXT_DMA_ARCH 1
+#define DWC_INT_DMA_ARCH 2
+
+#define DWC_MODE_HNP_SRP_CAPABLE	0
+#define DWC_MODE_SRP_ONLY_CAPABLE	1
+#define DWC_MODE_NO_HNP_SRP_CAPABLE		2
+#define DWC_MODE_SRP_CAPABLE_DEVICE		3
+#define DWC_MODE_NO_SRP_CAPABLE_DEVICE	4
+#define DWC_MODE_SRP_CAPABLE_HOST	5
+#define DWC_MODE_NO_SRP_CAPABLE_HOST	6
+
+	/**User HW Config3 Register (Read Only).  <i>Offset: 04Ch</i> */
+	volatile uint32_t ghwcfg3;
+	/**User HW Config4 Register (Read Only).  <i>Offset: 050h</i>*/
+	volatile uint32_t ghwcfg4;
+	/** Core LPM Configuration register <i>Offset: 054h</i>*/
+	volatile uint32_t glpmcfg;
+	/** Global PowerDn Register <i>Offset: 058h</i> */
+	volatile uint32_t gpwrdn;
+	/** Global DFIFO SW Config Register  <i>Offset: 05Ch</i> */
+	volatile uint32_t gdfifocfg;
+	/** ADP Control Register  <i>Offset: 060h</i> */
+	volatile uint32_t adpctl;
+	/** Reserved  <i>Offset: 064h-0FFh</i> */
+	volatile uint32_t reserved39[39];
+	/** Host Periodic Transmit FIFO Size Register. <i>Offset: 100h</i> */
+	volatile uint32_t hptxfsiz;
+	/** Device Periodic Transmit FIFO#n Register if dedicated fifos are disabled,
+		otherwise Device Transmit FIFO#n Register.
+	 * <i>Offset: 104h + (FIFO_Number-1)*04h, 1 <= FIFO Number <= 15 (1<=n<=15).</i> */
+	volatile uint32_t dtxfsiz[15];
+} dwc_otg_core_global_regs_t;
+
+/**
+ * This union represents the bit fields of the Core OTG Control
+ * and Status Register (GOTGCTL).  Set the bits using the bit
+ * fields then write the <i>d32</i> value to the register.
+ */
+typedef union gotgctl_data {
+	/** raw register data */
+	uint32_t d32;
+	/** register bits */
+	struct {
+		unsigned sesreqscs:1;
+		unsigned sesreq:1;
+		unsigned vbvalidoven:1;
+		unsigned vbvalidovval:1;
+		unsigned avalidoven:1;
+		unsigned avalidovval:1;
+		unsigned bvalidoven:1;
+		unsigned bvalidovval:1;
+		unsigned hstnegscs:1;
+		unsigned hnpreq:1;
+		unsigned hstsethnpen:1;
+		unsigned devhnpen:1;
+		unsigned reserved12_15:4;
+		unsigned conidsts:1;
+		unsigned dbnctime:1;
+		unsigned asesvld:1;
+		unsigned bsesvld:1;
+		unsigned otgver:1;
+		unsigned reserved1:1;
+		unsigned multvalidbc:5;
+		unsigned chirpen:1;
+		unsigned reserved28_31:4;
+	} b;
+} gotgctl_data_t;
+
+/**
+ * This union represents the bit fields of the Core OTG Interrupt Register
+ * (GOTGINT).  Set/clear the bits using the bit fields then write the <i>d32</i>
+ * value to the register.
+ */
+typedef union gotgint_data {
+	/** raw register data */
+	uint32_t d32;
+	/** register bits */
+	struct {
+		/** Current Mode */
+		unsigned reserved0_1:2;
+
+		/** Session End Detected */
+		unsigned sesenddet:1;
+
+		unsigned reserved3_7:5;
+
+		/** Session Request Success Status Change */
+		unsigned sesreqsucstschng:1;
+		/** Host Negotiation Success Status Change */
+		unsigned hstnegsucstschng:1;
+
+		unsigned reserved10_16:7;
+
+		/** Host Negotiation Detected */
+		unsigned hstnegdet:1;
+		/** A-Device Timeout Change */
+		unsigned adevtoutchng:1;
+		/** Debounce Done */
+		unsigned debdone:1;
+		/** Multi-Valued input changed */
+		unsigned mvic:1;
+
+		unsigned reserved31_21:11;
+
+	} b;
+} gotgint_data_t;
+
+/**
+ * This union represents the bit fields of the Core AHB Configuration
+ * Register (GAHBCFG). Set/clear the bits using the bit fields then
+ * write the <i>d32</i> value to the register.
+ */
+typedef union gahbcfg_data {
+	/** raw register data */
+	uint32_t d32;
+	/** register bits */
+	struct {
+		unsigned glblintrmsk:1;
+#define DWC_GAHBCFG_GLBINT_ENABLE		1
+
+		unsigned hburstlen:4;
+#define DWC_GAHBCFG_INT_DMA_BURST_SINGLE	0
+#define DWC_GAHBCFG_INT_DMA_BURST_INCR		1
+#define DWC_GAHBCFG_INT_DMA_BURST_INCR4		3
+#define DWC_GAHBCFG_INT_DMA_BURST_INCR8		5
+#define DWC_GAHBCFG_INT_DMA_BURST_INCR16	7
+
+		unsigned dmaenable:1;
+#define DWC_GAHBCFG_DMAENABLE			1
+		unsigned reserved:1;
+		unsigned nptxfemplvl_txfemplvl:1;
+		unsigned ptxfemplvl:1;
+#define DWC_GAHBCFG_TXFEMPTYLVL_EMPTY		1
+#define DWC_GAHBCFG_TXFEMPTYLVL_HALFEMPTY	0
+		unsigned reserved9_20:12;
+		unsigned remmemsupp:1;
+		unsigned notialldmawrit:1;
+		unsigned ahbsingle:1;
+		unsigned reserved24_31:8;
+	} b;
+} gahbcfg_data_t;
+
+/**
+ * This union represents the bit fields of the Core USB Configuration
+ * Register (GUSBCFG). Set the bits using the bit fields then write
+ * the <i>d32</i> value to the register.
+ */
+typedef union gusbcfg_data {
+	/** raw register data */
+	uint32_t d32;
+	/** register bits */
+	struct {
+		unsigned toutcal:3;
+		unsigned phyif:1;
+		unsigned ulpi_utmi_sel:1;
+		unsigned fsintf:1;
+		unsigned physel:1;
+		unsigned ddrsel:1;
+		unsigned srpcap:1;
+		unsigned hnpcap:1;
+		unsigned usbtrdtim:4;
+		unsigned reserved1:1;
+		unsigned phylpwrclksel:1;
+		unsigned otgutmifssel:1;
+		unsigned ulpi_fsls:1;
+		unsigned ulpi_auto_res:1;
+		unsigned ulpi_clk_sus_m:1;
+		unsigned ulpi_ext_vbus_drv:1;
+		unsigned ulpi_int_vbus_indicator:1;
+		unsigned term_sel_dl_pulse:1;
+		unsigned indicator_complement:1;
+		unsigned indicator_pass_through:1;
+		unsigned ulpi_int_prot_dis:1;
+		unsigned ic_usb_cap:1;
+		unsigned ic_traffic_pull_remove:1;
+		unsigned tx_end_delay:1;
+		unsigned force_host_mode:1;
+		unsigned force_dev_mode:1;
+		unsigned reserved31:1;
+	} b;
+} gusbcfg_data_t;
+
+/**
+ * This union represents the bit fields of the Core Reset Register
+ * (GRSTCTL).  Set/clear the bits using the bit fields then write the
+ * <i>d32</i> value to the register.
+ */
+typedef union grstctl_data {
+	/** raw register data */
+	uint32_t d32;
+	/** register bits */
+	struct {
+		/** Core Soft Reset (CSftRst) (Device and Host)
+		 *
+		 * The application can flush the control logic in the
+		 * entire core using this bit. This bit resets the
+		 * pipelines in the AHB Clock domain as well as the
+		 * PHY Clock domain.
+		 *
+		 * The state machines are reset to an IDLE state, the
+		 * control bits in the CSRs are cleared, all the
+		 * transmit FIFOs and the receive FIFO are flushed.
+		 *
+		 * The status mask bits that control the generation of
+		 * the interrupt, are cleared, to clear the
+		 * interrupt. The interrupt status bits are not
+		 * cleared, so the application can get the status of
+		 * any events that occurred in the core after it has
+		 * set this bit.
+		 *
+		 * Any transactions on the AHB are terminated as soon
+		 * as possible following the protocol. Any
+		 * transactions on the USB are terminated immediately.
+		 *
+		 * The configuration settings in the CSRs are
+		 * unchanged, so the software doesn't have to
+		 * reprogram these registers (Device
+		 * Configuration/Host Configuration/Core System
+		 * Configuration/Core PHY Configuration).
+		 *
+		 * The application can write to this bit, any time it
+		 * wants to reset the core. This is a self clearing
+		 * bit and the core clears this bit after all the
+		 * necessary logic is reset in the core, which may
+		 * take several clocks, depending on the current state
+		 * of the core.
+		 */
+		unsigned csftrst:1;
+		/** Hclk Soft Reset
+		 *
+		 * The application uses this bit to reset the control logic in
+		 * the AHB clock domain. Only AHB clock domain pipelines are
+		 * reset.
+		 */
+		unsigned hsftrst:1;
+		/** Host Frame Counter Reset (Host Only)<br>
+		 *
+		 * The application can reset the (micro)frame number
+		 * counter inside the core, using this bit. When the
+		 * (micro)frame counter is reset, the subsequent SOF
+		 * sent out by the core, will have a (micro)frame
+		 * number of 0.
+		 */
+		unsigned hstfrm:1;
+		/** In Token Sequence Learning Queue Flush
+		 * (INTknQFlsh) (Device Only)
+		 */
+		unsigned intknqflsh:1;
+		/** RxFIFO Flush (RxFFlsh) (Device and Host)
+		 *
+		 * The application can flush the entire Receive FIFO
+		 * using this bit. The application must first
+		 * ensure that the core is not in the middle of a
+		 * transaction. The application should write into
+		 * this bit, only after making sure that neither the
+		 * DMA engine is reading from the RxFIFO nor the MAC
+		 * is writing the data in to the FIFO. The
+		 * application should wait until the bit is cleared
+		 * before performing any other operations. This bit
+		 * will takes 8 clocks (slowest of PHY or AHB clock)
+		 * to clear.
+		 */
+		unsigned rxfflsh:1;
+		/** TxFIFO Flush (TxFFlsh) (Device and Host).
+		 *
+		 * This bit is used to selectively flush a single or
+		 * all transmit FIFOs. The application must first
+		 * ensure that the core is not in the middle of a
+		 * transaction. The application should write into
+		 * this bit, only after making sure that neither the
+		 * DMA engine is writing into the TxFIFO nor the MAC
+		 * is reading the data out of the FIFO. The
+		 * application should wait until the core clears this
+		 * bit, before performing any operations. This bit
+		 * will takes 8 clocks (slowest of PHY or AHB clock)
+		 * to clear.
+		 */
+		unsigned txfflsh:1;
+
+		/** TxFIFO Number (TxFNum) (Device and Host).
+		 *
+		 * This is the FIFO number which needs to be flushed,
+		 * using the TxFIFO Flush bit. This field should not
+		 * be changed until the TxFIFO Flush bit is cleared by
+		 * the core.
+		 *	 - 0x0 : Non Periodic TxFIFO Flush
+		 *	 - 0x1 : Periodic TxFIFO #1 Flush in device mode
+		 *	   or Periodic TxFIFO in host mode
+		 *	 - 0x2 : Periodic TxFIFO #2 Flush in device mode.
+		 *	 - ...
+		 *	 - 0xF : Periodic TxFIFO #15 Flush in device mode
+		 *	 - 0x10: Flush all the Transmit NonPeriodic and
+		 *	   Transmit Periodic FIFOs in the core
+		 */
+		unsigned txfnum:5;
+		/** Reserved */
+		unsigned reserved11_29:19;
+		/** DMA Request Signal.	 Indicated DMA request is in
+		 * probress. Used for debug purpose. */
+		unsigned dmareq:1;
+		/** AHB Master Idle.  Indicates the AHB Master State
+		 * Machine is in IDLE condition. */
+		unsigned ahbidle:1;
+	} b;
+} grstctl_t;
+
+/**
+ * This union represents the bit fields of the Core Interrupt Mask
+ * Register (GINTMSK). Set/clear the bits using the bit fields then
+ * write the <i>d32</i> value to the register.
+ */
+typedef union gintmsk_data {
+	/** raw register data */
+	uint32_t d32;
+	/** register bits */
+	struct {
+		unsigned reserved0:1;
+		unsigned modemismatch:1;
+		unsigned otgintr:1;
+		unsigned sofintr:1;
+		unsigned rxstsqlvl:1;
+		unsigned nptxfempty:1;
+		unsigned ginnakeff:1;
+		unsigned goutnakeff:1;
+		unsigned ulpickint:1;
+		unsigned i2cintr:1;
+		unsigned erlysuspend:1;
+		unsigned usbsuspend:1;
+		unsigned usbreset:1;
+		unsigned enumdone:1;
+		unsigned isooutdrop:1;
+		unsigned eopframe:1;
+		unsigned restoredone:1;
+		unsigned epmismatch:1;
+		unsigned inepintr:1;
+		unsigned outepintr:1;
+		unsigned incomplisoin:1;
+		unsigned incomplisoout:1;
+		unsigned fetsusp:1;
+		unsigned resetdet:1;
+		unsigned portintr:1;
+		unsigned hcintr:1;
+		unsigned ptxfempty:1;
+		unsigned lpmtranrcvd:1;
+		unsigned conidstschng:1;
+		unsigned disconnect:1;
+		unsigned sessreqintr:1;
+		unsigned wkupintr:1;
+	} b;
+} gintmsk_data_t;
+/**
+ * This union represents the bit fields of the Core Interrupt Register
+ * (GINTSTS).  Set/clear the bits using the bit fields then write the
+ * <i>d32</i> value to the register.
+ */
+typedef union gintsts_data {
+	/** raw register data */
+	uint32_t d32;
+#define DWC_SOF_INTR_MASK 0x0008
+	/** register bits */
+	struct {
+#define DWC_HOST_MODE 1
+		unsigned curmode:1;
+		unsigned modemismatch:1;
+		unsigned otgintr:1;
+		unsigned sofintr:1;
+		unsigned rxstsqlvl:1;
+		unsigned nptxfempty:1;
+		unsigned ginnakeff:1;
+		unsigned goutnakeff:1;
+		unsigned ulpickint:1;
+		unsigned i2cintr:1;
+		unsigned erlysuspend:1;
+		unsigned usbsuspend:1;
+		unsigned usbreset:1;
+		unsigned enumdone:1;
+		unsigned isooutdrop:1;
+		unsigned eopframe:1;
+		unsigned restoredone:1;
+		unsigned epmismatch:1;
+		unsigned inepint:1;
+		unsigned outepintr:1;
+		unsigned incomplisoin:1;
+		unsigned incomplisoout:1;
+		unsigned fetsusp:1;
+		unsigned resetdet:1;
+		unsigned portintr:1;
+		unsigned hcintr:1;
+		unsigned ptxfempty:1;
+		unsigned lpmtranrcvd:1;
+		unsigned conidstschng:1;
+		unsigned disconnect:1;
+		unsigned sessreqintr:1;
+		unsigned wkupintr:1;
+	} b;
+} gintsts_data_t;
+
+/**
+ * This union represents the bit fields in the Device Receive Status Read and
+ * Pop Registers (GRXSTSR, GRXSTSP) Read the register into the <i>d32</i>
+ * element then read out the bits using the <i>b</i>it elements.
+ */
+typedef union device_grxsts_data {
+	/** raw register data */
+	uint32_t d32;
+	/** register bits */
+	struct {
+		unsigned epnum:4;
+		unsigned bcnt:11;
+		unsigned dpid:2;
+
+#define DWC_STS_DATA_UPDT		0x2	// OUT Data Packet
+#define DWC_STS_XFER_COMP		0x3	// OUT Data Transfer Complete
+
+#define DWC_DSTS_GOUT_NAK		0x1	// Global OUT NAK
+#define DWC_DSTS_SETUP_COMP		0x4	// Setup Phase Complete
+#define DWC_DSTS_SETUP_UPDT 0x6	// SETUP Packet
+		unsigned pktsts:4;
+		unsigned fn:4;
+		unsigned reserved25_31:7;
+	} b;
+} device_grxsts_data_t;
+
+/**
+ * This union represents the bit fields in the Host Receive Status Read and
+ * Pop Registers (GRXSTSR, GRXSTSP) Read the register into the <i>d32</i>
+ * element then read out the bits using the <i>b</i>it elements.
+ */
+typedef union host_grxsts_data {
+	/** raw register data */
+	uint32_t d32;
+	/** register bits */
+	struct {
+		unsigned chnum:4;
+		unsigned bcnt:11;
+		unsigned dpid:2;
+
+		unsigned pktsts:4;
+#define DWC_GRXSTS_PKTSTS_IN			  0x2
+#define DWC_GRXSTS_PKTSTS_IN_XFER_COMP	  0x3
+#define DWC_GRXSTS_PKTSTS_DATA_TOGGLE_ERR 0x5
+#define DWC_GRXSTS_PKTSTS_CH_HALTED		  0x7
+
+		unsigned reserved21_31:11;
+	} b;
+} host_grxsts_data_t;
+
+/**
+ * This union represents the bit fields in the FIFO Size Registers (HPTXFSIZ,
+ * GNPTXFSIZ, DPTXFSIZn, DIEPTXFn). Read the register into the <i>d32</i> element
+ * then read out the bits using the <i>b</i>it elements.
+ */
+typedef union fifosize_data {
+	/** raw register data */
+	uint32_t d32;
+	/** register bits */
+	struct {
+		unsigned startaddr:16;
+		unsigned depth:16;
+	} b;
+} fifosize_data_t;
+
+/**
+ * This union represents the bit fields in the Non-Periodic Transmit
+ * FIFO/Queue Status Register (GNPTXSTS). Read the register into the
+ * <i>d32</i> element then read out the bits using the <i>b</i>it
+ * elements.
+ */
+typedef union gnptxsts_data {
+	/** raw register data */
+	uint32_t d32;
+	/** register bits */
+	struct {
+		unsigned nptxfspcavail:16;
+		unsigned nptxqspcavail:8;
+		/** Top of the Non-Periodic Transmit Request Queue
+		 *	- bit 24 - Terminate (Last entry for the selected
+		 *	  channel/EP)
+		 *	- bits 26:25 - Token Type
+		 *	  - 2'b00 - IN/OUT
+		 *	  - 2'b01 - Zero Length OUT
+		 *	  - 2'b10 - PING/Complete Split
+		 *	  - 2'b11 - Channel Halt
+		 *	- bits 30:27 - Channel/EP Number
+		 */
+		unsigned nptxqtop_terminate:1;
+		unsigned nptxqtop_token:2;
+		unsigned nptxqtop_chnep:4;
+		unsigned reserved:1;
+	} b;
+} gnptxsts_data_t;
+
+/**
+ * This union represents the bit fields in the Transmit
+ * FIFO Status Register (DTXFSTS). Read the register into the
+ * <i>d32</i> element then read out the bits using the <i>b</i>it
+ * elements.
+ */
+typedef union dtxfsts_data {
+	/** raw register data */
+	uint32_t d32;
+	/** register bits */
+	struct {
+		unsigned txfspcavail:16;
+		unsigned reserved:16;
+	} b;
+} dtxfsts_data_t;
+
+/**
+ * This union represents the bit fields in the I2C Control Register
+ * (I2CCTL). Read the register into the <i>d32</i> element then read out the
+ * bits using the <i>b</i>it elements.
+ */
+typedef union gi2cctl_data {
+	/** raw register data */
+	uint32_t d32;
+	/** register bits */
+	struct {
+		unsigned rwdata:8;
+		unsigned regaddr:8;
+		unsigned addr:7;
+		unsigned i2cen:1;
+		unsigned ack:1;
+		unsigned i2csuspctl:1;
+		unsigned i2cdevaddr:2;
+		unsigned i2cdatse0:1;
+		unsigned reserved:1;
+		unsigned rw:1;
+		unsigned bsydne:1;
+	} b;
+} gi2cctl_data_t;
+
+/**
+ * This union represents the bit fields in the PHY Vendor Control Register
+ * (GPVNDCTL). Read the register into the <i>d32</i> element then read out the
+ * bits using the <i>b</i>it elements.
+ */
+typedef union gpvndctl_data {
+	/** raw register data */
+	uint32_t d32;
+	/** register bits */
+	struct {
+		unsigned regdata:8;
+		unsigned vctrl:8;
+		unsigned regaddr16_21:6;
+		unsigned regwr:1;
+		unsigned reserved23_24:2;
+		unsigned newregreq:1;
+		unsigned vstsbsy:1;
+		unsigned vstsdone:1;
+		unsigned reserved28_30:3;
+		unsigned disulpidrvr:1;
+	} b;
+} gpvndctl_data_t;
+
+/**
+ * This union represents the bit fields in the General Purpose
+ * Input/Output Register (GGPIO).
+ * Read the register into the <i>d32</i> element then read out the
+ * bits using the <i>b</i>it elements.
+ */
+typedef union ggpio_data {
+	/** raw register data */
+	uint32_t d32;
+	/** register bits */
+	struct {
+		unsigned gpi:16;
+		unsigned gpo:16;
+	} b;
+} ggpio_data_t;
+
+/**
+ * This union represents the bit fields in the User ID Register
+ * (GUID). Read the register into the <i>d32</i> element then read out the
+ * bits using the <i>b</i>it elements.
+ */
+typedef union guid_data {
+	/** raw register data */
+	uint32_t d32;
+	/** register bits */
+	struct {
+		unsigned rwdata:32;
+	} b;
+} guid_data_t;
+
+/**
+ * This union represents the bit fields in the Synopsys ID Register
+ * (GSNPSID). Read the register into the <i>d32</i> element then read out the
+ * bits using the <i>b</i>it elements.
+ */
+typedef union gsnpsid_data {
+	/** raw register data */
+	uint32_t d32;
+	/** register bits */
+	struct {
+		unsigned rwdata:32;
+	} b;
+} gsnpsid_data_t;
+
+/**
+ * This union represents the bit fields in the User HW Config1
+ * Register.  Read the register into the <i>d32</i> element then read
+ * out the bits using the <i>b</i>it elements.
+ */
+typedef union hwcfg1_data {
+	/** raw register data */
+	uint32_t d32;
+	/** register bits */
+	struct {
+		unsigned ep_dir0:2;
+		unsigned ep_dir1:2;
+		unsigned ep_dir2:2;
+		unsigned ep_dir3:2;
+		unsigned ep_dir4:2;
+		unsigned ep_dir5:2;
+		unsigned ep_dir6:2;
+		unsigned ep_dir7:2;
+		unsigned ep_dir8:2;
+		unsigned ep_dir9:2;
+		unsigned ep_dir10:2;
+		unsigned ep_dir11:2;
+		unsigned ep_dir12:2;
+		unsigned ep_dir13:2;
+		unsigned ep_dir14:2;
+		unsigned ep_dir15:2;
+	} b;
+} hwcfg1_data_t;
+
+/**
+ * This union represents the bit fields in the User HW Config2
+ * Register.  Read the register into the <i>d32</i> element then read
+ * out the bits using the <i>b</i>it elements.
+ */
+typedef union hwcfg2_data {
+	/** raw register data */
+	uint32_t d32;
+	/** register bits */
+	struct {
+		/* GHWCFG2 */
+		unsigned op_mode:3;
+#define DWC_HWCFG2_OP_MODE_HNP_SRP_CAPABLE_OTG 0
+#define DWC_HWCFG2_OP_MODE_SRP_ONLY_CAPABLE_OTG 1
+#define DWC_HWCFG2_OP_MODE_NO_HNP_SRP_CAPABLE_OTG 2
+#define DWC_HWCFG2_OP_MODE_SRP_CAPABLE_DEVICE 3
+#define DWC_HWCFG2_OP_MODE_NO_SRP_CAPABLE_DEVICE 4
+#define DWC_HWCFG2_OP_MODE_SRP_CAPABLE_HOST 5
+#define DWC_HWCFG2_OP_MODE_NO_SRP_CAPABLE_HOST 6
+
+		unsigned architecture:2;
+		unsigned point2point:1;
+		unsigned hs_phy_type:2;
+#define DWC_HWCFG2_HS_PHY_TYPE_NOT_SUPPORTED 0
+#define DWC_HWCFG2_HS_PHY_TYPE_UTMI 1
+#define DWC_HWCFG2_HS_PHY_TYPE_ULPI 2
+#define DWC_HWCFG2_HS_PHY_TYPE_UTMI_ULPI 3
+
+		unsigned fs_phy_type:2;
+		unsigned num_dev_ep:4;
+		unsigned num_host_chan:4;
+		unsigned perio_ep_supported:1;
+		unsigned dynamic_fifo:1;
+		unsigned multi_proc_int:1;
+		unsigned reserved21:1;
+		unsigned nonperio_tx_q_depth:2;
+		unsigned host_perio_tx_q_depth:2;
+		unsigned dev_token_q_depth:5;
+		unsigned otg_enable_ic_usb:1;
+	} b;
+} hwcfg2_data_t;
+
+/**
+ * This union represents the bit fields in the User HW Config3
+ * Register.  Read the register into the <i>d32</i> element then read
+ * out the bits using the <i>b</i>it elements.
+ */
+typedef union hwcfg3_data {
+	/** raw register data */
+	uint32_t d32;
+	/** register bits */
+	struct {
+		/* GHWCFG3 */
+		unsigned xfer_size_cntr_width:4;
+		unsigned packet_size_cntr_width:3;
+		unsigned otg_func:1;
+		unsigned i2c:1;
+		unsigned vendor_ctrl_if:1;
+		unsigned optional_features:1;
+		unsigned synch_reset_type:1;
+		unsigned adp_supp:1;
+		unsigned otg_enable_hsic:1;
+		unsigned bc_support:1;
+		unsigned otg_lpm_en:1;
+		unsigned dfifo_depth:16;
+	} b;
+} hwcfg3_data_t;
+
+/**
+ * This union represents the bit fields in the User HW Config4
+ * Register.  Read the register into the <i>d32</i> element then read
+ * out the bits using the <i>b</i>it elements.
+ */
+typedef union hwcfg4_data {
+	/** raw register data */
+	uint32_t d32;
+	/** register bits */
+	struct {
+		unsigned num_dev_perio_in_ep:4;
+		unsigned power_optimiz:1;
+		unsigned min_ahb_freq:1;
+		unsigned hiber:1;
+		unsigned xhiber:1;
+		unsigned reserved:6;
+		unsigned utmi_phy_data_width:2;
+		unsigned num_dev_mode_ctrl_ep:4;
+		unsigned iddig_filt_en:1;
+		unsigned vbus_valid_filt_en:1;
+		unsigned a_valid_filt_en:1;
+		unsigned b_valid_filt_en:1;
+		unsigned session_end_filt_en:1;
+		unsigned ded_fifo_en:1;
+		unsigned num_in_eps:4;
+		unsigned desc_dma:1;
+		unsigned desc_dma_dyn:1;
+	} b;
+} hwcfg4_data_t;
+
+/**
+ * This union represents the bit fields of the Core LPM Configuration
+ * Register (GLPMCFG). Set the bits using bit fields then write
+ * the <i>d32</i> value to the register.
+ */
+typedef union glpmctl_data {
+	/** raw register data */
+	uint32_t d32;
+	/** register bits */
+	struct {
+		/** LPM-Capable (LPMCap) (Device and Host)
+		 * The application uses this bit to control
+		 * the DWC_otg core LPM capabilities.
+		 */
+		unsigned lpm_cap_en:1;
+		/** LPM response programmed by application (AppL1Res) (Device)
+		 * Handshake response to LPM token pre-programmed
+		 * by device application software.
+		 */
+		unsigned appl_resp:1;
+		/** Host Initiated Resume Duration (HIRD) (Device and Host)
+		 * In Host mode this field indicates the value of HIRD
+		 * to be sent in an LPM transaction.
+		 * In Device mode this field is updated with the
+		 * Received LPM Token HIRD bmAttribute
+		 * when an ACK/NYET/STALL response is sent
+		 * to an LPM transaction.
+		 */
+		unsigned hird:4;
+		/** RemoteWakeEnable (bRemoteWake) (Device and Host)
+		 * In Host mode this bit indicates the value of remote
+		 * wake up to be sent in wIndex field of LPM transaction.
+		 * In Device mode this field is updated with the
+		 * Received LPM Token bRemoteWake bmAttribute
+		 * when an ACK/NYET/STALL response is sent
+		 * to an LPM transaction.
+		 */
+		unsigned rem_wkup_en:1;
+		/** Enable utmi_sleep_n (EnblSlpM) (Device and Host)
+		 * The application uses this bit to control
+		 * the utmi_sleep_n assertion to the PHY when in L1 state.
+		 */
+		unsigned en_utmi_sleep:1;
+		/** HIRD Threshold (HIRD_Thres) (Device and Host)
+		 */
+		unsigned hird_thres:5;
+		/** LPM Response (CoreL1Res) (Device and Host)
+		 * In Host mode this bit contains handsake response to
+		 * LPM transaction.
+		 * In Device mode the response of the core to
+		 * LPM transaction received is reflected in these two bits.
+			- 0x0 : ERROR (No handshake response)
+			- 0x1 : STALL
+			- 0x2 : NYET
+			- 0x3 : ACK
+		 */
+		unsigned lpm_resp:2;
+		/** Port Sleep Status (SlpSts) (Device and Host)
+		 * This bit is set as long as a Sleep condition
+		 * is present on the USB bus.
+		 */
+		unsigned prt_sleep_sts:1;
+		/** Sleep State Resume OK (L1ResumeOK) (Device and Host)
+		 * Indicates that the application or host
+		 * can start resume from Sleep state.
+		 */
+		unsigned sleep_state_resumeok:1;
+		/** LPM channel Index (LPM_Chnl_Indx) (Host)
+		 * The channel number on which the LPM transaction
+		 * has to be applied while sending
+		 * an LPM transaction to the local device.
+		 */
+		unsigned lpm_chan_index:4;
+		/** LPM Retry Count (LPM_Retry_Cnt) (Host)
+		 * Number host retries that would be performed
+		 * if the device response was not valid response.
+		 */
+		unsigned retry_count:3;
+		/** Send LPM Transaction (SndLPM) (Host)
+		 * When set by application software,
+		 * an LPM transaction containing two tokens
+		 * is sent.
+		 */
+		unsigned send_lpm:1;
+		/** LPM Retry status (LPM_RetryCnt_Sts) (Host)
+		 * Number of LPM Host Retries still remaining
+		 * to be transmitted for the current LPM sequence
+		 */
+		unsigned retry_count_sts:3;
+		unsigned reserved28_29:2;
+		/** In host mode once this bit is set, the host
+		 * configures to drive the HSIC Idle state on the bus.
+		 * It then waits for the  device to initiate the Connect sequence.
+		 * In device mode once this bit is set, the device waits for
+		 * the HSIC Idle line state on the bus. Upon receving the Idle
+		 * line state, it initiates the HSIC Connect sequence.
+		 */
+		unsigned hsic_connect:1;
+		/** This bit overrides and functionally inverts
+		 * the if_select_hsic input port signal.
+		 */
+		unsigned inv_sel_hsic:1;
+	} b;
+} glpmcfg_data_t;
+
+/**
+ * This union represents the bit fields of the Core ADP Timer, Control and
+ * Status Register (ADPTIMCTLSTS). Set the bits using bit fields then write
+ * the <i>d32</i> value to the register.
+ */
+typedef union adpctl_data {
+	/** raw register data */
+	uint32_t d32;
+	/** register bits */
+	struct {
+		/** Probe Discharge (PRB_DSCHG)
+		 *  These bits set the times for TADP_DSCHG.
+		 *  These bits are defined as follows:
+		 *  2'b00 - 4 msec
+		 *  2'b01 - 8 msec
+		 *  2'b10 - 16 msec
+		 *  2'b11 - 32 msec
+		 */
+		unsigned prb_dschg:2;
+		/** Probe Delta (PRB_DELTA)
+		 *  These bits set the resolution for RTIM   value.
+		 *  The bits are defined in units of 32 kHz clock cycles as follows:
+		 *  2'b00  -  1 cycles
+		 *  2'b01  -  2 cycles
+		 *  2'b10 -  3 cycles
+		 *  2'b11 - 4 cycles
+		 *  For example if this value is chosen to 2'b01, it means that RTIM
+		 *  increments for every 3(three) 32Khz clock cycles.
+		 */
+		unsigned prb_delta:2;
+		/** Probe Period (PRB_PER)
+		 *  These bits sets the TADP_PRD as shown in Figure 4 as follows:
+		 *  2'b00  -  0.625 to 0.925 sec (typical 0.775 sec)
+		 *  2'b01  -  1.25 to 1.85 sec (typical 1.55 sec)
+		 *  2'b10  -  1.9 to 2.6 sec (typical 2.275 sec)
+		 *  2'b11  -  Reserved
+		 */
+		unsigned prb_per:2;
+		/** These bits capture the latest time it took for VBUS to ramp from
+		 *  VADP_SINK to VADP_PRB.
+		 *  0x000  -  1 cycles
+		 *  0x001  -  2 cycles
+		 *  0x002  -  3 cycles
+		 *  etc
+		 *  0x7FF  -  2048 cycles
+		 *  A time of 1024 cycles at 32 kHz corresponds to a time of 32 msec.
+		*/
+		unsigned rtim:11;
+		/** Enable Probe (EnaPrb)
+		 *  When programmed to 1'b1, the core performs a probe operation.
+		 *  This bit is valid only if OTG_Ver = 1'b1.
+		 */
+		unsigned enaprb:1;
+		/** Enable Sense (EnaSns)
+		 *  When programmed to 1'b1, the core performs a Sense operation.
+		 *  This bit is valid only if OTG_Ver = 1'b1.
+		 */
+		unsigned enasns:1;
+		/** ADP Reset (ADPRes)
+		 *  When set, ADP controller is reset.
+		 *  This bit is valid only if OTG_Ver = 1'b1.
+		 */
+		unsigned adpres:1;
+		/** ADP Enable (ADPEn)
+		 *  When set, the core performs either ADP probing or sensing
+		 *  based on EnaPrb or EnaSns.
+		 *  This bit is valid only if OTG_Ver = 1'b1.
+		 */
+		unsigned adpen:1;
+		/** ADP Probe Interrupt (ADP_PRB_INT)
+		 *  When this bit is set, it means that the VBUS
+		 *  voltage is greater than VADP_PRB or VADP_PRB is reached.
+		 *  This bit is valid only if OTG_Ver = 1'b1.
+		 */
+		unsigned adp_prb_int:1;
+		/**
+		 *  ADP Sense Interrupt (ADP_SNS_INT)
+		 *  When this bit is set, it means that the VBUS voltage is greater than
+		 *  VADP_SNS value or VADP_SNS is reached.
+		 *  This bit is valid only if OTG_Ver = 1'b1.
+		 */
+		unsigned adp_sns_int:1;
+		/** ADP Tomeout Interrupt (ADP_TMOUT_INT)
+		 *  This bit is relevant only for an ADP probe.
+		 *  When this bit is set, it means that the ramp time has
+		 *  completed ie ADPCTL.RTIM has reached its terminal value
+		 *  of 0x7FF.  This is a debug feature that allows software
+		 *  to read the ramp time after each cycle.
+		 *  This bit is valid only if OTG_Ver = 1'b1.
+		 */
+		unsigned adp_tmout_int:1;
+		/** ADP Probe Interrupt Mask (ADP_PRB_INT_MSK)
+		 *  When this bit is set, it unmasks the interrupt due to ADP_PRB_INT.
+		 *  This bit is valid only if OTG_Ver = 1'b1.
+		 */
+		unsigned adp_prb_int_msk:1;
+		/** ADP Sense Interrupt Mask (ADP_SNS_INT_MSK)
+		 *  When this bit is set, it unmasks the interrupt due to ADP_SNS_INT.
+		 *  This bit is valid only if OTG_Ver = 1'b1.
+		 */
+		unsigned adp_sns_int_msk:1;
+		/** ADP Timoeout Interrupt Mask (ADP_TMOUT_MSK)
+		 *  When this bit is set, it unmasks the interrupt due to ADP_TMOUT_INT.
+		 *  This bit is valid only if OTG_Ver = 1'b1.
+		 */
+		unsigned adp_tmout_int_msk:1;
+		/** Access Request
+		 * 2'b00 - Read/Write Valid (updated by the core)
+		 * 2'b01 - Read
+		 * 2'b00 - Write
+		 * 2'b00 - Reserved
+		 */
+		unsigned ar:2;
+		 /** Reserved */
+		unsigned reserved29_31:3;
+	} b;
+} adpctl_data_t;
+
+////////////////////////////////////////////
+// Device Registers
+/**
+ * Device Global Registers. <i>Offsets 800h-BFFh</i>
+ *
+ * The following structures define the size and relative field offsets
+ * for the Device Mode Registers.
+ *
+ * <i>These registers are visible only in Device mode and must not be
+ * accessed in Host mode, as the results are unknown.</i>
+ */
+typedef struct dwc_otg_dev_global_regs {
+	/** Device Configuration Register. <i>Offset 800h</i> */
+	volatile uint32_t dcfg;
+	/** Device Control Register. <i>Offset: 804h</i> */
+	volatile uint32_t dctl;
+	/** Device Status Register (Read Only). <i>Offset: 808h</i> */
+	volatile uint32_t dsts;
+	/** Reserved. <i>Offset: 80Ch</i> */
+	uint32_t unused;
+	/** Device IN Endpoint Common Interrupt Mask
+	 * Register. <i>Offset: 810h</i> */
+	volatile uint32_t diepmsk;
+	/** Device OUT Endpoint Common Interrupt Mask
+	 * Register. <i>Offset: 814h</i> */
+	volatile uint32_t doepmsk;
+	/** Device All Endpoints Interrupt Register.  <i>Offset: 818h</i> */
+	volatile uint32_t daint;
+	/** Device All Endpoints Interrupt Mask Register.  <i>Offset:
+	 * 81Ch</i> */
+	volatile uint32_t daintmsk;
+	/** Device IN Token Queue Read Register-1 (Read Only).
+	 * <i>Offset: 820h</i> */
+	volatile uint32_t dtknqr1;
+	/** Device IN Token Queue Read Register-2 (Read Only).
+	 * <i>Offset: 824h</i> */
+	volatile uint32_t dtknqr2;
+	/** Device VBUS	 discharge Register.  <i>Offset: 828h</i> */
+	volatile uint32_t dvbusdis;
+	/** Device VBUS Pulse Register.	 <i>Offset: 82Ch</i> */
+	volatile uint32_t dvbuspulse;
+	/** Device IN Token Queue Read Register-3 (Read Only). /
+	 *	Device Thresholding control register (Read/Write)
+	 * <i>Offset: 830h</i> */
+	volatile uint32_t dtknqr3_dthrctl;
+	/** Device IN Token Queue Read Register-4 (Read Only). /
+	 *	Device IN EPs empty Inr. Mask Register (Read/Write)
+	 * <i>Offset: 834h</i> */
+	volatile uint32_t dtknqr4_fifoemptymsk;
+	/** Device Each Endpoint Interrupt Register (Read Only). /
+	 * <i>Offset: 838h</i> */
+	volatile uint32_t deachint;
+	/** Device Each Endpoint Interrupt mask Register (Read/Write). /
+	 * <i>Offset: 83Ch</i> */
+	volatile uint32_t deachintmsk;
+	/** Device Each In Endpoint Interrupt mask Register (Read/Write). /
+	 * <i>Offset: 840h</i> */
+	volatile uint32_t diepeachintmsk[MAX_EPS_CHANNELS];
+	/** Device Each Out Endpoint Interrupt mask Register (Read/Write). /
+	 * <i>Offset: 880h</i> */
+	volatile uint32_t doepeachintmsk[MAX_EPS_CHANNELS];
+} dwc_otg_device_global_regs_t;
+
+/**
+ * This union represents the bit fields in the Device Configuration
+ * Register.  Read the register into the <i>d32</i> member then
+ * set/clear the bits using the <i>b</i>it elements.  Write the
+ * <i>d32</i> member to the dcfg register.
+ */
+typedef union dcfg_data {
+	/** raw register data */
+	uint32_t d32;
+	/** register bits */
+	struct {
+		/** Device Speed */
+		unsigned devspd:2;
+		/** Non Zero Length Status OUT Handshake */
+		unsigned nzstsouthshk:1;
+#define DWC_DCFG_SEND_STALL 1
+
+		unsigned ena32khzs:1;
+		/** Device Addresses */
+		unsigned devaddr:7;
+		/** Periodic Frame Interval */
+		unsigned perfrint:2;
+#define DWC_DCFG_FRAME_INTERVAL_80 0
+#define DWC_DCFG_FRAME_INTERVAL_85 1
+#define DWC_DCFG_FRAME_INTERVAL_90 2
+#define DWC_DCFG_FRAME_INTERVAL_95 3
+
+		/** Enable Device OUT NAK for bulk in DDMA mode */
+		unsigned endevoutnak:1;
+
+		unsigned reserved14_17:4;
+		/** In Endpoint Mis-match count */
+		unsigned epmscnt:5;
+		/** Enable Descriptor DMA in Device mode */
+		unsigned descdma:1;
+		unsigned perschintvl:2;
+		unsigned resvalid:6;
+	} b;
+} dcfg_data_t;
+
+/**
+ * This union represents the bit fields in the Device Control
+ * Register.  Read the register into the <i>d32</i> member then
+ * set/clear the bits using the <i>b</i>it elements.
+ */
+typedef union dctl_data {
+	/** raw register data */
+	uint32_t d32;
+	/** register bits */
+	struct {
+		/** Remote Wakeup */
+		unsigned rmtwkupsig:1;
+		/** Soft Disconnect */
+		unsigned sftdiscon:1;
+		/** Global Non-Periodic IN NAK Status */
+		unsigned gnpinnaksts:1;
+		/** Global OUT NAK Status */
+		unsigned goutnaksts:1;
+		/** Test Control */
+		unsigned tstctl:3;
+		/** Set Global Non-Periodic IN NAK */
+		unsigned sgnpinnak:1;
+		/** Clear Global Non-Periodic IN NAK */
+		unsigned cgnpinnak:1;
+		/** Set Global OUT NAK */
+		unsigned sgoutnak:1;
+		/** Clear Global OUT NAK */
+		unsigned cgoutnak:1;
+		/** Power-On Programming Done */
+		unsigned pwronprgdone:1;
+		/** Reserved */
+		unsigned reserved:1;
+		/** Global Multi Count */
+		unsigned gmc:2;
+		/** Ignore Frame Number for ISOC EPs */
+		unsigned ifrmnum:1;
+		/** NAK on Babble */
+		unsigned nakonbble:1;
+		/** Enable Continue on BNA */
+		unsigned encontonbna:1;
+
+		unsigned reserved18_31:14;
+	} b;
+} dctl_data_t;
+
+/**
+ * This union represents the bit fields in the Device Status
+ * Register.  Read the register into the <i>d32</i> member then
+ * set/clear the bits using the <i>b</i>it elements.
+ */
+typedef union dsts_data {
+	/** raw register data */
+	uint32_t d32;
+	/** register bits */
+	struct {
+		/** Suspend Status */
+		unsigned suspsts:1;
+		/** Enumerated Speed */
+		unsigned enumspd:2;
+#define DWC_DSTS_ENUMSPD_HS_PHY_30MHZ_OR_60MHZ 0
+#define DWC_DSTS_ENUMSPD_FS_PHY_30MHZ_OR_60MHZ 1
+#define DWC_DSTS_ENUMSPD_LS_PHY_6MHZ		   2
+#define DWC_DSTS_ENUMSPD_FS_PHY_48MHZ		   3
+		/** Erratic Error */
+		unsigned errticerr:1;
+		unsigned reserved4_7:4;
+		/** Frame or Microframe Number of the received SOF */
+		unsigned soffn:14;
+		unsigned reserved22_31:10;
+	} b;
+} dsts_data_t;
+
+/**
+ * This union represents the bit fields in the Device IN EP Interrupt
+ * Register and the Device IN EP Common Mask Register.
+ *
+ * - Read the register into the <i>d32</i> member then set/clear the
+ *	 bits using the <i>b</i>it elements.
+ */
+typedef union diepint_data {
+	/** raw register data */
+	uint32_t d32;
+	/** register bits */
+	struct {
+		/** Transfer complete mask */
+		unsigned xfercompl:1;
+		/** Endpoint disable mask */
+		unsigned epdisabled:1;
+		/** AHB Error mask */
+		unsigned ahberr:1;
+		/** TimeOUT Handshake mask (non-ISOC EPs) */
+		unsigned timeout:1;
+		/** IN Token received with TxF Empty mask */
+		unsigned intktxfemp:1;
+		/** IN Token Received with EP mismatch mask */
+		unsigned intknepmis:1;
+		/** IN Endpoint NAK Effective mask */
+		unsigned inepnakeff:1;
+		/** Reserved */
+		unsigned emptyintr:1;
+
+		unsigned txfifoundrn:1;
+
+		/** BNA Interrupt mask */
+		unsigned bna:1;
+
+		unsigned reserved10_12:3;
+		/** BNA Interrupt mask */
+		unsigned nak:1;
+
+		unsigned reserved14_31:18;
+	} b;
+} diepint_data_t;
+
+/**
+ * This union represents the bit fields in the Device IN EP
+ * Common/Dedicated Interrupt Mask Register.
+ */
+typedef union diepint_data diepmsk_data_t;
+
+/**
+ * This union represents the bit fields in the Device OUT EP Interrupt
+ * Registerand Device OUT EP Common Interrupt Mask Register.
+ *
+ * - Read the register into the <i>d32</i> member then set/clear the
+ *	 bits using the <i>b</i>it elements.
+ */
+typedef union doepint_data {
+	/** raw register data */
+	uint32_t d32;
+	/** register bits */
+	struct {
+		/** Transfer complete */
+		unsigned xfercompl:1;
+		/** Endpoint disable  */
+		unsigned epdisabled:1;
+		/** AHB Error */
+		unsigned ahberr:1;
+		/** Setup Phase Done (contorl EPs) */
+		unsigned setup:1;
+		/** OUT Token Received when Endpoint Disabled */
+		unsigned outtknepdis:1;
+
+		unsigned stsphsercvd:1;
+		/** Back-to-Back SETUP Packets Received */
+		unsigned back2backsetup:1;
+
+		unsigned reserved7:1;
+		/** OUT packet Error */
+		unsigned outpkterr:1;
+		/** BNA Interrupt */
+		unsigned bna:1;
+
+		unsigned reserved10:1;
+		/** Packet Drop Status */
+		unsigned pktdrpsts:1;
+		/** Babble Interrupt */
+		unsigned babble:1;
+		/** NAK Interrupt */
+		unsigned nak:1;
+		/** NYET Interrupt */
+		unsigned nyet:1;
+		/** Bit indicating setup packet received */
+		unsigned sr:1;
+
+		unsigned reserved16_31:16;
+	} b;
+} doepint_data_t;
+
+/**
+ * This union represents the bit fields in the Device OUT EP
+ * Common/Dedicated Interrupt Mask Register.
+ */
+typedef union doepint_data doepmsk_data_t;
+
+/**
+ * This union represents the bit fields in the Device All EP Interrupt
+ * and Mask Registers.
+ * - Read the register into the <i>d32</i> member then set/clear the
+ *	 bits using the <i>b</i>it elements.
+ */
+typedef union daint_data {
+	/** raw register data */
+	uint32_t d32;
+	/** register bits */
+	struct {
+		/** IN Endpoint bits */
+		unsigned in:16;
+		/** OUT Endpoint bits */
+		unsigned out:16;
+	} ep;
+	struct {
+		/** IN Endpoint bits */
+		unsigned inep0:1;
+		unsigned inep1:1;
+		unsigned inep2:1;
+		unsigned inep3:1;
+		unsigned inep4:1;
+		unsigned inep5:1;
+		unsigned inep6:1;
+		unsigned inep7:1;
+		unsigned inep8:1;
+		unsigned inep9:1;
+		unsigned inep10:1;
+		unsigned inep11:1;
+		unsigned inep12:1;
+		unsigned inep13:1;
+		unsigned inep14:1;
+		unsigned inep15:1;
+		/** OUT Endpoint bits */
+		unsigned outep0:1;
+		unsigned outep1:1;
+		unsigned outep2:1;
+		unsigned outep3:1;
+		unsigned outep4:1;
+		unsigned outep5:1;
+		unsigned outep6:1;
+		unsigned outep7:1;
+		unsigned outep8:1;
+		unsigned outep9:1;
+		unsigned outep10:1;
+		unsigned outep11:1;
+		unsigned outep12:1;
+		unsigned outep13:1;
+		unsigned outep14:1;
+		unsigned outep15:1;
+	} b;
+} daint_data_t;
+
+/**
+ * This union represents the bit fields in the Device IN Token Queue
+ * Read Registers.
+ * - Read the register into the <i>d32</i> member.
+ * - READ-ONLY Register
+ */
+typedef union dtknq1_data {
+	/** raw register data */
+	uint32_t d32;
+	/** register bits */
+	struct {
+		/** In Token Queue Write Pointer */
+		unsigned intknwptr:5;
+		/** Reserved */
+		unsigned reserved05_06:2;
+		/** write pointer has wrapped. */
+		unsigned wrap_bit:1;
+		/** EP Numbers of IN Tokens 0 ... 4 */
+		unsigned epnums0_5:24;
+	} b;
+} dtknq1_data_t;
+
+/**
+ * This union represents Threshold control Register
+ * - Read and write the register into the <i>d32</i> member.
+ * - READ-WRITABLE Register
+ */
+typedef union dthrctl_data {
+	/** raw register data */
+	uint32_t d32;
+	/** register bits */
+	struct {
+		/** non ISO Tx Thr. Enable */
+		unsigned non_iso_thr_en:1;
+		/** ISO Tx Thr. Enable */
+		unsigned iso_thr_en:1;
+		/** Tx Thr. Length */
+		unsigned tx_thr_len:9;
+		/** AHB Threshold ratio */
+		unsigned ahb_thr_ratio:2;
+		/** Reserved */
+		unsigned reserved13_15:3;
+		/** Rx Thr. Enable */
+		unsigned rx_thr_en:1;
+		/** Rx Thr. Length */
+		unsigned rx_thr_len:9;
+		unsigned reserved26:1;
+		/** Arbiter Parking Enable*/
+		unsigned arbprken:1;
+		/** Reserved */
+		unsigned reserved28_31:4;
+	} b;
+} dthrctl_data_t;
+
+/**
+ * Device Logical IN Endpoint-Specific Registers. <i>Offsets
+ * 900h-AFCh</i>
+ *
+ * There will be one set of endpoint registers per logical endpoint
+ * implemented.
+ *
+ * <i>These registers are visible only in Device mode and must not be
+ * accessed in Host mode, as the results are unknown.</i>
+ */
+typedef struct dwc_otg_dev_in_ep_regs {
+	/** Device IN Endpoint Control Register. <i>Offset:900h +
+	 * (ep_num * 20h) + 00h</i> */
+	volatile uint32_t diepctl;
+	/** Reserved. <i>Offset:900h + (ep_num * 20h) + 04h</i> */
+	uint32_t reserved04;
+	/** Device IN Endpoint Interrupt Register. <i>Offset:900h +
+	 * (ep_num * 20h) + 08h</i> */
+	volatile uint32_t diepint;
+	/** Reserved. <i>Offset:900h + (ep_num * 20h) + 0Ch</i> */
+	uint32_t reserved0C;
+	/** Device IN Endpoint Transfer Size
+	 * Register. <i>Offset:900h + (ep_num * 20h) + 10h</i> */
+	volatile uint32_t dieptsiz;
+	/** Device IN Endpoint DMA Address Register. <i>Offset:900h +
+	 * (ep_num * 20h) + 14h</i> */
+	volatile uint32_t diepdma;
+	/** Device IN Endpoint Transmit FIFO Status Register. <i>Offset:900h +
+	 * (ep_num * 20h) + 18h</i> */
+	volatile uint32_t dtxfsts;
+	/** Device IN Endpoint DMA Buffer Register. <i>Offset:900h +
+	 * (ep_num * 20h) + 1Ch</i> */
+	volatile uint32_t diepdmab;
+} dwc_otg_dev_in_ep_regs_t;
+
+/**
+ * Device Logical OUT Endpoint-Specific Registers. <i>Offsets:
+ * B00h-CFCh</i>
+ *
+ * There will be one set of endpoint registers per logical endpoint
+ * implemented.
+ *
+ * <i>These registers are visible only in Device mode and must not be
+ * accessed in Host mode, as the results are unknown.</i>
+ */
+typedef struct dwc_otg_dev_out_ep_regs {
+	/** Device OUT Endpoint Control Register. <i>Offset:B00h +
+	 * (ep_num * 20h) + 00h</i> */
+	volatile uint32_t doepctl;
+	/** Reserved. <i>Offset:B00h + (ep_num * 20h) + 04h</i> */
+	uint32_t reserved04;
+	/** Device OUT Endpoint Interrupt Register. <i>Offset:B00h +
+	 * (ep_num * 20h) + 08h</i> */
+	volatile uint32_t doepint;
+	/** Reserved. <i>Offset:B00h + (ep_num * 20h) + 0Ch</i> */
+	uint32_t reserved0C;
+	/** Device OUT Endpoint Transfer Size Register. <i>Offset:
+	 * B00h + (ep_num * 20h) + 10h</i> */
+	volatile uint32_t doeptsiz;
+	/** Device OUT Endpoint DMA Address Register. <i>Offset:B00h
+	 * + (ep_num * 20h) + 14h</i> */
+	volatile uint32_t doepdma;
+	/** Reserved. <i>Offset:B00h + 	 * (ep_num * 20h) + 18h</i> */
+	uint32_t unused;
+	/** Device OUT Endpoint DMA Buffer Register. <i>Offset:B00h
+	 * + (ep_num * 20h) + 1Ch</i> */
+	uint32_t doepdmab;
+} dwc_otg_dev_out_ep_regs_t;
+
+/**
+ * This union represents the bit fields in the Device EP Control
+ * Register.  Read the register into the <i>d32</i> member then
+ * set/clear the bits using the <i>b</i>it elements.
+ */
+typedef union depctl_data {
+	/** raw register data */
+	uint32_t d32;
+	/** register bits */
+	struct {
+		/** Maximum Packet Size
+		 * IN/OUT EPn
+		 * IN/OUT EP0 - 2 bits
+		 *	 2'b00: 64 Bytes
+		 *	 2'b01: 32
+		 *	 2'b10: 16
+		 *	 2'b11: 8 */
+		unsigned mps:11;
+#define DWC_DEP0CTL_MPS_64	 0
+#define DWC_DEP0CTL_MPS_32	 1
+#define DWC_DEP0CTL_MPS_16	 2
+#define DWC_DEP0CTL_MPS_8	 3
+
+		/** Next Endpoint
+		 * IN EPn/IN EP0
+		 * OUT EPn/OUT EP0 - reserved */
+		unsigned nextep:4;
+
+		/** USB Active Endpoint */
+		unsigned usbactep:1;
+
+		/** Endpoint DPID (INTR/Bulk IN and OUT endpoints)
+		 * This field contains the PID of the packet going to
+		 * be received or transmitted on this endpoint. The
+		 * application should program the PID of the first
+		 * packet going to be received or transmitted on this
+		 * endpoint , after the endpoint is
+		 * activated. Application use the SetD1PID and
+		 * SetD0PID fields of this register to program either
+		 * D0 or D1 PID.
+		 *
+		 * The encoding for this field is
+		 *	 - 0: D0
+		 *	 - 1: D1
+		 */
+		unsigned dpid:1;
+
+		/** NAK Status */
+		unsigned naksts:1;
+
+		/** Endpoint Type
+		 *	2'b00: Control
+		 *	2'b01: Isochronous
+		 *	2'b10: Bulk
+		 *	2'b11: Interrupt */
+		unsigned eptype:2;
+
+		/** Snoop Mode
+		 * OUT EPn/OUT EP0
+		 * IN EPn/IN EP0 - reserved */
+		unsigned snp:1;
+
+		/** Stall Handshake */
+		unsigned stall:1;
+
+		/** Tx Fifo Number
+		 * IN EPn/IN EP0
+		 * OUT EPn/OUT EP0 - reserved */
+		unsigned txfnum:4;
+
+		/** Clear NAK */
+		unsigned cnak:1;
+		/** Set NAK */
+		unsigned snak:1;
+		/** Set DATA0 PID (INTR/Bulk IN and OUT endpoints)
+		 * Writing to this field sets the Endpoint DPID (DPID)
+		 * field in this register to DATA0. Set Even
+		 * (micro)frame (SetEvenFr) (ISO IN and OUT Endpoints)
+		 * Writing to this field sets the Even/Odd
+		 * (micro)frame (EO_FrNum) field to even (micro)
+		 * frame.
+		 */
+		unsigned setd0pid:1;
+		/** Set DATA1 PID (INTR/Bulk IN and OUT endpoints)
+		 * Writing to this field sets the Endpoint DPID (DPID)
+		 * field in this register to DATA1 Set Odd
+		 * (micro)frame (SetOddFr) (ISO IN and OUT Endpoints)
+		 * Writing to this field sets the Even/Odd
+		 * (micro)frame (EO_FrNum) field to odd (micro) frame.
+		 */
+		unsigned setd1pid:1;
+
+		/** Endpoint Disable */
+		unsigned epdis:1;
+		/** Endpoint Enable */
+		unsigned epena:1;
+	} b;
+} depctl_data_t;
+
+/**
+ * This union represents the bit fields in the Device EP Transfer
+ * Size Register.  Read the register into the <i>d32</i> member then
+ * set/clear the bits using the <i>b</i>it elements.
+ */
+typedef union deptsiz_data {
+		/** raw register data */
+	uint32_t d32;
+		/** register bits */
+	struct {
+		/** Transfer size */
+		unsigned xfersize:19;
+/** Max packet count for EP (pow(2,10)-1) */
+#define MAX_PKT_CNT 1023
+		/** Packet Count */
+		unsigned pktcnt:10;
+		/** Multi Count - Periodic IN endpoints */
+		unsigned mc:2;
+		unsigned reserved:1;
+	} b;
+} deptsiz_data_t;
+
+/**
+ * This union represents the bit fields in the Device EP 0 Transfer
+ * Size Register.  Read the register into the <i>d32</i> member then
+ * set/clear the bits using the <i>b</i>it elements.
+ */
+typedef union deptsiz0_data {
+		/** raw register data */
+	uint32_t d32;
+		/** register bits */
+	struct {
+		/** Transfer size */
+		unsigned xfersize:7;
+				/** Reserved */
+		unsigned reserved7_18:12;
+		/** Packet Count */
+		unsigned pktcnt:2;
+				/** Reserved */
+		unsigned reserved21_28:8;
+				/**Setup Packet Count (DOEPTSIZ0 Only) */
+		unsigned supcnt:2;
+		unsigned reserved31;
+	} b;
+} deptsiz0_data_t;
+
+/////////////////////////////////////////////////
+// DMA Descriptor Specific Structures
+//
+
+/** Buffer status definitions */
+
+#define BS_HOST_READY	0x0
+#define BS_DMA_BUSY		0x1
+#define BS_DMA_DONE		0x2
+#define BS_HOST_BUSY	0x3
+
+/** Receive/Transmit status definitions */
+
+#define RTS_SUCCESS		0x0
+#define RTS_BUFFLUSH	0x1
+#define RTS_RESERVED	0x2
+#define RTS_BUFERR		0x3
+
+/**
+ * This union represents the bit fields in the DMA Descriptor
+ * status quadlet. Read the quadlet into the <i>d32</i> member then
+ * set/clear the bits using the <i>b</i>it, <i>b_iso_out</i> and
+ * <i>b_iso_in</i> elements.
+ */
+typedef union dev_dma_desc_sts {
+		/** raw register data */
+	uint32_t d32;
+		/** quadlet bits */
+	struct {
+		/** Received number of bytes */
+		unsigned bytes:16;
+		/** NAK bit - only for OUT EPs */
+		unsigned nak:1;
+		unsigned reserved17_22:6;
+		/** Multiple Transfer - only for OUT EPs */
+		unsigned mtrf:1;
+		/** Setup Packet received - only for OUT EPs */
+		unsigned sr:1;
+		/** Interrupt On Complete */
+		unsigned ioc:1;
+		/** Short Packet */
+		unsigned sp:1;
+		/** Last */
+		unsigned l:1;
+		/** Receive Status */
+		unsigned sts:2;
+		/** Buffer Status */
+		unsigned bs:2;
+	} b;
+
+//#ifdef DWC_EN_ISOC
+		/** iso out quadlet bits */
+	struct {
+		/** Received number of bytes */
+		unsigned rxbytes:11;
+
+		unsigned reserved11:1;
+		/** Frame Number */
+		unsigned framenum:11;
+		/** Received ISO Data PID */
+		unsigned pid:2;
+		/** Interrupt On Complete */
+		unsigned ioc:1;
+		/** Short Packet */
+		unsigned sp:1;
+		/** Last */
+		unsigned l:1;
+		/** Receive Status */
+		unsigned rxsts:2;
+		/** Buffer Status */
+		unsigned bs:2;
+	} b_iso_out;
+
+		/** iso in quadlet bits */
+	struct {
+		/** Transmited number of bytes */
+		unsigned txbytes:12;
+		/** Frame Number */
+		unsigned framenum:11;
+		/** Transmited ISO Data PID */
+		unsigned pid:2;
+		/** Interrupt On Complete */
+		unsigned ioc:1;
+		/** Short Packet */
+		unsigned sp:1;
+		/** Last */
+		unsigned l:1;
+		/** Transmit Status */
+		unsigned txsts:2;
+		/** Buffer Status */
+		unsigned bs:2;
+	} b_iso_in;
+//#endif                                /* DWC_EN_ISOC */
+} dev_dma_desc_sts_t;
+
+/**
+ * DMA Descriptor structure
+ *
+ * DMA Descriptor structure contains two quadlets:
+ * Status quadlet and Data buffer pointer.
+ */
+typedef struct dwc_otg_dev_dma_desc {
+	/** DMA Descriptor status quadlet */
+	dev_dma_desc_sts_t status;
+	/** DMA Descriptor data buffer pointer */
+	uint32_t buf;
+} dwc_otg_dev_dma_desc_t;
+
+/**
+ * The dwc_otg_dev_if structure contains information needed to manage
+ * the DWC_otg controller acting in device mode. It represents the
+ * programming view of the device-specific aspects of the controller.
+ */
+typedef struct dwc_otg_dev_if {
+	/** Pointer to device Global registers.
+	 * Device Global Registers starting at offset 800h
+	 */
+	dwc_otg_device_global_regs_t *dev_global_regs;
+#define DWC_DEV_GLOBAL_REG_OFFSET 0x800
+
+	/**
+	 * Device Logical IN Endpoint-Specific Registers 900h-AFCh
+	 */
+	dwc_otg_dev_in_ep_regs_t *in_ep_regs[MAX_EPS_CHANNELS];
+#define DWC_DEV_IN_EP_REG_OFFSET 0x900
+#define DWC_EP_REG_OFFSET 0x20
+
+	/** Device Logical OUT Endpoint-Specific Registers B00h-CFCh */
+	dwc_otg_dev_out_ep_regs_t *out_ep_regs[MAX_EPS_CHANNELS];
+#define DWC_DEV_OUT_EP_REG_OFFSET 0xB00
+
+	/* Device configuration information */
+	uint8_t speed;				 /**< Device Speed	0: Unknown, 1: LS, 2:FS, 3: HS */
+	uint8_t num_in_eps;		 /**< Number # of Tx EP range: 0-15 exept ep0 */
+	uint8_t num_out_eps;		 /**< Number # of Rx EP range: 0-15 exept ep 0*/
+
+	/** Size of periodic FIFOs (Bytes) */
+	uint16_t perio_tx_fifo_size[MAX_PERIO_FIFOS];
+
+	/** Size of Tx FIFOs (Bytes) */
+	uint16_t tx_fifo_size[MAX_TX_FIFOS];
+
+	/** Thresholding enable flags and length varaiables **/
+	uint16_t rx_thr_en;
+	uint16_t iso_tx_thr_en;
+	uint16_t non_iso_tx_thr_en;
+
+	uint16_t rx_thr_length;
+	uint16_t tx_thr_length;
+
+	/**
+	 * Pointers to the DMA Descriptors for EP0 Control
+	 * transfers (virtual and physical)
+	 */
+
+	/** 2 descriptors for SETUP packets */
+	dwc_dma_t dma_setup_desc_addr[2];
+	dwc_otg_dev_dma_desc_t *setup_desc_addr[2];
+
+	/** Pointer to Descriptor with latest SETUP packet */
+	dwc_otg_dev_dma_desc_t *psetup;
+
+	/** Index of current SETUP handler descriptor */
+	uint32_t setup_desc_index;
+
+	/** Descriptor for Data In or Status In phases */
+	dwc_dma_t dma_in_desc_addr;
+	dwc_otg_dev_dma_desc_t *in_desc_addr;
+
+	/** Descriptor for Data Out or Status Out phases */
+	dwc_dma_t dma_out_desc_addr;
+	dwc_otg_dev_dma_desc_t *out_desc_addr;
+
+	/** Setup Packet Detected - if set clear NAK when queueing */
+	uint32_t spd;
+	/** Isoc ep pointer on which incomplete happens */
+	void *isoc_ep;
+
+} dwc_otg_dev_if_t;
+
+/////////////////////////////////////////////////
+// Host Mode Register Structures
+//
+/**
+ * The Host Global Registers structure defines the size and relative
+ * field offsets for the Host Mode Global Registers.  Host Global
+ * Registers offsets 400h-7FFh.
+*/
+typedef struct dwc_otg_host_global_regs {
+	/** Host Configuration Register.   <i>Offset: 400h</i> */
+	volatile uint32_t hcfg;
+	/** Host Frame Interval Register.	<i>Offset: 404h</i> */
+	volatile uint32_t hfir;
+	/** Host Frame Number / Frame Remaining Register. <i>Offset: 408h</i> */
+	volatile uint32_t hfnum;
+	/** Reserved.	<i>Offset: 40Ch</i> */
+	uint32_t reserved40C;
+	/** Host Periodic Transmit FIFO/ Queue Status Register. <i>Offset: 410h</i> */
+	volatile uint32_t hptxsts;
+	/** Host All Channels Interrupt Register. <i>Offset: 414h</i> */
+	volatile uint32_t haint;
+	/** Host All Channels Interrupt Mask Register. <i>Offset: 418h</i> */
+	volatile uint32_t haintmsk;
+	/** Host Frame List Base Address Register . <i>Offset: 41Ch</i> */
+	volatile uint32_t hflbaddr;
+} dwc_otg_host_global_regs_t;
+
+/**
+ * This union represents the bit fields in the Host Configuration Register.
+ * Read the register into the <i>d32</i> member then set/clear the bits using
+ * the <i>b</i>it elements. Write the <i>d32</i> member to the hcfg register.
+ */
+typedef union hcfg_data {
+	/** raw register data */
+	uint32_t d32;
+
+	/** register bits */
+	struct {
+		/** FS/LS Phy Clock Select */
+		unsigned fslspclksel:2;
+#define DWC_HCFG_30_60_MHZ 0
+#define DWC_HCFG_48_MHZ	   1
+#define DWC_HCFG_6_MHZ	   2
+
+		/** FS/LS Only Support */
+		unsigned fslssupp:1;
+		unsigned reserved3_6:4;
+		/** Enable 32-KHz Suspend Mode */
+		unsigned ena32khzs:1;
+		/** Resume Validation Periiod */
+		unsigned resvalid:8;
+		unsigned reserved16_22:7;
+		/** Enable Scatter/gather DMA in Host mode */
+		unsigned descdma:1;
+		/** Frame List Entries */
+		unsigned frlisten:2;
+		/** Enable Periodic Scheduling */
+		unsigned perschedena:1;
+		unsigned reserved27_30:4;
+		unsigned modechtimen:1;
+	} b;
+} hcfg_data_t;
+
+/**
+ * This union represents the bit fields in the Host Frame Remaing/Number
+ * Register.
+ */
+typedef union hfir_data {
+	/** raw register data */
+	uint32_t d32;
+
+	/** register bits */
+	struct {
+		unsigned frint:16;
+		unsigned hfirrldctrl:1;
+		unsigned reserved:15;
+	} b;
+} hfir_data_t;
+
+/**
+ * This union represents the bit fields in the Host Frame Remaing/Number
+ * Register.
+ */
+typedef union hfnum_data {
+	/** raw register data */
+	uint32_t d32;
+
+	/** register bits */
+	struct {
+		unsigned frnum:16;
+#define DWC_HFNUM_MAX_FRNUM 0x3FFF
+		unsigned frrem:16;
+	} b;
+} hfnum_data_t;
+
+typedef union hptxsts_data {
+	/** raw register data */
+	uint32_t d32;
+
+	/** register bits */
+	struct {
+		unsigned ptxfspcavail:16;
+		unsigned ptxqspcavail:8;
+		/** Top of the Periodic Transmit Request Queue
+		 *	- bit 24 - Terminate (last entry for the selected channel)
+		 *	- bits 26:25 - Token Type
+		 *	  - 2'b00 - Zero length
+		 *	  - 2'b01 - Ping
+		 *	  - 2'b10 - Disable
+		 *	- bits 30:27 - Channel Number
+		 *	- bit 31 - Odd/even microframe
+		 */
+		unsigned ptxqtop_terminate:1;
+		unsigned ptxqtop_token:2;
+		unsigned ptxqtop_chnum:4;
+		unsigned ptxqtop_odd:1;
+	} b;
+} hptxsts_data_t;
+
+/**
+ * This union represents the bit fields in the Host Port Control and Status
+ * Register. Read the register into the <i>d32</i> member then set/clear the
+ * bits using the <i>b</i>it elements. Write the <i>d32</i> member to the
+ * hprt0 register.
+ */
+typedef union hprt0_data {
+	/** raw register data */
+	uint32_t d32;
+	/** register bits */
+	struct {
+		unsigned prtconnsts:1;
+		unsigned prtconndet:1;
+		unsigned prtena:1;
+		unsigned prtenchng:1;
+		unsigned prtovrcurract:1;
+		unsigned prtovrcurrchng:1;
+		unsigned prtres:1;
+		unsigned prtsusp:1;
+		unsigned prtrst:1;
+		unsigned reserved9:1;
+		unsigned prtlnsts:2;
+		unsigned prtpwr:1;
+		unsigned prttstctl:4;
+		unsigned prtspd:2;
+#define DWC_HPRT0_PRTSPD_HIGH_SPEED 0
+#define DWC_HPRT0_PRTSPD_FULL_SPEED 1
+#define DWC_HPRT0_PRTSPD_LOW_SPEED	2
+		unsigned reserved19_31:13;
+	} b;
+} hprt0_data_t;
+
+/**
+ * This union represents the bit fields in the Host All Interrupt
+ * Register.
+ */
+typedef union haint_data {
+	/** raw register data */
+	uint32_t d32;
+	/** register bits */
+	struct {
+		unsigned ch0:1;
+		unsigned ch1:1;
+		unsigned ch2:1;
+		unsigned ch3:1;
+		unsigned ch4:1;
+		unsigned ch5:1;
+		unsigned ch6:1;
+		unsigned ch7:1;
+		unsigned ch8:1;
+		unsigned ch9:1;
+		unsigned ch10:1;
+		unsigned ch11:1;
+		unsigned ch12:1;
+		unsigned ch13:1;
+		unsigned ch14:1;
+		unsigned ch15:1;
+		unsigned reserved:16;
+	} b;
+
+	struct {
+		unsigned chint:16;
+		unsigned reserved:16;
+	} b2;
+} haint_data_t;
+
+/**
+ * This union represents the bit fields in the Host All Interrupt
+ * Register.
+ */
+typedef union haintmsk_data {
+	/** raw register data */
+	uint32_t d32;
+	/** register bits */
+	struct {
+		unsigned ch0:1;
+		unsigned ch1:1;
+		unsigned ch2:1;
+		unsigned ch3:1;
+		unsigned ch4:1;
+		unsigned ch5:1;
+		unsigned ch6:1;
+		unsigned ch7:1;
+		unsigned ch8:1;
+		unsigned ch9:1;
+		unsigned ch10:1;
+		unsigned ch11:1;
+		unsigned ch12:1;
+		unsigned ch13:1;
+		unsigned ch14:1;
+		unsigned ch15:1;
+		unsigned reserved:16;
+	} b;
+
+	struct {
+		unsigned chint:16;
+		unsigned reserved:16;
+	} b2;
+} haintmsk_data_t;
+
+/**
+ * Host Channel Specific Registers. <i>500h-5FCh</i>
+ */
+typedef struct dwc_otg_hc_regs {
+	/** Host Channel 0 Characteristic Register. <i>Offset: 500h + (chan_num * 20h) + 00h</i> */
+	volatile uint32_t hcchar;
+	/** Host Channel 0 Split Control Register. <i>Offset: 500h + (chan_num * 20h) + 04h</i> */
+	volatile uint32_t hcsplt;
+	/** Host Channel 0 Interrupt Register. <i>Offset: 500h + (chan_num * 20h) + 08h</i> */
+	volatile uint32_t hcint;
+	/** Host Channel 0 Interrupt Mask Register. <i>Offset: 500h + (chan_num * 20h) + 0Ch</i> */
+	volatile uint32_t hcintmsk;
+	/** Host Channel 0 Transfer Size Register. <i>Offset: 500h + (chan_num * 20h) + 10h</i> */
+	volatile uint32_t hctsiz;
+	/** Host Channel 0 DMA Address Register. <i>Offset: 500h + (chan_num * 20h) + 14h</i> */
+	volatile uint32_t hcdma;
+	volatile uint32_t reserved;
+	/** Host Channel 0 DMA Buffer Address Register. <i>Offset: 500h + (chan_num * 20h) + 1Ch</i> */
+	volatile uint32_t hcdmab;
+} dwc_otg_hc_regs_t;
+
+/**
+ * This union represents the bit fields in the Host Channel Characteristics
+ * Register. Read the register into the <i>d32</i> member then set/clear the
+ * bits using the <i>b</i>it elements. Write the <i>d32</i> member to the
+ * hcchar register.
+ */
+typedef union hcchar_data {
+	/** raw register data */
+	uint32_t d32;
+
+	/** register bits */
+	struct {
+		/** Maximum packet size in bytes */
+		unsigned mps:11;
+
+		/** Endpoint number */
+		unsigned epnum:4;
+
+		/** 0: OUT, 1: IN */
+		unsigned epdir:1;
+
+		unsigned reserved:1;
+
+		/** 0: Full/high speed device, 1: Low speed device */
+		unsigned lspddev:1;
+
+		/** 0: Control, 1: Isoc, 2: Bulk, 3: Intr */
+		unsigned eptype:2;
+
+		/** Packets per frame for periodic transfers. 0 is reserved. */
+		unsigned multicnt:2;
+
+		/** Device address */
+		unsigned devaddr:7;
+
+		/**
+		 * Frame to transmit periodic transaction.
+		 * 0: even, 1: odd
+		 */
+		unsigned oddfrm:1;
+
+		/** Channel disable */
+		unsigned chdis:1;
+
+		/** Channel enable */
+		unsigned chen:1;
+	} b;
+} hcchar_data_t;
+
+typedef union hcsplt_data {
+	/** raw register data */
+	uint32_t d32;
+
+	/** register bits */
+	struct {
+		/** Port Address */
+		unsigned prtaddr:7;
+
+		/** Hub Address */
+		unsigned hubaddr:7;
+
+		/** Transaction Position */
+		unsigned xactpos:2;
+#define DWC_HCSPLIT_XACTPOS_MID 0
+#define DWC_HCSPLIT_XACTPOS_END 1
+#define DWC_HCSPLIT_XACTPOS_BEGIN 2
+#define DWC_HCSPLIT_XACTPOS_ALL 3
+
+		/** Do Complete Split */
+		unsigned compsplt:1;
+
+		/** Reserved */
+		unsigned reserved:14;
+
+		/** Split Enble */
+		unsigned spltena:1;
+	} b;
+} hcsplt_data_t;
+
+/**
+ * This union represents the bit fields in the Host All Interrupt
+ * Register.
+ */
+typedef union hcint_data {
+	/** raw register data */
+	uint32_t d32;
+	/** register bits */
+	struct {
+		/** Transfer Complete */
+		unsigned xfercomp:1;
+		/** Channel Halted */
+		unsigned chhltd:1;
+		/** AHB Error */
+		unsigned ahberr:1;
+		/** STALL Response Received */
+		unsigned stall:1;
+		/** NAK Response Received */
+		unsigned nak:1;
+		/** ACK Response Received */
+		unsigned ack:1;
+		/** NYET Response Received */
+		unsigned nyet:1;
+		/** Transaction Err */
+		unsigned xacterr:1;
+		/** Babble Error */
+		unsigned bblerr:1;
+		/** Frame Overrun */
+		unsigned frmovrun:1;
+		/** Data Toggle Error */
+		unsigned datatglerr:1;
+		/** Buffer Not Available (only for DDMA mode) */
+		unsigned bna:1;
+		/** Exessive transaction error (only for DDMA mode) */
+		unsigned xcs_xact:1;
+		/** Frame List Rollover interrupt */
+		unsigned frm_list_roll:1;
+		/** Reserved */
+		unsigned reserved14_31:18;
+	} b;
+} hcint_data_t;
+
+/**
+ * This union represents the bit fields in the Host Channel Interrupt Mask
+ * Register. Read the register into the <i>d32</i> member then set/clear the
+ * bits using the <i>b</i>it elements. Write the <i>d32</i> member to the
+ * hcintmsk register.
+ */
+typedef union hcintmsk_data {
+	/** raw register data */
+	uint32_t d32;
+
+	/** register bits */
+	struct {
+		unsigned xfercompl:1;
+		unsigned chhltd:1;
+		unsigned ahberr:1;
+		unsigned stall:1;
+		unsigned nak:1;
+		unsigned ack:1;
+		unsigned nyet:1;
+		unsigned xacterr:1;
+		unsigned bblerr:1;
+		unsigned frmovrun:1;
+		unsigned datatglerr:1;
+		unsigned bna:1;
+		unsigned xcs_xact:1;
+		unsigned frm_list_roll:1;
+		unsigned reserved14_31:18;
+	} b;
+} hcintmsk_data_t;
+
+/**
+ * This union represents the bit fields in the Host Channel Transfer Size
+ * Register. Read the register into the <i>d32</i> member then set/clear the
+ * bits using the <i>b</i>it elements. Write the <i>d32</i> member to the
+ * hcchar register.
+ */
+
+typedef union hctsiz_data {
+	/** raw register data */
+	uint32_t d32;
+
+	/** register bits */
+	struct {
+		/** Total transfer size in bytes */
+		unsigned xfersize:19;
+
+		/** Data packets to transfer */
+		unsigned pktcnt:10;
+
+		/**
+		 * Packet ID for next data packet
+		 * 0: DATA0
+		 * 1: DATA2
+		 * 2: DATA1
+		 * 3: MDATA (non-Control), SETUP (Control)
+		 */
+		unsigned pid:2;
+#define DWC_HCTSIZ_DATA0 0
+#define DWC_HCTSIZ_DATA1 2
+#define DWC_HCTSIZ_DATA2 1
+#define DWC_HCTSIZ_MDATA 3
+#define DWC_HCTSIZ_SETUP 3
+
+		/** Do PING protocol when 1 */
+		unsigned dopng:1;
+	} b;
+
+	/** register bits */
+	struct {
+		/** Scheduling information */
+		unsigned schinfo:8;
+
+		/** Number of transfer descriptors.
+		 * Max value:
+		 * 64 in general,
+		 * 256 only for HS isochronous endpoint.
+		 */
+		unsigned ntd:8;
+
+		/** Data packets to transfer */
+		unsigned reserved16_28:13;
+
+		/**
+		 * Packet ID for next data packet
+		 * 0: DATA0
+		 * 1: DATA2
+		 * 2: DATA1
+		 * 3: MDATA (non-Control)
+		 */
+		unsigned pid:2;
+
+		/** Do PING protocol when 1 */
+		unsigned dopng:1;
+	} b_ddma;
+} hctsiz_data_t;
+
+/**
+ * This union represents the bit fields in the Host DMA Address
+ * Register used in Descriptor DMA mode.
+ */
+typedef union hcdma_data {
+	/** raw register data */
+	uint32_t d32;
+	/** register bits */
+	struct {
+		unsigned reserved0_2:3;
+		/** Current Transfer Descriptor. Not used for ISOC */
+		unsigned ctd:8;
+		/** Start Address of Descriptor List */
+		unsigned dma_addr:21;
+	} b;
+} hcdma_data_t;
+
+/**
+ * This union represents the bit fields in the DMA Descriptor
+ * status quadlet for host mode. Read the quadlet into the <i>d32</i> member then
+ * set/clear the bits using the <i>b</i>it elements.
+ */
+typedef union host_dma_desc_sts {
+	/** raw register data */
+	uint32_t d32;
+	/** quadlet bits */
+
+	/* for non-isochronous  */
+	struct {
+		/** Number of bytes */
+		unsigned n_bytes:17;
+		/** QTD offset to jump when Short Packet received - only for IN EPs */
+		unsigned qtd_offset:6;
+		/**
+		 * Set to request the core to jump to alternate QTD if
+		 * Short Packet received - only for IN EPs
+		 */
+		unsigned a_qtd:1;
+		 /**
+		  * Setup Packet bit. When set indicates that buffer contains
+		  * setup packet.
+		  */
+		unsigned sup:1;
+		/** Interrupt On Complete */
+		unsigned ioc:1;
+		/** End of List */
+		unsigned eol:1;
+		unsigned reserved27:1;
+		/** Rx/Tx Status */
+		unsigned sts:2;
+#define DMA_DESC_STS_PKTERR	1
+		unsigned reserved30:1;
+		/** Active Bit */
+		unsigned a:1;
+	} b;
+	/* for isochronous */
+	struct {
+		/** Number of bytes */
+		unsigned n_bytes:12;
+		unsigned reserved12_24:13;
+		/** Interrupt On Complete */
+		unsigned ioc:1;
+		unsigned reserved26_27:2;
+		/** Rx/Tx Status */
+		unsigned sts:2;
+		unsigned reserved30:1;
+		/** Active Bit */
+		unsigned a:1;
+	} b_isoc;
+} host_dma_desc_sts_t;
+
+#define	MAX_DMA_DESC_SIZE		131071
+#define MAX_DMA_DESC_NUM_GENERIC	64
+#define MAX_DMA_DESC_NUM_HS_ISOC	256
+#define MAX_FRLIST_EN_NUM		64
+/**
+ * Host-mode DMA Descriptor structure
+ *
+ * DMA Descriptor structure contains two quadlets:
+ * Status quadlet and Data buffer pointer.
+ */
+typedef struct dwc_otg_host_dma_desc {
+	/** DMA Descriptor status quadlet */
+	host_dma_desc_sts_t status;
+	/** DMA Descriptor data buffer pointer */
+	uint32_t buf;
+} dwc_otg_host_dma_desc_t;
+
+/** OTG Host Interface Structure.
+ *
+ * The OTG Host Interface Structure structure contains information
+ * needed to manage the DWC_otg controller acting in host mode. It
+ * represents the programming view of the host-specific aspects of the
+ * controller.
+ */
+typedef struct dwc_otg_host_if {
+	/** Host Global Registers starting at offset 400h.*/
+	dwc_otg_host_global_regs_t *host_global_regs;
+#define DWC_OTG_HOST_GLOBAL_REG_OFFSET 0x400
+
+	/** Host Port 0 Control and Status Register */
+	volatile uint32_t *hprt0;
+#define DWC_OTG_HOST_PORT_REGS_OFFSET 0x440
+
+	/** Host Channel Specific Registers at offsets 500h-5FCh. */
+	dwc_otg_hc_regs_t *hc_regs[MAX_EPS_CHANNELS];
+#define DWC_OTG_HOST_CHAN_REGS_OFFSET 0x500
+#define DWC_OTG_CHAN_REGS_OFFSET 0x20
+
+	/* Host configuration information */
+	/** Number of Host Channels (range: 1-16) */
+	uint8_t num_host_channels;
+	/** Periodic EPs supported (0: no, 1: yes) */
+	uint8_t perio_eps_supported;
+	/** Periodic Tx FIFO Size (Only 1 host periodic Tx FIFO) */
+	uint16_t perio_tx_fifo_size;
+
+} dwc_otg_host_if_t;
+
+/**
+ * This union represents the bit fields in the Power and Clock Gating Control
+ * Register. Read the register into the <i>d32</i> member then set/clear the
+ * bits using the <i>b</i>it elements.
+ */
+typedef union pcgcctl_data {
+	/** raw register data */
+	uint32_t d32;
+
+	/** register bits */
+	struct {
+		/** Stop Pclk */
+		unsigned stoppclk:1;
+		/** Gate Hclk */
+		unsigned gatehclk:1;
+		/** Power Clamp */
+		unsigned pwrclmp:1;
+		/** Reset Power Down Modules */
+		unsigned rstpdwnmodule:1;
+		/** Reserved */
+		unsigned reserved:1;
+		/** Enable Sleep Clock Gating (Enbl_L1Gating) */
+		unsigned enbl_sleep_gating:1;
+		/** PHY In Sleep (PhySleep) */
+		unsigned phy_in_sleep:1;
+		/** Deep Sleep*/
+		unsigned deep_sleep:1;
+		unsigned resetaftsusp:1;
+		unsigned restoremode:1;
+		unsigned enbl_extnd_hiber:1;
+		unsigned extnd_hiber_pwrclmp:1;
+		unsigned extnd_hiber_switch:1;
+		unsigned ess_reg_restored:1;
+		unsigned prt_clk_sel:2;
+		unsigned port_power:1;
+		unsigned max_xcvrselect:2;
+		unsigned max_termsel:1;
+		unsigned mac_dev_addr:7;
+		unsigned p2hd_dev_enum_spd:2;
+		unsigned p2hd_prt_spd:2;
+		unsigned if_dev_mode:1;
+	} b;
+} pcgcctl_data_t;
+
+/**
+ * This union represents the bit fields in the Global Data FIFO Software
+ * Configuration Register. Read the register into the <i>d32</i> member then
+ * set/clear the bits using the <i>b</i>it elements.
+ */
+typedef union gdfifocfg_data {
+	/* raw register data */
+	uint32_t d32;
+	/** register bits */
+	struct {
+		/** OTG Data FIFO depth */
+		unsigned gdfifocfg:16;
+		/** Start address of EP info controller */
+		unsigned epinfobase:16;
+	} b;
+} gdfifocfg_data_t;
+
+/**
+ * This union represents the bit fields in the Global Power Down Register
+ * Register. Read the register into the <i>d32</i> member then set/clear the
+ * bits using the <i>b</i>it elements.
+ */
+typedef union gpwrdn_data {
+	/* raw register data */
+	uint32_t d32;
+
+	/** register bits */
+	struct {
+		/** PMU Interrupt Select */
+		unsigned pmuintsel:1;
+		/** PMU Active */
+		unsigned pmuactv:1;
+		/** Restore */
+		unsigned restore:1;
+		/** Power Down Clamp */
+		unsigned pwrdnclmp:1;
+		/** Power Down Reset */
+		unsigned pwrdnrstn:1;
+		/** Power Down Switch */
+		unsigned pwrdnswtch:1;
+		/** Disable VBUS */
+		unsigned dis_vbus:1;
+		/** Line State Change */
+		unsigned lnstschng:1;
+		/** Line state change mask */
+		unsigned lnstchng_msk:1;
+		/** Reset Detected */
+		unsigned rst_det:1;
+		/** Reset Detect mask */
+		unsigned rst_det_msk:1;
+		/** Disconnect Detected */
+		unsigned disconn_det:1;
+		/** Disconnect Detect mask */
+		unsigned disconn_det_msk:1;
+		/** Connect Detected*/
+		unsigned connect_det:1;
+		/** Connect Detected Mask*/
+		unsigned connect_det_msk:1;
+		/** SRP Detected */
+		unsigned srp_det:1;
+		/** SRP Detect mask */
+		unsigned srp_det_msk:1;
+		/** Status Change Interrupt */
+		unsigned sts_chngint:1;
+		/** Status Change Interrupt Mask */
+		unsigned sts_chngint_msk:1;
+		/** Line State */
+		unsigned linestate:2;
+		/** Indicates current mode(status of IDDIG signal) */
+		unsigned idsts:1;
+		/** B Session Valid signal status*/
+		unsigned bsessvld:1;
+		/** ADP Event Detected */
+		unsigned adp_int:1;
+		/** Multi Valued ID pin */
+		unsigned mult_val_id_bc:5;
+		/** Reserved 24_31 */
+		unsigned reserved29_31:3;
+	} b;
+} gpwrdn_data_t;
+
+#endif
diff -Nur --no-dereference linux-5.10.52-orig/drivers/usb/host/dwc_otg/Makefile linux-5.10.52-v7l+/drivers/usb/host/dwc_otg/Makefile
--- linux-5.10.52-orig/drivers/usb/host/dwc_otg/Makefile	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/usb/host/dwc_otg/Makefile	2021-07-25 16:46:28.657910448 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+#
+# Makefile for DWC_otg Highspeed USB controller driver
+#
+
+ifneq ($(KERNELRELEASE),)
+
+# Use the BUS_INTERFACE variable to compile the software for either
+# PCI(PCI_INTERFACE) or LM(LM_INTERFACE) bus.
+ifeq ($(BUS_INTERFACE),)
+#	BUS_INTERFACE = -DPCI_INTERFACE
+#	BUS_INTERFACE = -DLM_INTERFACE
+        BUS_INTERFACE = -DPLATFORM_INTERFACE
+endif
+
+#ccflags-y	+= -DDEBUG
+#ccflags-y	+= -DDWC_OTG_DEBUGLEV=1 # reduce common debug msgs
+
+# Use one of the following flags to compile the software in host-only or
+# device-only mode.
+#ccflags-y        += -DDWC_HOST_ONLY
+#ccflags-y        += -DDWC_DEVICE_ONLY
+
+ccflags-y	+= -Dlinux -DDWC_HS_ELECT_TST
+#ccflags-y	+= -DDWC_EN_ISOC
+ccflags-y   	+= -I$(srctree)/drivers/usb/host/dwc_common_port
+#ccflags-y   	+= -I$(PORTLIB)
+ccflags-y   	+= -DDWC_LINUX
+ccflags-y   	+= $(CFI)
+ccflags-y	+= $(BUS_INTERFACE)
+#ccflags-y	+= -DDWC_DEV_SRPCAP
+
+obj-$(CONFIG_USB_DWCOTG) += dwc_otg.o
+
+dwc_otg-objs	:= dwc_otg_driver.o dwc_otg_attr.o
+dwc_otg-objs	+= dwc_otg_cil.o dwc_otg_cil_intr.o
+dwc_otg-objs	+= dwc_otg_pcd_linux.o dwc_otg_pcd.o dwc_otg_pcd_intr.o
+dwc_otg-objs	+= dwc_otg_hcd.o dwc_otg_hcd_linux.o dwc_otg_hcd_intr.o dwc_otg_hcd_queue.o dwc_otg_hcd_ddma.o
+dwc_otg-objs	+= dwc_otg_adp.o
+dwc_otg-objs	+= dwc_otg_fiq_fsm.o
+ifneq ($(CONFIG_ARM64),y)
+dwc_otg-objs	+= dwc_otg_fiq_stub.o
+endif
+
+ifneq ($(CFI),)
+dwc_otg-objs	+= dwc_otg_cfi.o
+endif
+
+kernrelwd := $(subst ., ,$(KERNELRELEASE))
+kernrel3 := $(word 1,$(kernrelwd)).$(word 2,$(kernrelwd)).$(word 3,$(kernrelwd))
+
+ifneq ($(kernrel3),2.6.20)
+ccflags-y += $(CPPFLAGS)
+endif
+
+else
+
+PWD		:= $(shell pwd)
+PORTLIB		:= $(PWD)/../dwc_common_port
+
+# Command paths
+CTAGS		:= $(CTAGS)
+DOXYGEN		:= $(DOXYGEN)
+
+default: portlib
+	$(MAKE) -C$(KDIR) M=$(PWD) ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE) modules
+
+install: default
+	$(MAKE) -C$(KDIR) M=$(PORTLIB) modules_install
+	$(MAKE) -C$(KDIR) M=$(PWD) modules_install
+
+portlib:
+	$(MAKE) -C$(KDIR) M=$(PORTLIB) ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE) modules
+	cp $(PORTLIB)/Module.symvers $(PWD)/
+
+docs:	$(wildcard *.[hc]) doc/doxygen.cfg
+	$(DOXYGEN) doc/doxygen.cfg
+
+tags:	$(wildcard *.[hc])
+	$(CTAGS) -e $(wildcard *.[hc]) $(wildcard linux/*.[hc]) $(wildcard $(KDIR)/include/linux/usb*.h)
+
+
+clean:
+	rm -rf   *.o *.ko .*cmd *.mod.c .tmp_versions Module.symvers
+
+endif
diff -Nur --no-dereference linux-5.10.52-orig/drivers/usb/host/dwc_otg/test/dwc_otg_test.pm linux-5.10.52-v7l+/drivers/usb/host/dwc_otg/test/dwc_otg_test.pm
--- linux-5.10.52-orig/drivers/usb/host/dwc_otg/test/dwc_otg_test.pm	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/usb/host/dwc_otg/test/dwc_otg_test.pm	2021-07-25 16:46:28.717909442 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+package dwc_otg_test;
+
+use strict;
+use Exporter ();
+
+use vars qw(@ISA @EXPORT
+$sysfsdir $paramdir $errors $params
+);
+
+@ISA = qw(Exporter);
+
+#
+# Globals
+#
+$sysfsdir = "/sys/devices/lm0";
+$paramdir = "/sys/module/dwc_otg";
+$errors = 0;
+
+$params = [
+	   {
+	    NAME => "otg_cap",
+	    DEFAULT => 0,
+	    ENUM => [],
+	    LOW => 0,
+	    HIGH => 2
+	   },
+	   {
+	    NAME => "dma_enable",
+	    DEFAULT => 0,
+	    ENUM => [],
+	    LOW => 0,
+	    HIGH => 1
+	   },
+	   {
+	    NAME => "dma_burst_size",
+	    DEFAULT => 32,
+	    ENUM => [1, 4, 8, 16, 32, 64, 128, 256],
+	    LOW => 1,
+	    HIGH => 256
+	   },
+	   {
+	    NAME => "host_speed",
+	    DEFAULT => 0,
+	    ENUM => [],
+	    LOW => 0,
+	    HIGH => 1
+	   },
+	   {
+	    NAME => "host_support_fs_ls_low_power",
+	    DEFAULT => 0,
+	    ENUM => [],
+	    LOW => 0,
+	    HIGH => 1
+	   },
+	   {
+	    NAME => "host_ls_low_power_phy_clk",
+	    DEFAULT => 0,
+	    ENUM => [],
+	    LOW => 0,
+	    HIGH => 1
+	   },
+	   {
+	    NAME => "dev_speed",
+	    DEFAULT => 0,
+	    ENUM => [],
+	    LOW => 0,
+	    HIGH => 1
+	   },
+	   {
+	    NAME => "enable_dynamic_fifo",
+	    DEFAULT => 1,
+	    ENUM => [],
+	    LOW => 0,
+	    HIGH => 1
+	   },
+	   {
+	    NAME => "data_fifo_size",
+	    DEFAULT => 8192,
+	    ENUM => [],
+	    LOW => 32,
+	    HIGH => 32768
+	   },
+	   {
+	    NAME => "dev_rx_fifo_size",
+	    DEFAULT => 1064,
+	    ENUM => [],
+	    LOW => 16,
+	    HIGH => 32768
+	   },
+	   {
+	    NAME => "dev_nperio_tx_fifo_size",
+	    DEFAULT => 1024,
+	    ENUM => [],
+	    LOW => 16,
+	    HIGH => 32768
+	   },
+	   {
+	    NAME => "dev_perio_tx_fifo_size_1",
+	    DEFAULT => 256,
+	    ENUM => [],
+	    LOW => 4,
+	    HIGH => 768
+	   },
+	   {
+	    NAME => "dev_perio_tx_fifo_size_2",
+	    DEFAULT => 256,
+	    ENUM => [],
+	    LOW => 4,
+	    HIGH => 768
+	   },
+	   {
+	    NAME => "dev_perio_tx_fifo_size_3",
+	    DEFAULT => 256,
+	    ENUM => [],
+	    LOW => 4,
+	    HIGH => 768
+	   },
+	   {
+	    NAME => "dev_perio_tx_fifo_size_4",
+	    DEFAULT => 256,
+	    ENUM => [],
+	    LOW => 4,
+	    HIGH => 768
+	   },
+	   {
+	    NAME => "dev_perio_tx_fifo_size_5",
+	    DEFAULT => 256,
+	    ENUM => [],
+	    LOW => 4,
+	    HIGH => 768
+	   },
+	   {
+	    NAME => "dev_perio_tx_fifo_size_6",
+	    DEFAULT => 256,
+	    ENUM => [],
+	    LOW => 4,
+	    HIGH => 768
+	   },
+	   {
+	    NAME => "dev_perio_tx_fifo_size_7",
+	    DEFAULT => 256,
+	    ENUM => [],
+	    LOW => 4,
+	    HIGH => 768
+	   },
+	   {
+	    NAME => "dev_perio_tx_fifo_size_8",
+	    DEFAULT => 256,
+	    ENUM => [],
+	    LOW => 4,
+	    HIGH => 768
+	   },
+	   {
+	    NAME => "dev_perio_tx_fifo_size_9",
+	    DEFAULT => 256,
+	    ENUM => [],
+	    LOW => 4,
+	    HIGH => 768
+	   },
+	   {
+	    NAME => "dev_perio_tx_fifo_size_10",
+	    DEFAULT => 256,
+	    ENUM => [],
+	    LOW => 4,
+	    HIGH => 768
+	   },
+	   {
+	    NAME => "dev_perio_tx_fifo_size_11",
+	    DEFAULT => 256,
+	    ENUM => [],
+	    LOW => 4,
+	    HIGH => 768
+	   },
+	   {
+	    NAME => "dev_perio_tx_fifo_size_12",
+	    DEFAULT => 256,
+	    ENUM => [],
+	    LOW => 4,
+	    HIGH => 768
+	   },
+	   {
+	    NAME => "dev_perio_tx_fifo_size_13",
+	    DEFAULT => 256,
+	    ENUM => [],
+	    LOW => 4,
+	    HIGH => 768
+	   },
+	   {
+	    NAME => "dev_perio_tx_fifo_size_14",
+	    DEFAULT => 256,
+	    ENUM => [],
+	    LOW => 4,
+	    HIGH => 768
+	   },
+	   {
+	    NAME => "dev_perio_tx_fifo_size_15",
+	    DEFAULT => 256,
+	    ENUM => [],
+	    LOW => 4,
+	    HIGH => 768
+	   },
+	   {
+	    NAME => "host_rx_fifo_size",
+	    DEFAULT => 1024,
+	    ENUM => [],
+	    LOW => 16,
+	    HIGH => 32768
+	   },
+	   {
+	    NAME => "host_nperio_tx_fifo_size",
+	    DEFAULT => 1024,
+	    ENUM => [],
+	    LOW => 16,
+	    HIGH => 32768
+	   },
+	   {
+	    NAME => "host_perio_tx_fifo_size",
+	    DEFAULT => 1024,
+	    ENUM => [],
+	    LOW => 16,
+	    HIGH => 32768
+	   },
+	   {
+	    NAME => "max_transfer_size",
+	    DEFAULT => 65535,
+	    ENUM => [],
+	    LOW => 2047,
+	    HIGH => 65535
+	   },
+	   {
+	    NAME => "max_packet_count",
+	    DEFAULT => 511,
+	    ENUM => [],
+	    LOW => 15,
+	    HIGH => 511
+	   },
+	   {
+	    NAME => "host_channels",
+	    DEFAULT => 12,
+	    ENUM => [],
+	    LOW => 1,
+	    HIGH => 16
+	   },
+	   {
+	    NAME => "dev_endpoints",
+	    DEFAULT => 6,
+	    ENUM => [],
+	    LOW => 1,
+	    HIGH => 15
+	   },
+	   {
+	    NAME => "phy_type",
+	    DEFAULT => 1,
+	    ENUM => [],
+	    LOW => 0,
+	    HIGH => 2
+	   },
+	   {
+	    NAME => "phy_utmi_width",
+	    DEFAULT => 16,
+	    ENUM => [8, 16],
+	    LOW => 8,
+	    HIGH => 16
+	   },
+	   {
+	    NAME => "phy_ulpi_ddr",
+	    DEFAULT => 0,
+	    ENUM => [],
+	    LOW => 0,
+	    HIGH => 1
+	   },
+	  ];
+
+
+#
+#
+sub check_arch {
+  $_ = `uname -m`;
+  chomp;
+  unless (m/armv4tl/) {
+    warn "# \n# Can't execute on $_.  Run on integrator platform.\n# \n";
+    return 0;
+  }
+  return 1;
+}
+
+#
+#
+sub load_module {
+  my $params = shift;
+  print "\nRemoving Module\n";
+  system "rmmod dwc_otg";
+  print "Loading Module\n";
+  if ($params ne "") {
+    print "Module Parameters: $params\n";
+  }
+  if (system("modprobe dwc_otg $params")) {
+    warn "Unable to load module\n";
+    return 0;
+  }
+  return 1;
+}
+
+#
+#
+sub test_status {
+  my $arg = shift;
+
+  print "\n";
+
+  if (defined $arg) {
+    warn "WARNING: $arg\n";
+  }
+
+  if ($errors > 0) {
+    warn "TEST FAILED with $errors errors\n";
+    return 0;
+  } else {
+    print "TEST PASSED\n";
+    return 0 if (defined $arg);
+  }
+  return 1;
+}
+
+#
+#
+@EXPORT = qw(
+$sysfsdir
+$paramdir
+$params
+$errors
+check_arch
+load_module
+test_status
+);
+
+1;
diff -Nur --no-dereference linux-5.10.52-orig/drivers/usb/host/dwc_otg/test/Makefile linux-5.10.52-v7l+/drivers/usb/host/dwc_otg/test/Makefile
--- linux-5.10.52-orig/drivers/usb/host/dwc_otg/test/Makefile	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/usb/host/dwc_otg/test/Makefile	2021-07-25 16:46:28.717909442 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+
+PERL=/usr/bin/perl
+PL_TESTS=test_sysfs.pl test_mod_param.pl
+
+.PHONY : test
+test : perl_tests
+
+perl_tests :
+	@echo
+	@echo Running perl tests
+	@for test in $(PL_TESTS); do \
+	  if $(PERL) ./$$test ; then \
+	    echo "=======> $$test, PASSED" ; \
+	  else echo "=======> $$test, FAILED" ; \
+	  fi \
+	done
diff -Nur --no-dereference linux-5.10.52-orig/drivers/usb/host/dwc_otg/test/test_mod_param.pl linux-5.10.52-v7l+/drivers/usb/host/dwc_otg/test/test_mod_param.pl
--- linux-5.10.52-orig/drivers/usb/host/dwc_otg/test/test_mod_param.pl	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/usb/host/dwc_otg/test/test_mod_param.pl	2021-07-25 16:46:28.717909442 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+#!/usr/bin/perl -w
+#
+# Run this program on the integrator.
+#
+# - Tests module parameter default values.
+# - Tests setting of valid module parameter values via modprobe.
+# - Tests invalid module parameter values.
+# -----------------------------------------------------------------------------
+use strict;
+use dwc_otg_test;
+
+check_arch() or die;
+
+#
+#
+sub test {
+  my ($param,$expected) = @_;
+  my $value = get($param);
+
+  if ($value == $expected) {
+    print "$param = $value, okay\n";
+  }
+
+  else {
+    warn "ERROR: value of $param != $expected, $value\n";
+    $errors ++;
+  }
+}
+
+#
+#
+sub get {
+  my $param = shift;
+  my $tmp = `cat $paramdir/$param`;
+  chomp $tmp;
+  return $tmp;
+}
+
+#
+#
+sub test_main {
+
+  print "\nTesting Module Parameters\n";
+
+  load_module("") or die;
+
+  # Test initial values
+  print "\nTesting Default Values\n";
+  foreach (@{$params}) {
+    test ($_->{NAME}, $_->{DEFAULT});
+  }
+
+  # Test low value
+  print "\nTesting Low Value\n";
+  my $cmd_params = "";
+  foreach (@{$params}) {
+    $cmd_params = $cmd_params . "$_->{NAME}=$_->{LOW} ";
+  }
+  load_module($cmd_params) or die;
+
+  foreach (@{$params}) {
+    test ($_->{NAME}, $_->{LOW});
+  }
+
+  # Test high value
+  print "\nTesting High Value\n";
+  $cmd_params = "";
+  foreach (@{$params}) {
+    $cmd_params = $cmd_params . "$_->{NAME}=$_->{HIGH} ";
+  }
+  load_module($cmd_params) or die;
+
+  foreach (@{$params}) {
+    test ($_->{NAME}, $_->{HIGH});
+  }
+
+  # Test Enum
+  print "\nTesting Enumerated\n";
+  foreach (@{$params}) {
+    if (defined $_->{ENUM}) {
+      my $value;
+      foreach $value (@{$_->{ENUM}}) {
+	$cmd_params = "$_->{NAME}=$value";
+	load_module($cmd_params) or die;
+	test ($_->{NAME}, $value);
+      }
+    }
+  }
+
+  # Test Invalid Values
+  print "\nTesting Invalid Values\n";
+  $cmd_params = "";
+  foreach (@{$params}) {
+    $cmd_params = $cmd_params . sprintf "$_->{NAME}=%d ", $_->{LOW}-1;
+  }
+  load_module($cmd_params) or die;
+
+  foreach (@{$params}) {
+    test ($_->{NAME}, $_->{DEFAULT});
+  }
+
+  $cmd_params = "";
+  foreach (@{$params}) {
+    $cmd_params = $cmd_params . sprintf "$_->{NAME}=%d ", $_->{HIGH}+1;
+  }
+  load_module($cmd_params) or die;
+
+  foreach (@{$params}) {
+    test ($_->{NAME}, $_->{DEFAULT});
+  }
+
+  print "\nTesting Enumerated\n";
+  foreach (@{$params}) {
+    if (defined $_->{ENUM}) {
+      my $value;
+      foreach $value (@{$_->{ENUM}}) {
+	$value = $value + 1;
+	$cmd_params = "$_->{NAME}=$value";
+	load_module($cmd_params) or die;
+	test ($_->{NAME}, $_->{DEFAULT});
+	$value = $value - 2;
+	$cmd_params = "$_->{NAME}=$value";
+	load_module($cmd_params) or die;
+	test ($_->{NAME}, $_->{DEFAULT});
+      }
+    }
+  }
+
+  test_status() or die;
+}
+
+test_main();
+0;
diff -Nur --no-dereference linux-5.10.52-orig/drivers/usb/host/dwc_otg/test/test_sysfs.pl linux-5.10.52-v7l+/drivers/usb/host/dwc_otg/test/test_sysfs.pl
--- linux-5.10.52-orig/drivers/usb/host/dwc_otg/test/test_sysfs.pl	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/usb/host/dwc_otg/test/test_sysfs.pl	2021-07-25 16:46:28.717909442 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+#!/usr/bin/perl -w
+#
+# Run this program on the integrator
+# - Tests select sysfs attributes.
+# - Todo ... test more attributes, hnp/srp, buspower/bussuspend, etc.
+# -----------------------------------------------------------------------------
+use strict;
+use dwc_otg_test;
+
+check_arch() or die;
+
+#
+#
+sub test {
+  my ($attr,$expected) = @_;
+  my $string = get($attr);
+
+  if ($string eq $expected) {
+    printf("$attr = $string, okay\n");
+  }
+  else {
+    warn "ERROR: value of $attr != $expected, $string\n";
+    $errors ++;
+  }
+}
+
+#
+#
+sub set {
+  my ($reg, $value) = @_;
+  system "echo $value > $sysfsdir/$reg";
+}
+
+#
+#
+sub get {
+  my $attr = shift;
+  my $string = `cat $sysfsdir/$attr`;
+  chomp $string;
+  if ($string =~ m/\s\=\s/) {
+    my $tmp;
+    ($tmp, $string) = split /\s=\s/, $string;
+  }
+  return $string;
+}
+
+#
+#
+sub test_main {
+  print("\nTesting Sysfs Attributes\n");
+
+  load_module("") or die;
+
+  # Test initial values of regoffset/regvalue/guid/gsnpsid
+  print("\nTesting Default Values\n");
+
+  test("regoffset", "0xffffffff");
+  test("regvalue", "invalid offset");
+  test("guid", "0x12345678");	# this will fail if it has been changed
+  test("gsnpsid", "0x4f54200a");
+
+  # Test operation of regoffset/regvalue
+  print("\nTesting regoffset\n");
+  set('regoffset', '5a5a5a5a');
+  test("regoffset", "0xffffffff");
+
+  set('regoffset', '0');
+  test("regoffset", "0x00000000");
+
+  set('regoffset', '40000');
+  test("regoffset", "0x00000000");
+
+  set('regoffset', '3ffff');
+  test("regoffset", "0x0003ffff");
+
+  set('regoffset', '1');
+  test("regoffset", "0x00000001");
+
+  print("\nTesting regvalue\n");
+  set('regoffset', '3c');
+  test("regvalue", "0x12345678");
+  set('regvalue', '5a5a5a5a');
+  test("regvalue", "0x5a5a5a5a");
+  set('regvalue','a5a5a5a5');
+  test("regvalue", "0xa5a5a5a5");
+  set('guid','12345678');
+
+  # Test HNP Capable
+  print("\nTesting HNP Capable bit\n");
+  set('hnpcapable', '1');
+  test("hnpcapable", "0x1");
+  set('hnpcapable','0');
+  test("hnpcapable", "0x0");
+
+  set('regoffset','0c');
+
+  my $old = get('gusbcfg');
+  print("setting hnpcapable\n");
+  set('hnpcapable', '1');
+  test("hnpcapable", "0x1");
+  test('gusbcfg', sprintf "0x%08x", (oct ($old) | (1<<9)));
+  test('regvalue', sprintf "0x%08x", (oct ($old) | (1<<9)));
+
+  $old = get('gusbcfg');
+  print("clearing hnpcapable\n");
+  set('hnpcapable', '0');
+  test("hnpcapable", "0x0");
+  test ('gusbcfg', sprintf "0x%08x", oct ($old) & (~(1<<9)));
+  test ('regvalue', sprintf "0x%08x", oct ($old) & (~(1<<9)));
+
+  # Test SRP Capable
+  print("\nTesting SRP Capable bit\n");
+  set('srpcapable', '1');
+  test("srpcapable", "0x1");
+  set('srpcapable','0');
+  test("srpcapable", "0x0");
+
+  set('regoffset','0c');
+
+  $old = get('gusbcfg');
+  print("setting srpcapable\n");
+  set('srpcapable', '1');
+  test("srpcapable", "0x1");
+  test('gusbcfg', sprintf "0x%08x", (oct ($old) | (1<<8)));
+  test('regvalue', sprintf "0x%08x", (oct ($old) | (1<<8)));
+
+  $old = get('gusbcfg');
+  print("clearing srpcapable\n");
+  set('srpcapable', '0');
+  test("srpcapable", "0x0");
+  test('gusbcfg', sprintf "0x%08x", oct ($old) & (~(1<<8)));
+  test('regvalue', sprintf "0x%08x", oct ($old) & (~(1<<8)));
+
+  # Test GGPIO
+  print("\nTesting GGPIO\n");
+  set('ggpio','5a5a5a5a');
+  test('ggpio','0x5a5a0000');
+  set('ggpio','a5a5a5a5');
+  test('ggpio','0xa5a50000');
+  set('ggpio','11110000');
+  test('ggpio','0x11110000');
+  set('ggpio','00001111');
+  test('ggpio','0x00000000');
+
+  # Test DEVSPEED
+  print("\nTesting DEVSPEED\n");
+  set('regoffset','800');
+  $old = get('regvalue');
+  set('devspeed','0');
+  test('devspeed','0x0');
+  test('regvalue',sprintf("0x%08x", oct($old) & ~(0x3)));
+  set('devspeed','1');
+  test('devspeed','0x1');
+  test('regvalue',sprintf("0x%08x", oct($old) & ~(0x3) | 1));
+  set('devspeed','2');
+  test('devspeed','0x2');
+  test('regvalue',sprintf("0x%08x", oct($old) & ~(0x3) | 2));
+  set('devspeed','3');
+  test('devspeed','0x3');
+  test('regvalue',sprintf("0x%08x", oct($old) & ~(0x3) | 3));
+  set('devspeed','4');
+  test('devspeed','0x0');
+  test('regvalue',sprintf("0x%08x", oct($old) & ~(0x3)));
+  set('devspeed','5');
+  test('devspeed','0x1');
+  test('regvalue',sprintf("0x%08x", oct($old) & ~(0x3) | 1));
+
+
+  #  mode	Returns the current mode:0 for device mode1 for host mode	Read
+  #  hnp	Initiate the Host Negotiation Protocol.  Read returns the status.	Read/Write
+  #  srp	Initiate the Session Request Protocol.  Read returns the status.	Read/Write
+  #  buspower	Get or Set the Power State of the bus (0 - Off or 1 - On) 	Read/Write
+  #  bussuspend	Suspend the USB bus.	Read/Write
+  #  busconnected	Get the connection status of the bus 	Read
+
+  #  gotgctl	Get or set the Core Control Status Register.	Read/Write
+  ##  gusbcfg	Get or set the Core USB Configuration Register	Read/Write
+  #  grxfsiz	Get or set the Receive FIFO Size Register	Read/Write
+  #  gnptxfsiz	Get or set the non-periodic Transmit Size Register	Read/Write
+  #  gpvndctl	Get or set the PHY Vendor Control Register	Read/Write
+  ##  ggpio	Get the value in the lower 16-bits of the General Purpose IO Register or Set the upper 16 bits.	Read/Write
+  ##  guid	Get or set the value of the User ID Register	Read/Write
+  ##  gsnpsid	Get the value of the Synopsys ID Regester	Read
+  ##  devspeed	Get or set the device speed setting in the DCFG register	Read/Write
+  #  enumspeed	Gets the device enumeration Speed.	Read
+  #  hptxfsiz	Get the value of the Host Periodic Transmit FIFO	Read
+  #  hprt0	Get or Set the value in the Host Port Control and Status Register	Read/Write
+
+  test_status("TEST NYI") or die;
+}
+
+test_main();
+0;
diff -Nur --no-dereference linux-5.10.52-orig/drivers/usb/host/Kconfig linux-5.10.52-v7l+/drivers/usb/host/Kconfig
--- linux-5.10.52-orig/drivers/usb/host/Kconfig	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/usb/host/Kconfig	2021-07-25 16:46:28.637910783 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:744 @
 	  To compile this driver as a module, choose M here: the
 	  module will be called renesas-usbhs.
 
+config USB_DWCOTG
+	bool "Synopsis DWC host support"
+	depends on USB && (FIQ || ARM64)
+	help
+	  The Synopsis DWC controller is a dual-role
+	  host/peripheral/OTG ("On The Go") USB controllers.
+
+	  Enable this option to support this IP in host controller mode.
+	  If unsure, say N.
+
 config USB_IMX21_HCD
 	tristate "i.MX21 HCD support"
 	depends on ARM && ARCH_MXC
diff -Nur --no-dereference linux-5.10.52-orig/drivers/usb/host/Makefile linux-5.10.52-v7l+/drivers/usb/host/Makefile
--- linux-5.10.52-orig/drivers/usb/host/Makefile	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/usb/host/Makefile	2021-07-25 16:46:28.657910448 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:84 @
 obj-$(CONFIG_USB_SL811_CS)	+= sl811_cs.o
 obj-$(CONFIG_USB_U132_HCD)	+= u132-hcd.o
 obj-$(CONFIG_USB_R8A66597_HCD)	+= r8a66597-hcd.o
+obj-$(CONFIG_USB_DWCOTG)        += dwc_otg/ dwc_common_port/
 obj-$(CONFIG_USB_IMX21_HCD)	+= imx21-hcd.o
 obj-$(CONFIG_USB_FSL_USB2)	+= fsl-mph-dr-of.o
 obj-$(CONFIG_USB_EHCI_FSL)	+= fsl-mph-dr-of.o
diff -Nur --no-dereference linux-5.10.52-orig/drivers/usb/host/xhci.c linux-5.10.52-v7l+/drivers/usb/host/xhci.c
--- linux-5.10.52-orig/drivers/usb/host/xhci.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/usb/host/xhci.c	2021-07-25 16:46:28.827907598 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1470 @
 }
 
 /*
+ * RPI: Fixup endpoint intervals when requested
+ * - Check interval versus the (cached) endpoint context
+ * - set the endpoint interval to the new value
+ * - force an endpoint configure command
+ * XXX: bandwidth is not recalculated. We should probably do that.
+ */
+static void xhci_fixup_endpoint(struct usb_hcd *hcd, struct usb_device *udev,
+				struct usb_host_endpoint *ep, int interval)
+{
+	struct xhci_hcd *xhci;
+	struct xhci_ep_ctx *ep_ctx_out, *ep_ctx_in;
+	struct xhci_command *command;
+	struct xhci_input_control_ctx *ctrl_ctx;
+	struct xhci_virt_device *vdev;
+	int xhci_interval;
+	int ret;
+	int ep_index;
+	unsigned long flags;
+	u32 ep_info_tmp;
+
+	xhci = hcd_to_xhci(hcd);
+	ep_index = xhci_get_endpoint_index(&ep->desc);
+
+	/* FS/LS interval translations */
+	if ((udev->speed == USB_SPEED_FULL ||
+	     udev->speed == USB_SPEED_LOW))
+		interval *= 8;
+
+	mutex_lock(&xhci->mutex);
+
+	spin_lock_irqsave(&xhci->lock, flags);
+
+	vdev = xhci->devs[udev->slot_id];
+	/* Get context-derived endpoint interval */
+	ep_ctx_out = xhci_get_ep_ctx(xhci, vdev->out_ctx, ep_index);
+	ep_ctx_in = xhci_get_ep_ctx(xhci, vdev->in_ctx, ep_index);
+	xhci_interval = EP_INTERVAL_TO_UFRAMES(le32_to_cpu(ep_ctx_out->ep_info));
+
+	if (interval == xhci_interval) {
+		spin_unlock_irqrestore(&xhci->lock, flags);
+		mutex_unlock(&xhci->mutex);
+		return;
+	}
+
+	xhci_dbg(xhci, "Fixup interval=%d xhci_interval=%d\n",
+		 interval, xhci_interval);
+	command = xhci_alloc_command_with_ctx(xhci, true, GFP_ATOMIC);
+	if (!command) {
+		/* Failure here is benign, poll at the original rate */
+		spin_unlock_irqrestore(&xhci->lock, flags);
+		mutex_unlock(&xhci->mutex);
+		return;
+	}
+
+	/* xHCI uses exponents for intervals... */
+	xhci_interval = fls(interval) - 1;
+	xhci_interval = clamp_val(xhci_interval, 3, 10);
+	ep_info_tmp = le32_to_cpu(ep_ctx_out->ep_info);
+	ep_info_tmp &= ~EP_INTERVAL(255);
+	ep_info_tmp |= EP_INTERVAL(xhci_interval);
+
+	/* Keep the endpoint context up-to-date while issuing the command. */
+	xhci_endpoint_copy(xhci, vdev->in_ctx,
+			   vdev->out_ctx, ep_index);
+	ep_ctx_in->ep_info = cpu_to_le32(ep_info_tmp);
+
+	/*
+	 * We need to drop the lock, so take an explicit copy
+	 * of the ep context.
+	 */
+	xhci_endpoint_copy(xhci, command->in_ctx, vdev->in_ctx, ep_index);
+
+	ctrl_ctx = xhci_get_input_control_ctx(command->in_ctx);
+	if (!ctrl_ctx) {
+		xhci_warn(xhci,
+			  "%s: Could not get input context, bad type.\n",
+			  __func__);
+		spin_unlock_irqrestore(&xhci->lock, flags);
+		xhci_free_command(xhci, command);
+		mutex_unlock(&xhci->mutex);
+		return;
+	}
+	ctrl_ctx->add_flags = xhci_get_endpoint_flag_from_index(ep_index);
+	ctrl_ctx->drop_flags = 0;
+
+	spin_unlock_irqrestore(&xhci->lock, flags);
+
+	ret = xhci_configure_endpoint(xhci, udev, command,
+				      false, false);
+	if (ret)
+		xhci_warn(xhci, "%s: Configure endpoint failed: %d\n",
+			  __func__, ret);
+	xhci_free_command(xhci, command);
+	mutex_unlock(&xhci->mutex);
+}
+
+/*
  * non-error returns are a promise to giveback() the urb later
  * we drop ownership so next owner (or urb unlink) can get it
  */
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:5461 @
 	.endpoint_reset =	xhci_endpoint_reset,
 	.check_bandwidth =	xhci_check_bandwidth,
 	.reset_bandwidth =	xhci_reset_bandwidth,
+	.fixup_endpoint =	xhci_fixup_endpoint,
 	.address_device =	xhci_address_device,
 	.enable_device =	xhci_enable_device,
 	.update_hub_device =	xhci_update_hub_device,
diff -Nur --no-dereference linux-5.10.52-orig/drivers/usb/host/xhci.h linux-5.10.52-v7l+/drivers/usb/host/xhci.h
--- linux-5.10.52-orig/drivers/usb/host/xhci.h	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/usb/host/xhci.h	2021-07-25 16:46:28.827907598 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1652 @
  * Each segment table entry is 4*32bits long.  1K seems like an ok size:
  * (1K bytes * 8bytes/bit) / (4*32 bits) = 64 segment entries in the table,
  * meaning 64 ring segments.
- * Initial allocated size of the ERST, in number of entries */
-#define	ERST_NUM_SEGS	1
+ * Maximum number of segments in the ERST */
+#define	ERST_MAX_SEGS	8
 /* Initial allocated size of the ERST, in number of entries */
 #define	ERST_SIZE	64
 /* Initial number of event segment rings allocated */
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1883 @
 #define XHCI_DISABLE_SPARSE	BIT_ULL(38)
 #define XHCI_SG_TRB_CACHE_SIZE_QUIRK	BIT_ULL(39)
 #define XHCI_NO_SOFT_RETRY	BIT_ULL(40)
+#define XHCI_EP_CTX_BROKEN_DCS	BIT_ULL(41)
+#define XHCI_AVOID_DQ_ON_LINK	BIT_ULL(42)
 
 	unsigned int		num_active_eps;
 	unsigned int		limit_active_eps;
diff -Nur --no-dereference linux-5.10.52-orig/drivers/usb/host/xhci-mem.c linux-5.10.52-v7l+/drivers/usb/host/xhci-mem.c
--- linux-5.10.52-orig/drivers/usb/host/xhci-mem.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/usb/host/xhci-mem.c	2021-07-25 16:46:28.797908101 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:2528 @
 	 * Event ring setup: Allocate a normal ring, but also setup
 	 * the event ring segment table (ERST).  Section 4.9.3.
 	 */
+	val2 = 1 << HCS_ERST_MAX(xhci->hcs_params2);
+	val2 = min_t(unsigned int, ERST_MAX_SEGS, val2);
 	xhci_dbg_trace(xhci, trace_xhci_dbg_init, "// Allocating event ring");
-	xhci->event_ring = xhci_ring_alloc(xhci, ERST_NUM_SEGS, 1, TYPE_EVENT,
-					0, flags);
+	xhci->event_ring = xhci_ring_alloc(xhci, val2, 1, TYPE_EVENT,
+					   0, flags);
 	if (!xhci->event_ring)
 		goto fail;
 	if (xhci_check_trb_in_td_math(xhci) < 0)
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:2545 @
 	/* set ERST count with the number of entries in the segment table */
 	val = readl(&xhci->ir_set->erst_size);
 	val &= ERST_SIZE_MASK;
-	val |= ERST_NUM_SEGS;
+	val |= val2;
 	xhci_dbg_trace(xhci, trace_xhci_dbg_init,
 			"// Write ERST size = %i to ir_set 0 (some bits preserved)",
 			val);
diff -Nur --no-dereference linux-5.10.52-orig/drivers/usb/host/xhci-pci.c linux-5.10.52-v7l+/drivers/usb/host/xhci-pci.c
--- linux-5.10.52-orig/drivers/usb/host/xhci-pci.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/usb/host/xhci-pci.c	2021-07-25 16:46:28.797908101 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:277 @
 			pdev->device == 0x3432)
 		xhci->quirks |= XHCI_BROKEN_STREAMS;
 
-	if (pdev->vendor == PCI_VENDOR_ID_VIA && pdev->device == 0x3483)
+	if (pdev->vendor == PCI_VENDOR_ID_VIA && pdev->device == 0x3483) {
 		xhci->quirks |= XHCI_LPM_SUPPORT;
+		xhci->quirks |= XHCI_EP_CTX_BROKEN_DCS;
+		xhci->quirks |= XHCI_AVOID_DQ_ON_LINK;
+	}
 
 	if (pdev->vendor == PCI_VENDOR_ID_ASMEDIA &&
 		pdev->device == PCI_DEVICE_ID_ASMEDIA_1042_XHCI)
diff -Nur --no-dereference linux-5.10.52-orig/drivers/usb/host/xhci-ring.c linux-5.10.52-v7l+/drivers/usb/host/xhci-ring.c
--- linux-5.10.52-orig/drivers/usb/host/xhci-ring.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/usb/host/xhci-ring.c	2021-07-25 16:46:28.807907933 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:536 @
 	struct xhci_virt_ep *ep = &dev->eps[ep_index];
 	struct xhci_ring *ep_ring;
 	struct xhci_segment *new_seg;
+	struct xhci_segment *halted_seg = NULL;
 	union xhci_trb *new_deq;
+	union xhci_trb *halted_trb;
+	int index = 0;
 	dma_addr_t addr;
 	u64 hw_dequeue;
 	bool cycle_found = false;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:577 @
 	hw_dequeue = xhci_get_hw_deq(xhci, dev, ep_index, stream_id);
 	new_seg = ep_ring->deq_seg;
 	new_deq = ep_ring->dequeue;
-	state->new_cycle_state = hw_dequeue & 0x1;
+
+	/*
+	 * Quirk: xHC write-back of the DCS field in the hardware dequeue
+	 * pointer is wrong - use the cycle state of the TRB pointed to by
+	 * the dequeue pointer.
+	 */
+	if (xhci->quirks & XHCI_EP_CTX_BROKEN_DCS &&
+	    !(ep->ep_state & EP_HAS_STREAMS))
+		halted_seg = trb_in_td(xhci, cur_td->start_seg,
+				       cur_td->first_trb, cur_td->last_trb,
+				       hw_dequeue & ~0xf, false);
+	if (halted_seg) {
+		index = ((dma_addr_t)(hw_dequeue & ~0xf) - halted_seg->dma) /
+			 sizeof(*halted_trb);
+		halted_trb = &halted_seg->trbs[index];
+		state->new_cycle_state = halted_trb->generic.field[3] & 0x1;
+		xhci_dbg(xhci, "Endpoint DCS = %d TRB index = %d cycle = %d\n",
+			 (u8)(hw_dequeue & 0x1), index,
+			 state->new_cycle_state);
+	} else {
+		state->new_cycle_state = hw_dequeue & 0x1;
+	}
 	state->stream_id = stream_id;
 
 	/*
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:633 @
 
 	} while (!cycle_found || !td_last_trb_found);
 
+	/*
+	 * Quirk: the xHC does not correctly parse link TRBs if the HW Dequeue
+	 * pointer is set to one. Advance to the next TRB (and next segment).
+	 */
+	if (xhci->quirks & XHCI_AVOID_DQ_ON_LINK && trb_is_link(new_deq)) {
+		if (link_trb_toggles_cycle(new_deq))
+			state->new_cycle_state ^= 0x1;
+		next_trb(xhci, ep_ring, &new_seg, &new_deq);
+	}
+
 	state->new_deq_seg = new_seg;
 	state->new_deq_ptr = new_deq;
 
diff -Nur --no-dereference linux-5.10.52-orig/drivers/usb/Makefile linux-5.10.52-v7l+/drivers/usb/Makefile
--- linux-5.10.52-orig/drivers/usb/Makefile	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/usb/Makefile	2021-07-25 16:46:27.187935092 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:12 @
 obj-$(CONFIG_USB)		+= core/
 obj-$(CONFIG_USB_SUPPORT)	+= phy/
 
+obj-$(CONFIG_USB_DWCOTG)	+= host/
 obj-$(CONFIG_USB_DWC3)		+= dwc3/
 obj-$(CONFIG_USB_DWC2)		+= dwc2/
 obj-$(CONFIG_USB_ISP1760)	+= isp1760/
diff -Nur --no-dereference linux-5.10.52-orig/drivers/video/backlight/Kconfig linux-5.10.52-v7l+/drivers/video/backlight/Kconfig
--- linux-5.10.52-orig/drivers/video/backlight/Kconfig	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/video/backlight/Kconfig	2021-07-25 16:46:29.107902904 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:251 @
 	  If you have a LCD backlight adjustable by PWM, say Y to enable
 	  this driver.
 
+config BACKLIGHT_RPI
+	tristate "Raspberry Pi display firmware driven backlight"
+	depends on RASPBERRYPI_FIRMWARE
+	help
+	  If you have the Raspberry Pi DSI touchscreen display, say Y to
+	  enable the mailbox-controlled backlight driver.
+
 config BACKLIGHT_DA903X
 	tristate "Backlight Driver for DA9030/DA9034 using WLED"
 	depends on PMIC_DA903X
diff -Nur --no-dereference linux-5.10.52-orig/drivers/video/backlight/Makefile linux-5.10.52-v7l+/drivers/video/backlight/Makefile
--- linux-5.10.52-orig/drivers/video/backlight/Makefile	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/video/backlight/Makefile	2021-07-25 16:46:29.107902904 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:52 @
 obj-$(CONFIG_BACKLIGHT_PCF50633)	+= pcf50633-backlight.o
 obj-$(CONFIG_BACKLIGHT_PWM)		+= pwm_bl.o
 obj-$(CONFIG_BACKLIGHT_QCOM_WLED)	+= qcom-wled.o
+obj-$(CONFIG_BACKLIGHT_RPI)			+= rpi_backlight.o
 obj-$(CONFIG_BACKLIGHT_SAHARA)		+= kb3886_bl.o
 obj-$(CONFIG_BACKLIGHT_SKY81452)	+= sky81452-backlight.o
 obj-$(CONFIG_BACKLIGHT_TOSA)		+= tosa_bl.o
diff -Nur --no-dereference linux-5.10.52-orig/drivers/video/backlight/rpi_backlight.c linux-5.10.52-v7l+/drivers/video/backlight/rpi_backlight.c
--- linux-5.10.52-orig/drivers/video/backlight/rpi_backlight.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/video/backlight/rpi_backlight.c	2021-07-25 16:46:29.117902736 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/*
+ * rpi_bl.c - Backlight controller through VPU
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/backlight.h>
+#include <linux/err.h>
+#include <linux/fb.h>
+#include <linux/gpio.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <soc/bcm2835/raspberrypi-firmware.h>
+
+struct rpi_backlight {
+	struct device *dev;
+	struct device *fbdev;
+	struct rpi_firmware *fw;
+};
+
+static int rpi_backlight_update_status(struct backlight_device *bl)
+{
+	struct rpi_backlight *gbl = bl_get_data(bl);
+	int brightness = bl->props.brightness;
+	int ret;
+
+	if (bl->props.power != FB_BLANK_UNBLANK ||
+	    bl->props.fb_blank != FB_BLANK_UNBLANK ||
+	    bl->props.state & (BL_CORE_SUSPENDED | BL_CORE_FBBLANK))
+		brightness = 0;
+
+	ret = rpi_firmware_property(gbl->fw,
+			RPI_FIRMWARE_FRAMEBUFFER_SET_BACKLIGHT,
+			&brightness, sizeof(brightness));
+	if (ret) {
+		dev_err(gbl->dev, "Failed to set brightness\n");
+		return ret;
+	}
+
+	if (brightness < 0) {
+		dev_err(gbl->dev, "Backlight change failed\n");
+		return -EAGAIN;
+	}
+
+	return 0;
+}
+
+static const struct backlight_ops rpi_backlight_ops = {
+	.options	= BL_CORE_SUSPENDRESUME,
+	.update_status	= rpi_backlight_update_status,
+};
+
+static int rpi_backlight_probe(struct platform_device *pdev)
+{
+	struct backlight_properties props;
+	struct backlight_device *bl;
+	struct rpi_backlight *gbl;
+	struct device_node *fw_node;
+
+	gbl = devm_kzalloc(&pdev->dev, sizeof(*gbl), GFP_KERNEL);
+	if (gbl == NULL)
+		return -ENOMEM;
+
+	gbl->dev = &pdev->dev;
+
+	fw_node = of_parse_phandle(pdev->dev.of_node, "firmware", 0);
+	if (!fw_node) {
+		dev_err(&pdev->dev, "Missing firmware node\n");
+		return -ENOENT;
+	}
+
+	gbl->fw = rpi_firmware_get(fw_node);
+	if (!gbl->fw)
+		return -EPROBE_DEFER;
+
+	memset(&props, 0, sizeof(props));
+	props.type = BACKLIGHT_RAW;
+	props.max_brightness = 255;
+	bl = devm_backlight_device_register(&pdev->dev, dev_name(&pdev->dev),
+					&pdev->dev, gbl, &rpi_backlight_ops,
+					&props);
+	if (IS_ERR(bl)) {
+		dev_err(&pdev->dev, "failed to register backlight\n");
+		return PTR_ERR(bl);
+	}
+
+	bl->props.brightness = 255;
+	backlight_update_status(bl);
+
+	platform_set_drvdata(pdev, bl);
+	return 0;
+}
+
+static const struct of_device_id rpi_backlight_of_match[] = {
+	{ .compatible = "raspberrypi,rpi-backlight" },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, rpi_backlight_of_match);
+
+static struct platform_driver rpi_backlight_driver = {
+	.driver		= {
+		.name		= "rpi-backlight",
+		.of_match_table = of_match_ptr(rpi_backlight_of_match),
+	},
+	.probe		= rpi_backlight_probe,
+};
+
+module_platform_driver(rpi_backlight_driver);
+
+MODULE_AUTHOR("Gordon Hollingworth <gordon@raspberrypi.org>");
+MODULE_DESCRIPTION("Raspberry Pi mailbox based Backlight Driver");
+MODULE_LICENSE("GPL");
diff -Nur --no-dereference linux-5.10.52-orig/drivers/video/fbdev/bcm2708_fb.c linux-5.10.52-v7l+/drivers/video/fbdev/bcm2708_fb.c
--- linux-5.10.52-orig/drivers/video/fbdev/bcm2708_fb.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/video/fbdev/bcm2708_fb.c	2021-07-25 16:46:29.177901730 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/*
+ *  linux/drivers/video/bcm2708_fb.c
+ *
+ * Copyright (C) 2010 Broadcom
+ * Copyright (C) 2018 Raspberry Pi (Trading) Ltd
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file COPYING in the main directory of this archive
+ * for more details.
+ *
+ * Broadcom simple framebuffer driver
+ *
+ * This file is derived from cirrusfb.c
+ * Copyright 1999-2001 Jeff Garzik <jgarzik@pobox.com>
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/fb.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/list.h>
+#include <linux/platform_data/dma-bcm2708.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/printk.h>
+#include <linux/console.h>
+#include <linux/debugfs.h>
+#include <linux/uaccess.h>
+#include <linux/io.h>
+#include <linux/dma-mapping.h>
+#include <linux/cred.h>
+#include <soc/bcm2835/raspberrypi-firmware.h>
+#include <linux/mutex.h>
+#include <linux/compat.h>
+
+//#define BCM2708_FB_DEBUG
+#define MODULE_NAME "bcm2708_fb"
+
+#ifdef BCM2708_FB_DEBUG
+#define print_debug(fmt, ...) pr_debug("%s:%s:%d: " fmt, \
+			MODULE_NAME, __func__, __LINE__, ##__VA_ARGS__)
+#else
+#define print_debug(fmt, ...)
+#endif
+
+/* This is limited to 16 characters when displayed by X startup */
+static const char *bcm2708_name = "BCM2708 FB";
+
+#define DRIVER_NAME "bcm2708_fb"
+
+static int fbwidth = 800;	/* module parameter */
+static int fbheight = 480;	/* module parameter */
+static int fbdepth = 32;	/* module parameter */
+static int fbswap;		/* module parameter */
+
+static u32 dma_busy_wait_threshold = 1 << 15;
+module_param(dma_busy_wait_threshold, int, 0644);
+MODULE_PARM_DESC(dma_busy_wait_threshold, "Busy-wait for DMA completion below this area");
+
+struct fb_alloc_tags {
+	struct rpi_firmware_property_tag_header tag1;
+	u32 xres, yres;
+	struct rpi_firmware_property_tag_header tag2;
+	u32 xres_virtual, yres_virtual;
+	struct rpi_firmware_property_tag_header tag3;
+	u32 bpp;
+	struct rpi_firmware_property_tag_header tag4;
+	u32 xoffset, yoffset;
+	struct rpi_firmware_property_tag_header tag5;
+	u32 base, screen_size;
+	struct rpi_firmware_property_tag_header tag6;
+	u32 pitch;
+};
+
+struct bcm2708_fb_stats {
+	struct debugfs_regset32 regset;
+	u32 dma_copies;
+	u32 dma_irqs;
+};
+
+struct vc4_display_settings_t {
+	u32 display_num;
+	u32 width;
+	u32 height;
+	u32 depth;
+	u32 pitch;
+	u32 virtual_width;
+	u32 virtual_height;
+	u32 virtual_width_offset;
+	u32 virtual_height_offset;
+	unsigned long fb_bus_address;
+};
+
+struct bcm2708_fb_dev;
+
+struct bcm2708_fb {
+	struct fb_info fb;
+	struct platform_device *dev;
+	u32 cmap[16];
+	u32 gpu_cmap[256];
+	struct dentry *debugfs_dir;
+	struct dentry *debugfs_subdir;
+	unsigned long fb_bus_address;
+	struct { u32 base, length; } gpu;
+	struct vc4_display_settings_t display_settings;
+	struct debugfs_regset32 screeninfo_regset;
+	struct bcm2708_fb_dev *fbdev;
+	unsigned int image_size;
+	dma_addr_t dma_addr;
+	void *cpuaddr;
+};
+
+#define MAX_FRAMEBUFFERS 3
+
+struct bcm2708_fb_dev {
+	int firmware_supports_multifb;
+	/* Protects the DMA system from multiple FB access */
+	struct mutex dma_mutex;
+	int dma_chan;
+	int dma_irq;
+	void __iomem *dma_chan_base;
+	wait_queue_head_t dma_waitq;
+	bool disable_arm_alloc;
+	struct bcm2708_fb_stats dma_stats;
+	void *cb_base;	/* DMA control blocks */
+	dma_addr_t cb_handle;
+	int instance_count;
+	int num_displays;
+	struct rpi_firmware *fw;
+	struct bcm2708_fb displays[MAX_FRAMEBUFFERS];
+};
+
+#define to_bcm2708(info)	container_of(info, struct bcm2708_fb, fb)
+
+static void bcm2708_fb_debugfs_deinit(struct bcm2708_fb *fb)
+{
+	debugfs_remove_recursive(fb->debugfs_subdir);
+	fb->debugfs_subdir = NULL;
+
+	fb->fbdev->instance_count--;
+
+	if (!fb->fbdev->instance_count) {
+		debugfs_remove_recursive(fb->debugfs_dir);
+		fb->debugfs_dir = NULL;
+	}
+}
+
+static int bcm2708_fb_debugfs_init(struct bcm2708_fb *fb)
+{
+	char buf[3];
+	struct bcm2708_fb_dev *fbdev = fb->fbdev;
+
+	static struct debugfs_reg32 stats_registers[] = {
+	{"dma_copies", offsetof(struct bcm2708_fb_stats, dma_copies)},
+	{"dma_irqs",   offsetof(struct bcm2708_fb_stats, dma_irqs)},
+	};
+
+	static struct debugfs_reg32 screeninfo[] = {
+	{"width",	 offsetof(struct fb_var_screeninfo, xres)},
+	{"height",	 offsetof(struct fb_var_screeninfo, yres)},
+	{"bpp",		 offsetof(struct fb_var_screeninfo, bits_per_pixel)},
+	{"xres_virtual", offsetof(struct fb_var_screeninfo, xres_virtual)},
+	{"yres_virtual", offsetof(struct fb_var_screeninfo, yres_virtual)},
+	{"xoffset",	 offsetof(struct fb_var_screeninfo, xoffset)},
+	{"yoffset",	 offsetof(struct fb_var_screeninfo, yoffset)},
+	};
+
+	fb->debugfs_dir = debugfs_lookup(DRIVER_NAME, NULL);
+
+	if (!fb->debugfs_dir)
+		fb->debugfs_dir = debugfs_create_dir(DRIVER_NAME, NULL);
+
+	if (!fb->debugfs_dir) {
+		dev_warn(fb->fb.dev, "%s: could not create debugfs folder\n",
+			 __func__);
+		return -EFAULT;
+	}
+
+	snprintf(buf, sizeof(buf), "%u", fb->display_settings.display_num);
+
+	fb->debugfs_subdir = debugfs_create_dir(buf, fb->debugfs_dir);
+
+	if (!fb->debugfs_subdir) {
+		dev_warn(fb->fb.dev, "%s: could not create debugfs entry %u\n",
+			 __func__, fb->display_settings.display_num);
+		return -EFAULT;
+	}
+
+	fbdev->dma_stats.regset.regs = stats_registers;
+	fbdev->dma_stats.regset.nregs = ARRAY_SIZE(stats_registers);
+	fbdev->dma_stats.regset.base = &fbdev->dma_stats;
+
+	debugfs_create_regset32("dma_stats", 0444, fb->debugfs_subdir,
+				&fbdev->dma_stats.regset);
+
+	fb->screeninfo_regset.regs = screeninfo;
+	fb->screeninfo_regset.nregs = ARRAY_SIZE(screeninfo);
+	fb->screeninfo_regset.base = &fb->fb.var;
+
+	debugfs_create_regset32("screeninfo", 0444, fb->debugfs_subdir,
+				&fb->screeninfo_regset);
+
+	fbdev->instance_count++;
+
+	return 0;
+}
+
+static void set_display_num(struct bcm2708_fb *fb)
+{
+	if (fb && fb->fbdev && fb->fbdev->firmware_supports_multifb) {
+		u32 tmp = fb->display_settings.display_num;
+
+		if (rpi_firmware_property(fb->fbdev->fw,
+					  RPI_FIRMWARE_FRAMEBUFFER_SET_DISPLAY_NUM,
+					  &tmp,
+					  sizeof(tmp)))
+			dev_warn_once(fb->fb.dev,
+				      "Set display number call failed. Old GPU firmware?");
+	}
+}
+
+static int bcm2708_fb_set_bitfields(struct fb_var_screeninfo *var)
+{
+	int ret = 0;
+
+	memset(&var->transp, 0, sizeof(var->transp));
+
+	var->red.msb_right = 0;
+	var->green.msb_right = 0;
+	var->blue.msb_right = 0;
+
+	switch (var->bits_per_pixel) {
+	case 1:
+	case 2:
+	case 4:
+	case 8:
+		var->red.length = var->bits_per_pixel;
+		var->red.offset = 0;
+		var->green.length = var->bits_per_pixel;
+		var->green.offset = 0;
+		var->blue.length = var->bits_per_pixel;
+		var->blue.offset = 0;
+		break;
+	case 16:
+		var->red.length = 5;
+		var->blue.length = 5;
+		/*
+		 * Green length can be 5 or 6 depending whether
+		 * we're operating in RGB555 or RGB565 mode.
+		 */
+		if (var->green.length != 5 && var->green.length != 6)
+			var->green.length = 6;
+		break;
+	case 24:
+		var->red.length = 8;
+		var->blue.length = 8;
+		var->green.length = 8;
+		break;
+	case 32:
+		var->red.length = 8;
+		var->green.length = 8;
+		var->blue.length = 8;
+		var->transp.length = 8;
+		break;
+	default:
+		ret = -EINVAL;
+		break;
+	}
+
+	/*
+	 * >= 16bpp displays have separate colour component bitfields
+	 * encoded in the pixel data.  Calculate their position from
+	 * the bitfield length defined above.
+	 */
+	if (ret == 0 && var->bits_per_pixel >= 24 && fbswap) {
+		var->blue.offset = 0;
+		var->green.offset = var->blue.offset + var->blue.length;
+		var->red.offset = var->green.offset + var->green.length;
+		var->transp.offset = var->red.offset + var->red.length;
+	} else if (ret == 0 && var->bits_per_pixel >= 24) {
+		var->red.offset = 0;
+		var->green.offset = var->red.offset + var->red.length;
+		var->blue.offset = var->green.offset + var->green.length;
+		var->transp.offset = var->blue.offset + var->blue.length;
+	} else if (ret == 0 && var->bits_per_pixel >= 16) {
+		var->blue.offset = 0;
+		var->green.offset = var->blue.offset + var->blue.length;
+		var->red.offset = var->green.offset + var->green.length;
+		var->transp.offset = var->red.offset + var->red.length;
+	}
+
+	return ret;
+}
+
+static int bcm2708_fb_check_var(struct fb_var_screeninfo *var,
+				struct fb_info *info)
+{
+	/* info input, var output */
+	print_debug("%s(%p) %ux%u (%ux%u), %ul, %u\n",
+		    __func__, info, info->var.xres, info->var.yres,
+		    info->var.xres_virtual, info->var.yres_virtual,
+		    info->screen_size, info->var.bits_per_pixel);
+	print_debug("%s(%p) %ux%u (%ux%u), %u\n", __func__, var, var->xres,
+		    var->yres, var->xres_virtual, var->yres_virtual,
+		    var->bits_per_pixel);
+
+	if (!var->bits_per_pixel)
+		var->bits_per_pixel = 16;
+
+	if (bcm2708_fb_set_bitfields(var) != 0) {
+		pr_err("%s: invalid bits_per_pixel %d\n", __func__,
+		       var->bits_per_pixel);
+		return -EINVAL;
+	}
+
+	if (var->xres_virtual < var->xres)
+		var->xres_virtual = var->xres;
+	/* use highest possible virtual resolution */
+	if (var->yres_virtual == -1) {
+		var->yres_virtual = 480;
+
+		pr_err("%s: virtual resolution set to maximum of %dx%d\n",
+		       __func__, var->xres_virtual, var->yres_virtual);
+	}
+	if (var->yres_virtual < var->yres)
+		var->yres_virtual = var->yres;
+
+	if (var->xoffset < 0)
+		var->xoffset = 0;
+	if (var->yoffset < 0)
+		var->yoffset = 0;
+
+	/* truncate xoffset and yoffset to maximum if too high */
+	if (var->xoffset > var->xres_virtual - var->xres)
+		var->xoffset = var->xres_virtual - var->xres - 1;
+	if (var->yoffset > var->yres_virtual - var->yres)
+		var->yoffset = var->yres_virtual - var->yres - 1;
+
+	return 0;
+}
+
+static int bcm2708_fb_set_par(struct fb_info *info)
+{
+	struct bcm2708_fb *fb = to_bcm2708(info);
+	struct fb_alloc_tags fbinfo = {
+		.tag1 = { RPI_FIRMWARE_FRAMEBUFFER_SET_PHYSICAL_WIDTH_HEIGHT,
+			  8, 0, },
+			.xres = info->var.xres,
+			.yres = info->var.yres,
+		.tag2 = { RPI_FIRMWARE_FRAMEBUFFER_SET_VIRTUAL_WIDTH_HEIGHT,
+			  8, 0, },
+			.xres_virtual = info->var.xres_virtual,
+			.yres_virtual = info->var.yres_virtual,
+		.tag3 = { RPI_FIRMWARE_FRAMEBUFFER_SET_DEPTH, 4, 0 },
+			.bpp = info->var.bits_per_pixel,
+		.tag4 = { RPI_FIRMWARE_FRAMEBUFFER_SET_VIRTUAL_OFFSET, 8, 0 },
+			.xoffset = info->var.xoffset,
+			.yoffset = info->var.yoffset,
+		.tag5 = { RPI_FIRMWARE_FRAMEBUFFER_ALLOCATE, 8, 0 },
+			/* base and screen_size will be initialised later */
+		.tag6 = { RPI_FIRMWARE_FRAMEBUFFER_SET_PITCH, 4, 0 },
+			/* pitch will be initialised later */
+	};
+	int ret, image_size;
+
+	print_debug("%s(%p) %dx%d (%dx%d), %d, %d (display %d)\n", __func__,
+		    info,
+		    info->var.xres, info->var.yres, info->var.xres_virtual,
+		    info->var.yres_virtual, (int)info->screen_size,
+		    info->var.bits_per_pixel, value);
+
+	/* Need to set the display number to act on first
+	 * Cannot do it in the tag list because on older firmware the call
+	 * will fail and stop the rest of the list being executed.
+	 * We can ignore this call failing as the default at other end is 0
+	 */
+	set_display_num(fb);
+
+	/* Try allocating our own buffer. We can specify all the parameters */
+	image_size = ((info->var.xres * info->var.yres) *
+		      info->var.bits_per_pixel) >> 3;
+
+	if (!fb->fbdev->disable_arm_alloc &&
+	    (image_size != fb->image_size || !fb->dma_addr)) {
+		if (fb->dma_addr) {
+			dma_free_coherent(info->device, fb->image_size,
+					  fb->cpuaddr, fb->dma_addr);
+			fb->image_size = 0;
+			fb->cpuaddr = NULL;
+			fb->dma_addr = 0;
+		}
+
+		fb->cpuaddr = dma_alloc_coherent(info->device, image_size,
+						 &fb->dma_addr, GFP_KERNEL);
+
+		if (!fb->cpuaddr) {
+			fb->dma_addr = 0;
+			fb->fbdev->disable_arm_alloc = true;
+		} else {
+			fb->image_size = image_size;
+		}
+	}
+
+	if (fb->cpuaddr) {
+		fbinfo.base = fb->dma_addr;
+		fbinfo.screen_size = image_size;
+		fbinfo.pitch = (info->var.xres * info->var.bits_per_pixel) >> 3;
+
+		ret = rpi_firmware_property_list(fb->fbdev->fw, &fbinfo,
+						 sizeof(fbinfo));
+		if (ret || fbinfo.base != fb->dma_addr) {
+			/* Firmware either failed, or assigned a different base
+			 * address (ie it doesn't support being passed an FB
+			 * allocation).
+			 * Destroy the allocation, and don't try again.
+			 */
+			dma_free_coherent(info->device, fb->image_size,
+					  fb->cpuaddr, fb->dma_addr);
+			fb->image_size = 0;
+			fb->cpuaddr = NULL;
+			fb->dma_addr = 0;
+			fb->fbdev->disable_arm_alloc = true;
+		}
+	} else {
+		/* Our allocation failed - drop into the old scheme of
+		 * allocation by the VPU.
+		 */
+		ret = -ENOMEM;
+	}
+
+	if (ret) {
+		/* Old scheme:
+		 * - FRAMEBUFFER_ALLOCATE passes 0 for base and screen_size.
+		 * - GET_PITCH instead of SET_PITCH.
+		 */
+		fbinfo.base = 0;
+		fbinfo.screen_size = 0;
+		fbinfo.tag6.tag = RPI_FIRMWARE_FRAMEBUFFER_GET_PITCH;
+		fbinfo.pitch = 0;
+
+		ret = rpi_firmware_property_list(fb->fbdev->fw, &fbinfo,
+						 sizeof(fbinfo));
+		if (ret) {
+			dev_err(info->device,
+				"Failed to allocate GPU framebuffer (%d)\n",
+				ret);
+			return ret;
+		}
+	}
+
+	if (info->var.bits_per_pixel <= 8)
+		fb->fb.fix.visual = FB_VISUAL_PSEUDOCOLOR;
+	else
+		fb->fb.fix.visual = FB_VISUAL_TRUECOLOR;
+
+	fb->fb.fix.line_length = fbinfo.pitch;
+	fbinfo.base |= 0x40000000;
+	fb->fb_bus_address = fbinfo.base;
+	fbinfo.base &= ~0xc0000000;
+	fb->fb.fix.smem_start = fbinfo.base;
+	fb->fb.fix.smem_len = fbinfo.pitch * fbinfo.yres_virtual;
+	fb->fb.screen_size = fbinfo.screen_size;
+
+	if (!fb->dma_addr) {
+		if (fb->fb.screen_base)
+			iounmap(fb->fb.screen_base);
+
+		fb->fb.screen_base = ioremap_wc(fbinfo.base,
+						fb->fb.screen_size);
+	} else {
+		fb->fb.screen_base = fb->cpuaddr;
+	}
+
+	if (!fb->fb.screen_base) {
+		/* the console may currently be locked */
+		console_trylock();
+		console_unlock();
+		dev_err(info->device, "Failed to set screen_base\n");
+		return -ENOMEM;
+	}
+
+	print_debug("%s: start = %p,%p width=%d, height=%d, bpp=%d, pitch=%d size=%d\n",
+		    __func__, (void *)fb->fb.screen_base,
+		    (void *)fb->fb_bus_address, fbinfo.xres, fbinfo.yres,
+		    fbinfo.bpp, fbinfo.pitch, (int)fb->fb.screen_size);
+
+	return 0;
+}
+
+static inline u32 convert_bitfield(int val, struct fb_bitfield *bf)
+{
+	unsigned int mask = (1 << bf->length) - 1;
+
+	return (val >> (16 - bf->length) & mask) << bf->offset;
+}
+
+static int bcm2708_fb_setcolreg(unsigned int regno, unsigned int red,
+				unsigned int green, unsigned int blue,
+				unsigned int transp, struct fb_info *info)
+{
+	struct bcm2708_fb *fb = to_bcm2708(info);
+
+	if (fb->fb.var.bits_per_pixel <= 8) {
+		if (regno < 256) {
+			/* blue [23:16], green [15:8], red [7:0] */
+			fb->gpu_cmap[regno] = ((red   >> 8) & 0xff) << 0 |
+					      ((green >> 8) & 0xff) << 8 |
+					      ((blue  >> 8) & 0xff) << 16;
+		}
+		/* Hack: we need to tell GPU the palette has changed, but
+		 * currently bcm2708_fb_set_par takes noticeable time when
+		 * called for every (256) colour
+		 * So just call it for what looks like the last colour in a
+		 * list for now.
+		 */
+		if (regno == 15 || regno == 255) {
+			struct packet {
+				u32 offset;
+				u32 length;
+				u32 cmap[256];
+			} *packet;
+			int ret;
+
+			packet = kmalloc(sizeof(*packet), GFP_KERNEL);
+			if (!packet)
+				return -ENOMEM;
+			packet->offset = 0;
+			packet->length = regno + 1;
+			memcpy(packet->cmap, fb->gpu_cmap,
+			       sizeof(packet->cmap));
+
+			set_display_num(fb);
+
+			ret = rpi_firmware_property(fb->fbdev->fw,
+						    RPI_FIRMWARE_FRAMEBUFFER_SET_PALETTE,
+						    packet,
+						    (2 + packet->length) * sizeof(u32));
+			if (ret || packet->offset)
+				dev_err(info->device,
+					"Failed to set palette (%d,%u)\n",
+					ret, packet->offset);
+			kfree(packet);
+		}
+	} else if (regno < 16) {
+		fb->cmap[regno] = convert_bitfield(transp, &fb->fb.var.transp) |
+				  convert_bitfield(blue, &fb->fb.var.blue) |
+				  convert_bitfield(green, &fb->fb.var.green) |
+				  convert_bitfield(red, &fb->fb.var.red);
+	}
+	return regno > 255;
+}
+
+static int bcm2708_fb_blank(int blank_mode, struct fb_info *info)
+{
+	struct bcm2708_fb *fb = to_bcm2708(info);
+	u32 value;
+	int ret;
+
+	switch (blank_mode) {
+	case FB_BLANK_UNBLANK:
+		value = 0;
+		break;
+	case FB_BLANK_NORMAL:
+	case FB_BLANK_VSYNC_SUSPEND:
+	case FB_BLANK_HSYNC_SUSPEND:
+	case FB_BLANK_POWERDOWN:
+		value = 1;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	set_display_num(fb);
+
+	ret = rpi_firmware_property(fb->fbdev->fw, RPI_FIRMWARE_FRAMEBUFFER_BLANK,
+				    &value, sizeof(value));
+
+	if (ret)
+		dev_err(info->device, "%s(%d) failed: %d\n", __func__,
+			blank_mode, ret);
+
+	return ret;
+}
+
+static int bcm2708_fb_pan_display(struct fb_var_screeninfo *var,
+				  struct fb_info *info)
+{
+	s32 result;
+
+	info->var.xoffset = var->xoffset;
+	info->var.yoffset = var->yoffset;
+	result = bcm2708_fb_set_par(info);
+	if (result != 0)
+		pr_err("%s(%u,%u) returns=%d\n", __func__, var->xoffset,
+		       var->yoffset, result);
+	return result;
+}
+
+static void dma_memcpy(struct bcm2708_fb *fb, dma_addr_t dst, dma_addr_t src,
+		       int size)
+{
+	struct bcm2708_fb_dev *fbdev = fb->fbdev;
+	struct bcm2708_dma_cb *cb = fbdev->cb_base;
+	int burst_size = (fbdev->dma_chan == 0) ? 8 : 2;
+
+	cb->info = BCM2708_DMA_BURST(burst_size) | BCM2708_DMA_S_WIDTH |
+		   BCM2708_DMA_S_INC | BCM2708_DMA_D_WIDTH |
+		   BCM2708_DMA_D_INC;
+	cb->dst = dst;
+	cb->src = src;
+	cb->length = size;
+	cb->stride = 0;
+	cb->pad[0] = 0;
+	cb->pad[1] = 0;
+	cb->next = 0;
+
+	// Not sure what to do if this gets a signal whilst waiting
+	if (mutex_lock_interruptible(&fbdev->dma_mutex))
+		return;
+
+	if (size < dma_busy_wait_threshold) {
+		bcm_dma_start(fbdev->dma_chan_base, fbdev->cb_handle);
+		bcm_dma_wait_idle(fbdev->dma_chan_base);
+	} else {
+		void __iomem *local_dma_chan = fbdev->dma_chan_base;
+
+		cb->info |= BCM2708_DMA_INT_EN;
+		bcm_dma_start(fbdev->dma_chan_base, fbdev->cb_handle);
+		while (bcm_dma_is_busy(local_dma_chan)) {
+			wait_event_interruptible(fbdev->dma_waitq,
+						 !bcm_dma_is_busy(local_dma_chan));
+		}
+		fbdev->dma_stats.dma_irqs++;
+	}
+	fbdev->dma_stats.dma_copies++;
+
+	mutex_unlock(&fbdev->dma_mutex);
+}
+
+/* address with no aliases */
+#define INTALIAS_NORMAL(x) ((x) & ~0xc0000000)
+/* cache coherent but non-allocating in L1 and L2 */
+#define INTALIAS_L1L2_NONALLOCATING(x) (((x) & ~0xc0000000) | 0x80000000)
+
+static long vc_mem_copy(struct bcm2708_fb *fb, struct fb_dmacopy *ioparam)
+{
+	size_t size = PAGE_SIZE;
+	u32 *buf = NULL;
+	dma_addr_t bus_addr;
+	long rc = 0;
+	size_t offset;
+
+	/* restrict this to root user */
+	if (!uid_eq(current_euid(), GLOBAL_ROOT_UID)) {
+		rc = -EFAULT;
+		goto out;
+	}
+
+	if (!fb->gpu.base || !fb->gpu.length) {
+		pr_err("[%s]: Unable to determine gpu memory (%x,%x)\n",
+		       __func__, fb->gpu.base, fb->gpu.length);
+		return -EFAULT;
+	}
+
+	if (INTALIAS_NORMAL(ioparam->src) < fb->gpu.base ||
+	    INTALIAS_NORMAL(ioparam->src) >= fb->gpu.base + fb->gpu.length) {
+		pr_err("[%s]: Invalid memory access %x (%x-%x)", __func__,
+		       INTALIAS_NORMAL(ioparam->src), fb->gpu.base,
+		       fb->gpu.base + fb->gpu.length);
+		return -EFAULT;
+	}
+
+	buf = dma_alloc_coherent(fb->fb.device, PAGE_ALIGN(size), &bus_addr,
+				 GFP_ATOMIC);
+	if (!buf) {
+		pr_err("[%s]: failed to dma_alloc_coherent(%zd)\n", __func__,
+		       size);
+		rc = -ENOMEM;
+		goto out;
+	}
+
+	for (offset = 0; offset < ioparam->length; offset += size) {
+		size_t remaining = ioparam->length - offset;
+		size_t s = min(size, remaining);
+		u8 *p = (u8 *)((uintptr_t)ioparam->src + offset);
+		u8 *q = (u8 *)ioparam->dst + offset;
+
+		dma_memcpy(fb, bus_addr,
+			   INTALIAS_L1L2_NONALLOCATING((u32)(uintptr_t)p),
+						       size);
+		if (copy_to_user(q, buf, s) != 0) {
+			pr_err("[%s]: failed to copy-to-user\n", __func__);
+			rc = -EFAULT;
+			goto out;
+		}
+	}
+out:
+	if (buf)
+		dma_free_coherent(fb->fb.device, PAGE_ALIGN(size), buf,
+				  bus_addr);
+	return rc;
+}
+
+static int bcm2708_ioctl(struct fb_info *info, unsigned int cmd,
+			 unsigned long arg)
+{
+	struct bcm2708_fb *fb = to_bcm2708(info);
+	u32 dummy = 0;
+	int ret;
+
+	switch (cmd) {
+	case FBIO_WAITFORVSYNC:
+		set_display_num(fb);
+
+		ret = rpi_firmware_property(fb->fbdev->fw,
+					    RPI_FIRMWARE_FRAMEBUFFER_SET_VSYNC,
+					    &dummy, sizeof(dummy));
+		break;
+
+	case FBIODMACOPY:
+	{
+		struct fb_dmacopy ioparam;
+		/* Get the parameter data.
+		 */
+		if (copy_from_user
+		    (&ioparam, (void *)arg, sizeof(ioparam))) {
+			pr_err("[%s]: failed to copy-from-user\n", __func__);
+			ret = -EFAULT;
+			break;
+		}
+		ret = vc_mem_copy(fb, &ioparam);
+		break;
+	}
+	default:
+		dev_dbg(info->device, "Unknown ioctl 0x%x\n", cmd);
+		return -ENOTTY;
+	}
+
+	if (ret)
+		dev_err(info->device, "ioctl 0x%x failed (%d)\n", cmd, ret);
+
+	return ret;
+}
+
+#ifdef CONFIG_COMPAT
+struct fb_dmacopy32 {
+	compat_uptr_t dst;
+	__u32 src;
+	__u32 length;
+};
+
+#define FBIODMACOPY32		_IOW('z', 0x22, struct fb_dmacopy32)
+
+static int bcm2708_compat_ioctl(struct fb_info *info, unsigned int cmd,
+				unsigned long arg)
+{
+	struct bcm2708_fb *fb = to_bcm2708(info);
+	int ret;
+
+	switch (cmd) {
+	case FBIODMACOPY32:
+	{
+		struct fb_dmacopy32 param32;
+		struct fb_dmacopy param;
+		/* Get the parameter data.
+		 */
+		if (copy_from_user(&param32, (void *)arg, sizeof(param32))) {
+			pr_err("[%s]: failed to copy-from-user\n", __func__);
+			ret = -EFAULT;
+			break;
+		}
+		param.dst = compat_ptr(param32.dst);
+		param.src = param32.src;
+		param.length = param32.length;
+		ret = vc_mem_copy(fb, &param);
+		break;
+	}
+	default:
+		ret = bcm2708_ioctl(info, cmd, arg);
+		break;
+	}
+	return ret;
+}
+#endif
+
+static void bcm2708_fb_fillrect(struct fb_info *info,
+				const struct fb_fillrect *rect)
+{
+	cfb_fillrect(info, rect);
+}
+
+/* A helper function for configuring dma control block */
+static void set_dma_cb(struct bcm2708_dma_cb *cb,
+		int        burst_size,
+		dma_addr_t dst,
+		int        dst_stride,
+		dma_addr_t src,
+		int        src_stride,
+		int        w,
+		int        h)
+{
+	cb->info = BCM2708_DMA_BURST(burst_size) | BCM2708_DMA_S_WIDTH |
+		BCM2708_DMA_S_INC | BCM2708_DMA_D_WIDTH |
+		BCM2708_DMA_D_INC | BCM2708_DMA_TDMODE;
+	cb->dst = dst;
+	cb->src = src;
+	/*
+	 * This is not really obvious from the DMA documentation,
+	 * but the top 16 bits must be programmmed to "height -1"
+	 * and not "height" in 2D mode.
+	 */
+	cb->length = ((h - 1) << 16) | w;
+	cb->stride = ((dst_stride - w) << 16) | (u16)(src_stride - w);
+	cb->pad[0] = 0;
+	cb->pad[1] = 0;
+}
+
+static void bcm2708_fb_copyarea(struct fb_info *info,
+				const struct fb_copyarea *region)
+{
+	struct bcm2708_fb *fb = to_bcm2708(info);
+	struct bcm2708_fb_dev *fbdev = fb->fbdev;
+	struct bcm2708_dma_cb *cb = fbdev->cb_base;
+	int bytes_per_pixel = (info->var.bits_per_pixel + 7) >> 3;
+
+	/* Channel 0 supports larger bursts and is a bit faster */
+	int burst_size = (fbdev->dma_chan == 0) ? 8 : 2;
+	int pixels = region->width * region->height;
+
+	/* If DMA is currently in use (ie being used on another FB), then
+	 * rather than wait for it to finish, just use the cfb_copyarea
+	 */
+	if (!mutex_trylock(&fbdev->dma_mutex) ||
+	    bytes_per_pixel > 4 ||
+	    info->var.xres * info->var.yres > 1920 * 1200 ||
+	    region->width <= 0 || region->width > info->var.xres ||
+	    region->height <= 0 || region->height > info->var.yres ||
+	    region->sx < 0 || region->sx >= info->var.xres ||
+	    region->sy < 0 || region->sy >= info->var.yres ||
+	    region->dx < 0 || region->dx >= info->var.xres ||
+	    region->dy < 0 || region->dy >= info->var.yres ||
+	    region->sx + region->width > info->var.xres ||
+	    region->dx + region->width > info->var.xres ||
+	    region->sy + region->height > info->var.yres ||
+	    region->dy + region->height > info->var.yres) {
+		cfb_copyarea(info, region);
+		return;
+	}
+
+	if (region->dy == region->sy && region->dx > region->sx) {
+		/*
+		 * A difficult case of overlapped copy. Because DMA can't
+		 * copy individual scanlines in backwards direction, we need
+		 * two-pass processing. We do it by programming a chain of dma
+		 * control blocks in the first 16K part of the buffer and use
+		 * the remaining 48K as the intermediate temporary scratch
+		 * buffer. The buffer size is sufficient to handle up to
+		 * 1920x1200 resolution at 32bpp pixel depth.
+		 */
+		int y;
+		dma_addr_t control_block_pa = fbdev->cb_handle;
+		dma_addr_t scratchbuf = fbdev->cb_handle + 16 * 1024;
+		int scanline_size = bytes_per_pixel * region->width;
+		int scanlines_per_cb = (64 * 1024 - 16 * 1024) / scanline_size;
+
+		for (y = 0; y < region->height; y += scanlines_per_cb) {
+			dma_addr_t src =
+				fb->fb_bus_address +
+				bytes_per_pixel * region->sx +
+				(region->sy + y) * fb->fb.fix.line_length;
+			dma_addr_t dst =
+				fb->fb_bus_address +
+				bytes_per_pixel * region->dx +
+				(region->dy + y) * fb->fb.fix.line_length;
+
+			if (region->height - y < scanlines_per_cb)
+				scanlines_per_cb = region->height - y;
+
+			set_dma_cb(cb, burst_size, scratchbuf, scanline_size,
+				   src, fb->fb.fix.line_length,
+				   scanline_size, scanlines_per_cb);
+			control_block_pa += sizeof(struct bcm2708_dma_cb);
+			cb->next = control_block_pa;
+			cb++;
+
+			set_dma_cb(cb, burst_size, dst, fb->fb.fix.line_length,
+				   scratchbuf, scanline_size,
+				   scanline_size, scanlines_per_cb);
+			control_block_pa += sizeof(struct bcm2708_dma_cb);
+			cb->next = control_block_pa;
+			cb++;
+		}
+		/* move the pointer back to the last dma control block */
+		cb--;
+	} else {
+		/* A single dma control block is enough. */
+		int sy, dy, stride;
+
+		if (region->dy <= region->sy) {
+			/* processing from top to bottom */
+			dy = region->dy;
+			sy = region->sy;
+			stride = fb->fb.fix.line_length;
+		} else {
+			/* processing from bottom to top */
+			dy = region->dy + region->height - 1;
+			sy = region->sy + region->height - 1;
+			stride = -fb->fb.fix.line_length;
+		}
+		set_dma_cb(cb, burst_size,
+			   fb->fb_bus_address + dy * fb->fb.fix.line_length +
+			   bytes_per_pixel * region->dx,
+			   stride,
+			   fb->fb_bus_address + sy * fb->fb.fix.line_length +
+			   bytes_per_pixel * region->sx,
+			   stride,
+			   region->width * bytes_per_pixel,
+			   region->height);
+	}
+
+	/* end of dma control blocks chain */
+	cb->next = 0;
+
+	if (pixels < dma_busy_wait_threshold) {
+		bcm_dma_start(fbdev->dma_chan_base, fbdev->cb_handle);
+		bcm_dma_wait_idle(fbdev->dma_chan_base);
+	} else {
+		void __iomem *local_dma_chan = fbdev->dma_chan_base;
+
+		cb->info |= BCM2708_DMA_INT_EN;
+		bcm_dma_start(fbdev->dma_chan_base, fbdev->cb_handle);
+		while (bcm_dma_is_busy(local_dma_chan)) {
+			wait_event_interruptible(fbdev->dma_waitq,
+						 !bcm_dma_is_busy(local_dma_chan));
+		}
+		fbdev->dma_stats.dma_irqs++;
+	}
+	fbdev->dma_stats.dma_copies++;
+
+	mutex_unlock(&fbdev->dma_mutex);
+}
+
+static void bcm2708_fb_imageblit(struct fb_info *info,
+				 const struct fb_image *image)
+{
+	cfb_imageblit(info, image);
+}
+
+static irqreturn_t bcm2708_fb_dma_irq(int irq, void *cxt)
+{
+	struct bcm2708_fb_dev *fbdev = cxt;
+
+	/* FIXME: should read status register to check if this is
+	 * actually interrupting us or not, in case this interrupt
+	 * ever becomes shared amongst several DMA channels
+	 *
+	 * readl(dma_chan_base + BCM2708_DMA_CS) & BCM2708_DMA_IRQ;
+	 */
+
+	/* acknowledge the interrupt */
+	writel(BCM2708_DMA_INT, fbdev->dma_chan_base + BCM2708_DMA_CS);
+
+	wake_up(&fbdev->dma_waitq);
+	return IRQ_HANDLED;
+}
+
+static struct fb_ops bcm2708_fb_ops = {
+	.owner = THIS_MODULE,
+	.fb_check_var = bcm2708_fb_check_var,
+	.fb_set_par = bcm2708_fb_set_par,
+	.fb_setcolreg = bcm2708_fb_setcolreg,
+	.fb_blank = bcm2708_fb_blank,
+	.fb_fillrect = bcm2708_fb_fillrect,
+	.fb_copyarea = bcm2708_fb_copyarea,
+	.fb_imageblit = bcm2708_fb_imageblit,
+	.fb_pan_display = bcm2708_fb_pan_display,
+	.fb_ioctl = bcm2708_ioctl,
+#ifdef CONFIG_COMPAT
+	.fb_compat_ioctl = bcm2708_compat_ioctl,
+#endif
+};
+
+static int bcm2708_fb_register(struct bcm2708_fb *fb)
+{
+	int ret;
+
+	fb->fb.fbops = &bcm2708_fb_ops;
+	fb->fb.flags = FBINFO_FLAG_DEFAULT | FBINFO_HWACCEL_COPYAREA;
+	fb->fb.pseudo_palette = fb->cmap;
+
+	strncpy(fb->fb.fix.id, bcm2708_name, sizeof(fb->fb.fix.id));
+	fb->fb.fix.type = FB_TYPE_PACKED_PIXELS;
+	fb->fb.fix.type_aux = 0;
+	fb->fb.fix.xpanstep = 1;
+	fb->fb.fix.ypanstep = 1;
+	fb->fb.fix.ywrapstep = 0;
+	fb->fb.fix.accel = FB_ACCEL_NONE;
+
+	/* If we have data from the VC4 on FB's, use that, otherwise use the
+	 * module parameters
+	 */
+	if (fb->display_settings.width) {
+		fb->fb.var.xres = fb->display_settings.width;
+		fb->fb.var.yres = fb->display_settings.height;
+		fb->fb.var.xres_virtual = fb->fb.var.xres;
+		fb->fb.var.yres_virtual = fb->fb.var.yres;
+		fb->fb.var.bits_per_pixel = fb->display_settings.depth;
+	} else {
+		fb->fb.var.xres = fbwidth;
+		fb->fb.var.yres = fbheight;
+		fb->fb.var.xres_virtual = fbwidth;
+		fb->fb.var.yres_virtual = fbheight;
+		fb->fb.var.bits_per_pixel = fbdepth;
+	}
+
+	fb->fb.var.vmode = FB_VMODE_NONINTERLACED;
+	fb->fb.var.activate = FB_ACTIVATE_NOW;
+	fb->fb.var.nonstd = 0;
+	fb->fb.var.height = -1;		/* height of picture in mm    */
+	fb->fb.var.width = -1;		/* width of picture in mm    */
+	fb->fb.var.accel_flags = 0;
+
+	fb->fb.monspecs.hfmin = 0;
+	fb->fb.monspecs.hfmax = 100000;
+	fb->fb.monspecs.vfmin = 0;
+	fb->fb.monspecs.vfmax = 400;
+	fb->fb.monspecs.dclkmin = 1000000;
+	fb->fb.monspecs.dclkmax = 100000000;
+
+	bcm2708_fb_set_bitfields(&fb->fb.var);
+
+	/*
+	 * Allocate colourmap.
+	 */
+	fb_set_var(&fb->fb, &fb->fb.var);
+
+	ret = bcm2708_fb_set_par(&fb->fb);
+
+	if (ret)
+		return ret;
+
+	ret = register_framebuffer(&fb->fb);
+
+	if (ret == 0)
+		goto out;
+
+	dev_warn(fb->fb.dev, "Unable to register framebuffer (%d)\n", ret);
+out:
+	return ret;
+}
+
+static int bcm2708_fb_probe(struct platform_device *dev)
+{
+	struct device_node *fw_np;
+	struct rpi_firmware *fw;
+	int ret, i;
+	u32 num_displays;
+	struct bcm2708_fb_dev *fbdev;
+	struct { u32 base, length; } gpu_mem;
+
+	fbdev = devm_kzalloc(&dev->dev, sizeof(*fbdev), GFP_KERNEL);
+
+	if (!fbdev)
+		return -ENOMEM;
+
+	fw_np = of_parse_phandle(dev->dev.of_node, "firmware", 0);
+
+/* Remove comment when booting without Device Tree is no longer supported
+ *	if (!fw_np) {
+ *		dev_err(&dev->dev, "Missing firmware node\n");
+ *		return -ENOENT;
+ *	}
+ */
+	fw = rpi_firmware_get(fw_np);
+	fbdev->fw = fw;
+
+	if (!fw)
+		return -EPROBE_DEFER;
+
+	ret = rpi_firmware_property(fw,
+				    RPI_FIRMWARE_FRAMEBUFFER_GET_NUM_DISPLAYS,
+				    &num_displays, sizeof(u32));
+
+	/* If we fail to get the number of displays, or it returns 0, then
+	 * assume old firmware that doesn't have the mailbox call, so just
+	 * set one display
+	 */
+	if (ret || num_displays == 0) {
+		dev_err(&dev->dev,
+			"Unable to determine number of FBs. Disabling driver.\n");
+		return -ENOENT;
+	} else {
+		fbdev->firmware_supports_multifb = 1;
+	}
+
+	if (num_displays > MAX_FRAMEBUFFERS) {
+		dev_warn(&dev->dev,
+			 "More displays reported from firmware than supported in driver (%u vs %u)",
+			 num_displays, MAX_FRAMEBUFFERS);
+		num_displays = MAX_FRAMEBUFFERS;
+	}
+
+	dev_info(&dev->dev, "FB found %d display(s)\n", num_displays);
+
+	/* Set up the DMA information. Note we have just one set of DMA
+	 * parameters to work with all the FB's so requires synchronising when
+	 * being used
+	 */
+
+	mutex_init(&fbdev->dma_mutex);
+
+	fbdev->cb_base = dma_alloc_wc(&dev->dev, SZ_64K,
+						&fbdev->cb_handle,
+						GFP_KERNEL);
+	if (!fbdev->cb_base) {
+		dev_err(&dev->dev, "cannot allocate DMA CBs\n");
+		ret = -ENOMEM;
+		goto free_fb;
+	}
+
+	ret = bcm_dma_chan_alloc(BCM_DMA_FEATURE_BULK,
+				 &fbdev->dma_chan_base,
+				 &fbdev->dma_irq);
+	if (ret < 0) {
+		dev_err(&dev->dev, "Couldn't allocate a DMA channel\n");
+		goto free_cb;
+	}
+	fbdev->dma_chan = ret;
+
+	ret = request_irq(fbdev->dma_irq, bcm2708_fb_dma_irq,
+			  0, "bcm2708_fb DMA", fbdev);
+	if (ret) {
+		dev_err(&dev->dev,
+			"Failed to request DMA irq\n");
+		goto free_dma_chan;
+	}
+
+	rpi_firmware_property(fbdev->fw,
+			      RPI_FIRMWARE_GET_VC_MEMORY,
+			      &gpu_mem, sizeof(gpu_mem));
+
+	for (i = 0; i < num_displays; i++) {
+		struct bcm2708_fb *fb = &fbdev->displays[i];
+
+		fb->display_settings.display_num = i;
+		fb->dev = dev;
+		fb->fb.device = &dev->dev;
+		fb->fbdev = fbdev;
+
+		fb->gpu.base = gpu_mem.base;
+		fb->gpu.length = gpu_mem.length;
+
+		if (fbdev->firmware_supports_multifb) {
+			ret = rpi_firmware_property(fw,
+						    RPI_FIRMWARE_FRAMEBUFFER_GET_DISPLAY_SETTINGS,
+						    &fb->display_settings,
+						    GET_DISPLAY_SETTINGS_PAYLOAD_SIZE);
+		} else {
+			memset(&fb->display_settings, 0,
+			       sizeof(fb->display_settings));
+		}
+
+		ret = bcm2708_fb_register(fb);
+
+		if (ret == 0) {
+			bcm2708_fb_debugfs_init(fb);
+
+			fbdev->num_displays++;
+
+			dev_info(&dev->dev,
+				 "Registered framebuffer for display %u, size %ux%u\n",
+				 fb->display_settings.display_num,
+				 fb->fb.var.xres,
+				 fb->fb.var.yres);
+		} else {
+			// Use this to flag if this FB entry is in use.
+			fb->fbdev = NULL;
+		}
+	}
+
+	// Did we actually successfully create any FB's?
+	if (fbdev->num_displays) {
+		init_waitqueue_head(&fbdev->dma_waitq);
+		platform_set_drvdata(dev, fbdev);
+		return ret;
+	}
+
+free_dma_chan:
+	bcm_dma_chan_free(fbdev->dma_chan);
+free_cb:
+	dma_free_wc(&dev->dev, SZ_64K, fbdev->cb_base,
+			      fbdev->cb_handle);
+free_fb:
+	dev_err(&dev->dev, "probe failed, err %d\n", ret);
+
+	return ret;
+}
+
+static int bcm2708_fb_remove(struct platform_device *dev)
+{
+	struct bcm2708_fb_dev *fbdev = platform_get_drvdata(dev);
+	int i;
+
+	platform_set_drvdata(dev, NULL);
+
+	for (i = 0; i < fbdev->num_displays; i++) {
+		if (fbdev->displays[i].fb.screen_base)
+			iounmap(fbdev->displays[i].fb.screen_base);
+
+		if (fbdev->displays[i].fbdev) {
+			unregister_framebuffer(&fbdev->displays[i].fb);
+			bcm2708_fb_debugfs_deinit(&fbdev->displays[i]);
+		}
+	}
+
+	dma_free_wc(&dev->dev, SZ_64K, fbdev->cb_base,
+			      fbdev->cb_handle);
+	bcm_dma_chan_free(fbdev->dma_chan);
+	free_irq(fbdev->dma_irq, fbdev);
+
+	mutex_destroy(&fbdev->dma_mutex);
+
+	return 0;
+}
+
+static const struct of_device_id bcm2708_fb_of_match_table[] = {
+	{ .compatible = "brcm,bcm2708-fb", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, bcm2708_fb_of_match_table);
+
+static struct platform_driver bcm2708_fb_driver = {
+	.probe = bcm2708_fb_probe,
+	.remove = bcm2708_fb_remove,
+	.driver = {
+		  .name = DRIVER_NAME,
+		  .owner = THIS_MODULE,
+		  .of_match_table = bcm2708_fb_of_match_table,
+		  },
+};
+
+static int __init bcm2708_fb_init(void)
+{
+	return platform_driver_register(&bcm2708_fb_driver);
+}
+
+module_init(bcm2708_fb_init);
+
+static void __exit bcm2708_fb_exit(void)
+{
+	platform_driver_unregister(&bcm2708_fb_driver);
+}
+
+module_exit(bcm2708_fb_exit);
+
+module_param(fbwidth, int, 0644);
+module_param(fbheight, int, 0644);
+module_param(fbdepth, int, 0644);
+module_param(fbswap, int, 0644);
+
+MODULE_DESCRIPTION("BCM2708 framebuffer driver");
+MODULE_LICENSE("GPL");
+
+MODULE_PARM_DESC(fbwidth, "Width of ARM Framebuffer");
+MODULE_PARM_DESC(fbheight, "Height of ARM Framebuffer");
+MODULE_PARM_DESC(fbdepth, "Bit depth of ARM Framebuffer");
+MODULE_PARM_DESC(fbswap, "Swap order of red and blue in 24 and 32 bit modes");
diff -Nur --no-dereference linux-5.10.52-orig/drivers/video/fbdev/core/cfbimgblt.c linux-5.10.52-v7l+/drivers/video/fbdev/core/cfbimgblt.c
--- linux-5.10.52-orig/drivers/video/fbdev/core/cfbimgblt.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/video/fbdev/core/cfbimgblt.c	2021-07-25 16:46:29.187901562 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:31 @
  *
  *  Also need to add code to deal with cards endians that are different than
  *  the native cpu endians. I also need to deal with MSB position in the word.
+ *  Modified by Harm Hanemaaijer (fgenfb@yahoo.com) 2013:
+ *  - Provide optimized versions of fast_imageblit for 16 and 32bpp that are
+ *    significantly faster than the previous implementation.
+ *  - Simplify the fast/slow_imageblit selection code, avoiding integer
+ *    divides.
  */
 #include <linux/module.h>
 #include <linux/string.h>
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:270 @
 	}
 }	
 	
+/*
+ * Optimized fast_imageblit for bpp == 16. ppw = 2, bit_mask = 3 folded
+ * into the code, main loop unrolled.
+ */
+
+static inline void fast_imageblit16(const struct fb_image *image,
+				    struct fb_info *p, u8 __iomem * dst1,
+				    u32 fgcolor, u32 bgcolor)
+{
+	u32 fgx = fgcolor, bgx = bgcolor;
+	u32 spitch = (image->width + 7) / 8;
+	u32 end_mask, eorx;
+	const char *s = image->data, *src;
+	u32 __iomem *dst;
+	const u32 *tab = NULL;
+	int i, j, k;
+
+	tab = fb_be_math(p) ? cfb_tab16_be : cfb_tab16_le;
+
+	fgx <<= 16;
+	bgx <<= 16;
+	fgx |= fgcolor;
+	bgx |= bgcolor;
+
+	eorx = fgx ^ bgx;
+	k = image->width / 2;
+
+	for (i = image->height; i--;) {
+		dst = (u32 __iomem *) dst1;
+		src = s;
+
+		j = k;
+		while (j >= 4) {
+			u8 bits = *src;
+			end_mask = tab[(bits >> 6) & 3];
+			FB_WRITEL((end_mask & eorx) ^ bgx, dst++);
+			end_mask = tab[(bits >> 4) & 3];
+			FB_WRITEL((end_mask & eorx) ^ bgx, dst++);
+			end_mask = tab[(bits >> 2) & 3];
+			FB_WRITEL((end_mask & eorx) ^ bgx, dst++);
+			end_mask = tab[bits & 3];
+			FB_WRITEL((end_mask & eorx) ^ bgx, dst++);
+			src++;
+			j -= 4;
+		}
+		if (j != 0) {
+			u8 bits = *src;
+			end_mask = tab[(bits >> 6) & 3];
+			FB_WRITEL((end_mask & eorx) ^ bgx, dst++);
+			if (j >= 2) {
+				end_mask = tab[(bits >> 4) & 3];
+				FB_WRITEL((end_mask & eorx) ^ bgx, dst++);
+				if (j == 3) {
+					end_mask = tab[(bits >> 2) & 3];
+					FB_WRITEL((end_mask & eorx) ^ bgx, dst);
+				}
+			}
+		}
+		dst1 += p->fix.line_length;
+		s += spitch;
+	}
+}
+
+/*
+ * Optimized fast_imageblit for bpp == 32. ppw = 1, bit_mask = 1 folded
+ * into the code, main loop unrolled.
+ */
+
+static inline void fast_imageblit32(const struct fb_image *image,
+				    struct fb_info *p, u8 __iomem * dst1,
+				    u32 fgcolor, u32 bgcolor)
+{
+	u32 fgx = fgcolor, bgx = bgcolor;
+	u32 spitch = (image->width + 7) / 8;
+	u32 end_mask, eorx;
+	const char *s = image->data, *src;
+	u32 __iomem *dst;
+	const u32 *tab = NULL;
+	int i, j, k;
+
+	tab = cfb_tab32;
+
+	eorx = fgx ^ bgx;
+	k = image->width;
+
+	for (i = image->height; i--;) {
+		dst = (u32 __iomem *) dst1;
+		src = s;
+
+		j = k;
+		while (j >= 8) {
+			u8 bits = *src;
+			end_mask = tab[(bits >> 7) & 1];
+			FB_WRITEL((end_mask & eorx) ^ bgx, dst++);
+			end_mask = tab[(bits >> 6) & 1];
+			FB_WRITEL((end_mask & eorx) ^ bgx, dst++);
+			end_mask = tab[(bits >> 5) & 1];
+			FB_WRITEL((end_mask & eorx) ^ bgx, dst++);
+			end_mask = tab[(bits >> 4) & 1];
+			FB_WRITEL((end_mask & eorx) ^ bgx, dst++);
+			end_mask = tab[(bits >> 3) & 1];
+			FB_WRITEL((end_mask & eorx) ^ bgx, dst++);
+			end_mask = tab[(bits >> 2) & 1];
+			FB_WRITEL((end_mask & eorx) ^ bgx, dst++);
+			end_mask = tab[(bits >> 1) & 1];
+			FB_WRITEL((end_mask & eorx) ^ bgx, dst++);
+			end_mask = tab[bits & 1];
+			FB_WRITEL((end_mask & eorx) ^ bgx, dst++);
+			src++;
+			j -= 8;
+		}
+		if (j != 0) {
+			u32 bits = (u32) * src;
+			while (j > 1) {
+				end_mask = tab[(bits >> 7) & 1];
+				FB_WRITEL((end_mask & eorx) ^ bgx, dst++);
+				bits <<= 1;
+				j--;
+			}
+			end_mask = tab[(bits >> 7) & 1];
+			FB_WRITEL((end_mask & eorx) ^ bgx, dst);
+		}
+		dst1 += p->fix.line_length;
+		s += spitch;
+	}
+}
+
 void cfb_imageblit(struct fb_info *p, const struct fb_image *image)
 {
 	u32 fgcolor, bgcolor, start_index, bitstart, pitch_index = 0;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:429 @
 			bgcolor = image->bg_color;
 		}	
 		
-		if (32 % bpp == 0 && !start_index && !pitch_index && 
-		    ((width & (32/bpp-1)) == 0) &&
-		    bpp >= 8 && bpp <= 32) 			
-			fast_imageblit(image, p, dst1, fgcolor, bgcolor);
-		else 
+		if (!start_index && !pitch_index) {
+			if (bpp == 32)
+				fast_imageblit32(image, p, dst1, fgcolor,
+						 bgcolor);
+			else if (bpp == 16 && (width & 1) == 0)
+				fast_imageblit16(image, p, dst1, fgcolor,
+						 bgcolor);
+			else if (bpp == 8 && (width & 3) == 0)
+				fast_imageblit(image, p, dst1, fgcolor,
+					       bgcolor);
+			else
+				slow_imageblit(image, p, dst1, fgcolor,
+					       bgcolor,
+					       start_index, pitch_index);
+		} else
 			slow_imageblit(image, p, dst1, fgcolor, bgcolor,
 					start_index, pitch_index);
 	} else
diff -Nur --no-dereference linux-5.10.52-orig/drivers/video/fbdev/core/fbmem.c linux-5.10.52-v7l+/drivers/video/fbdev/core/fbmem.c
--- linux-5.10.52-orig/drivers/video/fbdev/core/fbmem.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/video/fbdev/core/fbmem.c	2021-07-25 16:46:29.187901562 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1082 @
 }
 EXPORT_SYMBOL(fb_blank);
 
+static int fb_copyarea_user(struct fb_info *info,
+			    struct fb_copyarea *copy)
+{
+	int ret = 0;
+	lock_fb_info(info);
+	if (copy->dx >= info->var.xres ||
+	    copy->sx >= info->var.xres ||
+	    copy->width > info->var.xres ||
+	    copy->dy >= info->var.yres ||
+	    copy->sy >= info->var.yres ||
+	    copy->height > info->var.yres ||
+	    copy->dx + copy->width > info->var.xres ||
+	    copy->sx + copy->width > info->var.xres ||
+	    copy->dy + copy->height > info->var.yres ||
+	    copy->sy + copy->height > info->var.yres) {
+		ret = -EINVAL;
+		goto out;
+	}
+	info->fbops->fb_copyarea(info, copy);
+out:
+	unlock_fb_info(info);
+	return ret;
+}
+
 static long do_fb_ioctl(struct fb_info *info, unsigned int cmd,
 			unsigned long arg)
 {
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1114 @
 	struct fb_fix_screeninfo fix;
 	struct fb_cmap cmap_from;
 	struct fb_cmap_user cmap;
+	struct fb_copyarea copy;
 	void __user *argp = (void __user *)arg;
 	long ret = 0;
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1190 @
 		unlock_fb_info(info);
 		console_unlock();
 		break;
+	case FBIOCOPYAREA:
+		if (info->flags & FBINFO_HWACCEL_COPYAREA) {
+			/* only provide this ioctl if it is accelerated */
+			if (copy_from_user(&copy, argp, sizeof(copy)))
+				return -EFAULT;
+			ret = fb_copyarea_user(info, &copy);
+			break;
+		}
+		/* fall through */
 	default:
 		lock_fb_info(info);
 		fb = info->fbops;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1344 @
 	case FBIOPAN_DISPLAY:
 	case FBIOGET_CON2FBMAP:
 	case FBIOPUT_CON2FBMAP:
+	case FBIOCOPYAREA:
 		arg = (unsigned long) compat_ptr(arg);
 		fallthrough;
 	case FBIOBLANK:
diff -Nur --no-dereference linux-5.10.52-orig/drivers/video/fbdev/Kconfig linux-5.10.52-v7l+/drivers/video/fbdev/Kconfig
--- linux-5.10.52-orig/drivers/video/fbdev/Kconfig	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/video/fbdev/Kconfig	2021-07-25 16:46:29.137902401 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:222 @
 comment "Frame buffer hardware drivers"
 	depends on FB
 
+config FB_BCM2708
+	tristate "BCM2708 framebuffer support"
+	depends on FB && RASPBERRYPI_FIRMWARE
+	select FB_CFB_FILLRECT
+	select FB_CFB_COPYAREA
+	select FB_CFB_IMAGEBLIT
+	help
+	  This framebuffer device driver is for the BCM2708 framebuffer.
+
+	  If you want to compile this as a module (=code which can be
+	  inserted into and removed from the running kernel), say M
+	  here and read <file:Documentation/kbuild/modules.txt>.  The module
+	  will be called bcm2708_fb.
+
 config FB_GRVGA
 	tristate "Aeroflex Gaisler framebuffer support"
 	depends on FB && SPARC
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:2253 @
 	  called sm712fb. If you want to compile it as a module, say M
 	  here and read <file:Documentation/kbuild/modules.rst>.
 
+config FB_RPISENSE
+	tristate "Raspberry Pi Sense HAT framebuffer"
+	depends on FB
+	select MFD_RPISENSE_CORE
+	select FB_SYS_FOPS
+	select FB_SYS_FILLRECT
+	select FB_SYS_COPYAREA
+	select FB_SYS_IMAGEBLIT
+	select FB_DEFERRED_IO
+
+	help
+	  This is the framebuffer driver for the Raspberry Pi Sense HAT
+
 source "drivers/video/fbdev/omap/Kconfig"
 source "drivers/video/fbdev/omap2/Kconfig"
 source "drivers/video/fbdev/mmp/Kconfig"
diff -Nur --no-dereference linux-5.10.52-orig/drivers/video/fbdev/Makefile linux-5.10.52-v7l+/drivers/video/fbdev/Makefile
--- linux-5.10.52-orig/drivers/video/fbdev/Makefile	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/video/fbdev/Makefile	2021-07-25 16:46:29.137902401 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:14 @
 obj-$(CONFIG_FB_WMT_GE_ROPS)   += wmt_ge_rops.o
 
 # Hardware specific drivers go first
+obj-$(CONFIG_FB_BCM2708)	  += bcm2708_fb.o
 obj-$(CONFIG_FB_AMIGA)            += amifb.o c2p_planar.o
 obj-$(CONFIG_FB_ARC)              += arcfb.o
 obj-$(CONFIG_FB_CLPS711X)	  += clps711x-fb.o
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:133 @
 obj-$(CONFIG_FB_DA8XX)		  += da8xx-fb.o
 obj-$(CONFIG_FB_SSD1307)	  += ssd1307fb.o
 obj-$(CONFIG_FB_SIMPLE)           += simplefb.o
+obj-$(CONFIG_FB_RPISENSE)	  += rpisense-fb.o
 
 # the test framebuffer is last
 obj-$(CONFIG_FB_VIRTUAL)          += vfb.o
diff -Nur --no-dereference linux-5.10.52-orig/drivers/video/fbdev/rpisense-fb.c linux-5.10.52-v7l+/drivers/video/fbdev/rpisense-fb.c
--- linux-5.10.52-orig/drivers/video/fbdev/rpisense-fb.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/drivers/video/fbdev/rpisense-fb.c	2021-07-25 16:46:29.407897874 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/*
+ * Raspberry Pi Sense HAT framebuffer driver
+ * http://raspberrypi.org
+ *
+ * Copyright (C) 2015 Raspberry Pi
+ *
+ * Author: Serge Schneider
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/delay.h>
+#include <linux/fb.h>
+#include <linux/init.h>
+
+#include <linux/mfd/rpisense/framebuffer.h>
+#include <linux/mfd/rpisense/core.h>
+
+static bool lowlight;
+module_param(lowlight, bool, 0);
+MODULE_PARM_DESC(lowlight, "Reduce LED matrix brightness to one third");
+
+static struct rpisense *rpisense;
+
+struct rpisense_fb_param {
+	char __iomem *vmem;
+	u8 *vmem_work;
+	u32 vmemsize;
+	u8 *gamma;
+};
+
+static u8 gamma_default[32] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01,
+			       0x02, 0x02, 0x03, 0x03, 0x04, 0x05, 0x06, 0x07,
+			       0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0E, 0x0F, 0x11,
+			       0x12, 0x14, 0x15, 0x17, 0x19, 0x1B, 0x1D, 0x1F,};
+
+static u8 gamma_low[32] = {0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+			   0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02,
+			   0x03, 0x03, 0x03, 0x04, 0x04, 0x05, 0x05, 0x06,
+			   0x06, 0x07, 0x07, 0x08, 0x08, 0x09, 0x0A, 0x0A,};
+
+static u8 gamma_user[32];
+
+static u32 pseudo_palette[16];
+
+static struct rpisense_fb_param rpisense_fb_param = {
+	.vmem = NULL,
+	.vmemsize = 128,
+	.gamma = gamma_default,
+};
+
+static struct fb_deferred_io rpisense_fb_defio;
+
+static struct fb_fix_screeninfo rpisense_fb_fix = {
+	.id =		"RPi-Sense FB",
+	.type =		FB_TYPE_PACKED_PIXELS,
+	.visual =	FB_VISUAL_TRUECOLOR,
+	.xpanstep =	0,
+	.ypanstep =	0,
+	.ywrapstep =	0,
+	.accel =	FB_ACCEL_NONE,
+	.line_length =	16,
+};
+
+static struct fb_var_screeninfo rpisense_fb_var = {
+	.xres		= 8,
+	.yres		= 8,
+	.xres_virtual	= 8,
+	.yres_virtual	= 8,
+	.bits_per_pixel = 16,
+	.red		= {11, 5, 0},
+	.green		= {5, 6, 0},
+	.blue		= {0, 5, 0},
+};
+
+static ssize_t rpisense_fb_write(struct fb_info *info,
+				 const char __user *buf, size_t count,
+				 loff_t *ppos)
+{
+	ssize_t res = fb_sys_write(info, buf, count, ppos);
+
+	schedule_delayed_work(&info->deferred_work, rpisense_fb_defio.delay);
+	return res;
+}
+
+static void rpisense_fb_fillrect(struct fb_info *info,
+				 const struct fb_fillrect *rect)
+{
+	sys_fillrect(info, rect);
+	schedule_delayed_work(&info->deferred_work, rpisense_fb_defio.delay);
+}
+
+static void rpisense_fb_copyarea(struct fb_info *info,
+				 const struct fb_copyarea *area)
+{
+	sys_copyarea(info, area);
+	schedule_delayed_work(&info->deferred_work, rpisense_fb_defio.delay);
+}
+
+static void rpisense_fb_imageblit(struct fb_info *info,
+				  const struct fb_image *image)
+{
+	sys_imageblit(info, image);
+	schedule_delayed_work(&info->deferred_work, rpisense_fb_defio.delay);
+}
+
+static void rpisense_fb_deferred_io(struct fb_info *info,
+				struct list_head *pagelist)
+{
+	int i;
+	int j;
+	u8 *vmem_work = rpisense_fb_param.vmem_work;
+	u16 *mem = (u16 *)rpisense_fb_param.vmem;
+	u8 *gamma = rpisense_fb_param.gamma;
+
+	vmem_work[0] = 0;
+	for (j = 0; j < 8; j++) {
+		for (i = 0; i < 8; i++) {
+			vmem_work[(j * 24) + i + 1] =
+				gamma[(mem[(j * 8) + i] >> 11) & 0x1F];
+			vmem_work[(j * 24) + (i + 8) + 1] =
+				gamma[(mem[(j * 8) + i] >> 6) & 0x1F];
+			vmem_work[(j * 24) + (i + 16) + 1] =
+				gamma[(mem[(j * 8) + i]) & 0x1F];
+		}
+	}
+	rpisense_block_write(rpisense, vmem_work, 193);
+}
+
+static struct fb_deferred_io rpisense_fb_defio = {
+	.delay		= HZ/100,
+	.deferred_io	= rpisense_fb_deferred_io,
+};
+
+static int rpisense_fb_ioctl(struct fb_info *info, unsigned int cmd,
+			     unsigned long arg)
+{
+	switch (cmd) {
+	case SENSEFB_FBIOGET_GAMMA:
+		if (copy_to_user((void __user *) arg, rpisense_fb_param.gamma,
+				 sizeof(u8[32])))
+			return -EFAULT;
+		return 0;
+	case SENSEFB_FBIOSET_GAMMA:
+		if (copy_from_user(gamma_user, (void __user *)arg,
+				   sizeof(u8[32])))
+			return -EFAULT;
+		rpisense_fb_param.gamma = gamma_user;
+		schedule_delayed_work(&info->deferred_work,
+				      rpisense_fb_defio.delay);
+		return 0;
+	case SENSEFB_FBIORESET_GAMMA:
+		switch (arg) {
+		case 0:
+			rpisense_fb_param.gamma = gamma_default;
+			break;
+		case 1:
+			rpisense_fb_param.gamma = gamma_low;
+			break;
+		case 2:
+			rpisense_fb_param.gamma = gamma_user;
+			break;
+		default:
+			return -EINVAL;
+		}
+		schedule_delayed_work(&info->deferred_work,
+				      rpisense_fb_defio.delay);
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static struct fb_ops rpisense_fb_ops = {
+	.owner		= THIS_MODULE,
+	.fb_read	= fb_sys_read,
+	.fb_write	= rpisense_fb_write,
+	.fb_fillrect	= rpisense_fb_fillrect,
+	.fb_copyarea	= rpisense_fb_copyarea,
+	.fb_imageblit	= rpisense_fb_imageblit,
+	.fb_ioctl	= rpisense_fb_ioctl,
+};
+
+static int rpisense_fb_probe(struct platform_device *pdev)
+{
+	struct fb_info *info;
+	int ret = -ENOMEM;
+	struct rpisense_fb *rpisense_fb;
+
+	rpisense = rpisense_get_dev();
+	rpisense_fb = &rpisense->framebuffer;
+
+	rpisense_fb_param.vmem = vzalloc(rpisense_fb_param.vmemsize);
+	if (!rpisense_fb_param.vmem)
+		return ret;
+
+	rpisense_fb_param.vmem_work = devm_kmalloc(&pdev->dev, 193, GFP_KERNEL);
+	if (!rpisense_fb_param.vmem_work)
+		goto err_malloc;
+
+	info = framebuffer_alloc(0, &pdev->dev);
+	if (!info) {
+		dev_err(&pdev->dev, "Could not allocate framebuffer.\n");
+		goto err_malloc;
+	}
+	rpisense_fb->info = info;
+
+	rpisense_fb_fix.smem_start = (unsigned long)rpisense_fb_param.vmem;
+	rpisense_fb_fix.smem_len = rpisense_fb_param.vmemsize;
+
+	info->fbops = &rpisense_fb_ops;
+	info->fix = rpisense_fb_fix;
+	info->var = rpisense_fb_var;
+	info->fbdefio = &rpisense_fb_defio;
+	info->flags = FBINFO_FLAG_DEFAULT | FBINFO_VIRTFB;
+	info->screen_base = rpisense_fb_param.vmem;
+	info->screen_size = rpisense_fb_param.vmemsize;
+	info->pseudo_palette = pseudo_palette;
+
+	if (lowlight)
+		rpisense_fb_param.gamma = gamma_low;
+
+	fb_deferred_io_init(info);
+
+	ret = register_framebuffer(info);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "Could not register framebuffer.\n");
+		goto err_fballoc;
+	}
+
+	fb_info(info, "%s frame buffer device\n", info->fix.id);
+	schedule_delayed_work(&info->deferred_work, rpisense_fb_defio.delay);
+	return 0;
+err_fballoc:
+	framebuffer_release(info);
+err_malloc:
+	vfree(rpisense_fb_param.vmem);
+	return ret;
+}
+
+static int rpisense_fb_remove(struct platform_device *pdev)
+{
+	struct rpisense_fb *rpisense_fb = &rpisense->framebuffer;
+	struct fb_info *info = rpisense_fb->info;
+
+	if (info) {
+		unregister_framebuffer(info);
+		fb_deferred_io_cleanup(info);
+		framebuffer_release(info);
+		vfree(rpisense_fb_param.vmem);
+	}
+
+	return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id rpisense_fb_id[] = {
+	{ .compatible = "rpi,rpi-sense-fb" },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, rpisense_fb_id);
+#endif
+
+static struct platform_device_id rpisense_fb_device_id[] = {
+	{ .name = "rpi-sense-fb" },
+	{ },
+};
+MODULE_DEVICE_TABLE(platform, rpisense_fb_device_id);
+
+static struct platform_driver rpisense_fb_driver = {
+	.probe = rpisense_fb_probe,
+	.remove = rpisense_fb_remove,
+	.driver = {
+		.name = "rpi-sense-fb",
+		.owner = THIS_MODULE,
+	},
+};
+
+module_platform_driver(rpisense_fb_driver);
+
+MODULE_DESCRIPTION("Raspberry Pi Sense HAT framebuffer driver");
+MODULE_AUTHOR("Serge Schneider <serge@raspberrypi.org>");
+MODULE_LICENSE("GPL");
+
diff -Nur --no-dereference linux-5.10.52-orig/drivers/video/logo/logo_linux_clut224.ppm linux-5.10.52-v7l+/drivers/video/logo/logo_linux_clut224.ppm
--- linux-5.10.52-orig/drivers/video/logo/logo_linux_clut224.ppm	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/video/logo/logo_linux_clut224.ppm	2021-07-25 16:46:29.567895192 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
 P3
-# Standard 224-color Linux logo
-80 80
+63 80
 255
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  6   6   6   6   6   6  10  10  10  10  10  10
- 10  10  10   6   6   6   6   6   6   6   6   6
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   6   6   6  10  10  10  14  14  14
- 22  22  22  26  26  26  30  30  30  34  34  34
- 30  30  30  30  30  30  26  26  26  18  18  18
- 14  14  14  10  10  10   6   6   6   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   1   0   0   1   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  6   6   6  14  14  14  26  26  26  42  42  42
- 54  54  54  66  66  66  78  78  78  78  78  78
- 78  78  78  74  74  74  66  66  66  54  54  54
- 42  42  42  26  26  26  18  18  18  10  10  10
-  6   6   6   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   1   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0  10  10  10
- 22  22  22  42  42  42  66  66  66  86  86  86
- 66  66  66  38  38  38  38  38  38  22  22  22
- 26  26  26  34  34  34  54  54  54  66  66  66
- 86  86  86  70  70  70  46  46  46  26  26  26
- 14  14  14   6   6   6   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   1   0   0   1   0   0   1   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0  10  10  10  26  26  26
- 50  50  50  82  82  82  58  58  58   6   6   6
-  2   2   6   2   2   6   2   2   6   2   2   6
-  2   2   6   2   2   6   2   2   6   2   2   6
-  6   6   6  54  54  54  86  86  86  66  66  66
- 38  38  38  18  18  18   6   6   6   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   6   6   6  22  22  22  50  50  50
- 78  78  78  34  34  34   2   2   6   2   2   6
-  2   2   6   2   2   6   2   2   6   2   2   6
-  2   2   6   2   2   6   2   2   6   2   2   6
-  2   2   6   2   2   6   6   6   6  70  70  70
- 78  78  78  46  46  46  22  22  22   6   6   6
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   1   0   0   1   0   0   1   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  6   6   6  18  18  18  42  42  42  82  82  82
- 26  26  26   2   2   6   2   2   6   2   2   6
-  2   2   6   2   2   6   2   2   6   2   2   6
-  2   2   6   2   2   6   2   2   6  14  14  14
- 46  46  46  34  34  34   6   6   6   2   2   6
- 42  42  42  78  78  78  42  42  42  18  18  18
-  6   6   6   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   1   0   0   0   0   0   1   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
- 10  10  10  30  30  30  66  66  66  58  58  58
-  2   2   6   2   2   6   2   2   6   2   2   6
-  2   2   6   2   2   6   2   2   6   2   2   6
-  2   2   6   2   2   6   2   2   6  26  26  26
- 86  86  86 101 101 101  46  46  46  10  10  10
-  2   2   6  58  58  58  70  70  70  34  34  34
- 10  10  10   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   1   0   0   1   0   0   1   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
- 14  14  14  42  42  42  86  86  86  10  10  10
-  2   2   6   2   2   6   2   2   6   2   2   6
-  2   2   6   2   2   6   2   2   6   2   2   6
-  2   2   6   2   2   6   2   2   6  30  30  30
- 94  94  94  94  94  94  58  58  58  26  26  26
-  2   2   6   6   6   6  78  78  78  54  54  54
- 22  22  22   6   6   6   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   6   6   6
- 22  22  22  62  62  62  62  62  62   2   2   6
-  2   2   6   2   2   6   2   2   6   2   2   6
-  2   2   6   2   2   6   2   2   6   2   2   6
-  2   2   6   2   2   6   2   2   6  26  26  26
- 54  54  54  38  38  38  18  18  18  10  10  10
-  2   2   6   2   2   6  34  34  34  82  82  82
- 38  38  38  14  14  14   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   1   0   0   1   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   6   6   6
- 30  30  30  78  78  78  30  30  30   2   2   6
-  2   2   6   2   2   6   2   2   6   2   2   6
-  2   2   6   2   2   6   2   2   6   2   2   6
-  2   2   6   2   2   6   2   2   6  10  10  10
- 10  10  10   2   2   6   2   2   6   2   2   6
-  2   2   6   2   2   6   2   2   6  78  78  78
- 50  50  50  18  18  18   6   6   6   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   1   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0  10  10  10
- 38  38  38  86  86  86  14  14  14   2   2   6
-  2   2   6   2   2   6   2   2   6   2   2   6
-  2   2   6   2   2   6   2   2   6   2   2   6
-  2   2   6   2   2   6   2   2   6   2   2   6
-  2   2   6   2   2   6   2   2   6   2   2   6
-  2   2   6   2   2   6   2   2   6  54  54  54
- 66  66  66  26  26  26   6   6   6   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   1   0   0   1   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0  14  14  14
- 42  42  42  82  82  82   2   2   6   2   2   6
-  2   2   6   6   6   6  10  10  10   2   2   6
-  2   2   6   2   2   6   2   2   6   2   2   6
-  2   2   6   2   2   6   2   2   6   6   6   6
- 14  14  14  10  10  10   2   2   6   2   2   6
-  2   2   6   2   2   6   2   2   6  18  18  18
- 82  82  82  34  34  34  10  10  10   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   1   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0  14  14  14
- 46  46  46  86  86  86   2   2   6   2   2   6
-  6   6   6   6   6   6  22  22  22  34  34  34
-  6   6   6   2   2   6   2   2   6   2   2   6
-  2   2   6   2   2   6  18  18  18  34  34  34
- 10  10  10  50  50  50  22  22  22   2   2   6
-  2   2   6   2   2   6   2   2   6  10  10  10
- 86  86  86  42  42  42  14  14  14   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   1   0   0   1   0   0   1   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0  14  14  14
- 46  46  46  86  86  86   2   2   6   2   2   6
- 38  38  38 116 116 116  94  94  94  22  22  22
- 22  22  22   2   2   6   2   2   6   2   2   6
- 14  14  14  86  86  86 138 138 138 162 162 162
-154 154 154  38  38  38  26  26  26   6   6   6
-  2   2   6   2   2   6   2   2   6   2   2   6
- 86  86  86  46  46  46  14  14  14   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0  14  14  14
- 46  46  46  86  86  86   2   2   6  14  14  14
-134 134 134 198 198 198 195 195 195 116 116 116
- 10  10  10   2   2   6   2   2   6   6   6   6
-101  98  89 187 187 187 210 210 210 218 218 218
-214 214 214 134 134 134  14  14  14   6   6   6
-  2   2   6   2   2   6   2   2   6   2   2   6
- 86  86  86  50  50  50  18  18  18   6   6   6
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   1   0   0   0
-  0   0   1   0   0   1   0   0   1   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0  14  14  14
- 46  46  46  86  86  86   2   2   6  54  54  54
-218 218 218 195 195 195 226 226 226 246 246 246
- 58  58  58   2   2   6   2   2   6  30  30  30
-210 210 210 253 253 253 174 174 174 123 123 123
-221 221 221 234 234 234  74  74  74   2   2   6
-  2   2   6   2   2   6   2   2   6   2   2   6
- 70  70  70  58  58  58  22  22  22   6   6   6
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0  14  14  14
- 46  46  46  82  82  82   2   2   6 106 106 106
-170 170 170  26  26  26  86  86  86 226 226 226
-123 123 123  10  10  10  14  14  14  46  46  46
-231 231 231 190 190 190   6   6   6  70  70  70
- 90  90  90 238 238 238 158 158 158   2   2   6
-  2   2   6   2   2   6   2   2   6   2   2   6
- 70  70  70  58  58  58  22  22  22   6   6   6
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   1   0   0   0
-  0   0   1   0   0   1   0   0   1   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0  14  14  14
- 42  42  42  86  86  86   6   6   6 116 116 116
-106 106 106   6   6   6  70  70  70 149 149 149
-128 128 128  18  18  18  38  38  38  54  54  54
-221 221 221 106 106 106   2   2   6  14  14  14
- 46  46  46 190 190 190 198 198 198   2   2   6
-  2   2   6   2   2   6   2   2   6   2   2   6
- 74  74  74  62  62  62  22  22  22   6   6   6
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   1   0   0   0
-  0   0   1   0   0   0   0   0   1   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0  14  14  14
- 42  42  42  94  94  94  14  14  14 101 101 101
-128 128 128   2   2   6  18  18  18 116 116 116
-118  98  46 121  92   8 121  92   8  98  78  10
-162 162 162 106 106 106   2   2   6   2   2   6
-  2   2   6 195 195 195 195 195 195   6   6   6
-  2   2   6   2   2   6   2   2   6   2   2   6
- 74  74  74  62  62  62  22  22  22   6   6   6
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   1   0   0   1
-  0   0   1   0   0   0   0   0   1   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0  10  10  10
- 38  38  38  90  90  90  14  14  14  58  58  58
-210 210 210  26  26  26  54  38   6 154 114  10
-226 170  11 236 186  11 225 175  15 184 144  12
-215 174  15 175 146  61  37  26   9   2   2   6
- 70  70  70 246 246 246 138 138 138   2   2   6
-  2   2   6   2   2   6   2   2   6   2   2   6
- 70  70  70  66  66  66  26  26  26   6   6   6
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0  10  10  10
- 38  38  38  86  86  86  14  14  14  10  10  10
-195 195 195 188 164 115 192 133   9 225 175  15
-239 182  13 234 190  10 232 195  16 232 200  30
-245 207  45 241 208  19 232 195  16 184 144  12
-218 194 134 211 206 186  42  42  42   2   2   6
-  2   2   6   2   2   6   2   2   6   2   2   6
- 50  50  50  74  74  74  30  30  30   6   6   6
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0  10  10  10
- 34  34  34  86  86  86  14  14  14   2   2   6
-121  87  25 192 133   9 219 162  10 239 182  13
-236 186  11 232 195  16 241 208  19 244 214  54
-246 218  60 246 218  38 246 215  20 241 208  19
-241 208  19 226 184  13 121  87  25   2   2   6
-  2   2   6   2   2   6   2   2   6   2   2   6
- 50  50  50  82  82  82  34  34  34  10  10  10
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0  10  10  10
- 34  34  34  82  82  82  30  30  30  61  42   6
-180 123   7 206 145  10 230 174  11 239 182  13
-234 190  10 238 202  15 241 208  19 246 218  74
-246 218  38 246 215  20 246 215  20 246 215  20
-226 184  13 215 174  15 184 144  12   6   6   6
-  2   2   6   2   2   6   2   2   6   2   2   6
- 26  26  26  94  94  94  42  42  42  14  14  14
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0  10  10  10
- 30  30  30  78  78  78  50  50  50 104  69   6
-192 133   9 216 158  10 236 178  12 236 186  11
-232 195  16 241 208  19 244 214  54 245 215  43
-246 215  20 246 215  20 241 208  19 198 155  10
-200 144  11 216 158  10 156 118  10   2   2   6
-  2   2   6   2   2   6   2   2   6   2   2   6
-  6   6   6  90  90  90  54  54  54  18  18  18
-  6   6   6   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0  10  10  10
- 30  30  30  78  78  78  46  46  46  22  22  22
-137  92   6 210 162  10 239 182  13 238 190  10
-238 202  15 241 208  19 246 215  20 246 215  20
-241 208  19 203 166  17 185 133  11 210 150  10
-216 158  10 210 150  10 102  78  10   2   2   6
-  6   6   6  54  54  54  14  14  14   2   2   6
-  2   2   6  62  62  62  74  74  74  30  30  30
- 10  10  10   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0  10  10  10
- 34  34  34  78  78  78  50  50  50   6   6   6
- 94  70  30 139 102  15 190 146  13 226 184  13
-232 200  30 232 195  16 215 174  15 190 146  13
-168 122  10 192 133   9 210 150  10 213 154  11
-202 150  34 182 157 106 101  98  89   2   2   6
-  2   2   6  78  78  78 116 116 116  58  58  58
-  2   2   6  22  22  22  90  90  90  46  46  46
- 18  18  18   6   6   6   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0  10  10  10
- 38  38  38  86  86  86  50  50  50   6   6   6
-128 128 128 174 154 114 156 107  11 168 122  10
-198 155  10 184 144  12 197 138  11 200 144  11
-206 145  10 206 145  10 197 138  11 188 164 115
-195 195 195 198 198 198 174 174 174  14  14  14
-  2   2   6  22  22  22 116 116 116 116 116 116
- 22  22  22   2   2   6  74  74  74  70  70  70
- 30  30  30  10  10  10   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   6   6   6  18  18  18
- 50  50  50 101 101 101  26  26  26  10  10  10
-138 138 138 190 190 190 174 154 114 156 107  11
-197 138  11 200 144  11 197 138  11 192 133   9
-180 123   7 190 142  34 190 178 144 187 187 187
-202 202 202 221 221 221 214 214 214  66  66  66
-  2   2   6   2   2   6  50  50  50  62  62  62
-  6   6   6   2   2   6  10  10  10  90  90  90
- 50  50  50  18  18  18   6   6   6   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0  10  10  10  34  34  34
- 74  74  74  74  74  74   2   2   6   6   6   6
-144 144 144 198 198 198 190 190 190 178 166 146
-154 121  60 156 107  11 156 107  11 168 124  44
-174 154 114 187 187 187 190 190 190 210 210 210
-246 246 246 253 253 253 253 253 253 182 182 182
-  6   6   6   2   2   6   2   2   6   2   2   6
-  2   2   6   2   2   6   2   2   6  62  62  62
- 74  74  74  34  34  34  14  14  14   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0  10  10  10  22  22  22  54  54  54
- 94  94  94  18  18  18   2   2   6  46  46  46
-234 234 234 221 221 221 190 190 190 190 190 190
-190 190 190 187 187 187 187 187 187 190 190 190
-190 190 190 195 195 195 214 214 214 242 242 242
-253 253 253 253 253 253 253 253 253 253 253 253
- 82  82  82   2   2   6   2   2   6   2   2   6
-  2   2   6   2   2   6   2   2   6  14  14  14
- 86  86  86  54  54  54  22  22  22   6   6   6
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  6   6   6  18  18  18  46  46  46  90  90  90
- 46  46  46  18  18  18   6   6   6 182 182 182
-253 253 253 246 246 246 206 206 206 190 190 190
-190 190 190 190 190 190 190 190 190 190 190 190
-206 206 206 231 231 231 250 250 250 253 253 253
-253 253 253 253 253 253 253 253 253 253 253 253
-202 202 202  14  14  14   2   2   6   2   2   6
-  2   2   6   2   2   6   2   2   6   2   2   6
- 42  42  42  86  86  86  42  42  42  18  18  18
-  6   6   6   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   6   6   6
- 14  14  14  38  38  38  74  74  74  66  66  66
-  2   2   6   6   6   6  90  90  90 250 250 250
-253 253 253 253 253 253 238 238 238 198 198 198
-190 190 190 190 190 190 195 195 195 221 221 221
-246 246 246 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 253 253 253 253 253 253
-253 253 253  82  82  82   2   2   6   2   2   6
-  2   2   6   2   2   6   2   2   6   2   2   6
-  2   2   6  78  78  78  70  70  70  34  34  34
- 14  14  14   6   6   6   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0  14  14  14
- 34  34  34  66  66  66  78  78  78   6   6   6
-  2   2   6  18  18  18 218 218 218 253 253 253
-253 253 253 253 253 253 253 253 253 246 246 246
-226 226 226 231 231 231 246 246 246 253 253 253
-253 253 253 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 253 253 253 253 253 253
-253 253 253 178 178 178   2   2   6   2   2   6
-  2   2   6   2   2   6   2   2   6   2   2   6
-  2   2   6  18  18  18  90  90  90  62  62  62
- 30  30  30  10  10  10   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0  10  10  10  26  26  26
- 58  58  58  90  90  90  18  18  18   2   2   6
-  2   2   6 110 110 110 253 253 253 253 253 253
-253 253 253 253 253 253 253 253 253 253 253 253
-250 250 250 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 253 253 253 253 253 253
-253 253 253 231 231 231  18  18  18   2   2   6
-  2   2   6   2   2   6   2   2   6   2   2   6
-  2   2   6   2   2   6  18  18  18  94  94  94
- 54  54  54  26  26  26  10  10  10   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   6   6   6  22  22  22  50  50  50
- 90  90  90  26  26  26   2   2   6   2   2   6
- 14  14  14 195 195 195 250 250 250 253 253 253
-253 253 253 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 253 253 253 253 253 253
-250 250 250 242 242 242  54  54  54   2   2   6
-  2   2   6   2   2   6   2   2   6   2   2   6
-  2   2   6   2   2   6   2   2   6  38  38  38
- 86  86  86  50  50  50  22  22  22   6   6   6
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  6   6   6  14  14  14  38  38  38  82  82  82
- 34  34  34   2   2   6   2   2   6   2   2   6
- 42  42  42 195 195 195 246 246 246 253 253 253
-253 253 253 253 253 253 253 253 253 250 250 250
-242 242 242 242 242 242 250 250 250 253 253 253
-253 253 253 253 253 253 253 253 253 253 253 253
-253 253 253 250 250 250 246 246 246 238 238 238
-226 226 226 231 231 231 101 101 101   6   6   6
-  2   2   6   2   2   6   2   2   6   2   2   6
-  2   2   6   2   2   6   2   2   6   2   2   6
- 38  38  38  82  82  82  42  42  42  14  14  14
-  6   6   6   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
- 10  10  10  26  26  26  62  62  62  66  66  66
-  2   2   6   2   2   6   2   2   6   6   6   6
- 70  70  70 170 170 170 206 206 206 234 234 234
-246 246 246 250 250 250 250 250 250 238 238 238
-226 226 226 231 231 231 238 238 238 250 250 250
-250 250 250 250 250 250 246 246 246 231 231 231
-214 214 214 206 206 206 202 202 202 202 202 202
-198 198 198 202 202 202 182 182 182  18  18  18
-  2   2   6   2   2   6   2   2   6   2   2   6
-  2   2   6   2   2   6   2   2   6   2   2   6
-  2   2   6  62  62  62  66  66  66  30  30  30
- 10  10  10   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
- 14  14  14  42  42  42  82  82  82  18  18  18
-  2   2   6   2   2   6   2   2   6  10  10  10
- 94  94  94 182 182 182 218 218 218 242 242 242
-250 250 250 253 253 253 253 253 253 250 250 250
-234 234 234 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 253 253 253 246 246 246
-238 238 238 226 226 226 210 210 210 202 202 202
-195 195 195 195 195 195 210 210 210 158 158 158
-  6   6   6  14  14  14  50  50  50  14  14  14
-  2   2   6   2   2   6   2   2   6   2   2   6
-  2   2   6   6   6   6  86  86  86  46  46  46
- 18  18  18   6   6   6   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   6   6   6
- 22  22  22  54  54  54  70  70  70   2   2   6
-  2   2   6  10  10  10   2   2   6  22  22  22
-166 166 166 231 231 231 250 250 250 253 253 253
-253 253 253 253 253 253 253 253 253 250 250 250
-242 242 242 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 253 253 253 246 246 246
-231 231 231 206 206 206 198 198 198 226 226 226
- 94  94  94   2   2   6   6   6   6  38  38  38
- 30  30  30   2   2   6   2   2   6   2   2   6
-  2   2   6   2   2   6  62  62  62  66  66  66
- 26  26  26  10  10  10   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0  10  10  10
- 30  30  30  74  74  74  50  50  50   2   2   6
- 26  26  26  26  26  26   2   2   6 106 106 106
-238 238 238 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 253 253 253 253 253 253
-253 253 253 246 246 246 218 218 218 202 202 202
-210 210 210  14  14  14   2   2   6   2   2   6
- 30  30  30  22  22  22   2   2   6   2   2   6
-  2   2   6   2   2   6  18  18  18  86  86  86
- 42  42  42  14  14  14   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0  14  14  14
- 42  42  42  90  90  90  22  22  22   2   2   6
- 42  42  42   2   2   6  18  18  18 218 218 218
-253 253 253 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 250 250 250 221 221 221
-218 218 218 101 101 101   2   2   6  14  14  14
- 18  18  18  38  38  38  10  10  10   2   2   6
-  2   2   6   2   2   6   2   2   6  78  78  78
- 58  58  58  22  22  22   6   6   6   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   6   6   6  18  18  18
- 54  54  54  82  82  82   2   2   6  26  26  26
- 22  22  22   2   2   6 123 123 123 253 253 253
-253 253 253 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 253 253 253 250 250 250
-238 238 238 198 198 198   6   6   6  38  38  38
- 58  58  58  26  26  26  38  38  38   2   2   6
-  2   2   6   2   2   6   2   2   6  46  46  46
- 78  78  78  30  30  30  10  10  10   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0  10  10  10  30  30  30
- 74  74  74  58  58  58   2   2   6  42  42  42
-  2   2   6  22  22  22 231 231 231 253 253 253
-253 253 253 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 253 253 253 250 250 250
-253 253 253 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 253 253 253 253 253 253
-253 253 253 246 246 246  46  46  46  38  38  38
- 42  42  42  14  14  14  38  38  38  14  14  14
-  2   2   6   2   2   6   2   2   6   6   6   6
- 86  86  86  46  46  46  14  14  14   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   6   6   6  14  14  14  42  42  42
- 90  90  90  18  18  18  18  18  18  26  26  26
-  2   2   6 116 116 116 253 253 253 253 253 253
-253 253 253 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 250 250 250 238 238 238
-253 253 253 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253  94  94  94   6   6   6
-  2   2   6   2   2   6  10  10  10  34  34  34
-  2   2   6   2   2   6   2   2   6   2   2   6
- 74  74  74  58  58  58  22  22  22   6   6   6
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0  10  10  10  26  26  26  66  66  66
- 82  82  82   2   2   6  38  38  38   6   6   6
- 14  14  14 210 210 210 253 253 253 253 253 253
-253 253 253 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 246 246 246 242 242 242
-253 253 253 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 144 144 144   2   2   6
-  2   2   6   2   2   6   2   2   6  46  46  46
-  2   2   6   2   2   6   2   2   6   2   2   6
- 42  42  42  74  74  74  30  30  30  10  10  10
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  6   6   6  14  14  14  42  42  42  90  90  90
- 26  26  26   6   6   6  42  42  42   2   2   6
- 74  74  74 250 250 250 253 253 253 253 253 253
-253 253 253 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 242 242 242 242 242 242
-253 253 253 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 182 182 182   2   2   6
-  2   2   6   2   2   6   2   2   6  46  46  46
-  2   2   6   2   2   6   2   2   6   2   2   6
- 10  10  10  86  86  86  38  38  38  10  10  10
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
- 10  10  10  26  26  26  66  66  66  82  82  82
-  2   2   6  22  22  22  18  18  18   2   2   6
-149 149 149 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 234 234 234 242 242 242
-253 253 253 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 206 206 206   2   2   6
-  2   2   6   2   2   6   2   2   6  38  38  38
-  2   2   6   2   2   6   2   2   6   2   2   6
-  6   6   6  86  86  86  46  46  46  14  14  14
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   6   6   6
- 18  18  18  46  46  46  86  86  86  18  18  18
-  2   2   6  34  34  34  10  10  10   6   6   6
-210 210 210 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 234 234 234 242 242 242
-253 253 253 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 221 221 221   6   6   6
-  2   2   6   2   2   6   6   6   6  30  30  30
-  2   2   6   2   2   6   2   2   6   2   2   6
-  2   2   6  82  82  82  54  54  54  18  18  18
-  6   6   6   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0  10  10  10
- 26  26  26  66  66  66  62  62  62   2   2   6
-  2   2   6  38  38  38  10  10  10  26  26  26
-238 238 238 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 231 231 231 238 238 238
-253 253 253 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 231 231 231   6   6   6
-  2   2   6   2   2   6  10  10  10  30  30  30
-  2   2   6   2   2   6   2   2   6   2   2   6
-  2   2   6  66  66  66  58  58  58  22  22  22
-  6   6   6   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0  10  10  10
- 38  38  38  78  78  78   6   6   6   2   2   6
-  2   2   6  46  46  46  14  14  14  42  42  42
-246 246 246 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 231 231 231 242 242 242
-253 253 253 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 234 234 234  10  10  10
-  2   2   6   2   2   6  22  22  22  14  14  14
-  2   2   6   2   2   6   2   2   6   2   2   6
-  2   2   6  66  66  66  62  62  62  22  22  22
-  6   6   6   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   6   6   6  18  18  18
- 50  50  50  74  74  74   2   2   6   2   2   6
- 14  14  14  70  70  70  34  34  34  62  62  62
-250 250 250 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 231 231 231 246 246 246
-253 253 253 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 234 234 234  14  14  14
-  2   2   6   2   2   6  30  30  30   2   2   6
-  2   2   6   2   2   6   2   2   6   2   2   6
-  2   2   6  66  66  66  62  62  62  22  22  22
-  6   6   6   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   6   6   6  18  18  18
- 54  54  54  62  62  62   2   2   6   2   2   6
-  2   2   6  30  30  30  46  46  46  70  70  70
-250 250 250 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 231 231 231 246 246 246
-253 253 253 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 226 226 226  10  10  10
-  2   2   6   6   6   6  30  30  30   2   2   6
-  2   2   6   2   2   6   2   2   6   2   2   6
-  2   2   6  66  66  66  58  58  58  22  22  22
-  6   6   6   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   6   6   6  22  22  22
- 58  58  58  62  62  62   2   2   6   2   2   6
-  2   2   6   2   2   6  30  30  30  78  78  78
-250 250 250 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 231 231 231 246 246 246
-253 253 253 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 206 206 206   2   2   6
- 22  22  22  34  34  34  18  14   6  22  22  22
- 26  26  26  18  18  18   6   6   6   2   2   6
-  2   2   6  82  82  82  54  54  54  18  18  18
-  6   6   6   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   6   6   6  26  26  26
- 62  62  62 106 106 106  74  54  14 185 133  11
-210 162  10 121  92   8   6   6   6  62  62  62
-238 238 238 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 231 231 231 246 246 246
-253 253 253 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 158 158 158  18  18  18
- 14  14  14   2   2   6   2   2   6   2   2   6
-  6   6   6  18  18  18  66  66  66  38  38  38
-  6   6   6  94  94  94  50  50  50  18  18  18
-  6   6   6   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   6   6   6
- 10  10  10  10  10  10  18  18  18  38  38  38
- 78  78  78 142 134 106 216 158  10 242 186  14
-246 190  14 246 190  14 156 118  10  10  10  10
- 90  90  90 238 238 238 253 253 253 253 253 253
-253 253 253 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 231 231 231 250 250 250
-253 253 253 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 253 253 253 246 230 190
-238 204  91 238 204  91 181 142  44  37  26   9
-  2   2   6   2   2   6   2   2   6   2   2   6
-  2   2   6   2   2   6  38  38  38  46  46  46
- 26  26  26 106 106 106  54  54  54  18  18  18
-  6   6   6   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   6   6   6  14  14  14  22  22  22
- 30  30  30  38  38  38  50  50  50  70  70  70
-106 106 106 190 142  34 226 170  11 242 186  14
-246 190  14 246 190  14 246 190  14 154 114  10
-  6   6   6  74  74  74 226 226 226 253 253 253
-253 253 253 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 231 231 231 250 250 250
-253 253 253 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 253 253 253 228 184  62
-241 196  14 241 208  19 232 195  16  38  30  10
-  2   2   6   2   2   6   2   2   6   2   2   6
-  2   2   6   6   6   6  30  30  30  26  26  26
-203 166  17 154 142  90  66  66  66  26  26  26
-  6   6   6   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  6   6   6  18  18  18  38  38  38  58  58  58
- 78  78  78  86  86  86 101 101 101 123 123 123
-175 146  61 210 150  10 234 174  13 246 186  14
-246 190  14 246 190  14 246 190  14 238 190  10
-102  78  10   2   2   6  46  46  46 198 198 198
-253 253 253 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 234 234 234 242 242 242
-253 253 253 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 253 253 253 224 178  62
-242 186  14 241 196  14 210 166  10  22  18   6
-  2   2   6   2   2   6   2   2   6   2   2   6
-  2   2   6   2   2   6   6   6   6 121  92   8
-238 202  15 232 195  16  82  82  82  34  34  34
- 10  10  10   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
- 14  14  14  38  38  38  70  70  70 154 122  46
-190 142  34 200 144  11 197 138  11 197 138  11
-213 154  11 226 170  11 242 186  14 246 190  14
-246 190  14 246 190  14 246 190  14 246 190  14
-225 175  15  46  32   6   2   2   6  22  22  22
-158 158 158 250 250 250 253 253 253 253 253 253
-253 253 253 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 253 253 253 253 253 253
-253 253 253 250 250 250 242 242 242 224 178  62
-239 182  13 236 186  11 213 154  11  46  32   6
-  2   2   6   2   2   6   2   2   6   2   2   6
-  2   2   6   2   2   6  61  42   6 225 175  15
-238 190  10 236 186  11 112 100  78  42  42  42
- 14  14  14   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   6   6   6
- 22  22  22  54  54  54 154 122  46 213 154  11
-226 170  11 230 174  11 226 170  11 226 170  11
-236 178  12 242 186  14 246 190  14 246 190  14
-246 190  14 246 190  14 246 190  14 246 190  14
-241 196  14 184 144  12  10  10  10   2   2   6
-  6   6   6 116 116 116 242 242 242 253 253 253
-253 253 253 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 253 253 253 253 253 253
-253 253 253 231 231 231 198 198 198 214 170  54
-236 178  12 236 178  12 210 150  10 137  92   6
- 18  14   6   2   2   6   2   2   6   2   2   6
-  6   6   6  70  47   6 200 144  11 236 178  12
-239 182  13 239 182  13 124 112  88  58  58  58
- 22  22  22   6   6   6   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0  10  10  10
- 30  30  30  70  70  70 180 133  36 226 170  11
-239 182  13 242 186  14 242 186  14 246 186  14
-246 190  14 246 190  14 246 190  14 246 190  14
-246 190  14 246 190  14 246 190  14 246 190  14
-246 190  14 232 195  16  98  70   6   2   2   6
-  2   2   6   2   2   6  66  66  66 221 221 221
-253 253 253 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 253 253 253 253 253 253
-253 253 253 206 206 206 198 198 198 214 166  58
-230 174  11 230 174  11 216 158  10 192 133   9
-163 110   8 116  81   8 102  78  10 116  81   8
-167 114   7 197 138  11 226 170  11 239 182  13
-242 186  14 242 186  14 162 146  94  78  78  78
- 34  34  34  14  14  14   6   6   6   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   6   6   6
- 30  30  30  78  78  78 190 142  34 226 170  11
-239 182  13 246 190  14 246 190  14 246 190  14
-246 190  14 246 190  14 246 190  14 246 190  14
-246 190  14 246 190  14 246 190  14 246 190  14
-246 190  14 241 196  14 203 166  17  22  18   6
-  2   2   6   2   2   6   2   2   6  38  38  38
-218 218 218 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 253 253 253 253 253 253
-250 250 250 206 206 206 198 198 198 202 162  69
-226 170  11 236 178  12 224 166  10 210 150  10
-200 144  11 197 138  11 192 133   9 197 138  11
-210 150  10 226 170  11 242 186  14 246 190  14
-246 190  14 246 186  14 225 175  15 124 112  88
- 62  62  62  30  30  30  14  14  14   6   6   6
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0  10  10  10
- 30  30  30  78  78  78 174 135  50 224 166  10
-239 182  13 246 190  14 246 190  14 246 190  14
-246 190  14 246 190  14 246 190  14 246 190  14
-246 190  14 246 190  14 246 190  14 246 190  14
-246 190  14 246 190  14 241 196  14 139 102  15
-  2   2   6   2   2   6   2   2   6   2   2   6
- 78  78  78 250 250 250 253 253 253 253 253 253
-253 253 253 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 253 253 253 253 253 253
-250 250 250 214 214 214 198 198 198 190 150  46
-219 162  10 236 178  12 234 174  13 224 166  10
-216 158  10 213 154  11 213 154  11 216 158  10
-226 170  11 239 182  13 246 190  14 246 190  14
-246 190  14 246 190  14 242 186  14 206 162  42
-101 101 101  58  58  58  30  30  30  14  14  14
-  6   6   6   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0  10  10  10
- 30  30  30  74  74  74 174 135  50 216 158  10
-236 178  12 246 190  14 246 190  14 246 190  14
-246 190  14 246 190  14 246 190  14 246 190  14
-246 190  14 246 190  14 246 190  14 246 190  14
-246 190  14 246 190  14 241 196  14 226 184  13
- 61  42   6   2   2   6   2   2   6   2   2   6
- 22  22  22 238 238 238 253 253 253 253 253 253
-253 253 253 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 253 253 253 253 253 253
-253 253 253 226 226 226 187 187 187 180 133  36
-216 158  10 236 178  12 239 182  13 236 178  12
-230 174  11 226 170  11 226 170  11 230 174  11
-236 178  12 242 186  14 246 190  14 246 190  14
-246 190  14 246 190  14 246 186  14 239 182  13
-206 162  42 106 106 106  66  66  66  34  34  34
- 14  14  14   6   6   6   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   6   6   6
- 26  26  26  70  70  70 163 133  67 213 154  11
-236 178  12 246 190  14 246 190  14 246 190  14
-246 190  14 246 190  14 246 190  14 246 190  14
-246 190  14 246 190  14 246 190  14 246 190  14
-246 190  14 246 190  14 246 190  14 241 196  14
-190 146  13  18  14   6   2   2   6   2   2   6
- 46  46  46 246 246 246 253 253 253 253 253 253
-253 253 253 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 253 253 253 253 253 253
-253 253 253 221 221 221  86  86  86 156 107  11
-216 158  10 236 178  12 242 186  14 246 186  14
-242 186  14 239 182  13 239 182  13 242 186  14
-242 186  14 246 186  14 246 190  14 246 190  14
-246 190  14 246 190  14 246 190  14 246 190  14
-242 186  14 225 175  15 142 122  72  66  66  66
- 30  30  30  10  10  10   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   6   6   6
- 26  26  26  70  70  70 163 133  67 210 150  10
-236 178  12 246 190  14 246 190  14 246 190  14
-246 190  14 246 190  14 246 190  14 246 190  14
-246 190  14 246 190  14 246 190  14 246 190  14
-246 190  14 246 190  14 246 190  14 246 190  14
-232 195  16 121  92   8  34  34  34 106 106 106
-221 221 221 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 253 253 253 253 253 253
-242 242 242  82  82  82  18  14   6 163 110   8
-216 158  10 236 178  12 242 186  14 246 190  14
-246 190  14 246 190  14 246 190  14 246 190  14
-246 190  14 246 190  14 246 190  14 246 190  14
-246 190  14 246 190  14 246 190  14 246 190  14
-246 190  14 246 190  14 242 186  14 163 133  67
- 46  46  46  18  18  18   6   6   6   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0  10  10  10
- 30  30  30  78  78  78 163 133  67 210 150  10
-236 178  12 246 186  14 246 190  14 246 190  14
-246 190  14 246 190  14 246 190  14 246 190  14
-246 190  14 246 190  14 246 190  14 246 190  14
-246 190  14 246 190  14 246 190  14 246 190  14
-241 196  14 215 174  15 190 178 144 253 253 253
-253 253 253 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 253 253 253 218 218 218
- 58  58  58   2   2   6  22  18   6 167 114   7
-216 158  10 236 178  12 246 186  14 246 190  14
-246 190  14 246 190  14 246 190  14 246 190  14
-246 190  14 246 190  14 246 190  14 246 190  14
-246 190  14 246 190  14 246 190  14 246 190  14
-246 190  14 246 186  14 242 186  14 190 150  46
- 54  54  54  22  22  22   6   6   6   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0  14  14  14
- 38  38  38  86  86  86 180 133  36 213 154  11
-236 178  12 246 186  14 246 190  14 246 190  14
-246 190  14 246 190  14 246 190  14 246 190  14
-246 190  14 246 190  14 246 190  14 246 190  14
-246 190  14 246 190  14 246 190  14 246 190  14
-246 190  14 232 195  16 190 146  13 214 214 214
-253 253 253 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 253 253 253 253 253 253
-253 253 253 250 250 250 170 170 170  26  26  26
-  2   2   6   2   2   6  37  26   9 163 110   8
-219 162  10 239 182  13 246 186  14 246 190  14
-246 190  14 246 190  14 246 190  14 246 190  14
-246 190  14 246 190  14 246 190  14 246 190  14
-246 190  14 246 190  14 246 190  14 246 190  14
-246 186  14 236 178  12 224 166  10 142 122  72
- 46  46  46  18  18  18   6   6   6   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   6   6   6  18  18  18
- 50  50  50 109 106  95 192 133   9 224 166  10
-242 186  14 246 190  14 246 190  14 246 190  14
-246 190  14 246 190  14 246 190  14 246 190  14
-246 190  14 246 190  14 246 190  14 246 190  14
-246 190  14 246 190  14 246 190  14 246 190  14
-242 186  14 226 184  13 210 162  10 142 110  46
-226 226 226 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 253 253 253 253 253 253
-253 253 253 253 253 253 253 253 253 253 253 253
-198 198 198  66  66  66   2   2   6   2   2   6
-  2   2   6   2   2   6  50  34   6 156 107  11
-219 162  10 239 182  13 246 186  14 246 190  14
-246 190  14 246 190  14 246 190  14 246 190  14
-246 190  14 246 190  14 246 190  14 246 190  14
-246 190  14 246 190  14 246 190  14 242 186  14
-234 174  13 213 154  11 154 122  46  66  66  66
- 30  30  30  10  10  10   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   6   6   6  22  22  22
- 58  58  58 154 121  60 206 145  10 234 174  13
-242 186  14 246 186  14 246 190  14 246 190  14
-246 190  14 246 190  14 246 190  14 246 190  14
-246 190  14 246 190  14 246 190  14 246 190  14
-246 190  14 246 190  14 246 190  14 246 190  14
-246 186  14 236 178  12 210 162  10 163 110   8
- 61  42   6 138 138 138 218 218 218 250 250 250
-253 253 253 253 253 253 253 253 253 250 250 250
-242 242 242 210 210 210 144 144 144  66  66  66
-  6   6   6   2   2   6   2   2   6   2   2   6
-  2   2   6   2   2   6  61  42   6 163 110   8
-216 158  10 236 178  12 246 190  14 246 190  14
-246 190  14 246 190  14 246 190  14 246 190  14
-246 190  14 246 190  14 246 190  14 246 190  14
-246 190  14 239 182  13 230 174  11 216 158  10
-190 142  34 124 112  88  70  70  70  38  38  38
- 18  18  18   6   6   6   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   6   6   6  22  22  22
- 62  62  62 168 124  44 206 145  10 224 166  10
-236 178  12 239 182  13 242 186  14 242 186  14
-246 186  14 246 190  14 246 190  14 246 190  14
-246 190  14 246 190  14 246 190  14 246 190  14
-246 190  14 246 190  14 246 190  14 246 190  14
-246 190  14 236 178  12 216 158  10 175 118   6
- 80  54   7   2   2   6   6   6   6  30  30  30
- 54  54  54  62  62  62  50  50  50  38  38  38
- 14  14  14   2   2   6   2   2   6   2   2   6
-  2   2   6   2   2   6   2   2   6   2   2   6
-  2   2   6   6   6   6  80  54   7 167 114   7
-213 154  11 236 178  12 246 190  14 246 190  14
-246 190  14 246 190  14 246 190  14 246 190  14
-246 190  14 242 186  14 239 182  13 239 182  13
-230 174  11 210 150  10 174 135  50 124 112  88
- 82  82  82  54  54  54  34  34  34  18  18  18
-  6   6   6   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   6   6   6  18  18  18
- 50  50  50 158 118  36 192 133   9 200 144  11
-216 158  10 219 162  10 224 166  10 226 170  11
-230 174  11 236 178  12 239 182  13 239 182  13
-242 186  14 246 186  14 246 190  14 246 190  14
-246 190  14 246 190  14 246 190  14 246 190  14
-246 186  14 230 174  11 210 150  10 163 110   8
-104  69   6  10  10  10   2   2   6   2   2   6
-  2   2   6   2   2   6   2   2   6   2   2   6
-  2   2   6   2   2   6   2   2   6   2   2   6
-  2   2   6   2   2   6   2   2   6   2   2   6
-  2   2   6   6   6   6  91  60   6 167 114   7
-206 145  10 230 174  11 242 186  14 246 190  14
-246 190  14 246 190  14 246 186  14 242 186  14
-239 182  13 230 174  11 224 166  10 213 154  11
-180 133  36 124 112  88  86  86  86  58  58  58
- 38  38  38  22  22  22  10  10  10   6   6   6
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0  14  14  14
- 34  34  34  70  70  70 138 110  50 158 118  36
-167 114   7 180 123   7 192 133   9 197 138  11
-200 144  11 206 145  10 213 154  11 219 162  10
-224 166  10 230 174  11 239 182  13 242 186  14
-246 186  14 246 186  14 246 186  14 246 186  14
-239 182  13 216 158  10 185 133  11 152  99   6
-104  69   6  18  14   6   2   2   6   2   2   6
-  2   2   6   2   2   6   2   2   6   2   2   6
-  2   2   6   2   2   6   2   2   6   2   2   6
-  2   2   6   2   2   6   2   2   6   2   2   6
-  2   2   6   6   6   6  80  54   7 152  99   6
-192 133   9 219 162  10 236 178  12 239 182  13
-246 186  14 242 186  14 239 182  13 236 178  12
-224 166  10 206 145  10 192 133   9 154 121  60
- 94  94  94  62  62  62  42  42  42  22  22  22
- 14  14  14   6   6   6   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   6   6   6
- 18  18  18  34  34  34  58  58  58  78  78  78
-101  98  89 124 112  88 142 110  46 156 107  11
-163 110   8 167 114   7 175 118   6 180 123   7
-185 133  11 197 138  11 210 150  10 219 162  10
-226 170  11 236 178  12 236 178  12 234 174  13
-219 162  10 197 138  11 163 110   8 130  83   6
- 91  60   6  10  10  10   2   2   6   2   2   6
- 18  18  18  38  38  38  38  38  38  38  38  38
- 38  38  38  38  38  38  38  38  38  38  38  38
- 38  38  38  38  38  38  26  26  26   2   2   6
-  2   2   6   6   6   6  70  47   6 137  92   6
-175 118   6 200 144  11 219 162  10 230 174  11
-234 174  13 230 174  11 219 162  10 210 150  10
-192 133   9 163 110   8 124 112  88  82  82  82
- 50  50  50  30  30  30  14  14  14   6   6   6
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  6   6   6  14  14  14  22  22  22  34  34  34
- 42  42  42  58  58  58  74  74  74  86  86  86
-101  98  89 122 102  70 130  98  46 121  87  25
-137  92   6 152  99   6 163 110   8 180 123   7
-185 133  11 197 138  11 206 145  10 200 144  11
-180 123   7 156 107  11 130  83   6 104  69   6
- 50  34   6  54  54  54 110 110 110 101  98  89
- 86  86  86  82  82  82  78  78  78  78  78  78
- 78  78  78  78  78  78  78  78  78  78  78  78
- 78  78  78  82  82  82  86  86  86  94  94  94
-106 106 106 101 101 101  86  66  34 124  80   6
-156 107  11 180 123   7 192 133   9 200 144  11
-206 145  10 200 144  11 192 133   9 175 118   6
-139 102  15 109 106  95  70  70  70  42  42  42
- 22  22  22  10  10  10   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   6   6   6  10  10  10
- 14  14  14  22  22  22  30  30  30  38  38  38
- 50  50  50  62  62  62  74  74  74  90  90  90
-101  98  89 112 100  78 121  87  25 124  80   6
-137  92   6 152  99   6 152  99   6 152  99   6
-138  86   6 124  80   6  98  70   6  86  66  30
-101  98  89  82  82  82  58  58  58  46  46  46
- 38  38  38  34  34  34  34  34  34  34  34  34
- 34  34  34  34  34  34  34  34  34  34  34  34
- 34  34  34  34  34  34  38  38  38  42  42  42
- 54  54  54  82  82  82  94  86  76  91  60   6
-134  86   6 156 107  11 167 114   7 175 118   6
-175 118   6 167 114   7 152  99   6 121  87  25
-101  98  89  62  62  62  34  34  34  18  18  18
-  6   6   6   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   6   6   6   6   6   6  10  10  10
- 18  18  18  22  22  22  30  30  30  42  42  42
- 50  50  50  66  66  66  86  86  86 101  98  89
-106  86  58  98  70   6 104  69   6 104  69   6
-104  69   6  91  60   6  82  62  34  90  90  90
- 62  62  62  38  38  38  22  22  22  14  14  14
- 10  10  10  10  10  10  10  10  10  10  10  10
- 10  10  10  10  10  10   6   6   6  10  10  10
- 10  10  10  10  10  10  10  10  10  14  14  14
- 22  22  22  42  42  42  70  70  70  89  81  66
- 80  54   7 104  69   6 124  80   6 137  92   6
-134  86   6 116  81   8 100  82  52  86  86  86
- 58  58  58  30  30  30  14  14  14   6   6   6
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   6   6   6  10  10  10  14  14  14
- 18  18  18  26  26  26  38  38  38  54  54  54
- 70  70  70  86  86  86  94  86  76  89  81  66
- 89  81  66  86  86  86  74  74  74  50  50  50
- 30  30  30  14  14  14   6   6   6   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  6   6   6  18  18  18  34  34  34  58  58  58
- 82  82  82  89  81  66  89  81  66  89  81  66
- 94  86  66  94  86  76  74  74  74  50  50  50
- 26  26  26  14  14  14   6   6   6   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  6   6   6   6   6   6  14  14  14  18  18  18
- 30  30  30  38  38  38  46  46  46  54  54  54
- 50  50  50  42  42  42  30  30  30  18  18  18
- 10  10  10   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   6   6   6  14  14  14  26  26  26
- 38  38  38  50  50  50  58  58  58  58  58  58
- 54  54  54  42  42  42  30  30  30  18  18  18
- 10  10  10   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   6   6   6
-  6   6   6  10  10  10  14  14  14  18  18  18
- 18  18  18  14  14  14  10  10  10   6   6   6
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   6   6   6
- 14  14  14  18  18  18  22  22  22  22  22  22
- 18  18  18  14  14  14  10  10  10   6   6   6
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
-  0   0   0   0   0   0   0   0   0   0   0   0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 1 0  0 0 0  0 0 0  1 1 0
+0 1 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  1 1 0  0 0 0  0 0 0
+0 1 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  1 1 0
+10 15 3  2 3 1  12 18 4  42 61 14  19 27 6  11 16 4
+38 55 13  10 15 3  3 4 1  10 15 3  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  2 3 1
+12 18 4  1 1 0  23 34 8  31 45 11  10 15 3  32 47 11
+34 49 12  3 4 1  3 4 1  3 4 1  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  10 15 3  29 42 10  26 37 9  12 18 4
+55 80 19  81 118 28  55 80 19  92 132 31  106 153 36  69 100 23
+100 144 34  80 116 27  42 61 14  81 118 28  23 34 8  27 40 9
+15 21 5  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  1 1 0  29 42 10  15 21 5  50 72 17
+74 107 25  45 64 15  102 148 35  80 116 27  84 121 28  111 160 38
+69 100 23  65 94 22  81 118 28  29 42 10  17 25 6  29 42 10
+23 34 8  2 3 1  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  3 4 1
+15 21 5  15 21 5  34 49 12  101 146 34  111 161 38  97 141 33
+97 141 33  119 172 41  117 170 40  116 167 40  118 170 40  118 171 40
+117 169 40  118 170 40  111 160 38  118 170 40  96 138 32  89 128 30
+81 118 28  11 16 4  10 15 3  1 1 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+3 4 1  3 4 1  34 49 12  101 146 34  79 115 27  111 160 38
+114 165 39  113 163 39  118 170 40  117 169 40  118 171 40  117 169 40
+116 167 40  119 172 41  113 163 39  92 132 31  105 151 36  113 163 39
+75 109 26  19 27 6  16 23 5  11 16 4  0 1 0  0 0 0
+0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  10 15 3
+80 116 27  106 153 36  105 151 36  114 165 39  118 170 40  118 171 40
+118 171 40  117 169 40  117 169 40  117 169 40  117 169 40  117 169 40
+117 169 40  117 169 40  117 170 40  117 169 40  118 170 40  118 170 40
+117 170 40  75 109 26  75 109 26  34 49 12  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  3 4 1
+64 92 22  65 94 22  100 144 34  118 171 40  118 170 40  117 169 40
+117 169 40  117 169 40  117 169 40  117 169 40  117 169 40  117 169 40
+117 169 40  117 169 40  117 169 40  118 171 41  118 170 40  117 169 40
+109 158 37  105 151 36  104 150 35  47 69 16  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+42 61 14  115 167 39  118 170 40  117 169 40  117 169 40  117 169 40
+117 170 40  117 170 40  117 169 40  117 169 40  117 169 40  117 169 40
+117 169 40  117 169 40  117 169 40  117 169 40  117 169 40  117 169 40
+117 169 40  117 169 40  118 170 40  96 138 32  17 25 6  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  47 69 16
+114 165 39  117 168 40  117 170 40  117 169 40  117 169 40  117 169 40
+117 169 40  117 169 40  117 169 40  117 169 40  117 169 40  117 169 40
+117 169 40  117 169 40  118 170 40  117 169 40  117 169 40  117 169 40
+117 170 40  119 172 41  96 138 32  12 18 4  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  10 15 3
+32 47 11  105 151 36  118 170 40  117 169 40  117 169 40  116 168 40
+109 157 37  111 160 38  117 169 40  118 171 40  117 169 40  117 169 40
+117 169 40  117 169 40  117 169 40  117 169 40  117 169 40  117 169 40
+117 169 40  117 169 40  117 169 40  118 171 40  69 100 23  2 3 1
+0 0 0  0 0 0  0 0 0  0 0 0  19 27 6  101 146 34
+118 171 40  117 169 40  117 169 40  117 169 40  117 169 40  117 169 40
+117 169 40  117 169 40  117 169 40  117 169 40  117 169 40  117 170 40
+118 171 40  115 166 39  107 154 36  111 161 38  117 169 40  117 169 40
+117 169 40  118 171 40  75 109 26  19 27 6  2 3 1  0 0 0
+0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  16 23 5
+89 128 30  117 169 40  117 169 40  117 169 40  117 169 40  117 169 40
+111 160 38  92 132 31  79 115 27  96 138 32  115 166 39  119 171 41
+117 169 40  117 169 40  117 169 40  117 169 40  117 169 40  117 169 40
+117 169 40  117 169 40  117 169 40  118 170 40  109 157 37  26 37 9
+0 0 0  0 0 0  0 0 0  0 0 0  64 92 22  118 171 40
+117 169 40  117 169 40  117 169 40  117 169 40  117 169 40  117 169 40
+117 169 40  117 169 40  117 169 40  118 170 40  118 171 40  109 157 37
+89 128 30  81 118 28  100 144 34  115 166 39  117 169 40  117 169 40
+117 169 40  117 170 40  113 163 39  60 86 20  1 1 0  0 0 0
+0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+27 40 9  96 138 32  118 170 40  117 169 40  117 169 40  117 169 40
+117 170 40  117 169 40  101 146 34  67 96 23  55 80 19  84 121 28
+113 163 39  119 171 41  117 169 40  117 169 40  117 169 40  117 169 40
+117 169 40  117 169 40  117 169 40  117 169 40  119 171 41  65 94 22
+0 0 0  0 0 0  0 0 0  15 21 5  101 146 34  118 171 40
+117 169 40  117 169 40  117 169 40  117 169 40  117 169 40  117 169 40
+117 169 40  118 170 40  118 171 40  104 150 35  69 100 23  53 76 18
+81 118 28  111 160 38  118 170 40  117 169 40  117 169 40  117 169 40
+117 169 40  114 165 39  69 100 23  10 15 3  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  1 1 0
+31 45 11  77 111 26  117 169 40  117 169 40  117 169 40  117 169 40
+117 169 40  117 169 40  118 170 40  116 168 40  92 132 31  47 69 16
+38 55 13  81 118 28  113 163 39  119 171 41  117 169 40  117 169 40
+117 169 40  117 169 40  117 169 40  117 169 40  118 171 41  92 132 31
+10 15 3  0 0 0  0 0 0  36 52 12  115 166 39  117 169 40
+117 169 40  117 169 40  117 169 40  117 169 40  117 169 40  118 170 40
+118 171 40  102 148 35  64 92 22  34 49 12  65 94 22  106 153 36
+118 171 40  117 170 40  117 169 40  117 169 40  117 169 40  117 169 40
+118 170 40  107 154 36  55 80 19  15 21 5  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+29 42 10  101 146 34  118 171 40  117 169 40  117 169 40  117 169 40
+117 169 40  117 169 40  117 169 40  117 169 40  118 171 40  113 163 39
+75 109 26  27 40 9  36 52 12  89 128 30  116 167 40  118 171 40
+117 169 40  117 169 40  117 169 40  117 169 40  118 170 40  104 150 35
+16 23 5  0 0 0  0 0 0  53 76 18  118 171 40  117 169 40
+117 169 40  117 169 40  117 169 40  117 169 40  119 171 41  109 157 37
+67 96 23  23 34 8  42 61 14  96 138 32  118 170 40  118 170 40
+117 169 40  117 169 40  117 169 40  117 169 40  117 169 40  117 169 40
+117 169 40  117 169 40  74 107 25  10 15 3  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  31 45 11  101 146 34  118 170 40  117 169 40  117 169 40
+117 169 40  117 169 40  117 169 40  117 169 40  117 169 40  117 169 40
+119 171 41  102 148 35  47 69 16  14 20 5  50 72 17  102 148 35
+118 171 40  117 169 40  117 169 40  117 169 40  118 170 40  102 148 35
+15 21 5  0 0 0  0 0 0  50 72 17  118 170 40  117 169 40
+117 169 40  117 169 40  118 170 40  116 167 40  84 121 28  27 40 9
+19 27 6  74 107 25  114 165 39  118 171 40  117 169 40  117 169 40
+117 169 40  117 169 40  117 169 40  117 169 40  117 169 40  117 169 40
+117 169 40  75 109 26  10 15 4  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  38 55 13  102 148 35  118 171 40  117 169 40  117 169 40
+117 169 40  117 169 40  117 169 40  117 169 40  117 169 40  117 169 40
+117 169 40  118 170 40  115 167 39  77 111 26  17 25 6  19 27 6
+77 111 26  115 166 39  118 170 40  117 169 40  119 172 41  81 118 28
+3 4 1  0 0 0  0 0 0  27 40 9  111 160 38  118 170 40
+117 169 40  118 171 40  105 151 36  50 72 17  10 15 3  38 55 13
+100 144 34  118 171 40  117 169 40  117 169 40  117 169 40  117 169 40
+117 169 40  117 169 40  117 169 40  117 169 40  117 169 40  117 169 40
+117 169 40  79 115 27  15 21 5  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  10 15 3  64 92 22  111 160 38  117 169 40  117 169 40
+117 169 40  117 169 40  117 169 40  117 169 40  117 169 40  117 169 40
+117 169 40  117 169 40  117 169 40  118 171 40  96 138 32  32 47 11
+3 4 1  50 72 17  107 154 36  120 173 41  105 151 36  31 45 11
+0 0 0  0 0 0  0 0 0  3 4 1  65 94 22  117 169 40
+118 170 40  89 128 30  26 37 9  3 4 1  60 86 20  111 161 38
+118 171 40  117 169 40  117 169 40  117 169 40  117 169 40  117 169 40
+117 169 40  117 169 40  117 169 40  117 169 40  117 169 40  117 169 40
+97 141 33  36 52 12  1 1 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  14 20 5  75 109 26  117 168 40  117 169 40
+117 169 40  117 169 40  117 169 40  117 169 40  117 169 40  117 169 40
+117 169 40  117 169 40  117 169 40  117 169 40  118 171 40  107 154 36
+45 64 15  2 3 1  31 45 11  75 109 26  32 47 11  0 1 0
+0 0 0  0 0 0  0 0 0  0 0 0  10 15 3  55 80 19
+65 94 22  11 16 4  11 16 4  75 109 26  116 168 40  118 170 40
+117 169 40  117 169 40  117 169 40  117 169 40  117 169 40  117 169 40
+117 169 40  117 169 40  117 169 40  117 169 40  118 170 40  107 154 36
+47 69 16  3 4 1  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  12 18 4  69 100 23  111 161 38  118 171 40
+117 169 40  117 169 40  117 169 40  117 169 40  117 169 40  117 169 40
+117 169 40  117 169 40  117 169 40  117 169 40  117 169 40  118 170 40
+111 160 38  50 72 17  2 3 1  2 3 1  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  1 1 0
+1 1 0  12 18 4  81 118 28  118 170 40  117 169 40  117 169 40
+117 169 40  117 169 40  117 169 40  117 169 40  117 169 40  117 169 40
+117 169 40  117 169 40  117 169 40  117 170 40  118 171 40  101 146 34
+42 61 14  2 3 1  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  3 4 1  36 52 12  89 128 30
+117 169 40  117 169 40  117 169 40  117 169 40  117 169 40  117 169 40
+117 169 40  117 169 40  117 169 40  117 169 40  117 169 40  117 169 40
+118 171 41  101 146 34  14 20 5  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  47 69 16  118 170 40  117 169 40  117 169 40  117 169 40
+117 169 40  117 169 40  117 169 40  117 169 40  117 169 40  117 169 40
+117 169 40  117 169 40  117 170 40  111 160 38  69 100 23  19 27 6
+0 1 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  11 16 4  69 100 23
+115 167 39  119 172 41  117 169 40  117 169 40  117 169 40  117 169 40
+117 169 40  117 169 40  117 169 40  117 169 40  117 169 40  117 169 40
+119 172 41  75 109 26  3 4 1  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  23 34 8  106 153 36  118 170 40  117 169 40  117 169 40
+117 169 40  117 169 40  117 169 40  117 169 40  117 169 40  117 169 40
+117 169 40  118 170 40  119 172 41  105 151 36  42 61 14  2 3 1
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  1 1 0  15 21 5
+45 64 15  80 116 27  114 165 39  118 170 40  117 169 40  117 169 40
+117 169 40  117 169 40  117 169 40  117 169 40  117 169 40  119 172 41
+97 141 33  20 30 7  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  1 1 0  53 76 18  114 165 39  118 171 40  117 169 40
+117 169 40  117 169 40  117 169 40  117 169 40  117 169 40  117 169 40
+118 171 40  104 150 35  64 92 22  31 45 11  10 15 3  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  36 52 12  97 141 33  109 158 37  113 163 39  116 168 40
+117 169 40  117 170 40  118 170 40  119 172 41  115 167 39  84 121 28
+23 34 8  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  3 4 1  50 72 17  102 148 35  118 171 40
+119 171 41  118 170 40  117 169 40  117 169 40  115 166 39  111 161 38
+109 157 37  79 115 27  12 18 4  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  3 4 1  15 21 5  23 34 8  45 64 15  106 153 36
+116 167 40  111 160 38  101 146 34  79 115 27  42 61 14  10 15 3
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  1 1 0  20 30 7  60 86 20
+89 128 30  106 153 36  113 163 39  117 169 40  84 121 28  29 42 10
+19 27 6  10 15 3  2 3 1  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  16 23 5  38 55 13
+36 52 12  26 37 9  12 18 4  2 3 1  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  1 0 0  19 2 7  52 5 18
+78 7 27  88 8 31  81 7 29  56 5 19  25 2 9  3 0 1
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+3 4 1  19 27 6  31 45 11  38 55 13  32 47 11  3 4 1
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  3 0 1
+9 0 3  12 1 4  9 0 3  4 0 1  0 0 0  0 0 0
+0 0 0  0 0 0  28 3 10  99 9 35  156 14 55  182 16 64
+189 17 66  190 17 67  189 17 66  184 17 65  166 15 58  118 13 41
+45 4 16  3 0 1  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  11 1 4  52 5 18  101 9 35  134 12 47
+151 14 53  154 14 54  151 14 53  113 10 40  11 1 4  0 0 0
+3 0 1  67 6 24  159 14 56  190 17 67  190 17 67  188 17 66
+188 17 66  188 17 66  188 17 66  188 17 66  190 17 67  191 17 67
+174 16 61  101 9 35  14 1 5  0 0 0  35 3 12  108 10 38
+122 11 43  122 11 43  112 10 39  87 8 30  50 5 17  13 1 5
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+3 0 1  56 5 19  141 13 49  182 16 64  191 17 67  191 17 67
+190 17 67  190 17 67  191 17 67  113 10 40  3 0 1  1 0 0
+79 7 28  180 16 63  190 17 67  188 17 66  188 17 66  188 17 66
+188 17 66  188 17 66  188 17 66  188 17 66  188 17 66  188 17 66
+189 17 66  188 17 66  122 11 43  11 1 4  41 4 14  176 16 62
+191 17 67  191 17 67  191 17 67  190 17 67  181 16 63  146 13 51
+75 7 26  10 1 4  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  7 1 2
+90 8 32  178 16 62  191 17 67  188 17 66  188 17 66  188 17 66
+188 17 66  190 17 67  141 13 49  22 2 8  0 0 0  41 4 14
+173 16 61  190 17 67  188 17 66  188 17 66  188 17 66  188 17 66
+188 17 66  188 17 66  188 17 66  188 17 66  188 17 66  188 17 66
+188 17 66  188 17 66  188 17 66  88 8 31  1 0 0  89 8 31
+185 17 65  189 17 66  188 17 66  188 17 66  189 17 66  191 17 67
+186 17 65  124 11 43  25 2 9  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  2 0 1  89 8 31
+184 17 65  189 17 66  188 17 66  188 17 66  188 17 66  188 17 66
+190 17 67  151 14 53  34 3 12  0 0 0  0 0 0  79 7 28
+190 17 67  188 17 66  188 17 66  188 17 66  188 17 66  188 17 66
+188 17 66  188 17 66  188 17 66  188 17 66  188 17 66  188 17 66
+188 17 66  188 17 66  191 17 67  146 13 51  9 1 3  7 1 2
+108 10 38  187 17 66  189 17 66  188 17 66  188 17 66  188 17 66
+188 17 66  190 17 67  141 13 49  22 2 8  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  52 5 18  176 16 62
+189 17 66  188 17 66  188 17 66  188 17 66  188 17 66  190 17 67
+151 14 53  38 3 13  0 0 0  0 0 0  0 0 0  50 5 17
+180 16 63  189 17 66  188 17 66  188 17 66  188 17 66  188 17 66
+188 17 66  188 17 66  188 17 66  188 17 66  188 17 66  188 17 66
+188 17 66  188 17 66  191 17 67  141 13 49  7 1 3  0 0 0
+11 1 4  112 10 39  187 17 66  189 17 66  188 17 66  188 17 66
+188 17 66  188 17 66  190 17 67  113 10 40  5 0 2  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  7 1 3  132 12 46  191 17 67
+188 17 66  188 17 66  188 17 66  188 17 66  190 17 67  146 13 51
+35 3 12  0 0 0  0 0 0  0 0 0  0 0 0  5 0 2
+101 9 35  185 17 65  190 17 67  188 17 66  188 17 66  188 17 66
+188 17 66  188 17 66  188 17 66  188 17 66  188 17 66  188 17 66
+188 17 66  190 17 67  180 16 63  67 6 24  0 0 0  0 0 0
+0 0 0  11 1 4  108 10 38  186 17 65  189 17 66  188 17 66
+188 17 66  188 17 66  189 17 66  180 16 63  56 5 19  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  44 4 15  177 16 62  189 17 66
+188 17 66  188 17 66  189 17 66  189 17 66  134 12 47  28 3 10
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+8 1 3  79 7 28  159 14 56  188 17 66  191 17 67  190 17 67
+189 17 66  189 17 66  189 17 66  189 17 66  190 17 67  191 17 67
+188 17 66  158 14 55  72 7 25  4 0 1  0 0 0  0 0 0
+0 0 0  0 0 0  8 1 3  95 9 33  182 16 64  189 17 67
+188 17 66  188 17 66  188 17 66  191 17 67  122 11 43  3 0 1
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  88 8 31  190 17 67  188 17 66
+188 17 66  189 17 66  185 17 65  113 10 40  18 2 6  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  1 0 0  24 2 8  77 7 27  124 11 43  154 14 54
+168 15 59  173 16 61  173 16 61  168 15 59  154 14 54  124 11 43
+77 7 27  22 2 8  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  5 0 2  77 7 27  173 16 61
+190 17 67  188 17 66  188 17 66  190 17 67  164 15 57  23 2 8
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  1 0 0  118 13 41  191 17 67  188 17 66
+190 17 67  174 16 61  87 8 30  8 1 3  0 0 0  0 0 0
+0 0 0  0 0 0  10 1 4  29 3 10  40 4 14  36 3 13
+18 2 6  2 0 1  0 0 0  0 0 0  3 0 1  14 1 5
+26 2 9  33 3 11  32 3 11  25 2 9  13 1 5  3 0 1
+0 0 0  14 1 5  56 5 19  95 9 33  109 10 38  101 9 35
+77 7 27  35 3 12  5 0 2  0 0 0  1 0 0  56 5 19
+156 14 55  190 17 67  188 17 66  188 17 66  182 16 64  50 5 17
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  5 0 2  134 12 47  191 17 67  189 17 66
+151 14 53  52 5 18  2 0 1  0 0 0  0 0 0  1 0 0
+28 3 10  90 8 32  146 13 51  170 15 60  178 16 62  174 16 61
+158 14 55  112 10 39  40 4 14  1 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  3 0 1
+56 5 19  146 13 51  183 17 64  191 17 67  191 17 67  191 17 67
+188 17 66  173 16 61  122 11 43  41 4 14  1 0 0  0 0 0
+30 3 10  124 11 43  185 17 65  190 17 67  187 17 66  67 6 24
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  6 1 2  134 12 47  168 15 59  99 9 35
+21 2 7  0 0 0  0 0 0  0 0 0  6 1 2  77 7 27
+162 15 57  190 17 67  191 17 67  189 17 66  189 17 66  189 17 66
+190 17 67  191 17 67  169 15 59  75 7 26  3 0 1  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  2 0 1  79 7 28
+178 16 62  191 17 67  188 17 66  188 17 66  188 17 66  188 17 66
+188 17 66  189 17 66  191 17 67  170 15 60  79 7 28  5 0 2
+0 0 0  10 1 3  78 7 27  159 14 56  188 17 66  75 7 26
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  1 0 0  35 3 12  29 3 10  2 0 1
+0 0 0  0 0 0  0 0 0  9 1 3  101 9 35  183 17 64
+190 17 67  188 17 66  188 17 66  188 17 66  188 17 66  188 17 66
+188 17 66  188 17 66  190 17 67  178 16 63  67 6 23  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  52 5 18  174 16 61
+190 17 67  188 17 66  188 17 66  188 17 66  188 17 66  188 17 66
+188 17 66  188 17 66  188 17 66  190 17 67  182 16 64  89 8 31
+4 0 1  0 0 0  0 0 0  25 2 9  73 7 26  31 3 11
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  4 0 1  98 9 34  187 17 66  189 17 66
+188 17 66  188 17 66  188 17 66  188 17 66  188 17 66  188 17 66
+188 17 66  188 17 66  188 17 66  190 17 67  158 14 55  25 2 9
+0 0 0  0 0 0  0 0 0  8 1 3  134 12 47  191 17 67
+188 17 66  188 17 66  188 17 66  188 17 66  188 17 66  188 17 66
+188 17 66  188 17 66  188 17 66  188 17 66  189 17 66  180 16 63
+68 6 24  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  6 1 2  19 2 7  3 0 1  0 0 0  0 0 0
+0 0 0  0 0 0  65 6 23  180 16 63  189 17 66  188 17 66
+188 17 66  188 17 66  188 17 66  188 17 66  188 17 66  188 17 66
+188 17 66  188 17 66  188 17 66  188 17 66  189 17 66  83 8 29
+0 0 0  0 0 0  0 0 0  41 4 14  177 16 62  189 17 66
+188 17 66  188 17 66  188 17 66  188 17 66  188 17 66  188 17 66
+188 17 66  188 17 66  188 17 66  188 17 66  188 17 66  190 17 67
+159 14 56  28 3 10  0 0 0  0 0 0  0 0 0  23 2 8
+41 4 14  5 0 2  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+23 2 8  113 10 40  159 14 56  65 6 23  0 0 0  0 0 0
+0 0 0  16 1 6  146 13 51  191 17 67  188 17 66  188 17 66
+188 17 66  188 17 66  188 17 66  188 17 66  188 17 66  188 17 66
+188 17 66  188 17 66  188 17 66  188 17 66  191 17 67  132 12 46
+5 0 2  0 0 0  0 0 0  77 7 27  189 17 66  188 17 66
+188 17 66  188 17 66  188 17 66  188 17 66  188 17 66  188 17 66
+188 17 66  188 17 66  188 17 66  188 17 66  188 17 66  188 17 66
+190 17 67  98 9 34  0 0 0  0 0 0  12 1 4  134 12 47
+178 16 63  108 10 38  16 1 6  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  30 3 10
+141 13 49  190 17 67  191 17 67  134 12 47  6 1 2  0 0 0
+0 0 0  68 6 24  186 17 65  188 17 66  188 17 66  188 17 66
+188 17 66  188 17 66  188 17 66  188 17 66  188 17 66  188 17 66
+188 17 66  188 17 66  188 17 66  188 17 66  190 17 67  156 14 55
+14 1 5  0 0 0  0 0 0  98 9 34  191 17 67  188 17 66
+188 17 66  188 17 66  188 17 66  188 17 66  188 17 66  188 17 66
+188 17 66  188 17 66  188 17 66  188 17 66  188 17 66  188 17 66
+190 17 67  156 14 55  19 2 7  0 0 0  47 4 16  181 16 63
+190 17 67  189 17 66  126 14 44  17 2 6  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  16 1 6  134 12 47
+191 17 67  188 17 66  190 17 67  162 15 57  19 2 7  0 0 0
+3 0 1  123 11 43  191 17 67  188 17 66  188 17 66  188 17 66
+188 17 66  188 17 66  188 17 66  188 17 66  188 17 66  188 17 66
+188 17 66  188 17 66  188 17 66  188 17 66  190 17 67  163 15 57
+20 2 7  0 0 0  0 0 0  101 9 35  191 17 67  188 17 66
+188 17 66  188 17 66  188 17 66  188 17 66  188 17 66  188 17 66
+188 17 66  188 17 66  188 17 66  188 17 66  188 17 66  188 17 66
+188 17 66  182 16 64  52 5 18  0 0 0  73 7 26  188 17 66
+188 17 66  188 17 66  189 17 66  109 10 38  5 0 2  0 0 0
+0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  95 9 33  189 17 66
+188 17 66  188 17 66  189 17 66  171 15 60  29 3 10  0 0 0
+16 1 6  156 14 55  190 17 67  188 17 66  188 17 66  188 17 66
+188 17 66  188 17 66  188 17 66  188 17 66  188 17 66  188 17 66
+188 17 66  188 17 66  188 17 66  188 17 66  190 17 67  158 14 55
+17 2 6  0 0 0  0 0 0  85 8 30  190 17 67  188 17 66
+188 17 66  188 17 66  188 17 66  188 17 66  188 17 66  188 17 66
+188 17 66  188 17 66  188 17 66  188 17 66  188 17 66  188 17 66
+188 17 66  189 17 66  81 7 29  0 0 0  85 8 30  190 17 67
+188 17 66  188 17 66  189 17 66  180 16 63  56 5 19  0 0 0
+0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  25 2 9  162 15 57  190 17 67
+188 17 66  188 17 66  189 17 66  173 16 61  31 3 11  0 0 0
+30 3 10  171 15 60  189 17 66  188 17 66  188 17 66  188 17 66
+188 17 66  188 17 66  188 17 66  188 17 66  188 17 66  188 17 66
+188 17 66  188 17 66  188 17 66  188 17 66  191 17 67  141 13 49
+7 1 2  0 0 0  0 0 0  56 5 19  183 17 64  188 17 66
+188 17 66  188 17 66  188 17 66  188 17 66  188 17 66  188 17 66
+188 17 66  188 17 66  188 17 66  188 17 66  188 17 66  188 17 66
+188 17 66  191 17 67  98 9 34  0 0 0  88 8 31  190 17 67
+188 17 66  188 17 66  188 17 66  191 17 67  124 11 43  5 0 2
+0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  68 6 24  187 17 66  188 17 66
+188 17 66  188 17 66  189 17 66  170 15 60  28 3 10  0 0 0
+34 3 12  174 16 61  189 17 66  188 17 66  188 17 66  188 17 66
+188 17 66  188 17 66  188 17 66  188 17 66  188 17 66  188 17 66
+188 17 66  188 17 66  188 17 66  188 17 66  191 17 67  101 9 35
+0 0 0  0 0 0  0 0 0  21 2 7  159 14 56  190 17 67
+188 17 66  188 17 66  188 17 66  188 17 66  188 17 66  188 17 66
+188 17 66  188 17 66  188 17 66  188 17 66  188 17 66  188 17 66
+188 17 66  191 17 67  98 9 34  0 0 0  81 7 29  189 17 66
+188 17 66  188 17 66  188 17 66  189 17 66  168 15 59  28 3 10
+0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  109 10 38  191 17 67  188 17 66
+188 17 66  188 17 66  190 17 67  163 15 57  21 2 7  0 0 0
+26 2 9  168 15 59  189 17 66  188 17 66  188 17 66  188 17 66
+188 17 66  188 17 66  188 17 66  188 17 66  188 17 66  188 17 66
+188 17 66  188 17 66  188 17 66  189 17 66  180 16 63  47 4 16
+0 0 0  0 0 0  0 0 0  0 0 0  108 10 38  190 17 67
+188 17 66  188 17 66  188 17 66  188 17 66  188 17 66  188 17 66
+188 17 66  188 17 66  188 17 66  188 17 66  188 17 66  188 17 66
+188 17 66  189 17 66  78 7 27  0 0 0  68 6 24  187 17 66
+188 17 66  188 17 66  188 17 66  188 17 66  183 17 64  56 5 19
+0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  3 0 1  131 12 46  191 17 67  188 17 66
+188 17 66  188 17 66  190 17 67  151 14 53  12 1 4  0 0 0
+11 1 4  146 13 51  190 17 67  188 17 66  188 17 66  188 17 66
+188 17 66  188 17 66  188 17 66  188 17 66  188 17 66  188 17 66
+188 17 66  188 17 66  188 17 66  191 17 67  126 14 44  7 1 2
+0 0 0  0 0 0  0 0 0  0 0 0  32 3 11  164 15 58
+190 17 67  188 17 66  188 17 66  188 17 66  188 17 66  188 17 66
+188 17 66  188 17 66  188 17 66  188 17 66  188 17 66  188 17 66
+189 17 66  178 16 62  44 4 15  0 0 0  50 5 17  182 16 64
+188 17 66  188 17 66  188 17 66  188 17 66  188 17 66  72 7 25
+0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  5 0 2  134 12 47  191 17 67  188 17 66
+188 17 66  188 17 66  191 17 67  131 12 46  3 0 1  0 0 0
+0 0 0  101 9 35  190 17 67  188 17 66  188 17 66  188 17 66
+188 17 66  188 17 66  188 17 66  188 17 66  188 17 66  188 17 66
+188 17 66  188 17 66  190 17 67  170 15 60  44 4 15  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  77 7 27
+183 17 64  189 17 66  188 17 66  188 17 66  188 17 66  188 17 66
+188 17 66  188 17 66  188 17 66  188 17 66  188 17 66  188 17 66
+191 17 67  134 12 47  9 1 3  0 0 0  31 3 11  171 15 60
+189 17 66  188 17 66  188 17 66  188 17 66  188 17 66  72 7 25
+0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  2 0 1  124 11 43  191 17 67  188 17 66
+188 17 66  188 17 66  191 17 67  101 9 35  0 0 0  0 0 0
+0 0 0  35 3 12  168 15 59  190 17 67  188 17 66  188 17 66
+188 17 66  188 17 66  188 17 66  188 17 66  188 17 66  188 17 66
+188 17 66  189 17 66  182 16 64  77 7 27  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  6 1 2
+99 9 35  185 17 65  189 17 66  188 17 66  188 17 66  188 17 66
+188 17 66  188 17 66  188 17 66  188 17 66  188 17 66  189 17 66
+177 16 62  56 5 19  0 0 0  0 0 0  13 1 5  151 14 53
+190 17 67  188 17 66  188 17 66  188 17 66  185 17 65  56 5 19
+0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  99 9 35  191 17 67  188 17 66
+188 17 66  188 17 66  186 17 65  65 6 23  0 0 0  0 0 0
+0 0 0  0 0 0  79 7 28  182 16 64  190 17 67  188 17 66
+188 17 66  188 17 66  188 17 66  188 17 66  188 17 66  188 17 66
+191 17 67  177 16 62  83 8 29  4 0 1  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+8 1 3  89 8 31  175 16 62  191 17 67  189 17 66  188 17 66
+188 17 66  188 17 66  188 17 66  188 17 66  190 17 67  181 16 63
+85 8 30  3 0 1  0 0 0  0 0 0  1 0 0  118 13 41
+191 17 67  188 17 66  188 17 66  189 17 66  173 16 61  34 3 12
+0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  56 5 19  183 17 64  188 17 66
+188 17 66  189 17 66  169 15 59  30 3 10  0 0 0  0 0 0
+0 0 0  0 0 0  5 0 2  83 8 29  173 16 61  191 17 67
+190 17 67  189 17 66  189 17 66  190 17 67  191 17 67  187 17 66
+151 14 53  56 5 19  3 0 1  0 0 0  16 1 6  50 5 17
+79 7 28  95 9 33  95 9 33  75 7 26  41 4 14  10 1 4
+0 0 0  2 0 1  50 5 17  132 12 46  178 16 62  190 17 67
+191 17 67  191 17 67  191 17 67  186 17 65  154 14 54  68 6 24
+4 0 1  0 0 0  0 0 0  0 0 0  0 0 0  72 7 25
+187 17 66  188 17 66  188 17 66  191 17 67  141 13 49  9 1 3
+0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  14 1 5  151 14 53  190 17 67
+188 17 66  191 17 67  131 12 46  5 0 2  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  2 0 1  44 4 15  113 10 40
+156 14 55  173 16 61  174 16 61  164 15 58  134 12 47  77 7 27
+18 2 6  0 0 0  16 1 6  85 8 30  151 14 53  182 16 64
+189 17 66  191 17 67  190 17 67  188 17 66  177 16 62  141 13 49
+68 6 24  8 1 3  0 0 0  8 1 3  44 4 15  88 8 31
+113 10 40  122 11 43  108 10 38  67 6 24  20 2 7  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  28 3 10
+166 15 58  190 17 67  188 17 66  187 17 66  79 7 28  0 0 0
+0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  73 7 26  185 17 65
+189 17 66  184 17 65  65 6 23  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  2 0 1
+17 2 6  32 3 11  34 3 12  22 2 8  6 1 2  0 0 0
+0 0 0  38 3 13  141 13 49  188 17 66  190 17 67  188 17 66
+188 17 66  188 17 66  188 17 66  188 17 66  189 17 66  191 17 67
+184 17 65  122 11 43  21 2 7  0 0 0  0 0 0  0 0 0
+0 0 0  1 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  1 0 0
+108 10 38  191 17 67  191 17 67  141 13 49  16 1 6  0 0 0
+0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  8 1 3  112 10 39
+186 17 65  124 11 43  10 1 4  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+36 3 13  156 14 55  191 17 67  188 17 66  188 17 66  188 17 66
+188 17 66  188 17 66  188 17 66  188 17 66  188 17 66  188 17 66
+189 17 66  190 17 67  134 12 47  18 2 6  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  7 1 2  41 4 14  75 7 26  66 5 23  19 2 7
+26 2 9  144 13 50  154 14 54  40 4 14  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  13 1 5
+56 5 19  19 2 7  0 0 0  7 1 2  29 3 10  35 3 12
+19 2 7  2 0 1  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  13 1 5
+134 12 47  191 17 67  188 17 66  188 17 66  188 17 66  188 17 66
+188 17 66  188 17 66  188 17 66  188 17 66  188 17 66  188 17 66
+188 17 66  188 17 66  189 17 67  108 10 38  3 0 1  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  1 0 0
+40 4 14  124 11 43  177 16 62  188 17 66  187 17 66  144 13 50
+24 2 8  17 2 6  22 2 8  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  19 2 7  122 11 43  171 15 60  175 16 62
+159 14 56  112 10 39  40 4 14  2 0 1  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  72 7 25
+186 17 65  188 17 66  188 17 66  188 17 66  188 17 66  188 17 66
+188 17 66  188 17 66  188 17 66  188 17 66  188 17 66  188 17 66
+188 17 66  188 17 66  189 17 66  174 16 61  41 4 14  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  3 0 1  72 7 25
+168 15 59  191 17 67  189 17 66  188 17 66  188 17 66  190 17 67
+95 9 33  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  95 9 33  191 17 67  189 17 66  189 17 66
+190 17 67  191 17 67  171 15 60  90 8 32  12 1 4  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  5 0 2  132 12 46
+191 17 67  188 17 66  188 17 66  188 17 66  188 17 66  188 17 66
+188 17 66  188 17 66  188 17 66  188 17 66  188 17 66  188 17 66
+188 17 66  188 17 66  188 17 66  190 17 67  98 9 34  0 0 0
+0 0 0  0 0 0  0 0 0  5 0 2  88 8 31  180 16 63
+190 17 67  188 17 66  188 17 66  188 17 66  188 17 66  191 17 67
+146 13 51  11 1 4  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  9 1 3  144 13 50  191 17 67  188 17 66  188 17 66
+188 17 66  188 17 66  189 17 66  187 17 66  123 11 43  20 2 7
+0 0 0  0 0 0  0 0 0  0 0 0  21 2 7  163 15 57
+190 17 67  188 17 66  188 17 66  188 17 66  188 17 66  188 17 66
+188 17 66  188 17 66  188 17 66  188 17 66  188 17 66  188 17 66
+188 17 66  188 17 66  188 17 66  191 17 67  134 12 47  5 0 2
+0 0 0  0 0 0  3 0 1  88 8 31  182 16 64  189 17 66
+188 17 66  188 17 66  188 17 66  188 17 66  188 17 66  189 17 66
+171 15 60  31 3 11  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  20 2 7  162 15 57  190 17 67  188 17 66  188 17 66
+188 17 66  188 17 66  188 17 66  188 17 66  190 17 67  132 12 46
+20 2 7  0 0 0  0 0 0  0 0 0  32 3 11  173 16 61
+189 17 66  188 17 66  188 17 66  188 17 66  188 17 66  188 17 66
+188 17 66  188 17 66  188 17 66  188 17 66  188 17 66  188 17 66
+188 17 66  188 17 66  188 17 66  190 17 67  151 14 53  12 1 4
+0 0 0  0 0 0  72 7 25  180 16 63  189 17 66  188 17 66
+188 17 66  188 17 66  188 17 66  188 17 66  188 17 66  188 17 66
+181 16 63  47 4 16  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  21 2 7  163 15 57  190 17 67  188 17 66  188 17 66
+188 17 66  188 17 66  188 17 66  188 17 66  188 17 66  190 17 67
+122 11 43  9 1 3  0 0 0  0 0 0  30 3 10  171 15 60
+189 17 66  188 17 66  188 17 66  188 17 66  188 17 66  188 17 66
+188 17 66  188 17 66  188 17 66  188 17 66  188 17 66  188 17 66
+188 17 66  188 17 66  188 17 66  190 17 67  146 13 51  10 1 4
+0 0 0  38 3 13  166 15 58  190 17 67  188 17 66  188 17 66
+188 17 66  188 17 66  188 17 66  188 17 66  188 17 66  188 17 66
+183 17 64  52 5 18  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  13 1 5  154 14 54  190 17 67  188 17 66  188 17 66
+188 17 66  188 17 66  188 17 66  188 17 66  188 17 66  188 17 66
+186 17 65  79 7 28  0 0 0  0 0 0  14 1 5  156 14 54
+190 17 67  188 17 66  188 17 66  188 17 66  188 17 66  188 17 66
+188 17 66  188 17 66  188 17 66  188 17 66  188 17 66  188 17 66
+188 17 66  188 17 66  188 17 66  191 17 67  124 11 43  2 0 1
+5 0 2  122 11 43  191 17 67  188 17 66  188 17 66  188 17 66
+188 17 66  188 17 66  188 17 66  188 17 66  188 17 66  188 17 66
+182 16 64  47 4 16  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  3 0 1  126 14 44  191 17 67  188 17 66  188 17 66
+188 17 66  188 17 66  188 17 66  188 17 66  188 17 66  188 17 66
+190 17 67  158 14 55  23 2 8  0 0 0  1 0 0  113 10 40
+191 17 67  188 17 66  188 17 66  188 17 66  188 17 66  188 17 66
+188 17 66  188 17 66  188 17 66  188 17 66  188 17 66  188 17 66
+188 17 66  188 17 66  188 17 66  188 17 66  78 7 27  0 0 0
+47 4 16  177 16 62  189 17 66  188 17 66  188 17 66  188 17 66
+188 17 66  188 17 66  188 17 66  188 17 66  188 17 66  189 17 66
+173 16 61  34 3 12  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  85 8 30  189 17 66  188 17 66  188 17 66
+188 17 66  188 17 66  188 17 66  188 17 66  188 17 66  188 17 66
+188 17 66  188 17 66  79 7 28  0 0 0  0 0 0  47 4 16
+175 16 62  189 17 66  188 17 66  188 17 66  188 17 66  188 17 66
+188 17 66  188 17 66  188 17 66  188 17 66  188 17 66  188 17 66
+188 17 66  188 17 66  190 17 67  156 14 55  22 2 8  0 0 0
+109 10 38  191 17 67  188 17 66  188 17 66  188 17 66  188 17 66
+188 17 66  188 17 66  188 17 66  188 17 66  188 17 66  190 17 67
+151 14 53  13 1 5  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  35 3 12  173 16 61  189 17 66  188 17 66
+188 17 66  188 17 66  188 17 66  188 17 66  188 17 66  188 17 66
+188 17 66  191 17 67  134 12 47  7 1 2  0 0 0  3 0 1
+99 9 35  188 17 66  189 17 66  188 17 66  188 17 66  188 17 66
+188 17 66  188 17 66  188 17 66  188 17 66  188 17 66  188 17 66
+188 17 66  189 17 66  181 16 63  68 6 24  0 0 0  18 2 6
+156 14 55  190 17 67  188 17 66  188 17 66  188 17 66  188 17 66
+188 17 66  188 17 66  188 17 66  188 17 66  188 17 66  190 17 67
+101 9 35  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  3 0 1  118 13 41  191 17 67  188 17 66
+188 17 66  188 17 66  188 17 66  188 17 66  188 17 66  188 17 66
+188 17 66  189 17 66  168 15 59  28 3 10  0 0 0  0 0 0
+12 1 4  113 10 40  187 17 66  189 17 67  188 17 66  188 17 66
+188 17 66  188 17 66  188 17 66  188 17 66  188 17 66  188 17 66
+190 17 67  180 16 63  88 8 31  4 0 1  0 0 0  47 4 16
+180 16 63  189 17 66  188 17 66  188 17 66  188 17 66  188 17 66
+188 17 66  188 17 66  188 17 66  188 17 66  190 17 67  168 15 59
+36 3 13  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  38 3 13  164 15 58  190 17 67
+188 17 66  188 17 66  188 17 66  188 17 66  188 17 66  188 17 66
+188 17 66  188 17 66  182 16 64  50 5 17  0 0 0  0 0 0
+0 0 0  11 1 4  90 8 32  169 15 59  190 17 67  190 17 67
+189 17 66  189 17 66  189 17 66  189 17 66  191 17 67  189 17 66
+158 14 55  68 6 24  4 0 1  0 0 0  0 0 0  73 7 26
+188 17 66  188 17 66  188 17 66  188 17 66  188 17 66  188 17 66
+188 17 66  188 17 66  188 17 66  189 17 66  185 17 65  83 8 29
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  65 6 23  174 16 61
+190 17 67  188 17 66  188 17 66  188 17 66  188 17 66  188 17 66
+188 17 66  188 17 66  185 17 65  56 5 19  0 0 0  0 0 0
+0 0 0  0 0 0  2 0 1  35 3 12  99 9 35  146 13 51
+170 15 60  177 16 62  177 16 62  166 15 58  141 13 49  85 8 30
+24 2 8  0 0 0  0 0 0  0 0 0  0 0 0  85 8 30
+190 17 67  188 17 66  188 17 66  188 17 66  188 17 66  188 17 66
+188 17 66  188 17 66  188 17 66  189 17 66  112 10 39  8 1 3
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  1 0 0  68 6 24
+170 15 60  191 17 67  188 17 66  188 17 66  188 17 66  188 17 66
+188 17 66  188 17 66  182 16 64  50 5 17  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  1 0 0  11 1 4
+28 3 10  40 4 14  38 3 13  25 2 9  8 1 3  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  78 7 27
+189 17 66  188 17 66  188 17 66  188 17 66  188 17 66  188 17 66
+188 17 66  189 17 66  187 17 66  113 10 40  14 1 5  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  1 0 0
+47 4 16  141 13 49  186 17 65  191 17 67  190 17 67  189 17 66
+189 17 66  191 17 67  156 14 55  20 2 7  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  44 4 15
+178 16 62  190 17 67  188 17 66  188 17 66  188 17 66  190 17 67
+191 17 67  173 16 61  90 8 32  10 1 4  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  14 1 5  68 6 24  131 12 46  162 15 57  174 16 61
+171 15 60  146 13 51  56 5 19  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  3 0 1  14 1 5  29 3 10
+41 4 14  47 4 16  50 5 17  45 4 16  34 3 12  18 2 6
+5 0 2  0 0 0  0 0 0  0 0 0  0 0 0  5 0 2
+90 8 32  169 15 59  185 17 65  187 17 66  182 16 64  163 15 57
+113 10 40  41 4 14  2 0 1  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  5 0 2  21 2 7  34 3 12
+29 3 10  11 1 4  0 0 0  0 0 0  0 0 0  0 0 0
+3 0 1  32 3 11  79 7 28  124 11 43  154 14 54  171 15 60
+180 16 63  182 16 64  182 16 64  180 16 63  174 16 61  159 14 56
+132 12 46  88 8 31  34 3 12  3 0 1  0 0 0  0 0 0
+3 0 1  29 3 10  56 5 19  65 6 23  50 5 17  23 2 8
+3 0 1  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  25 2 9
+109 10 38  169 15 59  189 17 66  191 17 67  190 17 67  189 17 66
+189 17 66  188 17 66  188 17 66  188 17 66  189 17 66  190 17 67
+191 17 67  190 17 67  171 15 60  98 9 34  10 1 3  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  14 1 5  141 13 49
+191 17 67  189 17 66  188 17 66  188 17 66  188 17 66  188 17 66
+188 17 66  188 17 66  188 17 66  188 17 66  188 17 66  188 17 66
+188 17 66  188 17 66  189 17 67  186 17 65  65 6 23  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  23 2 8  166 15 58
+190 17 67  188 17 66  188 17 66  188 17 66  188 17 66  188 17 66
+188 17 66  188 17 66  188 17 66  188 17 66  188 17 66  188 17 66
+188 17 66  188 17 66  189 17 66  176 16 62  45 4 16  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  1 0 0  83 8 29
+183 17 64  189 17 66  188 17 66  188 17 66  188 17 66  188 17 66
+188 17 66  188 17 66  188 17 66  188 17 66  188 17 66  188 17 66
+188 17 66  189 17 66  185 17 65  95 9 33  3 0 1  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  5 0 2
+85 8 30  176 16 62  191 17 67  188 17 66  188 17 66  188 17 66
+188 17 66  188 17 66  188 17 66  188 17 66  188 17 66  188 17 66
+191 17 67  180 16 63  95 9 33  7 1 3  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+2 0 1  52 5 18  141 13 49  185 17 65  191 17 67  189 17 67
+189 17 66  188 17 66  188 17 66  189 17 66  191 17 67  187 17 66
+146 13 51  56 5 19  4 0 1  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  14 1 5  68 6 24  131 12 46  166 15 58
+180 16 63  183 17 64  180 16 63  168 15 59  134 12 47  75 7 26
+17 2 6  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  5 0 2  24 2 8
+44 4 15  52 5 18  45 4 16  26 2 9  6 1 2  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0
+0 0 0  0 0 0  0 0 0
diff -Nur --no-dereference linux-5.10.52-orig/drivers/w1/masters/w1-gpio.c linux-5.10.52-v7l+/drivers/w1/masters/w1-gpio.c
--- linux-5.10.52-orig/drivers/w1/masters/w1-gpio.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/w1/masters/w1-gpio.c	2021-07-25 16:46:29.697893012 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:33 @
 			 * This will OVERRIDE open drain emulation and force-pull
 			 * the line high for some time.
 			 */
-			gpiod_set_raw_value(pdata->gpiod, 1);
+			gpiod_direction_output_raw(pdata->gpiod, 1);
 			msleep(pdata->pullup_duration);
 			/*
 			 * This will simply set the line as input since we are doing
diff -Nur --no-dereference linux-5.10.52-orig/drivers/watchdog/bcm2835_wdt.c linux-5.10.52-v7l+/drivers/watchdog/bcm2835_wdt.c
--- linux-5.10.52-orig/drivers/watchdog/bcm2835_wdt.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/drivers/watchdog/bcm2835_wdt.c	2021-07-25 16:46:29.727892509 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:35 @
 #define PM_RSTC_WRCFG_SET		0x00000030
 #define PM_RSTC_WRCFG_FULL_RESET	0x00000020
 #define PM_RSTC_RESET			0x00000102
-
-/*
- * The Raspberry Pi firmware uses the RSTS register to know which partition
- * to boot from. The partition value is spread into bits 0, 2, 4, 6, 8, 10.
- * Partition 63 is a special partition used by the firmware to indicate halt.
- */
-#define PM_RSTS_RASPBERRYPI_HALT	0x555
+#define PM_RSTS_PARTITION_CLR          0xfffffaaa
 
 #define SECS_TO_WDOG_TICKS(x) ((x) << 16)
 #define WDOG_TICKS_TO_SECS(x) ((x) >> 16)
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:94 @
 	return WDOG_TICKS_TO_SECS(ret & PM_WDOG_TIME_SET);
 }
 
-static void __bcm2835_restart(struct bcm2835_wdt *wdt)
+/*
+ * The Raspberry Pi firmware uses the RSTS register to know which partiton
+ * to boot from. The partiton value is spread into bits 0, 2, 4, 6, 8, 10.
+ * Partiton 63 is a special partition used by the firmware to indicate halt.
+ */
+
+static void __bcm2835_restart(struct bcm2835_wdt *wdt, u8 partition)
 {
-	u32 val;
+	u32 val, rsts;
+
+	rsts = (partition & BIT(0)) | ((partition & BIT(1)) << 1) |
+	       ((partition & BIT(2)) << 2) | ((partition & BIT(3)) << 3) |
+	       ((partition & BIT(4)) << 4) | ((partition & BIT(5)) << 5);
+
+	val = readl_relaxed(wdt->base + PM_RSTS);
+	val &= PM_RSTS_PARTITION_CLR;
+	val |= PM_PASSWORD | rsts;
+	writel_relaxed(val, wdt->base + PM_RSTS);
 
 	/* use a timeout of 10 ticks (~150us) */
 	writel_relaxed(10 | PM_PASSWORD, wdt->base + PM_WDOG);
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:129 @
 {
 	struct bcm2835_wdt *wdt = watchdog_get_drvdata(wdog);
 
-	__bcm2835_restart(wdt);
+	unsigned long val;
+	u8 partition = 0;
+
+	// Allow extra arguments separated by spaces after
+	// the partition number.
+	if (data && sscanf(data, "%lu", &val) && val < 63)
+		partition = val;
+
+	__bcm2835_restart(wdt, partition);
 
 	return 0;
 }
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:172 @
 static void bcm2835_power_off(void)
 {
 	struct bcm2835_wdt *wdt = bcm2835_power_off_wdt;
-	u32 val;
-
-	/*
-	 * We set the watchdog hard reset bit here to distinguish this reset
-	 * from the normal (full) reset. bootcode.bin will not reboot after a
-	 * hard reset.
-	 */
-	val = readl_relaxed(wdt->base + PM_RSTS);
-	val |= PM_PASSWORD | PM_RSTS_RASPBERRYPI_HALT;
-	writel_relaxed(val, wdt->base + PM_RSTS);
 
-	/* Continue with normal reset mechanism */
-	__bcm2835_restart(wdt);
+	/* Partition 63 tells the firmware that this is a halt */
+	__bcm2835_restart(wdt, 63);
 }
 
 static int bcm2835_wdt_probe(struct platform_device *pdev)
diff -Nur --no-dereference linux-5.10.52-orig/.git/config linux-5.10.52-v7l+/.git/config
--- linux-5.10.52-orig/.git/config	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/.git/config	2021-07-25 16:45:32.678848969 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+[core]
+	repositoryformatversion = 0
+	filemode = true
+	bare = false
+	logallrefupdates = true
+[remote "origin"]
+	url = https://github.com/raspberrypi/linux.git
+	fetch = +refs/heads/*:refs/remotes/origin/*
+[branch "rpi-5.10.y"]
+	remote = origin
+	merge = refs/heads/rpi-5.10.y
diff -Nur --no-dereference linux-5.10.52-orig/.git/description linux-5.10.52-v7l+/.git/description
--- linux-5.10.52-orig/.git/description	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/.git/description	2021-07-25 16:31:10.753048086 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1 @
+Unnamed repository; edit this file 'description' to name the repository.
diff -Nur --no-dereference linux-5.10.52-orig/.git/HEAD linux-5.10.52-v7l+/.git/HEAD
--- linux-5.10.52-orig/.git/HEAD	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/.git/HEAD	2021-07-25 16:45:32.678848969 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1 @
+ref: refs/heads/rpi-5.10.y
diff -Nur --no-dereference linux-5.10.52-orig/.git/hooks/applypatch-msg.sample linux-5.10.52-v7l+/.git/hooks/applypatch-msg.sample
--- linux-5.10.52-orig/.git/hooks/applypatch-msg.sample	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/.git/hooks/applypatch-msg.sample	2021-07-25 16:31:10.763047922 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+#!/bin/sh
+#
+# An example hook script to check the commit log message taken by
+# applypatch from an e-mail message.
+#
+# The hook should exit with non-zero status after issuing an
+# appropriate message if it wants to stop the commit.  The hook is
+# allowed to edit the commit message file.
+#
+# To enable this hook, rename this file to "applypatch-msg".
+
+. git-sh-setup
+commitmsg="$(git rev-parse --git-path hooks/commit-msg)"
+test -x "$commitmsg" && exec "$commitmsg" ${1+"$@"}
+:
diff -Nur --no-dereference linux-5.10.52-orig/.git/hooks/commit-msg.sample linux-5.10.52-v7l+/.git/hooks/commit-msg.sample
--- linux-5.10.52-orig/.git/hooks/commit-msg.sample	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/.git/hooks/commit-msg.sample	2021-07-25 16:31:10.763047922 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+#!/bin/sh
+#
+# An example hook script to check the commit log message.
+# Called by "git commit" with one argument, the name of the file
+# that has the commit message.  The hook should exit with non-zero
+# status after issuing an appropriate message if it wants to stop the
+# commit.  The hook is allowed to edit the commit message file.
+#
+# To enable this hook, rename this file to "commit-msg".
+
+# Uncomment the below to add a Signed-off-by line to the message.
+# Doing this in a hook is a bad idea in general, but the prepare-commit-msg
+# hook is more suited to it.
+#
+# SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p')
+# grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1"
+
+# This example catches duplicate Signed-off-by lines.
+
+test "" = "$(grep '^Signed-off-by: ' "$1" |
+	 sort | uniq -c | sed -e '/^[ 	]*1[ 	]/d')" || {
+	echo >&2 Duplicate Signed-off-by lines.
+	exit 1
+}
diff -Nur --no-dereference linux-5.10.52-orig/.git/hooks/fsmonitor-watchman.sample linux-5.10.52-v7l+/.git/hooks/fsmonitor-watchman.sample
--- linux-5.10.52-orig/.git/hooks/fsmonitor-watchman.sample	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/.git/hooks/fsmonitor-watchman.sample	2021-07-25 16:31:10.763047922 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+use IPC::Open2;
+
+# An example hook script to integrate Watchman
+# (https://facebook.github.io/watchman/) with git to speed up detecting
+# new and modified files.
+#
+# The hook is passed a version (currently 1) and a time in nanoseconds
+# formatted as a string and outputs to stdout all files that have been
+# modified since the given time. Paths must be relative to the root of
+# the working tree and separated by a single NUL.
+#
+# To enable this hook, rename this file to "query-watchman" and set
+# 'git config core.fsmonitor .git/hooks/query-watchman'
+#
+my ($version, $time) = @ARGV;
+
+# Check the hook interface version
+
+if ($version == 1) {
+	# convert nanoseconds to seconds
+	$time = int $time / 1000000000;
+} else {
+	die "Unsupported query-fsmonitor hook version '$version'.\n" .
+	    "Falling back to scanning...\n";
+}
+
+my $git_work_tree;
+if ($^O =~ 'msys' || $^O =~ 'cygwin') {
+	$git_work_tree = Win32::GetCwd();
+	$git_work_tree =~ tr/\\/\//;
+} else {
+	require Cwd;
+	$git_work_tree = Cwd::cwd();
+}
+
+my $retry = 1;
+
+launch_watchman();
+
+sub launch_watchman {
+
+	my $pid = open2(\*CHLD_OUT, \*CHLD_IN, 'watchman -j --no-pretty')
+	    or die "open2() failed: $!\n" .
+	    "Falling back to scanning...\n";
+
+	# In the query expression below we're asking for names of files that
+	# changed since $time but were not transient (ie created after
+	# $time but no longer exist).
+	#
+	# To accomplish this, we're using the "since" generator to use the
+	# recency index to select candidate nodes and "fields" to limit the
+	# output to file names only. Then we're using the "expression" term to
+	# further constrain the results.
+	#
+	# The category of transient files that we want to ignore will have a
+	# creation clock (cclock) newer than $time_t value and will also not
+	# currently exist.
+
+	my $query = <<"	END";
+		["query", "$git_work_tree", {
+			"since": $time,
+			"fields": ["name"],
+			"expression": ["not", ["allof", ["since", $time, "cclock"], ["not", "exists"]]]
+		}]
+	END
+
+	print CHLD_IN $query;
+	close CHLD_IN;
+	my $response = do {local $/; <CHLD_OUT>};
+
+	die "Watchman: command returned no output.\n" .
+	    "Falling back to scanning...\n" if $response eq "";
+	die "Watchman: command returned invalid output: $response\n" .
+	    "Falling back to scanning...\n" unless $response =~ /^\{/;
+
+	my $json_pkg;
+	eval {
+		require JSON::XS;
+		$json_pkg = "JSON::XS";
+		1;
+	} or do {
+		require JSON::PP;
+		$json_pkg = "JSON::PP";
+	};
+
+	my $o = $json_pkg->new->utf8->decode($response);
+
+	if ($retry > 0 and $o->{error} and $o->{error} =~ m/unable to resolve root .* directory (.*) is not watched/) {
+		print STDERR "Adding '$git_work_tree' to watchman's watch list.\n";
+		$retry--;
+		qx/watchman watch "$git_work_tree"/;
+		die "Failed to make watchman watch '$git_work_tree'.\n" .
+		    "Falling back to scanning...\n" if $? != 0;
+
+		# Watchman will always return all files on the first query so
+		# return the fast "everything is dirty" flag to git and do the
+		# Watchman query just to get it over with now so we won't pay
+		# the cost in git to look up each individual file.
+		print "/\0";
+		eval { launch_watchman() };
+		exit 0;
+	}
+
+	die "Watchman: $o->{error}.\n" .
+	    "Falling back to scanning...\n" if $o->{error};
+
+	binmode STDOUT, ":utf8";
+	local $, = "\0";
+	print @{$o->{files}};
+}
diff -Nur --no-dereference linux-5.10.52-orig/.git/hooks/post-update.sample linux-5.10.52-v7l+/.git/hooks/post-update.sample
--- linux-5.10.52-orig/.git/hooks/post-update.sample	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/.git/hooks/post-update.sample	2021-07-25 16:31:10.763047922 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+#!/bin/sh
+#
+# An example hook script to prepare a packed repository for use over
+# dumb transports.
+#
+# To enable this hook, rename this file to "post-update".
+
+exec git update-server-info
diff -Nur --no-dereference linux-5.10.52-orig/.git/hooks/pre-applypatch.sample linux-5.10.52-v7l+/.git/hooks/pre-applypatch.sample
--- linux-5.10.52-orig/.git/hooks/pre-applypatch.sample	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/.git/hooks/pre-applypatch.sample	2021-07-25 16:31:10.763047922 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+#!/bin/sh
+#
+# An example hook script to verify what is about to be committed
+# by applypatch from an e-mail message.
+#
+# The hook should exit with non-zero status after issuing an
+# appropriate message if it wants to stop the commit.
+#
+# To enable this hook, rename this file to "pre-applypatch".
+
+. git-sh-setup
+precommit="$(git rev-parse --git-path hooks/pre-commit)"
+test -x "$precommit" && exec "$precommit" ${1+"$@"}
+:
diff -Nur --no-dereference linux-5.10.52-orig/.git/hooks/pre-commit.sample linux-5.10.52-v7l+/.git/hooks/pre-commit.sample
--- linux-5.10.52-orig/.git/hooks/pre-commit.sample	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/.git/hooks/pre-commit.sample	2021-07-25 16:31:10.763047922 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+#!/bin/sh
+#
+# An example hook script to verify what is about to be committed.
+# Called by "git commit" with no arguments.  The hook should
+# exit with non-zero status after issuing an appropriate message if
+# it wants to stop the commit.
+#
+# To enable this hook, rename this file to "pre-commit".
+
+if git rev-parse --verify HEAD >/dev/null 2>&1
+then
+	against=HEAD
+else
+	# Initial commit: diff against an empty tree object
+	against=$(git hash-object -t tree /dev/null)
+fi
+
+# If you want to allow non-ASCII filenames set this variable to true.
+allownonascii=$(git config --bool hooks.allownonascii)
+
+# Redirect output to stderr.
+exec 1>&2
+
+# Cross platform projects tend to avoid non-ASCII filenames; prevent
+# them from being added to the repository. We exploit the fact that the
+# printable range starts at the space character and ends with tilde.
+if [ "$allownonascii" != "true" ] &&
+	# Note that the use of brackets around a tr range is ok here, (it's
+	# even required, for portability to Solaris 10's /usr/bin/tr), since
+	# the square bracket bytes happen to fall in the designated range.
+	test $(git diff --cached --name-only --diff-filter=A -z $against |
+	  LC_ALL=C tr -d '[ -~]\0' | wc -c) != 0
+then
+	cat <<\EOF
+Error: Attempt to add a non-ASCII file name.
+
+This can cause problems if you want to work with people on other platforms.
+
+To be portable it is advisable to rename the file.
+
+If you know what you are doing you can disable this check using:
+
+  git config hooks.allownonascii true
+EOF
+	exit 1
+fi
+
+# If there are whitespace errors, print the offending file names and fail.
+exec git diff-index --check --cached $against --
diff -Nur --no-dereference linux-5.10.52-orig/.git/hooks/prepare-commit-msg.sample linux-5.10.52-v7l+/.git/hooks/prepare-commit-msg.sample
--- linux-5.10.52-orig/.git/hooks/prepare-commit-msg.sample	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/.git/hooks/prepare-commit-msg.sample	2021-07-25 16:31:10.763047922 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+#!/bin/sh
+#
+# An example hook script to prepare the commit log message.
+# Called by "git commit" with the name of the file that has the
+# commit message, followed by the description of the commit
+# message's source.  The hook's purpose is to edit the commit
+# message file.  If the hook fails with a non-zero status,
+# the commit is aborted.
+#
+# To enable this hook, rename this file to "prepare-commit-msg".
+
+# This hook includes three examples. The first one removes the
+# "# Please enter the commit message..." help message.
+#
+# The second includes the output of "git diff --name-status -r"
+# into the message, just before the "git status" output.  It is
+# commented because it doesn't cope with --amend or with squashed
+# commits.
+#
+# The third example adds a Signed-off-by line to the message, that can
+# still be edited.  This is rarely a good idea.
+
+COMMIT_MSG_FILE=$1
+COMMIT_SOURCE=$2
+SHA1=$3
+
+/usr/bin/perl -i.bak -ne 'print unless(m/^. Please enter the commit message/..m/^#$/)' "$COMMIT_MSG_FILE"
+
+# case "$COMMIT_SOURCE,$SHA1" in
+#  ,|template,)
+#    /usr/bin/perl -i.bak -pe '
+#       print "\n" . `git diff --cached --name-status -r`
+# 	 if /^#/ && $first++ == 0' "$COMMIT_MSG_FILE" ;;
+#  *) ;;
+# esac
+
+# SOB=$(git var GIT_COMMITTER_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p')
+# git interpret-trailers --in-place --trailer "$SOB" "$COMMIT_MSG_FILE"
+# if test -z "$COMMIT_SOURCE"
+# then
+#   /usr/bin/perl -i.bak -pe 'print "\n" if !$first_line++' "$COMMIT_MSG_FILE"
+# fi
diff -Nur --no-dereference linux-5.10.52-orig/.git/hooks/pre-push.sample linux-5.10.52-v7l+/.git/hooks/pre-push.sample
--- linux-5.10.52-orig/.git/hooks/pre-push.sample	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/.git/hooks/pre-push.sample	2021-07-25 16:31:10.763047922 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+#!/bin/sh
+
+# An example hook script to verify what is about to be pushed.  Called by "git
+# push" after it has checked the remote status, but before anything has been
+# pushed.  If this script exits with a non-zero status nothing will be pushed.
+#
+# This hook is called with the following parameters:
+#
+# $1 -- Name of the remote to which the push is being done
+# $2 -- URL to which the push is being done
+#
+# If pushing without using a named remote those arguments will be equal.
+#
+# Information about the commits which are being pushed is supplied as lines to
+# the standard input in the form:
+#
+#   <local ref> <local sha1> <remote ref> <remote sha1>
+#
+# This sample shows how to prevent push of commits where the log message starts
+# with "WIP" (work in progress).
+
+remote="$1"
+url="$2"
+
+z40=0000000000000000000000000000000000000000
+
+while read local_ref local_sha remote_ref remote_sha
+do
+	if [ "$local_sha" = $z40 ]
+	then
+		# Handle delete
+		:
+	else
+		if [ "$remote_sha" = $z40 ]
+		then
+			# New branch, examine all commits
+			range="$local_sha"
+		else
+			# Update to existing branch, examine new commits
+			range="$remote_sha..$local_sha"
+		fi
+
+		# Check for WIP commit
+		commit=`git rev-list -n 1 --grep '^WIP' "$range"`
+		if [ -n "$commit" ]
+		then
+			echo >&2 "Found WIP commit in $local_ref, not pushing"
+			exit 1
+		fi
+	fi
+done
+
+exit 0
diff -Nur --no-dereference linux-5.10.52-orig/.git/hooks/pre-rebase.sample linux-5.10.52-v7l+/.git/hooks/pre-rebase.sample
--- linux-5.10.52-orig/.git/hooks/pre-rebase.sample	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/.git/hooks/pre-rebase.sample	2021-07-25 16:31:10.763047922 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+#!/bin/sh
+#
+# Copyright (c) 2006, 2008 Junio C Hamano
+#
+# The "pre-rebase" hook is run just before "git rebase" starts doing
+# its job, and can prevent the command from running by exiting with
+# non-zero status.
+#
+# The hook is called with the following parameters:
+#
+# $1 -- the upstream the series was forked from.
+# $2 -- the branch being rebased (or empty when rebasing the current branch).
+#
+# This sample shows how to prevent topic branches that are already
+# merged to 'next' branch from getting rebased, because allowing it
+# would result in rebasing already published history.
+
+publish=next
+basebranch="$1"
+if test "$#" = 2
+then
+	topic="refs/heads/$2"
+else
+	topic=`git symbolic-ref HEAD` ||
+	exit 0 ;# we do not interrupt rebasing detached HEAD
+fi
+
+case "$topic" in
+refs/heads/??/*)
+	;;
+*)
+	exit 0 ;# we do not interrupt others.
+	;;
+esac
+
+# Now we are dealing with a topic branch being rebased
+# on top of master.  Is it OK to rebase it?
+
+# Does the topic really exist?
+git show-ref -q "$topic" || {
+	echo >&2 "No such branch $topic"
+	exit 1
+}
+
+# Is topic fully merged to master?
+not_in_master=`git rev-list --pretty=oneline ^master "$topic"`
+if test -z "$not_in_master"
+then
+	echo >&2 "$topic is fully merged to master; better remove it."
+	exit 1 ;# we could allow it, but there is no point.
+fi
+
+# Is topic ever merged to next?  If so you should not be rebasing it.
+only_next_1=`git rev-list ^master "^$topic" ${publish} | sort`
+only_next_2=`git rev-list ^master           ${publish} | sort`
+if test "$only_next_1" = "$only_next_2"
+then
+	not_in_topic=`git rev-list "^$topic" master`
+	if test -z "$not_in_topic"
+	then
+		echo >&2 "$topic is already up to date with master"
+		exit 1 ;# we could allow it, but there is no point.
+	else
+		exit 0
+	fi
+else
+	not_in_next=`git rev-list --pretty=oneline ^${publish} "$topic"`
+	/usr/bin/perl -e '
+		my $topic = $ARGV[0];
+		my $msg = "* $topic has commits already merged to public branch:\n";
+		my (%not_in_next) = map {
+			/^([0-9a-f]+) /;
+			($1 => 1);
+		} split(/\n/, $ARGV[1]);
+		for my $elem (map {
+				/^([0-9a-f]+) (.*)$/;
+				[$1 => $2];
+			} split(/\n/, $ARGV[2])) {
+			if (!exists $not_in_next{$elem->[0]}) {
+				if ($msg) {
+					print STDERR $msg;
+					undef $msg;
+				}
+				print STDERR " $elem->[1]\n";
+			}
+		}
+	' "$topic" "$not_in_next" "$not_in_master"
+	exit 1
+fi
+
+<<\DOC_END
+
+This sample hook safeguards topic branches that have been
+published from being rewound.
+
+The workflow assumed here is:
+
+ * Once a topic branch forks from "master", "master" is never
+   merged into it again (either directly or indirectly).
+
+ * Once a topic branch is fully cooked and merged into "master",
+   it is deleted.  If you need to build on top of it to correct
+   earlier mistakes, a new topic branch is created by forking at
+   the tip of the "master".  This is not strictly necessary, but
+   it makes it easier to keep your history simple.
+
+ * Whenever you need to test or publish your changes to topic
+   branches, merge them into "next" branch.
+
+The script, being an example, hardcodes the publish branch name
+to be "next", but it is trivial to make it configurable via
+$GIT_DIR/config mechanism.
+
+With this workflow, you would want to know:
+
+(1) ... if a topic branch has ever been merged to "next".  Young
+    topic branches can have stupid mistakes you would rather
+    clean up before publishing, and things that have not been
+    merged into other branches can be easily rebased without
+    affecting other people.  But once it is published, you would
+    not want to rewind it.
+
+(2) ... if a topic branch has been fully merged to "master".
+    Then you can delete it.  More importantly, you should not
+    build on top of it -- other people may already want to
+    change things related to the topic as patches against your
+    "master", so if you need further changes, it is better to
+    fork the topic (perhaps with the same name) afresh from the
+    tip of "master".
+
+Let's look at this example:
+
+		   o---o---o---o---o---o---o---o---o---o "next"
+		  /       /           /           /
+		 /   a---a---b A     /           /
+		/   /               /           /
+	       /   /   c---c---c---c B         /
+	      /   /   /             \         /
+	     /   /   /   b---b C     \       /
+	    /   /   /   /             \     /
+    ---o---o---o---o---o---o---o---o---o---o---o "master"
+
+
+A, B and C are topic branches.
+
+ * A has one fix since it was merged up to "next".
+
+ * B has finished.  It has been fully merged up to "master" and "next",
+   and is ready to be deleted.
+
+ * C has not merged to "next" at all.
+
+We would want to allow C to be rebased, refuse A, and encourage
+B to be deleted.
+
+To compute (1):
+
+	git rev-list ^master ^topic next
+	git rev-list ^master        next
+
+	if these match, topic has not merged in next at all.
+
+To compute (2):
+
+	git rev-list master..topic
+
+	if this is empty, it is fully merged to "master".
+
+DOC_END
diff -Nur --no-dereference linux-5.10.52-orig/.git/hooks/pre-receive.sample linux-5.10.52-v7l+/.git/hooks/pre-receive.sample
--- linux-5.10.52-orig/.git/hooks/pre-receive.sample	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/.git/hooks/pre-receive.sample	2021-07-25 16:31:10.763047922 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+#!/bin/sh
+#
+# An example hook script to make use of push options.
+# The example simply echoes all push options that start with 'echoback='
+# and rejects all pushes when the "reject" push option is used.
+#
+# To enable this hook, rename this file to "pre-receive".
+
+if test -n "$GIT_PUSH_OPTION_COUNT"
+then
+	i=0
+	while test "$i" -lt "$GIT_PUSH_OPTION_COUNT"
+	do
+		eval "value=\$GIT_PUSH_OPTION_$i"
+		case "$value" in
+		echoback=*)
+			echo "echo from the pre-receive-hook: ${value#*=}" >&2
+			;;
+		reject)
+			exit 1
+		esac
+		i=$((i + 1))
+	done
+fi
diff -Nur --no-dereference linux-5.10.52-orig/.git/hooks/update.sample linux-5.10.52-v7l+/.git/hooks/update.sample
--- linux-5.10.52-orig/.git/hooks/update.sample	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/.git/hooks/update.sample	2021-07-25 16:31:10.763047922 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+#!/bin/sh
+#
+# An example hook script to block unannotated tags from entering.
+# Called by "git receive-pack" with arguments: refname sha1-old sha1-new
+#
+# To enable this hook, rename this file to "update".
+#
+# Config
+# ------
+# hooks.allowunannotated
+#   This boolean sets whether unannotated tags will be allowed into the
+#   repository.  By default they won't be.
+# hooks.allowdeletetag
+#   This boolean sets whether deleting tags will be allowed in the
+#   repository.  By default they won't be.
+# hooks.allowmodifytag
+#   This boolean sets whether a tag may be modified after creation. By default
+#   it won't be.
+# hooks.allowdeletebranch
+#   This boolean sets whether deleting branches will be allowed in the
+#   repository.  By default they won't be.
+# hooks.denycreatebranch
+#   This boolean sets whether remotely creating branches will be denied
+#   in the repository.  By default this is allowed.
+#
+
+# --- Command line
+refname="$1"
+oldrev="$2"
+newrev="$3"
+
+# --- Safety check
+if [ -z "$GIT_DIR" ]; then
+	echo "Don't run this script from the command line." >&2
+	echo " (if you want, you could supply GIT_DIR then run" >&2
+	echo "  $0 <ref> <oldrev> <newrev>)" >&2
+	exit 1
+fi
+
+if [ -z "$refname" -o -z "$oldrev" -o -z "$newrev" ]; then
+	echo "usage: $0 <ref> <oldrev> <newrev>" >&2
+	exit 1
+fi
+
+# --- Config
+allowunannotated=$(git config --bool hooks.allowunannotated)
+allowdeletebranch=$(git config --bool hooks.allowdeletebranch)
+denycreatebranch=$(git config --bool hooks.denycreatebranch)
+allowdeletetag=$(git config --bool hooks.allowdeletetag)
+allowmodifytag=$(git config --bool hooks.allowmodifytag)
+
+# check for no description
+projectdesc=$(sed -e '1q' "$GIT_DIR/description")
+case "$projectdesc" in
+"Unnamed repository"* | "")
+	echo "*** Project description file hasn't been set" >&2
+	exit 1
+	;;
+esac
+
+# --- Check types
+# if $newrev is 0000...0000, it's a commit to delete a ref.
+zero="0000000000000000000000000000000000000000"
+if [ "$newrev" = "$zero" ]; then
+	newrev_type=delete
+else
+	newrev_type=$(git cat-file -t $newrev)
+fi
+
+case "$refname","$newrev_type" in
+	refs/tags/*,commit)
+		# un-annotated tag
+		short_refname=${refname##refs/tags/}
+		if [ "$allowunannotated" != "true" ]; then
+			echo "*** The un-annotated tag, $short_refname, is not allowed in this repository" >&2
+			echo "*** Use 'git tag [ -a | -s ]' for tags you want to propagate." >&2
+			exit 1
+		fi
+		;;
+	refs/tags/*,delete)
+		# delete tag
+		if [ "$allowdeletetag" != "true" ]; then
+			echo "*** Deleting a tag is not allowed in this repository" >&2
+			exit 1
+		fi
+		;;
+	refs/tags/*,tag)
+		# annotated tag
+		if [ "$allowmodifytag" != "true" ] && git rev-parse $refname > /dev/null 2>&1
+		then
+			echo "*** Tag '$refname' already exists." >&2
+			echo "*** Modifying a tag is not allowed in this repository." >&2
+			exit 1
+		fi
+		;;
+	refs/heads/*,commit)
+		# branch
+		if [ "$oldrev" = "$zero" -a "$denycreatebranch" = "true" ]; then
+			echo "*** Creating a branch is not allowed in this repository" >&2
+			exit 1
+		fi
+		;;
+	refs/heads/*,delete)
+		# delete branch
+		if [ "$allowdeletebranch" != "true" ]; then
+			echo "*** Deleting a branch is not allowed in this repository" >&2
+			exit 1
+		fi
+		;;
+	refs/remotes/*,commit)
+		# tracking branch
+		;;
+	refs/remotes/*,delete)
+		# delete tracking branch
+		if [ "$allowdeletebranch" != "true" ]; then
+			echo "*** Deleting a tracking branch is not allowed in this repository" >&2
+			exit 1
+		fi
+		;;
+	*)
+		# Anything else (is there anything else?)
+		echo "*** Update hook: unknown type of update to ref $refname of type $newrev_type" >&2
+		exit 1
+		;;
+esac
+
+# --- Finished
+exit 0
Warning: `tput cols` did not return numeric input
────────────────────────────────────────────────────────────────────────────────
renamed: linux-5.10.52-orig/.git/index to linux-5.10.52-v7l+/.git/index (binary)
────────────────────────────────────────────────────────────────────────────────
diff -Nur --no-dereference linux-5.10.52-orig/.git/info/exclude linux-5.10.52-v7l+/.git/info/exclude
--- linux-5.10.52-orig/.git/info/exclude	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/.git/info/exclude	2021-07-25 16:31:10.763047922 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:3 @
+# git ls-files --others --exclude-from=.git/info/exclude
+# Lines that start with '#' are comments.
+# For a project mostly in C, the following would be a good set of
+# exclude patterns (uncomment them if you want to use them):
+# *.[oa]
+# *~
diff -Nur --no-dereference linux-5.10.52-orig/.git/logs/HEAD linux-5.10.52-v7l+/.git/logs/HEAD
--- linux-5.10.52-orig/.git/logs/HEAD	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/.git/logs/HEAD	2021-07-25 16:45:32.678848969 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1 @
+0000000000000000000000000000000000000000 7aa66c086ae01c4f77d54aaeb1d9f62573a7b748 root <root@rack4slot1s> 1627224332 +0200	clone: from https://github.com/raspberrypi/linux.git
diff -Nur --no-dereference linux-5.10.52-orig/.git/logs/refs/heads/rpi-5.10.y linux-5.10.52-v7l+/.git/logs/refs/heads/rpi-5.10.y
--- linux-5.10.52-orig/.git/logs/refs/heads/rpi-5.10.y	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/.git/logs/refs/heads/rpi-5.10.y	2021-07-25 16:45:32.678848969 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1 @
+0000000000000000000000000000000000000000 7aa66c086ae01c4f77d54aaeb1d9f62573a7b748 root <root@rack4slot1s> 1627224332 +0200	clone: from https://github.com/raspberrypi/linux.git
diff -Nur --no-dereference linux-5.10.52-orig/.git/logs/refs/remotes/origin/HEAD linux-5.10.52-v7l+/.git/logs/refs/remotes/origin/HEAD
--- linux-5.10.52-orig/.git/logs/refs/remotes/origin/HEAD	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/.git/logs/refs/remotes/origin/HEAD	2021-07-25 16:45:32.678848969 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1 @
+0000000000000000000000000000000000000000 7aa66c086ae01c4f77d54aaeb1d9f62573a7b748 root <root@rack4slot1s> 1627224332 +0200	clone: from https://github.com/raspberrypi/linux.git
────────────────────────────────────────────────────────────────────────────────
renamed: linux-5.10.52-orig/.git/objects/pack/pack-e556742bcc43422abd0b8807b59535f7e429d2db.idx to linux-5.10.52-v7l+/.git/objects/pack/pack-e556742bcc43422abd0b8807b59535f7e429d2db.idx (binary)
────────────────────────────────────────────────────────────────────────────────
────────────────────────────────────────────────────────────────────────────────
renamed: linux-5.10.52-orig/.git/objects/pack/pack-e556742bcc43422abd0b8807b59535f7e429d2db.pack to linux-5.10.52-v7l+/.git/objects/pack/pack-e556742bcc43422abd0b8807b59535f7e429d2db.pack (binary)
────────────────────────────────────────────────────────────────────────────────
diff -Nur --no-dereference linux-5.10.52-orig/.git/packed-refs linux-5.10.52-v7l+/.git/packed-refs
--- linux-5.10.52-orig/.git/packed-refs	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/.git/packed-refs	2021-07-25 16:45:32.668849137 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+# pack-refs with: peeled fully-peeled sorted 
+51af817611f2c0987030d024f24fc7ea95dd33e6 refs/remotes/origin/linux_stable
+645fd9b0c0b3c1f79f71f92dac79bd2f87010444 refs/remotes/origin/master
+9e4aa51491256fe757bb06914edf42e77770159c refs/remotes/origin/pull/2638/head
+45407db240dfa9d6744f11e35ec8befdca4cbc43 refs/remotes/origin/pull/2922/head
+1b49b450222df26e4abf7abb6d9302f72b2ed386 refs/remotes/origin/rpi-3.10.y
+d894df614c20e2f56ce0ff0576a4c517352684de refs/remotes/origin/rpi-3.10.y-next
+8f768c5f2a3314e4eacce8d667c787f8dadfda74 refs/remotes/origin/rpi-3.11.y
+102fb4ef5c1e22f8e4ac94209ac85ff2a646bd81 refs/remotes/origin/rpi-3.12.y
+a26c4d1f1428082f6aba79207535527c91115c76 refs/remotes/origin/rpi-3.13.y
+2514e943096ff51dd5a83b28b4b4d45ec2693434 refs/remotes/origin/rpi-3.13.y-next
+e294028d7733a30f3befacc41d473c251096a515 refs/remotes/origin/rpi-3.14.y
+8812404b672364ee794411ed19080dc9b6287682 refs/remotes/origin/rpi-3.15.y
+377c82aa1d31b37f1096096b0e4c65beb0bc5c49 refs/remotes/origin/rpi-3.16.y
+a2a6c3e259254f3687bde843f4caff011611cf7e refs/remotes/origin/rpi-3.17.y
+1bb18c8f721ef674a447f3622273f2e2de7a205c refs/remotes/origin/rpi-3.18.y
+786219b8ef922ec8407895b51381cda3160e6e49 refs/remotes/origin/rpi-3.18.y-rebase
+108e489c19d92800dfc7f04ec93f57468ffff751 refs/remotes/origin/rpi-3.19.y
+ada8b4415ff44d535d63e4291a0eca733bc2ad0f refs/remotes/origin/rpi-3.2.27
+2a8d45ec0883e3cbdce920855b3461ac77308a5f refs/remotes/origin/rpi-3.6.y
+d996a1b91b2bf3dc06f4f4f822a56f4496457aa1 refs/remotes/origin/rpi-3.8.y
+d5572370289f698b101f3d0198b1c99f17f0d278 refs/remotes/origin/rpi-3.9.y
+351f30b523ee17e84e618e92ee9a382e18fa5f86 refs/remotes/origin/rpi-4.0.y
+ff45bc0e8917c77461b2901e2743e6339bb70413 refs/remotes/origin/rpi-4.1.y
+ef4ef49f5d92fe9050b065f7ead913c3ab502300 refs/remotes/origin/rpi-4.1.y_rebase
+9411792647f688d1401dab486ed01ac4d190a4f4 refs/remotes/origin/rpi-4.10.y
+d5c0e92fd7fc8b1be1559a0c179d8d25d13531a7 refs/remotes/origin/rpi-4.11.y
+6fe4193dd99f1e299e56c9fa6ba43681398cc041 refs/remotes/origin/rpi-4.12.y
+c9b4367b82af057031953b24c50662205ab6faee refs/remotes/origin/rpi-4.13.y
+7688b39276ff9952df381d79de63b258e73971ce refs/remotes/origin/rpi-4.14.y
+e46e9c2829482271ab5577d380aac16da83f1803 refs/remotes/origin/rpi-4.14.y-rt
+1b75d3e7314049bbf1222d447e1c44928e9a478b refs/remotes/origin/rpi-4.15.y
+fd4f278ad0cf6d4368182fdf299f0fab0da3257d refs/remotes/origin/rpi-4.16.y
+b0b5a34c8538d2dc557aca9214365d0011c5cf65 refs/remotes/origin/rpi-4.17.y
+d58c595b013f8cfe4f19b6f61344067da72bc360 refs/remotes/origin/rpi-4.18.y
+bfef951a177d4d3f10e8b5072316a517d68a2a27 refs/remotes/origin/rpi-4.19.y
+1c082fa78a09436ff22f30bfc82b9b7a127686d5 refs/remotes/origin/rpi-4.19.y-rt
+99b4d38f11cfdcead6be5105620e1acde36bdde5 refs/remotes/origin/rpi-4.2.y
+41f5d37cd29dda87fd2181c1fdb828815ef1a3ac refs/remotes/origin/rpi-4.20.y
+dc8ffff0c13d01cde05406017f4afac0a4af6c5c refs/remotes/origin/rpi-4.3.y
+f30cdaff08f24e1d91d74b0444ddb23c39f0c6bf refs/remotes/origin/rpi-4.3.y_rebase
+04c8e47067d4873c584395e5cb260b4f170a99ea refs/remotes/origin/rpi-4.4.y
+9faebbe7636f4c4f39f8cb550365ec35e02a2a7b refs/remotes/origin/rpi-4.4.y_irq
+b6cafbf16c095a671b1a5e90dd734390f53046b2 refs/remotes/origin/rpi-4.4.y_rebase
+237402141fd74ca989bd86ebb76d834cb6fa5454 refs/remotes/origin/rpi-4.5.y
+2b1791a6693b0e22568426a5c99efa92403f3ffe refs/remotes/origin/rpi-4.6.y
+c2cbd9c611256e7b957f75c23d9f76d58a4893c1 refs/remotes/origin/rpi-4.7.y
+061dccce6cf6705bbb5da29a643f4b0ad1d11630 refs/remotes/origin/rpi-4.8.y
+7f9c648dad6473469b4133898fa6bb8d818ecff9 refs/remotes/origin/rpi-4.9.y
+7175dd6f062127fb973ef2ba03566c02b5b413bc refs/remotes/origin/rpi-4.9.y-rebase
+921b6254452db87ae0304beaa9833cfdf5083ed8 refs/remotes/origin/rpi-4.9.y-stable
+0222b82b2f1fcf1e0c477f77c50688c98f35e2bf refs/remotes/origin/rpi-5.0.y
+29ee73a82540e8b94c6bde0ad1acae65f73653ea refs/remotes/origin/rpi-5.1.y
+7aa66c086ae01c4f77d54aaeb1d9f62573a7b748 refs/remotes/origin/rpi-5.10.y
+01594d1282026c91baf231bf00da3c4a8c74243b refs/remotes/origin/rpi-5.11.y
+543d07caefc9d449342651cc87d90730bfefb859 refs/remotes/origin/rpi-5.12.y
+49d70567dcae78d1f04b62d4228f9a0219ca6005 refs/remotes/origin/rpi-5.13.y
+dad7d122bd2af70d8688427dfdb013693cd09b19 refs/remotes/origin/rpi-5.14.y
+94dbfa7606fc2f3df44979ed9c2d1d3676f2c159 refs/remotes/origin/rpi-5.2.y
+32ba05a62a8071d091d7582cc37b4bac2962b1dd refs/remotes/origin/rpi-5.3.y
+ec0dcf3064b8ba99f226438214407fcea9870f76 refs/remotes/origin/rpi-5.4.y
+96a3278497cc6540d32dc4a7dba9100da6df16f0 refs/remotes/origin/rpi-5.5.y
+ff0aa78166a59d44bc8d472386699624c8db6da5 refs/remotes/origin/rpi-5.6.y
+fe872d026d3a280a5151af08eb949eddf586fa1b refs/remotes/origin/rpi-5.7.y
+9b3199ca416d89b1f810fc65d8ffe1ad2a03ac0e refs/remotes/origin/rpi-5.8.y
+104137d44c6365fafc36ec1ea59db06f58ef5dfc refs/remotes/origin/rpi-5.9.y
+8fa2e19c4fdbe17704947e444674e19ea47c8ddd refs/remotes/origin/rpi-patches
+935c7ce84c982a26f567a03a58a1537424569938 refs/remotes/origin/rpi-r.9.y
+68bead249ca6ad97fc39a0780471f4b65402afc1 refs/tags/raspberrypi-kernel_1.20160506-1
+68bead249ca6ad97fc39a0780471f4b65402afc1 refs/tags/raspberrypi-kernel_1.20160512-1
+233755da0e7903fccb41f0b8c14e1da5244b69ec refs/tags/raspberrypi-kernel_1.20160523-1
+e88b69aa792dbe2b289ff528c0d07303068aa0aa refs/tags/raspberrypi-kernel_1.20160620-1
+2d31cd571e6f2ac61a3eda273d973058e1bb1f58 refs/tags/raspberrypi-kernel_1.20160921-1
+1315ab6319b02a436a6c1a2b38608e52b94d22fb refs/tags/raspberrypi-kernel_1.20161020-1
+8b0a53e9f9d3a2c2e07b2341ccbf5eb4adc8100f refs/tags/raspberrypi-kernel_1.20161116-1
+fa00f31867d8f7b7bb2ad2eb6c35f32fa468ca1e refs/tags/raspberrypi-kernel_1.20161125-1
+03f35fe498e5516091e4fd66d1700e8d0b0f4915 refs/tags/raspberrypi-kernel_1.20161215-1
+3273ed969add6d80fcaacc9a1756b6f544c8194a refs/tags/raspberrypi-kernel_1.20170215-1
+e223d71ef728c559aa865d0c5a4cedbdf8789cfd refs/tags/raspberrypi-kernel_1.20170303-1
+e223d71ef728c559aa865d0c5a4cedbdf8789cfd refs/tags/raspberrypi-kernel_1.20170405-1
+ef3b440e0e4d9ca70060483aa33d5b1201ceceb8 refs/tags/raspberrypi-kernel_1.20170427-1
+1423ac8bfbfb2a9d092b604c676e7a58a5fa3367 refs/tags/raspberrypi-kernel_1.20170515-1
+be2540e540f5442d7b372208787fb64100af0c54 refs/tags/raspberrypi-kernel_1.20170702-1
+be2540e540f5442d7b372208787fb64100af0c54 refs/tags/raspberrypi-kernel_1.20170703-1
+be2540e540f5442d7b372208787fb64100af0c54 refs/tags/raspberrypi-kernel_1.20170703-2
+4153f509b449f1c1c816cf124c314975c3daa824 refs/tags/raspberrypi-kernel_1.20170811-1
+6820d0cbec64cfee481b961833feffec8880111e refs/tags/raspberrypi-kernel_1.20171029-1
+921b6254452db87ae0304beaa9833cfdf5083ed8 refs/tags/raspberrypi-kernel_1.20180313-1
+81dda1af754c3af667944af7156bf1c5cdf9beee refs/tags/raspberrypi-kernel_1.20180328-1
+f70eae405b5d75f7c41ea300b9f790656f99a203 refs/tags/raspberrypi-kernel_1.20180417-1
+f1a1eec4c38d3c36b67c609f6c27805d04dab978 refs/tags/raspberrypi-kernel_1.20180619-1
+1f89ad77bf9b204c18fb6fdd167b4ee92d064f95 refs/tags/raspberrypi-kernel_1.20180817-1
+675e29ff7124059cb3b8b56fd7ae0ea131196982 refs/tags/raspberrypi-kernel_1.20180919-1
+4f30a5365296658d66ef8a9f878a25d229e2d5fa refs/tags/raspberrypi-kernel_1.20180924-1
+36612d5d7a88672a3e7dd6cb458dbbbca0d75efe refs/tags/raspberrypi-kernel_1.20181112-1
+5d63a4595d32a8505590d5fea5c4ec1ca79fd49d refs/tags/raspberrypi-kernel_1.20190215-1
+5d63a4595d32a8505590d5fea5c4ec1ca79fd49d refs/tags/raspberrypi-kernel_1.20190401-1
+f35a1df812cdfc093fdf3f352650e19905e41c48 refs/tags/raspberrypi-kernel_1.20190517-1
+883a1a5bf14abc9b3a014a3907772cd0b3129c8a refs/tags/raspberrypi-kernel_1.20190620-1
+6e787b87ace9f945f17a9022288e2758ed281cf7 refs/tags/raspberrypi-kernel_1.20190709-1
+246113692edbef9a438b31ab2dd0172a30ed5eb2 refs/tags/raspberrypi-kernel_1.20190718-1
+bd991fd87ccf2c0e1596cdd6713c1f46c6d79343 refs/tags/raspberrypi-kernel_1.20190819-1
+6d8bf28fa4b1ca0a35c0cd1dcb267fb216daf720 refs/tags/raspberrypi-kernel_1.20190925-1
+767722747b98c2d57425f984da2a8192ae30cc69 refs/tags/raspberrypi-kernel_1.20200114-1
+f16e91dad2af9d57aef477cc1f522040353849f5 refs/tags/raspberrypi-kernel_1.20200205-1
+f16e91dad2af9d57aef477cc1f522040353849f5 refs/tags/raspberrypi-kernel_1.20200210-1
+f16e91dad2af9d57aef477cc1f522040353849f5 refs/tags/raspberrypi-kernel_1.20200212-1
+fe2c7bf4cad4641dfb6f12712755515ab15815ca refs/tags/raspberrypi-kernel_1.20200512-2
+971a2bb14b459819db1bda8fcdf953e493242b42 refs/tags/raspberrypi-kernel_1.20200527-1
+971a2bb14b459819db1bda8fcdf953e493242b42 refs/tags/raspberrypi-kernel_1.20200601+arm64-1
+fe2c7bf4cad4641dfb6f12712755515ab15815ca refs/tags/raspberrypi-kernel_1.20200601-1
+3e92327867b6b40fed95ed36bb20f56d6e0cc879 refs/tags/raspberrypi-kernel_1.20200717-1
+f0eeafb5d9109c2a1d512f045d7326a8a1d33e7e refs/tags/raspberrypi-kernel_1.20200723-1
+f2f7e4b23d8788e96f81a7522b2f703e51c53e70 refs/tags/raspberrypi-kernel_1.20200811-1
+f2f7e4b23d8788e96f81a7522b2f703e51c53e70 refs/tags/raspberrypi-kernel_1.20200819-1
+f2f7e4b23d8788e96f81a7522b2f703e51c53e70 refs/tags/raspberrypi-kernel_1.20200902-1
+ff93994fb3f92070d8521d709ad04675ecaa5817 refs/tags/raspberrypi-kernel_1.20201022-1
+9797f1a4938c20139b00a25de93cc99efb5c291b refs/tags/raspberrypi-kernel_1.20201126-1
+9797f1a4938c20139b00a25de93cc99efb5c291b refs/tags/raspberrypi-kernel_1.20201201-1
+76c49e60e742d0bebd798be972d67dd3fd007691 refs/tags/raspberrypi-kernel_1.20210104-1
+b32354539bd4e3cff29eda643b5d3a983a24c063 refs/tags/raspberrypi-kernel_1.20210107.master-1
+76c49e60e742d0bebd798be972d67dd3fd007691 refs/tags/raspberrypi-kernel_1.20210108-1
+a7eebc039b08185dfe72c2bbe8275e930b37b575 refs/tags/raspberrypi-kernel_1.20210108.master-1
+a896a00ebffd0b016627f65ff236b660e66e782b refs/tags/raspberrypi-kernel_1.20210111.master-1
+6af8ae321a801a4e20183454c65eb0d23069d8ac refs/tags/raspberrypi-kernel_1.20210201-1
+623ca2ba45d86eb1b0323637330295c3f8d93c76 refs/tags/raspberrypi-kernel_1.20210303-1
+3a33f11c48572b9dd0fecac164b3990fc9234da8 refs/tags/raspberrypi-kernel_1.20210430-1
+081f560bde7188ca6f40cf297bd55c680e0179c0 refs/tags/raspberrypi-kernel_1.20210527-1
+e850ab437a2d0fa607d410b7bae62f90fb941430 refs/tags/rpi-5.10.y_20201122
+bc1669c846b629cface0aaa367afb2b9c6226faf refs/tags/rpi-bootloader-1.20151118-1
+6330c27817186caf38fc80e467cf858d9dd167fd refs/tags/rpi-bootloader-1.20160202-1
+ca312f557513e057c456598528e663fe9d009498 refs/tags/rpi-bootloader-1.20160209-1
+74105533c976f03ef137d9d8c43464b09a79b141 refs/tags/rpi-bootloader-1.20160309-1
+20fe468af4bb40fec0f81753da4b20a8bfc259c9 refs/tags/rpi-bootloader-1.20160315-1
+4b6528be8c7364e515d9e23054a379d8b528f3e3 refs/tags/rpi53_2019019
+^66d86f0e7604f80f82005b1938f96673f722ce73
+e63f402a78c18afa4d66da1f6556ccc69606effe refs/tags/tag1
+^276dc46ac79a79569f1873ca66684544dd6f0694
+b787a6acd3130c85fafff70a3361fae40487644f refs/tags/tag2
+^41a5ce7cdf9abb13e7b71de3dcfad58445628d98
+3b68600aac9df9aca2f88cf3e927b385b9992609 refs/tags/tag4
+^2528b76a7e5715e605b625cd3d2a3f360e93dfcf
+0c45535aeae95e05c40542efd2cab599d9b814f1 refs/tags/tag5
+^47ee7516cc3af54154e7ce4033a11dba55bb1272
diff -Nur --no-dereference linux-5.10.52-orig/.git/refs/heads/rpi-5.10.y linux-5.10.52-v7l+/.git/refs/heads/rpi-5.10.y
--- linux-5.10.52-orig/.git/refs/heads/rpi-5.10.y	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/.git/refs/heads/rpi-5.10.y	2021-07-25 16:45:32.678848969 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1 @
+7aa66c086ae01c4f77d54aaeb1d9f62573a7b748
diff -Nur --no-dereference linux-5.10.52-orig/.git/refs/remotes/origin/HEAD linux-5.10.52-v7l+/.git/refs/remotes/origin/HEAD
--- linux-5.10.52-orig/.git/refs/remotes/origin/HEAD	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/.git/refs/remotes/origin/HEAD	2021-07-25 16:45:32.678848969 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1 @
+ref: refs/remotes/origin/rpi-5.10.y
diff -Nur --no-dereference linux-5.10.52-orig/.github/ISSUE_TEMPLATE/bug_report.md linux-5.10.52-v7l+/.github/ISSUE_TEMPLATE/bug_report.md
--- linux-5.10.52-orig/.github/ISSUE_TEMPLATE/bug_report.md	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/.github/ISSUE_TEMPLATE/bug_report.md	2021-07-25 16:45:33.038842934 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+---
+name: Bug report
+about: Create a report to help us fix your issue
+
+---
+
+**Is this the right place for my bug report?**
+This repository contains the Linux kernel used on the Raspberry Pi. If you believe that the issue you are seeing is kernel-related, this is the right place. If not, we have other repositories for the GPU firmware at [github.com/raspberrypi/firmware](https://github.com/raspberrypi/firmware) and Raspberry Pi userland applications at [github.com/raspberrypi/userland](https://github.com/raspberrypi/userland). If you have problems with the Raspbian distribution packages, report them in the [github.com/RPi-Distro/repo](https://github.com/RPi-Distro/repo). If you simply have a question, then [the Raspberry Pi forums](https://www.raspberrypi.org/forums) are the best place to ask it.
+
+**Describe the bug**
+Add a clear and concise description of what you think the bug is.
+
+**To reproduce**
+List the steps required to reproduce the issue.
+
+**Expected behaviour**
+Add a clear and concise description of what you expected to happen.
+
+**Actual behaviour**
+Add a clear and concise description of what actually happened.
+
+**System**
+ Copy and paste the results of the raspinfo command in to this section. Alternatively, copy and paste a pastebin link, or add answers to the following questions:
+
+* Which model of Raspberry Pi? e.g. Pi3B+, PiZeroW
+* Which OS and version (`cat /etc/rpi-issue`)?
+* Which firmware version (`vcgencmd version`)?
+* Which kernel version (`uname -a`)?
+
+**Logs**
+If applicable, add the relevant output from `dmesg` or similar.
+
+**Additional context**
+Add any other relevant context for the problem.
diff -Nur --no-dereference linux-5.10.52-orig/.gitignore linux-5.10.52-v7l+/.gitignore
--- linux-5.10.52-orig/.gitignore	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/.gitignore	2021-07-25 16:45:33.038842934 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:21 @
 *.c.[012]*.*
 *.dt.yaml
 *.dtb
+*.dtbo
 *.dtb.S
 *.dwo
 *.elf
diff -Nur --no-dereference linux-5.10.52-orig/include/drm/drm_atomic.h linux-5.10.52-v7l+/include/drm/drm_atomic.h
--- linux-5.10.52-orig/include/drm/drm_atomic.h	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/include/drm/drm_atomic.h	2021-07-25 16:46:32.667843221 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:251 @
  *    drm_dev_register()
  * 2/ all calls to drm_atomic_private_obj_fini() must be done after calling
  *    drm_dev_unregister()
+ *
+ * If that private object is used to store a state shared by multiple
+ * CRTCs, proper care must be taken to ensure that non-blocking commits are
+ * properly ordered to avoid a use-after-free issue.
+ *
+ * Indeed, assuming a sequence of two non-blocking &drm_atomic_commit on two
+ * different &drm_crtc using different &drm_plane and &drm_connector, so with no
+ * resources shared, there's no guarantee on which commit is going to happen
+ * first. However, the second &drm_atomic_commit will consider the first
+ * &drm_private_obj its old state, and will be in charge of freeing it whenever
+ * the second &drm_atomic_commit is done.
+ *
+ * If the first &drm_atomic_commit happens after it, it will consider its
+ * &drm_private_obj the new state and will be likely to access it, resulting in
+ * an access to a freed memory region. Drivers should store (and get a reference
+ * to) the &drm_crtc_commit structure in our private state in
+ * &drm_mode_config_helper_funcs.atomic_commit_setup, and then wait for that
+ * commit to complete as the first step of
+ * &drm_mode_config_helper_funcs.atomic_commit_tail, similar to
+ * drm_atomic_helper_wait_for_dependencies().
  */
 struct drm_private_obj {
 	/**
diff -Nur --no-dereference linux-5.10.52-orig/include/drm/drm_atomic_helper.h linux-5.10.52-v7l+/include/drm/drm_atomic_helper.h
--- linux-5.10.52-orig/include/drm/drm_atomic_helper.h	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/include/drm/drm_atomic_helper.h	2021-07-25 16:46:32.667843221 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:150 @
 				uint32_t flags,
 				uint32_t target,
 				struct drm_modeset_acquire_ctx *ctx);
-int drm_atomic_helper_legacy_gamma_set(struct drm_crtc *crtc,
-				       u16 *red, u16 *green, u16 *blue,
-				       uint32_t size,
-				       struct drm_modeset_acquire_ctx *ctx);
 
 /**
  * drm_atomic_crtc_for_each_plane - iterate over planes currently attached to CRTC
diff -Nur --no-dereference linux-5.10.52-orig/include/drm/drm_connector.h linux-5.10.52-v7l+/include/drm/drm_connector.h
--- linux-5.10.52-orig/include/drm/drm_connector.h	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/include/drm/drm_connector.h	2021-07-25 16:46:32.667843221 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1625 @
 					       u32 scaling_mode_mask);
 int drm_connector_attach_vrr_capable_property(
 		struct drm_connector *connector);
+int drm_connector_attach_colorspace_property(struct drm_connector *connector);
+int drm_connector_attach_hdr_output_metadata_property(struct drm_connector *connector);
+bool drm_connector_atomic_hdr_metadata_equal(struct drm_connector_state *old_state,
+					     struct drm_connector_state *new_state);
 int drm_mode_create_aspect_ratio_property(struct drm_device *dev);
 int drm_mode_create_hdmi_colorspace_property(struct drm_connector *connector);
 int drm_mode_create_dp_colorspace_property(struct drm_connector *connector);
diff -Nur --no-dereference linux-5.10.52-orig/include/drm/drm_modeset_helper_vtables.h linux-5.10.52-v7l+/include/drm/drm_modeset_helper_vtables.h
--- linux-5.10.52-orig/include/drm/drm_modeset_helper_vtables.h	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/include/drm/drm_modeset_helper_vtables.h	2021-07-25 16:46:32.687842885 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:339 @
 	 *
 	 * This function is called in the check phase of an atomic update. The
 	 * driver is not allowed to change anything outside of the free-standing
-	 * state objects passed-in or assembled in the overall &drm_atomic_state
-	 * update tracking structure.
+	 * state object passed-in.
 	 *
 	 * Also beware that userspace can request its own custom modes, neither
 	 * core nor helpers filter modes to the list of probe modes reported by
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:355 @
 	 * deadlock.
 	 */
 	int (*atomic_check)(struct drm_crtc *crtc,
-			    struct drm_crtc_state *state);
+			    struct drm_atomic_state *state);
 
 	/**
 	 * @atomic_begin:
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:376 @
 	 * transitional plane helpers, but it is optional.
 	 */
 	void (*atomic_begin)(struct drm_crtc *crtc,
-			     struct drm_crtc_state *old_crtc_state);
+			     struct drm_atomic_state *state);
 	/**
 	 * @atomic_flush:
 	 *
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:400 @
 	 * transitional plane helpers, but it is optional.
 	 */
 	void (*atomic_flush)(struct drm_crtc *crtc,
-			     struct drm_crtc_state *old_crtc_state);
+			     struct drm_atomic_state *state);
 
 	/**
 	 * @atomic_enable:
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:419 @
 	 * @atomic_enable must be the inverse of @atomic_disable for atomic
 	 * drivers.
 	 *
-	 * Drivers can use the @old_crtc_state input parameter if the operations
-	 * needed to enable the CRTC don't depend solely on the new state but
-	 * also on the transition between the old state and the new state.
-	 *
 	 * This function is optional.
 	 */
 	void (*atomic_enable)(struct drm_crtc *crtc,
-			      struct drm_crtc_state *old_crtc_state);
+			      struct drm_atomic_state *state);
 
 	/**
 	 * @atomic_disable:
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:439 @
 	 * need to implement it if there's no need to disable anything at the
 	 * CRTC level.
 	 *
-	 * Comparing to @disable, this one provides the additional input
-	 * parameter @old_crtc_state which could be used to access the old
-	 * state. Atomic drivers should consider to use this one instead
-	 * of @disable.
-	 *
 	 * This function is optional.
 	 */
 	void (*atomic_disable)(struct drm_crtc *crtc,
-			       struct drm_crtc_state *old_crtc_state);
+			       struct drm_atomic_state *state);
 
 	/**
 	 * @get_scanout_position:
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1047 @
 	 * NOTE:
 	 *
 	 * This function is called in the check phase of an atomic update. The
-	 * driver is not allowed to change anything outside of the free-standing
-	 * state objects passed-in or assembled in the overall &drm_atomic_state
-	 * update tracking structure.
+	 * driver is not allowed to change anything outside of the
+	 * &drm_atomic_state update tracking structure passed in.
 	 *
 	 * RETURNS:
 	 *
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1058 @
 	 * for this.
 	 */
 	struct drm_encoder *(*atomic_best_encoder)(struct drm_connector *connector,
-						   struct drm_connector_state *connector_state);
+						   struct drm_atomic_state *state);
 
 	/**
 	 * @atomic_check:
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1099 @
 	 *
 	 * This hook is to be used by drivers implementing writeback connectors
 	 * that need a point when to commit the writeback job to the hardware.
-	 * The writeback_job to commit is available in
-	 * &drm_connector_state.writeback_job.
+	 * The writeback_job to commit is available in the new connector state,
+	 * in &drm_connector_state.writeback_job.
 	 *
 	 * This hook is optional.
 	 *
 	 * This callback is used by the atomic modeset helpers.
 	 */
 	void (*atomic_commit)(struct drm_connector *connector,
-			      struct drm_connector_state *state);
+			      struct drm_atomic_state *state);
 
 	/**
 	 * @prepare_writeback_job:
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1398 @
 	 * drm_atomic_helper_commit_tail().
 	 */
 	void (*atomic_commit_tail)(struct drm_atomic_state *state);
+
+	/**
+	 * @atomic_commit_setup:
+	 *
+	 * This hook is used by the default atomic_commit() hook implemented in
+	 * drm_atomic_helper_commit() together with the nonblocking helpers (see
+	 * drm_atomic_helper_setup_commit()) to extend the DRM commit setup. It
+	 * is not used by the atomic helpers.
+	 *
+	 * This function is called at the end of
+	 * drm_atomic_helper_setup_commit(), so once the commit has been
+	 * properly setup across the generic DRM object states. It allows
+	 * drivers to do some additional commit tracking that isn't related to a
+	 * CRTC, plane or connector, tracked in a &drm_private_obj structure.
+	 *
+	 * Note that the documentation of &drm_private_obj has more details on
+	 * how one should implement this.
+	 *
+	 * This hook is optional.
+	 */
+	int (*atomic_commit_setup)(struct drm_atomic_state *state);
 };
 
 #endif
diff -Nur --no-dereference linux-5.10.52-orig/include/drm/gud.h linux-5.10.52-v7l+/include/drm/gud.h
--- linux-5.10.52-orig/include/drm/gud.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/include/drm/gud.h	2021-07-25 16:46:32.687842885 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright 2020 Noralf Trønnes
+ */
+
+#ifndef __LINUX_GUD_H
+#define __LINUX_GUD_H
+
+#include <linux/types.h>
+
+/*
+ * struct gud_display_descriptor_req - Display descriptor
+ * @magic: Magic value GUD_DISPLAY_MAGIC
+ * @version: Protocol version
+ * @flags: Flags
+ *         - STATUS_ON_SET: Always do a status request after a SET request.
+ *                          This is used by the Linux gadget driver since it has
+ *                          no way to control the status stage of a control OUT
+ *                          request that has a payload.
+ *         - FULL_UPDATE:   Always send the entire framebuffer when flushing changes.
+ *                          The GUD_REQ_SET_BUFFER request will not be sent
+ *                          before each bulk transfer, it will only be sent if the
+ *                          previous bulk transfer had failed. This gives the device
+ *                          a chance to reset its state machine if needed.
+ *                          This flag can not be used in combination with compression.
+ * @compression: Supported compression types
+ *               - GUD_COMPRESSION_LZ4: LZ4 lossless compression.
+ * @max_buffer_size: Maximum buffer size the device can handle (optional).
+ *                   This is useful for devices that don't have a big enough
+ *                   buffer to decompress the entire framebuffer in one go.
+ * @min_width: Minimum pixel width the controller can handle
+ * @max_width: Maximum width
+ * @min_height: Minimum height
+ * @max_height: Maximum height
+ *
+ * Devices that have only one display mode will have min_width == max_width
+ * and min_height == max_height.
+ */
+struct gud_display_descriptor_req {
+	__le32 magic;
+#define GUD_DISPLAY_MAGIC			0x1d50614d
+	__u8 version;
+	__le32 flags;
+#define GUD_DISPLAY_FLAG_STATUS_ON_SET		BIT(0)
+#define GUD_DISPLAY_FLAG_FULL_UPDATE		BIT(1)
+	__u8 compression;
+#define GUD_COMPRESSION_LZ4			BIT(0)
+	__le32 max_buffer_size;
+	__le32 min_width;
+	__le32 max_width;
+	__le32 min_height;
+	__le32 max_height;
+} __packed;
+
+/*
+ * struct gud_property_req - Property
+ * @prop: Property
+ * @val: Value
+ */
+struct gud_property_req {
+	__le16 prop;
+	__le64 val;
+} __packed;
+
+/*
+ * struct gud_display_mode_req - Display mode
+ * @clock: Pixel clock in kHz
+ * @hdisplay: Horizontal display size
+ * @hsync_start: Horizontal sync start
+ * @hsync_end: Horizontal sync end
+ * @htotal: Horizontal total size
+ * @vdisplay: Vertical display size
+ * @vsync_start: Vertical sync start
+ * @vsync_end: Vertical sync end
+ * @vtotal: Vertical total size
+ * @flags: Bits 0-13 are the same as in the RandR protocol and also what DRM uses.
+ *         The deprecated bits are reused for internal protocol flags leaving us
+ *         free to follow DRM for the other bits in the future.
+ *         - FLAG_PREFERRED: Set on the preferred display mode.
+ */
+struct gud_display_mode_req {
+	__le32 clock;
+	__le16 hdisplay;
+	__le16 hsync_start;
+	__le16 hsync_end;
+	__le16 htotal;
+	__le16 vdisplay;
+	__le16 vsync_start;
+	__le16 vsync_end;
+	__le16 vtotal;
+	__le32 flags;
+#define GUD_DISPLAY_MODE_FLAG_PHSYNC		BIT(0)
+#define GUD_DISPLAY_MODE_FLAG_NHSYNC		BIT(1)
+#define GUD_DISPLAY_MODE_FLAG_PVSYNC		BIT(2)
+#define GUD_DISPLAY_MODE_FLAG_NVSYNC		BIT(3)
+#define GUD_DISPLAY_MODE_FLAG_INTERLACE		BIT(4)
+#define GUD_DISPLAY_MODE_FLAG_DBLSCAN		BIT(5)
+#define GUD_DISPLAY_MODE_FLAG_CSYNC		BIT(6)
+#define GUD_DISPLAY_MODE_FLAG_PCSYNC		BIT(7)
+#define GUD_DISPLAY_MODE_FLAG_NCSYNC		BIT(8)
+#define GUD_DISPLAY_MODE_FLAG_HSKEW		BIT(9)
+/* BCast and PixelMultiplex are deprecated */
+#define GUD_DISPLAY_MODE_FLAG_DBLCLK		BIT(12)
+#define GUD_DISPLAY_MODE_FLAG_CLKDIV2		BIT(13)
+#define GUD_DISPLAY_MODE_FLAG_USER_MASK		\
+		(GUD_DISPLAY_MODE_FLAG_PHSYNC | GUD_DISPLAY_MODE_FLAG_NHSYNC | \
+		GUD_DISPLAY_MODE_FLAG_PVSYNC | GUD_DISPLAY_MODE_FLAG_NVSYNC | \
+		GUD_DISPLAY_MODE_FLAG_INTERLACE | GUD_DISPLAY_MODE_FLAG_DBLSCAN | \
+		GUD_DISPLAY_MODE_FLAG_CSYNC | GUD_DISPLAY_MODE_FLAG_PCSYNC | \
+		GUD_DISPLAY_MODE_FLAG_NCSYNC | GUD_DISPLAY_MODE_FLAG_HSKEW | \
+		GUD_DISPLAY_MODE_FLAG_DBLCLK | GUD_DISPLAY_MODE_FLAG_CLKDIV2)
+/* Internal protocol flags */
+#define GUD_DISPLAY_MODE_FLAG_PREFERRED		BIT(10)
+} __packed;
+
+/*
+ * struct gud_connector_descriptor_req - Connector descriptor
+ * @connector_type: Connector type (GUD_CONNECTOR_TYPE_*).
+ *                  If the host doesn't support the type it should fall back to PANEL.
+ * @flags: Flags
+ *         - POLL_STATUS: Connector status can change (polled every 10 seconds)
+ *         - INTERLACE: Interlaced modes are supported
+ *         - DOUBLESCAN: Doublescan modes are supported
+ */
+struct gud_connector_descriptor_req {
+	__u8 connector_type;
+#define GUD_CONNECTOR_TYPE_PANEL		0
+#define GUD_CONNECTOR_TYPE_VGA			1
+#define GUD_CONNECTOR_TYPE_COMPOSITE		2
+#define GUD_CONNECTOR_TYPE_SVIDEO		3
+#define GUD_CONNECTOR_TYPE_COMPONENT		4
+#define GUD_CONNECTOR_TYPE_DVI			5
+#define GUD_CONNECTOR_TYPE_DISPLAYPORT		6
+#define GUD_CONNECTOR_TYPE_HDMI			7
+	__le32 flags;
+#define GUD_CONNECTOR_FLAGS_POLL_STATUS		BIT(0)
+#define GUD_CONNECTOR_FLAGS_INTERLACE		BIT(1)
+#define GUD_CONNECTOR_FLAGS_DOUBLESCAN		BIT(2)
+} __packed;
+
+/*
+ * struct gud_set_buffer_req - Set buffer transfer info
+ * @x: X position of rectangle
+ * @y: Y position
+ * @width: Pixel width of rectangle
+ * @height: Pixel height
+ * @length: Buffer length in bytes
+ * @compression: Transfer compression
+ * @compressed_length: Compressed buffer length
+ *
+ * This request is issued right before the bulk transfer.
+ * @x, @y, @width and @height specifies the rectangle where the buffer should be
+ * placed inside the framebuffer.
+ */
+struct gud_set_buffer_req {
+	__le32 x;
+	__le32 y;
+	__le32 width;
+	__le32 height;
+	__le32 length;
+	__u8 compression;
+	__le32 compressed_length;
+} __packed;
+
+/*
+ * struct gud_state_req - Display state
+ * @mode: Display mode
+ * @format: Pixel format GUD_PIXEL_FORMAT_*
+ * @connector: Connector index
+ * @properties: Array of properties
+ *
+ * The entire state is transferred each time there's a change.
+ */
+struct gud_state_req {
+	struct gud_display_mode_req mode;
+	__u8 format;
+	__u8 connector;
+	struct gud_property_req properties[];
+} __packed;
+
+/* List of supported connector properties: */
+
+/* Margins in pixels to deal with overscan, range 0-100 */
+#define GUD_PROPERTY_TV_LEFT_MARGIN			1
+#define GUD_PROPERTY_TV_RIGHT_MARGIN			2
+#define GUD_PROPERTY_TV_TOP_MARGIN			3
+#define GUD_PROPERTY_TV_BOTTOM_MARGIN			4
+#define GUD_PROPERTY_TV_MODE				5
+/* Brightness in percent, range 0-100 */
+#define GUD_PROPERTY_TV_BRIGHTNESS			6
+/* Contrast in percent, range 0-100 */
+#define GUD_PROPERTY_TV_CONTRAST			7
+/* Flicker reduction in percent, range 0-100 */
+#define GUD_PROPERTY_TV_FLICKER_REDUCTION		8
+/* Overscan in percent, range 0-100 */
+#define GUD_PROPERTY_TV_OVERSCAN			9
+/* Saturation in percent, range 0-100 */
+#define GUD_PROPERTY_TV_SATURATION			10
+/* Hue in percent, range 0-100 */
+#define GUD_PROPERTY_TV_HUE				11
+
+/*
+ * Backlight brightness is in the range 0-100 inclusive. The value represents the human perceptual
+ * brightness and not a linear PWM value. 0 is minimum brightness which should not turn the
+ * backlight completely off. The DPMS connector property should be used to control power which will
+ * trigger a GUD_REQ_SET_DISPLAY_ENABLE request.
+ *
+ * This does not map to a DRM property, it is used with the backlight device.
+ */
+#define GUD_PROPERTY_BACKLIGHT_BRIGHTNESS		12
+
+/* List of supported properties that are not connector propeties: */
+
+/*
+ * Plane rotation. Should return the supported bitmask on
+ * GUD_REQ_GET_PROPERTIES. GUD_ROTATION_0 is mandatory.
+ *
+ * Note: This is not display rotation so 90/270 will need scaling to make it fit (unless squared).
+ */
+#define GUD_PROPERTY_ROTATION				50
+  #define GUD_ROTATION_0			BIT(0)
+  #define GUD_ROTATION_90			BIT(1)
+  #define GUD_ROTATION_180			BIT(2)
+  #define GUD_ROTATION_270			BIT(3)
+  #define GUD_ROTATION_REFLECT_X		BIT(4)
+  #define GUD_ROTATION_REFLECT_Y		BIT(5)
+  #define GUD_ROTATION_MASK			(GUD_ROTATION_0 | GUD_ROTATION_90 | \
+						GUD_ROTATION_180 | GUD_ROTATION_270 | \
+						GUD_ROTATION_REFLECT_X | GUD_ROTATION_REFLECT_Y)
+
+/* USB Control requests: */
+
+/* Get status from the last GET/SET control request. Value is u8. */
+#define GUD_REQ_GET_STATUS				0x00
+  /* Status values: */
+  #define GUD_STATUS_OK				0x00
+  #define GUD_STATUS_BUSY			0x01
+  #define GUD_STATUS_REQUEST_NOT_SUPPORTED	0x02
+  #define GUD_STATUS_PROTOCOL_ERROR		0x03
+  #define GUD_STATUS_INVALID_PARAMETER		0x04
+  #define GUD_STATUS_ERROR			0x05
+
+/* Get display descriptor as a &gud_display_descriptor_req */
+#define GUD_REQ_GET_DESCRIPTOR				0x01
+
+/* Get supported pixel formats as a byte array of GUD_PIXEL_FORMAT_* */
+#define GUD_REQ_GET_FORMATS				0x40
+  #define GUD_FORMATS_MAX_NUM			32
+  /* R1 is a 1-bit monochrome transfer format presented to userspace as XRGB8888 */
+  #define GUD_PIXEL_FORMAT_R1			0x01
+  #define GUD_PIXEL_FORMAT_XRGB1111		0x20
+  #define GUD_PIXEL_FORMAT_RGB565		0x40
+  #define GUD_PIXEL_FORMAT_XRGB8888		0x80
+  #define GUD_PIXEL_FORMAT_ARGB8888		0x81
+
+/*
+ * Get supported properties that are not connector propeties as a &gud_property_req array.
+ * gud_property_req.val often contains the initial value for the property.
+ */
+#define GUD_REQ_GET_PROPERTIES				0x41
+  #define GUD_PROPERTIES_MAX_NUM		32
+
+/* Connector requests have the connector index passed in the wValue field */
+
+/* Get connector descriptors as an array of &gud_connector_descriptor_req */
+#define GUD_REQ_GET_CONNECTORS				0x50
+  #define GUD_CONNECTORS_MAX_NUM		32
+
+/*
+ * Get properties supported by the connector as a &gud_property_req array.
+ * gud_property_req.val often contains the initial value for the property.
+ */
+#define GUD_REQ_GET_CONNECTOR_PROPERTIES		0x51
+  #define GUD_CONNECTOR_PROPERTIES_MAX_NUM	32
+
+/*
+ * Issued when there's a TV_MODE property present.
+ * Gets an array of the supported TV_MODE names each entry of length
+ * GUD_CONNECTOR_TV_MODE_NAME_LEN. Names must be NUL-terminated.
+ */
+#define GUD_REQ_GET_CONNECTOR_TV_MODE_VALUES		0x52
+  #define GUD_CONNECTOR_TV_MODE_NAME_LEN	16
+  #define GUD_CONNECTOR_TV_MODE_MAX_NUM		16
+
+/* When userspace checks connector status, this is issued first, not used for poll requests. */
+#define GUD_REQ_SET_CONNECTOR_FORCE_DETECT		0x53
+
+/*
+ * Get connector status. Value is u8.
+ *
+ * Userspace will get a HOTPLUG uevent if one of the following is true:
+ * - Connection status has changed since last
+ * - CHANGED is set
+ */
+#define GUD_REQ_GET_CONNECTOR_STATUS			0x54
+  #define GUD_CONNECTOR_STATUS_DISCONNECTED	0x00
+  #define GUD_CONNECTOR_STATUS_CONNECTED	0x01
+  #define GUD_CONNECTOR_STATUS_UNKNOWN		0x02
+  #define GUD_CONNECTOR_STATUS_CONNECTED_MASK	0x03
+  #define GUD_CONNECTOR_STATUS_CHANGED		BIT(7)
+
+/*
+ * Display modes can be fetched as either EDID data or an array of &gud_display_mode_req.
+ *
+ * If GUD_REQ_GET_CONNECTOR_MODES returns zero, EDID is used to create display modes.
+ * If both display modes and EDID are returned, EDID is just passed on to userspace
+ * in the EDID connector property.
+ */
+
+/* Get &gud_display_mode_req array of supported display modes */
+#define GUD_REQ_GET_CONNECTOR_MODES			0x55
+  #define GUD_CONNECTOR_MAX_NUM_MODES		128
+
+/* Get Extended Display Identification Data */
+#define GUD_REQ_GET_CONNECTOR_EDID			0x56
+  #define GUD_CONNECTOR_MAX_EDID_LEN		2048
+
+/* Set buffer properties before bulk transfer as &gud_set_buffer_req */
+#define GUD_REQ_SET_BUFFER				0x60
+
+/* Check display configuration as &gud_state_req */
+#define GUD_REQ_SET_STATE_CHECK				0x61
+
+/* Apply the previous STATE_CHECK configuration */
+#define GUD_REQ_SET_STATE_COMMIT			0x62
+
+/* Enable/disable the display controller, value is u8: 0/1 */
+#define GUD_REQ_SET_CONTROLLER_ENABLE			0x63
+
+/* Enable/disable display/output (DPMS), value is u8: 0/1 */
+#define GUD_REQ_SET_DISPLAY_ENABLE			0x64
+
+#endif
diff -Nur --no-dereference linux-5.10.52-orig/include/dt-bindings/gpio/gpio-fsm.h linux-5.10.52-v7l+/include/dt-bindings/gpio/gpio-fsm.h
--- linux-5.10.52-orig/include/dt-bindings/gpio/gpio-fsm.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/include/dt-bindings/gpio/gpio-fsm.h	2021-07-25 16:46:32.747841880 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * This header provides constants for binding rpi,gpio-fsm.
+ */
+
+#ifndef _DT_BINDINGS_GPIO_FSM_H
+#define _DT_BINDINGS_GPIO_FSM_H
+
+#define GF_IN       0
+#define GF_OUT      1
+#define GF_SOFT     2
+#define GF_DELAY    3
+#define GF_SHUTDOWN 4
+
+#define GF_IO(t, v) (((v) << 16) | ((t) & 0xffff))
+
+#define GF_IP(x)    GF_IO(GF_IN, (x))
+#define GF_OP(x)    GF_IO(GF_OUT, (x))
+#define GF_SW(x)    GF_IO(GF_SOFT, (x))
+
+#endif
diff -Nur --no-dereference linux-5.10.52-orig/include/linux/brcmphy.h linux-5.10.52-v7l+/include/linux/brcmphy.h
--- linux-5.10.52-orig/include/linux/brcmphy.h	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/include/linux/brcmphy.h	2021-07-25 16:46:32.827840538 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:25 @
 #define PHY_ID_BCM5411			0x00206070
 #define PHY_ID_BCM5421			0x002060e0
 #define PHY_ID_BCM54210E		0x600d84a0
+#define PHY_ID_BCM54213PE		0x600d84a2
 #define PHY_ID_BCM5464			0x002060b0
 #define PHY_ID_BCM5461			0x002060c0
 #define PHY_ID_BCM54612E		0x03625e60
diff -Nur --no-dereference linux-5.10.52-orig/include/linux/broadcom/bcm2835_smi.h linux-5.10.52-v7l+/include/linux/broadcom/bcm2835_smi.h
--- linux-5.10.52-orig/include/linux/broadcom/bcm2835_smi.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/include/linux/broadcom/bcm2835_smi.h	2021-07-25 16:46:32.827840538 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/**
+ * Declarations and definitions for Broadcom's Secondary Memory Interface
+ *
+ * Written by Luke Wren <luke@raspberrypi.org>
+ * Copyright (c) 2015, Raspberry Pi (Trading) Ltd.
+ * Copyright (c) 2010-2012 Broadcom. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The names of the above-listed copyright holders may not be used
+ *    to endorse or promote products derived from this software without
+ *    specific prior written permission.
+ *
+ * ALTERNATIVELY, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2, as published by the Free
+ * Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef BCM2835_SMI_H
+#define BCM2835_SMI_H
+
+#include <linux/ioctl.h>
+
+#ifndef __KERNEL__
+#include <stdint.h>
+#include <stdbool.h>
+#endif
+
+#define BCM2835_SMI_IOC_MAGIC 0x1
+#define BCM2835_SMI_INVALID_HANDLE (~0)
+
+/* IOCTLs 0x100...0x1ff are not device-specific - we can use them */
+#define BCM2835_SMI_IOC_GET_SETTINGS    _IO(BCM2835_SMI_IOC_MAGIC, 0)
+#define BCM2835_SMI_IOC_WRITE_SETTINGS  _IO(BCM2835_SMI_IOC_MAGIC, 1)
+#define BCM2835_SMI_IOC_ADDRESS	 _IO(BCM2835_SMI_IOC_MAGIC, 2)
+#define BCM2835_SMI_IOC_MAX	     2
+
+#define SMI_WIDTH_8BIT 0
+#define SMI_WIDTH_16BIT 1
+#define SMI_WIDTH_9BIT 2
+#define SMI_WIDTH_18BIT 3
+
+/* max number of bytes where DMA will not be used */
+#define DMA_THRESHOLD_BYTES 128
+#define DMA_BOUNCE_BUFFER_SIZE (1024 * 1024 / 2)
+#define DMA_BOUNCE_BUFFER_COUNT 3
+
+
+struct smi_settings {
+	int data_width;
+	/* Whether or not to pack multiple SMI transfers into a
+	   single 32 bit FIFO word */
+	bool pack_data;
+
+	/* Timing for reads (writes the same but for WE)
+	 *
+	 * OE ----------+	   +--------------------
+	 *		|	   |
+	 *		+----------+
+	 * SD -<==============================>-----------
+	 * SA -<=========================================>-
+	 *    <-setup->  <-strobe ->  <-hold ->  <- pace ->
+	 */
+
+	int read_setup_time;
+	int read_hold_time;
+	int read_pace_time;
+	int read_strobe_time;
+
+	int write_setup_time;
+	int write_hold_time;
+	int write_pace_time;
+	int write_strobe_time;
+
+	bool dma_enable;		/* DREQs */
+	bool dma_passthrough_enable;	/* External DREQs */
+	int dma_read_thresh;
+	int dma_write_thresh;
+	int dma_panic_read_thresh;
+	int dma_panic_write_thresh;
+};
+
+/****************************************************************************
+*
+*   Declare exported SMI functions
+*
+***************************************************************************/
+
+#ifdef __KERNEL__
+
+#include <linux/dmaengine.h> /* for enum dma_transfer_direction */
+#include <linux/of.h>
+#include <linux/semaphore.h>
+
+struct bcm2835_smi_instance;
+
+struct bcm2835_smi_bounce_info {
+	struct semaphore callback_sem;
+	void *buffer[DMA_BOUNCE_BUFFER_COUNT];
+	dma_addr_t phys[DMA_BOUNCE_BUFFER_COUNT];
+	struct scatterlist sgl[DMA_BOUNCE_BUFFER_COUNT];
+};
+
+
+void bcm2835_smi_set_regs_from_settings(struct bcm2835_smi_instance *);
+
+struct smi_settings *bcm2835_smi_get_settings_from_regs(
+	struct bcm2835_smi_instance *inst);
+
+void bcm2835_smi_write_buf(
+	struct bcm2835_smi_instance *inst,
+	const void *buf,
+	size_t n_bytes);
+
+void bcm2835_smi_read_buf(
+	struct bcm2835_smi_instance *inst,
+	void *buf,
+	size_t n_bytes);
+
+void bcm2835_smi_set_address(struct bcm2835_smi_instance *inst,
+	unsigned int address);
+
+ssize_t bcm2835_smi_user_dma(
+	struct bcm2835_smi_instance *inst,
+	enum dma_transfer_direction dma_dir,
+	char __user *user_ptr,
+	size_t count,
+	struct bcm2835_smi_bounce_info **bounce);
+
+struct bcm2835_smi_instance *bcm2835_smi_get(struct device_node *node);
+
+#endif /* __KERNEL__ */
+
+/****************************************************************
+*
+*	Implementation-only declarations
+*
+****************************************************************/
+
+#ifdef BCM2835_SMI_IMPLEMENTATION
+
+/* Clock manager registers for SMI clock: */
+#define CM_SMI_BASE_ADDRESS ((BCM2708_PERI_BASE) + 0x1010b0)
+/* Clock manager "password" to protect registers from spurious writes */
+#define CM_PWD (0x5a << 24)
+
+#define CM_SMI_CTL	0x00
+#define CM_SMI_DIV	0x04
+
+#define CM_SMI_CTL_FLIP (1 << 8)
+#define CM_SMI_CTL_BUSY (1 << 7)
+#define CM_SMI_CTL_KILL (1 << 5)
+#define CM_SMI_CTL_ENAB (1 << 4)
+#define CM_SMI_CTL_SRC_MASK (0xf)
+#define CM_SMI_CTL_SRC_OFFS (0)
+
+#define CM_SMI_DIV_DIVI_MASK (0xf <<  12)
+#define CM_SMI_DIV_DIVI_OFFS (12)
+#define CM_SMI_DIV_DIVF_MASK (0xff << 4)
+#define CM_SMI_DIV_DIVF_OFFS (4)
+
+/* SMI register mapping:*/
+#define SMI_BASE_ADDRESS ((BCM2708_PERI_BASE) + 0x600000)
+
+#define SMICS	0x00	/* control + status register		*/
+#define SMIL	0x04	/* length/count (n external txfers)	*/
+#define SMIA	0x08	/* address register			*/
+#define SMID	0x0c	/* data register			*/
+#define SMIDSR0	0x10	/* device 0 read settings		*/
+#define SMIDSW0	0x14	/* device 0 write settings		*/
+#define SMIDSR1	0x18	/* device 1 read settings		*/
+#define SMIDSW1	0x1c	/* device 1 write settings		*/
+#define SMIDSR2	0x20	/* device 2 read settings		*/
+#define SMIDSW2	0x24	/* device 2 write settings		*/
+#define SMIDSR3	0x28	/* device 3 read settings		*/
+#define SMIDSW3	0x2c	/* device 3 write settings		*/
+#define SMIDC	0x30	/* DMA control registers		*/
+#define SMIDCS	0x34	/* direct control/status register	*/
+#define SMIDA	0x38	/* direct address register		*/
+#define SMIDD	0x3c	/* direct data registers		*/
+#define SMIFD	0x40	/* FIFO debug register			*/
+
+
+
+/* Control and Status register bits:
+ * SMICS_RXF	: RX fifo full: 1 when RX fifo is full
+ * SMICS_TXE	: TX fifo empty: 1 when empty.
+ * SMICS_RXD	: RX fifo contains data: 1 when there is data.
+ * SMICS_TXD	: TX fifo can accept data: 1 when true.
+ * SMICS_RXR	: RX fifo needs reading: 1 when fifo more than 3/4 full, or
+ *		  when "DONE" and fifo not emptied.
+ * SMICS_TXW	: TX fifo needs writing: 1 when less than 1/4 full.
+ * SMICS_AFERR	: AXI FIFO error: 1 when fifo read when empty or written
+ *		  when full. Write 1 to clear.
+ * SMICS_EDREQ	: 1 when external DREQ received.
+ * SMICS_PXLDAT	:  Pixel data:	write 1 to enable pixel transfer modes.
+ * SMICS_SETERR	: 1 if there was an error writing to setup regs (e.g.
+ *		  tx was in progress). Write 1 to clear.
+ * SMICS_PVMODE	: Set to 1 to enable pixel valve mode.
+ * SMICS_INTR	: Set to 1 to enable interrupt on RX.
+ * SMICS_INTT	: Set to 1 to enable interrupt on TX.
+ * SMICS_INTD	: Set to 1 to enable interrupt on DONE condition.
+ * SMICS_TEEN	: Tear effect mode enabled: Programmed transfers will wait
+ *		  for a TE trigger before writing.
+ * SMICS_PAD1	: Padding settings for external transfers. For writes: the
+ *		  number of bytes initially written to  the TX fifo that
+ * SMICS_PAD0	: should be ignored. For reads: the number of bytes that will
+ *		  be read before the data, and should be dropped.
+ * SMICS_WRITE	: Transfer direction: 1 = write to external device, 0 = read
+ * SMICS_CLEAR	: Write 1 to clear the FIFOs.
+ * SMICS_START	: Write 1 to start the programmed transfer.
+ * SMICS_ACTIVE	: Reads as 1 when a programmed transfer is underway.
+ * SMICS_DONE	: Reads as 1 when transfer finished. For RX, not set until
+ *		  FIFO emptied.
+ * SMICS_ENABLE	: Set to 1 to enable the SMI peripheral, 0 to disable.
+ */
+
+#define SMICS_RXF	(1 << 31)
+#define SMICS_TXE	(1 << 30)
+#define SMICS_RXD	(1 << 29)
+#define SMICS_TXD	(1 << 28)
+#define SMICS_RXR	(1 << 27)
+#define SMICS_TXW	(1 << 26)
+#define SMICS_AFERR	(1 << 25)
+#define SMICS_EDREQ	(1 << 15)
+#define SMICS_PXLDAT	(1 << 14)
+#define SMICS_SETERR	(1 << 13)
+#define SMICS_PVMODE	(1 << 12)
+#define SMICS_INTR	(1 << 11)
+#define SMICS_INTT	(1 << 10)
+#define SMICS_INTD	(1 << 9)
+#define SMICS_TEEN	(1 << 8)
+#define SMICS_PAD1	(1 << 7)
+#define SMICS_PAD0	(1 << 6)
+#define SMICS_WRITE	(1 << 5)
+#define SMICS_CLEAR	(1 << 4)
+#define SMICS_START	(1 << 3)
+#define SMICS_ACTIVE	(1 << 2)
+#define SMICS_DONE	(1 << 1)
+#define SMICS_ENABLE	(1 << 0)
+
+/* Address register bits: */
+
+#define SMIA_DEVICE_MASK ((1 << 9) | (1 << 8))
+#define SMIA_DEVICE_OFFS (8)
+#define SMIA_ADDR_MASK (0x3f)	/* bits 5 -> 0 */
+#define SMIA_ADDR_OFFS (0)
+
+/* DMA control register bits:
+ * SMIDC_DMAEN	: DMA enable: set 1: DMA requests will be issued.
+ * SMIDC_DMAP	: DMA passthrough: when set to 0, top two data pins are used by
+ *		  SMI as usual. When set to 1, the top two pins are used for
+ *		  external DREQs: pin 16 read request, 17 write.
+ * SMIDC_PANIC*	: Threshold at which DMA will panic during read/write.
+ * SMIDC_REQ*	: Threshold at which DMA will generate a DREQ.
+ */
+
+#define SMIDC_DMAEN		(1 << 28)
+#define SMIDC_DMAP		(1 << 24)
+#define SMIDC_PANICR_MASK	(0x3f << 18)
+#define SMIDC_PANICR_OFFS	(18)
+#define SMIDC_PANICW_MASK	(0x3f << 12)
+#define SMIDC_PANICW_OFFS	(12)
+#define SMIDC_REQR_MASK		(0x3f << 6)
+#define SMIDC_REQR_OFFS		(6)
+#define SMIDC_REQW_MASK		(0x3f)
+#define SMIDC_REQW_OFFS		(0)
+
+/* Device settings register bits: same for all 4 (or 3?) device register sets.
+ * Device read settings:
+ * SMIDSR_RWIDTH	: Read transfer width. 00 = 8bit, 01 = 16bit,
+ *			  10 = 18bit, 11 = 9bit.
+ * SMIDSR_RSETUP	: Read setup time: number of core cycles between chip
+ *			  select/address and read strobe. Min 1, max 64.
+ * SMIDSR_MODE68	: 1 for System 68 mode (i.e. enable + direction pins,
+ *			  rather than OE + WE pin)
+ * SMIDSR_FSETUP	: If set to 1, setup time only applies to first
+ *			  transfer after address change.
+ * SMIDSR_RHOLD		: Number of core cycles between read strobe going
+ *			  inactive and CS/address going inactive. Min 1, max 64
+ * SMIDSR_RPACEALL	: When set to 1, this device's RPACE value will always
+ *			  be used for the next transaction, even if it is not
+ *			  to this device.
+ * SMIDSR_RPACE		: Number of core cycles spent waiting between CS
+ *			  deassert and start of next transfer. Min 1, max 128
+ * SMIDSR_RDREQ		: 1 = use external DMA request on SD16 to pace reads
+ *			  from device. Must also set DMAP in SMICS.
+ * SMIDSR_RSTROBE	: Number of cycles to assert the read strobe.
+ *			  min 1, max 128.
+ */
+#define SMIDSR_RWIDTH_MASK	((1<<31)|(1<<30))
+#define SMIDSR_RWIDTH_OFFS	(30)
+#define SMIDSR_RSETUP_MASK	(0x3f << 24)
+#define SMIDSR_RSETUP_OFFS	(24)
+#define SMIDSR_MODE68		(1 << 23)
+#define SMIDSR_FSETUP		(1 << 22)
+#define SMIDSR_RHOLD_MASK	(0x3f << 16)
+#define SMIDSR_RHOLD_OFFS	(16)
+#define SMIDSR_RPACEALL		(1 << 15)
+#define SMIDSR_RPACE_MASK	(0x7f << 8)
+#define SMIDSR_RPACE_OFFS	(8)
+#define SMIDSR_RDREQ		(1 << 7)
+#define SMIDSR_RSTROBE_MASK	(0x7f)
+#define SMIDSR_RSTROBE_OFFS	(0)
+
+/* Device write settings:
+ * SMIDSW_WWIDTH	: Write transfer width. 00 = 8bit, 01 = 16bit,
+ *			  10= 18bit, 11 = 9bit.
+ * SMIDSW_WSETUP	: Number of cycles between CS assert and write strobe.
+ *			  Min 1, max 64.
+ * SMIDSW_WFORMAT	: Pixel format of input. 0 = 16bit RGB 565,
+ *			  1 = 32bit RGBA 8888
+ * SMIDSW_WSWAP		: 1 = swap pixel data bits. (Use with SMICS_PXLDAT)
+ * SMIDSW_WHOLD		: Time between WE deassert and CS deassert. 1 to 64
+ * SMIDSW_WPACEALL	: 1: this device's WPACE will be used for the next
+ *			  transfer, regardless of that transfer's device.
+ * SMIDSW_WPACE		: Cycles between CS deassert and next CS assert.
+ *			  Min 1, max 128
+ * SMIDSW_WDREQ		: Use external DREQ on pin 17 to pace writes. DMAP must
+ *			  be set in SMICS.
+ * SMIDSW_WSTROBE	: Number of cycles to assert the write strobe.
+ *			  Min 1, max 128
+ */
+#define SMIDSW_WWIDTH_MASK	 ((1<<31)|(1<<30))
+#define SMIDSW_WWIDTH_OFFS	(30)
+#define SMIDSW_WSETUP_MASK	(0x3f << 24)
+#define SMIDSW_WSETUP_OFFS	(24)
+#define SMIDSW_WFORMAT		(1 << 23)
+#define SMIDSW_WSWAP		(1 << 22)
+#define SMIDSW_WHOLD_MASK	(0x3f << 16)
+#define SMIDSW_WHOLD_OFFS	(16)
+#define SMIDSW_WPACEALL		(1 << 15)
+#define SMIDSW_WPACE_MASK	(0x7f << 8)
+#define SMIDSW_WPACE_OFFS	(8)
+#define SMIDSW_WDREQ		(1 << 7)
+#define SMIDSW_WSTROBE_MASK	 (0x7f)
+#define SMIDSW_WSTROBE_OFFS	 (0)
+
+/* Direct transfer control + status register
+ * SMIDCS_WRITE	: Direction of transfer: 1 -> write, 0 -> read
+ * SMIDCS_DONE	: 1 when a transfer has finished. Write 1 to clear.
+ * SMIDCS_START	: Write 1 to start a transfer, if one is not already underway.
+ * SMIDCE_ENABLE: Write 1 to enable SMI in direct mode.
+ */
+
+#define SMIDCS_WRITE		(1 << 3)
+#define SMIDCS_DONE		(1 << 2)
+#define SMIDCS_START		(1 << 1)
+#define SMIDCS_ENABLE		(1 << 0)
+
+/* Direct transfer address register
+ * SMIDA_DEVICE	: Indicates which of the device settings banks should be used.
+ * SMIDA_ADDR	: The value to be asserted on the address pins.
+ */
+
+#define SMIDA_DEVICE_MASK	((1<<9)|(1<<8))
+#define SMIDA_DEVICE_OFFS	(8)
+#define SMIDA_ADDR_MASK		(0x3f)
+#define SMIDA_ADDR_OFFS		(0)
+
+/* FIFO debug register
+ * SMIFD_FLVL	: The high-tide mark of FIFO count during the most recent txfer
+ * SMIFD_FCNT	: The current FIFO count.
+ */
+#define SMIFD_FLVL_MASK		(0x3f << 8)
+#define SMIFD_FLVL_OFFS		(8)
+#define SMIFD_FCNT_MASK		(0x3f)
+#define SMIFD_FCNT_OFFS		(0)
+
+#endif /* BCM2835_SMI_IMPLEMENTATION */
+
+#endif /* BCM2835_SMI_H */
diff -Nur --no-dereference linux-5.10.52-orig/include/linux/broadcom/vc_mem.h linux-5.10.52-v7l+/include/linux/broadcom/vc_mem.h
--- linux-5.10.52-orig/include/linux/broadcom/vc_mem.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/include/linux/broadcom/vc_mem.h	2021-07-25 16:46:32.827840538 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/*
+ * Copyright 2010 - 2011 Broadcom Corporation.  All rights reserved.
+ *
+ * Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2, available at
+ * http://www.broadcom.com/licenses/GPLv2.php (the "GPL").
+ *
+ * Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a
+ * license other than the GPL, without Broadcom's express prior written
+ * consent.
+ */
+
+#ifndef _VC_MEM_H
+#define _VC_MEM_H
+
+#include <linux/ioctl.h>
+
+#define VC_MEM_IOC_MAGIC  'v'
+
+#define VC_MEM_IOC_MEM_PHYS_ADDR    _IOR(VC_MEM_IOC_MAGIC, 0, unsigned long)
+#define VC_MEM_IOC_MEM_SIZE         _IOR(VC_MEM_IOC_MAGIC, 1, unsigned int)
+#define VC_MEM_IOC_MEM_BASE         _IOR(VC_MEM_IOC_MAGIC, 2, unsigned int)
+#define VC_MEM_IOC_MEM_LOAD         _IOR(VC_MEM_IOC_MAGIC, 3, unsigned int)
+
+#ifdef __KERNEL__
+#define VC_MEM_TO_ARM_ADDR_MASK 0x3FFFFFFF
+
+extern unsigned long mm_vc_mem_phys_addr;
+extern unsigned int  mm_vc_mem_size;
+extern int vc_mem_get_current_size(void);
+#endif
+
+#ifdef CONFIG_COMPAT
+#define VC_MEM_IOC_MEM_PHYS_ADDR32  _IOR(VC_MEM_IOC_MAGIC, 0, compat_ulong_t)
+#endif
+
+#endif  /* _VC_MEM_H */
diff -Nur --no-dereference linux-5.10.52-orig/include/linux/clk.h linux-5.10.52-v7l+/include/linux/clk.h
--- linux-5.10.52-orig/include/linux/clk.h	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/include/linux/clk.h	2021-07-25 16:46:32.847840203 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:18 @
 
 struct device;
 struct clk;
+struct clk_request;
 struct device_node;
 struct of_phandle_args;
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:747 @
  */
 void clk_restore_context(void);
 
+struct clk_request *clk_request_start(struct clk *clk, unsigned long rate);
+void clk_request_done(struct clk_request *req);
+
 #else /* !CONFIG_HAVE_CLK */
 
 static inline struct clk *clk_get(struct device *dev, const char *id)
diff -Nur --no-dereference linux-5.10.52-orig/include/linux/hdmi.h linux-5.10.52-v7l+/include/linux/hdmi.h
--- linux-5.10.52-orig/include/linux/hdmi.h	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/include/linux/hdmi.h	2021-07-25 16:46:32.927838862 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:159 @
 };
 
 enum hdmi_metadata_type {
-	HDMI_STATIC_METADATA_TYPE1 = 1,
+	HDMI_STATIC_METADATA_TYPE1 = 0,
 };
 
 enum hdmi_eotf {
diff -Nur --no-dereference linux-5.10.52-orig/include/linux/leds.h linux-5.10.52-v7l+/include/linux/leds.h
--- linux-5.10.52-orig/include/linux/leds.h	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/include/linux/leds.h	2021-07-25 16:46:32.997837688 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:82 @
 #define LED_BRIGHT_HW_CHANGED	BIT(21)
 #define LED_RETAIN_AT_SHUTDOWN	BIT(22)
 #define LED_INIT_DEFAULT_TRIGGER BIT(23)
+	/* Additions for Raspberry Pi PWR LED */
+#define SET_GPIO_INPUT		BIT(30)
+#define SET_GPIO_OUTPUT		BIT(31)
 
 	/* set_brightness_work / blink_timer flags, atomic, private. */
 	unsigned long		work_flags;
diff -Nur --no-dereference linux-5.10.52-orig/include/linux/mfd/bcm2835-pm.h linux-5.10.52-v7l+/include/linux/mfd/bcm2835-pm.h
--- linux-5.10.52-orig/include/linux/mfd/bcm2835-pm.h	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/include/linux/mfd/bcm2835-pm.h	2021-07-25 16:46:33.067836515 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:12 @
 	struct device *dev;
 	void __iomem *base;
 	void __iomem *asb;
+	void __iomem *rpivid_asb;
 };
 
 #endif /* BCM2835_MFD_PM_H */
diff -Nur --no-dereference linux-5.10.52-orig/include/linux/mfd/rpisense/core.h linux-5.10.52-v7l+/include/linux/mfd/rpisense/core.h
--- linux-5.10.52-orig/include/linux/mfd/rpisense/core.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/include/linux/mfd/rpisense/core.h	2021-07-25 16:46:33.167834838 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/*
+ * Raspberry Pi Sense HAT core driver
+ * http://raspberrypi.org
+ *
+ * Copyright (C) 2015 Raspberry Pi
+ *
+ * Author: Serge Schneider
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ */
+
+#ifndef __LINUX_MFD_RPISENSE_CORE_H_
+#define __LINUX_MFD_RPISENSE_CORE_H_
+
+#include <linux/mfd/rpisense/joystick.h>
+#include <linux/mfd/rpisense/framebuffer.h>
+
+/*
+ * Register values.
+ */
+#define RPISENSE_FB			0x00
+#define RPISENSE_WAI			0xF0
+#define RPISENSE_VER			0xF1
+#define RPISENSE_KEYS			0xF2
+#define RPISENSE_EE_WP			0xF3
+
+#define RPISENSE_ID			's'
+
+struct rpisense {
+	struct device *dev;
+	struct i2c_client *i2c_client;
+
+	/* Client devices */
+	struct rpisense_js joystick;
+	struct rpisense_fb framebuffer;
+};
+
+struct rpisense *rpisense_get_dev(void);
+s32 rpisense_reg_read(struct rpisense *rpisense, int reg);
+int rpisense_reg_write(struct rpisense *rpisense, int reg, u16 val);
+int rpisense_block_write(struct rpisense *rpisense, const char *buf, int count);
+
+#endif
diff -Nur --no-dereference linux-5.10.52-orig/include/linux/mfd/rpisense/framebuffer.h linux-5.10.52-v7l+/include/linux/mfd/rpisense/framebuffer.h
--- linux-5.10.52-orig/include/linux/mfd/rpisense/framebuffer.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/include/linux/mfd/rpisense/framebuffer.h	2021-07-25 16:46:33.167834838 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/*
+ * Raspberry Pi Sense HAT framebuffer driver
+ * http://raspberrypi.org
+ *
+ * Copyright (C) 2015 Raspberry Pi
+ *
+ * Author: Serge Schneider
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ */
+
+#ifndef __LINUX_RPISENSE_FB_H_
+#define __LINUX_RPISENSE_FB_H_
+
+#define SENSEFB_FBIO_IOC_MAGIC 0xF1
+
+#define SENSEFB_FBIOGET_GAMMA _IO(SENSEFB_FBIO_IOC_MAGIC, 0)
+#define SENSEFB_FBIOSET_GAMMA _IO(SENSEFB_FBIO_IOC_MAGIC, 1)
+#define SENSEFB_FBIORESET_GAMMA _IO(SENSEFB_FBIO_IOC_MAGIC, 2)
+
+struct rpisense;
+
+struct rpisense_fb {
+	struct platform_device *pdev;
+	struct fb_info *info;
+};
+
+#endif
diff -Nur --no-dereference linux-5.10.52-orig/include/linux/mfd/rpisense/joystick.h linux-5.10.52-v7l+/include/linux/mfd/rpisense/joystick.h
--- linux-5.10.52-orig/include/linux/mfd/rpisense/joystick.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/include/linux/mfd/rpisense/joystick.h	2021-07-25 16:46:33.167834838 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/*
+ * Raspberry Pi Sense HAT joystick driver
+ * http://raspberrypi.org
+ *
+ * Copyright (C) 2015 Raspberry Pi
+ *
+ * Author: Serge Schneider
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ */
+
+#ifndef __LINUX_RPISENSE_JOYSTICK_H_
+#define __LINUX_RPISENSE_JOYSTICK_H_
+
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/gpio/consumer.h>
+#include <linux/platform_device.h>
+
+struct rpisense;
+
+struct rpisense_js {
+	struct platform_device *pdev;
+	struct input_dev *keys_dev;
+	struct gpio_desc *keys_desc;
+	struct work_struct keys_work_s;
+	int keys_irq;
+};
+
+
+#endif
diff -Nur --no-dereference linux-5.10.52-orig/include/linux/microchipphy.h linux-5.10.52-v7l+/include/linux/microchipphy.h
--- linux-5.10.52-orig/include/linux/microchipphy.h	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/include/linux/microchipphy.h	2021-07-25 16:46:33.267833162 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:64 @
 /* Registers specific to the LAN7800/LAN7850 embedded phy */
 #define LAN78XX_PHY_LED_MODE_SELECT		(0x1D)
 
+#define LAN78XX_PHY_CTRL3			(0x14)
+#define LAN78XX_PHY_CTRL3_AUTO_DOWNSHIFT	(0x0010)
+#define LAN78XX_PHY_CTRL3_DOWNSHIFT_CTRL_MASK	(0x000c)
+#define LAN78XX_PHY_CTRL3_DOWNSHIFT_CTRL_2	(0x0000)
+#define LAN78XX_PHY_CTRL3_DOWNSHIFT_CTRL_3	(0x0004)
+#define LAN78XX_PHY_CTRL3_DOWNSHIFT_CTRL_4	(0x0008)
+#define LAN78XX_PHY_CTRL3_DOWNSHIFT_CTRL_5	(0x000c)
+
 /* DSP registers */
 #define PHY_ARDENNES_MMD_DEV_3_PHY_CFG		(0x806A)
 #define PHY_ARDENNES_MMD_DEV_3_PHY_CFG_ZD_DLY_EN_	(0x2000)
diff -Nur --no-dereference linux-5.10.52-orig/include/linux/mmc/card.h linux-5.10.52-v7l+/include/linux/mmc/card.h
--- linux-5.10.52-orig/include/linux/mmc/card.h	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/include/linux/mmc/card.h	2021-07-25 16:46:33.327832156 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:274 @
 #define MMC_QUIRK_TRIM_BROKEN	(1<<12)		/* Skip trim */
 #define MMC_QUIRK_BROKEN_HPI	(1<<13)		/* Disable broken HPI support */
 
+#define MMC_QUIRK_ERASE_BROKEN	(1<<31)		/* Skip erase */
+
 	bool			reenable_cmdq;	/* Re-enable Command Queue */
 
 	unsigned int		erase_size;	/* erase size in sectors */
diff -Nur --no-dereference linux-5.10.52-orig/include/linux/platform_data/dma-bcm2708.h linux-5.10.52-v7l+/include/linux/platform_data/dma-bcm2708.h
--- linux-5.10.52-orig/include/linux/platform_data/dma-bcm2708.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/include/linux/platform_data/dma-bcm2708.h	2021-07-25 16:46:33.577827965 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/*
+ *  Copyright (C) 2010 Broadcom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _PLAT_BCM2708_DMA_H
+#define _PLAT_BCM2708_DMA_H
+
+/* DMA CS Control and Status bits */
+#define BCM2708_DMA_ACTIVE	BIT(0)
+#define BCM2708_DMA_INT		BIT(2)
+#define BCM2708_DMA_ISPAUSED	BIT(4)  /* Pause requested or not active */
+#define BCM2708_DMA_ISHELD	BIT(5)  /* Is held by DREQ flow control */
+#define BCM2708_DMA_ERR		BIT(8)
+#define BCM2708_DMA_ABORT	BIT(30) /* stop current CB, go to next, WO */
+#define BCM2708_DMA_RESET	BIT(31) /* WO, self clearing */
+
+/* DMA control block "info" field bits */
+#define BCM2708_DMA_INT_EN	BIT(0)
+#define BCM2708_DMA_TDMODE	BIT(1)
+#define BCM2708_DMA_WAIT_RESP	BIT(3)
+#define BCM2708_DMA_D_INC	BIT(4)
+#define BCM2708_DMA_D_WIDTH	BIT(5)
+#define BCM2708_DMA_D_DREQ	BIT(6)
+#define BCM2708_DMA_S_INC	BIT(8)
+#define BCM2708_DMA_S_WIDTH	BIT(9)
+#define BCM2708_DMA_S_DREQ	BIT(10)
+
+#define	BCM2708_DMA_BURST(x)	(((x) & 0xf) << 12)
+#define	BCM2708_DMA_PER_MAP(x)	((x) << 16)
+#define	BCM2708_DMA_WAITS(x)	(((x) & 0x1f) << 21)
+
+#define BCM2708_DMA_DREQ_EMMC	11
+#define BCM2708_DMA_DREQ_SDHOST	13
+
+#define BCM2708_DMA_CS		0x00 /* Control and Status */
+#define BCM2708_DMA_ADDR	0x04
+/* the current control block appears in the following registers - read only */
+#define BCM2708_DMA_INFO	0x08
+#define BCM2708_DMA_SOURCE_AD	0x0c
+#define BCM2708_DMA_DEST_AD	0x10
+#define BCM2708_DMA_NEXTCB	0x1C
+#define BCM2708_DMA_DEBUG	0x20
+
+#define BCM2708_DMA4_CS		(BCM2708_DMA_CHAN(4) + BCM2708_DMA_CS)
+#define BCM2708_DMA4_ADDR	(BCM2708_DMA_CHAN(4) + BCM2708_DMA_ADDR)
+
+#define BCM2708_DMA_TDMODE_LEN(w, h) ((h) << 16 | (w))
+
+/* When listing features we can ask for when allocating DMA channels give
+   those with higher priority smaller ordinal numbers */
+#define BCM_DMA_FEATURE_FAST_ORD	0
+#define BCM_DMA_FEATURE_BULK_ORD	1
+#define BCM_DMA_FEATURE_NORMAL_ORD	2
+#define BCM_DMA_FEATURE_LITE_ORD	3
+#define BCM_DMA_FEATURE_FAST		BIT(BCM_DMA_FEATURE_FAST_ORD)
+#define BCM_DMA_FEATURE_BULK		BIT(BCM_DMA_FEATURE_BULK_ORD)
+#define BCM_DMA_FEATURE_NORMAL		BIT(BCM_DMA_FEATURE_NORMAL_ORD)
+#define BCM_DMA_FEATURE_LITE		BIT(BCM_DMA_FEATURE_LITE_ORD)
+#define BCM_DMA_FEATURE_COUNT		4
+
+struct bcm2708_dma_cb {
+	u32 info;
+	u32 src;
+	u32 dst;
+	u32 length;
+	u32 stride;
+	u32 next;
+	u32 pad[2];
+};
+
+struct scatterlist;
+struct platform_device;
+
+#if defined(CONFIG_DMA_BCM2708) || defined(CONFIG_DMA_BCM2708_MODULE)
+
+int bcm_sg_suitable_for_dma(struct scatterlist *sg_ptr, int sg_len);
+void bcm_dma_start(void __iomem *dma_chan_base, dma_addr_t control_block);
+void bcm_dma_wait_idle(void __iomem *dma_chan_base);
+bool bcm_dma_is_busy(void __iomem *dma_chan_base);
+int bcm_dma_abort(void __iomem *dma_chan_base);
+
+/* return channel no or -ve error */
+int bcm_dma_chan_alloc(unsigned preferred_feature_set,
+		       void __iomem **out_dma_base, int *out_dma_irq);
+int bcm_dma_chan_free(int channel);
+
+int bcm_dmaman_probe(struct platform_device *pdev, void __iomem *base,
+		     u32 chans_available);
+int bcm_dmaman_remove(struct platform_device *pdev);
+
+#else /* CONFIG_DMA_BCM2708 */
+
+static inline int bcm_sg_suitable_for_dma(struct scatterlist *sg_ptr,
+					  int sg_len)
+{
+	return 0;
+}
+
+static inline void bcm_dma_start(void __iomem *dma_chan_base,
+				 dma_addr_t control_block) { }
+
+static inline void bcm_dma_wait_idle(void __iomem *dma_chan_base) { }
+
+static inline bool bcm_dma_is_busy(void __iomem *dma_chan_base)
+{
+	return false;
+}
+
+static inline int bcm_dma_abort(void __iomem *dma_chan_base)
+{
+	return -EINVAL;
+}
+
+static inline int bcm_dma_chan_alloc(unsigned preferred_feature_set,
+				     void __iomem **out_dma_base,
+				     int *out_dma_irq)
+{
+	return -EINVAL;
+}
+
+static inline int bcm_dma_chan_free(int channel)
+{
+	return -EINVAL;
+}
+
+static inline int bcm_dmaman_probe(struct platform_device *pdev,
+				   void __iomem *base, u32 chans_available)
+{
+	return 0;
+}
+
+static inline int bcm_dmaman_remove(struct platform_device *pdev)
+{
+	return 0;
+}
+
+#endif /* CONFIG_DMA_BCM2708 || CONFIG_DMA_BCM2708_MODULE */
+
+#endif /* _PLAT_BCM2708_DMA_H */
diff -Nur --no-dereference linux-5.10.52-orig/include/linux/usb/hcd.h linux-5.10.52-v7l+/include/linux/usb/hcd.h
--- linux-5.10.52-orig/include/linux/usb/hcd.h	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/include/linux/usb/hcd.h	2021-07-25 16:46:34.037820253 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:385 @
 		 * or bandwidth constraints.
 		 */
 	void	(*reset_bandwidth)(struct usb_hcd *, struct usb_device *);
+		/* Override the endpoint-derived interval
+		 * (if there is any cached hardware state).
+		 */
+	void	(*fixup_endpoint)(struct usb_hcd *hcd, struct usb_device *udev,
+				  struct usb_host_endpoint *ep, int interval);
 		/* Returns the hardware-chosen device address */
 	int	(*address_device)(struct usb_hcd *, struct usb_device *udev);
 		/* prepares the hardware to send commands to the device */
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:451 @
 extern void usb_hcd_unmap_urb_for_dma(struct usb_hcd *, struct urb *);
 extern void usb_hcd_flush_endpoint(struct usb_device *udev,
 		struct usb_host_endpoint *ep);
+extern void usb_hcd_fixup_endpoint(struct usb_device *udev,
+		struct usb_host_endpoint *ep, int interval);
 extern void usb_hcd_disable_endpoint(struct usb_device *udev,
 		struct usb_host_endpoint *ep);
 extern void usb_hcd_reset_endpoint(struct usb_device *udev,
diff -Nur --no-dereference linux-5.10.52-orig/include/linux/usb/r8152.h linux-5.10.52-v7l+/include/linux/usb/r8152.h
--- linux-5.10.52-orig/include/linux/usb/r8152.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/include/linux/usb/r8152.h	2021-07-25 16:46:34.057819918 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ *  Copyright (c) 2020 Realtek Semiconductor Corp. All rights reserved.
+ */
+
+#ifndef	__LINUX_R8152_H
+#define __LINUX_R8152_H
+
+#define RTL8152_REQT_READ		0xc0
+#define RTL8152_REQT_WRITE		0x40
+#define RTL8152_REQ_GET_REGS		0x05
+#define RTL8152_REQ_SET_REGS		0x05
+
+#define BYTE_EN_DWORD			0xff
+#define BYTE_EN_WORD			0x33
+#define BYTE_EN_BYTE			0x11
+#define BYTE_EN_SIX_BYTES		0x3f
+#define BYTE_EN_START_MASK		0x0f
+#define BYTE_EN_END_MASK		0xf0
+
+#define MCU_TYPE_PLA			0x0100
+#define MCU_TYPE_USB			0x0000
+
+/* Define these values to match your device */
+#define VENDOR_ID_REALTEK		0x0bda
+#define VENDOR_ID_MICROSOFT		0x045e
+#define VENDOR_ID_SAMSUNG		0x04e8
+#define VENDOR_ID_LENOVO		0x17ef
+#define VENDOR_ID_LINKSYS		0x13b1
+#define VENDOR_ID_NVIDIA		0x0955
+#define VENDOR_ID_TPLINK		0x2357
+
+#if IS_REACHABLE(CONFIG_USB_RTL8152)
+extern u8 rtl8152_get_version(struct usb_interface *intf);
+#endif
+
+#endif /* __LINUX_R8152_H */
diff -Nur --no-dereference linux-5.10.52-orig/include/linux/usb.h linux-5.10.52-v7l+/include/linux/usb.h
--- linux-5.10.52-orig/include/linux/usb.h	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/include/linux/usb.h	2021-07-25 16:46:34.037820253 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1841 @
 extern int usb_reset_configuration(struct usb_device *dev);
 extern int usb_set_interface(struct usb_device *dev, int ifnum, int alternate);
 extern void usb_reset_endpoint(struct usb_device *dev, unsigned int epaddr);
+extern void usb_fixup_endpoint(struct usb_device *dev, int epaddr,
+			       int interval);
 
 /* this request isn't really synchronous, but it belongs with the others */
 extern int usb_driver_set_configuration(struct usb_device *udev, int config);
diff -Nur --no-dereference linux-5.10.52-orig/include/media/hevc-ctrls.h linux-5.10.52-v7l+/include/media/hevc-ctrls.h
--- linux-5.10.52-orig/include/media/hevc-ctrls.h	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/include/media/hevc-ctrls.h	2021-07-25 16:46:34.167818073 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:22 @
 #define V4L2_CID_MPEG_VIDEO_HEVC_SPS		(V4L2_CID_MPEG_BASE + 1008)
 #define V4L2_CID_MPEG_VIDEO_HEVC_PPS		(V4L2_CID_MPEG_BASE + 1009)
 #define V4L2_CID_MPEG_VIDEO_HEVC_SLICE_PARAMS	(V4L2_CID_MPEG_BASE + 1010)
+#define V4L2_CID_MPEG_VIDEO_HEVC_SCALING_MATRIX	(V4L2_CID_MPEG_BASE + 1011)
 #define V4L2_CID_MPEG_VIDEO_HEVC_DECODE_MODE	(V4L2_CID_MPEG_BASE + 1015)
 #define V4L2_CID_MPEG_VIDEO_HEVC_START_CODE	(V4L2_CID_MPEG_BASE + 1016)
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:30 @
 #define V4L2_CTRL_TYPE_HEVC_SPS 0x0120
 #define V4L2_CTRL_TYPE_HEVC_PPS 0x0121
 #define V4L2_CTRL_TYPE_HEVC_SLICE_PARAMS 0x0122
+#define V4L2_CTRL_TYPE_HEVC_SCALING_MATRIX 0x0123
 
 enum v4l2_mpeg_video_hevc_decode_mode {
 	V4L2_MPEG_VIDEO_HEVC_DECODE_MODE_SLICE_BASED,
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:171 @
 	__u32	bit_size;
 	__u32	data_bit_offset;
 
+	/* ISO/IEC 23008-2, ITU-T Rec. H.265: General slice segment header */
+	__u32	slice_segment_addr;
+	__u32	num_entry_point_offsets;
+
 	/* ISO/IEC 23008-2, ITU-T Rec. H.265: NAL unit header */
 	__u8	nal_unit_type;
 	__u8	nuh_temporal_id_plus1;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:210 @
 
 	__u8	padding;
 
+	__u32	entry_point_offset_minus1[256];
+
 	/* ISO/IEC 23008-2, ITU-T Rec. H.265: General slice segment header */
 	struct v4l2_hevc_dpb_entry dpb[V4L2_HEVC_DPB_ENTRIES_NUM_MAX];
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:221 @
 	__u64	flags;
 };
 
+struct v4l2_ctrl_hevc_scaling_matrix {
+	__u8	scaling_list_4x4[6][16];
+	__u8	scaling_list_8x8[6][64];
+	__u8	scaling_list_16x16[6][64];
+	__u8	scaling_list_32x32[2][64];
+	__u8	scaling_list_dc_coef_16x16[6];
+	__u8	scaling_list_dc_coef_32x32[2];
+};
+
 #endif
diff -Nur --no-dereference linux-5.10.52-orig/include/media/media-request.h linux-5.10.52-v7l+/include/media/media-request.h
--- linux-5.10.52-orig/include/media/media-request.h	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/include/media/media-request.h	2021-07-25 16:46:34.187817738 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:192 @
  */
 void media_request_put(struct media_request *req);
 
+void media_request_pin(struct media_request *req);
+
+void media_request_unpin(struct media_request *req);
+
 /**
  * media_request_get_by_fd - Get a media request by fd
  *
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:235 @
 {
 }
 
+static inline void media_request_pin(struct media_request *req)
+{
+}
+
+static inline void media_request_unpin(struct media_request *req)
+{
+}
+
 static inline struct media_request *
 media_request_get_by_fd(struct media_device *mdev, int request_fd)
 {
diff -Nur --no-dereference linux-5.10.52-orig/include/media/v4l2-mediabus.h linux-5.10.52-v7l+/include/media/v4l2-mediabus.h
--- linux-5.10.52-orig/include/media/v4l2-mediabus.h	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/include/media/v4l2-mediabus.h	2021-07-25 16:46:34.207817403 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:95 @
 					 V4L2_MBUS_CSI2_CHANNEL_1 | \
 					 V4L2_MBUS_CSI2_CHANNEL_2 | \
 					 V4L2_MBUS_CSI2_CHANNEL_3)
+/*
+ * Number of lanes in use, 0 == use all available lanes (default)
+ *
+ * This is a temporary fix for devices that need to reduce the number of active
+ * lanes for certain modes, until g_mbus_config() can be replaced with a better
+ * solution.
+ */
+#define V4L2_MBUS_CSI2_LANE_MASK                (0xf << 10)
 
 /**
  * enum v4l2_mbus_type - media bus type
diff -Nur --no-dereference linux-5.10.52-orig/include/media/videobuf2-core.h linux-5.10.52-v7l+/include/media/videobuf2-core.h
--- linux-5.10.52-orig/include/media/videobuf2-core.h	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/include/media/videobuf2-core.h	2021-07-25 16:46:34.237816900 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:905 @
 int vb2_core_streamoff(struct vb2_queue *q, unsigned int type);
 
 /**
+ * vb2_core_expbuf_dmabuf() - Export a buffer as a dma_buf structure
+ * @q:         videobuf2 queue
+ * @type:      buffer type
+ * @index:     id number of the buffer
+ * @plane:     index of the plane to be exported, 0 for single plane queues
+ * @flags:     flags for newly created file, currently only O_CLOEXEC is
+ *             supported, refer to manual of open syscall for more details
+ * @dmabuf:    Returns the dmabuf pointer
+ *
+ */
+int vb2_core_expbuf_dmabuf(struct vb2_queue *q, unsigned int type,
+			   unsigned int index, unsigned int plane,
+			   unsigned int flags, struct dma_buf **dmabuf);
+
+/**
  * vb2_core_expbuf() - Export a buffer as a file descriptor.
  * @q:		pointer to &struct vb2_queue with videobuf2 queue.
  * @fd:		pointer to the file descriptor associated with DMABUF
diff -Nur --no-dereference linux-5.10.52-orig/include/soc/bcm2835/raspberrypi-firmware.h linux-5.10.52-v7l+/include/soc/bcm2835/raspberrypi-firmware.h
--- linux-5.10.52-orig/include/soc/bcm2835/raspberrypi-firmware.h	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/include/soc/bcm2835/raspberrypi-firmware.h	2021-07-25 16:46:34.537811870 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:12 @
 #include <linux/types.h>
 #include <linux/of_device.h>
 
+#define RPI_FIRMWARE_CHAN_FB		1
+
 struct rpi_firmware;
 
 enum rpi_firmware_property_status {
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:41 @
 enum rpi_firmware_property_tag {
 	RPI_FIRMWARE_PROPERTY_END =                           0,
 	RPI_FIRMWARE_GET_FIRMWARE_REVISION =                  0x00000001,
+	RPI_FIRMWARE_GET_FIRMWARE_VARIANT =                   0x00000002,
+	RPI_FIRMWARE_GET_FIRMWARE_HASH =                      0x00000003,
 
 	RPI_FIRMWARE_SET_CURSOR_INFO =                        0x00008010,
 	RPI_FIRMWARE_SET_CURSOR_STATE =                       0x00008011,
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:78 @
 	RPI_FIRMWARE_GET_DISPMANX_RESOURCE_MEM_HANDLE =       0x00030014,
 	RPI_FIRMWARE_GET_EDID_BLOCK =                         0x00030020,
 	RPI_FIRMWARE_GET_CUSTOMER_OTP =                       0x00030021,
+	RPI_FIRMWARE_GET_EDID_BLOCK_DISPLAY =                 0x00030023,
 	RPI_FIRMWARE_GET_DOMAIN_STATE =                       0x00030030,
 	RPI_FIRMWARE_GET_THROTTLED =                          0x00030046,
 	RPI_FIRMWARE_GET_CLOCK_MEASURED =                     0x00030047,
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:97 @
 	RPI_FIRMWARE_GET_PERIPH_REG =                         0x00030045,
 	RPI_FIRMWARE_SET_PERIPH_REG =                         0x00038045,
 	RPI_FIRMWARE_GET_POE_HAT_VAL =                        0x00030049,
-	RPI_FIRMWARE_SET_POE_HAT_VAL =                        0x00030050,
+	RPI_FIRMWARE_SET_POE_HAT_VAL =                        0x00038049,
+	RPI_FIRMWARE_SET_POE_HAT_VAL_OLD =                    0x00030050,
 	RPI_FIRMWARE_NOTIFY_XHCI_RESET =                      0x00030058,
+	RPI_FIRMWARE_GET_REBOOT_FLAGS =                       0x00030064,
+	RPI_FIRMWARE_SET_REBOOT_FLAGS =                       0x00038064,
+	RPI_FIRMWARE_NOTIFY_DISPLAY_DONE =                    0x00030066,
 
 	/* Dispmanx TAGS */
 	RPI_FIRMWARE_FRAMEBUFFER_ALLOCATE =                   0x00040001,
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:116 @
 	RPI_FIRMWARE_FRAMEBUFFER_GET_VIRTUAL_OFFSET =         0x00040009,
 	RPI_FIRMWARE_FRAMEBUFFER_GET_OVERSCAN =               0x0004000a,
 	RPI_FIRMWARE_FRAMEBUFFER_GET_PALETTE =                0x0004000b,
+	RPI_FIRMWARE_FRAMEBUFFER_GET_LAYER =                  0x0004000c,
+	RPI_FIRMWARE_FRAMEBUFFER_GET_TRANSFORM =              0x0004000d,
+	RPI_FIRMWARE_FRAMEBUFFER_GET_VSYNC =                  0x0004000e,
 	RPI_FIRMWARE_FRAMEBUFFER_GET_TOUCHBUF =               0x0004000f,
 	RPI_FIRMWARE_FRAMEBUFFER_GET_GPIOVIRTBUF =            0x00040010,
 	RPI_FIRMWARE_FRAMEBUFFER_RELEASE =                    0x00048001,
+	RPI_FIRMWARE_FRAMEBUFFER_GET_DISPLAY_ID =             0x00040016,
+	RPI_FIRMWARE_FRAMEBUFFER_SET_DISPLAY_NUM =            0x00048013,
+	RPI_FIRMWARE_FRAMEBUFFER_GET_NUM_DISPLAYS =           0x00040013,
+	RPI_FIRMWARE_FRAMEBUFFER_GET_DISPLAY_SETTINGS =       0x00040014,
 	RPI_FIRMWARE_FRAMEBUFFER_TEST_PHYSICAL_WIDTH_HEIGHT = 0x00044003,
 	RPI_FIRMWARE_FRAMEBUFFER_TEST_VIRTUAL_WIDTH_HEIGHT =  0x00044004,
 	RPI_FIRMWARE_FRAMEBUFFER_TEST_DEPTH =                 0x00044005,
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:134 @
 	RPI_FIRMWARE_FRAMEBUFFER_TEST_VIRTUAL_OFFSET =        0x00044009,
 	RPI_FIRMWARE_FRAMEBUFFER_TEST_OVERSCAN =              0x0004400a,
 	RPI_FIRMWARE_FRAMEBUFFER_TEST_PALETTE =               0x0004400b,
+	RPI_FIRMWARE_FRAMEBUFFER_TEST_LAYER =                 0x0004400c,
+	RPI_FIRMWARE_FRAMEBUFFER_TEST_TRANSFORM =             0x0004400d,
 	RPI_FIRMWARE_FRAMEBUFFER_TEST_VSYNC =                 0x0004400e,
 	RPI_FIRMWARE_FRAMEBUFFER_SET_PHYSICAL_WIDTH_HEIGHT =  0x00048003,
 	RPI_FIRMWARE_FRAMEBUFFER_SET_VIRTUAL_WIDTH_HEIGHT =   0x00048004,
 	RPI_FIRMWARE_FRAMEBUFFER_SET_DEPTH =                  0x00048005,
 	RPI_FIRMWARE_FRAMEBUFFER_SET_PIXEL_ORDER =            0x00048006,
 	RPI_FIRMWARE_FRAMEBUFFER_SET_ALPHA_MODE =             0x00048007,
+	RPI_FIRMWARE_FRAMEBUFFER_SET_PITCH =                  0x00048008,
 	RPI_FIRMWARE_FRAMEBUFFER_SET_VIRTUAL_OFFSET =         0x00048009,
 	RPI_FIRMWARE_FRAMEBUFFER_SET_OVERSCAN =               0x0004800a,
 	RPI_FIRMWARE_FRAMEBUFFER_SET_PALETTE =                0x0004800b,
+
 	RPI_FIRMWARE_FRAMEBUFFER_SET_TOUCHBUF =               0x0004801f,
 	RPI_FIRMWARE_FRAMEBUFFER_SET_GPIOVIRTBUF =            0x00048020,
 	RPI_FIRMWARE_FRAMEBUFFER_SET_VSYNC =                  0x0004800e,
+	RPI_FIRMWARE_FRAMEBUFFER_SET_LAYER =                  0x0004800c,
+	RPI_FIRMWARE_FRAMEBUFFER_SET_TRANSFORM =              0x0004800d,
 	RPI_FIRMWARE_FRAMEBUFFER_SET_BACKLIGHT =              0x0004800f,
 
 	RPI_FIRMWARE_VCHIQ_INIT =                             0x00048010,
 
+	RPI_FIRMWARE_SET_PLANE =                              0x00048015,
+	RPI_FIRMWARE_GET_DISPLAY_TIMING =                     0x00040017,
+	RPI_FIRMWARE_SET_TIMING =                             0x00048017,
+	RPI_FIRMWARE_GET_DISPLAY_CFG =                        0x00040018,
+	RPI_FIRMWARE_SET_DISPLAY_POWER =		      0x00048019,
 	RPI_FIRMWARE_GET_COMMAND_LINE =                       0x00050001,
 	RPI_FIRMWARE_GET_DMA_CHANNELS =                       0x00060001,
 };
 
+#define GET_DISPLAY_SETTINGS_PAYLOAD_SIZE 64
+
 #if IS_ENABLED(CONFIG_RASPBERRYPI_FIRMWARE)
 int rpi_firmware_property(struct rpi_firmware *fw,
 			  u32 tag, void *data, size_t len);
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:191 @
 	return NULL;
 }
 #endif
+int rpi_firmware_transaction(struct rpi_firmware *fw, u32 chan, u32 data);
 
 #endif /* __SOC_RASPBERRY_FIRMWARE_H__ */
diff -Nur --no-dereference linux-5.10.52-orig/include/sound/hdmi-codec.h linux-5.10.52-v7l+/include/sound/hdmi-codec.h
--- linux-5.10.52-orig/include/sound/hdmi-codec.h	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/include/sound/hdmi-codec.h	2021-07-25 16:46:34.607810697 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:37 @
 	unsigned int frame_clk_inv:1;
 	unsigned int bit_clk_master:1;
 	unsigned int frame_clk_master:1;
+	/* bit_fmt could be standard PCM format or
+	 * IEC958 encoded format. ALSA IEC958 plugin will pass
+	 * IEC958_SUBFRAME format to the underneath driver.
+	 */
+	snd_pcm_format_t bit_fmt;
 };
 
 /*
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:68 @
 
 	/*
 	 * Configures HDMI-encoder for audio stream.
-	 * Mandatory
+	 * Having either prepare or hw_params is mandatory.
 	 */
 	int (*hw_params)(struct device *dev, void *data,
 			 struct hdmi_codec_daifmt *fmt,
 			 struct hdmi_codec_params *hparms);
 
 	/*
+	 * Configures HDMI-encoder for audio stream. Can be called
+	 * multiple times for each setup.
+	 *
+	 * Having either prepare or hw_params is mandatory.
+	 */
+	int (*prepare)(struct device *dev, void *data,
+		       struct hdmi_codec_daifmt *fmt,
+		       struct hdmi_codec_params *hparms);
+
+	/*
 	 * Shuts down the audio stream.
 	 * Mandatory
 	 */
diff -Nur --no-dereference linux-5.10.52-orig/include/sound/pcm_iec958.h linux-5.10.52-v7l+/include/sound/pcm_iec958.h
--- linux-5.10.52-orig/include/sound/pcm_iec958.h	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/include/sound/pcm_iec958.h	2021-07-25 16:46:34.607810697 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:7 @
 
 #include <linux/types.h>
 
+int snd_pcm_create_iec958_consumer_default(u8 *cs, size_t len);
+
+int snd_pcm_fill_iec958_consumer(struct snd_pcm_runtime *runtime, u8 *cs,
+				 size_t len);
+
+int snd_pcm_fill_iec958_consumer_hw_params(struct snd_pcm_hw_params *params,
+					   u8 *cs, size_t len);
+
 int snd_pcm_create_iec958_consumer(struct snd_pcm_runtime *runtime, u8 *cs,
 	size_t len);
 
diff -Nur --no-dereference linux-5.10.52-orig/include/uapi/drm/drm_fourcc.h linux-5.10.52-v7l+/include/uapi/drm/drm_fourcc.h
--- linux-5.10.52-orig/include/uapi/drm/drm_fourcc.h	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/include/uapi/drm/drm_fourcc.h	2021-07-25 16:46:34.737808517 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:291 @
 #define DRM_FORMAT_Q401		fourcc_code('Q', '4', '0', '1')
 
 /*
+ * 2 plane YCbCr MSB aligned, 3 pixels packed into 4 bytes.
+ * index 0 = Y plane, [31:0] x:Y2:Y1:Y0 2:10:10:10 little endian
+ * index 1 = Cr:Cb plane, [63:0] x:Cr2:Cb2:Cr1:x:Cb1:Cr0:Cb0 [2:10:10:10:2:10:10:10] little endian
+ */
+#define DRM_FORMAT_P030		fourcc_code('P', '0', '3', '0') /* 2x2 subsampled Cr:Cb plane 10 bits per channel packed */
+
+/*
  * 3 plane YCbCr
  * index 0: Y plane, [7:0] Y
  * index 1: Cb plane, [7:0] Cb
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:787 @
  * and UV.  Some SAND-using hardware stores UV in a separate tiled
  * image from Y to reduce the column height, which is not supported
  * with these modifiers.
+ *
+ * The DRM_FORMAT_MOD_BROADCOM_SAND128_COL_HEIGHT modifier is also
+ * supported for DRM_FORMAT_P030 where the columns remain as 128 bytes
+ * wide, but as this is a 10 bpp format that translates to 96 pixels.
  */
 
 #define DRM_FORMAT_MOD_BROADCOM_SAND32_COL_HEIGHT(v) \
diff -Nur --no-dereference linux-5.10.52-orig/include/uapi/drm/drm_mode.h linux-5.10.52-v7l+/include/uapi/drm/drm_mode.h
--- linux-5.10.52-orig/include/uapi/drm/drm_mode.h	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/include/uapi/drm/drm_mode.h	2021-07-25 16:46:34.737808517 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:370 @
 #define DRM_MODE_CONNECTOR_DPI		17
 #define DRM_MODE_CONNECTOR_WRITEBACK	18
 #define DRM_MODE_CONNECTOR_SPI		19
+#define DRM_MODE_CONNECTOR_USB		20
 
 struct drm_mode_get_connector {
 
diff -Nur --no-dereference linux-5.10.52-orig/include/uapi/linux/bcm2835-isp.h linux-5.10.52-v7l+/include/uapi/linux/bcm2835-isp.h
--- linux-5.10.52-orig/include/uapi/linux/bcm2835-isp.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/include/uapi/linux/bcm2835-isp.h	2021-07-25 16:46:34.767808015 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/* SPDX-License-Identifier: ((GPL-2.0+ WITH Linux-syscall-note) OR BSD-3-Clause) */
+/*
+ * bcm2835-isp.h
+ *
+ * BCM2835 ISP driver - user space header file.
+ *
+ * Copyright © 2019-2020 Raspberry Pi (Trading) Ltd.
+ *
+ * Author: Naushir Patuck (naush@raspberrypi.com)
+ *
+ */
+
+#ifndef __BCM2835_ISP_H_
+#define __BCM2835_ISP_H_
+
+#include <linux/v4l2-controls.h>
+
+#define V4L2_CID_USER_BCM2835_ISP_CC_MATRIX	\
+				(V4L2_CID_USER_BCM2835_ISP_BASE + 0x0001)
+#define V4L2_CID_USER_BCM2835_ISP_LENS_SHADING	\
+				(V4L2_CID_USER_BCM2835_ISP_BASE + 0x0002)
+#define V4L2_CID_USER_BCM2835_ISP_BLACK_LEVEL	\
+				(V4L2_CID_USER_BCM2835_ISP_BASE + 0x0003)
+#define V4L2_CID_USER_BCM2835_ISP_GEQ		\
+				(V4L2_CID_USER_BCM2835_ISP_BASE + 0x0004)
+#define V4L2_CID_USER_BCM2835_ISP_GAMMA		\
+				(V4L2_CID_USER_BCM2835_ISP_BASE + 0x0005)
+#define V4L2_CID_USER_BCM2835_ISP_DENOISE	\
+				(V4L2_CID_USER_BCM2835_ISP_BASE + 0x0006)
+#define V4L2_CID_USER_BCM2835_ISP_SHARPEN	\
+				(V4L2_CID_USER_BCM2835_ISP_BASE + 0x0007)
+#define V4L2_CID_USER_BCM2835_ISP_DPC		\
+				(V4L2_CID_USER_BCM2835_ISP_BASE + 0x0008)
+#define V4L2_CID_USER_BCM2835_ISP_CDN \
+				(V4L2_CID_USER_BCM2835_ISP_BASE + 0x0009)
+
+/*
+ * All structs below are directly mapped onto the equivalent structs in
+ * drivers/staging/vc04_services/vchiq-mmal/mmal-parameters.h
+ * for convenience.
+ */
+
+/**
+ * struct bcm2835_isp_rational - Rational value type.
+ *
+ * @num:	Numerator.
+ * @den:	Denominator.
+ */
+struct bcm2835_isp_rational {
+	__s32 num;
+	__u32 den;
+};
+
+/**
+ * struct bcm2835_isp_ccm - Colour correction matrix.
+ *
+ * @ccm:	3x3 correction matrix coefficients.
+ * @offsets:	1x3 correction offsets.
+ */
+struct bcm2835_isp_ccm {
+	struct bcm2835_isp_rational ccm[3][3];
+	__s32 offsets[3];
+};
+
+/**
+ * struct bcm2835_isp_custom_ccm - Custom CCM applied with the
+ *				   V4L2_CID_USER_BCM2835_ISP_CC_MATRIX ctrl.
+ *
+ * @enabled:	Enable custom CCM.
+ * @ccm:	Custom CCM coefficients and offsets.
+ */
+struct bcm2835_isp_custom_ccm {
+	__u32 enabled;
+	struct bcm2835_isp_ccm ccm;
+};
+
+/**
+ * enum bcm2835_isp_gain_format - format of the gains in the lens shading
+ *				  tables used with the
+ *				  V4L2_CID_USER_BCM2835_ISP_LENS_SHADING ctrl.
+ *
+ * @GAIN_FORMAT_U0P8_1:		Gains are u0.8 format, starting at 1.0
+ * @GAIN_FORMAT_U1P7_0:		Gains are u1.7 format, starting at 0.0
+ * @GAIN_FORMAT_U1P7_1:		Gains are u1.7 format, starting at 1.0
+ * @GAIN_FORMAT_U2P6_0:		Gains are u2.6 format, starting at 0.0
+ * @GAIN_FORMAT_U2P6_1:		Gains are u2.6 format, starting at 1.0
+ * @GAIN_FORMAT_U3P5_0:		Gains are u3.5 format, starting at 0.0
+ * @GAIN_FORMAT_U3P5_1:		Gains are u3.5 format, starting at 1.0
+ * @GAIN_FORMAT_U4P10:		Gains are u4.10 format, starting at 0.0
+ */
+enum bcm2835_isp_gain_format {
+	GAIN_FORMAT_U0P8_1 = 0,
+	GAIN_FORMAT_U1P7_0 = 1,
+	GAIN_FORMAT_U1P7_1 = 2,
+	GAIN_FORMAT_U2P6_0 = 3,
+	GAIN_FORMAT_U2P6_1 = 4,
+	GAIN_FORMAT_U3P5_0 = 5,
+	GAIN_FORMAT_U3P5_1 = 6,
+	GAIN_FORMAT_U4P10  = 7,
+};
+
+/**
+ * struct bcm2835_isp_lens_shading - Lens shading tables supplied with the
+ *				     V4L2_CID_USER_BCM2835_ISP_LENS_SHADING
+ *				     ctrl.
+ *
+ * @enabled:		Enable lens shading.
+ * @grid_cell_size:	Size of grid cells in samples (16, 32, 64, 128 or 256).
+ * @grid_width:		Width of lens shading tables in grid cells.
+ * @grid_stride:	Row to row distance (in grid cells) between grid cells
+ *			in the same horizontal location.
+ * @grid_height:	Height of lens shading tables in grid cells.
+ * @dmabuf:		dmabuf file handle containing the table.
+ * @ref_transform:	Reference transform - unsupported, please pass zero.
+ * @corner_sampled:	Whether the gains are sampled at the corner points
+ *			of the grid cells or in the cell centres.
+ * @gain_format:	Format of the gains (see enum &bcm2835_isp_gain_format).
+ */
+struct bcm2835_isp_lens_shading {
+	__u32 enabled;
+	__u32 grid_cell_size;
+	__u32 grid_width;
+	__u32 grid_stride;
+	__u32 grid_height;
+	__s32 dmabuf;
+	__u32 ref_transform;
+	__u32 corner_sampled;
+	__u32 gain_format;
+};
+
+/**
+ * struct bcm2835_isp_black_level - Sensor black level set with the
+ *				    V4L2_CID_USER_BCM2835_ISP_BLACK_LEVEL ctrl.
+ *
+ * @enabled:		Enable black level.
+ * @black_level_r:	Black level for red channel.
+ * @black_level_g:	Black level for green channels.
+ * @black_level_b:	Black level for blue channel.
+ */
+struct bcm2835_isp_black_level {
+	__u32 enabled;
+	__u16 black_level_r;
+	__u16 black_level_g;
+	__u16 black_level_b;
+	__u8 padding[2]; /* Unused */
+};
+
+/**
+ * struct bcm2835_isp_geq - Green equalisation parameters set with the
+ *			    V4L2_CID_USER_BCM2835_ISP_GEQ ctrl.
+ *
+ * @enabled:	Enable green equalisation.
+ * @offset:	Fixed offset of the green equalisation threshold.
+ * @slope:	Slope of the green equalisation threshold.
+ */
+struct bcm2835_isp_geq {
+	__u32 enabled;
+	__u32 offset;
+	struct bcm2835_isp_rational slope;
+};
+
+#define BCM2835_NUM_GAMMA_PTS 33
+
+/**
+ * struct bcm2835_isp_gamma - Gamma parameters set with the
+ *			      V4L2_CID_USER_BCM2835_ISP_GAMMA ctrl.
+ *
+ * @enabled:	Enable gamma adjustment.
+ * @X:		X values of the points defining the gamma curve.
+ *		Values should be scaled to 16 bits.
+ * @Y:		Y values of the points defining the gamma curve.
+ *		Values should be scaled to 16 bits.
+ */
+struct bcm2835_isp_gamma {
+	__u32 enabled;
+	__u16 x[BCM2835_NUM_GAMMA_PTS];
+	__u16 y[BCM2835_NUM_GAMMA_PTS];
+};
+
+/**
+ * enum bcm2835_isp_cdn_mode - Mode of operation for colour denoise.
+ *
+ * @CDN_MODE_FAST:		Fast (but lower quality) colour denoise
+ *				algorithm, typically used for video recording.
+ * @CDN_HIGH_QUALITY:		High quality (but slower) colour denoise
+ *				algorithm, typically used for stills capture.
+ */
+enum bcm2835_isp_cdn_mode {
+	CDN_MODE_FAST = 0,
+	CDN_MODE_HIGH_QUALITY = 1,
+};
+
+/**
+ * struct bcm2835_isp_cdn - Colour denoise parameters set with the
+ *			    V4L2_CID_USER_BCM2835_ISP_CDN ctrl.
+ *
+ * @enabled:	Enable colour denoise.
+ * @mode:	Colour denoise operating mode (see enum &bcm2835_isp_cdn_mode)
+ */
+struct bcm2835_isp_cdn {
+	__u32 enabled;
+	__u32 mode;
+};
+
+/**
+ * struct bcm2835_isp_denoise - Denoise parameters set with the
+ *				V4L2_CID_USER_BCM2835_ISP_DENOISE ctrl.
+ *
+ * @enabled:	Enable denoise.
+ * @constant:	Fixed offset of the noise threshold.
+ * @slope:	Slope of the noise threshold.
+ * @strength:	Denoise strength between 0.0 (off) and 1.0 (maximum).
+ */
+struct bcm2835_isp_denoise {
+	__u32 enabled;
+	__u32 constant;
+	struct bcm2835_isp_rational slope;
+	struct bcm2835_isp_rational strength;
+};
+
+/**
+ * struct bcm2835_isp_sharpen - Sharpen parameters set with the
+ *				V4L2_CID_USER_BCM2835_ISP_SHARPEN ctrl.
+ *
+ * @enabled:	Enable sharpening.
+ * @threshold:	Threshold at which to start sharpening pixels.
+ * @strength:	Strength with which pixel sharpening increases.
+ * @limit:	Limit to the amount of sharpening applied.
+ */
+struct bcm2835_isp_sharpen {
+	__u32 enabled;
+	struct bcm2835_isp_rational threshold;
+	struct bcm2835_isp_rational strength;
+	struct bcm2835_isp_rational limit;
+};
+
+/**
+ * enum bcm2835_isp_dpc_mode - defective pixel correction (DPC) strength.
+ *
+ * @DPC_MODE_OFF:		No DPC.
+ * @DPC_MODE_NORMAL:		Normal DPC.
+ * @DPC_MODE_STRONG:		Strong DPC.
+ */
+enum bcm2835_isp_dpc_mode {
+	DPC_MODE_OFF = 0,
+	DPC_MODE_NORMAL = 1,
+	DPC_MODE_STRONG = 2,
+};
+
+/**
+ * struct bcm2835_isp_dpc - Defective pixel correction (DPC) parameters set
+ *			    with the V4L2_CID_USER_BCM2835_ISP_DPC ctrl.
+ *
+ * @enabled:	Enable DPC.
+ * @strength:	DPC strength (see enum &bcm2835_isp_dpc_mode).
+ */
+struct bcm2835_isp_dpc {
+	__u32 enabled;
+	__u32 strength;
+};
+
+/*
+ * ISP statistics structures.
+ *
+ * The bcm2835_isp_stats structure is generated at the output of the
+ * statistics node.  Note that this does not directly map onto the statistics
+ * output of the ISP HW.  Instead, the MMAL firmware code maps the HW statistics
+ * to the bcm2835_isp_stats structure.
+ */
+#define DEFAULT_AWB_REGIONS_X 16
+#define DEFAULT_AWB_REGIONS_Y 12
+
+#define NUM_HISTOGRAMS 2
+#define NUM_HISTOGRAM_BINS 128
+#define AWB_REGIONS (DEFAULT_AWB_REGIONS_X * DEFAULT_AWB_REGIONS_Y)
+#define FLOATING_REGIONS 16
+#define AGC_REGIONS 16
+#define FOCUS_REGIONS 12
+
+/**
+ * struct bcm2835_isp_stats_hist - Histogram statistics
+ *
+ * @r_hist:	Red channel histogram.
+ * @g_hist:	Combined green channel histogram.
+ * @b_hist:	Blue channel histogram.
+ */
+struct bcm2835_isp_stats_hist {
+	__u32 r_hist[NUM_HISTOGRAM_BINS];
+	__u32 g_hist[NUM_HISTOGRAM_BINS];
+	__u32 b_hist[NUM_HISTOGRAM_BINS];
+};
+
+/**
+ * struct bcm2835_isp_stats_region - Region sums.
+ *
+ * @counted:	The number of 2x2 bayer tiles accumulated.
+ * @notcounted:	The number of 2x2 bayer tiles not accumulated.
+ * @r_sum:	Total sum of counted pixels in the red channel for a region.
+ * @g_sum:	Total sum of counted pixels in the green channel for a region.
+ * @b_sum:	Total sum of counted pixels in the blue channel for a region.
+ */
+struct bcm2835_isp_stats_region {
+	__u32 counted;
+	__u32 notcounted;
+	__u64 r_sum;
+	__u64 g_sum;
+	__u64 b_sum;
+};
+
+/**
+ * struct bcm2835_isp_stats_focus - Focus statistics.
+ *
+ * @contrast_val:	Focus measure - accumulated output of the focus filter.
+ *			In the first dimension, index [0] counts pixels below a
+ *			preset threshold, and index [1] counts pixels above the
+ *			threshold.  In the second dimension, index [0] uses the
+ *			first predefined filter, and index [1] uses the second
+ *			predefined filter.
+ * @contrast_val_num:	The number of counted pixels in the above accumulation.
+ */
+struct bcm2835_isp_stats_focus {
+	__u64 contrast_val[2][2];
+	__u32 contrast_val_num[2][2];
+};
+
+/**
+ * struct bcm2835_isp_stats - ISP statistics.
+ *
+ * @version:		Version of the bcm2835_isp_stats structure.
+ * @size:		Size of the bcm2835_isp_stats structure.
+ * @hist:		Histogram statistics for the entire image.
+ * @awb_stats:		Statistics for the regions defined for AWB calculations.
+ * @floating_stats:	Statistics for arbitrarily placed (floating) regions.
+ * @agc_stats:		Statistics for the regions defined for AGC calculations.
+ * @focus_stats:	Focus filter statistics for the focus regions.
+ */
+struct bcm2835_isp_stats {
+	__u32 version;
+	__u32 size;
+	struct bcm2835_isp_stats_hist hist[NUM_HISTOGRAMS];
+	struct bcm2835_isp_stats_region awb_stats[AWB_REGIONS];
+	struct bcm2835_isp_stats_region floating_stats[FLOATING_REGIONS];
+	struct bcm2835_isp_stats_region agc_stats[AGC_REGIONS];
+	struct bcm2835_isp_stats_focus focus_stats[FOCUS_REGIONS];
+};
+
+#endif /* __BCM2835_ISP_H_ */
diff -Nur --no-dereference linux-5.10.52-orig/include/uapi/linux/fb.h linux-5.10.52-v7l+/include/uapi/linux/fb.h
--- linux-5.10.52-orig/include/uapi/linux/fb.h	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/include/uapi/linux/fb.h	2021-07-25 16:46:34.827807009 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:38 @
 #define FBIOPUT_MODEINFO        0x4617
 #define FBIOGET_DISPINFO        0x4618
 #define FBIO_WAITFORVSYNC	_IOW('F', 0x20, __u32)
+/*
+ * HACK: use 'z' in order not to clash with any other ioctl numbers which might
+ * be concurrently added to the mainline kernel
+ */
+#define FBIOCOPYAREA		_IOW('z', 0x21, struct fb_copyarea)
+#define FBIODMACOPY 		_IOW('z', 0x22, struct fb_dmacopy)
 
 #define FB_TYPE_PACKED_PIXELS		0	/* Packed Pixels	*/
 #define FB_TYPE_PLANES			1	/* Non interleaved planes */
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:357 @
 	__u32 sy;
 };
 
+struct fb_dmacopy {
+	void *dst;
+	__u32 src;
+	__u32 length;
+};
+
 struct fb_fillrect {
 	__u32 dx;	/* screen-relative */
 	__u32 dy;
diff -Nur --no-dereference linux-5.10.52-orig/include/uapi/linux/media-bus-format.h linux-5.10.52-v7l+/include/uapi/linux/media-bus-format.h
--- linux-5.10.52-orig/include/uapi/linux/media-bus-format.h	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/include/uapi/linux/media-bus-format.h	2021-07-25 16:46:34.877806170 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:37 @
 
 #define MEDIA_BUS_FMT_FIXED			0x0001
 
-/* RGB - next is	0x101d */
+/* RGB - next is	0x1020 */
 #define MEDIA_BUS_FMT_RGB444_1X12		0x1016
 #define MEDIA_BUS_FMT_RGB444_2X8_PADHI_BE	0x1001
 #define MEDIA_BUS_FMT_RGB444_2X8_PADHI_LE	0x1002
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:48 @
 #define MEDIA_BUS_FMT_BGR565_2X8_LE		0x1006
 #define MEDIA_BUS_FMT_RGB565_2X8_BE		0x1007
 #define MEDIA_BUS_FMT_RGB565_2X8_LE		0x1008
+#define MEDIA_BUS_FMT_BGR666_1X18		0x101f
 #define MEDIA_BUS_FMT_RGB666_1X18		0x1009
 #define MEDIA_BUS_FMT_RBG888_1X24		0x100e
+#define MEDIA_BUS_FMT_BGR666_1X24_CPADHI	0x101e
 #define MEDIA_BUS_FMT_RGB666_1X24_CPADHI	0x1015
 #define MEDIA_BUS_FMT_RGB666_1X7X3_SPWG		0x1010
 #define MEDIA_BUS_FMT_BGR888_1X24		0x1013
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:161 @
 /* HSV - next is	0x6002 */
 #define MEDIA_BUS_FMT_AHSV8888_1X32		0x6001
 
+/* Sensor ancillary metadata formats - next is 0x7002 */
+#define MEDIA_BUS_FMT_SENSOR_DATA		0x7001
+
 #endif /* __LINUX_MEDIA_BUS_FORMAT_H */
diff -Nur --no-dereference linux-5.10.52-orig/include/uapi/linux/v4l2-controls.h linux-5.10.52-v7l+/include/uapi/linux/v4l2-controls.h
--- linux-5.10.52-orig/include/uapi/linux/v4l2-controls.h	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/include/uapi/linux/v4l2-controls.h	2021-07-25 16:46:35.037803488 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:201 @
  */
 #define V4L2_CID_USER_ATMEL_ISC_BASE		(V4L2_CID_USER_BASE + 0x10c0)
 
+/* The base for the bcm2835-isp driver controls.
+ * We reserve 16 controls for this driver. */
+#define V4L2_CID_USER_BCM2835_ISP_BASE		(V4L2_CID_USER_BASE + 0x10e0)
+
 /* MPEG-class control IDs */
 /* The MPEG controls are applicable to all codec controls
  * and the 'MPEG' part of the define is historical */
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:893 @
 	V4L2_WHITE_BALANCE_FLASH		= 7,
 	V4L2_WHITE_BALANCE_CLOUDY		= 8,
 	V4L2_WHITE_BALANCE_SHADE		= 9,
+	V4L2_WHITE_BALANCE_GREYWORLD		= 10,
 };
 
 #define V4L2_CID_WIDE_DYNAMIC_RANGE		(V4L2_CID_CAMERA_CLASS_BASE+21)
diff -Nur --no-dereference linux-5.10.52-orig/include/uapi/linux/videodev2.h linux-5.10.52-v7l+/include/uapi/linux/videodev2.h
--- linux-5.10.52-orig/include/uapi/linux/videodev2.h	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/include/uapi/linux/videodev2.h	2021-07-25 16:46:35.037803488 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:85 @
 	((__u32)(a) | ((__u32)(b) << 8) | ((__u32)(c) << 16) | ((__u32)(d) << 24))
 #define v4l2_fourcc_be(a, b, c, d)	(v4l2_fourcc(a, b, c, d) | (1U << 31))
 
+#define V4L2_FOURCC_CONV "%c%c%c%c%s"
+#define V4L2_FOURCC_CONV_ARGS(fourcc) \
+	(fourcc) & 0x7f, ((fourcc) >> 8) & 0x7f, ((fourcc) >> 16) & 0x7f, \
+	((fourcc) >> 24) & 0x7f, (fourcc) & BIT(31) ? "-BE" : ""
+
 /*
  *	E N U M S
  */
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:583 @
 /* Grey bit-packed formats */
 #define V4L2_PIX_FMT_Y10BPACK    v4l2_fourcc('Y', '1', '0', 'B') /* 10  Greyscale bit-packed */
 #define V4L2_PIX_FMT_Y10P    v4l2_fourcc('Y', '1', '0', 'P') /* 10  Greyscale, MIPI RAW10 packed */
+#define V4L2_PIX_FMT_Y12P    v4l2_fourcc('Y', '1', '2', 'P') /* 12  Greyscale, MIPI RAW12 packed */
+#define V4L2_PIX_FMT_Y14P    v4l2_fourcc('Y', '1', '4', 'P') /* 14  Greyscale, MIPI RAW12 packed */
 
 /* Palette formats */
 #define V4L2_PIX_FMT_PAL8    v4l2_fourcc('P', 'A', 'L', '8') /*  8  8-bit palette */
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:750 @
 #define V4L2_PIX_FMT_INZI     v4l2_fourcc('I', 'N', 'Z', 'I') /* Intel Planar Greyscale 10-bit and Depth 16-bit */
 #define V4L2_PIX_FMT_SUNXI_TILED_NV12 v4l2_fourcc('S', 'T', '1', '2') /* Sunxi Tiled NV12 Format */
 #define V4L2_PIX_FMT_CNF4     v4l2_fourcc('C', 'N', 'F', '4') /* Intel 4-bit packed depth confidence information */
+#define V4L2_PIX_FMT_NV12_COL128 v4l2_fourcc('N', 'C', '1', '2') /* 12  Y/CbCr 4:2:0 128 pixel wide column */
+#define V4L2_PIX_FMT_NV12_10_COL128 v4l2_fourcc('N', 'C', '3', '0')
+								/* Y/CbCr 4:2:0 10bpc, 3x10 packed as 4 bytes in
+								 * a 128 bytes / 96 pixel wide column */
 
 /* 10bit raw bayer packed, 32 bytes for every 25 pixels, last LSB 6 bits unused */
 #define V4L2_PIX_FMT_IPU3_SBGGR10	v4l2_fourcc('i', 'p', '3', 'b') /* IPU3 packed 10-bit BGGR bayer */
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:783 @
 #define V4L2_META_FMT_UVC         v4l2_fourcc('U', 'V', 'C', 'H') /* UVC Payload Header metadata */
 #define V4L2_META_FMT_D4XX        v4l2_fourcc('D', '4', 'X', 'X') /* D4XX Payload Header metadata */
 #define V4L2_META_FMT_VIVID	  v4l2_fourcc('V', 'I', 'V', 'D') /* Vivid Metadata */
+#define V4L2_META_FMT_SENSOR_DATA v4l2_fourcc('S', 'E', 'N', 'S') /* Sensor Ancillary metadata */
+#define V4L2_META_FMT_BCM2835_ISP_STATS v4l2_fourcc('B', 'S', 'T', 'A') /* BCM2835 ISP image statistics output */
 
 /* priv field value to indicates that subsequent fields are valid. */
 #define V4L2_PIX_FMT_PRIV_MAGIC		0xfeedcafe
diff -Nur --no-dereference linux-5.10.52-orig/kernel/cgroup/cgroup.c linux-5.10.52-v7l+/kernel/cgroup/cgroup.c
--- linux-5.10.52-orig/kernel/cgroup/cgroup.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/kernel/cgroup/cgroup.c	2021-07-25 16:46:35.217800470 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:5668 @
 	return 0;
 }
 
+static u16 cgroup_enable_mask __initdata;
+static int __init cgroup_disable(char *str);
+
 /**
  * cgroup_init - cgroup initialization
  *
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:5709 @
 
 	mutex_unlock(&cgroup_mutex);
 
+	/*
+	 * Apply an implicit disable, knowing that an explicit enable will
+	 * prevent if from doing anything.
+	 */
+	cgroup_disable("memory");
+
 	for_each_subsys(ss, ssid) {
 		if (ss->early_init) {
 			struct cgroup_subsys_state *css =
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:6252 @
 			    strcmp(token, ss->legacy_name))
 				continue;
 
+			/* An explicit cgroup_enable overrides a disable */
+			if (cgroup_enable_mask & (1 << i))
+				continue;
+
 			static_branch_disable(cgroup_subsys_enabled_key[i]);
 			pr_info("Disabling %s control group subsystem\n",
 				ss->name);
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:6265 @
 }
 __setup("cgroup_disable=", cgroup_disable);
 
+static int __init cgroup_enable(char *str)
+{
+	struct cgroup_subsys *ss;
+	char *token;
+	int i;
+
+	while ((token = strsep(&str, ",")) != NULL) {
+		if (!*token)
+			continue;
+
+		for_each_subsys(ss, i) {
+			if (strcmp(token, ss->name) &&
+			    strcmp(token, ss->legacy_name))
+				continue;
+
+			cgroup_enable_mask |= 1 << i;
+			static_branch_enable(cgroup_subsys_enabled_key[i]);
+			pr_info("Enabling %s control group subsystem\n",
+				ss->name);
+		}
+	}
+	return 1;
+}
+__setup("cgroup_enable=", cgroup_enable);
+
 void __init __weak enable_debug_cgroup(void) { }
 
 static int __init enable_cgroup_debug(char *str)
diff -Nur --no-dereference linux-5.10.52-orig/kernel/resource.c linux-5.10.52-v7l+/kernel/resource.c
--- linux-5.10.52-orig/kernel/resource.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/kernel/resource.c	2021-07-25 16:46:35.577794435 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:217 @
 {
 	struct resource *tmp, **p, *chd;
 
+	if (!old->parent) {
+		WARN(old->sibling, "sibling but no parent");
+		if (old->sibling)
+			return -EINVAL;
+		return 0;
+	}
 	p = &old->parent->child;
 	for (;;) {
 		tmp = *p;
diff -Nur --no-dereference linux-5.10.52-orig/MAINTAINERS linux-5.10.52-v7l+/MAINTAINERS
--- linux-5.10.52-orig/MAINTAINERS	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/MAINTAINERS	2021-07-25 16:45:34.528817953 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:3420 @
 N:	bcm216*
 N:	kona
 
+BROADCOM BCM2711 HEVC DECODER
+M:	Raspberry Pi Kernel Maintenance <kernel-list@raspberrypi.com>
+L:	linux-media@vger.kernel.org
+S:	Maintained
+F:	Documentation/devicetree/bindings/media/rpivid_hevc.jaml
+F:	drivers/staging/media/rpivid
+
+BROADCOM BCM2835 CAMERA DRIVER
+M:	Raspberry Pi Kernel Maintenance <kernel-list@raspberrypi.com>
+L:	linux-media@vger.kernel.org
+S:	Maintained
+F:	drivers/media/platform/bcm2835/
+F:	Documentation/devicetree/bindings/media/brcm,bcm2835-unicam.yaml
+
+BROADCOM BCM2835 ISP DRIVER
+M:	Raspberry Pi Kernel Maintenance <kernel-list@raspberrypi.com>
+L:	linux-media@vger.kernel.org
+S:	Maintained
+F:	Documentation/media/uapi/v4l/pixfmt-meta-bcm2835-isp-stats.rst
+F:	Documentation/media/v4l-drivers/bcm2835-isp.rst
+F:	drivers/staging/vc04_services/bcm2835-isp
+F:	include/uapi/linux/bcm2835-isp.h
+
 BROADCOM BCM47XX MIPS ARCHITECTURE
 M:	Hauke Mehrtens <hauke@hauke-m.de>
 M:	Rafał Miłecki <zajec5@gmail.com>
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:5528 @
 F:	Documentation/devicetree/bindings/display/panel/feiyang,fy07024di26a30d.yaml
 F:	drivers/gpu/drm/panel/panel-feiyang-fy07024di26a30d.c
 
+DRM DRIVER FOR GENERIC USB DISPLAY
+M:	Noralf Trønnes <noralf@tronnes.org>
+S:	Maintained
+W:	https://github.com/notro/gud/wiki
+T:	git git://anongit.freedesktop.org/drm/drm-misc
+F:	drivers/gpu/drm/gud/
+F:	include/drm/gud.h
+
 DRM DRIVER FOR GRAIN MEDIA GM12U320 PROJECTORS
 M:	Hans de Goede <hdegoede@redhat.com>
 S:	Maintained
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:16366 @
 T:	git git://linuxtv.org/media_tree.git
 F:	drivers/media/i2c/imx355.c
 
+SONY IMX477 SENSOR DRIVER
+M:	Raspberry Pi Kernel Maintenance <kernel-list@raspberrypi.com>
+L:	linux-media@vger.kernel.org
+S:	Maintained
+T:	git git://linuxtv.org/media_tree.git
+F:	Documentation/devicetree/bindings/media/i2c/imx378.yaml
+F:	Documentation/devicetree/bindings/media/i2c/imx477.yaml
+F:	drivers/media/i2c/imx477.c
+
 SONY MEMORYSTICK SUBSYSTEM
 M:	Maxim Levitsky <maximlevitsky@gmail.com>
 M:	Alex Dubov <oakad@yahoo.com>
diff -Nur --no-dereference linux-5.10.52-orig/Makefile linux-5.10.52-v7l+/Makefile
--- linux-5.10.52-orig/Makefile	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/Makefile	2021-07-25 16:45:34.528817953 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1356 @
 %.dtb: include/config/kernel.release scripts_dtc
 	$(Q)$(MAKE) $(build)=$(dtstree) $(dtstree)/$@
 
+%.dtbo: include/config/kernel.release scripts_dtc
+	$(Q)$(MAKE) $(build)=$(dtstree) $(dtstree)/$@
+
 PHONY += dtbs dtbs_install dtbs_check
 dtbs: include/config/kernel.release scripts_dtc
 	$(Q)$(MAKE) $(build)=$(dtstree)
diff -Nur --no-dereference linux-5.10.52-orig/mm/page_alloc.c linux-5.10.52-v7l+/mm/page_alloc.c
--- linux-5.10.52-orig/mm/page_alloc.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/mm/page_alloc.c	2021-07-25 16:46:36.417780353 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:8569 @
 
 	/* Make sure the range is really isolated. */
 	if (test_pages_isolated(outer_start, end, 0)) {
-		pr_info_ratelimited("%s: [%lx, %lx) PFNs busy\n",
-			__func__, outer_start, end);
 		ret = -EBUSY;
 		goto done;
 	}
diff -Nur --no-dereference linux-5.10.52-orig/mm/zswap.c linux-5.10.52-v7l+/mm/zswap.c
--- linux-5.10.52-orig/mm/zswap.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/mm/zswap.c	2021-07-25 16:46:36.517778676 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:595 @
 	return NULL;
 }
 
-static __init struct zswap_pool *__zswap_pool_create_fallback(void)
+static bool zswap_try_pool_create(void)
 {
+	struct zswap_pool *pool;
 	bool has_comp, has_zpool;
 
 	has_comp = crypto_has_comp(zswap_compressor, 0, 0);
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:633 @
 	}
 
 	if (!has_comp || !has_zpool)
-		return NULL;
+		return false;
+
+	pool = zswap_pool_create(zswap_zpool_type, zswap_compressor);
 
-	return zswap_pool_create(zswap_zpool_type, zswap_compressor);
+	if (pool) {
+		pr_info("loaded using pool %s/%s\n", pool->tfm_name,
+			zpool_get_type(pool->zpool));
+		list_add(&pool->list, &zswap_pools);
+		zswap_has_pool = true;
+	} else {
+		pr_err("pool creation failed\n");
+		zswap_enabled = false;
+	}
+
+	return zswap_enabled;
 }
 
 static void zswap_pool_destroy(struct zswap_pool *pool)
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:820 @
 static int zswap_enabled_param_set(const char *val,
 				   const struct kernel_param *kp)
 {
+	int ret;
+
 	if (zswap_init_failed) {
 		pr_err("can't enable, initialization failed\n");
 		return -ENODEV;
 	}
-	if (!zswap_has_pool && zswap_init_started) {
-		pr_err("can't enable, no pool configured\n");
-		return -ENODEV;
-	}
 
-	return param_set_bool(val, kp);
+	ret = param_set_bool(val, kp);
+	if (!ret && zswap_enabled && zswap_init_started && !zswap_has_pool)
+		if (!zswap_try_pool_create())
+			ret = -ENODEV;
+
+	return ret;
 }
 
 /*********************************
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1333 @
 **********************************/
 static int __init init_zswap(void)
 {
-	struct zswap_pool *pool;
 	int ret;
 
 	zswap_init_started = true;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1356 @
 	if (ret)
 		goto hp_fail;
 
-	pool = __zswap_pool_create_fallback();
-	if (pool) {
-		pr_info("loaded using pool %s/%s\n", pool->tfm_name,
-			zpool_get_type(pool->zpool));
-		list_add(&pool->list, &zswap_pools);
-		zswap_has_pool = true;
-	} else {
-		pr_err("pool creation failed\n");
-		zswap_enabled = false;
-	}
-
 	shrink_wq = create_workqueue("zswap-shrink");
 	if (!shrink_wq)
-		goto fallback_fail;
+		goto hp_fail;
 
 	frontswap_register_ops(&zswap_frontswap_ops);
 	if (zswap_debugfs_init())
 		pr_warn("debugfs initialization failed\n");
+
+	if (zswap_enabled)
+		zswap_try_pool_create();
+
 	return 0;
 
-fallback_fail:
-	if (pool)
-		zswap_pool_destroy(pool);
 hp_fail:
 	cpuhp_remove_state(CPUHP_MM_ZSWP_MEM_PREPARE);
 dstmem_fail:
diff -Nur --no-dereference linux-5.10.52-orig/net/bluetooth/smp.c linux-5.10.52-v7l+/net/bluetooth/smp.c
--- linux-5.10.52-orig/net/bluetooth/smp.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/net/bluetooth/smp.c	2021-07-25 16:46:36.687775826 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:886 @
 	    hcon->io_capability == HCI_IO_NO_INPUT_OUTPUT)
 		smp->method = JUST_WORKS;
 
-	/* If Just Works, Continue with Zero TK and ask user-space for
-	 * confirmation */
+	/* If Just Works, Continue with Zero TK */
 	if (smp->method == JUST_WORKS) {
-		ret = mgmt_user_confirm_request(hcon->hdev, &hcon->dst,
-						hcon->type,
-						hcon->dst_type,
-						passkey, 1);
-		if (ret)
-			return ret;
-		set_bit(SMP_FLAG_WAIT_USER, &smp->flags);
+		set_bit(SMP_FLAG_TK_VALID, &smp->flags);
 		return 0;
 	}
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:2197 @
 	if (err)
 		return SMP_UNSPECIFIED;
 
-	if (smp->method == REQ_OOB) {
+	if (smp->method == JUST_WORKS || smp->method == REQ_OOB) {
 		if (hcon->out) {
 			sc_dhkey_check(smp);
 			SMP_ALLOW_CMD(smp, SMP_CMD_DHKEY_CHECK);
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:2212 @
 	confirm_hint = 0;
 
 confirm:
-	if (smp->method == JUST_WORKS)
-		confirm_hint = 1;
-
 	err = mgmt_user_confirm_request(hcon->hdev, &hcon->dst, hcon->type,
 					hcon->dst_type, passkey, confirm_hint);
 	if (err)
diff -Nur --no-dereference linux-5.10.52-orig/scripts/gcc-plugin.sh linux-5.10.52-v7l+/scripts/gcc-plugin.sh
--- linux-5.10.52-orig/scripts/gcc-plugin.sh	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/scripts/gcc-plugin.sh	2021-07-25 16:46:38.857739446 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:3 @
 #!/bin/sh
 # SPDX-License-Identifier: GPL-2.0
+exit 1 # Disable plugins
 
 set -e
 
diff -Nur --no-dereference linux-5.10.52-orig/scripts/Makefile.dtbinst linux-5.10.52-v7l+/scripts/Makefile.dtbinst
--- linux-5.10.52-orig/scripts/Makefile.dtbinst	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/scripts/Makefile.dtbinst	2021-07-25 16:46:38.727741626 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:21 @
 include $(src)/Makefile
 
 dtbs    := $(addprefix $(dst)/, $(dtb-y) $(if $(CONFIG_OF_ALL_DTBS),$(dtb-)))
+dtbos   := $(addprefix $(dst)/, $(dtbo-y) $(if $(CONFIG_OF_ALL_DTBS),$(dtb-)))
 subdirs := $(addprefix $(obj)/, $(subdir-y) $(subdir-m))
 
-__dtbs_install: $(dtbs) $(subdirs)
+__dtbs_install: $(dtbs) $(dtbos) $(subdirs)
 	@:
 
 quiet_cmd_dtb_install = INSTALL $@
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:33 @
 $(dst)/%.dtb: $(obj)/%.dtb
 	$(call cmd,dtb_install)
 
+$(dst)/%.dtbo: $(obj)/%.dtbo
+	$(call cmd,dtb_install)
+
 PHONY += $(subdirs)
 $(subdirs):
 	$(Q)$(MAKE) $(dtbinst)=$@ dst=$(patsubst $(obj)/%,$(dst)/%,$@)
diff -Nur --no-dereference linux-5.10.52-orig/scripts/Makefile.lib linux-5.10.52-v7l+/scripts/Makefile.lib
--- linux-5.10.52-orig/scripts/Makefile.lib	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/scripts/Makefile.lib	2021-07-25 16:46:38.727741626 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:284 @
 ifeq ($(findstring 1,$(KBUILD_EXTRA_WARN)),)
 DTC_FLAGS += -Wno-unit_address_vs_reg \
 	-Wno-unit_address_format \
+	-Wno-gpios_property \
 	-Wno-avoid_unnecessary_addr_size \
 	-Wno-alias_paths \
 	-Wno-graph_child_address \
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:345 @
 $(obj)/%.dt.yaml: $(src)/%.dts $(DTC) $(DT_TMP_SCHEMA) FORCE
 	$(call if_changed_rule,dtc,yaml)
 
+quiet_cmd_dtco = DTCO    $@
+cmd_dtco = mkdir -p $(dir ${dtc-tmp}) ; \
+	$(CPP) $(dtc_cpp_flags) -x assembler-with-cpp -o $(dtc-tmp) $< ; \
+	$(DTC) -@ -H epapr -O dtb -o $@ -b 0 \
+		-i $(dir $<) $(DTC_FLAGS) \
+		-Wno-interrupts_property \
+		-Wno-label_is_string \
+		-Wno-reg_format \
+		-Wno-pci_device_bus_num \
+		-Wno-i2c_bus_reg \
+		-Wno-spi_bus_reg \
+		-Wno-avoid_default_addr_size \
+		-d $(depfile).dtc.tmp $(dtc-tmp) ; \
+	cat $(depfile).pre.tmp $(depfile).dtc.tmp > $(depfile)
+
+$(obj)/%.dtbo: $(src)/%-overlay.dts FORCE
+	$(call if_changed_dep,dtco)
+
 dtc-tmp = $(subst $(comma),_,$(dot-target).dts.tmp)
 
 # Bzip2
diff -Nur --no-dereference linux-5.10.52-orig/sound/core/pcm_iec958.c linux-5.10.52-v7l+/sound/core/pcm_iec958.c
--- linux-5.10.52-orig/sound/core/pcm_iec958.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/sound/core/pcm_iec958.c	2021-07-25 16:46:39.237733076 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:12 @
 #include <sound/pcm_params.h>
 #include <sound/pcm_iec958.h>
 
-static int create_iec958_consumer(uint rate, uint sample_width,
-				  u8 *cs, size_t len)
+int snd_pcm_create_iec958_consumer_default(u8 *cs, size_t len)
 {
-	unsigned int fs, ws;
-
 	if (len < 4)
 		return -EINVAL;
 
-	switch (rate) {
-	case 32000:
-		fs = IEC958_AES3_CON_FS_32000;
-		break;
-	case 44100:
-		fs = IEC958_AES3_CON_FS_44100;
-		break;
-	case 48000:
-		fs = IEC958_AES3_CON_FS_48000;
-		break;
-	case 88200:
-		fs = IEC958_AES3_CON_FS_88200;
-		break;
-	case 96000:
-		fs = IEC958_AES3_CON_FS_96000;
-		break;
-	case 176400:
-		fs = IEC958_AES3_CON_FS_176400;
-		break;
-	case 192000:
-		fs = IEC958_AES3_CON_FS_192000;
-		break;
-	default:
+	memset(cs, 0, len);
+
+	cs[0] = IEC958_AES0_CON_NOT_COPYRIGHT | IEC958_AES0_CON_EMPHASIS_NONE;
+	cs[1] = IEC958_AES1_CON_GENERAL;
+	cs[2] = IEC958_AES2_CON_SOURCE_UNSPEC | IEC958_AES2_CON_CHANNEL_UNSPEC;
+	cs[3] = IEC958_AES3_CON_CLOCK_1000PPM | IEC958_AES3_CON_FS_NOTID;
+
+	if (len > 4)
+		cs[4] = IEC958_AES4_CON_WORDLEN_NOTID;
+
+	return len;
+}
+EXPORT_SYMBOL(snd_pcm_create_iec958_consumer_default);
+
+static int fill_iec958_consumer(uint rate, uint sample_width,
+				u8 *cs, size_t len)
+{
+	if (len < 4)
 		return -EINVAL;
+
+	if ((cs[3] & IEC958_AES3_CON_FS) == IEC958_AES3_CON_FS_NOTID) {
+		unsigned int fs;
+
+		switch (rate) {
+			case 32000:
+				fs = IEC958_AES3_CON_FS_32000;
+				break;
+			case 44100:
+				fs = IEC958_AES3_CON_FS_44100;
+				break;
+			case 48000:
+				fs = IEC958_AES3_CON_FS_48000;
+				break;
+			case 88200:
+				fs = IEC958_AES3_CON_FS_88200;
+				break;
+			case 96000:
+				fs = IEC958_AES3_CON_FS_96000;
+				break;
+			case 176400:
+				fs = IEC958_AES3_CON_FS_176400;
+				break;
+			case 192000:
+				fs = IEC958_AES3_CON_FS_192000;
+				break;
+			default:
+				return -EINVAL;
+		}
+
+		cs[3] &= ~IEC958_AES3_CON_FS;
+		cs[3] |= fs;
 	}
 
-	if (len > 4) {
+	if (len > 4 &&
+	    (cs[4] & IEC958_AES4_CON_WORDLEN) == IEC958_AES4_CON_WORDLEN_NOTID) {
+		unsigned int ws;
+
 		switch (sample_width) {
 		case 16:
 			ws = IEC958_AES4_CON_WORDLEN_20_16;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:94 @
 		default:
 			return -EINVAL;
 		}
-	}
 
-	memset(cs, 0, len);
+		cs[4] &= ~IEC958_AES4_CON_WORDLEN;
+		cs[4] |= ws;
+	}
 
-	cs[0] = IEC958_AES0_CON_NOT_COPYRIGHT | IEC958_AES0_CON_EMPHASIS_NONE;
-	cs[1] = IEC958_AES1_CON_GENERAL;
-	cs[2] = IEC958_AES2_CON_SOURCE_UNSPEC | IEC958_AES2_CON_CHANNEL_UNSPEC;
-	cs[3] = IEC958_AES3_CON_CLOCK_1000PPM | fs;
+	return len;
+}
 
-	if (len > 4)
-		cs[4] = ws;
+int snd_pcm_fill_iec958_consumer_hw_params(struct snd_pcm_hw_params *params,
+					   u8 *cs, size_t len)
+{
+	return fill_iec958_consumer(params_rate(params), params_width(params), cs, len);
+}
+EXPORT_SYMBOL(snd_pcm_fill_iec958_consumer_hw_params);
 
-	return len;
+int snd_pcm_fill_iec958_consumer(struct snd_pcm_runtime *runtime,
+				 u8 *cs, size_t len)
+{
+	return fill_iec958_consumer(runtime->rate,
+				    snd_pcm_format_width(runtime->format),
+				    cs, len);
 }
+EXPORT_SYMBOL(snd_pcm_fill_iec958_consumer);
 
 /**
  * snd_pcm_create_iec958_consumer - create consumer format IEC958 channel status
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:134 @
 int snd_pcm_create_iec958_consumer(struct snd_pcm_runtime *runtime, u8 *cs,
 	size_t len)
 {
-	return create_iec958_consumer(runtime->rate,
-				      snd_pcm_format_width(runtime->format),
-				      cs, len);
+	int ret;
+
+	ret = snd_pcm_create_iec958_consumer_default(cs, len);
+	if (ret < 0)
+		return ret;
+
+	return snd_pcm_fill_iec958_consumer(runtime, cs, len);
 }
 EXPORT_SYMBOL(snd_pcm_create_iec958_consumer);
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:160 @
 int snd_pcm_create_iec958_consumer_hw_params(struct snd_pcm_hw_params *params,
 					     u8 *cs, size_t len)
 {
-	return create_iec958_consumer(params_rate(params), params_width(params),
-				      cs, len);
+	int ret;
+
+	ret = snd_pcm_create_iec958_consumer_default(cs, len);
+	if (ret < 0)
+		return ret;
+
+	return fill_iec958_consumer(params_rate(params), params_width(params), cs, len);
 }
 EXPORT_SYMBOL(snd_pcm_create_iec958_consumer_hw_params);
diff -Nur --no-dereference linux-5.10.52-orig/sound/soc/bcm/allo-boss2-dac.c linux-5.10.52-v7l+/sound/soc/bcm/allo-boss2-dac.c
--- linux-5.10.52-orig/sound/soc/bcm/allo-boss2-dac.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/sound/soc/bcm/allo-boss2-dac.c	2021-07-25 16:46:40.127718155 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/*
+ * Driver for the ALLO KATANA CODEC
+ *
+ * Author: Jaikumar <sudeepkumar@cem-solutions.net>
+ *		Copyright 2018
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
+#include <linux/platform_device.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/of_device.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <linux/of_gpio.h>
+#include <linux/regulator/consumer.h>
+#include <linux/pm_runtime.h>
+#include <linux/of_irq.h>
+#include <linux/completion.h>
+#include <linux/mutex.h>
+#include <linux/workqueue.h>
+#include <sound/jack.h>
+
+#include "../codecs/cs43130.h"
+
+#include <linux/clk.h>
+#include <linux/gcd.h>
+#define DEBUG
+
+#define CS43130_DSD_EN_MASK             0x10
+#define CS43130_PDN_DONE_INT_MASK        0x00
+
+static struct gpio_desc *snd_allo_clk44gpio;
+static struct gpio_desc *snd_allo_clk48gpio;
+
+struct  cs43130_priv {
+	struct snd_soc_component        *component;
+	struct regmap                   *regmap;
+	struct regulator_bulk_data      supplies[CS43130_NUM_SUPPLIES];
+	struct gpio_desc                *reset_gpio;
+	unsigned int                    dev_id; /* codec device ID */
+	int                             xtal_ibias;
+	/* shared by both DAIs */
+	struct mutex                    clk_mutex;
+	int                             clk_req;
+	bool                            pll_bypass;
+	struct completion               xtal_rdy;
+	struct completion               pll_rdy;
+	unsigned int                    mclk;
+	unsigned int                    mclk_int;
+	int                             mclk_int_src;
+
+	/* DAI specific */
+	struct cs43130_dai              dais[CS43130_DAI_ID_MAX];
+
+	/* HP load specific */
+	bool                            dc_meas;
+	bool                            ac_meas;
+	bool                            hpload_done;
+	struct completion               hpload_evt;
+	unsigned int                    hpload_stat;
+	u16                             hpload_dc[2];
+	u16                             dc_threshold[CS43130_DC_THRESHOLD];
+	u16                             ac_freq[CS43130_AC_FREQ];
+	u16                             hpload_ac[CS43130_AC_FREQ][2];
+	struct workqueue_struct         *wq;
+	struct work_struct              work;
+	struct snd_soc_jack             jack;
+};
+
+static const struct reg_default cs43130_reg_defaults[] = {
+	{CS43130_SYS_CLK_CTL_1, 0x06},
+	{CS43130_SP_SRATE, 0x01},
+	{CS43130_SP_BITSIZE, 0x05},
+	{CS43130_PAD_INT_CFG, 0x03},
+	{CS43130_PWDN_CTL, 0xFE},
+	{CS43130_CRYSTAL_SET, 0x04},
+	{CS43130_PLL_SET_1, 0x00},
+	{CS43130_PLL_SET_2, 0x00},
+	{CS43130_PLL_SET_3, 0x00},
+	{CS43130_PLL_SET_4, 0x00},
+	{CS43130_PLL_SET_5, 0x40},
+	{CS43130_PLL_SET_6, 0x10},
+	{CS43130_PLL_SET_7, 0x80},
+	{CS43130_PLL_SET_8, 0x03},
+	{CS43130_PLL_SET_9, 0x02},
+	{CS43130_PLL_SET_10, 0x02},
+	{CS43130_CLKOUT_CTL, 0x00},
+	{CS43130_ASP_NUM_1, 0x01},
+	{CS43130_ASP_NUM_2, 0x00},
+	{CS43130_ASP_DEN_1, 0x08},
+	{CS43130_ASP_DEN_2, 0x00},
+	{CS43130_ASP_LRCK_HI_TIME_1, 0x1F},
+	{CS43130_ASP_LRCK_HI_TIME_2, 0x00},
+	{CS43130_ASP_LRCK_PERIOD_1, 0x3F},
+	{CS43130_ASP_LRCK_PERIOD_2, 0x00},
+	{CS43130_ASP_CLOCK_CONF, 0x0C},
+	{CS43130_ASP_FRAME_CONF, 0x0A},
+	{CS43130_XSP_NUM_1, 0x01},
+	{CS43130_XSP_NUM_2, 0x00},
+	{CS43130_XSP_DEN_1, 0x02},
+	{CS43130_XSP_DEN_2, 0x00},
+	{CS43130_XSP_LRCK_HI_TIME_1, 0x1F},
+	{CS43130_XSP_LRCK_HI_TIME_2, 0x00},
+	{CS43130_XSP_LRCK_PERIOD_1, 0x3F},
+	{CS43130_XSP_LRCK_PERIOD_2, 0x00},
+	{CS43130_XSP_CLOCK_CONF, 0x0C},
+	{CS43130_XSP_FRAME_CONF, 0x0A},
+	{CS43130_ASP_CH_1_LOC, 0x00},
+	{CS43130_ASP_CH_2_LOC, 0x00},
+	{CS43130_ASP_CH_1_SZ_EN, 0x06},
+	{CS43130_ASP_CH_2_SZ_EN, 0x0E},
+	{CS43130_XSP_CH_1_LOC, 0x00},
+	{CS43130_XSP_CH_2_LOC, 0x00},
+	{CS43130_XSP_CH_1_SZ_EN, 0x06},
+	{CS43130_XSP_CH_2_SZ_EN, 0x0E},
+	{CS43130_DSD_VOL_B, 0x78},
+	{CS43130_DSD_VOL_A, 0x78},
+	{CS43130_DSD_PATH_CTL_1, 0xA8},
+	{CS43130_DSD_INT_CFG, 0x00},
+	{CS43130_DSD_PATH_CTL_2, 0x02},
+	{CS43130_DSD_PCM_MIX_CTL, 0x00},
+	{CS43130_DSD_PATH_CTL_3, 0x40},
+	{CS43130_HP_OUT_CTL_1, 0x30},
+	{CS43130_PCM_FILT_OPT, 0x02},
+	{CS43130_PCM_VOL_B, 0x78},
+	{CS43130_PCM_VOL_A, 0x78},
+	{CS43130_PCM_PATH_CTL_1, 0xA8},
+	{CS43130_PCM_PATH_CTL_2, 0x00},
+	{CS43130_CLASS_H_CTL, 0x1E},
+	{CS43130_HP_DETECT, 0x04},
+	{CS43130_HP_LOAD_1, 0x00},
+	{CS43130_HP_MEAS_LOAD_1, 0x00},
+	{CS43130_HP_MEAS_LOAD_2, 0x00},
+	{CS43130_INT_MASK_1, 0xFF},
+	{CS43130_INT_MASK_2, 0xFF},
+	{CS43130_INT_MASK_3, 0xFF},
+	{CS43130_INT_MASK_4, 0xFF},
+	{CS43130_INT_MASK_5, 0xFF},
+};
+static bool cs43130_volatile_register(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case CS43130_INT_STATUS_1 ... CS43130_INT_STATUS_5:
+	case CS43130_HP_DC_STAT_1 ... CS43130_HP_DC_STAT_2:
+	case CS43130_HP_AC_STAT_1 ... CS43130_HP_AC_STAT_2:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static const char * const pcm_spd_texts[] = {
+	"Fast",
+	"Slow",
+};
+
+static SOC_ENUM_SINGLE_DECL(pcm_spd_enum, CS43130_PCM_FILT_OPT, 7,
+			pcm_spd_texts);
+
+static const SNDRV_CTL_TLVD_DECLARE_DB_MINMAX(master_tlv, -12750, 0);
+
+static const struct snd_kcontrol_new cs43130_controls[] = {
+	SOC_DOUBLE_R_TLV("Master Playback Volume", CS43130_PCM_VOL_B,
+			CS43130_PCM_VOL_A, 0, 255, 1, master_tlv),
+	SOC_DOUBLE("Master Playback Switch", CS43130_PCM_PATH_CTL_1,
+			0, 1, 1, 1),
+	SOC_DOUBLE_R_TLV("Digital Playback Volume", CS43130_DSD_VOL_B,
+			CS43130_DSD_VOL_A, 0, 255, 1, master_tlv),
+	SOC_DOUBLE("Digital Playback Switch", CS43130_DSD_PATH_CTL_1,
+			0, 1, 1, 1),
+	SOC_SINGLE("HV_Enable", CS43130_HP_OUT_CTL_1, 0, 1, 0),
+	SOC_ENUM("PCM Filter Speed", pcm_spd_enum),
+	SOC_SINGLE("PCM Phase Compensation", CS43130_PCM_FILT_OPT, 6, 1, 0),
+	SOC_SINGLE("PCM Nonoversample Emulate", CS43130_PCM_FILT_OPT, 5, 1, 0),
+	SOC_SINGLE("PCM High-pass Filter", CS43130_PCM_FILT_OPT, 1, 1, 0),
+	SOC_SINGLE("PCM De-emphasis Filter", CS43130_PCM_FILT_OPT, 0, 1, 0),
+};
+
+static bool cs43130_readable_register(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case CS43130_DEVID_AB ... CS43130_SYS_CLK_CTL_1:
+	case CS43130_SP_SRATE ... CS43130_PAD_INT_CFG:
+	case CS43130_PWDN_CTL:
+	case CS43130_CRYSTAL_SET:
+	case CS43130_PLL_SET_1 ... CS43130_PLL_SET_5:
+	case CS43130_PLL_SET_6:
+	case CS43130_PLL_SET_7:
+	case CS43130_PLL_SET_8:
+	case CS43130_PLL_SET_9:
+	case CS43130_PLL_SET_10:
+	case CS43130_CLKOUT_CTL:
+	case CS43130_ASP_NUM_1 ... CS43130_ASP_FRAME_CONF:
+	case CS43130_XSP_NUM_1 ... CS43130_XSP_FRAME_CONF:
+	case CS43130_ASP_CH_1_LOC:
+	case CS43130_ASP_CH_2_LOC:
+	case CS43130_ASP_CH_1_SZ_EN:
+	case CS43130_ASP_CH_2_SZ_EN:
+	case CS43130_XSP_CH_1_LOC:
+	case CS43130_XSP_CH_2_LOC:
+	case CS43130_XSP_CH_1_SZ_EN:
+	case CS43130_XSP_CH_2_SZ_EN:
+	case CS43130_DSD_VOL_B ... CS43130_DSD_PATH_CTL_3:
+	case CS43130_HP_OUT_CTL_1:
+	case CS43130_PCM_FILT_OPT ... CS43130_PCM_PATH_CTL_2:
+	case CS43130_CLASS_H_CTL:
+	case CS43130_HP_DETECT:
+	case CS43130_HP_STATUS:
+	case CS43130_HP_LOAD_1:
+	case CS43130_HP_MEAS_LOAD_1:
+	case CS43130_HP_MEAS_LOAD_2:
+	case CS43130_HP_DC_STAT_1:
+	case CS43130_HP_DC_STAT_2:
+	case CS43130_HP_AC_STAT_1:
+	case CS43130_HP_AC_STAT_2:
+	case CS43130_HP_LOAD_STAT:
+	case CS43130_INT_STATUS_1 ... CS43130_INT_STATUS_5:
+	case CS43130_INT_MASK_1 ... CS43130_INT_MASK_5:
+		return true;
+	default:
+		return false;
+	}
+}
+static bool cs43130_precious_register(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case CS43130_INT_STATUS_1 ... CS43130_INT_STATUS_5:
+		return true;
+	default:
+		return false;
+	}
+}
+static int cs43130_pcm_pdn(struct snd_soc_component *component)
+{
+	struct cs43130_priv *cs43130 =
+				snd_soc_component_get_drvdata(component);
+	int ret;
+	unsigned int reg, pdn_int;
+
+	regmap_write(cs43130->regmap, CS43130_DSD_PATH_CTL_2, 0x02);
+	regmap_update_bits(cs43130->regmap, CS43130_INT_MASK_1,
+			CS43130_PDN_DONE_INT_MASK, 0);
+	regmap_update_bits(cs43130->regmap, CS43130_PWDN_CTL,
+			CS43130_PDN_HP_MASK, 1 << CS43130_PDN_HP_SHIFT);
+	usleep_range(10, 50);
+	ret = regmap_read(cs43130->regmap, CS43130_INT_STATUS_1, &reg);
+	pdn_int = reg & 0xFE;
+	regmap_update_bits(cs43130->regmap, CS43130_PWDN_CTL,
+			CS43130_PDN_ASP_MASK, 1 << CS43130_PDN_ASP_SHIFT);
+	return 0;
+
+}
+static int cs43130_pwr_up_asp_dac(struct snd_soc_component *component)
+{
+	struct cs43130_priv *cs43130 =
+				snd_soc_component_get_drvdata(component);
+
+	regmap_update_bits(cs43130->regmap, CS43130_PAD_INT_CFG,
+			CS43130_ASP_3ST_MASK, 0);
+	regmap_write(cs43130->regmap, CS43130_DXD1, 0x99);
+	regmap_write(cs43130->regmap, CS43130_DXD13, 0x20);
+	regmap_update_bits(cs43130->regmap, CS43130_PWDN_CTL,
+			CS43130_PDN_ASP_MASK, 0);
+	regmap_update_bits(cs43130->regmap, CS43130_PWDN_CTL,
+			CS43130_PDN_HP_MASK, 0);
+	usleep_range(10000, 12000);
+	regmap_write(cs43130->regmap, CS43130_DXD1, 0x00);
+	regmap_write(cs43130->regmap, CS43130_DXD13, 0x00);
+	return 0;
+}
+static int cs43130_change_clksrc(struct snd_soc_component *component,
+				enum cs43130_mclk_src_sel src)
+{
+	int ret;
+	struct cs43130_priv *cs43130 =
+				snd_soc_component_get_drvdata(component);
+	int mclk_int_decoded;
+
+	if (src == cs43130->mclk_int_src) {
+		/* clk source has not changed */
+		return 0;
+	}
+	switch (cs43130->mclk_int) {
+	case CS43130_MCLK_22M:
+		mclk_int_decoded = CS43130_MCLK_22P5;
+		break;
+	case CS43130_MCLK_24M:
+		mclk_int_decoded = CS43130_MCLK_24P5;
+		break;
+	default:
+		dev_err(component->dev, "Invalid MCLK INT freq: %u\n",
+			cs43130->mclk_int);
+		return -EINVAL;
+	}
+
+	switch (src) {
+	case CS43130_MCLK_SRC_EXT:
+		cs43130->pll_bypass = true;
+		cs43130->mclk_int_src = CS43130_MCLK_SRC_EXT;
+		if (cs43130->xtal_ibias == CS43130_XTAL_UNUSED) {
+			regmap_update_bits(cs43130->regmap, CS43130_PWDN_CTL,
+					CS43130_PDN_XTAL_MASK,
+					1 << CS43130_PDN_XTAL_SHIFT);
+		} else {
+			reinit_completion(&cs43130->xtal_rdy);
+			regmap_update_bits(cs43130->regmap, CS43130_INT_MASK_1,
+					CS43130_XTAL_RDY_INT_MASK, 0);
+			regmap_update_bits(cs43130->regmap, CS43130_PWDN_CTL,
+					CS43130_PDN_XTAL_MASK, 0);
+			ret = wait_for_completion_timeout(&cs43130->xtal_rdy,
+					msecs_to_jiffies(100));
+			regmap_update_bits(cs43130->regmap, CS43130_INT_MASK_1,
+					CS43130_XTAL_RDY_INT_MASK,
+					1 << CS43130_XTAL_RDY_INT_SHIFT);
+			if (ret == 0) {
+				dev_err(component->dev, "Timeout waiting for XTAL_READY interrupt\n");
+				return -ETIMEDOUT;
+			}
+		}
+	regmap_update_bits(cs43130->regmap, CS43130_SYS_CLK_CTL_1,
+				CS43130_MCLK_SRC_SEL_MASK,
+				src << CS43130_MCLK_SRC_SEL_SHIFT);
+	regmap_update_bits(cs43130->regmap, CS43130_SYS_CLK_CTL_1,
+				CS43130_MCLK_INT_MASK,
+				mclk_int_decoded << CS43130_MCLK_INT_SHIFT);
+	usleep_range(150, 200);
+	regmap_update_bits(cs43130->regmap, CS43130_PWDN_CTL,
+				CS43130_PDN_PLL_MASK,
+				1 << CS43130_PDN_PLL_SHIFT);
+	break;
+	case CS43130_MCLK_SRC_RCO:
+		cs43130->mclk_int_src = CS43130_MCLK_SRC_RCO;
+
+		regmap_update_bits(cs43130->regmap, CS43130_SYS_CLK_CTL_1,
+				CS43130_MCLK_SRC_SEL_MASK,
+				src << CS43130_MCLK_SRC_SEL_SHIFT);
+		regmap_update_bits(cs43130->regmap, CS43130_SYS_CLK_CTL_1,
+				CS43130_MCLK_INT_MASK,
+				CS43130_MCLK_22P5 << CS43130_MCLK_INT_SHIFT);
+		usleep_range(150, 200);
+		regmap_update_bits(cs43130->regmap, CS43130_PWDN_CTL,
+				CS43130_PDN_XTAL_MASK,
+				1 << CS43130_PDN_XTAL_SHIFT);
+		regmap_update_bits(cs43130->regmap, CS43130_PWDN_CTL,
+				CS43130_PDN_PLL_MASK,
+				1 << CS43130_PDN_PLL_SHIFT);
+	break;
+	default:
+		dev_err(component->dev, "Invalid MCLK source value\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+static const struct cs43130_bitwidth_map cs43130_bitwidth_table[] = {
+	{8,     CS43130_SP_BIT_SIZE_8,  CS43130_CH_BIT_SIZE_8},
+	{16,    CS43130_SP_BIT_SIZE_16, CS43130_CH_BIT_SIZE_16},
+	{24,    CS43130_SP_BIT_SIZE_24, CS43130_CH_BIT_SIZE_24},
+	{32,    CS43130_SP_BIT_SIZE_32, CS43130_CH_BIT_SIZE_32},
+};
+
+static const struct cs43130_bitwidth_map *cs43130_get_bitwidth_table(
+					unsigned int bitwidth)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(cs43130_bitwidth_table); i++) {
+		if (cs43130_bitwidth_table[i].bitwidth == bitwidth)
+			return &cs43130_bitwidth_table[i];
+	}
+
+	return NULL;
+}
+static int cs43130_set_bitwidth(int dai_id, unsigned int bitwidth_dai,
+				struct regmap *regmap)
+{
+	const struct cs43130_bitwidth_map *bw_map;
+
+	bw_map = cs43130_get_bitwidth_table(bitwidth_dai);
+	if (!bw_map)
+		return -EINVAL;
+
+	switch (dai_id) {
+	case CS43130_ASP_PCM_DAI:
+	case CS43130_ASP_DOP_DAI:
+		regmap_update_bits(regmap, CS43130_ASP_CH_1_SZ_EN,
+				CS43130_CH_BITSIZE_MASK, bw_map->ch_bit);
+		regmap_update_bits(regmap, CS43130_ASP_CH_2_SZ_EN,
+				CS43130_CH_BITSIZE_MASK, bw_map->ch_bit);
+		regmap_update_bits(regmap, CS43130_SP_BITSIZE,
+				CS43130_ASP_BITSIZE_MASK, bw_map->sp_bit);
+		break;
+	case CS43130_XSP_DOP_DAI:
+		regmap_update_bits(regmap, CS43130_XSP_CH_1_SZ_EN,
+				CS43130_CH_BITSIZE_MASK, bw_map->ch_bit);
+		regmap_update_bits(regmap, CS43130_XSP_CH_2_SZ_EN,
+				CS43130_CH_BITSIZE_MASK, bw_map->ch_bit);
+		regmap_update_bits(regmap, CS43130_SP_BITSIZE,
+				CS43130_XSP_BITSIZE_MASK, bw_map->sp_bit <<
+				CS43130_XSP_BITSIZE_SHIFT);
+	break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+static const struct cs43130_rate_map cs43130_rate_table[] = {
+	{32000,         CS43130_ASP_SPRATE_32K},
+	{44100,         CS43130_ASP_SPRATE_44_1K},
+	{48000,         CS43130_ASP_SPRATE_48K},
+	{88200,         CS43130_ASP_SPRATE_88_2K},
+	{96000,         CS43130_ASP_SPRATE_96K},
+	{176400,        CS43130_ASP_SPRATE_176_4K},
+	{192000,        CS43130_ASP_SPRATE_192K},
+	{352800,        CS43130_ASP_SPRATE_352_8K},
+	{384000,        CS43130_ASP_SPRATE_384K},
+};
+
+static const struct cs43130_rate_map *cs43130_get_rate_table(int fs)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(cs43130_rate_table); i++) {
+		if (cs43130_rate_table[i].fs == fs)
+			return &cs43130_rate_table[i];
+	}
+
+	return NULL;
+}
+
+static const struct cs43130_clk_gen *cs43130_get_clk_gen(int mclk_int, int fs,
+	const struct cs43130_clk_gen *clk_gen_table, int len_clk_gen_table)
+{
+	int i;
+
+	for (i = 0; i < len_clk_gen_table; i++) {
+		if (clk_gen_table[i].mclk_int == mclk_int &&
+					clk_gen_table[i].fs == fs)
+			return &clk_gen_table[i];
+	}
+	return NULL;
+}
+
+static int cs43130_set_sp_fmt(int dai_id, unsigned int bitwidth_sclk,
+				struct snd_pcm_hw_params *params,
+				struct cs43130_priv *cs43130)
+{
+	u16 frm_size;
+	u16 hi_size;
+	u8 frm_delay;
+	u8 frm_phase;
+	u8 frm_data;
+	u8 sclk_edge;
+	u8 lrck_edge;
+	u8 clk_data;
+	u8 loc_ch1;
+	u8 loc_ch2;
+	u8 dai_mode_val;
+	const struct cs43130_clk_gen *clk_gen;
+
+	switch (cs43130->dais[dai_id].dai_format) {
+	case SND_SOC_DAIFMT_I2S:
+		hi_size = bitwidth_sclk;
+		frm_delay = 2;
+		frm_phase = 0;
+		break;
+	case SND_SOC_DAIFMT_LEFT_J:
+		hi_size = bitwidth_sclk;
+		frm_delay = 2;
+		frm_phase = 1;
+		break;
+	case SND_SOC_DAIFMT_DSP_A:
+		hi_size = 1;
+		frm_delay = 2;
+		frm_phase = 1;
+		break;
+	case SND_SOC_DAIFMT_DSP_B:
+		hi_size = 1;
+		frm_delay = 0;
+		frm_phase = 1;
+		break;
+	default:
+		return -EINVAL;
+	}
+	switch (cs43130->dais[dai_id].dai_mode) {
+	case SND_SOC_DAIFMT_CBS_CFS:
+		dai_mode_val = 0;
+		break;
+	case SND_SOC_DAIFMT_CBM_CFM:
+		dai_mode_val = 1;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	frm_size = bitwidth_sclk * params_channels(params);
+	sclk_edge = 1;
+	lrck_edge = 0;
+	loc_ch1 = 0;
+	loc_ch2 = bitwidth_sclk * (params_channels(params) - 1);
+
+	frm_data = frm_delay & CS43130_SP_FSD_MASK;
+	frm_data |= (frm_phase << CS43130_SP_STP_SHIFT) & CS43130_SP_STP_MASK;
+
+	clk_data = lrck_edge & CS43130_SP_LCPOL_IN_MASK;
+	clk_data |= (lrck_edge << CS43130_SP_LCPOL_OUT_SHIFT) &
+			CS43130_SP_LCPOL_OUT_MASK;
+	clk_data |= (sclk_edge << CS43130_SP_SCPOL_IN_SHIFT) &
+			CS43130_SP_SCPOL_IN_MASK;
+	clk_data |= (sclk_edge << CS43130_SP_SCPOL_OUT_SHIFT) &
+			CS43130_SP_SCPOL_OUT_MASK;
+	clk_data |= (dai_mode_val << CS43130_SP_MODE_SHIFT) &
+			CS43130_SP_MODE_MASK;
+	switch (dai_id) {
+	case CS43130_ASP_PCM_DAI:
+	case CS43130_ASP_DOP_DAI:
+		regmap_update_bits(cs43130->regmap, CS43130_ASP_LRCK_PERIOD_1,
+			CS43130_SP_LCPR_DATA_MASK, (frm_size - 1) >>
+			CS43130_SP_LCPR_LSB_DATA_SHIFT);
+		regmap_update_bits(cs43130->regmap, CS43130_ASP_LRCK_PERIOD_2,
+			CS43130_SP_LCPR_DATA_MASK, (frm_size - 1) >>
+			CS43130_SP_LCPR_MSB_DATA_SHIFT);
+		regmap_update_bits(cs43130->regmap, CS43130_ASP_LRCK_HI_TIME_1,
+			CS43130_SP_LCHI_DATA_MASK, (hi_size - 1) >>
+			CS43130_SP_LCHI_LSB_DATA_SHIFT);
+		regmap_update_bits(cs43130->regmap, CS43130_ASP_LRCK_HI_TIME_2,
+			CS43130_SP_LCHI_DATA_MASK, (hi_size - 1) >>
+			CS43130_SP_LCHI_MSB_DATA_SHIFT);
+		regmap_write(cs43130->regmap, CS43130_ASP_FRAME_CONF, frm_data);
+		regmap_write(cs43130->regmap, CS43130_ASP_CH_1_LOC, loc_ch1);
+		regmap_write(cs43130->regmap, CS43130_ASP_CH_2_LOC, loc_ch2);
+		regmap_update_bits(cs43130->regmap, CS43130_ASP_CH_1_SZ_EN,
+			CS43130_CH_EN_MASK, 1 << CS43130_CH_EN_SHIFT);
+		regmap_update_bits(cs43130->regmap, CS43130_ASP_CH_2_SZ_EN,
+			CS43130_CH_EN_MASK, 1 << CS43130_CH_EN_SHIFT);
+		regmap_write(cs43130->regmap, CS43130_ASP_CLOCK_CONF, clk_data);
+		break;
+	case CS43130_XSP_DOP_DAI:
+		regmap_update_bits(cs43130->regmap, CS43130_XSP_LRCK_PERIOD_1,
+			CS43130_SP_LCPR_DATA_MASK, (frm_size - 1) >>
+			CS43130_SP_LCPR_LSB_DATA_SHIFT);
+		regmap_update_bits(cs43130->regmap, CS43130_XSP_LRCK_PERIOD_2,
+			CS43130_SP_LCPR_DATA_MASK, (frm_size - 1) >>
+			CS43130_SP_LCPR_MSB_DATA_SHIFT);
+		regmap_update_bits(cs43130->regmap, CS43130_XSP_LRCK_HI_TIME_1,
+			CS43130_SP_LCHI_DATA_MASK, (hi_size - 1) >>
+			CS43130_SP_LCHI_LSB_DATA_SHIFT);
+		regmap_update_bits(cs43130->regmap, CS43130_XSP_LRCK_HI_TIME_2,
+			CS43130_SP_LCHI_DATA_MASK, (hi_size - 1) >>
+			CS43130_SP_LCHI_MSB_DATA_SHIFT);
+		regmap_write(cs43130->regmap, CS43130_XSP_FRAME_CONF, frm_data);
+		regmap_write(cs43130->regmap, CS43130_XSP_CH_1_LOC, loc_ch1);
+		regmap_write(cs43130->regmap, CS43130_XSP_CH_2_LOC, loc_ch2);
+		regmap_update_bits(cs43130->regmap, CS43130_XSP_CH_1_SZ_EN,
+			CS43130_CH_EN_MASK, 1 << CS43130_CH_EN_SHIFT);
+		regmap_update_bits(cs43130->regmap, CS43130_XSP_CH_2_SZ_EN,
+			CS43130_CH_EN_MASK, 1 << CS43130_CH_EN_SHIFT);
+		regmap_write(cs43130->regmap, CS43130_XSP_CLOCK_CONF, clk_data);
+		break;
+	default:
+		return -EINVAL;
+	}
+	switch (frm_size) {
+	case 16:
+		clk_gen = cs43130_get_clk_gen(cs43130->mclk_int,
+						params_rate(params),
+						cs43130_16_clk_gen,
+						ARRAY_SIZE(cs43130_16_clk_gen));
+		break;
+	case 32:
+		clk_gen = cs43130_get_clk_gen(cs43130->mclk_int,
+						params_rate(params),
+						cs43130_32_clk_gen,
+						ARRAY_SIZE(cs43130_32_clk_gen));
+		break;
+	case 48:
+		clk_gen = cs43130_get_clk_gen(cs43130->mclk_int,
+						params_rate(params),
+						cs43130_48_clk_gen,
+						ARRAY_SIZE(cs43130_48_clk_gen));
+		break;
+	case 64:
+		clk_gen = cs43130_get_clk_gen(cs43130->mclk_int,
+						params_rate(params),
+						cs43130_64_clk_gen,
+						ARRAY_SIZE(cs43130_64_clk_gen));
+		break;
+	default:
+		return -EINVAL;
+	}
+	if (!clk_gen)
+		return -EINVAL;
+	switch (dai_id) {
+	case CS43130_ASP_PCM_DAI:
+	case CS43130_ASP_DOP_DAI:
+		regmap_write(cs43130->regmap, CS43130_ASP_DEN_1,
+				(clk_gen->den & CS43130_SP_M_LSB_DATA_MASK) >>
+				CS43130_SP_M_LSB_DATA_SHIFT);
+		regmap_write(cs43130->regmap, CS43130_ASP_DEN_2,
+				(clk_gen->den & CS43130_SP_M_MSB_DATA_MASK) >>
+				CS43130_SP_M_MSB_DATA_SHIFT);
+		regmap_write(cs43130->regmap, CS43130_ASP_NUM_1,
+				(clk_gen->num & CS43130_SP_N_LSB_DATA_MASK) >>
+				CS43130_SP_N_LSB_DATA_SHIFT);
+		regmap_write(cs43130->regmap, CS43130_ASP_NUM_2,
+				(clk_gen->num & CS43130_SP_N_MSB_DATA_MASK) >>
+				CS43130_SP_N_MSB_DATA_SHIFT);
+		break;
+	case CS43130_XSP_DOP_DAI:
+		regmap_write(cs43130->regmap, CS43130_XSP_DEN_1,
+				(clk_gen->den & CS43130_SP_M_LSB_DATA_MASK) >>
+				CS43130_SP_M_LSB_DATA_SHIFT);
+		regmap_write(cs43130->regmap, CS43130_XSP_DEN_2,
+				(clk_gen->den & CS43130_SP_M_MSB_DATA_MASK) >>
+				CS43130_SP_M_MSB_DATA_SHIFT);
+		regmap_write(cs43130->regmap, CS43130_XSP_NUM_1,
+				(clk_gen->num & CS43130_SP_N_LSB_DATA_MASK) >>
+				CS43130_SP_N_LSB_DATA_SHIFT);
+		regmap_write(cs43130->regmap, CS43130_XSP_NUM_2,
+				(clk_gen->num & CS43130_SP_N_MSB_DATA_MASK) >>
+				CS43130_SP_N_MSB_DATA_SHIFT);
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int cs43130_hw_params(struct snd_pcm_substream *substream,
+				struct snd_pcm_hw_params *params,
+				struct snd_soc_dai *dai)
+{
+	struct snd_soc_component *component = dai->component;
+	struct cs43130_priv *cs43130 =
+				snd_soc_component_get_drvdata(component);
+	const struct cs43130_rate_map *rate_map;
+	unsigned int sclk = cs43130->dais[dai->id].sclk;
+	unsigned int bitwidth_sclk;
+	unsigned int bitwidth_dai = (unsigned int)(params_width(params));
+	unsigned int dop_rate = (unsigned int)(params_rate(params));
+	unsigned int required_clk, ret;
+	u8 dsd_speed;
+
+	cs43130->pll_bypass = true;
+	cs43130_pcm_pdn(component);
+	mutex_lock(&cs43130->clk_mutex);
+	if (!cs43130->clk_req) {
+		/* no DAI is currently using clk */
+		if (!(CS43130_MCLK_22M % params_rate(params))) {
+			required_clk = CS43130_MCLK_22M;
+			cs43130->mclk_int =  CS43130_MCLK_22M;
+			gpiod_set_value_cansleep(snd_allo_clk44gpio, 1);
+			gpiod_set_value_cansleep(snd_allo_clk48gpio, 0);
+			usleep_range(13500, 14000);
+		} else {
+			required_clk = CS43130_MCLK_24M;
+			cs43130->mclk_int =  CS43130_MCLK_24M;
+			gpiod_set_value_cansleep(snd_allo_clk48gpio, 1);
+			gpiod_set_value_cansleep(snd_allo_clk44gpio, 0);
+			usleep_range(13500, 14000);
+		}
+		if (cs43130->pll_bypass)
+			cs43130_change_clksrc(component, CS43130_MCLK_SRC_EXT);
+		else
+			cs43130_change_clksrc(component, CS43130_MCLK_SRC_PLL);
+	}
+
+	cs43130->clk_req++;
+	mutex_unlock(&cs43130->clk_mutex);
+
+	switch (dai->id) {
+	case CS43130_ASP_DOP_DAI:
+	case CS43130_XSP_DOP_DAI:
+		/* DoP bitwidth is always 24-bit */
+		bitwidth_dai = 24;
+		sclk = params_rate(params) * bitwidth_dai *
+				params_channels(params);
+
+		switch (params_rate(params)) {
+		case 176400:
+			dsd_speed = 0;
+			break;
+		case 352800:
+			dsd_speed = 1;
+			break;
+		default:
+			dev_err(component->dev, "Rate(%u) not supported\n",
+				params_rate(params));
+			return -EINVAL;
+		}
+
+		regmap_update_bits(cs43130->regmap, CS43130_DSD_PATH_CTL_2,
+					CS43130_DSD_SPEED_MASK,
+					dsd_speed << CS43130_DSD_SPEED_SHIFT);
+		break;
+	case CS43130_ASP_PCM_DAI:
+		rate_map = cs43130_get_rate_table(params_rate(params));
+		if (!rate_map)
+			return -EINVAL;
+
+		regmap_write(cs43130->regmap, CS43130_SP_SRATE, rate_map->val);
+		if ((dop_rate == 176400) && (bitwidth_dai == 24)) {
+			dsd_speed = 0;
+			regmap_update_bits(cs43130->regmap,
+					CS43130_DSD_PATH_CTL_2,
+					CS43130_DSD_SPEED_MASK,
+					dsd_speed << CS43130_DSD_SPEED_SHIFT);
+			regmap_update_bits(cs43130->regmap,
+					CS43130_DSD_PATH_CTL_2,
+					CS43130_DSD_SRC_MASK,
+					CS43130_DSD_SRC_ASP <<
+					CS43130_DSD_SRC_SHIFT);
+			regmap_update_bits(cs43130->regmap,
+					CS43130_DSD_PATH_CTL_2,
+					CS43130_DSD_EN_MASK, 0x01 <<
+					CS43130_DSD_EN_SHIFT);
+		}
+		break;
+	default:
+		dev_err(component->dev, "Invalid DAI (%d)\n", dai->id);
+		return -EINVAL;
+	}
+
+	switch (dai->id) {
+	case CS43130_ASP_DOP_DAI:
+		regmap_update_bits(cs43130->regmap, CS43130_DSD_PATH_CTL_2,
+				CS43130_DSD_SRC_MASK, CS43130_DSD_SRC_ASP <<
+				CS43130_DSD_SRC_SHIFT);
+		regmap_update_bits(cs43130->regmap, CS43130_DSD_PATH_CTL_2,
+				CS43130_DSD_EN_MASK, 0x01 <<
+				CS43130_DSD_EN_SHIFT);
+		break;
+	case CS43130_XSP_DOP_DAI:
+		regmap_update_bits(cs43130->regmap, CS43130_DSD_PATH_CTL_2,
+				CS43130_DSD_SRC_MASK, CS43130_DSD_SRC_XSP <<
+				CS43130_DSD_SRC_SHIFT);
+		break;
+	}
+	if (!sclk && cs43130->dais[dai->id].dai_mode ==
+						SND_SOC_DAIFMT_CBM_CFM) {
+		/* Calculate SCLK in master mode if unassigned */
+		sclk = params_rate(params) * bitwidth_dai *
+				params_channels(params);
+	}
+	if (!sclk) {
+		/* at this point, SCLK must be set */
+		dev_err(component->dev, "SCLK freq is not set\n");
+		return -EINVAL;
+	}
+
+	bitwidth_sclk = (sclk / params_rate(params)) / params_channels(params);
+	if (bitwidth_sclk < bitwidth_dai) {
+		dev_err(component->dev, "Format not supported: SCLK freq is too low\n");
+		return -EINVAL;
+	}
+
+	dev_dbg(component->dev,
+		"sclk = %u, fs = %d, bitwidth_dai = %u\n",
+		sclk, params_rate(params), bitwidth_dai);
+
+	dev_dbg(component->dev,
+		"bitwidth_sclk = %u, num_ch = %u\n",
+		bitwidth_sclk, params_channels(params));
+
+	cs43130_set_bitwidth(dai->id, bitwidth_dai, cs43130->regmap);
+	cs43130_set_sp_fmt(dai->id, bitwidth_sclk, params, cs43130);
+	ret = cs43130_pwr_up_asp_dac(component);
+	return 0;
+}
+
+static int cs43130_hw_free(struct snd_pcm_substream *substream,
+					struct snd_soc_dai *dai)
+{
+	struct snd_soc_component *component = dai->component;
+	struct cs43130_priv *cs43130 =
+				snd_soc_component_get_drvdata(component);
+
+	mutex_lock(&cs43130->clk_mutex);
+	cs43130->clk_req--;
+	if (!cs43130->clk_req) {
+		/* no DAI is currently using clk */
+		cs43130_change_clksrc(component, CS43130_MCLK_SRC_RCO);
+		cs43130_pcm_pdn(component);
+	}
+	mutex_unlock(&cs43130->clk_mutex);
+
+	return 0;
+}
+
+static const unsigned int cs43130_asp_src_rates[] = {
+	32000, 44100, 48000, 88200, 96000, 176400, 192000
+};
+
+static const struct snd_pcm_hw_constraint_list cs43130_asp_constraints = {
+	.count  = ARRAY_SIZE(cs43130_asp_src_rates),
+	.list   = cs43130_asp_src_rates,
+};
+
+static int cs43130_pcm_startup(struct snd_pcm_substream *substream,
+					struct snd_soc_dai *dai)
+{
+	return snd_pcm_hw_constraint_list(substream->runtime, 0,
+					SNDRV_PCM_HW_PARAM_RATE,
+					&cs43130_asp_constraints);
+}
+
+static int cs43130_pcm_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+	struct snd_soc_component *component = codec_dai->component;
+	struct cs43130_priv *cs43130 =
+				snd_soc_component_get_drvdata(component);
+
+	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+	case SND_SOC_DAIFMT_CBS_CFS:
+		cs43130->dais[codec_dai->id].dai_mode = SND_SOC_DAIFMT_CBS_CFS;
+		break;
+	case SND_SOC_DAIFMT_CBM_CFM:
+		cs43130->dais[codec_dai->id].dai_mode = SND_SOC_DAIFMT_CBM_CFM;
+		break;
+	default:
+		dev_err(component->dev, "unsupported mode\n");
+		return -EINVAL;
+	}
+
+	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_I2S:
+		cs43130->dais[codec_dai->id].dai_format = SND_SOC_DAIFMT_I2S;
+		break;
+	case SND_SOC_DAIFMT_LEFT_J:
+		cs43130->dais[codec_dai->id].dai_format = SND_SOC_DAIFMT_LEFT_J;
+		break;
+	default:
+		dev_err(component->dev,
+			"unsupported audio format\n");
+		return -EINVAL;
+	}
+
+	dev_dbg(component->dev, "dai_id = %d,  dai_mode = %u, dai_format = %u\n",
+			codec_dai->id,
+			cs43130->dais[codec_dai->id].dai_mode,
+			cs43130->dais[codec_dai->id].dai_format);
+
+	return 0;
+}
+
+static int cs43130_set_sysclk(struct snd_soc_dai *codec_dai,
+					int clk_id, unsigned int freq, int dir)
+{
+	struct snd_soc_component *component = codec_dai->component;
+	struct cs43130_priv *cs43130 =
+				snd_soc_component_get_drvdata(component);
+
+	cs43130->dais[codec_dai->id].sclk = freq;
+	dev_dbg(component->dev, "dai_id = %d,  sclk = %u\n", codec_dai->id,
+				cs43130->dais[codec_dai->id].sclk);
+
+	return 0;
+}
+
+static int cs43130_component_set_sysclk(struct snd_soc_component *component,
+					int clk_id, int source,
+					unsigned int freq, int dir)
+{
+	struct cs43130_priv *cs43130 =
+				snd_soc_component_get_drvdata(component);
+
+	dev_dbg(component->dev, "clk_id = %d, source = %d, freq = %d, dir = %d\n",
+		clk_id, source, freq, dir);
+
+	switch (freq) {
+	case CS43130_MCLK_22M:
+	case CS43130_MCLK_24M:
+		cs43130->mclk = freq;
+		break;
+	default:
+		dev_err(component->dev, "Invalid MCLK INT freq: %u\n", freq);
+		return -EINVAL;
+	}
+
+	if (source == CS43130_MCLK_SRC_EXT) {
+		cs43130->pll_bypass = true;
+	} else {
+		dev_err(component->dev, "Invalid MCLK source\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+static u16 const cs43130_ac_freq[CS43130_AC_FREQ] = {
+	24,
+	43,
+	93,
+	200,
+	431,
+	928,
+	2000,
+	4309,
+	9283,
+	20000,
+};
+static const struct snd_soc_dai_ops cs43130_dai_ops = {
+	.startup        = cs43130_pcm_startup,
+	.hw_params	= cs43130_hw_params,
+	.hw_free        = cs43130_hw_free,
+	.set_sysclk     = cs43130_set_sysclk,
+	.set_fmt        = cs43130_pcm_set_fmt,
+};
+
+static struct snd_soc_dai_driver cs43130_codec_dai = {
+	.name = "allo-cs43130",
+	.playback = {
+		.stream_name = "Playback",
+		.channels_min = 2,
+		.channels_max = 2,
+		.rates = SNDRV_PCM_RATE_CONTINUOUS,
+		.rate_min = 44100,
+		.rate_max = 192000,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE |
+			SNDRV_PCM_FMTBIT_S24_LE |
+			SNDRV_PCM_FMTBIT_S32_LE
+
+	},
+	.ops = &cs43130_dai_ops,
+};
+
+static struct snd_soc_component_driver cs43130_component_driver = {
+	.idle_bias_on           = true,
+	.controls		= cs43130_controls,
+	.num_controls		= ARRAY_SIZE(cs43130_controls),
+	.set_sysclk             = cs43130_component_set_sysclk,
+	.idle_bias_on           = 1,
+	.use_pmdown_time        = 1,
+	.endianness             = 1,
+	.non_legacy_dai_naming  = 1,
+};
+
+static const struct regmap_config cs43130_regmap = {
+	.reg_bits               = 24,
+	.pad_bits               = 8,
+	.val_bits               = 8,
+
+	.max_register           = CS43130_LASTREG,
+	.reg_defaults           = cs43130_reg_defaults,
+	.num_reg_defaults       = ARRAY_SIZE(cs43130_reg_defaults),
+	.readable_reg           = cs43130_readable_register,
+	.precious_reg           = cs43130_precious_register,
+	.volatile_reg           = cs43130_volatile_register,
+	.cache_type             = REGCACHE_RBTREE,
+	/* needed for regcache_sync */
+	.use_single_read        = true,
+	.use_single_write       = true,
+};
+
+static u16 const cs43130_dc_threshold[CS43130_DC_THRESHOLD] = {
+	50,
+	120,
+};
+
+static int cs43130_handle_device_data(struct i2c_client *i2c_client,
+					struct cs43130_priv *cs43130)
+{
+	struct device_node *np = i2c_client->dev.of_node;
+	unsigned int val;
+	int i;
+
+	if (of_property_read_u32(np, "cirrus,xtal-ibias", &val) < 0) {
+	/* Crystal is unused. System clock is used for external MCLK */
+		cs43130->xtal_ibias = CS43130_XTAL_UNUSED;
+		return 0;
+	}
+
+	switch (val) {
+	case 1:
+		cs43130->xtal_ibias = CS43130_XTAL_IBIAS_7_5UA;
+		break;
+	case 2:
+		cs43130->xtal_ibias = CS43130_XTAL_IBIAS_12_5UA;
+		break;
+	case 3:
+		cs43130->xtal_ibias = CS43130_XTAL_IBIAS_15UA;
+		break;
+	default:
+		dev_err(&i2c_client->dev,
+			"Invalid cirrus,xtal-ibias value: %d\n", val);
+		return -EINVAL;
+	}
+
+	cs43130->dc_meas = of_property_read_bool(np, "cirrus,dc-measure");
+	cs43130->ac_meas = of_property_read_bool(np, "cirrus,ac-measure");
+
+	if (of_property_read_u16_array(np, "cirrus,ac-freq", cs43130->ac_freq,
+					CS43130_AC_FREQ) < 0) {
+		for (i = 0; i < CS43130_AC_FREQ; i++)
+			cs43130->ac_freq[i] = cs43130_ac_freq[i];
+	}
+
+	if (of_property_read_u16_array(np, "cirrus,dc-threshold",
+					cs43130->dc_threshold,
+					CS43130_DC_THRESHOLD) < 0) {
+		for (i = 0; i < CS43130_DC_THRESHOLD; i++)
+			cs43130->dc_threshold[i] = cs43130_dc_threshold[i];
+	}
+
+	return 0;
+}
+
+
+static int allo_cs43130_component_probe(struct i2c_client *i2c,
+			     const struct i2c_device_id *id)
+{
+	struct regmap *regmap;
+	struct regmap_config config = cs43130_regmap;
+	struct device *dev = &i2c->dev;
+	struct cs43130_priv *cs43130;
+	unsigned int devid = 0;
+	unsigned int reg;
+	int ret;
+
+	regmap = devm_regmap_init_i2c(i2c, &config);
+	if (IS_ERR(regmap))
+		return PTR_ERR(regmap);
+
+	cs43130 = devm_kzalloc(dev, sizeof(struct cs43130_priv),
+					GFP_KERNEL);
+	if (!cs43130)
+		return -ENOMEM;
+
+	dev_set_drvdata(dev, cs43130);
+	cs43130->regmap = regmap;
+
+	if (i2c->dev.of_node) {
+		ret = cs43130_handle_device_data(i2c, cs43130);
+		if (ret != 0)
+			return ret;
+	}
+	usleep_range(2000, 2050);
+
+	ret = regmap_read(cs43130->regmap, CS43130_DEVID_AB, &reg);
+	devid = (reg & 0xFF) << 12;
+	ret = regmap_read(cs43130->regmap, CS43130_DEVID_CD, &reg);
+	devid |= (reg & 0xFF) << 4;
+	ret = regmap_read(cs43130->regmap, CS43130_DEVID_E, &reg);
+	devid |= (reg & 0xF0) >> 4;
+	if (devid != CS43198_CHIP_ID) {
+		dev_err(dev, "Failed to read Chip or wrong Chip id: %d\n", ret);
+		return ret;
+	}
+
+	cs43130->mclk_int_src = CS43130_MCLK_SRC_RCO;
+	msleep(20);
+
+	ret = snd_soc_register_component(dev, &cs43130_component_driver,
+				    &cs43130_codec_dai, 1);
+	if (ret != 0) {
+		dev_err(dev, "failed to register codec: %d\n", ret);
+		return ret;
+	}
+	regmap_update_bits(cs43130->regmap, CS43130_PAD_INT_CFG,
+			CS43130_ASP_3ST_MASK, 0);
+	regmap_update_bits(cs43130->regmap, CS43130_PAD_INT_CFG,
+			CS43130_XSP_3ST_MASK, 1);
+	regmap_update_bits(cs43130->regmap, CS43130_PWDN_CTL,
+			CS43130_PDN_HP_MASK, 1 << CS43130_PDN_HP_SHIFT);
+	msleep(20);
+	regmap_write(cs43130->regmap, CS43130_CLASS_H_CTL, 0x06);
+	snd_allo_clk44gpio = devm_gpiod_get(dev, "clock44", GPIOD_OUT_HIGH);
+	if (IS_ERR(snd_allo_clk44gpio))
+		dev_err(dev, "devm_gpiod_get() failed\n");
+
+	snd_allo_clk48gpio = devm_gpiod_get(dev, "clock48", GPIOD_OUT_LOW);
+	if (IS_ERR(snd_allo_clk48gpio))
+		dev_err(dev, "devm_gpiod_get() failed\n");
+
+	return 0;
+}
+
+static int allo_cs43130_component_remove(struct i2c_client *i2c)
+{
+	snd_soc_unregister_component(&i2c->dev);
+	return 0;
+}
+
+static const struct i2c_device_id allo_cs43130_component_id[] = {
+	{ "allo-cs43198", },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, allo_cs43130_component_id);
+
+static const struct of_device_id allo_cs43130_codec_of_match[] = {
+	{ .compatible = "allo,allo-cs43198", },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, allo_cs43130_codec_of_match);
+
+static struct i2c_driver allo_cs43130_component_driver = {
+	.probe		= allo_cs43130_component_probe,
+	.remove		= allo_cs43130_component_remove,
+	.id_table	= allo_cs43130_component_id,
+	.driver		= {
+	.name		= "allo-cs43198",
+	.of_match_table = allo_cs43130_codec_of_match,
+	},
+};
+
+module_i2c_driver(allo_cs43130_component_driver);
+
+MODULE_DESCRIPTION("ASoC Allo Boss2 Codec Driver");
+MODULE_AUTHOR("Sudeepkumar <sudeepkumar@cem-solutions.net>");
+MODULE_LICENSE("GPL v2");
diff -Nur --no-dereference linux-5.10.52-orig/sound/soc/bcm/allo-boss-dac.c linux-5.10.52-v7l+/sound/soc/bcm/allo-boss-dac.c
--- linux-5.10.52-orig/sound/soc/bcm/allo-boss-dac.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/sound/soc/bcm/allo-boss-dac.c	2021-07-25 16:46:40.127718155 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/*
+ * ALSA ASoC Machine Driver for Allo Boss DAC
+ *
+ * Author:	Baswaraj K <jaikumar@cem-solutions.net>
+ *		Copyright 2017
+ *		based on code by Daniel Matuschek,
+ *				 Stuart MacLean <stuart@hifiberry.com>
+ *		based on code by Florian Meier <florian.meier@koalo.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/gpio/consumer.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include "../codecs/pcm512x.h"
+
+#define ALLO_BOSS_NOCLOCK 0
+#define ALLO_BOSS_CLK44EN 1
+#define ALLO_BOSS_CLK48EN 2
+
+struct pcm512x_priv {
+	struct regmap *regmap;
+	struct clk *sclk;
+};
+
+static struct gpio_desc *mute_gpio;
+
+/* Clock rate of CLK44EN attached to GPIO6 pin */
+#define CLK_44EN_RATE 45158400UL
+/* Clock rate of CLK48EN attached to GPIO3 pin */
+#define CLK_48EN_RATE 49152000UL
+
+static bool slave;
+static bool snd_soc_allo_boss_master;
+static bool digital_gain_0db_limit = true;
+
+static void snd_allo_boss_select_clk(struct snd_soc_component *component,
+	int clk_id)
+{
+	switch (clk_id) {
+	case ALLO_BOSS_NOCLOCK:
+		snd_soc_component_update_bits(component, PCM512x_GPIO_CONTROL_1, 0x24, 0x00);
+		break;
+	case ALLO_BOSS_CLK44EN:
+		snd_soc_component_update_bits(component, PCM512x_GPIO_CONTROL_1, 0x24, 0x20);
+		break;
+	case ALLO_BOSS_CLK48EN:
+		snd_soc_component_update_bits(component, PCM512x_GPIO_CONTROL_1, 0x24, 0x04);
+		break;
+	}
+}
+
+static void snd_allo_boss_clk_gpio(struct snd_soc_component *component)
+{
+	snd_soc_component_update_bits(component, PCM512x_GPIO_EN, 0x24, 0x24);
+	snd_soc_component_update_bits(component, PCM512x_GPIO_OUTPUT_3, 0x0f, 0x02);
+	snd_soc_component_update_bits(component, PCM512x_GPIO_OUTPUT_6, 0x0f, 0x02);
+}
+
+static bool snd_allo_boss_is_sclk(struct snd_soc_component *component)
+{
+	unsigned int sck;
+
+	sck = snd_soc_component_read(component, PCM512x_RATE_DET_4);
+	return (!(sck & 0x40));
+}
+
+static bool snd_allo_boss_is_sclk_sleep(
+	struct snd_soc_component *component)
+{
+	msleep(2);
+	return snd_allo_boss_is_sclk(component);
+}
+
+static bool snd_allo_boss_is_master_card(struct snd_soc_component *component)
+{
+	bool isClk44EN, isClk48En, isNoClk;
+
+	snd_allo_boss_clk_gpio(component);
+
+	snd_allo_boss_select_clk(component, ALLO_BOSS_CLK44EN);
+	isClk44EN = snd_allo_boss_is_sclk_sleep(component);
+
+	snd_allo_boss_select_clk(component, ALLO_BOSS_NOCLOCK);
+	isNoClk = snd_allo_boss_is_sclk_sleep(component);
+
+	snd_allo_boss_select_clk(component, ALLO_BOSS_CLK48EN);
+	isClk48En = snd_allo_boss_is_sclk_sleep(component);
+
+	return (isClk44EN && isClk48En && !isNoClk);
+}
+
+static int snd_allo_boss_clk_for_rate(int sample_rate)
+{
+	int type;
+
+	switch (sample_rate) {
+	case 11025:
+	case 22050:
+	case 44100:
+	case 88200:
+	case 176400:
+	case 352800:
+		type = ALLO_BOSS_CLK44EN;
+		break;
+	default:
+		type = ALLO_BOSS_CLK48EN;
+		break;
+	}
+	return type;
+}
+
+static void snd_allo_boss_set_sclk(struct snd_soc_component *component,
+	int sample_rate)
+{
+	struct pcm512x_priv *pcm512x = snd_soc_component_get_drvdata(component);
+
+	if (!IS_ERR(pcm512x->sclk)) {
+		int ctype;
+
+		ctype =	snd_allo_boss_clk_for_rate(sample_rate);
+		clk_set_rate(pcm512x->sclk, (ctype == ALLO_BOSS_CLK44EN)
+				? CLK_44EN_RATE : CLK_48EN_RATE);
+		snd_allo_boss_select_clk(component, ctype);
+	}
+}
+
+static int snd_allo_boss_init(struct snd_soc_pcm_runtime *rtd)
+{
+	struct snd_soc_component *component = asoc_rtd_to_codec(rtd, 0)->component;
+	struct pcm512x_priv *priv = snd_soc_component_get_drvdata(component);
+
+	if (slave)
+		snd_soc_allo_boss_master = false;
+	else
+		snd_soc_allo_boss_master =
+			snd_allo_boss_is_master_card(component);
+
+	if (snd_soc_allo_boss_master) {
+		struct snd_soc_dai_link *dai = rtd->dai_link;
+
+		dai->name = "BossDAC";
+		dai->stream_name = "Boss DAC HiFi [Master]";
+		dai->dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
+			| SND_SOC_DAIFMT_CBM_CFM;
+
+		snd_soc_component_update_bits(component, PCM512x_BCLK_LRCLK_CFG, 0x31, 0x11);
+		snd_soc_component_update_bits(component, PCM512x_MASTER_MODE, 0x03, 0x03);
+		snd_soc_component_update_bits(component, PCM512x_MASTER_CLKDIV_2, 0x7f, 63);
+		/*
+		* Default sclk to CLK_48EN_RATE, otherwise codec
+		*  pcm512x_dai_startup_master method could call
+		*  snd_pcm_hw_constraint_ratnums using CLK_44EN/64
+		*  which will mask 384k sample rate.
+		*/
+		if (!IS_ERR(priv->sclk))
+			clk_set_rate(priv->sclk, CLK_48EN_RATE);
+	} else {
+		priv->sclk = ERR_PTR(-ENOENT);
+	}
+
+	snd_soc_component_update_bits(component, PCM512x_GPIO_EN, 0x08, 0x08);
+	snd_soc_component_update_bits(component, PCM512x_GPIO_OUTPUT_4, 0x0f, 0x02);
+	snd_soc_component_update_bits(component, PCM512x_GPIO_CONTROL_1, 0x08, 0x08);
+
+	if (digital_gain_0db_limit) {
+		int ret;
+		struct snd_soc_card *card = rtd->card;
+
+		ret = snd_soc_limit_volume(card, "Digital Playback Volume",
+				207);
+		if (ret < 0)
+			dev_warn(card->dev, "Failed to set volume limit: %d\n",
+					ret);
+	}
+
+	return 0;
+}
+
+static int snd_allo_boss_update_rate_den(
+	struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_component *component = asoc_rtd_to_codec(rtd, 0)->component;
+	struct pcm512x_priv *pcm512x = snd_soc_component_get_drvdata(component);
+	struct snd_ratnum *rats_no_pll;
+	unsigned int num = 0, den = 0;
+	int err;
+
+	rats_no_pll = devm_kzalloc(rtd->dev, sizeof(*rats_no_pll), GFP_KERNEL);
+	if (!rats_no_pll)
+		return -ENOMEM;
+
+	rats_no_pll->num = clk_get_rate(pcm512x->sclk) / 64;
+	rats_no_pll->den_min = 1;
+	rats_no_pll->den_max = 128;
+	rats_no_pll->den_step = 1;
+
+	err = snd_interval_ratnum(hw_param_interval(params,
+		SNDRV_PCM_HW_PARAM_RATE), 1, rats_no_pll, &num, &den);
+	if (err >= 0 && den) {
+		params->rate_num = num;
+		params->rate_den = den;
+	}
+
+	devm_kfree(rtd->dev, rats_no_pll);
+	return 0;
+}
+
+static void snd_allo_boss_gpio_mute(struct snd_soc_card *card)
+{
+	if (mute_gpio)
+		gpiod_set_value_cansleep(mute_gpio, 1);
+}
+
+static void snd_allo_boss_gpio_unmute(struct snd_soc_card *card)
+{
+	if (mute_gpio)
+		gpiod_set_value_cansleep(mute_gpio, 0);
+}
+
+static int snd_allo_boss_set_bias_level(struct snd_soc_card *card,
+	struct snd_soc_dapm_context *dapm, enum snd_soc_bias_level level)
+{
+	struct snd_soc_pcm_runtime *rtd;
+	struct snd_soc_dai *codec_dai;
+
+	rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[0]);
+	codec_dai = asoc_rtd_to_codec(rtd, 0);
+
+	if (dapm->dev != codec_dai->dev)
+		return 0;
+
+	switch (level) {
+	case SND_SOC_BIAS_PREPARE:
+		if (dapm->bias_level != SND_SOC_BIAS_STANDBY)
+			break;
+		/* UNMUTE DAC */
+		snd_allo_boss_gpio_unmute(card);
+		break;
+
+	case SND_SOC_BIAS_STANDBY:
+		if (dapm->bias_level != SND_SOC_BIAS_PREPARE)
+			break;
+		/* MUTE DAC */
+		snd_allo_boss_gpio_mute(card);
+		break;
+
+	default:
+		break;
+	}
+
+	return 0;
+}
+
+static int snd_allo_boss_hw_params(
+	struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params)
+{
+	int ret = 0;
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	int channels = params_channels(params);
+	int width = snd_pcm_format_physical_width(params_format(params));
+
+	if (snd_soc_allo_boss_master) {
+		struct snd_soc_component *component = asoc_rtd_to_codec(rtd, 0)->component;
+
+		snd_allo_boss_set_sclk(component,
+			params_rate(params));
+
+		ret = snd_allo_boss_update_rate_den(
+			substream, params);
+		if (ret)
+			return ret;
+	}
+
+	ret = snd_soc_dai_set_bclk_ratio(asoc_rtd_to_cpu(rtd, 0), channels * width);
+	if (ret)
+		return ret;
+	ret = snd_soc_dai_set_bclk_ratio(asoc_rtd_to_codec(rtd, 0), channels * width);
+	return ret;
+}
+
+static int snd_allo_boss_startup(
+	struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_component *component = asoc_rtd_to_codec(rtd, 0)->component;
+	struct snd_soc_card *card = rtd->card;
+
+	snd_soc_component_update_bits(component, PCM512x_GPIO_CONTROL_1, 0x08, 0x08);
+	snd_allo_boss_gpio_mute(card);
+
+	if (snd_soc_allo_boss_master) {
+		struct pcm512x_priv *priv = snd_soc_component_get_drvdata(component);
+		/*
+		 * Default sclk to CLK_48EN_RATE, otherwise codec
+		 * pcm512x_dai_startup_master method could call
+		 * snd_pcm_hw_constraint_ratnums using CLK_44EN/64
+		 * which will mask 384k sample rate.
+		 */
+		if (!IS_ERR(priv->sclk))
+			clk_set_rate(priv->sclk, CLK_48EN_RATE);
+	}
+
+	return 0;
+}
+
+static void snd_allo_boss_shutdown(
+	struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_component *component = asoc_rtd_to_codec(rtd, 0)->component;
+
+	snd_soc_component_update_bits(component, PCM512x_GPIO_CONTROL_1, 0x08, 0x00);
+}
+
+static int snd_allo_boss_prepare(
+	struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_card *card = rtd->card;
+
+	snd_allo_boss_gpio_unmute(card);
+	return 0;
+}
+/* machine stream operations */
+static struct snd_soc_ops snd_allo_boss_ops = {
+	.hw_params = snd_allo_boss_hw_params,
+	.startup = snd_allo_boss_startup,
+	.shutdown = snd_allo_boss_shutdown,
+	.prepare = snd_allo_boss_prepare,
+};
+
+SND_SOC_DAILINK_DEFS(allo_boss,
+	DAILINK_COMP_ARRAY(COMP_CPU("bcm2708-i2s.0")),
+	DAILINK_COMP_ARRAY(COMP_CODEC("pcm512x.1-004d", "pcm512x-hifi")),
+	DAILINK_COMP_ARRAY(COMP_PLATFORM("bcm2708-i2s.0")));
+
+static struct snd_soc_dai_link snd_allo_boss_dai[] = {
+{
+	.name		= "Boss DAC",
+	.stream_name	= "Boss DAC HiFi",
+	.dai_fmt	= SND_SOC_DAIFMT_I2S |
+			  SND_SOC_DAIFMT_NB_NF |
+			  SND_SOC_DAIFMT_CBS_CFS,
+	.ops		= &snd_allo_boss_ops,
+	.init		= snd_allo_boss_init,
+	SND_SOC_DAILINK_REG(allo_boss),
+},
+};
+
+/* audio machine driver */
+static struct snd_soc_card snd_allo_boss = {
+	.name         = "BossDAC",
+	.owner        = THIS_MODULE,
+	.dai_link     = snd_allo_boss_dai,
+	.num_links    = ARRAY_SIZE(snd_allo_boss_dai),
+};
+
+static int snd_allo_boss_probe(struct platform_device *pdev)
+{
+	int ret = 0;
+
+	snd_allo_boss.dev = &pdev->dev;
+
+	if (pdev->dev.of_node) {
+		struct device_node *i2s_node;
+		struct snd_soc_dai_link *dai;
+
+		dai = &snd_allo_boss_dai[0];
+		i2s_node = of_parse_phandle(pdev->dev.of_node,
+					    "i2s-controller", 0);
+
+		if (i2s_node) {
+			dai->cpus->dai_name = NULL;
+			dai->cpus->of_node = i2s_node;
+			dai->platforms->name = NULL;
+			dai->platforms->of_node = i2s_node;
+		}
+
+		digital_gain_0db_limit = !of_property_read_bool(
+			pdev->dev.of_node, "allo,24db_digital_gain");
+		slave = of_property_read_bool(pdev->dev.of_node,
+						"allo,slave");
+
+		mute_gpio = devm_gpiod_get_optional(&pdev->dev, "mute",
+							GPIOD_OUT_LOW);
+		if (IS_ERR(mute_gpio)) {
+			ret = PTR_ERR(mute_gpio);
+			dev_err(&pdev->dev,
+				"failed to get mute gpio: %d\n", ret);
+			return ret;
+		}
+
+		if (mute_gpio)
+			snd_allo_boss.set_bias_level =
+				snd_allo_boss_set_bias_level;
+
+		ret = snd_soc_register_card(&snd_allo_boss);
+		if (ret) {
+			dev_err(&pdev->dev,
+				"snd_soc_register_card() failed: %d\n", ret);
+			return ret;
+		}
+
+		if (mute_gpio)
+			snd_allo_boss_gpio_mute(&snd_allo_boss);
+
+		return 0;
+	}
+
+	return -EINVAL;
+}
+
+static int snd_allo_boss_remove(struct platform_device *pdev)
+{
+	snd_allo_boss_gpio_mute(&snd_allo_boss);
+	return snd_soc_unregister_card(&snd_allo_boss);
+}
+
+static const struct of_device_id snd_allo_boss_of_match[] = {
+	{ .compatible = "allo,boss-dac", },
+	{ /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, snd_allo_boss_of_match);
+
+static struct platform_driver snd_allo_boss_driver = {
+	.driver = {
+		.name   = "snd-allo-boss-dac",
+		.owner  = THIS_MODULE,
+		.of_match_table = snd_allo_boss_of_match,
+	},
+	.probe          = snd_allo_boss_probe,
+	.remove         = snd_allo_boss_remove,
+};
+
+module_platform_driver(snd_allo_boss_driver);
+
+MODULE_AUTHOR("Baswaraj K <jaikumar@cem-solutions.net>");
+MODULE_DESCRIPTION("ALSA ASoC Machine Driver for Allo Boss DAC");
+MODULE_LICENSE("GPL v2");
diff -Nur --no-dereference linux-5.10.52-orig/sound/soc/bcm/allo-katana-codec.c linux-5.10.52-v7l+/sound/soc/bcm/allo-katana-codec.c
--- linux-5.10.52-orig/sound/soc/bcm/allo-katana-codec.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/sound/soc/bcm/allo-katana-codec.c	2021-07-25 16:46:40.127718155 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/*
+ * Driver for the ALLO KATANA CODEC
+ *
+ * Author: Jaikumar <jaikumar@cem-solutions.net>
+ *		Copyright 2018
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/clk.h>
+#include <linux/kernel.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/gcd.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/pcm_params.h>
+#include <sound/tlv.h>
+#include <linux/i2c.h>
+
+
+#define KATANA_CODEC_CHIP_ID		0x30
+#define KATANA_CODEC_VIRT_BASE		0x100
+#define KATANA_CODEC_PAGE		0
+
+#define KATANA_CODEC_CHIP_ID_REG	(KATANA_CODEC_VIRT_BASE + 0)
+#define KATANA_CODEC_RESET		(KATANA_CODEC_VIRT_BASE + 1)
+#define KATANA_CODEC_VOLUME_1		(KATANA_CODEC_VIRT_BASE + 2)
+#define KATANA_CODEC_VOLUME_2		(KATANA_CODEC_VIRT_BASE + 3)
+#define KATANA_CODEC_MUTE		(KATANA_CODEC_VIRT_BASE + 4)
+#define KATANA_CODEC_DSP_PROGRAM	(KATANA_CODEC_VIRT_BASE + 5)
+#define KATANA_CODEC_DEEMPHASIS		(KATANA_CODEC_VIRT_BASE + 6)
+#define KATANA_CODEC_DOP		(KATANA_CODEC_VIRT_BASE + 7)
+#define KATANA_CODEC_FORMAT		(KATANA_CODEC_VIRT_BASE + 8)
+#define KATANA_CODEC_COMMAND		(KATANA_CODEC_VIRT_BASE + 9)
+#define KATANA_CODEC_MUTE_STREAM	(KATANA_CODEC_VIRT_BASE + 10)
+
+#define KATANA_CODEC_MAX_REGISTER	(KATANA_CODEC_VIRT_BASE + 10)
+
+#define KATANA_CODEC_FMT		0xff
+#define KATANA_CODEC_CHAN_MONO		0x00
+#define KATANA_CODEC_CHAN_STEREO	0x80
+#define KATANA_CODEC_ALEN_16		0x10
+#define KATANA_CODEC_ALEN_24		0x20
+#define KATANA_CODEC_ALEN_32		0x30
+#define KATANA_CODEC_RATE_11025		0x01
+#define KATANA_CODEC_RATE_22050		0x02
+#define KATANA_CODEC_RATE_32000		0x03
+#define KATANA_CODEC_RATE_44100		0x04
+#define KATANA_CODEC_RATE_48000		0x05
+#define KATANA_CODEC_RATE_88200		0x06
+#define KATANA_CODEC_RATE_96000		0x07
+#define KATANA_CODEC_RATE_176400	0x08
+#define KATANA_CODEC_RATE_192000	0x09
+#define KATANA_CODEC_RATE_352800	0x0a
+#define KATANA_CODEC_RATE_384000	0x0b
+
+
+struct katana_codec_priv {
+	struct regmap *regmap;
+	int fmt;
+};
+
+static const struct reg_default katana_codec_reg_defaults[] = {
+	{ KATANA_CODEC_RESET,		0x00 },
+	{ KATANA_CODEC_VOLUME_1,	0xF0 },
+	{ KATANA_CODEC_VOLUME_2,	0xF0 },
+	{ KATANA_CODEC_MUTE,		0x00 },
+	{ KATANA_CODEC_DSP_PROGRAM,	0x04 },
+	{ KATANA_CODEC_DEEMPHASIS,	0x00 },
+	{ KATANA_CODEC_DOP,		0x00 },
+	{ KATANA_CODEC_FORMAT,		0xb4 },
+};
+
+static const char * const katana_codec_dsp_program_texts[] = {
+	"Linear Phase Fast Roll-off Filter",
+	"Linear Phase Slow Roll-off Filter",
+	"Minimum Phase Fast Roll-off Filter",
+	"Minimum Phase Slow Roll-off Filter",
+	"Apodizing Fast Roll-off Filter",
+	"Corrected Minimum Phase Fast Roll-off Filter",
+	"Brick Wall Filter",
+};
+
+static const unsigned int katana_codec_dsp_program_values[] = {
+	0,
+	1,
+	2,
+	3,
+	4,
+	6,
+	7,
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(katana_codec_dsp_program,
+				  KATANA_CODEC_DSP_PROGRAM, 0, 0x07,
+				  katana_codec_dsp_program_texts,
+				  katana_codec_dsp_program_values);
+
+static const char * const katana_codec_deemphasis_texts[] = {
+	"Bypass",
+	"32kHz",
+	"44.1kHz",
+	"48kHz",
+};
+
+static const unsigned int katana_codec_deemphasis_values[] = {
+	0,
+	1,
+	2,
+	3,
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(katana_codec_deemphasis,
+				  KATANA_CODEC_DEEMPHASIS, 0, 0x03,
+				  katana_codec_deemphasis_texts,
+				  katana_codec_deemphasis_values);
+
+static const SNDRV_CTL_TLVD_DECLARE_DB_MINMAX(master_tlv, -12750, 0);
+
+static const struct snd_kcontrol_new katana_codec_controls[] = {
+	SOC_DOUBLE_R_TLV("Master Playback Volume", KATANA_CODEC_VOLUME_1,
+			KATANA_CODEC_VOLUME_2, 0, 255, 1, master_tlv),
+	SOC_DOUBLE("Master Playback Switch", KATANA_CODEC_MUTE, 0, 0, 1, 1),
+	SOC_ENUM("DSP Program Route", katana_codec_dsp_program),
+	SOC_ENUM("Deemphasis Route", katana_codec_deemphasis),
+	SOC_SINGLE("DoP Playback Switch", KATANA_CODEC_DOP, 0, 1, 1)
+};
+
+static bool katana_codec_readable_register(struct device *dev,
+				unsigned int reg)
+{
+	switch (reg) {
+	case KATANA_CODEC_CHIP_ID_REG:
+		return true;
+	default:
+		return reg < 0xff;
+	}
+}
+
+static int katana_codec_hw_params(struct snd_pcm_substream *substream,
+			     struct snd_pcm_hw_params *params,
+			     struct snd_soc_dai *dai)
+{
+	struct snd_soc_component *component = dai->component;
+	struct katana_codec_priv *katana_codec =
+		snd_soc_component_get_drvdata(component);
+	int fmt = 0;
+	int ret;
+
+	dev_dbg(component->card->dev, "hw_params %u Hz, %u channels, %u bits\n",
+			params_rate(params),
+			params_channels(params),
+			params_width(params));
+
+	switch (katana_codec->fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+	case SND_SOC_DAIFMT_CBM_CFM: // master
+		if (params_channels(params) == 2)
+			fmt = KATANA_CODEC_CHAN_STEREO;
+		else
+			fmt = KATANA_CODEC_CHAN_MONO;
+
+		switch (params_width(params)) {
+		case 16:
+			fmt |= KATANA_CODEC_ALEN_16;
+			break;
+		case 24:
+			fmt |= KATANA_CODEC_ALEN_24;
+			break;
+		case 32:
+			fmt |= KATANA_CODEC_ALEN_32;
+			break;
+		default:
+			dev_err(component->card->dev, "Bad frame size: %d\n",
+					params_width(params));
+			return -EINVAL;
+		}
+
+		switch (params_rate(params)) {
+		case 44100:
+			fmt |= KATANA_CODEC_RATE_44100;
+			break;
+		case 48000:
+			fmt |= KATANA_CODEC_RATE_48000;
+			break;
+		case 88200:
+			fmt |= KATANA_CODEC_RATE_88200;
+			break;
+		case 96000:
+			fmt |= KATANA_CODEC_RATE_96000;
+			break;
+		case 176400:
+			fmt |= KATANA_CODEC_RATE_176400;
+			break;
+		case 192000:
+			fmt |= KATANA_CODEC_RATE_192000;
+			break;
+		case 352800:
+			fmt |= KATANA_CODEC_RATE_352800;
+			break;
+		case 384000:
+			fmt |= KATANA_CODEC_RATE_384000;
+			break;
+		default:
+			dev_err(component->card->dev, "Bad sample rate: %d\n",
+					params_rate(params));
+			return -EINVAL;
+		}
+
+		ret = regmap_write(katana_codec->regmap, KATANA_CODEC_FORMAT,
+					fmt);
+		if (ret != 0) {
+			dev_err(component->card->dev, "Failed to set format: %d\n", ret);
+			return ret;
+		}
+		break;
+
+	case SND_SOC_DAIFMT_CBS_CFS:
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int katana_codec_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+	struct snd_soc_component *component = dai->component;
+	struct katana_codec_priv *katana_codec =
+		snd_soc_component_get_drvdata(component);
+
+	katana_codec->fmt = fmt;
+
+	return 0;
+}
+
+int katana_codec_dai_mute_stream(struct snd_soc_dai *dai, int mute,
+						int stream)
+{
+	struct snd_soc_component *component = dai->component;
+	struct katana_codec_priv *katana_codec =
+		snd_soc_component_get_drvdata(component);
+	int ret = 0;
+
+	ret = regmap_write(katana_codec->regmap, KATANA_CODEC_MUTE_STREAM,
+				mute);
+	if (ret != 0) {
+		dev_err(component->card->dev, "Failed to set mute: %d\n", ret);
+		return ret;
+	}
+	return ret;
+}
+
+static const struct snd_soc_dai_ops katana_codec_dai_ops = {
+	.mute_stream = katana_codec_dai_mute_stream,
+	.hw_params = katana_codec_hw_params,
+	.set_fmt = katana_codec_set_fmt,
+};
+
+static struct snd_soc_dai_driver katana_codec_dai = {
+	.name = "allo-katana-codec",
+	.playback = {
+		.stream_name = "Playback",
+		.channels_min = 2,
+		.channels_max = 2,
+		.rates = SNDRV_PCM_RATE_CONTINUOUS,
+		.rate_min = 44100,
+		.rate_max = 384000,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE |
+			SNDRV_PCM_FMTBIT_S32_LE
+	},
+	.ops = &katana_codec_dai_ops,
+};
+
+static struct snd_soc_component_driver katana_codec_component_driver = {
+	.idle_bias_on = true,
+
+	.controls		= katana_codec_controls,
+	.num_controls	= ARRAY_SIZE(katana_codec_controls),
+};
+
+static const struct regmap_range_cfg katana_codec_range = {
+	.name = "Pages", .range_min = KATANA_CODEC_VIRT_BASE,
+	.range_max = KATANA_CODEC_MAX_REGISTER,
+	.selector_reg = KATANA_CODEC_PAGE,
+	.selector_mask = 0xff,
+	.window_start = 0, .window_len = 0x100,
+};
+
+const struct regmap_config katana_codec_regmap = {
+	.reg_bits = 8,
+	.val_bits = 8,
+
+	.ranges = &katana_codec_range,
+	.num_ranges = 1,
+
+	.max_register = KATANA_CODEC_MAX_REGISTER,
+	.readable_reg = katana_codec_readable_register,
+	.reg_defaults = katana_codec_reg_defaults,
+	.num_reg_defaults = ARRAY_SIZE(katana_codec_reg_defaults),
+	.cache_type = REGCACHE_RBTREE,
+};
+
+static int allo_katana_component_probe(struct i2c_client *i2c,
+			     const struct i2c_device_id *id)
+{
+	struct regmap *regmap;
+	struct regmap_config config = katana_codec_regmap;
+	struct device *dev = &i2c->dev;
+	struct katana_codec_priv *katana_codec;
+	unsigned int chip_id = 0;
+	int ret;
+
+	regmap = devm_regmap_init_i2c(i2c, &config);
+	if (IS_ERR(regmap))
+		return PTR_ERR(regmap);
+
+	katana_codec = devm_kzalloc(dev, sizeof(struct katana_codec_priv),
+					GFP_KERNEL);
+	if (!katana_codec)
+		return -ENOMEM;
+
+	dev_set_drvdata(dev, katana_codec);
+	katana_codec->regmap = regmap;
+
+	ret = regmap_read(regmap, KATANA_CODEC_CHIP_ID_REG, &chip_id);
+	if ((ret != 0) || (chip_id != KATANA_CODEC_CHIP_ID)) {
+		dev_err(dev, "Failed to read Chip or wrong Chip id: %d\n", ret);
+		return ret;
+	}
+	regmap_update_bits(regmap, KATANA_CODEC_RESET, 0x01, 0x01);
+	msleep(10);
+
+	ret = snd_soc_register_component(dev, &katana_codec_component_driver,
+				    &katana_codec_dai, 1);
+	if (ret != 0) {
+		dev_err(dev, "failed to register codec: %d\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int allo_katana_component_remove(struct i2c_client *i2c)
+{
+	snd_soc_unregister_component(&i2c->dev);
+	return 0;
+}
+
+static const struct i2c_device_id allo_katana_component_id[] = {
+	{ "allo-katana-codec", },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, allo_katana_component_id);
+
+static const struct of_device_id allo_katana_codec_of_match[] = {
+	{ .compatible = "allo,allo-katana-codec", },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, allo_katana_codec_of_match);
+
+static struct i2c_driver allo_katana_component_driver = {
+	.probe		= allo_katana_component_probe,
+	.remove		= allo_katana_component_remove,
+	.id_table	= allo_katana_component_id,
+	.driver		= {
+	.name		= "allo-katana-codec",
+	.of_match_table = allo_katana_codec_of_match,
+	},
+};
+
+module_i2c_driver(allo_katana_component_driver);
+
+MODULE_DESCRIPTION("ASoC Allo Katana Codec Driver");
+MODULE_AUTHOR("Jaikumar <jaikumar@cem-solutions.net>");
+MODULE_LICENSE("GPL v2");
diff -Nur --no-dereference linux-5.10.52-orig/sound/soc/bcm/allo-piano-dac.c linux-5.10.52-v7l+/sound/soc/bcm/allo-piano-dac.c
--- linux-5.10.52-orig/sound/soc/bcm/allo-piano-dac.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/sound/soc/bcm/allo-piano-dac.c	2021-07-25 16:46:40.127718155 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/*
+ * ALSA ASoC Machine Driver for Allo Piano DAC
+ *
+ * Author:	Baswaraj K <jaikumar@cem-solutions.net>
+ *		Copyright 2016
+ *		based on code by Daniel Matuschek <info@crazy-audio.com>
+ *		based on code by Florian Meier <florian.meier@koalo.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+static bool digital_gain_0db_limit = true;
+
+static int snd_allo_piano_dac_init(struct snd_soc_pcm_runtime *rtd)
+{
+	if (digital_gain_0db_limit) {
+		int ret;
+		struct snd_soc_card *card = rtd->card;
+
+		ret = snd_soc_limit_volume(card, "Digital Playback Volume",
+					   207);
+		if (ret < 0)
+			dev_warn(card->dev, "Failed to set volume limit: %d\n",
+				 ret);
+	}
+
+	return 0;
+}
+
+SND_SOC_DAILINK_DEFS(allo_piano_dai,
+	DAILINK_COMP_ARRAY(COMP_CPU("bcm2708-i2s.0")),
+	DAILINK_COMP_ARRAY(COMP_CODEC("pcm512x.1-004c", "pcm512x-hifi")),
+	DAILINK_COMP_ARRAY(COMP_PLATFORM("bcm2708-i2s.0")));
+
+static struct snd_soc_dai_link snd_allo_piano_dac_dai[] = {
+{
+	.name		= "Piano DAC",
+	.stream_name	= "Piano DAC HiFi",
+	.dai_fmt	= SND_SOC_DAIFMT_I2S |
+			  SND_SOC_DAIFMT_NB_NF |
+			  SND_SOC_DAIFMT_CBS_CFS,
+	.init		= snd_allo_piano_dac_init,
+	SND_SOC_DAILINK_REG(allo_piano_dai),
+},
+};
+
+/* audio machine driver */
+static struct snd_soc_card snd_allo_piano_dac = {
+	.name         = "PianoDAC",
+	.owner        = THIS_MODULE,
+	.dai_link     = snd_allo_piano_dac_dai,
+	.num_links    = ARRAY_SIZE(snd_allo_piano_dac_dai),
+};
+
+static int snd_allo_piano_dac_probe(struct platform_device *pdev)
+{
+	int ret = 0;
+
+	snd_allo_piano_dac.dev = &pdev->dev;
+
+	if (pdev->dev.of_node) {
+		struct device_node *i2s_node;
+		struct snd_soc_dai_link *dai;
+
+		dai = &snd_allo_piano_dac_dai[0];
+		i2s_node = of_parse_phandle(pdev->dev.of_node,
+					    "i2s-controller", 0);
+
+		if (i2s_node) {
+			dai->cpus->dai_name = NULL;
+			dai->cpus->of_node = i2s_node;
+			dai->platforms->name = NULL;
+			dai->platforms->of_node = i2s_node;
+		}
+
+		digital_gain_0db_limit = !of_property_read_bool(
+			pdev->dev.of_node, "allo,24db_digital_gain");
+	}
+
+	ret = devm_snd_soc_register_card(&pdev->dev, &snd_allo_piano_dac);
+	if (ret && ret != -EPROBE_DEFER)
+		dev_err(&pdev->dev,
+			"snd_soc_register_card() failed: %d\n", ret);
+
+	return ret;
+}
+
+static const struct of_device_id snd_allo_piano_dac_of_match[] = {
+	{ .compatible = "allo,piano-dac", },
+	{ /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, snd_allo_piano_dac_of_match);
+
+static struct platform_driver snd_allo_piano_dac_driver = {
+	.driver = {
+		.name   = "snd-allo-piano-dac",
+		.owner  = THIS_MODULE,
+		.of_match_table = snd_allo_piano_dac_of_match,
+	},
+	.probe          = snd_allo_piano_dac_probe,
+};
+
+module_platform_driver(snd_allo_piano_dac_driver);
+
+MODULE_AUTHOR("Baswaraj K <jaikumar@cem-solutions.net>");
+MODULE_DESCRIPTION("ALSA ASoC Machine Driver for Allo Piano DAC");
+MODULE_LICENSE("GPL v2");
diff -Nur --no-dereference linux-5.10.52-orig/sound/soc/bcm/allo-piano-dac-plus.c linux-5.10.52-v7l+/sound/soc/bcm/allo-piano-dac-plus.c
--- linux-5.10.52-orig/sound/soc/bcm/allo-piano-dac-plus.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/sound/soc/bcm/allo-piano-dac-plus.c	2021-07-25 16:46:40.127718155 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/*
+ * ALSA ASoC Machine Driver for Allo Piano DAC Plus Subwoofer
+ *
+ * Author:	Baswaraj K <jaikumar@cem-solutions.net>
+ *		Copyright 2020
+ *		based on code by David Knell <david.knell@gmail.com)
+ *		based on code by Daniel Matuschek <info@crazy-audio.com>
+ *		based on code by Florian Meier <florian.meier@koalo.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/gpio/consumer.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <linux/firmware.h>
+#include <linux/delay.h>
+#include <sound/tlv.h>
+#include "../codecs/pcm512x.h"
+
+#define P_DAC_LEFT_MUTE		0x10
+#define P_DAC_RIGHT_MUTE	0x01
+#define P_DAC_MUTE		0x11
+#define P_DAC_UNMUTE		0x00
+#define P_MUTE			1
+#define P_UNMUTE		0
+
+struct dsp_code {
+	char i2c_addr;
+	char offset;
+	char val;
+};
+
+struct glb_pool {
+	struct mutex lock;
+	unsigned int dual_mode;
+	unsigned int set_lowpass;
+	unsigned int set_mode;
+	unsigned int set_rate;
+	unsigned int dsp_page_number;
+};
+
+static bool digital_gain_0db_limit = true;
+bool glb_mclk;
+
+static struct gpio_desc *mute_gpio[2];
+
+static const char * const allo_piano_mode_texts[] = {
+	"None",
+	"2.0",
+	"2.1",
+	"2.2",
+};
+
+static const SOC_ENUM_SINGLE_DECL(allo_piano_mode_enum,
+		0, 0, allo_piano_mode_texts);
+
+static const char * const allo_piano_dual_mode_texts[] = {
+	"None",
+	"Dual-Mono",
+	"Dual-Stereo",
+};
+
+static const SOC_ENUM_SINGLE_DECL(allo_piano_dual_mode_enum,
+		0, 0, allo_piano_dual_mode_texts);
+
+static const char * const allo_piano_dsp_low_pass_texts[] = {
+	"60",
+	"70",
+	"80",
+	"90",
+	"100",
+	"110",
+	"120",
+	"130",
+	"140",
+	"150",
+	"160",
+	"170",
+	"180",
+	"190",
+	"200",
+};
+
+static const SOC_ENUM_SINGLE_DECL(allo_piano_enum,
+		0, 0, allo_piano_dsp_low_pass_texts);
+
+static int __snd_allo_piano_dsp_program(struct snd_soc_pcm_runtime *rtd,
+		unsigned int mode, unsigned int rate, unsigned int lowpass)
+{
+	const struct firmware *fw;
+	struct snd_soc_card *card = rtd->card;
+	struct glb_pool *glb_ptr = card->drvdata;
+	char firmware_name[60];
+	int ret = 0, dac = 0;
+
+	if (rate <= 46000)
+		rate = 44100;
+	else if (rate <= 68000)
+		rate = 48000;
+	else if (rate <= 92000)
+		rate = 88200;
+	else if (rate <= 136000)
+		rate = 96000;
+	else if (rate <= 184000)
+		rate = 176400;
+	else
+		rate = 192000;
+
+	if (lowpass > 14)
+		glb_ptr->set_lowpass = lowpass = 0;
+
+	if (mode > 3)
+		glb_ptr->set_mode = mode = 0;
+
+	if (mode > 0)
+		glb_ptr->dual_mode = 0;
+
+	/* same configuration loaded */
+	if ((rate == glb_ptr->set_rate) && (lowpass == glb_ptr->set_lowpass)
+			&& (mode == glb_ptr->set_mode))
+		return 0;
+
+	switch (mode) {
+	case 0: /* None */
+		return 1;
+
+	case 1: /* 2.0 */
+		snd_soc_component_write(asoc_rtd_to_codec(rtd, 0)->component,
+				PCM512x_MUTE, P_DAC_UNMUTE);
+		snd_soc_component_write(asoc_rtd_to_codec(rtd, 1)->component,
+				PCM512x_MUTE, P_DAC_MUTE);
+		glb_ptr->set_rate = rate;
+		glb_ptr->set_mode = mode;
+		glb_ptr->set_lowpass = lowpass;
+		return 1;
+
+	default:
+		snd_soc_component_write(asoc_rtd_to_codec(rtd, 0)->component,
+				PCM512x_MUTE, P_DAC_UNMUTE);
+		snd_soc_component_write(asoc_rtd_to_codec(rtd, 1)->component,
+				PCM512x_MUTE, P_DAC_UNMUTE);
+	}
+
+	for (dac = 0; dac < rtd->num_codecs; dac++) {
+		struct dsp_code *dsp_code_read;
+		int i = 1;
+
+		if (dac == 0) { /* high */
+			snprintf(firmware_name, sizeof(firmware_name),
+				"allo/piano/2.2/allo-piano-dsp-%d-%d-%d.bin",
+				rate, ((lowpass * 10) + 60), dac);
+		} else { /* low */
+			snprintf(firmware_name, sizeof(firmware_name),
+				"allo/piano/2.%d/allo-piano-dsp-%d-%d-%d.bin",
+				(mode - 1), rate, ((lowpass * 10) + 60), dac);
+		}
+
+		dev_info(rtd->card->dev, "Dsp Firmware File Name: %s\n",
+				firmware_name);
+
+		ret = request_firmware(&fw, firmware_name, rtd->card->dev);
+		if (ret < 0) {
+			dev_err(rtd->card->dev,
+				"Error: Allo Piano Firmware %s missing. %d\n",
+				firmware_name, ret);
+			goto err;
+		}
+
+		while (i < (fw->size - 1)) {
+			dsp_code_read = (struct dsp_code *)&fw->data[i];
+
+			if (dsp_code_read->offset == 0) {
+				glb_ptr->dsp_page_number = dsp_code_read->val;
+				ret = snd_soc_component_write(asoc_rtd_to_codec(rtd, dac)->component,
+						PCM512x_PAGE_BASE(0),
+						dsp_code_read->val);
+
+			} else if (dsp_code_read->offset != 0) {
+				ret = snd_soc_component_write(asoc_rtd_to_codec(rtd, dac)->component,
+					(PCM512x_PAGE_BASE(
+						glb_ptr->dsp_page_number) +
+					dsp_code_read->offset),
+					dsp_code_read->val);
+			}
+			if (ret < 0) {
+				dev_err(rtd->card->dev,
+					"Failed to write Register: %d\n", ret);
+				release_firmware(fw);
+				goto err;
+			}
+			i = i + 3;
+		}
+		release_firmware(fw);
+	}
+	glb_ptr->set_rate = rate;
+	glb_ptr->set_mode = mode;
+	glb_ptr->set_lowpass = lowpass;
+	return 1;
+
+err:
+	return ret;
+}
+
+static int snd_allo_piano_dsp_program(struct snd_soc_pcm_runtime *rtd,
+		unsigned int mode, unsigned int rate, unsigned int lowpass)
+{
+	struct snd_soc_card *card = rtd->card;
+	struct glb_pool *glb_ptr = card->drvdata;
+	int ret = 0;
+
+	mutex_lock(&glb_ptr->lock);
+
+	ret = __snd_allo_piano_dsp_program(rtd, mode, rate, lowpass);
+
+	mutex_unlock(&glb_ptr->lock);
+
+	return ret;
+}
+
+static int snd_allo_piano_dual_mode_get(struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
+	struct glb_pool *glb_ptr = card->drvdata;
+
+	ucontrol->value.integer.value[0] = glb_ptr->dual_mode;
+
+	return 0;
+}
+
+static int snd_allo_piano_dual_mode_put(struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
+	struct glb_pool *glb_ptr = card->drvdata;
+	struct snd_soc_pcm_runtime *rtd;
+	struct snd_card *snd_card_ptr = card->snd_card;
+	struct snd_kcontrol *kctl;
+	struct soc_mixer_control *mc;
+	unsigned int left_val = 0, right_val = 0;
+
+	rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[0]);
+
+	if (ucontrol->value.integer.value[0] > 0) {
+		glb_ptr->dual_mode = ucontrol->value.integer.value[0];
+		glb_ptr->set_mode = 0;
+	} else {
+		if (glb_ptr->set_mode <= 0) {
+			glb_ptr->dual_mode = 1;
+			glb_ptr->set_mode = 0;
+		} else {
+			glb_ptr->dual_mode = 0;
+			return 0;
+		}
+	}
+
+	if (glb_ptr->dual_mode == 1) { // Dual Mono
+		snd_soc_component_write(asoc_rtd_to_codec(rtd, 0)->component,
+				PCM512x_MUTE, P_DAC_RIGHT_MUTE);
+		snd_soc_component_write(asoc_rtd_to_codec(rtd, 1)->component,
+				PCM512x_MUTE, P_DAC_LEFT_MUTE);
+		snd_soc_component_write(asoc_rtd_to_codec(rtd, 0)->component,
+				PCM512x_DIGITAL_VOLUME_3, 0xff);
+		snd_soc_component_write(asoc_rtd_to_codec(rtd, 1)->component,
+				PCM512x_DIGITAL_VOLUME_2, 0xff);
+
+		list_for_each_entry(kctl, &snd_card_ptr->controls, list) {
+			if (!strncmp(kctl->id.name, "Main Digital Playback Volume",
+				sizeof(kctl->id.name))) {
+				mc = (struct soc_mixer_control *)
+					kctl->private_value;
+				mc->rreg = mc->reg;
+				break;
+			}
+			if (!strncmp(kctl->id.name, "Sub Digital Playback Volume",
+				sizeof(kctl->id.name))) {
+				mc = (struct soc_mixer_control *)
+					kctl->private_value;
+				mc->rreg = mc->reg;
+				break;
+			}
+		}
+	} else {
+		left_val = snd_soc_component_read(asoc_rtd_to_codec(rtd, 0)->component,
+						PCM512x_DIGITAL_VOLUME_2);
+		right_val = snd_soc_component_read(asoc_rtd_to_codec(rtd, 1)->component,
+						PCM512x_DIGITAL_VOLUME_3);
+
+		list_for_each_entry(kctl, &snd_card_ptr->controls, list) {
+			if (!strncmp(kctl->id.name, "Main Digital Playback Volume",
+				sizeof(kctl->id.name))) {
+				mc = (struct soc_mixer_control *)
+					kctl->private_value;
+				mc->rreg = PCM512x_DIGITAL_VOLUME_3;
+				break;
+			}
+			if (!strncmp(kctl->id.name, "Sub Digital Playback Volume",
+				sizeof(kctl->id.name))) {
+				mc = (struct soc_mixer_control *)
+					kctl->private_value;
+				mc->rreg = PCM512x_DIGITAL_VOLUME_2;
+				break;
+			}
+		}
+
+		snd_soc_component_write(asoc_rtd_to_codec(rtd, 0)->component,
+				PCM512x_DIGITAL_VOLUME_3, left_val);
+		snd_soc_component_write(asoc_rtd_to_codec(rtd, 1)->component,
+				PCM512x_DIGITAL_VOLUME_2, right_val);
+		snd_soc_component_write(asoc_rtd_to_codec(rtd, 0)->component,
+				PCM512x_MUTE, P_DAC_UNMUTE);
+		snd_soc_component_write(asoc_rtd_to_codec(rtd, 1)->component,
+				PCM512x_MUTE, P_DAC_UNMUTE);
+	}
+
+	return 0;
+}
+
+static int snd_allo_piano_mode_get(struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
+	struct glb_pool *glb_ptr = card->drvdata;
+
+	ucontrol->value.integer.value[0] = glb_ptr->set_mode;
+	return 0;
+}
+
+static int snd_allo_piano_mode_put(struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
+	struct snd_soc_pcm_runtime *rtd;
+	struct glb_pool *glb_ptr = card->drvdata;
+	struct snd_card *snd_card_ptr = card->snd_card;
+	struct snd_kcontrol *kctl;
+	struct soc_mixer_control *mc;
+	unsigned int left_val = 0, right_val = 0;
+
+	rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[0]);
+
+	if ((glb_ptr->dual_mode == 1) &&
+			(ucontrol->value.integer.value[0] > 0)) {
+		left_val = snd_soc_component_read(asoc_rtd_to_codec(rtd, 0)->component,
+						PCM512x_DIGITAL_VOLUME_2);
+		right_val = snd_soc_component_read(asoc_rtd_to_codec(rtd, 1)->component,
+						PCM512x_DIGITAL_VOLUME_2);
+
+		list_for_each_entry(kctl, &snd_card_ptr->controls, list) {
+			if (!strncmp(kctl->id.name, "Main Digital Playback Volume",
+				sizeof(kctl->id.name))) {
+				mc = (struct soc_mixer_control *)
+					kctl->private_value;
+				mc->rreg = PCM512x_DIGITAL_VOLUME_3;
+				break;
+			}
+			if (!strncmp(kctl->id.name, "Sub Digital Playback Volume",
+				sizeof(kctl->id.name))) {
+				mc = (struct soc_mixer_control *)
+					kctl->private_value;
+				mc->rreg = PCM512x_DIGITAL_VOLUME_2;
+				break;
+			}
+		}
+		snd_soc_component_write(asoc_rtd_to_codec(rtd, 0)->component,
+				PCM512x_DIGITAL_VOLUME_3, left_val);
+		snd_soc_component_write(asoc_rtd_to_codec(rtd, 1)->component,
+				PCM512x_DIGITAL_VOLUME_3, right_val);
+	}
+
+	return(snd_allo_piano_dsp_program(rtd,
+				ucontrol->value.integer.value[0],
+				glb_ptr->set_rate, glb_ptr->set_lowpass));
+}
+
+static int snd_allo_piano_lowpass_get(struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
+	struct glb_pool *glb_ptr = card->drvdata;
+
+	ucontrol->value.integer.value[0] = glb_ptr->set_lowpass;
+	return 0;
+}
+
+static int snd_allo_piano_lowpass_put(struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
+	struct snd_soc_pcm_runtime *rtd;
+	struct glb_pool *glb_ptr = card->drvdata;
+
+	rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[0]);
+	return(snd_allo_piano_dsp_program(rtd,
+				glb_ptr->set_mode, glb_ptr->set_rate,
+				ucontrol->value.integer.value[0]));
+}
+
+static int pcm512x_get_reg_sub(struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_value *ucontrol)
+{
+	struct soc_mixer_control *mc =
+		(struct soc_mixer_control *)kcontrol->private_value;
+	struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
+	struct glb_pool *glb_ptr = card->drvdata;
+	struct snd_soc_pcm_runtime *rtd;
+	unsigned int left_val = 0;
+	unsigned int right_val = 0;
+	rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[0]);
+
+	right_val = snd_soc_component_read(asoc_rtd_to_codec(rtd, 1)->component,
+			PCM512x_DIGITAL_VOLUME_3);
+	if (glb_ptr->dual_mode != 1) {
+		left_val = snd_soc_component_read(asoc_rtd_to_codec(rtd, 1)->component,
+				PCM512x_DIGITAL_VOLUME_2);
+
+	} else {
+		left_val = right_val;
+	}
+
+	ucontrol->value.integer.value[0] =
+				(~(left_val >> mc->shift)) & mc->max;
+	ucontrol->value.integer.value[1] =
+				(~(right_val >> mc->shift)) & mc->max;
+
+	return 0;
+}
+
+static int pcm512x_set_reg_sub(struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_value *ucontrol)
+{
+	struct soc_mixer_control *mc =
+		(struct soc_mixer_control *)kcontrol->private_value;
+	struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
+	struct glb_pool *glb_ptr = card->drvdata;
+	struct snd_soc_pcm_runtime *rtd;
+	unsigned int left_val = (ucontrol->value.integer.value[0] & mc->max);
+	unsigned int right_val = (ucontrol->value.integer.value[1] & mc->max);
+	int ret = 0;
+
+	rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[0]);
+
+	if (digital_gain_0db_limit) {
+		ret = snd_soc_limit_volume(card, "Subwoofer Playback Volume",
+					207);
+		if (ret < 0)
+			dev_warn(card->dev, "Failed to set volume limit: %d\n",
+				ret);
+	}
+
+	// When in Dual Mono, Sub vol control should not set anything.
+	if (glb_ptr->dual_mode != 1) { //Not in Dual Mono mode
+
+		ret = snd_soc_component_write(asoc_rtd_to_codec(rtd, 1)->component,
+				PCM512x_DIGITAL_VOLUME_2, (~left_val));
+		if (ret < 0)
+			return ret;
+
+		ret = snd_soc_component_write(asoc_rtd_to_codec(rtd, 1)->component,
+				PCM512x_DIGITAL_VOLUME_3, (~right_val));
+		if (ret < 0)
+			return ret;
+
+	}
+
+	return 1;
+}
+
+static int pcm512x_get_reg_sub_switch(struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
+	struct snd_soc_pcm_runtime *rtd;
+	int val = 0;
+
+	rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[0]);
+	val = snd_soc_component_read(asoc_rtd_to_codec(rtd, 1)->component, PCM512x_MUTE);
+
+	ucontrol->value.integer.value[0] =
+			(val & P_DAC_LEFT_MUTE) ? P_UNMUTE : P_MUTE;
+	ucontrol->value.integer.value[1] =
+			(val & P_DAC_RIGHT_MUTE) ? P_UNMUTE : P_MUTE;
+
+	return val;
+}
+
+static int pcm512x_set_reg_sub_switch(struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
+	struct snd_soc_pcm_runtime *rtd;
+	struct glb_pool *glb_ptr = card->drvdata;
+	unsigned int left_val = (ucontrol->value.integer.value[0]);
+	unsigned int right_val = (ucontrol->value.integer.value[1]);
+	int ret = 0;
+
+	rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[0]);
+	if (glb_ptr->set_mode != 1) {
+		ret = snd_soc_component_write(asoc_rtd_to_codec(rtd, 1)->component, PCM512x_MUTE,
+				~((left_val & 0x01)<<4 | (right_val & 0x01)));
+		if (ret < 0)
+			return ret;
+	}
+	return 1;
+
+}
+
+static int pcm512x_get_reg_master(struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_value *ucontrol)
+{
+	struct soc_mixer_control *mc =
+		(struct soc_mixer_control *)kcontrol->private_value;
+	struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
+	struct glb_pool *glb_ptr = card->drvdata;
+	struct snd_soc_pcm_runtime *rtd;
+	unsigned int left_val = 0, right_val = 0;
+
+	rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[0]);
+
+	left_val = snd_soc_component_read(asoc_rtd_to_codec(rtd, 0)->component,
+			PCM512x_DIGITAL_VOLUME_2);
+
+	if (glb_ptr->dual_mode == 1) {  // in Dual Mono mode
+		right_val = snd_soc_component_read(asoc_rtd_to_codec(rtd, 1)->component,
+				PCM512x_DIGITAL_VOLUME_3);
+	} else {
+		right_val = snd_soc_component_read(asoc_rtd_to_codec(rtd, 0)->component,
+				PCM512x_DIGITAL_VOLUME_3);
+	}
+
+	ucontrol->value.integer.value[0] =
+		(~(left_val  >> mc->shift)) & mc->max;
+	ucontrol->value.integer.value[1] =
+		(~(right_val >> mc->shift)) & mc->max;
+
+	return 0;
+}
+
+static int pcm512x_set_reg_master(struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_value *ucontrol)
+{
+	struct soc_mixer_control *mc =
+		(struct soc_mixer_control *)kcontrol->private_value;
+	struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
+	struct glb_pool *glb_ptr = card->drvdata;
+	struct snd_soc_pcm_runtime *rtd;
+	unsigned int left_val = (ucontrol->value.integer.value[0] & mc->max);
+	unsigned int right_val = (ucontrol->value.integer.value[1] & mc->max);
+	int ret = 0;
+
+	rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[0]);
+
+	if (digital_gain_0db_limit) {
+		ret = snd_soc_limit_volume(card, "Master Playback Volume",
+					207);
+		if (ret < 0)
+			dev_warn(card->dev, "Failed to set volume limit: %d\n",
+				ret);
+	}
+
+	if (glb_ptr->dual_mode == 1) { //in Dual Mono Mode
+
+		ret = snd_soc_component_write(asoc_rtd_to_codec(rtd, 0)->component,
+				PCM512x_DIGITAL_VOLUME_2, (~left_val));
+		if (ret < 0)
+			return ret;
+
+		ret = snd_soc_component_write(asoc_rtd_to_codec(rtd, 1)->component,
+				PCM512x_DIGITAL_VOLUME_3, (~right_val));
+		if (ret < 0)
+			return ret;
+
+	} else {
+
+		ret = snd_soc_component_write(asoc_rtd_to_codec(rtd, 0)->component,
+				PCM512x_DIGITAL_VOLUME_2, (~left_val));
+		if (ret < 0)
+			return ret;
+
+		ret = snd_soc_component_write(asoc_rtd_to_codec(rtd, 0)->component,
+				PCM512x_DIGITAL_VOLUME_3, (~right_val));
+		if (ret < 0)
+			return ret;
+
+	}
+	return 1;
+}
+
+static int pcm512x_get_reg_master_switch(struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
+	struct glb_pool *glb_ptr = card->drvdata;
+	struct snd_soc_pcm_runtime *rtd;
+	int val = 0;
+
+	rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[0]);
+
+	val = snd_soc_component_read(asoc_rtd_to_codec(rtd, 0)->component, PCM512x_MUTE);
+
+	ucontrol->value.integer.value[0] =
+			(val & P_DAC_LEFT_MUTE) ? P_UNMUTE : P_MUTE;
+
+	if (glb_ptr->dual_mode == 1) {
+		val = snd_soc_component_read(asoc_rtd_to_codec(rtd, 1)->component, PCM512x_MUTE);
+	}
+	ucontrol->value.integer.value[1] =
+			(val & P_DAC_RIGHT_MUTE) ? P_UNMUTE : P_MUTE;
+
+	return val;
+}
+
+static int pcm512x_set_reg_master_switch(struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
+	struct snd_soc_pcm_runtime *rtd;
+	struct glb_pool *glb_ptr = card->drvdata;
+	unsigned int left_val = (ucontrol->value.integer.value[0]);
+	unsigned int right_val = (ucontrol->value.integer.value[1]);
+	int ret = 0;
+
+	rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[0]);
+	if (glb_ptr->dual_mode == 1) {
+		ret = snd_soc_component_write(asoc_rtd_to_codec(rtd, 0)->component, PCM512x_MUTE,
+				~((left_val & 0x01)<<4));
+		if (ret < 0)
+			return ret;
+		ret = snd_soc_component_write(asoc_rtd_to_codec(rtd, 1)->component, PCM512x_MUTE,
+				~((right_val & 0x01)));
+		if (ret < 0)
+			return ret;
+
+	} else if (glb_ptr->set_mode == 1) {
+		ret = snd_soc_component_write(asoc_rtd_to_codec(rtd, 0)->component, PCM512x_MUTE,
+				~((left_val & 0x01)<<4 | (right_val & 0x01)));
+		if (ret < 0)
+			return ret;
+
+	} else {
+		ret = snd_soc_component_write(asoc_rtd_to_codec(rtd, 0)->component, PCM512x_MUTE,
+				~((left_val & 0x01)<<4 | (right_val & 0x01)));
+		if (ret < 0)
+			return ret;
+
+		ret = snd_soc_component_write(asoc_rtd_to_codec(rtd, 1)->component, PCM512x_MUTE,
+				~((left_val & 0x01)<<4 | (right_val & 0x01)));
+		if (ret < 0)
+			return ret;
+	}
+	return 1;
+}
+
+static const DECLARE_TLV_DB_SCALE(digital_tlv_sub, -10350, 50, 1);
+static const DECLARE_TLV_DB_SCALE(digital_tlv_master, -10350, 50, 1);
+
+static const struct snd_kcontrol_new allo_piano_controls[] = {
+	SOC_ENUM_EXT("Subwoofer mode Route",
+			allo_piano_mode_enum,
+			snd_allo_piano_mode_get,
+			snd_allo_piano_mode_put),
+
+	SOC_ENUM_EXT("Dual Mode Route",
+			allo_piano_dual_mode_enum,
+			snd_allo_piano_dual_mode_get,
+			snd_allo_piano_dual_mode_put),
+
+	SOC_ENUM_EXT("Lowpass Route", allo_piano_enum,
+			snd_allo_piano_lowpass_get,
+			snd_allo_piano_lowpass_put),
+
+	SOC_DOUBLE_R_EXT_TLV("Subwoofer Playback Volume",
+			PCM512x_DIGITAL_VOLUME_2,
+			PCM512x_DIGITAL_VOLUME_3, 0, 255, 1,
+			pcm512x_get_reg_sub,
+			pcm512x_set_reg_sub,
+			digital_tlv_sub),
+
+	SOC_DOUBLE_EXT("Subwoofer Playback Switch",
+			PCM512x_MUTE,
+			PCM512x_RQML_SHIFT,
+			PCM512x_RQMR_SHIFT, 1, 1,
+			pcm512x_get_reg_sub_switch,
+			pcm512x_set_reg_sub_switch),
+
+	SOC_DOUBLE_R_EXT_TLV("Master Playback Volume",
+			PCM512x_DIGITAL_VOLUME_2,
+			PCM512x_DIGITAL_VOLUME_3, 0, 255, 1,
+			pcm512x_get_reg_master,
+			pcm512x_set_reg_master,
+			digital_tlv_master),
+
+	SOC_DOUBLE_EXT("Master Playback Switch",
+			PCM512x_MUTE,
+			PCM512x_RQML_SHIFT,
+			PCM512x_RQMR_SHIFT, 1, 1,
+			pcm512x_get_reg_master_switch,
+			pcm512x_set_reg_master_switch),
+};
+
+static const char * const codec_ctl_pfx[] = { "Main", "Sub" };
+static const char * const codec_ctl_name[] = {
+	"Digital Playback Volume",
+	"Digital Playback Switch",
+	"Auto Mute Mono Switch",
+	"Auto Mute Switch",
+	"Auto Mute Time Left",
+	"Auto Mute Time Right",
+	"Clock Missing Period",
+	"Max Overclock DAC",
+	"Max Overclock DSP",
+	"Max Overclock PLL",
+	"Volume Ramp Down Emergency Rate",
+	"Volume Ramp Down Emergency Step",
+	"Volume Ramp Up Rate",
+	"Volume Ramp Down Rate",
+	"Volume Ramp Up Step",
+	"Volume Ramp Down Step"
+};
+
+static int snd_allo_piano_dac_init(struct snd_soc_pcm_runtime *rtd)
+{
+	struct snd_soc_card *card = rtd->card;
+	struct glb_pool *glb_ptr;
+	struct snd_kcontrol *kctl;
+	int i, j;
+
+	glb_ptr = kzalloc(sizeof(struct glb_pool), GFP_KERNEL);
+	if (!glb_ptr)
+		return -ENOMEM;
+
+	card->drvdata = glb_ptr;
+	glb_ptr->dual_mode = 2;
+	glb_ptr->set_mode = 0;
+
+	mutex_init(&glb_ptr->lock);
+
+	if (digital_gain_0db_limit) {
+		int ret;
+
+		//Set volume limit on both dacs
+		for (i = 0; i < ARRAY_SIZE(codec_ctl_pfx); i++) {
+			char cname[256];
+
+			sprintf(cname, "%s %s", codec_ctl_pfx[i], codec_ctl_name[0]);
+			ret = snd_soc_limit_volume(card, cname, 207);
+			if (ret < 0)
+				dev_warn(card->dev, "Failed to set volume limit: %d\n",
+					ret);
+		}
+	}
+
+	// Remove codec controls
+	for (i = 0; i < ARRAY_SIZE(codec_ctl_pfx); i++) {
+		for (j = 0; j < ARRAY_SIZE(codec_ctl_name); j++) {
+			char cname[256];
+
+			sprintf(cname, "%s %s", codec_ctl_pfx[i], codec_ctl_name[j]);
+			kctl = snd_soc_card_get_kcontrol(card, cname);
+			if (!kctl) {
+				dev_err(rtd->card->dev, "Control %s not found\n",
+				       cname);
+			} else {
+				kctl->vd[0].access =
+					SNDRV_CTL_ELEM_ACCESS_READWRITE;
+				snd_ctl_remove(card->snd_card, kctl);
+			}
+		}
+	}
+
+	return 0;
+}
+
+static void snd_allo_piano_gpio_mute(struct snd_soc_card *card)
+{
+	if (mute_gpio[0])
+		gpiod_set_value_cansleep(mute_gpio[0], P_MUTE);
+
+	if (mute_gpio[1])
+		gpiod_set_value_cansleep(mute_gpio[1], P_MUTE);
+}
+
+static void snd_allo_piano_gpio_unmute(struct snd_soc_card *card)
+{
+	if (mute_gpio[0])
+		gpiod_set_value_cansleep(mute_gpio[0], P_UNMUTE);
+
+	if (mute_gpio[1])
+		gpiod_set_value_cansleep(mute_gpio[1], P_UNMUTE);
+}
+
+static int snd_allo_piano_set_bias_level(struct snd_soc_card *card,
+	struct snd_soc_dapm_context *dapm, enum snd_soc_bias_level level)
+{
+	struct snd_soc_pcm_runtime *rtd;
+	struct snd_soc_dai *codec_dai;
+
+	rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[0]);
+	codec_dai = asoc_rtd_to_codec(rtd, 0);
+
+	if (dapm->dev != codec_dai->dev)
+		return 0;
+
+	switch (level) {
+	case SND_SOC_BIAS_PREPARE:
+		if (dapm->bias_level != SND_SOC_BIAS_STANDBY)
+			break;
+		/* UNMUTE DAC */
+		snd_allo_piano_gpio_unmute(card);
+		break;
+
+	case SND_SOC_BIAS_STANDBY:
+		if (dapm->bias_level != SND_SOC_BIAS_PREPARE)
+			break;
+		/* MUTE DAC */
+		snd_allo_piano_gpio_mute(card);
+		break;
+
+	default:
+		break;
+	}
+
+	return 0;
+}
+
+static int snd_allo_piano_dac_startup(
+	struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_card *card = rtd->card;
+
+	snd_allo_piano_gpio_mute(card);
+
+	return 0;
+}
+
+static int snd_allo_piano_dac_hw_params(
+		struct snd_pcm_substream *substream,
+		struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	unsigned int rate = params_rate(params);
+	struct snd_soc_card *card = rtd->card;
+	struct glb_pool *glb_ptr = card->drvdata;
+	int ret = 0, val = 0, dac;
+
+	for (dac = 0; (glb_mclk && dac < 2); dac++) {
+		/* Configure the PLL clock reference for both the Codecs */
+		val = snd_soc_component_read(asoc_rtd_to_codec(rtd, dac)->component,
+					PCM512x_RATE_DET_4);
+
+		if (val & 0x40) {
+			snd_soc_component_write(asoc_rtd_to_codec(rtd, dac)->component,
+					PCM512x_PLL_REF,
+					PCM512x_SREF_BCK);
+
+			dev_info(asoc_rtd_to_codec(rtd, dac)->component->dev,
+				"Setting BCLK as input clock & Enable PLL\n");
+		} else {
+			snd_soc_component_write(asoc_rtd_to_codec(rtd, dac)->component,
+					PCM512x_PLL_EN,
+					0x00);
+
+			snd_soc_component_write(asoc_rtd_to_codec(rtd, dac)->component,
+					PCM512x_PLL_REF,
+					PCM512x_SREF_SCK);
+
+			dev_info(asoc_rtd_to_codec(rtd, dac)->component->dev,
+				"Setting SCLK as input clock & disabled PLL\n");
+		}
+	}
+
+	ret = snd_allo_piano_dsp_program(rtd, glb_ptr->set_mode, rate,
+						glb_ptr->set_lowpass);
+	if (ret < 0)
+		return ret;
+
+	return ret;
+}
+
+static int snd_allo_piano_dac_prepare(
+	struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_card *card = rtd->card;
+
+	snd_allo_piano_gpio_unmute(card);
+
+	return 0;
+}
+
+/* machine stream operations */
+static struct snd_soc_ops snd_allo_piano_dac_ops = {
+	.startup = snd_allo_piano_dac_startup,
+	.hw_params = snd_allo_piano_dac_hw_params,
+	.prepare = snd_allo_piano_dac_prepare,
+};
+
+static struct snd_soc_dai_link_component allo_piano_2_1_codecs[] = {
+	{
+		.dai_name = "pcm512x-hifi",
+	},
+	{
+		.dai_name = "pcm512x-hifi",
+	},
+};
+
+SND_SOC_DAILINK_DEFS(allo_piano_dai_plus,
+	DAILINK_COMP_ARRAY(COMP_EMPTY()),
+	DAILINK_COMP_ARRAY(COMP_CODEC("pcm512x.1-004c", "pcm512x-hifi"),
+			   COMP_CODEC("pcm512x.1-004d", "pcm512x-hifi")),
+	DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+static struct snd_soc_dai_link snd_allo_piano_dac_dai[] = {
+	{
+		.name		= "PianoDACPlus",
+		.stream_name	= "PianoDACPlus",
+		.dai_fmt	= SND_SOC_DAIFMT_I2S |
+				SND_SOC_DAIFMT_NB_NF |
+				SND_SOC_DAIFMT_CBS_CFS,
+		.ops		= &snd_allo_piano_dac_ops,
+		.init		= snd_allo_piano_dac_init,
+		SND_SOC_DAILINK_REG(allo_piano_dai_plus),
+	},
+};
+
+/* audio machine driver */
+static struct snd_soc_card snd_allo_piano_dac = {
+	.name = "PianoDACPlus",
+	.owner = THIS_MODULE,
+	.dai_link = snd_allo_piano_dac_dai,
+	.num_links = ARRAY_SIZE(snd_allo_piano_dac_dai),
+	.controls = allo_piano_controls,
+	.num_controls = ARRAY_SIZE(allo_piano_controls),
+};
+
+static int snd_allo_piano_dac_probe(struct platform_device *pdev)
+{
+	struct snd_soc_card *card = &snd_allo_piano_dac;
+	int ret = 0, i = 0;
+
+	card->dev = &pdev->dev;
+	platform_set_drvdata(pdev, &snd_allo_piano_dac);
+
+	if (pdev->dev.of_node) {
+		struct device_node *i2s_node;
+		struct snd_soc_dai_link *dai;
+
+		dai = &snd_allo_piano_dac_dai[0];
+		i2s_node = of_parse_phandle(pdev->dev.of_node,
+						"i2s-controller", 0);
+		if (i2s_node) {
+			for (i = 0; i < card->num_links; i++) {
+				dai->cpus->dai_name = NULL;
+				dai->cpus->of_node = i2s_node;
+				dai->platforms->name = NULL;
+				dai->platforms->of_node = i2s_node;
+			}
+		}
+		digital_gain_0db_limit =
+			!of_property_read_bool(pdev->dev.of_node,
+						"allo,24db_digital_gain");
+
+		glb_mclk = of_property_read_bool(pdev->dev.of_node,
+						"allo,glb_mclk");
+
+		allo_piano_2_1_codecs[0].of_node =
+			of_parse_phandle(pdev->dev.of_node, "audio-codec", 0);
+		if (!allo_piano_2_1_codecs[0].of_node) {
+			dev_err(&pdev->dev,
+				"Property 'audio-codec' missing or invalid\n");
+			return -EINVAL;
+		}
+
+		allo_piano_2_1_codecs[1].of_node =
+			of_parse_phandle(pdev->dev.of_node, "audio-codec", 1);
+		if (!allo_piano_2_1_codecs[1].of_node) {
+			dev_err(&pdev->dev,
+				"Property 'audio-codec' missing or invalid\n");
+			return -EINVAL;
+		}
+
+		mute_gpio[0] = devm_gpiod_get_optional(&pdev->dev, "mute1",
+							GPIOD_OUT_LOW);
+		if (IS_ERR(mute_gpio[0])) {
+			ret = PTR_ERR(mute_gpio[0]);
+			dev_err(&pdev->dev,
+				"failed to get mute1 gpio6: %d\n", ret);
+			return ret;
+		}
+
+		mute_gpio[1] = devm_gpiod_get_optional(&pdev->dev, "mute2",
+							GPIOD_OUT_LOW);
+		if (IS_ERR(mute_gpio[1])) {
+			ret = PTR_ERR(mute_gpio[1]);
+			dev_err(&pdev->dev,
+				"failed to get mute2 gpio25: %d\n", ret);
+			return ret;
+		}
+
+		if (mute_gpio[0] && mute_gpio[1])
+			snd_allo_piano_dac.set_bias_level =
+				snd_allo_piano_set_bias_level;
+
+		ret = snd_soc_register_card(&snd_allo_piano_dac);
+		if (ret < 0) {
+			dev_err(&pdev->dev,
+				"snd_soc_register_card() failed: %d\n", ret);
+			return ret;
+		}
+
+		if ((mute_gpio[0]) && (mute_gpio[1]))
+			snd_allo_piano_gpio_mute(&snd_allo_piano_dac);
+
+		return 0;
+	}
+
+	return -EINVAL;
+}
+
+static int snd_allo_piano_dac_remove(struct platform_device *pdev)
+{
+	struct snd_soc_card *card = platform_get_drvdata(pdev);
+
+	kfree(&card->drvdata);
+	snd_allo_piano_gpio_mute(&snd_allo_piano_dac);
+	return snd_soc_unregister_card(&snd_allo_piano_dac);
+}
+
+static const struct of_device_id snd_allo_piano_dac_of_match[] = {
+	{ .compatible = "allo,piano-dac-plus", },
+	{ /* sentinel */ },
+};
+
+MODULE_DEVICE_TABLE(of, snd_allo_piano_dac_of_match);
+
+static struct platform_driver snd_allo_piano_dac_driver = {
+	.driver = {
+		.name = "snd-allo-piano-dac-plus",
+		.owner = THIS_MODULE,
+		.of_match_table = snd_allo_piano_dac_of_match,
+	},
+	.probe = snd_allo_piano_dac_probe,
+	.remove = snd_allo_piano_dac_remove,
+};
+
+module_platform_driver(snd_allo_piano_dac_driver);
+
+MODULE_AUTHOR("Baswaraj K <jaikumar@cem-solutions.net>");
+MODULE_DESCRIPTION("ALSA ASoC Machine Driver for Allo Piano DAC Plus");
+MODULE_LICENSE("GPL v2");
diff -Nur --no-dereference linux-5.10.52-orig/sound/soc/bcm/audioinjector-isolated-soundcard.c linux-5.10.52-v7l+/sound/soc/bcm/audioinjector-isolated-soundcard.c
--- linux-5.10.52-orig/sound/soc/bcm/audioinjector-isolated-soundcard.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/sound/soc/bcm/audioinjector-isolated-soundcard.c	2021-07-25 16:46:40.127718155 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/*
+ * ASoC Driver for AudioInjector.net isolated soundcard
+ *
+ *  Created on: 20-February-2020
+ *      Author: flatmax@flatmax.org
+ *              based on audioinjector-octo-soundcard.c
+ *
+ * Copyright (C) 2020 Flatmax Pty. Ltd.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/gpio/consumer.h>
+
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <sound/pcm_params.h>
+#include <sound/control.h>
+
+static struct gpio_desc *mute_gpio;
+
+static const unsigned int audioinjector_isolated_rates[] = {
+	192000, 96000, 48000, 32000, 24000, 16000, 8000
+};
+
+static struct snd_pcm_hw_constraint_list audioinjector_isolated_constraints = {
+	.list = audioinjector_isolated_rates,
+	.count = ARRAY_SIZE(audioinjector_isolated_rates),
+};
+
+static int audioinjector_isolated_dai_init(struct snd_soc_pcm_runtime *rtd)
+{
+	int ret=snd_soc_dai_set_sysclk(asoc_rtd_to_codec(rtd, 0), 0, 24576000, 0);
+	if (ret)
+		return ret;
+
+	return snd_soc_dai_set_bclk_ratio(asoc_rtd_to_cpu(rtd, 0), 64);
+}
+
+static int audioinjector_isolated_startup(struct snd_pcm_substream *substream)
+{
+	snd_pcm_hw_constraint_list(substream->runtime, 0,
+				SNDRV_PCM_HW_PARAM_RATE, &audioinjector_isolated_constraints);
+
+	return 0;
+}
+
+static int audioinjector_isolated_trigger(struct snd_pcm_substream *substream,
+								int cmd){
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		gpiod_set_value(mute_gpio, 0);
+		break;
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		gpiod_set_value(mute_gpio, 1);
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static struct snd_soc_ops audioinjector_isolated_ops = {
+	.startup	= audioinjector_isolated_startup,
+	.trigger = audioinjector_isolated_trigger,
+};
+
+SND_SOC_DAILINK_DEFS(audioinjector_isolated,
+	DAILINK_COMP_ARRAY(COMP_CPU("bcm2708-i2s.0")),
+	DAILINK_COMP_ARRAY(COMP_CODEC("cs4271.1-0010", "cs4271-hifi")),
+	DAILINK_COMP_ARRAY(COMP_PLATFORM("bcm2708-i2s.0")));
+
+static struct snd_soc_dai_link audioinjector_isolated_dai[] = {
+	{
+		.name = "AudioInjector ISO",
+		.stream_name = "AI-HIFI",
+		.ops = &audioinjector_isolated_ops,
+		.init = audioinjector_isolated_dai_init,
+		.symmetric_rates = 1,
+		.symmetric_channels = 1,
+		.dai_fmt = SND_SOC_DAIFMT_CBM_CFM|SND_SOC_DAIFMT_I2S|SND_SOC_DAIFMT_NB_NF,
+		SND_SOC_DAILINK_REG(audioinjector_isolated),
+	}
+};
+
+static const struct snd_soc_dapm_widget audioinjector_isolated_widgets[] = {
+	SND_SOC_DAPM_OUTPUT("OUTPUTS"),
+	SND_SOC_DAPM_INPUT("INPUTS"),
+};
+
+static const struct snd_soc_dapm_route audioinjector_isolated_route[] = {
+	/* Balanced outputs */
+	{"OUTPUTS", NULL, "AOUTA+"},
+	{"OUTPUTS", NULL, "AOUTA-"},
+	{"OUTPUTS", NULL, "AOUTB+"},
+	{"OUTPUTS", NULL, "AOUTB-"},
+
+	/* Balanced inputs */
+	{"AINA", NULL, "INPUTS"},
+	{"AINB", NULL, "INPUTS"},
+};
+
+static struct snd_soc_card snd_soc_audioinjector_isolated = {
+	.name = "audioinjector-isolated-soundcard",
+	.dai_link = audioinjector_isolated_dai,
+	.num_links = ARRAY_SIZE(audioinjector_isolated_dai),
+
+	.dapm_widgets = audioinjector_isolated_widgets,
+	.num_dapm_widgets = ARRAY_SIZE(audioinjector_isolated_widgets),
+	.dapm_routes = audioinjector_isolated_route,
+	.num_dapm_routes = ARRAY_SIZE(audioinjector_isolated_route),
+};
+
+static int audioinjector_isolated_probe(struct platform_device *pdev)
+{
+	struct snd_soc_card *card = &snd_soc_audioinjector_isolated;
+	int ret;
+
+	card->dev = &pdev->dev;
+
+	if (pdev->dev.of_node) {
+		struct snd_soc_dai_link *dai = &audioinjector_isolated_dai[0];
+		struct device_node *i2s_node =
+					of_parse_phandle(pdev->dev.of_node, "i2s-controller", 0);
+
+		if (i2s_node) {
+			dai->cpus->dai_name = NULL;
+			dai->cpus->of_node = i2s_node;
+			dai->platforms->name = NULL;
+			dai->platforms->of_node = i2s_node;
+		} else {
+				dev_err(&pdev->dev,
+				"i2s-controller missing or invalid in DT\n");
+				return -EINVAL;
+		}
+
+		mute_gpio = devm_gpiod_get_optional(&pdev->dev, "mute", GPIOD_OUT_LOW);
+		if (IS_ERR(mute_gpio)){
+			dev_err(&pdev->dev, "mute gpio not found in dt overlay\n");
+			return PTR_ERR(mute_gpio);
+		}
+	}
+
+	ret = devm_snd_soc_register_card(&pdev->dev, card);
+	if (ret && ret != -EPROBE_DEFER)
+		dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret);
+	return ret;
+}
+
+static const struct of_device_id audioinjector_isolated_of_match[] = {
+	{ .compatible = "ai,audioinjector-isolated-soundcard", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, audioinjector_isolated_of_match);
+
+static struct platform_driver audioinjector_isolated_driver = {
+	.driver	= {
+		.name			= "audioinjector-isolated",
+		.owner			= THIS_MODULE,
+		.of_match_table = audioinjector_isolated_of_match,
+	},
+	.probe	= audioinjector_isolated_probe,
+};
+
+module_platform_driver(audioinjector_isolated_driver);
+MODULE_AUTHOR("Matt Flax <flatmax@flatmax.org>");
+MODULE_DESCRIPTION("AudioInjector.net isolated Soundcard");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:audioinjector-isolated-soundcard");
diff -Nur --no-dereference linux-5.10.52-orig/sound/soc/bcm/audioinjector-octo-soundcard.c linux-5.10.52-v7l+/sound/soc/bcm/audioinjector-octo-soundcard.c
--- linux-5.10.52-orig/sound/soc/bcm/audioinjector-octo-soundcard.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/sound/soc/bcm/audioinjector-octo-soundcard.c	2021-07-25 16:46:40.147717820 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/*
+ * ASoC Driver for AudioInjector Pi octo channel soundcard (hat)
+ *
+ *  Created on: 27-October-2016
+ *      Author: flatmax@flatmax.org
+ *              based on audioinjector-pi-soundcard.c
+ *
+ * Copyright (C) 2016 Flatmax Pty. Ltd.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/gpio/consumer.h>
+
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <sound/pcm_params.h>
+#include <sound/control.h>
+
+static struct gpio_descs *mult_gpios;
+static struct gpio_desc *codec_rst_gpio;
+static unsigned int audioinjector_octo_rate;
+static bool non_stop_clocks;
+
+static const unsigned int audioinjector_octo_rates[] = {
+	96000, 48000, 32000, 24000, 16000, 8000, 88200, 44100, 29400, 22050, 14700,
+};
+
+static struct snd_pcm_hw_constraint_list audioinjector_octo_constraints = {
+	.list = audioinjector_octo_rates,
+	.count = ARRAY_SIZE(audioinjector_octo_rates),
+};
+
+static int audioinjector_octo_dai_init(struct snd_soc_pcm_runtime *rtd)
+{
+	return snd_soc_dai_set_bclk_ratio(asoc_rtd_to_cpu(rtd, 0), 64);
+}
+
+static int audioinjector_octo_startup(struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	asoc_rtd_to_cpu(rtd, 0)->driver->playback.channels_min = 8;
+	asoc_rtd_to_cpu(rtd, 0)->driver->playback.channels_max = 8;
+	asoc_rtd_to_cpu(rtd, 0)->driver->capture.channels_min = 8;
+	asoc_rtd_to_cpu(rtd, 0)->driver->capture.channels_max = 8;
+	asoc_rtd_to_codec(rtd, 0)->driver->capture.channels_max = 8;
+
+	snd_pcm_hw_constraint_list(substream->runtime, 0,
+				SNDRV_PCM_HW_PARAM_RATE,
+				&audioinjector_octo_constraints);
+
+	return 0;
+}
+
+static void audioinjector_octo_shutdown(struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	asoc_rtd_to_cpu(rtd, 0)->driver->playback.channels_min = 2;
+	asoc_rtd_to_cpu(rtd, 0)->driver->playback.channels_max = 2;
+	asoc_rtd_to_cpu(rtd, 0)->driver->capture.channels_min = 2;
+	asoc_rtd_to_cpu(rtd, 0)->driver->capture.channels_max = 2;
+	asoc_rtd_to_codec(rtd, 0)->driver->capture.channels_max = 6;
+}
+
+static int audioinjector_octo_hw_params(struct snd_pcm_substream *substream,
+	struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+
+	// set codec DAI configuration
+	int ret = snd_soc_dai_set_fmt(asoc_rtd_to_codec(rtd, 0),
+			SND_SOC_DAIFMT_CBS_CFS|SND_SOC_DAIFMT_DSP_A|
+			SND_SOC_DAIFMT_NB_NF);
+	if (ret < 0)
+		return ret;
+
+	// set cpu DAI configuration
+	ret = snd_soc_dai_set_fmt(asoc_rtd_to_cpu(rtd, 0),
+			SND_SOC_DAIFMT_CBM_CFM|SND_SOC_DAIFMT_I2S|
+			SND_SOC_DAIFMT_NB_NF);
+	if (ret < 0)
+		return ret;
+
+	audioinjector_octo_rate = params_rate(params);
+
+	// Set the correct sysclock for the codec
+	switch (audioinjector_octo_rate) {
+	case 96000:
+	case 48000:
+		return snd_soc_dai_set_sysclk(asoc_rtd_to_codec(rtd, 0), 0, 49152000,
+									0);
+		break;
+	case 24000:
+		return snd_soc_dai_set_sysclk(asoc_rtd_to_codec(rtd, 0), 0, 49152000/2,
+									0);
+		break;
+	case 32000:
+	case 16000:
+		return snd_soc_dai_set_sysclk(asoc_rtd_to_codec(rtd, 0), 0, 49152000/3,
+									0);
+		break;
+	case 8000:
+		return snd_soc_dai_set_sysclk(asoc_rtd_to_codec(rtd, 0), 0, 49152000/6,
+									0);
+		break;
+	case 88200:
+	case 44100:
+		return snd_soc_dai_set_sysclk(asoc_rtd_to_codec(rtd, 0), 0, 45185400,
+									0);
+		break;
+	case 22050:
+		return snd_soc_dai_set_sysclk(asoc_rtd_to_codec(rtd, 0), 0, 45185400/2,
+									0);
+		break;
+	case 29400:
+	case 14700:
+		return snd_soc_dai_set_sysclk(asoc_rtd_to_codec(rtd, 0), 0, 45185400/3,
+									0);
+		break;
+	default:
+		return -EINVAL;
+	}
+}
+
+static int audioinjector_octo_trigger(struct snd_pcm_substream *substream,
+								int cmd){
+	DECLARE_BITMAP(mult, 4);
+
+	memset(mult, 0, sizeof(mult));
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		if (!non_stop_clocks)
+			break;
+		/* fall through */
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		switch (audioinjector_octo_rate) {
+		case 96000:
+			__assign_bit(3, mult, 1);
+			/* fall through */
+		case 88200:
+			__assign_bit(1, mult, 1);
+			__assign_bit(2, mult, 1);
+			break;
+		case 48000:
+			__assign_bit(3, mult, 1);
+			/* fall through */
+		case 44100:
+			__assign_bit(2, mult, 1);
+			break;
+		case 32000:
+			__assign_bit(3, mult, 1);
+			/* fall through */
+		case 29400:
+			__assign_bit(0, mult, 1);
+			__assign_bit(1, mult, 1);
+			break;
+		case 24000:
+			__assign_bit(3, mult, 1);
+			/* fall through */
+		case 22050:
+			__assign_bit(1, mult, 1);
+			break;
+		case 16000:
+			__assign_bit(3, mult, 1);
+			/* fall through */
+		case 14700:
+			__assign_bit(0, mult, 1);
+			break;
+		case 8000:
+			__assign_bit(3, mult, 1);
+			break;
+		default:
+			return -EINVAL;
+		}
+		break;
+	default:
+		return -EINVAL;
+	}
+	gpiod_set_array_value_cansleep(mult_gpios->ndescs, mult_gpios->desc,
+				       NULL, mult);
+
+	return 0;
+}
+
+static struct snd_soc_ops audioinjector_octo_ops = {
+	.startup	= audioinjector_octo_startup,
+	.shutdown	= audioinjector_octo_shutdown,
+	.hw_params = audioinjector_octo_hw_params,
+	.trigger = audioinjector_octo_trigger,
+};
+
+SND_SOC_DAILINK_DEFS(audioinjector_octo,
+	DAILINK_COMP_ARRAY(COMP_EMPTY()),
+	DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "cs42448")),
+	DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+static struct snd_soc_dai_link audioinjector_octo_dai[] = {
+	{
+		.name = "AudioInjector Octo",
+		.stream_name = "AudioInject-HIFI",
+		.ops = &audioinjector_octo_ops,
+		.init = audioinjector_octo_dai_init,
+		.symmetric_rates = 1,
+		.symmetric_channels = 1,
+		SND_SOC_DAILINK_REG(audioinjector_octo),
+	},
+};
+
+static const struct snd_soc_dapm_widget audioinjector_octo_widgets[] = {
+	SND_SOC_DAPM_OUTPUT("OUTPUTS0"),
+	SND_SOC_DAPM_OUTPUT("OUTPUTS1"),
+	SND_SOC_DAPM_OUTPUT("OUTPUTS2"),
+	SND_SOC_DAPM_OUTPUT("OUTPUTS3"),
+	SND_SOC_DAPM_INPUT("INPUTS0"),
+	SND_SOC_DAPM_INPUT("INPUTS1"),
+	SND_SOC_DAPM_INPUT("INPUTS2"),
+};
+
+static const struct snd_soc_dapm_route audioinjector_octo_route[] = {
+	/* Balanced outputs */
+	{"OUTPUTS0", NULL, "AOUT1L"},
+	{"OUTPUTS0", NULL, "AOUT1R"},
+	{"OUTPUTS1", NULL, "AOUT2L"},
+	{"OUTPUTS1", NULL, "AOUT2R"},
+	{"OUTPUTS2", NULL, "AOUT3L"},
+	{"OUTPUTS2", NULL, "AOUT3R"},
+	{"OUTPUTS3", NULL, "AOUT4L"},
+	{"OUTPUTS3", NULL, "AOUT4R"},
+
+	/* Balanced inputs */
+	{"AIN1L", NULL, "INPUTS0"},
+	{"AIN1R", NULL, "INPUTS0"},
+	{"AIN2L", NULL, "INPUTS1"},
+	{"AIN2R", NULL, "INPUTS1"},
+	{"AIN3L", NULL, "INPUTS2"},
+	{"AIN3R", NULL, "INPUTS2"},
+};
+
+static struct snd_soc_card snd_soc_audioinjector_octo = {
+	.name = "audioinjector-octo-soundcard",
+	.dai_link = audioinjector_octo_dai,
+	.num_links = ARRAY_SIZE(audioinjector_octo_dai),
+
+	.dapm_widgets = audioinjector_octo_widgets,
+	.num_dapm_widgets = ARRAY_SIZE(audioinjector_octo_widgets),
+	.dapm_routes = audioinjector_octo_route,
+	.num_dapm_routes = ARRAY_SIZE(audioinjector_octo_route),
+};
+
+static int audioinjector_octo_probe(struct platform_device *pdev)
+{
+	struct snd_soc_card *card = &snd_soc_audioinjector_octo;
+	int ret;
+
+	card->dev = &pdev->dev;
+
+	if (pdev->dev.of_node) {
+		struct snd_soc_dai_link *dai = &audioinjector_octo_dai[0];
+		struct device_node *i2s_node =
+					of_parse_phandle(pdev->dev.of_node,
+							"i2s-controller", 0);
+		struct device_node *codec_node =
+					of_parse_phandle(pdev->dev.of_node,
+								"codec", 0);
+
+		mult_gpios = devm_gpiod_get_array_optional(&pdev->dev, "mult",
+								GPIOD_OUT_LOW);
+		if (IS_ERR(mult_gpios))
+			return PTR_ERR(mult_gpios);
+
+		codec_rst_gpio = devm_gpiod_get_optional(&pdev->dev, "reset",
+								GPIOD_OUT_LOW);
+		if (IS_ERR(codec_rst_gpio))
+			return PTR_ERR(codec_rst_gpio);
+
+		non_stop_clocks = of_property_read_bool(pdev->dev.of_node, "non-stop-clocks");
+
+		if (codec_rst_gpio)
+			gpiod_set_value(codec_rst_gpio, 1);
+		msleep(500);
+		if (codec_rst_gpio)
+			gpiod_set_value(codec_rst_gpio, 0);
+		msleep(500);
+		if (codec_rst_gpio)
+			gpiod_set_value(codec_rst_gpio, 1);
+		msleep(500);
+
+		if (i2s_node && codec_node) {
+			dai->cpus->dai_name = NULL;
+			dai->cpus->of_node = i2s_node;
+			dai->platforms->name = NULL;
+			dai->platforms->of_node = i2s_node;
+			dai->codecs->name = NULL;
+			dai->codecs->of_node = codec_node;
+		} else
+			if (!i2s_node) {
+				dev_err(&pdev->dev,
+				"i2s-controller missing or invalid in DT\n");
+				return -EINVAL;
+			} else {
+				dev_err(&pdev->dev,
+				"Property 'codec' missing or invalid\n");
+				return -EINVAL;
+			}
+	}
+
+	ret = devm_snd_soc_register_card(&pdev->dev, card);
+	if (ret != 0)
+		dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret);
+	return ret;
+}
+
+static const struct of_device_id audioinjector_octo_of_match[] = {
+	{ .compatible = "ai,audioinjector-octo-soundcard", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, audioinjector_octo_of_match);
+
+static struct platform_driver audioinjector_octo_driver = {
+	.driver	= {
+		.name			= "audioinjector-octo",
+		.owner			= THIS_MODULE,
+		.of_match_table = audioinjector_octo_of_match,
+	},
+	.probe	= audioinjector_octo_probe,
+};
+
+module_platform_driver(audioinjector_octo_driver);
+MODULE_AUTHOR("Matt Flax <flatmax@flatmax.org>");
+MODULE_DESCRIPTION("AudioInjector.net octo Soundcard");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:audioinjector-octo-soundcard");
diff -Nur --no-dereference linux-5.10.52-orig/sound/soc/bcm/audioinjector-pi-soundcard.c linux-5.10.52-v7l+/sound/soc/bcm/audioinjector-pi-soundcard.c
--- linux-5.10.52-orig/sound/soc/bcm/audioinjector-pi-soundcard.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/sound/soc/bcm/audioinjector-pi-soundcard.c	2021-07-25 16:46:40.147717820 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/*
+ * ASoC Driver for AudioInjector Pi add on soundcard
+ *
+ *  Created on: 13-May-2016
+ *      Author: flatmax@flatmax.org
+ *              based on code by  Cliff Cai <Cliff.Cai@analog.com> for the ssm2602 machine blackfin.
+ *              with help from Lars-Peter Clausen for simplifying the original code to use the dai_fmt field.
+ *		i2s_node code taken from the other sound/soc/bcm machine drivers.
+ *
+ * Copyright (C) 2016 Flatmax Pty. Ltd.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <sound/pcm_params.h>
+#include <sound/control.h>
+
+#include "../codecs/wm8731.h"
+
+static const unsigned int bcm2835_rates_12000000[] = {
+	8000, 16000, 32000, 44100, 48000, 96000, 88200,
+};
+
+static struct snd_pcm_hw_constraint_list bcm2835_constraints_12000000 = {
+	.list = bcm2835_rates_12000000,
+	.count = ARRAY_SIZE(bcm2835_rates_12000000),
+};
+
+static int snd_audioinjector_pi_soundcard_startup(struct snd_pcm_substream *substream)
+{
+	/* Setup constraints, because there is a 12 MHz XTAL on the board */
+	snd_pcm_hw_constraint_list(substream->runtime, 0,
+				SNDRV_PCM_HW_PARAM_RATE,
+				&bcm2835_constraints_12000000);
+	return 0;
+}
+
+static int snd_audioinjector_pi_soundcard_hw_params(struct snd_pcm_substream *substream,
+				       struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
+
+	switch (params_rate(params)){
+		case 8000:
+			return snd_soc_dai_set_bclk_ratio(cpu_dai, 1);
+		case 16000:
+			return snd_soc_dai_set_bclk_ratio(cpu_dai, 750);
+		case 32000:
+			return snd_soc_dai_set_bclk_ratio(cpu_dai, 375);
+		case 44100:
+			return snd_soc_dai_set_bclk_ratio(cpu_dai, 272);
+		case 48000:
+			return snd_soc_dai_set_bclk_ratio(cpu_dai, 250);
+		case 88200:
+			return snd_soc_dai_set_bclk_ratio(cpu_dai, 136);
+		case 96000:
+			return snd_soc_dai_set_bclk_ratio(cpu_dai, 125);
+		default:
+			return snd_soc_dai_set_bclk_ratio(cpu_dai, 125);
+	}
+}
+
+/* machine stream operations */
+static struct snd_soc_ops snd_audioinjector_pi_soundcard_ops = {
+	.startup = snd_audioinjector_pi_soundcard_startup,
+	.hw_params = snd_audioinjector_pi_soundcard_hw_params,
+};
+
+static int audioinjector_pi_soundcard_dai_init(struct snd_soc_pcm_runtime *rtd)
+{
+	return snd_soc_dai_set_sysclk(asoc_rtd_to_codec(rtd, 0), WM8731_SYSCLK_XTAL, 12000000, SND_SOC_CLOCK_IN);
+}
+
+SND_SOC_DAILINK_DEFS(audioinjector_pi,
+	DAILINK_COMP_ARRAY(COMP_CPU("bcm2708-i2s.0")),
+	DAILINK_COMP_ARRAY(COMP_CODEC("wm8731.1-001a", "wm8731-hifi")),
+	DAILINK_COMP_ARRAY(COMP_PLATFORM("bcm2835-i2s.0")));
+
+static struct snd_soc_dai_link audioinjector_pi_soundcard_dai[] = {
+	{
+		.name = "AudioInjector audio",
+		.stream_name = "AudioInjector audio",
+		.ops = &snd_audioinjector_pi_soundcard_ops,
+		.init = audioinjector_pi_soundcard_dai_init,
+		.dai_fmt = SND_SOC_DAIFMT_CBM_CFM|SND_SOC_DAIFMT_I2S|SND_SOC_DAIFMT_NB_NF,
+		SND_SOC_DAILINK_REG(audioinjector_pi),
+	},
+};
+
+static const struct snd_soc_dapm_widget wm8731_dapm_widgets[] = {
+	SND_SOC_DAPM_HP("Headphone Jack", NULL),
+	SND_SOC_DAPM_SPK("Ext Spk", NULL),
+	SND_SOC_DAPM_LINE("Line In Jacks", NULL),
+	SND_SOC_DAPM_MIC("Microphone", NULL),
+};
+
+static const struct snd_soc_dapm_route audioinjector_audio_map[] = {
+	/* headphone connected to LHPOUT, RHPOUT */
+	{"Headphone Jack", NULL, "LHPOUT"},
+	{"Headphone Jack", NULL, "RHPOUT"},
+
+	/* speaker connected to LOUT, ROUT */
+	{"Ext Spk", NULL, "ROUT"},
+	{"Ext Spk", NULL, "LOUT"},
+
+	/* line inputs */
+	{"Line In Jacks", NULL, "Line Input"},
+
+	/* mic is connected to Mic Jack, with WM8731 Mic Bias */
+	{"Microphone", NULL, "Mic Bias"},
+};
+
+static struct snd_soc_card snd_soc_audioinjector = {
+	.name = "audioinjector-pi-soundcard",
+	.dai_link = audioinjector_pi_soundcard_dai,
+	.num_links = ARRAY_SIZE(audioinjector_pi_soundcard_dai),
+
+	.dapm_widgets = wm8731_dapm_widgets,
+	.num_dapm_widgets = ARRAY_SIZE(wm8731_dapm_widgets),
+	.dapm_routes = audioinjector_audio_map,
+	.num_dapm_routes = ARRAY_SIZE(audioinjector_audio_map),
+};
+
+static int audioinjector_pi_soundcard_probe(struct platform_device *pdev)
+{
+	struct snd_soc_card *card = &snd_soc_audioinjector;
+	int ret;
+	
+	card->dev = &pdev->dev;
+
+	if (pdev->dev.of_node) {
+		struct snd_soc_dai_link *dai = &audioinjector_pi_soundcard_dai[0];
+		struct device_node *i2s_node = of_parse_phandle(pdev->dev.of_node,
+								"i2s-controller", 0);
+
+		if (i2s_node) {
+			dai->cpus->dai_name = NULL;
+			dai->cpus->of_node = i2s_node;
+			dai->platforms->name = NULL;
+			dai->platforms->of_node = i2s_node;
+		} else
+			if (!dai->cpus->of_node) {
+				dev_err(&pdev->dev, "Property 'i2s-controller' missing or invalid\n");
+				return -EINVAL;
+			}
+	}
+
+	if ((ret = devm_snd_soc_register_card(&pdev->dev, card))) {
+		dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret);
+	}
+	return ret;
+}
+
+static const struct of_device_id audioinjector_pi_soundcard_of_match[] = {
+	{ .compatible = "ai,audioinjector-pi-soundcard", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, audioinjector_pi_soundcard_of_match);
+
+static struct platform_driver audioinjector_pi_soundcard_driver = {
+       .driver         = {
+		.name   = "audioinjector-stereo",
+		.owner  = THIS_MODULE,
+		.of_match_table = audioinjector_pi_soundcard_of_match,
+       },
+       .probe          = audioinjector_pi_soundcard_probe,
+};
+
+module_platform_driver(audioinjector_pi_soundcard_driver);
+MODULE_AUTHOR("Matt Flax <flatmax@flatmax.org>");
+MODULE_DESCRIPTION("AudioInjector.net Pi Soundcard");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:audioinjector-pi-soundcard");
+
diff -Nur --no-dereference linux-5.10.52-orig/sound/soc/bcm/audiosense-pi.c linux-5.10.52-v7l+/sound/soc/bcm/audiosense-pi.c
--- linux-5.10.52-orig/sound/soc/bcm/audiosense-pi.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/sound/soc/bcm/audiosense-pi.c	2021-07-25 16:46:40.147717820 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/*
+ * ASoC Driver for AudioSense add on soundcard
+ * Author:
+ *	Bhargav A K <anur.bhargav@gmail.com>
+ *	Copyright 2017
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/i2c.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/jack.h>
+#include <sound/control.h>
+
+#include <sound/tlv320aic32x4.h>
+#include "../codecs/tlv320aic32x4.h"
+
+#define AIC32X4_SYSCLK_XTAL	0x00
+
+/*
+ * Setup Codec Sample Rates and Channels
+ * Supported Rates:
+ *	8000, 11025, 16000, 22050, 32000, 44100, 48000, 64000, 88200, 96000,
+ */
+static const unsigned int audiosense_pi_rate[] = {
+	48000,
+};
+
+static struct snd_pcm_hw_constraint_list audiosense_constraints_rates = {
+	.list = audiosense_pi_rate,
+	.count = ARRAY_SIZE(audiosense_pi_rate),
+};
+
+static const unsigned int audiosense_pi_channels[] = {
+	2,
+};
+
+static struct snd_pcm_hw_constraint_list audiosense_constraints_ch = {
+	.count = ARRAY_SIZE(audiosense_pi_channels),
+	.list = audiosense_pi_channels,
+	.mask = 0,
+};
+
+/* Setup DAPM widgets and paths */
+static const struct snd_soc_dapm_widget audiosense_pi_dapm_widgets[] = {
+	SND_SOC_DAPM_HP("Headphone Jack", NULL),
+	SND_SOC_DAPM_LINE("Line Out", NULL),
+	SND_SOC_DAPM_LINE("Line In", NULL),
+	SND_SOC_DAPM_INPUT("CM_L"),
+	SND_SOC_DAPM_INPUT("CM_R"),
+};
+
+static const struct snd_soc_dapm_route audiosense_pi_audio_map[] = {
+	/* Line Inputs are connected to
+	 * (IN1_L | IN1_R)
+	 * (IN2_L | IN2_R)
+	 * (IN3_L | IN3_R)
+	 */
+	{"IN1_L", NULL, "Line In"},
+	{"IN1_R", NULL, "Line In"},
+	{"IN2_L", NULL, "Line In"},
+	{"IN2_R", NULL, "Line In"},
+	{"IN3_L", NULL, "Line In"},
+	{"IN3_R", NULL, "Line In"},
+
+	/* Mic is connected to IN2_L and IN2_R */
+	{"Left ADC", NULL, "Mic Bias"},
+	{"Right ADC", NULL, "Mic Bias"},
+
+	/* Headphone connected to HPL, HPR */
+	{"Headphone Jack", NULL, "HPL"},
+	{"Headphone Jack", NULL, "HPR"},
+
+	/* Speakers connected to LOL and LOR */
+	{"Line Out", NULL, "LOL"},
+	{"Line Out", NULL, "LOR"},
+};
+
+static int audiosense_pi_card_init(struct snd_soc_pcm_runtime *rtd)
+{
+	/* TODO: init of the codec specific dapm data, ignore suspend/resume */
+	struct snd_soc_component *component = asoc_rtd_to_codec(rtd, 0)->component;
+
+	snd_soc_component_update_bits(component, AIC32X4_MICBIAS, 0x78,
+				      AIC32X4_MICBIAS_LDOIN |
+				      AIC32X4_MICBIAS_2075V);
+	snd_soc_component_update_bits(component, AIC32X4_PWRCFG, 0x08,
+				      AIC32X4_AVDDWEAKDISABLE);
+	snd_soc_component_update_bits(component, AIC32X4_LDOCTL, 0x01,
+				      AIC32X4_LDOCTLEN);
+
+	return 0;
+}
+
+static int audiosense_pi_card_hw_params(
+		struct snd_pcm_substream *substream,
+		struct snd_pcm_hw_params *params)
+{
+	int ret = 0;
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
+
+	/* Set the codec system clock, there is a 12 MHz XTAL on the board */
+	ret = snd_soc_dai_set_sysclk(codec_dai, AIC32X4_SYSCLK_XTAL,
+				     12000000, SND_SOC_CLOCK_IN);
+	if (ret) {
+		dev_err(rtd->card->dev,
+			"could not set codec driver clock params\n");
+		return ret;
+	}
+	return 0;
+}
+
+static int audiosense_pi_card_startup(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+
+	/*
+	 * Set codec to 48Khz Sampling, Stereo I/O and 16 bit audio
+	 */
+	runtime->hw.channels_max = 2;
+	snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+				   &audiosense_constraints_ch);
+
+	runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE;
+	snd_pcm_hw_constraint_msbits(runtime, 0, 16, 16);
+
+
+	snd_pcm_hw_constraint_list(substream->runtime, 0,
+				   SNDRV_PCM_HW_PARAM_RATE,
+				   &audiosense_constraints_rates);
+	return 0;
+}
+
+static struct snd_soc_ops audiosense_pi_card_ops = {
+	.startup = audiosense_pi_card_startup,
+	.hw_params = audiosense_pi_card_hw_params,
+};
+
+SND_SOC_DAILINK_DEFS(audiosense_pi,
+	DAILINK_COMP_ARRAY(COMP_CPU("bcm2708-i2s.0")),
+	DAILINK_COMP_ARRAY(COMP_CODEC("tlv320aic32x4.1-0018", "tlv320aic32x4-hifi")),
+	DAILINK_COMP_ARRAY(COMP_PLATFORM("bcm2708-i2s.0")));
+
+static struct snd_soc_dai_link audiosense_pi_card_dai[] = {
+	{
+		.name           = "TLV320AIC3204 Audio",
+		.stream_name    = "TLV320AIC3204 Hifi Audio",
+		.dai_fmt        = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+			SND_SOC_DAIFMT_CBM_CFM,
+		.ops            = &audiosense_pi_card_ops,
+		.init           = audiosense_pi_card_init,
+		SND_SOC_DAILINK_REG(audiosense_pi),
+	},
+};
+
+static struct snd_soc_card audiosense_pi_card = {
+	.name		= "audiosense-pi",
+	.driver_name	= "audiosense-pi",
+	.dai_link	= audiosense_pi_card_dai,
+	.owner		= THIS_MODULE,
+	.num_links	= ARRAY_SIZE(audiosense_pi_card_dai),
+	.dapm_widgets	= audiosense_pi_dapm_widgets,
+	.num_dapm_widgets = ARRAY_SIZE(audiosense_pi_dapm_widgets),
+	.dapm_routes	= audiosense_pi_audio_map,
+	.num_dapm_routes = ARRAY_SIZE(audiosense_pi_audio_map),
+};
+
+static int audiosense_pi_card_probe(struct platform_device *pdev)
+{
+	int ret = 0;
+	struct snd_soc_card *card = &audiosense_pi_card;
+	struct snd_soc_dai_link *dai = &audiosense_pi_card_dai[0];
+	struct device_node *i2s_node = pdev->dev.of_node;
+
+	card->dev = &pdev->dev;
+
+	if (!dai) {
+		dev_err(&pdev->dev, "DAI not found. Missing or Invalid\n");
+		return -EINVAL;
+	}
+
+	i2s_node = of_parse_phandle(pdev->dev.of_node, "i2s-controller", 0);
+	if (!i2s_node) {
+		dev_err(&pdev->dev,
+			"Property 'i2s-controller' missing or invalid\n");
+		return -EINVAL;
+	}
+
+	dai->cpus->dai_name = NULL;
+	dai->cpus->of_node = i2s_node;
+	dai->platforms->name = NULL;
+	dai->platforms->of_node = i2s_node;
+
+	of_node_put(i2s_node);
+
+	ret = snd_soc_register_card(card);
+	if (ret && ret != -EPROBE_DEFER)
+		dev_err(&pdev->dev,
+			"snd_soc_register_card() failed: %d\n", ret);
+
+	return ret;
+}
+
+static int audiosense_pi_card_remove(struct platform_device *pdev)
+{
+	struct snd_soc_card *card = platform_get_drvdata(pdev);
+
+	return snd_soc_unregister_card(card);
+
+}
+
+static const struct of_device_id audiosense_pi_card_of_match[] = {
+	{ .compatible = "as,audiosense-pi", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, audiosense_pi_card_of_match);
+
+static struct platform_driver audiosense_pi_card_driver = {
+	.driver = {
+		.name = "audiosense-snd-card",
+		.owner = THIS_MODULE,
+		.of_match_table = audiosense_pi_card_of_match,
+	},
+	.probe = audiosense_pi_card_probe,
+	.remove = audiosense_pi_card_remove,
+};
+
+module_platform_driver(audiosense_pi_card_driver);
+
+MODULE_AUTHOR("Bhargav AK <anur.bhargav@gmail.com>");
+MODULE_DESCRIPTION("ASoC Driver for TLV320AIC3204 Audio");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:audiosense-pi");
+
diff -Nur --no-dereference linux-5.10.52-orig/sound/soc/bcm/digidac1-soundcard.c linux-5.10.52-v7l+/sound/soc/bcm/digidac1-soundcard.c
--- linux-5.10.52-orig/sound/soc/bcm/digidac1-soundcard.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/sound/soc/bcm/digidac1-soundcard.c	2021-07-25 16:46:40.147717820 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/*
+ * ASoC Driver for RRA DigiDAC1
+ * Copyright 2016
+ * Author: José M. Tasende <vintage@redrocksaudio.es>
+ * based on the HifiBerry DAC driver by Florian Meier <florian.meier@koalo.de>
+ * and the Wolfson card driver by Nikesh Oswal, <Nikesh.Oswal@wolfsonmicro.com>
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/i2c.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/jack.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+#include <linux/regulator/consumer.h>
+
+#include "../codecs/wm8804.h"
+#include "../codecs/wm8741.h"
+
+#define WM8741_NUM_SUPPLIES 2
+
+/* codec private data */
+struct wm8741_priv {
+	struct wm8741_platform_data pdata;
+	struct regmap *regmap;
+	struct regulator_bulk_data supplies[WM8741_NUM_SUPPLIES];
+	unsigned int sysclk;
+	const struct snd_pcm_hw_constraint_list *sysclk_constraints;
+};
+
+static int samplerate = 44100;
+
+/* New Alsa Controls not exposed by original wm8741 codec driver	*/
+/* in actual driver the att. adjustment is wrong because		*/
+/* this DAC has a coarse attenuation register with 4dB steps		*/
+/* and a fine level register with 0.125dB steps				*/
+/* each register has 32 steps so combining both we have	1024 steps	*/
+/* of 0.125 dB.								*/
+/* The original level controls from driver are removed at startup	*/
+/* and replaced by the corrected ones.					*/
+/* The same wm8741 driver can be used for wm8741 and wm8742 devices	*/
+
+static const DECLARE_TLV_DB_SCALE(dac_tlv_fine, 0, 13, 0);
+static const DECLARE_TLV_DB_SCALE(dac_tlv_coarse, -12700, 400, 1);
+static const char *w8741_dither[4] = {"Off", "RPDF", "TPDF", "HPDF"};
+static const char *w8741_filter[5] = {
+		"Type 1", "Type 2", "Type 3", "Type 4", "Type 5"};
+static const char *w8741_switch[2] = {"Off", "On"};
+static const struct soc_enum w8741_enum[] = {
+SOC_ENUM_SINGLE(WM8741_MODE_CONTROL_2, 0, 4, w8741_dither),/* dithering type */
+SOC_ENUM_SINGLE(WM8741_FILTER_CONTROL, 0, 5, w8741_filter),/* filter type */
+SOC_ENUM_SINGLE(WM8741_FORMAT_CONTROL, 6, 2, w8741_switch),/* phase invert */
+SOC_ENUM_SINGLE(WM8741_VOLUME_CONTROL, 0, 2, w8741_switch),/* volume ramp */
+SOC_ENUM_SINGLE(WM8741_VOLUME_CONTROL, 3, 2, w8741_switch),/* soft mute */
+};
+
+static const struct snd_kcontrol_new w8741_snd_controls_stereo[] = {
+SOC_DOUBLE_R_TLV("DAC Fine Playback Volume", WM8741_DACLLSB_ATTENUATION,
+		WM8741_DACRLSB_ATTENUATION, 0, 31, 1, dac_tlv_fine),
+SOC_DOUBLE_R_TLV("Digital Playback Volume", WM8741_DACLMSB_ATTENUATION,
+		WM8741_DACRMSB_ATTENUATION, 0, 31, 1, dac_tlv_coarse),
+SOC_ENUM("DAC Dither", w8741_enum[0]),
+SOC_ENUM("DAC Digital Filter", w8741_enum[1]),
+SOC_ENUM("DAC Phase Invert", w8741_enum[2]),
+SOC_ENUM("DAC Volume Ramp", w8741_enum[3]),
+SOC_ENUM("DAC Soft Mute", w8741_enum[4]),
+};
+
+static const struct snd_kcontrol_new w8741_snd_controls_mono_left[] = {
+SOC_SINGLE_TLV("DAC Fine Playback Volume", WM8741_DACLLSB_ATTENUATION,
+		0, 31, 0, dac_tlv_fine),
+SOC_SINGLE_TLV("Digital Playback Volume", WM8741_DACLMSB_ATTENUATION,
+		0, 31, 1, dac_tlv_coarse),
+SOC_ENUM("DAC Dither", w8741_enum[0]),
+SOC_ENUM("DAC Digital Filter", w8741_enum[1]),
+SOC_ENUM("DAC Phase Invert", w8741_enum[2]),
+SOC_ENUM("DAC Volume Ramp", w8741_enum[3]),
+SOC_ENUM("DAC Soft Mute", w8741_enum[4]),
+};
+
+static const struct snd_kcontrol_new w8741_snd_controls_mono_right[] = {
+SOC_SINGLE_TLV("DAC Fine Playback Volume", WM8741_DACRLSB_ATTENUATION,
+	0, 31, 0, dac_tlv_fine),
+SOC_SINGLE_TLV("Digital Playback Volume", WM8741_DACRMSB_ATTENUATION,
+	0, 31, 1, dac_tlv_coarse),
+SOC_ENUM("DAC Dither", w8741_enum[0]),
+SOC_ENUM("DAC Digital Filter", w8741_enum[1]),
+SOC_ENUM("DAC Phase Invert", w8741_enum[2]),
+SOC_ENUM("DAC Volume Ramp", w8741_enum[3]),
+SOC_ENUM("DAC Soft Mute", w8741_enum[4]),
+};
+
+static int w8741_add_controls(struct snd_soc_component *component)
+{
+	struct wm8741_priv *wm8741 = snd_soc_component_get_drvdata(component);
+
+	switch (wm8741->pdata.diff_mode) {
+	case WM8741_DIFF_MODE_STEREO:
+	case WM8741_DIFF_MODE_STEREO_REVERSED:
+		snd_soc_add_component_controls(component,
+				w8741_snd_controls_stereo,
+				ARRAY_SIZE(w8741_snd_controls_stereo));
+		break;
+	case WM8741_DIFF_MODE_MONO_LEFT:
+		snd_soc_add_component_controls(component,
+				w8741_snd_controls_mono_left,
+				ARRAY_SIZE(w8741_snd_controls_mono_left));
+		break;
+	case WM8741_DIFF_MODE_MONO_RIGHT:
+		snd_soc_add_component_controls(component,
+				w8741_snd_controls_mono_right,
+				ARRAY_SIZE(w8741_snd_controls_mono_right));
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int digidac1_soundcard_init(struct snd_soc_pcm_runtime *rtd)
+{
+	struct snd_soc_component *component = asoc_rtd_to_codec(rtd, 0)->component;
+	struct snd_soc_card *card = rtd->card;
+	struct snd_soc_pcm_runtime *wm8741_rtd;
+	struct snd_soc_component *wm8741_component;
+	struct snd_card *sound_card = card->snd_card;
+	struct snd_kcontrol *kctl;
+	int ret;
+
+	wm8741_rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[1]);
+	if (!wm8741_rtd) {
+		dev_warn(card->dev, "digidac1_soundcard_init: couldn't get wm8741 rtd\n");
+		return -EFAULT;
+	}
+	wm8741_component = asoc_rtd_to_codec(wm8741_rtd, 0)->component;
+	ret = w8741_add_controls(wm8741_component);
+	if (ret < 0)
+		dev_warn(card->dev, "Failed to add new wm8741 controls: %d\n",
+		ret);
+
+	/* enable TX output */
+	snd_soc_component_update_bits(component, WM8804_PWRDN, 0x4, 0x0);
+
+	kctl = snd_soc_card_get_kcontrol(card,
+		"Playback Volume");
+	if (kctl) {
+		kctl->vd[0].access = SNDRV_CTL_ELEM_ACCESS_READWRITE;
+		snd_ctl_remove(sound_card, kctl);
+		}
+	kctl = snd_soc_card_get_kcontrol(card,
+		"Fine Playback Volume");
+	if (kctl) {
+		kctl->vd[0].access = SNDRV_CTL_ELEM_ACCESS_READWRITE;
+		snd_ctl_remove(sound_card, kctl);
+		}
+	return 0;
+}
+
+static int digidac1_soundcard_startup(struct snd_pcm_substream *substream)
+{
+	/* turn on wm8804 digital output */
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_component *component = asoc_rtd_to_codec(rtd, 0)->component;
+	struct snd_soc_card *card = rtd->card;
+	struct snd_soc_pcm_runtime *wm8741_rtd;
+	struct snd_soc_component *wm8741_component;
+
+	snd_soc_component_update_bits(component, WM8804_PWRDN, 0x3c, 0x00);
+	wm8741_rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[1]);
+	if (!wm8741_rtd) {
+		dev_warn(card->dev, "digidac1_soundcard_startup: couldn't get WM8741 rtd\n");
+		return -EFAULT;
+	}
+	wm8741_component = asoc_rtd_to_codec(wm8741_rtd, 0)->component;
+
+	/* latch wm8741 level */
+	snd_soc_component_update_bits(wm8741_component, WM8741_DACLLSB_ATTENUATION,
+		WM8741_UPDATELL, WM8741_UPDATELL);
+	snd_soc_component_update_bits(wm8741_component, WM8741_DACLMSB_ATTENUATION,
+		WM8741_UPDATELM, WM8741_UPDATELM);
+	snd_soc_component_update_bits(wm8741_component, WM8741_DACRLSB_ATTENUATION,
+		WM8741_UPDATERL, WM8741_UPDATERL);
+	snd_soc_component_update_bits(wm8741_component, WM8741_DACRMSB_ATTENUATION,
+		WM8741_UPDATERM, WM8741_UPDATERM);
+
+	return 0;
+}
+
+static void digidac1_soundcard_shutdown(struct snd_pcm_substream *substream)
+{
+	/* turn off wm8804 digital output */
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_component *component = asoc_rtd_to_codec(rtd, 0)->component;
+
+	snd_soc_component_update_bits(component, WM8804_PWRDN, 0x3c, 0x3c);
+}
+
+static int digidac1_soundcard_hw_params(struct snd_pcm_substream *substream,
+				       struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
+	struct snd_soc_component *component = asoc_rtd_to_codec(rtd, 0)->component;
+	struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
+	struct snd_soc_card *card = rtd->card;
+	struct snd_soc_pcm_runtime *wm8741_rtd;
+	struct snd_soc_component *wm8741_component;
+
+	int sysclk = 27000000;
+	long mclk_freq = 0;
+	int mclk_div = 1;
+	int sampling_freq = 1;
+	int ret;
+
+	wm8741_rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[1]);
+	if (!wm8741_rtd) {
+		dev_warn(card->dev, "digidac1_soundcard_hw_params: couldn't get WM8741 rtd\n");
+		return -EFAULT;
+	}
+	wm8741_component = asoc_rtd_to_codec(wm8741_rtd, 0)->component;
+	samplerate = params_rate(params);
+
+	if (samplerate <= 96000) {
+		mclk_freq = samplerate*256;
+		mclk_div = WM8804_MCLKDIV_256FS;
+	} else {
+		mclk_freq = samplerate*128;
+		mclk_div = WM8804_MCLKDIV_128FS;
+		}
+
+	switch (samplerate) {
+	case 32000:
+		sampling_freq = 0x03;
+		break;
+	case 44100:
+		sampling_freq = 0x00;
+		break;
+	case 48000:
+		sampling_freq = 0x02;
+		break;
+	case 88200:
+		sampling_freq = 0x08;
+		break;
+	case 96000:
+		sampling_freq = 0x0a;
+		break;
+	case 176400:
+		sampling_freq = 0x0c;
+		break;
+	case 192000:
+		sampling_freq = 0x0e;
+		break;
+	default:
+		dev_err(card->dev,
+		"Failed to set WM8804 SYSCLK, unsupported samplerate %d\n",
+		samplerate);
+	}
+
+	snd_soc_dai_set_clkdiv(codec_dai, WM8804_MCLK_DIV, mclk_div);
+	snd_soc_dai_set_pll(codec_dai, 0, 0, sysclk, mclk_freq);
+
+	ret = snd_soc_dai_set_sysclk(codec_dai, WM8804_TX_CLKSRC_PLL,
+		sysclk, SND_SOC_CLOCK_OUT);
+	if (ret < 0) {
+		dev_err(card->dev,
+		"Failed to set WM8804 SYSCLK: %d\n", ret);
+		return ret;
+	}
+	/* Enable wm8804 TX output */
+	snd_soc_component_update_bits(component, WM8804_PWRDN, 0x4, 0x0);
+
+	/* wm8804 Power on */
+	snd_soc_component_update_bits(component, WM8804_PWRDN, 0x9, 0);
+
+	/* wm8804 set sampling frequency status bits */
+	snd_soc_component_update_bits(component, WM8804_SPDTX4, 0x0f, sampling_freq);
+
+	/* Now update wm8741 registers for the correct oversampling */
+	if (samplerate <= 48000)
+		snd_soc_component_update_bits(wm8741_component, WM8741_MODE_CONTROL_1,
+		 WM8741_OSR_MASK, 0x00);
+	else if (samplerate <= 96000)
+		snd_soc_component_update_bits(wm8741_component, WM8741_MODE_CONTROL_1,
+		 WM8741_OSR_MASK, 0x20);
+	else
+		snd_soc_component_update_bits(wm8741_component, WM8741_MODE_CONTROL_1,
+		 WM8741_OSR_MASK, 0x40);
+
+	/* wm8741 bit size */
+	switch (params_width(params)) {
+	case 16:
+		snd_soc_component_update_bits(wm8741_component, WM8741_FORMAT_CONTROL,
+		 WM8741_IWL_MASK, 0x00);
+		break;
+	case 20:
+		snd_soc_component_update_bits(wm8741_component, WM8741_FORMAT_CONTROL,
+		 WM8741_IWL_MASK, 0x01);
+		break;
+	case 24:
+		snd_soc_component_update_bits(wm8741_component, WM8741_FORMAT_CONTROL,
+		 WM8741_IWL_MASK, 0x02);
+		break;
+	case 32:
+		snd_soc_component_update_bits(wm8741_component, WM8741_FORMAT_CONTROL,
+		 WM8741_IWL_MASK, 0x03);
+		break;
+	default:
+		dev_dbg(card->dev, "wm8741_hw_params:    Unsupported bit size param = %d",
+			params_width(params));
+		return -EINVAL;
+	}
+
+	return snd_soc_dai_set_bclk_ratio(cpu_dai, 64);
+}
+/* machine stream operations */
+static struct snd_soc_ops digidac1_soundcard_ops = {
+	.hw_params	= digidac1_soundcard_hw_params,
+	.startup	= digidac1_soundcard_startup,
+	.shutdown	= digidac1_soundcard_shutdown,
+};
+
+SND_SOC_DAILINK_DEFS(digidac1,
+	DAILINK_COMP_ARRAY(COMP_CPU("bcm2708-i2s.0")),
+	DAILINK_COMP_ARRAY(COMP_CODEC("wm8804.1-003b", "wm8804-spdif")),
+	DAILINK_COMP_ARRAY(COMP_PLATFORM("bcm2835-i2s.0")));
+
+SND_SOC_DAILINK_DEFS(digidac11,
+	DAILINK_COMP_ARRAY(COMP_CPU("wm8804-spdif")),
+	DAILINK_COMP_ARRAY(COMP_CODEC("wm8741.1-001a", "wm8741")),
+	DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+static struct snd_soc_dai_link digidac1_soundcard_dai[] = {
+	{
+	.name		= "RRA DigiDAC1",
+	.stream_name	= "RRA DigiDAC1 HiFi",
+	.dai_fmt	= SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+				SND_SOC_DAIFMT_CBM_CFM,
+	.ops		= &digidac1_soundcard_ops,
+	.init		= digidac1_soundcard_init,
+	SND_SOC_DAILINK_REG(digidac1),
+	},
+	{
+	.name		= "RRA DigiDAC11",
+	.stream_name	= "RRA DigiDAC11 HiFi",
+	.dai_fmt	= SND_SOC_DAIFMT_I2S
+			| SND_SOC_DAIFMT_NB_NF
+			| SND_SOC_DAIFMT_CBS_CFS,
+	SND_SOC_DAILINK_REG(digidac11),
+	},
+};
+
+/* audio machine driver */
+static struct snd_soc_card digidac1_soundcard = {
+	.name		= "digidac1-soundcard",
+	.owner		= THIS_MODULE,
+	.dai_link	= digidac1_soundcard_dai,
+	.num_links	= ARRAY_SIZE(digidac1_soundcard_dai),
+};
+
+static int digidac1_soundcard_probe(struct platform_device *pdev)
+{
+	int ret = 0;
+
+	digidac1_soundcard.dev = &pdev->dev;
+
+	if (pdev->dev.of_node) {
+		struct device_node *i2s_node;
+		struct snd_soc_dai_link *dai = &digidac1_soundcard_dai[0];
+
+		i2s_node = of_parse_phandle(pdev->dev.of_node,
+					"i2s-controller", 0);
+
+		if (i2s_node) {
+			dai->cpus->dai_name = NULL;
+			dai->cpus->of_node = i2s_node;
+			dai->platforms->name = NULL;
+			dai->platforms->of_node = i2s_node;
+		}
+	}
+
+	ret = devm_snd_soc_register_card(&pdev->dev, &digidac1_soundcard);
+	if (ret && ret != -EPROBE_DEFER)
+		dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
+			ret);
+
+	return ret;
+}
+
+static const struct of_device_id digidac1_soundcard_of_match[] = {
+	{ .compatible = "rra,digidac1-soundcard", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, digidac1_soundcard_of_match);
+
+static struct platform_driver digidac1_soundcard_driver = {
+	.driver = {
+			.name		= "digidac1-audio",
+			.owner		= THIS_MODULE,
+			.of_match_table	= digidac1_soundcard_of_match,
+	},
+	.probe		= digidac1_soundcard_probe,
+};
+
+module_platform_driver(digidac1_soundcard_driver);
+
+MODULE_AUTHOR("José M. Tasende <vintage@redrocksaudio.es>");
+MODULE_DESCRIPTION("ASoC Driver for RRA DigiDAC1");
+MODULE_LICENSE("GPL v2");
diff -Nur --no-dereference linux-5.10.52-orig/sound/soc/bcm/dionaudio_loco.c linux-5.10.52-v7l+/sound/soc/bcm/dionaudio_loco.c
--- linux-5.10.52-orig/sound/soc/bcm/dionaudio_loco.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/sound/soc/bcm/dionaudio_loco.c	2021-07-25 16:46:40.147717820 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/*
+ * ASoC Driver for Dion Audio LOCO DAC-AMP
+ *
+ * Author:      Miquel Blauw <info@dionaudio.nl>
+ *              Copyright 2016
+ *
+ * Based on the software of the RPi-DAC writen by Florian Meier
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/jack.h>
+
+static int snd_rpi_dionaudio_loco_hw_params(
+	struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
+
+	unsigned int sample_bits =
+		snd_pcm_format_physical_width(params_format(params));
+
+	return snd_soc_dai_set_bclk_ratio(cpu_dai, sample_bits * 2);
+}
+
+/* machine stream operations */
+static struct snd_soc_ops snd_rpi_dionaudio_loco_ops = {
+	.hw_params = snd_rpi_dionaudio_loco_hw_params,
+};
+
+SND_SOC_DAILINK_DEFS(dionaudio_loco,
+	DAILINK_COMP_ARRAY(COMP_CPU("bcm2708-i2s.0")),
+	DAILINK_COMP_ARRAY(COMP_CODEC("pcm5102a-codec", "pcm5102a-hifi")),
+	DAILINK_COMP_ARRAY(COMP_PLATFORM("bcm2708-i2s.0")));
+
+static struct snd_soc_dai_link snd_rpi_dionaudio_loco_dai[] = {
+{
+	.name		= "DionAudio LOCO",
+	.stream_name	= "DionAudio LOCO DAC-AMP",
+	.dai_fmt	= SND_SOC_DAIFMT_I2S |
+			  SND_SOC_DAIFMT_NB_NF |
+			  SND_SOC_DAIFMT_CBS_CFS,
+	.ops		= &snd_rpi_dionaudio_loco_ops,
+	SND_SOC_DAILINK_REG(dionaudio_loco),
+},
+};
+
+/* audio machine driver */
+static struct snd_soc_card snd_rpi_dionaudio_loco = {
+	.name		= "snd_rpi_dionaudio_loco",
+	.dai_link	= snd_rpi_dionaudio_loco_dai,
+	.num_links	= ARRAY_SIZE(snd_rpi_dionaudio_loco_dai),
+};
+
+static int snd_rpi_dionaudio_loco_probe(struct platform_device *pdev)
+{
+	struct device_node *np;
+	int ret = 0;
+
+	snd_rpi_dionaudio_loco.dev = &pdev->dev;
+
+	np = pdev->dev.of_node;
+	if (np) {
+		struct snd_soc_dai_link *dai = &snd_rpi_dionaudio_loco_dai[0];
+		struct device_node *i2s_np;
+
+		i2s_np = of_parse_phandle(np, "i2s-controller", 0);
+		if (i2s_np) {
+			dai->cpus->dai_name = NULL;
+			dai->cpus->of_node = i2s_np;
+			dai->platforms->name = NULL;
+			dai->platforms->of_node = i2s_np;
+		}
+	}
+
+	ret = devm_snd_soc_register_card(&pdev->dev, &snd_rpi_dionaudio_loco);
+	if (ret && ret != -EPROBE_DEFER)
+		dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
+			ret);
+
+	return ret;
+}
+
+static const struct of_device_id snd_rpi_dionaudio_loco_of_match[] = {
+	{ .compatible = "dionaudio,loco-pcm5242-tpa3118", },
+	{ /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, snd_rpi_dionaudio_loco_of_match);
+
+static struct platform_driver snd_rpi_dionaudio_loco_driver = {
+	.driver = {
+		.name		= "snd-dionaudio-loco",
+		.owner		= THIS_MODULE,
+		.of_match_table	= snd_rpi_dionaudio_loco_of_match,
+	},
+	.probe  = snd_rpi_dionaudio_loco_probe,
+};
+
+module_platform_driver(snd_rpi_dionaudio_loco_driver);
+
+MODULE_AUTHOR("Miquel Blauw <info@dionaudio.nl>");
+MODULE_DESCRIPTION("ASoC Driver for DionAudio LOCO");
+MODULE_LICENSE("GPL v2");
diff -Nur --no-dereference linux-5.10.52-orig/sound/soc/bcm/dionaudio_loco-v2.c linux-5.10.52-v7l+/sound/soc/bcm/dionaudio_loco-v2.c
--- linux-5.10.52-orig/sound/soc/bcm/dionaudio_loco-v2.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/sound/soc/bcm/dionaudio_loco-v2.c	2021-07-25 16:46:40.147717820 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/*
+ * ASoC Driver for Dion Audio LOCO-V2 DAC-AMP
+ *
+ * Author:      Miquel Blauw <info@dionaudio.nl>
+ *              Copyright 2017
+ *
+ * Based on the software of the RPi-DAC writen by Florian Meier
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/jack.h>
+
+static bool digital_gain_0db_limit = true;
+
+static int snd_rpi_dionaudio_loco_v2_init(struct snd_soc_pcm_runtime *rtd)
+{
+	if (digital_gain_0db_limit) {
+		int ret;
+		struct snd_soc_card *card = rtd->card;
+
+		ret = snd_soc_limit_volume(card, "Digital Playback Volume", 207);
+		if (ret < 0)
+			dev_warn(card->dev, "Failed to set volume limit: %d\n", ret);
+	}
+
+	return 0;
+}
+
+SND_SOC_DAILINK_DEFS(dionaudio_loco_v2,
+	DAILINK_COMP_ARRAY(COMP_CPU("bcm2708-i2s.0")),
+	DAILINK_COMP_ARRAY(COMP_CODEC("pcm512x.1-004d", "pcm512x-hifi")),
+	DAILINK_COMP_ARRAY(COMP_PLATFORM("bcm2708-i2s.0")));
+
+static struct snd_soc_dai_link snd_rpi_dionaudio_loco_v2_dai[] = {
+{
+	.name		= "DionAudio LOCO-V2",
+	.stream_name	= "DionAudio LOCO-V2 DAC-AMP",
+	.dai_fmt	= SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+			  SND_SOC_DAIFMT_CBS_CFS,
+	.init		= snd_rpi_dionaudio_loco_v2_init,
+	SND_SOC_DAILINK_REG(dionaudio_loco_v2),
+},};
+
+/* audio machine driver */
+static struct snd_soc_card snd_rpi_dionaudio_loco_v2 = {
+	.name         = "Dion Audio LOCO-V2",
+	.dai_link     = snd_rpi_dionaudio_loco_v2_dai,
+	.num_links    = ARRAY_SIZE(snd_rpi_dionaudio_loco_v2_dai),
+};
+
+static int snd_rpi_dionaudio_loco_v2_probe(struct platform_device *pdev)
+{
+	int ret = 0;
+
+	snd_rpi_dionaudio_loco_v2.dev = &pdev->dev;
+
+	if (pdev->dev.of_node) {
+		struct device_node *i2s_node;
+		struct snd_soc_dai_link *dai =
+					&snd_rpi_dionaudio_loco_v2_dai[0];
+
+		i2s_node = of_parse_phandle(pdev->dev.of_node,
+					    "i2s-controller", 0);
+		if (i2s_node) {
+			dai->cpus->dai_name = NULL;
+			dai->cpus->of_node = i2s_node;
+			dai->platforms->name = NULL;
+			dai->platforms->of_node = i2s_node;
+		}
+
+		digital_gain_0db_limit = !of_property_read_bool(
+			pdev->dev.of_node, "dionaudio,24db_digital_gain");
+	}
+
+	ret = devm_snd_soc_register_card(&pdev->dev, &snd_rpi_dionaudio_loco_v2);
+	if (ret)
+		dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
+			ret);
+
+	return ret;
+}
+
+static const struct of_device_id dionaudio_of_match[] = {
+	{ .compatible = "dionaudio,dionaudio-loco-v2", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, dionaudio_of_match);
+
+static struct platform_driver snd_rpi_dionaudio_loco_v2_driver = {
+	.driver = {
+		.name   = "snd-rpi-dionaudio-loco-v2",
+		.owner  = THIS_MODULE,
+		.of_match_table = dionaudio_of_match,
+	},
+	.probe          = snd_rpi_dionaudio_loco_v2_probe,
+};
+
+module_platform_driver(snd_rpi_dionaudio_loco_v2_driver);
+
+MODULE_AUTHOR("Miquel Blauw <info@dionaudio.nl>");
+MODULE_DESCRIPTION("ASoC Driver for DionAudio LOCO-V2");
+MODULE_LICENSE("GPL v2");
diff -Nur --no-dereference linux-5.10.52-orig/sound/soc/bcm/fe-pi-audio.c linux-5.10.52-v7l+/sound/soc/bcm/fe-pi-audio.c
--- linux-5.10.52-orig/sound/soc/bcm/fe-pi-audio.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/sound/soc/bcm/fe-pi-audio.c	2021-07-25 16:46:40.147717820 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/*
+ * ASoC Driver for Fe-Pi Audio Sound Card
+ *
+ * Author:	Henry Kupis <kuupaz@gmail.com>
+ *		Copyright 2016
+ *		based on code by Florian Meier <florian.meier@koalo.de>
+ *		based on code by Shawn Guo <shawn.guo@linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/jack.h>
+
+#include "../codecs/sgtl5000.h"
+
+static int snd_fe_pi_audio_init(struct snd_soc_pcm_runtime *rtd)
+{
+	struct snd_soc_card *card = rtd->card;
+	struct snd_soc_component *component = asoc_rtd_to_codec(rtd, 0)->component;
+
+	snd_soc_dapm_force_enable_pin(&card->dapm, "LO");
+	snd_soc_dapm_force_enable_pin(&card->dapm, "ADC");
+	snd_soc_dapm_force_enable_pin(&card->dapm, "DAC");
+	snd_soc_dapm_force_enable_pin(&card->dapm, "HP");
+	snd_soc_component_update_bits(component, SGTL5000_CHIP_ANA_POWER,
+			SGTL5000_VAG_POWERUP, SGTL5000_VAG_POWERUP);
+
+	return 0;
+}
+
+static int snd_fe_pi_audio_hw_params(struct snd_pcm_substream *substream,
+	struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct device *dev = rtd->card->dev;
+	struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
+
+	int ret;
+
+	/* Set SGTL5000's SYSCLK */
+	ret = snd_soc_dai_set_sysclk(codec_dai, SGTL5000_SYSCLK, 12288000, SND_SOC_CLOCK_IN);
+	if (ret) {
+		dev_err(dev, "could not set codec driver clock params\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+
+static struct snd_soc_ops snd_fe_pi_audio_ops = {
+	.hw_params = snd_fe_pi_audio_hw_params,
+};
+
+SND_SOC_DAILINK_DEFS(fe_pi,
+	DAILINK_COMP_ARRAY(COMP_CPU("bcm2708-i2s.0")),
+	DAILINK_COMP_ARRAY(COMP_CODEC("sgtl5000.1-000a", "sgtl5000")),
+	DAILINK_COMP_ARRAY(COMP_PLATFORM("bcm2708-i2s.0")));
+
+static struct snd_soc_dai_link snd_fe_pi_audio_dai[] = {
+	{
+		.name		= "FE-PI",
+		.stream_name	= "Fe-Pi HiFi",
+		.dai_fmt	= SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+					SND_SOC_DAIFMT_CBM_CFM,
+		.ops		= &snd_fe_pi_audio_ops,
+		.init		= snd_fe_pi_audio_init,
+		SND_SOC_DAILINK_REG(fe_pi),
+	},
+};
+
+static const struct snd_soc_dapm_route fe_pi_audio_dapm_routes[] = {
+	{"ADC", NULL, "Mic Bias"},
+};
+
+
+static struct snd_soc_card fe_pi_audio = {
+	.name         = "Fe-Pi Audio",
+	.owner        = THIS_MODULE,
+	.dai_link     = snd_fe_pi_audio_dai,
+	.num_links    = ARRAY_SIZE(snd_fe_pi_audio_dai),
+
+	.dapm_routes = fe_pi_audio_dapm_routes,
+	.num_dapm_routes = ARRAY_SIZE(fe_pi_audio_dapm_routes),
+};
+
+static int snd_fe_pi_audio_probe(struct platform_device *pdev)
+{
+	int ret = 0;
+	struct snd_soc_card *card = &fe_pi_audio;
+	struct device_node *np = pdev->dev.of_node;
+	struct device_node *i2s_node;
+	struct snd_soc_dai_link *dai = &snd_fe_pi_audio_dai[0];
+
+	fe_pi_audio.dev = &pdev->dev;
+
+	i2s_node = of_parse_phandle(np, "i2s-controller", 0);
+	if (!i2s_node) {
+		dev_err(&pdev->dev, "i2s_node phandle missing or invalid\n");
+		return -EINVAL;
+	}
+
+	dai->cpus->dai_name = NULL;
+	dai->cpus->of_node = i2s_node;
+	dai->platforms->name = NULL;
+	dai->platforms->of_node = i2s_node;
+
+	of_node_put(i2s_node);
+
+	card->dev = &pdev->dev;
+	platform_set_drvdata(pdev, card);
+
+	ret = devm_snd_soc_register_card(&pdev->dev, card);
+	if (ret && ret != -EPROBE_DEFER)
+		dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n", ret);
+
+	return ret;
+}
+
+static const struct of_device_id snd_fe_pi_audio_of_match[] = {
+	{ .compatible = "fe-pi,fe-pi-audio", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, snd_fe_pi_audio_of_match);
+
+static struct platform_driver snd_fe_pi_audio_driver = {
+        .driver = {
+                .name   = "snd-fe-pi-audio",
+                .owner  = THIS_MODULE,
+                .of_match_table = snd_fe_pi_audio_of_match,
+        },
+        .probe          = snd_fe_pi_audio_probe,
+};
+
+module_platform_driver(snd_fe_pi_audio_driver);
+
+MODULE_AUTHOR("Henry Kupis <fe-pi@cox.net>");
+MODULE_DESCRIPTION("ASoC Driver for Fe-Pi Audio");
+MODULE_LICENSE("GPL v2");
diff -Nur --no-dereference linux-5.10.52-orig/sound/soc/bcm/googlevoicehat-codec.c linux-5.10.52-v7l+/sound/soc/bcm/googlevoicehat-codec.c
--- linux-5.10.52-orig/sound/soc/bcm/googlevoicehat-codec.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/sound/soc/bcm/googlevoicehat-codec.c	2021-07-25 16:46:40.147717820 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/*
+ * Driver for the Google voiceHAT audio codec for Raspberry Pi.
+ *
+ * Author:	Peter Malkin <petermalkin@google.com>
+ *		Copyright 2016
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/version.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+#include <sound/soc-dapm.h>
+
+#define ICS43432_RATE_MIN_HZ	7190  /* from data sheet */
+#define ICS43432_RATE_MAX_HZ	52800 /* from data sheet */
+/* Delay in enabling SDMODE after clock settles to remove pop */
+#define SDMODE_DELAY_MS		5
+
+struct voicehat_priv {
+	struct delayed_work enable_sdmode_work;
+	struct gpio_desc *sdmode_gpio;
+	unsigned long sdmode_delay_jiffies;
+};
+
+static void voicehat_enable_sdmode_work(struct work_struct *work)
+{
+	struct voicehat_priv *voicehat = container_of(work,
+						      struct voicehat_priv,
+						      enable_sdmode_work.work);
+	gpiod_set_value(voicehat->sdmode_gpio, 1);
+}
+
+static int voicehat_component_probe(struct snd_soc_component *component)
+{
+	struct voicehat_priv *voicehat =
+				snd_soc_component_get_drvdata(component);
+
+	voicehat->sdmode_gpio = devm_gpiod_get(component->dev, "sdmode",
+					       GPIOD_OUT_LOW);
+	if (IS_ERR(voicehat->sdmode_gpio)) {
+		dev_err(component->dev, "Unable to allocate GPIO pin\n");
+		return PTR_ERR(voicehat->sdmode_gpio);
+	}
+
+	INIT_DELAYED_WORK(&voicehat->enable_sdmode_work,
+			  voicehat_enable_sdmode_work);
+	return 0;
+}
+
+static void voicehat_component_remove(struct snd_soc_component *component)
+{
+	struct voicehat_priv *voicehat =
+				snd_soc_component_get_drvdata(component);
+
+	cancel_delayed_work_sync(&voicehat->enable_sdmode_work);
+}
+
+static const struct snd_soc_dapm_widget voicehat_dapm_widgets[] = {
+	SND_SOC_DAPM_OUTPUT("Speaker"),
+};
+
+static const struct snd_soc_dapm_route voicehat_dapm_routes[] = {
+	{"Speaker", NULL, "HiFi Playback"},
+};
+
+static const struct snd_soc_component_driver voicehat_component_driver = {
+	.probe = voicehat_component_probe,
+	.remove = voicehat_component_remove,
+	.dapm_widgets = voicehat_dapm_widgets,
+	.num_dapm_widgets = ARRAY_SIZE(voicehat_dapm_widgets),
+	.dapm_routes = voicehat_dapm_routes,
+	.num_dapm_routes = ARRAY_SIZE(voicehat_dapm_routes),
+};
+
+static int voicehat_daiops_trigger(struct snd_pcm_substream *substream, int cmd,
+				   struct snd_soc_dai *dai)
+{
+	struct snd_soc_component *component = dai->component;
+	struct voicehat_priv *voicehat =
+				snd_soc_component_get_drvdata(component);
+
+	if (voicehat->sdmode_delay_jiffies == 0)
+		return 0;
+
+	dev_dbg(dai->dev, "CMD             %d", cmd);
+	dev_dbg(dai->dev, "Playback Active %d", dai->stream_active[SNDRV_PCM_STREAM_PLAYBACK]);
+	dev_dbg(dai->dev, "Capture Active  %d", dai->stream_active[SNDRV_PCM_STREAM_CAPTURE]);
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		if (dai->stream_active[SNDRV_PCM_STREAM_PLAYBACK]) {
+			dev_info(dai->dev, "Enabling audio amp...\n");
+			queue_delayed_work(
+				system_power_efficient_wq,
+				&voicehat->enable_sdmode_work,
+				voicehat->sdmode_delay_jiffies);
+		}
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		if (dai->stream_active[SNDRV_PCM_STREAM_PLAYBACK]) {
+			cancel_delayed_work(&voicehat->enable_sdmode_work);
+			dev_info(dai->dev, "Disabling audio amp...\n");
+			gpiod_set_value(voicehat->sdmode_gpio, 0);
+		}
+		break;
+	}
+	return 0;
+}
+
+static const struct snd_soc_dai_ops voicehat_dai_ops = {
+	.trigger = voicehat_daiops_trigger,
+};
+
+static struct snd_soc_dai_driver voicehat_dai = {
+	.name = "voicehat-hifi",
+	.capture = {
+		.stream_name = "HiFi Capture",
+		.channels_min = 2,
+		.channels_max = 2,
+		.rates = SNDRV_PCM_RATE_48000,
+		.formats = SNDRV_PCM_FMTBIT_S32_LE
+	},
+	.playback = {
+		.stream_name = "HiFi Playback",
+		.channels_min = 2,
+		.channels_max = 2,
+		.rates = SNDRV_PCM_RATE_48000,
+		.formats = SNDRV_PCM_FMTBIT_S32_LE
+	},
+	.ops = &voicehat_dai_ops,
+	.symmetric_rates = 1
+};
+
+#ifdef CONFIG_OF
+static const struct of_device_id voicehat_ids[] = {
+		{ .compatible = "google,voicehat", }, {}
+	};
+	MODULE_DEVICE_TABLE(of, voicehat_ids);
+#endif
+
+static int voicehat_platform_probe(struct platform_device *pdev)
+{
+	struct voicehat_priv *voicehat;
+	unsigned int sdmode_delay;
+	int ret;
+
+	voicehat = devm_kzalloc(&pdev->dev, sizeof(*voicehat), GFP_KERNEL);
+	if (!voicehat)
+		return -ENOMEM;
+
+	ret = device_property_read_u32(&pdev->dev, "voicehat_sdmode_delay",
+				       &sdmode_delay);
+
+	if (ret) {
+		sdmode_delay = SDMODE_DELAY_MS;
+		dev_info(&pdev->dev,
+			 "property 'voicehat_sdmode_delay' not found default 5 mS");
+	} else {
+		dev_info(&pdev->dev, "property 'voicehat_sdmode_delay' found delay= %d mS",
+			 sdmode_delay);
+	}
+	voicehat->sdmode_delay_jiffies = msecs_to_jiffies(sdmode_delay);
+
+	dev_set_drvdata(&pdev->dev, voicehat);
+
+	return snd_soc_register_component(&pdev->dev,
+					  &voicehat_component_driver,
+					  &voicehat_dai,
+					  1);
+}
+
+static int voicehat_platform_remove(struct platform_device *pdev)
+{
+	snd_soc_unregister_component(&pdev->dev);
+	return 0;
+}
+
+static struct platform_driver voicehat_driver = {
+	.driver = {
+		.name = "voicehat-codec",
+		.of_match_table = of_match_ptr(voicehat_ids),
+	},
+	.probe = voicehat_platform_probe,
+	.remove = voicehat_platform_remove,
+};
+
+module_platform_driver(voicehat_driver);
+
+MODULE_DESCRIPTION("Google voiceHAT Codec driver");
+MODULE_AUTHOR("Peter Malkin <petermalkin@google.com>");
+MODULE_LICENSE("GPL v2");
diff -Nur --no-dereference linux-5.10.52-orig/sound/soc/bcm/hifiberry_dacplusadc.c linux-5.10.52-v7l+/sound/soc/bcm/hifiberry_dacplusadc.c
--- linux-5.10.52-orig/sound/soc/bcm/hifiberry_dacplusadc.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/sound/soc/bcm/hifiberry_dacplusadc.c	2021-07-25 16:46:40.147717820 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/*
+ * ASoC Driver for HiFiBerry DAC+ / DAC Pro with ADC
+ *
+ * Author:	Daniel Matuschek, Stuart MacLean <stuart@hifiberry.com>
+ *		Copyright 2014-2015
+ *		based on code by Florian Meier <florian.meier@koalo.de>
+ *		ADC added by Joerg Schambacher <joscha@schambacher.com>
+ *		Copyright 2018
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/kernel.h>
+#include <linux/clk.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/jack.h>
+
+#include "../codecs/pcm512x.h"
+
+#define HIFIBERRY_DACPRO_NOCLOCK 0
+#define HIFIBERRY_DACPRO_CLK44EN 1
+#define HIFIBERRY_DACPRO_CLK48EN 2
+
+struct platform_device *dmic_codec_dev;
+
+struct pcm512x_priv {
+	struct regmap *regmap;
+	struct clk *sclk;
+};
+
+/* Clock rate of CLK44EN attached to GPIO6 pin */
+#define CLK_44EN_RATE 22579200UL
+/* Clock rate of CLK48EN attached to GPIO3 pin */
+#define CLK_48EN_RATE 24576000UL
+
+static bool slave;
+static bool snd_rpi_hifiberry_is_dacpro;
+static bool digital_gain_0db_limit = true;
+static bool leds_off;
+
+static void snd_rpi_hifiberry_dacplusadc_select_clk(struct snd_soc_component *component,
+	int clk_id)
+{
+	switch (clk_id) {
+	case HIFIBERRY_DACPRO_NOCLOCK:
+		snd_soc_component_update_bits(component, PCM512x_GPIO_CONTROL_1, 0x24, 0x00);
+		break;
+	case HIFIBERRY_DACPRO_CLK44EN:
+		snd_soc_component_update_bits(component, PCM512x_GPIO_CONTROL_1, 0x24, 0x20);
+		break;
+	case HIFIBERRY_DACPRO_CLK48EN:
+		snd_soc_component_update_bits(component, PCM512x_GPIO_CONTROL_1, 0x24, 0x04);
+		break;
+	}
+}
+
+static void snd_rpi_hifiberry_dacplusadc_clk_gpio(struct snd_soc_component *component)
+{
+	snd_soc_component_update_bits(component, PCM512x_GPIO_EN, 0x24, 0x24);
+	snd_soc_component_update_bits(component, PCM512x_GPIO_OUTPUT_3, 0x0f, 0x02);
+	snd_soc_component_update_bits(component, PCM512x_GPIO_OUTPUT_6, 0x0f, 0x02);
+}
+
+static bool snd_rpi_hifiberry_dacplusadc_is_sclk(struct snd_soc_component *component)
+{
+	unsigned int sck;
+
+	sck = snd_soc_component_read(component, PCM512x_RATE_DET_4);
+	return (!(sck & 0x40));
+}
+
+static bool snd_rpi_hifiberry_dacplusadc_is_sclk_sleep(
+	struct snd_soc_component *component)
+{
+	msleep(2);
+	return snd_rpi_hifiberry_dacplusadc_is_sclk(component);
+}
+
+static bool snd_rpi_hifiberry_dacplusadc_is_pro_card(struct snd_soc_component *component)
+{
+	bool isClk44EN, isClk48En, isNoClk;
+
+	snd_rpi_hifiberry_dacplusadc_clk_gpio(component);
+
+	snd_rpi_hifiberry_dacplusadc_select_clk(component, HIFIBERRY_DACPRO_CLK44EN);
+	isClk44EN = snd_rpi_hifiberry_dacplusadc_is_sclk_sleep(component);
+
+	snd_rpi_hifiberry_dacplusadc_select_clk(component, HIFIBERRY_DACPRO_NOCLOCK);
+	isNoClk = snd_rpi_hifiberry_dacplusadc_is_sclk_sleep(component);
+
+	snd_rpi_hifiberry_dacplusadc_select_clk(component, HIFIBERRY_DACPRO_CLK48EN);
+	isClk48En = snd_rpi_hifiberry_dacplusadc_is_sclk_sleep(component);
+
+	return (isClk44EN && isClk48En && !isNoClk);
+}
+
+static int snd_rpi_hifiberry_dacplusadc_clk_for_rate(int sample_rate)
+{
+	int type;
+
+	switch (sample_rate) {
+	case 11025:
+	case 22050:
+	case 44100:
+	case 88200:
+	case 176400:
+	case 352800:
+		type = HIFIBERRY_DACPRO_CLK44EN;
+		break;
+	default:
+		type = HIFIBERRY_DACPRO_CLK48EN;
+		break;
+	}
+	return type;
+}
+
+static void snd_rpi_hifiberry_dacplusadc_set_sclk(struct snd_soc_component *component,
+	int sample_rate)
+{
+	struct pcm512x_priv *pcm512x = snd_soc_component_get_drvdata(component);
+
+	if (!IS_ERR(pcm512x->sclk)) {
+		int ctype;
+
+		ctype = snd_rpi_hifiberry_dacplusadc_clk_for_rate(sample_rate);
+		clk_set_rate(pcm512x->sclk, (ctype == HIFIBERRY_DACPRO_CLK44EN)
+			? CLK_44EN_RATE : CLK_48EN_RATE);
+		snd_rpi_hifiberry_dacplusadc_select_clk(component, ctype);
+	}
+}
+
+static int snd_rpi_hifiberry_dacplusadc_init(struct snd_soc_pcm_runtime *rtd)
+{
+	struct snd_soc_component *component = asoc_rtd_to_codec(rtd, 0)->component;
+	struct pcm512x_priv *priv;
+
+	if (slave)
+		snd_rpi_hifiberry_is_dacpro = false;
+	else
+		snd_rpi_hifiberry_is_dacpro =
+				snd_rpi_hifiberry_dacplusadc_is_pro_card(component);
+
+	if (snd_rpi_hifiberry_is_dacpro) {
+		struct snd_soc_dai_link *dai = rtd->dai_link;
+
+		dai->name = "HiFiBerry ADCDAC+ Pro";
+		dai->stream_name = "HiFiBerry ADCDAC+ Pro HiFi";
+		dai->dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
+			| SND_SOC_DAIFMT_CBM_CFM;
+
+		snd_soc_component_update_bits(component, PCM512x_BCLK_LRCLK_CFG, 0x31, 0x11);
+		snd_soc_component_update_bits(component, PCM512x_MASTER_MODE, 0x03, 0x03);
+		snd_soc_component_update_bits(component, PCM512x_MASTER_CLKDIV_2, 0x7f, 63);
+	} else {
+		priv = snd_soc_component_get_drvdata(component);
+		priv->sclk = ERR_PTR(-ENOENT);
+	}
+
+	snd_soc_component_update_bits(component, PCM512x_GPIO_EN, 0x08, 0x08);
+	snd_soc_component_update_bits(component, PCM512x_GPIO_OUTPUT_4, 0x0f, 0x02);
+	if (leds_off)
+		snd_soc_component_update_bits(component, PCM512x_GPIO_CONTROL_1, 0x08, 0x00);
+	else
+		snd_soc_component_update_bits(component, PCM512x_GPIO_CONTROL_1, 0x08, 0x08);
+
+	if (digital_gain_0db_limit) {
+		int ret;
+		struct snd_soc_card *card = rtd->card;
+
+		ret = snd_soc_limit_volume(card, "Digital Playback Volume", 207);
+		if (ret < 0)
+			dev_warn(card->dev, "Failed to set volume limit: %d\n", ret);
+	}
+
+	return 0;
+}
+
+static int snd_rpi_hifiberry_dacplusadc_update_rate_den(
+	struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_component *component = asoc_rtd_to_codec(rtd, 0)->component;
+	struct pcm512x_priv *pcm512x = snd_soc_component_get_drvdata(component);
+	struct snd_ratnum *rats_no_pll;
+	unsigned int num = 0, den = 0;
+	int err;
+
+	rats_no_pll = devm_kzalloc(rtd->dev, sizeof(*rats_no_pll), GFP_KERNEL);
+	if (!rats_no_pll)
+		return -ENOMEM;
+
+	rats_no_pll->num = clk_get_rate(pcm512x->sclk) / 64;
+	rats_no_pll->den_min = 1;
+	rats_no_pll->den_max = 128;
+	rats_no_pll->den_step = 1;
+
+	err = snd_interval_ratnum(hw_param_interval(params,
+		SNDRV_PCM_HW_PARAM_RATE), 1, rats_no_pll, &num, &den);
+	if (err >= 0 && den) {
+		params->rate_num = num;
+		params->rate_den = den;
+	}
+
+	devm_kfree(rtd->dev, rats_no_pll);
+	return 0;
+}
+
+static int snd_rpi_hifiberry_dacplusadc_hw_params(
+	struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params)
+{
+	int ret = 0;
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	int channels = params_channels(params);
+	int width = 32;
+
+	if (snd_rpi_hifiberry_is_dacpro) {
+		struct snd_soc_component *component = asoc_rtd_to_codec(rtd, 0)->component;
+
+		width = snd_pcm_format_physical_width(params_format(params));
+
+		snd_rpi_hifiberry_dacplusadc_set_sclk(component,
+			params_rate(params));
+
+		ret = snd_rpi_hifiberry_dacplusadc_update_rate_den(
+			substream, params);
+	}
+
+	ret = snd_soc_dai_set_bclk_ratio(asoc_rtd_to_cpu(rtd, 0), channels * width);
+	if (ret)
+		return ret;
+	ret = snd_soc_dai_set_bclk_ratio(asoc_rtd_to_codec(rtd, 0), channels * width);
+	return ret;
+}
+
+static int hifiberry_dacplusadc_LED_cnt;
+
+static int snd_rpi_hifiberry_dacplusadc_startup(
+	struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_component *component = asoc_rtd_to_codec(rtd, 0)->component;
+
+	if (leds_off)
+		return 0;
+	snd_soc_component_update_bits(component, PCM512x_GPIO_CONTROL_1,
+					 0x08, 0x08);
+	hifiberry_dacplusadc_LED_cnt++;
+	return 0;
+}
+
+static void snd_rpi_hifiberry_dacplusadc_shutdown(
+	struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_component *component = asoc_rtd_to_codec(rtd, 0)->component;
+
+	hifiberry_dacplusadc_LED_cnt--;
+	if (!hifiberry_dacplusadc_LED_cnt)
+		snd_soc_component_update_bits(component, PCM512x_GPIO_CONTROL_1,
+						 0x08, 0x00);
+}
+
+/* machine stream operations */
+static struct snd_soc_ops snd_rpi_hifiberry_dacplusadc_ops = {
+	.hw_params = snd_rpi_hifiberry_dacplusadc_hw_params,
+	.startup = snd_rpi_hifiberry_dacplusadc_startup,
+	.shutdown = snd_rpi_hifiberry_dacplusadc_shutdown,
+};
+
+SND_SOC_DAILINK_DEFS(hifi,
+	DAILINK_COMP_ARRAY(COMP_CPU("bcm2708-i2s.0")),
+	DAILINK_COMP_ARRAY(COMP_CODEC("pcm512x.1-004d", "pcm512x-hifi"),
+			   COMP_CODEC("dmic-codec", "dmic-hifi")),
+	DAILINK_COMP_ARRAY(COMP_PLATFORM("bcm2708-i2s.0")));
+
+static struct snd_soc_dai_link snd_rpi_hifiberry_dacplusadc_dai[] = {
+{
+	.name		= "HiFiBerry DAC+ADC",
+	.stream_name	= "HiFiBerry DAC+ADC HiFi",
+	.dai_fmt	= SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+				SND_SOC_DAIFMT_CBS_CFS,
+	.ops		= &snd_rpi_hifiberry_dacplusadc_ops,
+	.init		= snd_rpi_hifiberry_dacplusadc_init,
+	SND_SOC_DAILINK_REG(hifi),
+},
+};
+
+/* audio machine driver */
+static struct snd_soc_card snd_rpi_hifiberry_dacplusadc = {
+	.name         = "snd_rpi_hifiberry_dacplusadc",
+	.driver_name  = "HifiberryDacpAdc",
+	.owner        = THIS_MODULE,
+	.dai_link     = snd_rpi_hifiberry_dacplusadc_dai,
+	.num_links    = ARRAY_SIZE(snd_rpi_hifiberry_dacplusadc_dai),
+};
+
+
+static int snd_rpi_hifiberry_dacplusadc_probe(struct platform_device *pdev)
+{
+	int ret = 0;
+
+	snd_rpi_hifiberry_dacplusadc.dev = &pdev->dev;
+	if (pdev->dev.of_node) {
+		struct device_node *i2s_node;
+		struct snd_soc_dai_link *dai;
+
+		dai = &snd_rpi_hifiberry_dacplusadc_dai[0];
+		i2s_node = of_parse_phandle(pdev->dev.of_node,
+			"i2s-controller", 0);
+		if (i2s_node) {
+			dai->cpus->of_node = i2s_node;
+			dai->platforms->of_node = i2s_node;
+			dai->cpus->dai_name = NULL;
+			dai->platforms->name = NULL;
+		}
+	}
+	digital_gain_0db_limit = !of_property_read_bool(
+		pdev->dev.of_node, "hifiberry,24db_digital_gain");
+	slave = of_property_read_bool(pdev->dev.of_node,
+					"hifiberry-dacplusadc,slave");
+	leds_off = of_property_read_bool(pdev->dev.of_node,
+					"hifiberry-dacplusadc,leds_off");
+
+	ret = devm_snd_soc_register_card(&pdev->dev,
+						 &snd_rpi_hifiberry_dacplusadc);
+	if (ret && ret != -EPROBE_DEFER)
+		dev_err(&pdev->dev,
+			"snd_soc_register_card() failed: %d\n", ret);
+
+	return ret;
+}
+
+static const struct of_device_id snd_rpi_hifiberry_dacplusadc_of_match[] = {
+	{ .compatible = "hifiberry,hifiberry-dacplusadc", },
+	{},
+};
+
+MODULE_DEVICE_TABLE(of, snd_rpi_hifiberry_dacplusadc_of_match);
+
+static struct platform_driver snd_rpi_hifiberry_dacplusadc_driver = {
+	.driver = {
+		.name   = "snd-rpi-hifiberry-dacplusadc",
+		.owner  = THIS_MODULE,
+		.of_match_table = snd_rpi_hifiberry_dacplusadc_of_match,
+	},
+	.probe          = snd_rpi_hifiberry_dacplusadc_probe,
+};
+
+static int __init hifiberry_dacplusadc_init(void)
+{
+	int ret;
+
+	dmic_codec_dev = platform_device_register_simple("dmic-codec", -1, NULL,
+							 0);
+	if (IS_ERR(dmic_codec_dev)) {
+		pr_err("%s: dmic-codec device registration failed\n", __func__);
+		return PTR_ERR(dmic_codec_dev);
+	}
+
+	ret = platform_driver_register(&snd_rpi_hifiberry_dacplusadc_driver);
+	if (ret) {
+		pr_err("%s: platform driver registration failed\n", __func__);
+		platform_device_unregister(dmic_codec_dev);
+	}
+
+	return ret;
+}
+module_init(hifiberry_dacplusadc_init);
+
+static void __exit hifiberry_dacplusadc_exit(void)
+{
+	platform_driver_unregister(&snd_rpi_hifiberry_dacplusadc_driver);
+	platform_device_unregister(dmic_codec_dev);
+}
+module_exit(hifiberry_dacplusadc_exit);
+
+MODULE_AUTHOR("Joerg Schambacher <joscha@schambacher.com>");
+MODULE_AUTHOR("Daniel Matuschek <daniel@hifiberry.com>");
+MODULE_DESCRIPTION("ASoC Driver for HiFiBerry DAC+ADC");
+MODULE_LICENSE("GPL v2");
diff -Nur --no-dereference linux-5.10.52-orig/sound/soc/bcm/hifiberry_dacplusadcpro.c linux-5.10.52-v7l+/sound/soc/bcm/hifiberry_dacplusadcpro.c
--- linux-5.10.52-orig/sound/soc/bcm/hifiberry_dacplusadcpro.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/sound/soc/bcm/hifiberry_dacplusadcpro.c	2021-07-25 16:46:40.147717820 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/*
+ * ASoC Driver for HiFiBerry DAC+ / DAC Pro with ADC PRO Version (SW control)
+ *
+ * Author:	Daniel Matuschek, Stuart MacLean <stuart@hifiberry.com>
+ *		Copyright 2014-2015
+ *		based on code by Florian Meier <florian.meier@koalo.de>
+ *		ADC added by Joerg Schambacher <joerg@i2audio.com>
+ *		Copyright 2018-19
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/kernel.h>
+#include <linux/clk.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/jack.h>
+#include <sound/tlv.h>
+
+#include "../codecs/pcm512x.h"
+#include "../codecs/pcm186x.h"
+
+#define HIFIBERRY_DACPRO_NOCLOCK 0
+#define HIFIBERRY_DACPRO_CLK44EN 1
+#define HIFIBERRY_DACPRO_CLK48EN 2
+
+struct pcm512x_priv {
+	struct regmap *regmap;
+	struct clk *sclk;
+};
+
+/* Clock rate of CLK44EN attached to GPIO6 pin */
+#define CLK_44EN_RATE 22579200UL
+/* Clock rate of CLK48EN attached to GPIO3 pin */
+#define CLK_48EN_RATE 24576000UL
+
+static bool slave;
+static bool snd_rpi_hifiberry_is_dacpro;
+static bool digital_gain_0db_limit = true;
+static bool leds_off;
+
+static const unsigned int pcm186x_adc_input_channel_sel_value[] = {
+	0x00, 0x01, 0x02, 0x03, 0x10
+};
+
+static const char * const pcm186x_adcl_input_channel_sel_text[] = {
+	"No Select",
+	"VINL1[SE]",					/* Default for ADCL */
+	"VINL2[SE]",
+	"VINL2[SE] + VINL1[SE]",
+	"{VIN1P, VIN1M}[DIFF]"
+};
+
+static const char * const pcm186x_adcr_input_channel_sel_text[] = {
+	"No Select",
+	"VINR1[SE]",					/* Default for ADCR */
+	"VINR2[SE]",
+	"VINR2[SE] + VINR1[SE]",
+	"{VIN2P, VIN2M}[DIFF]"
+};
+
+static const struct soc_enum pcm186x_adc_input_channel_sel[] = {
+	SOC_VALUE_ENUM_SINGLE(PCM186X_ADC1_INPUT_SEL_L, 0,
+			      PCM186X_ADC_INPUT_SEL_MASK,
+			      ARRAY_SIZE(pcm186x_adcl_input_channel_sel_text),
+			      pcm186x_adcl_input_channel_sel_text,
+			      pcm186x_adc_input_channel_sel_value),
+	SOC_VALUE_ENUM_SINGLE(PCM186X_ADC1_INPUT_SEL_R, 0,
+			      PCM186X_ADC_INPUT_SEL_MASK,
+			      ARRAY_SIZE(pcm186x_adcr_input_channel_sel_text),
+			      pcm186x_adcr_input_channel_sel_text,
+			      pcm186x_adc_input_channel_sel_value),
+};
+
+static const unsigned int pcm186x_mic_bias_sel_value[] = {
+	0x00, 0x01, 0x11
+};
+
+static const char * const pcm186x_mic_bias_sel_text[] = {
+	"Mic Bias off",
+	"Mic Bias on",
+	"Mic Bias with Bypass Resistor"
+};
+
+static const struct soc_enum pcm186x_mic_bias_sel[] = {
+	SOC_VALUE_ENUM_SINGLE(PCM186X_MIC_BIAS_CTRL, 0,
+			      GENMASK(4, 0),
+			      ARRAY_SIZE(pcm186x_mic_bias_sel_text),
+			      pcm186x_mic_bias_sel_text,
+			      pcm186x_mic_bias_sel_value),
+};
+
+static const unsigned int pcm186x_gain_sel_value[] = {
+	0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
+	0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
+	0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff,
+	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+	0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+	0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+	0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+	0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
+	0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
+	0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
+	0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
+	0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
+	0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
+	0x50
+};
+
+static const char * const pcm186x_gain_sel_text[] = {
+	"-12.0dB", "-11.5dB", "-11.0dB", "-10.5dB", "-10.0dB", "-9.5dB",
+	"-9.0dB", "-8.5dB", "-8.0dB", "-7.5dB", "-7.0dB", "-6.5dB",
+	"-6.0dB", "-5.5dB", "-5.0dB", "-4.5dB", "-4.0dB", "-3.5dB",
+	"-3.0dB", "-2.5dB", "-2.0dB", "-1.5dB", "-1.0dB", "-0.5dB",
+	"0.0dB", "0.5dB", "1.0dB", "1.5dB", "2.0dB", "2.5dB",
+	"3.0dB", "3.5dB", "4.0dB", "4.5dB", "5.0dB", "5.5dB",
+	"6.0dB", "6.5dB", "7.0dB", "7.5dB", "8.0dB", "8.5dB",
+	"9.0dB", "9.5dB", "10.0dB", "10.5dB", "11.0dB", "11.5dB",
+	"12.0dB", "12.5dB", "13.0dB", "13.5dB", "14.0dB", "14.5dB",
+	"15.0dB", "15.5dB", "16.0dB", "16.5dB", "17.0dB", "17.5dB",
+	"18.0dB", "18.5dB", "19.0dB", "19.5dB", "20.0dB", "20.5dB",
+	"21.0dB", "21.5dB", "22.0dB", "22.5dB", "23.0dB", "23.5dB",
+	"24.0dB", "24.5dB", "25.0dB", "25.5dB", "26.0dB", "26.5dB",
+	"27.0dB", "27.5dB", "28.0dB", "28.5dB", "29.0dB", "29.5dB",
+	"30.0dB", "30.5dB", "31.0dB", "31.5dB", "32.0dB", "32.5dB",
+	"33.0dB", "33.5dB", "34.0dB", "34.5dB", "35.0dB", "35.5dB",
+	"36.0dB", "36.5dB", "37.0dB", "37.5dB", "38.0dB", "38.5dB",
+	"39.0dB", "39.5dB", "40.0dB"};
+
+static const struct soc_enum pcm186x_gain_sel[] = {
+	SOC_VALUE_ENUM_SINGLE(PCM186X_PGA_VAL_CH1_L, 0,
+			      0xff,
+			      ARRAY_SIZE(pcm186x_gain_sel_text),
+			      pcm186x_gain_sel_text,
+			      pcm186x_gain_sel_value),
+	SOC_VALUE_ENUM_SINGLE(PCM186X_PGA_VAL_CH1_R, 0,
+			      0xff,
+			      ARRAY_SIZE(pcm186x_gain_sel_text),
+			      pcm186x_gain_sel_text,
+			      pcm186x_gain_sel_value),
+};
+
+static const struct snd_kcontrol_new pcm1863_snd_controls_card[] = {
+	SOC_ENUM("ADC Left Input", pcm186x_adc_input_channel_sel[0]),
+	SOC_ENUM("ADC Right Input", pcm186x_adc_input_channel_sel[1]),
+	SOC_ENUM("ADC Mic Bias", pcm186x_mic_bias_sel),
+	SOC_ENUM("PGA Gain Left", pcm186x_gain_sel[0]),
+	SOC_ENUM("PGA Gain Right", pcm186x_gain_sel[1]),
+};
+
+static int pcm1863_add_controls(struct snd_soc_component *component)
+{
+	snd_soc_add_component_controls(component,
+			pcm1863_snd_controls_card,
+			ARRAY_SIZE(pcm1863_snd_controls_card));
+	return 0;
+}
+
+static void snd_rpi_hifiberry_dacplusadcpro_select_clk(
+					struct snd_soc_component *component, int clk_id)
+{
+	switch (clk_id) {
+	case HIFIBERRY_DACPRO_NOCLOCK:
+		snd_soc_component_update_bits(component,
+				PCM512x_GPIO_CONTROL_1, 0x24, 0x00);
+		break;
+	case HIFIBERRY_DACPRO_CLK44EN:
+		snd_soc_component_update_bits(component,
+				PCM512x_GPIO_CONTROL_1, 0x24, 0x20);
+		break;
+	case HIFIBERRY_DACPRO_CLK48EN:
+		snd_soc_component_update_bits(component,
+				PCM512x_GPIO_CONTROL_1, 0x24, 0x04);
+		break;
+	}
+	usleep_range(3000, 4000);
+}
+
+static void snd_rpi_hifiberry_dacplusadcpro_clk_gpio(struct snd_soc_component *component)
+{
+	snd_soc_component_update_bits(component, PCM512x_GPIO_EN, 0x24, 0x24);
+	snd_soc_component_update_bits(component, PCM512x_GPIO_OUTPUT_3, 0x0f, 0x02);
+	snd_soc_component_update_bits(component, PCM512x_GPIO_OUTPUT_6, 0x0f, 0x02);
+}
+
+static bool snd_rpi_hifiberry_dacplusadcpro_is_sclk(struct snd_soc_component *component)
+{
+	unsigned int sck;
+
+	sck = snd_soc_component_read(component, PCM512x_RATE_DET_4);
+	return (!(sck & 0x40));
+}
+
+static bool snd_rpi_hifiberry_dacplusadcpro_is_pro_card(struct snd_soc_component *component)
+{
+	bool isClk44EN, isClk48En, isNoClk;
+
+	snd_rpi_hifiberry_dacplusadcpro_clk_gpio(component);
+
+	snd_rpi_hifiberry_dacplusadcpro_select_clk(component, HIFIBERRY_DACPRO_CLK44EN);
+	isClk44EN = snd_rpi_hifiberry_dacplusadcpro_is_sclk(component);
+
+	snd_rpi_hifiberry_dacplusadcpro_select_clk(component, HIFIBERRY_DACPRO_NOCLOCK);
+	isNoClk = snd_rpi_hifiberry_dacplusadcpro_is_sclk(component);
+
+	snd_rpi_hifiberry_dacplusadcpro_select_clk(component, HIFIBERRY_DACPRO_CLK48EN);
+	isClk48En = snd_rpi_hifiberry_dacplusadcpro_is_sclk(component);
+
+	return (isClk44EN && isClk48En && !isNoClk);
+}
+
+static int snd_rpi_hifiberry_dacplusadcpro_clk_for_rate(int sample_rate)
+{
+	int type;
+
+	switch (sample_rate) {
+	case 11025:
+	case 22050:
+	case 44100:
+	case 88200:
+	case 176400:
+	case 352800:
+		type = HIFIBERRY_DACPRO_CLK44EN;
+		break;
+	default:
+		type = HIFIBERRY_DACPRO_CLK48EN;
+		break;
+	}
+	return type;
+}
+
+static void snd_rpi_hifiberry_dacplusadcpro_set_sclk(struct snd_soc_component *component,
+	int sample_rate)
+{
+	struct pcm512x_priv *pcm512x = snd_soc_component_get_drvdata(component);
+
+	if (!IS_ERR(pcm512x->sclk)) {
+		int ctype;
+
+		ctype = snd_rpi_hifiberry_dacplusadcpro_clk_for_rate(sample_rate);
+		clk_set_rate(pcm512x->sclk, (ctype == HIFIBERRY_DACPRO_CLK44EN)
+			? CLK_44EN_RATE : CLK_48EN_RATE);
+		snd_rpi_hifiberry_dacplusadcpro_select_clk(component, ctype);
+	}
+}
+
+static int snd_rpi_hifiberry_dacplusadcpro_init(struct snd_soc_pcm_runtime *rtd)
+{
+	struct snd_soc_component *dac = asoc_rtd_to_codec(rtd, 0)->component;
+	struct snd_soc_component *adc = asoc_rtd_to_codec(rtd, 1)->component;
+	struct snd_soc_dai_driver *adc_driver = asoc_rtd_to_codec(rtd, 1)->driver;
+	struct pcm512x_priv *priv;
+	int ret;
+
+	if (slave)
+		snd_rpi_hifiberry_is_dacpro = false;
+	else
+		snd_rpi_hifiberry_is_dacpro =
+				snd_rpi_hifiberry_dacplusadcpro_is_pro_card(dac);
+
+	if (snd_rpi_hifiberry_is_dacpro) {
+		struct snd_soc_dai_link *dai = rtd->dai_link;
+
+		dai->name = "HiFiBerry DAC+ADC Pro";
+		dai->stream_name = "HiFiBerry DAC+ADC Pro HiFi";
+		dai->dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
+			| SND_SOC_DAIFMT_CBM_CFM;
+
+		// set DAC DAI configuration
+		ret = snd_soc_dai_set_fmt(asoc_rtd_to_codec(rtd, 0),
+				SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
+			| SND_SOC_DAIFMT_CBM_CFM);
+		if (ret < 0)
+			return ret;
+
+		// set ADC DAI configuration
+		ret = snd_soc_dai_set_fmt(asoc_rtd_to_codec(rtd, 1),
+				SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
+			| SND_SOC_DAIFMT_CBS_CFS);
+		if (ret < 0)
+			return ret;
+
+		// set CPU DAI configuration
+		ret = snd_soc_dai_set_fmt(asoc_rtd_to_cpu(rtd, 0),
+			SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
+		if (ret < 0)
+			return ret;
+
+		snd_soc_component_update_bits(dac, PCM512x_BCLK_LRCLK_CFG, 0x31, 0x11);
+		snd_soc_component_update_bits(dac, PCM512x_MASTER_MODE, 0x03, 0x03);
+		snd_soc_component_update_bits(dac, PCM512x_MASTER_CLKDIV_2, 0x7f, 63);
+	} else {
+		priv = snd_soc_component_get_drvdata(dac);
+		priv->sclk = ERR_PTR(-ENOENT);
+	}
+
+	/* disable 24bit mode as long as I2S module does not have sign extension fixed */
+	adc_driver->capture.formats = SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S16_LE;
+
+	snd_soc_component_update_bits(dac, PCM512x_GPIO_EN, 0x08, 0x08);
+	snd_soc_component_update_bits(dac, PCM512x_GPIO_OUTPUT_4, 0x0f, 0x02);
+	if (leds_off)
+		snd_soc_component_update_bits(dac, PCM512x_GPIO_CONTROL_1, 0x08, 0x00);
+	else
+		snd_soc_component_update_bits(dac, PCM512x_GPIO_CONTROL_1, 0x08, 0x08);
+
+	ret = pcm1863_add_controls(adc);
+	if (ret < 0)
+		dev_warn(rtd->dev, "Failed to add pcm1863 controls: %d\n",
+		ret);
+
+	/* set GPIO2 to output, GPIO3 input */
+	snd_soc_component_write(adc, PCM186X_GPIO3_2_CTRL, 0x00);
+	snd_soc_component_write(adc, PCM186X_GPIO3_2_DIR_CTRL, 0x04);
+	if (leds_off)
+		snd_soc_component_update_bits(adc, PCM186X_GPIO_IN_OUT, 0x40, 0x00);
+	else
+		snd_soc_component_update_bits(adc, PCM186X_GPIO_IN_OUT, 0x40, 0x40);
+
+	if (digital_gain_0db_limit) {
+		int ret;
+		struct snd_soc_card *card = rtd->card;
+
+		ret = snd_soc_limit_volume(card, "Digital Playback Volume", 207);
+		if (ret < 0)
+			dev_warn(card->dev, "Failed to set volume limit: %d\n", ret);
+	}
+
+	return 0;
+}
+
+static int snd_rpi_hifiberry_dacplusadcpro_update_rate_den(
+	struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_component *component = asoc_rtd_to_codec(rtd, 0)->component; /* only use DAC */
+	struct pcm512x_priv *pcm512x = snd_soc_component_get_drvdata(component);
+	struct snd_ratnum *rats_no_pll;
+	unsigned int num = 0, den = 0;
+	int err;
+
+	rats_no_pll = devm_kzalloc(rtd->dev, sizeof(*rats_no_pll), GFP_KERNEL);
+	if (!rats_no_pll)
+		return -ENOMEM;
+
+	rats_no_pll->num = clk_get_rate(pcm512x->sclk) / 64;
+	rats_no_pll->den_min = 1;
+	rats_no_pll->den_max = 128;
+	rats_no_pll->den_step = 1;
+
+	err = snd_interval_ratnum(hw_param_interval(params,
+		SNDRV_PCM_HW_PARAM_RATE), 1, rats_no_pll, &num, &den);
+	if (err >= 0 && den) {
+		params->rate_num = num;
+		params->rate_den = den;
+	}
+
+	devm_kfree(rtd->dev, rats_no_pll);
+	return 0;
+}
+
+static int snd_rpi_hifiberry_dacplusadcpro_hw_params(
+	struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params)
+{
+	int ret = 0;
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	int channels = params_channels(params);
+	int width = 32;
+	struct snd_soc_component *dac = asoc_rtd_to_codec(rtd, 0)->component;
+	struct snd_soc_dai *dai = asoc_rtd_to_codec(rtd, 0);
+	struct snd_soc_dai_driver *drv = dai->driver;
+	const struct snd_soc_dai_ops *ops = drv->ops;
+
+	if (snd_rpi_hifiberry_is_dacpro) {
+		width = snd_pcm_format_physical_width(params_format(params));
+
+		snd_rpi_hifiberry_dacplusadcpro_set_sclk(dac,
+			params_rate(params));
+
+		ret = snd_rpi_hifiberry_dacplusadcpro_update_rate_den(
+			substream, params);
+		if (ret)
+			return ret;
+	}
+
+	ret = snd_soc_dai_set_bclk_ratio(asoc_rtd_to_cpu(rtd, 0), channels * width);
+	if (ret)
+		return ret;
+	ret = snd_soc_dai_set_bclk_ratio(asoc_rtd_to_codec(rtd, 0), channels * width);
+	if (ret)
+		return ret;
+	if (snd_rpi_hifiberry_is_dacpro && ops->hw_params)
+		ret = ops->hw_params(substream, params, dai);
+	return ret;
+}
+
+static int snd_rpi_hifiberry_dacplusadcpro_startup(
+	struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_component *dac = asoc_rtd_to_codec(rtd, 0)->component;
+	struct snd_soc_component *adc = asoc_rtd_to_codec(rtd, 1)->component;
+
+	if (leds_off)
+		return 0;
+	/* switch on respective LED */
+	if (!substream->stream)
+		snd_soc_component_update_bits(dac, PCM512x_GPIO_CONTROL_1, 0x08, 0x08);
+	else
+		snd_soc_component_update_bits(adc, PCM186X_GPIO_IN_OUT, 0x40, 0x40);
+	return 0;
+}
+
+static void snd_rpi_hifiberry_dacplusadcpro_shutdown(
+	struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_component *dac = asoc_rtd_to_codec(rtd, 0)->component;
+	struct snd_soc_component *adc = asoc_rtd_to_codec(rtd, 1)->component;
+
+	/* switch off respective LED */
+	if (!substream->stream)
+		snd_soc_component_update_bits(dac, PCM512x_GPIO_CONTROL_1, 0x08, 0x00);
+	else
+		snd_soc_component_update_bits(adc, PCM186X_GPIO_IN_OUT, 0x40, 0x00);
+}
+
+
+/* machine stream operations */
+static struct snd_soc_ops snd_rpi_hifiberry_dacplusadcpro_ops = {
+	.hw_params = snd_rpi_hifiberry_dacplusadcpro_hw_params,
+	.startup = snd_rpi_hifiberry_dacplusadcpro_startup,
+	.shutdown = snd_rpi_hifiberry_dacplusadcpro_shutdown,
+};
+
+SND_SOC_DAILINK_DEFS(hifi,
+	DAILINK_COMP_ARRAY(COMP_CPU("bcm2708-i2s.0")),
+	DAILINK_COMP_ARRAY(COMP_CODEC("pcm512x.1-004d", "pcm512x-hifi"),
+			   COMP_CODEC("pcm186x.1-004a", "pcm1863-aif")),
+	DAILINK_COMP_ARRAY(COMP_PLATFORM("bcm2708-i2s.0")));
+
+static struct snd_soc_dai_link snd_rpi_hifiberry_dacplusadcpro_dai[] = {
+{
+	.name		= "HiFiBerry DAC+ADC PRO",
+	.stream_name	= "HiFiBerry DAC+ADC PRO HiFi",
+	.dai_fmt	= SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+				SND_SOC_DAIFMT_CBS_CFS,
+	.ops		= &snd_rpi_hifiberry_dacplusadcpro_ops,
+	.init		= snd_rpi_hifiberry_dacplusadcpro_init,
+	SND_SOC_DAILINK_REG(hifi),
+},
+};
+
+/* audio machine driver */
+static struct snd_soc_card snd_rpi_hifiberry_dacplusadcpro = {
+	.name         = "snd_rpi_hifiberry_dacplusadcpro",
+	.driver_name  = "HifiberryDacpAdcPro",
+	.owner        = THIS_MODULE,
+	.dai_link     = snd_rpi_hifiberry_dacplusadcpro_dai,
+	.num_links    = ARRAY_SIZE(snd_rpi_hifiberry_dacplusadcpro_dai),
+};
+
+static int snd_rpi_hifiberry_dacplusadcpro_probe(struct platform_device *pdev)
+{
+	int ret = 0, i = 0;
+	struct snd_soc_card *card = &snd_rpi_hifiberry_dacplusadcpro;
+
+	snd_rpi_hifiberry_dacplusadcpro.dev = &pdev->dev;
+	if (pdev->dev.of_node) {
+		struct device_node *i2s_node;
+		struct snd_soc_dai_link *dai;
+
+		dai = &snd_rpi_hifiberry_dacplusadcpro_dai[0];
+		i2s_node = of_parse_phandle(pdev->dev.of_node,
+			"i2s-controller", 0);
+		if (i2s_node) {
+			for (i = 0; i < card->num_links; i++) {
+				dai->cpus->dai_name = NULL;
+				dai->cpus->of_node = i2s_node;
+				dai->platforms->name = NULL;
+				dai->platforms->of_node = i2s_node;
+			}
+		}
+	}
+	digital_gain_0db_limit = !of_property_read_bool(
+		pdev->dev.of_node, "hifiberry-dacplusadcpro,24db_digital_gain");
+	slave = of_property_read_bool(pdev->dev.of_node,
+					"hifiberry-dacplusadcpro,slave");
+	leds_off = of_property_read_bool(pdev->dev.of_node,
+					"hifiberry-dacplusadcpro,leds_off");
+	ret = snd_soc_register_card(&snd_rpi_hifiberry_dacplusadcpro);
+	if (ret && ret != -EPROBE_DEFER)
+		dev_err(&pdev->dev,
+			"snd_soc_register_card() failed: %d\n", ret);
+
+	return ret;
+}
+
+static const struct of_device_id snd_rpi_hifiberry_dacplusadcpro_of_match[] = {
+	{ .compatible = "hifiberry,hifiberry-dacplusadcpro", },
+	{},
+};
+
+MODULE_DEVICE_TABLE(of, snd_rpi_hifiberry_dacplusadcpro_of_match);
+
+static struct platform_driver snd_rpi_hifiberry_dacplusadcpro_driver = {
+	.driver = {
+		.name   = "snd-rpi-hifiberry-dacplusadcpro",
+		.owner  = THIS_MODULE,
+		.of_match_table = snd_rpi_hifiberry_dacplusadcpro_of_match,
+	},
+	.probe          = snd_rpi_hifiberry_dacplusadcpro_probe,
+};
+
+module_platform_driver(snd_rpi_hifiberry_dacplusadcpro_driver);
+
+MODULE_AUTHOR("Joerg Schambacher <joerg@i2audio.com>");
+MODULE_AUTHOR("Daniel Matuschek <daniel@hifiberry.com>");
+MODULE_DESCRIPTION("ASoC Driver for HiFiBerry DAC+ADC");
+MODULE_LICENSE("GPL v2");
diff -Nur --no-dereference linux-5.10.52-orig/sound/soc/bcm/hifiberry_dacplus.c linux-5.10.52-v7l+/sound/soc/bcm/hifiberry_dacplus.c
--- linux-5.10.52-orig/sound/soc/bcm/hifiberry_dacplus.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/sound/soc/bcm/hifiberry_dacplus.c	2021-07-25 16:46:40.147717820 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/*
+ * ASoC Driver for HiFiBerry DAC+ / DAC Pro / AMP100
+ *
+ * Author:	Daniel Matuschek, Stuart MacLean <stuart@hifiberry.com>
+ *		Copyright 2014-2015
+ *		based on code by Florian Meier <florian.meier@koalo.de>
+ *		Headphone/AMP100 Joerg Schambacher <joerg@hifiberry.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/gpio/consumer.h>
+#include <../drivers/gpio/gpiolib.h>
+#include <linux/platform_device.h>
+#include <linux/kernel.h>
+#include <linux/clk.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/jack.h>
+
+#include "../codecs/pcm512x.h"
+
+#define HIFIBERRY_DACPRO_NOCLOCK 0
+#define HIFIBERRY_DACPRO_CLK44EN 1
+#define HIFIBERRY_DACPRO_CLK48EN 2
+
+struct pcm512x_priv {
+	struct regmap *regmap;
+	struct clk *sclk;
+};
+
+/* Clock rate of CLK44EN attached to GPIO6 pin */
+#define CLK_44EN_RATE 22579200UL
+/* Clock rate of CLK48EN attached to GPIO3 pin */
+#define CLK_48EN_RATE 24576000UL
+
+static bool slave;
+static bool snd_rpi_hifiberry_is_dacpro;
+static bool digital_gain_0db_limit = true;
+static bool leds_off;
+static bool auto_mute;
+static int mute_ext_ctl;
+static int mute_ext;
+static struct gpio_desc *snd_mute_gpio;
+static struct gpio_desc *snd_reset_gpio;
+static struct snd_soc_card snd_rpi_hifiberry_dacplus;
+
+static int snd_rpi_hifiberry_dacplus_mute_set(int mute)
+{
+	gpiod_set_value_cansleep(snd_mute_gpio, mute);
+	return 1;
+}
+
+static int snd_rpi_hifiberry_dacplus_mute_get(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	ucontrol->value.integer.value[0] = mute_ext;
+
+	return 0;
+}
+
+static int snd_rpi_hifiberry_dacplus_mute_put(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	if (mute_ext == ucontrol->value.integer.value[0])
+		return 0;
+
+	mute_ext = ucontrol->value.integer.value[0];
+
+	return snd_rpi_hifiberry_dacplus_mute_set(mute_ext);
+}
+
+static const char * const mute_text[] = {"Play", "Mute"};
+static const struct soc_enum hb_dacplus_opt_mute_enum =
+	SOC_ENUM_SINGLE_EXT(2, mute_text);
+
+static const struct snd_kcontrol_new hb_dacplus_opt_mute_controls[] = {
+	SOC_ENUM_EXT("Mute(ext)", hb_dacplus_opt_mute_enum,
+			      snd_rpi_hifiberry_dacplus_mute_get,
+			      snd_rpi_hifiberry_dacplus_mute_put),
+};
+
+static void snd_rpi_hifiberry_dacplus_select_clk(struct snd_soc_component *component,
+	int clk_id)
+{
+	switch (clk_id) {
+	case HIFIBERRY_DACPRO_NOCLOCK:
+		snd_soc_component_update_bits(component, PCM512x_GPIO_CONTROL_1, 0x24, 0x00);
+		break;
+	case HIFIBERRY_DACPRO_CLK44EN:
+		snd_soc_component_update_bits(component, PCM512x_GPIO_CONTROL_1, 0x24, 0x20);
+		break;
+	case HIFIBERRY_DACPRO_CLK48EN:
+		snd_soc_component_update_bits(component, PCM512x_GPIO_CONTROL_1, 0x24, 0x04);
+		break;
+	}
+	usleep_range(3000, 4000);
+}
+
+static void snd_rpi_hifiberry_dacplus_clk_gpio(struct snd_soc_component *component)
+{
+	snd_soc_component_update_bits(component, PCM512x_GPIO_EN, 0x24, 0x24);
+	snd_soc_component_update_bits(component, PCM512x_GPIO_OUTPUT_3, 0x0f, 0x02);
+	snd_soc_component_update_bits(component, PCM512x_GPIO_OUTPUT_6, 0x0f, 0x02);
+}
+
+static bool snd_rpi_hifiberry_dacplus_is_sclk(struct snd_soc_component *component)
+{
+	unsigned int sck;
+
+	sck = snd_soc_component_read(component, PCM512x_RATE_DET_4);
+	return (!(sck & 0x40));
+}
+
+static bool snd_rpi_hifiberry_dacplus_is_pro_card(struct snd_soc_component *component)
+{
+	bool isClk44EN, isClk48En, isNoClk;
+
+	snd_rpi_hifiberry_dacplus_clk_gpio(component);
+
+	snd_rpi_hifiberry_dacplus_select_clk(component, HIFIBERRY_DACPRO_CLK44EN);
+	isClk44EN = snd_rpi_hifiberry_dacplus_is_sclk(component);
+
+	snd_rpi_hifiberry_dacplus_select_clk(component, HIFIBERRY_DACPRO_NOCLOCK);
+	isNoClk = snd_rpi_hifiberry_dacplus_is_sclk(component);
+
+	snd_rpi_hifiberry_dacplus_select_clk(component, HIFIBERRY_DACPRO_CLK48EN);
+	isClk48En = snd_rpi_hifiberry_dacplus_is_sclk(component);
+
+	return (isClk44EN && isClk48En && !isNoClk);
+}
+
+static int snd_rpi_hifiberry_dacplus_clk_for_rate(int sample_rate)
+{
+	int type;
+
+	switch (sample_rate) {
+	case 11025:
+	case 22050:
+	case 44100:
+	case 88200:
+	case 176400:
+	case 352800:
+		type = HIFIBERRY_DACPRO_CLK44EN;
+		break;
+	default:
+		type = HIFIBERRY_DACPRO_CLK48EN;
+		break;
+	}
+	return type;
+}
+
+static void snd_rpi_hifiberry_dacplus_set_sclk(struct snd_soc_component *component,
+	int sample_rate)
+{
+	struct pcm512x_priv *pcm512x = snd_soc_component_get_drvdata(component);
+
+	if (!IS_ERR(pcm512x->sclk)) {
+		int ctype;
+
+		ctype = snd_rpi_hifiberry_dacplus_clk_for_rate(sample_rate);
+		clk_set_rate(pcm512x->sclk, (ctype == HIFIBERRY_DACPRO_CLK44EN)
+			? CLK_44EN_RATE : CLK_48EN_RATE);
+		snd_rpi_hifiberry_dacplus_select_clk(component, ctype);
+	}
+}
+
+static int snd_rpi_hifiberry_dacplus_init(struct snd_soc_pcm_runtime *rtd)
+{
+	struct snd_soc_component *component = asoc_rtd_to_codec(rtd, 0)->component;
+	struct pcm512x_priv *priv;
+	struct snd_soc_card *card = &snd_rpi_hifiberry_dacplus;
+
+	if (slave)
+		snd_rpi_hifiberry_is_dacpro = false;
+	else
+		snd_rpi_hifiberry_is_dacpro =
+				snd_rpi_hifiberry_dacplus_is_pro_card(component);
+
+	if (snd_rpi_hifiberry_is_dacpro) {
+		struct snd_soc_dai_link *dai = rtd->dai_link;
+
+		dai->name = "HiFiBerry DAC+ Pro";
+		dai->stream_name = "HiFiBerry DAC+ Pro HiFi";
+		dai->dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
+			| SND_SOC_DAIFMT_CBM_CFM;
+
+		snd_soc_component_update_bits(component, PCM512x_BCLK_LRCLK_CFG, 0x31, 0x11);
+		snd_soc_component_update_bits(component, PCM512x_MASTER_MODE, 0x03, 0x03);
+		snd_soc_component_update_bits(component, PCM512x_MASTER_CLKDIV_2, 0x7f, 63);
+	} else {
+		priv = snd_soc_component_get_drvdata(component);
+		priv->sclk = ERR_PTR(-ENOENT);
+	}
+
+	snd_soc_component_update_bits(component, PCM512x_GPIO_EN, 0x08, 0x08);
+	snd_soc_component_update_bits(component, PCM512x_GPIO_OUTPUT_4, 0x0f, 0x02);
+	if (leds_off)
+		snd_soc_component_update_bits(component, PCM512x_GPIO_CONTROL_1, 0x08, 0x00);
+	else
+		snd_soc_component_update_bits(component, PCM512x_GPIO_CONTROL_1, 0x08, 0x08);
+
+	if (digital_gain_0db_limit) {
+		int ret;
+		struct snd_soc_card *card = rtd->card;
+
+		ret = snd_soc_limit_volume(card, "Digital Playback Volume", 207);
+		if (ret < 0)
+			dev_warn(card->dev, "Failed to set volume limit: %d\n", ret);
+	}
+	if (snd_reset_gpio) {
+		gpiod_set_value_cansleep(snd_reset_gpio, 0);
+		msleep(1);
+		gpiod_set_value_cansleep(snd_reset_gpio, 1);
+		msleep(1);
+		gpiod_set_value_cansleep(snd_reset_gpio, 0);
+	}
+
+	if (mute_ext_ctl)
+		snd_soc_add_card_controls(card,	hb_dacplus_opt_mute_controls,
+				ARRAY_SIZE(hb_dacplus_opt_mute_controls));
+
+	if (snd_mute_gpio)
+		gpiod_set_value_cansleep(snd_mute_gpio,	mute_ext);
+
+	return 0;
+}
+
+static int snd_rpi_hifiberry_dacplus_update_rate_den(
+	struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_component *component = asoc_rtd_to_codec(rtd, 0)->component;
+	struct pcm512x_priv *pcm512x = snd_soc_component_get_drvdata(component);
+	struct snd_ratnum *rats_no_pll;
+	unsigned int num = 0, den = 0;
+	int err;
+
+	rats_no_pll = devm_kzalloc(rtd->dev, sizeof(*rats_no_pll), GFP_KERNEL);
+	if (!rats_no_pll)
+		return -ENOMEM;
+
+	rats_no_pll->num = clk_get_rate(pcm512x->sclk) / 64;
+	rats_no_pll->den_min = 1;
+	rats_no_pll->den_max = 128;
+	rats_no_pll->den_step = 1;
+
+	err = snd_interval_ratnum(hw_param_interval(params,
+		SNDRV_PCM_HW_PARAM_RATE), 1, rats_no_pll, &num, &den);
+	if (err >= 0 && den) {
+		params->rate_num = num;
+		params->rate_den = den;
+	}
+
+	devm_kfree(rtd->dev, rats_no_pll);
+	return 0;
+}
+
+static int snd_rpi_hifiberry_dacplus_hw_params(
+	struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params)
+{
+	int ret = 0;
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	int channels = params_channels(params);
+	int width = 32;
+
+	if (snd_rpi_hifiberry_is_dacpro) {
+		struct snd_soc_component *component = asoc_rtd_to_codec(rtd, 0)->component;
+
+		width = snd_pcm_format_physical_width(params_format(params));
+
+		snd_rpi_hifiberry_dacplus_set_sclk(component,
+			params_rate(params));
+
+		ret = snd_rpi_hifiberry_dacplus_update_rate_den(
+			substream, params);
+	}
+
+	ret = snd_soc_dai_set_bclk_ratio(asoc_rtd_to_cpu(rtd, 0), channels * width);
+	if (ret)
+		return ret;
+	ret = snd_soc_dai_set_bclk_ratio(asoc_rtd_to_codec(rtd, 0), channels * width);
+	return ret;
+}
+
+static int snd_rpi_hifiberry_dacplus_startup(
+	struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_component *component = asoc_rtd_to_codec(rtd, 0)->component;
+
+	if (auto_mute)
+		gpiod_set_value_cansleep(snd_mute_gpio, 0);
+	if (leds_off)
+		return 0;
+	snd_soc_component_update_bits(component, PCM512x_GPIO_CONTROL_1, 0x08, 0x08);
+	return 0;
+}
+
+static void snd_rpi_hifiberry_dacplus_shutdown(
+	struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_component *component = asoc_rtd_to_codec(rtd, 0)->component;
+
+	snd_soc_component_update_bits(component, PCM512x_GPIO_CONTROL_1, 0x08, 0x00);
+	if (auto_mute)
+		gpiod_set_value_cansleep(snd_mute_gpio, 1);
+}
+
+/* machine stream operations */
+static struct snd_soc_ops snd_rpi_hifiberry_dacplus_ops = {
+	.hw_params = snd_rpi_hifiberry_dacplus_hw_params,
+	.startup = snd_rpi_hifiberry_dacplus_startup,
+	.shutdown = snd_rpi_hifiberry_dacplus_shutdown,
+};
+
+SND_SOC_DAILINK_DEFS(rpi_hifiberry_dacplus,
+	DAILINK_COMP_ARRAY(COMP_CPU("bcm2708-i2s.0")),
+	DAILINK_COMP_ARRAY(COMP_CODEC("pcm512x.1-004d", "pcm512x-hifi")),
+	DAILINK_COMP_ARRAY(COMP_PLATFORM("bcm2708-i2s.0")));
+
+static struct snd_soc_dai_link snd_rpi_hifiberry_dacplus_dai[] = {
+{
+	.name		= "HiFiBerry DAC+",
+	.stream_name	= "HiFiBerry DAC+ HiFi",
+	.dai_fmt	= SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+				SND_SOC_DAIFMT_CBS_CFS,
+	.ops		= &snd_rpi_hifiberry_dacplus_ops,
+	.init		= snd_rpi_hifiberry_dacplus_init,
+	SND_SOC_DAILINK_REG(rpi_hifiberry_dacplus),
+},
+};
+
+/* aux device for optional headphone amp */
+static struct snd_soc_aux_dev hifiberry_dacplus_aux_devs[] = {
+	{
+		.dlc = {
+			.name = "tpa6130a2.1-0060",
+		},
+	},
+};
+
+/* audio machine driver */
+static struct snd_soc_card snd_rpi_hifiberry_dacplus = {
+	.name         = "snd_rpi_hifiberry_dacplus",
+	.driver_name  = "HifiberryDacp",
+	.owner        = THIS_MODULE,
+	.dai_link     = snd_rpi_hifiberry_dacplus_dai,
+	.num_links    = ARRAY_SIZE(snd_rpi_hifiberry_dacplus_dai),
+};
+
+static int hb_hp_detect(void)
+{
+	struct i2c_adapter *adap = i2c_get_adapter(1);
+	int ret;
+	struct i2c_client tpa_i2c_client = {
+		.addr = 0x60,
+		.adapter = adap,
+	};
+
+	if (!adap)
+		return -EPROBE_DEFER;	/* I2C module not yet available */
+
+	ret = i2c_smbus_read_byte(&tpa_i2c_client) >= 0;
+	i2c_put_adapter(adap);
+	return ret;
+};
+
+static struct property tpa_enable_prop = {
+	       .name = "status",
+	       .length = 4 + 1, /* length 'okay' + 1 */
+	       .value = "okay",
+	};
+
+static int snd_rpi_hifiberry_dacplus_probe(struct platform_device *pdev)
+{
+	int ret = 0;
+	struct snd_soc_card *card = &snd_rpi_hifiberry_dacplus;
+	int len;
+	struct device_node *tpa_node;
+	struct property *tpa_prop;
+	struct of_changeset ocs;
+	struct property *pp;
+	int tmp;
+
+	/* probe for head phone amp */
+	ret = hb_hp_detect();
+	if (ret < 0)
+		return ret;
+	if (ret) {
+		card->aux_dev = hifiberry_dacplus_aux_devs;
+		card->num_aux_devs =
+				ARRAY_SIZE(hifiberry_dacplus_aux_devs);
+		tpa_node = of_find_compatible_node(NULL, NULL, "ti,tpa6130a2");
+		tpa_prop = of_find_property(tpa_node, "status", &len);
+
+		if (strcmp((char *)tpa_prop->value, "okay")) {
+			/* and activate headphone using change_sets */
+			dev_info(&pdev->dev, "activating headphone amplifier");
+			of_changeset_init(&ocs);
+			ret = of_changeset_update_property(&ocs, tpa_node,
+							&tpa_enable_prop);
+			if (ret) {
+				dev_err(&pdev->dev,
+				"cannot activate headphone amplifier\n");
+				return -ENODEV;
+			}
+			ret = of_changeset_apply(&ocs);
+			if (ret) {
+				dev_err(&pdev->dev,
+				"cannot activate headphone amplifier\n");
+				return -ENODEV;
+			}
+		}
+	}
+
+	snd_rpi_hifiberry_dacplus.dev = &pdev->dev;
+	if (pdev->dev.of_node) {
+		struct device_node *i2s_node;
+		struct snd_soc_dai_link *dai;
+
+		dai = &snd_rpi_hifiberry_dacplus_dai[0];
+		i2s_node = of_parse_phandle(pdev->dev.of_node,
+			"i2s-controller", 0);
+
+		if (i2s_node) {
+			dai->cpus->dai_name = NULL;
+			dai->cpus->of_node = i2s_node;
+			dai->platforms->name = NULL;
+			dai->platforms->of_node = i2s_node;
+		}
+
+		digital_gain_0db_limit = !of_property_read_bool(
+			pdev->dev.of_node, "hifiberry,24db_digital_gain");
+		slave = of_property_read_bool(pdev->dev.of_node,
+						"hifiberry-dacplus,slave");
+		leds_off = of_property_read_bool(pdev->dev.of_node,
+						"hifiberry-dacplus,leds_off");
+		auto_mute = of_property_read_bool(pdev->dev.of_node,
+						"hifiberry-dacplus,auto_mute");
+
+		/*
+		 * check for HW MUTE as defined in DT-overlay
+		 * active high, therefore default to HIGH to MUTE
+		 */
+		snd_mute_gpio =	devm_gpiod_get_optional(&pdev->dev,
+						 "mute", GPIOD_OUT_HIGH);
+		if (IS_ERR(snd_mute_gpio)) {
+			dev_err(&pdev->dev, "Can't allocate GPIO (HW-MUTE)");
+			return PTR_ERR(snd_mute_gpio);
+		}
+
+		/* add ALSA control if requested in DT-overlay (AMP100) */
+		pp = of_find_property(pdev->dev.of_node,
+				"hifiberry-dacplus,mute_ext_ctl", &tmp);
+		if (pp) {
+			if (!of_property_read_u32(pdev->dev.of_node,
+				"hifiberry-dacplus,mute_ext_ctl", &mute_ext)) {
+				/* ALSA control will be used */
+				mute_ext_ctl = 1;
+			}
+		}
+
+		/* check for HW RESET (AMP100) */
+		snd_reset_gpio = devm_gpiod_get_optional(&pdev->dev,
+						"reset", GPIOD_OUT_HIGH);
+		if (IS_ERR(snd_reset_gpio)) {
+			dev_err(&pdev->dev, "Can't allocate GPIO (HW-RESET)");
+			return PTR_ERR(snd_reset_gpio);
+		}
+
+	}
+
+	ret = devm_snd_soc_register_card(&pdev->dev,
+			&snd_rpi_hifiberry_dacplus);
+	if (ret && ret != -EPROBE_DEFER)
+		dev_err(&pdev->dev,
+			"snd_soc_register_card() failed: %d\n", ret);
+	if (!ret) {
+		if (snd_mute_gpio)
+			dev_info(&pdev->dev, "GPIO%i for HW-MUTE selected",
+					gpio_chip_hwgpio(snd_mute_gpio));
+		if (snd_reset_gpio)
+			dev_info(&pdev->dev, "GPIO%i for HW-RESET selected",
+					gpio_chip_hwgpio(snd_reset_gpio));
+	}
+	return ret;
+}
+
+static const struct of_device_id snd_rpi_hifiberry_dacplus_of_match[] = {
+	{ .compatible = "hifiberry,hifiberry-dacplus", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, snd_rpi_hifiberry_dacplus_of_match);
+
+static struct platform_driver snd_rpi_hifiberry_dacplus_driver = {
+	.driver = {
+		.name   = "snd-rpi-hifiberry-dacplus",
+		.owner  = THIS_MODULE,
+		.of_match_table = snd_rpi_hifiberry_dacplus_of_match,
+	},
+	.probe          = snd_rpi_hifiberry_dacplus_probe,
+};
+
+module_platform_driver(snd_rpi_hifiberry_dacplus_driver);
+
+MODULE_AUTHOR("Daniel Matuschek <daniel@hifiberry.com>");
+MODULE_DESCRIPTION("ASoC Driver for HiFiBerry DAC+");
+MODULE_LICENSE("GPL v2");
diff -Nur --no-dereference linux-5.10.52-orig/sound/soc/bcm/hifiberry_dacplusdsp.c linux-5.10.52-v7l+/sound/soc/bcm/hifiberry_dacplusdsp.c
--- linux-5.10.52-orig/sound/soc/bcm/hifiberry_dacplusdsp.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/sound/soc/bcm/hifiberry_dacplusdsp.c	2021-07-25 16:46:40.147717820 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ASoC Driver for HiFiBerry DAC + DSP
+ *
+ * Author:	Joerg Schambacher <joscha@schambacher.com>
+ *		Copyright 2018
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <sound/soc.h>
+
+static struct snd_soc_component_driver dacplusdsp_component_driver;
+
+static struct snd_soc_dai_driver dacplusdsp_dai = {
+	.name = "dacplusdsp-hifi",
+	.capture = {
+		.stream_name = "DAC+DSP Capture",
+		.channels_min = 2,
+		.channels_max = 2,
+		.rates = SNDRV_PCM_RATE_CONTINUOUS,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE |
+			   SNDRV_PCM_FMTBIT_S24_LE |
+			   SNDRV_PCM_FMTBIT_S32_LE,
+	},
+	.playback = {
+		.stream_name = "DACP+DSP Playback",
+		.channels_min = 2,
+		.channels_max = 2,
+		.rates = SNDRV_PCM_RATE_CONTINUOUS,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE |
+			   SNDRV_PCM_FMTBIT_S24_LE |
+			   SNDRV_PCM_FMTBIT_S32_LE,
+	},
+	.symmetric_rates = 1};
+
+#ifdef CONFIG_OF
+static const struct of_device_id dacplusdsp_ids[] = {
+	{
+		.compatible = "hifiberry,dacplusdsp",
+	},
+	{} };
+MODULE_DEVICE_TABLE(of, dacplusdsp_ids);
+#endif
+
+static int dacplusdsp_platform_probe(struct platform_device *pdev)
+{
+	int ret;
+
+	ret = snd_soc_register_component(&pdev->dev,
+			&dacplusdsp_component_driver, &dacplusdsp_dai, 1);
+	if (ret) {
+		pr_alert("snd_soc_register_component failed\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static int dacplusdsp_platform_remove(struct platform_device *pdev)
+{
+	snd_soc_unregister_component(&pdev->dev);
+	return 0;
+}
+
+static struct platform_driver dacplusdsp_driver = {
+	.driver = {
+		.name = "hifiberry-dacplusdsp-codec",
+		.of_match_table = of_match_ptr(dacplusdsp_ids),
+		},
+		.probe = dacplusdsp_platform_probe,
+		.remove = dacplusdsp_platform_remove,
+};
+
+module_platform_driver(dacplusdsp_driver);
+
+MODULE_AUTHOR("Joerg Schambacher <joerg@i2audio.com>");
+MODULE_DESCRIPTION("ASoC Driver for HiFiBerry DAC+DSP");
+MODULE_LICENSE("GPL v2");
diff -Nur --no-dereference linux-5.10.52-orig/sound/soc/bcm/hifiberry_dacplushd.c linux-5.10.52-v7l+/sound/soc/bcm/hifiberry_dacplushd.c
--- linux-5.10.52-orig/sound/soc/bcm/hifiberry_dacplushd.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/sound/soc/bcm/hifiberry_dacplushd.c	2021-07-25 16:46:40.147717820 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ASoC Driver for HiFiBerry DAC+ HD
+ *
+ * Author:	Joerg Schambacher, i2Audio GmbH for HiFiBerry
+ *		Copyright 2020
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <linux/i2c.h>
+#include <linux/clk.h>
+
+#include "../codecs/pcm179x.h"
+
+#define DEFAULT_RATE		44100
+
+struct brd_drv_data {
+	struct regmap *regmap;
+	struct clk *sclk;
+};
+
+static struct brd_drv_data drvdata;
+static struct gpio_desc *reset_gpio;
+static const unsigned int hb_dacplushd_rates[] = {
+	192000, 96000, 48000, 176400, 88200, 44100,
+};
+
+static struct snd_pcm_hw_constraint_list hb_dacplushd_constraints = {
+	.list = hb_dacplushd_rates,
+	.count = ARRAY_SIZE(hb_dacplushd_rates),
+};
+
+static int snd_rpi_hb_dacplushd_startup(struct snd_pcm_substream *substream)
+{
+	/* constraints for standard sample rates */
+	snd_pcm_hw_constraint_list(substream->runtime, 0,
+				SNDRV_PCM_HW_PARAM_RATE,
+				&hb_dacplushd_constraints);
+	return 0;
+}
+
+static void snd_rpi_hifiberry_dacplushd_set_sclk(
+		struct snd_soc_component *component,
+		int sample_rate)
+{
+	if (!IS_ERR(drvdata.sclk))
+		clk_set_rate(drvdata.sclk, sample_rate);
+}
+
+static int snd_rpi_hifiberry_dacplushd_init(struct snd_soc_pcm_runtime *rtd)
+{
+	struct snd_soc_dai_link *dai = rtd->dai_link;
+	struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
+
+	dai->name = "HiFiBerry DAC+ HD";
+	dai->stream_name = "HiFiBerry DAC+ HD HiFi";
+	dai->dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
+		| SND_SOC_DAIFMT_CBM_CFM;
+
+	/* allow only fixed 32 clock counts per channel */
+	snd_soc_dai_set_bclk_ratio(cpu_dai, 32*2);
+
+	return 0;
+}
+
+static int snd_rpi_hifiberry_dacplushd_hw_params(
+	struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params)
+{
+	int ret = 0;
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+
+	struct snd_soc_component *component = asoc_rtd_to_codec(rtd, 0)->component;
+
+	snd_rpi_hifiberry_dacplushd_set_sclk(component, params_rate(params));
+	return ret;
+}
+
+/* machine stream operations */
+static struct snd_soc_ops snd_rpi_hifiberry_dacplushd_ops = {
+	.startup = snd_rpi_hb_dacplushd_startup,
+	.hw_params = snd_rpi_hifiberry_dacplushd_hw_params,
+};
+
+SND_SOC_DAILINK_DEFS(hifi,
+	DAILINK_COMP_ARRAY(COMP_CPU("bcm2708-i2s.0")),
+	DAILINK_COMP_ARRAY(COMP_CODEC("pcm179x.1-004c", "pcm179x-hifi")),
+	DAILINK_COMP_ARRAY(COMP_PLATFORM("bcm2708-i2s.0")));
+
+
+static struct snd_soc_dai_link snd_rpi_hifiberry_dacplushd_dai[] = {
+{
+	.name		= "HiFiBerry DAC+ HD",
+	.stream_name	= "HiFiBerry DAC+ HD HiFi",
+	.dai_fmt	= SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+				SND_SOC_DAIFMT_CBS_CFS,
+	.ops		= &snd_rpi_hifiberry_dacplushd_ops,
+	.init		= snd_rpi_hifiberry_dacplushd_init,
+	SND_SOC_DAILINK_REG(hifi),
+},
+};
+
+/* audio machine driver */
+static struct snd_soc_card snd_rpi_hifiberry_dacplushd = {
+	.name         = "snd_rpi_hifiberry_dacplushd",
+	.driver_name  = "HifiberryDacplusHD",
+	.owner        = THIS_MODULE,
+	.dai_link     = snd_rpi_hifiberry_dacplushd_dai,
+	.num_links    = ARRAY_SIZE(snd_rpi_hifiberry_dacplushd_dai),
+};
+
+static int snd_rpi_hifiberry_dacplushd_probe(struct platform_device *pdev)
+{
+	int ret = 0;
+	static int dac_reset_done;
+	struct device *dev = &pdev->dev;
+	struct device_node *dev_node = dev->of_node;
+
+	snd_rpi_hifiberry_dacplushd.dev = &pdev->dev;
+
+	/* get GPIO and release DAC from RESET */
+	if (!dac_reset_done) {
+		reset_gpio = gpiod_get(&pdev->dev, "reset", GPIOD_OUT_LOW);
+		if (IS_ERR(reset_gpio)) {
+			dev_err(&pdev->dev, "gpiod_get() failed\n");
+			return -EINVAL;
+		}
+		dac_reset_done = 1;
+	}
+	if (!IS_ERR(reset_gpio))
+		gpiod_set_value(reset_gpio, 0);
+	msleep(1);
+	if (!IS_ERR(reset_gpio))
+		gpiod_set_value(reset_gpio, 1);
+	msleep(1);
+	if (!IS_ERR(reset_gpio))
+		gpiod_set_value(reset_gpio, 0);
+
+	if (pdev->dev.of_node) {
+		struct device_node *i2s_node;
+		struct snd_soc_dai_link *dai;
+
+		dai = &snd_rpi_hifiberry_dacplushd_dai[0];
+		i2s_node = of_parse_phandle(pdev->dev.of_node,
+			"i2s-controller", 0);
+
+		if (i2s_node) {
+			dai->cpus->of_node = i2s_node;
+			dai->platforms->of_node = i2s_node;
+			dai->cpus->dai_name = NULL;
+			dai->platforms->name = NULL;
+		} else {
+			return -EPROBE_DEFER;
+		}
+
+	}
+
+	ret = devm_snd_soc_register_card(&pdev->dev,
+			&snd_rpi_hifiberry_dacplushd);
+	if (ret && ret != -EPROBE_DEFER) {
+		dev_err(&pdev->dev,
+			"snd_soc_register_card() failed: %d\n", ret);
+		return ret;
+	}
+	if (ret == -EPROBE_DEFER)
+		return ret;
+
+	dev_set_drvdata(dev, &drvdata);
+	if (dev_node == NULL) {
+		dev_err(&pdev->dev, "Device tree node not found\n");
+		return -ENODEV;
+	}
+
+	drvdata.sclk = devm_clk_get(dev, NULL);
+	if (IS_ERR(drvdata.sclk)) {
+		drvdata.sclk = ERR_PTR(-ENOENT);
+		return -ENODEV;
+	}
+
+	clk_set_rate(drvdata.sclk, DEFAULT_RATE);
+
+	return ret;
+}
+
+static int snd_rpi_hifiberry_dacplushd_remove(struct platform_device *pdev)
+{
+	if (IS_ERR(reset_gpio))
+		return -EINVAL;
+
+	/* put DAC into RESET and release GPIO */
+	gpiod_set_value(reset_gpio, 0);
+	gpiod_put(reset_gpio);
+
+	return 0;
+}
+
+static const struct of_device_id snd_rpi_hifiberry_dacplushd_of_match[] = {
+	{ .compatible = "hifiberry,hifiberry-dacplushd", },
+	{},
+};
+
+MODULE_DEVICE_TABLE(of, snd_rpi_hifiberry_dacplushd_of_match);
+
+static struct platform_driver snd_rpi_hifiberry_dacplushd_driver = {
+	.driver = {
+		.name   = "snd-rpi-hifiberry-dacplushd",
+		.owner  = THIS_MODULE,
+		.of_match_table = snd_rpi_hifiberry_dacplushd_of_match,
+	},
+	.probe          = snd_rpi_hifiberry_dacplushd_probe,
+	.remove		= snd_rpi_hifiberry_dacplushd_remove,
+};
+
+module_platform_driver(snd_rpi_hifiberry_dacplushd_driver);
+
+MODULE_AUTHOR("Joerg Schambacher <joerg@i2audio.com>");
+MODULE_DESCRIPTION("ASoC Driver for HiFiBerry DAC+ HD");
+MODULE_LICENSE("GPL v2");
diff -Nur --no-dereference linux-5.10.52-orig/sound/soc/bcm/iqaudio-codec.c linux-5.10.52-v7l+/sound/soc/bcm/iqaudio-codec.c
--- linux-5.10.52-orig/sound/soc/bcm/iqaudio-codec.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/sound/soc/bcm/iqaudio-codec.c	2021-07-25 16:46:40.167717485 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/*
+ * ASoC Driver for IQaudIO Raspberry Pi Codec board
+ *
+ * Author:	Gordon Garrity <gordon@iqaudio.com>
+ *		(C) Copyright IQaudio Limited, 2017-2019
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/gpio/consumer.h>
+#include <linux/platform_device.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/jack.h>
+
+#include <linux/acpi.h>
+#include <linux/slab.h>
+#include "../codecs/da7213.h"
+
+static int pll_out = DA7213_PLL_FREQ_OUT_90316800;
+
+static int snd_rpi_iqaudio_pll_control(struct snd_soc_dapm_widget *w,
+				       struct snd_kcontrol *k, int  event)
+{
+	int ret = 0;
+	struct snd_soc_dapm_context *dapm = w->dapm;
+	struct snd_soc_card *card = dapm->card;
+	struct snd_soc_pcm_runtime *rtd =
+		snd_soc_get_pcm_runtime(card, &card->dai_link[0]);
+	struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
+
+	if (SND_SOC_DAPM_EVENT_OFF(event)) {
+		ret = snd_soc_dai_set_pll(codec_dai, 0, DA7213_SYSCLK_MCLK, 0,
+					  0);
+		if (ret)
+			dev_err(card->dev, "Failed to bypass PLL: %d\n", ret);
+		/* Allow PLL time to bypass */
+		msleep(100);
+	} else if (SND_SOC_DAPM_EVENT_ON(event)) {
+		ret = snd_soc_dai_set_pll(codec_dai, 0, DA7213_SYSCLK_PLL, 0,
+					  pll_out);
+		if (ret)
+			dev_err(card->dev, "Failed to enable PLL: %d\n", ret);
+		/* Allow PLL time to lock */
+		msleep(100);
+	}
+
+	return ret;
+}
+
+static int snd_rpi_iqaudio_post_dapm_event(struct snd_soc_dapm_widget *w,
+                              struct snd_kcontrol *kcontrol,
+                              int event)
+{
+     switch (event) {
+     case SND_SOC_DAPM_POST_PMU:
+           /* Delay for mic bias ramp */
+           msleep(1000);
+           break;
+     default:
+           break;
+     }
+
+     return 0;
+}
+
+static const struct snd_kcontrol_new dapm_controls[] = {
+	SOC_DAPM_PIN_SWITCH("HP Jack"),
+	SOC_DAPM_PIN_SWITCH("MIC Jack"),
+	SOC_DAPM_PIN_SWITCH("Onboard MIC"),
+	SOC_DAPM_PIN_SWITCH("AUX Jack"),
+};
+
+static const struct snd_soc_dapm_widget dapm_widgets[] = {
+	SND_SOC_DAPM_HP("HP Jack", NULL),
+	SND_SOC_DAPM_MIC("MIC Jack", NULL),
+	SND_SOC_DAPM_MIC("Onboard MIC", NULL),
+	SND_SOC_DAPM_LINE("AUX Jack", NULL),
+	SND_SOC_DAPM_SUPPLY("PLL Control", SND_SOC_NOPM, 0, 0,
+			    snd_rpi_iqaudio_pll_control,
+			    SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_POST("Post Power Up Event", snd_rpi_iqaudio_post_dapm_event),
+};
+
+static const struct snd_soc_dapm_route audio_map[] = {
+	{"HP Jack", NULL, "HPL"},
+	{"HP Jack", NULL, "HPR"},
+	{"HP Jack", NULL, "PLL Control"},
+
+	{"AUXR", NULL, "AUX Jack"},
+	{"AUXL", NULL, "AUX Jack"},
+	{"AUX Jack", NULL, "PLL Control"},
+
+	/* Assume Mic1 is linked to Headset and Mic2 to on-board mic */
+	{"MIC1", NULL, "MIC Jack"},
+	{"MIC Jack", NULL, "PLL Control"},
+	{"MIC2", NULL, "Onboard MIC"},
+	{"Onboard MIC", NULL, "PLL Control"},
+};
+
+/* machine stream operations */
+
+static int snd_rpi_iqaudio_codec_init(struct snd_soc_pcm_runtime *rtd)
+{
+	struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
+	struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
+	int ret;
+
+	/*
+	 * Disable AUX Jack Pin by default to prevent PLL being enabled at
+	 * startup. This avoids holding the PLL to a fixed SR config for
+	 * subsequent streams.
+	 *
+	 * This pin can still be enabled later, as required by user-space.
+	 */
+	snd_soc_dapm_disable_pin(&rtd->card->dapm, "AUX Jack");
+	snd_soc_dapm_sync(&rtd->card->dapm);
+
+	/* Set bclk ratio to align with codec's BCLK rate */
+	ret = snd_soc_dai_set_bclk_ratio(cpu_dai, 64);
+	if (ret) {
+		dev_err(rtd->dev, "Failed to set CPU BLCK ratio\n");
+		return ret;
+	}
+
+	/* Set MCLK frequency to codec, onboard 11.2896MHz clock */
+	return snd_soc_dai_set_sysclk(codec_dai, DA7213_CLKSRC_MCLK, 11289600,
+				      SND_SOC_CLOCK_OUT);
+}
+
+static int snd_rpi_iqaudio_codec_hw_params(struct snd_pcm_substream *substream,
+					   struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	unsigned int samplerate = params_rate(params);
+
+	switch (samplerate) {
+	case  8000:
+	case 16000:
+	case 32000:
+	case 48000:
+	case 96000:
+		pll_out = DA7213_PLL_FREQ_OUT_98304000;
+		return 0;
+	case 44100:
+	case 88200:
+		pll_out = DA7213_PLL_FREQ_OUT_90316800;
+		return 0;
+	default:
+		dev_err(rtd->dev,"Unsupported samplerate %d\n", samplerate);
+		return -EINVAL;
+	}
+}
+
+static const struct snd_soc_ops snd_rpi_iqaudio_codec_ops = {
+	.hw_params = snd_rpi_iqaudio_codec_hw_params,
+};
+
+SND_SOC_DAILINK_DEFS(rpi_iqaudio,
+	DAILINK_COMP_ARRAY(COMP_CPU("bcm2708-i2s.0")),
+	DAILINK_COMP_ARRAY(COMP_CODEC("da7213.1-001a", "da7213-hifi")),
+	DAILINK_COMP_ARRAY(COMP_PLATFORM("bcm2835-i2s.0")));
+
+static struct snd_soc_dai_link snd_rpi_iqaudio_codec_dai[] = {
+{
+	.dai_fmt 		= SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+				  SND_SOC_DAIFMT_CBM_CFM,
+	.init			= snd_rpi_iqaudio_codec_init,
+	.ops			= &snd_rpi_iqaudio_codec_ops,
+	.symmetric_rates	= 1,
+	.symmetric_channels	= 1,
+	.symmetric_samplebits	= 1,
+	SND_SOC_DAILINK_REG(rpi_iqaudio),
+},
+};
+
+/* audio machine driver */
+static struct snd_soc_card snd_rpi_iqaudio_codec = {
+	.owner			= THIS_MODULE,
+	.dai_link		= snd_rpi_iqaudio_codec_dai,
+	.num_links		= ARRAY_SIZE(snd_rpi_iqaudio_codec_dai),
+	.controls		= dapm_controls,
+	.num_controls		= ARRAY_SIZE(dapm_controls),
+	.dapm_widgets		= dapm_widgets,
+	.num_dapm_widgets	= ARRAY_SIZE(dapm_widgets),
+	.dapm_routes		= audio_map,
+	.num_dapm_routes	= ARRAY_SIZE(audio_map),
+};
+
+static int snd_rpi_iqaudio_codec_probe(struct platform_device *pdev)
+{
+	int ret = 0;
+
+	snd_rpi_iqaudio_codec.dev = &pdev->dev;
+
+	if (pdev->dev.of_node) {
+		struct device_node *i2s_node;
+		struct snd_soc_card *card = &snd_rpi_iqaudio_codec;
+		struct snd_soc_dai_link *dai = &snd_rpi_iqaudio_codec_dai[0];
+
+		i2s_node = of_parse_phandle(pdev->dev.of_node,
+					    "i2s-controller", 0);
+		if (i2s_node) {
+			dai->cpus->dai_name = NULL;
+			dai->cpus->of_node = i2s_node;
+			dai->platforms->name = NULL;
+			dai->platforms->of_node = i2s_node;
+		}
+
+		if (of_property_read_string(pdev->dev.of_node, "card_name",
+					    &card->name))
+			card->name = "IQaudIOCODEC";
+
+		if (of_property_read_string(pdev->dev.of_node, "dai_name",
+					    &dai->name))
+			dai->name = "IQaudIO CODEC";
+
+		if (of_property_read_string(pdev->dev.of_node,
+					"dai_stream_name", &dai->stream_name))
+			dai->stream_name = "IQaudIO CODEC HiFi v1.2";
+
+	}
+
+	ret = snd_soc_register_card(&snd_rpi_iqaudio_codec);
+	if (ret) {
+		if (ret != -EPROBE_DEFER)
+			dev_err(&pdev->dev,
+				"snd_soc_register_card() failed: %d\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int snd_rpi_iqaudio_codec_remove(struct platform_device *pdev)
+{
+	return snd_soc_unregister_card(&snd_rpi_iqaudio_codec);
+}
+
+static const struct of_device_id iqaudio_of_match[] = {
+	{ .compatible = "iqaudio,iqaudio-codec", },
+	{},
+};
+
+MODULE_DEVICE_TABLE(of, iqaudio_of_match);
+
+static struct platform_driver snd_rpi_iqaudio_codec_driver = {
+	.driver = {
+		.name   = "snd-rpi-iqaudio-codec",
+		.owner  = THIS_MODULE,
+		.of_match_table = iqaudio_of_match,
+	},
+	.probe          = snd_rpi_iqaudio_codec_probe,
+	.remove         = snd_rpi_iqaudio_codec_remove,
+};
+
+
+
+module_platform_driver(snd_rpi_iqaudio_codec_driver);
+
+MODULE_AUTHOR("Gordon Garrity <gordon@iqaudio.com>");
+MODULE_DESCRIPTION("ASoC Driver for IQaudIO CODEC");
+MODULE_LICENSE("GPL v2");
diff -Nur --no-dereference linux-5.10.52-orig/sound/soc/bcm/iqaudio-dac.c linux-5.10.52-v7l+/sound/soc/bcm/iqaudio-dac.c
--- linux-5.10.52-orig/sound/soc/bcm/iqaudio-dac.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/sound/soc/bcm/iqaudio-dac.c	2021-07-25 16:46:40.167717485 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/*
+ * ASoC Driver for IQaudIO DAC
+ *
+ * Author:	Florian Meier <florian.meier@koalo.de>
+ *		Copyright 2013
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/gpio/consumer.h>
+#include <linux/platform_device.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/jack.h>
+
+static bool digital_gain_0db_limit = true;
+
+static struct gpio_desc *mute_gpio;
+
+static int snd_rpi_iqaudio_dac_init(struct snd_soc_pcm_runtime *rtd)
+{
+	if (digital_gain_0db_limit)
+	{
+		int ret;
+		struct snd_soc_card *card = rtd->card;
+
+		ret = snd_soc_limit_volume(card, "Digital Playback Volume", 207);
+		if (ret < 0)
+			dev_warn(card->dev, "Failed to set volume limit: %d\n", ret);
+	}
+
+	return 0;
+}
+
+static void snd_rpi_iqaudio_gpio_mute(struct snd_soc_card *card)
+{
+	if (mute_gpio) {
+		dev_info(card->dev, "%s: muting amp using GPIO22\n",
+			 __func__);
+		gpiod_set_value_cansleep(mute_gpio, 0);
+	}
+}
+
+static void snd_rpi_iqaudio_gpio_unmute(struct snd_soc_card *card)
+{
+	if (mute_gpio) {
+		dev_info(card->dev, "%s: un-muting amp using GPIO22\n",
+			 __func__);
+		gpiod_set_value_cansleep(mute_gpio, 1);
+	}
+}
+
+static int snd_rpi_iqaudio_set_bias_level(struct snd_soc_card *card,
+	struct snd_soc_dapm_context *dapm, enum snd_soc_bias_level level)
+{
+	struct snd_soc_pcm_runtime *rtd;
+	struct snd_soc_dai *codec_dai;
+
+	rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[0]);
+	codec_dai = asoc_rtd_to_codec(rtd, 0);
+
+	if (dapm->dev != codec_dai->dev)
+		return 0;
+
+	switch (level) {
+	case SND_SOC_BIAS_PREPARE:
+		if (dapm->bias_level != SND_SOC_BIAS_STANDBY)
+			break;
+
+		/* UNMUTE AMP */
+		snd_rpi_iqaudio_gpio_unmute(card);
+
+		break;
+	case SND_SOC_BIAS_STANDBY:
+		if (dapm->bias_level != SND_SOC_BIAS_PREPARE)
+			break;
+
+		/* MUTE AMP */
+		snd_rpi_iqaudio_gpio_mute(card);
+
+		break;
+	default:
+		break;
+	}
+
+	return 0;
+}
+
+SND_SOC_DAILINK_DEFS(hifi,
+        DAILINK_COMP_ARRAY(COMP_CPU("bcm2708-i2s.0")),
+        DAILINK_COMP_ARRAY(COMP_CODEC("pcm512x.1-004c", "pcm512x-hifi")),
+        DAILINK_COMP_ARRAY(COMP_PLATFORM("bcm2708-i2s.0")));
+
+static struct snd_soc_dai_link snd_rpi_iqaudio_dac_dai[] = {
+{
+	.dai_fmt	= SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+				SND_SOC_DAIFMT_CBS_CFS,
+	.init		= snd_rpi_iqaudio_dac_init,
+	SND_SOC_DAILINK_REG(hifi),
+},
+};
+
+/* audio machine driver */
+static struct snd_soc_card snd_rpi_iqaudio_dac = {
+	.owner        = THIS_MODULE,
+	.dai_link     = snd_rpi_iqaudio_dac_dai,
+	.num_links    = ARRAY_SIZE(snd_rpi_iqaudio_dac_dai),
+};
+
+static int snd_rpi_iqaudio_dac_probe(struct platform_device *pdev)
+{
+	int ret = 0;
+	bool gpio_unmute = false;
+
+	snd_rpi_iqaudio_dac.dev = &pdev->dev;
+
+	if (pdev->dev.of_node) {
+		struct device_node *i2s_node;
+		struct snd_soc_card *card = &snd_rpi_iqaudio_dac;
+		struct snd_soc_dai_link *dai = &snd_rpi_iqaudio_dac_dai[0];
+		bool auto_gpio_mute = false;
+
+		i2s_node = of_parse_phandle(pdev->dev.of_node,
+					    "i2s-controller", 0);
+		if (i2s_node) {
+			dai->cpus->dai_name = NULL;
+			dai->cpus->of_node = i2s_node;
+			dai->platforms->name = NULL;
+			dai->platforms->of_node = i2s_node;
+		}
+
+		digital_gain_0db_limit = !of_property_read_bool(
+			pdev->dev.of_node, "iqaudio,24db_digital_gain");
+
+		if (of_property_read_string(pdev->dev.of_node, "card_name",
+					    &card->name))
+			card->name = "IQaudIODAC";
+
+		if (of_property_read_string(pdev->dev.of_node, "dai_name",
+					    &dai->name))
+			dai->name = "IQaudIO DAC";
+
+		if (of_property_read_string(pdev->dev.of_node,
+					"dai_stream_name", &dai->stream_name))
+			dai->stream_name = "IQaudIO DAC HiFi";
+
+		/* gpio_unmute - one time unmute amp using GPIO */
+		gpio_unmute = of_property_read_bool(pdev->dev.of_node,
+						    "iqaudio-dac,unmute-amp");
+
+		/* auto_gpio_mute - mute/unmute amp using GPIO */
+		auto_gpio_mute = of_property_read_bool(pdev->dev.of_node,
+						"iqaudio-dac,auto-mute-amp");
+
+		if (auto_gpio_mute || gpio_unmute) {
+			mute_gpio = devm_gpiod_get_optional(&pdev->dev, "mute",
+							    GPIOD_OUT_LOW);
+			if (IS_ERR(mute_gpio)) {
+				ret = PTR_ERR(mute_gpio);
+				dev_err(&pdev->dev,
+					"Failed to get mute gpio: %d\n", ret);
+				return ret;
+			}
+
+			if (auto_gpio_mute && mute_gpio)
+				snd_rpi_iqaudio_dac.set_bias_level =
+						snd_rpi_iqaudio_set_bias_level;
+		}
+	}
+
+	ret = snd_soc_register_card(&snd_rpi_iqaudio_dac);
+	if (ret) {
+		if (ret != -EPROBE_DEFER)
+			dev_err(&pdev->dev,
+				"snd_soc_register_card() failed: %d\n", ret);
+		return ret;
+	}
+
+	if (gpio_unmute && mute_gpio)
+		snd_rpi_iqaudio_gpio_unmute(&snd_rpi_iqaudio_dac);
+
+	return 0;
+}
+
+static int snd_rpi_iqaudio_dac_remove(struct platform_device *pdev)
+{
+	snd_rpi_iqaudio_gpio_mute(&snd_rpi_iqaudio_dac);
+
+	return snd_soc_unregister_card(&snd_rpi_iqaudio_dac);
+}
+
+static const struct of_device_id iqaudio_of_match[] = {
+	{ .compatible = "iqaudio,iqaudio-dac", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, iqaudio_of_match);
+
+static struct platform_driver snd_rpi_iqaudio_dac_driver = {
+	.driver = {
+		.name   = "snd-rpi-iqaudio-dac",
+		.owner  = THIS_MODULE,
+		.of_match_table = iqaudio_of_match,
+	},
+	.probe          = snd_rpi_iqaudio_dac_probe,
+	.remove         = snd_rpi_iqaudio_dac_remove,
+};
+
+module_platform_driver(snd_rpi_iqaudio_dac_driver);
+
+MODULE_AUTHOR("Florian Meier <florian.meier@koalo.de>");
+MODULE_DESCRIPTION("ASoC Driver for IQAudio DAC");
+MODULE_LICENSE("GPL v2");
diff -Nur --no-dereference linux-5.10.52-orig/sound/soc/bcm/i-sabre-q2m.c linux-5.10.52-v7l+/sound/soc/bcm/i-sabre-q2m.c
--- linux-5.10.52-orig/sound/soc/bcm/i-sabre-q2m.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/sound/soc/bcm/i-sabre-q2m.c	2021-07-25 16:46:40.167717485 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/*
+ * ASoC Driver for I-Sabre Q2M
+ *
+ * Author: Satoru Kawase
+ * Modified by: Xiao Qingyong
+ * Update kernel v4.18+ by : Audiophonics
+ * 		Copyright 2018 Audiophonics
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/fs.h>
+#include <asm/uaccess.h>
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+
+#include "../codecs/i-sabre-codec.h"
+
+
+static int snd_rpi_i_sabre_q2m_init(struct snd_soc_pcm_runtime *rtd)
+{
+	struct snd_soc_component *component = asoc_rtd_to_codec(rtd, 0)->component;
+	unsigned int value;
+
+	/* Device ID */
+	value = snd_soc_component_read(component, ISABRECODEC_REG_01);
+	dev_info(component->card->dev, "Audiophonics Device ID : %02X\n", value);
+
+	/* API revision */
+	value = snd_soc_component_read(component, ISABRECODEC_REG_02);
+	dev_info(component->card->dev, "Audiophonics API revision : %02X\n", value);
+
+	return 0;
+}
+
+static int snd_rpi_i_sabre_q2m_hw_params(
+	struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd     = substream->private_data;
+	struct snd_soc_dai         *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
+	int bclk_ratio;
+
+	bclk_ratio = snd_pcm_format_physical_width(
+			params_format(params)) * params_channels(params);
+	return snd_soc_dai_set_bclk_ratio(cpu_dai, bclk_ratio);
+}
+
+/* machine stream operations */
+static struct snd_soc_ops snd_rpi_i_sabre_q2m_ops = {
+	.hw_params = snd_rpi_i_sabre_q2m_hw_params,
+};
+
+SND_SOC_DAILINK_DEFS(rpi_i_sabre_q2m,
+	DAILINK_COMP_ARRAY(COMP_CPU("bcm2708-i2s.0")),
+	DAILINK_COMP_ARRAY(COMP_CODEC("i-sabre-codec-i2c.1-0048", "i-sabre-codec-dai")),
+	DAILINK_COMP_ARRAY(COMP_PLATFORM("bcm2708-i2s.0")));
+
+static struct snd_soc_dai_link snd_rpi_i_sabre_q2m_dai[] = {
+	{
+		.name           = "I-Sabre Q2M",
+		.stream_name    = "I-Sabre Q2M DAC",
+		.dai_fmt        = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
+						| SND_SOC_DAIFMT_CBS_CFS,
+		.init           = snd_rpi_i_sabre_q2m_init,
+		.ops            = &snd_rpi_i_sabre_q2m_ops,
+		SND_SOC_DAILINK_REG(rpi_i_sabre_q2m),
+	}
+};
+
+/* audio machine driver */
+static struct snd_soc_card snd_rpi_i_sabre_q2m = {
+	.name      = "I-Sabre Q2M DAC",
+	.owner     = THIS_MODULE,
+	.dai_link  = snd_rpi_i_sabre_q2m_dai,
+	.num_links = ARRAY_SIZE(snd_rpi_i_sabre_q2m_dai)
+};
+
+
+static int snd_rpi_i_sabre_q2m_probe(struct platform_device *pdev)
+{
+	int ret = 0;
+
+	snd_rpi_i_sabre_q2m.dev = &pdev->dev;
+	if (pdev->dev.of_node) {
+		struct device_node *i2s_node;
+		struct snd_soc_dai_link *dai;
+
+		dai = &snd_rpi_i_sabre_q2m_dai[0];
+		i2s_node = of_parse_phandle(pdev->dev.of_node,
+							"i2s-controller", 0);
+		if (i2s_node) {
+			dai->cpus->dai_name     = NULL;
+			dai->cpus->of_node      = i2s_node;
+			dai->platforms->name    = NULL;
+			dai->platforms->of_node = i2s_node;
+		} else {
+			dev_err(&pdev->dev,
+			    "Property 'i2s-controller' missing or invalid\n");
+			return (-EINVAL);
+		}
+
+		dai->name        = "I-Sabre Q2M";
+		dai->stream_name = "I-Sabre Q2M DAC";
+		dai->dai_fmt     = SND_SOC_DAIFMT_I2S
+					| SND_SOC_DAIFMT_NB_NF
+					| SND_SOC_DAIFMT_CBS_CFS;
+	}
+
+	/* Wait for registering codec driver */
+	mdelay(50);
+
+	ret = snd_soc_register_card(&snd_rpi_i_sabre_q2m);
+	if (ret) {
+		dev_err(&pdev->dev,
+			"snd_soc_register_card() failed: %d\n", ret);
+	}
+
+	return ret;
+}
+
+static int snd_rpi_i_sabre_q2m_remove(struct platform_device *pdev)
+{
+	return snd_soc_unregister_card(&snd_rpi_i_sabre_q2m);
+}
+
+static const struct of_device_id snd_rpi_i_sabre_q2m_of_match[] = {
+	{ .compatible = "audiophonics,i-sabre-q2m", },
+	{}
+};
+MODULE_DEVICE_TABLE(of, snd_rpi_i_sabre_q2m_of_match);
+
+static struct platform_driver snd_rpi_i_sabre_q2m_driver = {
+	.driver = {
+		.name           = "snd-rpi-i-sabre-q2m",
+		.owner          = THIS_MODULE,
+		.of_match_table = snd_rpi_i_sabre_q2m_of_match,
+	},
+	.probe  = snd_rpi_i_sabre_q2m_probe,
+	.remove = snd_rpi_i_sabre_q2m_remove,
+};
+module_platform_driver(snd_rpi_i_sabre_q2m_driver);
+
+MODULE_DESCRIPTION("ASoC Driver for I-Sabre Q2M");
+MODULE_AUTHOR("Audiophonics <http://www.audiophonics.fr>");
+MODULE_LICENSE("GPL");
diff -Nur --no-dereference linux-5.10.52-orig/sound/soc/bcm/justboom-both.c linux-5.10.52-v7l+/sound/soc/bcm/justboom-both.c
--- linux-5.10.52-orig/sound/soc/bcm/justboom-both.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/sound/soc/bcm/justboom-both.c	2021-07-25 16:46:40.167717485 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * rpi--wm8804.c -- ALSA SoC Raspberry Pi soundcard.
+ *
+ * Authors: Johannes Krude <johannes@krude.de
+ *
+ * Driver for when connecting simultaneously justboom-digi and justboom-dac
+ *
+ * Based upon code from:
+ * justboom-digi.c
+ * by Milan Neskovic <info@justboom.co>
+ * justboom-dac.c
+ * by Milan Neskovic <info@justboom.co>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/jack.h>
+
+#include "../codecs/wm8804.h"
+#include "../codecs/pcm512x.h"
+
+
+static bool digital_gain_0db_limit = true;
+
+static int snd_rpi_justboom_both_init(struct snd_soc_pcm_runtime *rtd)
+{
+	struct snd_soc_component *digi = asoc_rtd_to_codec(rtd, 0)->component;
+	struct snd_soc_component *dac = asoc_rtd_to_codec(rtd, 1)->component;
+
+	/* enable  TX output */
+	snd_soc_component_update_bits(digi, WM8804_PWRDN, 0x4, 0x0);
+
+	snd_soc_component_update_bits(dac, PCM512x_GPIO_EN, 0x08, 0x08);
+	snd_soc_component_update_bits(dac, PCM512x_GPIO_OUTPUT_4, 0xf, 0x02);
+	snd_soc_component_update_bits(dac, PCM512x_GPIO_CONTROL_1, 0x08, 0x08);
+
+	if (digital_gain_0db_limit) {
+		int ret;
+		struct snd_soc_card *card = rtd->card;
+
+		ret = snd_soc_limit_volume(card, "Digital Playback Volume",
+									207);
+		if (ret < 0)
+			dev_warn(card->dev, "Failed to set volume limit: %d\n",
+									ret);
+	}
+
+	return 0;
+}
+
+static int snd_rpi_justboom_both_hw_params(struct snd_pcm_substream *substream,
+				       struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
+	struct snd_soc_component *digi = asoc_rtd_to_codec(rtd, 0)->component;
+	struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
+
+	int sysclk = 27000000; /* This is fixed on this board */
+
+	long mclk_freq    = 0;
+	int mclk_div      = 1;
+	int sampling_freq = 1;
+
+	int ret;
+
+	int samplerate = params_rate(params);
+
+	if (samplerate <= 96000) {
+		mclk_freq = samplerate*256;
+		mclk_div  = WM8804_MCLKDIV_256FS;
+	} else {
+		mclk_freq = samplerate*128;
+		mclk_div  = WM8804_MCLKDIV_128FS;
+	}
+
+	switch (samplerate) {
+	case 32000:
+		sampling_freq = 0x03;
+		break;
+	case 44100:
+		sampling_freq = 0x00;
+		break;
+	case 48000:
+		sampling_freq = 0x02;
+		break;
+	case 88200:
+		sampling_freq = 0x08;
+		break;
+	case 96000:
+		sampling_freq = 0x0a;
+		break;
+	case 176400:
+		sampling_freq = 0x0c;
+		break;
+	case 192000:
+		sampling_freq = 0x0e;
+		break;
+	default:
+		dev_err(rtd->card->dev,
+		"Failed to set WM8804 SYSCLK, unsupported samplerate %d\n",
+		samplerate);
+	}
+
+	snd_soc_dai_set_clkdiv(codec_dai, WM8804_MCLK_DIV, mclk_div);
+	snd_soc_dai_set_pll(codec_dai, 0, 0, sysclk, mclk_freq);
+
+	ret = snd_soc_dai_set_sysclk(codec_dai, WM8804_TX_CLKSRC_PLL,
+					sysclk, SND_SOC_CLOCK_OUT);
+	if (ret < 0) {
+		dev_err(rtd->card->dev,
+		"Failed to set WM8804 SYSCLK: %d\n", ret);
+		return ret;
+	}
+
+	/* Enable TX output */
+	snd_soc_component_update_bits(digi, WM8804_PWRDN, 0x4, 0x0);
+
+	/* Power on */
+	snd_soc_component_update_bits(digi, WM8804_PWRDN, 0x9, 0);
+
+	/* set sampling frequency status bits */
+	snd_soc_component_update_bits(digi, WM8804_SPDTX4, 0x0f, sampling_freq);
+
+	return snd_soc_dai_set_bclk_ratio(cpu_dai, 64);
+}
+
+static int snd_rpi_justboom_both_startup(struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_component *digi = asoc_rtd_to_codec(rtd, 0)->component;
+	struct snd_soc_component *dac = asoc_rtd_to_codec(rtd, 1)->component;
+
+	/* turn on digital output */
+	snd_soc_component_update_bits(digi, WM8804_PWRDN, 0x3c, 0x00);
+
+	snd_soc_component_update_bits(dac, PCM512x_GPIO_CONTROL_1, 0x08, 0x08);
+
+	return 0;
+}
+
+static void snd_rpi_justboom_both_shutdown(struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_component *digi = asoc_rtd_to_codec(rtd, 0)->component;
+	struct snd_soc_component *dac = asoc_rtd_to_codec(rtd, 1)->component;
+
+	snd_soc_component_update_bits(dac, PCM512x_GPIO_CONTROL_1, 0x08, 0x00);
+
+	/* turn off output */
+	snd_soc_component_update_bits(digi, WM8804_PWRDN, 0x3c, 0x3c);
+}
+
+/* machine stream operations */
+static struct snd_soc_ops snd_rpi_justboom_both_ops = {
+	.hw_params = snd_rpi_justboom_both_hw_params,
+	.startup   = snd_rpi_justboom_both_startup,
+	.shutdown  = snd_rpi_justboom_both_shutdown,
+};
+
+SND_SOC_DAILINK_DEFS(rpi_justboom_both,
+	DAILINK_COMP_ARRAY(COMP_CPU("bcm2708-i2s.0")),
+	DAILINK_COMP_ARRAY(COMP_CODEC("pcm512x.1-004d", "pcm512x-hifi"),
+			   COMP_CODEC("wm8804.1-003b", "wm8804-spdif")),
+	DAILINK_COMP_ARRAY(COMP_PLATFORM("bcm2708-i2s.0")));
+
+static struct snd_soc_dai_link snd_rpi_justboom_both_dai[] = {
+{
+	.name           = "JustBoom Digi",
+	.stream_name    = "JustBoom Digi HiFi",
+	.dai_fmt        = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+					SND_SOC_DAIFMT_CBM_CFM,
+	.ops            = &snd_rpi_justboom_both_ops,
+	.init           = snd_rpi_justboom_both_init,
+	SND_SOC_DAILINK_REG(rpi_justboom_both),
+},
+};
+
+/* audio machine driver */
+static struct snd_soc_card snd_rpi_justboom_both = {
+	.name             = "snd_rpi_justboom_both",
+	.driver_name      = "JustBoomBoth",
+	.owner            = THIS_MODULE,
+	.dai_link         = snd_rpi_justboom_both_dai,
+	.num_links        = ARRAY_SIZE(snd_rpi_justboom_both_dai),
+};
+
+static int snd_rpi_justboom_both_probe(struct platform_device *pdev)
+{
+	int ret = 0;
+	struct snd_soc_card *card = &snd_rpi_justboom_both;
+
+	snd_rpi_justboom_both.dev = &pdev->dev;
+
+	if (pdev->dev.of_node) {
+		struct device_node *i2s_node;
+		struct snd_soc_dai_link *dai = &snd_rpi_justboom_both_dai[0];
+
+		i2s_node = of_parse_phandle(pdev->dev.of_node,
+					    "i2s-controller", 0);
+
+		if (i2s_node) {
+			int i;
+
+			for (i = 0; i < card->num_links; i++) {
+				dai->cpus->dai_name = NULL;
+				dai->cpus->of_node = i2s_node;
+				dai->platforms->name = NULL;
+				dai->platforms->of_node = i2s_node;
+			}
+		}
+
+		digital_gain_0db_limit = !of_property_read_bool(
+			pdev->dev.of_node, "justboom,24db_digital_gain");
+	}
+
+	ret = snd_soc_register_card(card);
+	if (ret && ret != -EPROBE_DEFER) {
+		dev_err(&pdev->dev,
+			"snd_soc_register_card() failed: %d\n", ret);
+	}
+
+	return ret;
+}
+
+static int snd_rpi_justboom_both_remove(struct platform_device *pdev)
+{
+	return snd_soc_unregister_card(&snd_rpi_justboom_both);
+}
+
+static const struct of_device_id snd_rpi_justboom_both_of_match[] = {
+	{ .compatible = "justboom,justboom-both", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, snd_rpi_justboom_both_of_match);
+
+static struct platform_driver snd_rpi_justboom_both_driver = {
+	.driver = {
+		.name   = "snd-rpi-justboom-both",
+		.owner  = THIS_MODULE,
+		.of_match_table = snd_rpi_justboom_both_of_match,
+	},
+	.probe          = snd_rpi_justboom_both_probe,
+	.remove         = snd_rpi_justboom_both_remove,
+};
+
+module_platform_driver(snd_rpi_justboom_both_driver);
+
+MODULE_AUTHOR("Johannes Krude <johannes@krude.de>");
+MODULE_DESCRIPTION("ASoC Driver for simultaneous use of JustBoom PI Digi & DAC HAT Sound Cards");
+MODULE_LICENSE("GPL v2");
diff -Nur --no-dereference linux-5.10.52-orig/sound/soc/bcm/justboom-dac.c linux-5.10.52-v7l+/sound/soc/bcm/justboom-dac.c
--- linux-5.10.52-orig/sound/soc/bcm/justboom-dac.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/sound/soc/bcm/justboom-dac.c	2021-07-25 16:46:40.167717485 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/*
+ * ASoC Driver for JustBoom DAC Raspberry Pi HAT Sound Card
+ *
+ * Author:	Milan Neskovic
+ *		Copyright 2016
+ *		based on code by Daniel Matuschek <info@crazy-audio.com>
+ *		based on code by Florian Meier <florian.meier@koalo.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/jack.h>
+
+#include "../codecs/pcm512x.h"
+
+static bool digital_gain_0db_limit = true;
+
+static int snd_rpi_justboom_dac_init(struct snd_soc_pcm_runtime *rtd)
+{
+	struct snd_soc_component *component = asoc_rtd_to_codec(rtd, 0)->component;
+	snd_soc_component_update_bits(component, PCM512x_GPIO_EN, 0x08, 0x08);
+	snd_soc_component_update_bits(component, PCM512x_GPIO_OUTPUT_4, 0xf, 0x02);
+	snd_soc_component_update_bits(component, PCM512x_GPIO_CONTROL_1, 0x08,0x08);
+
+	if (digital_gain_0db_limit)
+	{
+		int ret;
+		struct snd_soc_card *card = rtd->card;
+
+		ret = snd_soc_limit_volume(card, "Digital Playback Volume", 207);
+		if (ret < 0)
+			dev_warn(card->dev, "Failed to set volume limit: %d\n", ret);
+	}
+
+	return 0;
+}
+
+static int snd_rpi_justboom_dac_startup(struct snd_pcm_substream *substream) {
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_component *component = asoc_rtd_to_codec(rtd, 0)->component;
+	snd_soc_component_update_bits(component, PCM512x_GPIO_CONTROL_1, 0x08,0x08);
+	return 0;
+}
+
+static void snd_rpi_justboom_dac_shutdown(struct snd_pcm_substream *substream) {
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_component *component = asoc_rtd_to_codec(rtd, 0)->component;
+	snd_soc_component_update_bits(component, PCM512x_GPIO_CONTROL_1, 0x08,0x00);
+}
+
+/* machine stream operations */
+static struct snd_soc_ops snd_rpi_justboom_dac_ops = {
+	.startup = snd_rpi_justboom_dac_startup,
+	.shutdown = snd_rpi_justboom_dac_shutdown,
+};
+
+SND_SOC_DAILINK_DEFS(hifi,
+	DAILINK_COMP_ARRAY(COMP_CPU("bcm2708-i2s.0")),
+	DAILINK_COMP_ARRAY(COMP_CODEC("pcm512x.1-004d", "pcm512x-hifi")),
+	DAILINK_COMP_ARRAY(COMP_PLATFORM("bcm2708-i2s.0")));
+
+static struct snd_soc_dai_link snd_rpi_justboom_dac_dai[] = {
+{
+	.name		= "JustBoom DAC",
+	.stream_name	= "JustBoom DAC HiFi",
+	.dai_fmt	= SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+				SND_SOC_DAIFMT_CBS_CFS,
+	.ops		= &snd_rpi_justboom_dac_ops,
+	.init		= snd_rpi_justboom_dac_init,
+	SND_SOC_DAILINK_REG(hifi),
+},
+};
+
+/* audio machine driver */
+static struct snd_soc_card snd_rpi_justboom_dac = {
+	.name         = "snd_rpi_justboom_dac",
+	.driver_name  = "JustBoomDac",
+	.owner        = THIS_MODULE,
+	.dai_link     = snd_rpi_justboom_dac_dai,
+	.num_links    = ARRAY_SIZE(snd_rpi_justboom_dac_dai),
+};
+
+static int snd_rpi_justboom_dac_probe(struct platform_device *pdev)
+{
+	int ret = 0;
+
+	snd_rpi_justboom_dac.dev = &pdev->dev;
+
+	if (pdev->dev.of_node) {
+	    struct device_node *i2s_node;
+	    struct snd_soc_dai_link *dai = &snd_rpi_justboom_dac_dai[0];
+	    i2s_node = of_parse_phandle(pdev->dev.of_node,
+					"i2s-controller", 0);
+
+	    if (i2s_node) {
+			dai->cpus->dai_name = NULL;
+			dai->cpus->of_node = i2s_node;
+			dai->platforms->name = NULL;
+			dai->platforms->of_node = i2s_node;
+	    }
+
+	    digital_gain_0db_limit = !of_property_read_bool(
+			pdev->dev.of_node, "justboom,24db_digital_gain");
+	}
+
+	ret = devm_snd_soc_register_card(&pdev->dev, &snd_rpi_justboom_dac);
+	if (ret && ret != -EPROBE_DEFER)
+		dev_err(&pdev->dev,
+			"snd_soc_register_card() failed: %d\n", ret);
+
+	return ret;
+}
+
+static const struct of_device_id snd_rpi_justboom_dac_of_match[] = {
+	{ .compatible = "justboom,justboom-dac", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, snd_rpi_justboom_dac_of_match);
+
+static struct platform_driver snd_rpi_justboom_dac_driver = {
+	.driver = {
+		.name   = "snd-rpi-justboom-dac",
+		.owner  = THIS_MODULE,
+		.of_match_table = snd_rpi_justboom_dac_of_match,
+	},
+	.probe          = snd_rpi_justboom_dac_probe,
+};
+
+module_platform_driver(snd_rpi_justboom_dac_driver);
+
+MODULE_AUTHOR("Milan Neskovic <info@justboom.co>");
+MODULE_DESCRIPTION("ASoC Driver for JustBoom PI DAC HAT Sound Card");
+MODULE_LICENSE("GPL v2");
diff -Nur --no-dereference linux-5.10.52-orig/sound/soc/bcm/Kconfig linux-5.10.52-v7l+/sound/soc/bcm/Kconfig
--- linux-5.10.52-orig/sound/soc/bcm/Kconfig	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/sound/soc/bcm/Kconfig	2021-07-25 16:46:40.127718155 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:29 @
 	  DSL/PON chips (bcm63158, bcm63178)
 
 	  If you don't know what to do here, say N
+
+config SND_BCM2708_SOC_GOOGLEVOICEHAT_SOUNDCARD
+	tristate "Support for Google voiceHAT soundcard"
+	depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
+	select SND_SOC_VOICEHAT
+	select SND_RPI_SIMPLE_SOUNDCARD
+	help
+          Say Y or M if you want to add support for voiceHAT soundcard.
+
+config SND_BCM2708_SOC_HIFIBERRY_DAC
+        tristate "Support for HifiBerry DAC"
+        depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
+        select SND_SOC_PCM5102A
+        select SND_RPI_SIMPLE_SOUNDCARD
+        help
+         Say Y or M if you want to add support for HifiBerry DAC.
+
+config SND_BCM2708_SOC_HIFIBERRY_DACPLUS
+        tristate "Support for HifiBerry DAC+"
+        depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
+        select SND_SOC_PCM512x
+        select SND_SOC_TPA6130A2
+        select COMMON_CLK_HIFIBERRY_DACPRO
+        help
+         Say Y or M if you want to add support for HifiBerry DAC+.
+
+config SND_BCM2708_SOC_HIFIBERRY_DACPLUSHD
+        tristate "Support for HifiBerry DAC+ HD"
+        depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
+        select SND_SOC_PCM179X_I2C
+        select COMMON_CLK_HIFIBERRY_DACPLUSHD
+        help
+         Say Y or M if you want to add support for HifiBerry DAC+ HD.
+
+config SND_BCM2708_SOC_HIFIBERRY_DACPLUSADC
+        tristate "Support for HifiBerry DAC+ADC"
+        depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
+        select SND_SOC_PCM512x_I2C
+	select SND_SOC_DMIC
+        select COMMON_CLK_HIFIBERRY_DACPRO
+        help
+         Say Y or M if you want to add support for HifiBerry DAC+ADC.
+
+config SND_BCM2708_SOC_HIFIBERRY_DACPLUSADCPRO
+        tristate "Support for HifiBerry DAC+ADC PRO"
+        depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
+        select SND_SOC_PCM512x_I2C
+	select SND_SOC_PCM186X_I2C
+        select COMMON_CLK_HIFIBERRY_DACPRO
+        help
+         Say Y or M if you want to add support for HifiBerry DAC+ADC PRO.
+
+config SND_BCM2708_SOC_HIFIBERRY_DACPLUSDSP
+        tristate "Support for HifiBerry DAC+DSP"
+        depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
+	select SND_RPI_SIMPLE_SOUNDCARD
+        help
+         Say Y or M if you want to add support for HifiBerry DSP-DAC.
+
+config SND_BCM2708_SOC_HIFIBERRY_DIGI
+        tristate "Support for HifiBerry Digi"
+        depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
+        select SND_SOC_WM8804
+        help
+         Say Y or M if you want to add support for HifiBerry Digi S/PDIF output board.
+
+config SND_BCM2708_SOC_HIFIBERRY_AMP
+        tristate "Support for the HifiBerry Amp"
+        depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
+        select SND_SOC_TAS5713
+        select SND_RPI_SIMPLE_SOUNDCARD
+        help
+         Say Y or M if you want to add support for the HifiBerry Amp amplifier board.
+
+ config SND_BCM2708_SOC_PIFI_40
+         tristate "Support for the PiFi-40 amp"
+         depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
+         select SND_SOC_TAS571X
+         select SND_PIFI_40
+         help
+          Say Y or M if you want to add support for the PiFi40 amp board
+
+config SND_BCM2708_SOC_RPI_CIRRUS
+        tristate "Support for Cirrus Logic Audio Card"
+        depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
+        select SND_SOC_WM5102
+        select SND_SOC_WM8804
+        help
+         Say Y or M if you want to add support for the Wolfson and
+         Cirrus Logic audio cards.
+
+config SND_BCM2708_SOC_RPI_DAC
+        tristate "Support for RPi-DAC"
+        depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
+        select SND_SOC_PCM1794A
+        select SND_RPI_SIMPLE_SOUNDCARD
+        help
+         Say Y or M if you want to add support for RPi-DAC.
+
+config SND_BCM2708_SOC_RPI_PROTO
+	tristate "Support for Rpi-PROTO"
+	depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
+	select SND_SOC_WM8731
+	help
+	  Say Y or M if you want to add support for Audio Codec Board PROTO (WM8731).
+
+config SND_BCM2708_SOC_JUSTBOOM_BOTH
+	tristate "Support for simultaneous JustBoom Digi and JustBoom DAC"
+	depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
+	select SND_SOC_WM8804
+	select SND_SOC_PCM512x
+	help
+		Say Y or M if you want to add support for simultaneous
+		JustBoom Digi and JustBoom DAC.
+
+		This is not the right choice if you only have one but both of
+		these cards.
+
+config SND_BCM2708_SOC_JUSTBOOM_DAC
+	tristate "Support for JustBoom DAC"
+	depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
+	select SND_SOC_PCM512x
+	help
+	  Say Y or M if you want to add support for JustBoom DAC.
+
+config SND_BCM2708_SOC_JUSTBOOM_DIGI
+	tristate "Support for JustBoom Digi"
+	depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
+	select SND_SOC_WM8804
+        select SND_RPI_WM8804_SOUNDCARD
+	help
+	  Say Y or M if you want to add support for JustBoom Digi.
+
+config SND_BCM2708_SOC_IQAUDIO_CODEC
+	tristate "Support for IQaudIO-CODEC"
+	depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
+	select SND_SOC_DA7213
+	help
+	  Say Y or M if you want to add support for IQaudIO-CODEC.
+
+config SND_BCM2708_SOC_IQAUDIO_DAC
+	tristate "Support for IQaudIO-DAC"
+	depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
+	select SND_SOC_PCM512x_I2C
+	help
+	  Say Y or M if you want to add support for IQaudIO-DAC.
+
+config SND_BCM2708_SOC_IQAUDIO_DIGI
+	tristate "Support for IQAudIO Digi"
+	depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
+	select SND_SOC_WM8804
+	select SND_RPI_WM8804_SOUNDCARD
+	help
+	  Say Y or M if you want to add support for IQAudIO Digital IO board.
+
+config SND_BCM2708_SOC_I_SABRE_Q2M
+        tristate "Support for Audiophonics I-Sabre Q2M DAC"
+        depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
+        select SND_SOC_I_SABRE_CODEC
+        help
+        Say Y or M if you want to add support for Audiophonics I-SABRE Q2M DAC
+
+config SND_BCM2708_SOC_ADAU1977_ADC
+	tristate "Support for ADAU1977 ADC"
+	depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
+	select SND_SOC_ADAU1977_I2C
+	select SND_RPI_SIMPLE_SOUNDCARD
+	help
+	  Say Y or M if you want to add support for ADAU1977 ADC.
+
+config SND_AUDIOINJECTOR_PI_SOUNDCARD
+	tristate "Support for audioinjector.net Pi add on soundcard"
+	depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
+	select SND_SOC_WM8731
+	help
+	  Say Y or M if you want to add support for audioinjector.net Pi Hat
+
+config SND_AUDIOINJECTOR_OCTO_SOUNDCARD
+	tristate "Support for audioinjector.net Octo channel (Hat) soundcard"
+	depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
+	select SND_SOC_CS42XX8_I2C
+	help
+	  Say Y or M if you want to add support for audioinjector.net octo add on
+
+config SND_AUDIOINJECTOR_ISOLATED_SOUNDCARD
+	tristate "Support for audioinjector.net isolated DAC and ADC soundcard"
+	depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
+	select SND_SOC_CS4271_I2C
+	help
+	  Say Y or M if you want to add support for audioinjector.net isolated soundcard
+
+config SND_AUDIOSENSE_PI
+	tristate "Support for AudioSense Add-On Soundcard"
+	depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
+	select SND_SOC_TLV320AIC32X4_I2C
+	help
+	  Say Y or M if you want to add support for tlv320aic32x4 add-on
+
+config SND_DIGIDAC1_SOUNDCARD
+        tristate "Support for Red Rocks Audio DigiDAC1"
+        depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
+        select SND_SOC_WM8804
+        select SND_SOC_WM8741
+        help
+         Say Y or M if you want to add support for Red Rocks Audio DigiDAC1 board.
+
+config SND_BCM2708_SOC_DIONAUDIO_LOCO
+	tristate "Support for Dion Audio LOCO DAC-AMP"
+	depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
+	select SND_SOC_PCM5102a
+	help
+	  Say Y or M if you want to add support for Dion Audio LOCO.
+
+config SND_BCM2708_SOC_DIONAUDIO_LOCO_V2
+	tristate "Support for Dion Audio LOCO-V2 DAC-AMP"
+	depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
+	select SND_SOC_PCM5122
+	help
+	  Say Y or M if you want to add support for Dion Audio LOCO-V2.
+
+config SND_BCM2708_SOC_ALLO_PIANO_DAC
+	tristate "Support for Allo Piano DAC"
+	depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
+	select SND_SOC_PCM512x_I2C
+	help
+	  Say Y or M if you want to add support for Allo Piano DAC.
+
+config SND_BCM2708_SOC_ALLO_PIANO_DAC_PLUS
+	tristate "Support for Allo Piano DAC Plus"
+	depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
+	select SND_SOC_PCM512x_I2C
+	help
+	  Say Y or M if you want to add support for Allo Piano DAC Plus.
+
+config SND_BCM2708_SOC_ALLO_BOSS_DAC
+	tristate "Support for Allo Boss DAC"
+	depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
+	select SND_SOC_PCM512x_I2C
+	help
+	  Say Y or M if you want to add support for Allo Boss DAC.
+
+config SND_BCM2708_SOC_ALLO_BOSS2_DAC
+	tristate "Support for Allo Boss2 DAC"
+	depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
+	depends on I2C
+	select REGMAP_I2C
+	select SND_AUDIO_GRAPH_CARD
+	help
+	  Say Y or M if you want to add support for Allo Boss2 DAC.
+
+config SND_BCM2708_SOC_ALLO_DIGIONE
+	tristate "Support for Allo DigiOne"
+	depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
+	select SND_SOC_WM8804
+	select SND_RPI_WM8804_SOUNDCARD
+	help
+	  Say Y or M if you want to add support for Allo DigiOne.
+
+config SND_BCM2708_SOC_ALLO_KATANA_DAC
+	tristate "Support for Allo Katana DAC"
+	depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
+	depends on I2C
+	select REGMAP_I2C
+	select SND_AUDIO_GRAPH_CARD
+	help
+	  Say Y or M if you want to add support for Allo Katana DAC.
+
+config SND_BCM2708_SOC_FE_PI_AUDIO
+	tristate "Support for Fe-Pi-Audio"
+	depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
+	select SND_SOC_SGTL5000
+	help
+	  Say Y or M if you want to add support for Fe-Pi-Audio.
+
+config SND_PISOUND
+	tristate "Support for Blokas Labs pisound"
+	depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
+	select SND_RAWMIDI
+	help
+	  Say Y or M if you want to add support for Blokas Labs pisound.
+
+config SND_RPI_SIMPLE_SOUNDCARD
+	tristate "Support for Raspberry Pi simple soundcards"
+	help
+	  Say Y or M if you want to add support Raspbery Pi simple soundcards
+
+config SND_RPI_WM8804_SOUNDCARD
+	tristate "Support for Raspberry Pi generic WM8804 soundcards"
+	help
+	  Say Y or M if you want to add support for the Raspberry Pi
+          generic driver for WM8804 based soundcards.
diff -Nur --no-dereference linux-5.10.52-orig/sound/soc/bcm/Makefile linux-5.10.52-v7l+/sound/soc/bcm/Makefile
--- linux-5.10.52-orig/sound/soc/bcm/Makefile	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/sound/soc/bcm/Makefile	2021-07-25 16:46:40.127718155 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:15 @
 # BCM63XX Platform Support
 snd-soc-63xx-objs := bcm63xx-i2s-whistler.o bcm63xx-pcm-whistler.o
 
-obj-$(CONFIG_SND_BCM63XX_I2S_WHISTLER) += snd-soc-63xx.o
\ No newline at end of file
+obj-$(CONFIG_SND_BCM63XX_I2S_WHISTLER) += snd-soc-63xx.o
+
+# Google voiceHAT custom codec support
+snd-soc-googlevoicehat-codec-objs := googlevoicehat-codec.o
+
+# BCM2708 Machine Support
+snd-soc-hifiberry-dacplus-objs := hifiberry_dacplus.o
+snd-soc-hifiberry-dacplushd-objs := hifiberry_dacplushd.o
+snd-soc-hifiberry-dacplusadc-objs := hifiberry_dacplusadc.o
+snd-soc-hifiberry-dacplusadcpro-objs := hifiberry_dacplusadcpro.o
+snd-soc-hifiberry-dacplusdsp-objs := hifiberry_dacplusdsp.o
+snd-soc-justboom-both-objs := justboom-both.o
+snd-soc-justboom-dac-objs := justboom-dac.o
+snd-soc-rpi-cirrus-objs := rpi-cirrus.o
+snd-soc-rpi-proto-objs := rpi-proto.o
+snd-soc-iqaudio-codec-objs := iqaudio-codec.o
+snd-soc-iqaudio-dac-objs := iqaudio-dac.o
+ snd-soc-i-sabre-q2m-objs := i-sabre-q2m.o
+snd-soc-audioinjector-pi-soundcard-objs := audioinjector-pi-soundcard.o
+snd-soc-audioinjector-octo-soundcard-objs := audioinjector-octo-soundcard.o
+snd-soc-audioinjector-isolated-soundcard-objs := audioinjector-isolated-soundcard.o
+snd-soc-audiosense-pi-objs := audiosense-pi.o
+snd-soc-digidac1-soundcard-objs := digidac1-soundcard.o
+snd-soc-dionaudio-loco-objs := dionaudio_loco.o
+snd-soc-dionaudio-loco-v2-objs := dionaudio_loco-v2.o
+snd-soc-allo-boss-dac-objs := allo-boss-dac.o
+snd-soc-allo-boss2-dac-objs := allo-boss2-dac.o
+snd-soc-allo-piano-dac-objs := allo-piano-dac.o
+snd-soc-allo-piano-dac-plus-objs := allo-piano-dac-plus.o
+snd-soc-allo-katana-codec-objs := allo-katana-codec.o
+snd-soc-pisound-objs := pisound.o
+snd-soc-fe-pi-audio-objs := fe-pi-audio.o
+snd-soc-rpi-simple-soundcard-objs := rpi-simple-soundcard.o
+snd-soc-rpi-wm8804-soundcard-objs := rpi-wm8804-soundcard.o
+snd-soc-pifi-40-objs := pifi-40.o
+
+obj-$(CONFIG_SND_BCM2708_SOC_GOOGLEVOICEHAT_SOUNDCARD)  += snd-soc-googlevoicehat-codec.o
+obj-$(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DACPLUS) += snd-soc-hifiberry-dacplus.o
+obj-$(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DACPLUSHD) += snd-soc-hifiberry-dacplushd.o
+obj-$(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DACPLUSADC) += snd-soc-hifiberry-dacplusadc.o
+obj-$(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DACPLUSADCPRO) += snd-soc-hifiberry-dacplusadcpro.o
+obj-$(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DACPLUSDSP) += snd-soc-hifiberry-dacplusdsp.o
+obj-$(CONFIG_SND_BCM2708_SOC_JUSTBOOM_BOTH) += snd-soc-justboom-both.o
+obj-$(CONFIG_SND_BCM2708_SOC_JUSTBOOM_DAC) += snd-soc-justboom-dac.o
+obj-$(CONFIG_SND_BCM2708_SOC_RPI_CIRRUS) += snd-soc-rpi-cirrus.o
+obj-$(CONFIG_SND_BCM2708_SOC_RPI_PROTO) += snd-soc-rpi-proto.o
+obj-$(CONFIG_SND_BCM2708_SOC_IQAUDIO_CODEC) += snd-soc-iqaudio-codec.o
+obj-$(CONFIG_SND_BCM2708_SOC_IQAUDIO_DAC) += snd-soc-iqaudio-dac.o
+obj-$(CONFIG_SND_BCM2708_SOC_I_SABRE_Q2M) += snd-soc-i-sabre-q2m.o
+obj-$(CONFIG_SND_AUDIOINJECTOR_PI_SOUNDCARD) += snd-soc-audioinjector-pi-soundcard.o
+obj-$(CONFIG_SND_AUDIOINJECTOR_OCTO_SOUNDCARD) += snd-soc-audioinjector-octo-soundcard.o
+obj-$(CONFIG_SND_AUDIOINJECTOR_ISOLATED_SOUNDCARD) += snd-soc-audioinjector-isolated-soundcard.o
+obj-$(CONFIG_SND_AUDIOSENSE_PI) += snd-soc-audiosense-pi.o
+obj-$(CONFIG_SND_DIGIDAC1_SOUNDCARD) += snd-soc-digidac1-soundcard.o
+obj-$(CONFIG_SND_BCM2708_SOC_DIONAUDIO_LOCO) += snd-soc-dionaudio-loco.o
+obj-$(CONFIG_SND_BCM2708_SOC_DIONAUDIO_LOCO_V2) += snd-soc-dionaudio-loco-v2.o
+obj-$(CONFIG_SND_BCM2708_SOC_ALLO_BOSS_DAC) += snd-soc-allo-boss-dac.o
+obj-$(CONFIG_SND_BCM2708_SOC_ALLO_BOSS2_DAC) += snd-soc-allo-boss2-dac.o
+obj-$(CONFIG_SND_BCM2708_SOC_ALLO_PIANO_DAC) += snd-soc-allo-piano-dac.o
+obj-$(CONFIG_SND_BCM2708_SOC_ALLO_PIANO_DAC_PLUS) += snd-soc-allo-piano-dac-plus.o
+obj-$(CONFIG_SND_BCM2708_SOC_ALLO_KATANA_DAC) += snd-soc-allo-katana-codec.o
+obj-$(CONFIG_SND_PISOUND) += snd-soc-pisound.o
+obj-$(CONFIG_SND_BCM2708_SOC_FE_PI_AUDIO) += snd-soc-fe-pi-audio.o
+obj-$(CONFIG_SND_RPI_SIMPLE_SOUNDCARD) += snd-soc-rpi-simple-soundcard.o
+obj-$(CONFIG_SND_RPI_WM8804_SOUNDCARD) += snd-soc-rpi-wm8804-soundcard.o
+obj-$(CONFIG_SND_BCM2708_SOC_PIFI_40) += snd-soc-pifi-40.o
+
diff -Nur --no-dereference linux-5.10.52-orig/sound/soc/bcm/pifi-40.c linux-5.10.52-v7l+/sound/soc/bcm/pifi-40.c
--- linux-5.10.52-orig/sound/soc/bcm/pifi-40.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/sound/soc/bcm/pifi-40.c	2021-07-25 16:46:40.167717485 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * ALSA ASoC Machine Driver for PiFi-40
+ *
+ * Author:	David Knell <david.knell@gmail.com)
+ *		based on code by Daniel Matuschek <info@crazy-audio.com>
+ *		based on code by Florian Meier <florian.meier@koalo.de>
+ * Copyright (C) 2020
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/gpio/consumer.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <linux/firmware.h>
+#include <linux/delay.h>
+#include <sound/tlv.h>
+
+static struct gpio_desc *pdn_gpio;
+static int vol = 0x30;
+
+// Volume control
+static int pifi_40_vol_get(struct snd_kcontrol *kcontrol,
+			   struct snd_ctl_elem_value *ucontrol)
+{
+	ucontrol->value.integer.value[0] = vol;
+	ucontrol->value.integer.value[1] = vol;
+	return 0;
+}
+
+static int pifi_40_vol_set(struct snd_kcontrol *kcontrol,
+			   struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
+	struct snd_soc_pcm_runtime *rtd;
+	unsigned int v = ucontrol->value.integer.value[0];
+	struct snd_soc_component *dac[2];
+
+	rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[0]);
+	dac[0] = asoc_rtd_to_codec(rtd, 0)->component;
+	dac[1] = asoc_rtd_to_codec(rtd, 1)->component;
+
+	snd_soc_component_write(dac[0], 0x07, 255 - v);
+	snd_soc_component_write(dac[1], 0x07, 255 - v);
+
+	vol = v;
+	return 1;
+}
+
+static const DECLARE_TLV_DB_SCALE(digital_tlv_master, -10350, 50, 1);
+static const struct snd_kcontrol_new pifi_40_controls[] = {
+	SOC_DOUBLE_R_EXT_TLV("Master Volume", 0x00, 0x01,
+			     0x00, // Min
+			     0xff, // Max
+			     0x01, // Invert
+			     pifi_40_vol_get, pifi_40_vol_set,
+			     digital_tlv_master)
+};
+
+static const char * const codec_ctl_pfx[] = { "Left", "Right" };
+
+static const char * const codec_ctl_name[] = { "Master Volume",
+					"Speaker Volume",
+					"Speaker Switch" };
+
+static int snd_pifi_40_init(struct snd_soc_pcm_runtime *rtd)
+{
+	struct snd_soc_card *card = rtd->card;
+	struct snd_soc_component *dac[2];
+	struct snd_kcontrol *kctl;
+	int i, j;
+
+	dac[0] = asoc_rtd_to_codec(rtd, 0)->component;
+	dac[1] = asoc_rtd_to_codec(rtd, 1)->component;
+
+
+	// Set up cards - pulse power down first
+	gpiod_set_value_cansleep(pdn_gpio, 1);
+	usleep_range(1000, 10000);
+	gpiod_set_value_cansleep(pdn_gpio, 0);
+	usleep_range(20000, 30000);
+
+	// Oscillator trim
+	snd_soc_component_write(dac[0], 0x1b, 0);
+	snd_soc_component_write(dac[1], 0x1b, 0);
+	usleep_range(60000, 80000);
+
+	// Common setup
+	for (i = 0; i < 2; i++) {
+		// MCLK at 64fs, sample rate 44.1 or 48kHz
+		snd_soc_component_write(dac[i], 0x00, 0x60);
+
+		// Set up for PBTL
+		snd_soc_component_write(dac[i], 0x19, 0x3A);
+		snd_soc_component_write(dac[i], 0x25, 0x01103245);
+
+		// Master vol to -10db
+		snd_soc_component_write(dac[i], 0x07, 0x44);
+	}
+	// Inputs set to L and R respectively
+	snd_soc_component_write(dac[0], 0x20, 0x00017772);
+	snd_soc_component_write(dac[1], 0x20, 0x00107772);
+
+	// Remove codec controls
+	for (i = 0; i < 2; i++) {
+		for (j = 0; j < 3; j++) {
+			char cname[256];
+
+			sprintf(cname, "%s %s", codec_ctl_pfx[i],
+				codec_ctl_name[j]);
+			kctl = snd_soc_card_get_kcontrol(card, cname);
+			if (!kctl) {
+				pr_info("Control %s not found\n",
+				       cname);
+			} else {
+				kctl->vd[0].access =
+					SNDRV_CTL_ELEM_ACCESS_READWRITE;
+				snd_ctl_remove(card->snd_card, kctl);
+			}
+		}
+	}
+
+	return 0;
+}
+
+static int snd_pifi_40_hw_params(struct snd_pcm_substream *substream,
+				 struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
+	unsigned int sample_bits;
+
+	sample_bits = snd_pcm_format_physical_width(params_format(params));
+	return snd_soc_dai_set_bclk_ratio(cpu_dai, 64);
+}
+
+static struct snd_soc_ops snd_pifi_40_ops = { .hw_params =
+						      snd_pifi_40_hw_params };
+
+static struct snd_soc_dai_link_component pifi_40_codecs[] = {
+	{
+		.dai_name = "tas571x-hifi",
+	},
+	{
+		.dai_name = "tas571x-hifi",
+	},
+};
+
+SND_SOC_DAILINK_DEFS(
+	pifi_40_dai, DAILINK_COMP_ARRAY(COMP_EMPTY()),
+	DAILINK_COMP_ARRAY(COMP_CODEC("tas571x.1-001a", "tas571x-hifi"),
+			   COMP_CODEC("tas571x.1-001b", "tas571x-hifi")),
+	DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+static struct snd_soc_dai_link snd_pifi_40_dai[] = {
+	{
+		.name = "PiFi40",
+		.stream_name = "PiFi40",
+		.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+			   SND_SOC_DAIFMT_CBS_CFS,
+		.ops = &snd_pifi_40_ops,
+		.init = snd_pifi_40_init,
+		SND_SOC_DAILINK_REG(pifi_40_dai),
+	},
+};
+
+// Machine driver
+static struct snd_soc_card snd_pifi_40 = {
+	.name = "PiFi40",
+	.owner = THIS_MODULE,
+	.dai_link = snd_pifi_40_dai,
+	.num_links = ARRAY_SIZE(snd_pifi_40_dai),
+	.controls = pifi_40_controls,
+	.num_controls = ARRAY_SIZE(pifi_40_controls)
+};
+
+static void snd_pifi_40_pdn(struct snd_soc_card *card, int on)
+{
+	if (pdn_gpio)
+		gpiod_set_value_cansleep(pdn_gpio, on ? 0 : 1);
+}
+
+static int snd_pifi_40_probe(struct platform_device *pdev)
+{
+	struct snd_soc_card *card = &snd_pifi_40;
+	int ret = 0, i = 0;
+
+	card->dev = &pdev->dev;
+	platform_set_drvdata(pdev, &snd_pifi_40);
+
+	if (pdev->dev.of_node) {
+		struct device_node *i2s_node;
+		struct snd_soc_dai_link *dai;
+
+		dai = &snd_pifi_40_dai[0];
+		i2s_node = of_parse_phandle(pdev->dev.of_node, "i2s-controller",
+					    0);
+		if (i2s_node) {
+			for (i = 0; i < card->num_links; i++) {
+				dai->cpus->dai_name = NULL;
+				dai->cpus->of_node = i2s_node;
+				dai->platforms->name = NULL;
+				dai->platforms->of_node = i2s_node;
+			}
+		}
+
+		pifi_40_codecs[0].of_node =
+			of_parse_phandle(pdev->dev.of_node, "audio-codec", 0);
+		pifi_40_codecs[1].of_node =
+			of_parse_phandle(pdev->dev.of_node, "audio-codec", 1);
+		if (!pifi_40_codecs[0].of_node || !pifi_40_codecs[1].of_node) {
+			dev_err(&pdev->dev,
+				"Property 'audio-codec' missing or invalid\n");
+			return -EINVAL;
+		}
+
+		pdn_gpio = devm_gpiod_get_optional(&pdev->dev, "pdn",
+						   GPIOD_OUT_LOW);
+		if (IS_ERR(pdn_gpio)) {
+			ret = PTR_ERR(pdn_gpio);
+			dev_err(&pdev->dev, "failed to get pdn gpio: %d\n",
+				ret);
+			return ret;
+		}
+
+		ret = snd_soc_register_card(&snd_pifi_40);
+		if (ret < 0) {
+			dev_err(&pdev->dev,
+				"snd_soc_register_card() failed: %d\n", ret);
+			return ret;
+		}
+
+		return 0;
+	}
+
+	return -EINVAL;
+}
+
+static int snd_pifi_40_remove(struct platform_device *pdev)
+{
+	struct snd_soc_card *card = platform_get_drvdata(pdev);
+
+	kfree(&card->drvdata);
+	snd_pifi_40_pdn(&snd_pifi_40, 0);
+	return snd_soc_unregister_card(&snd_pifi_40);
+}
+
+static const struct of_device_id snd_pifi_40_of_match[] = {
+	{
+		.compatible = "pifi,pifi-40",
+	},
+	{ /* sentinel */ },
+};
+
+MODULE_DEVICE_TABLE(of, snd_pifi_40_of_match);
+
+static struct platform_driver snd_pifi_40_driver = {
+	.driver = {
+		.name = "snd-pifi-40",
+		.owner = THIS_MODULE,
+		.of_match_table = snd_pifi_40_of_match,
+	},
+	.probe = snd_pifi_40_probe,
+	.remove = snd_pifi_40_remove,
+};
+
+module_platform_driver(snd_pifi_40_driver);
+
+MODULE_AUTHOR("David Knell <david.knell@gmail.com>");
+MODULE_DESCRIPTION("ALSA ASoC Machine Driver for PiFi-40");
+MODULE_LICENSE("GPL v2");
diff -Nur --no-dereference linux-5.10.52-orig/sound/soc/bcm/pisound.c linux-5.10.52-v7l+/sound/soc/bcm/pisound.c
--- linux-5.10.52-orig/sound/soc/bcm/pisound.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/sound/soc/bcm/pisound.c	2021-07-25 16:46:40.167717485 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/*
+ * Pisound Linux kernel module.
+ * Copyright (C) 2016-2020  Vilniaus Blokas UAB, https://blokas.io/pisound
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; version 2 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA  02110-1301, USA.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <linux/kobject.h>
+#include <linux/sysfs.h>
+#include <linux/delay.h>
+#include <linux/spi/spi.h>
+#include <linux/interrupt.h>
+#include <linux/kfifo.h>
+#include <linux/jiffies.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/jack.h>
+#include <sound/rawmidi.h>
+#include <sound/asequencer.h>
+#include <sound/control.h>
+
+static int pisnd_spi_init(struct device *dev);
+static void pisnd_spi_uninit(void);
+
+static void pisnd_spi_flush(void);
+static void pisnd_spi_start(void);
+static uint8_t pisnd_spi_recv(uint8_t *buffer, uint8_t length);
+
+typedef void (*pisnd_spi_recv_cb)(void *data);
+static void pisnd_spi_set_callback(pisnd_spi_recv_cb cb, void *data);
+
+static const char *pisnd_spi_get_serial(void);
+static const char *pisnd_spi_get_id(void);
+static const char *pisnd_spi_get_fw_version(void);
+static const char *pisnd_spi_get_hw_version(void);
+
+static int pisnd_midi_init(struct snd_card *card);
+static void pisnd_midi_uninit(void);
+
+enum task_e {
+	TASK_PROCESS = 0,
+};
+
+static void pisnd_schedule_process(enum task_e task);
+
+#define PISOUND_LOG_PREFIX "pisound: "
+
+#ifdef PISOUND_DEBUG
+#	define printd(...) pr_alert(PISOUND_LOG_PREFIX __VA_ARGS__)
+#else
+#	define printd(...) do {} while (0)
+#endif
+
+#define printe(...) pr_err(PISOUND_LOG_PREFIX __VA_ARGS__)
+#define printi(...) pr_info(PISOUND_LOG_PREFIX __VA_ARGS__)
+
+static struct snd_rawmidi *g_rmidi;
+static struct snd_rawmidi_substream *g_midi_output_substream;
+
+static int pisnd_output_open(struct snd_rawmidi_substream *substream)
+{
+	g_midi_output_substream = substream;
+	return 0;
+}
+
+static int pisnd_output_close(struct snd_rawmidi_substream *substream)
+{
+	g_midi_output_substream = NULL;
+	return 0;
+}
+
+static void pisnd_output_trigger(
+	struct snd_rawmidi_substream *substream,
+	int up
+	)
+{
+	if (substream != g_midi_output_substream) {
+		printe("MIDI output trigger called for an unexpected stream!");
+		return;
+	}
+
+	if (!up)
+		return;
+
+	pisnd_spi_start();
+}
+
+static void pisnd_output_drain(struct snd_rawmidi_substream *substream)
+{
+	pisnd_spi_flush();
+}
+
+static int pisnd_input_open(struct snd_rawmidi_substream *substream)
+{
+	return 0;
+}
+
+static int pisnd_input_close(struct snd_rawmidi_substream *substream)
+{
+	return 0;
+}
+
+static void pisnd_midi_recv_callback(void *substream)
+{
+	uint8_t data[128];
+	uint8_t n = 0;
+
+	while ((n = pisnd_spi_recv(data, sizeof(data)))) {
+		int res = snd_rawmidi_receive(substream, data, n);
+		(void)res;
+		printd("midi recv %u bytes, res = %d\n", n, res);
+	}
+}
+
+static void pisnd_input_trigger(struct snd_rawmidi_substream *substream, int up)
+{
+	if (up) {
+		pisnd_spi_set_callback(pisnd_midi_recv_callback, substream);
+		pisnd_schedule_process(TASK_PROCESS);
+	} else {
+		pisnd_spi_set_callback(NULL, NULL);
+	}
+}
+
+static struct snd_rawmidi_ops pisnd_output_ops = {
+	.open = pisnd_output_open,
+	.close = pisnd_output_close,
+	.trigger = pisnd_output_trigger,
+	.drain = pisnd_output_drain,
+};
+
+static struct snd_rawmidi_ops pisnd_input_ops = {
+	.open = pisnd_input_open,
+	.close = pisnd_input_close,
+	.trigger = pisnd_input_trigger,
+};
+
+static void pisnd_get_port_info(
+	struct snd_rawmidi *rmidi,
+	int number,
+	struct snd_seq_port_info *seq_port_info
+	)
+{
+	seq_port_info->type =
+		SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC |
+		SNDRV_SEQ_PORT_TYPE_HARDWARE |
+		SNDRV_SEQ_PORT_TYPE_PORT;
+	seq_port_info->midi_voices = 0;
+}
+
+static struct snd_rawmidi_global_ops pisnd_global_ops = {
+	.get_port_info = pisnd_get_port_info,
+};
+
+static int pisnd_midi_init(struct snd_card *card)
+{
+	int err;
+
+	g_midi_output_substream = NULL;
+
+	err = snd_rawmidi_new(card, "pisound MIDI", 0, 1, 1, &g_rmidi);
+
+	if (err < 0) {
+		printe("snd_rawmidi_new failed: %d\n", err);
+		return err;
+	}
+
+	strcpy(g_rmidi->name, "pisound MIDI ");
+	strcat(g_rmidi->name, pisnd_spi_get_serial());
+
+	g_rmidi->info_flags =
+		SNDRV_RAWMIDI_INFO_OUTPUT |
+		SNDRV_RAWMIDI_INFO_INPUT |
+		SNDRV_RAWMIDI_INFO_DUPLEX;
+
+	g_rmidi->ops = &pisnd_global_ops;
+
+	g_rmidi->private_data = (void *)0;
+
+	snd_rawmidi_set_ops(
+		g_rmidi,
+		SNDRV_RAWMIDI_STREAM_OUTPUT,
+		&pisnd_output_ops
+		);
+
+	snd_rawmidi_set_ops(
+		g_rmidi,
+		SNDRV_RAWMIDI_STREAM_INPUT,
+		&pisnd_input_ops
+		);
+
+	return 0;
+}
+
+static void pisnd_midi_uninit(void)
+{
+}
+
+static void *g_recvData;
+static pisnd_spi_recv_cb g_recvCallback;
+
+#define FIFO_SIZE 4096
+
+static char g_serial_num[11];
+static char g_id[25];
+enum { MAX_VERSION_STR_LEN = 6 };
+static char g_fw_version[MAX_VERSION_STR_LEN];
+static char g_hw_version[MAX_VERSION_STR_LEN];
+
+static uint8_t g_ledFlashDuration;
+static bool    g_ledFlashDurationChanged;
+
+DEFINE_KFIFO(spi_fifo_in,  uint8_t, FIFO_SIZE);
+DEFINE_KFIFO(spi_fifo_out, uint8_t, FIFO_SIZE);
+
+static struct gpio_desc *data_available;
+static struct gpio_desc *spi_reset;
+
+static struct spi_device *pisnd_spi_device;
+
+static struct workqueue_struct *pisnd_workqueue;
+static struct work_struct pisnd_work_process;
+
+static void pisnd_work_handler(struct work_struct *work);
+
+static void spi_transfer(const uint8_t *txbuf, uint8_t *rxbuf, int len);
+static uint16_t spi_transfer16(uint16_t val);
+
+static int pisnd_init_workqueues(void)
+{
+	pisnd_workqueue = create_singlethread_workqueue("pisnd_workqueue");
+	INIT_WORK(&pisnd_work_process, pisnd_work_handler);
+
+	return 0;
+}
+
+static void pisnd_uninit_workqueues(void)
+{
+	flush_workqueue(pisnd_workqueue);
+	destroy_workqueue(pisnd_workqueue);
+
+	pisnd_workqueue = NULL;
+}
+
+static bool pisnd_spi_has_more(void)
+{
+	return gpiod_get_value(data_available);
+}
+
+static void pisnd_schedule_process(enum task_e task)
+{
+	if (pisnd_spi_device != NULL &&
+		pisnd_workqueue != NULL &&
+		!work_pending(&pisnd_work_process)
+		) {
+		printd("schedule: has more = %d\n", pisnd_spi_has_more());
+		if (task == TASK_PROCESS)
+			queue_work(pisnd_workqueue, &pisnd_work_process);
+	}
+}
+
+static irqreturn_t data_available_interrupt_handler(int irq, void *dev_id)
+{
+	if (irq == gpiod_to_irq(data_available) && pisnd_spi_has_more()) {
+		printd("schedule from irq\n");
+		pisnd_schedule_process(TASK_PROCESS);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static uint16_t spi_transfer16(uint16_t val)
+{
+	uint8_t txbuf[2];
+	uint8_t rxbuf[2];
+
+	if (!pisnd_spi_device) {
+		printe("pisnd_spi_device null, returning\n");
+		return 0;
+	}
+
+	txbuf[0] = val >> 8;
+	txbuf[1] = val & 0xff;
+
+	spi_transfer(txbuf, rxbuf, sizeof(txbuf));
+
+	printd("received: %02x%02x\n", rxbuf[0], rxbuf[1]);
+
+	return (rxbuf[0] << 8) | rxbuf[1];
+}
+
+static void spi_transfer(const uint8_t *txbuf, uint8_t *rxbuf, int len)
+{
+	int err;
+	struct spi_transfer transfer;
+	struct spi_message msg;
+
+	memset(rxbuf, 0, len);
+
+	if (!pisnd_spi_device) {
+		printe("pisnd_spi_device null, returning\n");
+		return;
+	}
+
+	spi_message_init(&msg);
+
+	memset(&transfer, 0, sizeof(transfer));
+
+	transfer.tx_buf = txbuf;
+	transfer.rx_buf = rxbuf;
+	transfer.len = len;
+	transfer.speed_hz = 150000;
+	transfer.delay_usecs = 10;
+	spi_message_add_tail(&transfer, &msg);
+
+	err = spi_sync(pisnd_spi_device, &msg);
+
+	if (err < 0) {
+		printe("spi_sync error %d\n", err);
+		return;
+	}
+
+	printd("hasMore %d\n", pisnd_spi_has_more());
+}
+
+static int spi_read_bytes(char *dst, size_t length, uint8_t *bytesRead)
+{
+	uint16_t rx;
+	uint8_t size;
+	uint8_t i;
+
+	memset(dst, 0, length);
+	*bytesRead = 0;
+
+	rx = spi_transfer16(0);
+	if (!(rx >> 8))
+		return -EINVAL;
+
+	size = rx & 0xff;
+
+	if (size > length)
+		return -EINVAL;
+
+	for (i = 0; i < size; ++i) {
+		rx = spi_transfer16(0);
+		if (!(rx >> 8))
+			return -EINVAL;
+
+		dst[i] = rx & 0xff;
+	}
+
+	*bytesRead = i;
+
+	return 0;
+}
+
+static int spi_device_match(struct device *dev, const void *data)
+{
+	struct spi_device *spi = container_of(dev, struct spi_device, dev);
+
+	printd("      %s %s %dkHz %d bits mode=0x%02X\n",
+		spi->modalias, dev_name(dev), spi->max_speed_hz/1000,
+		spi->bits_per_word, spi->mode);
+
+	if (strcmp("pisound-spi", spi->modalias) == 0) {
+		printi("\tFound!\n");
+		return 1;
+	}
+
+	printe("\tNot found!\n");
+	return 0;
+}
+
+static struct spi_device *pisnd_spi_find_device(void)
+{
+	struct device *dev;
+
+	printi("Searching for spi device...\n");
+	dev = bus_find_device(&spi_bus_type, NULL, NULL, spi_device_match);
+	if (dev != NULL)
+		return container_of(dev, struct spi_device, dev);
+	else
+		return NULL;
+}
+
+static void pisnd_work_handler(struct work_struct *work)
+{
+	enum { TRANSFER_SIZE = 4 };
+	enum { PISOUND_OUTPUT_BUFFER_SIZE_MILLIBYTES = 127 * 1000 };
+	enum { MIDI_MILLIBYTES_PER_JIFFIE = (3125 * 1000) / HZ };
+	int out_buffer_used_millibytes = 0;
+	unsigned long now;
+	uint8_t val;
+	uint8_t txbuf[TRANSFER_SIZE];
+	uint8_t rxbuf[TRANSFER_SIZE];
+	uint8_t midibuf[TRANSFER_SIZE];
+	int i, n;
+	bool had_data;
+
+	unsigned long last_transfer_at = jiffies;
+
+	if (work == &pisnd_work_process) {
+		if (pisnd_spi_device == NULL)
+			return;
+
+		do {
+			if (g_midi_output_substream &&
+				kfifo_avail(&spi_fifo_out) >= sizeof(midibuf)) {
+
+				n = snd_rawmidi_transmit_peek(
+					g_midi_output_substream,
+					midibuf, sizeof(midibuf)
+				);
+
+				if (n > 0) {
+					for (i = 0; i < n; ++i)
+						kfifo_put(
+							&spi_fifo_out,
+							midibuf[i]
+							);
+					snd_rawmidi_transmit_ack(
+						g_midi_output_substream,
+						i
+						);
+				}
+			}
+
+			had_data = false;
+			memset(txbuf, 0, sizeof(txbuf));
+			for (i = 0; i < sizeof(txbuf) &&
+				((out_buffer_used_millibytes+1000 <
+				PISOUND_OUTPUT_BUFFER_SIZE_MILLIBYTES) ||
+				g_ledFlashDurationChanged);
+				i += 2) {
+
+				val = 0;
+
+				if (g_ledFlashDurationChanged) {
+					txbuf[i+0] = 0xf0;
+					txbuf[i+1] = g_ledFlashDuration;
+					g_ledFlashDuration = 0;
+					g_ledFlashDurationChanged = false;
+				} else if (kfifo_get(&spi_fifo_out, &val)) {
+					txbuf[i+0] = 0x0f;
+					txbuf[i+1] = val;
+					out_buffer_used_millibytes += 1000;
+				}
+			}
+
+			spi_transfer(txbuf, rxbuf, sizeof(txbuf));
+			/* Estimate the Pisound's MIDI output buffer usage, so
+			 * that we don't overflow it. Space in the buffer should
+			 * be becoming available at the UART MIDI byte transfer
+			 * rate.
+			 */
+			now = jiffies;
+			if (now != last_transfer_at) {
+				out_buffer_used_millibytes -=
+					(now - last_transfer_at) *
+					MIDI_MILLIBYTES_PER_JIFFIE;
+				if (out_buffer_used_millibytes < 0)
+					out_buffer_used_millibytes = 0;
+				last_transfer_at = now;
+			}
+
+			for (i = 0; i < sizeof(rxbuf); i += 2) {
+				if (rxbuf[i]) {
+					kfifo_put(&spi_fifo_in, rxbuf[i+1]);
+					if (kfifo_len(&spi_fifo_in) > 16 &&
+						g_recvCallback)
+						g_recvCallback(g_recvData);
+					had_data = true;
+				}
+			}
+		} while (had_data
+			|| !kfifo_is_empty(&spi_fifo_out)
+			|| pisnd_spi_has_more()
+			|| g_ledFlashDurationChanged
+			|| out_buffer_used_millibytes != 0
+			);
+
+		if (!kfifo_is_empty(&spi_fifo_in) && g_recvCallback)
+			g_recvCallback(g_recvData);
+	}
+}
+
+static int pisnd_spi_gpio_init(struct device *dev)
+{
+	spi_reset = gpiod_get_index(dev, "reset", 1, GPIOD_ASIS);
+	data_available = gpiod_get_index(dev, "data_available", 0, GPIOD_ASIS);
+
+	gpiod_direction_output(spi_reset, 1);
+	gpiod_direction_input(data_available);
+
+	/* Reset the slave. */
+	gpiod_set_value(spi_reset, false);
+	mdelay(1);
+	gpiod_set_value(spi_reset, true);
+
+	/* Give time for spi slave to start. */
+	mdelay(64);
+
+	return 0;
+}
+
+static void pisnd_spi_gpio_uninit(void)
+{
+	gpiod_set_value(spi_reset, false);
+	gpiod_put(spi_reset);
+	spi_reset = NULL;
+
+	gpiod_put(data_available);
+	data_available = NULL;
+}
+
+static int pisnd_spi_gpio_irq_init(struct device *dev)
+{
+	return request_threaded_irq(
+		gpiod_to_irq(data_available), NULL,
+		data_available_interrupt_handler,
+		IRQF_TIMER | IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+		"data_available_int",
+		NULL
+		);
+}
+
+static void pisnd_spi_gpio_irq_uninit(void)
+{
+	free_irq(gpiod_to_irq(data_available), NULL);
+}
+
+static int spi_read_info(void)
+{
+	uint16_t tmp;
+	uint8_t count;
+	uint8_t n;
+	uint8_t i;
+	uint8_t j;
+	char buffer[257];
+	int ret;
+	char *p;
+
+	memset(g_serial_num, 0, sizeof(g_serial_num));
+	memset(g_fw_version, 0, sizeof(g_fw_version));
+	strcpy(g_hw_version, "1.0"); // Assume 1.0 hw version.
+	memset(g_id, 0, sizeof(g_id));
+
+	tmp = spi_transfer16(0);
+
+	if (!(tmp >> 8))
+		return -EINVAL;
+
+	count = tmp & 0xff;
+
+	for (i = 0; i < count; ++i) {
+		memset(buffer, 0, sizeof(buffer));
+		ret = spi_read_bytes(buffer, sizeof(buffer)-1, &n);
+
+		if (ret < 0)
+			return ret;
+
+		switch (i) {
+		case 0:
+			if (n != 2)
+				return -EINVAL;
+
+			snprintf(
+				g_fw_version,
+				MAX_VERSION_STR_LEN,
+				"%x.%02x",
+				buffer[0],
+				buffer[1]
+				);
+
+			g_fw_version[MAX_VERSION_STR_LEN-1] = '\0';
+			break;
+		case 3:
+			if (n != 2)
+				return -EINVAL;
+
+			snprintf(
+				g_hw_version,
+				MAX_VERSION_STR_LEN,
+				"%x.%x",
+				buffer[0],
+				buffer[1]
+			);
+
+			g_hw_version[MAX_VERSION_STR_LEN-1] = '\0';
+			break;
+		case 1:
+			if (n >= sizeof(g_serial_num))
+				return -EINVAL;
+
+			memcpy(g_serial_num, buffer, sizeof(g_serial_num));
+			break;
+		case 2:
+			{
+				if (n*2 >= sizeof(g_id))
+					return -EINVAL;
+
+				p = g_id;
+				for (j = 0; j < n; ++j)
+					p += sprintf(p, "%02x", buffer[j]);
+
+				*p = '\0';
+			}
+			break;
+		default:
+			break;
+		}
+	}
+
+	return 0;
+}
+
+static int pisnd_spi_init(struct device *dev)
+{
+	int ret;
+	struct spi_device *spi;
+
+	memset(g_serial_num, 0, sizeof(g_serial_num));
+	memset(g_id, 0, sizeof(g_id));
+	memset(g_fw_version, 0, sizeof(g_fw_version));
+	memset(g_hw_version, 0, sizeof(g_hw_version));
+
+	spi = pisnd_spi_find_device();
+
+	if (spi != NULL) {
+		printd("initializing spi!\n");
+		pisnd_spi_device = spi;
+		ret = spi_setup(pisnd_spi_device);
+	} else {
+		printe("SPI device not found, deferring!\n");
+		return -EPROBE_DEFER;
+	}
+
+	ret = pisnd_spi_gpio_init(dev);
+
+	if (ret < 0) {
+		printe("SPI GPIO init failed: %d\n", ret);
+		spi_dev_put(pisnd_spi_device);
+		pisnd_spi_device = NULL;
+		pisnd_spi_gpio_uninit();
+		return ret;
+	}
+
+	ret = spi_read_info();
+
+	if (ret < 0) {
+		printe("Reading card info failed: %d\n", ret);
+		spi_dev_put(pisnd_spi_device);
+		pisnd_spi_device = NULL;
+		pisnd_spi_gpio_uninit();
+		return ret;
+	}
+
+	/* Flash the LEDs. */
+	spi_transfer16(0xf008);
+
+	ret = pisnd_spi_gpio_irq_init(dev);
+	if (ret < 0) {
+		printe("SPI irq request failed: %d\n", ret);
+		spi_dev_put(pisnd_spi_device);
+		pisnd_spi_device = NULL;
+		pisnd_spi_gpio_irq_uninit();
+		pisnd_spi_gpio_uninit();
+	}
+
+	ret = pisnd_init_workqueues();
+	if (ret != 0) {
+		printe("Workqueue initialization failed: %d\n", ret);
+		spi_dev_put(pisnd_spi_device);
+		pisnd_spi_device = NULL;
+		pisnd_spi_gpio_irq_uninit();
+		pisnd_spi_gpio_uninit();
+		pisnd_uninit_workqueues();
+		return ret;
+	}
+
+	if (pisnd_spi_has_more()) {
+		printd("data is available, scheduling from init\n");
+		pisnd_schedule_process(TASK_PROCESS);
+	}
+
+	return 0;
+}
+
+static void pisnd_spi_uninit(void)
+{
+	pisnd_uninit_workqueues();
+
+	spi_dev_put(pisnd_spi_device);
+	pisnd_spi_device = NULL;
+
+	pisnd_spi_gpio_irq_uninit();
+	pisnd_spi_gpio_uninit();
+}
+
+static void pisnd_spi_flash_leds(uint8_t duration)
+{
+	g_ledFlashDuration = duration;
+	g_ledFlashDurationChanged = true;
+	printd("schedule from spi_flash_leds\n");
+	pisnd_schedule_process(TASK_PROCESS);
+}
+
+static void pisnd_spi_flush(void)
+{
+	while (!kfifo_is_empty(&spi_fifo_out)) {
+		pisnd_spi_start();
+		flush_workqueue(pisnd_workqueue);
+	}
+}
+
+static void pisnd_spi_start(void)
+{
+	printd("schedule from spi_start\n");
+	pisnd_schedule_process(TASK_PROCESS);
+}
+
+static uint8_t pisnd_spi_recv(uint8_t *buffer, uint8_t length)
+{
+	return kfifo_out(&spi_fifo_in, buffer, length);
+}
+
+static void pisnd_spi_set_callback(pisnd_spi_recv_cb cb, void *data)
+{
+	g_recvData = data;
+	g_recvCallback = cb;
+}
+
+static const char *pisnd_spi_get_serial(void)
+{
+	return g_serial_num;
+}
+
+static const char *pisnd_spi_get_id(void)
+{
+	return g_id;
+}
+
+static const char *pisnd_spi_get_fw_version(void)
+{
+	return g_fw_version;
+}
+
+static const char *pisnd_spi_get_hw_version(void)
+{
+	return g_hw_version;
+}
+
+static const struct of_device_id pisound_of_match[] = {
+	{ .compatible = "blokaslabs,pisound", },
+	{ .compatible = "blokaslabs,pisound-spi", },
+	{},
+};
+
+enum {
+	SWITCH = 0,
+	VOLUME = 1,
+};
+
+static int pisnd_ctl_info(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_info *uinfo)
+{
+	if (kcontrol->private_value == SWITCH) {
+		uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+		uinfo->count = 1;
+		uinfo->value.integer.min = 0;
+		uinfo->value.integer.max = 1;
+		return 0;
+	} else if (kcontrol->private_value == VOLUME) {
+		uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+		uinfo->count = 1;
+		uinfo->value.integer.min = 0;
+		uinfo->value.integer.max = 100;
+		return 0;
+	}
+	return -EINVAL;
+}
+
+static int pisnd_ctl_get(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	if (kcontrol->private_value == SWITCH) {
+		ucontrol->value.integer.value[0] = 1;
+		return 0;
+	} else if (kcontrol->private_value == VOLUME) {
+		ucontrol->value.integer.value[0] = 100;
+		return 0;
+	}
+
+	return -EINVAL;
+}
+
+static struct snd_kcontrol_new pisnd_ctl[] = {
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "PCM Playback Switch",
+		.index = 0,
+		.private_value = SWITCH,
+		.access = SNDRV_CTL_ELEM_ACCESS_READ,
+		.info = pisnd_ctl_info,
+		.get = pisnd_ctl_get,
+	},
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "PCM Playback Volume",
+		.index = 0,
+		.private_value = VOLUME,
+		.access = SNDRV_CTL_ELEM_ACCESS_READ,
+		.info = pisnd_ctl_info,
+		.get = pisnd_ctl_get,
+	},
+};
+
+static int pisnd_ctl_init(struct snd_card *card)
+{
+	int err, i;
+
+	for (i = 0; i < ARRAY_SIZE(pisnd_ctl); ++i) {
+		err = snd_ctl_add(card, snd_ctl_new1(&pisnd_ctl[i], NULL));
+		if (err < 0)
+			return err;
+	}
+
+	return 0;
+}
+
+static int pisnd_ctl_uninit(void)
+{
+	return 0;
+}
+
+static struct gpio_desc *osr0, *osr1, *osr2;
+static struct gpio_desc *reset;
+static struct gpio_desc *button;
+
+static int pisnd_hw_params(
+	struct snd_pcm_substream *substream,
+	struct snd_pcm_hw_params *params
+	)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
+
+	/* Pisound runs on fixed 32 clock counts per channel,
+	 * as generated by the master ADC.
+	 */
+	snd_soc_dai_set_bclk_ratio(cpu_dai, 32*2);
+
+	printd("rate   = %d\n", params_rate(params));
+	printd("ch     = %d\n", params_channels(params));
+	printd("bits   = %u\n",
+		snd_pcm_format_physical_width(params_format(params)));
+	printd("format = %d\n", params_format(params));
+
+	gpiod_set_value(reset, false);
+
+	switch (params_rate(params)) {
+	case 48000:
+		gpiod_set_value(osr0, true);
+		gpiod_set_value(osr1, false);
+		gpiod_set_value(osr2, false);
+		break;
+	case 96000:
+		gpiod_set_value(osr0, true);
+		gpiod_set_value(osr1, false);
+		gpiod_set_value(osr2, true);
+		break;
+	case 192000:
+		gpiod_set_value(osr0, true);
+		gpiod_set_value(osr1, true);
+		gpiod_set_value(osr2, true);
+		break;
+	default:
+		printe("Unsupported rate %u!\n", params_rate(params));
+		return -EINVAL;
+	}
+
+	gpiod_set_value(reset, true);
+
+	return 0;
+}
+
+static unsigned int rates[3] = {
+	48000, 96000, 192000
+};
+
+static struct snd_pcm_hw_constraint_list constraints_rates = {
+	.count = ARRAY_SIZE(rates),
+	.list = rates,
+	.mask = 0,
+};
+
+static int pisnd_startup(struct snd_pcm_substream *substream)
+{
+	int err = snd_pcm_hw_constraint_list(
+		substream->runtime,
+		0,
+		SNDRV_PCM_HW_PARAM_RATE,
+		&constraints_rates
+		);
+
+	if (err < 0)
+		return err;
+
+	err = snd_pcm_hw_constraint_single(
+		substream->runtime,
+		SNDRV_PCM_HW_PARAM_CHANNELS,
+		2
+		);
+
+	if (err < 0)
+		return err;
+
+	err = snd_pcm_hw_constraint_mask64(
+		substream->runtime,
+		SNDRV_PCM_HW_PARAM_FORMAT,
+		SNDRV_PCM_FMTBIT_S16_LE |
+		SNDRV_PCM_FMTBIT_S24_LE |
+		SNDRV_PCM_FMTBIT_S32_LE
+		);
+
+	if (err < 0)
+		return err;
+
+	return 0;
+}
+
+static struct snd_soc_ops pisnd_ops = {
+	.startup = pisnd_startup,
+	.hw_params = pisnd_hw_params,
+};
+
+SND_SOC_DAILINK_DEFS(pisnd,
+	DAILINK_COMP_ARRAY(COMP_CPU("bcm2708-i2s.0")),
+	DAILINK_COMP_ARRAY(COMP_DUMMY()),
+	DAILINK_COMP_ARRAY(COMP_PLATFORM("bcm2708-i2s.0")));
+
+static struct snd_soc_dai_link pisnd_dai[] = {
+	{
+		.name           = "pisound",
+		.stream_name    = "pisound",
+		.dai_fmt        =
+			SND_SOC_DAIFMT_I2S |
+			SND_SOC_DAIFMT_NB_NF |
+			SND_SOC_DAIFMT_CBM_CFM,
+		.ops            = &pisnd_ops,
+		SND_SOC_DAILINK_REG(pisnd),
+	},
+};
+
+static int pisnd_card_probe(struct snd_soc_card *card)
+{
+	int err = pisnd_midi_init(card->snd_card);
+
+	if (err < 0) {
+		printe("pisnd_midi_init failed: %d\n", err);
+		return err;
+	}
+
+	err = pisnd_ctl_init(card->snd_card);
+	if (err < 0) {
+		printe("pisnd_ctl_init failed: %d\n", err);
+		return err;
+	}
+
+	return 0;
+}
+
+static int pisnd_card_remove(struct snd_soc_card *card)
+{
+	pisnd_ctl_uninit();
+	pisnd_midi_uninit();
+	return 0;
+}
+
+static struct snd_soc_card pisnd_card = {
+	.name         = "pisound",
+	.owner        = THIS_MODULE,
+	.dai_link     = pisnd_dai,
+	.num_links    = ARRAY_SIZE(pisnd_dai),
+	.probe        = pisnd_card_probe,
+	.remove       = pisnd_card_remove,
+};
+
+static int pisnd_init_gpio(struct device *dev)
+{
+	osr0 = gpiod_get_index(dev, "osr", 0, GPIOD_ASIS);
+	osr1 = gpiod_get_index(dev, "osr", 1, GPIOD_ASIS);
+	osr2 = gpiod_get_index(dev, "osr", 2, GPIOD_ASIS);
+
+	reset = gpiod_get_index(dev, "reset", 0, GPIOD_ASIS);
+
+	button = gpiod_get_index(dev, "button", 0, GPIOD_ASIS);
+
+	gpiod_direction_output(osr0,  1);
+	gpiod_direction_output(osr1,  1);
+	gpiod_direction_output(osr2,  1);
+	gpiod_direction_output(reset, 1);
+
+	gpiod_set_value(reset, false);
+	gpiod_set_value(osr0,   true);
+	gpiod_set_value(osr1,  false);
+	gpiod_set_value(osr2,  false);
+	gpiod_set_value(reset,  true);
+
+	gpiod_export(button, false);
+
+	return 0;
+}
+
+static int pisnd_uninit_gpio(void)
+{
+	int i;
+
+	struct gpio_desc **gpios[] = {
+		&osr0, &osr1, &osr2, &reset, &button,
+	};
+
+	gpiod_unexport(button);
+
+	for (i = 0; i < ARRAY_SIZE(gpios); ++i) {
+		if (*gpios[i] == NULL) {
+			printd("weird, GPIO[%d] is NULL already\n", i);
+			continue;
+		}
+
+		gpiod_put(*gpios[i]);
+		*gpios[i] = NULL;
+	}
+
+	return 0;
+}
+
+static struct kobject *pisnd_kobj;
+
+static ssize_t pisnd_serial_show(
+	struct kobject *kobj,
+	struct kobj_attribute *attr,
+	char *buf
+	)
+{
+	return sprintf(buf, "%s\n", pisnd_spi_get_serial());
+}
+
+static ssize_t pisnd_id_show(
+	struct kobject *kobj,
+	struct kobj_attribute *attr,
+	char *buf
+	)
+{
+	return sprintf(buf, "%s\n", pisnd_spi_get_id());
+}
+
+static ssize_t pisnd_fw_version_show(
+	struct kobject *kobj,
+	struct kobj_attribute *attr,
+	char *buf
+	)
+{
+	return sprintf(buf, "%s\n", pisnd_spi_get_fw_version());
+}
+
+static ssize_t pisnd_hw_version_show(
+	struct kobject *kobj,
+	struct kobj_attribute *attr,
+	char *buf
+)
+{
+	return sprintf(buf, "%s\n", pisnd_spi_get_hw_version());
+}
+
+static ssize_t pisnd_led_store(
+	struct kobject *kobj,
+	struct kobj_attribute *attr,
+	const char *buf,
+	size_t length
+	)
+{
+	uint32_t timeout;
+	int err;
+
+	err = kstrtou32(buf, 10, &timeout);
+
+	if (err == 0 && timeout <= 255)
+		pisnd_spi_flash_leds(timeout);
+
+	return length;
+}
+
+static struct kobj_attribute pisnd_serial_attribute =
+	__ATTR(serial, 0444, pisnd_serial_show, NULL);
+static struct kobj_attribute pisnd_id_attribute =
+	__ATTR(id, 0444, pisnd_id_show, NULL);
+static struct kobj_attribute pisnd_fw_version_attribute =
+	__ATTR(version, 0444, pisnd_fw_version_show, NULL);
+static struct kobj_attribute pisnd_hw_version_attribute =
+__ATTR(hw_version, 0444, pisnd_hw_version_show, NULL);
+static struct kobj_attribute pisnd_led_attribute =
+	__ATTR(led, 0644, NULL, pisnd_led_store);
+
+static struct attribute *attrs[] = {
+	&pisnd_serial_attribute.attr,
+	&pisnd_id_attribute.attr,
+	&pisnd_fw_version_attribute.attr,
+	&pisnd_hw_version_attribute.attr,
+	&pisnd_led_attribute.attr,
+	NULL
+};
+
+static struct attribute_group attr_group = { .attrs = attrs };
+
+static int pisnd_probe(struct platform_device *pdev)
+{
+	int ret = 0;
+	int i;
+
+	ret = pisnd_spi_init(&pdev->dev);
+	if (ret < 0) {
+		printe("pisnd_spi_init failed: %d\n", ret);
+		return ret;
+	}
+
+	printi("Detected Pisound card:\n");
+	printi("\tSerial:           %s\n", pisnd_spi_get_serial());
+	printi("\tFirmware Version: %s\n", pisnd_spi_get_fw_version());
+	printi("\tHardware Version: %s\n", pisnd_spi_get_hw_version());
+	printi("\tId:               %s\n", pisnd_spi_get_id());
+
+	pisnd_kobj = kobject_create_and_add("pisound", kernel_kobj);
+	if (!pisnd_kobj) {
+		pisnd_spi_uninit();
+		return -ENOMEM;
+	}
+
+	ret = sysfs_create_group(pisnd_kobj, &attr_group);
+	if (ret < 0) {
+		pisnd_spi_uninit();
+		kobject_put(pisnd_kobj);
+		return -ENOMEM;
+	}
+
+	pisnd_init_gpio(&pdev->dev);
+	pisnd_card.dev = &pdev->dev;
+
+	if (pdev->dev.of_node) {
+		struct device_node *i2s_node;
+
+		i2s_node = of_parse_phandle(
+			pdev->dev.of_node,
+			"i2s-controller",
+			0
+			);
+
+		for (i = 0; i < pisnd_card.num_links; ++i) {
+			struct snd_soc_dai_link *dai = &pisnd_dai[i];
+
+			if (i2s_node) {
+				dai->cpus->dai_name = NULL;
+				dai->cpus->of_node = i2s_node;
+				dai->platforms->name = NULL;
+				dai->platforms->of_node = i2s_node;
+				dai->stream_name = pisnd_spi_get_serial();
+			}
+		}
+	}
+
+	ret = snd_soc_register_card(&pisnd_card);
+
+	if (ret < 0) {
+		if (ret != -EPROBE_DEFER)
+			printe("snd_soc_register_card() failed: %d\n", ret);
+		pisnd_uninit_gpio();
+		kobject_put(pisnd_kobj);
+		pisnd_spi_uninit();
+	}
+
+	return ret;
+}
+
+static int pisnd_remove(struct platform_device *pdev)
+{
+	printi("Unloading.\n");
+
+	if (pisnd_kobj) {
+		kobject_put(pisnd_kobj);
+		pisnd_kobj = NULL;
+	}
+
+	pisnd_spi_uninit();
+
+	/* Turn off */
+	gpiod_set_value(reset, false);
+	pisnd_uninit_gpio();
+
+	return snd_soc_unregister_card(&pisnd_card);
+}
+
+MODULE_DEVICE_TABLE(of, pisound_of_match);
+
+static struct platform_driver pisnd_driver = {
+	.driver = {
+		.name           = "snd-rpi-pisound",
+		.owner          = THIS_MODULE,
+		.of_match_table = pisound_of_match,
+	},
+	.probe              = pisnd_probe,
+	.remove             = pisnd_remove,
+};
+
+module_platform_driver(pisnd_driver);
+
+MODULE_AUTHOR("Giedrius Trainavicius <giedrius@blokas.io>");
+MODULE_DESCRIPTION("ASoC Driver for Pisound, https://blokas.io/pisound");
+MODULE_LICENSE("GPL v2");
diff -Nur --no-dereference linux-5.10.52-orig/sound/soc/bcm/rpi-cirrus.c linux-5.10.52-v7l+/sound/soc/bcm/rpi-cirrus.c
--- linux-5.10.52-orig/sound/soc/bcm/rpi-cirrus.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/sound/soc/bcm/rpi-cirrus.c	2021-07-25 16:46:40.167717485 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/*
+ * ASoC machine driver for Cirrus Logic Audio Card
+ * (with WM5102 and WM8804 codecs)
+ *
+ * Copyright 2015-2017 Matthias Reichl <hias@horus.com>
+ *
+ * Based on rpi-cirrus-sound-pi driver (c) Wolfson / Cirrus Logic Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+#include <linux/delay.h>
+#include <sound/pcm_params.h>
+
+#include <linux/mfd/arizona/registers.h>
+
+#include "../codecs/wm5102.h"
+#include "../codecs/wm8804.h"
+
+#define WM8804_CLKOUT_HZ 12000000
+
+#define RPI_CIRRUS_DEFAULT_RATE 44100
+#define WM5102_MAX_SYSCLK_1 49152000 /* max sysclk for 4K family */
+#define WM5102_MAX_SYSCLK_2 45158400 /* max sysclk for 11.025K family */
+
+static inline unsigned int calc_sysclk(unsigned int rate)
+{
+	return (rate % 4000) ? WM5102_MAX_SYSCLK_2 : WM5102_MAX_SYSCLK_1;
+}
+
+enum {
+	DAI_WM5102 = 0,
+	DAI_WM8804,
+};
+
+struct rpi_cirrus_priv {
+	/* mutex for synchronzing FLL1 access with DAPM */
+	struct mutex lock;
+	unsigned int card_rate;
+	int sync_path_enable;
+	int fll1_freq; /* negative means RefClock in spdif rx case */
+
+	/* track hw params/free for substreams */
+	unsigned int params_set;
+	unsigned int min_rate_idx, max_rate_idx;
+	unsigned char iec958_status[4];
+};
+
+/* helper functions */
+static inline struct snd_soc_pcm_runtime *get_wm5102_runtime(
+	struct snd_soc_card *card) {
+	return snd_soc_get_pcm_runtime(card, &card->dai_link[DAI_WM5102]);
+}
+
+static inline struct snd_soc_pcm_runtime *get_wm8804_runtime(
+	struct snd_soc_card *card) {
+	return snd_soc_get_pcm_runtime(card, &card->dai_link[DAI_WM8804]);
+}
+
+
+struct rate_info {
+	unsigned int value;
+	char *text;
+};
+
+static struct rate_info min_rates[] = {
+	{     0, "off"},
+	{ 32000, "32kHz"},
+	{ 44100, "44.1kHz"}
+};
+
+#define NUM_MIN_RATES ARRAY_SIZE(min_rates)
+
+static int rpi_cirrus_min_rate_info(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_info *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = 1;
+	uinfo->value.enumerated.items = NUM_MIN_RATES;
+
+	if (uinfo->value.enumerated.item >= NUM_MIN_RATES)
+		uinfo->value.enumerated.item = NUM_MIN_RATES - 1;
+	strcpy(uinfo->value.enumerated.name,
+		min_rates[uinfo->value.enumerated.item].text);
+	return 0;
+}
+
+static int rpi_cirrus_min_rate_get(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
+	struct rpi_cirrus_priv *priv = snd_soc_card_get_drvdata(card);
+
+	ucontrol->value.enumerated.item[0] = priv->min_rate_idx;
+	return 0;
+}
+
+static int rpi_cirrus_min_rate_put(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
+	struct rpi_cirrus_priv *priv = snd_soc_card_get_drvdata(card);
+	int changed = 0;
+
+	if (priv->min_rate_idx != ucontrol->value.enumerated.item[0]) {
+		changed = 1;
+		priv->min_rate_idx = ucontrol->value.enumerated.item[0];
+	}
+
+	return changed;
+}
+
+static struct rate_info max_rates[] = {
+	{     0, "off"},
+	{ 48000, "48kHz"},
+	{ 96000, "96kHz"}
+};
+
+#define NUM_MAX_RATES ARRAY_SIZE(max_rates)
+
+static int rpi_cirrus_max_rate_info(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_info *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = 1;
+	uinfo->value.enumerated.items = NUM_MAX_RATES;
+	if (uinfo->value.enumerated.item >= NUM_MAX_RATES)
+		uinfo->value.enumerated.item = NUM_MAX_RATES - 1;
+	strcpy(uinfo->value.enumerated.name,
+		max_rates[uinfo->value.enumerated.item].text);
+	return 0;
+}
+
+static int rpi_cirrus_max_rate_get(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
+	struct rpi_cirrus_priv *priv = snd_soc_card_get_drvdata(card);
+
+	ucontrol->value.enumerated.item[0] = priv->max_rate_idx;
+	return 0;
+}
+
+static int rpi_cirrus_max_rate_put(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
+	struct rpi_cirrus_priv *priv = snd_soc_card_get_drvdata(card);
+	int changed = 0;
+
+	if (priv->max_rate_idx != ucontrol->value.enumerated.item[0]) {
+		changed = 1;
+		priv->max_rate_idx = ucontrol->value.enumerated.item[0];
+	}
+
+	return changed;
+}
+
+static int rpi_cirrus_spdif_info(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_info *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+	uinfo->count = 1;
+	return 0;
+}
+
+static int rpi_cirrus_spdif_playback_get(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
+	struct rpi_cirrus_priv *priv = snd_soc_card_get_drvdata(card);
+	int i;
+
+	for (i = 0; i < 4; i++)
+		ucontrol->value.iec958.status[i] = priv->iec958_status[i];
+
+	return 0;
+}
+
+static int rpi_cirrus_spdif_playback_put(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
+	struct snd_soc_component *wm8804_component =
+		asoc_rtd_to_codec(get_wm8804_runtime(card), 0)->component;
+	struct rpi_cirrus_priv *priv = snd_soc_card_get_drvdata(card);
+	unsigned char *stat = priv->iec958_status;
+	unsigned char *ctrl_stat = ucontrol->value.iec958.status;
+	unsigned int mask;
+	int i, changed = 0;
+
+	for (i = 0; i < 4; i++) {
+		mask = (i == 3) ? 0x3f : 0xff;
+		if ((ctrl_stat[i] & mask) != (stat[i] & mask)) {
+			changed = 1;
+			stat[i] = ctrl_stat[i] & mask;
+			snd_soc_component_update_bits(wm8804_component,
+				WM8804_SPDTX1 + i, mask, stat[i]);
+		}
+	}
+
+	return changed;
+}
+
+static int rpi_cirrus_spdif_mask_get(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	ucontrol->value.iec958.status[0] = 0xff;
+	ucontrol->value.iec958.status[1] = 0xff;
+	ucontrol->value.iec958.status[2] = 0xff;
+	ucontrol->value.iec958.status[3] = 0x3f;
+
+	return 0;
+}
+
+static int rpi_cirrus_spdif_capture_get(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
+	struct snd_soc_component *wm8804_component =
+		asoc_rtd_to_codec(get_wm8804_runtime(card), 0)->component;
+	unsigned int val, mask;
+	int i;
+
+	for (i = 0; i < 4; i++) {
+		val = snd_soc_component_read(wm8804_component,
+			WM8804_RXCHAN1 + i);
+		mask = (i == 3) ? 0x3f : 0xff;
+		ucontrol->value.iec958.status[i] = val & mask;
+	}
+
+	return 0;
+}
+
+#define SPDIF_FLAG_CTRL(desc, reg, bit, invert) \
+{ \
+		.access =  SNDRV_CTL_ELEM_ACCESS_READ \
+			   | SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
+		.iface =   SNDRV_CTL_ELEM_IFACE_MIXER, \
+		.name =    SNDRV_CTL_NAME_IEC958("", CAPTURE, NONE) \
+				desc " Flag", \
+		.info =    snd_ctl_boolean_mono_info, \
+		.get =     rpi_cirrus_spdif_status_flag_get, \
+		.private_value = \
+			(bit) | ((reg) << 8) | ((invert) << 16) \
+}
+
+static int rpi_cirrus_spdif_status_flag_get(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
+	struct snd_soc_component *wm8804_component =
+		asoc_rtd_to_codec(get_wm8804_runtime(card), 0)->component;
+
+	unsigned int bit = kcontrol->private_value & 0xff;
+	unsigned int reg = (kcontrol->private_value >> 8) & 0xff;
+	unsigned int invert = (kcontrol->private_value >> 16) & 0xff;
+	unsigned int val;
+	bool flag;
+
+	val = snd_soc_component_read(wm8804_component, reg);
+
+	flag = val & (1 << bit);
+
+	ucontrol->value.integer.value[0] = invert ? !flag : flag;
+
+	return 0;
+}
+
+static const char * const recovered_frequency_texts[] = {
+	"176.4/192 kHz",
+	"88.2/96 kHz",
+	"44.1/48 kHz",
+	"32 kHz"
+};
+
+#define NUM_RECOVERED_FREQUENCIES \
+	ARRAY_SIZE(recovered_frequency_texts)
+
+static int rpi_cirrus_recovered_frequency_info(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_info *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = 1;
+	uinfo->value.enumerated.items = NUM_RECOVERED_FREQUENCIES;
+	if (uinfo->value.enumerated.item >= NUM_RECOVERED_FREQUENCIES)
+		uinfo->value.enumerated.item = NUM_RECOVERED_FREQUENCIES - 1;
+	strcpy(uinfo->value.enumerated.name,
+		recovered_frequency_texts[uinfo->value.enumerated.item]);
+	return 0;
+}
+
+static int rpi_cirrus_recovered_frequency_get(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
+	struct snd_soc_component *wm8804_component =
+		asoc_rtd_to_codec(get_wm8804_runtime(card), 0)->component;
+	unsigned int val;
+
+	val = snd_soc_component_read(wm8804_component, WM8804_SPDSTAT);
+
+	ucontrol->value.enumerated.item[0] = (val >> 4) & 0x03;
+	return 0;
+}
+
+static const struct snd_kcontrol_new rpi_cirrus_controls[] = {
+	{
+		.iface =   SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name =    "Min Sample Rate",
+		.info =    rpi_cirrus_min_rate_info,
+		.get =     rpi_cirrus_min_rate_get,
+		.put =     rpi_cirrus_min_rate_put,
+	},
+	{
+		.iface =   SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name =    "Max Sample Rate",
+		.info =    rpi_cirrus_max_rate_info,
+		.get =     rpi_cirrus_max_rate_get,
+		.put =     rpi_cirrus_max_rate_put,
+	},
+	{
+		.iface =   SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name =    SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT),
+		.info =    rpi_cirrus_spdif_info,
+		.get =     rpi_cirrus_spdif_playback_get,
+		.put =     rpi_cirrus_spdif_playback_put,
+	},
+	{
+		.access =  SNDRV_CTL_ELEM_ACCESS_READ
+			   | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+		.iface =   SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name =    SNDRV_CTL_NAME_IEC958("", CAPTURE, DEFAULT),
+		.info =    rpi_cirrus_spdif_info,
+		.get =     rpi_cirrus_spdif_capture_get,
+	},
+	{
+		.access =  SNDRV_CTL_ELEM_ACCESS_READ,
+		.iface =   SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name =    SNDRV_CTL_NAME_IEC958("", PLAYBACK, MASK),
+		.info =    rpi_cirrus_spdif_info,
+		.get =     rpi_cirrus_spdif_mask_get,
+	},
+	{
+		.access =  SNDRV_CTL_ELEM_ACCESS_READ
+			   | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+		.iface =   SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name =    SNDRV_CTL_NAME_IEC958("", CAPTURE, NONE)
+				"Recovered Frequency",
+		.info =    rpi_cirrus_recovered_frequency_info,
+		.get =     rpi_cirrus_recovered_frequency_get,
+	},
+	SPDIF_FLAG_CTRL("Audio", WM8804_SPDSTAT, 0, 1),
+	SPDIF_FLAG_CTRL("Non-PCM", WM8804_SPDSTAT, 1, 0),
+	SPDIF_FLAG_CTRL("Copyright", WM8804_SPDSTAT, 2, 1),
+	SPDIF_FLAG_CTRL("De-Emphasis", WM8804_SPDSTAT, 3, 0),
+	SPDIF_FLAG_CTRL("Lock", WM8804_SPDSTAT, 6, 1),
+	SPDIF_FLAG_CTRL("Invalid", WM8804_INTSTAT, 1, 0),
+	SPDIF_FLAG_CTRL("TransErr", WM8804_INTSTAT, 3, 0),
+};
+
+static const char * const linein_micbias_texts[] = {
+	"off", "on",
+};
+
+static SOC_ENUM_SINGLE_VIRT_DECL(linein_micbias_enum,
+	linein_micbias_texts);
+
+static const struct snd_kcontrol_new linein_micbias_mux =
+	SOC_DAPM_ENUM("Route", linein_micbias_enum);
+
+static int rpi_cirrus_spdif_rx_enable_event(struct snd_soc_dapm_widget *w,
+	struct snd_kcontrol *kcontrol, int event);
+
+const struct snd_soc_dapm_widget rpi_cirrus_dapm_widgets[] = {
+	SND_SOC_DAPM_MIC("DMIC", NULL),
+	SND_SOC_DAPM_MIC("Headset Mic", NULL),
+	SND_SOC_DAPM_INPUT("Line Input"),
+	SND_SOC_DAPM_MIC("Line Input with Micbias", NULL),
+	SND_SOC_DAPM_MUX("Line Input Micbias", SND_SOC_NOPM, 0, 0,
+		&linein_micbias_mux),
+	SND_SOC_DAPM_INPUT("dummy SPDIF in"),
+	SND_SOC_DAPM_PGA_E("dummy SPDIFRX", SND_SOC_NOPM, 0, 0, NULL, 0,
+		rpi_cirrus_spdif_rx_enable_event,
+		SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_INPUT("Dummy Input"),
+	SND_SOC_DAPM_OUTPUT("Dummy Output"),
+};
+
+const struct snd_soc_dapm_route rpi_cirrus_dapm_routes[] = {
+	{ "IN1L", NULL, "Headset Mic" },
+	{ "IN1R", NULL, "Headset Mic" },
+	{ "Headset Mic", NULL, "MICBIAS1" },
+
+	{ "IN2L", NULL, "DMIC" },
+	{ "IN2R", NULL, "DMIC" },
+	{ "DMIC", NULL, "MICBIAS2" },
+
+	{ "IN3L", NULL, "Line Input Micbias" },
+	{ "IN3R", NULL, "Line Input Micbias" },
+
+	{ "Line Input Micbias", "off", "Line Input" },
+	{ "Line Input Micbias", "on", "Line Input with Micbias" },
+
+	/* Make sure MICVDD is enabled, otherwise we get noise */
+	{ "Line Input", NULL, "MICVDD" },
+	{ "Line Input with Micbias", NULL, "MICBIAS3" },
+
+	/* Dummy routes to check whether SPDIF RX is enabled or not */
+	{"dummy SPDIFRX", NULL, "dummy SPDIF in"},
+	{"AIFTX", NULL, "dummy SPDIFRX"},
+
+	/*
+	 * Dummy routes to keep wm5102 from staying off on
+	 * playback/capture if all mixers are off.
+	 */
+	{ "Dummy Output", NULL, "AIF1RX1" },
+	{ "Dummy Output", NULL, "AIF1RX2" },
+	{ "AIF1TX1", NULL, "Dummy Input" },
+	{ "AIF1TX2", NULL, "Dummy Input" },
+};
+
+static int rpi_cirrus_clear_flls(struct snd_soc_card *card,
+	struct snd_soc_component *wm5102_component) {
+
+	int ret1, ret2;
+
+	ret1 = snd_soc_component_set_pll(wm5102_component,
+		WM5102_FLL1, ARIZONA_FLL_SRC_NONE, 0, 0);
+	ret2 = snd_soc_component_set_pll(wm5102_component,
+		WM5102_FLL1_REFCLK, ARIZONA_FLL_SRC_NONE, 0, 0);
+
+	if (ret1) {
+		dev_warn(card->dev,
+			"setting FLL1 to zero failed: %d\n", ret1);
+		return ret1;
+	}
+	if (ret2) {
+		dev_warn(card->dev,
+			"setting FLL1_REFCLK to zero failed: %d\n", ret2);
+		return ret2;
+	}
+	return 0;
+}
+
+static int rpi_cirrus_set_fll(struct snd_soc_card *card,
+	struct snd_soc_component *wm5102_component, unsigned int clk_freq)
+{
+	int ret = snd_soc_component_set_pll(wm5102_component,
+		WM5102_FLL1,
+		ARIZONA_CLK_SRC_MCLK1,
+		WM8804_CLKOUT_HZ,
+		clk_freq);
+	if (ret)
+		dev_err(card->dev, "Failed to set FLL1 to %d: %d\n",
+			clk_freq, ret);
+
+	usleep_range(1000, 2000);
+	return ret;
+}
+
+static int rpi_cirrus_set_fll_refclk(struct snd_soc_card *card,
+	struct snd_soc_component *wm5102_component,
+	unsigned int clk_freq, unsigned int aif2_freq)
+{
+	int ret = snd_soc_component_set_pll(wm5102_component,
+		WM5102_FLL1_REFCLK,
+		ARIZONA_CLK_SRC_MCLK1,
+		WM8804_CLKOUT_HZ,
+		clk_freq);
+	if (ret) {
+		dev_err(card->dev,
+			"Failed to set FLL1_REFCLK to %d: %d\n",
+			clk_freq, ret);
+		return ret;
+	}
+
+	ret = snd_soc_component_set_pll(wm5102_component,
+		WM5102_FLL1,
+		ARIZONA_CLK_SRC_AIF2BCLK,
+		aif2_freq, clk_freq);
+	if (ret)
+		dev_err(card->dev,
+			"Failed to set FLL1 with Sync Clock %d to %d: %d\n",
+			aif2_freq, clk_freq, ret);
+
+	usleep_range(1000, 2000);
+	return ret;
+}
+
+static int rpi_cirrus_spdif_rx_enable_event(struct snd_soc_dapm_widget *w,
+	struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_card *card = w->dapm->card;
+	struct rpi_cirrus_priv *priv = snd_soc_card_get_drvdata(card);
+	struct snd_soc_component *wm5102_component =
+		asoc_rtd_to_codec(get_wm5102_runtime(card), 0)->component;
+
+	unsigned int clk_freq, aif2_freq;
+	int ret = 0;
+
+	switch (event) {
+	case SND_SOC_DAPM_POST_PMU:
+		mutex_lock(&priv->lock);
+
+		/* Enable sync path in case of SPDIF capture use case */
+
+		clk_freq = calc_sysclk(priv->card_rate);
+		aif2_freq = 64 * priv->card_rate;
+
+		dev_dbg(card->dev,
+			"spdif_rx: changing FLL1 to use Ref Clock clk: %d spdif: %d\n",
+			clk_freq, aif2_freq);
+
+		ret = rpi_cirrus_clear_flls(card, wm5102_component);
+		if (ret) {
+			dev_err(card->dev, "spdif_rx: failed to clear FLLs\n");
+			goto out;
+		}
+
+		ret = rpi_cirrus_set_fll_refclk(card, wm5102_component,
+			clk_freq, aif2_freq);
+
+		if (ret) {
+			dev_err(card->dev, "spdif_rx: failed to set FLLs\n");
+			goto out;
+		}
+
+		/* set to negative to indicate we're doing spdif rx */
+		priv->fll1_freq = -clk_freq;
+		priv->sync_path_enable = 1;
+		break;
+
+	case SND_SOC_DAPM_POST_PMD:
+		mutex_lock(&priv->lock);
+		priv->sync_path_enable = 0;
+		break;
+
+	default:
+		return 0;
+	}
+
+out:
+	mutex_unlock(&priv->lock);
+	return ret;
+}
+
+static int rpi_cirrus_set_bias_level(struct snd_soc_card *card,
+	struct snd_soc_dapm_context *dapm,
+	enum snd_soc_bias_level level)
+{
+	struct rpi_cirrus_priv *priv = snd_soc_card_get_drvdata(card);
+	struct snd_soc_pcm_runtime *wm5102_runtime = get_wm5102_runtime(card);
+	struct snd_soc_component *wm5102_component =
+		asoc_rtd_to_codec(wm5102_runtime, 0)->component;
+
+	int ret = 0;
+	unsigned int clk_freq;
+
+	if (dapm->dev != asoc_rtd_to_codec(wm5102_runtime, 0)->dev)
+		return 0;
+
+	switch (level) {
+	case SND_SOC_BIAS_PREPARE:
+		if (dapm->bias_level == SND_SOC_BIAS_ON)
+			break;
+
+		mutex_lock(&priv->lock);
+
+		if (!priv->sync_path_enable) {
+			clk_freq = calc_sysclk(priv->card_rate);
+
+			dev_dbg(card->dev,
+				"set_bias: changing FLL1 from %d to %d\n",
+				priv->fll1_freq, clk_freq);
+
+			ret = rpi_cirrus_set_fll(card,
+				wm5102_component, clk_freq);
+			if (ret)
+				dev_err(card->dev,
+					"set_bias: Failed to set FLL1\n");
+			else
+				priv->fll1_freq = clk_freq;
+		}
+		mutex_unlock(&priv->lock);
+		break;
+	default:
+		break;
+	}
+
+	return ret;
+}
+
+static int rpi_cirrus_set_bias_level_post(struct snd_soc_card *card,
+	struct snd_soc_dapm_context *dapm,
+	enum snd_soc_bias_level level)
+{
+	struct rpi_cirrus_priv *priv = snd_soc_card_get_drvdata(card);
+	struct snd_soc_pcm_runtime *wm5102_runtime = get_wm5102_runtime(card);
+	struct snd_soc_component *wm5102_component =
+		asoc_rtd_to_codec(wm5102_runtime, 0)->component;
+
+	if (dapm->dev != asoc_rtd_to_codec(wm5102_runtime, 0)->dev)
+		return 0;
+
+	switch (level) {
+	case SND_SOC_BIAS_STANDBY:
+		mutex_lock(&priv->lock);
+
+		dev_dbg(card->dev,
+			"set_bias_post: changing FLL1 from %d to off\n",
+				priv->fll1_freq);
+
+		if (rpi_cirrus_clear_flls(card, wm5102_component))
+			dev_err(card->dev,
+				"set_bias_post: failed to clear FLLs\n");
+		else
+			priv->fll1_freq = 0;
+
+		mutex_unlock(&priv->lock);
+
+		break;
+	default:
+		break;
+	}
+
+	return 0;
+}
+
+static int rpi_cirrus_set_wm8804_pll(struct snd_soc_card *card,
+	struct snd_soc_dai *wm8804_dai, unsigned int rate)
+{
+	int ret;
+
+	/* use 256fs */
+	unsigned int clk_freq = rate * 256;
+
+	ret = snd_soc_dai_set_pll(wm8804_dai, 0, 0,
+		WM8804_CLKOUT_HZ, clk_freq);
+	if (ret) {
+		dev_err(card->dev,
+			"Failed to set WM8804 PLL to %d: %d\n", clk_freq, ret);
+		return ret;
+	}
+
+	/* Set MCLK as PLL Output */
+	ret = snd_soc_dai_set_sysclk(wm8804_dai,
+		WM8804_TX_CLKSRC_PLL, clk_freq, 0);
+	if (ret) {
+		dev_err(card->dev,
+			"Failed to set MCLK as PLL Output: %d\n", ret);
+		return ret;
+	}
+
+	return ret;
+}
+
+static int rpi_cirrus_startup(struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_card *card = rtd->card;
+	struct rpi_cirrus_priv *priv = snd_soc_card_get_drvdata(card);
+	unsigned int min_rate = min_rates[priv->min_rate_idx].value;
+	unsigned int max_rate = max_rates[priv->max_rate_idx].value;
+
+	if (min_rate || max_rate) {
+		if (max_rate == 0)
+			max_rate = UINT_MAX;
+
+		dev_dbg(card->dev,
+			"startup: limiting rate to %u-%u\n",
+			min_rate, max_rate);
+
+		snd_pcm_hw_constraint_minmax(substream->runtime,
+			SNDRV_PCM_HW_PARAM_RATE, min_rate, max_rate);
+	}
+
+	return 0;
+}
+
+static struct snd_soc_pcm_stream rpi_cirrus_dai_link2_params = {
+	.formats = SNDRV_PCM_FMTBIT_S24_LE,
+	.channels_min = 2,
+	.channels_max = 2,
+	.rate_min = RPI_CIRRUS_DEFAULT_RATE,
+	.rate_max = RPI_CIRRUS_DEFAULT_RATE,
+};
+
+static int rpi_cirrus_hw_params(struct snd_pcm_substream *substream,
+	struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_card *card = rtd->card;
+	struct rpi_cirrus_priv *priv = snd_soc_card_get_drvdata(card);
+	struct snd_soc_dai *bcm_i2s_dai = asoc_rtd_to_cpu(rtd, 0);
+	struct snd_soc_component *wm5102_component = asoc_rtd_to_codec(rtd, 0)->component;
+	struct snd_soc_dai *wm8804_dai = asoc_rtd_to_codec(get_wm8804_runtime(card), 0);
+
+	int ret;
+
+	unsigned int width = snd_pcm_format_physical_width(
+		params_format(params));
+	unsigned int rate = params_rate(params);
+	unsigned int clk_freq = calc_sysclk(rate);
+
+	mutex_lock(&priv->lock);
+
+	dev_dbg(card->dev, "hw_params: setting rate to %d\n", rate);
+
+	ret = snd_soc_dai_set_bclk_ratio(bcm_i2s_dai, 2 * width);
+	if (ret) {
+		dev_err(card->dev, "set_bclk_ratio failed: %d\n", ret);
+		goto out;
+	}
+
+	ret = snd_soc_dai_set_tdm_slot(asoc_rtd_to_codec(rtd, 0), 0x03, 0x03, 2, width);
+	if (ret) {
+		dev_err(card->dev, "set_tdm_slot failed: %d\n", ret);
+		goto out;
+	}
+
+	/* WM8804 supports sample rates from 32k only */
+	if (rate >= 32000) {
+		ret = rpi_cirrus_set_wm8804_pll(card, wm8804_dai, rate);
+		if (ret)
+			goto out;
+	}
+
+	ret = snd_soc_component_set_sysclk(wm5102_component,
+		ARIZONA_CLK_SYSCLK,
+		ARIZONA_CLK_SRC_FLL1,
+		clk_freq,
+		SND_SOC_CLOCK_IN);
+	if (ret) {
+		dev_err(card->dev, "Failed to set SYSCLK: %d\n", ret);
+		goto out;
+	}
+
+	if ((priv->fll1_freq > 0) && (priv->fll1_freq != clk_freq)) {
+		dev_dbg(card->dev,
+			"hw_params: changing FLL1 from %d to %d\n",
+			priv->fll1_freq, clk_freq);
+
+		if (rpi_cirrus_clear_flls(card, wm5102_component)) {
+			dev_err(card->dev, "hw_params: failed to clear FLLs\n");
+			goto out;
+		}
+
+		if (rpi_cirrus_set_fll(card, wm5102_component, clk_freq)) {
+			dev_err(card->dev, "hw_params: failed to set FLL\n");
+			goto out;
+		}
+
+		priv->fll1_freq = clk_freq;
+	}
+
+	priv->card_rate = rate;
+	rpi_cirrus_dai_link2_params.rate_min = rate;
+	rpi_cirrus_dai_link2_params.rate_max = rate;
+
+	priv->params_set |= 1 << substream->stream;
+
+out:
+	mutex_unlock(&priv->lock);
+
+	return ret;
+}
+
+static int rpi_cirrus_hw_free(struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_card *card = rtd->card;
+	struct rpi_cirrus_priv *priv = snd_soc_card_get_drvdata(card);
+	struct snd_soc_component *wm5102_component = asoc_rtd_to_codec(rtd, 0)->component;
+	int ret;
+	unsigned int old_params_set = priv->params_set;
+
+	priv->params_set &= ~(1 << substream->stream);
+
+	/* disable sysclk if this was the last open stream */
+	if (priv->params_set == 0 && old_params_set) {
+		dev_dbg(card->dev,
+			"hw_free: Setting SYSCLK to Zero\n");
+
+		ret = snd_soc_component_set_sysclk(wm5102_component,
+			ARIZONA_CLK_SYSCLK,
+			ARIZONA_CLK_SRC_FLL1,
+			0,
+			SND_SOC_CLOCK_IN);
+		if (ret)
+			dev_err(card->dev,
+				"hw_free: Failed to set SYSCLK to Zero: %d\n",
+				ret);
+	}
+	return 0;
+}
+
+static int rpi_cirrus_init_wm5102(struct snd_soc_pcm_runtime *rtd)
+{
+	struct snd_soc_component *component = asoc_rtd_to_codec(rtd, 0)->component;
+	int ret;
+
+	/* no 32kHz input, derive it from sysclk if needed  */
+	snd_soc_component_update_bits(component,
+			ARIZONA_CLOCK_32K_1, ARIZONA_CLK_32K_SRC_MASK, 2);
+
+	if (rpi_cirrus_clear_flls(rtd->card, component))
+		dev_warn(rtd->card->dev,
+			"init_wm5102: failed to clear FLLs\n");
+
+	ret = snd_soc_component_set_sysclk(component,
+		ARIZONA_CLK_SYSCLK, ARIZONA_CLK_SRC_FLL1,
+		0, SND_SOC_CLOCK_IN);
+	if (ret) {
+		dev_err(rtd->card->dev,
+			"Failed to set SYSCLK to Zero: %d\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int rpi_cirrus_init_wm8804(struct snd_soc_pcm_runtime *rtd)
+{
+	struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
+	struct snd_soc_component *component = codec_dai->component;
+	struct snd_soc_card *card = rtd->card;
+	struct rpi_cirrus_priv *priv = snd_soc_card_get_drvdata(card);
+	unsigned int val, mask;
+	int i, ret;
+
+	for (i = 0; i < 4; i++) {
+		val = snd_soc_component_read(component,
+			WM8804_SPDTX1 + i);
+		mask = (i == 3) ? 0x3f : 0xff;
+		priv->iec958_status[i] = val & mask;
+	}
+
+	/* Setup for 256fs */
+	ret = snd_soc_dai_set_clkdiv(codec_dai,
+		WM8804_MCLK_DIV, WM8804_MCLKDIV_256FS);
+	if (ret) {
+		dev_err(card->dev,
+			"init_wm8804: Failed to set MCLK_DIV to 256fs: %d\n",
+			ret);
+		return ret;
+	}
+
+	/* Output OSC on CLKOUT */
+	ret = snd_soc_dai_set_sysclk(codec_dai,
+		WM8804_CLKOUT_SRC_OSCCLK, WM8804_CLKOUT_HZ, 0);
+	if (ret)
+		dev_err(card->dev,
+			"init_wm8804: Failed to set CLKOUT as OSC Frequency: %d\n",
+			ret);
+
+	/* Init PLL with default samplerate */
+	ret = rpi_cirrus_set_wm8804_pll(card, codec_dai,
+		RPI_CIRRUS_DEFAULT_RATE);
+	if (ret)
+		dev_err(card->dev,
+			"init_wm8804: Failed to setup PLL for %dHz: %d\n",
+			RPI_CIRRUS_DEFAULT_RATE, ret);
+
+	return ret;
+}
+
+static struct snd_soc_ops rpi_cirrus_ops = {
+	.startup = rpi_cirrus_startup,
+	.hw_params = rpi_cirrus_hw_params,
+	.hw_free = rpi_cirrus_hw_free,
+};
+
+SND_SOC_DAILINK_DEFS(wm5102,
+     DAILINK_COMP_ARRAY(COMP_EMPTY()),
+     DAILINK_COMP_ARRAY(COMP_CODEC("wm5102-codec", "wm5102-aif1")),
+     DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(wm8804,
+     DAILINK_COMP_ARRAY(COMP_CPU("wm5102-aif2")),
+     DAILINK_COMP_ARRAY(COMP_CODEC("wm8804.1-003b", "wm8804-spdif")));
+
+static struct snd_soc_dai_link rpi_cirrus_dai[] = {
+	[DAI_WM5102] = {
+		.name		= "WM5102",
+		.stream_name	= "WM5102 AiFi",
+		.dai_fmt	=   SND_SOC_DAIFMT_I2S
+				  | SND_SOC_DAIFMT_NB_NF
+				  | SND_SOC_DAIFMT_CBM_CFM,
+		.ops		= &rpi_cirrus_ops,
+		.init		= rpi_cirrus_init_wm5102,
+		SND_SOC_DAILINK_REG(wm5102),
+	},
+	[DAI_WM8804] = {
+		.name		= "WM5102 SPDIF",
+		.stream_name	= "SPDIF Tx/Rx",
+		.dai_fmt	=   SND_SOC_DAIFMT_I2S
+				  | SND_SOC_DAIFMT_NB_NF
+				  | SND_SOC_DAIFMT_CBM_CFM,
+		.ignore_suspend = 1,
+		.params		= &rpi_cirrus_dai_link2_params,
+		.init		= rpi_cirrus_init_wm8804,
+		SND_SOC_DAILINK_REG(wm8804),
+	},
+};
+
+
+static int rpi_cirrus_late_probe(struct snd_soc_card *card)
+{
+	struct rpi_cirrus_priv *priv = snd_soc_card_get_drvdata(card);
+	struct snd_soc_pcm_runtime *wm5102_runtime = get_wm5102_runtime(card);
+	struct snd_soc_pcm_runtime *wm8804_runtime = get_wm8804_runtime(card);
+	int ret;
+
+	dev_dbg(card->dev, "iec958_bits: %02x %02x %02x %02x\n",
+		priv->iec958_status[0],
+		priv->iec958_status[1],
+		priv->iec958_status[2],
+		priv->iec958_status[3]);
+
+	ret = snd_soc_dai_set_sysclk(
+		asoc_rtd_to_codec(wm5102_runtime, 0), ARIZONA_CLK_SYSCLK, 0, 0);
+	if (ret) {
+		dev_err(card->dev,
+			"Failed to set WM5102 codec dai clk domain: %d\n", ret);
+		return ret;
+	}
+
+	ret = snd_soc_dai_set_sysclk(
+		asoc_rtd_to_cpu(wm8804_runtime, 0), ARIZONA_CLK_SYSCLK, 0, 0);
+	if (ret)
+		dev_err(card->dev,
+			"Failed to set WM8804 codec dai clk domain: %d\n", ret);
+
+	return ret;
+}
+
+/* audio machine driver */
+static struct snd_soc_card rpi_cirrus_card = {
+	.name			= "RPi-Cirrus",
+	.driver_name		= "RPiCirrus",
+	.owner			= THIS_MODULE,
+	.dai_link		= rpi_cirrus_dai,
+	.num_links		= ARRAY_SIZE(rpi_cirrus_dai),
+	.late_probe		= rpi_cirrus_late_probe,
+	.controls		= rpi_cirrus_controls,
+	.num_controls		= ARRAY_SIZE(rpi_cirrus_controls),
+	.dapm_widgets		= rpi_cirrus_dapm_widgets,
+	.num_dapm_widgets	= ARRAY_SIZE(rpi_cirrus_dapm_widgets),
+	.dapm_routes		= rpi_cirrus_dapm_routes,
+	.num_dapm_routes	= ARRAY_SIZE(rpi_cirrus_dapm_routes),
+	.set_bias_level		= rpi_cirrus_set_bias_level,
+	.set_bias_level_post	= rpi_cirrus_set_bias_level_post,
+};
+
+static int rpi_cirrus_probe(struct platform_device *pdev)
+{
+	int ret = 0;
+	struct rpi_cirrus_priv *priv;
+	struct device_node *i2s_node;
+
+	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	priv->min_rate_idx = 1; /* min samplerate 32kHz */
+	priv->card_rate = RPI_CIRRUS_DEFAULT_RATE;
+
+	mutex_init(&priv->lock);
+
+	snd_soc_card_set_drvdata(&rpi_cirrus_card, priv);
+
+	if (!pdev->dev.of_node)
+		return -ENODEV;
+
+	i2s_node = of_parse_phandle(
+			pdev->dev.of_node, "i2s-controller", 0);
+	if (!i2s_node) {
+		dev_err(&pdev->dev, "i2s-controller missing in DT\n");
+		return -ENODEV;
+	}
+
+	rpi_cirrus_dai[DAI_WM5102].cpus->of_node = i2s_node;
+	rpi_cirrus_dai[DAI_WM5102].platforms->of_node = i2s_node;
+
+	rpi_cirrus_card.dev = &pdev->dev;
+
+	ret = devm_snd_soc_register_card(&pdev->dev, &rpi_cirrus_card);
+	if (ret) {
+		if (ret == -EPROBE_DEFER)
+			dev_dbg(&pdev->dev,
+				"register card requested probe deferral\n");
+		else
+			dev_err(&pdev->dev,
+				"Failed to register card: %d\n", ret);
+	}
+
+	return ret;
+}
+
+static const struct of_device_id rpi_cirrus_of_match[] = {
+	{ .compatible = "wlf,rpi-cirrus", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, rpi_cirrus_of_match);
+
+static struct platform_driver rpi_cirrus_driver = {
+	.driver	= {
+		.name   = "snd-rpi-cirrus",
+		.of_match_table = of_match_ptr(rpi_cirrus_of_match),
+	},
+	.probe	= rpi_cirrus_probe,
+};
+
+module_platform_driver(rpi_cirrus_driver);
+
+MODULE_AUTHOR("Matthias Reichl <hias@horus.com>");
+MODULE_DESCRIPTION("ASoC driver for Cirrus Logic Audio Card");
+MODULE_LICENSE("GPL");
diff -Nur --no-dereference linux-5.10.52-orig/sound/soc/bcm/rpi-proto.c linux-5.10.52-v7l+/sound/soc/bcm/rpi-proto.c
--- linux-5.10.52-orig/sound/soc/bcm/rpi-proto.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/sound/soc/bcm/rpi-proto.c	2021-07-25 16:46:40.167717485 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/*
+ * ASoC driver for PROTO AudioCODEC (with a WM8731)
+ * connected to a Raspberry Pi
+ *
+ * Author:      Florian Meier, <koalo@koalo.de>
+ *	      Copyright 2013
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/jack.h>
+
+#include "../codecs/wm8731.h"
+
+static const unsigned int wm8731_rates_12288000[] = {
+	8000, 32000, 48000, 96000,
+};
+
+static struct snd_pcm_hw_constraint_list wm8731_constraints_12288000 = {
+	.list = wm8731_rates_12288000,
+	.count = ARRAY_SIZE(wm8731_rates_12288000),
+};
+
+static int snd_rpi_proto_startup(struct snd_pcm_substream *substream)
+{
+	/* Setup constraints, because there is a 12.288 MHz XTAL on the board */
+	snd_pcm_hw_constraint_list(substream->runtime, 0,
+				SNDRV_PCM_HW_PARAM_RATE,
+				&wm8731_constraints_12288000);
+	return 0;
+}
+
+static int snd_rpi_proto_hw_params(struct snd_pcm_substream *substream,
+				       struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
+	struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
+	int sysclk = 12288000; /* This is fixed on this board */
+
+	/* Set proto bclk */
+	int ret = snd_soc_dai_set_bclk_ratio(cpu_dai,32*2);
+	if (ret < 0){
+		dev_err(rtd->card->dev,
+				"Failed to set BCLK ratio %d\n", ret);
+		return ret;
+	}
+
+	/* Set proto sysclk */
+	ret = snd_soc_dai_set_sysclk(codec_dai, WM8731_SYSCLK_XTAL,
+			sysclk, SND_SOC_CLOCK_IN);
+	if (ret < 0) {
+		dev_err(rtd->card->dev,
+				"Failed to set WM8731 SYSCLK: %d\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+/* machine stream operations */
+static struct snd_soc_ops snd_rpi_proto_ops = {
+	.startup = snd_rpi_proto_startup,
+	.hw_params = snd_rpi_proto_hw_params,
+};
+
+SND_SOC_DAILINK_DEFS(rpi_proto,
+	DAILINK_COMP_ARRAY(COMP_CPU("bcm2708-i2s.0")),
+	DAILINK_COMP_ARRAY(COMP_CODEC("wm8731.1-001a", "wm8731-hifi")),
+	DAILINK_COMP_ARRAY(COMP_PLATFORM("bcm2708-i2s.0")));
+
+static struct snd_soc_dai_link snd_rpi_proto_dai[] = {
+{
+	.name		= "WM8731",
+	.stream_name	= "WM8731 HiFi",
+	.dai_fmt	= SND_SOC_DAIFMT_I2S
+				| SND_SOC_DAIFMT_NB_NF
+				| SND_SOC_DAIFMT_CBM_CFM,
+	.ops		= &snd_rpi_proto_ops,
+	SND_SOC_DAILINK_REG(rpi_proto),
+},
+};
+
+/* audio machine driver */
+static struct snd_soc_card snd_rpi_proto = {
+	.name		= "snd_rpi_proto",
+	.owner		= THIS_MODULE,
+	.dai_link	= snd_rpi_proto_dai,
+	.num_links	= ARRAY_SIZE(snd_rpi_proto_dai),
+};
+
+static int snd_rpi_proto_probe(struct platform_device *pdev)
+{
+	int ret = 0;
+
+	snd_rpi_proto.dev = &pdev->dev;
+
+	if (pdev->dev.of_node) {
+		struct device_node *i2s_node;
+		struct snd_soc_dai_link *dai = &snd_rpi_proto_dai[0];
+		i2s_node = of_parse_phandle(pdev->dev.of_node,
+				            "i2s-controller", 0);
+
+		if (i2s_node) {
+			dai->cpus->dai_name = NULL;
+			dai->cpus->of_node = i2s_node;
+			dai->platforms->name = NULL;
+			dai->platforms->of_node = i2s_node;
+		}
+	}
+
+	ret = devm_snd_soc_register_card(&pdev->dev, &snd_rpi_proto);
+	if (ret && ret != -EPROBE_DEFER)
+		dev_err(&pdev->dev,
+				"snd_soc_register_card() failed: %d\n", ret);
+
+	return ret;
+}
+
+static const struct of_device_id snd_rpi_proto_of_match[] = {
+	{ .compatible = "rpi,rpi-proto", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, snd_rpi_proto_of_match);
+
+static struct platform_driver snd_rpi_proto_driver = {
+	.driver = {
+		.name   = "snd-rpi-proto",
+		.owner  = THIS_MODULE,
+		.of_match_table = snd_rpi_proto_of_match,
+	},
+	.probe	  = snd_rpi_proto_probe,
+};
+
+module_platform_driver(snd_rpi_proto_driver);
+
+MODULE_AUTHOR("Florian Meier");
+MODULE_DESCRIPTION("ASoC Driver for Raspberry Pi connected to PROTO board (WM8731)");
+MODULE_LICENSE("GPL");
diff -Nur --no-dereference linux-5.10.52-orig/sound/soc/bcm/rpi-simple-soundcard.c linux-5.10.52-v7l+/sound/soc/bcm/rpi-simple-soundcard.c
--- linux-5.10.52-orig/sound/soc/bcm/rpi-simple-soundcard.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/sound/soc/bcm/rpi-simple-soundcard.c	2021-07-25 16:46:40.167717485 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * rpi-simple-soundcard.c -- ALSA SoC Raspberry Pi soundcard.
+ *
+ * Copyright (C) 2018 Raspberry Pi.
+ *
+ * Authors: Tim Gover <tim.gover@raspberrypi.org>
+ *
+ * Based on code:
+ * hifiberry_amp.c, hifiberry_dac.c, rpi-dac.c
+ * by Florian Meier <florian.meier@koalo.de>
+ *
+ * googlevoicehat-soundcard.c
+ * by Peter Malkin <petermalkin@google.com>
+ *
+ * adau1977-adc.c
+ * by Andrey Grodzovsky <andrey2805@gmail.com>
+ *
+ * merus-amp.c
+ * by Ariel Muszkat <ariel.muszkat@gmail.com>
+ *		Jorgen Kragh Jakobsen <jorgen.kraghjakobsen@infineon.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/gpio/consumer.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+/* Parameters for generic RPI functions */
+struct snd_rpi_simple_drvdata {
+	struct snd_soc_dai_link *dai;
+	const char* card_name;
+	unsigned int fixed_bclk_ratio;
+};
+
+static struct snd_soc_card snd_rpi_simple = {
+	.driver_name  = "RPi-simple",
+	.owner        = THIS_MODULE,
+	.dai_link     = NULL,
+	.num_links    = 1, /* Only a single DAI supported at the moment */
+};
+
+static int snd_rpi_simple_init(struct snd_soc_pcm_runtime *rtd)
+{
+	struct snd_rpi_simple_drvdata *drvdata =
+		snd_soc_card_get_drvdata(rtd->card);
+	struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
+
+	if (drvdata->fixed_bclk_ratio > 0)
+		return snd_soc_dai_set_bclk_ratio(cpu_dai,
+				drvdata->fixed_bclk_ratio);
+
+	return 0;
+}
+
+static int pifi_mini_210_init(struct snd_soc_pcm_runtime *rtd)
+{
+	struct snd_soc_component *dac;
+	struct gpio_desc *pdn_gpio, *rst_gpio;
+	struct snd_soc_dai *codec_dai;
+	int ret;
+
+	snd_rpi_simple_init(rtd);
+	codec_dai = asoc_rtd_to_codec(rtd, 0);
+
+	dac = codec_dai[0].component;
+
+	pdn_gpio = devm_gpiod_get_optional(snd_rpi_simple.dev, "pdn",
+						GPIOD_OUT_LOW);
+	if (IS_ERR(pdn_gpio)) {
+		ret = PTR_ERR(pdn_gpio);
+		dev_err(snd_rpi_simple.dev, "failed to get pdn gpio: %d\n", ret);
+		return ret;
+	}
+
+	rst_gpio = devm_gpiod_get_optional(snd_rpi_simple.dev, "rst",
+						GPIOD_OUT_LOW);
+	if (IS_ERR(rst_gpio)) {
+		ret = PTR_ERR(rst_gpio);
+		dev_err(snd_rpi_simple.dev, "failed to get rst gpio: %d\n", ret);
+		return ret;
+	}
+
+	// Set up cards - pulse power down and reset first, then
+	// set up according to datasheet
+	gpiod_set_value_cansleep(pdn_gpio, 1);
+	gpiod_set_value_cansleep(rst_gpio, 1);
+	usleep_range(1000, 10000);
+	gpiod_set_value_cansleep(pdn_gpio, 0);
+	usleep_range(20000, 30000);
+	gpiod_set_value_cansleep(rst_gpio, 0);
+	usleep_range(20000, 30000);
+
+	// Oscillator trim
+	snd_soc_component_write(dac, 0x1b, 0);
+	usleep_range(60000, 80000);
+
+	// MCLK at 64fs, sample rate 44.1 or 48kHz
+	snd_soc_component_write(dac, 0x00, 0x60);
+
+	// Set up for BTL - AD/BD mode - AD is 0x00107772, BD is 0x00987772
+	snd_soc_component_write(dac, 0x20, 0x00107772);
+
+	// End mute
+	snd_soc_component_write(dac, 0x05, 0x00);
+
+	return 0;
+}
+
+static int snd_rpi_simple_hw_params(struct snd_pcm_substream *substream,
+		struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
+	struct snd_rpi_simple_drvdata *drvdata;
+	unsigned int sample_bits;
+
+	drvdata = snd_soc_card_get_drvdata(rtd->card);
+
+	if (drvdata->fixed_bclk_ratio > 0)
+		return 0; // BCLK is configured in .init
+
+	/* The simple drivers just set the bclk_ratio to sample_bits * 2 so
+	 * hard-code this for now. More complex drivers could just replace
+	 * the hw_params routine.
+	 */
+	sample_bits = snd_pcm_format_physical_width(params_format(params));
+	return snd_soc_dai_set_bclk_ratio(cpu_dai, sample_bits * 2);
+}
+
+static struct snd_soc_ops snd_rpi_simple_ops = {
+	.hw_params = snd_rpi_simple_hw_params,
+};
+
+enum adau1977_clk_id {
+	ADAU1977_SYSCLK,
+};
+
+enum adau1977_sysclk_src {
+	ADAU1977_SYSCLK_SRC_MCLK,
+	ADAU1977_SYSCLK_SRC_LRCLK,
+};
+
+static int adau1977_init(struct snd_soc_pcm_runtime *rtd)
+{
+	int ret;
+	struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
+
+	ret = snd_soc_dai_set_tdm_slot(codec_dai, 0, 0, 0, 0);
+	if (ret < 0)
+		return ret;
+
+	return snd_soc_component_set_sysclk(codec_dai->component,
+			ADAU1977_SYSCLK, ADAU1977_SYSCLK_SRC_MCLK,
+			11289600, SND_SOC_CLOCK_IN);
+}
+
+SND_SOC_DAILINK_DEFS(adau1977,
+	DAILINK_COMP_ARRAY(COMP_EMPTY()),
+	DAILINK_COMP_ARRAY(COMP_CODEC("adau1977.1-0011", "adau1977-hifi")),
+	DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+static struct snd_soc_dai_link snd_rpi_adau1977_dai[] = {
+	{
+	.name           = "adau1977",
+	.stream_name    = "ADAU1977",
+	.init           = adau1977_init,
+	.dai_fmt = SND_SOC_DAIFMT_I2S |
+		SND_SOC_DAIFMT_NB_NF |
+		SND_SOC_DAIFMT_CBM_CFM,
+	SND_SOC_DAILINK_REG(adau1977),
+	},
+};
+
+static struct snd_rpi_simple_drvdata drvdata_adau1977 = {
+	.card_name = "snd_rpi_adau1977_adc",
+	.dai       = snd_rpi_adau1977_dai,
+};
+
+SND_SOC_DAILINK_DEFS(gvchat,
+	DAILINK_COMP_ARRAY(COMP_EMPTY()),
+	DAILINK_COMP_ARRAY(COMP_CODEC("voicehat-codec", "voicehat-hifi")),
+	DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+static struct snd_soc_dai_link snd_googlevoicehat_soundcard_dai[] = {
+{
+	.name           = "Google voiceHAT SoundCard",
+	.stream_name    = "Google voiceHAT SoundCard HiFi",
+	.dai_fmt        =  SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+				SND_SOC_DAIFMT_CBS_CFS,
+	SND_SOC_DAILINK_REG(gvchat),
+},
+};
+
+static struct snd_rpi_simple_drvdata drvdata_googlevoicehat = {
+	.card_name = "snd_rpi_googlevoicehat_soundcard",
+	.dai       = snd_googlevoicehat_soundcard_dai,
+};
+
+SND_SOC_DAILINK_DEFS(hifiberry_dacplusdsp,
+	DAILINK_COMP_ARRAY(COMP_EMPTY()),
+	DAILINK_COMP_ARRAY(COMP_CODEC("dacplusdsp-codec", "dacplusdsp-hifi")),
+	DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+static struct snd_soc_dai_link snd_hifiberrydacplusdsp_soundcard_dai[] = {
+{
+	.name           = "Hifiberry DAC+DSP SoundCard",
+	.stream_name    = "Hifiberry DAC+DSP SoundCard HiFi",
+	.dai_fmt        =  SND_SOC_DAIFMT_I2S |
+			   SND_SOC_DAIFMT_NB_NF |
+			   SND_SOC_DAIFMT_CBS_CFS,
+	SND_SOC_DAILINK_REG(hifiberry_dacplusdsp),
+},
+};
+
+static struct snd_rpi_simple_drvdata drvdata_hifiberrydacplusdsp = {
+	.card_name = "snd_rpi_hifiberrydacplusdsp_soundcard",
+	.dai       = snd_hifiberrydacplusdsp_soundcard_dai,
+};
+
+SND_SOC_DAILINK_DEFS(hifiberry_amp,
+	DAILINK_COMP_ARRAY(COMP_EMPTY()),
+	DAILINK_COMP_ARRAY(COMP_CODEC("tas5713.1-001b", "tas5713-hifi")),
+	DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+static struct snd_soc_dai_link snd_hifiberry_amp_dai[] = {
+	{
+		.name           = "HifiBerry AMP",
+		.stream_name    = "HifiBerry AMP HiFi",
+		.dai_fmt        = SND_SOC_DAIFMT_I2S |
+					SND_SOC_DAIFMT_NB_NF |
+					SND_SOC_DAIFMT_CBS_CFS,
+		SND_SOC_DAILINK_REG(hifiberry_amp),
+	},
+};
+
+static struct snd_rpi_simple_drvdata drvdata_hifiberry_amp = {
+	.card_name        = "snd_rpi_hifiberry_amp",
+	.dai              = snd_hifiberry_amp_dai,
+	.fixed_bclk_ratio = 64,
+};
+
+SND_SOC_DAILINK_DEFS(hifiberry_dac,
+	DAILINK_COMP_ARRAY(COMP_EMPTY()),
+	DAILINK_COMP_ARRAY(COMP_CODEC("pcm5102a-codec", "pcm5102a-hifi")),
+	DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+static struct snd_soc_dai_link snd_hifiberry_dac_dai[] = {
+	{
+		.name           = "HifiBerry DAC",
+		.stream_name    = "HifiBerry DAC HiFi",
+		.dai_fmt        = SND_SOC_DAIFMT_I2S |
+					SND_SOC_DAIFMT_NB_NF |
+					SND_SOC_DAIFMT_CBS_CFS,
+		SND_SOC_DAILINK_REG(hifiberry_dac),
+	},
+};
+
+static struct snd_rpi_simple_drvdata drvdata_hifiberry_dac = {
+	.card_name = "snd_rpi_hifiberry_dac",
+	.dai       = snd_hifiberry_dac_dai,
+};
+
+SND_SOC_DAILINK_DEFS(rpi_dac,
+	DAILINK_COMP_ARRAY(COMP_EMPTY()),
+	DAILINK_COMP_ARRAY(COMP_CODEC("pcm1794a-codec", "pcm1794a-hifi")),
+	DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+static struct snd_soc_dai_link snd_rpi_dac_dai[] = {
+{
+	.name		= "RPi-DAC",
+	.stream_name	= "RPi-DAC HiFi",
+	.dai_fmt	= SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+				SND_SOC_DAIFMT_CBS_CFS,
+	SND_SOC_DAILINK_REG(rpi_dac),
+},
+};
+
+static struct snd_rpi_simple_drvdata drvdata_rpi_dac = {
+	.card_name        = "snd_rpi_rpi_dac",
+	.dai              = snd_rpi_dac_dai,
+	.fixed_bclk_ratio = 64,
+};
+
+SND_SOC_DAILINK_DEFS(merus_amp,
+	DAILINK_COMP_ARRAY(COMP_EMPTY()),
+	DAILINK_COMP_ARRAY(COMP_CODEC("ma120x0p.1-0020","ma120x0p-amp")),
+	DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+static struct snd_soc_dai_link snd_merus_amp_dai[] = {
+	{
+		.name           = "MerusAmp",
+		.stream_name    = "Merus Audio Amp",
+		.dai_fmt        = SND_SOC_DAIFMT_I2S |
+					SND_SOC_DAIFMT_NB_NF |
+					SND_SOC_DAIFMT_CBS_CFS,
+		SND_SOC_DAILINK_REG(merus_amp),
+	},
+};
+
+static struct snd_rpi_simple_drvdata drvdata_merus_amp = {
+	.card_name        = "snd_rpi_merus_amp",
+	.dai              = snd_merus_amp_dai,
+	.fixed_bclk_ratio = 64,
+};
+
+SND_SOC_DAILINK_DEFS(pifi_mini_210,
+	DAILINK_COMP_ARRAY(COMP_EMPTY()),
+	DAILINK_COMP_ARRAY(COMP_CODEC("tas571x.1-001a", "tas571x-hifi")),
+	DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+static struct snd_soc_dai_link snd_pifi_mini_210_dai[] = {
+	{
+		.name           = "PiFi Mini 210",
+		.stream_name    = "PiFi Mini 210 HiFi",
+		.init			= pifi_mini_210_init,
+		.dai_fmt        = SND_SOC_DAIFMT_I2S |
+					SND_SOC_DAIFMT_NB_NF |
+					SND_SOC_DAIFMT_CBS_CFS,
+		SND_SOC_DAILINK_REG(pifi_mini_210),
+	},
+};
+
+static struct snd_rpi_simple_drvdata drvdata_pifi_mini_210 = {
+	.card_name        = "snd_pifi_mini_210",
+	.dai              = snd_pifi_mini_210_dai,
+	.fixed_bclk_ratio = 64,
+};
+
+static const struct of_device_id snd_rpi_simple_of_match[] = {
+	{ .compatible = "adi,adau1977-adc",
+		.data = (void *) &drvdata_adau1977 },
+	{ .compatible = "googlevoicehat,googlevoicehat-soundcard",
+		.data = (void *) &drvdata_googlevoicehat },
+	{ .compatible = "hifiberrydacplusdsp,hifiberrydacplusdsp-soundcard",
+		.data = (void *) &drvdata_hifiberrydacplusdsp },
+	{ .compatible = "hifiberry,hifiberry-amp",
+		.data = (void *) &drvdata_hifiberry_amp },
+	{ .compatible = "hifiberry,hifiberry-dac",
+		.data = (void *) &drvdata_hifiberry_dac },
+	{ .compatible = "rpi,rpi-dac", &drvdata_rpi_dac},
+	{ .compatible = "merus,merus-amp",
+		.data = (void *) &drvdata_merus_amp },
+	{ .compatible = "pifi,pifi-mini-210",
+		.data = (void *) &drvdata_pifi_mini_210 },
+	{},
+};
+
+static int snd_rpi_simple_probe(struct platform_device *pdev)
+{
+	int ret = 0;
+	const struct of_device_id *of_id;
+
+	snd_rpi_simple.dev = &pdev->dev;
+	of_id = of_match_node(snd_rpi_simple_of_match, pdev->dev.of_node);
+
+	if (pdev->dev.of_node && of_id->data) {
+		struct device_node *i2s_node;
+		struct snd_rpi_simple_drvdata *drvdata =
+			(struct snd_rpi_simple_drvdata *) of_id->data;
+		struct snd_soc_dai_link *dai = drvdata->dai;
+
+		snd_soc_card_set_drvdata(&snd_rpi_simple, drvdata);
+
+		/* More complex drivers might override individual functions */
+		if (!dai->init)
+			dai->init = snd_rpi_simple_init;
+		if (!dai->ops)
+			dai->ops = &snd_rpi_simple_ops;
+
+		snd_rpi_simple.name = drvdata->card_name;
+
+		snd_rpi_simple.dai_link = dai;
+		i2s_node = of_parse_phandle(pdev->dev.of_node,
+				"i2s-controller", 0);
+		if (!i2s_node) {
+			pr_err("Failed to find i2s-controller DT node\n");
+			return -ENODEV;
+		}
+
+		dai->cpus->of_node = i2s_node;
+		dai->platforms->of_node = i2s_node;
+	}
+
+	ret = devm_snd_soc_register_card(&pdev->dev, &snd_rpi_simple);
+	if (ret && ret != -EPROBE_DEFER)
+		dev_err(&pdev->dev, "Failed to register card %d\n", ret);
+
+	return ret;
+}
+
+static struct platform_driver snd_rpi_simple_driver = {
+	.driver = {
+		.name   = "snd-rpi-simple",
+		.owner  = THIS_MODULE,
+		.of_match_table = snd_rpi_simple_of_match,
+	},
+	.probe          = snd_rpi_simple_probe,
+};
+MODULE_DEVICE_TABLE(of, snd_rpi_simple_of_match);
+
+module_platform_driver(snd_rpi_simple_driver);
+
+MODULE_AUTHOR("Tim Gover <tim.gover@raspberrypi.org>");
+MODULE_DESCRIPTION("ASoC Raspberry Pi simple soundcard driver ");
+MODULE_LICENSE("GPL v2");
diff -Nur --no-dereference linux-5.10.52-orig/sound/soc/bcm/rpi-wm8804-soundcard.c linux-5.10.52-v7l+/sound/soc/bcm/rpi-wm8804-soundcard.c
--- linux-5.10.52-orig/sound/soc/bcm/rpi-wm8804-soundcard.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/sound/soc/bcm/rpi-wm8804-soundcard.c	2021-07-25 16:46:40.167717485 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * rpi--wm8804.c -- ALSA SoC Raspberry Pi soundcard.
+ *
+ * Copyright (C) 2018 Raspberry Pi.
+ *
+ * Authors: Tim Gover <tim.gover@raspberrypi.org>
+ *
+ * Generic driver for Pi Hat WM8804 digi sounds cards
+ *
+ * Based upon code from:
+ * justboom-digi.c
+ * by Milan Neskovic <info@justboom.co>
+ *
+ * iqaudio_digi.c
+ * by Daniel Matuschek <info@crazy-audio.com>
+ *
+ * allo-digione.c
+ * by Baswaraj <jaikumar@cem-solutions.net>
+ *
+ * hifiberry-digi.c
+ * Daniel Matuschek <info@crazy-audio.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+
+#include <linux/gpio/consumer.h>
+#include <linux/platform_device.h>
+#include <linux/module.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#include "../codecs/wm8804.h"
+
+struct wm8804_clk_cfg {
+	unsigned int sysclk_freq;
+	unsigned int mclk_freq;
+	unsigned int mclk_div;
+};
+
+/* Parameters for generic functions */
+struct snd_rpi_wm8804_drvdata {
+	/* Required - pointer to the DAI structure */
+	struct snd_soc_dai_link *dai;
+	/* Required - snd_soc_card name */
+	const char *card_name;
+	/* Optional DT node names if card info is defined in DT */
+	const char *card_name_dt;
+	const char *dai_name_dt;
+	const char *dai_stream_name_dt;
+	/* Optional probe extension - called prior to register_card */
+	int (*probe)(struct platform_device *pdev);
+};
+
+static struct gpio_desc *snd_clk44gpio;
+static struct gpio_desc *snd_clk48gpio;
+static int wm8804_samplerate = 0;
+
+/* Forward declarations */
+static struct snd_soc_dai_link snd_allo_digione_dai[];
+static struct snd_soc_card snd_rpi_wm8804;
+
+
+#define CLK_44EN_RATE 22579200UL
+#define CLK_48EN_RATE 24576000UL
+
+static unsigned int snd_rpi_wm8804_enable_clock(unsigned int samplerate)
+{
+	switch (samplerate) {
+	case 11025:
+	case 22050:
+	case 44100:
+	case 88200:
+	case 176400:
+		gpiod_set_value_cansleep(snd_clk44gpio, 1);
+		gpiod_set_value_cansleep(snd_clk48gpio, 0);
+		return CLK_44EN_RATE;
+	default:
+		gpiod_set_value_cansleep(snd_clk48gpio, 1);
+		gpiod_set_value_cansleep(snd_clk44gpio, 0);
+		return CLK_48EN_RATE;
+	}
+}
+
+static void snd_rpi_wm8804_clk_cfg(unsigned int samplerate,
+		struct wm8804_clk_cfg *clk_cfg)
+{
+	clk_cfg->sysclk_freq = 27000000;
+
+	if (samplerate <= 96000 ||
+	    snd_rpi_wm8804.dai_link == snd_allo_digione_dai) {
+		clk_cfg->mclk_freq = samplerate * 256;
+		clk_cfg->mclk_div = WM8804_MCLKDIV_256FS;
+	} else {
+		clk_cfg->mclk_freq = samplerate * 128;
+		clk_cfg->mclk_div = WM8804_MCLKDIV_128FS;
+	}
+
+	if (!(IS_ERR(snd_clk44gpio) || IS_ERR(snd_clk48gpio)))
+		clk_cfg->sysclk_freq = snd_rpi_wm8804_enable_clock(samplerate);
+}
+
+static int snd_rpi_wm8804_hw_params(struct snd_pcm_substream *substream,
+		struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
+	struct snd_soc_component *component = asoc_rtd_to_codec(rtd, 0)->component;
+	struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
+	int sampling_freq = 1;
+	int ret;
+	struct wm8804_clk_cfg clk_cfg;
+	int samplerate = params_rate(params);
+
+	if (samplerate == wm8804_samplerate)
+		return 0;
+
+	/* clear until all clocks are setup properly */
+	wm8804_samplerate = 0;
+
+	snd_rpi_wm8804_clk_cfg(samplerate, &clk_cfg);
+
+	pr_debug("%s samplerate: %d mclk_freq: %u mclk_div: %u sysclk: %u\n",
+			__func__, samplerate, clk_cfg.mclk_freq,
+			clk_cfg.mclk_div, clk_cfg.sysclk_freq);
+
+	switch (samplerate) {
+	case 32000:
+		sampling_freq = 0x03;
+		break;
+	case 44100:
+		sampling_freq = 0x00;
+		break;
+	case 48000:
+		sampling_freq = 0x02;
+		break;
+	case 88200:
+		sampling_freq = 0x08;
+		break;
+	case 96000:
+		sampling_freq = 0x0a;
+		break;
+	case 176400:
+		sampling_freq = 0x0c;
+		break;
+	case 192000:
+		sampling_freq = 0x0e;
+		break;
+	default:
+		dev_err(rtd->card->dev,
+		"Failed to set WM8804 SYSCLK, unsupported samplerate %d\n",
+		samplerate);
+	}
+
+	snd_soc_dai_set_clkdiv(codec_dai, WM8804_MCLK_DIV, clk_cfg.mclk_div);
+	snd_soc_dai_set_pll(codec_dai, 0, 0,
+			clk_cfg.sysclk_freq, clk_cfg.mclk_freq);
+
+	ret = snd_soc_dai_set_sysclk(codec_dai, WM8804_TX_CLKSRC_PLL,
+			clk_cfg.sysclk_freq, SND_SOC_CLOCK_OUT);
+	if (ret < 0) {
+		dev_err(rtd->card->dev,
+		"Failed to set WM8804 SYSCLK: %d\n", ret);
+		return ret;
+	}
+
+	wm8804_samplerate = samplerate;
+
+	/* set sampling frequency status bits */
+	snd_soc_component_update_bits(component, WM8804_SPDTX4, 0x0f,
+			sampling_freq);
+
+	return snd_soc_dai_set_bclk_ratio(cpu_dai, 64);
+}
+
+static struct snd_soc_ops snd_rpi_wm8804_ops = {
+	.hw_params = snd_rpi_wm8804_hw_params,
+};
+
+SND_SOC_DAILINK_DEFS(justboom_digi,
+	DAILINK_COMP_ARRAY(COMP_EMPTY()),
+	DAILINK_COMP_ARRAY(COMP_EMPTY()),
+	DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+static struct snd_soc_dai_link snd_justboom_digi_dai[] = {
+{
+	.name        = "JustBoom Digi",
+	.stream_name = "JustBoom Digi HiFi",
+	SND_SOC_DAILINK_REG(justboom_digi),
+},
+};
+
+static struct snd_rpi_wm8804_drvdata drvdata_justboom_digi = {
+	.card_name            = "snd_rpi_justboom_digi",
+	.dai                  = snd_justboom_digi_dai,
+};
+
+SND_SOC_DAILINK_DEFS(iqaudio_digi,
+	DAILINK_COMP_ARRAY(COMP_EMPTY()),
+	DAILINK_COMP_ARRAY(COMP_EMPTY()),
+	DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+static struct snd_soc_dai_link snd_iqaudio_digi_dai[] = {
+{
+	.name        = "IQAudIO Digi",
+	.stream_name = "IQAudIO Digi HiFi",
+	SND_SOC_DAILINK_REG(iqaudio_digi),
+},
+};
+
+static struct snd_rpi_wm8804_drvdata drvdata_iqaudio_digi = {
+	.card_name          = "IQAudIODigi",
+	.dai                = snd_iqaudio_digi_dai,
+	.card_name_dt       = "wm8804-digi,card-name",
+	.dai_name_dt        = "wm8804-digi,dai-name",
+	.dai_stream_name_dt = "wm8804-digi,dai-stream-name",
+};
+
+static int snd_allo_digione_probe(struct platform_device *pdev)
+{
+	pr_debug("%s\n", __func__);
+
+	if (IS_ERR(snd_clk44gpio) || IS_ERR(snd_clk48gpio)) {
+		dev_err(&pdev->dev, "devm_gpiod_get() failed\n");
+		return -EINVAL;
+	}
+	return 0;
+}
+
+SND_SOC_DAILINK_DEFS(allo_digione,
+	DAILINK_COMP_ARRAY(COMP_EMPTY()),
+	DAILINK_COMP_ARRAY(COMP_EMPTY()),
+	DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+static struct snd_soc_dai_link snd_allo_digione_dai[] = {
+{
+	.name        = "Allo DigiOne",
+	.stream_name = "Allo DigiOne HiFi",
+	SND_SOC_DAILINK_REG(allo_digione),
+},
+};
+
+static struct snd_rpi_wm8804_drvdata drvdata_allo_digione = {
+	.card_name = "snd_allo_digione",
+	.dai       = snd_allo_digione_dai,
+	.probe     = snd_allo_digione_probe,
+};
+
+SND_SOC_DAILINK_DEFS(hifiberry_digi,
+	DAILINK_COMP_ARRAY(COMP_EMPTY()),
+	DAILINK_COMP_ARRAY(COMP_EMPTY()),
+	DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+static struct snd_soc_dai_link snd_hifiberry_digi_dai[] = {
+{
+	.name        = "HifiBerry Digi",
+	.stream_name = "HifiBerry Digi HiFi",
+	SND_SOC_DAILINK_REG(hifiberry_digi),
+},
+};
+
+static int snd_hifiberry_digi_probe(struct platform_device *pdev)
+{
+	pr_debug("%s\n", __func__);
+
+	if (IS_ERR(snd_clk44gpio) || IS_ERR(snd_clk48gpio))
+		return 0;
+
+	snd_hifiberry_digi_dai->name = "HiFiBerry Digi+ Pro";
+	snd_hifiberry_digi_dai->stream_name = "HiFiBerry Digi+ Pro HiFi";
+	return 0;
+}
+
+static struct snd_rpi_wm8804_drvdata drvdata_hifiberry_digi = {
+	.card_name = "snd_rpi_hifiberry_digi",
+	.dai       = snd_hifiberry_digi_dai,
+	.probe     = snd_hifiberry_digi_probe,
+};
+
+static const struct of_device_id snd_rpi_wm8804_of_match[] = {
+	{ .compatible = "justboom,justboom-digi",
+		.data = (void *) &drvdata_justboom_digi },
+	{ .compatible = "iqaudio,wm8804-digi",
+		.data = (void *) &drvdata_iqaudio_digi },
+	{ .compatible = "allo,allo-digione",
+		.data = (void *) &drvdata_allo_digione },
+	{ .compatible = "hifiberry,hifiberry-digi",
+		.data = (void *) &drvdata_hifiberry_digi },
+	{},
+};
+
+static struct snd_soc_card snd_rpi_wm8804 = {
+	.driver_name  = "RPi-WM8804",
+	.owner        = THIS_MODULE,
+	.dai_link     = NULL,
+	.num_links    = 1,
+};
+
+static int snd_rpi_wm8804_probe(struct platform_device *pdev)
+{
+	int ret = 0;
+	const struct of_device_id *of_id;
+
+	snd_rpi_wm8804.dev = &pdev->dev;
+	of_id = of_match_node(snd_rpi_wm8804_of_match, pdev->dev.of_node);
+
+	if (pdev->dev.of_node && of_id->data) {
+		struct device_node *i2s_node;
+		struct snd_rpi_wm8804_drvdata *drvdata =
+			(struct snd_rpi_wm8804_drvdata *) of_id->data;
+		struct snd_soc_dai_link *dai = drvdata->dai;
+
+		snd_soc_card_set_drvdata(&snd_rpi_wm8804, drvdata);
+
+		if (!dai->ops)
+			dai->ops = &snd_rpi_wm8804_ops;
+		if (!dai->codecs->dai_name)
+			dai->codecs->dai_name = "wm8804-spdif";
+		if (!dai->codecs->name)
+			dai->codecs->name = "wm8804.1-003b";
+		if (!dai->dai_fmt)
+			dai->dai_fmt = SND_SOC_DAIFMT_I2S |
+				SND_SOC_DAIFMT_NB_NF |
+				SND_SOC_DAIFMT_CBM_CFM;
+
+		snd_rpi_wm8804.dai_link = dai;
+		i2s_node = of_parse_phandle(pdev->dev.of_node,
+				"i2s-controller", 0);
+		if (!i2s_node) {
+			pr_err("Failed to find i2s-controller DT node\n");
+			return -ENODEV;
+		}
+
+		snd_rpi_wm8804.name = drvdata->card_name;
+
+		/* If requested by in drvdata get card & DAI names from DT */
+		if (drvdata->card_name_dt)
+			of_property_read_string(i2s_node,
+					drvdata->card_name_dt,
+					&snd_rpi_wm8804.name);
+
+		if (drvdata->dai_name_dt)
+			of_property_read_string(i2s_node,
+					drvdata->dai_name_dt,
+					&dai->name);
+
+		if (drvdata->dai_stream_name_dt)
+			of_property_read_string(i2s_node,
+					drvdata->dai_stream_name_dt,
+					&dai->stream_name);
+
+		dai->cpus->of_node = i2s_node;
+		dai->platforms->of_node = i2s_node;
+
+		/*
+		 * clk44gpio and clk48gpio are not required by all cards so
+		 * don't check the error status.
+		 */
+		snd_clk44gpio =
+			devm_gpiod_get(&pdev->dev, "clock44", GPIOD_OUT_LOW);
+
+		snd_clk48gpio =
+			devm_gpiod_get(&pdev->dev, "clock48", GPIOD_OUT_LOW);
+
+		if (drvdata->probe) {
+			ret = drvdata->probe(pdev);
+			if (ret < 0) {
+				dev_err(&pdev->dev, "Custom probe failed %d\n",
+						ret);
+				return ret;
+			}
+		}
+
+		pr_debug("%s card: %s dai: %s stream: %s\n", __func__,
+				snd_rpi_wm8804.name,
+				dai->name, dai->stream_name);
+	}
+
+	ret = devm_snd_soc_register_card(&pdev->dev, &snd_rpi_wm8804);
+	if (ret && ret != -EPROBE_DEFER)
+		dev_err(&pdev->dev, "Failed to register card %d\n", ret);
+
+	return ret;
+}
+
+static struct platform_driver snd_rpi_wm8804_driver = {
+	.driver = {
+		.name           = "snd-rpi-wm8804",
+		.owner          = THIS_MODULE,
+		.of_match_table = snd_rpi_wm8804_of_match,
+	},
+	.probe  = snd_rpi_wm8804_probe,
+};
+MODULE_DEVICE_TABLE(of, snd_rpi_wm8804_of_match);
+
+module_platform_driver(snd_rpi_wm8804_driver);
+
+MODULE_AUTHOR("Tim Gover <tim.gover@raspberrypi.org>");
+MODULE_DESCRIPTION("ASoC Raspberry Pi Hat generic digi driver for WM8804 based cards");
+MODULE_LICENSE("GPL v2");
diff -Nur --no-dereference linux-5.10.52-orig/sound/soc/codecs/cs42xx8.c linux-5.10.52-v7l+/sound/soc/codecs/cs42xx8.c
--- linux-5.10.52-orig/sound/soc/codecs/cs42xx8.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/sound/soc/codecs/cs42xx8.c	2021-07-25 16:46:40.317714970 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:520 @
 	{ .compatible = "cirrus,cs42888", .data = &cs42888_data, },
 	{ /* sentinel */ }
 };
+#if !IS_ENABLED(CONFIG_SND_SOC_CS42XX8_I2C)
 MODULE_DEVICE_TABLE(of, cs42xx8_of_match);
 EXPORT_SYMBOL_GPL(cs42xx8_of_match);
+#endif
 
 int cs42xx8_probe(struct device *dev, struct regmap *regmap)
 {
diff -Nur --no-dereference linux-5.10.52-orig/sound/soc/codecs/cs42xx8-i2c.c linux-5.10.52-v7l+/sound/soc/codecs/cs42xx8-i2c.c
--- linux-5.10.52-orig/sound/soc/codecs/cs42xx8-i2c.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/sound/soc/codecs/cs42xx8-i2c.c	2021-07-25 16:46:40.297715305 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:48 @
 };
 MODULE_DEVICE_TABLE(i2c, cs42xx8_i2c_id);
 
+const struct of_device_id cs42xx8_i2c_of_match[] = {
+	{ .compatible = "cirrus,cs42448", .data = &cs42448_data, },
+	{ .compatible = "cirrus,cs42888", .data = &cs42888_data, },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, cs42xx8_i2c_of_match);
+
 static struct i2c_driver cs42xx8_i2c_driver = {
 	.driver = {
 		.name = "cs42xx8",
 		.pm = &cs42xx8_pm,
-		.of_match_table = cs42xx8_of_match,
+		.of_match_table = cs42xx8_i2c_of_match,
 	},
 	.probe = cs42xx8_i2c_probe,
 	.remove = cs42xx8_i2c_remove,
diff -Nur --no-dereference linux-5.10.52-orig/sound/soc/codecs/hdmi-codec.c linux-5.10.52-v7l+/sound/soc/codecs/hdmi-codec.c
--- linux-5.10.52-orig/sound/soc/codecs/hdmi-codec.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/sound/soc/codecs/hdmi-codec.c	2021-07-25 16:46:40.407713461 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:281 @
 	bool busy;
 	struct snd_soc_jack *jack;
 	unsigned int jack_status;
+	u8 iec_status[5];
 };
 
 static const struct snd_soc_dapm_widget hdmi_widgets[] = {
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:389 @
 	return 0;
 }
 
+static int hdmi_codec_iec958_info(struct snd_kcontrol *kcontrol,
+				  struct snd_ctl_elem_info *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+	uinfo->count = 1;
+	return 0;
+}
+
+static int hdmi_codec_iec958_default_get(struct snd_kcontrol *kcontrol,
+					 struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+	struct hdmi_codec_priv *hcp = snd_soc_component_get_drvdata(component);
+
+	memcpy(ucontrol->value.iec958.status, hcp->iec_status,
+	       sizeof(hcp->iec_status));
+
+	return 0;
+}
+
+static int hdmi_codec_iec958_default_put(struct snd_kcontrol *kcontrol,
+					 struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+	struct hdmi_codec_priv *hcp = snd_soc_component_get_drvdata(component);
+
+	memcpy(hcp->iec_status, ucontrol->value.iec958.status,
+	       sizeof(hcp->iec_status));
+
+	return 0;
+}
+
+static int hdmi_codec_iec958_mask_get(struct snd_kcontrol *kcontrol,
+				      struct snd_ctl_elem_value *ucontrol)
+{
+	memset(ucontrol->value.iec958.status, 0xff,
+	       sizeof_field(struct hdmi_codec_priv, iec_status));
+
+	return 0;
+}
+
 static int hdmi_codec_startup(struct snd_pcm_substream *substream,
 			      struct snd_soc_dai *dai)
 {
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:483 @
 	mutex_unlock(&hcp->lock);
 }
 
+static int hdmi_codec_fill_codec_params(struct snd_soc_dai *dai,
+					unsigned int sample_width,
+					unsigned int sample_rate,
+					unsigned int channels,
+					struct hdmi_codec_params *hp)
+{
+	struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai);
+	int idx;
+
+	/* Select a channel allocation that matches with ELD and pcm channels */
+	idx = hdmi_codec_get_ch_alloc_table_idx(hcp, channels);
+	if (idx < 0) {
+		dev_err(dai->dev, "Not able to map channels to speakers (%d)\n",
+			idx);
+		hcp->chmap_idx = HDMI_CODEC_CHMAP_IDX_UNKNOWN;
+		return idx;
+	}
+
+	memset(hp, 0, sizeof(*hp));
+
+	hdmi_audio_infoframe_init(&hp->cea);
+	hp->cea.channels = channels;
+	hp->cea.coding_type = HDMI_AUDIO_CODING_TYPE_STREAM;
+	hp->cea.sample_size = HDMI_AUDIO_SAMPLE_SIZE_STREAM;
+	hp->cea.sample_frequency = HDMI_AUDIO_SAMPLE_FREQUENCY_STREAM;
+	hp->cea.channel_allocation = hdmi_codec_channel_alloc[idx].ca_id;
+
+	hp->sample_width = sample_width;
+	hp->sample_rate = sample_rate;
+	hp->channels = channels;
+
+	hcp->chmap_idx = hdmi_codec_channel_alloc[idx].ca_id;
+
+	return 0;
+}
+
 static int hdmi_codec_hw_params(struct snd_pcm_substream *substream,
 				struct snd_pcm_hw_params *params,
 				struct snd_soc_dai *dai)
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:533 @
 			.dig_subframe = { 0 },
 		}
 	};
-	int ret, idx;
+	int ret;
+
+	if (!hcp->hcd.ops->hw_params)
+		return 0;
 
 	dev_dbg(dai->dev, "%s() width %d rate %d channels %d\n", __func__,
 		params_width(params), params_rate(params),
 		params_channels(params));
 
-	ret = snd_pcm_create_iec958_consumer_hw_params(params, hp.iec.status,
-						       sizeof(hp.iec.status));
+	ret = hdmi_codec_fill_codec_params(dai,
+					   params_width(params),
+					   params_rate(params),
+					   params_channels(params),
+					   &hp);
+	if (ret < 0)
+		return ret;
+
+	memcpy(hp.iec.status, hcp->iec_status, sizeof(hp.iec.status));
+	ret = snd_pcm_fill_iec958_consumer_hw_params(params, hp.iec.status,
+						     sizeof(hp.iec.status));
 	if (ret < 0) {
 		dev_err(dai->dev, "Creating IEC958 channel status failed %d\n",
 			ret);
 		return ret;
 	}
 
-	hdmi_audio_infoframe_init(&hp.cea);
-	hp.cea.channels = params_channels(params);
-	hp.cea.coding_type = HDMI_AUDIO_CODING_TYPE_STREAM;
-	hp.cea.sample_size = HDMI_AUDIO_SAMPLE_SIZE_STREAM;
-	hp.cea.sample_frequency = HDMI_AUDIO_SAMPLE_FREQUENCY_STREAM;
+	cf->bit_fmt = params_format(params);
+	return hcp->hcd.ops->hw_params(dai->dev->parent, hcp->hcd.data,
+				       cf, &hp);
+}
 
-	/* Select a channel allocation that matches with ELD and pcm channels */
-	idx = hdmi_codec_get_ch_alloc_table_idx(hcp, hp.cea.channels);
-	if (idx < 0) {
-		dev_err(dai->dev, "Not able to map channels to speakers (%d)\n",
-			idx);
-		hcp->chmap_idx = HDMI_CODEC_CHMAP_IDX_UNKNOWN;
-		return idx;
-	}
-	hp.cea.channel_allocation = hdmi_codec_channel_alloc[idx].ca_id;
-	hcp->chmap_idx = hdmi_codec_channel_alloc[idx].ca_id;
+static int hdmi_codec_prepare(struct snd_pcm_substream *substream,
+			      struct snd_soc_dai *dai)
+{
+	struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai);
+	struct hdmi_codec_daifmt *cf = dai->playback_dma_data;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	unsigned int channels = runtime->channels;
+	unsigned int width = snd_pcm_format_width(runtime->format);
+	unsigned int rate = runtime->rate;
+	struct hdmi_codec_params hp;
+	int ret;
 
-	hp.sample_width = params_width(params);
-	hp.sample_rate = params_rate(params);
-	hp.channels = params_channels(params);
+	if (!hcp->hcd.ops->prepare)
+		return 0;
 
-	return hcp->hcd.ops->hw_params(dai->dev->parent, hcp->hcd.data,
-				       cf, &hp);
+	dev_dbg(dai->dev, "%s() width %d rate %d channels %d\n", __func__,
+		width, rate, channels);
+
+	ret = hdmi_codec_fill_codec_params(dai, width, rate, channels, &hp);
+	if (ret < 0)
+		return ret;
+
+	memcpy(hp.iec.status, hcp->iec_status, sizeof(hp.iec.status));
+	ret = snd_pcm_fill_iec958_consumer(runtime, hp.iec.status,
+					   sizeof(hp.iec.status));
+	if (ret < 0) {
+		dev_err(dai->dev, "Creating IEC958 channel status failed %d\n",
+			ret);
+		return ret;
+	}
+
+	cf->bit_fmt = runtime->format;
+	return hcp->hcd.ops->prepare(dai->dev->parent, hcp->hcd.data,
+				     cf, &hp);
 }
 
 static int hdmi_codec_i2s_set_fmt(struct snd_soc_dai *dai,
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:691 @
 	.startup	= hdmi_codec_startup,
 	.shutdown	= hdmi_codec_shutdown,
 	.hw_params	= hdmi_codec_hw_params,
+	.prepare	= hdmi_codec_prepare,
 	.set_fmt	= hdmi_codec_i2s_set_fmt,
 	.mute_stream	= hdmi_codec_mute,
 };
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:725 @
 			 SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S20_3BE |\
 			 SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_3BE |\
 			 SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE |\
-			 SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S32_BE)
+			 SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S32_BE |\
+			 SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE)
+
+struct snd_kcontrol_new hdmi_codec_controls[] = {
+	{
+		.access = SNDRV_CTL_ELEM_ACCESS_READ,
+		.iface = SNDRV_CTL_ELEM_IFACE_PCM,
+		.name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, MASK),
+		.info = hdmi_codec_iec958_info,
+		.get = hdmi_codec_iec958_mask_get,
+	},
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_PCM,
+		.name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT),
+		.info = hdmi_codec_iec958_info,
+		.get = hdmi_codec_iec958_default_get,
+		.put = hdmi_codec_iec958_default_put,
+	},
+	{
+		.access	= (SNDRV_CTL_ELEM_ACCESS_READ |
+			   SNDRV_CTL_ELEM_ACCESS_VOLATILE),
+		.iface	= SNDRV_CTL_ELEM_IFACE_PCM,
+		.name	= "ELD",
+		.info	= hdmi_eld_ctl_info,
+		.get	= hdmi_eld_ctl_get,
+	},
+};
 
 static int hdmi_codec_pcm_new(struct snd_soc_pcm_runtime *rtd,
 			      struct snd_soc_dai *dai)
 {
 	struct snd_soc_dai_driver *drv = dai->driver;
 	struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai);
-	struct snd_kcontrol *kctl;
-	struct snd_kcontrol_new hdmi_eld_ctl = {
-		.access	= SNDRV_CTL_ELEM_ACCESS_READ |
-			  SNDRV_CTL_ELEM_ACCESS_VOLATILE,
-		.iface	= SNDRV_CTL_ELEM_IFACE_PCM,
-		.name	= "ELD",
-		.info	= hdmi_eld_ctl_info,
-		.get	= hdmi_eld_ctl_get,
-		.device	= rtd->pcm->device,
-	};
+	unsigned int i;
 	int ret;
 
 	ret =  snd_pcm_add_chmap_ctls(rtd->pcm, SNDRV_PCM_STREAM_PLAYBACK,
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:775 @
 	hcp->chmap_info->chmap = hdmi_codec_stereo_chmaps;
 	hcp->chmap_idx = HDMI_CODEC_CHMAP_IDX_UNKNOWN;
 
-	/* add ELD ctl with the device number corresponding to the PCM stream */
-	kctl = snd_ctl_new1(&hdmi_eld_ctl, dai->component);
-	if (!kctl)
-		return -ENOMEM;
+	for (i = 0; i < ARRAY_SIZE(hdmi_codec_controls); i++) {
+		struct snd_kcontrol *kctl;
 
-	return snd_ctl_add(rtd->card->snd_card, kctl);
+		/* add ELD ctl with the device number corresponding to the PCM stream */
+		kctl = snd_ctl_new1(&hdmi_codec_controls[i], dai->component);
+		if (!kctl)
+			return -ENOMEM;
+
+		kctl->id.device = rtd->pcm->device;
+		ret = snd_ctl_add(rtd->card->snd_card, kctl);
+		if (ret < 0)
+			return ret;
+	}
+
+	return 0;
 }
 
 static int hdmi_dai_probe(struct snd_soc_dai *dai)
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:955 @
 	}
 
 	dai_count = hcd->i2s + hcd->spdif;
-	if (dai_count < 1 || !hcd->ops || !hcd->ops->hw_params ||
+	if (dai_count < 1 || !hcd->ops ||
+	    (!hcd->ops->hw_params && !hcd->ops->prepare) ||
 	    !hcd->ops->audio_shutdown) {
 		dev_err(dev, "%s: Invalid parameters\n", __func__);
 		return -EINVAL;
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:969 @
 	hcp->hcd = *hcd;
 	mutex_init(&hcp->lock);
 
+	ret = snd_pcm_create_iec958_consumer_default(hcp->iec_status,
+						     sizeof(hcp->iec_status));
+	if (ret < 0)
+		return ret;
+
 	daidrv = devm_kcalloc(dev, dai_count, sizeof(*daidrv), GFP_KERNEL);
 	if (!daidrv)
 		return -ENOMEM;
diff -Nur --no-dereference linux-5.10.52-orig/sound/soc/codecs/i-sabre-codec.c linux-5.10.52-v7l+/sound/soc/codecs/i-sabre-codec.c
--- linux-5.10.52-orig/sound/soc/codecs/i-sabre-codec.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/sound/soc/codecs/i-sabre-codec.c	2021-07-25 16:46:40.407713461 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/*
+ * Driver for I-Sabre Q2M
+ *
+ * Author: Satoru Kawase
+ * Modified by: Xiao Qingyong
+ * Modified by: JC BARBAUD (Mute)
+ * Update kernel v4.18+ by : Audiophonics
+ * 		Copyright 2018 Audiophonics
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/i2c.h>
+#include <sound/soc.h>
+#include <sound/pcm_params.h>
+#include <sound/tlv.h>
+
+#include "i-sabre-codec.h"
+
+
+/* I-Sabre Q2M Codec Private Data */
+struct i_sabre_codec_priv {
+	struct regmap *regmap;
+	unsigned int fmt;
+};
+
+
+/* I-Sabre Q2M Codec Default Register Value */
+static const struct reg_default i_sabre_codec_reg_defaults[] = {
+	{ ISABRECODEC_REG_10, 0x00 },
+	{ ISABRECODEC_REG_20, 0x00 },
+	{ ISABRECODEC_REG_21, 0x00 },
+	{ ISABRECODEC_REG_22, 0x00 },
+	{ ISABRECODEC_REG_24, 0x00 },
+};
+
+
+static bool i_sabre_codec_writeable(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case ISABRECODEC_REG_10:
+	case ISABRECODEC_REG_20:
+	case ISABRECODEC_REG_21:
+	case ISABRECODEC_REG_22:
+	case ISABRECODEC_REG_24:
+		return true;
+
+	default:
+		return false;
+	}
+}
+
+static bool i_sabre_codec_readable(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case ISABRECODEC_REG_01:
+	case ISABRECODEC_REG_02:
+	case ISABRECODEC_REG_10:
+	case ISABRECODEC_REG_20:
+	case ISABRECODEC_REG_21:
+	case ISABRECODEC_REG_22:
+	case ISABRECODEC_REG_24:
+		return true;
+
+	default:
+		return false;
+	}
+}
+
+static bool i_sabre_codec_volatile(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case ISABRECODEC_REG_01:
+	case ISABRECODEC_REG_02:
+		return true;
+
+	default:
+		return false;
+	}
+}
+
+
+/* Volume Scale */
+static const DECLARE_TLV_DB_SCALE(volume_tlv, -10000, 100, 0);
+
+
+/* Filter Type */
+static const char * const fir_filter_type_texts[] = {
+	"brick wall",
+	"corrected minimum phase fast",
+	"minimum phase slow",
+	"minimum phase fast",
+	"linear phase slow",
+	"linear phase fast",
+	"apodizing fast",
+};
+
+static SOC_ENUM_SINGLE_DECL(i_sabre_fir_filter_type_enum,
+				ISABRECODEC_REG_22, 0, fir_filter_type_texts);
+
+
+/* I2S / SPDIF Select */
+static const char * const iis_spdif_sel_texts[] = {
+	"I2S",
+	"SPDIF",
+};
+
+static SOC_ENUM_SINGLE_DECL(i_sabre_iis_spdif_sel_enum,
+				ISABRECODEC_REG_24, 0, iis_spdif_sel_texts);
+
+
+/* Control */
+static const struct snd_kcontrol_new i_sabre_codec_controls[] = {
+SOC_SINGLE_RANGE_TLV("Digital Playback Volume", ISABRECODEC_REG_20, 0, 0, 100, 1, volume_tlv),
+SOC_SINGLE("Digital Playback Switch", ISABRECODEC_REG_21, 0, 1, 1),
+SOC_ENUM("FIR Filter Type", i_sabre_fir_filter_type_enum),
+SOC_ENUM("I2S/SPDIF Select", i_sabre_iis_spdif_sel_enum),
+};
+
+
+static const u32 i_sabre_codec_dai_rates_slave[] = {
+	8000, 11025, 16000, 22050, 32000,
+	44100, 48000, 64000, 88200, 96000,
+	176400, 192000, 352800, 384000,
+	705600, 768000, 1411200, 1536000
+};
+
+static const struct snd_pcm_hw_constraint_list constraints_slave = {
+	.list  = i_sabre_codec_dai_rates_slave,
+	.count = ARRAY_SIZE(i_sabre_codec_dai_rates_slave),
+};
+
+static int i_sabre_codec_dai_startup_slave(
+		struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+	struct snd_soc_component *component = dai->component;
+	int ret;
+
+	ret = snd_pcm_hw_constraint_list(substream->runtime,
+			0, SNDRV_PCM_HW_PARAM_RATE, &constraints_slave);
+	if (ret != 0) {
+		dev_err(component->card->dev, "Failed to setup rates constraints: %d\n", ret);
+	}
+
+	return ret;
+}
+
+static int i_sabre_codec_dai_startup(
+		struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+	struct snd_soc_component      *component = dai->component;
+	struct i_sabre_codec_priv *i_sabre_codec
+					= snd_soc_component_get_drvdata(component);
+
+	switch (i_sabre_codec->fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+	case SND_SOC_DAIFMT_CBS_CFS:
+		return i_sabre_codec_dai_startup_slave(substream, dai);
+
+	default:
+		return (-EINVAL);
+	}
+}
+
+static int i_sabre_codec_hw_params(
+	struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params,
+	struct snd_soc_dai *dai)
+{
+	struct snd_soc_component      *component = dai->component;
+	struct i_sabre_codec_priv *i_sabre_codec
+					= snd_soc_component_get_drvdata(component);
+	unsigned int daifmt;
+	int format_width;
+
+	dev_dbg(component->card->dev, "hw_params %u Hz, %u channels\n",
+			params_rate(params), params_channels(params));
+
+	/* Check I2S Format (Bit Size) */
+	format_width = snd_pcm_format_width(params_format(params));
+	if ((format_width != 32) && (format_width != 16)) {
+		dev_err(component->card->dev, "Bad frame size: %d\n",
+				snd_pcm_format_width(params_format(params)));
+		return (-EINVAL);
+	}
+
+	/* Check Slave Mode */
+	daifmt = i_sabre_codec->fmt & SND_SOC_DAIFMT_MASTER_MASK;
+	if (daifmt != SND_SOC_DAIFMT_CBS_CFS) {
+		return (-EINVAL);
+	}
+
+	/* Notify Sampling Frequency  */
+	switch (params_rate(params))
+	{
+	case 44100:
+	case 48000:
+	case 88200:
+	case 96000:
+	case 176400:
+	case 192000:
+		snd_soc_component_update_bits(component, ISABRECODEC_REG_10, 0x01, 0x00);
+		break;
+
+	case 352800:
+	case 384000:
+	case 705600:
+	case 768000:
+	case 1411200:
+	case 1536000:
+		snd_soc_component_update_bits(component, ISABRECODEC_REG_10, 0x01, 0x01);
+		break;
+	}
+
+	return 0;
+}
+
+static int i_sabre_codec_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+	struct snd_soc_component      *component = dai->component;
+	struct i_sabre_codec_priv *i_sabre_codec
+					= snd_soc_component_get_drvdata(component);
+
+	/* interface format */
+	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_I2S:
+		break;
+
+	case SND_SOC_DAIFMT_RIGHT_J:
+	case SND_SOC_DAIFMT_LEFT_J:
+	default:
+		return (-EINVAL);
+	}
+
+	/* clock inversion */
+	if ((fmt & SND_SOC_DAIFMT_INV_MASK) != SND_SOC_DAIFMT_NB_NF) {
+		return (-EINVAL);
+	}
+
+	/* Set Audio Data Format */
+	i_sabre_codec->fmt = fmt;
+
+	return 0;
+}
+
+static int i_sabre_codec_dac_mute(struct snd_soc_dai *dai, int mute, int direction)
+{
+	struct snd_soc_component *component = dai->component;
+
+	if (mute) {
+		snd_soc_component_update_bits(component, ISABRECODEC_REG_21, 0x01, 0x01);
+	} else {
+		snd_soc_component_update_bits(component, ISABRECODEC_REG_21, 0x01, 0x00);
+	}
+
+	return 0;
+}
+
+
+static const struct snd_soc_dai_ops i_sabre_codec_dai_ops = {
+	.startup      = i_sabre_codec_dai_startup,
+	.hw_params    = i_sabre_codec_hw_params,
+	.set_fmt      = i_sabre_codec_set_fmt,
+	.mute_stream  = i_sabre_codec_dac_mute,
+};
+
+static struct snd_soc_dai_driver i_sabre_codec_dai = {
+	.name = "i-sabre-codec-dai",
+	.playback = {
+		.stream_name  = "Playback",
+		.channels_min = 2,
+		.channels_max = 2,
+		.rates = SNDRV_PCM_RATE_CONTINUOUS,
+		.rate_min = 8000,
+		.rate_max = 1536000,
+		.formats      = SNDRV_PCM_FMTBIT_S16_LE
+				| SNDRV_PCM_FMTBIT_S32_LE,
+	},
+	.ops = &i_sabre_codec_dai_ops,
+};
+
+static struct snd_soc_component_driver i_sabre_codec_codec_driver = {
+		.controls         = i_sabre_codec_controls,
+		.num_controls     = ARRAY_SIZE(i_sabre_codec_controls),
+};
+
+
+static const struct regmap_config i_sabre_codec_regmap = {
+	.reg_bits         = 8,
+	.val_bits         = 8,
+	.max_register     = ISABRECODEC_MAX_REG,
+
+	.reg_defaults     = i_sabre_codec_reg_defaults,
+	.num_reg_defaults = ARRAY_SIZE(i_sabre_codec_reg_defaults),
+
+	.writeable_reg    = i_sabre_codec_writeable,
+	.readable_reg     = i_sabre_codec_readable,
+	.volatile_reg     = i_sabre_codec_volatile,
+
+	.cache_type       = REGCACHE_RBTREE,
+};
+
+
+static int i_sabre_codec_probe(struct device *dev, struct regmap *regmap)
+{
+	struct i_sabre_codec_priv *i_sabre_codec;
+	int ret;
+
+	i_sabre_codec = devm_kzalloc(dev, sizeof(*i_sabre_codec), GFP_KERNEL);
+	if (!i_sabre_codec) {
+		dev_err(dev, "devm_kzalloc");
+		return (-ENOMEM);
+	}
+
+	i_sabre_codec->regmap = regmap;
+
+	dev_set_drvdata(dev, i_sabre_codec);
+
+	ret = snd_soc_register_component(dev,
+			&i_sabre_codec_codec_driver, &i_sabre_codec_dai, 1);
+	if (ret != 0) {
+		dev_err(dev, "Failed to register CODEC: %d\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static void i_sabre_codec_remove(struct device *dev)
+{
+	snd_soc_unregister_component(dev);
+}
+
+
+static int i_sabre_codec_i2c_probe(
+		struct i2c_client *i2c, const struct i2c_device_id *id)
+{
+	struct regmap *regmap;
+
+	regmap = devm_regmap_init_i2c(i2c, &i_sabre_codec_regmap);
+	if (IS_ERR(regmap)) {
+		return PTR_ERR(regmap);
+	}
+
+	return i_sabre_codec_probe(&i2c->dev, regmap);
+}
+
+static int i_sabre_codec_i2c_remove(struct i2c_client *i2c)
+{
+	i_sabre_codec_remove(&i2c->dev);
+
+	return 0;
+}
+
+
+static const struct i2c_device_id i_sabre_codec_i2c_id[] = {
+	{ "i-sabre-codec", },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, i_sabre_codec_i2c_id);
+
+static const struct of_device_id i_sabre_codec_of_match[] = {
+	{ .compatible = "audiophonics,i-sabre-codec", },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, i_sabre_codec_of_match);
+
+static struct i2c_driver i_sabre_codec_i2c_driver = {
+	.driver = {
+		.name           = "i-sabre-codec-i2c",
+		.owner          = THIS_MODULE,
+		.of_match_table = of_match_ptr(i_sabre_codec_of_match),
+	},
+	.probe    = i_sabre_codec_i2c_probe,
+	.remove   = i_sabre_codec_i2c_remove,
+	.id_table = i_sabre_codec_i2c_id,
+};
+module_i2c_driver(i_sabre_codec_i2c_driver);
+
+
+MODULE_DESCRIPTION("ASoC I-Sabre Q2M codec driver");
+MODULE_AUTHOR("Audiophonics <http://www.audiophonics.fr>");
+MODULE_LICENSE("GPL");
diff -Nur --no-dereference linux-5.10.52-orig/sound/soc/codecs/i-sabre-codec.h linux-5.10.52-v7l+/sound/soc/codecs/i-sabre-codec.h
--- linux-5.10.52-orig/sound/soc/codecs/i-sabre-codec.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/sound/soc/codecs/i-sabre-codec.h	2021-07-25 16:46:40.427713126 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/*
+ * Driver for I-Sabre Q2M
+ *
+ * Author: Satoru Kawase
+ * Modified by: Xiao Qingyong
+ *      Copyright 2018 Audiophonics
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+
+#ifndef _SND_SOC_ISABRECODEC
+#define _SND_SOC_ISABRECODEC
+
+
+/* ISABRECODEC Register Address */
+#define ISABRECODEC_REG_01	0x01	/* Virtual Device ID  :  0x01 = es9038q2m */
+#define ISABRECODEC_REG_02	0x02	/* API revision       :  0x01 = Revision 01 */
+#define ISABRECODEC_REG_10	0x10	/* 0x01 = above 192kHz, 0x00 = otherwise */
+#define ISABRECODEC_REG_20	0x20	/* 0 - 100 (decimal value, 0 = min., 100 = max.) */
+#define ISABRECODEC_REG_21	0x21	/* 0x00 = Mute OFF, 0x01 = Mute ON */
+#define ISABRECODEC_REG_22	0x22	
+/*
+   0x00 = brick wall,
+   0x01 = corrected minimum phase fast,
+   0x02 = minimum phase slow,
+   0x03 = minimum phase fast,
+   0x04 = linear phase slow,
+   0x05 = linear phase fast,
+   0x06 = apodizing fast,
+*/
+//#define ISABRECODEC_REG_23	0x23	/* reserved */
+#define ISABRECODEC_REG_24	0x24	/* 0x00 = I2S, 0x01 = SPDIF */
+#define ISABRECODEC_MAX_REG	0x24	/* Maximum Register Number */
+
+#endif /* _SND_SOC_ISABRECODEC */
diff -Nur --no-dereference linux-5.10.52-orig/sound/soc/codecs/Kconfig linux-5.10.52-v7l+/sound/soc/codecs/Kconfig
--- linux-5.10.52-orig/sound/soc/codecs/Kconfig	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/sound/soc/codecs/Kconfig	2021-07-25 16:46:40.187717149 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:103 @
 	imply SND_SOC_ICS43432
 	imply SND_SOC_INNO_RK3036
 	imply SND_SOC_ISABELLE
+	imply SND_SOC_I_SABRE_CODEC
 	imply SND_SOC_JZ4740_CODEC
 	imply SND_SOC_JZ4725B_CODEC
 	imply SND_SOC_JZ4770_CODEC
 	imply SND_SOC_LM4857
 	imply SND_SOC_LM49453
 	imply SND_SOC_LOCHNAGAR_SC
+	imply SND_SOC_MA120X0P
 	imply SND_SOC_MAX98088
 	imply SND_SOC_MAX98090
 	imply SND_SOC_MAX98095
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:147 @
 	imply SND_SOC_PCM179X_SPI
 	imply SND_SOC_PCM186X_I2C
 	imply SND_SOC_PCM186X_SPI
+	imply SND_SOC_PCM1794A
 	imply SND_SOC_PCM3008
 	imply SND_SOC_PCM3060_I2C
 	imply SND_SOC_PCM3060_SPI
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:212 @
 	imply SND_SOC_TLV320ADCX140
 	imply SND_SOC_TLV320AIC23_I2C
 	imply SND_SOC_TLV320AIC23_SPI
+	imply SND_SOC_TAS5713
 	imply SND_SOC_TLV320AIC26
 	imply SND_SOC_TLV320AIC31XX
 	imply SND_SOC_TLV320AIC32X4_I2C
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:350 @
 	tristate
 
 config SND_SOC_AD193X_SPI
-	tristate
+	tristate "Analog Devices AU193X CODEC - SPI"
 	depends on SPI_MASTER
 	select SND_SOC_AD193X
 
 config SND_SOC_AD193X_I2C
-	tristate
+	tristate "Analog Devices AU193X CODEC - I2C"
 	depends on I2C
 	select SND_SOC_AD193X
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:811 @
 	select SND_HDA
 
 config SND_SOC_ICS43432
-	tristate
+	tristate "InvenSense ICS43432 I2S microphone codec"
 
 config SND_SOC_INNO_RK3036
 	tristate "Inno codec driver for RK3036 SoC"
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:832 @
 	  This driver support the sound card functionality of the Cirrus
 	  Logic Lochnagar audio development board.
 
+config SND_SOC_MA120X0P
+	tristate "Infineon Merus(TM) MA120X0P Multilevel Class-D Audio amplifiers"
+	depends on I2C
+	help
+		Enable support for Infineon MA120X0P Multilevel Class-D audio power
+		amplifiers.
+
 config SND_SOC_MADERA
 	tristate
 	default y if SND_SOC_CS47L15=y
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1139 @
 	tristate "Realtek RT5616 CODEC"
 	depends on I2C
 
+config SND_SOC_PCM1794A
+	tristate
+	depends on I2C
+
 config SND_SOC_RT5631
 	tristate "Realtek ALC5631/RT5631 CODEC"
 	depends on I2C
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1364 @
 	tristate "NXP Semiconductors TFA9879 amplifier"
 	depends on I2C
 
+config SND_SOC_TAS5713
+	tristate
+
 config SND_SOC_TLV320AIC23
 	tristate
 
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1805 @
 	tristate "Texas Instruments TPA6130A2 headphone amplifier"
 	depends on I2C
 
+config SND_SOC_I_SABRE_CODEC
+	tristate "Audiophonics I-SABRE Codec"
+	depends on I2C
+
 endmenu
diff -Nur --no-dereference linux-5.10.52-orig/sound/soc/codecs/ma120x0p.c linux-5.10.52-v7l+/sound/soc/codecs/ma120x0p.c
--- linux-5.10.52-orig/sound/soc/codecs/ma120x0p.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/sound/soc/codecs/ma120x0p.c	2021-07-25 16:46:40.447712790 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * ASoC Driver for Infineon Merus(TM) ma120x0p multi-level class-D amplifier
+ *
+ * Authors:	Ariel Muszkat <ariel.muszkat@gmail.com>
+ * Jorgen Kragh Jakobsen <jorgen.kraghjakobsen@infineon.com>
+ *
+ * Copyright (C) 2019 Infineon Technologies AG
+ *
+ */
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm_runtime.h>
+#include <linux/i2c.h>
+#include <linux/of_device.h>
+#include <linux/spi/spi.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <linux/gpio/consumer.h>
+#include <linux/gpio.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <linux/interrupt.h>
+
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/fs.h>
+#include <linux/uaccess.h>
+
+#ifndef _MA120X0P_
+#define _MA120X0P_
+//------------------------------------------------------------------manualPM---
+// Select Manual PowerMode control
+#define ma_manualpm__a 0
+#define ma_manualpm__len 1
+#define ma_manualpm__mask 0x40
+#define ma_manualpm__shift 0x06
+#define ma_manualpm__reset 0x00
+//--------------------------------------------------------------------pm_man---
+// manual selected power mode
+#define ma_pm_man__a 0
+#define ma_pm_man__len 2
+#define ma_pm_man__mask 0x30
+#define ma_pm_man__shift 0x04
+#define ma_pm_man__reset 0x03
+//------------------------------------------ ----------------------mthr_1to2---
+// mod. index threshold value for pm1=>pm2 change.
+#define ma_mthr_1to2__a 1
+#define ma_mthr_1to2__len 8
+#define ma_mthr_1to2__mask 0xff
+#define ma_mthr_1to2__shift 0x00
+#define ma_mthr_1to2__reset 0x3c
+//-----------------------------------------------------------------mthr_2to1---
+// mod. index threshold value for pm2=>pm1 change.
+#define ma_mthr_2to1__a 2
+#define ma_mthr_2to1__len 8
+#define ma_mthr_2to1__mask 0xff
+#define ma_mthr_2to1__shift 0x00
+#define ma_mthr_2to1__reset 0x32
+//-----------------------------------------------------------------mthr_2to3---
+// mod. index threshold value for pm2=>pm3 change.
+#define ma_mthr_2to3__a 3
+#define ma_mthr_2to3__len 8
+#define ma_mthr_2to3__mask 0xff
+#define ma_mthr_2to3__shift 0x00
+#define ma_mthr_2to3__reset 0x5a
+//-----------------------------------------------------------------mthr_3to2---
+// mod. index threshold value for pm3=>pm2 change.
+#define ma_mthr_3to2__a 4
+#define ma_mthr_3to2__len 8
+#define ma_mthr_3to2__mask 0xff
+#define ma_mthr_3to2__shift 0x00
+#define ma_mthr_3to2__reset 0x50
+//-------------------------------------------------------------pwmclkdiv_nom---
+// pwm default clock divider value
+#define ma_pwmclkdiv_nom__a 8
+#define ma_pwmclkdiv_nom__len 8
+#define ma_pwmclkdiv_nom__mask 0xff
+#define ma_pwmclkdiv_nom__shift 0x00
+#define ma_pwmclkdiv_nom__reset 0x26
+//--------- ----------------------------------------------------ocp_latch_en---
+// high to use permanently latching level-2 ocp
+#define ma_ocp_latch_en__a 10
+#define ma_ocp_latch_en__len 1
+#define ma_ocp_latch_en__mask 0x02
+#define ma_ocp_latch_en__shift 0x01
+#define ma_ocp_latch_en__reset 0x00
+//---------------------------------------------------------------lf_clamp_en---
+// high (default) to enable lf int2+3 clamping on clip
+#define ma_lf_clamp_en__a 10
+#define ma_lf_clamp_en__len 1
+#define ma_lf_clamp_en__mask 0x80
+#define ma_lf_clamp_en__shift 0x07
+#define ma_lf_clamp_en__reset 0x00
+//-------------------------------------------------------pmcfg_btl_b.modtype---
+//
+#define ma_pmcfg_btl_b__modtype__a 18
+#define ma_pmcfg_btl_b__modtype__len 2
+#define ma_pmcfg_btl_b__modtype__mask 0x18
+#define ma_pmcfg_btl_b__modtype__shift 0x03
+#define ma_pmcfg_btl_b__modtype__reset 0x02
+//-------------------------------------------------------pmcfg_btl_b.freqdiv---
+#define ma_pmcfg_btl_b__freqdiv__a 18
+#define ma_pmcfg_btl_b__freqdiv__len 2
+#define ma_pmcfg_btl_b__freqdiv__mask 0x06
+#define ma_pmcfg_btl_b__freqdiv__shift 0x01
+#define ma_pmcfg_btl_b__freqdiv__reset 0x01
+//----------------------------------------------------pmcfg_btl_b.lf_gain_ol---
+//
+#define ma_pmcfg_btl_b__lf_gain_ol__a 18
+#define ma_pmcfg_btl_b__lf_gain_ol__len 1
+#define ma_pmcfg_btl_b__lf_gain_ol__mask 0x01
+#define ma_pmcfg_btl_b__lf_gain_ol__shift 0x00
+#define ma_pmcfg_btl_b__lf_gain_ol__reset 0x01
+//-------------------------------------------------------pmcfg_btl_c.freqdiv---
+//
+#define ma_pmcfg_btl_c__freqdiv__a 19
+#define ma_pmcfg_btl_c__freqdiv__len 2
+#define ma_pmcfg_btl_c__freqdiv__mask 0x06
+#define ma_pmcfg_btl_c__freqdiv__shift 0x01
+#define ma_pmcfg_btl_c__freqdiv__reset 0x01
+//-------------------------------------------------------pmcfg_btl_c.modtype---
+//
+#define ma_pmcfg_btl_c__modtype__a 19
+#define ma_pmcfg_btl_c__modtype__len 2
+#define ma_pmcfg_btl_c__modtype__mask 0x18
+#define ma_pmcfg_btl_c__modtype__shift 0x03
+#define ma_pmcfg_btl_c__modtype__reset 0x01
+//----------------------------------------------------pmcfg_btl_c.lf_gain_ol---
+//
+#define ma_pmcfg_btl_c__lf_gain_ol__a 19
+#define ma_pmcfg_btl_c__lf_gain_ol__len 1
+#define ma_pmcfg_btl_c__lf_gain_ol__mask 0x01
+#define ma_pmcfg_btl_c__lf_gain_ol__shift 0x00
+#define ma_pmcfg_btl_c__lf_gain_ol__reset 0x00
+//-------------------------------------------------------pmcfg_btl_d.modtype---
+//
+#define ma_pmcfg_btl_d__modtype__a 20
+#define ma_pmcfg_btl_d__modtype__len 2
+#define ma_pmcfg_btl_d__modtype__mask 0x18
+#define ma_pmcfg_btl_d__modtype__shift 0x03
+#define ma_pmcfg_btl_d__modtype__reset 0x02
+//-------------------------------------------------------pmcfg_btl_d.freqdiv---
+//
+#define ma_pmcfg_btl_d__freqdiv__a 20
+#define ma_pmcfg_btl_d__freqdiv__len 2
+#define ma_pmcfg_btl_d__freqdiv__mask 0x06
+#define ma_pmcfg_btl_d__freqdiv__shift 0x01
+#define ma_pmcfg_btl_d__freqdiv__reset 0x02
+//----------------------------------------------------pmcfg_btl_d.lf_gain_ol---
+//
+#define ma_pmcfg_btl_d__lf_gain_ol__a 20
+#define ma_pmcfg_btl_d__lf_gain_ol__len 1
+#define ma_pmcfg_btl_d__lf_gain_ol__mask 0x01
+#define ma_pmcfg_btl_d__lf_gain_ol__shift 0x00
+#define ma_pmcfg_btl_d__lf_gain_ol__reset 0x00
+//------------ -------------------------------------------pmcfg_se_a.modtype---
+//
+#define ma_pmcfg_se_a__modtype__a 21
+#define ma_pmcfg_se_a__modtype__len 2
+#define ma_pmcfg_se_a__modtype__mask 0x18
+#define ma_pmcfg_se_a__modtype__shift 0x03
+#define ma_pmcfg_se_a__modtype__reset 0x01
+//--------------------------------------------------------pmcfg_se_a.freqdiv---
+//
+#define ma_pmcfg_se_a__freqdiv__a 21
+#define ma_pmcfg_se_a__freqdiv__len 2
+#define ma_pmcfg_se_a__freqdiv__mask 0x06
+#define ma_pmcfg_se_a__freqdiv__shift 0x01
+#define ma_pmcfg_se_a__freqdiv__reset 0x00
+//-----------------------------------------------------pmcfg_se_a.lf_gain_ol---
+//
+#define ma_pmcfg_se_a__lf_gain_ol__a 21
+#define ma_pmcfg_se_a__lf_gain_ol__len 1
+#define ma_pmcfg_se_a__lf_gain_ol__mask 0x01
+#define ma_pmcfg_se_a__lf_gain_ol__shift 0x00
+#define ma_pmcfg_se_a__lf_gain_ol__reset 0x01
+//-----------------------------------------------------pmcfg_se_b.lf_gain_ol---
+//
+#define ma_pmcfg_se_b__lf_gain_ol__a 22
+#define ma_pmcfg_se_b__lf_gain_ol__len 1
+#define ma_pmcfg_se_b__lf_gain_ol__mask 0x01
+#define ma_pmcfg_se_b__lf_gain_ol__shift 0x00
+#define ma_pmcfg_se_b__lf_gain_ol__reset 0x00
+//--------------------------------------------------------pmcfg_se_b.freqdiv---
+//
+#define ma_pmcfg_se_b__freqdiv__a 22
+#define ma_pmcfg_se_b__freqdiv__len 2
+#define ma_pmcfg_se_b__freqdiv__mask 0x06
+#define ma_pmcfg_se_b__freqdiv__shift 0x01
+#define ma_pmcfg_se_b__freqdiv__reset 0x01
+//--------------------------------------------------------pmcfg_se_b.modtype---
+//
+#define ma_pmcfg_se_b__modtype__a 22
+#define ma_pmcfg_se_b__modtype__len 2
+#define ma_pmcfg_se_b__modtype__mask 0x18
+#define ma_pmcfg_se_b__modtype__shift 0x03
+#define ma_pmcfg_se_b__modtype__reset 0x01
+//----------------------------------------------------------balwaitcount_pm1---
+// pm1 balancing period.
+#define ma_balwaitcount_pm1__a 23
+#define ma_balwaitcount_pm1__len 8
+#define ma_balwaitcount_pm1__mask 0xff
+#define ma_balwaitcount_pm1__shift 0x00
+#define ma_balwaitcount_pm1__reset 0x14
+//----------------------------------------------------------balwaitcount_pm2---
+// pm2 balancing period.
+#define ma_balwaitcount_pm2__a 24
+#define ma_balwaitcount_pm2__len 8
+#define ma_balwaitcount_pm2__mask 0xff
+#define ma_balwaitcount_pm2__shift 0x00
+#define ma_balwaitcount_pm2__reset 0x14
+//----------------------------------------------------------balwaitcount_pm3---
+// pm3 balancing period.
+#define ma_balwaitcount_pm3__a 25
+#define ma_balwaitcount_pm3__len 8
+#define ma_balwaitcount_pm3__mask 0xff
+#define ma_balwaitcount_pm3__shift 0x00
+#define ma_balwaitcount_pm3__reset 0x1a
+//-------------------------------------------------------------usespread_pm1---
+// pm1 pwm spread-spectrum mode on/off.
+#define ma_usespread_pm1__a 26
+#define ma_usespread_pm1__len 1
+#define ma_usespread_pm1__mask 0x40
+#define ma_usespread_pm1__shift 0x06
+#define ma_usespread_pm1__reset 0x00
+//---------------------------------------------------------------dtsteps_pm1---
+// pm1 dead time setting [10ns steps].
+#define ma_dtsteps_pm1__a 26
+#define ma_dtsteps_pm1__len 3
+#define ma_dtsteps_pm1__mask 0x38
+#define ma_dtsteps_pm1__shift 0x03
+#define ma_dtsteps_pm1__reset 0x04
+//---------------------------------------------------------------baltype_pm1---
+// pm1 balancing sensor scheme.
+#define ma_baltype_pm1__a 26
+#define ma_baltype_pm1__len 3
+#define ma_baltype_pm1__mask 0x07
+#define ma_baltype_pm1__shift 0x00
+#define ma_baltype_pm1__reset 0x00
+//-------------------------------------------------------------usespread_pm2---
+// pm2 pwm spread-spectrum mode on/off.
+#define ma_usespread_pm2__a 27
+#define ma_usespread_pm2__len 1
+#define ma_usespread_pm2__mask 0x40
+#define ma_usespread_pm2__shift 0x06
+#define ma_usespread_pm2__reset 0x00
+//---------------------------------------------------------------dtsteps_pm2---
+// pm2 dead time setting [10ns steps].
+#define ma_dtsteps_pm2__a 27
+#define ma_dtsteps_pm2__len 3
+#define ma_dtsteps_pm2__mask 0x38
+#define ma_dtsteps_pm2__shift 0x03
+#define ma_dtsteps_pm2__reset 0x03
+//---------------------------------------------------------------baltype_pm2---
+// pm2 balancing sensor scheme.
+#define ma_baltype_pm2__a 27
+#define ma_baltype_pm2__len 3
+#define ma_baltype_pm2__mask 0x07
+#define ma_baltype_pm2__shift 0x00
+#define ma_baltype_pm2__reset 0x01
+//-------------------------------------------------------------usespread_pm3---
+// pm3 pwm spread-spectrum mode on/off.
+#define ma_usespread_pm3__a 28
+#define ma_usespread_pm3__len 1
+#define ma_usespread_pm3__mask 0x40
+#define ma_usespread_pm3__shift 0x06
+#define ma_usespread_pm3__reset 0x00
+//---------------------------------------------------------------dtsteps_pm3---
+// pm3 dead time setting [10ns steps].
+#define ma_dtsteps_pm3__a 28
+#define ma_dtsteps_pm3__len 3
+#define ma_dtsteps_pm3__mask 0x38
+#define ma_dtsteps_pm3__shift 0x03
+#define ma_dtsteps_pm3__reset 0x01
+//---------------------------------------------------------------baltype_pm3---
+// pm3 balancing sensor scheme.
+#define ma_baltype_pm3__a 28
+#define ma_baltype_pm3__len 3
+#define ma_baltype_pm3__mask 0x07
+#define ma_baltype_pm3__shift 0x00
+#define ma_baltype_pm3__reset 0x03
+//-----------------------------------------------------------------pmprofile---
+// pm profile select. valid presets: 0-1-2-3-4. 5=> custom profile.
+#define ma_pmprofile__a 29
+#define ma_pmprofile__len 3
+#define ma_pmprofile__mask 0x07
+#define ma_pmprofile__shift 0x00
+#define ma_pmprofile__reset 0x00
+//-------------------------------------------------------------------pm3_man---
+// custom profile pm3 contents. 0=>a,  1=>b,  2=>c,  3=>d
+#define ma_pm3_man__a 30
+#define ma_pm3_man__len 2
+#define ma_pm3_man__mask 0x30
+#define ma_pm3_man__shift 0x04
+#define ma_pm3_man__reset 0x02
+//-------------------------------------------------------------------pm2_man---
+// custom profile pm2 contents. 0=>a,  1=>b,  2=>c,  3=>d
+#define ma_pm2_man__a 30
+#define ma_pm2_man__len 2
+#define ma_pm2_man__mask 0x0c
+#define ma_pm2_man__shift 0x02
+#define ma_pm2_man__reset 0x03
+//-------------------------------------------------------------------pm1_man---
+// custom profile pm1 contents. 0=>a,  1=>b,  2=>c,  3=>d
+#define ma_pm1_man__a 30
+#define ma_pm1_man__len 2
+#define ma_pm1_man__mask 0x03
+#define ma_pm1_man__shift 0x00
+#define ma_pm1_man__reset 0x03
+//-----------------------------------------------------------ocp_latch_clear---
+// low-high clears current ocp latched condition.
+#define ma_ocp_latch_clear__a 32
+#define ma_ocp_latch_clear__len 1
+#define ma_ocp_latch_clear__mask 0x80
+#define ma_ocp_latch_clear__shift 0x07
+#define ma_ocp_latch_clear__reset 0x00
+//-------------------------------------------------------------audio_in_mode---
+// audio input mode; 0-1-2-3-4-5
+#define ma_audio_in_mode__a 37
+#define ma_audio_in_mode__len 3
+#define ma_audio_in_mode__mask 0xe0
+#define ma_audio_in_mode__shift 0x05
+#define ma_audio_in_mode__reset 0x00
+//-----------------------------------------------------------------eh_dcshdn---
+// high to enable dc protection
+#define ma_eh_dcshdn__a 38
+#define ma_eh_dcshdn__len 1
+#define ma_eh_dcshdn__mask 0x04
+#define ma_eh_dcshdn__shift 0x02
+#define ma_eh_dcshdn__reset 0x01
+//---------------------------------------------------------audio_in_mode_ext---
+// if set,  audio_in_mode is controlled from audio_in_mode register. if not set
+//audio_in_mode is set from fuse bank setting
+#define ma_audio_in_mode_ext__a 39
+#define ma_audio_in_mode_ext__len 1
+#define ma_audio_in_mode_ext__mask 0x20
+#define ma_audio_in_mode_ext__shift 0x05
+#define ma_audio_in_mode_ext__reset 0x00
+//------------------------------------------------------------------eh_clear---
+// flip to clear error registers
+#define ma_eh_clear__a 45
+#define ma_eh_clear__len 1
+#define ma_eh_clear__mask 0x04
+#define ma_eh_clear__shift 0x02
+#define ma_eh_clear__reset 0x00
+//----------------------------------------------------------thermal_compr_en---
+// enable otw-contr.  input compression?
+#define ma_thermal_compr_en__a 45
+#define ma_thermal_compr_en__len 1
+#define ma_thermal_compr_en__mask 0x20
+#define ma_thermal_compr_en__shift 0x05
+#define ma_thermal_compr_en__reset 0x01
+//---------------------------------------------------------------system_mute---
+// 1 = mute system,  0 = normal operation
+#define ma_system_mute__a 45
+#define ma_system_mute__len 1
+#define ma_system_mute__mask 0x40
+#define ma_system_mute__shift 0x06
+#define ma_system_mute__reset 0x00
+//------------------------------------------------------thermal_compr_max_db---
+// audio limiter max thermal reduction
+#define ma_thermal_compr_max_db__a 46
+#define ma_thermal_compr_max_db__len 3
+#define ma_thermal_compr_max_db__mask 0x07
+#define ma_thermal_compr_max_db__shift 0x00
+#define ma_thermal_compr_max_db__reset 0x04
+//---------------------------------------------------------audio_proc_enable---
+// enable audio proc,  bypass if not enabled
+#define ma_audio_proc_enable__a 53
+#define ma_audio_proc_enable__len 1
+#define ma_audio_proc_enable__mask 0x08
+#define ma_audio_proc_enable__shift 0x03
+#define ma_audio_proc_enable__reset 0x00
+//--------------------------------------------------------audio_proc_release---
+// 00:slow,  01:normal,  10:fast
+#define ma_audio_proc_release__a 53
+#define ma_audio_proc_release__len 2
+#define ma_audio_proc_release__mask 0x30
+#define ma_audio_proc_release__shift 0x04
+#define ma_audio_proc_release__reset 0x00
+//---------------------------------------------------------audio_proc_attack---
+// 00:slow,  01:normal,  10:fast
+#define ma_audio_proc_attack__a 53
+#define ma_audio_proc_attack__len 2
+#define ma_audio_proc_attack__mask 0xc0
+#define ma_audio_proc_attack__shift 0x06
+#define ma_audio_proc_attack__reset 0x00
+//----------------------------------------------------------------i2s_format---
+// i2s basic data format,  000 = std. i2s,  001 = left justified (default)
+#define ma_i2s_format__a 53
+#define ma_i2s_format__len 3
+#define ma_i2s_format__mask 0x07
+#define ma_i2s_format__shift 0x00
+#define ma_i2s_format__reset 0x01
+//--------------------------------------------------audio_proc_limiterenable---
+// 1: enable limiter,  0: disable limiter
+#define ma_audio_proc_limiterenable__a 54
+#define ma_audio_proc_limiterenable__len 1
+#define ma_audio_proc_limiterenable__mask 0x40
+#define ma_audio_proc_limiterenable__shift 0x06
+#define ma_audio_proc_limiterenable__reset 0x00
+//-----------------------------------------------------------audio_proc_mute---
+// 1: mute,  0: unmute
+#define ma_audio_proc_mute__a 54
+#define ma_audio_proc_mute__len 1
+#define ma_audio_proc_mute__mask 0x80
+#define ma_audio_proc_mute__shift 0x07
+#define ma_audio_proc_mute__reset 0x00
+//---------------------------------------------------------------i2s_sck_pol---
+// i2s sck polarity cfg. 0 = rising edge data change
+#define ma_i2s_sck_pol__a 54
+#define ma_i2s_sck_pol__len 1
+#define ma_i2s_sck_pol__mask 0x01
+#define ma_i2s_sck_pol__shift 0x00
+#define ma_i2s_sck_pol__reset 0x01
+//-------------------------------------------------------------i2s_framesize---
+// i2s word length. 00 = 32bit,  01 = 24bit
+#define ma_i2s_framesize__a 54
+#define ma_i2s_framesize__len 2
+#define ma_i2s_framesize__mask 0x18
+#define ma_i2s_framesize__shift 0x03
+#define ma_i2s_framesize__reset 0x00
+//----------------------------------------------------------------i2s_ws_pol---
+// i2s ws polarity. 0 = low first
+#define ma_i2s_ws_pol__a 54
+#define ma_i2s_ws_pol__len 1
+#define ma_i2s_ws_pol__mask 0x02
+#define ma_i2s_ws_pol__shift 0x01
+#define ma_i2s_ws_pol__reset 0x00
+//-----------------------------------------------------------------i2s_order---
+// i2s word bit order. 0 = msb first
+#define ma_i2s_order__a 54
+#define ma_i2s_order__len 1
+#define ma_i2s_order__mask 0x04
+#define ma_i2s_order__shift 0x02
+#define ma_i2s_order__reset 0x00
+//------------------------------------------------------------i2s_rightfirst---
+// i2s l/r word order; 0 = left first
+#define ma_i2s_rightfirst__a 54
+#define ma_i2s_rightfirst__len 1
+#define ma_i2s_rightfirst__mask 0x20
+#define ma_i2s_rightfirst__shift 0x05
+#define ma_i2s_rightfirst__reset 0x00
+//-------------------------------------------------------------vol_db_master---
+// master volume db
+#define ma_vol_db_master__a 64
+#define ma_vol_db_master__len 8
+#define ma_vol_db_master__mask 0xff
+#define ma_vol_db_master__shift 0x00
+#define ma_vol_db_master__reset 0x18
+//------------------------------------------------------------vol_lsb_master---
+// master volume lsb 1/4 steps
+#define ma_vol_lsb_master__a 65
+#define ma_vol_lsb_master__len 2
+#define ma_vol_lsb_master__mask 0x03
+#define ma_vol_lsb_master__shift 0x00
+#define ma_vol_lsb_master__reset 0x00
+//----------------------------------------------------------------vol_db_ch0---
+// volume channel 0
+#define ma_vol_db_ch0__a 66
+#define ma_vol_db_ch0__len 8
+#define ma_vol_db_ch0__mask 0xff
+#define ma_vol_db_ch0__shift 0x00
+#define ma_vol_db_ch0__reset 0x18
+//----------------------------------------------------------------vol_db_ch1---
+// volume channel 1
+#define ma_vol_db_ch1__a 67
+#define ma_vol_db_ch1__len 8
+#define ma_vol_db_ch1__mask 0xff
+#define ma_vol_db_ch1__shift 0x00
+#define ma_vol_db_ch1__reset 0x18
+//----------------------------------------------------------------vol_db_ch2---
+// volume channel 2
+#define ma_vol_db_ch2__a 68
+#define ma_vol_db_ch2__len 8
+#define ma_vol_db_ch2__mask 0xff
+#define ma_vol_db_ch2__shift 0x00
+#define ma_vol_db_ch2__reset 0x18
+//----------------------------------------------------------------vol_db_ch3---
+// volume channel 3
+#define ma_vol_db_ch3__a 69
+#define ma_vol_db_ch3__len 8
+#define ma_vol_db_ch3__mask 0xff
+#define ma_vol_db_ch3__shift 0x00
+#define ma_vol_db_ch3__reset 0x18
+//---------------------------------------------------------------vol_lsb_ch0---
+// volume channel 1 - 1/4 steps
+#define ma_vol_lsb_ch0__a 70
+#define ma_vol_lsb_ch0__len 2
+#define ma_vol_lsb_ch0__mask 0x03
+#define ma_vol_lsb_ch0__shift 0x00
+#define ma_vol_lsb_ch0__reset 0x00
+//---------------------------------------------------------------vol_lsb_ch1---
+// volume channel 3 - 1/4 steps
+#define ma_vol_lsb_ch1__a 70
+#define ma_vol_lsb_ch1__len 2
+#define ma_vol_lsb_ch1__mask 0x0c
+#define ma_vol_lsb_ch1__shift 0x02
+#define ma_vol_lsb_ch1__reset 0x00
+//---------------------------------------------------------------vol_lsb_ch2---
+// volume channel 2 - 1/4 steps
+#define ma_vol_lsb_ch2__a 70
+#define ma_vol_lsb_ch2__len 2
+#define ma_vol_lsb_ch2__mask 0x30
+#define ma_vol_lsb_ch2__shift 0x04
+#define ma_vol_lsb_ch2__reset 0x00
+//---------------------------------------------------------------vol_lsb_ch3---
+// volume channel 3 - 1/4 steps
+#define ma_vol_lsb_ch3__a 70
+#define ma_vol_lsb_ch3__len 2
+#define ma_vol_lsb_ch3__mask 0xc0
+#define ma_vol_lsb_ch3__shift 0x06
+#define ma_vol_lsb_ch3__reset 0x00
+//----------------------------------------------------------------thr_db_ch0---
+// thr_db channel 0
+#define ma_thr_db_ch0__a 71
+#define ma_thr_db_ch0__len 8
+#define ma_thr_db_ch0__mask 0xff
+#define ma_thr_db_ch0__shift 0x00
+#define ma_thr_db_ch0__reset 0x18
+//----------------------------------------------------------------thr_db_ch1---
+// thr db ch1
+#define ma_thr_db_ch1__a 72
+#define ma_thr_db_ch1__len 8
+#define ma_thr_db_ch1__mask 0xff
+#define ma_thr_db_ch1__shift 0x00
+#define ma_thr_db_ch1__reset 0x18
+//----------------------------------------------------------------thr_db_ch2---
+// thr db ch2
+#define ma_thr_db_ch2__a 73
+#define ma_thr_db_ch2__len 8
+#define ma_thr_db_ch2__mask 0xff
+#define ma_thr_db_ch2__shift 0x00
+#define ma_thr_db_ch2__reset 0x18
+//----------------------------------------------------------------thr_db_ch3---
+// threshold db ch3
+#define ma_thr_db_ch3__a 74
+#define ma_thr_db_ch3__len 8
+#define ma_thr_db_ch3__mask 0xff
+#define ma_thr_db_ch3__shift 0x00
+#define ma_thr_db_ch3__reset 0x18
+//---------------------------------------------------------------thr_lsb_ch0---
+// thr lsb ch0
+#define ma_thr_lsb_ch0__a 75
+#define ma_thr_lsb_ch0__len 2
+#define ma_thr_lsb_ch0__mask 0x03
+#define ma_thr_lsb_ch0__shift 0x00
+#define ma_thr_lsb_ch0__reset 0x00
+//---------------------------------------------------------------thr_lsb_ch1---
+// thr lsb ch1
+#define ma_thr_lsb_ch1__a 75
+#define ma_thr_lsb_ch1__len 2
+#define ma_thr_lsb_ch1__mask 0x0c
+#define ma_thr_lsb_ch1__shift 0x02
+#define ma_thr_lsb_ch1__reset 0x00
+//---------------------------------------------------------------thr_lsb_ch2---
+// thr lsb ch2 1/4 db step
+#define ma_thr_lsb_ch2__a 75
+#define ma_thr_lsb_ch2__len 2
+#define ma_thr_lsb_ch2__mask 0x30
+#define ma_thr_lsb_ch2__shift 0x04
+#define ma_thr_lsb_ch2__reset 0x00
+//---------------------------------------------------------------thr_lsb_ch3---
+// threshold lsb ch3
+#define ma_thr_lsb_ch3__a 75
+#define ma_thr_lsb_ch3__len 2
+#define ma_thr_lsb_ch3__mask 0xc0
+#define ma_thr_lsb_ch3__shift 0x06
+#define ma_thr_lsb_ch3__reset 0x00
+//-----------------------------------------------------------dcu_mon0.pm_mon---
+// power mode monitor channel 0
+#define ma_dcu_mon0__pm_mon__a 96
+#define ma_dcu_mon0__pm_mon__len 2
+#define ma_dcu_mon0__pm_mon__mask 0x03
+#define ma_dcu_mon0__pm_mon__shift 0x00
+#define ma_dcu_mon0__pm_mon__reset 0x00
+//-----------------------------------------------------dcu_mon0.freqmode_mon---
+// frequence mode monitor channel 0
+#define ma_dcu_mon0__freqmode_mon__a 96
+#define ma_dcu_mon0__freqmode_mon__len 3
+#define ma_dcu_mon0__freqmode_mon__mask 0x70
+#define ma_dcu_mon0__freqmode_mon__shift 0x04
+#define ma_dcu_mon0__freqmode_mon__reset 0x00
+//-------------------------------------------------------dcu_mon0.pps_passed---
+// dcu0 pps completion indicator
+#define ma_dcu_mon0__pps_passed__a 96
+#define ma_dcu_mon0__pps_passed__len 1
+#define ma_dcu_mon0__pps_passed__mask 0x80
+#define ma_dcu_mon0__pps_passed__shift 0x07
+#define ma_dcu_mon0__pps_passed__reset 0x00
+//----------------------------------------------------------dcu_mon0.ocp_mon---
+// ocp monitor channel 0
+#define ma_dcu_mon0__ocp_mon__a 97
+#define ma_dcu_mon0__ocp_mon__len 1
+#define ma_dcu_mon0__ocp_mon__mask 0x01
+#define ma_dcu_mon0__ocp_mon__shift 0x00
+#define ma_dcu_mon0__ocp_mon__reset 0x00
+//--------------------------------------------------------dcu_mon0.vcfly1_ok---
+// cfly1 protection monitor channel 0.
+#define ma_dcu_mon0__vcfly1_ok__a 97
+#define ma_dcu_mon0__vcfly1_ok__len 1
+#define ma_dcu_mon0__vcfly1_ok__mask 0x02
+#define ma_dcu_mon0__vcfly1_ok__shift 0x01
+#define ma_dcu_mon0__vcfly1_ok__reset 0x00
+//--------------------------------------------------------dcu_mon0.vcfly2_ok---
+// cfly2 protection monitor channel 0.
+#define ma_dcu_mon0__vcfly2_ok__a 97
+#define ma_dcu_mon0__vcfly2_ok__len 1
+#define ma_dcu_mon0__vcfly2_ok__mask 0x04
+#define ma_dcu_mon0__vcfly2_ok__shift 0x02
+#define ma_dcu_mon0__vcfly2_ok__reset 0x00
+//----------------------------------------------------------dcu_mon0.pvdd_ok---
+// dcu0 pvdd monitor
+#define ma_dcu_mon0__pvdd_ok__a 97
+#define ma_dcu_mon0__pvdd_ok__len 1
+#define ma_dcu_mon0__pvdd_ok__mask 0x08
+#define ma_dcu_mon0__pvdd_ok__shift 0x03
+#define ma_dcu_mon0__pvdd_ok__reset 0x00
+//-----------------------------------------------------------dcu_mon0.vdd_ok---
+// dcu0 vdd monitor
+#define ma_dcu_mon0__vdd_ok__a 97
+#define ma_dcu_mon0__vdd_ok__len 1
+#define ma_dcu_mon0__vdd_ok__mask 0x10
+#define ma_dcu_mon0__vdd_ok__shift 0x04
+#define ma_dcu_mon0__vdd_ok__reset 0x00
+//-------------------------------------------------------------dcu_mon0.mute---
+// dcu0 mute monitor
+#define ma_dcu_mon0__mute__a 97
+#define ma_dcu_mon0__mute__len 1
+#define ma_dcu_mon0__mute__mask 0x20
+#define ma_dcu_mon0__mute__shift 0x05
+#define ma_dcu_mon0__mute__reset 0x00
+//------------------------------------------------------------dcu_mon0.m_mon---
+// m sense monitor channel 0
+#define ma_dcu_mon0__m_mon__a 98
+#define ma_dcu_mon0__m_mon__len 8
+#define ma_dcu_mon0__m_mon__mask 0xff
+#define ma_dcu_mon0__m_mon__shift 0x00
+#define ma_dcu_mon0__m_mon__reset 0x00
+//-----------------------------------------------------------dcu_mon1.pm_mon---
+// power mode monitor channel 1
+#define ma_dcu_mon1__pm_mon__a 100
+#define ma_dcu_mon1__pm_mon__len 2
+#define ma_dcu_mon1__pm_mon__mask 0x03
+#define ma_dcu_mon1__pm_mon__shift 0x00
+#define ma_dcu_mon1__pm_mon__reset 0x00
+//-----------------------------------------------------dcu_mon1.freqmode_mon---
+// frequence mode monitor channel 1
+#define ma_dcu_mon1__freqmode_mon__a 100
+#define ma_dcu_mon1__freqmode_mon__len 3
+#define ma_dcu_mon1__freqmode_mon__mask 0x70
+#define ma_dcu_mon1__freqmode_mon__shift 0x04
+#define ma_dcu_mon1__freqmode_mon__reset 0x00
+//-------------------------------------------------------dcu_mon1.pps_passed---
+// dcu1 pps completion indicator
+#define ma_dcu_mon1__pps_passed__a 100
+#define ma_dcu_mon1__pps_passed__len 1
+#define ma_dcu_mon1__pps_passed__mask 0x80
+#define ma_dcu_mon1__pps_passed__shift 0x07
+#define ma_dcu_mon1__pps_passed__reset 0x00
+//----------------------------------------------------------dcu_mon1.ocp_mon---
+// ocp monitor channel 1
+#define ma_dcu_mon1__ocp_mon__a 101
+#define ma_dcu_mon1__ocp_mon__len 1
+#define ma_dcu_mon1__ocp_mon__mask 0x01
+#define ma_dcu_mon1__ocp_mon__shift 0x00
+#define ma_dcu_mon1__ocp_mon__reset 0x00
+//--------------------------------------------------------dcu_mon1.vcfly1_ok---
+// cfly1 protcetion monitor channel 1
+#define ma_dcu_mon1__vcfly1_ok__a 101
+#define ma_dcu_mon1__vcfly1_ok__len 1
+#define ma_dcu_mon1__vcfly1_ok__mask 0x02
+#define ma_dcu_mon1__vcfly1_ok__shift 0x01
+#define ma_dcu_mon1__vcfly1_ok__reset 0x00
+//--------------------------------------------------------dcu_mon1.vcfly2_ok---
+// cfly2 protection monitor channel 1
+#define ma_dcu_mon1__vcfly2_ok__a 101
+#define ma_dcu_mon1__vcfly2_ok__len 1
+#define ma_dcu_mon1__vcfly2_ok__mask 0x04
+#define ma_dcu_mon1__vcfly2_ok__shift 0x02
+#define ma_dcu_mon1__vcfly2_ok__reset 0x00
+//----------------------------------------------------------dcu_mon1.pvdd_ok---
+// dcu1 pvdd monitor
+#define ma_dcu_mon1__pvdd_ok__a 101
+#define ma_dcu_mon1__pvdd_ok__len 1
+#define ma_dcu_mon1__pvdd_ok__mask 0x08
+#define ma_dcu_mon1__pvdd_ok__shift 0x03
+#define ma_dcu_mon1__pvdd_ok__reset 0x00
+//-----------------------------------------------------------dcu_mon1.vdd_ok---
+// dcu1 vdd monitor
+#define ma_dcu_mon1__vdd_ok__a 101
+#define ma_dcu_mon1__vdd_ok__len 1
+#define ma_dcu_mon1__vdd_ok__mask 0x10
+#define ma_dcu_mon1__vdd_ok__shift 0x04
+#define ma_dcu_mon1__vdd_ok__reset 0x00
+//-------------------------------------------------------------dcu_mon1.mute---
+// dcu1 mute monitor
+#define ma_dcu_mon1__mute__a 101
+#define ma_dcu_mon1__mute__len 1
+#define ma_dcu_mon1__mute__mask 0x20
+#define ma_dcu_mon1__mute__shift 0x05
+#define ma_dcu_mon1__mute__reset 0x00
+//------------------------------------------------------------dcu_mon1.m_mon---
+// m sense monitor channel 1
+#define ma_dcu_mon1__m_mon__a 102
+#define ma_dcu_mon1__m_mon__len 8
+#define ma_dcu_mon1__m_mon__mask 0xff
+#define ma_dcu_mon1__m_mon__shift 0x00
+#define ma_dcu_mon1__m_mon__reset 0x00
+//--------------------------------------------------------dcu_mon0.sw_enable---
+// dcu0 switch enable monitor
+#define ma_dcu_mon0__sw_enable__a 104
+#define ma_dcu_mon0__sw_enable__len 1
+#define ma_dcu_mon0__sw_enable__mask 0x40
+#define ma_dcu_mon0__sw_enable__shift 0x06
+#define ma_dcu_mon0__sw_enable__reset 0x00
+//--------------------------------------------------------dcu_mon1.sw_enable---
+// dcu1 switch enable monitor
+#define ma_dcu_mon1__sw_enable__a 104
+#define ma_dcu_mon1__sw_enable__len 1
+#define ma_dcu_mon1__sw_enable__mask 0x80
+#define ma_dcu_mon1__sw_enable__shift 0x07
+#define ma_dcu_mon1__sw_enable__reset 0x00
+//------------------------------------------------------------hvboot0_ok_mon---
+// hvboot0_ok for test/debug
+#define ma_hvboot0_ok_mon__a 105
+#define ma_hvboot0_ok_mon__len 1
+#define ma_hvboot0_ok_mon__mask 0x40
+#define ma_hvboot0_ok_mon__shift 0x06
+#define ma_hvboot0_ok_mon__reset 0x00
+//------------------------------------------------------------hvboot1_ok_mon---
+// hvboot1_ok for test/debug
+#define ma_hvboot1_ok_mon__a 105
+#define ma_hvboot1_ok_mon__len 1
+#define ma_hvboot1_ok_mon__mask 0x80
+#define ma_hvboot1_ok_mon__shift 0x07
+#define ma_hvboot1_ok_mon__reset 0x00
+//-----------------------------------------------------------------error_acc---
+// accumulated errors,  at and after triggering
+#define ma_error_acc__a 109
+#define ma_error_acc__len 8
+#define ma_error_acc__mask 0xff
+#define ma_error_acc__shift 0x00
+#define ma_error_acc__reset 0x00
+//-------------------------------------------------------------i2s_data_rate---
+// detected i2s data rate: 00/01/10 = x1/x2/x4
+#define ma_i2s_data_rate__a 116
+#define ma_i2s_data_rate__len 2
+#define ma_i2s_data_rate__mask 0x03
+#define ma_i2s_data_rate__shift 0x00
+#define ma_i2s_data_rate__reset 0x00
+//---------------------------------------------------------audio_in_mode_mon---
+// audio input mode monitor
+#define ma_audio_in_mode_mon__a 116
+#define ma_audio_in_mode_mon__len 3
+#define ma_audio_in_mode_mon__mask 0x1c
+#define ma_audio_in_mode_mon__shift 0x02
+#define ma_audio_in_mode_mon__reset 0x00
+//------------------------------------------------------------------msel_mon---
+// msel[2:0] monitor register
+#define ma_msel_mon__a 117
+#define ma_msel_mon__len 3
+#define ma_msel_mon__mask 0x07
+#define ma_msel_mon__shift 0x00
+#define ma_msel_mon__reset 0x00
+//---------------------------------------------------------------------error---
+// current error flag monitor reg - for app. ctrl.
+#define ma_error__a 124
+#define ma_error__len 8
+#define ma_error__mask 0xff
+#define ma_error__shift 0x00
+#define ma_error__reset 0x00
+//----------------------------------------------------audio_proc_limiter_mon---
+// b7-b4: channel 3-0 limiter active
+#define ma_audio_proc_limiter_mon__a 126
+#define ma_audio_proc_limiter_mon__len 4
+#define ma_audio_proc_limiter_mon__mask 0xf0
+#define ma_audio_proc_limiter_mon__shift 0x04
+#define ma_audio_proc_limiter_mon__reset 0x00
+//-------------------------------------------------------audio_proc_clip_mon---
+// b3-b0: channel 3-0 clipping monitor
+#define ma_audio_proc_clip_mon__a 126
+#define ma_audio_proc_clip_mon__len 4
+#define ma_audio_proc_clip_mon__mask 0x0f
+#define ma_audio_proc_clip_mon__shift 0x00
+#define ma_audio_proc_clip_mon__reset 0x00
+#endif
+
+#define SOC_ENUM_ERR(xname, xenum)\
+{	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname),\
+	.access = SNDRV_CTL_ELEM_ACCESS_READ,\
+	.info = snd_soc_info_enum_double,\
+	.get = snd_soc_get_enum_double, .put = snd_soc_put_enum_double,\
+	.private_value = (unsigned long)&(xenum) }
+
+static struct i2c_client *i2c;
+
+struct ma120x0p_priv {
+	struct regmap *regmap;
+	int mclk_div;
+	struct snd_soc_component *component;
+	struct gpio_desc *enable_gpio;
+	struct gpio_desc *mute_gpio;
+	struct gpio_desc *booster_gpio;
+	struct gpio_desc *error_gpio;
+};
+
+static struct ma120x0p_priv *priv_data;
+
+//Used to share the IRQ number within this file
+static unsigned int irqNumber;
+
+// Function prototype for the custom IRQ handler function
+static irqreturn_t ma120x0p_irq_handler(int irq, void *data);
+
+//Alsa Controls
+static const char * const limenable_text[] = {"Bypassed", "Enabled"};
+static const char * const limatack_text[] = {"Slow", "Normal", "Fast"};
+static const char * const limrelease_text[] = {"Slow", "Normal", "Fast"};
+
+static const char * const err_flycap_text[] = {"Ok", "Error"};
+static const char * const err_overcurr_text[] = {"Ok", "Error"};
+static const char * const err_pllerr_text[] = {"Ok", "Error"};
+static const char * const err_pvddunder_text[] = {"Ok", "Error"};
+static const char * const err_overtempw_text[] = {"Ok", "Error"};
+static const char * const err_overtempe_text[] = {"Ok", "Error"};
+static const char * const err_pinlowimp_text[] = {"Ok", "Error"};
+static const char * const err_dcprot_text[] = {"Ok", "Error"};
+
+static const char * const pwr_mode_prof_text[] = {"PMF0", "PMF1", "PMF2",
+"PMF3", "PMF4"};
+
+static const struct soc_enum lim_enable_ctrl =
+	SOC_ENUM_SINGLE(ma_audio_proc_limiterenable__a,
+		ma_audio_proc_limiterenable__shift,
+		ma_audio_proc_limiterenable__len + 1,
+		limenable_text);
+static const struct soc_enum limatack_ctrl =
+	SOC_ENUM_SINGLE(ma_audio_proc_attack__a,
+		ma_audio_proc_attack__shift,
+		ma_audio_proc_attack__len + 1,
+		limatack_text);
+static const struct soc_enum limrelease_ctrl =
+	SOC_ENUM_SINGLE(ma_audio_proc_release__a,
+		ma_audio_proc_release__shift,
+		ma_audio_proc_release__len + 1,
+		limrelease_text);
+static const struct soc_enum err_flycap_ctrl =
+	SOC_ENUM_SINGLE(ma_error__a, 0, 3, err_flycap_text);
+static const struct soc_enum err_overcurr_ctrl =
+	SOC_ENUM_SINGLE(ma_error__a, 1, 3, err_overcurr_text);
+static const struct soc_enum err_pllerr_ctrl =
+	SOC_ENUM_SINGLE(ma_error__a, 2, 3, err_pllerr_text);
+static const struct soc_enum err_pvddunder_ctrl =
+	SOC_ENUM_SINGLE(ma_error__a, 3, 3, err_pvddunder_text);
+static const struct soc_enum err_overtempw_ctrl =
+	SOC_ENUM_SINGLE(ma_error__a, 4, 3, err_overtempw_text);
+static const struct soc_enum err_overtempe_ctrl =
+	SOC_ENUM_SINGLE(ma_error__a, 5, 3, err_overtempe_text);
+static const struct soc_enum err_pinlowimp_ctrl =
+	SOC_ENUM_SINGLE(ma_error__a, 6, 3, err_pinlowimp_text);
+static const struct soc_enum err_dcprot_ctrl =
+	SOC_ENUM_SINGLE(ma_error__a, 7, 3, err_dcprot_text);
+static const struct soc_enum pwr_mode_prof_ctrl =
+	SOC_ENUM_SINGLE(ma_pmprofile__a, ma_pmprofile__shift, 5,
+		pwr_mode_prof_text);
+
+static const char * const pwr_mode_texts[] = {
+		"Dynamic power mode",
+		"Power mode 1",
+		"Power mode 2",
+		"Power mode 3",
+	};
+
+static const int pwr_mode_values[] = {
+		0x10,
+		0x50,
+		0x60,
+		0x70,
+	};
+
+static const SOC_VALUE_ENUM_SINGLE_DECL(pwr_mode_ctrl,
+	ma_pm_man__a, 0, 0x70,
+	pwr_mode_texts,
+	pwr_mode_values);
+
+static const DECLARE_TLV_DB_SCALE(ma120x0p_vol_tlv, -5000, 100,  0);
+static const DECLARE_TLV_DB_SCALE(ma120x0p_lim_tlv, -5000, 100,  0);
+static const DECLARE_TLV_DB_SCALE(ma120x0p_lr_tlv, -5000, 100,  0);
+
+static const struct snd_kcontrol_new ma120x0p_snd_controls[] = {
+	//Master Volume
+	SOC_SINGLE_RANGE_TLV("A.Mstr Vol Volume",
+		ma_vol_db_master__a, 0, 0x18, 0x4a, 1, ma120x0p_vol_tlv),
+
+	//L-R Volume ch0
+	SOC_SINGLE_RANGE_TLV("B.L Vol Volume",
+		ma_vol_db_ch0__a, 0, 0x18, 0x4a, 1, ma120x0p_lr_tlv),
+	SOC_SINGLE_RANGE_TLV("C.R Vol Volume",
+		ma_vol_db_ch1__a, 0, 0x18, 0x4a, 1, ma120x0p_lr_tlv),
+
+	//L-R Limiter Threshold ch0-ch1
+	SOC_DOUBLE_R_RANGE_TLV("D.Lim thresh Volume",
+		ma_thr_db_ch0__a, ma_thr_db_ch1__a, 0, 0x0e, 0x4a, 1,
+		ma120x0p_lim_tlv),
+
+	//Enum Switches/Selectors
+	//SOC_ENUM("E.AudioProc Mute", audioproc_mute_ctrl),
+	SOC_ENUM("F.Limiter Enable", lim_enable_ctrl),
+	SOC_ENUM("G.Limiter Attck", limatack_ctrl),
+	SOC_ENUM("H.Limiter Rls", limrelease_ctrl),
+
+	//Enum Error Monitor (read-only)
+	SOC_ENUM_ERR("I.Err flycap", err_flycap_ctrl),
+	SOC_ENUM_ERR("J.Err overcurr", err_overcurr_ctrl),
+	SOC_ENUM_ERR("K.Err pllerr", err_pllerr_ctrl),
+	SOC_ENUM_ERR("L.Err pvddunder", err_pvddunder_ctrl),
+	SOC_ENUM_ERR("M.Err overtempw", err_overtempw_ctrl),
+	SOC_ENUM_ERR("N.Err overtempe", err_overtempe_ctrl),
+	SOC_ENUM_ERR("O.Err pinlowimp", err_pinlowimp_ctrl),
+	SOC_ENUM_ERR("P.Err dcprot", err_dcprot_ctrl),
+
+	//Power modes profiles
+	SOC_ENUM("Q.PM Prof", pwr_mode_prof_ctrl),
+
+	// Power mode selection (Dynamic,1,2,3)
+	SOC_ENUM("R.Power Mode", pwr_mode_ctrl),
+};
+
+//Machine Driver
+static int ma120x0p_hw_params(struct snd_pcm_substream *substream,
+	struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+	u16 blen = 0x00;
+
+	struct snd_soc_component *component = dai->component;
+
+	priv_data->component = component;
+
+	switch (params_format(params)) {
+	case SNDRV_PCM_FORMAT_S16_LE:
+		blen = 0x10;
+		break;
+	case SNDRV_PCM_FORMAT_S24_LE:
+		blen = 0x00;
+		break;
+	case SNDRV_PCM_FORMAT_S32_LE:
+		blen = 0x00;
+		break;
+	default:
+		dev_err(dai->dev, "Unsupported word length: %u\n",
+		params_format(params));
+		return -EINVAL;
+	}
+
+	// set word length
+	snd_soc_component_update_bits(component, ma_i2s_framesize__a,
+		ma_i2s_framesize__mask, blen);
+
+	return 0;
+}
+
+static int ma120x0p_mute_stream(struct snd_soc_dai *dai, int mute, int stream)
+{
+	int val = 0;
+
+	struct ma120x0p_priv *ma120x0p;
+
+	struct snd_soc_component *component = dai->component;
+
+	ma120x0p = snd_soc_component_get_drvdata(component);
+
+	if (mute)
+		val = 0;
+	else
+		val = 1;
+
+	gpiod_set_value_cansleep(priv_data->mute_gpio, val);
+
+	return 0;
+}
+
+static const struct snd_soc_dai_ops ma120x0p_dai_ops = {
+	.hw_params		=	ma120x0p_hw_params,
+	.mute_stream	=	ma120x0p_mute_stream,
+};
+
+static struct snd_soc_dai_driver ma120x0p_dai = {
+	.name		= "ma120x0p-amp",
+	.playback	=	{
+		.stream_name	= "Playback",
+		.channels_min	= 2,
+		.channels_max	= 2,
+		.rates = SNDRV_PCM_RATE_CONTINUOUS,
+		.rate_min = 44100,
+		.rate_max = 96000,
+		.formats = SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE
+	},
+	.ops        = &ma120x0p_dai_ops,
+};
+
+//Codec Driver
+static int ma120x0p_clear_err(struct snd_soc_component *component)
+{
+	int ret = 0;
+
+	struct ma120x0p_priv *ma120x0p;
+
+	ma120x0p = snd_soc_component_get_drvdata(component);
+
+	ret = snd_soc_component_update_bits(component,
+		ma_eh_clear__a, ma_eh_clear__mask, 0x00);
+	if (ret < 0)
+		return ret;
+
+	ret = snd_soc_component_update_bits(component,
+		ma_eh_clear__a, ma_eh_clear__mask, 0x04);
+	if (ret < 0)
+		return ret;
+
+	ret = snd_soc_component_update_bits(component,
+		ma_eh_clear__a, ma_eh_clear__mask, 0x00);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static void ma120x0p_remove(struct snd_soc_component *component)
+{
+	struct ma120x0p_priv *ma120x0p;
+
+	ma120x0p = snd_soc_component_get_drvdata(component);
+}
+
+static int ma120x0p_probe(struct snd_soc_component *component)
+{
+	struct ma120x0p_priv *ma120x0p;
+
+	int ret = 0;
+
+	i2c = container_of(component->dev, struct i2c_client, dev);
+
+	ma120x0p = snd_soc_component_get_drvdata(component);
+
+	//Reset error
+	ma120x0p_clear_err(component);
+	if (ret < 0)
+		return ret;
+
+	// set serial audio format I2S and enable audio processor
+	ret = snd_soc_component_write(component, ma_i2s_format__a, 0x08);
+	if (ret < 0)
+		return ret;
+
+	// Enable audio limiter
+	ret = snd_soc_component_update_bits(component,
+		ma_audio_proc_limiterenable__a,
+		ma_audio_proc_limiterenable__mask, 0x40);
+	if (ret < 0)
+		return ret;
+
+	// Set lim attack to fast
+	ret = snd_soc_component_update_bits(component,
+		ma_audio_proc_attack__a, ma_audio_proc_attack__mask, 0x80);
+	if (ret < 0)
+		return ret;
+
+	// Set lim attack to low
+	ret = snd_soc_component_update_bits(component,
+		ma_audio_proc_release__a, ma_audio_proc_release__mask, 0x00);
+	if (ret < 0)
+		return ret;
+
+	// set volume to 0dB
+	ret = snd_soc_component_write(component, ma_vol_db_master__a, 0x18);
+	if (ret < 0)
+		return ret;
+
+	// set ch0 lim thresh to -15dB
+	ret = snd_soc_component_write(component, ma_thr_db_ch0__a, 0x27);
+	if (ret < 0)
+		return ret;
+
+	// set ch1 lim thresh to -15dB
+	ret = snd_soc_component_write(component, ma_thr_db_ch1__a, 0x27);
+	if (ret < 0)
+		return ret;
+
+	//Check for errors
+	ret = snd_soc_component_test_bits(component, ma_error_acc__a, 0x00, 0);
+	if (ret < 0)
+		return ret;
+	ret = snd_soc_component_test_bits(component, ma_error_acc__a, 0x01, 0);
+	if (ret < 0)
+		return ret;
+	ret = snd_soc_component_test_bits(component, ma_error_acc__a, 0x02, 0);
+	if (ret < 0)
+		return ret;
+	ret = snd_soc_component_test_bits(component, ma_error_acc__a, 0x08, 0);
+	if (ret < 0)
+		return ret;
+	ret = snd_soc_component_test_bits(component, ma_error_acc__a, 0x10, 0);
+	if (ret < 0)
+		return ret;
+	ret = snd_soc_component_test_bits(component, ma_error_acc__a, 0x20, 0);
+	if (ret < 0)
+		return ret;
+	ret = snd_soc_component_test_bits(component, ma_error_acc__a, 0x40, 0);
+	if (ret < 0)
+		return ret;
+	ret = snd_soc_component_test_bits(component, ma_error_acc__a, 0x80, 0);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static int ma120x0p_set_bias_level(struct snd_soc_component *component,
+	enum snd_soc_bias_level level)
+{
+	int ret = 0;
+
+	struct ma120x0p_priv *ma120x0p;
+
+	ma120x0p = snd_soc_component_get_drvdata(component);
+
+	switch (level) {
+	case SND_SOC_BIAS_ON:
+		break;
+
+	case SND_SOC_BIAS_PREPARE:
+		break;
+
+	case SND_SOC_BIAS_STANDBY:
+		ret = gpiod_get_value_cansleep(priv_data->enable_gpio);
+		if (ret != 0) {
+			dev_err(component->dev, "Device ma120x0p disabled in STANDBY BIAS: %d\n",
+			ret);
+			return ret;
+		}
+		break;
+
+	case SND_SOC_BIAS_OFF:
+		break;
+	}
+
+	return 0;
+}
+
+static const struct snd_soc_dapm_widget ma120x0p_dapm_widgets[] = {
+	SND_SOC_DAPM_OUTPUT("OUT_A"),
+	SND_SOC_DAPM_OUTPUT("OUT_B"),
+};
+
+static const struct snd_soc_dapm_route ma120x0p_dapm_routes[] = {
+	{ "OUT_B",  NULL, "Playback" },
+	{ "OUT_A",  NULL, "Playback" },
+};
+
+static const struct snd_soc_component_driver ma120x0p_component_driver = {
+	.probe = ma120x0p_probe,
+	.remove = ma120x0p_remove,
+	.set_bias_level = ma120x0p_set_bias_level,
+	.dapm_widgets		= ma120x0p_dapm_widgets,
+	.num_dapm_widgets	= ARRAY_SIZE(ma120x0p_dapm_widgets),
+	.dapm_routes		= ma120x0p_dapm_routes,
+	.num_dapm_routes	= ARRAY_SIZE(ma120x0p_dapm_routes),
+	.controls = ma120x0p_snd_controls,
+	.num_controls = ARRAY_SIZE(ma120x0p_snd_controls),
+	.use_pmdown_time	= 1,
+	.endianness		= 1,
+	.non_legacy_dai_naming	= 1,
+};
+
+//I2C Driver
+static const struct reg_default ma120x0p_reg_defaults[] = {
+	{	0x01,	0x3c	},
+};
+
+static bool ma120x0p_reg_volatile(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case ma_error__a:
+			return true;
+	default:
+			return false;
+	}
+}
+
+static const struct of_device_id ma120x0p_of_match[] = {
+	{ .compatible = "ma,ma120x0p", },
+	{ }
+};
+
+MODULE_DEVICE_TABLE(of, ma120x0p_of_match);
+
+static struct regmap_config ma120x0p_regmap_config = {
+	.reg_bits = 8,
+	.val_bits = 8,
+
+	.max_register = 255,
+	.volatile_reg = ma120x0p_reg_volatile,
+
+	.cache_type = REGCACHE_RBTREE,
+	.reg_defaults = ma120x0p_reg_defaults,
+	.num_reg_defaults = ARRAY_SIZE(ma120x0p_reg_defaults),
+};
+
+static int ma120x0p_i2c_probe(struct i2c_client *i2c,
+	const struct i2c_device_id *id)
+{
+	int ret;
+
+	priv_data = devm_kzalloc(&i2c->dev, sizeof(*priv_data), GFP_KERNEL);
+	if (!priv_data)
+		return -ENOMEM;
+	i2c_set_clientdata(i2c, priv_data);
+
+	priv_data->regmap = devm_regmap_init_i2c(i2c, &ma120x0p_regmap_config);
+	if (IS_ERR(priv_data->regmap)) {
+		ret = PTR_ERR(priv_data->regmap);
+		return ret;
+	}
+
+	//Startup sequence
+
+	//Make sure the device is muted
+	priv_data->mute_gpio = devm_gpiod_get_optional(&i2c->dev, "mute_gp",
+		GPIOD_OUT_LOW);
+	if (IS_ERR(priv_data->mute_gpio)) {
+		ret = PTR_ERR(priv_data->mute_gpio);
+		dev_err(&i2c->dev, "Failed to get mute gpio line: %d\n", ret);
+		return ret;
+	}
+	msleep(50);
+
+// MA120xx0P devices are usually powered by an integrated boost converter.
+// An option GPIO control line is provided to enable the booster properly and
+// in sync with the enable and mute GPIO lines.
+	priv_data->booster_gpio = devm_gpiod_get_optional(&i2c->dev,
+		"booster_gp", GPIOD_OUT_LOW);
+	if (IS_ERR(priv_data->booster_gpio)) {
+		ret = PTR_ERR(priv_data->booster_gpio);
+		dev_err(&i2c->dev,
+		"Failed to get booster enable gpio line: %d\n", ret);
+		return ret;
+	}
+	msleep(50);
+
+	//Enable booster and wait 200ms until stable PVDD
+	gpiod_set_value_cansleep(priv_data->booster_gpio, 1);
+	msleep(200);
+
+	//Enable ma120x0pp
+	priv_data->enable_gpio = devm_gpiod_get_optional(&i2c->dev,
+		"enable_gp", GPIOD_OUT_LOW);
+	if (IS_ERR(priv_data->enable_gpio)) {
+		ret = PTR_ERR(priv_data->enable_gpio);
+		dev_err(&i2c->dev,
+		"Failed to get ma120x0p enable gpio line: %d\n", ret);
+		return ret;
+	}
+	msleep(50);
+
+	//Optional use of ma120x0pp error line as an interrupt trigger to
+	//platform GPIO.
+	//Get error input gpio ma120x0p
+	priv_data->error_gpio = devm_gpiod_get_optional(&i2c->dev,
+		 "error_gp", GPIOD_IN);
+	if (IS_ERR(priv_data->error_gpio)) {
+		ret = PTR_ERR(priv_data->error_gpio);
+		dev_err(&i2c->dev,
+			"Failed to get ma120x0p error gpio line: %d\n", ret);
+		return ret;
+	}
+
+	if (priv_data->error_gpio != NULL) {
+		irqNumber = gpiod_to_irq(priv_data->error_gpio);
+
+		ret = devm_request_threaded_irq(&i2c->dev,
+			 irqNumber, ma120x0p_irq_handler,
+			 NULL, IRQF_TRIGGER_FALLING,
+			 "ma120x0p", priv_data);
+		if (ret != 0)
+			dev_warn(&i2c->dev, "Failed to request IRQ: %d\n",
+				ret);
+	}
+
+	ret = devm_snd_soc_register_component(&i2c->dev,
+		&ma120x0p_component_driver, &ma120x0p_dai, 1);
+
+	return ret;
+}
+
+static irqreturn_t ma120x0p_irq_handler(int irq, void *data)
+{
+	gpiod_set_value_cansleep(priv_data->mute_gpio, 0);
+	gpiod_set_value_cansleep(priv_data->enable_gpio, 1);
+	return IRQ_HANDLED;
+}
+
+static int ma120x0p_i2c_remove(struct i2c_client *i2c)
+{
+	snd_soc_unregister_component(&i2c->dev);
+	i2c_set_clientdata(i2c, NULL);
+
+	gpiod_set_value_cansleep(priv_data->mute_gpio, 0);
+	msleep(30);
+	gpiod_set_value_cansleep(priv_data->enable_gpio, 1);
+	msleep(200);
+	gpiod_set_value_cansleep(priv_data->booster_gpio, 0);
+	msleep(200);
+
+	kfree(priv_data);
+
+	return 0;
+}
+
+static void ma120x0p_i2c_shutdown(struct i2c_client *i2c)
+{
+	snd_soc_unregister_component(&i2c->dev);
+	i2c_set_clientdata(i2c, NULL);
+
+	gpiod_set_value_cansleep(priv_data->mute_gpio, 0);
+	msleep(30);
+	gpiod_set_value_cansleep(priv_data->enable_gpio, 1);
+	msleep(200);
+	gpiod_set_value_cansleep(priv_data->booster_gpio, 0);
+	msleep(200);
+
+	kfree(priv_data);
+}
+
+static const struct i2c_device_id ma120x0p_i2c_id[] = {
+	{ "ma120x0p", 0 },
+	{ }
+};
+
+MODULE_DEVICE_TABLE(i2c, ma120x0p_i2c_id);
+
+static struct i2c_driver ma120x0p_i2c_driver = {
+	.driver = {
+		.name = "ma120x0p",
+		.owner = THIS_MODULE,
+		.of_match_table = ma120x0p_of_match,
+	},
+	.probe = ma120x0p_i2c_probe,
+	.remove = ma120x0p_i2c_remove,
+	.shutdown = ma120x0p_i2c_shutdown,
+	.id_table = ma120x0p_i2c_id
+};
+
+static int __init ma120x0p_modinit(void)
+{
+	int ret = 0;
+
+	ret = i2c_add_driver(&ma120x0p_i2c_driver);
+	if (ret != 0) {
+		pr_err("Failed to register MA120X0P I2C driver: %d\n", ret);
+		return ret;
+	}
+	return ret;
+}
+module_init(ma120x0p_modinit);
+
+static void __exit ma120x0p_exit(void)
+{
+	i2c_del_driver(&ma120x0p_i2c_driver);
+}
+module_exit(ma120x0p_exit);
+
+MODULE_AUTHOR("Ariel Muszkat ariel.muszkat@gmail.com>");
+MODULE_DESCRIPTION("ASoC driver for ma120x0p");
+MODULE_LICENSE("GPL v2");
diff -Nur --no-dereference linux-5.10.52-orig/sound/soc/codecs/Makefile linux-5.10.52-v7l+/sound/soc/codecs/Makefile
--- linux-5.10.52-orig/sound/soc/codecs/Makefile	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/sound/soc/codecs/Makefile	2021-07-25 16:46:40.187717149 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:99 @
 snd-soc-ics43432-objs := ics43432.o
 snd-soc-inno-rk3036-objs := inno_rk3036.o
 snd-soc-isabelle-objs := isabelle.o
+snd-soc-i-sabre-codec-objs := i-sabre-codec.o
 snd-soc-jz4740-codec-objs := jz4740.o
 snd-soc-jz4725b-codec-objs := jz4725b.o
 snd-soc-jz4770-codec-objs := jz4770.o
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:107 @
 snd-soc-lm4857-objs := lm4857.o
 snd-soc-lm49453-objs := lm49453.o
 snd-soc-lochnagar-sc-objs := lochnagar-sc.o
+snd-soc-ma120x0p-objs := ma120x0p.o
 snd-soc-madera-objs := madera.o
 snd-soc-max9759-objs := max9759.o
 snd-soc-max9768-objs := max9768.o
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:149 @
 snd-soc-pcm186x-objs := pcm186x.o
 snd-soc-pcm186x-i2c-objs := pcm186x-i2c.o
 snd-soc-pcm186x-spi-objs := pcm186x-spi.o
+snd-soc-pcm1794a-objs := pcm1794a.o
 snd-soc-pcm3008-objs := pcm3008.o
 snd-soc-pcm3060-objs := pcm3060.o
 snd-soc-pcm3060-i2c-objs := pcm3060-i2c.o
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:222 @
 snd-soc-tda7419-objs := tda7419.o
 snd-soc-tas2770-objs := tas2770.o
 snd-soc-tfa9879-objs := tfa9879.o
+snd-soc-tas5713-objs := tas5713.o
 snd-soc-tlv320aic23-objs := tlv320aic23.o
 snd-soc-tlv320aic23-i2c-objs := tlv320aic23-i2c.o
 snd-soc-tlv320aic23-spi-objs := tlv320aic23-spi.o
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:412 @
 obj-$(CONFIG_SND_SOC_ICS43432)	+= snd-soc-ics43432.o
 obj-$(CONFIG_SND_SOC_INNO_RK3036)	+= snd-soc-inno-rk3036.o
 obj-$(CONFIG_SND_SOC_ISABELLE)	+= snd-soc-isabelle.o
+obj-$(CONFIG_SND_SOC_I_SABRE_CODEC)	+= snd-soc-i-sabre-codec.o
 obj-$(CONFIG_SND_SOC_JZ4740_CODEC)	+= snd-soc-jz4740-codec.o
 obj-$(CONFIG_SND_SOC_JZ4725B_CODEC)	+= snd-soc-jz4725b-codec.o
 obj-$(CONFIG_SND_SOC_JZ4770_CODEC)	+= snd-soc-jz4770-codec.o
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:420 @
 obj-$(CONFIG_SND_SOC_LM4857)	+= snd-soc-lm4857.o
 obj-$(CONFIG_SND_SOC_LM49453)   += snd-soc-lm49453.o
 obj-$(CONFIG_SND_SOC_LOCHNAGAR_SC)	+= snd-soc-lochnagar-sc.o
+obj-$(CONFIG_SND_SOC_MA120X0P)   += snd-soc-ma120x0p.o
 obj-$(CONFIG_SND_SOC_MADERA)	+= snd-soc-madera.o
 obj-$(CONFIG_SND_SOC_MAX9759)	+= snd-soc-max9759.o
 obj-$(CONFIG_SND_SOC_MAX9768)	+= snd-soc-max9768.o
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:474 @
 obj-$(CONFIG_SND_SOC_PCM512x_I2C)	+= snd-soc-pcm512x-i2c.o
 obj-$(CONFIG_SND_SOC_PCM512x_SPI)	+= snd-soc-pcm512x-spi.o
 obj-$(CONFIG_SND_SOC_RK3328)	+= snd-soc-rk3328.o
+obj-$(CONFIG_SND_SOC_PCM1794A)	+= snd-soc-pcm1794a.o
 obj-$(CONFIG_SND_SOC_RL6231)	+= snd-soc-rl6231.o
 obj-$(CONFIG_SND_SOC_RL6347A)	+= snd-soc-rl6347a.o
 obj-$(CONFIG_SND_SOC_RT1011)	+= snd-soc-rt1011.o
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:536 @
 obj-$(CONFIG_SND_SOC_TDA7419)	+= snd-soc-tda7419.o
 obj-$(CONFIG_SND_SOC_TAS2770) += snd-soc-tas2770.o
 obj-$(CONFIG_SND_SOC_TFA9879)	+= snd-soc-tfa9879.o
+obj-$(CONFIG_SND_SOC_TAS5713)	+= snd-soc-tas5713.o
 obj-$(CONFIG_SND_SOC_TLV320AIC23)	+= snd-soc-tlv320aic23.o
 obj-$(CONFIG_SND_SOC_TLV320AIC23_I2C)	+= snd-soc-tlv320aic23-i2c.o
 obj-$(CONFIG_SND_SOC_TLV320AIC23_SPI)	+= snd-soc-tlv320aic23-spi.o
diff -Nur --no-dereference linux-5.10.52-orig/sound/soc/codecs/pcm1794a.c linux-5.10.52-v7l+/sound/soc/codecs/pcm1794a.c
--- linux-5.10.52-orig/sound/soc/codecs/pcm1794a.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/sound/soc/codecs/pcm1794a.c	2021-07-25 16:46:40.527711449 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/*
+ * Driver for the PCM1794A codec
+ *
+ * Author:	Florian Meier <florian.meier@koalo.de>
+ *		Copyright 2013
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#include <sound/soc.h>
+
+static struct snd_soc_dai_driver pcm1794a_dai = {
+	.name = "pcm1794a-hifi",
+	.playback = {
+		.channels_min = 2,
+		.channels_max = 2,
+		.rates = SNDRV_PCM_RATE_8000_192000,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE |
+			   SNDRV_PCM_FMTBIT_S24_LE
+	},
+};
+
+static struct snd_soc_component_driver soc_component_dev_pcm1794a;
+
+static int pcm1794a_probe(struct platform_device *pdev)
+{
+	return snd_soc_register_component(&pdev->dev, &soc_component_dev_pcm1794a,
+			&pcm1794a_dai, 1);
+}
+
+static int pcm1794a_remove(struct platform_device *pdev)
+{
+	snd_soc_unregister_component(&pdev->dev);
+	return 0;
+}
+
+static const struct of_device_id pcm1794a_of_match[] = {
+	{ .compatible = "ti,pcm1794a", },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, pcm1794a_of_match);
+
+static struct platform_driver pcm1794a_component_driver = {
+	.probe 		= pcm1794a_probe,
+	.remove 	= pcm1794a_remove,
+	.driver		= {
+		.name	= "pcm1794a-codec",
+		.owner	= THIS_MODULE,
+		.of_match_table = of_match_ptr(pcm1794a_of_match),
+	},
+};
+
+module_platform_driver(pcm1794a_component_driver);
+
+MODULE_DESCRIPTION("ASoC PCM1794A codec driver");
+MODULE_AUTHOR("Florian Meier <florian.meier@koalo.de>");
+MODULE_LICENSE("GPL v2");
diff -Nur --no-dereference linux-5.10.52-orig/sound/soc/codecs/pcm512x.c linux-5.10.52-v7l+/sound/soc/codecs/pcm512x.c
--- linux-5.10.52-orig/sound/soc/codecs/pcm512x.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/sound/soc/codecs/pcm512x.c	2021-07-25 16:46:40.527711449 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:537 @
 
 static const u32 pcm512x_dai_rates[] = {
 	8000, 11025, 16000, 22050, 32000, 44100, 48000, 64000,
-	88200, 96000, 176400, 192000, 384000,
+	88200, 96000, 176400, 192000, 352800, 384000,
 };
 
 static const struct snd_pcm_hw_constraint_list constraints_slave = {
diff -Nur --no-dereference linux-5.10.52-orig/sound/soc/codecs/tas5713.c linux-5.10.52-v7l+/sound/soc/codecs/tas5713.c
--- linux-5.10.52-orig/sound/soc/codecs/tas5713.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/sound/soc/codecs/tas5713.c	2021-07-25 16:46:40.687708767 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/*
+ * ASoC Driver for TAS5713
+ *
+ * Author:	Sebastian Eickhoff <basti.eickhoff@googlemail.com>
+ *		Copyright 2014
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/of_device.h>
+#include <linux/spi/spi.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/fs.h>
+#include <asm/uaccess.h>
+
+#include "tas5713.h"
+
+
+static struct i2c_client *i2c;
+
+struct tas5713_priv {
+	struct regmap *regmap;
+	int mclk_div;
+	struct snd_soc_component *component;
+};
+
+static struct tas5713_priv *priv_data;
+
+
+
+
+/*
+ *    _   _    ___   _      ___         _           _
+ *   /_\ | |  / __| /_\    / __|___ _ _| |_ _ _ ___| |___
+ *  / _ \| |__\__ \/ _ \  | (__/ _ \ ' \  _| '_/ _ \ (_-<
+ * /_/ \_\____|___/_/ \_\  \___\___/_||_\__|_| \___/_/__/
+ *
+ */
+
+static const DECLARE_TLV_DB_SCALE(tas5713_vol_tlv, -10000, 50, 1);
+
+
+static const struct snd_kcontrol_new tas5713_snd_controls[] = {
+	SOC_SINGLE_TLV  ("Master"    , TAS5713_VOL_MASTER, 0, 248, 1, tas5713_vol_tlv),
+	SOC_DOUBLE_R_TLV("Channels"  , TAS5713_VOL_CH1, TAS5713_VOL_CH2, 0, 248, 1, tas5713_vol_tlv)
+};
+
+
+
+
+/*
+ *  __  __         _    _            ___      _
+ * |  \/  |__ _ __| |_ (_)_ _  ___  |   \ _ _(_)_ _____ _ _
+ * | |\/| / _` / _| ' \| | ' \/ -_) | |) | '_| \ V / -_) '_|
+ * |_|  |_\__,_\__|_||_|_|_||_\___| |___/|_| |_|\_/\___|_|
+ *
+ */
+
+static int tas5713_hw_params(struct snd_pcm_substream *substream,
+			    struct snd_pcm_hw_params *params,
+			    struct snd_soc_dai *dai)
+{
+	u16 blen = 0x00;
+
+	struct snd_soc_component *component = dai->component;
+	priv_data->component = component;
+
+	switch (params_format(params)) {
+	case SNDRV_PCM_FORMAT_S16_LE:
+		blen = 0x03;
+		break;
+	case SNDRV_PCM_FORMAT_S20_3LE:
+		blen = 0x1;
+		break;
+	case SNDRV_PCM_FORMAT_S24_LE:
+		blen = 0x04;
+		break;
+	case SNDRV_PCM_FORMAT_S32_LE:
+		blen = 0x05;
+		break;
+	default:
+		dev_err(dai->dev, "Unsupported word length: %u\n",
+			params_format(params));
+		return -EINVAL;
+	}
+
+	// set word length
+	snd_soc_component_update_bits(component, TAS5713_SERIAL_DATA_INTERFACE, 0x7, blen);
+
+	return 0;
+}
+
+
+static int tas5713_mute_stream(struct snd_soc_dai *dai, int mute, int stream)
+{
+	unsigned int val = 0;
+
+	struct tas5713_priv *tas5713;
+	struct snd_soc_component *component = dai->component;
+	tas5713 = snd_soc_component_get_drvdata(component);
+
+	if (mute) {
+		val = TAS5713_SOFT_MUTE_ALL;
+	}
+
+	return regmap_write(tas5713->regmap, TAS5713_SOFT_MUTE, val);
+}
+
+
+static const struct snd_soc_dai_ops tas5713_dai_ops = {
+	.hw_params 		= tas5713_hw_params,
+	.mute_stream	= tas5713_mute_stream,
+};
+
+
+static struct snd_soc_dai_driver tas5713_dai = {
+	.name		= "tas5713-hifi",
+	.playback 	= {
+		.stream_name	= "Playback",
+		.channels_min	= 2,
+		.channels_max	= 2,
+		.rates		    = SNDRV_PCM_RATE_8000_48000,
+		.formats	    = (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE ),
+	},
+	.ops        = &tas5713_dai_ops,
+};
+
+
+
+
+/*
+ *   ___         _          ___      _
+ *  / __|___  __| |___ __  |   \ _ _(_)_ _____ _ _
+ * | (__/ _ \/ _` / -_) _| | |) | '_| \ V / -_) '_|
+ *  \___\___/\__,_\___\__| |___/|_| |_|\_/\___|_|
+ *
+ */
+
+static void tas5713_remove(struct snd_soc_component *component)
+{
+	struct tas5713_priv *tas5713;
+
+	tas5713 = snd_soc_component_get_drvdata(component);
+}
+
+
+static int tas5713_probe(struct snd_soc_component *component)
+{
+	struct tas5713_priv *tas5713;
+	int i, ret;
+
+	i2c = container_of(component->dev, struct i2c_client, dev);
+
+	tas5713 = snd_soc_component_get_drvdata(component);
+
+	// Reset error
+	ret = snd_soc_component_write(component, TAS5713_ERROR_STATUS, 0x00);
+	if (ret < 0) return ret;
+
+	// Trim oscillator
+	ret = snd_soc_component_write(component, TAS5713_OSC_TRIM, 0x00);
+	if (ret < 0) return ret;
+	msleep(1000);
+
+	// Reset error
+	ret = snd_soc_component_write(component, TAS5713_ERROR_STATUS, 0x00);
+	if (ret < 0) return ret;
+
+	// I2S 24bit
+	ret = snd_soc_component_write(component, TAS5713_SERIAL_DATA_INTERFACE, 0x05);
+	if (ret < 0) return ret;
+
+	// Unmute
+	ret = snd_soc_component_write(component, TAS5713_SYSTEM_CTRL2, 0x00);
+	if (ret < 0) return ret;
+	ret = snd_soc_component_write(component, TAS5713_SOFT_MUTE, 0x00);
+	if (ret < 0) return ret;
+
+	// Set volume to 0db
+	ret = snd_soc_component_write(component, TAS5713_VOL_MASTER, 0x00);
+	if (ret < 0) return ret;
+
+	// Now start programming the default initialization sequence
+	for (i = 0; i < ARRAY_SIZE(tas5713_init_sequence); ++i) {
+		ret = i2c_master_send(i2c,
+				     tas5713_init_sequence[i].data,
+				     tas5713_init_sequence[i].size);
+		if (ret < 0) {
+			printk(KERN_INFO "TAS5713 CODEC PROBE: InitSeq returns: %d\n", ret);
+		}
+	}
+
+	// Unmute
+	ret = snd_soc_component_write(component, TAS5713_SYSTEM_CTRL2, 0x00);
+	if (ret < 0) return ret;
+
+	return 0;
+}
+
+
+static struct snd_soc_component_driver soc_codec_dev_tas5713 = {
+	.probe = tas5713_probe,
+	.remove = tas5713_remove,
+	.controls = tas5713_snd_controls,
+	.num_controls = ARRAY_SIZE(tas5713_snd_controls),
+};
+
+
+
+
+/*
+ *   ___ ___ ___   ___      _
+ *  |_ _|_  ) __| |   \ _ _(_)_ _____ _ _
+ *   | | / / (__  | |) | '_| \ V / -_) '_|
+ *  |___/___\___| |___/|_| |_|\_/\___|_|
+ *
+ */
+
+static const struct reg_default tas5713_reg_defaults[] = {
+	{ 0x07 ,0x80 },     // R7  - VOL_MASTER    - -40dB
+	{ 0x08 ,  30 },     // R8  - VOL_CH1	   -   0dB
+	{ 0x09 ,  30 },     // R9  - VOL_CH2       -   0dB
+	{ 0x0A ,0x80 },     // R10 - VOL_HEADPHONE - -40dB
+};
+
+
+static bool tas5713_reg_volatile(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+		case TAS5713_DEVICE_ID:
+		case TAS5713_ERROR_STATUS:
+		case TAS5713_CLOCK_CTRL:
+			return true;
+	default:
+			return false;
+	}
+}
+
+
+static const struct of_device_id tas5713_of_match[] = {
+	{ .compatible = "ti,tas5713", },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, tas5713_of_match);
+
+
+static struct regmap_config tas5713_regmap_config = {
+	.reg_bits = 8,
+	.val_bits = 8,
+
+	.max_register = TAS5713_MAX_REGISTER,
+	.volatile_reg = tas5713_reg_volatile,
+
+	.cache_type = REGCACHE_RBTREE,
+	.reg_defaults = tas5713_reg_defaults,
+	.num_reg_defaults = ARRAY_SIZE(tas5713_reg_defaults),
+};
+
+
+static int tas5713_i2c_probe(struct i2c_client *i2c,
+			    const struct i2c_device_id *id)
+{
+	int ret;
+
+	priv_data = devm_kzalloc(&i2c->dev, sizeof *priv_data, GFP_KERNEL);
+	if (!priv_data)
+		return -ENOMEM;
+
+	priv_data->regmap = devm_regmap_init_i2c(i2c, &tas5713_regmap_config);
+	if (IS_ERR(priv_data->regmap)) {
+		ret = PTR_ERR(priv_data->regmap);
+		return ret;
+	}
+
+	i2c_set_clientdata(i2c, priv_data);
+
+	ret = snd_soc_register_component(&i2c->dev,
+				     &soc_codec_dev_tas5713, &tas5713_dai, 1);
+
+	return ret;
+}
+
+
+static int tas5713_i2c_remove(struct i2c_client *i2c)
+{
+	snd_soc_unregister_component(&i2c->dev);
+	i2c_set_clientdata(i2c, NULL);
+
+	kfree(priv_data);
+
+	return 0;
+}
+
+
+static const struct i2c_device_id tas5713_i2c_id[] = {
+	{ "tas5713", 0 },
+	{ }
+};
+
+MODULE_DEVICE_TABLE(i2c, tas5713_i2c_id);
+
+
+static struct i2c_driver tas5713_i2c_driver = {
+	.driver = {
+		.name = "tas5713",
+		.owner = THIS_MODULE,
+		.of_match_table = tas5713_of_match,
+	},
+	.probe = tas5713_i2c_probe,
+	.remove = tas5713_i2c_remove,
+	.id_table = tas5713_i2c_id
+};
+
+
+static int __init tas5713_modinit(void)
+{
+	int ret = 0;
+
+	ret = i2c_add_driver(&tas5713_i2c_driver);
+	if (ret) {
+		printk(KERN_ERR "Failed to register tas5713 I2C driver: %d\n",
+		       ret);
+	}
+
+	return ret;
+}
+module_init(tas5713_modinit);
+
+
+static void __exit tas5713_exit(void)
+{
+	i2c_del_driver(&tas5713_i2c_driver);
+}
+module_exit(tas5713_exit);
+
+
+MODULE_AUTHOR("Sebastian Eickhoff <basti.eickhoff@googlemail.com>");
+MODULE_DESCRIPTION("ASoC driver for TAS5713");
+MODULE_LICENSE("GPL v2");
diff -Nur --no-dereference linux-5.10.52-orig/sound/soc/codecs/tas5713.h linux-5.10.52-v7l+/sound/soc/codecs/tas5713.h
--- linux-5.10.52-orig/sound/soc/codecs/tas5713.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-5.10.52-v7l+/sound/soc/codecs/tas5713.h	2021-07-25 16:46:40.687708767 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:4 @
+/*
+ * ASoC Driver for TAS5713
+ *
+ * Author:      Sebastian Eickhoff <basti.eickhoff@googlemail.com>
+ *              Copyright 2014
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+
+#ifndef _TAS5713_H
+#define _TAS5713_H
+
+
+// TAS5713 I2C-bus register addresses
+
+#define TAS5713_CLOCK_CTRL              0x00
+#define TAS5713_DEVICE_ID               0x01
+#define TAS5713_ERROR_STATUS            0x02
+#define TAS5713_SYSTEM_CTRL1            0x03
+#define TAS5713_SERIAL_DATA_INTERFACE   0x04
+#define TAS5713_SYSTEM_CTRL2            0x05
+#define TAS5713_SOFT_MUTE               0x06
+#define TAS5713_VOL_MASTER              0x07
+#define TAS5713_VOL_CH1                 0x08
+#define TAS5713_VOL_CH2                 0x09
+#define TAS5713_VOL_HEADPHONE           0x0A
+#define TAS5713_VOL_CONFIG              0x0E
+#define TAS5713_MODULATION_LIMIT        0x10
+#define TAS5713_IC_DLY_CH1              0x11
+#define TAS5713_IC_DLY_CH2              0x12
+#define TAS5713_IC_DLY_CH3              0x13
+#define TAS5713_IC_DLY_CH4              0x14
+
+#define TAS5713_START_STOP_PERIOD       0x1A
+#define TAS5713_OSC_TRIM                0x1B
+#define TAS5713_BKND_ERR                0x1C
+
+#define TAS5713_INPUT_MUX               0x20
+#define TAS5713_SRC_SELECT_CH4          0x21
+#define TAS5713_PWM_MUX                 0x25
+
+#define TAS5713_CH1_BQ0                 0x29
+#define TAS5713_CH1_BQ1                 0x2A
+#define TAS5713_CH1_BQ2                 0x2B
+#define TAS5713_CH1_BQ3                 0x2C
+#define TAS5713_CH1_BQ4                 0x2D
+#define TAS5713_CH1_BQ5                 0x2E
+#define TAS5713_CH1_BQ6                 0x2F
+#define TAS5713_CH1_BQ7                 0x58
+#define TAS5713_CH1_BQ8                 0x59
+
+#define TAS5713_CH2_BQ0                 0x30
+#define TAS5713_CH2_BQ1                 0x31
+#define TAS5713_CH2_BQ2                 0x32
+#define TAS5713_CH2_BQ3                 0x33
+#define TAS5713_CH2_BQ4                 0x34
+#define TAS5713_CH2_BQ5                 0x35
+#define TAS5713_CH2_BQ6                 0x36
+#define TAS5713_CH2_BQ7                 0x5C
+#define TAS5713_CH2_BQ8                 0x5D
+
+#define TAS5713_CH4_BQ0                 0x5A
+#define TAS5713_CH4_BQ1                 0x5B
+#define TAS5713_CH3_BQ0                 0x5E
+#define TAS5713_CH3_BQ1                 0x5F
+
+#define TAS5713_DRC1_SOFTENING_FILTER_ALPHA_OMEGA       0x3B
+#define TAS5713_DRC1_ATTACK_RELEASE_RATE                0x3C
+#define TAS5713_DRC2_SOFTENING_FILTER_ALPHA_OMEGA       0x3E
+#define TAS5713_DRC2_ATTACK_RELEASE_RATE                0x3F
+#define TAS5713_DRC1_ATTACK_RELEASE_THRES               0x40
+#define TAS5713_DRC2_ATTACK_RELEASE_THRES               0x43
+#define TAS5713_DRC_CTRL                                0x46
+
+#define TAS5713_BANK_SW_CTRL            0x50
+#define TAS5713_CH1_OUTPUT_MIXER        0x51
+#define TAS5713_CH2_OUTPUT_MIXER        0x52
+#define TAS5713_CH1_INPUT_MIXER         0x53
+#define TAS5713_CH2_INPUT_MIXER         0x54
+#define TAS5713_OUTPUT_POST_SCALE       0x56
+#define TAS5713_OUTPUT_PRESCALE         0x57
+
+#define TAS5713_IDF_POST_SCALE          0x62
+
+#define TAS5713_CH1_INLINE_MIXER        0x70
+#define TAS5713_CH1_INLINE_DRC_EN_MIXER 0x71
+#define TAS5713_CH1_R_CHANNEL_MIXER     0x72
+#define TAS5713_CH1_L_CHANNEL_MIXER     0x73
+#define TAS5713_CH2_INLINE_MIXER        0x74
+#define TAS5713_CH2_INLINE_DRC_EN_MIXER 0x75
+#define TAS5713_CH2_L_CHANNEL_MIXER     0x76
+#define TAS5713_CH2_R_CHANNEL_MIXER     0x77
+
+#define TAS5713_UPDATE_DEV_ADDR_KEY     0xF8
+#define TAS5713_UPDATE_DEV_ADDR_REG     0xF9
+
+#define TAS5713_REGISTER_COUNT          0x46
+#define TAS5713_MAX_REGISTER            0xF9
+
+
+// Bitmasks for registers
+#define TAS5713_SOFT_MUTE_ALL           0x07
+
+
+
+struct tas5713_init_command {
+        const int size;
+        const char *const data;
+};
+
+static const struct tas5713_init_command tas5713_init_sequence[] = {
+
+        // Trim oscillator
+    { .size = 2,  .data = "\x1B\x00" },
+    // System control register 1 (0x03): block DC
+    { .size = 2,  .data = "\x03\x80" },
+    // Mute everything
+    { .size = 2,  .data = "\x05\x40" },
+    // Modulation limit register (0x10): 97.7%
+    { .size = 2,  .data = "\x10\x02" },
+    // Interchannel delay registers
+    // (0x11, 0x12, 0x13, and 0x14): BD mode
+    { .size = 2,  .data = "\x11\xB8" },
+    { .size = 2,  .data = "\x12\x60" },
+    { .size = 2,  .data = "\x13\xA0" },
+    { .size = 2,  .data = "\x14\x48" },
+    // PWM shutdown group register (0x19): no shutdown
+    { .size = 2,  .data = "\x19\x00" },
+    // Input multiplexer register (0x20): BD mode
+    { .size = 2,  .data = "\x20\x00\x89\x77\x72" },
+    // PWM output mux register (0x25)
+    // Channel 1 --> OUTA, channel 1 neg --> OUTB
+    // Channel 2 --> OUTC, channel 2 neg --> OUTD
+    { .size = 5,  .data = "\x25\x01\x02\x13\x45" },
+    // DRC control (0x46): DRC off
+    { .size = 5,  .data = "\x46\x00\x00\x00\x00" },
+    // BKND_ERR register (0x1C): 299ms reset period
+    { .size = 2,  .data = "\x1C\x07" },
+    // Mute channel 3
+    { .size = 2,  .data = "\x0A\xFF" },
+    // Volume configuration register (0x0E): volume slew 512 steps
+    { .size = 2,  .data = "\x0E\x90" },
+    // Clock control register (0x00): 44/48kHz, MCLK=64xfs
+    { .size = 2,  .data = "\x00\x60" },
+    // Bank switch and eq control (0x50): no bank switching
+    { .size = 5,  .data = "\x50\x00\x00\x00\x00" },
+    // Volume registers (0x07, 0x08, 0x09, 0x0A)
+    { .size = 2,  .data = "\x07\x20" },
+    { .size = 2,  .data = "\x08\x30" },
+    { .size = 2,  .data = "\x09\x30" },
+    { .size = 2,  .data = "\x0A\xFF" },
+    // 0x72, 0x73, 0x76, 0x77 input mixer:
+    // no intermix between channels
+    { .size = 5,  .data = "\x72\x00\x00\x00\x00" },
+    { .size = 5,  .data = "\x73\x00\x80\x00\x00" },
+    { .size = 5,  .data = "\x76\x00\x00\x00\x00" },
+    { .size = 5,  .data = "\x77\x00\x80\x00\x00" },
+    // 0x70, 0x71, 0x74, 0x75 inline DRC mixer:
+    // no inline DRC inmix
+    { .size = 5,  .data = "\x70\x00\x80\x00\x00" },
+    { .size = 5,  .data = "\x71\x00\x00\x00\x00" },
+    { .size = 5,  .data = "\x74\x00\x80\x00\x00" },
+    { .size = 5,  .data = "\x75\x00\x00\x00\x00" },
+    // 0x56, 0x57 Output scale
+    { .size = 5,  .data = "\x56\x00\x80\x00\x00" },
+    { .size = 5,  .data = "\x57\x00\x02\x00\x00" },
+    // 0x3B, 0x3c
+    { .size = 9,  .data = "\x3B\x00\x08\x00\x00\x00\x78\x00\x00" },
+    { .size = 9,  .data = "\x3C\x00\x00\x01\x00\xFF\xFF\xFF\x00" },
+    { .size = 9,  .data = "\x3E\x00\x08\x00\x00\x00\x78\x00\x00" },
+    { .size = 9,  .data = "\x3F\x00\x00\x01\x00\xFF\xFF\xFF\x00" },
+    { .size = 9,  .data = "\x40\x00\x00\x01\x00\xFF\xFF\xFF\x00" },
+    { .size = 9,  .data = "\x43\x00\x00\x01\x00\xFF\xFF\xFF\x00" },
+    // 0x51, 0x52: output mixer
+    { .size = 9,  .data = "\x51\x00\x80\x00\x00\x00\x00\x00\x00" },
+    { .size = 9,  .data = "\x52\x00\x80\x00\x00\x00\x00\x00\x00" },
+    // PEQ defaults
+    { .size = 21,  .data = "\x29\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" },
+    { .size = 21,  .data = "\x2A\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" },
+    { .size = 21,  .data = "\x2B\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" },
+    { .size = 21,  .data = "\x2C\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" },
+    { .size = 21,  .data = "\x2D\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" },
+    { .size = 21,  .data = "\x2E\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" },
+    { .size = 21,  .data = "\x2F\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" },
+    { .size = 21,  .data = "\x30\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" },
+    { .size = 21,  .data = "\x31\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" },
+    { .size = 21,  .data = "\x32\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" },
+    { .size = 21,  .data = "\x33\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" },
+    { .size = 21,  .data = "\x34\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" },
+    { .size = 21,  .data = "\x35\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" },
+    { .size = 21,  .data = "\x36\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" },
+    { .size = 21,  .data = "\x58\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" },
+    { .size = 21,  .data = "\x59\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" },
+    { .size = 21,  .data = "\x5C\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" },
+    { .size = 21,  .data = "\x5D\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" },
+    { .size = 21,  .data = "\x5E\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" },
+    { .size = 21,  .data = "\x5F\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" },
+    { .size = 21,  .data = "\x5A\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" },
+    { .size = 21,  .data = "\x5B\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" },
+};
+
+
+#endif  /* _TAS5713_H */
diff -Nur --no-dereference linux-5.10.52-orig/sound/soc/soc-core.c linux-5.10.52-v7l+/sound/soc/soc-core.c
--- linux-5.10.52-orig/sound/soc/soc-core.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/sound/soc/soc-core.c	2021-07-25 16:46:41.247699379 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1019 @
 	for_each_link_cpus(dai_link, i, cpu) {
 		asoc_rtd_to_cpu(rtd, i) = snd_soc_find_dai(cpu);
 		if (!asoc_rtd_to_cpu(rtd, i)) {
-			dev_info(card->dev, "ASoC: CPU DAI %s not registered\n",
+			dev_info(card->dev, "ASoC: CPU DAI %s not registered - will retry\n",
 				 cpu->dai_name);
 			goto _err_defer;
 		}
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1030 @
 	for_each_link_codecs(dai_link, i, codec) {
 		asoc_rtd_to_codec(rtd, i) = snd_soc_find_dai(codec);
 		if (!asoc_rtd_to_codec(rtd, i)) {
-			dev_info(card->dev, "ASoC: CODEC DAI %s not registered\n",
+			dev_info(card->dev, "ASoC: CODEC DAI %s not registered- will retry\n",
 				 codec->dai_name);
 			goto _err_defer;
 		}
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1432 @
 	int ret;
 
 	for_each_rtd_codec_dais(rtd, i, codec_dai) {
-		ret = snd_soc_dai_set_fmt(codec_dai, dai_fmt);
+		unsigned int codec_dai_fmt = dai_fmt;
+
+		// there can only be one master when using multiple codecs
+		if (i && (codec_dai_fmt & SND_SOC_DAIFMT_MASTER_MASK)) {
+			codec_dai_fmt &= ~SND_SOC_DAIFMT_MASTER_MASK;
+			codec_dai_fmt |= SND_SOC_DAIFMT_CBS_CFS;
+		}
+
+		ret = snd_soc_dai_set_fmt(codec_dai, codec_dai_fmt);
 		if (ret != 0 && ret != -ENOTSUPP) {
 			dev_warn(codec_dai->dev,
 				 "ASoC: Failed to set DAI format: %d\n", ret);
diff -Nur --no-dereference linux-5.10.52-orig/sound/usb/quirks.c linux-5.10.52-v7l+/sound/usb/quirks.c
--- linux-5.10.52-orig/sound/usb/quirks.c	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/sound/usb/quirks.c	2021-07-25 16:46:41.607693343 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:534 @
 				       struct usb_driver *driver,
 				       const struct snd_usb_audio_quirk *quirk)
 {
+	/*
+	 * Grab the interface, because on a webcam uvcvideo may race
+	 * with snd-usb-audio during probe and re-enable autosuspend.
+	 */
+	usb_autopm_get_interface(iface);
 	usb_disable_autosuspend(interface_to_usbdev(iface));
 	return 1;	/* Continue with creating streams and mixer */
 }
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:1538 @
 	case USB_ID(0x2912, 0x30c8): /* Audioengine D1 */
 	case USB_ID(0x413c, 0xa506): /* Dell AE515 sound bar */
 	case USB_ID(0x046d, 0x084c): /* Logitech ConferenceCam Connect */
+	case USB_ID(0x09da, 0x2695): /* A4Tech FHD 1080p webcam */
 		return true;
 	}
 
diff -Nur --no-dereference linux-5.10.52-orig/sound/usb/quirks-table.h linux-5.10.52-v7l+/sound/usb/quirks-table.h
--- linux-5.10.52-orig/sound/usb/quirks-table.h	2021-07-20 16:05:59.000000000 +0200
+++ linux-5.10.52-v7l+/sound/usb/quirks-table.h	2021-07-25 16:46:41.607693343 +0200
@ linux-5.10.52-v7l+/arch/arm/boot/dts/bcm2708.dtsi:50 @
 },
 
 {
+	/* A4Tech FHD 1080p webcam */
+	USB_DEVICE(0x09da, 0x2695),
+	.driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) {
+		.ifnum = QUIRK_ANY_INTERFACE,
+		.type = QUIRK_SETUP_DISABLE_AUTOSUSPEND
+	}
+},
+
+{
 	/* Creative BT-D1 */
 	USB_DEVICE(0x041e, 0x0005),
 	.driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) {