diff --git a/Documentation/userspace-api/media/v4l/ext-ctrls-image-source.rst b/Documentation/userspace-api/media/v4l/ext-ctrls-image-source.rst index 9457dc340c318e437c3e8329a64c7f43aa3c08b4..af8fa25026c0057428ae536fe467b68c1d094b2c 100644 --- a/Documentation/userspace-api/media/v4l/ext-ctrls-image-source.rst +++ b/Documentation/userspace-api/media/v4l/ext-ctrls-image-source.rst @@ -58,3 +58,23 @@ Image Source Control IDs The unit cell consists of the whole area of the pixel, sensitive and non-sensitive. This control is required for automatic calibration of sensors/cameras. + +``V4L2_CID_NOTIFY_GAINS (integer array)`` + The sensor is notified what gains will be applied to the different + colour channels by subsequent processing (such as by an ISP). The + sensor is merely informed of these values in case it performs + processing that requires them, but it does not apply them itself to + the output pixels. + + Currently it is defined only for Bayer sensors, and is an array + control taking 4 gain values, being the gains for each of the + Bayer channels. The gains are always in the order B, Gb, Gr and R, + irrespective of the exact Bayer order of the sensor itself. + + The use of an array allows this control to be extended to sensors + with, for example, non-Bayer CFAs (colour filter arrays). + + The units for the gain values are linear, with the default value + representing a gain of exactly 1.0. For example, if this default value + is reported as being (say) 128, then a value of 192 would represent + a gain of exactly 1.5. diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile index 12cd8bf582e1bfdf9cd7a8f833b282c58e6bdba3..ee163c52577ecb922f1cc002bb465aca731bf424 100644 --- a/arch/arm/boot/dts/Makefile +++ b/arch/arm/boot/dts/Makefile @@ -7,6 +7,8 @@ dtb-$(CONFIG_ARCH_BCM2835) += \ bcm2708-rpi-cm.dtb \ bcm2708-rpi-zero.dtb \ bcm2708-rpi-zero-w.dtb \ + bcm2710-rpi-zero-2.dtb \ + bcm2710-rpi-zero-2-w.dtb \ bcm2709-rpi-2-b.dtb \ bcm2710-rpi-2-b.dtb \ bcm2710-rpi-3-b.dtb \ @@ -14,7 +16,8 @@ dtb-$(CONFIG_ARCH_BCM2835) += \ bcm2711-rpi-4-b.dtb \ bcm2711-rpi-400.dtb \ bcm2710-rpi-cm3.dtb \ - bcm2711-rpi-cm4.dtb + bcm2711-rpi-cm4.dtb \ + bcm2711-rpi-cm4s.dtb dtb-$(CONFIG_ARCH_ALPINE) += \ alpine-db.dtb diff --git a/arch/arm/boot/dts/bcm2708-rpi-b-plus.dts b/arch/arm/boot/dts/bcm2708-rpi-b-plus.dts index e42cba84ab0e03de6c6501ccde28542fc6bd1713..a7d6427671b4f43e1afaf969b3e4401ea1519182 100644 --- a/arch/arm/boot/dts/bcm2708-rpi-b-plus.dts +++ b/arch/arm/boot/dts/bcm2708-rpi-b-plus.dts @@ -5,7 +5,6 @@ #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"; @@ -116,6 +115,9 @@ gpio = <&gpio 41 GPIO_ACTIVE_HIGH>; }; +cam0_reg: &cam_dummy_reg { +}; + / { __overrides__ { act_led_gpio = <&act_led>,"gpios:4"; diff --git a/arch/arm/boot/dts/bcm2708-rpi-b-rev1.dts b/arch/arm/boot/dts/bcm2708-rpi-b-rev1.dts index 4ea1e68f5e2980431fc276262245dd352a2f01fb..af1b477f7927cbc1ac751f51efef6ef6549f3a64 100644 --- a/arch/arm/boot/dts/bcm2708-rpi-b-rev1.dts +++ b/arch/arm/boot/dts/bcm2708-rpi-b-rev1.dts @@ -4,7 +4,6 @@ #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"; @@ -123,6 +122,9 @@ i2c_csi_dsi: &i2c1 { gpio = <&gpio 27 GPIO_ACTIVE_HIGH>; }; +cam0_reg: &cam_dummy_reg { +}; + / { __overrides__ { act_led_gpio = <&act_led>,"gpios:4"; diff --git a/arch/arm/boot/dts/bcm2708-rpi-b.dts b/arch/arm/boot/dts/bcm2708-rpi-b.dts index a152c1c8e648d2b2ac783cef34462a45688fc9c3..a5316257a18b875b82e21634558961ce08f42afb 100644 --- a/arch/arm/boot/dts/bcm2708-rpi-b.dts +++ b/arch/arm/boot/dts/bcm2708-rpi-b.dts @@ -5,7 +5,6 @@ #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"; @@ -110,6 +109,9 @@ gpio = <&gpio 21 GPIO_ACTIVE_HIGH>; }; +cam0_reg: &cam_dummy_reg { +}; + / { __overrides__ { act_led_gpio = <&act_led>,"gpios:4"; diff --git a/arch/arm/boot/dts/bcm2708-rpi-cm.dts b/arch/arm/boot/dts/bcm2708-rpi-cm.dts index f61e3418425a9dafbca90cd381b4cce353230a36..863bd207e323467210b14cd6c4f3139af793aaea 100644 --- a/arch/arm/boot/dts/bcm2708-rpi-cm.dts +++ b/arch/arm/boot/dts/bcm2708-rpi-cm.dts @@ -8,21 +8,15 @@ / { 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"; - }; +&cam1_reg { + gpio = <&gpio 2 GPIO_ACTIVE_HIGH>; + status = "disabled"; +}; + +cam0_reg: &cam0_regulator { + gpio = <&gpio 30 GPIO_ACTIVE_HIGH>; }; &uart0 { diff --git a/arch/arm/boot/dts/bcm2708-rpi-cm.dtsi b/arch/arm/boot/dts/bcm2708-rpi-cm.dtsi index d5572b2d21033d745a79c0af36ea4466f21df100..dd59f884d796cef293d946475fb50ce3292ad063 100644 --- a/arch/arm/boot/dts/bcm2708-rpi-cm.dtsi +++ b/arch/arm/boot/dts/bcm2708-rpi-cm.dtsi @@ -14,5 +14,9 @@ act_led_gpio = <&act_led>,"gpios:4"; act_led_activelow = <&act_led>,"gpios:8"; act_led_trigger = <&act_led>,"linux,default-trigger"; + cam0_reg = <&cam0_reg>,"status"; + cam0_reg_gpio = <&cam0_reg>,"gpios:4"; + cam1_reg = <&cam1_reg>,"status"; + cam1_reg_gpio = <&cam1_reg>,"gpios:4"; }; }; diff --git a/arch/arm/boot/dts/bcm2708-rpi-zero-w.dts b/arch/arm/boot/dts/bcm2708-rpi-zero-w.dts index 75a5b41514f9f0074cb67f97c026a52f043ee003..e4c6c352f3aa8c468251590e21395d2f0057d7b3 100644 --- a/arch/arm/boot/dts/bcm2708-rpi-zero-w.dts +++ b/arch/arm/boot/dts/bcm2708-rpi-zero-w.dts @@ -5,7 +5,6 @@ #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"; @@ -83,6 +82,13 @@ pinctrl-0 = <&sdio_pins>; bus-width = <4>; status = "okay"; + #address-cells = <1>; + #size-cells = <0>; + + brcmf: wifi@1 { + reg = <1>; + compatible = "brcm,bcm4329-fmac"; + }; }; &uart0 { @@ -160,6 +166,9 @@ gpio = <&gpio 44 GPIO_ACTIVE_HIGH>; }; +cam0_reg: &cam_dummy_reg { +}; + / { __overrides__ { act_led_gpio = <&act_led>,"gpios:4"; diff --git a/arch/arm/boot/dts/bcm2708-rpi-zero.dts b/arch/arm/boot/dts/bcm2708-rpi-zero.dts index 84591bd7d423e77c1e9b75d7efdf4a2f18fc2926..19dae0d682edd4485541535077be705fcc2e212e 100644 --- a/arch/arm/boot/dts/bcm2708-rpi-zero.dts +++ b/arch/arm/boot/dts/bcm2708-rpi-zero.dts @@ -4,7 +4,6 @@ #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"; @@ -114,6 +113,9 @@ gpio = <&gpio 41 GPIO_ACTIVE_HIGH>; }; +cam0_reg: &cam_dummy_reg { +}; + / { __overrides__ { act_led_gpio = <&act_led>,"gpios:4"; diff --git a/arch/arm/boot/dts/bcm2709-rpi-2-b.dts b/arch/arm/boot/dts/bcm2709-rpi-2-b.dts index e1381d2b3a2c6d9ba5118e6d72a494d13b58daf2..4c80d15981fe73703738dc521517b07d9e203b34 100644 --- a/arch/arm/boot/dts/bcm2709-rpi-2-b.dts +++ b/arch/arm/boot/dts/bcm2709-rpi-2-b.dts @@ -5,7 +5,6 @@ #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"; @@ -116,6 +115,9 @@ gpio = <&gpio 41 GPIO_ACTIVE_HIGH>; }; +cam0_reg: &cam_dummy_reg { +}; + / { __overrides__ { act_led_gpio = <&act_led>,"gpios:4"; diff --git a/arch/arm/boot/dts/bcm270x.dtsi b/arch/arm/boot/dts/bcm270x.dtsi index badcf341ecd2f5626177c12e98c52510c8f916a4..a5cabb5bc4a12c313654724e9e094d090a5cf694 100644 --- a/arch/arm/boot/dts/bcm270x.dtsi +++ b/arch/arm/boot/dts/bcm270x.dtsi @@ -153,6 +153,39 @@ }; }; + cam1_reg: cam1_regulator { + compatible = "regulator-fixed"; + regulator-name = "cam1-reg"; + enable-active-high; + /* Needs to be enabled, as removing a regulator is very unsafe */ + status = "okay"; + }; + + cam1_clk: cam1_clk { + compatible = "fixed-clock"; + #clock-cells = <0>; + status = "disabled"; + }; + + cam0_regulator: cam0_regulator { + compatible = "regulator-fixed"; + regulator-name = "cam0-reg"; + enable-active-high; + status = "disabled"; + }; + + cam0_clk: cam0_clk { + compatible = "fixed-clock"; + #clock-cells = <0>; + status = "disabled"; + }; + + cam_dummy_reg: cam_dummy_reg { + compatible = "regulator-fixed"; + regulator-name = "cam-dummy-reg"; + status = "okay"; + }; + __overrides__ { cam0-pwdn-ctrl; cam0-pwdn; @@ -189,6 +222,28 @@ 20 21>; brcm,function = ; }; + dpi_16bit_gpio0: dpi_16bit_gpio0 { + brcm,pins = <0 1 2 3 4 5 6 7 8 9 10 11 + 12 13 14 15 16 17 18 19>; + brcm,function = ; + }; + dpi_16bit_gpio2: dpi_16bit_gpio2 { + brcm,pins = <2 3 4 5 6 7 8 9 10 11 + 12 13 14 15 16 17 18 19>; + brcm,function = ; + }; + dpi_16bit_cpadhi_gpio0: dpi_16bit_cpadhi_gpio0 { + brcm,pins = <0 1 2 3 4 5 6 7 8 + 12 13 14 15 16 17 + 20 21 22 23 24>; + brcm,function = ; + }; + dpi_16bit_cpadhi_gpio2: dpi_16bit_cpadhi_gpio2 { + brcm,pins = <2 3 4 5 6 7 8 + 12 13 14 15 16 17 + 20 21 22 23 24>; + brcm,function = ; + }; }; &uart0 { diff --git a/arch/arm/boot/dts/bcm2710-rpi-2-b.dts b/arch/arm/boot/dts/bcm2710-rpi-2-b.dts index ae9db1b1be1b23c7ba98390894f8ded18cc97a0f..a8a18ef4d1bf45e143bbc5b09a86b7d7a6824632 100644 --- a/arch/arm/boot/dts/bcm2710-rpi-2-b.dts +++ b/arch/arm/boot/dts/bcm2710-rpi-2-b.dts @@ -5,7 +5,6 @@ #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"; @@ -116,6 +115,9 @@ gpio = <&gpio 41 GPIO_ACTIVE_HIGH>; }; +cam0_reg: &cam_dummy_reg { +}; + / { __overrides__ { act_led_gpio = <&act_led>,"gpios:4"; diff --git a/arch/arm/boot/dts/bcm2710-rpi-3-b-plus.dts b/arch/arm/boot/dts/bcm2710-rpi-3-b-plus.dts index 7e12c05cc28bd7a323741d234f6a138d2f0f70ae..93f9c8dddbca3dfa6e5c52911546c1827bd03a6d 100644 --- a/arch/arm/boot/dts/bcm2710-rpi-3-b-plus.dts +++ b/arch/arm/boot/dts/bcm2710-rpi-3-b-plus.dts @@ -6,7 +6,6 @@ #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"; @@ -84,6 +83,13 @@ pinctrl-0 = <&sdio_pins>; bus-width = <4>; status = "okay"; + #address-cells = <1>; + #size-cells = <0>; + + brcmf: wifi@1 { + reg = <1>; + compatible = "brcm,bcm4329-fmac"; + }; }; &firmware { @@ -181,6 +187,9 @@ gpio = <&expgpio 5 GPIO_ACTIVE_HIGH>; }; +cam0_reg: &cam_dummy_reg { +}; + / { __overrides__ { act_led_gpio = <&act_led>,"gpios:4"; diff --git a/arch/arm/boot/dts/bcm2710-rpi-3-b.dts b/arch/arm/boot/dts/bcm2710-rpi-3-b.dts index d40722ddc286ce14c519f6abe06d12a16539d54f..bc5d086beb936246879a5fb976fc80ec1d54fbd9 100644 --- a/arch/arm/boot/dts/bcm2710-rpi-3-b.dts +++ b/arch/arm/boot/dts/bcm2710-rpi-3-b.dts @@ -6,7 +6,6 @@ #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"; @@ -84,6 +83,13 @@ pinctrl-0 = <&sdio_pins>; bus-width = <4>; status = "okay"; + #address-cells = <1>; + #size-cells = <0>; + + brcmf: wifi@1 { + reg = <1>; + compatible = "brcm,bcm4329-fmac"; + }; }; &soc { @@ -190,6 +196,9 @@ gpio = <&expgpio 5 GPIO_ACTIVE_HIGH>; }; +cam0_reg: &cam_dummy_reg { +}; + / { __overrides__ { act_led_gpio = <&act_led>,"gpios:4"; diff --git a/arch/arm/boot/dts/bcm2710-rpi-cm3.dts b/arch/arm/boot/dts/bcm2710-rpi-cm3.dts index c386a855cdc3b3b5117e12ba077ca1d2d33b57c2..517ed47c257d9fdee1ba9bc8b5a9e90945c227bc 100644 --- a/arch/arm/boot/dts/bcm2710-rpi-cm3.dts +++ b/arch/arm/boot/dts/bcm2710-rpi-cm3.dts @@ -8,21 +8,15 @@ / { 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"; - }; +&cam1_reg { + gpio = <&gpio 2 GPIO_ACTIVE_HIGH>; + status = "disabled"; +}; + +cam0_reg: &cam0_regulator { + gpio = <&gpio 30 GPIO_ACTIVE_HIGH>; }; &uart0 { @@ -144,5 +138,9 @@ act_led_gpio = <&act_led>,"gpios:4"; act_led_activelow = <&act_led>,"gpios:8"; act_led_trigger = <&act_led>,"linux,default-trigger"; + cam0_reg = <&cam0_reg>,"status"; + cam0_reg_gpio = <&cam0_reg>,"gpios:4"; + cam1_reg = <&cam1_reg>,"status"; + cam1_reg_gpio = <&cam1_reg>,"gpios:4"; }; }; diff --git a/arch/arm/boot/dts/bcm2710-rpi-zero-2-w.dts b/arch/arm/boot/dts/bcm2710-rpi-zero-2-w.dts new file mode 100644 index 0000000000000000000000000000000000000000..38629ebfa47f4c000739fbb8ece024d4a323bf8f --- /dev/null +++ b/arch/arm/boot/dts/bcm2710-rpi-zero-2-w.dts @@ -0,0 +1,199 @@ +/dts-v1/; + +#include "bcm2710.dtsi" +#include "bcm2709-rpi.dtsi" +#include "bcm283x-rpi-csi1-2lane.dtsi" +#include "bcm283x-rpi-i2c0mux_0_44.dtsi" +#include "bcm2708-rpi-bt.dtsi" + +/ { + compatible = "raspberrypi,model-zero-2-w", "brcm,bcm2837"; + model = "Raspberry Pi Zero 2 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>; + }; + + 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"; + #address-cells = <1>; + #size-cells = <0>; + + brcmf: wifi@1 { + reg = <1>; + compatible = "brcm,bcm4329-fmac"; + + firmwares { + fw_43436p { + chipid = <43430>; + revmask = <4>; + fw_base = "brcm/brcmfmac43436-sdio"; + }; + fw_43436s { + chipid = <43430>; + revmask = <2>; + fw_base = "brcm/brcmfmac43436s-sdio"; + }; + }; + }; +}; + +&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: led-act { + label = "led0"; + linux,default-trigger = "actpwr"; + gpios = <&gpio 29 GPIO_ACTIVE_LOW>; + }; +}; + +&hdmi { + hpd-gpios = <&gpio 28 GPIO_ACTIVE_LOW>; +}; + +&audio { + pinctrl-names = "default"; + pinctrl-0 = <&audio_pins>; + brcm,disable-headphones = <1>; +}; + +&bt { + shutdown-gpios = <&gpio 42 GPIO_ACTIVE_HIGH>; +}; + +&minibt { + shutdown-gpios = <&gpio 42 GPIO_ACTIVE_HIGH>; +}; + +&cam1_reg { + gpio = <&gpio 40 GPIO_ACTIVE_HIGH>; +}; + +cam0_reg: &cam_dummy_reg { +}; + +/ { + __overrides__ { + act_led_gpio = <&act_led>,"gpios:4"; + act_led_activelow = <&act_led>,"gpios:8"; + act_led_trigger = <&act_led>,"linux,default-trigger"; + }; +}; diff --git a/arch/arm/boot/dts/bcm2710-rpi-zero-2.dts b/arch/arm/boot/dts/bcm2710-rpi-zero-2.dts new file mode 100644 index 0000000000000000000000000000000000000000..daa12bd30d6b6e1f8a6702c0a2262e9953a8b6bd --- /dev/null +++ b/arch/arm/boot/dts/bcm2710-rpi-zero-2.dts @@ -0,0 +1 @@ +#include "bcm2710-rpi-zero-2-w.dts" diff --git a/arch/arm/boot/dts/bcm2711-rpi-4-b.dts b/arch/arm/boot/dts/bcm2711-rpi-4-b.dts index 2fdeba0b95d708499b13479076652468a07072a2..fb44c89f3b0c939283d87d539f2bfd250b3e297b 100644 --- a/arch/arm/boot/dts/bcm2711-rpi-4-b.dts +++ b/arch/arm/boot/dts/bcm2711-rpi-4-b.dts @@ -181,12 +181,14 @@ &hdmi0 { clocks = <&firmware_clocks 13>, <&firmware_clocks 14>, <&dvp 0>, <&clk_27MHz>; clock-names = "hdmi", "bvb", "audio", "cec"; + wifi-2.4ghz-coexistence; status = "okay"; }; &hdmi1 { clocks = <&firmware_clocks 13>, <&firmware_clocks 14>, <&dvp 1>, <&clk_27MHz>; clock-names = "hdmi", "bvb", "audio", "cec"; + wifi-2.4ghz-coexistence; status = "okay"; }; @@ -337,7 +339,6 @@ #include "bcm2711-rpi.dtsi" #include "bcm283x-rpi-csi1-2lane.dtsi" #include "bcm283x-rpi-i2c0mux_0_44.dtsi" -#include "bcm283x-rpi-cam1-regulator.dtsi" / { chosen { @@ -606,6 +607,9 @@ gpio = <&expgpio 5 GPIO_ACTIVE_HIGH>; }; +cam0_reg: &cam_dummy_reg { +}; + / { __overrides__ { act_led_gpio = <&act_led>,"gpios:4"; diff --git a/arch/arm/boot/dts/bcm2711-rpi-400.dts b/arch/arm/boot/dts/bcm2711-rpi-400.dts index c25a97465d2ba3026d92a396465df13184eb63c2..ec0c758107545076174d000d385a674f656bc8db 100644 --- a/arch/arm/boot/dts/bcm2711-rpi-400.dts +++ b/arch/arm/boot/dts/bcm2711-rpi-400.dts @@ -93,9 +93,9 @@ "PWR_LED_OFF", "GLOBAL_RESET", "VDD_SD_IO_SEL", - "CAM_GPIO", + "GLOBAL_SHUTDOWN", "SD_PWR_ON", - "SD_OC_N"; + "SHUTDOWN_REQUEST"; status = "okay"; }; @@ -273,15 +273,16 @@ }; &pcie0 { - pci@1,0 { + pci@0,0 { + device_type = "pci"; #address-cells = <3>; #size-cells = <2>; ranges; reg = <0 0 0 0 0>; - usb@1,0 { - reg = <0x10000 0 0 0 0>; + usb@0,0 { + reg = <0 0 0 0 0>; resets = <&reset RASPBERRYPI_FIRMWARE_RESET_ID_USB>; }; }; diff --git a/arch/arm/boot/dts/bcm2711-rpi-cm4.dts b/arch/arm/boot/dts/bcm2711-rpi-cm4.dts index 1dcbb2173f5d71f1586e33e9f9fe9a4a3b4b20b8..5dbd1b77260bfee92ffb4b7d671bd2bb12a45d12 100644 --- a/arch/arm/boot/dts/bcm2711-rpi-cm4.dts +++ b/arch/arm/boot/dts/bcm2711-rpi-cm4.dts @@ -3,8 +3,6 @@ #include "bcm2711.dtsi" #include "bcm2835-rpi.dtsi" -#include - / { compatible = "raspberrypi,4-compute-module", "brcm,bcm2711"; model = "Raspberry Pi Compute Module 4"; @@ -286,17 +284,13 @@ }; &pcie0 { - pci@1,0 { + pci@0,0 { + device_type = "pci"; #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>; - }; }; }; @@ -352,7 +346,6 @@ #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 { diff --git a/arch/arm/boot/dts/bcm2711-rpi-cm4s.dts b/arch/arm/boot/dts/bcm2711-rpi-cm4s.dts new file mode 100644 index 0000000000000000000000000000000000000000..c0808eca28b341295f6e442a5be47069bb874117 --- /dev/null +++ b/arch/arm/boot/dts/bcm2711-rpi-cm4s.dts @@ -0,0 +1,459 @@ +// SPDX-License-Identifier: GPL-2.0 +/dts-v1/; +#include "bcm2711.dtsi" +#include "bcm2835-rpi.dtsi" + +#include + +/ { + compatible = "raspberrypi,4-compute-module-s", "brcm,bcm2711"; + model = "Raspberry Pi Compute Module 4S"; + + 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; + blconfig = &blconfig; + }; + + leds { + led-act { + gpios = <&virtgpio 0 0>; + }; + }; +}; + +&ddc0 { + status = "okay"; +}; + +&firmware { + firmware_clocks: clocks { + compatible = "raspberrypi,firmware-clocks"; + #clock-cells = <1>; + }; + + 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", + "GPIO28", + "GPIO29", + "GPIO30", + "GPIO31", + "GPIO32", + "GPIO33", + "GPIO34", + "GPIO35", + "GPIO36", + "GPIO37", + "GPIO38", + "GPIO39", + "PWM0_MISO", + "PWM1_MOSI", + "GPIO42", + "GPIO43", + "GPIO44", + "GPIO45"; +}; + +&hdmi0 { + clocks = <&firmware_clocks 13>, <&firmware_clocks 14>, <&dvp 0>, <&clk_27MHz>; + clock-names = "hdmi", "bvb", "audio", "cec"; + wifi-2.4ghz-coexistence; + 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"; + }; +}; + +/* EMMC2 is used to drive the EMMC card */ +&emmc2 { + bus-width = <8>; + broken-cd; + status = "okay"; +}; + +&pcie0 { + status = "disabled"; +}; + +&vchiq { + interrupts = ; +}; + +&vc4 { + status = "okay"; +}; + +&vec { + status = "disabled"; +}; + +// ============================================= +// Downstream rpi- changes + +#define BCM2711 + +#include "bcm270x.dtsi" + +/ { + soc { + /delete-node/ pixelvalve@7e807000; + /delete-node/ hdmi@7e902000; + + virtgpio: virtgpio { + compatible = "brcm,bcm2835-virtgpio"; + gpio-controller; + #gpio-cells = <2>; + firmware = <&firmware>; + status = "okay"; + }; + }; +}; + +#include "bcm2711-rpi.dtsi" +#include "bcm283x-rpi-csi0-2lane.dtsi" +#include "bcm283x-rpi-csi1-4lane.dtsi" +#include "bcm283x-rpi-i2c0mux_0_28.dtsi" + +/delete-node/ &hdmi1; + +/ { + chosen { + bootargs = "coherent_pool=1M 8250.nr_uarts=1 snd_bcm2835.enable_compat_alsa=0 snd_bcm2835.enable_hdmi=1"; + }; + + aliases { + serial0 = &uart0; + mmc0 = &emmc2; + mmc1 = &mmcnr; + mmc2 = &sdhost; + i2c3 = &i2c3; + i2c4 = &i2c4; + i2c5 = &i2c5; + i2c6 = &i2c6; + spi3 = &spi3; + spi4 = &spi4; + spi5 = &spi5; + spi6 = &spi6; + /delete-property/ intc; + }; + + /delete-node/ wifi-pwrseq; +}; + +&uart0 { + pinctrl-names = "default"; + pinctrl-0 = <&uart0_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>; + }; +}; + +&gpio { + spi0_pins: spi0_pins { + brcm,pins = <9 10 11>; + brcm,function = ; + }; + + spi0_cs_pins: spi0_cs_pins { + brcm,pins = <8 7>; + brcm,function = ; + }; + + spi3_pins: spi3_pins { + brcm,pins = <1 2 3>; + brcm,function = ; + }; + + spi3_cs_pins: spi3_cs_pins { + brcm,pins = <0 24>; + brcm,function = ; + }; + + spi4_pins: spi4_pins { + brcm,pins = <5 6 7>; + brcm,function = ; + }; + + spi4_cs_pins: spi4_cs_pins { + brcm,pins = <4 25>; + brcm,function = ; + }; + + spi5_pins: spi5_pins { + brcm,pins = <13 14 15>; + brcm,function = ; + }; + + spi5_cs_pins: spi5_cs_pins { + brcm,pins = <12 26>; + brcm,function = ; + }; + + spi6_pins: spi6_pins { + brcm,pins = <19 20 21>; + brcm,function = ; + }; + + spi6_cs_pins: spi6_cs_pins { + brcm,pins = <18 27>; + brcm,function = ; + }; + + i2c0_pins: i2c0 { + brcm,pins = <0 1>; + brcm,function = ; + brcm,pull = ; + }; + + i2c1_pins: i2c1 { + brcm,pins = <2 3>; + brcm,function = ; + brcm,pull = ; + }; + + i2c3_pins: i2c3 { + brcm,pins = <4 5>; + brcm,function = ; + brcm,pull = ; + }; + + i2c4_pins: i2c4 { + brcm,pins = <8 9>; + brcm,function = ; + brcm,pull = ; + }; + + i2c5_pins: i2c5 { + brcm,pins = <12 13>; + brcm,function = ; + brcm,pull = ; + }; + + i2c6_pins: i2c6 { + brcm,pins = <22 23>; + brcm,function = ; + brcm,pull = ; + }; + + i2s_pins: i2s { + brcm,pins = <18 19 20 21>; + brcm,function = ; + }; + + sdio_pins: sdio_pins { + brcm,pins = <34 35 36 37 38 39>; + brcm,function = ; // alt3 = SD1 + brcm,pull = <0 2 2 2 2 2>; + }; + + uart0_pins: uart0_pins { + brcm,pins; + brcm,function; + brcm,pull; + }; + + uart2_pins: uart2_pins { + brcm,pins = <0 1>; + brcm,function = ; + brcm,pull = <0 2>; + }; + + uart3_pins: uart3_pins { + brcm,pins = <4 5>; + brcm,function = ; + brcm,pull = <0 2>; + }; + + uart4_pins: uart4_pins { + brcm,pins = <8 9>; + brcm,function = ; + brcm,pull = <0 2>; + }; + + uart5_pins: uart5_pins { + brcm,pins = <12 13>; + brcm,function = ; + 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"; +}; + +&gpio { + audio_pins: audio_pins { + brcm,pins = <>; + brcm,function = <>; + }; +}; + +&leds { + act_led: led-act { + label = "led0"; + linux,default-trigger = "mmc0"; + gpios = <&virtgpio 0 0>; + }; +}; + +&pwm1 { + status = "disabled"; +}; + +&audio { + pinctrl-names = "default"; + pinctrl-0 = <&audio_pins>; + brcm,disable-headphones = <1>; +}; + +&cam1_reg { + gpio = <&gpio 2 GPIO_ACTIVE_HIGH>; + status = "disabled"; +}; + +cam0_reg: &cam0_regulator { + gpio = <&gpio 30 GPIO_ACTIVE_HIGH>; + status = "disabled"; +}; + +/ { + __overrides__ { + act_led_gpio = <&act_led>,"gpios:4"; + act_led_activelow = <&act_led>,"gpios:8"; + act_led_trigger = <&act_led>,"linux,default-trigger"; + + sd_poll_once = <&emmc2>, "non-removable?"; + spi_dma4 = <&spi0>, "dmas:0=", <&dma40>, + <&spi0>, "dmas:8=", <&dma40>; + }; +}; diff --git a/arch/arm/boot/dts/bcm2711.dtsi b/arch/arm/boot/dts/bcm2711.dtsi index adc402fa376b2e6f7b16dc1f355c83bc7a049250..2b834efddc06bce3f485ab6eaa9a3df6661bd612 100644 --- a/arch/arm/boot/dts/bcm2711.dtsi +++ b/arch/arm/boot/dts/bcm2711.dtsi @@ -303,7 +303,7 @@ vec: vec@7ec13000 { compatible = "brcm,bcm2711-vec"; reg = <0x7ec13000 0x1000>; - clocks = <&clocks BCM2835_CLOCK_VEC>; + clocks = <&firmware_clocks 15>; interrupts = ; status = "disabled"; }; @@ -319,7 +319,7 @@ aon_intr: interrupt-controller@7ef00100 { compatible = "brcm,bcm2711-l2-intc", "brcm,l2-intc"; reg = <0x7ef00100 0x30>; - interrupts = ; + interrupts = ; interrupt-controller; #interrupt-cells = <1>; status = "disabled"; @@ -402,7 +402,7 @@ <&clk_27MHz>; resets = <&dvp 1>; interrupt-parent = <&aon_intr>; - interrupts = <8>, <7>, <6>, + interrupts = <8>, <7>, <6>, // This is correct <9>, <10>, <11>; interrupt-names = "cec-tx", "cec-rx", "cec-low", "wakeup", "hpd-connected", "hpd-removed"; @@ -595,6 +595,8 @@ , ; + gpio-ranges = <&gpio 0 0 58>; + gpclk0_gpio49: gpclk0_gpio49 { pin-gpclk { pins = "gpio49"; diff --git a/arch/arm/boot/dts/bcm2835-common.dtsi b/arch/arm/boot/dts/bcm2835-common.dtsi index 06d8c3882cb7e3ebee1fc1df3f1c10e46227b12c..7f46a6ec512c1b5bcde067cb4e34c570502a984d 100644 --- a/arch/arm/boot/dts/bcm2835-common.dtsi +++ b/arch/arm/boot/dts/bcm2835-common.dtsi @@ -109,7 +109,7 @@ vec: vec@7e806000 { compatible = "brcm,bcm2835-vec"; reg = <0x7e806000 0x1000>; - clocks = <&clocks BCM2835_CLOCK_VEC>; + clocks = <&firmware_clocks 15>; interrupts = <2 27>; status = "disabled"; }; @@ -128,7 +128,7 @@ "hd"; interrupts = <2 8>, <2 9>; ddc = <&i2c2>; - clocks = <&clocks BCM2835_PLLH_PIX>, + clocks = <&firmware_clocks 9>, <&clocks BCM2835_CLOCK_HSM>; clock-names = "pixel", "hdmi"; dmas = <&dma (17|(1<<27)|(1<<24))>; diff --git a/arch/arm/boot/dts/bcm283x-rpi-cam1-regulator.dtsi b/arch/arm/boot/dts/bcm283x-rpi-cam1-regulator.dtsi deleted file mode 100644 index 55237d03ed949f0059d218868dc310519c9045cd..0000000000000000000000000000000000000000 --- a/arch/arm/boot/dts/bcm283x-rpi-cam1-regulator.dtsi +++ /dev/null @@ -1,10 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 - -/ { - cam1_reg: cam1_reg { - compatible = "regulator-fixed"; - regulator-name = "cam1-reg"; - enable-active-high; - status = "disabled"; - }; -}; diff --git a/arch/arm/boot/dts/bcm283x.dtsi b/arch/arm/boot/dts/bcm283x.dtsi index 06d04cde52b9ae8c4b3919f3b2ce1cb5d0297cd8..62d7ee5135498af70fb358fbc9e943d359471fc2 100644 --- a/arch/arm/boot/dts/bcm283x.dtsi +++ b/arch/arm/boot/dts/bcm283x.dtsi @@ -126,6 +126,8 @@ interrupt-controller; #interrupt-cells = <2>; + gpio-ranges = <&gpio 0 0 54>; + /* Defines common pin muxing groups * * While each pin can have its mux selected diff --git a/arch/arm/boot/dts/overlays/Makefile b/arch/arm/boot/dts/overlays/Makefile index b36c618b01d086f210c54eb8f05bfe05d859470a..bff07c1748b73049e092650435c5c1a3fde96ad9 100644 --- a/arch/arm/boot/dts/overlays/Makefile +++ b/arch/arm/boot/dts/overlays/Makefile @@ -34,6 +34,7 @@ dtbo-$(CONFIG_ARCH_BCM2835) += \ cap1106.dtbo \ chipdip-dac.dtbo \ cma.dtbo \ + cutiepi-panel.dtbo \ dht11.dtbo \ dionaudio-loco.dtbo \ dionaudio-loco-v2.dtbo \ @@ -49,6 +50,7 @@ dtbo-$(CONFIG_ARCH_BCM2835) += \ enc28j60.dtbo \ enc28j60-spi2.dtbo \ exc3000.dtbo \ + fbtft.dtbo \ fe-pi-audio.dtbo \ fsm-demo.dtbo \ ghost-amp.dtbo \ @@ -231,6 +233,7 @@ dtbo-$(CONFIG_ARCH_BCM2835) += \ vc4-fkms-v3d.dtbo \ vc4-fkms-v3d-pi4.dtbo \ vc4-kms-dpi-at056tn53v1.dtbo \ + vc4-kms-dpi-generic.dtbo \ vc4-kms-dsi-7inch.dtbo \ vc4-kms-dsi-lt070me05000.dtbo \ vc4-kms-dsi-lt070me05000-v2.dtbo \ @@ -239,6 +242,7 @@ dtbo-$(CONFIG_ARCH_BCM2835) += \ vc4-kms-v3d-pi4.dtbo \ vc4-kms-vga666.dtbo \ vga666.dtbo \ + vl805.dtbo \ w1-gpio.dtbo \ w1-gpio-pullup.dtbo \ w5500.dtbo \ diff --git a/arch/arm/boot/dts/overlays/README b/arch/arm/boot/dts/overlays/README index 5064d8eb2040269698054b72799ab75c07d8c980..f848a7fc87af311c34cdddd5a37e8fc185871e10 100644 --- a/arch/arm/boot/dts/overlays/README +++ b/arch/arm/boot/dts/overlays/README @@ -144,6 +144,16 @@ Params: See /sys/kernel/debug/raspberrypi_axi_monitor for the results. + cam0_reg Enables CAM 0 regulator. CM1 & 3 only. + + cam0_reg_gpio Set GPIO for CAM 0 regulator. Default 30. + CM1 & 3 only. + + cam1_reg Enables CAM 1 regulator. CM1 & 3 only. + + cam1_reg_gpio Set GPIO for CAM 1 regulator. Default 2. + CM1 & 3 only. + eee Enable Energy Efficient Ethernet support for compatible devices (default "on"). See also "tx_lpi_timer". Pi3B+ only. @@ -416,6 +426,8 @@ Info: Analog Devices ADV7282M analogue video to CSI2 bridge. variants. Load: dtoverlay=adv7282m,= Params: addr Overrides the I2C address (default 0x21) + media-controller Configure use of Media Controller API for + configuring the sensor (default off) Name: adv728x-m @@ -426,6 +438,8 @@ Params: addr Overrides the I2C address (default 0x21) adv7280m Select ADV7280-M. adv7281m Select ADV7281-M. adv7281ma Select ADV7281-MA. + media-controller Configure use of Media Controller API for + configuring the sensor (default off) Name: akkordion-iqdacplus @@ -665,6 +679,12 @@ Params: cma-512 CMA is 512MB (needs 1GB) cma-default Use upstream's default value +Name: cutiepi-panel +Info: 8" TFT LCD display and touch panel used by cutiepi.io +Load: dtoverlay=cutiepi-panel +Params: + + Name: dht11 Info: Overlay for the DHT11/DHT21/DHT22 humidity/temperature sensors Also sometimes found with the part number(s) AM230x. @@ -853,6 +873,130 @@ Params: interrupt GPIO used for interrupt (default 4) swapxy Touchscreen swapped x y axis +Name: fbtft +Info: Overlay for SPI-connected displays using the fbtft drivers. + + This overlay seeks to replace the functionality provided by fbtft_device + which is now gone from the kernel. + + Most displays from fbtft_device have been ported over. + Example: + dtoverlay=fbtft,spi0-0,rpi-display,reset_pin=23,dc_pin=24,led_pin=18,rotate=270 + + It is also possible to specify the controller (this will use the default + init sequence in the driver). + Example: + dtoverlay=fbtft,spi0-0,ili9341,bgr,reset_pin=23,dc_pin=24,led_pin=18,rotate=270 + + 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 following features of fbtft_device have not been ported over: + - parallel bus is not supported + - the init property which overrides the controller initialization + sequence is not supported as a parameter due to memory limitations in + the bootloader responsible for applying the overlay. + + See https://github.com/notro/fbtft/wiki/FBTFT-RPI-overlays for how to + create an overlay. + +Load: dtoverlay=fbtft,= +Params: + spi- Configure device at spi, cs + (boolean, required) + speed SPI bus speed in Hz (default 32000000) + cpha Shifted clock phase (CPHA) mode + cpol Inverse clock polarity (CPOL) mode + + adafruit18 Adafruit 1.8 + adafruit22 Adafruit 2.2 (old) + adafruit22a Adafruit 2.2 + adafruit28 Adafruit 2.8 + adafruit13m Adafruit 1.3 OLED + admatec_c-berry28 C-Berry28 + dogs102 EA DOGS102 + er_tftm050_2 ER-TFTM070-2 + er_tftm070_5 ER-TFTM070-5 + ew24ha0 EW24HA0 + ew24ha0_9bit EW24HA0 in 9-bit mode + freetronicsoled128 Freetronics OLED128 + hy28a HY28A + hy28b HY28B + itdb28_spi ITDB02-2.8 with SPI interface circuit + mi0283qt-2 Watterott MI0283QT-2 + mi0283qt-9a Watterott MI0283QT-9A + nokia3310 Nokia 3310 + nokia3310a Nokia 3310a + nokia5110 Nokia 5110 + piscreen PiScreen + pitft Adafruit PiTFT 2.8 + pioled ILSoft OLED + rpi-display Watterott rpi-display + sainsmart18 Sainsmart 1.8 + sainsmart32_spi Sainsmart 3.2 with SPI interfce circuit + tinylcd35 TinyLCD 3.5 + tm022hdh26 Tianma TM022HDH26 + tontec35_9481 Tontect 3.5 with ILI9481 controller + tontec35_9486 Tontect 3.5 with ILI9486 controller + waveshare32b Waveshare 3.2 + waveshare22 Waveshare 2.2 + + bd663474 BD663474 display controller + hx8340bn HX8340BN display controller + hx8347d HX8347D display controller + hx8353d HX8353D display controller + hx8357d HX8357D display controller + ili9163 ILI9163 display controller + ili9320 ILI9320 display controller + ili9325 ILI9325 display controller + ili9340 ILI9340 display controller + ili9341 ILI9341 display controller + ili9481 ILI9481 display controller + ili9486 ILI9486 display controller + pcd8544 PCD8544 display controller + ra8875 RA8875 display controller + s6d02a1 S6D02A1 display controller + s6d1121 S6D1121 display controller + seps525 SEPS525 display controller + sh1106 SH1106 display controller + ssd1289 SSD1289 display controller + ssd1305 SSD1305 display controller + ssd1306 SSD1306 display controller + ssd1325 SSD1325 display controller + ssd1331 SSD1331 display controller + ssd1351 SSD1351 display controller + st7735r ST7735R display controller + st7789v ST7789V display controller + tls8204 TLS8204 display controller + uc1611 UC1611 display controller + uc1701 UC1701 display controller + upd161704 UPD161704 display controller + + width Display width in pixels + height Display height in pixels + regwidth Display controller register width (default is + driver specific) + buswidth Display bus interface width (default 8) + debug Debug output level {0-7} + rotate Display rotation {0, 90, 180, 270} (counter + clockwise). Not supported by all drivers. + bgr Enable BGR mode (default off). Use if Red and + Blue are swapped. Not supported by all drivers. + fps Frames per second (default 30). In effect this + states how long the driver will wait after video + memory has been changed until display update + transfer is started. + txbuflen Length of the FBTFT transmit buffer + (default 4096) + startbyte Sets the Start byte used by fb_ili9320, + fb_ili9325 and fb_hx8347d. Common value is 0x70. + gamma String representation of Gamma Curve(s). Driver + specific. Not supported by all drivers. + reset_pin GPIO pin for RESET + dc_pin GPIO pin for D/C + led_pin GPIO pin for LED backlight + + Name: fe-pi-audio Info: Configures the Fe-Pi Audio Sound Card Load: dtoverlay=fe-pi-audio @@ -1027,6 +1171,11 @@ Info: Drives a GPIO high or low on poweroff (including halt). Using this or reboot). This also disables the ability to trigger a boot by driving GPIO3 low. + The GPIO starts in an inactive state. At poweroff time it is driven + active for 100ms, then inactive for 100ms, then active again. It is + safe to remove the power at any point after the initial activation of + the GPIO. + 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. @@ -1042,6 +1191,8 @@ Params: gpiopin GPIO for signalling (default 26) input Set if the gpio pin should be configured as an input. export Set to export the configured pin to sysfs + active_delay_ms Initial GPIO active period (default 100) + inactive_delay_ms Subsequent GPIO inactive period (default 100) timeout_ms Specify (in ms) how long the kernel waits for power-down before issuing a WARN (default 3000). @@ -1708,6 +1859,10 @@ Params: rotation Mounting rotation of the camera sensor (0 or 180, default 180) orientation Sensor orientation (0 = front, 1 = rear, 2 = external, default external) + media-controller Configure use of Media Controller API for + configuring the sensor (default on) + cam0 Adopt the default configuration for CAM0 on a + Compute Module (CSI0, i2c_vc, and cam0_reg). Name: imx290 @@ -1728,6 +1883,10 @@ Params: 4lane Enable 4 CSI2 lanes. This requires a Compute 2 = external, default external) rotation Mounting rotation of the camera sensor (0 or 180, default 0) + media-controller Configure use of Media Controller API for + configuring the sensor (default on) + cam0 Adopt the default configuration for CAM0 on a + Compute Module (CSI0, i2c_vc, and cam0_reg). Name: imx378 @@ -1739,6 +1898,10 @@ Params: rotation Mounting rotation of the camera sensor (0 or 180, default 180) orientation Sensor orientation (0 = front, 1 = rear, 2 = external, default external) + media-controller Configure use of Media Controller API for + configuring the sensor (default on) + cam0 Adopt the default configuration for CAM0 on a + Compute Module (CSI0, i2c_vc, and cam0_reg). Name: imx477 @@ -1750,6 +1913,10 @@ Params: rotation Mounting rotation of the camera sensor (0 or 180, default 180) orientation Sensor orientation (0 = front, 1 = rear, 2 = external, default external) + media-controller Configure use of Media Controller API for + configuring the sensor (default on) + cam0 Adopt the default configuration for CAM0 on a + Compute Module (CSI0, i2c_vc, and cam0_reg). Name: imx519 @@ -1761,6 +1928,10 @@ Params: rotation Mounting rotation of the camera sensor (0 or 180, default 0) orientation Sensor orientation (0 = front, 1 = rear, 2 = external, default external) + media-controller Configure use of Media Controller API for + configuring the sensor (default on) + cam0 Adopt the default configuration for CAM0 on a + Compute Module (CSI0, i2c_vc, and cam0_reg). Name: iqaudio-codec @@ -1824,8 +1995,11 @@ 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: +Load: dtoverlay=irs1125,= +Params: media-controller Configure use of Media Controller API for + configuring the sensor (default off) + cam0 Adopt the default configuration for CAM0 on a + Compute Module (CSI0, i2c_vc, and cam0_reg). Name: jedec-spi-nor @@ -2237,6 +2411,10 @@ Params: rotation Mounting rotation of the camera sensor (0 or 180, default 0) orientation Sensor orientation (0 = front, 1 = rear, 2 = external, default external) + media-controller Configure use of Media Controller API for + configuring the sensor (default on) + cam0 Adopt the default configuration for CAM0 on a + Compute Module (CSI0, i2c_vc, and cam0_reg). Name: ov7251 @@ -2248,6 +2426,10 @@ Params: rotation Mounting rotation of the camera sensor (0 or 180, default 0) orientation Sensor orientation (0 = front, 1 = rear, 2 = external, default external) + media-controller Configure use of Media Controller API for + configuring the sensor (default off) + cam0 Adopt the default configuration for CAM0 on a + Compute Module (CSI0, i2c_vc, and cam0_reg). Name: ov9281 @@ -2259,6 +2441,10 @@ Params: rotation Mounting rotation of the camera sensor (0 or 180, default 0) orientation Sensor orientation (0 = front, 1 = rear, 2 = external, default external) + media-controller Configure use of Media Controller API for + configuring the sensor (default on) + cam0 Adopt the default configuration for CAM0 on a + Compute Module (CSI0, i2c_vc, and cam0_reg). Name: papirus @@ -3239,6 +3425,10 @@ Params: 4lane Use 4 lanes (only applicable to Compute Modules link-frequency Set the link frequency. Only values of 297000000 (574Mbit/s) and 486000000 (972Mbit/s - default) are supported by the driver. + media-controller Configure use of Media Controller API for + configuring the sensor (default off) + cam0 Adopt the default configuration for CAM0 on a + Compute Module (CSI0, i2c_vc, and cam0_reg). Name: tc358743-audio @@ -3409,6 +3599,36 @@ Load: dtoverlay=vc4-kms-dpi-at056tn53v1 Params: +Name: vc4-kms-dpi-generic +Info: Enable a generic DPI display under KMS. Default timings are for the + Adafruit Kippah with 800x480 panel and RGB666 (GPIOs 0-21) + Requires vc4-kms-v3d to be loaded. +Load: dtoverlay=vc4-kms-dpi-generic,= +Params: clock-frequency Display clock frequency (Hz) + hactive Horizontal active pixels + hfp Horizontal front porch + hsync Horizontal sync pulse width + hbp Horizontal back porch + vactive Vertical active lines + vfp Vertical front porch + vsync Vertical sync pulse width + vbp Vertical back porch + hsync-invert Horizontal sync active low + vsync-invert Vertical sync active low + de-invert Data Enable active low + pixclk-invert Negative edge pixel clock + width-mm Define the screen width in mm + height-mm Define the screen height in mm + rgb565 Change to RGB565 output on GPIOs 0-19 + rgb666-padhi Change to RGB666 output on GPIOs 0-9, 12-17, and + 20-25 + rgb888 Change to RGB888 output on GPIOs 0-27 + bus-format Override the bus format for a MEDIA_BUS_FMT_* + value. NB also overridden by rgbXXX overrides. + backlight-gpio Defines a GPIO to be used for backlight control + (default of none). + + Name: vc4-kms-dsi-7inch Info: Enable the Raspberry Pi DSI 7" screen. Includes the edt-ft5406 for the touchscreen element. @@ -3466,8 +3686,8 @@ Params: cma-512 CMA is 512MB (needs 1GB) 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") + composite Enable the composite output (default "off") + N.B. Disables all other outputs on a Pi 4. Name: vc4-kms-v3d-pi4 @@ -3511,6 +3731,14 @@ Load: dtoverlay=vga666 Params: +Name: vl805 +Info: Overlay to enable a VIA VL805 USB3 controller on CM4 carriers + Will be loaded automatically by up-to-date firmware if "VL805=1" is + set in the EEPROM config. +Load: dtoverlay=vl805 +Params: + + 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. diff --git a/arch/arm/boot/dts/overlays/adv7282m-overlay.dts b/arch/arm/boot/dts/overlays/adv7282m-overlay.dts index 5d85dfd0595c987998898890a5c8adb6e19ddd1e..f7e97c4a13d80f1e65434181967192a8c2e5259b 100644 --- a/arch/arm/boot/dts/overlays/adv7282m-overlay.dts +++ b/arch/arm/boot/dts/overlays/adv7282m-overlay.dts @@ -59,7 +59,15 @@ }; }; + fragment@4 { + target = <&csi1>; + __dormant__ { + brcm,media-controller; + }; + }; + __overrides__ { addr = <&adv728x>,"reg:0"; + media-controller = <0>,"=4"; }; }; diff --git a/arch/arm/boot/dts/overlays/cutiepi-panel-overlay.dts b/arch/arm/boot/dts/overlays/cutiepi-panel-overlay.dts new file mode 100644 index 0000000000000000000000000000000000000000..6f9694e81d6af9ef33db7b203bdff7a11fe478fe --- /dev/null +++ b/arch/arm/boot/dts/overlays/cutiepi-panel-overlay.dts @@ -0,0 +1,117 @@ +/dts-v1/; +/plugin/; + +/ { + compatible = "brcm,bcm2711"; + + fragment@0 { + target=<&dsi1>; + + __overlay__ { + status = "okay"; + + #address-cells = <1>; + #size-cells = <0>; + + port { + dsi1_out_port: endpoint { + remote-endpoint = <&panel_dsi_in1>; + }; + }; + + display1: panel@0 { + compatible = "nwe,nwe080"; + reg=<0>; + backlight = <&rpi_backlight>; + reset-gpios = <&gpio 20 0>; + port { + panel_dsi_in1: endpoint { + remote-endpoint = <&dsi1_out_port>; + }; + }; + }; + }; + }; + + fragment@1 { + target = <&gpio>; + __overlay__ { + pwm_pins: pwm_pins { + brcm,pins = <12>; + brcm,function = <4>; // ALT0 + }; + }; + }; + + fragment@2 { + target = <&pwm>; + frag1: __overlay__ { + pinctrl-names = "default"; + pinctrl-0 = <&pwm_pins>; + assigned-clock-rates = <1000000>; + status = "okay"; + }; + }; + + fragment@3 { + target-path = "/"; + __overlay__ { + rpi_backlight: rpi_backlight { + compatible = "pwm-backlight"; + brightness-levels = <0 6 8 12 16 24 32 40 48 64 96 128 160 192 224 255>; + default-brightness-level = <6>; + pwms = <&pwm 0 200000>; + power-supply = <&vdd_3v3_reg>; + status = "okay"; + }; + }; + }; + + fragment@4 { + target = <&i2c6>; + frag0: __overlay__ { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <&i2c6_pins>; + clock-frequency = <100000>; + }; + }; + + fragment@5 { + target = <&i2c6_pins>; + __overlay__ { + brcm,pins = <22 23>; + }; + }; + + fragment@6 { + target = <&gpio>; + __overlay__ { + goodix_pins: goodix_pins { + brcm,pins = <21 26>; // interrupt and reset + brcm,function = <0 0>; // in + brcm,pull = <2 2>; // pull-up + }; + }; + }; + + fragment@7 { + target = <&i2c6>; + __overlay__ { + #address-cells = <1>; + #size-cells = <0>; + status = "okay"; + + gt9xx: gt9xx@5d { + compatible = "goodix,gt9271"; + reg = <0x5D>; + pinctrl-names = "default"; + pinctrl-0 = <&goodix_pins>; + interrupt-parent = <&gpio>; + interrupts = <21 2>; // high-to-low edge triggered + irq-gpios = <&gpio 21 0>; + reset-gpios = <&gpio 26 0>; + }; + }; + }; +}; diff --git a/arch/arm/boot/dts/overlays/edt-ft5406-overlay.dts b/arch/arm/boot/dts/overlays/edt-ft5406-overlay.dts index f82b4d0e50479748a6c85dc1101d5ca55c645e48..1210e4b8e6dc9e987012c8a791c50e6ecda122d6 100644 --- a/arch/arm/boot/dts/overlays/edt-ft5406-overlay.dts +++ b/arch/arm/boot/dts/overlays/edt-ft5406-overlay.dts @@ -8,3 +8,19 @@ /plugin/; #include "edt-ft5406.dtsi" + +/ { + fragment@0 { + target = <&i2c0if>; + __overlay__ { + status = "okay"; + }; + }; + + fragment@1 { + target = <&i2c0mux>; + __overlay__ { + status = "okay"; + }; + }; +}; diff --git a/arch/arm/boot/dts/overlays/edt-ft5406.dtsi b/arch/arm/boot/dts/overlays/edt-ft5406.dtsi index 0473ff17f19fac8add550e6e699aad1dacbeb900..2d0ff0e8b24ef26f7ac4a041930db208ae11b5bb 100644 --- a/arch/arm/boot/dts/overlays/edt-ft5406.dtsi +++ b/arch/arm/boot/dts/overlays/edt-ft5406.dtsi @@ -27,9 +27,8 @@ __overlay__ { #address-cells = <1>; #size-cells = <0>; - status = "okay"; ft5406: ts@38 { - compatible = "edt,edt-ft5406"; + compatible = "edt,edt-ft5506"; reg = <0x38>; touchscreen-size-x = < 800 >; @@ -38,13 +37,6 @@ }; }; - fragment@13 { - target = <&i2c0if>; - __overlay__ { - status = "okay"; - }; - }; - __overrides__ { sizex = <&ft5406>,"touchscreen-size-x:0"; sizey = <&ft5406>,"touchscreen-size-y:0"; diff --git a/arch/arm/boot/dts/overlays/fbtft-overlay.dts b/arch/arm/boot/dts/overlays/fbtft-overlay.dts new file mode 100644 index 0000000000000000000000000000000000000000..db45f8c53bcc652b178fd873729d811211b69c36 --- /dev/null +++ b/arch/arm/boot/dts/overlays/fbtft-overlay.dts @@ -0,0 +1,611 @@ +/* + * Device Tree overlay for fbtft drivers + */ + +/dts-v1/; +/plugin/; + +/ { + compatible = "brcm,bcm2835"; + + /* adafruit18 */ + fragment@0 { + target = <&display>; + __dormant__ { + compatible = "sitronix,st7735r"; + spi-max-frequency = <32000000>; + gamma = "02 1c 07 12 37 32 29 2d 29 25 2B 39 00 01 03 10\n03 1d 07 06 2E 2C 29 2D 2E 2E 37 3F 00 00 02 10"; + }; + }; + + /* adafruit22 */ + fragment@1 { + target = <&display>; + __dormant__ { + compatible = "himax,hx8340bn"; + spi-max-frequency = <32000000>; + buswidth = <9>; + bgr; + }; + }; + + /* adafruit22a */ + fragment@2 { + target = <&display>; + __dormant__ { + compatible = "ilitek,ili9340"; + spi-max-frequency = <32000000>; + bgr; + }; + }; + + /* adafruit28 */ + fragment@3 { + target = <&display>; + __dormant__ { + compatible = "ilitek,ili9341"; + spi-max-frequency = <32000000>; + bgr; + }; + }; + + /* adafruit13m */ + fragment@4 { + target = <&display>; + __dormant__ { + compatible = "solomon,ssd1306"; + spi-max-frequency = <16000000>; + }; + }; + + /* admatec_c-berry28 */ + fragment@5 { + target = <&display>; + __dormant__ { + compatible = "sitronix,st7789v"; + spi-max-frequency = <48000000>; + init = <0x01000011 + 0x02000078 + 0x0100003A 0x05 + 0x010000B2 0x0C 0x0C 0x00 0x33 0x33 + 0x010000B7 0x35 + 0x010000C2 0x01 0xFF + 0x010000C3 0x17 + 0x010000C4 0x20 + 0x010000BB 0x17 + 0x010000C5 0x20 + 0x010000D0 0xA4 0xA1 + 0x01000029>; + gamma = "D0 00 14 15 13 2C 42 43 4E 09 16 14 18 21\nD0 00 14 15 13 0B 43 55 53 0C 17 14 23 20"; + }; + }; + + /* dogs102 */ + fragment@6 { + target = <&display>; + __dormant__ { + compatible = "UltraChip,uc1701"; + spi-max-frequency = <8000000>; + bgr; + }; + }; + + /* er_tftm050_2 */ + fragment@7 { + target = <&display>; + __dormant__ { + compatible = "raio,ra8875"; + spi-max-frequency = <5000000>; + spi-cpha; + spi-cpol; + width = <480>; + height = <272>; + bgr; + }; + }; + + /* er_tftm070_5 */ + fragment@8 { + target = <&display>; + __dormant__ { + compatible = "raio,ra8875"; + spi-max-frequency = <5000000>; + spi-cpha; + spi-cpol; + width = <800>; + height = <480>; + bgr; + }; + }; + + /* ew24ha0 */ + fragment@9 { + target = <&display>; + __dormant__ { + compatible = "ultrachip,uc1611"; + spi-max-frequency = <32000000>; + spi-cpha; + spi-cpol; + }; + }; + + /* ew24ha0_9bit */ + fragment@10 { + target = <&display>; + __dormant__ { + compatible = "ultrachip,uc1611"; + spi-max-frequency = <32000000>; + spi-cpha; + spi-cpol; + buswidth = <9>; + }; + }; + + /* freetronicsoled128 */ + fragment@11 { + target = <&display>; + __dormant__ { + compatible = "solomon,ssd1351"; + spi-max-frequency = <20000000>; + backlight = <2>; /* FBTFT_ONBOARD_BACKLIGHT */ + bgr; + }; + }; + + /* hy28a */ + fragment@12 { + target = <&display>; + __dormant__ { + compatible = "ilitek,ili9320"; + spi-max-frequency = <32000000>; + spi-cpha; + spi-cpol; + startbyte = <0x70>; + bgr; + }; + }; + + /* hy28b */ + fragment@13 { + target = <&display>; + __dormant__ { + compatible = "ilitek,ili9325"; + spi-max-frequency = <48000000>; + spi-cpha; + spi-cpol; + init = <0x010000e7 0x0010 + 0x01000000 0x0001 + 0x01000001 0x0100 + 0x01000002 0x0700 + 0x01000003 0x1030 + 0x01000004 0x0000 + 0x01000008 0x0207 + 0x01000009 0x0000 + 0x0100000a 0x0000 + 0x0100000c 0x0001 + 0x0100000d 0x0000 + 0x0100000f 0x0000 + 0x01000010 0x0000 + 0x01000011 0x0007 + 0x01000012 0x0000 + 0x01000013 0x0000 + 0x02000032 + 0x01000010 0x1590 + 0x01000011 0x0227 + 0x02000032 + 0x01000012 0x009c + 0x02000032 + 0x01000013 0x1900 + 0x01000029 0x0023 + 0x0100002b 0x000e + 0x02000032 + 0x01000020 0x0000 + 0x01000021 0x0000 + 0x02000032 + 0x01000050 0x0000 + 0x01000051 0x00ef + 0x01000052 0x0000 + 0x01000053 0x013f + 0x01000060 0xa700 + 0x01000061 0x0001 + 0x0100006a 0x0000 + 0x01000080 0x0000 + 0x01000081 0x0000 + 0x01000082 0x0000 + 0x01000083 0x0000 + 0x01000084 0x0000 + 0x01000085 0x0000 + 0x01000090 0x0010 + 0x01000092 0x0000 + 0x01000093 0x0003 + 0x01000095 0x0110 + 0x01000097 0x0000 + 0x01000098 0x0000 + 0x01000007 0x0133 + 0x01000020 0x0000 + 0x01000021 0x0000 + 0x02000064>; + startbyte = <0x70>; + bgr; + fps = <50>; + gamma = "04 1F 4 7 7 0 7 7 6 0\n0F 00 1 7 4 0 0 0 6 7"; + }; + }; + + /* itdb28_spi */ + fragment@14 { + target = <&display>; + __dormant__ { + compatible = "ilitek,ili9325"; + spi-max-frequency = <32000000>; + bgr; + }; + }; + + /* mi0283qt-2 */ + fragment@15 { + target = <&display>; + __dormant__ { + compatible = "himax,hx8347d"; + spi-max-frequency = <32000000>; + startbyte = <0x70>; + bgr; + }; + }; + + /* mi0283qt-9a */ + fragment@16 { + target = <&display>; + __dormant__ { + compatible = "ilitek,ili9341"; + spi-max-frequency = <32000000>; + buswidth = <9>; + bgr; + }; + }; + + /* nokia3310 */ + fragment@17 { + target = <&display>; + __dormant__ { + compatible = "philips,pcd8544"; + spi-max-frequency = <400000>; + }; + }; + + /* nokia3310a */ + fragment@18 { + target = <&display>; + __dormant__ { + compatible = "teralane,tls8204"; + spi-max-frequency = <1000000>; + }; + }; + + /* nokia5110 */ + fragment@19 { + target = <&display>; + __dormant__ { + compatible = "ilitek,ili9163"; + spi-max-frequency = <12000000>; + bgr; + }; + }; + + /* piscreen */ + fragment@20 { + target = <&display>; + __dormant__ { + compatible = "ilitek,ili9486"; + spi-max-frequency = <32000000>; + regwidth = <16>; + bgr; + }; + }; + + /* pitft */ + fragment@21 { + target = <&display>; + __dormant__ { + compatible = "ilitek,ili9340"; + spi-max-frequency = <32000000>; + init = <0x01000001 + 0x02000005 + 0x01000028 + 0x010000EF 0x03 0x80 0x02 + 0x010000CF 0x00 0xC1 0x30 + 0x010000ED 0x64 0x03 0x12 0x81 + 0x010000E8 0x85 0x00 0x78 + 0x010000CB 0x39 0x2C 0x00 0x34 0x02 + 0x010000F7 0x20 + 0x010000EA 0x00 0x00 + 0x010000C0 0x23 + 0x010000C1 0x10 + 0x010000C5 0x3E 0x28 + 0x010000C7 0x86 + 0x0100003A 0x55 + 0x010000B1 0x00 0x18 + 0x010000B6 0x08 0x82 0x27 + 0x010000F2 0x00 + 0x01000026 0x01 + 0x010000E0 0x0F 0x31 0x2B 0x0C 0x0E 0x08 0x4E 0xF1 0x37 0x07 0x10 0x03 0x0E 0x09 0x00 + 0x010000E1 0x00 0x0E 0x14 0x03 0x11 0x07 0x31 0xC1 0x48 0x08 0x0F 0x0C 0x31 0x36 0x0F + 0x01000011 + 0x02000064 + 0x01000029 + 0x02000014>; + bgr; + }; + }; + + /* pioled */ + fragment@22 { + target = <&display>; + __dormant__ { + compatible = "solomon,ssd1351"; + spi-max-frequency = <20000000>; + bgr; + gamma = "0 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 4 4 4 4 4 4 4 4 4 4 4 4"; + }; + }; + + /* rpi-display */ + fragment@23 { + target = <&display>; + __dormant__ { + compatible = "ilitek,ili9341"; + spi-max-frequency = <32000000>; + bgr; + }; + }; + + /* sainsmart18 */ + fragment@24 { + target = <&display>; + __dormant__ { + compatible = "sitronix,st7735r"; + spi-max-frequency = <32000000>; + }; + }; + + /* sainsmart32_spi */ + fragment@25 { + target = <&display>; + __dormant__ { + compatible = "solomon,ssd1289"; + spi-max-frequency = <16000000>; + bgr; + }; + }; + + /* tinylcd35 */ + fragment@26 { + target = <&display>; + __dormant__ { + compatible = "neosec,tinylcd"; + spi-max-frequency = <32000000>; + bgr; + }; + }; + + /* tm022hdh26 */ + fragment@27 { + target = <&display>; + __dormant__ { + compatible = "ilitek,ili9341"; + spi-max-frequency = <32000000>; + bgr; + }; + }; + + /* tontec35_9481 - boards before 02 July 2014 */ + fragment@28 { + target = <&display>; + __dormant__ { + compatible = "ilitek,ili9481"; + spi-max-frequency = <128000000>; + spi-cpha; + spi-cpol; + bgr; + }; + }; + + /* tontec35_9486 - boards after 02 July 2014 */ + fragment@29 { + target = <&display>; + __dormant__ { + compatible = "ilitek,ili9486"; + spi-max-frequency = <128000000>; + spi-cpha; + spi-cpol; + bgr; + }; + }; + + /* waveshare32b */ + fragment@30 { + target = <&display>; + __dormant__ { + compatible = "ilitek,ili9340"; + spi-max-frequency = <48000000>; + init = <0x010000CB 0x39 0x2C 0x00 0x34 0x02 + 0x010000CF 0x00 0xC1 0x30 + 0x010000E8 0x85 0x00 0x78 + 0x010000EA 0x00 0x00 + 0x010000ED 0x64 0x03 0x12 0x81 + 0x010000F7 0x20 + 0x010000C0 0x23 + 0x010000C1 0x10 + 0x010000C5 0x3E 0x28 + 0x010000C7 0x86 + 0x01000036 0x28 + 0x0100003A 0x55 + 0x010000B1 0x00 0x18 + 0x010000B6 0x08 0x82 0x27 + 0x010000F2 0x00 + 0x01000026 0x01 + 0x010000E0 0x0F 0x31 0x2B 0x0C 0x0E 0x08 0x4E 0xF1 0x37 0x07 0x10 0x03 0x0E 0x09 0x00 + 0x010000E1 0x00 0x0E 0x14 0x03 0x11 0x07 0x31 0xC1 0x48 0x08 0x0F 0x0C 0x31 0x36 0x0F + 0x01000011 + 0x02000078 + 0x01000029 + 0x0100002C>; + bgr; + }; + }; + + /* waveshare22 */ + fragment@31 { + target = <&display>; + __dormant__ { + compatible = "hitachi,bd663474"; + spi-max-frequency = <32000000>; + spi-cpha; + spi-cpol; + }; + }; + + spidev_fragment: fragment@100 { + target-path = "spi0/spidev@0"; + __overlay__ { + status = "disabled"; + }; + }; + + display_fragment: fragment@101 { + target = <&spi0>; + __overlay__ { + /* needed to avoid dtc warning */ + #address-cells = <1>; + #size-cells = <0>; + + status = "okay"; + + display: display@0{ + reg = <0>; + spi-max-frequency = <32000000>; + fps = <30>; + buswidth = <8>; + }; + }; + }; + + __overrides__ { + spi0-0 = <&display_fragment>, "target:0=",<&spi0>, + <&spidev_fragment>, "target-path=spi0/spidev@0", + <&display>, "reg:0=0"; + spi0-1 = <&display_fragment>, "target:0=",<&spi0>, + <&spidev_fragment>, "target-path=spi0/spidev@1", + <&display>, "reg:0=1"; + spi1-0 = <&display_fragment>, "target:0=",<&spi1>, + <&spidev_fragment>, "target-path=spi1/spidev@0", + <&display>, "reg:0=0"; + spi1-1 = <&display_fragment>, "target:0=",<&spi1>, + <&spidev_fragment>, "target-path=spi1/spidev@1", + <&display>, "reg:0=1"; + spi1-2 = <&display_fragment>, "target:0=",<&spi1>, + <&spidev_fragment>, "target-path=spi1/spidev@2", + <&display>, "reg:0=2"; + spi2-0 = <&display_fragment>, "target:0=",<&spi2>, + <&spidev_fragment>, "target-path=spi2/spidev@0", + <&display>, "reg:0=0"; + spi2-1 = <&display_fragment>, "target:0=",<&spi2>, + <&spidev_fragment>, "target-path=spi2/spidev@1", + <&display>, "reg:0=1"; + spi2-2 = <&display_fragment>, "target:0=",<&spi2>, + <&spidev_fragment>, "target-path=spi2/spidev@2", + <&display>, "reg:0=2"; + + speed = <&display>, "spi-max-frequency:0"; + cpha = <&display>, "spi-cpha?"; + cpol = <&display>, "spi-cpol?"; + + /* Displays */ + adafruit18 = <0>, "+0"; + adafruit22 = <0>, "+1"; + adafruit22a = <0>, "+2"; + adafruit28 = <0>, "+3"; + adafruit13m = <0>, "+4"; + admatec_c-berry28 = <0>, "+5"; + dogs102 = <0>, "+6"; + er_tftm050_2 = <0>, "+7"; + er_tftm070_5 = <0>, "+8"; + ew24ha0 = <0>, "+9"; + ew24ha0_9bit = <0>, "+10"; + freetronicsoled128 = <0>, "+11"; + hy28a = <0>, "+12"; + hy28b = <0>, "+13"; + itdb28_spi = <0>, "+14"; + mi0283qt-2 = <0>, "+15"; + mi0283qt-9a = <0>, "+16"; + nokia3310 = <0>, "+17"; + nokia3310a = <0>, "+18"; + nokia5110 = <0>, "+19"; + piscreen = <0>, "+20"; + pitft = <0>, "+21"; + pioled = <0>, "+22"; + rpi-display = <0>, "+23"; + sainsmart18 = <0>, "+24"; + sainsmart32_spi = <0>, "+25"; + tinylcd35 = <0>, "+26"; + tm022hdh26 = <0>, "+27"; + tontec35_9481 = <0>, "+28"; + tontec35_9486 = <0>, "+29"; + waveshare32b = <0>, "+30"; + waveshare22 = <0>, "+31"; + + /* Controllers */ + bd663474 = <&display>, "compatible=hitachi,bd663474"; + hx8340bn = <&display>, "compatible=himax,hx8340bn"; + hx8347d = <&display>, "compatible=himax,hx8347d"; + hx8353d = <&display>, "compatible=himax,hx8353d"; + hx8357d = <&display>, "compatible=himax,hx8357d"; + ili9163 = <&display>, "compatible=ilitek,ili9163"; + ili9320 = <&display>, "compatible=ilitek,ili9320"; + ili9325 = <&display>, "compatible=ilitek,ili9325"; + ili9340 = <&display>, "compatible=ilitek,ili9340"; + ili9341 = <&display>, "compatible=ilitek,ili9341"; + ili9481 = <&display>, "compatible=ilitek,ili9481"; + ili9486 = <&display>, "compatible=ilitek,ili9486"; + pcd8544 = <&display>, "compatible=philips,pcd8544"; + ra8875 = <&display>, "compatible=raio,ra8875"; + s6d02a1 = <&display>, "compatible=samsung,s6d02a1"; + s6d1121 = <&display>, "compatible=samsung,s6d1121"; + seps525 = <&display>, "compatible=syncoam,seps525"; + sh1106 = <&display>, "compatible=sinowealth,sh1106"; + ssd1289 = <&display>, "compatible=solomon,ssd1289"; + ssd1305 = <&display>, "compatible=solomon,ssd1305"; + ssd1306 = <&display>, "compatible=solomon,ssd1306"; + ssd1325 = <&display>, "compatible=solomon,ssd1325"; + ssd1331 = <&display>, "compatible=solomon,ssd1331"; + ssd1351 = <&display>, "compatible=solomon,ssd1351"; + st7735r = <&display>, "compatible=sitronix,st7735r"; + st7789v = <&display>, "compatible=sitronix,st7789v"; + tls8204 = <&display>, "compatible=teralane,tls8204"; + uc1611 = <&display>, "compatible=ultrachip,uc1611"; + uc1701 = <&display>, "compatible=UltraChip,uc1701"; + upd161704 = <&display>, "compatible=nec,upd161704"; + + width = <&display>, "width:0"; + height = <&display>, "height:0"; + regwidth = <&display>, "regwidth:0"; + buswidth = <&display>, "buswidth:0"; + debug = <&display>, "debug:0"; + rotate = <&display>, "rotate:0"; + bgr = <&display>, "bgr?"; + fps = <&display>, "fps:0"; + txbuflen = <&display>, "txbuflen:0"; + startbyte = <&display>, "startbyte:0"; + gamma = <&display>, "gamma"; + + reset_pin = <&display>, "reset-gpios:0=", <&gpio>, + <&display>, "reset-gpios:4", + <&display>, "reset-gpios:8=1"; /* GPIO_ACTIVE_LOW */ + dc_pin = <&display>, "dc-gpios:0=", <&gpio>, + <&display>, "dc-gpios:4", + <&display>, "dc-gpios:8=0"; /* GPIO_ACTIVE_HIGH */ + led_pin = <&display>, "led-gpios:0=", <&gpio>, + <&display>, "led-gpios:4", + <&display>, "led-gpios:8=0"; /* GPIO_ACTIVE_HIGH */ + }; +}; diff --git a/arch/arm/boot/dts/overlays/gpio-poweroff-overlay.dts b/arch/arm/boot/dts/overlays/gpio-poweroff-overlay.dts index 416aa2bc797a3d778b1045cedf2795f45470e87b..8153f83f04270c3743e96bf07d470c6d48d61707 100644 --- a/arch/arm/boot/dts/overlays/gpio-poweroff-overlay.dts +++ b/arch/arm/boot/dts/overlays/gpio-poweroff-overlay.dts @@ -33,5 +33,7 @@ input = <&power_ctrl>,"input?"; export = <&power_ctrl>,"export?"; timeout_ms = <&power_ctrl>,"timeout-ms:0"; + active_delay_ms = <&power_ctrl>,"active-delay-ms:0"; + inactive_delay_ms = <&power_ctrl>,"inactive-delay-ms:0"; }; }; diff --git a/arch/arm/boot/dts/overlays/imx219-overlay.dts b/arch/arm/boot/dts/overlays/imx219-overlay.dts index 0c065bf09f54fa02b06538eccf89f269095afbb7..bc1217397dd5e15c2ca2f6300fa7aab8d0b4a3ff 100644 --- a/arch/arm/boot/dts/overlays/imx219-overlay.dts +++ b/arch/arm/boot/dts/overlays/imx219-overlay.dts @@ -9,6 +9,28 @@ compatible = "brcm,bcm2835"; fragment@0 { + target = <&i2c0if>; + __overlay__ { + status = "okay"; + }; + }; + + clk_frag: fragment@1 { + target = <&cam1_clk>; + __overlay__ { + status = "okay"; + clock-frequency = <24000000>; + }; + }; + + fragment@2 { + target = <&i2c0mux>; + __overlay__ { + status = "okay"; + }; + }; + + i2c_frag: fragment@100 { target = <&i2c_csi_dsi>; __overlay__ { #address-cells = <1>; @@ -20,19 +42,19 @@ reg = <0x10>; status = "okay"; - clocks = <&imx219_clk>; + clocks = <&cam1_clk>; clock-names = "xclk"; VANA-supply = <&cam1_reg>; /* 2.8v */ - VDIG-supply = <&imx219_vdig>; /* 1.8v */ - VDDL-supply = <&imx219_vddl>; /* 1.2v */ + VDIG-supply = <&cam_dummy_reg>; /* 1.8v */ + VDDL-supply = <&cam_dummy_reg>; /* 1.2v */ rotation = <180>; orientation = <2>; port { imx219_0: endpoint { - remote-endpoint = <&csi1_ep>; + remote-endpoint = <&csi_ep>; clock-lanes = <0>; data-lanes = <1 2>; clock-noncontinuous; @@ -44,13 +66,14 @@ }; }; - fragment@1 { + csi_frag: fragment@101 { target = <&csi1>; - __overlay__ { + csi: __overlay__ { status = "okay"; + brcm,media-controller; port { - csi1_ep: endpoint { + csi_ep: endpoint { remote-endpoint = <&imx219_0>; clock-lanes = <0>; data-lanes = <1 2>; @@ -60,56 +83,14 @@ }; }; - 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"; orientation = <&imx219>,"orientation:0"; + media-controller = <&csi>,"brcm,media-controller?"; + cam0 = <&i2c_frag>, "target:0=",<&i2c_vc>, + <&csi_frag>, "target:0=",<&csi0>, + <&clk_frag>, "target:0=",<&cam0_clk>, + <&imx219>, "clocks:0=",<&cam0_clk>, + <&imx219>, "VANA-supply:0=",<&cam0_reg>; }; }; diff --git a/arch/arm/boot/dts/overlays/imx290_327-overlay.dtsi b/arch/arm/boot/dts/overlays/imx290_327-overlay.dtsi index d4a5ed6dbbcfa445940ced43e1839ba248c8ca5e..111d69597554baf76e7a402de34ab95d2da35364 100644 --- a/arch/arm/boot/dts/overlays/imx290_327-overlay.dtsi +++ b/arch/arm/boot/dts/overlays/imx290_327-overlay.dtsi @@ -9,7 +9,7 @@ /{ compatible = "brcm,bcm2835"; - fragment@0 { + i2c_frag: fragment@0 { target = <&i2c_csi_dsi>; __overlay__ { #address-cells = <1>; @@ -20,7 +20,7 @@ reg = <0x1a>; status = "okay"; - clocks = <&imx290_clk>; + clocks = <&cam1_clk>; clock-names = "xclk"; clock-frequency = <37125000>; @@ -28,8 +28,8 @@ orientation = <2>; vdda-supply = <&cam1_reg>; /* 2.8v */ - vdddo-supply = <&imx290_vdddo>; /* 1.8v */ - vddd-supply = <&imx290_vddd>; /* 1.5v */ + vdddo-supply = <&cam_dummy_reg>; /* 1.8v */ + vddd-supply = <&cam_dummy_reg>; /* 1.5v */ port { imx290_0: endpoint { @@ -41,10 +41,11 @@ }; }; - fragment@1 { + csi_frag: fragment@1 { target = <&csi1>; - __overlay__ { + csi: __overlay__ { status = "okay"; + brcm,media-controller; port { csi1_ep: endpoint { @@ -61,27 +62,11 @@ }; }; - 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>; - }; + clk_frag: fragment@3 { + target = <&cam1_clk>; + cam_clk: __overlay__ { + status = "okay"; + clock-frequency = <37125000>; }; }; @@ -92,16 +77,6 @@ }; }; - 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__ { @@ -136,9 +111,15 @@ __overrides__ { 4lane = <0>, "-6+7-8+9"; - clock-frequency = <&imx290_clk>,"clock-frequency:0", + clock-frequency = <&cam_clk>,"clock-frequency:0", <&imx290>,"clock-frequency:0"; rotation = <&imx290>,"rotation:0"; orientation = <&imx290>,"orientation:0"; + media-controller = <&csi>,"brcm,media-controller?"; + cam0 = <&i2c_frag>, "target:0=",<&i2c_vc>, + <&csi_frag>, "target:0=",<&csi0>, + <&clk_frag>, "target:0=",<&cam0_clk>, + <&imx290>, "clocks:0=",<&cam0_clk>, + <&imx290>, "vdda-supply:0=",<&cam0_reg>; }; }; diff --git a/arch/arm/boot/dts/overlays/imx477_378-overlay.dtsi b/arch/arm/boot/dts/overlays/imx477_378-overlay.dtsi index bb9a9acdbbd7638f5610fbe4090665876281ae99..bfea40ce98d63b93878ea84b55c4d0a6c9619296 100644 --- a/arch/arm/boot/dts/overlays/imx477_378-overlay.dtsi +++ b/arch/arm/boot/dts/overlays/imx477_378-overlay.dtsi @@ -4,7 +4,36 @@ /{ compatible = "brcm,bcm2835"; - fragment@0 { + fragment@2 { + target = <&i2c0if>; + __overlay__ { + status = "okay"; + }; + }; + + clk_frag: fragment@3 { + target = <&cam1_clk>; + cam_clk: __overlay__ { + clock-frequency = <24000000>; + status = "okay"; + }; + }; + + fragment@4 { + target = <&i2c0mux>; + __overlay__ { + status = "okay"; + }; + }; + + reg_frag: fragment@5 { + target = <&cam1_reg>; + cam_reg: __overlay__ { + startup-delay-us = <300000>; + }; + }; + + i2c_frag: fragment@100 { target = <&i2c_csi_dsi>; __overlay__ { #address-cells = <1>; @@ -15,19 +44,19 @@ reg = <0x1a>; status = "okay"; - clocks = <&imx477_clk>; + clocks = <&cam1_clk>; clock-names = "xclk"; VANA-supply = <&cam1_reg>; /* 2.8v */ - VDIG-supply = <&imx477_vdig>; /* 1.05v */ - VDDL-supply = <&imx477_vddl>; /* 1.8v */ + VDIG-supply = <&cam_dummy_reg>; /* 1.05v */ + VDDL-supply = <&cam_dummy_reg>; /* 1.8v */ rotation = <180>; orientation = <2>; port { imx477_0: endpoint { - remote-endpoint = <&csi1_ep>; + remote-endpoint = <&csi_ep>; clock-lanes = <0>; data-lanes = <1 2>; clock-noncontinuous; @@ -39,13 +68,14 @@ }; }; - fragment@1 { + csi_frag: fragment@101 { target = <&csi1>; - __overlay__ { + csi: __overlay__ { status = "okay"; + brcm,media-controller; port { - csi1_ep: endpoint { + csi_ep: endpoint { remote-endpoint = <&imx477_0>; clock-lanes = <0>; data-lanes = <1 2>; @@ -55,56 +85,15 @@ }; }; - 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"; orientation = <&imx477>,"orientation:0"; + media-controller = <&csi>,"brcm,media-controller?"; + cam0 = <&i2c_frag>, "target:0=",<&i2c_vc>, + <&csi_frag>, "target:0=",<&csi0>, + <&clk_frag>, "target:0=",<&cam0_clk>, + <®_frag>, "target:0=",<&cam0_reg>, + <&imx477>, "clocks:0=",<&cam0_clk>, + <&imx477>, "vdda-supply:0=",<&cam0_reg>; }; }; diff --git a/arch/arm/boot/dts/overlays/imx519-overlay.dts b/arch/arm/boot/dts/overlays/imx519-overlay.dts index 693c267af1f0d24e0b3a48b11c57d2a728b0fd9d..ada1224dd19b88be14347926db58194cad4a98f9 100644 --- a/arch/arm/boot/dts/overlays/imx519-overlay.dts +++ b/arch/arm/boot/dts/overlays/imx519-overlay.dts @@ -8,7 +8,7 @@ /{ compatible = "brcm,bcm2835"; - fragment@0 { + i2c_frag: fragment@0 { target = <&i2c_csi_dsi>; __overlay__ { #address-cells = <1>; @@ -20,12 +20,12 @@ reg = <0x1a>; status = "okay"; - clocks = <&imx519_clk>; + clocks = <&cam1_clk>; clock-names = "xclk"; VANA-supply = <&cam1_reg>; /* 2.8v */ - VDIG-supply = <&imx519_vdig>; /* 1.8v */ - VDDL-supply = <&imx519_vddl>; /* 1.2v */ + VDIG-supply = <&cam_dummy_reg>; /* 1.8v */ + VDDL-supply = <&cam_dummy_reg>; /* 1.2v */ rotation = <0>; orientation = <2>; @@ -44,10 +44,11 @@ }; }; - fragment@1 { + csi_frag: fragment@1 { target = <&csi1>; - __overlay__ { + csi: __overlay__ { status = "okay"; + brcm,media-controller; port{ csi1_ep: endpoint{ @@ -67,27 +68,11 @@ }; }; - fragment@3 { - target-path="/"; + clk_frag: fragment@3 { + target = <&cam1_clk>; __overlay__ { - imx519_vdig: fixedregulator@1 { - compatible = "regulator-fixed"; - regulator-name = "imx519_vdig"; - regulator-min-microvolt = <1800000>; - regulator-max-microvolt = <1800000>; - }; - imx519_vddl: fixedregulator@2 { - compatible = "regulator-fixed"; - regulator-name = "imx519_vddl"; - regulator-min-microvolt = <1200000>; - regulator-max-microvolt = <1200000>; - }; - - imx519_clk: camera-clk { - compatible = "fixed-clock"; - #clock-cells = <0>; - clock-frequency = <24000000>; - }; + clock-frequency = <24000000>; + status = "okay"; }; }; @@ -98,18 +83,14 @@ }; }; - fragment@5 { - target = <&cam1_reg>; - __overlay__ { - status = "okay"; - regulator-name = "imx519_vana"; - regulator-min-microvolt = <2800000>; - regulator-max-microvolt = <2800000>; - }; - }; - __overrides__ { rotation = <&imx519>,"rotation:0"; orientation = <&imx519>,"orientation:0"; + media-controller = <&csi>,"brcm,media-controller?"; + cam0 = <&i2c_frag>, "target:0=",<&i2c_vc>, + <&csi_frag>, "target:0=",<&csi0>, + <&clk_frag>, "target:0=",<&cam0_clk>, + <&imx519>, "clocks:0=",<&cam0_clk>, + <&imx519>, "VANA-supply:0=",<&cam0_reg>; }; }; diff --git a/arch/arm/boot/dts/overlays/irs1125-overlay.dts b/arch/arm/boot/dts/overlays/irs1125-overlay.dts index e926e18e71fce10f36f171d8e0b48ab9ef9df728..8f8432c07a8952097d6ebfde16d0b79f87ea2a09 100644 --- a/arch/arm/boot/dts/overlays/irs1125-overlay.dts +++ b/arch/arm/boot/dts/overlays/irs1125-overlay.dts @@ -6,20 +6,20 @@ /{ compatible = "brcm,bcm2835"; - fragment@0 { + i2c_frag: fragment@0 { target = <&i2c_csi_dsi>; __overlay__ { #address-cells = <1>; #size-cells = <0>; status = "okay"; - irs1125: irs1125@3D { + irs1125: irs1125@3d { compatible = "infineon,irs1125"; - reg = <0x3D>; + reg = <0x3d>; status = "okay"; pwdn-gpios = <&gpio 5 0>; - clocks = <&irs1125_clk>; + clocks = <&cam1_clk>; port { irs1125_0: endpoint { @@ -35,9 +35,9 @@ }; }; - fragment@1 { + csi_frag: fragment@1 { target = <&csi1>; - __overlay__ { + csi: __overlay__ { status = "okay"; port { @@ -72,14 +72,19 @@ }; }; - fragment@5 { - target-path = "/"; + clk_frag: fragment@5 { + target = <&cam1_clk>; __overlay__ { - irs1125_clk: camera-clk { - compatible = "fixed-clock"; - #clock-cells = <0>; - clock-frequency = <26000000>; - }; + status = "okay"; + clock-frequency = <26000000>; }; }; + + __overrides__ { + media-controller = <&csi>,"brcm,media-controller?"; + cam0 = <&i2c_frag>, "target:0=",<&i2c_vc>, + <&csi_frag>, "target:0=",<&csi0>, + <&clk_frag>, "target:0=",<&cam0_clk>, + <&irs1125>, "clocks:0=",<&cam0_clk>; + }; }; diff --git a/arch/arm/boot/dts/overlays/ov5647-overlay.dts b/arch/arm/boot/dts/overlays/ov5647-overlay.dts index d7ed4703c9b0b073ad17a25cf59a923cedaf66a0..a1221024d3343f6aef75dd80953d15dcfc8ccf5a 100644 --- a/arch/arm/boot/dts/overlays/ov5647-overlay.dts +++ b/arch/arm/boot/dts/overlays/ov5647-overlay.dts @@ -6,7 +6,7 @@ /{ compatible = "brcm,bcm2835"; - fragment@0 { + i2c_frag: fragment@0 { target = <&i2c_csi_dsi>; __overlay__ { #address-cells = <1>; @@ -18,8 +18,11 @@ reg = <0x36>; status = "okay"; - pwdn-gpios = <&gpio 41 1>, <&gpio 32 1>; - clocks = <&ov5647_clk>; + clocks = <&cam1_clk>; + + avdd-supply = <&cam1_reg>; + dovdd-supply = <&cam_dummy_reg>; + dvdd-supply = <&cam_dummy_reg>; rotation = <0>; orientation = <2>; @@ -38,10 +41,11 @@ }; }; - fragment@1 { + csi_frag: fragment@1 { target = <&csi1>; - __overlay__ { + csi: __overlay__ { status = "okay"; + brcm,media-controller; port { csi1_ep: endpoint { @@ -66,29 +70,30 @@ }; }; - fragment@4 { - target-path="/__overrides__"; + reg_frag: fragment@4 { + target = <&cam1_reg>; __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"; + startup-delay-us = <20000>; }; }; - fragment@5 { - target-path = "/"; + clk_frag: fragment@5 { + target = <&cam1_clk>; __overlay__ { - ov5647_clk: camera-clk { - compatible = "fixed-clock"; - #clock-cells = <0>; - clock-frequency = <25000000>; - }; + status = "okay"; + clock-frequency = <25000000>; }; }; __overrides__ { rotation = <&ov5647>,"rotation:0"; orientation = <&ov5647>,"orientation:0"; + media-controller = <&csi>,"brcm,media-controller?"; + cam0 = <&i2c_frag>, "target:0=",<&i2c_vc>, + <&csi_frag>, "target:0=",<&csi0>, + <®_frag>, "target:0=",<&cam0_reg>, + <&clk_frag>, "target:0=",<&cam0_clk>, + <&ov5647>, "clocks:0=",<&cam0_clk>, + <&ov5647>, "avdd-supply:0=",<&cam0_reg>; }; }; diff --git a/arch/arm/boot/dts/overlays/ov7251-overlay.dts b/arch/arm/boot/dts/overlays/ov7251-overlay.dts index 09dbeda39d06453f4a3bd931f12aa153b6c1b03f..0e44be8a446865424b15e97646cf8c737c153065 100644 --- a/arch/arm/boot/dts/overlays/ov7251-overlay.dts +++ b/arch/arm/boot/dts/overlays/ov7251-overlay.dts @@ -8,7 +8,7 @@ /{ compatible = "brcm,bcm2835"; - fragment@0 { + i2c_frag: fragment@0 { target = <&i2c_csi_dsi>; __overlay__ { #address-cells = <1>; @@ -20,13 +20,13 @@ reg = <0x60>; status = "okay"; - clocks = <&ov7251_clk>; + clocks = <&cam1_clk>; clock-names = "xclk"; clock-frequency = <24000000>; - vdddo-supply = <&ov7251_dovdd>; + vdddo-supply = <&cam_dummy_reg>; vdda-supply = <&cam1_reg>; - vddd-supply = <&ov7251_dvdd>; + vddd-supply = <&cam_dummy_reg>; rotation = <0>; orientation = <2>; @@ -45,9 +45,9 @@ }; }; - fragment@1 { + csi_frag: fragment@1 { target = <&csi1>; - __overlay__ { + csi: __overlay__ { status = "okay"; port { @@ -67,47 +67,28 @@ }; 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>; + clk_frag: fragment@4 { + target = <&cam1_clk>; __overlay__ { status = "okay"; - regulator-name = "ov7251_avdd"; - regulator-min-microvolt = <2800000>; - regulator-max-microvolt = <2800000>; + clock-frequency = <24000000>; }; }; __overrides__ { rotation = <&ov7251>,"rotation:0"; orientation = <&ov7251>,"orientation:0"; + media-controller = <&csi>,"brcm,media-controller?"; + cam0 = <&i2c_frag>, "target:0=",<&i2c_vc>, + <&csi_frag>, "target:0=",<&csi0>, + <&clk_frag>, "target:0=",<&cam0_clk>, + <&ov7251>, "clocks:0=",<&cam0_clk>, + <&ov7251>, "vdda-supply:0=",<&cam0_reg>; }; }; diff --git a/arch/arm/boot/dts/overlays/ov9281-overlay.dts b/arch/arm/boot/dts/overlays/ov9281-overlay.dts index 277236c033584f977ec78bd7ea53544cc535e4a9..8c08a3a1077c3ab55eaefe35db05b465ae32dc1c 100644 --- a/arch/arm/boot/dts/overlays/ov9281-overlay.dts +++ b/arch/arm/boot/dts/overlays/ov9281-overlay.dts @@ -8,7 +8,7 @@ /{ compatible = "brcm,bcm2835"; - fragment@0 { + i2c_frag: fragment@0 { target = <&i2c_csi_dsi>; __overlay__ { #address-cells = <1>; @@ -20,12 +20,12 @@ reg = <0x60>; status = "okay"; - clocks = <&ov9281_clk>; + clocks = <&cam1_clk>; clock-names = "xvclk"; avdd-supply = <&cam1_reg>; - dovdd-supply = <&ov9281_dovdd>; - dvdd-supply = <&ov9281_dvdd>; + dovdd-supply = <&cam_dummy_reg>; + dvdd-supply = <&cam_dummy_reg>; rotation = <0>; orientation = <2>; @@ -44,10 +44,11 @@ }; }; - fragment@1 { + csi_frag: fragment@1 { target = <&csi1>; - __overlay__ { + csi: __overlay__ { status = "okay"; + brcm,media-controller; port { csi1_ep: endpoint { @@ -67,47 +68,28 @@ }; 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>; + clk_frag: fragment@4 { + target = <&cam1_clk>; __overlay__ { status = "okay"; - regulator-name = "ov9281_avdd"; - regulator-min-microvolt = <2800000>; - regulator-max-microvolt = <2800000>; + clock-frequency = <24000000>; }; }; __overrides__ { rotation = <&ov9281>,"rotation:0"; orientation = <&ov9281>,"orientation:0"; + media-controller = <&csi>,"brcm,media-controller?"; + cam0 = <&i2c_frag>, "target:0=",<&i2c_vc>, + <&csi_frag>, "target:0=",<&csi0>, + <&clk_frag>, "target:0=",<&cam0_clk>, + <&ov9281>, "clocks:0=",<&cam0_clk>, + <&ov9281>, "avdd-supply:0=",<&cam0_reg>; }; }; diff --git a/arch/arm/boot/dts/overlays/overlay_map.dts b/arch/arm/boot/dts/overlays/overlay_map.dts index bc6e3bce22c722e62f42d9b9f190a554c585e2f8..9fd31bcd55690f31738eb44df1e1306c9ee7462c 100644 --- a/arch/arm/boot/dts/overlays/overlay_map.dts +++ b/arch/arm/boot/dts/overlays/overlay_map.dts @@ -5,6 +5,10 @@ deprecated = "use i2c-sensor,bmp085"; }; + cutiepi-panel { + bcm2711; + }; + highperi { bcm2711; }; diff --git a/arch/arm/boot/dts/overlays/rpi-display-overlay.dts b/arch/arm/boot/dts/overlays/rpi-display-overlay.dts index de87432ff2bea449685172a58d91b042ea02608a..2cf937b564569d8ef2d660dbf13da6c6194bcbe9 100644 --- a/arch/arm/boot/dts/overlays/rpi-display-overlay.dts +++ b/arch/arm/boot/dts/overlays/rpi-display-overlay.dts @@ -61,7 +61,7 @@ buswidth = <8>; reset-gpios = <&gpio 23 1>; dc-gpios = <&gpio 24 0>; - led-gpios = <&gpio 18 0>; + led-gpios = <&gpio 18 1>; debug = <0>; }; diff --git a/arch/arm/boot/dts/overlays/tc358743-overlay.dts b/arch/arm/boot/dts/overlays/tc358743-overlay.dts index a1f8af36d2e7460280c92a9e03b0d3f6d9e9c594..c3eebfd1f6eed7736cf63facb2cc7ad7b8eca1fa 100644 --- a/arch/arm/boot/dts/overlays/tc358743-overlay.dts +++ b/arch/arm/boot/dts/overlays/tc358743-overlay.dts @@ -6,23 +6,23 @@ /{ compatible = "brcm,bcm2835"; - fragment@0 { + i2c_frag: fragment@0 { target = <&i2c_csi_dsi>; __overlay__ { #address-cells = <1>; #size-cells = <0>; status = "okay"; - tc358743@0f { + tc358743: tc358743@f { compatible = "toshiba,tc358743"; reg = <0x0f>; status = "okay"; - clocks = <&tc358743_clk>; + clocks = <&cam1_clk>; clock-names = "refclk"; port { - tc358743: endpoint { + tc358743_0: endpoint { remote-endpoint = <&csi1_ep>; clock-lanes = <0>; clock-noncontinuous; @@ -34,28 +34,28 @@ }; }; - fragment@1 { + csi_frag: fragment@1 { target = <&csi1>; - __overlay__ { + csi: __overlay__ { status = "okay"; port { csi1_ep: endpoint { - remote-endpoint = <&tc358743>; + remote-endpoint = <&tc358743_0>; }; }; }; }; fragment@2 { - target = <&tc358743>; + target = <&tc358743_0>; __overlay__ { data-lanes = <1 2>; }; }; fragment@3 { - target = <&tc358743>; + target = <&tc358743_0>; __dormant__ { data-lanes = <1 2 3 4>; }; @@ -75,14 +75,11 @@ }; }; - fragment@6 { - target-path = "/"; + clk_frag: fragment@6 { + target = <&cam1_clk>; __overlay__ { - tc358743_clk: bridge-clk { - compatible = "fixed-clock"; - #clock-cells = <0>; - clock-frequency = <27000000>; - }; + status = "okay"; + clock-frequency = <27000000>; }; }; @@ -102,6 +99,11 @@ __overrides__ { 4lane = <0>, "-2+3-7+8"; - link-frequency = <&tc358743>,"link-frequencies#0"; + link-frequency = <&tc358743_0>,"link-frequencies#0"; + media-controller = <&csi>,"brcm,media-controller?"; + cam0 = <&i2c_frag>, "target:0=",<&i2c_vc>, + <&csi_frag>, "target:0=",<&csi0>, + <&clk_frag>, "target:0=",<&cam0_clk>, + <&tc358743>, "clocks:0=",<&cam0_clk>; }; }; diff --git a/arch/arm/boot/dts/overlays/upstream-overlay.dts b/arch/arm/boot/dts/overlays/upstream-overlay.dts index 7c4071a7cb2702079c30fcd289e8bc83a172d283..2852bea52309bd4a016a685eb44a914b3938f8ee 100644 --- a/arch/arm/boot/dts/overlays/upstream-overlay.dts +++ b/arch/arm/boot/dts/overlays/upstream-overlay.dts @@ -1,4 +1,4 @@ -// redo: ovmerge -c vc4-kms-v3d-overlay.dts,cma-default dwc2-overlay.dts,dr_mode=otg +// redo: ovmerge -c vc4-kms-v3d-overlay.dts,cma-default,composite dwc2-overlay.dts,dr_mode=otg /dts-v1/; /plugin/; diff --git a/arch/arm/boot/dts/overlays/vc4-kms-dpi-generic-overlay.dts b/arch/arm/boot/dts/overlays/vc4-kms-dpi-generic-overlay.dts new file mode 100644 index 0000000000000000000000000000000000000000..7846d56c1d1a5c465b3d05388559f0d9383e01b1 --- /dev/null +++ b/arch/arm/boot/dts/overlays/vc4-kms-dpi-generic-overlay.dts @@ -0,0 +1,111 @@ +/* + * vc4-kms-dpi-at056tn53v1-overlay.dts + */ + +/dts-v1/; +/plugin/; + +#include +#include + +/ { + compatible = "brcm,bcm2835"; + + fragment@0 { + target-path = "/"; + __overlay__ { + panel: panel { + compatible = "panel-dpi"; + + width-mm = <154>; + height-mm = <83>; + bus-format = <0x1009>; + + timing: panel-timing { + clock-frequency = <29500000>; + hactive = <800>; + hfront-porch = <24>; + hsync-len = <72>; + hback-porch = <96>; + hsync-active = <0>; + vactive = <480>; + vfront-porch = <3>; + vsync-len = <10>; + vback-porch = <7>; + vsync-active = <0>; + + de-active = <1>; + pixelclk-active = <1>; + }; + + port { + panel_in: endpoint { + remote-endpoint = <&dpi_out>; + }; + }; + }; + }; + }; + + fragment@1 { + target = <&dpi>; + dpi_node: __overlay__ { + status = "okay"; + + pinctrl-names = "default"; + pinctrl-0 = <&dpi_18bit_gpio0>; + + port { + dpi_out: endpoint { + remote-endpoint = <&panel_in>; + }; + }; + }; + }; + + fragment@2 { + target = <&panel>; + __dormant__ { + backlight = <&backlight>; + }; + }; + + fragment@3 { + target-path = "/"; + __dormant__ { + backlight: backlight { + compatible = "gpio-backlight"; + gpios = <&gpio 255 GPIO_ACTIVE_HIGH>; + }; + }; + }; + + __overrides__ { + clock-frequency = <&timing>, "clock-frequency:0"; + hactive = <&timing>, "hactive:0"; + hfp = <&timing>, "hfront-porch:0"; + hsync = <&timing>, "hsync-len:0"; + hbp = <&timing>, "hback-porch:0"; + vactive = <&timing>, "vactive:0"; + vfp = <&timing>, "vfront-porch:0"; + vsync = <&timing>, "vsync-len:0"; + vbp = <&timing>, "vback-porch:0"; + hsync-invert = <&timing>, "hsync-active:0=0"; + vsync-invert = <&timing>, "vsync-active:0=0"; + de-invert = <&timing>, "de-active:0=0"; + pixclk-invert = <&timing>, "pixelclk-active:0=0"; + + width-mm = <&panel>, "width-mm:0"; + height-mm = <&panel>, "height-mm:0"; + + rgb565 = <&panel>, "bus-format:0=0x1017", + <&dpi_node>, "pinctrl-0:0=",<&dpi_16bit_gpio0>; + rgb666-padhi = <&panel>, "bus-format:0=0x1015", + <&dpi_node>, "pinctrl-0:0=",<&dpi_18bit_cpadhi_gpio0>; + rgb888 = <&panel>, "bus-format:0=0x100a", + <&dpi_node>, "pinctrl-0:0=",<&dpi_gpio0>; + bus-format = <&panel>, "bus-format:0"; + backlight-gpio = <0>, "+2+3", + <&backlight>, "gpios:4"; + }; +}; diff --git a/arch/arm/boot/dts/overlays/vc4-kms-dsi-7inch-overlay.dts b/arch/arm/boot/dts/overlays/vc4-kms-dsi-7inch-overlay.dts index ecd3bef3d65ab0f50fce30ee02f10169652b5cf5..5e1700d0367a0385a658791b84fffb5b3ed84601 100644 --- a/arch/arm/boot/dts/overlays/vc4-kms-dsi-7inch-overlay.dts +++ b/arch/arm/boot/dts/overlays/vc4-kms-dsi-7inch-overlay.dts @@ -113,6 +113,6 @@ }; __overrides__ { - disable_touch = <0>, "-10-11-12-13"; + disable_touch = <0>, "-10-11-12"; }; }; diff --git a/arch/arm/boot/dts/overlays/vc4-kms-v3d-overlay.dts b/arch/arm/boot/dts/overlays/vc4-kms-v3d-overlay.dts index 5a4efdeed6633618b52ec48c0c546cd44ee68a6a..351fc160e8030dce930e30c52d9051551b40b199 100644 --- a/arch/arm/boot/dts/overlays/vc4-kms-v3d-overlay.dts +++ b/arch/arm/boot/dts/overlays/vc4-kms-v3d-overlay.dts @@ -89,7 +89,7 @@ fragment@11 { target = <&vec>; - __overlay__ { + __dormant__ { status = "okay"; }; }; @@ -116,8 +116,8 @@ }; __overrides__ { - audio = <0>,"!13", <0>,"=14"; - noaudio = <0>,"=13", <0>,"!14"; - nocomposite = <0>, "!11"; + audio = <0>,"!13"; + noaudio = <0>,"=13"; + composite = <0>, "=11"; }; }; diff --git a/arch/arm/boot/dts/overlays/vc4-kms-v3d-pi4-overlay.dts b/arch/arm/boot/dts/overlays/vc4-kms-v3d-pi4-overlay.dts index 4285e12a4e538be0b861118e8df52c387b665ae2..76229cad7803e6b4df36af06c2eab736584e7134 100644 --- a/arch/arm/boot/dts/overlays/vc4-kms-v3d-pi4-overlay.dts +++ b/arch/arm/boot/dts/overlays/vc4-kms-v3d-pi4-overlay.dts @@ -180,7 +180,7 @@ __overrides__ { audio = <0>,"!17"; audio1 = <0>,"!18"; - noaudio = <0>,"=17", <0>,"=18", <0>,"!19"; + noaudio = <0>,"=17", <0>,"=18"; composite = <0>, "!1", <0>, "!2", <0>, "!3", diff --git a/arch/arm/boot/dts/overlays/vl805-overlay.dts b/arch/arm/boot/dts/overlays/vl805-overlay.dts new file mode 100644 index 0000000000000000000000000000000000000000..81adf34b29f24ba6ce6f580aa63d5756642c6871 --- /dev/null +++ b/arch/arm/boot/dts/overlays/vl805-overlay.dts @@ -0,0 +1,18 @@ +/dts-v1/; +/plugin/; + +#include + +/ { + compatible = "brcm,bcm2711"; + + fragment@0 { + target-path = "pcie0/pci@0,0"; + __overlay__ { + usb@0,0 { + reg = <0 0 0 0 0>; + resets = <&reset RASPBERRYPI_FIRMWARE_RESET_ID_USB>; + }; + }; + }; +}; diff --git a/arch/arm/configs/bcm2709_defconfig b/arch/arm/configs/bcm2709_defconfig index 7b4c28badf9108c6b6328da8fe6652a3277f238d..04a38fe4c6d99f3e038559ef2141f4ff5c912807 100644 --- a/arch/arm/configs/bcm2709_defconfig +++ b/arch/arm/configs/bcm2709_defconfig @@ -608,6 +608,8 @@ CONFIG_INPUT_JOYDEV=m CONFIG_INPUT_EVDEV=y # CONFIG_KEYBOARD_ATKBD is not set CONFIG_KEYBOARD_GPIO=m +CONFIG_KEYBOARD_TCA6416=m +CONFIG_KEYBOARD_TCA8418=m CONFIG_KEYBOARD_MATRIX=m CONFIG_KEYBOARD_CAP11XX=m # CONFIG_INPUT_MOUSE is not set @@ -1197,6 +1199,24 @@ 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 @@ -1223,6 +1243,7 @@ CONFIG_LEDS_PCA9532=m CONFIG_LEDS_GPIO=y CONFIG_LEDS_PCA955X=m CONFIG_LEDS_PCA963X=m +CONFIG_LEDS_PWM=y CONFIG_LEDS_IS31FL32XX=m CONFIG_LEDS_TRIGGER_TIMER=y CONFIG_LEDS_TRIGGER_ONESHOT=y @@ -1236,6 +1257,7 @@ CONFIG_LEDS_TRIGGER_CAMERA=m CONFIG_LEDS_TRIGGER_INPUT=y CONFIG_LEDS_TRIGGER_PANIC=y CONFIG_LEDS_TRIGGER_NETDEV=m +CONFIG_LEDS_TRIGGER_PATTERN=m CONFIG_LEDS_TRIGGER_ACTPWR=y CONFIG_ACCESSIBILITY=y CONFIG_SPEAKUP=m diff --git a/arch/arm/configs/bcm2711_defconfig b/arch/arm/configs/bcm2711_defconfig index a4543d42fc55c8c4a0ba98dbe131ddcd92722af0..77cccb33de2a5aaed2997dc75999bc304fc9ed94 100644 --- a/arch/arm/configs/bcm2711_defconfig +++ b/arch/arm/configs/bcm2711_defconfig @@ -621,6 +621,8 @@ CONFIG_INPUT_JOYDEV=m CONFIG_INPUT_EVDEV=y # CONFIG_KEYBOARD_ATKBD is not set CONFIG_KEYBOARD_GPIO=m +CONFIG_KEYBOARD_TCA6416=m +CONFIG_KEYBOARD_TCA8418=m CONFIG_KEYBOARD_MATRIX=m CONFIG_KEYBOARD_CAP11XX=m # CONFIG_INPUT_MOUSE is not set @@ -951,6 +953,7 @@ CONFIG_DRM=m CONFIG_DRM_LOAD_EDID_FIRMWARE=y CONFIG_DRM_UDL=m CONFIG_DRM_PANEL_SIMPLE=m +CONFIG_DRM_PANEL_ILITEK_ILI9881C=m CONFIG_DRM_PANEL_JDI_LT070ME05000=m CONFIG_DRM_PANEL_RASPBERRYPI_TOUCHSCREEN=m CONFIG_DRM_DISPLAY_CONNECTOR=m @@ -972,6 +975,7 @@ CONFIG_FB_UDL=m CONFIG_FB_SIMPLE=y CONFIG_FB_SSD1307=m CONFIG_FB_RPISENSE=m +CONFIG_BACKLIGHT_PWM=m CONFIG_BACKLIGHT_RPI=m CONFIG_BACKLIGHT_GPIO=m CONFIG_FRAMEBUFFER_CONSOLE=y @@ -1261,6 +1265,7 @@ CONFIG_LEDS_PCA9532=m CONFIG_LEDS_GPIO=y CONFIG_LEDS_PCA955X=m CONFIG_LEDS_PCA963X=m +CONFIG_LEDS_PWM=y CONFIG_LEDS_IS31FL32XX=m CONFIG_LEDS_TRIGGER_TIMER=y CONFIG_LEDS_TRIGGER_ONESHOT=y @@ -1274,6 +1279,7 @@ CONFIG_LEDS_TRIGGER_CAMERA=m CONFIG_LEDS_TRIGGER_INPUT=y CONFIG_LEDS_TRIGGER_PANIC=y CONFIG_LEDS_TRIGGER_NETDEV=m +CONFIG_LEDS_TRIGGER_PATTERN=m CONFIG_LEDS_TRIGGER_ACTPWR=y CONFIG_ACCESSIBILITY=y CONFIG_SPEAKUP=m diff --git a/arch/arm/configs/bcmrpi_defconfig b/arch/arm/configs/bcmrpi_defconfig index 5f1bd057138d59119f83d7396d6d83ab64269ac0..f2a474cdad5602deb0ffb9090aa39e302c80ba53 100644 --- a/arch/arm/configs/bcmrpi_defconfig +++ b/arch/arm/configs/bcmrpi_defconfig @@ -602,6 +602,8 @@ CONFIG_INPUT_JOYDEV=m CONFIG_INPUT_EVDEV=y # CONFIG_KEYBOARD_ATKBD is not set CONFIG_KEYBOARD_GPIO=m +CONFIG_KEYBOARD_TCA6416=m +CONFIG_KEYBOARD_TCA8418=m CONFIG_KEYBOARD_MATRIX=m CONFIG_KEYBOARD_CAP11XX=m # CONFIG_INPUT_MOUSE is not set @@ -1234,6 +1236,7 @@ CONFIG_LEDS_PCA9532=m CONFIG_LEDS_GPIO=y CONFIG_LEDS_PCA955X=m CONFIG_LEDS_PCA963X=m +CONFIG_LEDS_PWM=y CONFIG_LEDS_IS31FL32XX=m CONFIG_LEDS_TRIGGER_TIMER=y CONFIG_LEDS_TRIGGER_ONESHOT=y @@ -1247,6 +1250,7 @@ CONFIG_LEDS_TRIGGER_CAMERA=m CONFIG_LEDS_TRIGGER_INPUT=y CONFIG_LEDS_TRIGGER_PANIC=y CONFIG_LEDS_TRIGGER_NETDEV=m +CONFIG_LEDS_TRIGGER_PATTERN=m CONFIG_LEDS_TRIGGER_ACTPWR=y CONFIG_ACCESSIBILITY=y CONFIG_SPEAKUP=m diff --git a/arch/arm/lib/memcpy_rpi.S b/arch/arm/lib/memcpy_rpi.S index 30f8a9089a835ff265ce5aaf98f066258608eb92..77a1dbe28a188f590581a8007c985cf2ff76af2f 100644 --- a/arch/arm/lib/memcpy_rpi.S +++ b/arch/arm/lib/memcpy_rpi.S @@ -27,6 +27,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include +#include +#include #include "arm-mem.h" #include "memcpymove.h" diff --git a/arch/arm/lib/memcpymove.h b/arch/arm/lib/memcpymove.h index d8be5849c8609f4ead85527ec527184614a77371..65a6e065a7f2fe0c8e71b3a18d494bc2abffe77c 100644 --- a/arch/arm/lib/memcpymove.h +++ b/arch/arm/lib/memcpymove.h @@ -358,19 +358,13 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. LAST .req ip OFF .req lr - .cfi_startproc + UNWIND( .fnstart ) push {D, DAT1, DAT2, lr} + UNWIND( .fnend ) - .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 + UNWIND( .fnstart ) + UNWIND( .save {D, DAT1, DAT2, lr} ) .if backwards add D, D, N @@ -386,17 +380,11 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. /* Long case */ push {DAT3, DAT4, DAT5, DAT6, DAT7} + UNWIND( .fnend ) - .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 + UNWIND( .fnstart ) + UNWIND( .save {D, DAT1, DAT2, lr} ) + UNWIND( .save {DAT3, DAT4, DAT5, DAT6, DAT7} ) /* Adjust N so that the decrement instruction can also test for * inner loop termination. We want it to stop when there are @@ -436,16 +424,10 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 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 + UNWIND( .fnend ) + + UNWIND( .fnstart ) + UNWIND( .save {D, DAT1, DAT2, lr} ) 160: /* Medium case */ preload_all backwards, 0, 0, S, N, DAT2, OFF @@ -488,7 +470,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. memcpy_short_inner_loop backwards, 0 140: memcpy_short_inner_loop backwards, 1 - .cfi_endproc + UNWIND( .fnend ) .unreq D .unreq S diff --git a/arch/arm/lib/memmove_rpi.S b/arch/arm/lib/memmove_rpi.S index 8b0760c0904c51ef205e56d238b5df8a5226d7cf..5715dfd958597cb9bfc609638f14406e0857a82f 100644 --- a/arch/arm/lib/memmove_rpi.S +++ b/arch/arm/lib/memmove_rpi.S @@ -27,6 +27,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include +#include +#include #include "arm-mem.h" #include "memcpymove.h" diff --git a/arch/arm/lib/memset_rpi.S b/arch/arm/lib/memset_rpi.S index e8469cecabc15807a4a463436b04b7ce46eda4a1..2a2d867593970bc3a2bd791b4690d68a6b644e79 100644 --- a/arch/arm/lib/memset_rpi.S +++ b/arch/arm/lib/memset_rpi.S @@ -52,8 +52,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ ENTRY(mmioset) ENTRY(memset) -ENTRY(__memset32) -ENTRY(__memset64) S .req a1 DAT0 .req a2 @@ -63,10 +61,14 @@ ENTRY(__memset64) DAT3 .req lr orr DAT0, DAT0, DAT0, lsl #8 - push {S, lr} orr DAT0, DAT0, DAT0, lsl #16 + +ENTRY(__memset32) mov DAT1, DAT0 +ENTRY(__memset64) + push {S, lr} + /* See if we're guaranteed to have at least one 16-byte aligned 16-byte write */ cmp N, #31 blo 170f @@ -88,7 +90,7 @@ ENTRY(__memset64) 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 + mov DAT3, DAT1 /* Now the inner loop of 16-byte stores */ 165: stmia S!, {DAT0, DAT1, DAT2, DAT3} subs N, N, #16 @@ -104,7 +106,7 @@ ENTRY(__memset64) 170: /* Short case */ mov DAT2, DAT0 - mov DAT3, DAT0 + mov DAT3, DAT1 tst S, #3 beq 174f 172: subs N, N, #1 diff --git a/arch/arm/mm/proc-v7.S b/arch/arm/mm/proc-v7.S index fae01bd02629210cfc378ca9e13f0f59a7237838..2fcffcc60cc6cd9558734779fda606e9c4b08648 100644 --- a/arch/arm/mm/proc-v7.S +++ b/arch/arm/mm/proc-v7.S @@ -297,11 +297,7 @@ __v7_ca17mp_setup: 1: adr r0, __v7_setup_stack_ptr ldr r12, [r0] add r12, r12, r0 @ the local stack -1: stmia r12, {r1-r6, lr} @ v7_invalidate_l1 touches r0-r6 - ldr r0, [r12, #(6 * 4)] @ read back the return address - teq r0, lr @ confirm it is correct - bne 1b @ retrying if not bl v7_invalidate_l1 ldmia r12, {r1-r6, lr} #ifdef CONFIG_SMP @@ -487,11 +483,7 @@ __v7_setup: adr r0, __v7_setup_stack_ptr ldr r12, [r0] add r12, r12, r0 @ the local stack -1: stmia r12, {r1-r6, lr} @ v7_invalidate_l1 touches r0-r6 - ldr r0, [r12, #(6 * 4)] @ read back the return address - teq r0, lr @ confirm it is correct - bne 1b @ retrying if not bl v7_invalidate_l1 ldmia r12, {r1-r6, lr} diff --git a/arch/arm64/boot/dts/broadcom/Makefile b/arch/arm64/boot/dts/broadcom/Makefile index 69809bf2f45df7d2fb692bac51d9953ebe91cabc..e7c2c4fd59a8740259dd3e2deec50214a4b7f883 100644 --- a/arch/arm64/boot/dts/broadcom/Makefile +++ b/arch/arm64/boot/dts/broadcom/Makefile @@ -3,6 +3,7 @@ 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-zero-2.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 diff --git a/arch/arm64/boot/dts/broadcom/bcm2710-rpi-zero-2.dts b/arch/arm64/boot/dts/broadcom/bcm2710-rpi-zero-2.dts new file mode 100644 index 0000000000000000000000000000000000000000..f76f553599ef20f960b57bb16dc231f1fc669d39 --- /dev/null +++ b/arch/arm64/boot/dts/broadcom/bcm2710-rpi-zero-2.dts @@ -0,0 +1 @@ +#include "../../../../arm/boot/dts/bcm2710-rpi-zero-2.dts" diff --git a/arch/arm64/configs/bcm2711_defconfig b/arch/arm64/configs/bcm2711_defconfig index 88fa0a35d80278533b0988b7466122ddc4dcae99..b8e434797aa9a1c538f7ea0aa503003d576104fc 100644 --- a/arch/arm64/configs/bcm2711_defconfig +++ b/arch/arm64/configs/bcm2711_defconfig @@ -396,6 +396,7 @@ CONFIG_NET_ACT_SKBEDIT=m CONFIG_NET_ACT_CSUM=m CONFIG_BATMAN_ADV=m CONFIG_OPENVSWITCH=m +CONFIG_VSOCKETS=m CONFIG_CGROUP_NET_PRIO=y CONFIG_NET_PKTGEN=m CONFIG_HAMRADIO=y @@ -505,6 +506,7 @@ CONFIG_NETCONSOLE=m CONFIG_TUN=m CONFIG_VETH=m CONFIG_NET_VRF=m +CONFIG_VSOCKMON=m CONFIG_BCMGENET=y CONFIG_ENC28J60=m CONFIG_QCA7000_SPI=m @@ -616,6 +618,8 @@ CONFIG_INPUT_JOYDEV=m CONFIG_INPUT_EVDEV=y # CONFIG_KEYBOARD_ATKBD is not set CONFIG_KEYBOARD_GPIO=m +CONFIG_KEYBOARD_TCA6416=m +CONFIG_KEYBOARD_TCA8418=m CONFIG_KEYBOARD_MATRIX=m CONFIG_KEYBOARD_CAP11XX=m # CONFIG_INPUT_MOUSE is not set @@ -952,6 +956,7 @@ CONFIG_DRM=m CONFIG_DRM_LOAD_EDID_FIRMWARE=y CONFIG_DRM_UDL=m CONFIG_DRM_PANEL_SIMPLE=m +CONFIG_DRM_PANEL_ILITEK_ILI9881C=m CONFIG_DRM_PANEL_JDI_LT070ME05000=m CONFIG_DRM_PANEL_RASPBERRYPI_TOUCHSCREEN=m CONFIG_DRM_DISPLAY_CONNECTOR=m @@ -973,6 +978,7 @@ CONFIG_FB_UDL=m CONFIG_FB_SIMPLE=y CONFIG_FB_SSD1307=m CONFIG_FB_RPISENSE=m +CONFIG_BACKLIGHT_PWM=m CONFIG_BACKLIGHT_RPI=m CONFIG_BACKLIGHT_GPIO=m CONFIG_FRAMEBUFFER_CONSOLE=y @@ -1262,6 +1268,7 @@ CONFIG_LEDS_PCA9532=m CONFIG_LEDS_GPIO=y CONFIG_LEDS_PCA955X=m CONFIG_LEDS_PCA963X=m +CONFIG_LEDS_PWM=y CONFIG_LEDS_IS31FL32XX=m CONFIG_LEDS_TRIGGER_TIMER=y CONFIG_LEDS_TRIGGER_ONESHOT=y @@ -1275,6 +1282,7 @@ CONFIG_LEDS_TRIGGER_CAMERA=m CONFIG_LEDS_TRIGGER_INPUT=y CONFIG_LEDS_TRIGGER_PANIC=y CONFIG_LEDS_TRIGGER_NETDEV=m +CONFIG_LEDS_TRIGGER_PATTERN=m CONFIG_LEDS_TRIGGER_ACTPWR=y CONFIG_ACCESSIBILITY=y CONFIG_SPEAKUP=m @@ -1327,6 +1335,7 @@ CONFIG_HD44780=m CONFIG_UIO=m CONFIG_UIO_PDRV_GENIRQ=m CONFIG_VHOST_NET=m +CONFIG_VHOST_VSOCK=m CONFIG_VHOST_CROSS_ENDIAN_LEGACY=y CONFIG_STAGING=y CONFIG_PRISM2_USB=m diff --git a/arch/arm64/configs/bcmrpi3_defconfig b/arch/arm64/configs/bcmrpi3_defconfig index 00352da7300980bd7eb1212320081b10d0816c1a..57073001891c46a70e63e9ec81f448d67a6cdad8 100644 --- a/arch/arm64/configs/bcmrpi3_defconfig +++ b/arch/arm64/configs/bcmrpi3_defconfig @@ -392,6 +392,7 @@ CONFIG_NET_ACT_SKBEDIT=m CONFIG_NET_ACT_CSUM=m CONFIG_BATMAN_ADV=m CONFIG_OPENVSWITCH=m +CONFIG_VSOCKETS=m CONFIG_CGROUP_NET_PRIO=y CONFIG_NET_PKTGEN=m CONFIG_HAMRADIO=y @@ -491,6 +492,7 @@ CONFIG_NETCONSOLE=m CONFIG_TUN=m CONFIG_VETH=m CONFIG_NET_VRF=m +CONFIG_VSOCKMON=m CONFIG_ENC28J60=m CONFIG_QCA7000_SPI=m CONFIG_QCA7000_UART=m @@ -596,6 +598,8 @@ CONFIG_INPUT_JOYDEV=m CONFIG_INPUT_EVDEV=y # CONFIG_KEYBOARD_ATKBD is not set CONFIG_KEYBOARD_GPIO=m +CONFIG_KEYBOARD_TCA6416=m +CONFIG_KEYBOARD_TCA8418=m CONFIG_KEYBOARD_MATRIX=m CONFIG_KEYBOARD_CAP11XX=m # CONFIG_INPUT_MOUSE is not set @@ -885,6 +889,7 @@ 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 @@ -1124,6 +1129,7 @@ CONFIG_LEDS_PCA9532=m CONFIG_LEDS_GPIO=y CONFIG_LEDS_PCA955X=m CONFIG_LEDS_PCA963X=m +CONFIG_LEDS_PWM=y CONFIG_LEDS_IS31FL32XX=m CONFIG_LEDS_TRIGGER_TIMER=y CONFIG_LEDS_TRIGGER_ONESHOT=y @@ -1137,6 +1143,7 @@ CONFIG_LEDS_TRIGGER_CAMERA=m CONFIG_LEDS_TRIGGER_INPUT=y CONFIG_LEDS_TRIGGER_PANIC=y CONFIG_LEDS_TRIGGER_NETDEV=m +CONFIG_LEDS_TRIGGER_PATTERN=m CONFIG_LEDS_TRIGGER_ACTPWR=y CONFIG_ACCESSIBILITY=y CONFIG_SPEAKUP=m @@ -1186,6 +1193,7 @@ CONFIG_DMABUF_HEAPS_SYSTEM=y CONFIG_DMABUF_HEAPS_CMA=y CONFIG_UIO=m CONFIG_UIO_PDRV_GENIRQ=m +CONFIG_VHOST_VSOCK=m CONFIG_STAGING=y CONFIG_PRISM2_USB=m CONFIG_R8712U=m diff --git a/drivers/clk/bcm/clk-bcm2835.c b/drivers/clk/bcm/clk-bcm2835.c index 39fabced602a593573dd6f07cd21efceb92ed934..7fc2d55add2ea4a3662611c50b7f7ffda519c410 100644 --- a/drivers/clk/bcm/clk-bcm2835.c +++ b/drivers/clk/bcm/clk-bcm2835.c @@ -2214,21 +2214,6 @@ static const struct bcm2835_clk_desc clk_desc_array[] = { .frac_bits = 12, .tcnt_mux = 28), - /* TV encoder clock. Only operating frequency is 108Mhz. */ - [BCM2835_CLOCK_VEC] = REGISTER_PER_CLK( - SOC_ALL, - .name = "vec", - .ctl_reg = CM_VECCTL, - .div_reg = CM_VECDIV, - .int_bits = 4, - .frac_bits = 0, - /* - * Allow rate change propagation only on PLLH_AUX which is - * assigned index 7 in the parent array. - */ - .set_rate_parent = BIT(7), - .tcnt_mux = 29), - /* dsi clocks */ [BCM2835_CLOCK_DSI0E] = REGISTER_PER_CLK( SOC_ALL, diff --git a/drivers/clk/bcm/clk-raspberrypi.c b/drivers/clk/bcm/clk-raspberrypi.c index 94ce38a2d5aab7621bbd45d06a93ae925d21e2d1..c307e054f805b1ee9ddb670f6d4e366bc1db3231 100644 --- a/drivers/clk/bcm/clk-raspberrypi.c +++ b/drivers/clk/bcm/clk-raspberrypi.c @@ -33,6 +33,7 @@ enum rpi_firmware_clk_id { RPI_FIRMWARE_EMMC2_CLK_ID, RPI_FIRMWARE_M2MC_CLK_ID, RPI_FIRMWARE_PIXEL_BVB_CLK_ID, + RPI_FIRMWARE_VEC_CLK_ID, RPI_FIRMWARE_NUM_CLK_ID, }; @@ -51,6 +52,7 @@ static char *rpi_firmware_clk_names[] = { [RPI_FIRMWARE_EMMC2_CLK_ID] = "emmc2", [RPI_FIRMWARE_M2MC_CLK_ID] = "m2mc", [RPI_FIRMWARE_PIXEL_BVB_CLK_ID] = "pixel-bvb", + [RPI_FIRMWARE_VEC_CLK_ID] = "vec", }; #define RPI_FIRMWARE_STATE_ENABLE_BIT BIT(0) @@ -273,6 +275,8 @@ static int raspberrypi_discover_clocks(struct raspberrypi_clk *rpi, case RPI_FIRMWARE_V3D_CLK_ID: case RPI_FIRMWARE_HEVC_CLK_ID: case RPI_FIRMWARE_PIXEL_BVB_CLK_ID: + case RPI_FIRMWARE_VEC_CLK_ID: + case RPI_FIRMWARE_PIXEL_CLK_ID: hw = raspberrypi_clk_register(rpi, clks->parent, clks->id); if (IS_ERR(hw)) diff --git a/drivers/gpu/drm/drm_color_mgmt.c b/drivers/gpu/drm/drm_color_mgmt.c index 8c225fb7441d54dbdcaf1868dc31891f2a7f4fa1..78933e2a5f44cd0f2cda8fe9b6c9fa7050cd0a68 100644 --- a/drivers/gpu/drm/drm_color_mgmt.c +++ b/drivers/gpu/drm/drm_color_mgmt.c @@ -313,7 +313,10 @@ static int drm_crtc_legacy_gamma_set(struct drm_crtc *crtc, /* 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); + if (!crtc_state->gamma_lut || !crtc_state->gamma_lut->data || + memcmp(crtc_state->gamma_lut->data, blob_data, blob->length)) + replaced |= drm_property_replace_blob(&crtc_state->gamma_lut, blob); + crtc_state->color_mgmt_changed |= replaced; ret = drm_atomic_commit(state); diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index add317bd8d55c4c423c239aefa4d66f472235f33..46e41ad5e44fd7027ded09297036f38a7f71b4e4 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -4961,10 +4961,9 @@ static void drm_parse_hdmi_deep_color_info(struct drm_connector *connector, /* * Deep color support mandates RGB444 support for all video - * modes and forbids YCRCB422 support for all video modes per - * HDMI 1.3 spec. + * modes. */ - info->color_formats = DRM_COLOR_FORMAT_RGB444; + info->color_formats |= DRM_COLOR_FORMAT_RGB444; /* YCRCB444 is optional according to spec. */ if (hdmi[6] & DRM_EDID_HDMI_DC_Y444) { @@ -5712,13 +5711,13 @@ static const u32 hdmi_colorimetry_val[] = { #undef ACE /** - * drm_hdmi_avi_infoframe_colorspace() - fill the HDMI AVI infoframe - * colorspace information + * drm_hdmi_avi_infoframe_colorimetry() - fill the HDMI AVI infoframe + * colorimetry information * @frame: HDMI AVI infoframe * @conn_state: connector state */ void -drm_hdmi_avi_infoframe_colorspace(struct hdmi_avi_infoframe *frame, +drm_hdmi_avi_infoframe_colorimetry(struct hdmi_avi_infoframe *frame, const struct drm_connector_state *conn_state) { u32 colorimetry_val; @@ -5737,7 +5736,7 @@ drm_hdmi_avi_infoframe_colorspace(struct hdmi_avi_infoframe *frame, frame->extended_colorimetry = (colorimetry_val >> 2) & EXTENDED_COLORIMETRY_MASK; } -EXPORT_SYMBOL(drm_hdmi_avi_infoframe_colorspace); +EXPORT_SYMBOL(drm_hdmi_avi_infoframe_colorimetry); /** * drm_hdmi_avi_infoframe_quant_range() - fill the HDMI AVI infoframe diff --git a/drivers/gpu/drm/i915/display/intel_hdmi.c b/drivers/gpu/drm/i915/display/intel_hdmi.c index bf4da68260b9eb968bc4b19782965bb027647865..030b4d5ab8299b2d41e56af622f6749a52500529 100644 --- a/drivers/gpu/drm/i915/display/intel_hdmi.c +++ b/drivers/gpu/drm/i915/display/intel_hdmi.c @@ -746,7 +746,7 @@ intel_hdmi_compute_avi_infoframe(struct intel_encoder *encoder, else frame->colorspace = HDMI_COLORSPACE_RGB; - drm_hdmi_avi_infoframe_colorspace(frame, conn_state); + drm_hdmi_avi_infoframe_colorimetry(frame, conn_state); /* nonsense combination */ drm_WARN_ON(encoder->base.dev, crtc_state->limited_color_range && diff --git a/drivers/gpu/drm/panel/panel-ilitek-ili9881c.c b/drivers/gpu/drm/panel/panel-ilitek-ili9881c.c index 534dd7414d428cddead1d2ddfb23cba2c30cb504..6e03d9b0be60c4b5589779014e7f93ea2b5ec924 100644 --- a/drivers/gpu/drm/panel/panel-ilitek-ili9881c.c +++ b/drivers/gpu/drm/panel/panel-ilitek-ili9881c.c @@ -1,6 +1,8 @@ // SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2017-2018, Bootlin + * Copyright (C) 2021, Henson Li + * Copyright (C) 2021, Penk Chen */ #include @@ -42,6 +44,7 @@ struct ili9881c_desc { const struct ili9881c_instr *init; const size_t init_length; const struct drm_display_mode *mode; + const unsigned flags; }; struct ili9881c { @@ -453,6 +456,225 @@ static const struct ili9881c_instr k101_im2byl02_init[] = { ILI9881C_COMMAND_INSTR(0xD3, 0x3F), /* VN0 */ }; +static const struct ili9881c_instr nwe080_init[] = { + ILI9881C_SWITCH_PAGE_INSTR(3), + //GIP_1 + ILI9881C_COMMAND_INSTR(0x01, 0x00), + ILI9881C_COMMAND_INSTR(0x02, 0x00), + ILI9881C_COMMAND_INSTR(0x03, 0x73), + ILI9881C_COMMAND_INSTR(0x04, 0x00), + ILI9881C_COMMAND_INSTR(0x05, 0x00), + ILI9881C_COMMAND_INSTR(0x06, 0x0A), + ILI9881C_COMMAND_INSTR(0x07, 0x00), + ILI9881C_COMMAND_INSTR(0x08, 0x00), + ILI9881C_COMMAND_INSTR(0x09, 0x20), + ILI9881C_COMMAND_INSTR(0x0a, 0x20), + ILI9881C_COMMAND_INSTR(0x0b, 0x00), + ILI9881C_COMMAND_INSTR(0x0c, 0x00), + ILI9881C_COMMAND_INSTR(0x0d, 0x00), + ILI9881C_COMMAND_INSTR(0x0e, 0x00), + ILI9881C_COMMAND_INSTR(0x0f, 0x1E), + ILI9881C_COMMAND_INSTR(0x10, 0x1E), + ILI9881C_COMMAND_INSTR(0x11, 0x00), + ILI9881C_COMMAND_INSTR(0x12, 0x00), + ILI9881C_COMMAND_INSTR(0x13, 0x00), + ILI9881C_COMMAND_INSTR(0x14, 0x00), + ILI9881C_COMMAND_INSTR(0x15, 0x00), + ILI9881C_COMMAND_INSTR(0x16, 0x00), + ILI9881C_COMMAND_INSTR(0x17, 0x00), + ILI9881C_COMMAND_INSTR(0x18, 0x00), + ILI9881C_COMMAND_INSTR(0x19, 0x00), + ILI9881C_COMMAND_INSTR(0x1A, 0x00), + ILI9881C_COMMAND_INSTR(0x1B, 0x00), + ILI9881C_COMMAND_INSTR(0x1C, 0x00), + ILI9881C_COMMAND_INSTR(0x1D, 0x00), + ILI9881C_COMMAND_INSTR(0x1E, 0x40), + ILI9881C_COMMAND_INSTR(0x1F, 0x80), + ILI9881C_COMMAND_INSTR(0x20, 0x06), + ILI9881C_COMMAND_INSTR(0x21, 0x01), + ILI9881C_COMMAND_INSTR(0x22, 0x00), + ILI9881C_COMMAND_INSTR(0x23, 0x00), + ILI9881C_COMMAND_INSTR(0x24, 0x00), + ILI9881C_COMMAND_INSTR(0x25, 0x00), + ILI9881C_COMMAND_INSTR(0x26, 0x00), + ILI9881C_COMMAND_INSTR(0x27, 0x00), + ILI9881C_COMMAND_INSTR(0x28, 0x33), + ILI9881C_COMMAND_INSTR(0x29, 0x03), + ILI9881C_COMMAND_INSTR(0x2A, 0x00), + ILI9881C_COMMAND_INSTR(0x2B, 0x00), + ILI9881C_COMMAND_INSTR(0x2C, 0x00), + ILI9881C_COMMAND_INSTR(0x2D, 0x00), + ILI9881C_COMMAND_INSTR(0x2E, 0x00), + ILI9881C_COMMAND_INSTR(0x2F, 0x00), + ILI9881C_COMMAND_INSTR(0x30, 0x00), + ILI9881C_COMMAND_INSTR(0x31, 0x00), + ILI9881C_COMMAND_INSTR(0x32, 0x00), + ILI9881C_COMMAND_INSTR(0x33, 0x00), + ILI9881C_COMMAND_INSTR(0x34, 0x04), + ILI9881C_COMMAND_INSTR(0x35, 0x00), + ILI9881C_COMMAND_INSTR(0x36, 0x00), + ILI9881C_COMMAND_INSTR(0x37, 0x00), + ILI9881C_COMMAND_INSTR(0x38, 0x3C), + ILI9881C_COMMAND_INSTR(0x39, 0x00), + ILI9881C_COMMAND_INSTR(0x3A, 0x00), + ILI9881C_COMMAND_INSTR(0x3B, 0x00), + ILI9881C_COMMAND_INSTR(0x3C, 0x00), + ILI9881C_COMMAND_INSTR(0x3D, 0x00), + ILI9881C_COMMAND_INSTR(0x3E, 0x00), + ILI9881C_COMMAND_INSTR(0x3F, 0x00), + ILI9881C_COMMAND_INSTR(0x40, 0x00), + ILI9881C_COMMAND_INSTR(0x41, 0x00), + ILI9881C_COMMAND_INSTR(0x42, 0x00), + ILI9881C_COMMAND_INSTR(0x43, 0x00), + ILI9881C_COMMAND_INSTR(0x44, 0x00), + + ILI9881C_COMMAND_INSTR(0x50, 0x10), + ILI9881C_COMMAND_INSTR(0x51, 0x32), + ILI9881C_COMMAND_INSTR(0x52, 0x54), + ILI9881C_COMMAND_INSTR(0x53, 0x76), + ILI9881C_COMMAND_INSTR(0x54, 0x98), + ILI9881C_COMMAND_INSTR(0x55, 0xba), + ILI9881C_COMMAND_INSTR(0x56, 0x10), + ILI9881C_COMMAND_INSTR(0x57, 0x32), + ILI9881C_COMMAND_INSTR(0x58, 0x54), + ILI9881C_COMMAND_INSTR(0x59, 0x76), + ILI9881C_COMMAND_INSTR(0x5A, 0x98), + ILI9881C_COMMAND_INSTR(0x5B, 0xba), + ILI9881C_COMMAND_INSTR(0x5C, 0xdc), + ILI9881C_COMMAND_INSTR(0x5D, 0xfe), + + //GIP_3 + ILI9881C_COMMAND_INSTR(0x5E, 0x00), + ILI9881C_COMMAND_INSTR(0x5F, 0x01), + ILI9881C_COMMAND_INSTR(0x60, 0x00), + ILI9881C_COMMAND_INSTR(0x61, 0x15), + ILI9881C_COMMAND_INSTR(0x62, 0x14), + ILI9881C_COMMAND_INSTR(0x63, 0x0E), + ILI9881C_COMMAND_INSTR(0x64, 0x0F), + ILI9881C_COMMAND_INSTR(0x65, 0x0C), + ILI9881C_COMMAND_INSTR(0x66, 0x0D), + ILI9881C_COMMAND_INSTR(0x67, 0x06), + ILI9881C_COMMAND_INSTR(0x68, 0x02), + ILI9881C_COMMAND_INSTR(0x69, 0x02), + ILI9881C_COMMAND_INSTR(0x6A, 0x02), + ILI9881C_COMMAND_INSTR(0x6B, 0x02), + ILI9881C_COMMAND_INSTR(0x6C, 0x02), + ILI9881C_COMMAND_INSTR(0x6D, 0x02), + ILI9881C_COMMAND_INSTR(0x6E, 0x07), + ILI9881C_COMMAND_INSTR(0x6F, 0x02), + ILI9881C_COMMAND_INSTR(0x70, 0x02), + ILI9881C_COMMAND_INSTR(0x71, 0x02), + ILI9881C_COMMAND_INSTR(0x72, 0x02), + ILI9881C_COMMAND_INSTR(0x73, 0x02), + ILI9881C_COMMAND_INSTR(0x74, 0x02), + + ILI9881C_COMMAND_INSTR(0x75, 0x01), + ILI9881C_COMMAND_INSTR(0x76, 0x00), + ILI9881C_COMMAND_INSTR(0x77, 0x14), + ILI9881C_COMMAND_INSTR(0x78, 0x15), + ILI9881C_COMMAND_INSTR(0x79, 0x0E), + ILI9881C_COMMAND_INSTR(0x7A, 0x0F), + ILI9881C_COMMAND_INSTR(0x7B, 0x0C), + ILI9881C_COMMAND_INSTR(0x7C, 0x0D), + ILI9881C_COMMAND_INSTR(0x7D, 0x06), + ILI9881C_COMMAND_INSTR(0x7E, 0x02), + ILI9881C_COMMAND_INSTR(0x7F, 0x02), + ILI9881C_COMMAND_INSTR(0x80, 0x02), + ILI9881C_COMMAND_INSTR(0x81, 0x02), + ILI9881C_COMMAND_INSTR(0x82, 0x02), + ILI9881C_COMMAND_INSTR(0x83, 0x02), + ILI9881C_COMMAND_INSTR(0x84, 0x07), + ILI9881C_COMMAND_INSTR(0x85, 0x02), + ILI9881C_COMMAND_INSTR(0x86, 0x02), + ILI9881C_COMMAND_INSTR(0x87, 0x02), + ILI9881C_COMMAND_INSTR(0x88, 0x02), + ILI9881C_COMMAND_INSTR(0x89, 0x02), + ILI9881C_COMMAND_INSTR(0x8A, 0x02), + + ILI9881C_SWITCH_PAGE_INSTR(4), + ILI9881C_COMMAND_INSTR(0x6C, 0x15), + ILI9881C_COMMAND_INSTR(0x6E, 0x2A), + + //clamp 15V + ILI9881C_COMMAND_INSTR(0x6F, 0x35), + ILI9881C_COMMAND_INSTR(0x3A, 0x92), + ILI9881C_COMMAND_INSTR(0x8D, 0x1F), + ILI9881C_COMMAND_INSTR(0x87, 0xBA), + ILI9881C_COMMAND_INSTR(0x26, 0x76), + ILI9881C_COMMAND_INSTR(0xB2, 0xD1), + ILI9881C_COMMAND_INSTR(0xB5, 0x27), + ILI9881C_COMMAND_INSTR(0x31, 0x75), + ILI9881C_COMMAND_INSTR(0x30, 0x03), + ILI9881C_COMMAND_INSTR(0x3B, 0x98), + ILI9881C_COMMAND_INSTR(0x35, 0x17), + ILI9881C_COMMAND_INSTR(0x33, 0x14), + ILI9881C_COMMAND_INSTR(0x38, 0x01), + ILI9881C_COMMAND_INSTR(0x39, 0x00), + + ILI9881C_SWITCH_PAGE_INSTR(1), + // direction rotate + //ILI9881C_COMMAND_INSTR(0x22, 0x0B), + ILI9881C_COMMAND_INSTR(0x22, 0x0A), + ILI9881C_COMMAND_INSTR(0x31, 0x00), + ILI9881C_COMMAND_INSTR(0x53, 0x63), + ILI9881C_COMMAND_INSTR(0x55, 0x69), + ILI9881C_COMMAND_INSTR(0x50, 0xC7), + ILI9881C_COMMAND_INSTR(0x51, 0xC2), + ILI9881C_COMMAND_INSTR(0x60, 0x26), + + ILI9881C_COMMAND_INSTR(0xA0, 0x08), + ILI9881C_COMMAND_INSTR(0xA1, 0x0F), + ILI9881C_COMMAND_INSTR(0xA2, 0x25), + ILI9881C_COMMAND_INSTR(0xA3, 0x01), + ILI9881C_COMMAND_INSTR(0xA4, 0x23), + ILI9881C_COMMAND_INSTR(0xA5, 0x18), + ILI9881C_COMMAND_INSTR(0xA6, 0x11), + ILI9881C_COMMAND_INSTR(0xA7, 0x1A), + ILI9881C_COMMAND_INSTR(0xA8, 0x81), + ILI9881C_COMMAND_INSTR(0xA9, 0x19), + ILI9881C_COMMAND_INSTR(0xAA, 0x26), + ILI9881C_COMMAND_INSTR(0xAB, 0x7C), + ILI9881C_COMMAND_INSTR(0xAC, 0x24), + ILI9881C_COMMAND_INSTR(0xAD, 0x1E), + ILI9881C_COMMAND_INSTR(0xAE, 0x5C), + ILI9881C_COMMAND_INSTR(0xAF, 0x2A), + ILI9881C_COMMAND_INSTR(0xB0, 0x2B), + ILI9881C_COMMAND_INSTR(0xB1, 0x50), + ILI9881C_COMMAND_INSTR(0xB2, 0x5C), + ILI9881C_COMMAND_INSTR(0xB3, 0x39), + + ILI9881C_COMMAND_INSTR(0xC0, 0x08), + ILI9881C_COMMAND_INSTR(0xC1, 0x1F), + ILI9881C_COMMAND_INSTR(0xC2, 0x24), + ILI9881C_COMMAND_INSTR(0xC3, 0x1D), + ILI9881C_COMMAND_INSTR(0xC4, 0x04), + ILI9881C_COMMAND_INSTR(0xC5, 0x32), + ILI9881C_COMMAND_INSTR(0xC6, 0x24), + ILI9881C_COMMAND_INSTR(0xC7, 0x1F), + ILI9881C_COMMAND_INSTR(0xC8, 0x90), + ILI9881C_COMMAND_INSTR(0xC9, 0x20), + ILI9881C_COMMAND_INSTR(0xCA, 0x2C), + ILI9881C_COMMAND_INSTR(0xCB, 0x82), + ILI9881C_COMMAND_INSTR(0xCC, 0x19), + ILI9881C_COMMAND_INSTR(0xCD, 0x22), + ILI9881C_COMMAND_INSTR(0xCE, 0x4E), + ILI9881C_COMMAND_INSTR(0xCF, 0x28), + ILI9881C_COMMAND_INSTR(0xD0, 0x2D), + ILI9881C_COMMAND_INSTR(0xD1, 0x51), + ILI9881C_COMMAND_INSTR(0xD2, 0x5D), + ILI9881C_COMMAND_INSTR(0xD3, 0x39), + + ILI9881C_SWITCH_PAGE_INSTR(0), + //PWM + ILI9881C_COMMAND_INSTR(0x51, 0x0F), + ILI9881C_COMMAND_INSTR(0x52, 0xFF), + ILI9881C_COMMAND_INSTR(0x53, 0x2C), + + ILI9881C_COMMAND_INSTR(0x11, 0x00), + ILI9881C_COMMAND_INSTR(0x29, 0x00), + ILI9881C_COMMAND_INSTR(0x35, 0x00), +}; + static inline struct ili9881c *panel_to_ili9881c(struct drm_panel *panel) { return container_of(panel, struct ili9881c, panel); @@ -603,6 +825,23 @@ static const struct drm_display_mode k101_im2byl02_default_mode = { .height_mm = 217, }; +static const struct drm_display_mode nwe080_default_mode = { + .clock = 71750, + + .hdisplay = 800, + .hsync_start = 800 + 52, + .hsync_end = 800 + 52 + 8, + .htotal = 800 + 52 + 8 + 48, + + .vdisplay = 1280, + .vsync_start = 1280 + 16, + .vsync_end = 1280 + 16 + 6, + .vtotal = 1280 + 16 + 6 + 15, + + .width_mm = 107, + .height_mm = 170, +}; + static int ili9881c_get_modes(struct drm_panel *panel, struct drm_connector *connector) { @@ -670,7 +909,7 @@ static int ili9881c_dsi_probe(struct mipi_dsi_device *dsi) drm_panel_add(&ctx->panel); - dsi->mode_flags = MIPI_DSI_MODE_VIDEO_SYNC_PULSE; + dsi->mode_flags = ctx->desc->flags; dsi->format = MIPI_DSI_FMT_RGB888; dsi->lanes = 4; @@ -691,18 +930,28 @@ static const struct ili9881c_desc lhr050h41_desc = { .init = lhr050h41_init, .init_length = ARRAY_SIZE(lhr050h41_init), .mode = &lhr050h41_default_mode, + .flags = MIPI_DSI_MODE_VIDEO_SYNC_PULSE, }; static const struct ili9881c_desc k101_im2byl02_desc = { .init = k101_im2byl02_init, .init_length = ARRAY_SIZE(k101_im2byl02_init), .mode = &k101_im2byl02_default_mode, + .flags = MIPI_DSI_MODE_VIDEO_SYNC_PULSE, +}; + +static const struct ili9881c_desc nwe080_desc = { + .init = nwe080_init, + .init_length = ARRAY_SIZE(nwe080_init), + .mode = &nwe080_default_mode, + .flags = MIPI_DSI_MODE_VIDEO_SYNC_PULSE | MIPI_DSI_MODE_VIDEO, }; static const struct of_device_id ili9881c_of_match[] = { { .compatible = "bananapi,lhr050h41", .data = &lhr050h41_desc }, { .compatible = "feixin,k101-im2byl02", .data = &k101_im2byl02_desc }, - { } + { .compatible = "nwe,nwe080", .data = &nwe080_desc }, + {} }; MODULE_DEVICE_TABLE(of, ili9881c_of_match); diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c index 8becb0e318140b8effe66904a2b89d4ac87e2347..e99168cc2ae4e5bc9edeb72c09abb0651cadf89f 100644 --- a/drivers/gpu/drm/panel/panel-simple.c +++ b/drivers/gpu/drm/panel/panel-simple.c @@ -438,6 +438,7 @@ static int panel_dpi_probe(struct device *dev, of_property_read_u32(np, "width-mm", &desc->size.width); of_property_read_u32(np, "height-mm", &desc->size.height); + of_property_read_u32(np, "bus-format", &desc->bus_format); /* Extract bus_flags from display_timing */ bus_flags = 0; @@ -447,6 +448,8 @@ static int panel_dpi_probe(struct device *dev, /* We do not know the connector for the DT node, so guess it */ desc->connector_type = DRM_MODE_CONNECTOR_DPI; + /* Likewise for the bit depth. */ + desc->bpc = 8; panel->desc = desc; @@ -557,6 +560,7 @@ static int panel_simple_probe(struct device *dev, const struct panel_desc *desc) err = panel_dpi_probe(dev, panel); if (err) goto free_ddc; + desc = panel->desc; } else { if (!of_get_display_timing(dev->of_node, "panel-timing", &dt)) panel_simple_parse_panel_timing_node(dev, panel, &dt); diff --git a/drivers/gpu/drm/vc4/vc4_crtc.c b/drivers/gpu/drm/vc4/vc4_crtc.c index 59e10c37b740c20787794845cf28644e329edf73..f73d73425f081d0bd89237472e7b25616de27f7e 100644 --- a/drivers/gpu/drm/vc4/vc4_crtc.c +++ b/drivers/gpu/drm/vc4/vc4_crtc.c @@ -292,6 +292,23 @@ struct drm_encoder *vc4_get_crtc_encoder(struct drm_crtc *crtc, return NULL; } +#define drm_for_each_connector_mask(connector, dev, connector_mask) \ + list_for_each_entry((connector), &(dev)->mode_config.connector_list, head) \ + for_each_if ((connector_mask) & drm_connector_mask(connector)) + +struct drm_connector *vc4_get_crtc_connector(struct drm_crtc *crtc, + struct drm_crtc_state *state) +{ + struct drm_connector *connector; + + WARN_ON(hweight32(state->connector_mask) > 1); + + drm_for_each_connector_mask(connector, crtc->dev, state->connector_mask) + return connector; + + return NULL; +} + static void vc4_crtc_pixelvalve_reset(struct drm_crtc *crtc) { struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc); @@ -391,6 +408,7 @@ static void vc4_crtc_config_pv(struct drm_crtc *crtc, struct drm_encoder *encode CRTC_WRITE(PV_V_CONTROL, PV_VCONTROL_CONTINUOUS | (is_dsi ? PV_VCONTROL_DSI : 0)); + CRTC_WRITE(PV_VSYNCD_EVEN, 0); } CRTC_WRITE(PV_VERTA, @@ -717,14 +735,14 @@ 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; struct vc4_dev *vc4 = to_vc4_dev(dev); - struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(crtc->state); - u32 chan = vc4_state->assigned_channel; + u32 chan = vc4_crtc->current_hvs_channel; unsigned long flags; spin_lock_irqsave(&dev->event_lock, flags); + spin_lock(&vc4_crtc->irq_lock); if (vc4_crtc->event && - (vc4_state->mm.start == HVS_READ(SCALER_DISPLACTX(chan)) || - vc4_state->feed_txp)) { + (vc4_crtc->current_dlist == HVS_READ(SCALER_DISPLACTX(chan)) || + vc4_crtc->feeds_txp)) { drm_crtc_send_vblank_event(crtc, vc4_crtc->event); vc4_crtc->event = NULL; drm_crtc_vblank_put(crtc); @@ -737,6 +755,7 @@ static void vc4_crtc_handle_page_flip(struct vc4_crtc *vc4_crtc) */ vc4_hvs_unmask_underrun(dev, chan); } + spin_unlock(&vc4_crtc->irq_lock); spin_unlock_irqrestore(&dev->event_lock, flags); } @@ -948,7 +967,6 @@ struct drm_crtc_state *vc4_crtc_duplicate_state(struct drm_crtc *crtc) return NULL; old_vc4_state = to_vc4_crtc_state(crtc->state); - vc4_state->feed_txp = old_vc4_state->feed_txp; vc4_state->margins = old_vc4_state->margins; vc4_state->assigned_channel = old_vc4_state->assigned_channel; @@ -1009,6 +1027,7 @@ static const struct drm_crtc_funcs vc4_crtc_funcs = { static const struct drm_crtc_helper_funcs vc4_crtc_helper_funcs = { .mode_valid = vc4_crtc_mode_valid, .atomic_check = vc4_crtc_atomic_check, + .atomic_begin = vc4_hvs_atomic_begin, .atomic_flush = vc4_hvs_atomic_flush, .atomic_enable = vc4_crtc_atomic_enable, .atomic_disable = vc4_crtc_atomic_disable, @@ -1183,25 +1202,43 @@ int vc4_crtc_init(struct drm_device *drm, struct vc4_crtc *vc4_crtc, return PTR_ERR(primary_plane); } + spin_lock_init(&vc4_crtc->irq_lock); drm_crtc_init_with_planes(drm, crtc, primary_plane, NULL, crtc_funcs, NULL); drm_crtc_helper_add(crtc, crtc_helper_funcs); if (!vc4->hvs->hvs5) { drm_mode_crtc_set_gamma_size(crtc, ARRAY_SIZE(vc4_crtc->lut_r)); - drm_crtc_enable_color_mgmt(crtc, 0, false, crtc->gamma_size); + } + + if (!vc4->hvs->hvs5) { /* We support CTM, but only for one CRTC at a time. It's therefore * implemented as private driver state in vc4_kms, not here. */ - drm_crtc_enable_color_mgmt(crtc, 0, true, crtc->gamma_size); - } + drm_crtc_enable_color_mgmt(crtc, 0, true, 0); - for (i = 0; i < crtc->gamma_size; i++) { - vc4_crtc->lut_r[i] = i; - vc4_crtc->lut_g[i] = i; - vc4_crtc->lut_b[i] = i; + /* Initialize the VC4 gamma LUTs */ + for (i = 0; i < crtc->gamma_size; i++) { + vc4_crtc->lut_r[i] = i; + vc4_crtc->lut_g[i] = i; + vc4_crtc->lut_b[i] = i; + } + } else { + /* Initialize the VC5 gamma PWL entries. Assume 12-bit pipeline, + * evenly spread over full range. + */ + for (i = 0; i < SCALER5_DSPGAMMA_NUM_POINTS; i++) { + vc4_crtc->pwl_r[i] = + VC5_HVS_SET_GAMMA_ENTRY(i << 8, i << 12, 1 << 8); + vc4_crtc->pwl_g[i] = + VC5_HVS_SET_GAMMA_ENTRY(i << 8, i << 12, 1 << 8); + vc4_crtc->pwl_b[i] = + VC5_HVS_SET_GAMMA_ENTRY(i << 8, i << 12, 1 << 8); + vc4_crtc->pwl_a[i] = + VC5_HVS_SET_GAMMA_ENTRY(i << 8, i << 12, 1 << 8); + } } return 0; diff --git a/drivers/gpu/drm/vc4/vc4_dpi.c b/drivers/gpu/drm/vc4/vc4_dpi.c index dde2c601474843e5e2cb3e57a2963169a441bd83..08147d0eab8370f3166bd42ba3ca1b45d05ecbee 100644 --- a/drivers/gpu/drm/vc4/vc4_dpi.c +++ b/drivers/gpu/drm/vc4/vc4_dpi.c @@ -131,7 +131,7 @@ static void vc4_dpi_encoder_enable(struct drm_encoder *encoder) struct vc4_dpi *dpi = vc4_encoder->dpi; struct drm_connector_list_iter conn_iter; struct drm_connector *connector = NULL, *connector_scan; - u32 dpi_c = DPI_ENABLE | DPI_OUTPUT_ENABLE_MODE; + u32 dpi_c = DPI_ENABLE; int ret; /* Look up the connector attached to DPI so we can get the @@ -148,59 +148,84 @@ static void vc4_dpi_encoder_enable(struct drm_encoder *encoder) } drm_connector_list_iter_end(&conn_iter); - if (connector && connector->display_info.num_bus_formats) { - u32 bus_format = connector->display_info.bus_formats[0]; - - switch (bus_format) { - case MEDIA_BUS_FMT_RGB888_1X24: - dpi_c |= VC4_SET_FIELD(DPI_FORMAT_24BIT_888_RGB, - DPI_FORMAT); - break; - case MEDIA_BUS_FMT_BGR888_1X24: - dpi_c |= VC4_SET_FIELD(DPI_FORMAT_24BIT_888_RGB, - DPI_FORMAT); - dpi_c |= VC4_SET_FIELD(DPI_ORDER_BGR, DPI_ORDER); - break; - case MEDIA_BUS_FMT_RGB666_1X24_CPADHI: - 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: + if (connector) { + if (connector->display_info.num_bus_formats) { + u32 bus_format = connector->display_info.bus_formats[0]; + + switch (bus_format) { + case MEDIA_BUS_FMT_RGB888_1X24: + dpi_c |= VC4_SET_FIELD(DPI_FORMAT_24BIT_888_RGB, + DPI_FORMAT); + break; + case MEDIA_BUS_FMT_BGR888_1X24: + dpi_c |= VC4_SET_FIELD(DPI_FORMAT_24BIT_888_RGB, + DPI_FORMAT); + dpi_c |= VC4_SET_FIELD(DPI_ORDER_BGR, + DPI_ORDER); + break; + case MEDIA_BUS_FMT_RGB666_1X24_CPADHI: + 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; + default: + DRM_ERROR("Unknown media bus format %d\n", + bus_format); + fallthrough; + 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); + break; + } + } else { + /* Default to 18bit if no connector found. */ 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); - break; - default: - DRM_ERROR("Unknown media bus format %d\n", bus_format); - break; + } + + if (connector->display_info.bus_flags & + DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE) + dpi_c |= DPI_PIXEL_CLK_INVERT; + + if (connector->display_info.bus_flags & DRM_BUS_FLAG_DE_LOW) + dpi_c |= DPI_OUTPUT_ENABLE_INVERT; } else { /* 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) - dpi_c |= DPI_HSYNC_INVERT; - else if (!(mode->flags & DRM_MODE_FLAG_PHSYNC)) - dpi_c |= DPI_HSYNC_DISABLE; + if (mode->flags & DRM_MODE_FLAG_CSYNC) { + if (mode->flags & DRM_MODE_FLAG_NCSYNC) + dpi_c |= DPI_OUTPUT_ENABLE_INVERT; + } else { + dpi_c |= DPI_OUTPUT_ENABLE_MODE; - if (mode->flags & DRM_MODE_FLAG_NVSYNC) - dpi_c |= DPI_VSYNC_INVERT; - else if (!(mode->flags & DRM_MODE_FLAG_PVSYNC)) - dpi_c |= DPI_VSYNC_DISABLE; + if (mode->flags & DRM_MODE_FLAG_NHSYNC) + dpi_c |= DPI_HSYNC_INVERT; + else if (!(mode->flags & DRM_MODE_FLAG_PHSYNC)) + dpi_c |= DPI_HSYNC_DISABLE; + + if (mode->flags & DRM_MODE_FLAG_NVSYNC) + dpi_c |= DPI_VSYNC_INVERT; + else if (!(mode->flags & DRM_MODE_FLAG_PVSYNC)) + dpi_c |= DPI_VSYNC_DISABLE; + } DPI_WRITE(DPI_C, dpi_c); diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h index 7c749e08cb0c445557ee042157a1d4f31dce1f06..dd34de6e677b5ddfe80e58a90fedac1d3c2a14ec 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.h +++ b/drivers/gpu/drm/vc4/vc4_drv.h @@ -19,6 +19,7 @@ #include #include "uapi/drm/vc4_drm.h" +#include "vc4_regs.h" struct drm_device; struct drm_gem_object; @@ -482,6 +483,17 @@ struct vc4_pv_data { }; +struct vc5_gamma_entry { + u32 x_c_terms; + u32 grad_term; +}; + +#define VC5_HVS_SET_GAMMA_ENTRY(x, c, g) (struct vc5_gamma_entry){ \ + .x_c_terms = VC4_SET_FIELD((x), SCALER5_DSPGAMMA_OFF_X) | \ + VC4_SET_FIELD((c), SCALER5_DSPGAMMA_OFF_C), \ + .grad_term = (g) \ +} + struct vc4_crtc { struct drm_crtc base; struct platform_device *pdev; @@ -491,13 +503,50 @@ struct vc4_crtc { /* Timestamp at start of vblank irq - unaffected by lock delays. */ ktime_t t_vblank; - u8 lut_r[256]; - u8 lut_g[256]; - u8 lut_b[256]; + union { + struct { /* VC4 gamma LUT */ + u8 lut_r[256]; + u8 lut_g[256]; + u8 lut_b[256]; + }; + struct { /* VC5 gamma PWL entries */ + struct vc5_gamma_entry pwl_r[SCALER5_DSPGAMMA_NUM_POINTS]; + struct vc5_gamma_entry pwl_g[SCALER5_DSPGAMMA_NUM_POINTS]; + struct vc5_gamma_entry pwl_b[SCALER5_DSPGAMMA_NUM_POINTS]; + struct vc5_gamma_entry pwl_a[SCALER5_DSPGAMMA_NUM_POINTS]; + }; + }; struct drm_pending_vblank_event *event; struct debugfs_regset32 regset; + + /** + * @feeds_txp: True if the CRTC feeds our writeback controller. + */ + bool feeds_txp; + + /** + * @irq_lock: Spinlock protecting the resources shared between + * the atomic code and our vblank handler. + */ + spinlock_t irq_lock; + + /** + * @current_dlist: Start offset of the display list currently + * set in the HVS for that CRTC. Protected by @irq_lock, and + * copied in vc4_hvs_update_dlist() for the CRTC interrupt + * handler to have access to that value. + */ + unsigned int current_dlist; + + /** + * @current_hvs_channel: HVS channel currently assigned to the + * CRTC. Protected by @irq_lock, and copied in + * vc4_hvs_atomic_begin() for the CRTC interrupt handler to have + * access to that value. + */ + unsigned int current_hvs_channel; }; static inline struct vc4_crtc * @@ -520,6 +569,9 @@ vc4_crtc_to_vc4_pv_data(const struct vc4_crtc *crtc) return container_of(data, struct vc4_pv_data, base); } +struct drm_connector *vc4_get_crtc_connector(struct drm_crtc *crtc, + struct drm_crtc_state *state); + struct drm_encoder *vc4_get_crtc_encoder(struct drm_crtc *crtc, struct drm_crtc_state *state); @@ -527,7 +579,6 @@ struct vc4_crtc_state { struct drm_crtc_state base; /* Dlist area for this CRTC configuration. */ struct drm_mm_node mm; - bool feed_txp; bool txp_armed; unsigned int assigned_channel; @@ -928,6 +979,7 @@ 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_atomic_state *state); +void vc4_hvs_atomic_begin(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); diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c index f1adaa9dc5f34bca8ae670968c56b5afc7bc2fd8..d71cd43785053a4f119e19f47d2604d034b523e9 100644 --- a/drivers/gpu/drm/vc4/vc4_hdmi.c +++ b/drivers/gpu/drm/vc4/vc4_hdmi.c @@ -103,9 +103,41 @@ #define CEC_CLOCK_FREQ 40000 #define HDMI_14_MAX_TMDS_CLK (340 * 1000 * 1000) -static bool vc4_hdmi_mode_needs_scrambling(const struct drm_display_mode *mode) +static const char * const output_format_str[] = { + [VC4_HDMI_OUTPUT_RGB] = "RGB", + [VC4_HDMI_OUTPUT_YUV420] = "YUV 4:2:0", + [VC4_HDMI_OUTPUT_YUV422] = "YUV 4:2:2", + [VC4_HDMI_OUTPUT_YUV444] = "YUV 4:4:4", +}; + +static const char *vc4_hdmi_output_fmt_str(enum vc4_hdmi_output_format fmt) +{ + if (fmt >= ARRAY_SIZE(output_format_str)) + return "invalid"; + + return output_format_str[fmt]; +} + +static unsigned long long +vc4_hdmi_encoder_compute_mode_clock(const struct drm_display_mode *mode, + unsigned int bpc, enum vc4_hdmi_output_format fmt); + +static bool vc4_hdmi_mode_needs_scrambling(const struct drm_display_mode *mode, + unsigned int bpc, + enum vc4_hdmi_output_format fmt) { - return (mode->clock * 1000) > HDMI_14_MAX_TMDS_CLK; + unsigned long long clock = vc4_hdmi_encoder_compute_mode_clock(mode, bpc, fmt); + + return clock > HDMI_14_MAX_TMDS_CLK; +} + +static bool vc4_hdmi_is_full_range_rgb(struct vc4_hdmi *vc4_hdmi, + const struct drm_display_mode *mode) +{ + struct vc4_hdmi_encoder *vc4_encoder = &vc4_hdmi->encoder; + + return !vc4_encoder->hdmi_monitor || + drm_default_rgb_quant_range(mode) == HDMI_QUANTIZATION_RANGE_FULL; } static int vc4_hdmi_debugfs_regs(struct seq_file *m, void *unused) @@ -128,6 +160,10 @@ static int vc4_hdmi_debugfs_regs(struct seq_file *m, void *unused) static void vc4_hdmi_reset(struct vc4_hdmi *vc4_hdmi) { + unsigned long flags; + + spin_lock_irqsave(&vc4_hdmi->hw_lock, flags); + HDMI_WRITE(HDMI_M_CTL, VC4_HD_M_SW_RST); udelay(1); HDMI_WRITE(HDMI_M_CTL, 0); @@ -139,24 +175,36 @@ static void vc4_hdmi_reset(struct vc4_hdmi *vc4_hdmi) VC4_HDMI_SW_RESET_FORMAT_DETECT); HDMI_WRITE(HDMI_SW_RESET_CONTROL, 0); + + spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags); } static void vc5_hdmi_reset(struct vc4_hdmi *vc4_hdmi) { + unsigned long flags; + reset_control_reset(vc4_hdmi->reset); + spin_lock_irqsave(&vc4_hdmi->hw_lock, flags); + HDMI_WRITE(HDMI_DVP_CTL, 0); HDMI_WRITE(HDMI_CLOCK_STOP, HDMI_READ(HDMI_CLOCK_STOP) | VC4_DVP_HT_CLOCK_STOP_PIXEL); + + spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags); } #ifdef CONFIG_DRM_VC4_HDMI_CEC static void vc4_hdmi_cec_update_clk_div(struct vc4_hdmi *vc4_hdmi) { + unsigned long cec_rate = clk_get_rate(vc4_hdmi->cec_clock); + unsigned long flags; u16 clk_cnt; u32 value; + spin_lock_irqsave(&vc4_hdmi->hw_lock, flags); + value = HDMI_READ(HDMI_CEC_CNTRL_1); value &= ~VC4_HDMI_CEC_DIV_CLK_CNT_MASK; @@ -164,9 +212,11 @@ static void vc4_hdmi_cec_update_clk_div(struct vc4_hdmi *vc4_hdmi) * 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->cec_clock) / CEC_CLOCK_FREQ; + clk_cnt = cec_rate / CEC_CLOCK_FREQ; value |= clk_cnt << VC4_HDMI_CEC_DIV_CLK_CNT_SHIFT; HDMI_WRITE(HDMI_CEC_CNTRL_1, value); + + spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags); } #else static void vc4_hdmi_cec_update_clk_div(struct vc4_hdmi *vc4_hdmi) {} @@ -181,14 +231,18 @@ vc4_hdmi_connector_detect(struct drm_connector *connector, bool force) enum drm_connector_status ret = connector_status_disconnected; bool connected = false; + mutex_lock(&vc4_hdmi->mutex); + WARN_ON(pm_runtime_resume_and_get(&vc4_hdmi->pdev->dev)); if (vc4_hdmi->hpd_gpio) { if (gpio_get_value_cansleep(vc4_hdmi->hpd_gpio) ^ vc4_hdmi->hpd_active_low) connected = true; - } else if (HDMI_READ(HDMI_HOTPLUG) & VC4_HDMI_HOTPLUG_CONNECTED) { - connected = true; + } else { + if (vc4_hdmi->variant->hp_detect && + vc4_hdmi->variant->hp_detect(vc4_hdmi)) + connected = true; } if (connected) { @@ -199,6 +253,8 @@ vc4_hdmi_connector_detect(struct drm_connector *connector, bool force) cec_s_phys_addr_from_edid(vc4_hdmi->cec_adap, edid); vc4_hdmi->encoder.hdmi_monitor = drm_detect_hdmi_monitor(edid); kfree(edid); + } else { + vc4_hdmi->encoder.hdmi_monitor = false; } } @@ -208,10 +264,13 @@ vc4_hdmi_connector_detect(struct drm_connector *connector, bool force) goto out; } + vc4_hdmi->encoder.hdmi_monitor = false; + cec_phys_addr_invalidate(vc4_hdmi->cec_adap); out: pm_runtime_put(&vc4_hdmi->pdev->dev); + mutex_unlock(&vc4_hdmi->mutex); return ret; } @@ -228,10 +287,14 @@ static int vc4_hdmi_connector_get_modes(struct drm_connector *connector) int ret = 0; struct edid *edid; + mutex_lock(&vc4_hdmi->mutex); + edid = drm_get_edid(connector, vc4_hdmi->ddc); cec_s_phys_addr_from_edid(vc4_hdmi->cec_adap, edid); - if (!edid) - return -ENODEV; + if (!edid) { + ret = -ENODEV; + goto out; + } vc4_encoder->hdmi_monitor = drm_detect_hdmi_monitor(edid); @@ -244,13 +307,16 @@ static int vc4_hdmi_connector_get_modes(struct drm_connector *connector) struct drm_display_mode *mode; list_for_each_entry(mode, &connector->probed_modes, head) { - if (vc4_hdmi_mode_needs_scrambling(mode)) { + if (vc4_hdmi_mode_needs_scrambling(mode, 8, VC4_HDMI_OUTPUT_RGB)) { 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."); } } } +out: + mutex_unlock(&vc4_hdmi->mutex); + return ret; } @@ -298,6 +364,7 @@ static void vc4_hdmi_connector_reset(struct drm_connector *connector) new_state->base.max_bpc = 8; new_state->base.max_requested_bpc = 8; + new_state->output_format = VC4_HDMI_OUTPUT_RGB; drm_atomic_helper_connector_tv_reset(connector); } @@ -313,6 +380,8 @@ vc4_hdmi_connector_duplicate_state(struct drm_connector *connector) return NULL; new_state->pixel_rate = vc4_state->pixel_rate; + new_state->output_bpc = vc4_state->output_bpc; + new_state->output_format = vc4_state->output_format; __drm_atomic_helper_connector_duplicate_state(connector, &new_state->base); return &new_state->base; @@ -386,9 +455,12 @@ static int vc4_hdmi_stop_packet(struct drm_encoder *encoder, { struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder); u32 packet_id = type - 0x80; + unsigned long flags; + spin_lock_irqsave(&vc4_hdmi->hw_lock, flags); HDMI_WRITE(HDMI_RAM_PACKET_CONFIG, HDMI_READ(HDMI_RAM_PACKET_CONFIG) & ~BIT(packet_id)); + spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags); if (!poll) return 0; @@ -410,6 +482,7 @@ static void vc4_hdmi_write_infoframe(struct drm_encoder *encoder, void __iomem *base = __vc4_hdmi_get_field_base(vc4_hdmi, ram_packet_start->reg); uint8_t buffer[VC4_HDMI_PACKET_STRIDE] = {}; + unsigned long flags; ssize_t len, i; int ret; @@ -427,6 +500,8 @@ static void vc4_hdmi_write_infoframe(struct drm_encoder *encoder, return; } + spin_lock_irqsave(&vc4_hdmi->hw_lock, flags); + for (i = 0; i < len; i += 7) { writel(buffer[i + 0] << 0 | buffer[i + 1] << 8 | @@ -451,23 +526,53 @@ static void vc4_hdmi_write_infoframe(struct drm_encoder *encoder, HDMI_WRITE(HDMI_RAM_PACKET_CONFIG, HDMI_READ(HDMI_RAM_PACKET_CONFIG) | BIT(packet_id)); + + spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags); + ret = wait_for((HDMI_READ(HDMI_RAM_PACKET_STATUS) & BIT(packet_id)), 100); if (ret) DRM_ERROR("Failed to wait for infoframe to start: %d\n", ret); } +static void vc4_hdmi_avi_infoframe_colorspace(struct hdmi_avi_infoframe *frame, + enum vc4_hdmi_output_format fmt) +{ + switch (fmt) { + case VC4_HDMI_OUTPUT_RGB: + frame->colorspace = HDMI_COLORSPACE_RGB; + break; + + case VC4_HDMI_OUTPUT_YUV420: + frame->colorspace = HDMI_COLORSPACE_YUV420; + break; + + case VC4_HDMI_OUTPUT_YUV422: + frame->colorspace = HDMI_COLORSPACE_YUV422; + break; + + case VC4_HDMI_OUTPUT_YUV444: + frame->colorspace = HDMI_COLORSPACE_YUV444; + break; + + default: + break; + } +} + static void vc4_hdmi_set_avi_infoframe(struct drm_encoder *encoder) { struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder); - struct vc4_hdmi_encoder *vc4_encoder = to_vc4_hdmi_encoder(encoder); struct drm_connector *connector = &vc4_hdmi->connector; struct drm_connector_state *cstate = connector->state; - struct drm_crtc *crtc = encoder->crtc; - const struct drm_display_mode *mode = &crtc->state->adjusted_mode; + struct vc4_hdmi_connector_state *vc4_state = + conn_state_to_vc4_hdmi_conn_state(cstate); + const struct drm_display_mode *mode = &vc4_hdmi->saved_adjusted_mode; union hdmi_infoframe frame; int ret; + lockdep_assert_held(&vc4_hdmi->mutex); + ret = drm_hdmi_avi_infoframe_from_display_mode(&frame.avi, connector, mode); if (ret < 0) { @@ -477,10 +582,11 @@ static void vc4_hdmi_set_avi_infoframe(struct drm_encoder *encoder) drm_hdmi_avi_infoframe_quant_range(&frame.avi, connector, mode, - vc4_encoder->limited_rgb_range ? - HDMI_QUANTIZATION_RANGE_LIMITED : - HDMI_QUANTIZATION_RANGE_FULL); - drm_hdmi_avi_infoframe_colorspace(&frame.avi, cstate); + vc4_hdmi_is_full_range_rgb(vc4_hdmi, mode) ? + HDMI_QUANTIZATION_RANGE_FULL : + HDMI_QUANTIZATION_RANGE_LIMITED); + drm_hdmi_avi_infoframe_colorimetry(&frame.avi, cstate); + vc4_hdmi_avi_infoframe_colorspace(&frame.avi, vc4_state->output_format); drm_hdmi_avi_infoframe_bars(&frame.avi, cstate); vc4_hdmi_write_infoframe(encoder, &frame); @@ -519,6 +625,8 @@ static void vc4_hdmi_set_hdr_infoframe(struct drm_encoder *encoder) struct drm_connector_state *conn_state = connector->state; union hdmi_infoframe frame; + lockdep_assert_held(&vc4_hdmi->mutex); + if (!vc4_hdmi->variant->supports_hdr) return; @@ -535,6 +643,8 @@ static void vc4_hdmi_set_infoframes(struct drm_encoder *encoder) { struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder); + lockdep_assert_held(&vc4_hdmi->mutex); + vc4_hdmi_set_avi_infoframe(encoder); vc4_hdmi_set_spd_infoframe(encoder); /* @@ -554,6 +664,8 @@ static bool vc4_hdmi_supports_scrambling(struct drm_encoder *encoder, struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder); struct drm_display_info *display = &vc4_hdmi->connector.display_info; + lockdep_assert_held(&vc4_hdmi->mutex); + if (!vc4_encoder->hdmi_monitor) return false; @@ -568,24 +680,32 @@ static bool vc4_hdmi_supports_scrambling(struct drm_encoder *encoder, 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); + struct drm_display_mode *mode = &vc4_hdmi->saved_adjusted_mode; + unsigned long flags; + + lockdep_assert_held(&vc4_hdmi->mutex); 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)) + if (!vc4_hdmi_mode_needs_scrambling(mode, + vc4_hdmi->output_bpc, + vc4_hdmi->output_format)) return; drm_scdc_set_high_tmds_clock_ratio(vc4_hdmi->ddc, true); drm_scdc_set_scrambling(vc4_hdmi->ddc, true); + spin_lock_irqsave(&vc4_hdmi->hw_lock, flags); HDMI_WRITE(HDMI_SCRAMBLER_CTL, HDMI_READ(HDMI_SCRAMBLER_CTL) | VC5_HDMI_SCRAMBLER_CTL_ENABLE); + spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags); + + vc4_hdmi->scdc_enabled = true; queue_delayed_work(system_wq, &vc4_hdmi->scrambling_work, msecs_to_jiffies(SCRAMBLING_POLLING_DELAY_MS)); @@ -594,24 +714,22 @@ static void vc4_hdmi_enable_scrambling(struct drm_encoder *encoder) 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; + unsigned long flags; - /* - * 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; + lockdep_assert_held(&vc4_hdmi->mutex); - if (crtc && !vc4_hdmi_mode_needs_scrambling(&crtc->mode)) + if (!vc4_hdmi->scdc_enabled) return; + vc4_hdmi->scdc_enabled = false; + if (delayed_work_pending(&vc4_hdmi->scrambling_work)) cancel_delayed_work_sync(&vc4_hdmi->scrambling_work); + spin_lock_irqsave(&vc4_hdmi->hw_lock, flags); HDMI_WRITE(HDMI_SCRAMBLER_CTL, HDMI_READ(HDMI_SCRAMBLER_CTL) & ~VC5_HDMI_SCRAMBLER_CTL_ENABLE); + spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags); drm_scdc_set_scrambling(vc4_hdmi->ddc, false); drm_scdc_set_high_tmds_clock_ratio(vc4_hdmi->ddc, false); @@ -637,26 +755,44 @@ 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); + unsigned long flags; + + mutex_lock(&vc4_hdmi->mutex); + + vc4_hdmi->output_enabled = false; + spin_lock_irqsave(&vc4_hdmi->hw_lock, flags); HDMI_WRITE(HDMI_RAM_PACKET_CONFIG, 0); HDMI_WRITE(HDMI_VID_CTL, HDMI_READ(HDMI_VID_CTL) | VC4_HD_VID_CTL_CLRRGB); + spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags); + mdelay(1); + spin_lock_irqsave(&vc4_hdmi->hw_lock, flags); HDMI_WRITE(HDMI_VID_CTL, HDMI_READ(HDMI_VID_CTL) & ~VC4_HD_VID_CTL_ENABLE); + spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags); + vc4_hdmi_disable_scrambling(encoder); + + mutex_unlock(&vc4_hdmi->mutex); } 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); + unsigned long flags; int ret; + mutex_lock(&vc4_hdmi->mutex); + + spin_lock_irqsave(&vc4_hdmi->hw_lock, flags); HDMI_WRITE(HDMI_VID_CTL, HDMI_READ(HDMI_VID_CTL) | VC4_HD_VID_CTL_BLANKPIX); + spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags); if (vc4_hdmi->variant->phy_disable) vc4_hdmi->variant->phy_disable(vc4_hdmi); @@ -670,20 +806,23 @@ static void vc4_hdmi_encoder_post_crtc_powerdown(struct drm_encoder *encoder, ret = pm_runtime_put(&vc4_hdmi->pdev->dev); if (ret < 0) DRM_ERROR("Failed to release power domain: %d\n", ret); -} -static void vc4_hdmi_encoder_disable(struct drm_encoder *encoder) -{ + mutex_unlock(&vc4_hdmi->mutex); } -static void vc4_hdmi_csc_setup(struct vc4_hdmi *vc4_hdmi, bool enable) +static void vc4_hdmi_csc_setup(struct vc4_hdmi *vc4_hdmi, + struct drm_connector_state *state, + const struct drm_display_mode *mode) { + unsigned long flags; u32 csc_ctl; + spin_lock_irqsave(&vc4_hdmi->hw_lock, flags); + csc_ctl = VC4_SET_FIELD(VC4_HD_CSC_CTL_ORDER_BGR, VC4_HD_CSC_CTL_ORDER); - if (enable) { + if (!vc4_hdmi_is_full_range_rgb(vc4_hdmi, mode)) { /* CEA VICs other than #1 requre limited range RGB * output unless overridden by an AVI infoframe. * Apply a colorspace conversion to squash 0-255 down @@ -709,45 +848,142 @@ static void vc4_hdmi_csc_setup(struct vc4_hdmi *vc4_hdmi, bool enable) /* The RGB order applies even when CSC is disabled. */ HDMI_WRITE(HDMI_CSC_CTL, csc_ctl); + + spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags); } -static void vc5_hdmi_csc_setup(struct vc4_hdmi *vc4_hdmi, bool enable) + +/* + * If we need to output Full Range RGB, then use the unity matrix + * + * [ 1 0 0 0] + * [ 0 1 0 0] + * [ 0 0 1 0] + * + * Matrix is signed 2p13 fixed point, with signed 9p6 offsets + */ +static const u16 vc5_hdmi_csc_full_rgb_unity[3][4] = { + { 0x2000, 0x0000, 0x0000, 0x0000 }, + { 0x0000, 0x2000, 0x0000, 0x0000 }, + { 0x0000, 0x0000, 0x2000, 0x0000 }, +}; + +/* + * CEA VICs other than #1 require limited range RGB output unless + * overridden by an AVI infoframe. Apply a colorspace conversion to + * squash 0-255 down to 16-235. The matrix here is: + * + * [ 0.8594 0 0 16] + * [ 0 0.8594 0 16] + * [ 0 0 0.8594 16] + * + * Matrix is signed 2p13 fixed point, with signed 9p6 offsets + */ +static const u16 vc5_hdmi_csc_full_rgb_to_limited_rgb[3][4] = { + { 0x1b80, 0x0000, 0x0000, 0x0400 }, + { 0x0000, 0x1b80, 0x0000, 0x0400 }, + { 0x0000, 0x0000, 0x1b80, 0x0400 }, +}; + +/* + * Conversion between Full Range RGB and Full Range YUV422 using the + * BT.709 Colorspace + * + * [ 0.212639 0.715169 0.072192 0 ] + * [ -0.117208 -0.394207 0.511416 128 ] + * [ 0.511416 -0.464524 -0.046891 128 ] + * + * Matrix is signed 2p13 fixed point, with signed 9p6 offsets + */ +static const u16 vc5_hdmi_csc_full_rgb_to_full_yuv422_bt709[3][4] = { + { 0x06ce, 0x16e3, 0x024f, 0x0000 }, + { 0xfc41, 0xf364, 0x105e, 0x2000 }, + { 0x105e, 0xf124, 0xfe81, 0x2000 }, +}; + +/* + * Conversion between Full Range RGB and Full Range YUV444 using the + * BT.709 Colorspace + * + * [ -0.117208 -0.394207 0.511416 128 ] + * [ 0.511416 -0.464524 -0.046891 128 ] + * [ 0.212639 0.715169 0.072192 0 ] + * + * Matrix is signed 2p13 fixed point, with signed 9p6 offsets + */ +static const u16 vc5_hdmi_csc_full_rgb_to_full_yuv444_bt709[3][4] = { + { 0xfc41, 0xf364, 0x105e, 0x2000 }, + { 0x105e, 0xf124, 0xfe81, 0x2000 }, + { 0x06ce, 0x16e3, 0x024f, 0x0000 }, +}; + +static void vc5_hdmi_set_csc_coeffs(struct vc4_hdmi *vc4_hdmi, + const u16 coeffs[3][4]) { - u32 csc_ctl; + lockdep_assert_held(&vc4_hdmi->hw_lock); - csc_ctl = 0x07; /* RGB_CONVERT_MODE = custom matrix, || USE_RGB_TO_YCBCR */ + HDMI_WRITE(HDMI_CSC_12_11, (coeffs[0][1] << 16) | coeffs[0][0]); + HDMI_WRITE(HDMI_CSC_14_13, (coeffs[0][3] << 16) | coeffs[0][2]); + HDMI_WRITE(HDMI_CSC_22_21, (coeffs[1][1] << 16) | coeffs[1][0]); + HDMI_WRITE(HDMI_CSC_24_23, (coeffs[1][3] << 16) | coeffs[1][2]); + HDMI_WRITE(HDMI_CSC_32_31, (coeffs[2][1] << 16) | coeffs[2][0]); + HDMI_WRITE(HDMI_CSC_34_33, (coeffs[2][3] << 16) | coeffs[2][2]); +} - if (enable) { - /* CEA VICs other than #1 requre limited range RGB - * output unless overridden by an AVI infoframe. - * Apply a colorspace conversion to squash 0-255 down - * to 16-235. The matrix here is: - * - * [ 0.8594 0 0 16] - * [ 0 0.8594 0 16] - * [ 0 0 0.8594 16] - * [ 0 0 0 1] - * Matrix is signed 2p13 fixed point, with signed 9p6 offsets - */ - HDMI_WRITE(HDMI_CSC_12_11, (0x0000 << 16) | 0x1b80); - HDMI_WRITE(HDMI_CSC_14_13, (0x0400 << 16) | 0x0000); - HDMI_WRITE(HDMI_CSC_22_21, (0x1b80 << 16) | 0x0000); - HDMI_WRITE(HDMI_CSC_24_23, (0x0400 << 16) | 0x0000); - HDMI_WRITE(HDMI_CSC_32_31, (0x0000 << 16) | 0x0000); - HDMI_WRITE(HDMI_CSC_34_33, (0x0400 << 16) | 0x1b80); - } else { - /* Still use the matrix for full range, but make it unity. - * Matrix is signed 2p13 fixed point, with signed 9p6 offsets - */ - HDMI_WRITE(HDMI_CSC_12_11, (0x0000 << 16) | 0x2000); - HDMI_WRITE(HDMI_CSC_14_13, (0x0000 << 16) | 0x0000); - HDMI_WRITE(HDMI_CSC_22_21, (0x2000 << 16) | 0x0000); - HDMI_WRITE(HDMI_CSC_24_23, (0x0000 << 16) | 0x0000); - HDMI_WRITE(HDMI_CSC_32_31, (0x0000 << 16) | 0x0000); - HDMI_WRITE(HDMI_CSC_34_33, (0x0000 << 16) | 0x2000); +static void vc5_hdmi_csc_setup(struct vc4_hdmi *vc4_hdmi, + struct drm_connector_state *state, + const struct drm_display_mode *mode) +{ + struct vc4_hdmi_connector_state *vc4_state = + conn_state_to_vc4_hdmi_conn_state(state); + unsigned long flags; + u32 if_cfg = 0; + u32 if_xbar = 0x543210; + u32 csc_chan_ctl = 0; + u32 csc_ctl = VC5_MT_CP_CSC_CTL_ENABLE | VC4_SET_FIELD(VC4_HD_CSC_CTL_MODE_CUSTOM, + VC5_MT_CP_CSC_CTL_MODE); + + spin_lock_irqsave(&vc4_hdmi->hw_lock, flags); + + switch (vc4_state->output_format) { + case VC4_HDMI_OUTPUT_YUV444: + vc5_hdmi_set_csc_coeffs(vc4_hdmi, vc5_hdmi_csc_full_rgb_to_full_yuv444_bt709); + break; + + case VC4_HDMI_OUTPUT_YUV422: + csc_ctl |= VC4_SET_FIELD(VC5_MT_CP_CSC_CTL_FILTER_MODE_444_TO_422_STANDARD, + VC5_MT_CP_CSC_CTL_FILTER_MODE_444_TO_422) | + VC5_MT_CP_CSC_CTL_USE_444_TO_422 | + VC5_MT_CP_CSC_CTL_USE_RNG_SUPPRESSION; + + csc_chan_ctl |= VC4_SET_FIELD(VC5_MT_CP_CHANNEL_CTL_OUTPUT_REMAP_LEGACY_STYLE, + VC5_MT_CP_CHANNEL_CTL_OUTPUT_REMAP); + + if_cfg |= VC4_SET_FIELD(VC5_DVP_HT_VEC_INTERFACE_CFG_SEL_422_FORMAT_422_LEGACY, + VC5_DVP_HT_VEC_INTERFACE_CFG_SEL_422); + + vc5_hdmi_set_csc_coeffs(vc4_hdmi, vc5_hdmi_csc_full_rgb_to_full_yuv422_bt709); + break; + + case VC4_HDMI_OUTPUT_RGB: + if_xbar = 0x354021; + + if (!vc4_hdmi_is_full_range_rgb(vc4_hdmi, mode)) + vc5_hdmi_set_csc_coeffs(vc4_hdmi, vc5_hdmi_csc_full_rgb_to_limited_rgb); + else + vc5_hdmi_set_csc_coeffs(vc4_hdmi, vc5_hdmi_csc_full_rgb_unity); + break; + + default: + break; } + HDMI_WRITE(HDMI_VEC_INTERFACE_CFG, if_cfg); + HDMI_WRITE(HDMI_VEC_INTERFACE_XBAR, if_xbar); + HDMI_WRITE(HDMI_CSC_CHANNEL_CTL, csc_chan_ctl); HDMI_WRITE(HDMI_CSC_CTL, csc_ctl); + + spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags); } static void vc4_hdmi_set_timings(struct vc4_hdmi *vc4_hdmi, @@ -771,6 +1007,9 @@ static void vc4_hdmi_set_timings(struct vc4_hdmi *vc4_hdmi, VC4_SET_FIELD(mode->crtc_vtotal - mode->crtc_vsync_end, VC4_HDMI_VERTB_VBP)); + unsigned long flags; + + spin_lock_irqsave(&vc4_hdmi->hw_lock, flags); HDMI_WRITE(HDMI_HORZA, (vsync_pos ? VC4_HDMI_HORZA_VPOS : 0) | @@ -794,12 +1033,16 @@ static void vc4_hdmi_set_timings(struct vc4_hdmi *vc4_hdmi, HDMI_WRITE(HDMI_VERTB0, vertb_even); HDMI_WRITE(HDMI_VERTB1, vertb); + + spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags); } static void vc5_hdmi_set_timings(struct vc4_hdmi *vc4_hdmi, struct drm_connector_state *state, struct drm_display_mode *mode) { + const struct vc4_hdmi_connector_state *vc4_state = + conn_state_to_vc4_hdmi_conn_state(state); bool hsync_pos = mode->flags & DRM_MODE_FLAG_PHSYNC; bool vsync_pos = mode->flags & DRM_MODE_FLAG_PVSYNC; bool interlaced = mode->flags & DRM_MODE_FLAG_INTERLACE; @@ -817,11 +1060,13 @@ static void vc5_hdmi_set_timings(struct vc4_hdmi *vc4_hdmi, VC4_SET_FIELD(mode->crtc_vtotal - mode->crtc_vsync_end, VC4_HDMI_VERTB_VBP)); + unsigned long flags; unsigned char gcp; bool gcp_en; u32 reg; - HDMI_WRITE(HDMI_VEC_INTERFACE_XBAR, 0x354021); + spin_lock_irqsave(&vc4_hdmi->hw_lock, flags); + HDMI_WRITE(HDMI_HORZA, (vsync_pos ? VC5_HDMI_HORZA_VPOS : 0) | (hsync_pos ? VC5_HDMI_HORZA_HPOS : 0) | @@ -845,7 +1090,7 @@ static void vc5_hdmi_set_timings(struct vc4_hdmi *vc4_hdmi, HDMI_WRITE(HDMI_VERTB0, vertb_even); HDMI_WRITE(HDMI_VERTB1, vertb); - switch (state->max_bpc) { + switch (vc4_state->output_bpc) { case 12: gcp = 6; gcp_en = true; @@ -861,6 +1106,15 @@ static void vc5_hdmi_set_timings(struct vc4_hdmi *vc4_hdmi, break; } + /* + * YCC422 is always 36-bit and not considered deep colour so + * doesn't signal in GCP + */ + if (vc4_state->output_format == VC4_HDMI_OUTPUT_YUV422) { + gcp = 4; + gcp_en = false; + } + 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); @@ -884,13 +1138,18 @@ static void vc5_hdmi_set_timings(struct vc4_hdmi *vc4_hdmi, HDMI_WRITE(HDMI_MISC_CONTROL, reg); HDMI_WRITE(HDMI_CLOCK_STOP, 0); + + spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags); } static void vc4_hdmi_recenter_fifo(struct vc4_hdmi *vc4_hdmi) { + unsigned long flags; u32 drift; int ret; + spin_lock_irqsave(&vc4_hdmi->hw_lock, flags); + drift = HDMI_READ(HDMI_FIFO_CTL); drift &= VC4_HDMI_FIFO_VALID_WRITE_MASK; @@ -898,12 +1157,20 @@ static void vc4_hdmi_recenter_fifo(struct vc4_hdmi *vc4_hdmi) drift & ~VC4_HDMI_FIFO_CTL_RECENTER); HDMI_WRITE(HDMI_FIFO_CTL, drift | VC4_HDMI_FIFO_CTL_RECENTER); + + spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags); + usleep_range(1000, 1100); + + spin_lock_irqsave(&vc4_hdmi->hw_lock, flags); + HDMI_WRITE(HDMI_FIFO_CTL, drift & ~VC4_HDMI_FIFO_CTL_RECENTER); HDMI_WRITE(HDMI_FIFO_CTL, drift | VC4_HDMI_FIFO_CTL_RECENTER); + spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags); + ret = wait_for(HDMI_READ(HDMI_FIFO_CTL) & VC4_HDMI_FIFO_CTL_RECENTER_DONE, 1); WARN_ONCE(ret, "Timeout waiting for " @@ -919,14 +1186,17 @@ static void vc4_hdmi_encoder_pre_crtc_configure(struct drm_encoder *encoder, 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; + struct drm_display_mode *mode = &vc4_hdmi->saved_adjusted_mode; unsigned long bvb_rate, pixel_rate, hsm_rate; + unsigned long flags; int ret; + mutex_lock(&vc4_hdmi->mutex); + ret = pm_runtime_resume_and_get(&vc4_hdmi->pdev->dev); if (ret < 0) { DRM_ERROR("Failed to retain power domain: %d\n", ret); - return; + goto out; } pixel_rate = vc4_conn_state->pixel_rate; @@ -975,14 +1245,20 @@ static void vc4_hdmi_encoder_pre_crtc_configure(struct drm_encoder *encoder, if (vc4_hdmi->variant->phy_init) vc4_hdmi->variant->phy_init(vc4_hdmi, vc4_conn_state); + spin_lock_irqsave(&vc4_hdmi->hw_lock, flags); + HDMI_WRITE(HDMI_SCHEDULER_CONTROL, HDMI_READ(HDMI_SCHEDULER_CONTROL) | VC4_HDMI_SCHEDULER_CONTROL_MANUAL_FORMAT | VC4_HDMI_SCHEDULER_CONTROL_IGNORE_VSYNC_PREDICTS); + spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags); + if (vc4_hdmi->variant->set_timings) vc4_hdmi->variant->set_timings(vc4_hdmi, conn_state, mode); + mutex_unlock(&vc4_hdmi->mutex); + return; err_remove_bvb_req: @@ -994,42 +1270,48 @@ err_disable_pixel_clk: clk_disable_unprepare(vc4_hdmi->pixel_clock); err_runtime_pm: pm_runtime_put(&vc4_hdmi->pdev->dev); +out: + mutex_unlock(&vc4_hdmi->mutex); return; } 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); struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder); + struct drm_connector *connector = &vc4_hdmi->connector; + struct drm_display_mode *mode = &vc4_hdmi->saved_adjusted_mode; + struct drm_connector_state *conn_state = + drm_atomic_get_new_connector_state(state, connector); + unsigned long flags; - if (vc4_encoder->hdmi_monitor && - drm_default_rgb_quant_range(mode) == HDMI_QUANTIZATION_RANGE_LIMITED) { - if (vc4_hdmi->variant->csc_setup) - vc4_hdmi->variant->csc_setup(vc4_hdmi, true); - - vc4_encoder->limited_rgb_range = true; - } else { - if (vc4_hdmi->variant->csc_setup) - vc4_hdmi->variant->csc_setup(vc4_hdmi, false); + mutex_lock(&vc4_hdmi->mutex); - vc4_encoder->limited_rgb_range = false; - } + if (vc4_hdmi->variant->csc_setup) + vc4_hdmi->variant->csc_setup(vc4_hdmi, conn_state, mode); + spin_lock_irqsave(&vc4_hdmi->hw_lock, flags); HDMI_WRITE(HDMI_FIFO_CTL, VC4_HDMI_FIFO_CTL_MASTER_SLAVE_N); + spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags); + + mutex_unlock(&vc4_hdmi->mutex); } 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); + struct drm_display_mode *mode = &vc4_hdmi->saved_adjusted_mode; struct vc4_hdmi_encoder *vc4_encoder = to_vc4_hdmi_encoder(encoder); bool hsync_pos = mode->flags & DRM_MODE_FLAG_PHSYNC; bool vsync_pos = mode->flags & DRM_MODE_FLAG_PVSYNC; + unsigned long flags; int ret; + mutex_lock(&vc4_hdmi->mutex); + + spin_lock_irqsave(&vc4_hdmi->hw_lock, flags); + HDMI_WRITE(HDMI_VID_CTL, VC4_HD_VID_CTL_ENABLE | VC4_HD_VID_CTL_CLRRGB | @@ -1046,6 +1328,8 @@ static void vc4_hdmi_encoder_post_crtc_enable(struct drm_encoder *encoder, HDMI_READ(HDMI_SCHEDULER_CONTROL) | VC4_HDMI_SCHEDULER_CONTROL_MODE_HDMI); + spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags); + ret = wait_for(HDMI_READ(HDMI_SCHEDULER_CONTROL) & VC4_HDMI_SCHEDULER_CONTROL_HDMI_ACTIVE, 1000); WARN_ONCE(ret, "Timeout waiting for " @@ -1058,6 +1342,8 @@ static void vc4_hdmi_encoder_post_crtc_enable(struct drm_encoder *encoder, HDMI_READ(HDMI_SCHEDULER_CONTROL) & ~VC4_HDMI_SCHEDULER_CONTROL_MODE_HDMI); + spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags); + ret = wait_for(!(HDMI_READ(HDMI_SCHEDULER_CONTROL) & VC4_HDMI_SCHEDULER_CONTROL_HDMI_ACTIVE), 1000); WARN_ONCE(ret, "Timeout waiting for " @@ -1065,6 +1351,8 @@ static void vc4_hdmi_encoder_post_crtc_enable(struct drm_encoder *encoder, } if (vc4_encoder->hdmi_monitor) { + spin_lock_irqsave(&vc4_hdmi->hw_lock, flags); + WARN_ON(!(HDMI_READ(HDMI_SCHEDULER_CONTROL) & VC4_HDMI_SCHEDULER_CONTROL_HDMI_ACTIVE)); HDMI_WRITE(HDMI_SCHEDULER_CONTROL, @@ -1074,15 +1362,244 @@ static void vc4_hdmi_encoder_post_crtc_enable(struct drm_encoder *encoder, HDMI_WRITE(HDMI_RAM_PACKET_CONFIG, VC4_HDMI_RAM_PACKET_ENABLE); + spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags); + vc4_hdmi->output_enabled = true; + vc4_hdmi_set_infoframes(encoder); } vc4_hdmi_recenter_fifo(vc4_hdmi); vc4_hdmi_enable_scrambling(encoder); + + mutex_unlock(&vc4_hdmi->mutex); +} + +static void vc4_hdmi_encoder_atomic_mode_set(struct drm_encoder *encoder, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state) +{ + struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder); + struct vc4_hdmi_connector_state *vc4_state = + conn_state_to_vc4_hdmi_conn_state(conn_state); + + mutex_lock(&vc4_hdmi->mutex); + vc4_hdmi->output_bpc = vc4_state->output_bpc; + vc4_hdmi->output_format = vc4_state->output_format; + memcpy(&vc4_hdmi->saved_adjusted_mode, + &crtc_state->adjusted_mode, + sizeof(vc4_hdmi->saved_adjusted_mode)); + mutex_unlock(&vc4_hdmi->mutex); +} + +static bool +vc4_hdmi_sink_supports_format_bpc(const struct vc4_hdmi *vc4_hdmi, + const struct drm_display_info *info, + const struct drm_display_mode *mode, + unsigned int format, unsigned int bpc) +{ + struct drm_device *dev = vc4_hdmi->connector.dev; + u8 vic = drm_match_cea_mode(mode); + + if (vic == 1 && bpc != 8) { + drm_dbg(dev, "VIC1 requires a bpc of 8, got %u\n", bpc); + return false; + } + + if (!info->is_hdmi && + (format != VC4_HDMI_OUTPUT_RGB || bpc != 8)) { + drm_dbg(dev, "DVI Monitors require an RGB output at 8 bpc\n"); + return false; + } + + switch (format) { + case VC4_HDMI_OUTPUT_RGB: + drm_dbg(dev, "RGB Format, checking the constraints.\n"); + + if (bpc == 10 && !(info->edid_hdmi_dc_modes & DRM_EDID_HDMI_DC_30)) { + drm_dbg(dev, "10 BPC but sink doesn't support Deep Color 30.\n"); + return false; + } + + if (bpc == 12 && !(info->edid_hdmi_dc_modes & DRM_EDID_HDMI_DC_36)) { + drm_dbg(dev, "12 BPC but sink doesn't support Deep Color 36.\n"); + return false; + } + + drm_dbg(dev, "RGB format supported in that configuration.\n"); + + return true; + + case VC4_HDMI_OUTPUT_YUV422: + drm_dbg(dev, "YUV422 format, checking the constraints.\n"); + + if (!(info->color_formats & DRM_COLOR_FORMAT_YCRCB422)) { + drm_dbg(dev, "Sink doesn't support YUV422.\n"); + return false; + } + + if (bpc != 12) { + drm_dbg(dev, "YUV422 only supports 12 bpc.\n"); + return false; + } + + drm_dbg(dev, "YUV422 format supported in that configuration.\n"); + + return true; + + case VC4_HDMI_OUTPUT_YUV444: + drm_dbg(dev, "YUV444 format, checking the constraints.\n"); + + if (!(info->color_formats & DRM_COLOR_FORMAT_YCRCB444)) { + drm_dbg(dev, "Sink doesn't support YUV444.\n"); + return false; + } + + if (bpc == 10 && !(info->edid_hdmi_dc_modes & DRM_EDID_HDMI_DC_30)) { + drm_dbg(dev, "10 BPC but sink doesn't support Deep Color 30.\n"); + return false; + } + + if (bpc == 12 && !(info->edid_hdmi_dc_modes & DRM_EDID_HDMI_DC_36)) { + drm_dbg(dev, "12 BPC but sink doesn't support Deep Color 36.\n"); + return false; + } + + drm_dbg(dev, "YUV444 format supported in that configuration.\n"); + + return true; + } + + return false; +} + +static enum drm_mode_status +vc4_hdmi_encoder_clock_valid(const struct vc4_hdmi *vc4_hdmi, + unsigned long long clock) +{ + const struct drm_connector *connector = &vc4_hdmi->connector; + const struct drm_display_info *info = &connector->display_info; + + if (clock > vc4_hdmi->variant->max_pixel_clock) + return MODE_CLOCK_HIGH; + + if (vc4_hdmi->disable_4kp60 && clock > HDMI_14_MAX_TMDS_CLK) + return MODE_CLOCK_HIGH; + + if (info->max_tmds_clock && clock > (info->max_tmds_clock * 1000)) + return MODE_CLOCK_HIGH; + + return MODE_OK; +} + +static unsigned long long +vc4_hdmi_encoder_compute_mode_clock(const struct drm_display_mode *mode, + unsigned int bpc, + enum vc4_hdmi_output_format fmt) +{ + unsigned long long clock = mode->clock * 1000; + + if (mode->flags & DRM_MODE_FLAG_DBLCLK) + clock = clock * 2; + + if (fmt == VC4_HDMI_OUTPUT_YUV422) + bpc = 8; + + return clock * bpc / 8; } -static void vc4_hdmi_encoder_enable(struct drm_encoder *encoder) +static int +vc4_hdmi_encoder_compute_clock(const struct vc4_hdmi *vc4_hdmi, + struct vc4_hdmi_connector_state *vc4_state, + const struct drm_display_mode *mode, + unsigned int bpc, unsigned int fmt) { + unsigned long long clock; + + clock = vc4_hdmi_encoder_compute_mode_clock(mode, bpc, fmt); + if (vc4_hdmi_encoder_clock_valid(vc4_hdmi, clock) != MODE_OK) + return -EINVAL; + + vc4_state->pixel_rate = clock; + + return 0; +} + +static int +vc4_hdmi_encoder_compute_format(const struct vc4_hdmi *vc4_hdmi, + struct vc4_hdmi_connector_state *vc4_state, + const struct drm_display_mode *mode, + unsigned int bpc) +{ + struct drm_device *dev = vc4_hdmi->connector.dev; + const struct drm_connector *connector = &vc4_hdmi->connector; + const struct drm_display_info *info = &connector->display_info; + unsigned int format; + + drm_dbg(dev, "Trying with an RGB output\n"); + + format = VC4_HDMI_OUTPUT_RGB; + if (vc4_hdmi_sink_supports_format_bpc(vc4_hdmi, info, mode, format, bpc)) { + int ret; + + ret = vc4_hdmi_encoder_compute_clock(vc4_hdmi, vc4_state, + mode, bpc, format); + if (!ret) { + vc4_state->output_format = format; + return 0; + } + } + + drm_dbg(dev, "Failed, Trying with an YUV422 output\n"); + + format = VC4_HDMI_OUTPUT_YUV422; + if (vc4_hdmi_sink_supports_format_bpc(vc4_hdmi, info, mode, format, bpc)) { + int ret; + + ret = vc4_hdmi_encoder_compute_clock(vc4_hdmi, vc4_state, + mode, bpc, format); + if (!ret) { + vc4_state->output_format = format; + return 0; + } + } + + drm_dbg(dev, "Failed. No Format Supported for that bpc count.\n"); + + return -EINVAL; +} + +static int +vc4_hdmi_encoder_compute_config(const struct vc4_hdmi *vc4_hdmi, + struct vc4_hdmi_connector_state *vc4_state, + const struct drm_display_mode *mode) +{ + struct drm_device *dev = vc4_hdmi->connector.dev; + struct drm_connector_state *conn_state = &vc4_state->base; + unsigned int max_bpc = clamp_t(unsigned int, conn_state->max_bpc, 8, 12); + unsigned int bpc; + int ret; + + for (bpc = max_bpc; bpc >= 8; bpc -= 2) { + drm_dbg(dev, "Trying with a %d bpc output\n", bpc); + + ret = vc4_hdmi_encoder_compute_format(vc4_hdmi, vc4_state, + mode, bpc); + if (ret) + continue; + + vc4_state->output_bpc = bpc; + + drm_dbg(dev, + "Mode %ux%u @ %uHz: Found configuration: bpc: %u, fmt: %s, clock: %llu\n", + mode->hdisplay, mode->vdisplay, drm_mode_vrefresh(mode), + vc4_state->output_bpc, + vc4_hdmi_output_fmt_str(vc4_state->output_format), + vc4_state->pixel_rate); + + break; + } + + return ret; } #define WIFI_2_4GHz_CH1_MIN_FREQ 2400000000ULL @@ -1097,6 +1614,7 @@ static int vc4_hdmi_encoder_atomic_check(struct drm_encoder *encoder, struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder); unsigned long long pixel_rate = mode->clock * 1000; unsigned long long tmds_rate; + int ret; if (vc4_hdmi->variant->unsupported_odd_h_timings && !(mode->flags & DRM_MODE_FLAG_DBLCLK) && @@ -1118,24 +1636,9 @@ static int vc4_hdmi_encoder_atomic_check(struct drm_encoder *encoder, 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; + ret = vc4_hdmi_encoder_compute_config(vc4_hdmi, vc4_state, mode); + if (ret) + return ret; return 0; } @@ -1152,20 +1655,13 @@ vc4_hdmi_encoder_mode_valid(struct drm_encoder *encoder, (mode->hsync_end % 2) || (mode->htotal % 2))) return MODE_H_ILLEGAL; - 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; + return vc4_hdmi_encoder_clock_valid(vc4_hdmi, mode->clock * 1000); } static const struct drm_encoder_helper_funcs vc4_hdmi_encoder_helper_funcs = { .atomic_check = vc4_hdmi_encoder_atomic_check, + .atomic_mode_set = vc4_hdmi_encoder_atomic_mode_set, .mode_valid = vc4_hdmi_encoder_mode_valid, - .disable = vc4_hdmi_encoder_disable, - .enable = vc4_hdmi_encoder_enable, }; static u32 vc4_hdmi_calc_hsm_clock(struct vc4_hdmi *vc4_hdmi, unsigned long pixel_rate) @@ -1225,11 +1721,24 @@ static u32 vc5_hdmi_channel_map(struct vc4_hdmi *vc4_hdmi, u32 channel_mask) return channel_map; } +static bool vc5_hdmi_hp_detect(struct vc4_hdmi *vc4_hdmi) +{ + unsigned long flags; + u32 hotplug; + + spin_lock_irqsave(&vc4_hdmi->hw_lock, flags); + hotplug = HDMI_READ(HDMI_HOTPLUG); + spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags); + + return !!(hotplug & VC4_HDMI_HOTPLUG_CONNECTED); +} + /* HDMI audio codec callbacks */ 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 flags; unsigned long n, m; rational_best_approximation(hsm_clock, samplerate, @@ -1239,19 +1748,22 @@ static void vc4_hdmi_audio_set_mai_clock(struct vc4_hdmi *vc4_hdmi, VC4_HD_MAI_SMP_M_SHIFT) + 1, &n, &m); + spin_lock_irqsave(&vc4_hdmi->hw_lock, flags); HDMI_WRITE(HDMI_MAI_SMP, VC4_SET_FIELD(n, VC4_HD_MAI_SMP_N) | VC4_SET_FIELD(m - 1, VC4_HD_MAI_SMP_M)); + spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags); } 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; + const struct drm_display_mode *mode = &vc4_hdmi->saved_adjusted_mode; u32 n, cts; u64 tmp; + lockdep_assert_held(&vc4_hdmi->mutex); + lockdep_assert_held(&vc4_hdmi->hw_lock); + n = 128 * samplerate / 1000; tmp = (u64)(mode->clock * 1000) * n; do_div(tmp, 128 * samplerate); @@ -1277,31 +1789,48 @@ static inline struct vc4_hdmi *dai_to_hdmi(struct snd_soc_dai *dai) return snd_soc_card_get_drvdata(card); } -static int vc4_hdmi_audio_startup(struct device *dev, void *data) +static bool vc4_hdmi_audio_can_stream(struct vc4_hdmi *vc4_hdmi) { - struct vc4_hdmi *vc4_hdmi = dev_get_drvdata(dev); - struct drm_encoder *encoder = &vc4_hdmi->encoder.base.base; + lockdep_assert_held(&vc4_hdmi->mutex); /* - * If the HDMI encoder hasn't probed, or the encoder is - * currently in DVI mode, treat the codec dai as missing. + * If the encoder is currently in DVI mode, treat the codec DAI + * as missing. */ - if (!encoder->crtc || !(HDMI_READ(HDMI_RAM_PACKET_CONFIG) & - VC4_HDMI_RAM_PACKET_ENABLE)) + if (!vc4_hdmi->encoder.hdmi_monitor) + return false; + + return true; +} + +static int vc4_hdmi_audio_startup(struct device *dev, void *data) +{ + struct vc4_hdmi *vc4_hdmi = dev_get_drvdata(dev); + unsigned long flags; + + mutex_lock(&vc4_hdmi->mutex); + + if (!vc4_hdmi_audio_can_stream(vc4_hdmi)) { + mutex_unlock(&vc4_hdmi->mutex); return -ENODEV; + } vc4_hdmi->audio.streaming = true; + spin_lock_irqsave(&vc4_hdmi->hw_lock, flags); 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); + spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags); if (vc4_hdmi->variant->phy_rng_enable) vc4_hdmi->variant->phy_rng_enable(vc4_hdmi); + mutex_unlock(&vc4_hdmi->mutex); + return 0; } @@ -1309,32 +1838,48 @@ static void vc4_hdmi_audio_reset(struct vc4_hdmi *vc4_hdmi) { struct drm_encoder *encoder = &vc4_hdmi->encoder.base.base; struct device *dev = &vc4_hdmi->pdev->dev; + unsigned long flags; int ret; + lockdep_assert_held(&vc4_hdmi->mutex); + vc4_hdmi->audio.streaming = false; ret = vc4_hdmi_stop_packet(encoder, HDMI_INFOFRAME_TYPE_AUDIO, false); if (ret) dev_err(dev, "Failed to stop audio infoframe: %d\n", ret); + spin_lock_irqsave(&vc4_hdmi->hw_lock, flags); + HDMI_WRITE(HDMI_MAI_CTL, VC4_HD_MAI_CTL_RESET); HDMI_WRITE(HDMI_MAI_CTL, VC4_HD_MAI_CTL_ERRORF); HDMI_WRITE(HDMI_MAI_CTL, VC4_HD_MAI_CTL_FLUSH); + + spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags); } static void vc4_hdmi_audio_shutdown(struct device *dev, void *data) { struct vc4_hdmi *vc4_hdmi = dev_get_drvdata(dev); + unsigned long flags; + + mutex_lock(&vc4_hdmi->mutex); + + spin_lock_irqsave(&vc4_hdmi->hw_lock, flags); HDMI_WRITE(HDMI_MAI_CTL, VC4_HD_MAI_CTL_DLATE | VC4_HD_MAI_CTL_ERRORE | VC4_HD_MAI_CTL_ERRORF); + spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags); + 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); + + mutex_unlock(&vc4_hdmi->mutex); } static int sample_rate_to_mai_fmt(int samplerate) @@ -1384,6 +1929,7 @@ static int vc4_hdmi_audio_prepare(struct device *dev, void *data, struct drm_encoder *encoder = &vc4_hdmi->encoder.base.base; unsigned int sample_rate = params->sample_rate; unsigned int channels = params->channels; + unsigned long flags; u32 audio_packet_config, channel_mask; u32 channel_map; u32 mai_audio_format; @@ -1392,14 +1938,22 @@ static int vc4_hdmi_audio_prepare(struct device *dev, void *data, dev_dbg(dev, "%s: %u Hz, %d bit, %d channels\n", __func__, sample_rate, params->sample_width, channels); + mutex_lock(&vc4_hdmi->mutex); + + if (!vc4_hdmi_audio_can_stream(vc4_hdmi)) { + mutex_unlock(&vc4_hdmi->mutex); + return -EINVAL; + } + + vc4_hdmi_audio_set_mai_clock(vc4_hdmi, sample_rate); + + spin_lock_irqsave(&vc4_hdmi->hw_lock, flags); HDMI_WRITE(HDMI_MAI_CTL, 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) @@ -1437,10 +1991,16 @@ static int vc4_hdmi_audio_prepare(struct device *dev, void *data, 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, sample_rate); + spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags); + memcpy(&vc4_hdmi->audio.infoframe, ¶ms->cea, sizeof(params->cea)); - vc4_hdmi_set_audio_infoframe(encoder); + if (vc4_hdmi->output_enabled) + vc4_hdmi_set_audio_infoframe(encoder); + + mutex_unlock(&vc4_hdmi->mutex); return 0; } @@ -1505,7 +2065,9 @@ static int vc4_hdmi_audio_get_eld(struct device *dev, void *data, struct vc4_hdmi *vc4_hdmi = dev_get_drvdata(dev); struct drm_connector *connector = &vc4_hdmi->connector; + mutex_lock(&vc4_hdmi->mutex); memcpy(buf, connector->eld, min(sizeof(connector->eld), len)); + mutex_unlock(&vc4_hdmi->mutex); return 0; } @@ -1620,7 +2182,7 @@ static int vc4_hdmi_audio_init(struct vc4_hdmi *vc4_hdmi) snd_soc_card_set_drvdata(card, vc4_hdmi); ret = devm_snd_soc_register_card(dev, card); if (ret) - dev_err(dev, "Could not register sound card: %d\n", ret); + dev_err_probe(dev, ret, "Could not register sound card.\n"); return ret; @@ -1729,6 +2291,8 @@ static void vc4_cec_read_msg(struct vc4_hdmi *vc4_hdmi, u32 cntrl1) struct cec_msg *msg = &vc4_hdmi->cec_rx_msg; unsigned int i; + lockdep_assert_held(&vc4_hdmi->hw_lock); + msg->len = 1 + ((cntrl1 & VC4_HDMI_CEC_REC_WRD_CNT_MASK) >> VC4_HDMI_CEC_REC_WRD_CNT_SHIFT); @@ -1747,11 +2311,12 @@ static void vc4_cec_read_msg(struct vc4_hdmi *vc4_hdmi, u32 cntrl1) } } -static irqreturn_t vc4_cec_irq_handler_tx_bare(int irq, void *priv) +static irqreturn_t vc4_cec_irq_handler_tx_bare_locked(struct vc4_hdmi *vc4_hdmi) { - struct vc4_hdmi *vc4_hdmi = priv; u32 cntrl1; + lockdep_assert_held(&vc4_hdmi->hw_lock); + 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; @@ -1760,11 +2325,24 @@ static irqreturn_t vc4_cec_irq_handler_tx_bare(int irq, void *priv) return IRQ_WAKE_THREAD; } -static irqreturn_t vc4_cec_irq_handler_rx_bare(int irq, void *priv) +static irqreturn_t vc4_cec_irq_handler_tx_bare(int irq, void *priv) { struct vc4_hdmi *vc4_hdmi = priv; + irqreturn_t ret; + + spin_lock(&vc4_hdmi->hw_lock); + ret = vc4_cec_irq_handler_tx_bare_locked(vc4_hdmi); + spin_unlock(&vc4_hdmi->hw_lock); + + return ret; +} + +static irqreturn_t vc4_cec_irq_handler_rx_bare_locked(struct vc4_hdmi *vc4_hdmi) +{ u32 cntrl1; + lockdep_assert_held(&vc4_hdmi->hw_lock); + vc4_hdmi->cec_rx_msg.len = 0; cntrl1 = HDMI_READ(HDMI_CEC_CNTRL_1); vc4_cec_read_msg(vc4_hdmi, cntrl1); @@ -1777,6 +2355,18 @@ static irqreturn_t vc4_cec_irq_handler_rx_bare(int irq, void *priv) return IRQ_WAKE_THREAD; } +static irqreturn_t vc4_cec_irq_handler_rx_bare(int irq, void *priv) +{ + struct vc4_hdmi *vc4_hdmi = priv; + irqreturn_t ret; + + spin_lock(&vc4_hdmi->hw_lock); + ret = vc4_cec_irq_handler_rx_bare_locked(vc4_hdmi); + spin_unlock(&vc4_hdmi->hw_lock); + + return ret; +} + static irqreturn_t vc4_cec_irq_handler(int irq, void *priv) { struct vc4_hdmi *vc4_hdmi = priv; @@ -1787,14 +2377,17 @@ static irqreturn_t vc4_cec_irq_handler(int irq, void *priv) if (!(stat & VC4_HDMI_CPU_CEC)) return IRQ_NONE; + spin_lock(&vc4_hdmi->hw_lock); 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) - ret = vc4_cec_irq_handler_rx_bare(irq, priv); + ret = vc4_cec_irq_handler_rx_bare_locked(vc4_hdmi); else - ret = vc4_cec_irq_handler_tx_bare(irq, priv); + ret = vc4_cec_irq_handler_tx_bare_locked(vc4_hdmi); HDMI_WRITE(HDMI_CEC_CPU_CLEAR, VC4_HDMI_CPU_CEC); + spin_unlock(&vc4_hdmi->hw_lock); + return ret; } @@ -1803,13 +2396,27 @@ 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; + unsigned long flags; u32 val; int ret; + /* + * NOTE: This function should really take vc4_hdmi->mutex, but doing so + * results in a reentrancy since cec_s_phys_addr_from_edid() called in + * .detect or .get_modes might call .adap_enable, which leads to this + * function being called with that mutex held. + * + * Concurrency is not an issue for the moment since we don't share any + * state with KMS, so we can ignore the lock for now, but we need to + * keep it in mind if we were to change that assumption. + */ + ret = pm_runtime_resume_and_get(&vc4_hdmi->pdev->dev); if (ret) return ret; + spin_lock_irqsave(&vc4_hdmi->hw_lock, flags); + 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 | @@ -1840,12 +2447,28 @@ static int vc4_hdmi_cec_enable(struct cec_adapter *adap) if (!vc4_hdmi->variant->external_irq_controller) HDMI_WRITE(HDMI_CEC_CPU_MASK_CLEAR, VC4_HDMI_CPU_CEC); + spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags); + return 0; } static int vc4_hdmi_cec_disable(struct cec_adapter *adap) { struct vc4_hdmi *vc4_hdmi = cec_get_drvdata(adap); + unsigned long flags; + + /* + * NOTE: This function should really take vc4_hdmi->mutex, but doing so + * results in a reentrancy since cec_s_phys_addr_from_edid() called in + * .detect or .get_modes might call .adap_enable, which leads to this + * function being called with that mutex held. + * + * Concurrency is not an issue for the moment since we don't share any + * state with KMS, so we can ignore the lock for now, but we need to + * keep it in mind if we were to change that assumption. + */ + + spin_lock_irqsave(&vc4_hdmi->hw_lock, flags); if (!vc4_hdmi->variant->external_irq_controller) HDMI_WRITE(HDMI_CEC_CPU_MASK_SET, VC4_HDMI_CPU_CEC); @@ -1853,6 +2476,8 @@ static int vc4_hdmi_cec_disable(struct cec_adapter *adap) HDMI_WRITE(HDMI_CEC_CNTRL_5, HDMI_READ(HDMI_CEC_CNTRL_5) | VC4_HDMI_CEC_TX_SW_RESET | VC4_HDMI_CEC_RX_SW_RESET); + spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags); + pm_runtime_put(&vc4_hdmi->pdev->dev); return 0; @@ -1869,10 +2494,25 @@ static int vc4_hdmi_cec_adap_enable(struct cec_adapter *adap, bool enable) static int vc4_hdmi_cec_adap_log_addr(struct cec_adapter *adap, u8 log_addr) { struct vc4_hdmi *vc4_hdmi = cec_get_drvdata(adap); + unsigned long flags; + + /* + * NOTE: This function should really take vc4_hdmi->mutex, but doing so + * results in a reentrancy since cec_s_phys_addr_from_edid() called in + * .detect or .get_modes might call .adap_enable, which leads to this + * function being called with that mutex held. + * + * Concurrency is not an issue for the moment since we don't share any + * state with KMS, so we can ignore the lock for now, but we need to + * keep it in mind if we were to change that assumption. + */ + spin_lock_irqsave(&vc4_hdmi->hw_lock, flags); HDMI_WRITE(HDMI_CEC_CNTRL_1, (HDMI_READ(HDMI_CEC_CNTRL_1) & ~VC4_HDMI_CEC_ADDR_MASK) | (log_addr & 0xf) << VC4_HDMI_CEC_ADDR_SHIFT); + spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags); + return 0; } @@ -1881,14 +2521,28 @@ static int vc4_hdmi_cec_adap_transmit(struct cec_adapter *adap, u8 attempts, { struct vc4_hdmi *vc4_hdmi = cec_get_drvdata(adap); struct drm_device *dev = vc4_hdmi->connector.dev; + unsigned long flags; u32 val; unsigned int i; + /* + * NOTE: This function should really take vc4_hdmi->mutex, but doing so + * results in a reentrancy since cec_s_phys_addr_from_edid() called in + * .detect or .get_modes might call .adap_enable, which leads to this + * function being called with that mutex held. + * + * Concurrency is not an issue for the moment since we don't share any + * state with KMS, so we can ignore the lock for now, but we need to + * keep it in mind if we were to change that assumption. + */ + if (msg->len > 16) { drm_err(dev, "Attempting to transmit too much data (%d)\n", msg->len); return -ENOMEM; } + spin_lock_irqsave(&vc4_hdmi->hw_lock, flags); + for (i = 0; i < msg->len; i += 4) HDMI_WRITE(HDMI_CEC_TX_DATA_1 + (i >> 2), (msg->msg[i]) | @@ -1904,6 +2558,9 @@ static int vc4_hdmi_cec_adap_transmit(struct cec_adapter *adap, u8 attempts, val |= VC4_HDMI_CEC_START_XMIT_BEGIN; HDMI_WRITE(HDMI_CEC_CNTRL_1, val); + + spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags); + return 0; } @@ -1918,7 +2575,7 @@ static int vc4_hdmi_cec_init(struct vc4_hdmi *vc4_hdmi) struct cec_connector_info conn_info; struct platform_device *pdev = vc4_hdmi->pdev; struct device *dev = &pdev->dev; - u32 value; + unsigned long flags; int ret; if (!of_find_property(dev->of_node, "interrupts", NULL)) { @@ -1937,13 +2594,6 @@ static int vc4_hdmi_cec_init(struct vc4_hdmi *vc4_hdmi) cec_fill_conn_info_from_drm(&conn_info, &vc4_hdmi->connector); cec_s_conn_info(vc4_hdmi->cec_adap, &conn_info); - value = HDMI_READ(HDMI_CEC_CNTRL_1); - /* Set the logical address to Unregistered */ - value |= VC4_HDMI_CEC_ADDR_MASK; - HDMI_WRITE(HDMI_CEC_CNTRL_1, value); - - vc4_hdmi_cec_update_clk_div(vc4_hdmi); - if (vc4_hdmi->variant->external_irq_controller) { ret = request_threaded_irq(platform_get_irq_byname(pdev, "cec-rx"), vc4_cec_irq_handler_rx_bare, @@ -1959,7 +2609,9 @@ static int vc4_hdmi_cec_init(struct vc4_hdmi *vc4_hdmi) if (ret) goto err_remove_cec_rx_handler; } else { + spin_lock_irqsave(&vc4_hdmi->hw_lock, flags); HDMI_WRITE(HDMI_CEC_CPU_MASK_SET, 0xffffffff); + spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags); ret = request_threaded_irq(platform_get_irq(pdev, 0), vc4_cec_irq_handler, @@ -2004,6 +2656,29 @@ static void vc4_hdmi_cec_exit(struct vc4_hdmi *vc4_hdmi) cec_unregister_adapter(vc4_hdmi->cec_adap); } + +static int vc4_hdmi_cec_resume(struct vc4_hdmi *vc4_hdmi) +{ + unsigned long flags; + u32 value; + + spin_lock_irqsave(&vc4_hdmi->hw_lock, flags); + value = HDMI_READ(HDMI_CEC_CNTRL_1); + /* Set the logical address to Unregistered */ + value |= VC4_HDMI_CEC_ADDR_MASK; + HDMI_WRITE(HDMI_CEC_CNTRL_1, value); + spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags); + + vc4_hdmi_cec_update_clk_div(vc4_hdmi); + + if (!vc4_hdmi->variant->external_irq_controller) { + spin_lock_irqsave(&vc4_hdmi->hw_lock, flags); + HDMI_WRITE(HDMI_CEC_CPU_MASK_SET, 0xffffffff); + spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags); + } + + return 0; +} #else static int vc4_hdmi_cec_init(struct vc4_hdmi *vc4_hdmi) { @@ -2012,6 +2687,10 @@ static int vc4_hdmi_cec_init(struct vc4_hdmi *vc4_hdmi) static void vc4_hdmi_cec_exit(struct vc4_hdmi *vc4_hdmi) {}; +static int vc4_hdmi_cec_resume(struct vc4_hdmi *vc4_hdmi) +{ + return 0; +} #endif static int vc4_hdmi_build_regset(struct vc4_hdmi *vc4_hdmi, @@ -2247,6 +2926,15 @@ static int vc4_hdmi_runtime_resume(struct device *dev) if (ret) return ret; + if (vc4_hdmi->variant->reset) + vc4_hdmi->variant->reset(vc4_hdmi); + + ret = vc4_hdmi_cec_resume(vc4_hdmi); + if (ret) { + clk_disable_unprepare(vc4_hdmi->hsm_clock); + return ret; + } + return 0; } #endif @@ -2265,6 +2953,8 @@ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data) vc4_hdmi = devm_kzalloc(dev, sizeof(*vc4_hdmi), GFP_KERNEL); if (!vc4_hdmi) return -ENOMEM; + mutex_init(&vc4_hdmi->mutex); + spin_lock_init(&vc4_hdmi->hw_lock); INIT_DELAYED_WORK(&vc4_hdmi->scrambling_work, vc4_hdmi_scrambling_wq); dev_set_drvdata(dev, vc4_hdmi); @@ -2278,6 +2968,15 @@ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data) vc4_hdmi->pdev = pdev; vc4_hdmi->variant = variant; + /* + * Since we don't know the state of the controller and its + * display (if any), let's assume it's always enabled. + * vc4_hdmi_disable_scrambling() will thus run at boot, make + * sure it's disabled, and avoid any inconsistency. + */ + if (variant->max_pixel_clock > HDMI_14_MAX_TMDS_CLK) + vc4_hdmi->scdc_enabled = true; + ret = variant->init_resources(vc4_hdmi); if (ret) return ret; @@ -2349,20 +3048,11 @@ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data) if (ret) goto err_put_ddc; - /* - * 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); + ret = pm_runtime_resume_and_get(dev); + if (ret) + goto err_put_ddc; if ((of_device_is_compatible(dev->of_node, "brcm,bcm2711-hdmi0") || of_device_is_compatible(dev->of_node, "brcm,bcm2711-hdmi1")) && @@ -2518,6 +3208,7 @@ static const struct vc4_hdmi_variant bcm2711_hdmi0_variant = { .calc_hsm_clock = vc5_hdmi_calc_hsm_clock, .channel_map = vc5_hdmi_channel_map, .supports_hdr = true, + .hp_detect = vc5_hdmi_hp_detect, }; static const struct vc4_hdmi_variant bcm2711_hdmi1_variant = { @@ -2547,6 +3238,7 @@ static const struct vc4_hdmi_variant bcm2711_hdmi1_variant = { .calc_hsm_clock = vc5_hdmi_calc_hsm_clock, .channel_map = vc5_hdmi_channel_map, .supports_hdr = true, + .hp_detect = vc5_hdmi_hp_detect, }; static const struct of_device_id vc4_hdmi_dt_match[] = { diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.h b/drivers/gpu/drm/vc4/vc4_hdmi.h index 49fc91962fe4514de40a8fdfa1ad7255068c07b1..3dd0d2a53a44583c6dd5a3021f5d49bcb7ca2cf1 100644 --- a/drivers/gpu/drm/vc4/vc4_hdmi.h +++ b/drivers/gpu/drm/vc4/vc4_hdmi.h @@ -12,7 +12,6 @@ struct vc4_hdmi_encoder { struct vc4_encoder base; bool hdmi_monitor; - bool limited_rgb_range; }; static inline struct vc4_hdmi_encoder * @@ -77,7 +76,9 @@ struct vc4_hdmi_variant { void (*reset)(struct vc4_hdmi *vc4_hdmi); /* Callback to enable / disable the CSC */ - void (*csc_setup)(struct vc4_hdmi *vc4_hdmi, bool enable); + void (*csc_setup)(struct vc4_hdmi *vc4_hdmi, + struct drm_connector_state *state, + const struct drm_display_mode *mode); /* Callback to configure the video timings in the HDMI block */ void (*set_timings)(struct vc4_hdmi *vc4_hdmi, @@ -105,6 +106,9 @@ struct vc4_hdmi_variant { /* Enables HDR metadata */ bool supports_hdr; + + /* Callback for hardware specific hotplug detect */ + bool (*hp_detect)(struct vc4_hdmi *vc4_hdmi); }; /* HDMI audio information */ @@ -119,6 +123,13 @@ struct vc4_hdmi_audio { bool streaming; }; +enum vc4_hdmi_output_format { + VC4_HDMI_OUTPUT_RGB, + VC4_HDMI_OUTPUT_YUV422, + VC4_HDMI_OUTPUT_YUV444, + VC4_HDMI_OUTPUT_YUV420, +}; + /* General HDMI hardware state. */ struct vc4_hdmi { struct vc4_hdmi_audio audio; @@ -186,6 +197,7 @@ struct vc4_hdmi { /* 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; @@ -193,6 +205,55 @@ struct vc4_hdmi { struct debugfs_regset32 phy_regset; struct debugfs_regset32 ram_regset; struct debugfs_regset32 rm_regset; + + /** + * @hw_lock: Spinlock protecting device register access. + */ + spinlock_t hw_lock; + + /** + * @mutex: Mutex protecting the driver access across multiple + * frameworks (KMS, ALSA). + * + * NOTE: While supported, CEC has been left out since + * cec_s_phys_addr_from_edid() might call .adap_enable and lead to a + * reentrancy issue between .get_modes (or .detect) and .adap_enable. + * Since we don't share any state between the CEC hooks and KMS', it's + * not a big deal. The only trouble might come from updating the CEC + * clock divider which might be affected by a modeset, but CEC should + * be resilient to that. + */ + struct mutex mutex; + + /** + * @saved_adjusted_mode: Copy of @drm_crtc_state.adjusted_mode + * for use by ALSA hooks and interrupt handlers. Protected by @mutex. + */ + struct drm_display_mode saved_adjusted_mode; + + /** + * @output_enabled: Is the HDMI controller currently active? + * Protected by @mutex. + */ + bool output_enabled; + + /** + * @scdc_enabled: Is the HDMI controller currently running with + * the scrambler on? Protected by @mutex. + */ + bool scdc_enabled; + + /** + * @output_bpc: Copy of @vc4_connector_state.output_bpc for use + * outside of KMS hooks. Protected by @mutex. + */ + unsigned int output_bpc; + + /** + * @output_format: Copy of @vc4_connector_state.output_format + * for use outside of KMS hooks. Protected by @mutex. + */ + enum vc4_hdmi_output_format output_format; }; static inline struct vc4_hdmi * @@ -212,6 +273,8 @@ encoder_to_vc4_hdmi(struct drm_encoder *encoder) struct vc4_hdmi_connector_state { struct drm_connector_state base; unsigned long long pixel_rate; + unsigned int output_bpc; + enum vc4_hdmi_output_format output_format; }; static inline struct vc4_hdmi_connector_state * diff --git a/drivers/gpu/drm/vc4/vc4_hdmi_phy.c b/drivers/gpu/drm/vc4/vc4_hdmi_phy.c index 36535480f8e2b62302ff3eafce83b1facb69e16a..62148f0dc2845cc02ad998e0540091106070a1f4 100644 --- a/drivers/gpu/drm/vc4/vc4_hdmi_phy.c +++ b/drivers/gpu/drm/vc4/vc4_hdmi_phy.c @@ -130,31 +130,49 @@ void vc4_hdmi_phy_init(struct vc4_hdmi *vc4_hdmi, struct vc4_hdmi_connector_state *conn_state) { + unsigned long flags; + /* PHY should be in reset, like * vc4_hdmi_encoder_disable() does. */ + spin_lock_irqsave(&vc4_hdmi->hw_lock, flags); + HDMI_WRITE(HDMI_TX_PHY_RESET_CTL, 0xf << 16); HDMI_WRITE(HDMI_TX_PHY_RESET_CTL, 0); + + spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags); } void vc4_hdmi_phy_disable(struct vc4_hdmi *vc4_hdmi) { + unsigned long flags; + + spin_lock_irqsave(&vc4_hdmi->hw_lock, flags); HDMI_WRITE(HDMI_TX_PHY_RESET_CTL, 0xf << 16); + spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags); } void vc4_hdmi_phy_rng_enable(struct vc4_hdmi *vc4_hdmi) { + unsigned long flags; + + spin_lock_irqsave(&vc4_hdmi->hw_lock, flags); HDMI_WRITE(HDMI_TX_PHY_CTL_0, HDMI_READ(HDMI_TX_PHY_CTL_0) & ~VC4_HDMI_TX_PHY_RNG_PWRDN); + spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags); } void vc4_hdmi_phy_rng_disable(struct vc4_hdmi *vc4_hdmi) { + unsigned long flags; + + spin_lock_irqsave(&vc4_hdmi->hw_lock, flags); HDMI_WRITE(HDMI_TX_PHY_CTL_0, HDMI_READ(HDMI_TX_PHY_CTL_0) | VC4_HDMI_TX_PHY_RNG_PWRDN); + spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags); } static unsigned long long @@ -336,6 +354,8 @@ phy_get_channel_settings(enum vc4_hdmi_phy_channel chan, static void vc5_hdmi_reset_phy(struct vc4_hdmi *vc4_hdmi) { + lockdep_assert_held(&vc4_hdmi->hw_lock); + HDMI_WRITE(HDMI_TX_PHY_RESET_CTL, 0x0f); HDMI_WRITE(HDMI_TX_PHY_POWERDOWN_CTL, BIT(10)); } @@ -348,10 +368,13 @@ void vc5_hdmi_phy_init(struct vc4_hdmi *vc4_hdmi, unsigned long long pixel_freq = conn_state->pixel_rate; unsigned long long vco_freq; unsigned char word_sel; + unsigned long flags; u8 vco_sel, vco_div; vco_freq = phy_get_vco_freq(pixel_freq, &vco_sel, &vco_div); + spin_lock_irqsave(&vc4_hdmi->hw_lock, flags); + vc5_hdmi_reset_phy(vc4_hdmi); HDMI_WRITE(HDMI_TX_PHY_POWERDOWN_CTL, @@ -501,23 +524,37 @@ void vc5_hdmi_phy_init(struct vc4_hdmi *vc4_hdmi, HDMI_READ(HDMI_TX_PHY_RESET_CTL) | VC4_HDMI_TX_PHY_RESET_CTL_PLL_RESETB | VC4_HDMI_TX_PHY_RESET_CTL_PLLDIV_RESETB); + + spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags); } void vc5_hdmi_phy_disable(struct vc4_hdmi *vc4_hdmi) { + unsigned long flags; + + spin_lock_irqsave(&vc4_hdmi->hw_lock, flags); vc5_hdmi_reset_phy(vc4_hdmi); + spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags); } void vc5_hdmi_phy_rng_enable(struct vc4_hdmi *vc4_hdmi) { + unsigned long flags; + + spin_lock_irqsave(&vc4_hdmi->hw_lock, flags); HDMI_WRITE(HDMI_TX_PHY_POWERDOWN_CTL, HDMI_READ(HDMI_TX_PHY_POWERDOWN_CTL) & ~VC4_HDMI_TX_PHY_POWERDOWN_CTL_RNDGEN_PWRDN); + spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags); } void vc5_hdmi_phy_rng_disable(struct vc4_hdmi *vc4_hdmi) { + unsigned long flags; + + spin_lock_irqsave(&vc4_hdmi->hw_lock, flags); HDMI_WRITE(HDMI_TX_PHY_POWERDOWN_CTL, HDMI_READ(HDMI_TX_PHY_POWERDOWN_CTL) | VC4_HDMI_TX_PHY_POWERDOWN_CTL_RNDGEN_PWRDN); + spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags); } diff --git a/drivers/gpu/drm/vc4/vc4_hdmi_regs.h b/drivers/gpu/drm/vc4/vc4_hdmi_regs.h index 23930a8fa3769d1327d9ac3f0aaa60b1620dc6b6..0198de96c7b22ada5e286b6f6dcbb6ff592c1f6d 100644 --- a/drivers/gpu/drm/vc4/vc4_hdmi_regs.h +++ b/drivers/gpu/drm/vc4/vc4_hdmi_regs.h @@ -54,6 +54,7 @@ enum vc4_hdmi_field { HDMI_CSC_24_23, HDMI_CSC_32_31, HDMI_CSC_34_33, + HDMI_CSC_CHANNEL_CTL, HDMI_CSC_CTL, /* @@ -119,6 +120,7 @@ enum vc4_hdmi_field { HDMI_TX_PHY_POWERDOWN_CTL, HDMI_TX_PHY_RESET_CTL, HDMI_TX_PHY_TMDS_CLK_WORD_SEL, + HDMI_VEC_INTERFACE_CFG, HDMI_VEC_INTERFACE_XBAR, HDMI_VERTA0, HDMI_VERTA1, @@ -246,6 +248,7 @@ static const struct vc4_hdmi_register __maybe_unused vc5_hdmi_hdmi0_fields[] = { VC4_HDMI_REG(HDMI_SCRAMBLER_CTL, 0x1c4), VC5_DVP_REG(HDMI_CLOCK_STOP, 0x0bc), + VC5_DVP_REG(HDMI_VEC_INTERFACE_CFG, 0x0ec), VC5_DVP_REG(HDMI_VEC_INTERFACE_XBAR, 0x0f0), VC5_PHY_REG(HDMI_TX_PHY_RESET_CTL, 0x000), @@ -291,6 +294,7 @@ static const struct vc4_hdmi_register __maybe_unused vc5_hdmi_hdmi0_fields[] = { VC5_CSC_REG(HDMI_CSC_24_23, 0x010), VC5_CSC_REG(HDMI_CSC_32_31, 0x014), VC5_CSC_REG(HDMI_CSC_34_33, 0x018), + VC5_CSC_REG(HDMI_CSC_CHANNEL_CTL, 0x02c), }; static const struct vc4_hdmi_register __maybe_unused vc5_hdmi_hdmi1_fields[] = { @@ -327,6 +331,7 @@ static const struct vc4_hdmi_register __maybe_unused vc5_hdmi_hdmi1_fields[] = { VC4_HDMI_REG(HDMI_SCRAMBLER_CTL, 0x1c4), VC5_DVP_REG(HDMI_CLOCK_STOP, 0x0bc), + VC5_DVP_REG(HDMI_VEC_INTERFACE_CFG, 0x0ec), VC5_DVP_REG(HDMI_VEC_INTERFACE_XBAR, 0x0f0), VC5_PHY_REG(HDMI_TX_PHY_RESET_CTL, 0x000), @@ -372,6 +377,7 @@ static const struct vc4_hdmi_register __maybe_unused vc5_hdmi_hdmi1_fields[] = { VC5_CSC_REG(HDMI_CSC_24_23, 0x010), VC5_CSC_REG(HDMI_CSC_32_31, 0x014), VC5_CSC_REG(HDMI_CSC_34_33, 0x018), + VC5_CSC_REG(HDMI_CSC_CHANNEL_CTL, 0x02c), }; static inline @@ -417,7 +423,7 @@ static inline u32 vc4_hdmi_read(struct vc4_hdmi *hdmi, const struct vc4_hdmi_variant *variant = hdmi->variant; void __iomem *base; - WARN_ON(!pm_runtime_active(&hdmi->pdev->dev)); + WARN_ON(pm_runtime_status_suspended(&hdmi->pdev->dev)); if (reg >= variant->num_registers) { dev_warn(&hdmi->pdev->dev, @@ -445,7 +451,9 @@ static inline void vc4_hdmi_write(struct vc4_hdmi *hdmi, const struct vc4_hdmi_variant *variant = hdmi->variant; void __iomem *base; - WARN_ON(!pm_runtime_active(&hdmi->pdev->dev)); + lockdep_assert_held(&hdmi->hw_lock); + + WARN_ON(pm_runtime_status_suspended(&hdmi->pdev->dev)); if (reg >= variant->num_registers) { dev_warn(&hdmi->pdev->dev, diff --git a/drivers/gpu/drm/vc4/vc4_hvs.c b/drivers/gpu/drm/vc4/vc4_hvs.c index 6049923422d084d4d8b0c7c86ba07cdefcf7044e..a873d9d8fd58b80974f953966358ddd3557bede4 100644 --- a/drivers/gpu/drm/vc4/vc4_hvs.c +++ b/drivers/gpu/drm/vc4/vc4_hvs.c @@ -134,6 +134,84 @@ static int vc4_hvs_debugfs_dlist(struct seq_file *m, void *data) return 0; } +static int vc5_hvs_debugfs_gamma(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 i, chan; + u32 dispstat, dispbkgndx; + + for (chan = 0; chan < SCALER_CHANNELS_COUNT; chan++) { + u32 x_c, grad; + u32 offset = SCALER5_DSPGAMMA_START + + chan * SCALER5_DSPGAMMA_CHAN_OFFSET; + + dispstat = VC4_GET_FIELD(HVS_READ(SCALER_DISPSTATX(chan)), + SCALER_DISPSTATX_MODE); + if (dispstat == SCALER_DISPSTATX_MODE_DISABLED || + dispstat == SCALER_DISPSTATX_MODE_EOF) { + drm_printf(&p, "HVS channel %u: Channel disabled\n", chan); + continue; + } + + dispbkgndx = HVS_READ(SCALER_DISPBKGNDX(chan)); + if (!(dispbkgndx & SCALER_DISPBKGND_GAMMA)) { + drm_printf(&p, "HVS channel %u: Gamma disabled\n", chan); + continue; + } + + drm_printf(&p, "HVS channel %u:\n", chan); + drm_printf(&p, " red:\n"); + for (i = 0; i < SCALER5_DSPGAMMA_NUM_POINTS; i++, offset += 8) { + x_c = HVS_READ(offset); + grad = HVS_READ(offset + 4); + drm_printf(&p, " %08x %08x - x %u, c %u, grad %u\n", + x_c, grad, + VC4_GET_FIELD(x_c, SCALER5_DSPGAMMA_OFF_X), + VC4_GET_FIELD(x_c, SCALER5_DSPGAMMA_OFF_C), + grad); + } + drm_printf(&p, " green:\n"); + for (i = 0; i < SCALER5_DSPGAMMA_NUM_POINTS; i++, offset += 8) { + x_c = HVS_READ(offset); + grad = HVS_READ(offset + 4); + drm_printf(&p, " %08x %08x - x %u, c %u, grad %u\n", + x_c, grad, + VC4_GET_FIELD(x_c, SCALER5_DSPGAMMA_OFF_X), + VC4_GET_FIELD(x_c, SCALER5_DSPGAMMA_OFF_C), + grad); + } + drm_printf(&p, " blue:\n"); + for (i = 0; i < SCALER5_DSPGAMMA_NUM_POINTS; i++, offset += 8) { + x_c = HVS_READ(offset); + grad = HVS_READ(offset + 4); + drm_printf(&p, " %08x %08x - x %u, c %u, grad %u\n", + x_c, grad, + VC4_GET_FIELD(x_c, SCALER5_DSPGAMMA_OFF_X), + VC4_GET_FIELD(x_c, SCALER5_DSPGAMMA_OFF_C), + grad); + } + + /* Alpha only valid on channel 2 */ + if (chan != 2) + continue; + + drm_printf(&p, " alpha:\n"); + for (i = 0; i < SCALER5_DSPGAMMA_NUM_POINTS; i++, offset += 8) { + x_c = HVS_READ(offset); + grad = HVS_READ(offset + 4); + drm_printf(&p, " %08x %08x - x %u, c %u, grad %u\n", + x_c, grad, + VC4_GET_FIELD(x_c, SCALER5_DSPGAMMA_OFF_X), + VC4_GET_FIELD(x_c, SCALER5_DSPGAMMA_OFF_C), + grad); + } + } + return 0; +} + /* The filter kernel is composed of dwords each containing 3 9-bit * signed integers packed next to each other. */ @@ -236,6 +314,80 @@ static void vc4_hvs_update_gamma_lut(struct drm_crtc *crtc) vc4_hvs_lut_load(crtc); } +static void vc5_hvs_write_gamma_entry(struct vc4_dev *vc4, + u32 offset, + struct vc5_gamma_entry *gamma) +{ + HVS_WRITE(offset, gamma->x_c_terms); + HVS_WRITE(offset + 4, gamma->grad_term); +} + +static void vc5_hvs_lut_load(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 vc4_crtc_state *vc4_state = to_vc4_crtc_state(crtc->state); + u32 i; + u32 offset = SCALER5_DSPGAMMA_START + + vc4_state->assigned_channel * SCALER5_DSPGAMMA_CHAN_OFFSET; + + for (i = 0; i < SCALER5_DSPGAMMA_NUM_POINTS; i++, offset += 8) + vc5_hvs_write_gamma_entry(vc4, offset, &vc4_crtc->pwl_r[i]); + for (i = 0; i < SCALER5_DSPGAMMA_NUM_POINTS; i++, offset += 8) + vc5_hvs_write_gamma_entry(vc4, offset, &vc4_crtc->pwl_g[i]); + for (i = 0; i < SCALER5_DSPGAMMA_NUM_POINTS; i++, offset += 8) + vc5_hvs_write_gamma_entry(vc4, offset, &vc4_crtc->pwl_b[i]); + + if (vc4_state->assigned_channel == 2) { + /* Alpha only valid on channel 2 */ + for (i = 0; i < SCALER5_DSPGAMMA_NUM_POINTS; i++, offset += 8) + vc5_hvs_write_gamma_entry(vc4, offset, &vc4_crtc->pwl_a[i]); + } +} + +static void vc5_hvs_update_gamma_lut(struct drm_crtc *crtc) +{ + struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc); + struct drm_color_lut *lut = crtc->state->gamma_lut->data; + unsigned int step, i; + u32 start, end; + +#define VC5_HVS_UPDATE_GAMMA_ENTRY_FROM_LUT(pwl, chan) \ + start = drm_color_lut_extract(lut[i * step].chan, 12); \ + end = drm_color_lut_extract(lut[(i + 1) * step - 1].chan, 12); \ + \ + /* Negative gradients not permitted by the hardware, so \ + * flatten such points out. \ + */ \ + if (end < start) \ + end = start; \ + \ + /* Assume 12bit pipeline. \ + * X evenly spread over full range (12 bit). \ + * C as U12.4 format. \ + * Gradient as U4.8 format. \ + */ \ + vc4_crtc->pwl[i] = \ + VC5_HVS_SET_GAMMA_ENTRY(i << 8, start << 4, \ + ((end - start) << 4) / (step - 1)) + + /* HVS5 has a 16 point piecewise linear function for each colour + * channel (including alpha on channel 2) on each display channel. + * + * Currently take a crude subsample of the gamma LUT, but this could + * be improved to implement curve fitting. + */ + step = crtc->gamma_size / SCALER5_DSPGAMMA_NUM_POINTS; + for (i = 0; i < SCALER5_DSPGAMMA_NUM_POINTS; i++) { + VC5_HVS_UPDATE_GAMMA_ENTRY_FROM_LUT(pwl_r, red); + VC5_HVS_UPDATE_GAMMA_ENTRY_FROM_LUT(pwl_g, green); + VC5_HVS_UPDATE_GAMMA_ENTRY_FROM_LUT(pwl_b, blue); + } + + vc5_hvs_lut_load(crtc); +} + int vc4_hvs_get_fifo_from_output(struct drm_device *dev, unsigned int output) { struct vc4_dev *vc4 = to_vc4_dev(dev); @@ -328,15 +480,21 @@ static int vc4_hvs_init_channel(struct vc4_dev *vc4, struct drm_crtc *crtc, dispbkgndx &= ~SCALER_DISPBKGND_GAMMA; dispbkgndx &= ~SCALER_DISPBKGND_INTERLACE; + if (crtc->state->gamma_lut) + /* Enable gamma on if required */ + dispbkgndx |= SCALER_DISPBKGND_GAMMA; + HVS_WRITE(SCALER_DISPBKGNDX(chan), dispbkgndx | SCALER_DISPBKGND_AUTOHS | - ((!vc4->hvs->hvs5) ? SCALER_DISPBKGND_GAMMA : 0) | (interlace ? SCALER_DISPBKGND_INTERLACE : 0)); /* Reload the LUT, since the SRAMs would have been disabled if * all CRTCs had SCALER_DISPBKGND_GAMMA unset at once. */ - vc4_hvs_lut_load(crtc); + if (!vc4->hvs->hvs5) + vc4_hvs_lut_load(crtc); + else + vc5_hvs_lut_load(crtc); return 0; } @@ -365,6 +523,46 @@ void vc4_hvs_stop_channel(struct drm_device *dev, unsigned int chan) SCALER_DISPSTATX_EMPTY); } +static int vc4_hvs_gamma_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 drm_connector_state *conn_state; + struct drm_connector *connector; + struct drm_device *dev = crtc->dev; + struct vc4_dev *vc4 = to_vc4_dev(dev); + + if (!vc4->hvs->hvs5) + return 0; + + if (!crtc_state->color_mgmt_changed) + return 0; + + if (crtc_state->gamma_lut) { + unsigned int len = drm_color_lut_size(crtc_state->gamma_lut); + + if (len != crtc->gamma_size) { + DRM_DEBUG_KMS("Invalid LUT size; got %u, expected %u\n", + len, crtc->gamma_size); + return -EINVAL; + } + } + + connector = vc4_get_crtc_connector(crtc, crtc_state); + if (!connector) + return -EINVAL; + + if (!(connector->connector_type == DRM_MODE_CONNECTOR_HDMIA)) + return 0; + + conn_state = drm_atomic_get_connector_state(state, connector); + if (!conn_state) + return -EINVAL; + + crtc_state->mode_changed = true; + return 0; +} + int vc4_hvs_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); @@ -395,7 +593,7 @@ int vc4_hvs_atomic_check(struct drm_crtc *crtc, struct drm_atomic_state *state) if (ret) return ret; - return 0; + return vc4_hvs_gamma_check(crtc, state); } static void vc4_hvs_update_dlist(struct drm_crtc *crtc) @@ -404,17 +602,16 @@ static void vc4_hvs_update_dlist(struct drm_crtc *crtc) struct vc4_dev *vc4 = to_vc4_dev(dev); struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc); struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(crtc->state); + unsigned long flags; if (crtc->state->event) { - unsigned long flags; - crtc->state->event->pipe = drm_crtc_index(crtc); WARN_ON(drm_crtc_vblank_get(crtc) != 0); spin_lock_irqsave(&dev->event_lock, flags); - if (!vc4_state->feed_txp || vc4_state->txp_armed) { + if (!vc4_crtc->feeds_txp || vc4_state->txp_armed) { vc4_crtc->event = crtc->state->event; crtc->state->event = NULL; } @@ -427,6 +624,22 @@ static void vc4_hvs_update_dlist(struct drm_crtc *crtc) HVS_WRITE(SCALER_DISPLISTX(vc4_state->assigned_channel), vc4_state->mm.start); } + + spin_lock_irqsave(&vc4_crtc->irq_lock, flags); + vc4_crtc->current_dlist = vc4_state->mm.start; + spin_unlock_irqrestore(&vc4_crtc->irq_lock, flags); +} + +void vc4_hvs_atomic_begin(struct drm_crtc *crtc, + struct drm_atomic_state *state) +{ + struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc); + struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(crtc->state); + unsigned long flags; + + spin_lock_irqsave(&vc4_crtc->irq_lock, flags); + vc4_crtc->current_hvs_channel = vc4_state->assigned_channel; + spin_unlock_irqrestore(&vc4_crtc->irq_lock, flags); } void vc4_hvs_atomic_enable(struct drm_crtc *crtc, @@ -434,10 +647,9 @@ void vc4_hvs_atomic_enable(struct drm_crtc *crtc, { struct drm_device *dev = crtc->dev; struct vc4_dev *vc4 = to_vc4_dev(dev); - 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; + struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc); + bool oneshot = vc4_crtc->feeds_txp; vc4_hvs_update_dlist(crtc); vc4_hvs_init_channel(vc4, crtc, mode, oneshot); @@ -520,14 +732,25 @@ void vc4_hvs_atomic_flush(struct drm_crtc *crtc, u32 dispbkgndx = HVS_READ(SCALER_DISPBKGNDX(vc4_state->assigned_channel)); if (crtc->state->gamma_lut) { - vc4_hvs_update_gamma_lut(crtc); - dispbkgndx |= SCALER_DISPBKGND_GAMMA; + if (!vc4->hvs->hvs5) { + vc4_hvs_update_gamma_lut(crtc); + dispbkgndx |= SCALER_DISPBKGND_GAMMA; + } else { + vc5_hvs_update_gamma_lut(crtc); + } } else { /* Unsetting DISPBKGND_GAMMA skips the gamma lut step * in hardware, which is the same as a linear lut that * DRM expects us to use in absence of a user lut. + * + * Do NOT change state dynamically for hvs5 as it + * inserts a delay in the pipeline that will cause + * stalls if enabled/disabled whilst running. The other + * should already be disabling/enabling the pipeline + * when gamma changes. */ - dispbkgndx &= ~SCALER_DISPBKGND_GAMMA; + if (!vc4->hvs->hvs5) + dispbkgndx &= ~SCALER_DISPBKGND_GAMMA; } HVS_WRITE(SCALER_DISPBKGNDX(vc4_state->assigned_channel), dispbkgndx); } @@ -716,6 +939,9 @@ static int vc4_hvs_bind(struct device *dev, struct device *master, void *data) NULL); vc4_debugfs_add_file(drm, "hvs_dlists", vc4_hvs_debugfs_dlist, NULL); + if (hvs->hvs5) + vc4_debugfs_add_file(drm, "hvs_gamma", vc5_hvs_debugfs_gamma, + NULL); return 0; } diff --git a/drivers/gpu/drm/vc4/vc4_kms.c b/drivers/gpu/drm/vc4/vc4_kms.c index fb2465964d4631a89ad22aa812e364777883679f..422f2c211abd07727abd7dbf027f728f0598a826 100644 --- a/drivers/gpu/drm/vc4/vc4_kms.c +++ b/drivers/gpu/drm/vc4/vc4_kms.c @@ -222,6 +222,7 @@ static void vc4_hvs_pv_muxing_commit(struct vc4_dev *vc4, unsigned int i; for_each_new_crtc_in_state(state, crtc, crtc_state, i) { + struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc); struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(crtc_state); u32 dispctrl; u32 dsp3_mux; @@ -242,7 +243,7 @@ static void vc4_hvs_pv_muxing_commit(struct vc4_dev *vc4, * TXP IP, and we need to disable the FIFO2 -> pixelvalve1 * route. */ - if (vc4_state->feed_txp) + if (vc4_crtc->feeds_txp) dsp3_mux = VC4_SET_FIELD(3, SCALER_DISPCTRL_DSP3_MUX); else dsp3_mux = VC4_SET_FIELD(2, SCALER_DISPCTRL_DSP3_MUX); diff --git a/drivers/gpu/drm/vc4/vc4_plane.c b/drivers/gpu/drm/vc4/vc4_plane.c index 7947cf47b6e13466cc5a4e381f8c164cfc1ffa30..074bdfdb184c07901cc31dc7b7659657de0944f8 100644 --- a/drivers/gpu/drm/vc4/vc4_plane.c +++ b/drivers/gpu/drm/vc4/vc4_plane.c @@ -667,6 +667,48 @@ static const u32 colorspace_coeffs[2][DRM_COLOR_ENCODING_MAX][3] = { } }; +static u32 vc4_hvs4_get_alpha_blend_mode(struct drm_plane_state *state) +{ + if (!state->fb->format->has_alpha) + return VC4_SET_FIELD(SCALER_POS2_ALPHA_MODE_FIXED, + SCALER_POS2_ALPHA_MODE); + + switch (state->pixel_blend_mode) { + case DRM_MODE_BLEND_PIXEL_NONE: + return VC4_SET_FIELD(SCALER_POS2_ALPHA_MODE_FIXED, + SCALER_POS2_ALPHA_MODE); + default: + case DRM_MODE_BLEND_PREMULTI: + return VC4_SET_FIELD(SCALER_POS2_ALPHA_MODE_PIPELINE, + SCALER_POS2_ALPHA_MODE) | + SCALER_POS2_ALPHA_PREMULT; + case DRM_MODE_BLEND_COVERAGE: + return VC4_SET_FIELD(SCALER_POS2_ALPHA_MODE_PIPELINE, + SCALER_POS2_ALPHA_MODE); + } +} + +static u32 vc4_hvs5_get_alpha_blend_mode(struct drm_plane_state *state) +{ + if (!state->fb->format->has_alpha) + return VC4_SET_FIELD(SCALER5_CTL2_ALPHA_MODE_FIXED, + SCALER5_CTL2_ALPHA_MODE); + + switch (state->pixel_blend_mode) { + case DRM_MODE_BLEND_PIXEL_NONE: + return VC4_SET_FIELD(SCALER5_CTL2_ALPHA_MODE_FIXED, + SCALER5_CTL2_ALPHA_MODE); + default: + case DRM_MODE_BLEND_PREMULTI: + return VC4_SET_FIELD(SCALER5_CTL2_ALPHA_MODE_PIPELINE, + SCALER5_CTL2_ALPHA_MODE) | + SCALER5_CTL2_ALPHA_PREMULT; + case DRM_MODE_BLEND_COVERAGE: + return VC4_SET_FIELD(SCALER5_CTL2_ALPHA_MODE_PIPELINE, + SCALER5_CTL2_ALPHA_MODE); + } +} + /* Writes out a full display list for an active plane to the plane's * private dlist state. */ @@ -928,13 +970,8 @@ static int vc4_plane_mode_set(struct drm_plane *plane, /* Position Word 2: Source Image Size, Alpha */ vc4_state->pos2_offset = vc4_state->dlist_count; vc4_dlist_write(vc4_state, - VC4_SET_FIELD(fb->format->has_alpha ? - SCALER_POS2_ALPHA_MODE_PIPELINE : - SCALER_POS2_ALPHA_MODE_FIXED, - SCALER_POS2_ALPHA_MODE) | (mix_plane_alpha ? SCALER_POS2_ALPHA_MIX : 0) | - (fb->format->has_alpha ? - SCALER_POS2_ALPHA_PREMULT : 0) | + vc4_hvs4_get_alpha_blend_mode(state) | VC4_SET_FIELD(vc4_state->src_w[0], SCALER_POS2_WIDTH) | VC4_SET_FIELD(vc4_state->src_h[0], @@ -979,14 +1016,9 @@ static int vc4_plane_mode_set(struct drm_plane *plane, vc4_dlist_write(vc4_state, VC4_SET_FIELD(state->alpha >> 4, SCALER5_CTL2_ALPHA) | - (fb->format->has_alpha ? - SCALER5_CTL2_ALPHA_PREMULT : 0) | + vc4_hvs5_get_alpha_blend_mode(state) | (mix_plane_alpha ? - SCALER5_CTL2_ALPHA_MIX : 0) | - VC4_SET_FIELD(fb->format->has_alpha ? - SCALER5_CTL2_ALPHA_MODE_PIPELINE : - SCALER5_CTL2_ALPHA_MODE_FIXED, - SCALER5_CTL2_ALPHA_MODE) + SCALER5_CTL2_ALPHA_MIX : 0) ); /* Position Word 1: Scaled Image Dimensions. */ @@ -1470,6 +1502,10 @@ struct drm_plane *vc4_plane_init(struct drm_device *dev, drm_plane_helper_add(plane, &vc4_plane_helper_funcs); drm_plane_create_alpha_property(plane); + drm_plane_create_blend_mode_property(plane, + BIT(DRM_MODE_BLEND_PIXEL_NONE) | + BIT(DRM_MODE_BLEND_PREMULTI) | + BIT(DRM_MODE_BLEND_COVERAGE)); drm_plane_create_rotation_property(plane, DRM_MODE_ROTATE_0, DRM_MODE_ROTATE_0 | DRM_MODE_ROTATE_180 | diff --git a/drivers/gpu/drm/vc4/vc4_regs.h b/drivers/gpu/drm/vc4/vc4_regs.h index 7538b84a6dcaa8a14d93c4d5e732a13b52b88d94..f40dd93f2cdd9066c310bebd56aee870f6123b43 100644 --- a/drivers/gpu/drm/vc4/vc4_regs.h +++ b/drivers/gpu/drm/vc4/vc4_regs.h @@ -491,6 +491,28 @@ #define SCALER_DLIST_START 0x00002000 #define SCALER_DLIST_SIZE 0x00004000 +/* Gamma PWL for each channel. 16 points for each of 4 colour channels (alpha + * only on channel 2). 8 bytes per entry, offsets first, then gradient: + * Y = GRAD * X + C + * + * Values for X and C are left justified, and vary depending on the width of + * the HVS channel: + * 8-bit pipeline: X uses [31:24], C is U8.8 format, and GRAD is U4.8. + * 12-bit pipeline: X uses [31:20], C is U12.4 format, and GRAD is U4.8. + * + * The 3 HVS channels start at 0x400 offsets (ie chan 1 starts at 0x2400, and + * chan 2 at 0x2800). + */ +#define SCALER5_DSPGAMMA_NUM_POINTS 16 +#define SCALER5_DSPGAMMA_START 0x00002000 +#define SCALER5_DSPGAMMA_CHAN_OFFSET 0x400 +# define SCALER5_DSPGAMMA_OFF_X_MASK VC4_MASK(31, 20) +# define SCALER5_DSPGAMMA_OFF_X_SHIFT 20 +# define SCALER5_DSPGAMMA_OFF_C_MASK VC4_MASK(15, 0) +# define SCALER5_DSPGAMMA_OFF_C_SHIFT 0 +# define SCALER5_DSPGAMMA_GRAD_MASK VC4_MASK(11, 0) +# define SCALER5_DSPGAMMA_GRAD_SHIFT 0 + #define SCALER5_DLIST_START 0x00004000 # define VC4_HDMI_SW_RESET_FORMAT_DETECT BIT(1) @@ -774,8 +796,27 @@ enum { # define VC4_HD_CSC_CTL_RGB2YCC BIT(1) # define VC4_HD_CSC_CTL_ENABLE BIT(0) +# define VC5_MT_CP_CSC_CTL_USE_444_TO_422 BIT(6) +# define VC5_MT_CP_CSC_CTL_FILTER_MODE_444_TO_422_MASK \ + VC4_MASK(5, 4) +# define VC5_MT_CP_CSC_CTL_FILTER_MODE_444_TO_422_STANDARD \ + 3 +# define VC5_MT_CP_CSC_CTL_USE_RNG_SUPPRESSION BIT(3) +# define VC5_MT_CP_CSC_CTL_ENABLE BIT(2) +# define VC5_MT_CP_CSC_CTL_MODE_MASK VC4_MASK(1, 0) + +# define VC5_MT_CP_CHANNEL_CTL_OUTPUT_REMAP_MASK \ + VC4_MASK(7, 6) +# define VC5_MT_CP_CHANNEL_CTL_OUTPUT_REMAP_LEGACY_STYLE \ + 2 + # define VC4_DVP_HT_CLOCK_STOP_PIXEL BIT(1) +# define VC5_DVP_HT_VEC_INTERFACE_CFG_SEL_422_MASK \ + VC4_MASK(3, 2) +# define VC5_DVP_HT_VEC_INTERFACE_CFG_SEL_422_FORMAT_422_LEGACY \ + 2 + /* HVS display list information. */ #define HVS_BOOTLOADER_DLIST_END 32 diff --git a/drivers/gpu/drm/vc4/vc4_txp.c b/drivers/gpu/drm/vc4/vc4_txp.c index 2fc7f4b5fa0983b472c045ac861fb05e08308c1d..9809ca3e294516c70fac713902b8e169852eaabf 100644 --- a/drivers/gpu/drm/vc4/vc4_txp.c +++ b/drivers/gpu/drm/vc4/vc4_txp.c @@ -391,7 +391,6 @@ static int vc4_txp_atomic_check(struct drm_crtc *crtc, { 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); @@ -399,7 +398,6 @@ static int vc4_txp_atomic_check(struct drm_crtc *crtc, return ret; crtc_state->no_vblank = true; - vc4_state->feed_txp = true; return 0; } @@ -437,6 +435,7 @@ static void vc4_txp_atomic_disable(struct drm_crtc *crtc, static const struct drm_crtc_helper_funcs vc4_txp_crtc_helper_funcs = { .atomic_check = vc4_txp_atomic_check, + .atomic_begin = vc4_hvs_atomic_begin, .atomic_flush = vc4_hvs_atomic_flush, .atomic_enable = vc4_txp_atomic_enable, .atomic_disable = vc4_txp_atomic_disable, @@ -482,6 +481,7 @@ static int vc4_txp_bind(struct device *dev, struct device *master, void *data) vc4_crtc->pdev = pdev; vc4_crtc->data = &vc4_txp_crtc_data; + vc4_crtc->feeds_txp = true; txp->pdev = pdev; diff --git a/drivers/gpu/drm/vc4/vc4_vec.c b/drivers/gpu/drm/vc4/vc4_vec.c index 596b59ff6c9adbd500091d4718a36d735561d5cb..255e5c6c48e043c7943a3127b17fab33e2e3c3cf 100644 --- a/drivers/gpu/drm/vc4/vc4_vec.c +++ b/drivers/gpu/drm/vc4/vc4_vec.c @@ -251,7 +251,8 @@ enum vc4_vec_tv_mode_id { }; struct vc4_vec_tv_mode { - const struct drm_display_mode *mode; + const struct drm_display_mode *interlaced_mode; + const struct drm_display_mode *progressive_mode; u32 config0; u32 config1; u32 custom_freq; @@ -285,61 +286,81 @@ static const struct debugfs_reg32 vec_regs[] = { }; static const struct drm_display_mode drm_mode_480i = { - DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 13500, + DRM_MODE("720x480i", DRM_MODE_TYPE_DRIVER, 13500, 720, 720 + 14, 720 + 14 + 64, 720 + 14 + 64 + 60, 0, 480, 480 + 7, 480 + 7 + 6, 525, 0, DRM_MODE_FLAG_INTERLACE) }; +static const struct drm_display_mode drm_mode_240p = { + DRM_MODE("720x240", DRM_MODE_TYPE_DRIVER, 13500, + 720, 720 + 14, 720 + 14 + 64, 720 + 14 + 64 + 60, 0, + 240, 240 + 3, 240 + 3 + 3, 262, 0, 0) +}; + static const struct drm_display_mode drm_mode_576i = { - DRM_MODE("720x576", DRM_MODE_TYPE_DRIVER, 13500, + DRM_MODE("720x576i", DRM_MODE_TYPE_DRIVER, 13500, 720, 720 + 20, 720 + 20 + 64, 720 + 20 + 64 + 60, 0, 576, 576 + 4, 576 + 4 + 6, 625, 0, DRM_MODE_FLAG_INTERLACE) }; +static const struct drm_display_mode drm_mode_288p = { + DRM_MODE("720x288", DRM_MODE_TYPE_DRIVER, 13500, + 720, 720 + 20, 720 + 20 + 64, 720 + 20 + 64 + 60, 0, + 288, 288 + 2, 288 + 2 + 3, 312, 0, 0) +}; + static const struct vc4_vec_tv_mode vc4_vec_tv_modes[] = { [VC4_VEC_TV_MODE_NTSC] = { - .mode = &drm_mode_480i, + .interlaced_mode = &drm_mode_480i, + .progressive_mode = &drm_mode_240p, .config0 = VEC_CONFIG0_NTSC_STD | VEC_CONFIG0_PDEN, .config1 = VEC_CONFIG1_C_CVBS_CVBS, }, [VC4_VEC_TV_MODE_NTSC_J] = { - .mode = &drm_mode_480i, + .interlaced_mode = &drm_mode_480i, + .progressive_mode = &drm_mode_240p, .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, + .interlaced_mode = &drm_mode_480i, + .progressive_mode = &drm_mode_240p, .config0 = VEC_CONFIG0_NTSC_STD, .config1 = VEC_CONFIG1_C_CVBS_CVBS | VEC_CONFIG1_CUSTOM_FREQ, .custom_freq = 0x2a098acb, }, [VC4_VEC_TV_MODE_PAL] = { - .mode = &drm_mode_576i, + .interlaced_mode = &drm_mode_576i, + .progressive_mode = &drm_mode_288p, .config0 = VEC_CONFIG0_PAL_BDGHI_STD, .config1 = VEC_CONFIG1_C_CVBS_CVBS, }, [VC4_VEC_TV_MODE_PAL_M] = { - .mode = &drm_mode_480i, + .interlaced_mode = &drm_mode_480i, + .progressive_mode = &drm_mode_240p, .config0 = VEC_CONFIG0_PAL_M_STD, .config1 = VEC_CONFIG1_C_CVBS_CVBS, }, [VC4_VEC_TV_MODE_PAL_N] = { - .mode = &drm_mode_576i, + .interlaced_mode = &drm_mode_576i, + .progressive_mode = &drm_mode_288p, .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, + .interlaced_mode = &drm_mode_480i, + .progressive_mode = &drm_mode_240p, .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, + .interlaced_mode = &drm_mode_576i, + .progressive_mode = &drm_mode_288p, .config0 = VEC_CONFIG0_SECAM_STD, .config1 = VEC_CONFIG1_C_CVBS_CVBS, .custom_freq = 0x29c71c72, @@ -399,16 +420,32 @@ static void vc4_vec_connector_destroy(struct drm_connector *connector) static int vc4_vec_connector_get_modes(struct drm_connector *connector) { struct drm_connector_state *state = connector->state; - struct drm_display_mode *mode; - - mode = drm_mode_duplicate(connector->dev, - vc4_vec_tv_modes[state->tv.mode].mode); - if (!mode) { + struct drm_display_mode *interlaced_mode, *progressive_mode; + + interlaced_mode = + drm_mode_duplicate(connector->dev, + vc4_vec_tv_modes[state->tv.mode].interlaced_mode); + progressive_mode = + drm_mode_duplicate(connector->dev, + vc4_vec_tv_modes[state->tv.mode].progressive_mode); + if (!interlaced_mode || !progressive_mode) { DRM_ERROR("Failed to create a new display mode\n"); + drm_mode_destroy(connector->dev, interlaced_mode); + drm_mode_destroy(connector->dev, progressive_mode); return -ENOMEM; } - drm_mode_probed_add(connector, mode); + if (connector->cmdline_mode.specified && + connector->cmdline_mode.refresh_specified && + !connector->cmdline_mode.interlace) + /* progressive mode set at boot, let's make it preferred */ + progressive_mode->type |= DRM_MODE_TYPE_PREFERRED; + else + /* otherwise, interlaced mode is preferred */ + interlaced_mode->type |= DRM_MODE_TYPE_PREFERRED; + + drm_mode_probed_add(connector, interlaced_mode); + drm_mode_probed_add(connector, progressive_mode); return 1; } @@ -429,18 +466,11 @@ static int vc4_vec_connector_atomic_check(struct drm_connector *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) { + if (new_state->crtc && old_state->tv.mode != new_state->tv.mode) { 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; + crtc_state->mode_changed = true; } return 0; @@ -565,7 +595,10 @@ static void vc4_vec_encoder_enable(struct drm_encoder *encoder) VEC_WRITE(VEC_CLMP0_START, 0xac); VEC_WRITE(VEC_CLMP0_END, 0xec); VEC_WRITE(VEC_CONFIG2, - VEC_CONFIG2_UV_DIG_DIS | VEC_CONFIG2_RGB_DIG_DIS); + VEC_CONFIG2_UV_DIG_DIS | + VEC_CONFIG2_RGB_DIG_DIS | + ((encoder->crtc->state->adjusted_mode.flags & + DRM_MODE_FLAG_INTERLACE) ? 0 : VEC_CONFIG2_PROG_SCAN)); VEC_WRITE(VEC_CONFIG3, VEC_CONFIG3_HORIZ_LEN_STD); VEC_WRITE(VEC_DAC_CONFIG, vec->variant->dac_config); @@ -588,17 +621,88 @@ static void vc4_vec_encoder_enable(struct drm_encoder *encoder) } -static bool vc4_vec_encoder_mode_fixup(struct drm_encoder *encoder, - const struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) +static int vc4_vec_encoder_atomic_check(struct drm_encoder *encoder, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state) { - return true; + const struct drm_display_mode *reference_mode = + vc4_vec_tv_modes[conn_state->tv.mode].interlaced_mode; + + if (crtc_state->adjusted_mode.crtc_clock != reference_mode->clock || + crtc_state->adjusted_mode.crtc_htotal != reference_mode->htotal || + crtc_state->adjusted_mode.crtc_hdisplay % 4 != 0 || + crtc_state->adjusted_mode.crtc_hsync_end - + crtc_state->adjusted_mode.crtc_hsync_start < 1) + return -EINVAL; + + switch (reference_mode->vtotal) { + case 525: + if (crtc_state->adjusted_mode.crtc_vdisplay < 1 || + crtc_state->adjusted_mode.crtc_vdisplay > 253 || + crtc_state->adjusted_mode.crtc_vsync_start - + crtc_state->adjusted_mode.crtc_vdisplay < 1 || + crtc_state->adjusted_mode.crtc_vsync_end - + crtc_state->adjusted_mode.crtc_vsync_start != 3 || + crtc_state->adjusted_mode.crtc_vtotal - + crtc_state->adjusted_mode.crtc_vsync_end < 4 || + crtc_state->adjusted_mode.crtc_vtotal > 262) + return -EINVAL; + + if ((crtc_state->adjusted_mode.flags & + DRM_MODE_FLAG_INTERLACE) && + (crtc_state->adjusted_mode.vdisplay % 2 != 0 || + crtc_state->adjusted_mode.vsync_start % 2 != 1 || + crtc_state->adjusted_mode.vsync_end % 2 != 1 || + crtc_state->adjusted_mode.vtotal % 2 != 1)) + return -EINVAL; + + /* progressive mode is hard-wired to 262 total lines */ + if (!(crtc_state->adjusted_mode.flags & + DRM_MODE_FLAG_INTERLACE) && + crtc_state->adjusted_mode.crtc_vtotal != 262) + return -EINVAL; + + break; + + case 625: + if (crtc_state->adjusted_mode.crtc_vdisplay < 1 || + crtc_state->adjusted_mode.crtc_vdisplay > 305 || + crtc_state->adjusted_mode.crtc_vsync_start - + crtc_state->adjusted_mode.crtc_vdisplay < 1 || + crtc_state->adjusted_mode.crtc_vsync_end - + crtc_state->adjusted_mode.crtc_vsync_start != 3 || + crtc_state->adjusted_mode.crtc_vtotal - + crtc_state->adjusted_mode.crtc_vsync_end < 2 || + crtc_state->adjusted_mode.crtc_vtotal > 312) + return -EINVAL; + + if ((crtc_state->adjusted_mode.flags & + DRM_MODE_FLAG_INTERLACE) && + (crtc_state->adjusted_mode.vdisplay % 2 != 0 || + crtc_state->adjusted_mode.vsync_start % 2 != 0 || + crtc_state->adjusted_mode.vsync_end % 2 != 0 || + crtc_state->adjusted_mode.vtotal % 2 != 1)) + return -EINVAL; + + /* progressive mode is hard-wired to 312 total lines */ + if (!(crtc_state->adjusted_mode.flags & + DRM_MODE_FLAG_INTERLACE) && + crtc_state->adjusted_mode.crtc_vtotal != 312) + return -EINVAL; + + break; + + default: + 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, }; static const struct vc4_vec_variant bcm2835_vec_variant = { diff --git a/drivers/input/touchscreen/edt-ft5x06.c b/drivers/input/touchscreen/edt-ft5x06.c index d4060bc89ce9cc677ebf705220bbe1d262298b9e..9be7a6aba4097dc1ee8c155a7fe73edf0abf6f55 100644 --- a/drivers/input/touchscreen/edt-ft5x06.c +++ b/drivers/input/touchscreen/edt-ft5x06.c @@ -123,6 +123,7 @@ struct edt_ft5x06_ts_data { int offset_y; int report_rate; int max_support_points; + unsigned int known_ids; char name[EDT_NAME_LEN]; @@ -197,6 +198,10 @@ static irqreturn_t edt_ft5x06_ts_isr(int irq, void *dev_id) int i, type, x, y, id; int offset, tplen, datalen, crclen; int error; + unsigned int active_ids = 0, known_ids = tsdata->known_ids; + long released_ids; + int b = 0; + unsigned int num_points; switch (tsdata->version) { case EDT_M06: @@ -244,9 +249,15 @@ static irqreturn_t edt_ft5x06_ts_isr(int irq, void *dev_id) if (!edt_ft5x06_ts_check_crc(tsdata, rdbuf, datalen)) goto out; + num_points = tsdata->max_support_points; + } else { + /* Register 2 is TD_STATUS, containing the number of touch + * points. + */ + num_points = min(rdbuf[2] & 0xf, tsdata->max_support_points); } - for (i = 0; i < tsdata->max_support_points; i++) { + for (i = 0; i < num_points; i++) { u8 *buf = &rdbuf[i * tplen + offset]; type = buf[0] >> 6; @@ -268,10 +279,25 @@ static irqreturn_t edt_ft5x06_ts_isr(int irq, void *dev_id) input_mt_slot(tsdata->input, id); if (input_mt_report_slot_state(tsdata->input, MT_TOOL_FINGER, - type != TOUCH_EVENT_UP)) + type != TOUCH_EVENT_UP)) { touchscreen_report_pos(tsdata->input, &tsdata->prop, x, y, true); + active_ids |= BIT(id); + } else { + known_ids &= ~BIT(id); + } + } + + /* One issue with the device is the TOUCH_UP message is not always + * returned. Instead track which ids we know about and report when they + * are no longer updated + */ + released_ids = known_ids & ~active_ids; + for_each_set_bit_from(b, &released_ids, tsdata->max_support_points) { + input_mt_slot(tsdata->input, b); + input_mt_report_slot_inactive(tsdata->input); } + tsdata->known_ids = active_ids; input_mt_report_pointer_emulation(tsdata->input, true); input_sync(tsdata->input); diff --git a/drivers/media/i2c/imx219.c b/drivers/media/i2c/imx219.c index 79faa8cce94eac4e146eee7b4e9c158e463f9fd0..7f0fdd6fd7bcd11569de81b29571e6e5058c5ed6 100644 --- a/drivers/media/i2c/imx219.c +++ b/drivers/media/i2c/imx219.c @@ -55,7 +55,7 @@ #define IMX219_VTS_30FPS_640x480 0x06e3 #define IMX219_VTS_MAX 0xffff -#define IMX219_VBLANK_MIN 4 +#define IMX219_VBLANK_MIN 32 /*Frame Length Line*/ #define IMX219_FLL_MIN 0x08a6 @@ -681,7 +681,7 @@ static void imx219_set_default_format(struct imx219 *imx219) fmt = &imx219->fmt; fmt->code = MEDIA_BUS_FMT_SRGGB10_1X10; - fmt->colorspace = V4L2_COLORSPACE_SRGB; + fmt->colorspace = V4L2_COLORSPACE_RAW; fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->colorspace); fmt->quantization = V4L2_MAP_QUANTIZATION_DEFAULT(true, fmt->colorspace, @@ -877,7 +877,7 @@ static int imx219_enum_frame_size(struct v4l2_subdev *sd, static void imx219_reset_colorspace(struct v4l2_mbus_framefmt *fmt) { - fmt->colorspace = V4L2_COLORSPACE_SRGB; + fmt->colorspace = V4L2_COLORSPACE_RAW; fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->colorspace); fmt->quantization = V4L2_MAP_QUANTIZATION_DEFAULT(true, fmt->colorspace, diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c index a26106c41cc679b6a9b69b44f7c7be34a402b034..def912308daa4ed5b17ce560e9b0a898cff9c9e1 100644 --- a/drivers/media/i2c/imx290.c +++ b/drivers/media/i2c/imx290.c @@ -885,7 +885,7 @@ static int imx290_set_fmt(struct v4l2_subdev *sd, fmt->format.code = imx290->formats[i].code; fmt->format.field = V4L2_FIELD_NONE; - fmt->format.colorspace = V4L2_COLORSPACE_SRGB; + fmt->format.colorspace = V4L2_COLORSPACE_RAW; fmt->format.ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->format.colorspace); fmt->format.quantization = diff --git a/drivers/media/i2c/imx477.c b/drivers/media/i2c/imx477.c index 05cb530d331eb3b51f6e9cfe80fc83297a0df78d..b2d4fb54cb1ed037af3dd3bba14fa005f3121d71 100644 --- a/drivers/media/i2c/imx477.c +++ b/drivers/media/i2c/imx477.c @@ -25,6 +25,10 @@ static int dpc_enable = 1; module_param(dpc_enable, int, 0644); MODULE_PARM_DESC(dpc_enable, "Enable on-sensor DPC"); +static int trigger_mode; +module_param(trigger_mode, int, 0644); +MODULE_PARM_DESC(trigger_mode, "Set vsync trigger mode: 1=source, 2=sink"); + #define IMX477_REG_VALUE_08BIT 1 #define IMX477_REG_VALUE_16BIT 2 @@ -98,6 +102,12 @@ MODULE_PARM_DESC(dpc_enable, "Enable on-sensor DPC"); #define IMX477_TEST_PATTERN_B_DEFAULT 0 #define IMX477_TEST_PATTERN_GB_DEFAULT 0 +/* Trigger mode */ +#define IMX477_REG_MC_MODE 0x3f0b +#define IMX477_REG_MS_SEL 0x3041 +#define IMX477_REG_XVS_IO_CTRL 0x3040 +#define IMX477_REG_EXTOUT_EN 0x4b81 + /* Embedded metadata stream structure */ #define IMX477_EMBEDDED_LINE_WIDTH 16384 #define IMX477_NUM_EMBEDDED_LINES 1 @@ -1471,7 +1481,7 @@ static int imx477_enum_frame_size(struct v4l2_subdev *sd, static void imx477_reset_colorspace(struct v4l2_mbus_framefmt *fmt) { - fmt->colorspace = V4L2_COLORSPACE_SRGB; + fmt->colorspace = V4L2_COLORSPACE_RAW; fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->colorspace); fmt->quantization = V4L2_MAP_QUANTIZATION_DEFAULT(true, fmt->colorspace, @@ -1719,6 +1729,21 @@ static int imx477_start_streaming(struct imx477 *imx477) imx477_write_reg(imx477, 0x0b05, IMX477_REG_VALUE_08BIT, !!dpc_enable); imx477_write_reg(imx477, 0x0b06, IMX477_REG_VALUE_08BIT, !!dpc_enable); + /* Set vsync trigger mode */ + if (trigger_mode != 0) { + /* trigger_mode == 1 for source, 2 for sink */ + const u32 val = (trigger_mode == 1) ? 1 : 0; + + imx477_write_reg(imx477, IMX477_REG_MC_MODE, + IMX477_REG_VALUE_08BIT, 1); + imx477_write_reg(imx477, IMX477_REG_MS_SEL, + IMX477_REG_VALUE_08BIT, val); + imx477_write_reg(imx477, IMX477_REG_XVS_IO_CTRL, + IMX477_REG_VALUE_08BIT, val); + imx477_write_reg(imx477, IMX477_REG_EXTOUT_EN, + IMX477_REG_VALUE_08BIT, val); + } + /* Apply customized values from user */ ret = __v4l2_ctrl_handler_setup(imx477->sd.ctrl_handler); if (ret) diff --git a/drivers/media/i2c/imx519.c b/drivers/media/i2c/imx519.c index 4e98704a683418a53ee23463b1cf00ad986671d1..675b4a94e0655a17225e042c3c8b1d1e9d840a28 100644 --- a/drivers/media/i2c/imx519.c +++ b/drivers/media/i2c/imx519.c @@ -1317,7 +1317,7 @@ static int imx519_enum_frame_size(struct v4l2_subdev *sd, static void imx519_reset_colorspace(struct v4l2_mbus_framefmt *fmt) { - fmt->colorspace = V4L2_COLORSPACE_SRGB; + fmt->colorspace = V4L2_COLORSPACE_RAW; fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->colorspace); fmt->quantization = V4L2_MAP_QUANTIZATION_DEFAULT(true, fmt->colorspace, diff --git a/drivers/media/i2c/ov5647.c b/drivers/media/i2c/ov5647.c index 98a5329d68fbb199db9a8afb8baecd1b87926e0a..3b7a15852c5d2ce4730d903a0e1ae596e811ef79 100644 --- a/drivers/media/i2c/ov5647.c +++ b/drivers/media/i2c/ov5647.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -64,6 +65,8 @@ #define OV5647_REG_GAIN_LO 0x350B #define OV5647_REG_VTS_HI 0x380e #define OV5647_REG_VTS_LO 0x380f +#define OV5647_REG_VFLIP 0x3820 +#define OV5647_REG_HFLIP 0x3821 #define OV5647_REG_FRAME_OFF_NUMBER 0x4202 #define OV5647_REG_MIPI_CTRL00 0x4800 #define OV5647_REG_MIPI_CTRL14 0x4814 @@ -90,6 +93,15 @@ #define OV5647_EXPOSURE_DEFAULT 1000 #define OV5647_EXPOSURE_MAX 65535 +/* regulator supplies */ +static const char * const ov5647_supply_names[] = { + "avdd", /* Analog power */ + "dovdd", /* Digital I/O power */ + "dvdd", /* Digital core power */ +}; + +#define OV5647_NUM_SUPPLIES ARRAY_SIZE(ov5647_supply_names) + struct regval_list { u16 addr; u8 data; @@ -118,12 +130,15 @@ struct ov5647 { int power_count; struct clk *xclk; struct gpio_desc *pwdn; + struct regulator_bulk_data supplies[OV5647_NUM_SUPPLIES]; 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; + struct v4l2_ctrl *hflip; + struct v4l2_ctrl *vflip; bool write_mode_regs; }; @@ -152,7 +167,7 @@ static struct regval_list ov5647_640x480_8bit[] = { {0x3036, 0x46}, {0x303c, 0x11}, {0x3106, 0xf5}, - {0x3821, 0x07}, + {0x3821, 0x01}, {0x3820, 0x41}, {0x3827, 0xec}, {0x370c, 0x0f}, @@ -240,7 +255,7 @@ static struct regval_list ov5647_2592x1944_10bit[] = { {0x3036, 0x69}, {0x303c, 0x11}, {0x3106, 0xf5}, - {0x3821, 0x06}, + {0x3821, 0x00}, {0x3820, 0x00}, {0x3827, 0xec}, {0x370c, 0x03}, @@ -329,7 +344,7 @@ static struct regval_list ov5647_1080p30_10bit[] = { {0x3036, 0x62}, {0x303c, 0x11}, {0x3106, 0xf5}, - {0x3821, 0x06}, + {0x3821, 0x00}, {0x3820, 0x00}, {0x3827, 0xec}, {0x370c, 0x03}, @@ -493,7 +508,7 @@ static struct regval_list ov5647_2x2binned_10bit[] = { {0x4800, 0x24}, {0x3503, 0x03}, {0x3820, 0x41}, - {0x3821, 0x07}, + {0x3821, 0x01}, {0x350A, 0x00}, {0x350B, 0x10}, {0x3500, 0x00}, @@ -509,7 +524,7 @@ static struct regval_list ov5647_640x480_10bit[] = { {0x3035, 0x11}, {0x3036, 0x46}, {0x303c, 0x11}, - {0x3821, 0x07}, + {0x3821, 0x01}, {0x3820, 0x41}, {0x370c, 0x03}, {0x3612, 0x59}, @@ -601,7 +616,7 @@ static struct ov5647_mode supported_modes_8bit[] = { { .format = { .code = MEDIA_BUS_FMT_SBGGR8_1X8, - .colorspace = V4L2_COLORSPACE_SRGB, + .colorspace = V4L2_COLORSPACE_RAW, .field = V4L2_FIELD_NONE, .width = 640, .height = 480 @@ -627,7 +642,7 @@ static struct ov5647_mode supported_modes_10bit[] = { { .format = { .code = MEDIA_BUS_FMT_SBGGR10_1X10, - .colorspace = V4L2_COLORSPACE_SRGB, + .colorspace = V4L2_COLORSPACE_RAW, .field = V4L2_FIELD_NONE, .width = 2592, .height = 1944 @@ -651,7 +666,7 @@ static struct ov5647_mode supported_modes_10bit[] = { { .format = { .code = MEDIA_BUS_FMT_SBGGR10_1X10, - .colorspace = V4L2_COLORSPACE_SRGB, + .colorspace = V4L2_COLORSPACE_RAW, .field = V4L2_FIELD_NONE, .width = 1920, .height = 1080 @@ -674,7 +689,7 @@ static struct ov5647_mode supported_modes_10bit[] = { { .format = { .code = MEDIA_BUS_FMT_SBGGR10_1X10, - .colorspace = V4L2_COLORSPACE_SRGB, + .colorspace = V4L2_COLORSPACE_RAW, .field = V4L2_FIELD_NONE, .width = 1296, .height = 972 @@ -698,7 +713,7 @@ static struct ov5647_mode supported_modes_10bit[] = { { .format = { .code = MEDIA_BUS_FMT_SBGGR10_1X10, - .colorspace = V4L2_COLORSPACE_SRGB, + .colorspace = V4L2_COLORSPACE_RAW, .field = V4L2_FIELD_NONE, .width = 640, .height = 480 @@ -945,6 +960,13 @@ static int ov5647_sensor_power(struct v4l2_subdev *sd, int on) if (on && !ov5647->power_count) { dev_dbg(&client->dev, "OV5647 power on\n"); + ret = regulator_bulk_enable(OV5647_NUM_SUPPLIES, + ov5647->supplies); + if (ret < 0) { + dev_err(&client->dev, "Failed to enable regulators\n"); + goto out; + } + if (ov5647->pwdn) { gpiod_set_value_cansleep(ov5647->pwdn, 0); msleep(PWDN_ACTIVE_DELAY_MS); @@ -952,6 +974,8 @@ static int ov5647_sensor_power(struct v4l2_subdev *sd, int on) ret = clk_prepare_enable(ov5647->xclk); if (ret < 0) { + regulator_bulk_disable(OV5647_NUM_SUPPLIES, + ov5647->supplies); dev_err(&client->dev, "clk prepare enable failed\n"); goto out; } @@ -960,6 +984,8 @@ static int ov5647_sensor_power(struct v4l2_subdev *sd, int on) ARRAY_SIZE(sensor_oe_enable_regs)); if (ret < 0) { clk_disable_unprepare(ov5647->xclk); + regulator_bulk_disable(OV5647_NUM_SUPPLIES, + ov5647->supplies); dev_err(&client->dev, "write sensor_oe_enable_regs error\n"); goto out; @@ -971,6 +997,8 @@ static int ov5647_sensor_power(struct v4l2_subdev *sd, int on) ret = ov5647_stream_off(sd); if (ret < 0) { clk_disable_unprepare(ov5647->xclk); + regulator_bulk_disable(OV5647_NUM_SUPPLIES, + ov5647->supplies); dev_err(&client->dev, "Camera not available, check Power\n"); goto out; @@ -995,6 +1023,8 @@ static int ov5647_sensor_power(struct v4l2_subdev *sd, int on) clk_disable_unprepare(ov5647->xclk); gpiod_set_value_cansleep(ov5647->pwdn, 1); + + regulator_bulk_disable(OV5647_NUM_SUPPLIES, ov5647->supplies); } /* Update the power count. */ @@ -1116,18 +1146,45 @@ static const struct v4l2_subdev_video_ops ov5647_subdev_video_ops = { .s_stream = ov5647_s_stream, }; +/* This function returns the mbus code for the current settings of the + HFLIP and VFLIP controls. */ + +static u32 ov5647_get_mbus_code(struct v4l2_subdev *sd, int mode_8bit) +{ + struct ov5647 *state = to_state(sd); + /* The control values are only 0 or 1. */ + int index = state->hflip->val | (state->vflip->val << 1); + + static const u32 codes[2][4] = { + { + MEDIA_BUS_FMT_SGBRG10_1X10, + MEDIA_BUS_FMT_SBGGR10_1X10, + MEDIA_BUS_FMT_SRGGB10_1X10, + MEDIA_BUS_FMT_SGRBG10_1X10 + }, + { + MEDIA_BUS_FMT_SGBRG8_1X8, + MEDIA_BUS_FMT_SBGGR8_1X8, + MEDIA_BUS_FMT_SRGGB8_1X8, + MEDIA_BUS_FMT_SGRBG8_1X8 + } + }; + + return codes[mode_8bit][index]; +} + static int ov5647_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_mbus_code_enum *code) { if (code->index == 0 && ARRAY_SIZE(supported_modes_8bit)) - code->code = MEDIA_BUS_FMT_SBGGR8_1X8; + code->code = ov5647_get_mbus_code(sd, 1); else if (code->index == 0 && ARRAY_SIZE(supported_modes_8bit) == 0 && ARRAY_SIZE(supported_modes_10bit)) - code->code = MEDIA_BUS_FMT_SBGGR10_1X10; + code->code = ov5647_get_mbus_code(sd, 0); else if (code->index == 1 && ARRAY_SIZE(supported_modes_8bit) && ARRAY_SIZE(supported_modes_10bit)) - code->code = MEDIA_BUS_FMT_SBGGR10_1X10; + code->code = ov5647_get_mbus_code(sd, 0); else return -EINVAL; @@ -1140,11 +1197,11 @@ static int ov5647_enum_frame_size(struct v4l2_subdev *sd, { struct ov5647_mode *mode = NULL; - if (fse->code == MEDIA_BUS_FMT_SBGGR8_1X8) { + if (fse->code == ov5647_get_mbus_code(sd, 1)) { 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) { + } else if (fse->code == ov5647_get_mbus_code(sd, 0)) { if (fse->index >= ARRAY_SIZE(supported_modes_10bit)) return -EINVAL; mode = &supported_modes_10bit[fse->index]; @@ -1188,9 +1245,9 @@ static int ov5647_set_fmt(struct v4l2_subdev *sd, format.width, format.height, format->format.width, format->format.height); - if (format->format.code == MEDIA_BUS_FMT_SBGGR8_1X8 && mode_8bit) + if (format->format.code == ov5647_get_mbus_code(sd, 1) && mode_8bit) mode = mode_8bit; - else if (format->format.code == MEDIA_BUS_FMT_SBGGR10_1X10 && + else if (format->format.code == ov5647_get_mbus_code(sd, 0) && mode_10bit) mode = mode_10bit; else if (mode_10bit) @@ -1204,6 +1261,8 @@ static int ov5647_set_fmt(struct v4l2_subdev *sd, } *fmt = mode->format; + /* The mbus code we pass back must reflect the current H/VFLIP settings. */ + fmt->code = ov5647_get_mbus_code(sd, mode == mode_8bit); if (format->which == V4L2_SUBDEV_FORMAT_TRY) { framefmt = v4l2_subdev_get_try_format(sd, cfg, format->pad); *framefmt = format->format; @@ -1265,6 +1324,8 @@ static int ov5647_get_fmt(struct v4l2_subdev *sd, *fmt = *v4l2_subdev_get_try_format(sd, cfg, format->pad); else *fmt = state->mode->format; + /* The mbus code we pass back must reflect the current H/VFLIP settings. */ + fmt->code = ov5647_get_mbus_code(sd, fmt->code == MEDIA_BUS_FMT_SBGGR8_1X8); mutex_unlock(&state->lock); @@ -1424,6 +1485,25 @@ static int ov5647_s_exposure(struct v4l2_subdev *sd, u32 val) return ret; } +static int ov5647_s_flip( struct v4l2_subdev *sd, u16 reg, u32 ctrl_val) +{ + int ret; + u8 reg_val; + + /* Set or clear bit 1 and leave everything else alone. */ + ret = ov5647_read(sd, reg, ®_val); + if (ret == 0) { + if (ctrl_val) + reg_val |= 2; + else + reg_val &= ~2; + + ret = ov5647_write(sd, reg, reg_val); + } + + return ret; +} + static int ov5647_s_ctrl(struct v4l2_ctrl *ctrl) { struct ov5647 *state = container_of(ctrl->handler, @@ -1481,6 +1561,13 @@ static int ov5647_s_ctrl(struct v4l2_ctrl *ctrl) ret = ov5647_write16(sd, OV5647_REG_VTS_HI, state->mode->format.height + ctrl->val); break; + case V4L2_CID_HFLIP: + /* There's an in-built hflip in the sensor, so account for that here. */ + ov5647_s_flip(sd, OV5647_REG_HFLIP, !ctrl->val); + break; + case V4L2_CID_VFLIP: + ov5647_s_flip(sd, OV5647_REG_VFLIP, ctrl->val); + break; default: dev_info(&client->dev, "ctrl(id:0x%x,val:0x%x) is not handled\n", @@ -1496,6 +1583,18 @@ static const struct v4l2_ctrl_ops ov5647_ctrl_ops = { .s_ctrl = ov5647_s_ctrl, }; +static int ov5647_configure_regulators(struct device *dev, + struct ov5647 *ov5647) +{ + unsigned int i; + + for (i = 0; i < OV5647_NUM_SUPPLIES; i++) + ov5647->supplies[i].supply = ov5647_supply_names[i]; + + return devm_regulator_bulk_get(dev, OV5647_NUM_SUPPLIES, + ov5647->supplies); +} + static int ov5647_probe(struct i2c_client *client) { struct device *dev = &client->dev; @@ -1536,10 +1635,16 @@ static int ov5647_probe(struct i2c_client *client) sensor->pwdn = devm_gpiod_get_optional(&client->dev, "pwdn", GPIOD_OUT_HIGH); + ret = ov5647_configure_regulators(dev, sensor); + if (ret) { + dev_err(dev, "Failed to get power regulators\n"); + return ret; + } + mutex_init(&sensor->lock); /* Initialise controls. */ - v4l2_ctrl_handler_init(&sensor->ctrls, 9); + v4l2_ctrl_handler_init(&sensor->ctrls, 11); v4l2_ctrl_new_std(&sensor->ctrls, &ov5647_ctrl_ops, V4L2_CID_AUTOGAIN, 0, /* min */ @@ -1597,6 +1702,16 @@ static int ov5647_probe(struct i2c_client *client) sensor->mode->vts_def - sensor->mode->format.height); + sensor->hflip = v4l2_ctrl_new_std(&sensor->ctrls, &ov5647_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + if (sensor->hflip) + sensor->hflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT; + + sensor->vflip = v4l2_ctrl_new_std(&sensor->ctrls, &ov5647_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + if (sensor->vflip) + sensor->vflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT; + if (sensor->ctrls.error) { ret = sensor->ctrls.error; dev_err(&client->dev, "%s control init failed (%d)\n", @@ -1630,6 +1745,12 @@ static int ov5647_probe(struct i2c_client *client) if (ret < 0) goto mutex_remove; + ret = regulator_bulk_enable(OV5647_NUM_SUPPLIES, sensor->supplies); + if (ret < 0) { + dev_err(dev, "Failed to enable regulators\n"); + goto error; + } + if (sensor->pwdn) { gpiod_set_value_cansleep(sensor->pwdn, 0); msleep(PWDN_ACTIVE_DELAY_MS); @@ -1640,7 +1761,7 @@ static int ov5647_probe(struct i2c_client *client) gpiod_set_value_cansleep(sensor->pwdn, 1); if (ret < 0) - goto error; + goto power_down; ret = v4l2_async_register_subdev(sd); if (ret < 0) @@ -1648,6 +1769,8 @@ static int ov5647_probe(struct i2c_client *client) dev_dbg(dev, "OmniVision OV5647 camera driver probed\n"); return 0; +power_down: + regulator_bulk_disable(OV5647_NUM_SUPPLIES, sensor->supplies); error: media_entity_cleanup(&sd->entity); mutex_remove: diff --git a/drivers/media/i2c/ov7251.c b/drivers/media/i2c/ov7251.c index 2c554626319d95d466e065b1ff9e5066818ba605..f2d77aaed798fd95d3b1a8d348a4c9aae9ab58e0 100644 --- a/drivers/media/i2c/ov7251.c +++ b/drivers/media/i2c/ov7251.c @@ -1331,7 +1331,8 @@ static int ov7251_probe(struct i2c_client *client) return PTR_ERR(ov7251->analog_regulator); } - ov7251->enable_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_HIGH); + ov7251->enable_gpio = devm_gpiod_get_optional(dev, "enable", + GPIOD_OUT_HIGH); if (IS_ERR(ov7251->enable_gpio)) { dev_err(dev, "cannot get enable gpio\n"); return PTR_ERR(ov7251->enable_gpio); diff --git a/drivers/media/i2c/ov9281.c b/drivers/media/i2c/ov9281.c index a6ffcdd47b215b6b3ae84b0cc7bf962369493c33..ff1b99e26a205ea073081b6b4d64efa2375e28cb 100644 --- a/drivers/media/i2c/ov9281.c +++ b/drivers/media/i2c/ov9281.c @@ -52,7 +52,11 @@ #define OV9281_REG_EXPOSURE 0x3500 #define OV9281_EXPOSURE_MIN 4 #define OV9281_EXPOSURE_STEP 1 -#define OV9281_VTS_MAX 0x7fff +/* + * Number of lines less than frame length (VTS) that exposure must be. + * Datasheet states 25, although empirically 5 appears to work. + */ +#define OV9281_EXPOSURE_OFFSET 25 #define OV9281_REG_GAIN_H 0x3508 #define OV9281_REG_GAIN_L 0x3509 @@ -69,6 +73,7 @@ #define OV9281_TEST_PATTERN_DISABLE 0x0 #define OV9281_REG_VTS 0x380e +#define OV9281_VTS_MAX 0x7fff /* * OV9281 native and active pixel array size. @@ -507,7 +512,7 @@ static int ov9281_set_fmt(struct v4l2_subdev *sd, fmt->format.width = mode->width; fmt->format.height = mode->height; fmt->format.field = V4L2_FIELD_NONE; - fmt->format.colorspace = V4L2_COLORSPACE_SRGB; + fmt->format.colorspace = V4L2_COLORSPACE_RAW; fmt->format.ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->format.colorspace); fmt->format.quantization = @@ -557,7 +562,7 @@ static int ov9281_get_fmt(struct v4l2_subdev *sd, fmt->format.height = mode->height; fmt->format.code = ov9281->code; fmt->format.field = V4L2_FIELD_NONE; - fmt->format.colorspace = V4L2_COLORSPACE_SRGB; + fmt->format.colorspace = V4L2_COLORSPACE_RAW; fmt->format.ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->format.colorspace); fmt->format.quantization = @@ -908,7 +913,7 @@ static int ov9281_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) 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->colorspace = V4L2_COLORSPACE_RAW; try_fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(try_fmt->colorspace); try_fmt->quantization = V4L2_MAP_QUANTIZATION_DEFAULT(true, try_fmt->colorspace, @@ -964,7 +969,7 @@ static int ov9281_set_ctrl(struct v4l2_ctrl *ctrl) switch (ctrl->id) { case V4L2_CID_VBLANK: /* Update max exposure while meeting expected vblanking */ - max = ov9281->cur_mode->height + ctrl->val - 4; + max = ov9281->cur_mode->height + ctrl->val - OV9281_EXPOSURE_OFFSET; __v4l2_ctrl_modify_range(ov9281->exposure, ov9281->exposure->minimum, max, ov9281->exposure->step, @@ -1059,7 +1064,7 @@ static int ov9281_initialize_controls(struct ov9281 *ov9281) OV9281_VTS_MAX - mode->height, 1, vblank_def); - exposure_max = mode->vts_def - 4; + exposure_max = mode->vts_def - OV9281_EXPOSURE_OFFSET; ov9281->exposure = v4l2_ctrl_new_std(handler, &ov9281_ctrl_ops, V4L2_CID_EXPOSURE, OV9281_EXPOSURE_MIN, exposure_max, diff --git a/drivers/media/platform/bcm2835/bcm2835-unicam.c b/drivers/media/platform/bcm2835/bcm2835-unicam.c index 59163f93b20710ed2905d5f325610c49528d7db2..99edc044c934111858ce797abc4e950084bb1ad4 100644 --- a/drivers/media/platform/bcm2835/bcm2835-unicam.c +++ b/drivers/media/platform/bcm2835/bcm2835-unicam.c @@ -81,6 +81,10 @@ static int debug; module_param(debug, int, 0644); MODULE_PARM_DESC(debug, "Debug level 0-3"); +static int media_controller; +module_param(media_controller, int, 0644); +MODULE_PARM_DESC(media_controller, "Use media controller API"); + #define unicam_dbg(level, dev, fmt, arg...) \ v4l2_dbg(level, debug, &(dev)->v4l2_dev, fmt, ##arg) #define unicam_info(dev, fmt, arg...) \ @@ -117,7 +121,7 @@ MODULE_PARM_DESC(debug, "Debug level 0-3"); #define MIN_WIDTH 16 #define MIN_HEIGHT 16 /* Default size of the embedded buffer */ -#define UNICAM_EMBEDDED_SIZE 8192 +#define UNICAM_EMBEDDED_SIZE 16384 /* * Size of the dummy buffer. Can be any size really, but the DMA @@ -131,6 +135,22 @@ enum pad_types { MAX_NODES }; +#define MASK_CS_DEFAULT BIT(V4L2_COLORSPACE_DEFAULT) +#define MASK_CS_SMPTE170M BIT(V4L2_COLORSPACE_SMPTE170M) +#define MASK_CS_SMPTE240M BIT(V4L2_COLORSPACE_SMPTE240M) +#define MASK_CS_REC709 BIT(V4L2_COLORSPACE_REC709) +#define MASK_CS_BT878 BIT(V4L2_COLORSPACE_BT878) +#define MASK_CS_470_M BIT(V4L2_COLORSPACE_470_SYSTEM_M) +#define MASK_CS_470_BG BIT(V4L2_COLORSPACE_470_SYSTEM_BG) +#define MASK_CS_JPEG BIT(V4L2_COLORSPACE_JPEG) +#define MASK_CS_SRGB BIT(V4L2_COLORSPACE_SRGB) +#define MASK_CS_OPRGB BIT(V4L2_COLORSPACE_OPRGB) +#define MASK_CS_BT2020 BIT(V4L2_COLORSPACE_BT2020) +#define MASK_CS_RAW BIT(V4L2_COLORSPACE_RAW) +#define MASK_CS_DCI_P3 BIT(V4L2_COLORSPACE_DCI_P3) + +#define MAX_COLORSPACE 32 + /* * struct unicam_fmt - Unicam media bus format information * @pixelformat: V4L2 pixel format FCC identifier. 0 if n/a. @@ -139,8 +159,14 @@ enum pad_types { * @code: V4L2 media bus format code. * @depth: Bits per pixel as delivered from the source. * @csi_dt: CSI data type. + * @valid_colorspaces: Bitmask of valid colorspaces so that the Media Controller + * centric try_fmt can validate the colorspace and pass + * v4l2-compliance. * @check_variants: Flag to denote that there are multiple mediabus formats * still in the list that could match this V4L2 format. + * @mc_skip: Media Controller shouldn't list this format via ENUM_FMT as it is + * a duplicate of an earlier format. + * @metadata_fmt: This format only applies to the metadata pad. */ struct unicam_fmt { u32 fourcc; @@ -148,7 +174,10 @@ struct unicam_fmt { u32 code; u8 depth; u8 csi_dt; - u8 check_variants; + u32 valid_colorspaces; + u8 check_variants:1; + u8 mc_skip:1; + u8 metadata_fmt:1; }; static const struct unicam_fmt formats[] = { @@ -159,173 +188,216 @@ static const struct unicam_fmt formats[] = { .depth = 16, .csi_dt = 0x1e, .check_variants = 1, + .valid_colorspaces = MASK_CS_SMPTE170M | MASK_CS_REC709 | + MASK_CS_JPEG, }, { .fourcc = V4L2_PIX_FMT_UYVY, .code = MEDIA_BUS_FMT_UYVY8_2X8, .depth = 16, .csi_dt = 0x1e, .check_variants = 1, + .valid_colorspaces = MASK_CS_SMPTE170M | MASK_CS_REC709 | + MASK_CS_JPEG, }, { .fourcc = V4L2_PIX_FMT_YVYU, .code = MEDIA_BUS_FMT_YVYU8_2X8, .depth = 16, .csi_dt = 0x1e, .check_variants = 1, + .valid_colorspaces = MASK_CS_SMPTE170M | MASK_CS_REC709 | + MASK_CS_JPEG, }, { .fourcc = V4L2_PIX_FMT_VYUY, .code = MEDIA_BUS_FMT_VYUY8_2X8, .depth = 16, .csi_dt = 0x1e, .check_variants = 1, + .valid_colorspaces = MASK_CS_SMPTE170M | MASK_CS_REC709 | + MASK_CS_JPEG, }, { .fourcc = V4L2_PIX_FMT_YUYV, .code = MEDIA_BUS_FMT_YUYV8_1X16, .depth = 16, .csi_dt = 0x1e, + .mc_skip = 1, + .valid_colorspaces = MASK_CS_SMPTE170M | MASK_CS_REC709 | + MASK_CS_JPEG, }, { .fourcc = V4L2_PIX_FMT_UYVY, .code = MEDIA_BUS_FMT_UYVY8_1X16, .depth = 16, .csi_dt = 0x1e, + .mc_skip = 1, + .valid_colorspaces = MASK_CS_SMPTE170M | MASK_CS_REC709 | + MASK_CS_JPEG, }, { .fourcc = V4L2_PIX_FMT_YVYU, .code = MEDIA_BUS_FMT_YVYU8_1X16, .depth = 16, .csi_dt = 0x1e, + .mc_skip = 1, + .valid_colorspaces = MASK_CS_SMPTE170M | MASK_CS_REC709 | + MASK_CS_JPEG, }, { .fourcc = V4L2_PIX_FMT_VYUY, .code = MEDIA_BUS_FMT_VYUY8_1X16, .depth = 16, .csi_dt = 0x1e, + .mc_skip = 1, + .valid_colorspaces = MASK_CS_SMPTE170M | MASK_CS_REC709 | + MASK_CS_JPEG, }, { /* RGB Formats */ .fourcc = V4L2_PIX_FMT_RGB565, /* gggbbbbb rrrrrggg */ .code = MEDIA_BUS_FMT_RGB565_2X8_LE, .depth = 16, .csi_dt = 0x22, + .valid_colorspaces = MASK_CS_SRGB, }, { .fourcc = V4L2_PIX_FMT_RGB565X, /* rrrrrggg gggbbbbb */ .code = MEDIA_BUS_FMT_RGB565_2X8_BE, .depth = 16, - .csi_dt = 0x22 + .csi_dt = 0x22, + .valid_colorspaces = MASK_CS_SRGB, }, { .fourcc = V4L2_PIX_FMT_RGB555, /* gggbbbbb arrrrrgg */ .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE, .depth = 16, .csi_dt = 0x21, + .valid_colorspaces = MASK_CS_SRGB, }, { .fourcc = V4L2_PIX_FMT_RGB555X, /* arrrrrgg gggbbbbb */ .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE, .depth = 16, .csi_dt = 0x21, + .valid_colorspaces = MASK_CS_SRGB, }, { .fourcc = V4L2_PIX_FMT_RGB24, /* rgb */ .code = MEDIA_BUS_FMT_RGB888_1X24, .depth = 24, .csi_dt = 0x24, + .valid_colorspaces = MASK_CS_SRGB, }, { .fourcc = V4L2_PIX_FMT_BGR24, /* bgr */ .code = MEDIA_BUS_FMT_BGR888_1X24, .depth = 24, .csi_dt = 0x24, + .valid_colorspaces = MASK_CS_SRGB, }, { .fourcc = V4L2_PIX_FMT_RGB32, /* argb */ .code = MEDIA_BUS_FMT_ARGB8888_1X32, .depth = 32, .csi_dt = 0x0, + .valid_colorspaces = MASK_CS_SRGB, }, { /* Bayer Formats */ .fourcc = V4L2_PIX_FMT_SBGGR8, .code = MEDIA_BUS_FMT_SBGGR8_1X8, .depth = 8, .csi_dt = 0x2a, + .valid_colorspaces = MASK_CS_RAW, }, { .fourcc = V4L2_PIX_FMT_SGBRG8, .code = MEDIA_BUS_FMT_SGBRG8_1X8, .depth = 8, .csi_dt = 0x2a, + .valid_colorspaces = MASK_CS_RAW, }, { .fourcc = V4L2_PIX_FMT_SGRBG8, .code = MEDIA_BUS_FMT_SGRBG8_1X8, .depth = 8, .csi_dt = 0x2a, + .valid_colorspaces = MASK_CS_RAW, }, { .fourcc = V4L2_PIX_FMT_SRGGB8, .code = MEDIA_BUS_FMT_SRGGB8_1X8, .depth = 8, .csi_dt = 0x2a, + .valid_colorspaces = MASK_CS_RAW, }, { .fourcc = V4L2_PIX_FMT_SBGGR10P, .repacked_fourcc = V4L2_PIX_FMT_SBGGR10, .code = MEDIA_BUS_FMT_SBGGR10_1X10, .depth = 10, .csi_dt = 0x2b, + .valid_colorspaces = MASK_CS_RAW, }, { .fourcc = V4L2_PIX_FMT_SGBRG10P, .repacked_fourcc = V4L2_PIX_FMT_SGBRG10, .code = MEDIA_BUS_FMT_SGBRG10_1X10, .depth = 10, .csi_dt = 0x2b, + .valid_colorspaces = MASK_CS_RAW, }, { .fourcc = V4L2_PIX_FMT_SGRBG10P, .repacked_fourcc = V4L2_PIX_FMT_SGRBG10, .code = MEDIA_BUS_FMT_SGRBG10_1X10, .depth = 10, .csi_dt = 0x2b, + .valid_colorspaces = MASK_CS_RAW, }, { .fourcc = V4L2_PIX_FMT_SRGGB10P, .repacked_fourcc = V4L2_PIX_FMT_SRGGB10, .code = MEDIA_BUS_FMT_SRGGB10_1X10, .depth = 10, .csi_dt = 0x2b, + .valid_colorspaces = MASK_CS_RAW, }, { .fourcc = V4L2_PIX_FMT_SBGGR12P, .repacked_fourcc = V4L2_PIX_FMT_SBGGR12, .code = MEDIA_BUS_FMT_SBGGR12_1X12, .depth = 12, .csi_dt = 0x2c, + .valid_colorspaces = MASK_CS_RAW, }, { .fourcc = V4L2_PIX_FMT_SGBRG12P, .repacked_fourcc = V4L2_PIX_FMT_SGBRG12, .code = MEDIA_BUS_FMT_SGBRG12_1X12, .depth = 12, .csi_dt = 0x2c, + .valid_colorspaces = MASK_CS_RAW, }, { .fourcc = V4L2_PIX_FMT_SGRBG12P, .repacked_fourcc = V4L2_PIX_FMT_SGRBG12, .code = MEDIA_BUS_FMT_SGRBG12_1X12, .depth = 12, .csi_dt = 0x2c, + .valid_colorspaces = MASK_CS_RAW, }, { .fourcc = V4L2_PIX_FMT_SRGGB12P, .repacked_fourcc = V4L2_PIX_FMT_SRGGB12, .code = MEDIA_BUS_FMT_SRGGB12_1X12, .depth = 12, .csi_dt = 0x2c, + .valid_colorspaces = MASK_CS_RAW, }, { .fourcc = V4L2_PIX_FMT_SBGGR14P, .repacked_fourcc = V4L2_PIX_FMT_SBGGR14, .code = MEDIA_BUS_FMT_SBGGR14_1X14, .depth = 14, .csi_dt = 0x2d, + .valid_colorspaces = MASK_CS_RAW, }, { .fourcc = V4L2_PIX_FMT_SGBRG14P, .repacked_fourcc = V4L2_PIX_FMT_SGBRG14, .code = MEDIA_BUS_FMT_SGBRG14_1X14, .depth = 14, .csi_dt = 0x2d, + .valid_colorspaces = MASK_CS_RAW, }, { .fourcc = V4L2_PIX_FMT_SGRBG14P, .repacked_fourcc = V4L2_PIX_FMT_SGRBG14, .code = MEDIA_BUS_FMT_SGRBG14_1X14, .depth = 14, .csi_dt = 0x2d, + .valid_colorspaces = MASK_CS_RAW, }, { .fourcc = V4L2_PIX_FMT_SRGGB14P, .repacked_fourcc = V4L2_PIX_FMT_SRGGB14, .code = MEDIA_BUS_FMT_SRGGB14_1X14, .depth = 14, .csi_dt = 0x2d, + .valid_colorspaces = MASK_CS_RAW, }, { /* * 16 bit Bayer formats could be supported, but there is no CSI2 @@ -338,30 +410,35 @@ static const struct unicam_fmt formats[] = { .code = MEDIA_BUS_FMT_Y8_1X8, .depth = 8, .csi_dt = 0x2a, + .valid_colorspaces = MASK_CS_RAW, }, { .fourcc = V4L2_PIX_FMT_Y10P, .repacked_fourcc = V4L2_PIX_FMT_Y10, .code = MEDIA_BUS_FMT_Y10_1X10, .depth = 10, .csi_dt = 0x2b, + .valid_colorspaces = MASK_CS_RAW, }, { .fourcc = V4L2_PIX_FMT_Y12P, .repacked_fourcc = V4L2_PIX_FMT_Y12, .code = MEDIA_BUS_FMT_Y12_1X12, .depth = 12, .csi_dt = 0x2c, + .valid_colorspaces = MASK_CS_RAW, }, { .fourcc = V4L2_PIX_FMT_Y14P, .repacked_fourcc = V4L2_PIX_FMT_Y14, .code = MEDIA_BUS_FMT_Y14_1X14, .depth = 14, .csi_dt = 0x2d, + .valid_colorspaces = MASK_CS_RAW, }, /* Embedded data format */ { .fourcc = V4L2_META_FMT_SENSOR_DATA, .code = MEDIA_BUS_FMT_SENSOR_DATA, .depth = 8, + .metadata_fmt = 1, } }; @@ -380,6 +457,8 @@ struct unicam_node { int open; bool streaming; unsigned int pad_id; + /* Source pad id on the sensor for this node */ + unsigned int src_pad_id; /* Pointer pointing to current v4l2_buffer */ struct unicam_buffer *cur_frm; /* Pointer pointing to next v4l2_buffer */ @@ -404,6 +483,7 @@ struct unicam_node { struct unicam_device *dev; struct media_pad pad; unsigned int embedded_lines; + struct media_pipeline pipe; /* * Dummy buffer intended to be used by unicam * if we have no other queued buffers to swap to. @@ -457,6 +537,8 @@ struct unicam_device { struct unicam_node node[MAX_NODES]; struct v4l2_ctrl_handler ctrl_handler; + + bool mc_api; }; static inline struct unicam_device * @@ -590,7 +672,7 @@ static int __subdev_get_format(struct unicam_device *dev, { struct v4l2_subdev_format sd_fmt = { .which = V4L2_SUBDEV_FORMAT_ACTIVE, - .pad = pad_id + .pad = dev->node[pad_id].src_pad_id, }; int ret; @@ -612,7 +694,7 @@ static int __subdev_set_format(struct unicam_device *dev, { struct v4l2_subdev_format sd_fmt = { .which = V4L2_SUBDEV_FORMAT_ACTIVE, - .pad = pad_id + .pad = dev->node[pad_id].src_pad_id, }; int ret; @@ -869,6 +951,9 @@ static irqreturn_t unicam_isr(int irq, void *dev) if (unicam->node[i].cur_frm) unicam->node[i].cur_frm->vb.vb2_buf.timestamp = ts; + else + unicam_dbg(2, unicam, "ISR: [%d] Dropping frame, buffer not available at FS\n", + i); /* * Set the next frame output to go to a dummy frame * if we have not managed to obtain another frame @@ -898,14 +983,10 @@ static irqreturn_t unicam_isr(int irq, void *dev) } } - 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; } +/* V4L2 Common IOCTLs */ static int unicam_querycap(struct file *file, void *priv, struct v4l2_capability *cap) { @@ -923,6 +1004,38 @@ static int unicam_querycap(struct file *file, void *priv, return 0; } +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; +} + +/* V4L2 Video Centric IOCTLs */ static int unicam_enum_fmt_vid_cap(struct file *file, void *priv, struct v4l2_fmtdesc *f) { @@ -1267,902 +1380,1264 @@ static int unicam_g_fmt_meta_cap(struct file *file, void *priv, return 0; } -static int unicam_queue_setup(struct vb2_queue *vq, - unsigned int *nbuffers, - unsigned int *nplanes, - unsigned int sizes[], - struct device *alloc_devs[]) +static int unicam_enum_input(struct file *file, void *priv, + struct v4l2_input *inp) { - struct unicam_node *node = vb2_get_drv_priv(vq); + struct unicam_node *node = video_drvdata(file); 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; + int ret; - if (vq->num_buffers + *nbuffers < 3) - *nbuffers = 3 - vq->num_buffers; + if (inp->index != 0) + return -EINVAL; - if (*nplanes) { - if (sizes[0] < size) { - unicam_err(dev, "sizes[0] %i < size %u\n", sizes[0], - size); - return -EINVAL; - } - size = sizes[0]; + 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; } - *nplanes = 1; - sizes[0] = size; + 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_buffer_prepare(struct vb2_buffer *vb) +static int unicam_g_input(struct file *file, void *priv, unsigned int *i) { - 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; + *i = 0; - if (WARN_ON(!node->fmt)) - return -EINVAL; + return 0; +} - 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); +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; - } - vb2_set_plane_payload(&buf->vb.vb2_buf, 0, size); return 0; } -static void unicam_buffer_queue(struct vb2_buffer *vb) +static int unicam_querystd(struct file *file, void *priv, + v4l2_std_id *std) { - struct unicam_node *node = vb2_get_drv_priv(vb->vb2_queue); - struct unicam_buffer *buf = to_unicam_buffer(vb); - unsigned long flags; + struct unicam_node *node = video_drvdata(file); + struct unicam_device *dev = node->dev; - spin_lock_irqsave(&node->dma_queue_lock, flags); - list_add_tail(&buf->list, &node->dma_queue); - spin_unlock_irqrestore(&node->dma_queue_lock, flags); + return v4l2_subdev_call(dev->sensor, video, querystd, std); } -static void unicam_set_packing_config(struct unicam_device *dev) +static int unicam_g_std(struct file *file, void *priv, v4l2_std_id *std) { - u32 pack, unpack; - u32 val; + struct unicam_node *node = video_drvdata(file); + struct unicam_device *dev = node->dev; - 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; - } + return v4l2_subdev_call(dev->sensor, video, g_std, std); +} - /* Repacking is always to 16bpp */ - pack = UNICAM_PPM_PACK16; - } +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; - val = 0; - set_field(&val, unpack, UNICAM_PUM_MASK); - set_field(&val, pack, UNICAM_PPM_MASK); - reg_write(dev, UNICAM_IPIPE, val); + 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 void unicam_cfg_image_id(struct unicam_device *dev) +static int unicam_s_edid(struct file *file, void *priv, struct v4l2_edid *edid) { - 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); - } + struct unicam_node *node = video_drvdata(file); + struct unicam_device *dev = node->dev; + + return v4l2_subdev_call(dev->sensor, pad, set_edid, edid); } -static void unicam_enable_ed(struct unicam_device *dev) +static int unicam_g_edid(struct file *file, void *priv, struct v4l2_edid *edid) { - 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); + struct unicam_node *node = video_drvdata(file); + struct unicam_device *dev = node->dev; - reg_write(dev, UNICAM_DCS, val); + return v4l2_subdev_call(dev->sensor, pad, get_edid, edid); } -static void unicam_start_rx(struct unicam_device *dev, dma_addr_t *addr) +static int unicam_s_selection(struct file *file, void *priv, + struct v4l2_selection *sel) { - int line_int_freq = dev->node[IMAGE_PAD].v_fmt.fmt.pix.height >> 2; - unsigned int size, i; - u32 val; + 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 (line_int_freq < 128) - line_int_freq = 128; + if (sel->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; - /* Enable lane clocks */ - val = 1; - for (i = 0; i < dev->active_data_lanes; i++) - val = val << 2 | 1; - clk_write(dev, val); + return v4l2_subdev_call(dev->sensor, pad, set_selection, NULL, &sdsel); +} - /* Basic init */ - reg_write(dev, UNICAM_CTRL, UNICAM_MEM); +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; - /* 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); + if (sel->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; - /* Come out of reset */ - reg_write_field(dev, UNICAM_ANA, 0, UNICAM_AR); + ret = v4l2_subdev_call(dev->sensor, pad, get_selection, NULL, &sdsel); + if (!ret) + sel->r = sdsel.r; - /* Peripheral reset */ - reg_write_field(dev, UNICAM_CTRL, 1, UNICAM_CPR); - reg_write_field(dev, UNICAM_CTRL, 0, UNICAM_CPR); + return ret; +} - reg_write_field(dev, UNICAM_CTRL, 0, UNICAM_CPE); +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; - /* 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); + /* 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; } - /* 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); + fse.code = fmt->code; - /* 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); + fse.which = V4L2_SUBDEV_FORMAT_ACTIVE; + fse.index = fsize->index; + fse.pad = node->src_pad_id; - /* 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); + ret = v4l2_subdev_call(dev->sensor, pad, enum_frame_size, NULL, &fse); + if (ret) + return ret; - reg_write_field(dev, UNICAM_CTRL, 0, UNICAM_SOE); + 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); - /* 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); + fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; + fsize->discrete.width = fse.max_width; + fsize->discrete.height = fse.max_height; - /* 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); + return 0; +} - /* - * 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); +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, + .pad = node->src_pad_id, + .width = fival->width, + .height = fival->height, + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; + int ret; - if (dev->active_data_lanes == 1) - val = 0; - reg_write(dev, UNICAM_DAT1, val); + fmt = find_format_by_pix(dev, fival->pixel_format); + if (!fmt) + return -EINVAL; - 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); + fie.code = fmt->code; + ret = v4l2_subdev_call(dev->sensor, pad, enum_frame_interval, + NULL, &fie); + if (ret) + return ret; - if (dev->active_data_lanes == 3) - val = 0; - reg_write(dev, UNICAM_DAT3, val); - } + fival->type = V4L2_FRMIVAL_TYPE_DISCRETE; + fival->discrete = fie.interval; - 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); + return 0; +} - val = reg_read(dev, UNICAM_MISC); - set_field(&val, 1, UNICAM_FL0); - set_field(&val, 1, UNICAM_FL1); - reg_write(dev, UNICAM_MISC, val); +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; - 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); - } + return v4l2_g_parm_cap(video_devdata(file), dev->sensor, a); +} - /* Enable peripheral */ - reg_write_field(dev, UNICAM_CTRL, 1, UNICAM_CPE); +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; - /* Load image pointers */ - reg_write_field(dev, UNICAM_ICTL, 1, UNICAM_LIP_MASK); + return v4l2_s_parm_cap(video_devdata(file), dev->sensor, a); +} - /* 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); +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; - /* - * 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); + return v4l2_subdev_call(dev->sensor, video, g_dv_timings, timings); } -static void unicam_disable(struct unicam_device *dev) +static int unicam_s_dv_timings(struct file *file, void *priv, + struct v4l2_dv_timings *timings) { - /* Analogue lane control disable */ - reg_write_field(dev, UNICAM_ANA, 1, UNICAM_DDL); + struct unicam_node *node = video_drvdata(file); + struct unicam_device *dev = node->dev; + struct v4l2_dv_timings current_timings; + int ret; - /* Stop the output engine */ - reg_write_field(dev, UNICAM_CTRL, 1, UNICAM_SOE); + ret = v4l2_subdev_call(dev->sensor, video, g_dv_timings, + ¤t_timings); - /* Disable the data lanes. */ - reg_write(dev, UNICAM_DAT0, 0); - reg_write(dev, UNICAM_DAT1, 0); + if (ret < 0) + return ret; - if (dev->max_data_lanes > 2) { - reg_write(dev, UNICAM_DAT2, 0); - reg_write(dev, UNICAM_DAT3, 0); - } + if (v4l2_match_dv_timings(timings, ¤t_timings, 0, false)) + return 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); + if (vb2_is_busy(&node->buffer_queue)) + return -EBUSY; - /* Disable peripheral */ - reg_write_field(dev, UNICAM_CTRL, 0, UNICAM_CPE); + ret = v4l2_subdev_call(dev->sensor, video, s_dv_timings, timings); - /* Clear ED setup */ - reg_write(dev, UNICAM_DCS, 0); + /* Force recomputation of bytesperline */ + node->v_fmt.fmt.pix.bytesperline = 0; - /* Disable all lane clocks */ - clk_write(dev, 0); + unicam_reset_format(node); + + return ret; } -static void unicam_return_buffers(struct unicam_node *node, - enum vb2_buffer_state state) +static int unicam_query_dv_timings(struct file *file, void *priv, + struct v4l2_dv_timings *timings) { - struct unicam_buffer *buf, *tmp; - unsigned long flags; + struct unicam_node *node = video_drvdata(file); + struct unicam_device *dev = node->dev; - 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); - } + return v4l2_subdev_call(dev->sensor, video, query_dv_timings, timings); +} - 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); +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; + int ret; - node->cur_frm = NULL; - node->next_frm = NULL; - spin_unlock_irqrestore(&node->dma_queue_lock, flags); + timings->pad = node->src_pad_id; + ret = v4l2_subdev_call(dev->sensor, pad, enum_dv_timings, timings); + timings->pad = node->pad_id; + + return ret; } -static int unicam_start_streaming(struct vb2_queue *vq, unsigned int count) +static int unicam_dv_timings_cap(struct file *file, void *priv, + struct v4l2_dv_timings_cap *cap) { - struct unicam_node *node = vb2_get_drv_priv(vq); + struct unicam_node *node = video_drvdata(file); 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; + cap->pad = node->src_pad_id; + ret = v4l2_subdev_call(dev->sensor, pad, dv_timings_cap, cap); + cap->pad = node->pad_id; + + return ret; +} + +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); } - dev->sequence = 0; - ret = unicam_runtime_get(dev); - if (ret < 0) { - unicam_dbg(3, dev, "unicam_runtime_get failed\n"); - goto err_streaming; + return v4l2_ctrl_subscribe_event(fh, sub); +} + +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; } +} - dev->active_data_lanes = dev->max_data_lanes; +/* 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, - if (dev->bus_type == V4L2_MBUS_CSI2_DPHY) { - struct v4l2_mbus_config mbus_config = { 0 }; + .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, - 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; - } + .vidioc_enum_input = unicam_enum_input, + .vidioc_g_input = unicam_g_input, + .vidioc_s_input = unicam_s_input, - 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; - } - } + .vidioc_querystd = unicam_querystd, + .vidioc_s_std = unicam_s_std, + .vidioc_g_std = unicam_g_std, - unicam_dbg(1, dev, "Running with %u data lanes\n", - dev->active_data_lanes); + .vidioc_g_edid = unicam_g_edid, + .vidioc_s_edid = unicam_s_edid, - 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; - } + .vidioc_enum_framesizes = unicam_enum_framesizes, + .vidioc_enum_frameintervals = unicam_enum_frameintervals, - ret = clk_prepare_enable(dev->vpu_clock); - if (ret) { - unicam_err(dev, "Failed to enable VPU clock: %d\n", ret); - goto err_pm_put; - } + .vidioc_g_selection = unicam_g_selection, + .vidioc_s_selection = unicam_s_selection, - 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; - } + .vidioc_g_parm = unicam_g_parm, + .vidioc_s_parm = unicam_s_parm, - ret = clk_prepare_enable(dev->clock); - if (ret) { - unicam_err(dev, "Failed to enable CSI clock: %d\n", ret); - goto err_vpu_clock; - } + .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, - for (i = 0; i < ARRAY_SIZE(dev->node); i++) { - struct unicam_buffer *buf; + .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, - if (!dev->node[i].streaming) - continue; + .vidioc_log_status = unicam_log_status, + .vidioc_subscribe_event = unicam_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, +}; - 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); +/* V4L2 Media Controller Centric IOCTLs */ - buffer_addr[i] = - vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0); - } +static int unicam_mc_enum_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + int i, j; - unicam_start_rx(dev, buffer_addr); + for (i = 0, j = 0; i < ARRAY_SIZE(formats); i++) { + if (f->mbus_code && formats[i].code != f->mbus_code) + continue; + if (formats[i].mc_skip || formats[i].metadata_fmt) + continue; - 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; + if (formats[i].fourcc) { + if (j == f->index) { + f->pixelformat = formats[i].fourcc; + f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + return 0; + } + j++; + } + if (formats[i].repacked_fourcc) { + if (j == f->index) { + f->pixelformat = formats[i].repacked_fourcc; + f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + return 0; + } + j++; + } } - dev->clocks_enabled = true; - return 0; + return -EINVAL; +} -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; +static int unicam_mc_g_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct unicam_node *node = video_drvdata(file); - return ret; + if (node->pad_id != IMAGE_PAD) + return -EINVAL; + + *f = node->v_fmt; + + return 0; } -static void unicam_stop_streaming(struct vb2_queue *vq) +static void unicam_mc_try_fmt(struct unicam_node *node, struct v4l2_format *f, + const struct unicam_fmt **ret_fmt) { - struct unicam_node *node = vb2_get_drv_priv(vq); + struct v4l2_pix_format *v4l2_format = &f->fmt.pix; struct unicam_device *dev = node->dev; + const struct unicam_fmt *fmt; + int is_rgb; - node->streaming = false; + /* + * Default to the first format if the requested pixel format code isn't + * supported. + */ + fmt = find_format_by_pix(dev, v4l2_format->pixelformat); + if (!fmt) { + fmt = &formats[0]; + v4l2_format->pixelformat = fmt->fourcc; + } - 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_calc_format_size_bpl(dev, fmt, f); - unicam_disable(dev); + if (v4l2_format->field == V4L2_FIELD_ANY) + v4l2_format->field = V4L2_FIELD_NONE; - 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); + if (ret_fmt) + *ret_fmt = fmt; - } 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); + if (v4l2_format->colorspace >= MAX_COLORSPACE || + !(fmt->valid_colorspaces & (1 << v4l2_format->colorspace))) { + v4l2_format->colorspace = __ffs(fmt->valid_colorspaces); + + v4l2_format->xfer_func = + V4L2_MAP_XFER_FUNC_DEFAULT(v4l2_format->colorspace); + v4l2_format->ycbcr_enc = + V4L2_MAP_YCBCR_ENC_DEFAULT(v4l2_format->colorspace); + is_rgb = v4l2_format->colorspace == V4L2_COLORSPACE_SRGB; + v4l2_format->quantization = + V4L2_MAP_QUANTIZATION_DEFAULT(is_rgb, + v4l2_format->colorspace, + v4l2_format->ycbcr_enc); } - /* Clear all queued buffers for the node */ - unicam_return_buffers(node, VB2_BUF_STATE_ERROR); + unicam_dbg(3, dev, "%s: %08x %ux%u (bytesperline %u sizeimage %u)\n", + __func__, v4l2_format->pixelformat, + v4l2_format->width, v4l2_format->height, + v4l2_format->bytesperline, v4l2_format->sizeimage); } -static int unicam_enum_input(struct file *file, void *priv, - struct v4l2_input *inp) +static int unicam_mc_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; - int ret; - if (inp->index != 0) - return -EINVAL; + unicam_mc_try_fmt(node, f, NULL); + return 0; +} - 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; - } +static int unicam_mc_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; + const struct unicam_fmt *fmt; - 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; + if (vb2_is_busy(&node->buffer_queue)) { + unicam_dbg(3, dev, "%s device busy\n", __func__); + return -EBUSY; } - snprintf(inp->name, sizeof(inp->name), "Camera 0"); + unicam_mc_try_fmt(node, f, &fmt); + + node->v_fmt = *f; + node->fmt = fmt; + return 0; } -static int unicam_g_input(struct file *file, void *priv, unsigned int *i) +static int unicam_mc_enum_framesizes(struct file *file, void *fh, + struct v4l2_frmsizeenum *fsize) { - *i = 0; + struct unicam_node *node = video_drvdata(file); + struct unicam_device *dev = node->dev; + + if (fsize->index > 0) + return -EINVAL; + + if (!find_format_by_pix(dev, fsize->pixel_format)) { + unicam_dbg(3, dev, "Invalid pixel format 0x%08x\n", + fsize->pixel_format); + return -EINVAL; + } + + fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE; + fsize->stepwise.min_width = MIN_WIDTH; + fsize->stepwise.max_width = MAX_WIDTH; + fsize->stepwise.step_width = 1; + fsize->stepwise.min_height = MIN_HEIGHT; + fsize->stepwise.max_height = MAX_HEIGHT; + fsize->stepwise.step_height = 1; return 0; } -static int unicam_s_input(struct file *file, void *priv, unsigned int i) +static int unicam_mc_enum_fmt_meta_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *f) { - /* - * 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) + int i, j; + + for (i = 0, j = 0; i < ARRAY_SIZE(formats); i++) { + if (f->mbus_code && formats[i].code != f->mbus_code) + continue; + if (!formats[i].metadata_fmt) + continue; + + if (formats[i].fourcc) { + if (j == f->index) { + f->pixelformat = formats[i].fourcc; + f->type = V4L2_BUF_TYPE_META_CAPTURE; + return 0; + } + j++; + } + } + + return -EINVAL; +} + +static int unicam_mc_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_querystd(struct file *file, void *priv, - v4l2_std_id *std) +static int unicam_mc_try_fmt_meta_cap(struct file *file, void *priv, + struct v4l2_format *f) { struct unicam_node *node = video_drvdata(file); - struct unicam_device *dev = node->dev; - return v4l2_subdev_call(dev->sensor, video, querystd, std); + if (node->pad_id != METADATA_PAD) + return -EINVAL; + + f->fmt.meta.dataformat = V4L2_META_FMT_SENSOR_DATA; + + return 0; } -static int unicam_g_std(struct file *file, void *priv, v4l2_std_id *std) +static int unicam_mc_s_fmt_meta_cap(struct file *file, void *priv, + struct v4l2_format *f) { struct unicam_node *node = video_drvdata(file); - struct unicam_device *dev = node->dev; - return v4l2_subdev_call(dev->sensor, video, g_std, std); + if (node->pad_id != METADATA_PAD) + return -EINVAL; + + unicam_mc_try_fmt_meta_cap(file, priv, f); + + node->v_fmt = *f; + + return 0; } -static int unicam_s_std(struct file *file, void *priv, v4l2_std_id std) +static const struct v4l2_ioctl_ops unicam_mc_ioctl_ops = { + .vidioc_querycap = unicam_querycap, + .vidioc_enum_fmt_vid_cap = unicam_mc_enum_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = unicam_mc_g_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = unicam_mc_try_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = unicam_mc_s_fmt_vid_cap, + + .vidioc_enum_fmt_meta_cap = unicam_mc_enum_fmt_meta_cap, + .vidioc_g_fmt_meta_cap = unicam_mc_g_fmt_meta_cap, + .vidioc_try_fmt_meta_cap = unicam_mc_try_fmt_meta_cap, + .vidioc_s_fmt_meta_cap = unicam_mc_s_fmt_meta_cap, + + .vidioc_enum_framesizes = unicam_mc_enum_framesizes, + .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_mc_subdev_link_validate_get_format(struct media_pad *pad, + struct v4l2_subdev_format *fmt) { - struct unicam_node *node = video_drvdata(file); - struct unicam_device *dev = node->dev; + if (is_media_entity_v4l2_subdev(pad->entity)) { + struct v4l2_subdev *sd = + media_entity_to_v4l2_subdev(pad->entity); + + fmt->which = V4L2_SUBDEV_FORMAT_ACTIVE; + fmt->pad = pad->index; + return v4l2_subdev_call(sd, pad, get_fmt, NULL, fmt); + } + + return -EINVAL; +} + +static int unicam_mc_video_link_validate(struct media_link *link) +{ + struct video_device *vd = container_of(link->sink->entity, + struct video_device, entity); + struct unicam_node *node = container_of(vd, struct unicam_node, + video_dev); + struct unicam_device *unicam = node->dev; + struct v4l2_subdev_format source_fmt; int ret; - v4l2_std_id current_std; - ret = v4l2_subdev_call(dev->sensor, video, g_std, ¤t_std); - if (ret) - return ret; + if (!media_entity_remote_pad(link->sink->entity->pads)) { + unicam_dbg(1, unicam, + "video node %s pad not connected\n", vd->name); + return -ENOTCONN; + } - if (std == current_std) + ret = unicam_mc_subdev_link_validate_get_format(link->source, + &source_fmt); + if (ret < 0) return 0; - if (vb2_is_busy(&node->buffer_queue)) - return -EBUSY; + if (node->pad_id == IMAGE_PAD) { + struct v4l2_pix_format *pix_fmt = &node->v_fmt.fmt.pix; + const struct unicam_fmt *fmt; - ret = v4l2_subdev_call(dev->sensor, video, s_std, std); + if (source_fmt.format.width != pix_fmt->width || + source_fmt.format.height != pix_fmt->height) { + unicam_err(unicam, + "Wrong width or height %ux%u (remote pad set to %ux%u)\n", + pix_fmt->width, pix_fmt->height, + source_fmt.format.width, + source_fmt.format.height); + return -EINVAL; + } - /* Force recomputation of bytesperline */ - node->v_fmt.fmt.pix.bytesperline = 0; + fmt = find_format_by_code(source_fmt.format.code); - unicam_reset_format(node); + if (!fmt || (fmt->fourcc != pix_fmt->pixelformat && + fmt->repacked_fourcc != pix_fmt->pixelformat)) + return -EINVAL; + } else { + struct v4l2_meta_format *meta_fmt = &node->v_fmt.fmt.meta; + + if (source_fmt.format.width != meta_fmt->buffersize || + source_fmt.format.height != 1 || + source_fmt.format.code != MEDIA_BUS_FMT_SENSOR_DATA) { + unicam_err(unicam, + "Wrong metadata width/height/code %ux%u %08x (remote pad set to %ux%u %08x)\n", + meta_fmt->buffersize, 1, + MEDIA_BUS_FMT_SENSOR_DATA, + source_fmt.format.width, + source_fmt.format.height, + source_fmt.format.code); + return -EINVAL; + } + } + + return 0; +} + +static const struct media_entity_operations unicam_mc_entity_ops = { + .link_validate = unicam_mc_video_link_validate, +}; + +/* videobuf2 Operations */ + +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); + + val = UNICAM_FSIE | UNICAM_FEIE | 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); + } - return ret; -} + /* Enable peripheral */ + reg_write_field(dev, UNICAM_CTRL, 1, UNICAM_CPE); -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; + /* Load image pointers */ + reg_write_field(dev, UNICAM_ICTL, 1, UNICAM_LIP_MASK); - return v4l2_subdev_call(dev->sensor, pad, set_edid, edid); + /* 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); } -static int unicam_g_edid(struct file *file, void *priv, struct v4l2_edid *edid) +static void unicam_disable(struct unicam_device *dev) { - struct unicam_node *node = video_drvdata(file); - struct unicam_device *dev = node->dev; - - return v4l2_subdev_call(dev->sensor, pad, get_edid, edid); -} + /* Analogue lane control disable */ + reg_write_field(dev, UNICAM_ANA, 1, UNICAM_DDL); -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, - }; + /* Stop the output engine */ + reg_write_field(dev, UNICAM_CTRL, 1, UNICAM_SOE); - if (sel->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; + /* Disable the data lanes. */ + reg_write(dev, UNICAM_DAT0, 0); + reg_write(dev, UNICAM_DAT1, 0); - return v4l2_subdev_call(dev->sensor, pad, set_selection, NULL, &sdsel); -} + if (dev->max_data_lanes > 2) { + reg_write(dev, UNICAM_DAT2, 0); + reg_write(dev, UNICAM_DAT3, 0); + } -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; + /* Peripheral reset */ + reg_write_field(dev, UNICAM_CTRL, 1, UNICAM_CPR); + usleep_range(50, 100); + reg_write_field(dev, UNICAM_CTRL, 0, UNICAM_CPR); - if (sel->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; + /* Disable peripheral */ + reg_write_field(dev, UNICAM_CTRL, 0, UNICAM_CPE); - ret = v4l2_subdev_call(dev->sensor, pad, get_selection, NULL, &sdsel); - if (!ret) - sel->r = sdsel.r; + /* Clear ED setup */ + reg_write(dev, UNICAM_DCS, 0); - return ret; + /* Disable all lane clocks */ + clk_write(dev, 0); } -static int unicam_enum_framesizes(struct file *file, void *priv, - struct v4l2_frmsizeenum *fsize) +static void unicam_return_buffers(struct unicam_node *node, + enum vb2_buffer_state state) { - 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; + struct unicam_buffer *buf, *tmp; + unsigned long flags; - /* 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; + 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); } - 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; + 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); - return 0; + node->cur_frm = NULL; + node->next_frm = NULL; + spin_unlock_irqrestore(&node->dma_queue_lock, flags); } -static int unicam_enum_frameintervals(struct file *file, void *priv, - struct v4l2_frmivalenum *fival) +static int unicam_start_streaming(struct vb2_queue *vq, unsigned int count) { - struct unicam_node *node = video_drvdata(file); + struct unicam_node *node = vb2_get_drv_priv(vq); 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, - }; + dma_addr_t buffer_addr[MAX_NODES] = { 0 }; + unsigned long flags; + unsigned int i; int ret; - fmt = find_format_by_pix(dev, fival->pixel_format); - if (!fmt) - return -EINVAL; + 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; + } - fie.code = fmt->code; - ret = v4l2_subdev_call(dev->sensor, pad, enum_frame_interval, - NULL, &fie); - if (ret) - return ret; + dev->sequence = 0; + ret = unicam_runtime_get(dev); + if (ret < 0) { + unicam_dbg(3, dev, "unicam_runtime_get failed\n"); + goto err_streaming; + } - fival->type = V4L2_FRMIVAL_TYPE_DISCRETE; - fival->discrete = fie.interval; + ret = media_pipeline_start(&node->video_dev.entity, &node->pipe); + if (ret < 0) { + unicam_err(dev, "Failed to start media pipeline: %d\n", ret); + goto err_pm_put; + } - return 0; -} + dev->active_data_lanes = dev->max_data_lanes; -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; + if (dev->bus_type == V4L2_MBUS_CSI2_DPHY) { + struct v4l2_mbus_config mbus_config = { 0 }; - return v4l2_g_parm_cap(video_devdata(file), dev->sensor, a); -} + 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 error_pipeline; + } -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; + 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 error_pipeline; + } + } - return v4l2_s_parm_cap(video_devdata(file), dev->sensor, a); -} + unicam_dbg(1, dev, "Running with %u data lanes\n", + dev->active_data_lanes); -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; + 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 error_pipeline; + } - return v4l2_subdev_call(dev->sensor, video, g_dv_timings, timings); -} + ret = clk_prepare_enable(dev->vpu_clock); + if (ret) { + unicam_err(dev, "Failed to enable VPU clock: %d\n", ret); + goto error_pipeline; + } -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 = 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 = v4l2_subdev_call(dev->sensor, video, g_dv_timings, - ¤t_timings); + ret = clk_prepare_enable(dev->clock); + if (ret) { + unicam_err(dev, "Failed to enable CSI clock: %d\n", ret); + goto err_vpu_clock; + } - if (ret < 0) - return ret; + for (i = 0; i < ARRAY_SIZE(dev->node); i++) { + struct unicam_buffer *buf; - if (v4l2_match_dv_timings(timings, ¤t_timings, 0, false)) - return 0; + 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); + } - if (vb2_is_busy(&node->buffer_queue)) - return -EBUSY; + unicam_start_rx(dev, buffer_addr); - ret = v4l2_subdev_call(dev->sensor, video, s_dv_timings, timings); + 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; + } - /* Force recomputation of bytesperline */ - node->v_fmt.fmt.pix.bytesperline = 0; + dev->clocks_enabled = true; + return 0; - unicam_reset_format(node); +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); +error_pipeline: + media_pipeline_stop(&node->video_dev.entity); +err_pm_put: + unicam_runtime_put(dev); +err_streaming: + unicam_return_buffers(node, VB2_BUF_STATE_QUEUED); + node->streaming = false; return ret; } -static int unicam_query_dv_timings(struct file *file, void *priv, - struct v4l2_dv_timings *timings) +static void unicam_stop_streaming(struct vb2_queue *vq) { - struct unicam_node *node = video_drvdata(file); + struct unicam_node *node = vb2_get_drv_priv(vq); struct unicam_device *dev = node->dev; - return v4l2_subdev_call(dev->sensor, video, query_dv_timings, timings); -} + node->streaming = false; -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; + 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"); - return v4l2_subdev_call(dev->sensor, pad, enum_dv_timings, timings); -} + unicam_disable(dev); -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; + media_pipeline_stop(&node->video_dev.entity); - return v4l2_subdev_call(dev->sensor, pad, dv_timings_cap, cap); -} + 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); -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); + } 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); } - 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; + /* Clear all queued buffers for the node */ + unicam_return_buffers(node, VB2_BUF_STATE_ERROR); } -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, @@ -2245,60 +2720,6 @@ static const struct v4l2_file_operations unicam_fops = { .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, @@ -2349,11 +2770,11 @@ static void unicam_node_release(struct video_device *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) +static int unicam_set_default_format(struct unicam_device *unicam, + struct unicam_node *node, + int pad_id, + const struct unicam_fmt **ret_fmt) { - struct video_device *vdev; - struct vb2_queue *q; struct v4l2_mbus_framefmt mbus_fmt = {0}; const struct unicam_fmt *fmt; int ret; @@ -2374,14 +2795,12 @@ static int register_node(struct unicam_device *unicam, struct unicam_node *node, */ 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 (fmt) { + 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. */ @@ -2391,7 +2810,8 @@ static int register_node(struct unicam_device *unicam, struct unicam_node *node, return -EINVAL; } - node->v_fmt.fmt.pix.pixelformat = fmt->fourcc ? fmt->fourcc + if (fmt) + node->v_fmt.fmt.pix.pixelformat = fmt->fourcc ? fmt->fourcc : fmt->repacked_fourcc; } else { /* Fix this node format as embedded data. */ @@ -2399,14 +2819,69 @@ static int register_node(struct unicam_device *unicam, struct unicam_node *node, node->v_fmt.fmt.meta.dataformat = fmt->fourcc; } + *ret_fmt = fmt; + + return 0; +} + +static void unicam_mc_set_default_format(struct unicam_node *node, int pad_id) +{ + if (pad_id == IMAGE_PAD) { + struct v4l2_pix_format *pix_fmt = &node->v_fmt.fmt.pix; + + pix_fmt->width = 640; + pix_fmt->height = 480; + pix_fmt->field = V4L2_FIELD_NONE; + pix_fmt->colorspace = V4L2_COLORSPACE_SRGB; + pix_fmt->ycbcr_enc = V4L2_YCBCR_ENC_601; + pix_fmt->quantization = V4L2_QUANTIZATION_LIM_RANGE; + pix_fmt->xfer_func = V4L2_XFER_FUNC_SRGB; + pix_fmt->pixelformat = formats[0].fourcc; + unicam_calc_format_size_bpl(node->dev, &formats[0], + &node->v_fmt); + node->v_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + node->fmt = &formats[0]; + } else { + const struct unicam_fmt *fmt; + + /* 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->fmt = fmt; + + node->v_fmt.fmt.meta.buffersize = UNICAM_EMBEDDED_SIZE; + node->embedded_lines = 1; + node->v_fmt.type = V4L2_BUF_TYPE_META_CAPTURE; + } +} + +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; + int ret; + node->dev = unicam; node->pad_id = pad_id; - node->fmt = fmt; - /* Read current subdev format */ - unicam_reset_format(node); + if (!unicam->mc_api) { + const struct unicam_fmt *fmt; + + ret = unicam_set_default_format(unicam, node, pad_id, &fmt); + if (ret) + return ret; + node->fmt = fmt; + /* Read current subdev format */ + if (fmt) + unicam_reset_format(node); + } else { + unicam_mc_set_default_format(node, pad_id); + } - if (v4l2_subdev_has_op(unicam->sensor, video, s_std)) { + if (!unicam->mc_api && + v4l2_subdev_has_op(unicam->sensor, video, s_std)) { v4l2_std_id tvnorms; if (WARN_ON(!v4l2_subdev_has_op(unicam->sensor, video, @@ -2429,12 +2904,15 @@ static int register_node(struct unicam_device *unicam, struct unicam_node *node, 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 (!unicam->mc_api) { + /* 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 @@ -2466,7 +2944,8 @@ static int register_node(struct unicam_device *unicam, struct unicam_node *node, vdev->release = unicam_node_release; vdev->fops = &unicam_fops; - vdev->ioctl_ops = &unicam_ioctl_ops; + vdev->ioctl_ops = unicam->mc_api ? &unicam_mc_ioctl_ops : + &unicam_ioctl_ops; vdev->v4l2_dev = &unicam->v4l2_dev; vdev->vfl_dir = VFL_DIR_RX; vdev->queue = q; @@ -2474,6 +2953,10 @@ static int register_node(struct unicam_device *unicam, struct unicam_node *node, vdev->device_caps = (pad_id == IMAGE_PAD) ? V4L2_CAP_VIDEO_CAPTURE : V4L2_CAP_META_CAPTURE; vdev->device_caps |= V4L2_CAP_READWRITE | V4L2_CAP_STREAMING; + if (unicam->mc_api) { + vdev->device_caps |= V4L2_CAP_IO_MC; + vdev->entity.ops = &unicam_mc_entity_ops; + } /* Define the device names */ snprintf(vdev->name, sizeof(vdev->name), "%s-%s", UNICAM_MODULE_NAME, @@ -2493,47 +2976,61 @@ static int register_node(struct unicam_device *unicam, struct unicam_node *node, 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); + if (!unicam->mc_api) { + 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) { @@ -2551,7 +3048,8 @@ static int register_node(struct unicam_device *unicam, struct unicam_node *node, node->registered = true; if (pad_id != METADATA_PAD || unicam->sensor_embedded_data) { - ret = media_create_pad_link(&unicam->sensor->entity, pad_id, + ret = media_create_pad_link(&unicam->sensor->entity, + node->src_pad_id, &node->video_dev.entity, 0, MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE); @@ -2583,8 +3081,10 @@ static void unregister_nodes(struct unicam_device *unicam) } } -static int unicam_probe_complete(struct unicam_device *unicam) +static int unicam_async_complete(struct v4l2_async_notifier *notifier) { + struct unicam_device *unicam = to_unicam_device(notifier->v4l2_dev); + unsigned int i, source_pads = 0; int ret; unicam->v4l2_dev.notify = unicam_notify; @@ -2593,7 +3093,20 @@ static int unicam_probe_complete(struct unicam_device *unicam) if (!unicam->sensor_config) return -ENOMEM; - unicam->sensor_embedded_data = (unicam->sensor->entity.num_pads >= 2); + for (i = 0; i < unicam->sensor->entity.num_pads; i++) { + if (unicam->sensor->entity.pads[i].flags & MEDIA_PAD_FL_SOURCE) { + if (source_pads < MAX_NODES) { + unicam->node[source_pads].src_pad_id = i; + unicam_dbg(3, unicam, "source pad %u is index %u\n", + source_pads, i); + } + source_pads++; + } + } + if (!source_pads) { + unicam_err(unicam, "No source pads on sensor.\n"); + goto unregister; + } ret = register_node(unicam, &unicam->node[IMAGE_PAD], V4L2_BUF_TYPE_VIDEO_CAPTURE, IMAGE_PAD); @@ -2602,14 +3115,21 @@ static int unicam_probe_complete(struct unicam_device *unicam) 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; + if (source_pads >= 2) { + unicam->sensor_embedded_data = true; + + 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 (unicam->mc_api) + ret = v4l2_device_register_subdev_nodes(&unicam->v4l2_dev); + else + ret = v4l2_device_register_ro_subdev_nodes(&unicam->v4l2_dev); if (ret) { unicam_err(unicam, "Unable to register subdev nodes.\n"); goto unregister; @@ -2629,13 +3149,6 @@ unregister: 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, @@ -2744,7 +3257,7 @@ static int of_unicam_connect_subdevs(struct unicam_device *dev) dev->notifier.ops = &unicam_async_ops; dev->asd.match_type = V4L2_ASYNC_MATCH_FWNODE; - dev->asd.match.fwnode = of_fwnode_handle(sensor_node); + dev->asd.match.fwnode = fwnode_graph_get_remote_endpoint(of_fwnode_handle(ep_node)); ret = v4l2_async_notifier_add_subdev(&dev->notifier, &dev->asd); if (ret) { unicam_err(dev, "Error adding subdevice: %d\n", ret); @@ -2776,6 +3289,14 @@ static int unicam_probe(struct platform_device *pdev) kref_init(&unicam->kref); unicam->pdev = pdev; + /* + * Adopt the current setting of the module parameter, and check if + * device tree requests it. + */ + unicam->mc_api = media_controller; + if (of_property_read_bool(pdev->dev.of_node, "brcm,media-controller")) + unicam->mc_api = true; + unicam->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(unicam->base)) { unicam_err(unicam, "Failed to get main io block\n"); diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index 517c4499f56d3c257c0914035c6c159422e44570..2099a4d814e42ec93c08a73914e7b7912f3f9f20 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -1132,6 +1132,7 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_TEST_PATTERN_GREENR: return "Green (Red) Pixel Value"; case V4L2_CID_TEST_PATTERN_BLUE: return "Blue Pixel Value"; case V4L2_CID_TEST_PATTERN_GREENB: return "Green (Blue) Pixel Value"; + case V4L2_CID_NOTIFY_GAINS: return "Notify Gains"; /* Image processing controls */ /* Keep the order of the 'case's the same as in v4l2-controls.h! */ diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c index fbf0dcb313c820cd0d86d1b9a873767ea2b4beb1..bf3aa925245842bfdcae9987ddba9ed8e1a35972 100644 --- a/drivers/media/v4l2-core/v4l2-subdev.c +++ b/drivers/media/v4l2-core/v4l2-subdev.c @@ -768,21 +768,55 @@ int v4l2_subdev_link_validate_default(struct v4l2_subdev *sd, struct v4l2_subdev_format *source_fmt, struct v4l2_subdev_format *sink_fmt) { + bool pass = true; + /* The width, height and code must match. */ - if (source_fmt->format.width != sink_fmt->format.width - || source_fmt->format.height != sink_fmt->format.height - || source_fmt->format.code != sink_fmt->format.code) - return -EPIPE; + if (source_fmt->format.width != sink_fmt->format.width) { + dev_dbg(sd->entity.graph_obj.mdev->dev, + "%s: width does not match (source %u, sink %u)\n", + __func__, + source_fmt->format.width, sink_fmt->format.width); + pass = false; + } + + if (source_fmt->format.height != sink_fmt->format.height) { + dev_dbg(sd->entity.graph_obj.mdev->dev, + "%s: height does not match (source %u, sink %u)\n", + __func__, + source_fmt->format.height, sink_fmt->format.height); + pass = false; + } + + if (source_fmt->format.code != sink_fmt->format.code) { + dev_dbg(sd->entity.graph_obj.mdev->dev, + "%s: media bus code does not match (source 0x%8.8x, sink 0x%8.8x)\n", + __func__, + source_fmt->format.code, sink_fmt->format.code); + pass = false; + } /* The field order must match, or the sink field order must be NONE * to support interlaced hardware connected to bridges that support * progressive formats only. */ if (source_fmt->format.field != sink_fmt->format.field && - sink_fmt->format.field != V4L2_FIELD_NONE) - return -EPIPE; + sink_fmt->format.field != V4L2_FIELD_NONE) { + dev_dbg(sd->entity.graph_obj.mdev->dev, + "%s: field does not match (source %u, sink %u)\n", + __func__, + source_fmt->format.field, sink_fmt->format.field); + pass = false; + } - return 0; + if (pass) + return 0; + + dev_dbg(sd->entity.graph_obj.mdev->dev, + "%s: link was \"%s\":%u -> \"%s\":%u\n", __func__, + link->source->entity->name, link->source->index, + link->sink->entity->name, link->sink->index); + + return -EPIPE; } EXPORT_SYMBOL_GPL(v4l2_subdev_link_validate_default); diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/debug.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/debug.h index 4146faeed3449bb7f113eb655ea1a7689a0efd4f..d7d0f84f3693dd7bcd1ee9daa427b2e15dc087c4 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/debug.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/debug.h @@ -63,7 +63,12 @@ void __brcmf_err(struct brcmf_bus *bus, const char *func, const char *fmt, ...); #if defined(DEBUG) || defined(CONFIG_BRCM_TRACING) /* For debug/tracing purposes treat info messages as errors */ -#define brcmf_info brcmf_err +// #define brcmf_info brcmf_err + +#define brcmf_info(fmt, ...) \ + do { \ + pr_info("%s: " fmt, __func__, ##__VA_ARGS__); \ + } while (0) __printf(3, 4) void __brcmf_dbg(u32 level, const char *func, const char *fmt, ...); diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.c index a7554265f95f84396f9d71c88f2c8bb3bb1e9b3c..db4ac19f0800d8c664edc190bb318615f47cc884 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.c @@ -10,6 +10,7 @@ #include "debug.h" #include "core.h" #include "common.h" +#include "firmware.h" #include "of.h" void brcmf_of_probe(struct device *dev, enum brcmf_bus_type bus_type, @@ -65,3 +66,38 @@ void brcmf_of_probe(struct device *dev, enum brcmf_bus_type bus_type, sdio->oob_irq_nr = irq; sdio->oob_irq_flags = irqf; } + +struct brcmf_firmware_mapping * +brcmf_of_fwnames(struct device *dev, u32 *fwname_count) +{ + struct device_node *np = dev->of_node; + struct brcmf_firmware_mapping *fwnames; + struct device_node *map_np, *fw_np; + int of_count; + int count = 0; + + map_np = of_get_child_by_name(np, "firmwares"); + of_count = of_get_child_count(map_np); + if (!of_count) + return NULL; + + fwnames = devm_kcalloc(dev, of_count, + sizeof(struct brcmf_firmware_mapping), + GFP_KERNEL); + + for_each_child_of_node(map_np, fw_np) + { + struct brcmf_firmware_mapping *cur = &fwnames[count]; + + if (of_property_read_u32(fw_np, "chipid", &cur->chipid) || + of_property_read_u32(fw_np, "revmask", &cur->revmask)) + continue; + cur->fw_base = of_get_property(fw_np, "fw_base", NULL); + if (cur->fw_base) + count++; + } + + *fwname_count = count; + + return count ? fwnames : NULL; +} diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.h index 10bf52253337e762492f55f941b7862d88a7de73..5b39a39812d0c9e9acf2f2349dd358f5267ff97b 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.h @@ -5,9 +5,16 @@ #ifdef CONFIG_OF void brcmf_of_probe(struct device *dev, enum brcmf_bus_type bus_type, struct brcmf_mp_device *settings); +struct brcmf_firmware_mapping * +brcmf_of_fwnames(struct device *dev, u32 *map_count); #else static void brcmf_of_probe(struct device *dev, enum brcmf_bus_type bus_type, struct brcmf_mp_device *settings) { } +static struct brcmf_firmware_mapping * +brcmf_of_fwnames(struct device *dev, u32 *map_count) +{ + return NULL; +} #endif /* CONFIG_OF */ diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c index 3ab944c39677c5b06bc44aa3351dee6c2e10cb62..c8609fed3c3294a8dc7e091ce53a3bd7eb7817dc 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c @@ -35,6 +35,7 @@ #include "core.h" #include "common.h" #include "bcdc.h" +#include "of.h" #define DCMD_RESP_TIMEOUT msecs_to_jiffies(2500) #define CTL_DONE_TIMEOUT msecs_to_jiffies(2500) @@ -618,7 +619,6 @@ 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"); @@ -627,7 +627,7 @@ BRCMF_FW_DEF(4359, "brcmfmac4359-sdio"); BRCMF_FW_DEF(4373, "brcmfmac4373-sdio"); BRCMF_FW_DEF(43012, "brcmfmac43012-sdio"); -static const struct brcmf_firmware_mapping brcmf_sdio_fwnames[] = { +static const struct brcmf_firmware_mapping sdio_fwnames[] = { BRCMF_FW_ENTRY(BRCM_CC_43143_CHIP_ID, 0xFFFFFFFF, 43143), BRCMF_FW_ENTRY(BRCM_CC_43241_CHIP_ID, 0x0000001F, 43241B0), BRCMF_FW_ENTRY(BRCM_CC_43241_CHIP_ID, 0x00000020, 43241B4), @@ -641,8 +641,7 @@ static const struct brcmf_firmware_mapping brcmf_sdio_fwnames[] = { 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, 0xFFFFFFFA, 43430A1), - BRCMF_FW_ENTRY(BRCM_CC_43430_CHIP_ID, 0x00000004, 43436), + BRCMF_FW_ENTRY(BRCM_CC_43430_CHIP_ID, 0xFFFFFFFE, 43430A1), 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), @@ -652,6 +651,9 @@ static const struct brcmf_firmware_mapping brcmf_sdio_fwnames[] = { BRCMF_FW_ENTRY(CY_CC_43012_CHIP_ID, 0xFFFFFFFF, 43012) }; +static const struct brcmf_firmware_mapping *brcmf_sdio_fwnames; +static u32 brcmf_sdio_fwnames_count; + #define TXCTL_CREDITS 2 static void pkt_align(struct sk_buff *p, int len, int align) @@ -4137,7 +4139,7 @@ int brcmf_sdio_get_fwname(struct device *dev, const char *ext, u8 *fw_name, } fwreq = brcmf_fw_alloc_request(bus_if->chip, bus_if->chiprev, brcmf_sdio_fwnames, - ARRAY_SIZE(brcmf_sdio_fwnames), + brcmf_sdio_fwnames_count, fwnames, ARRAY_SIZE(fwnames)); if (!fwreq) return -ENOMEM; @@ -4193,6 +4195,9 @@ static const struct brcmf_bus_ops brcmf_sdio_bus_ops = { #define BRCMF_SDIO_FW_CODE 0 #define BRCMF_SDIO_FW_NVRAM 1 +static struct brcmf_fw_request * +brcmf_sdio_prepare_fw_request(struct brcmf_sdio *bus); + static void brcmf_sdio_firmware_callback(struct device *dev, int err, struct brcmf_fw_request *fwreq) { @@ -4208,6 +4213,22 @@ static void brcmf_sdio_firmware_callback(struct device *dev, int err, brcmf_dbg(TRACE, "Enter: dev=%s, err=%d\n", dev_name(dev), err); + if (err && brcmf_sdio_fwnames != sdio_fwnames) { + /* Try again with the standard firmware names */ + brcmf_sdio_fwnames = sdio_fwnames; + brcmf_sdio_fwnames_count = ARRAY_SIZE(sdio_fwnames); + kfree(fwreq); + fwreq = brcmf_sdio_prepare_fw_request(bus); + if (!fwreq) { + err = -ENOMEM; + goto fail; + } + err = brcmf_fw_get_firmwares(dev, fwreq, + brcmf_sdio_firmware_callback); + if (!err) + return; + } + if (err) goto fail; @@ -4415,7 +4436,7 @@ brcmf_sdio_prepare_fw_request(struct brcmf_sdio *bus) fwreq = brcmf_fw_alloc_request(bus->ci->chip, bus->ci->chiprev, brcmf_sdio_fwnames, - ARRAY_SIZE(brcmf_sdio_fwnames), + brcmf_sdio_fwnames_count, fwnames, ARRAY_SIZE(fwnames)); if (!fwreq) return NULL; @@ -4433,6 +4454,9 @@ struct brcmf_sdio *brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev) struct brcmf_sdio *bus; struct workqueue_struct *wq; struct brcmf_fw_request *fwreq; + struct brcmf_firmware_mapping *of_fwnames, *fwnames = NULL; + const int fwname_size = sizeof(struct brcmf_firmware_mapping); + u32 of_fw_count; brcmf_dbg(TRACE, "Enter\n"); @@ -4515,6 +4539,23 @@ struct brcmf_sdio *brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev) brcmf_dbg(INFO, "completed!!\n"); + brcmf_sdio_fwnames = sdio_fwnames; + brcmf_sdio_fwnames_count = ARRAY_SIZE(sdio_fwnames); + of_fwnames = brcmf_of_fwnames(sdiodev->dev, &of_fw_count); + if (of_fwnames) + fwnames = devm_kcalloc(sdiodev->dev, + of_fw_count + brcmf_sdio_fwnames_count, + fwname_size, GFP_KERNEL); + + if (fwnames) { + /* The array is scanned in order, so overrides come first */ + memcpy(fwnames, of_fwnames, of_fw_count * fwname_size); + memcpy(fwnames + of_fw_count, sdio_fwnames, + brcmf_sdio_fwnames_count * fwname_size); + brcmf_sdio_fwnames = fwnames; + brcmf_sdio_fwnames_count += of_fw_count; + } + fwreq = brcmf_sdio_prepare_fw_request(bus); if (!fwreq) { ret = -ENOMEM; diff --git a/drivers/regulator/rpi-panel-attiny-regulator.c b/drivers/regulator/rpi-panel-attiny-regulator.c index 8090b9a485b5e5808e663ee377d979072c9141dd..e3decc419814e11204ce6024189cf98d5a67e2e5 100644 --- a/drivers/regulator/rpi-panel-attiny-regulator.c +++ b/drivers/regulator/rpi-panel-attiny-regulator.c @@ -144,24 +144,8 @@ static int attiny_lcd_power_disable(struct regulator_dev *rdev) static int attiny_lcd_power_is_enabled(struct regulator_dev *rdev) { struct attiny_lcd *state = rdev_get_drvdata(rdev); - unsigned int data; - int ret, i; - - mutex_lock(&state->lock); - - for (i = 0; i < 10; i++) { - ret = regmap_read(rdev->regmap, REG_PORTC, &data); - if (!ret) - break; - usleep_range(10000, 12000); - } - - mutex_unlock(&state->lock); - - if (ret < 0) - return ret; - return data & PC_RST_BRIDGE_N; + return state->port_states[REG_PORTC - REG_PORTA] & PC_RST_BRIDGE_N; } static const struct regulator_init_data attiny_regulator_default = { @@ -250,6 +234,39 @@ static void attiny_gpio_set(struct gpio_chip *gc, unsigned int off, int val) mutex_unlock(&state->lock); } +static int attiny_i2c_read(struct i2c_client *client, u8 reg, unsigned int *buf) +{ + 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(5000, 10000); + + /* 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; + + *buf = data_buf[0]; + return 0; +} + /* * I2C driver interface functions */ @@ -280,7 +297,7 @@ static int attiny_i2c_probe(struct i2c_client *i2c, goto error; } - ret = regmap_read(regmap, REG_ID, &data); + ret = attiny_i2c_read(i2c, REG_ID, &data); if (ret < 0) { dev_err(&i2c->dev, "Failed to read REG_ID reg: %d\n", ret); goto error; diff --git a/drivers/staging/media/rpivid/rpivid_video.c b/drivers/staging/media/rpivid/rpivid_video.c index 1efaa99a55f601669f21bebae16334189a164619..93b3d86b41e39cf87f5ca264002be7a3471c2d7c 100644 --- a/drivers/staging/media/rpivid/rpivid_video.c +++ b/drivers/staging/media/rpivid/rpivid_video.c @@ -680,7 +680,6 @@ int rpivid_queue_init(void *priv, struct vb2_queue *src_vq, 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; diff --git a/drivers/staging/vc04_services/bcm2835-camera/bcm2835-camera.h b/drivers/staging/vc04_services/bcm2835-camera/bcm2835-camera.h index 75524adff0f57b4a858c1f14f9093a29ea609a75..fdbcc35ece5b4ffb6be7aa16cf90e06f12aad874 100644 --- a/drivers/staging/vc04_services/bcm2835-camera/bcm2835-camera.h +++ b/drivers/staging/vc04_services/bcm2835-camera/bcm2835-camera.h @@ -13,7 +13,7 @@ * core driver device */ -#define V4L2_CTRL_COUNT 29 /* number of v4l controls */ +#define V4L2_CTRL_COUNT 32 /* number of v4l controls */ enum { COMP_CAMERA = 0, diff --git a/drivers/staging/vc04_services/bcm2835-camera/controls.c b/drivers/staging/vc04_services/bcm2835-camera/controls.c index f3480a5c517022386d41559df8a551b613fcb51e..b1b02fbc473d938a1c75f6266d825d112c80428a 100644 --- a/drivers/staging/vc04_services/bcm2835-camera/controls.c +++ b/drivers/staging/vc04_services/bcm2835-camera/controls.c @@ -1270,6 +1270,39 @@ static const struct bm2835_mmal_v4l2_ctrl v4l2_ctrls[V4L2_CTRL_COUNT] = { .mmal_id = MMAL_PARAMETER_INTRAPERIOD, .setter = ctrl_set_video_encode_param_output, }, + { + .id = V4L2_CID_MPEG_VIDEO_H264_MIN_QP, + .type = MMAL_CONTROL_TYPE_STD, + .min = 0, + .max = 51, + .def = 0, + .step = 1, + .imenu = NULL, + .mmal_id = MMAL_PARAMETER_VIDEO_ENCODE_MIN_QUANT, + .setter = ctrl_set_video_encode_param_output, + }, + { + .id = V4L2_CID_MPEG_VIDEO_H264_MAX_QP, + .type = MMAL_CONTROL_TYPE_STD, + .min = 0, + .max = 51, + .def = 0, + .step = 1, + .imenu = NULL, + .mmal_id = MMAL_PARAMETER_VIDEO_ENCODE_MAX_QUANT, + .setter = ctrl_set_video_encode_param_output, + }, + { + .id = V4L2_CID_MPEG_VIDEO_FORCE_KEY_FRAME, + .type = MMAL_CONTROL_TYPE_STD, + .min = 0, + .max = 0, + .def = 0, + .step = 0, + .imenu = NULL, + .mmal_id = MMAL_PARAMETER_VIDEO_REQUEST_I_FRAME, + .setter = ctrl_set_video_encode_param_output, + }, }; int bm2835_mmal_set_all_camera_controls(struct bm2835_mmal_dev *dev) diff --git a/drivers/staging/vc04_services/bcm2835-codec/bcm2835-v4l2-codec.c b/drivers/staging/vc04_services/bcm2835-codec/bcm2835-v4l2-codec.c index 6078d6e2ace0193f3e9c3d9e1ed118a6c721be17..472d97d1d228c010341bc64f6ad9dcb3efa63fb7 100644 --- a/drivers/staging/vc04_services/bcm2835-codec/bcm2835-v4l2-codec.c +++ b/drivers/staging/vc04_services/bcm2835-codec/bcm2835-v4l2-codec.c @@ -88,6 +88,7 @@ enum bcm2835_codec_role { ENCODE, ISP, DEINTERLACE, + NUM_ROLES }; static const char * const roles[] = { @@ -145,7 +146,7 @@ static const char * const components[] = { struct bcm2835_codec_fmt { u32 fourcc; int depth; - int bytesperline_align; + u8 bytesperline_align[NUM_ROLES]; u32 flags; u32 mmal_fmt; int size_multiplier_x2; @@ -157,63 +158,63 @@ static const struct bcm2835_codec_fmt supported_formats[] = { /* YUV formats */ .fourcc = V4L2_PIX_FMT_YUV420, .depth = 8, - .bytesperline_align = 32, + .bytesperline_align = { 32, 64, 64, 32 }, .flags = 0, .mmal_fmt = MMAL_ENCODING_I420, .size_multiplier_x2 = 3, }, { .fourcc = V4L2_PIX_FMT_YVU420, .depth = 8, - .bytesperline_align = 32, + .bytesperline_align = { 32, 64, 64, 32 }, .flags = 0, .mmal_fmt = MMAL_ENCODING_YV12, .size_multiplier_x2 = 3, }, { .fourcc = V4L2_PIX_FMT_NV12, .depth = 8, - .bytesperline_align = 32, + .bytesperline_align = { 32, 32, 32, 32 }, .flags = 0, .mmal_fmt = MMAL_ENCODING_NV12, .size_multiplier_x2 = 3, }, { .fourcc = V4L2_PIX_FMT_NV21, .depth = 8, - .bytesperline_align = 32, + .bytesperline_align = { 32, 32, 32, 32 }, .flags = 0, .mmal_fmt = MMAL_ENCODING_NV21, .size_multiplier_x2 = 3, }, { .fourcc = V4L2_PIX_FMT_RGB565, .depth = 16, - .bytesperline_align = 32, + .bytesperline_align = { 32, 32, 32, 32 }, .flags = 0, .mmal_fmt = MMAL_ENCODING_RGB16, .size_multiplier_x2 = 2, }, { .fourcc = V4L2_PIX_FMT_YUYV, .depth = 16, - .bytesperline_align = 32, + .bytesperline_align = { 32, 32, 32, 32 }, .flags = 0, .mmal_fmt = MMAL_ENCODING_YUYV, .size_multiplier_x2 = 2, }, { .fourcc = V4L2_PIX_FMT_UYVY, .depth = 16, - .bytesperline_align = 32, + .bytesperline_align = { 32, 32, 32, 32 }, .flags = 0, .mmal_fmt = MMAL_ENCODING_UYVY, .size_multiplier_x2 = 2, }, { .fourcc = V4L2_PIX_FMT_YVYU, .depth = 16, - .bytesperline_align = 32, + .bytesperline_align = { 32, 32, 32, 32 }, .flags = 0, .mmal_fmt = MMAL_ENCODING_YVYU, .size_multiplier_x2 = 2, }, { .fourcc = V4L2_PIX_FMT_VYUY, .depth = 16, - .bytesperline_align = 32, + .bytesperline_align = { 32, 32, 32, 32 }, .flags = 0, .mmal_fmt = MMAL_ENCODING_VYUY, .size_multiplier_x2 = 2, @@ -221,21 +222,21 @@ static const struct bcm2835_codec_fmt supported_formats[] = { /* RGB formats */ .fourcc = V4L2_PIX_FMT_RGB24, .depth = 24, - .bytesperline_align = 32, + .bytesperline_align = { 32, 32, 32, 32 }, .flags = 0, .mmal_fmt = MMAL_ENCODING_RGB24, .size_multiplier_x2 = 2, }, { .fourcc = V4L2_PIX_FMT_BGR24, .depth = 24, - .bytesperline_align = 32, + .bytesperline_align = { 32, 32, 32, 32 }, .flags = 0, .mmal_fmt = MMAL_ENCODING_BGR24, .size_multiplier_x2 = 2, }, { .fourcc = V4L2_PIX_FMT_BGR32, .depth = 32, - .bytesperline_align = 32, + .bytesperline_align = { 32, 32, 32, 32 }, .flags = 0, .mmal_fmt = MMAL_ENCODING_BGRA, .size_multiplier_x2 = 2, @@ -244,7 +245,7 @@ static const struct bcm2835_codec_fmt supported_formats[] = { /* 8 bit */ .fourcc = V4L2_PIX_FMT_SRGGB8, .depth = 8, - .bytesperline_align = 32, + .bytesperline_align = { 32, 32, 32, 32 }, .flags = 0, .mmal_fmt = MMAL_ENCODING_BAYER_SRGGB8, .size_multiplier_x2 = 2, @@ -252,7 +253,7 @@ static const struct bcm2835_codec_fmt supported_formats[] = { }, { .fourcc = V4L2_PIX_FMT_SBGGR8, .depth = 8, - .bytesperline_align = 32, + .bytesperline_align = { 32, 32, 32, 32 }, .flags = 0, .mmal_fmt = MMAL_ENCODING_BAYER_SBGGR8, .size_multiplier_x2 = 2, @@ -260,7 +261,7 @@ static const struct bcm2835_codec_fmt supported_formats[] = { }, { .fourcc = V4L2_PIX_FMT_SGRBG8, .depth = 8, - .bytesperline_align = 32, + .bytesperline_align = { 32, 32, 32, 32 }, .flags = 0, .mmal_fmt = MMAL_ENCODING_BAYER_SGRBG8, .size_multiplier_x2 = 2, @@ -268,7 +269,7 @@ static const struct bcm2835_codec_fmt supported_formats[] = { }, { .fourcc = V4L2_PIX_FMT_SGBRG8, .depth = 8, - .bytesperline_align = 32, + .bytesperline_align = { 32, 32, 32, 32 }, .flags = 0, .mmal_fmt = MMAL_ENCODING_BAYER_SGBRG8, .size_multiplier_x2 = 2, @@ -277,7 +278,7 @@ static const struct bcm2835_codec_fmt supported_formats[] = { /* 10 bit */ .fourcc = V4L2_PIX_FMT_SRGGB10P, .depth = 10, - .bytesperline_align = 32, + .bytesperline_align = { 32, 32, 32, 32 }, .flags = 0, .mmal_fmt = MMAL_ENCODING_BAYER_SRGGB10P, .size_multiplier_x2 = 2, @@ -285,7 +286,7 @@ static const struct bcm2835_codec_fmt supported_formats[] = { }, { .fourcc = V4L2_PIX_FMT_SBGGR10P, .depth = 10, - .bytesperline_align = 32, + .bytesperline_align = { 32, 32, 32, 32 }, .flags = 0, .mmal_fmt = MMAL_ENCODING_BAYER_SBGGR10P, .size_multiplier_x2 = 2, @@ -293,7 +294,7 @@ static const struct bcm2835_codec_fmt supported_formats[] = { }, { .fourcc = V4L2_PIX_FMT_SGRBG10P, .depth = 10, - .bytesperline_align = 32, + .bytesperline_align = { 32, 32, 32, 32 }, .flags = 0, .mmal_fmt = MMAL_ENCODING_BAYER_SGRBG10P, .size_multiplier_x2 = 2, @@ -301,7 +302,7 @@ static const struct bcm2835_codec_fmt supported_formats[] = { }, { .fourcc = V4L2_PIX_FMT_SGBRG10P, .depth = 10, - .bytesperline_align = 32, + .bytesperline_align = { 32, 32, 32, 32 }, .flags = 0, .mmal_fmt = MMAL_ENCODING_BAYER_SGBRG10P, .size_multiplier_x2 = 2, @@ -310,7 +311,7 @@ static const struct bcm2835_codec_fmt supported_formats[] = { /* 12 bit */ .fourcc = V4L2_PIX_FMT_SRGGB12P, .depth = 12, - .bytesperline_align = 32, + .bytesperline_align = { 32, 32, 32, 32 }, .flags = 0, .mmal_fmt = MMAL_ENCODING_BAYER_SRGGB12P, .size_multiplier_x2 = 2, @@ -318,7 +319,7 @@ static const struct bcm2835_codec_fmt supported_formats[] = { }, { .fourcc = V4L2_PIX_FMT_SBGGR12P, .depth = 12, - .bytesperline_align = 32, + .bytesperline_align = { 32, 32, 32, 32 }, .flags = 0, .mmal_fmt = MMAL_ENCODING_BAYER_SBGGR12P, .size_multiplier_x2 = 2, @@ -326,7 +327,7 @@ static const struct bcm2835_codec_fmt supported_formats[] = { }, { .fourcc = V4L2_PIX_FMT_SGRBG12P, .depth = 12, - .bytesperline_align = 32, + .bytesperline_align = { 32, 32, 32, 32 }, .flags = 0, .mmal_fmt = MMAL_ENCODING_BAYER_SGRBG12P, .size_multiplier_x2 = 2, @@ -334,7 +335,7 @@ static const struct bcm2835_codec_fmt supported_formats[] = { }, { .fourcc = V4L2_PIX_FMT_SGBRG12P, .depth = 12, - .bytesperline_align = 32, + .bytesperline_align = { 32, 32, 32, 32 }, .flags = 0, .mmal_fmt = MMAL_ENCODING_BAYER_SGBRG12P, .size_multiplier_x2 = 2, @@ -343,7 +344,7 @@ static const struct bcm2835_codec_fmt supported_formats[] = { /* 14 bit */ .fourcc = V4L2_PIX_FMT_SRGGB14P, .depth = 14, - .bytesperline_align = 32, + .bytesperline_align = { 32, 32, 32, 32 }, .flags = 0, .mmal_fmt = MMAL_ENCODING_BAYER_SRGGB14P, .size_multiplier_x2 = 2, @@ -351,7 +352,7 @@ static const struct bcm2835_codec_fmt supported_formats[] = { }, { .fourcc = V4L2_PIX_FMT_SBGGR14P, .depth = 14, - .bytesperline_align = 32, + .bytesperline_align = { 32, 32, 32, 32 }, .flags = 0, .mmal_fmt = MMAL_ENCODING_BAYER_SBGGR14P, .size_multiplier_x2 = 2, @@ -360,7 +361,7 @@ static const struct bcm2835_codec_fmt supported_formats[] = { }, { .fourcc = V4L2_PIX_FMT_SGRBG14P, .depth = 14, - .bytesperline_align = 32, + .bytesperline_align = { 32, 32, 32, 32 }, .flags = 0, .mmal_fmt = MMAL_ENCODING_BAYER_SGRBG14P, .size_multiplier_x2 = 2, @@ -368,7 +369,7 @@ static const struct bcm2835_codec_fmt supported_formats[] = { }, { .fourcc = V4L2_PIX_FMT_SGBRG14P, .depth = 14, - .bytesperline_align = 32, + .bytesperline_align = { 32, 32, 32, 32 }, .flags = 0, .mmal_fmt = MMAL_ENCODING_BAYER_SGBRG14P, .size_multiplier_x2 = 2, @@ -377,7 +378,7 @@ static const struct bcm2835_codec_fmt supported_formats[] = { /* 16 bit */ .fourcc = V4L2_PIX_FMT_SRGGB16, .depth = 16, - .bytesperline_align = 32, + .bytesperline_align = { 32, 32, 32, 32 }, .flags = 0, .mmal_fmt = MMAL_ENCODING_BAYER_SRGGB16, .size_multiplier_x2 = 2, @@ -385,7 +386,7 @@ static const struct bcm2835_codec_fmt supported_formats[] = { }, { .fourcc = V4L2_PIX_FMT_SBGGR16, .depth = 16, - .bytesperline_align = 32, + .bytesperline_align = { 32, 32, 32, 32 }, .flags = 0, .mmal_fmt = MMAL_ENCODING_BAYER_SBGGR16, .size_multiplier_x2 = 2, @@ -393,7 +394,7 @@ static const struct bcm2835_codec_fmt supported_formats[] = { }, { .fourcc = V4L2_PIX_FMT_SGRBG16, .depth = 16, - .bytesperline_align = 32, + .bytesperline_align = { 32, 32, 32, 32 }, .flags = 0, .mmal_fmt = MMAL_ENCODING_BAYER_SGRBG16, .size_multiplier_x2 = 2, @@ -401,7 +402,7 @@ static const struct bcm2835_codec_fmt supported_formats[] = { }, { .fourcc = V4L2_PIX_FMT_SGBRG16, .depth = 16, - .bytesperline_align = 32, + .bytesperline_align = { 32, 32, 32, 32 }, .flags = 0, .mmal_fmt = MMAL_ENCODING_BAYER_SGBRG16, .size_multiplier_x2 = 2, @@ -411,7 +412,7 @@ static const struct bcm2835_codec_fmt supported_formats[] = { /* 10 bit */ .fourcc = V4L2_PIX_FMT_SRGGB10, .depth = 16, - .bytesperline_align = 32, + .bytesperline_align = { 32, 32, 32, 32 }, .flags = 0, .mmal_fmt = MMAL_ENCODING_BAYER_SRGGB10, .size_multiplier_x2 = 2, @@ -419,7 +420,7 @@ static const struct bcm2835_codec_fmt supported_formats[] = { }, { .fourcc = V4L2_PIX_FMT_SBGGR10, .depth = 16, - .bytesperline_align = 32, + .bytesperline_align = { 32, 32, 32, 32 }, .flags = 0, .mmal_fmt = MMAL_ENCODING_BAYER_SBGGR10, .size_multiplier_x2 = 2, @@ -427,7 +428,7 @@ static const struct bcm2835_codec_fmt supported_formats[] = { }, { .fourcc = V4L2_PIX_FMT_SGRBG10, .depth = 16, - .bytesperline_align = 32, + .bytesperline_align = { 32, 32, 32, 32 }, .flags = 0, .mmal_fmt = MMAL_ENCODING_BAYER_SGRBG10, .size_multiplier_x2 = 2, @@ -435,7 +436,7 @@ static const struct bcm2835_codec_fmt supported_formats[] = { }, { .fourcc = V4L2_PIX_FMT_SGBRG10, .depth = 16, - .bytesperline_align = 32, + .bytesperline_align = { 32, 32, 32, 32 }, .flags = 0, .mmal_fmt = MMAL_ENCODING_BAYER_SGBRG10, .size_multiplier_x2 = 2, @@ -444,7 +445,7 @@ static const struct bcm2835_codec_fmt supported_formats[] = { /* 12 bit */ .fourcc = V4L2_PIX_FMT_SRGGB12, .depth = 16, - .bytesperline_align = 32, + .bytesperline_align = { 32, 32, 32, 32 }, .flags = 0, .mmal_fmt = MMAL_ENCODING_BAYER_SRGGB12, .size_multiplier_x2 = 2, @@ -452,7 +453,7 @@ static const struct bcm2835_codec_fmt supported_formats[] = { }, { .fourcc = V4L2_PIX_FMT_SBGGR12, .depth = 16, - .bytesperline_align = 32, + .bytesperline_align = { 32, 32, 32, 32 }, .flags = 0, .mmal_fmt = MMAL_ENCODING_BAYER_SBGGR12, .size_multiplier_x2 = 2, @@ -460,7 +461,7 @@ static const struct bcm2835_codec_fmt supported_formats[] = { }, { .fourcc = V4L2_PIX_FMT_SGRBG12, .depth = 16, - .bytesperline_align = 32, + .bytesperline_align = { 32, 32, 32, 32 }, .flags = 0, .mmal_fmt = MMAL_ENCODING_BAYER_SGRBG12, .size_multiplier_x2 = 2, @@ -468,7 +469,7 @@ static const struct bcm2835_codec_fmt supported_formats[] = { }, { .fourcc = V4L2_PIX_FMT_SGBRG12, .depth = 16, - .bytesperline_align = 32, + .bytesperline_align = { 32, 32, 32, 32 }, .flags = 0, .mmal_fmt = MMAL_ENCODING_BAYER_SGBRG12, .size_multiplier_x2 = 2, @@ -477,7 +478,7 @@ static const struct bcm2835_codec_fmt supported_formats[] = { /* 14 bit */ .fourcc = V4L2_PIX_FMT_SRGGB14, .depth = 16, - .bytesperline_align = 32, + .bytesperline_align = { 32, 32, 32, 32 }, .flags = 0, .mmal_fmt = MMAL_ENCODING_BAYER_SRGGB14, .size_multiplier_x2 = 2, @@ -485,7 +486,7 @@ static const struct bcm2835_codec_fmt supported_formats[] = { }, { .fourcc = V4L2_PIX_FMT_SBGGR14, .depth = 16, - .bytesperline_align = 32, + .bytesperline_align = { 32, 32, 32, 32 }, .flags = 0, .mmal_fmt = MMAL_ENCODING_BAYER_SBGGR14, .size_multiplier_x2 = 2, @@ -493,7 +494,7 @@ static const struct bcm2835_codec_fmt supported_formats[] = { }, { .fourcc = V4L2_PIX_FMT_SGRBG14, .depth = 16, - .bytesperline_align = 32, + .bytesperline_align = { 32, 32, 32, 32 }, .flags = 0, .mmal_fmt = MMAL_ENCODING_BAYER_SGRBG14, .size_multiplier_x2 = 2, @@ -501,7 +502,7 @@ static const struct bcm2835_codec_fmt supported_formats[] = { }, { .fourcc = V4L2_PIX_FMT_SGBRG14, .depth = 16, - .bytesperline_align = 32, + .bytesperline_align = { 32, 32, 32, 32 }, .flags = 0, .mmal_fmt = MMAL_ENCODING_BAYER_SGBRG14, .size_multiplier_x2 = 2, @@ -511,7 +512,7 @@ static const struct bcm2835_codec_fmt supported_formats[] = { /* 8 bit */ .fourcc = V4L2_PIX_FMT_GREY, .depth = 8, - .bytesperline_align = 32, + .bytesperline_align = { 32, 32, 32, 32 }, .flags = 0, .mmal_fmt = MMAL_ENCODING_GREY, .size_multiplier_x2 = 2, @@ -519,7 +520,7 @@ static const struct bcm2835_codec_fmt supported_formats[] = { /* 10 bit */ .fourcc = V4L2_PIX_FMT_Y10P, .depth = 10, - .bytesperline_align = 32, + .bytesperline_align = { 32, 32, 32, 32 }, .flags = 0, .mmal_fmt = MMAL_ENCODING_Y10P, .size_multiplier_x2 = 2, @@ -527,7 +528,7 @@ static const struct bcm2835_codec_fmt supported_formats[] = { /* 12 bit */ .fourcc = V4L2_PIX_FMT_Y12P, .depth = 12, - .bytesperline_align = 32, + .bytesperline_align = { 32, 32, 32, 32 }, .flags = 0, .mmal_fmt = MMAL_ENCODING_Y12P, .size_multiplier_x2 = 2, @@ -535,7 +536,7 @@ static const struct bcm2835_codec_fmt supported_formats[] = { /* 14 bit */ .fourcc = V4L2_PIX_FMT_Y14P, .depth = 14, - .bytesperline_align = 32, + .bytesperline_align = { 32, 32, 32, 32 }, .flags = 0, .mmal_fmt = MMAL_ENCODING_Y14P, .size_multiplier_x2 = 2, @@ -543,7 +544,7 @@ static const struct bcm2835_codec_fmt supported_formats[] = { /* 16 bit */ .fourcc = V4L2_PIX_FMT_Y16, .depth = 16, - .bytesperline_align = 32, + .bytesperline_align = { 32, 32, 32, 32 }, .flags = 0, .mmal_fmt = MMAL_ENCODING_Y16, .size_multiplier_x2 = 2, @@ -551,7 +552,7 @@ static const struct bcm2835_codec_fmt supported_formats[] = { /* 10 bit as 16bpp */ .fourcc = V4L2_PIX_FMT_Y10, .depth = 16, - .bytesperline_align = 32, + .bytesperline_align = { 32, 32, 32, 32 }, .flags = 0, .mmal_fmt = MMAL_ENCODING_Y10, .size_multiplier_x2 = 2, @@ -559,7 +560,7 @@ static const struct bcm2835_codec_fmt supported_formats[] = { /* 12 bit as 16bpp */ .fourcc = V4L2_PIX_FMT_Y12, .depth = 16, - .bytesperline_align = 32, + .bytesperline_align = { 32, 32, 32, 32 }, .flags = 0, .mmal_fmt = MMAL_ENCODING_Y12, .size_multiplier_x2 = 2, @@ -567,7 +568,7 @@ static const struct bcm2835_codec_fmt supported_formats[] = { /* 14 bit as 16bpp */ .fourcc = V4L2_PIX_FMT_Y14, .depth = 16, - .bytesperline_align = 32, + .bytesperline_align = { 32, 32, 32, 32 }, .flags = 0, .mmal_fmt = MMAL_ENCODING_Y14, .size_multiplier_x2 = 2, @@ -597,11 +598,6 @@ static const struct bcm2835_codec_fmt supported_formats[] = { .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, @@ -845,9 +841,10 @@ static inline unsigned int get_sizeimage(int bpl, int width, int height, } static inline unsigned int get_bytesperline(int width, - struct bcm2835_codec_fmt *fmt) + struct bcm2835_codec_fmt *fmt, + enum bcm2835_codec_role role) { - return ALIGN((width * fmt->depth) >> 3, fmt->bytesperline_align); + return ALIGN((width * fmt->depth) >> 3, fmt->bytesperline_align[role]); } static void setup_mmal_port_format(struct bcm2835_codec_ctx *ctx, @@ -1045,7 +1042,7 @@ static void handle_fmt_changed(struct bcm2835_codec_ctx *ctx, */ q_data->selection_set = true; q_data->bytesperline = get_bytesperline(format->es.video.width, - q_data->fmt); + q_data->fmt, ctx->dev->role); q_data->height = format->es.video.height; q_data->sizeimage = format->buffer_size_min; @@ -1427,11 +1424,13 @@ static int vidioc_try_fmt(struct bcm2835_codec_ctx *ctx, struct v4l2_format *f, f->fmt.pix_mp.height = ALIGN(f->fmt.pix_mp.height, 16); } f->fmt.pix_mp.num_planes = 1; - min_bytesperline = get_bytesperline(f->fmt.pix_mp.width, fmt); + min_bytesperline = get_bytesperline(f->fmt.pix_mp.width, fmt, + ctx->dev->role); if (f->fmt.pix_mp.plane_fmt[0].bytesperline < min_bytesperline) f->fmt.pix_mp.plane_fmt[0].bytesperline = min_bytesperline; f->fmt.pix_mp.plane_fmt[0].bytesperline = - ALIGN(f->fmt.pix_mp.plane_fmt[0].bytesperline, fmt->bytesperline_align); + ALIGN(f->fmt.pix_mp.plane_fmt[0].bytesperline, + fmt->bytesperline_align[ctx->dev->role]); sizeimage = get_sizeimage(f->fmt.pix_mp.plane_fmt[0].bytesperline, f->fmt.pix_mp.width, f->fmt.pix_mp.height, @@ -1564,7 +1563,7 @@ static int vidioc_s_fmt(struct bcm2835_codec_ctx *ctx, struct v4l2_format *f, 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", + v4l2_dbg(1, debug, &ctx->dev->v4l2_dev, "Calculated bpl as %u, size %u\n", q_data->bytesperline, q_data->sizeimage); if (ctx->dev->role == DECODE && @@ -1586,7 +1585,8 @@ static int vidioc_s_fmt(struct bcm2835_codec_ctx *ctx, struct v4l2_format *f, 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); + get_bytesperline(f->fmt.pix_mp.width, q_data_dst->fmt, + ctx->dev->role); q_data_dst->sizeimage = get_sizeimage(q_data_dst->bytesperline, q_data_dst->crop_width, q_data_dst->height, @@ -1815,6 +1815,8 @@ static int vidioc_g_selection(struct file *file, void *priv, } } break; + case NUM_ROLES: + break; } return 0; @@ -1925,6 +1927,8 @@ static int vidioc_s_selection(struct file *file, void *priv, } break; } + case NUM_ROLES: + break; } return 0; @@ -2192,6 +2196,28 @@ static int bcm2835_codec_s_ctrl(struct v4l2_ctrl *ctrl) ret = bcm2835_codec_set_level_profile(ctx, ctrl); break; + case V4L2_CID_MPEG_VIDEO_H264_MIN_QP: + if (!ctx->component) + break; + + ret = vchiq_mmal_port_parameter_set(ctx->dev->instance, + &ctx->component->output[0], + MMAL_PARAMETER_VIDEO_ENCODE_MIN_QUANT, + &ctrl->val, + sizeof(ctrl->val)); + break; + + case V4L2_CID_MPEG_VIDEO_H264_MAX_QP: + if (!ctx->component) + break; + + ret = vchiq_mmal_port_parameter_set(ctx->dev->instance, + &ctx->component->output[0], + MMAL_PARAMETER_VIDEO_ENCODE_MAX_QUANT, + &ctrl->val, + sizeof(ctrl->val)); + break; + case V4L2_CID_MPEG_VIDEO_FORCE_KEY_FRAME: { u32 mmal_bool = 1; @@ -3070,7 +3096,8 @@ static int bcm2835_codec_open(struct file *file) 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].fmt, + dev->role); ctx->q_data[V4L2_M2M_SRC].sizeimage = get_sizeimage(ctx->q_data[V4L2_M2M_SRC].bytesperline, ctx->q_data[V4L2_M2M_SRC].crop_width, @@ -3083,7 +3110,8 @@ static int bcm2835_codec_open(struct file *file) 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].fmt, + dev->role); ctx->q_data[V4L2_M2M_DST].sizeimage = get_sizeimage(ctx->q_data[V4L2_M2M_DST].bytesperline, ctx->q_data[V4L2_M2M_DST].crop_width, @@ -3108,7 +3136,7 @@ static int bcm2835_codec_open(struct file *file) case ENCODE: { /* Encode controls */ - v4l2_ctrl_handler_init(hdl, 9); + v4l2_ctrl_handler_init(hdl, 11); v4l2_ctrl_new_std_menu(hdl, &bcm2835_codec_ctrl_ops, V4L2_CID_MPEG_VIDEO_BITRATE_MODE, @@ -3156,6 +3184,14 @@ static int bcm2835_codec_open(struct file *file) 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_H264_MIN_QP, + 0, 51, + 1, 20); + v4l2_ctrl_new_std(hdl, &bcm2835_codec_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_MAX_QP, + 0, 51, + 1, 51); v4l2_ctrl_new_std(hdl, &bcm2835_codec_ctrl_ops, V4L2_CID_MPEG_VIDEO_FORCE_KEY_FRAME, 0, 0, 0, 0); @@ -3205,6 +3241,8 @@ static int bcm2835_codec_open(struct file *file) v4l2_ctrl_handler_init(hdl, 0); } break; + case NUM_ROLES: + break; } ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev, ctx, &queue_init); diff --git a/drivers/staging/vc04_services/bcm2835-isp/bcm2835-isp-fmts.h b/drivers/staging/vc04_services/bcm2835-isp/bcm2835-isp-fmts.h index f6d6c88d13ba95c6247dede59cb7cfbccba0c868..a545dbf2b5dd3a766928b73640ba7a7d0bd59929 100644 --- a/drivers/staging/vc04_services/bcm2835-isp/bcm2835-isp-fmts.h +++ b/drivers/staging/vc04_services/bcm2835-isp/bcm2835-isp-fmts.h @@ -544,6 +544,7 @@ static const struct bcm2835_isp_fmt supported_formats[] = { .step_size = 2, }, { .fourcc = V4L2_META_FMT_BCM2835_ISP_STATS, + .depth = 8, .mmal_fmt = MMAL_ENCODING_BRCM_STATS, /* The rest are not valid fields for stats. */ } diff --git a/drivers/staging/vc04_services/bcm2835-isp/bcm2835-v4l2-isp.c b/drivers/staging/vc04_services/bcm2835-isp/bcm2835-v4l2-isp.c index 08dce8bba9bb041dda04e7fe324958509acde853..42c43438303cfe0a5c3d790b31263937c87cdce7 100644 --- a/drivers/staging/vc04_services/bcm2835-isp/bcm2835-v4l2-isp.c +++ b/drivers/staging/vc04_services/bcm2835-isp/bcm2835-v4l2-isp.c @@ -26,13 +26,19 @@ #include "bcm2835-isp-ctrls.h" #include "bcm2835-isp-fmts.h" +/* + * We want to instantiate 2 independent instances allowing 2 simultaneous users + * of the ISP hardware. + */ +#define BCM2835_ISP_NUM_INSTANCES 2 + 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"); +static unsigned int video_nr[BCM2835_ISP_NUM_INSTANCES] = { 13, 20 }; +module_param_array(video_nr, uint, NULL, 0644); +MODULE_PARM_DESC(video_nr, "base video device numbers"); #define BCM2835_ISP_NAME "bcm2835-isp" #define BCM2835_ISP_ENTITY_NAME_LEN 32 @@ -1032,7 +1038,9 @@ static int bcm2835_isp_node_try_fmt(struct file *file, void *priv, /* 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; + /* RAW counts as sRGB here so that we get full range. */ + is_rgb = f->fmt.pix.colorspace == V4L2_COLORSPACE_SRGB || + f->fmt.pix.colorspace == V4L2_COLORSPACE_RAW; f->fmt.pix.quantization = V4L2_MAP_QUANTIZATION_DEFAULT(is_rgb, f->fmt.pix.colorspace, f->fmt.pix.ycbcr_enc); @@ -1277,6 +1285,7 @@ static int bcm2835_isp_get_supported_fmts(struct bcm2835_isp_node *node) * or output nodes. */ static int register_node(struct bcm2835_isp_dev *dev, + unsigned int instance, struct bcm2835_isp_node *node, int index) { @@ -1285,6 +1294,7 @@ static int register_node(struct bcm2835_isp_dev *dev, int ret; mutex_init(&node->lock); + mutex_init(&node->queue_lock); node->dev = dev; vfd = &node->vfd; @@ -1437,7 +1447,7 @@ static int register_node(struct bcm2835_isp_dev *dev, 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); + ret = video_register_device(vfd, VFL_TYPE_VIDEO, video_nr[instance]); if (ret) { v4l2_err(&dev->v4l2_dev, "Failed to register video %s[%d] device node\n", @@ -1658,9 +1668,8 @@ done: return ret; } -static int bcm2835_isp_remove(struct platform_device *pdev) +static void bcm2835_isp_remove_instance(struct bcm2835_isp_dev *dev) { - struct bcm2835_isp_dev *dev = platform_get_drvdata(pdev); unsigned int i; media_controller_unregister(dev); @@ -1675,11 +1684,11 @@ static int bcm2835_isp_remove(struct platform_device *pdev) dev->component); vchiq_mmal_finalise(dev->mmal_instance); - - return 0; } -static int bcm2835_isp_probe(struct platform_device *pdev) +static int bcm2835_isp_probe_instance(struct platform_device *pdev, + struct bcm2835_isp_dev **dev_int, + unsigned int instance) { struct bcm2835_isp_dev *dev; unsigned int i; @@ -1689,6 +1698,7 @@ static int bcm2835_isp_probe(struct platform_device *pdev) if (!dev) return -ENOMEM; + *dev_int = dev; dev->dev = &pdev->dev; ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev); @@ -1706,7 +1716,7 @@ static int bcm2835_isp_probe(struct platform_device *pdev) if (ret) { v4l2_err(&dev->v4l2_dev, "%s: failed to create ril.isp component\n", __func__); - goto error; + return ret; } if (dev->component->inputs < BCM2835_ISP_NUM_OUTPUTS || @@ -1718,7 +1728,7 @@ static int bcm2835_isp_probe(struct platform_device *pdev) BCM2835_ISP_NUM_OUTPUTS, dev->component->outputs, BCM2835_ISP_NUM_CAPTURES + BCM2835_ISP_NUM_METADATA); - goto error; + return -EINVAL; } atomic_set(&dev->num_streaming, 0); @@ -1726,17 +1736,55 @@ static int bcm2835_isp_probe(struct platform_device *pdev) for (i = 0; i < BCM2835_ISP_NUM_NODES; i++) { struct bcm2835_isp_node *node = &dev->node[i]; - ret = register_node(dev, node, i); + ret = register_node(dev, instance, node, i); if (ret) - goto error; + return ret; } ret = media_controller_register(dev); if (ret) - goto error; + return ret; + + return 0; +} + +static int bcm2835_isp_remove(struct platform_device *pdev) +{ + struct bcm2835_isp_dev **bcm2835_isp_instances; + unsigned int i; + + bcm2835_isp_instances = platform_get_drvdata(pdev); + for (i = 0; i < BCM2835_ISP_NUM_INSTANCES; i++) { + if (bcm2835_isp_instances[i]) + bcm2835_isp_remove_instance(bcm2835_isp_instances[i]); + } + + return 0; +} + +static int bcm2835_isp_probe(struct platform_device *pdev) +{ + struct bcm2835_isp_dev **bcm2835_isp_instances; + unsigned int i; + int ret; + + bcm2835_isp_instances = devm_kzalloc(&pdev->dev, + sizeof(bcm2835_isp_instances) * + BCM2835_ISP_NUM_INSTANCES, + GFP_KERNEL); + if (!bcm2835_isp_instances) + return -ENOMEM; + + platform_set_drvdata(pdev, bcm2835_isp_instances); + + for (i = 0; i < BCM2835_ISP_NUM_INSTANCES; i++) { + ret = bcm2835_isp_probe_instance(pdev, + &bcm2835_isp_instances[i], i); + if (ret) + goto error; + } - platform_set_drvdata(pdev, dev); - v4l2_info(&dev->v4l2_dev, "Loaded V4L2 %s\n", BCM2835_ISP_NAME); + dev_info(&pdev->dev, "Loaded V4L2 %s\n", BCM2835_ISP_NAME); return 0; error: diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index 796122ac737bb0a4db351a1cc5497ad2c5d059c9..dce3990ea33bac71c520ba8d048b4746a2927969 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -98,6 +98,7 @@ static void xhci_free_segments_for_ring(struct xhci_hcd *xhci, */ static void xhci_link_segments(struct xhci_segment *prev, struct xhci_segment *next, + unsigned int trbs_per_seg, enum xhci_ring_type type, bool chain_links) { u32 val; @@ -106,16 +107,16 @@ static void xhci_link_segments(struct xhci_segment *prev, return; prev->next = next; if (type != TYPE_EVENT) { - prev->trbs[TRBS_PER_SEGMENT-1].link.segment_ptr = + prev->trbs[trbs_per_seg - 1].link.segment_ptr = cpu_to_le64(next->dma); /* Set the last TRB in the segment to have a TRB type ID of Link TRB */ - val = le32_to_cpu(prev->trbs[TRBS_PER_SEGMENT-1].link.control); + val = le32_to_cpu(prev->trbs[trbs_per_seg - 1].link.control); val &= ~TRB_TYPE_BITMASK; val |= TRB_TYPE(TRB_LINK); if (chain_links) val |= TRB_CHAIN; - prev->trbs[TRBS_PER_SEGMENT-1].link.control = cpu_to_le32(val); + prev->trbs[trbs_per_seg - 1].link.control = cpu_to_le32(val); } } @@ -139,15 +140,17 @@ static void xhci_link_rings(struct xhci_hcd *xhci, struct xhci_ring *ring, (xhci->quirks & XHCI_AMD_0x96_HOST))); next = ring->enq_seg->next; - xhci_link_segments(ring->enq_seg, first, ring->type, chain_links); - xhci_link_segments(last, next, ring->type, chain_links); + xhci_link_segments(ring->enq_seg, first, ring->trbs_per_seg, + ring->type, chain_links); + xhci_link_segments(last, next, ring->trbs_per_seg, + ring->type, chain_links); ring->num_segs += num_segs; - ring->num_trbs_free += (TRBS_PER_SEGMENT - 1) * num_segs; + ring->num_trbs_free += (ring->trbs_per_seg - 1) * num_segs; if (ring->type != TYPE_EVENT && ring->enq_seg == ring->last_seg) { - ring->last_seg->trbs[TRBS_PER_SEGMENT-1].link.control + ring->last_seg->trbs[ring->trbs_per_seg - 1].link.control &= ~cpu_to_le32(LINK_TOGGLE); - last->trbs[TRBS_PER_SEGMENT-1].link.control + last->trbs[ring->trbs_per_seg - 1].link.control |= cpu_to_le32(LINK_TOGGLE); ring->last_seg = last; } @@ -314,14 +317,15 @@ void xhci_initialize_ring_info(struct xhci_ring *ring, * Each segment has a link TRB, and leave an extra TRB for SW * accounting purpose */ - ring->num_trbs_free = ring->num_segs * (TRBS_PER_SEGMENT - 1) - 1; + ring->num_trbs_free = ring->num_segs * (ring->trbs_per_seg - 1) - 1; } /* Allocate segments and link them for a ring */ static int xhci_alloc_segments_for_ring(struct xhci_hcd *xhci, struct xhci_segment **first, struct xhci_segment **last, - unsigned int num_segs, unsigned int cycle_state, - enum xhci_ring_type type, unsigned int max_packet, gfp_t flags) + unsigned int num_segs, unsigned int trbs_per_seg, + unsigned int cycle_state, enum xhci_ring_type type, + unsigned int max_packet, gfp_t flags) { struct xhci_segment *prev; bool chain_links; @@ -350,12 +354,12 @@ static int xhci_alloc_segments_for_ring(struct xhci_hcd *xhci, } return -ENOMEM; } - xhci_link_segments(prev, next, type, chain_links); + xhci_link_segments(prev, next, trbs_per_seg, type, chain_links); prev = next; num_segs--; } - xhci_link_segments(prev, *first, type, chain_links); + xhci_link_segments(prev, *first, trbs_per_seg, type, chain_links); *last = prev; return 0; @@ -387,16 +391,28 @@ struct xhci_ring *xhci_ring_alloc(struct xhci_hcd *xhci, if (num_segs == 0) return ring; + ring->trbs_per_seg = TRBS_PER_SEGMENT; + /* + * The Via VL805 has a bug where cache readahead will fetch off the end + * of a page if the Link TRB of a transfer ring is in the last 4 slots. + * Where there are consecutive physical pages containing ring segments, + * this can cause a desync between the controller's view of a ring + * and the host. + */ + if (xhci->quirks & XHCI_VLI_TRB_CACHE_BUG && + type != TYPE_EVENT && type != TYPE_COMMAND) + ring->trbs_per_seg -= 4; + ret = xhci_alloc_segments_for_ring(xhci, &ring->first_seg, - &ring->last_seg, num_segs, cycle_state, type, - max_packet, flags); + &ring->last_seg, num_segs, ring->trbs_per_seg, + cycle_state, type, max_packet, flags); if (ret) goto fail; /* Only event ring does not use link TRB */ if (type != TYPE_EVENT) { /* See section 4.9.2.1 and 6.4.4.1 */ - ring->last_seg->trbs[TRBS_PER_SEGMENT - 1].link.control |= + ring->last_seg->trbs[ring->trbs_per_seg - 1].link.control |= cpu_to_le32(LINK_TOGGLE); } xhci_initialize_ring_info(ring, cycle_state); @@ -429,16 +445,15 @@ int xhci_ring_expansion(struct xhci_hcd *xhci, struct xhci_ring *ring, unsigned int num_segs_needed; int ret; - num_segs_needed = (num_trbs + (TRBS_PER_SEGMENT - 1) - 1) / - (TRBS_PER_SEGMENT - 1); - + num_segs_needed = (num_trbs + (ring->trbs_per_seg - 1) - 1) / + (ring->trbs_per_seg - 1); /* Allocate number of segments we needed, or double the ring size */ num_segs = ring->num_segs > num_segs_needed ? ring->num_segs : num_segs_needed; ret = xhci_alloc_segments_for_ring(xhci, &first, &last, - num_segs, ring->cycle_state, ring->type, - ring->bounce_buf_len, flags); + num_segs, ring->trbs_per_seg, ring->cycle_state, + ring->type, ring->bounce_buf_len, flags); if (ret) return -ENOMEM; @@ -1825,7 +1840,7 @@ int xhci_alloc_erst(struct xhci_hcd *xhci, for (val = 0; val < evt_ring->num_segs; val++) { entry = &erst->entries[val]; entry->seg_addr = cpu_to_le64(seg->dma); - entry->seg_size = cpu_to_le32(TRBS_PER_SEGMENT); + entry->seg_size = cpu_to_le32(evt_ring->trbs_per_seg); entry->rsvd = 0; seg = seg->next; } diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c index 0746ec9393e0705c0135328dc57e0c48ea9e6e10..239094b5c40bd70ca1cf7e84e2a3af3cf7a3988c 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c @@ -293,6 +293,7 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci) xhci->quirks |= XHCI_LPM_SUPPORT; xhci->quirks |= XHCI_EP_CTX_BROKEN_DCS; xhci->quirks |= XHCI_AVOID_DQ_ON_LINK; + xhci->quirks |= XHCI_VLI_TRB_CACHE_BUG; } if (pdev->vendor == PCI_VENDOR_ID_ZHAOXIN && diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index f4cbf6628c98b54879e2d4ae073d2865f238a0f8..545387a0679a9200e238c387e16fa672ec41becb 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -87,15 +87,16 @@ static bool trb_is_link(union xhci_trb *trb) return TRB_TYPE_LINK_LE32(trb->link.control); } -static bool last_trb_on_seg(struct xhci_segment *seg, union xhci_trb *trb) +static bool last_trb_on_seg(struct xhci_segment *seg, + unsigned int trbs_per_seg, union xhci_trb *trb) { - return trb == &seg->trbs[TRBS_PER_SEGMENT - 1]; + return trb == &seg->trbs[trbs_per_seg - 1]; } static bool last_trb_on_ring(struct xhci_ring *ring, struct xhci_segment *seg, union xhci_trb *trb) { - return last_trb_on_seg(seg, trb) && (seg->next == ring->first_seg); + return last_trb_on_seg(seg, ring->trbs_per_seg, trb) && (seg->next == ring->first_seg); } static bool link_trb_toggles_cycle(union xhci_trb *trb) @@ -157,7 +158,8 @@ void inc_deq(struct xhci_hcd *xhci, struct xhci_ring *ring) { /* event ring doesn't have link trbs, check for last trb */ if (ring->type == TYPE_EVENT) { - if (!last_trb_on_seg(ring->deq_seg, ring->dequeue)) { + if (!last_trb_on_seg(ring->deq_seg, ring->trbs_per_seg, + ring->dequeue)) { ring->dequeue++; goto out; } @@ -265,6 +267,12 @@ static inline int room_on_ring(struct xhci_hcd *xhci, struct xhci_ring *ring, return 0; if (ring->type != TYPE_COMMAND && ring->type != TYPE_EVENT) { + /* + * If the ring has a single segment the dequeue segment + * never changes, so don't use it as measure of free space. + */ + if (ring->num_segs == 1) + return ring->num_trbs_free >= num_trbs; num_trbs_in_deq_seg = ring->dequeue - ring->deq_seg->trbs; if (ring->num_trbs_free < num_trbs + num_trbs_in_deq_seg) return 0; @@ -2976,7 +2984,7 @@ irqreturn_t xhci_irq(struct usb_hcd *hcd) * that clears the EHB. */ while (xhci_handle_event(xhci) > 0) { - if (event_loop++ < TRBS_PER_SEGMENT / 2) + if (event_loop++ < xhci->event_ring->trbs_per_seg / 2) continue; xhci_update_erst_dequeue(xhci, event_ring_deq); event_loop = 0; diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 940c6b80967d09f1b474272e6acc97f0bd91811d..16b4a4b76da0844af8111cc444070e2e6f5878b5 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -858,8 +858,8 @@ static void xhci_clear_command_ring(struct xhci_hcd *xhci) seg = ring->deq_seg; do { memset(seg->trbs, 0, - sizeof(union xhci_trb) * (TRBS_PER_SEGMENT - 1)); - seg->trbs[TRBS_PER_SEGMENT - 1].link.control &= + sizeof(union xhci_trb) * (ring->trbs_per_seg - 1)); + seg->trbs[ring->trbs_per_seg - 1].link.control &= cpu_to_le32(~TRB_CYCLE); seg = seg->next; } while (seg != ring->deq_seg); @@ -870,7 +870,7 @@ static void xhci_clear_command_ring(struct xhci_hcd *xhci) ring->enq_seg = ring->deq_seg; ring->enqueue = ring->dequeue; - ring->num_trbs_free = ring->num_segs * (TRBS_PER_SEGMENT - 1) - 1; + ring->num_trbs_free = ring->num_segs * (ring->trbs_per_seg - 1) - 1; /* * Ring is now zeroed, so the HW should look for change of ownership * when the cycle bit is set to 1. diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 0f8542045677a5c5f3d8d4113cfb811dd45b0a2a..3a75a4dfed3c381e75e6d602a9f1787d3fce95cb 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -1615,6 +1615,7 @@ struct xhci_ring { unsigned int num_trbs_free; unsigned int num_trbs_free_temp; unsigned int bounce_buf_len; + unsigned int trbs_per_seg; enum xhci_ring_type type; bool last_td_was_short; struct radix_tree_root *trb_address_map; @@ -1888,6 +1889,7 @@ struct xhci_hcd { #define XHCI_ZHAOXIN_TRB_FETCH BIT_ULL(42) #define XHCI_EP_CTX_BROKEN_DCS BIT_ULL(43) #define XHCI_AVOID_DQ_ON_LINK BIT_ULL(44) +#define XHCI_VLI_TRB_CACHE_BUG BIT_ULL(45) unsigned int num_active_eps; unsigned int limit_active_eps; diff --git a/include/drm/drm_edid.h b/include/drm/drm_edid.h index e97daf6ffbb193a74505026492b9f29825ee365d..1d0ace87a6e89a067c5be6c4e235044f5881e2ef 100644 --- a/include/drm/drm_edid.h +++ b/include/drm/drm_edid.h @@ -371,8 +371,8 @@ drm_hdmi_vendor_infoframe_from_display_mode(struct hdmi_vendor_infoframe *frame, const struct drm_display_mode *mode); void -drm_hdmi_avi_infoframe_colorspace(struct hdmi_avi_infoframe *frame, - const struct drm_connector_state *conn_state); +drm_hdmi_avi_infoframe_colorimetry(struct hdmi_avi_infoframe *frame, + const struct drm_connector_state *conn_state); void drm_hdmi_avi_infoframe_bars(struct hdmi_avi_infoframe *frame, diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h index 0b8ae128b96181608fd99fa151fa953dd91c2224..773e24a1427d2fc24927e9f33acfbfd0cbd892c8 100644 --- a/include/uapi/linux/v4l2-controls.h +++ b/include/uapi/linux/v4l2-controls.h @@ -1083,6 +1083,7 @@ enum v4l2_jpeg_chroma_subsampling { #define V4L2_CID_TEST_PATTERN_BLUE (V4L2_CID_IMAGE_SOURCE_CLASS_BASE + 6) #define V4L2_CID_TEST_PATTERN_GREENB (V4L2_CID_IMAGE_SOURCE_CLASS_BASE + 7) #define V4L2_CID_UNIT_CELL_SIZE (V4L2_CID_IMAGE_SOURCE_CLASS_BASE + 8) +#define V4L2_CID_NOTIFY_GAINS (V4L2_CID_IMAGE_SOURCE_CLASS_BASE + 9) /* Image processing controls */ diff --git a/scripts/gcc-plugin.sh b/scripts/gcc-plugin.sh deleted file mode 100755 index c3d0c8fedbddfa23ec71d40750d781395ca938a1..0000000000000000000000000000000000000000 --- a/scripts/gcc-plugin.sh +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/sh -# SPDX-License-Identifier: GPL-2.0 -exit 1 # Disable plugins - -set -e - -srctree=$(dirname "$0") - -gccplugins_dir=$($* -print-file-name=plugin) - -# we need a c++ compiler that supports the designated initializer GNU extension -$HOSTCC -c -x c++ -std=gnu++98 - -fsyntax-only -I $srctree/gcc-plugins -I $gccplugins_dir/include 2>/dev/null <