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>; +}; + +ð_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 = <ð_phy>,"microchip,eee-enabled?"; + tx_lpi_timer = <ð_phy>,"microchip,tx-lpi-timer:0"; + eth_led0 = <ð_phy>,"microchip,led-modes:0"; + eth_led1 = <ð_phy>,"microchip,led-modes:4"; + eth_downshift_after = <ð_phy>,"microchip,downshift-after:0"; + eth_max_speed = <ð_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 = <ð1_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 = <ð1>, "interrupts:0", + <ð1_pins>, "brcm,pins:0"; + speed = <ð1>, "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 = <ð1_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 = <ð1>, "interrupts:0", + <ð1_pins>, "brcm,pins:0"; + speed = <ð1>, "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 = <®_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 = <& 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 = <&>,"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", + <>9271>,"interrupts:0", + <>9271>,"irq-gpios:4"; + reset = <&goodix_pins>,"brcm,pins:4", + <>9271>,"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 = <<c2941>, "lltc,resistor-sense:0", + <<c2942>, "lltc,resistor-sense:0", + <<c2943>, "lltc,resistor-sense:0", + <<c2944>, "lltc,resistor-sense:0"; + prescaler-exponent = <<c2941>, "lltc,prescaler-exponent:0", + <<c2942>, "lltc,prescaler-exponent:0", + <<c2943>, "lltc,prescaler-exponent:0", + <<c2944>, "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 = <®_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", + <®_mcp251xfd_xceiver>, "name=reg-mcp251xfd-spi0-0-xceiver", + <®_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", + <®_mcp251xfd_xceiver>, "name=reg-mcp251xfd-spi0-1-xceiver", + <®_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", + <®_mcp251xfd_xceiver>, "name=reg-mcp251xfd-spi1-0-xceiver", + <®_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", + <®_mcp251xfd_xceiver>, "name=reg-mcp251xfd-spi1-1-xceiver", + <®_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", + <®_mcp251xfd_xceiver>, "name=reg-mcp251xfd-spi1-2-xceiver", + <®_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", + <®_mcp251xfd_xceiver>, "name=reg-mcp251xfd-spi2-0-xceiver", + <®_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", + <®_mcp251xfd_xceiver>, "name=reg-mcp251xfd-spi2-1-xceiver", + <®_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", + <®_mcp251xfd_xceiver>, "name=reg-mcp251xfd-spi2-2-xceiver", + <®_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", + <®_mcp251xfd_xceiver>, "gpio:4"; + xceiver_active_high = <®_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 = <ð1_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 = <ð1>, "interrupts:0", + <ð1_pins>, "brcm,pins:0"; + speed = <ð1>, "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 = <<070me05000_pins>,"brcm,pins:8", + <<070me05000>,"reset-gpios:4"; + + enable = <<070me05000_pins>,"brcm,pins:0", + <<070me05000>,"enable-gpios:4"; + + dcdc-en = <<070me05000_pins>,"brcm,pins:4", + <<070me05000>,"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 = <ð1_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 = <ð1>, "interrupts:0", + <ð1_pins>, "brcm,pins:0"; + speed = <ð1>, "spi-max-frequency:0"; + cs = <ð1>, "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, ¶ms->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, ®); + 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, ®); + 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 *)®_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, ¤t_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, ¤t_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, ¤t_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, + ¤t_timings); + + if (ret < 0) + return ret; + + if (v4l2_match_dv_timings(timings, ¤t_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, ®s[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, + ¶m, + ¶m_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, + ¶m, + 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, + ¶m, 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, + ¶m, 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, + ¶m, 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, + ¶m_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, + ¶m_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, ¶m_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, ¶ms, + &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, ¤t->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(¬ifier->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, ¬ifier->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(¬ifier->observers)) { + observer_t *o; + + DWC_ERROR("Notifier %p has active observers when removing\n", notifier->object); + DWC_CIRCLEQ_FOREACH(o, ¬ifier->observers, list_entry) { + DWC_DEBUGC(" %p watching %s\n", o->observer, o->notification); + } + + DWC_ASSERT(DWC_CIRCLEQ_EMPTY(¬ifier->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(¬ifier->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, ¬ifier->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, ®val, 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(®s,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(®s); + set_fiq_regs(®s); +#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(¶m32, (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, ¶m); + 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(©, argp, sizeof(copy))) + return -EFAULT; + ret = fb_copyarea_user(info, ©); + 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, ®); + 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, ®); + devid = (reg & 0xFF) << 12; + ret = regmap_read(cs43130->regmap, CS43130_DEVID_CD, ®); + devid |= (reg & 0xFF) << 4; + ret = regmap_read(cs43130->regmap, CS43130_DEVID_E, ®); + 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) {