diff --git a/0000-raspberrypi-kernel.patch b/0000-raspberrypi-kernel.patch index 705de0898a76c5df9c1c328247b4d122d59b933d..77c05006d7ddf3f758518b512358bb19755c6ed2 100644 --- a/0000-raspberrypi-kernel.patch +++ b/0000-raspberrypi-kernel.patch @@ -1,7 +1,7 @@ -From d8f3b3161e079ddf441676146b5bf8f72997bbd2 Mon Sep 17 00:00:00 2001 +From 232b56a1e85105d24c7235a5da31e3d7fc53cde3 Mon Sep 17 00:00:00 2001 From: Yafen -Date: Fri, 24 Jan 2025 17:25:52 +0800 -Subject: [PATCH] apply RPi patch of 6.6.59 (openEuler 6.6.0-75.0.0) +Date: Tue, 8 Apr 2025 15:57:43 +0800 +Subject: [PATCH] apply RPi patch of 6.6.78 (openEuler 6.6.0-84.0.0) --- .../admin-guide/media/bcm2835-isp.rst | 127 + @@ -31,6 +31,7 @@ Subject: [PATCH] apply RPi patch of 6.6.59 (openEuler 6.6.0-75.0.0) .../bindings/media/i2c/ovti,ov64a40.yaml | 98 + .../bindings/media/i2c/rohm,bu64754.yaml | 48 + .../i2c/{imx258.yaml => sony,imx258.yaml} | 9 +- + .../bindings/media/i2c/sony,imx290.yaml | 2 + .../bindings/media/i2c/sony,imx500.yaml | 132 + .../bindings/media/i2c/sony,imx708.yaml | 128 + .../bindings/media/raspberrypi,pispbe.yaml | 63 + @@ -38,7 +39,7 @@ Subject: [PATCH] apply RPi patch of 6.6.59 (openEuler 6.6.0-75.0.0) .../bindings/misc/brcm,bcm2835-smi-dev.txt | 17 + .../bindings/misc/brcm,bcm2835-smi.txt | 48 + .../bindings/mmc/snps,dwcmshc-sdhci.yaml | 5 + - .../devicetree/bindings/net/cdns,macb.yaml | 16 + + .../devicetree/bindings/net/cdns,macb.yaml | 17 + .../bindings/net/microchip,lan78xx.txt | 3 + .../bindings/pci/brcm,stb-pcie.yaml | 8 + .../devicetree/bindings/pci/brcmstb-pcie.txt | 59 + @@ -50,7 +51,7 @@ Subject: [PATCH] apply RPi patch of 6.6.59 (openEuler 6.6.0-75.0.0) .../bindings/sound/snps,designware-i2s.yaml | 4 + .../spi/raspberrypi,rp2040-gpio-bridge.yaml | 77 + .../devicetree/bindings/spi/spi-gpio.yaml | 4 + - .../devicetree/bindings/usb/snps,dwc3.yaml | 9 +- + .../devicetree/bindings/usb/snps,dwc3.yaml | 19 +- .../devicetree/bindings/vendor-prefixes.txt | 463 ++ .../devicetree/bindings/vendor-prefixes.yaml | 6 + .../devicetree/configfs-overlays.txt | 31 + @@ -99,12 +100,12 @@ Subject: [PATCH] apply RPi patch of 6.6.59 (openEuler 6.6.0-75.0.0) .../boot/dts/broadcom/bcm2710-rpi-zero-2.dts | 1 + arch/arm/boot/dts/broadcom/bcm2710.dtsi | 32 + .../arm/boot/dts/broadcom/bcm2711-rpi-4-b.dts | 258 +- - .../arm/boot/dts/broadcom/bcm2711-rpi-400.dts | 49 +- - .../arm/boot/dts/broadcom/bcm2711-rpi-cm4.dts | 499 ++ + .../arm/boot/dts/broadcom/bcm2711-rpi-400.dts | 50 +- + .../arm/boot/dts/broadcom/bcm2711-rpi-cm4.dts | 501 ++ .../boot/dts/broadcom/bcm2711-rpi-cm4s.dts | 293 + .../arm/boot/dts/broadcom/bcm2711-rpi-ds.dtsi | 567 ++ arch/arm/boot/dts/broadcom/bcm2711-rpi.dtsi | 13 + - arch/arm/boot/dts/broadcom/bcm2711.dtsi | 2 +- + arch/arm/boot/dts/broadcom/bcm2711.dtsi | 13 +- .../arm/boot/dts/broadcom/bcm271x-rpi-bt.dtsi | 42 + .../dts/broadcom/bcm283x-rpi-csi0-2lane.dtsi | 4 + .../dts/broadcom/bcm283x-rpi-csi1-2lane.dtsi | 4 + @@ -112,15 +113,15 @@ Subject: [PATCH] apply RPi patch of 6.6.59 (openEuler 6.6.0-75.0.0) .../broadcom/bcm283x-rpi-i2c0mux_0_28.dtsi | 4 + .../broadcom/bcm283x-rpi-i2c0mux_0_44.dtsi | 4 + arch/arm/boot/dts/broadcom/bcm283x.dtsi | 8 +- - arch/arm/boot/dts/overlays/Makefile | 347 + - arch/arm/boot/dts/overlays/README | 5516 +++++++++++++ + arch/arm/boot/dts/overlays/Makefile | 355 + + arch/arm/boot/dts/overlays/README | 5669 +++++++++++++ .../arm/boot/dts/overlays/act-led-overlay.dts | 28 + .../dts/overlays/adafruit-st7735r-overlay.dts | 83 + .../boot/dts/overlays/adafruit18-overlay.dts | 55 + .../dts/overlays/adau1977-adc-overlay.dts | 40 + .../dts/overlays/adau7002-simple-overlay.dts | 52 + .../arm/boot/dts/overlays/ads1015-overlay.dts | 98 + - .../arm/boot/dts/overlays/ads1115-overlay.dts | 135 + + .../arm/boot/dts/overlays/ads1115-overlay.dts | 105 + .../arm/boot/dts/overlays/ads7846-overlay.dts | 89 + .../boot/dts/overlays/adv7282m-overlay.dts | 73 + .../boot/dts/overlays/adv728x-m-overlay.dts | 37 + @@ -172,8 +173,8 @@ Subject: [PATCH] apply RPi patch of 6.6.59 (openEuler 6.6.0-75.0.0) arch/arm/boot/dts/overlays/draws-overlay.dts | 208 + .../arm/boot/dts/overlays/dwc-otg-overlay.dts | 14 + arch/arm/boot/dts/overlays/dwc2-overlay.dts | 26 + - .../boot/dts/overlays/edt-ft5406-overlay.dts | 46 + - arch/arm/boot/dts/overlays/edt-ft5406.dtsi | 49 + + .../boot/dts/overlays/edt-ft5406-overlay.dts | 22 + + arch/arm/boot/dts/overlays/edt-ft5406.dtsi | 48 + .../boot/dts/overlays/enc28j60-overlay.dts | 53 + .../dts/overlays/enc28j60-spi2-overlay.dts | 47 + .../arm/boot/dts/overlays/exc3000-overlay.dts | 48 + @@ -182,7 +183,7 @@ Subject: [PATCH] apply RPi patch of 6.6.59 (openEuler 6.6.0-75.0.0) .../boot/dts/overlays/fsm-demo-overlay.dts | 104 + arch/arm/boot/dts/overlays/gc9a01-overlay.dts | 151 + .../boot/dts/overlays/ghost-amp-overlay.dts | 145 + - arch/arm/boot/dts/overlays/goodix-overlay.dts | 46 + + arch/arm/boot/dts/overlays/goodix-overlay.dts | 49 + .../googlevoicehat-soundcard-overlay.dts | 49 + .../dts/overlays/gpio-charger-overlay.dts | 42 + .../boot/dts/overlays/gpio-fan-overlay.dts | 89 + @@ -196,7 +197,7 @@ Subject: [PATCH] apply RPi patch of 6.6.59 (openEuler 6.6.0-75.0.0) .../dts/overlays/gpio-poweroff-overlay.dts | 39 + .../dts/overlays/gpio-shutdown-overlay.dts | 86 + arch/arm/boot/dts/overlays/hat_map.dts | 124 + - .../dts/overlays/hd44780-i2c-lcd-overlay.dts | 57 + + .../dts/overlays/hd44780-i2c-lcd-overlay.dts | 59 + .../boot/dts/overlays/hd44780-lcd-overlay.dts | 46 + .../hdmi-backlight-hwhack-gpio-overlay.dts | 47 + .../dts/overlays/hifiberry-adc-overlay.dts | 45 + @@ -206,7 +207,7 @@ Subject: [PATCH] apply RPi patch of 6.6.59 (openEuler 6.6.0-75.0.0) .../dts/overlays/hifiberry-amp3-overlay.dts | 57 + .../overlays/hifiberry-amp4pro-overlay.dts | 63 + .../dts/overlays/hifiberry-dac-overlay.dts | 34 + - .../dts/overlays/hifiberry-dac8x-overlay.dts | 50 + + .../dts/overlays/hifiberry-dac8x-overlay.dts | 56 + .../overlays/hifiberry-dacplus-overlay.dts | 68 + .../hifiberry-dacplus-pro-overlay.dts | 64 + .../hifiberry-dacplus-std-overlay.dts | 65 + @@ -222,15 +223,16 @@ Subject: [PATCH] apply RPi patch of 6.6.59 (openEuler 6.6.0-75.0.0) arch/arm/boot/dts/overlays/hy28b-overlay.dts | 148 + .../boot/dts/overlays/i-sabre-q2m-overlay.dts | 39 + .../boot/dts/overlays/i2c-bcm2708-overlay.dts | 13 + - .../arm/boot/dts/overlays/i2c-fan-overlay.dts | 108 + + arch/arm/boot/dts/overlays/i2c-buses.dtsi | 67 + + .../arm/boot/dts/overlays/i2c-fan-overlay.dts | 77 + .../boot/dts/overlays/i2c-gpio-overlay.dts | 47 + - .../arm/boot/dts/overlays/i2c-mux-overlay.dts | 183 + - .../dts/overlays/i2c-pwm-pca9685a-overlay.dts | 61 + + .../arm/boot/dts/overlays/i2c-mux-overlay.dts | 149 + + .../dts/overlays/i2c-pwm-pca9685a-overlay.dts | 28 + .../arm/boot/dts/overlays/i2c-rtc-common.dtsi | 367 + .../dts/overlays/i2c-rtc-gpio-overlay.dts | 31 + - .../arm/boot/dts/overlays/i2c-rtc-overlay.dts | 42 + - .../boot/dts/overlays/i2c-sensor-common.dtsi | 597 ++ - .../boot/dts/overlays/i2c-sensor-overlay.dts | 42 + + .../arm/boot/dts/overlays/i2c-rtc-overlay.dts | 6 + + .../boot/dts/overlays/i2c-sensor-common.dtsi | 634 ++ + .../boot/dts/overlays/i2c-sensor-overlay.dts | 6 + arch/arm/boot/dts/overlays/i2c0-overlay.dts | 83 + .../boot/dts/overlays/i2c0-pi5-overlay.dts | 34 + arch/arm/boot/dts/overlays/i2c1-overlay.dts | 44 + @@ -244,20 +246,22 @@ Subject: [PATCH] apply RPi patch of 6.6.59 (openEuler 6.6.0-75.0.0) .../arm/boot/dts/overlays/i2s-dac-overlay.dts | 34 + .../dts/overlays/i2s-gpio28-31-overlay.dts | 18 + .../dts/overlays/i2s-master-dac-overlay.dts | 50 + - .../boot/dts/overlays/ilitek251x-overlay.dts | 45 + - arch/arm/boot/dts/overlays/imx219-overlay.dts | 89 + + .../boot/dts/overlays/ilitek251x-overlay.dts | 47 + + arch/arm/boot/dts/overlays/imx219-overlay.dts | 106 + arch/arm/boot/dts/overlays/imx219.dtsi | 27 + arch/arm/boot/dts/overlays/imx258-overlay.dts | 131 + arch/arm/boot/dts/overlays/imx258.dtsi | 27 + arch/arm/boot/dts/overlays/imx290-overlay.dts | 32 + - .../boot/dts/overlays/imx290_327-overlay.dtsi | 112 + + .../boot/dts/overlays/imx290_327-overlay.dtsi | 110 + arch/arm/boot/dts/overlays/imx290_327.dtsi | 24 + arch/arm/boot/dts/overlays/imx296-overlay.dts | 114 + arch/arm/boot/dts/overlays/imx327-overlay.dts | 33 + arch/arm/boot/dts/overlays/imx378-overlay.dts | 17 + - arch/arm/boot/dts/overlays/imx462-overlay.dts | 39 + + arch/arm/boot/dts/overlays/imx415-overlay.dts | 116 + + arch/arm/boot/dts/overlays/imx415.dtsi | 27 + + arch/arm/boot/dts/overlays/imx462-overlay.dts | 35 + arch/arm/boot/dts/overlays/imx477-overlay.dts | 17 + - .../boot/dts/overlays/imx477_378-overlay.dtsi | 92 + + .../boot/dts/overlays/imx477_378-overlay.dtsi | 93 + arch/arm/boot/dts/overlays/imx477_378.dtsi | 24 + arch/arm/boot/dts/overlays/imx500-overlay.dts | 122 + .../boot/dts/overlays/imx500-pi5-overlay.dts | 127 + @@ -282,7 +286,7 @@ Subject: [PATCH] apply RPi patch of 6.6.59 (openEuler 6.6.0-75.0.0) .../boot/dts/overlays/max98357a-overlay.dts | 84 + .../boot/dts/overlays/maxtherm-overlay.dts | 186 + .../boot/dts/overlays/mbed-dac-overlay.dts | 64 + - .../boot/dts/overlays/mcp23017-overlay.dts | 103 + + .../boot/dts/overlays/mcp23017-overlay.dts | 65 + .../boot/dts/overlays/mcp23s17-overlay.dts | 732 ++ .../dts/overlays/mcp2515-can0-overlay.dts | 73 + .../dts/overlays/mcp2515-can1-overlay.dts | 73 + @@ -318,12 +322,12 @@ Subject: [PATCH] apply RPi patch of 6.6.59 (openEuler 6.6.0-75.0.0) arch/arm/boot/dts/overlays/ov64a40.dtsi | 34 + arch/arm/boot/dts/overlays/ov7251-overlay.dts | 77 + arch/arm/boot/dts/overlays/ov7251.dtsi | 28 + - arch/arm/boot/dts/overlays/ov9281-overlay.dts | 78 + - arch/arm/boot/dts/overlays/ov9281.dtsi | 27 + - arch/arm/boot/dts/overlays/overlay_map.dts | 514 ++ + arch/arm/boot/dts/overlays/ov9281-overlay.dts | 88 + + arch/arm/boot/dts/overlays/ov9281.dtsi | 26 + + arch/arm/boot/dts/overlays/overlay_map.dts | 518 ++ .../arm/boot/dts/overlays/papirus-overlay.dts | 84 + - .../arm/boot/dts/overlays/pca953x-overlay.dts | 240 + - .../arm/boot/dts/overlays/pcf857x-overlay.dts | 32 + + .../arm/boot/dts/overlays/pca953x-overlay.dts | 59 + + .../arm/boot/dts/overlays/pcf857x-overlay.dts | 34 + .../dts/overlays/pcie-32bit-dma-overlay.dts | 38 + .../overlays/pcie-32bit-dma-pi5-overlay.dts | 26 + .../overlays/pciex1-compat-pi5-overlay.dts | 40 + @@ -334,12 +338,13 @@ Subject: [PATCH] apply RPi patch of 6.6.59 (openEuler 6.6.0-75.0.0) .../dts/overlays/pifi-dac-zero-overlay.dts | 49 + .../dts/overlays/pifi-mini-210-overlay.dts | 42 + arch/arm/boot/dts/overlays/piglow-overlay.dts | 97 + + arch/arm/boot/dts/overlays/pimidi-overlay.dts | 53 + .../overlays/pineboards-hat-ai-overlay.dts | 18 + .../pineboards-hatdrive-poe-plus-overlay.dts | 19 + .../boot/dts/overlays/piscreen-overlay.dts | 110 + .../boot/dts/overlays/piscreen2r-overlay.dts | 106 + .../arm/boot/dts/overlays/pisound-overlay.dts | 118 + - .../boot/dts/overlays/pisound-pi5-overlay.dts | 31 + + .../boot/dts/overlays/pisound-pi5-overlay.dts | 28 + .../arm/boot/dts/overlays/pitft22-overlay.dts | 71 + .../overlays/pitft28-capacitive-overlay.dts | 93 + .../overlays/pitft28-resistive-overlay.dts | 126 + @@ -347,14 +352,17 @@ Subject: [PATCH] apply RPi patch of 6.6.59 (openEuler 6.6.0-75.0.0) .../boot/dts/overlays/pps-gpio-overlay.dts | 39 + .../boot/dts/overlays/proto-codec-overlay.dts | 39 + .../boot/dts/overlays/pwm-2chan-overlay.dts | 48 + + .../dts/overlays/pwm-gpio-fan-overlay.dts | 170 + .../boot/dts/overlays/pwm-gpio-overlay.dts | 38 + .../boot/dts/overlays/pwm-ir-tx-overlay.dts | 40 + arch/arm/boot/dts/overlays/pwm-overlay.dts | 44 + + .../arm/boot/dts/overlays/pwm-pio-overlay.dts | 39 + arch/arm/boot/dts/overlays/pwm1-overlay.dts | 59 + .../arm/boot/dts/overlays/qca7000-overlay.dts | 55 + .../dts/overlays/qca7000-uart0-overlay.dts | 46 + .../arm/boot/dts/overlays/ramoops-overlay.dts | 25 + .../boot/dts/overlays/ramoops-pi4-overlay.dts | 25 + + .../boot/dts/overlays/rootmaster-overlay.dts | 77 + .../dts/overlays/rotary-encoder-overlay.dts | 59 + .../dts/overlays/rpi-backlight-overlay.dts | 21 + .../dts/overlays/rpi-codeczero-overlay.dts | 9 + @@ -362,6 +370,7 @@ Subject: [PATCH] apply RPi patch of 6.6.59 (openEuler 6.6.0-75.0.0) .../boot/dts/overlays/rpi-dacpro-overlay.dts | 17 + .../dts/overlays/rpi-digiampplus-overlay.dts | 17 + .../boot/dts/overlays/rpi-ft5406-overlay.dts | 25 + + .../boot/dts/overlays/rpi-fw-uart-overlay.dts | 41 + .../arm/boot/dts/overlays/rpi-poe-overlay.dts | 154 + .../dts/overlays/rpi-poe-plus-overlay.dts | 49 + .../dts/overlays/rpi-rp2040-gpio-bridge.dtsi | 21 + @@ -370,16 +379,16 @@ Subject: [PATCH] apply RPi patch of 6.6.59 (openEuler 6.6.0-75.0.0) arch/arm/boot/dts/overlays/rpi-tv-overlay.dts | 34 + .../rra-digidac1-wm8741-audio-overlay.dts | 49 + .../boot/dts/overlays/sainsmart18-overlay.dts | 52 + - .../dts/overlays/sc16is750-i2c-overlay.dts | 57 + + .../dts/overlays/sc16is750-i2c-overlay.dts | 58 + .../dts/overlays/sc16is750-spi0-overlay.dts | 63 + - .../dts/overlays/sc16is752-i2c-overlay.dts | 57 + + .../dts/overlays/sc16is752-i2c-overlay.dts | 58 + .../dts/overlays/sc16is752-spi0-overlay.dts | 63 + .../dts/overlays/sc16is752-spi1-overlay.dts | 76 + arch/arm/boot/dts/overlays/sdhost-overlay.dts | 38 + arch/arm/boot/dts/overlays/sdio-overlay.dts | 77 + .../boot/dts/overlays/sdio-pi5-overlay.dts | 24 + .../overlays/seeed-can-fd-hat-v1-overlay.dts | 138 + - .../overlays/seeed-can-fd-hat-v2-overlay.dts | 117 + + .../overlays/seeed-can-fd-hat-v2-overlay.dts | 112 + .../boot/dts/overlays/sh1106-spi-overlay.dts | 84 + .../boot/dts/overlays/si446x-spi0-overlay.dts | 53 + .../arm/boot/dts/overlays/smi-dev-overlay.dts | 20 + @@ -389,6 +398,7 @@ Subject: [PATCH] apply RPi patch of 6.6.59 (openEuler 6.6.0-75.0.0) .../dts/overlays/spi-gpio40-45-overlay.dts | 36 + .../arm/boot/dts/overlays/spi-rtc-overlay.dts | 75 + .../boot/dts/overlays/spi0-0cs-overlay.dts | 39 + + .../overlays/spi0-1cs-inverted-overlay.dts | 59 + .../boot/dts/overlays/spi0-1cs-overlay.dts | 42 + .../boot/dts/overlays/spi0-2cs-overlay.dts | 37 + .../boot/dts/overlays/spi1-1cs-overlay.dts | 57 + @@ -439,43 +449,44 @@ Subject: [PATCH] apply RPi patch of 6.6.59 (openEuler 6.6.0-75.0.0) .../dts/overlays/ugreen-dabboard-overlay.dts | 49 + .../boot/dts/overlays/upstream-overlay.dts | 101 + .../dts/overlays/upstream-pi4-overlay.dts | 137 + - .../dts/overlays/vc4-fkms-v3d-overlay.dts | 46 + - .../dts/overlays/vc4-fkms-v3d-pi4-overlay.dts | 50 + - .../overlays/vc4-kms-dpi-generic-overlay.dts | 82 + + .../dts/overlays/vc4-fkms-v3d-overlay.dts | 43 + + .../dts/overlays/vc4-fkms-v3d-pi4-overlay.dts | 47 + + .../overlays/vc4-kms-dpi-generic-overlay.dts | 83 + .../dts/overlays/vc4-kms-dpi-hyperpixel.dtsi | 94 + .../vc4-kms-dpi-hyperpixel2r-overlay.dts | 114 + .../vc4-kms-dpi-hyperpixel4-overlay.dts | 57 + .../vc4-kms-dpi-hyperpixel4sq-overlay.dts | 36 + .../overlays/vc4-kms-dpi-panel-overlay.dts | 69 + arch/arm/boot/dts/overlays/vc4-kms-dpi.dtsi | 111 + - .../overlays/vc4-kms-dsi-7inch-overlay.dts | 124 + + .../overlays/vc4-kms-dsi-7inch-overlay.dts | 121 + .../overlays/vc4-kms-dsi-generic-overlay.dts | 106 + .../vc4-kms-dsi-ili9881-5inch-overlay.dts | 122 + - .../vc4-kms-dsi-ili9881-7inch-overlay.dts | 122 + + .../vc4-kms-dsi-ili9881-7inch-overlay.dts | 123 + .../vc4-kms-dsi-lt070me05000-overlay.dts | 69 + .../vc4-kms-dsi-lt070me05000-v2-overlay.dts | 64 + - .../vc4-kms-dsi-waveshare-800x480-overlay.dts | 119 + - .../vc4-kms-dsi-waveshare-panel-overlay.dts | 133 + + .../vc4-kms-dsi-waveshare-800x480-overlay.dts | 116 + + .../vc4-kms-dsi-waveshare-panel-overlay.dts | 143 + .../overlays/vc4-kms-kippah-7inch-overlay.dts | 26 + - .../boot/dts/overlays/vc4-kms-v3d-overlay.dts | 124 + - .../dts/overlays/vc4-kms-v3d-pi4-overlay.dts | 200 + + .../boot/dts/overlays/vc4-kms-v3d-overlay.dts | 121 + + .../dts/overlays/vc4-kms-v3d-pi4-overlay.dts | 197 + .../dts/overlays/vc4-kms-v3d-pi5-overlay.dts | 147 + .../dts/overlays/vc4-kms-vga666-overlay.dts | 107 + arch/arm/boot/dts/overlays/vga666-overlay.dts | 30 + arch/arm/boot/dts/overlays/vl805-overlay.dts | 18 + .../arm/boot/dts/overlays/w1-gpio-overlay.dts | 40 + - .../boot/dts/overlays/w1-gpio-pi5-overlay.dts | 15 + + .../boot/dts/overlays/w1-gpio-pi5-overlay.dts | 12 + .../dts/overlays/w1-gpio-pullup-overlay.dts | 42 + - .../overlays/w1-gpio-pullup-pi5-overlay.dts | 15 + + .../overlays/w1-gpio-pullup-pi5-overlay.dts | 12 + arch/arm/boot/dts/overlays/w5500-overlay.dts | 63 + .../overlays/watterott-display-overlay.dts | 150 + .../waveshare-can-fd-hat-mode-a-overlay.dts | 140 + .../waveshare-can-fd-hat-mode-b-overlay.dts | 103 + .../arm/boot/dts/overlays/wittypi-overlay.dts | 44 + .../dts/overlays/wm8960-soundcard-overlay.dts | 82 + - arch/arm/configs/bcm2709_defconfig | 1601 ++++ - arch/arm/configs/bcm2711_defconfig | 1633 ++++ - arch/arm/configs/bcmrpi_defconfig | 1594 ++++ + .../boot/dts/overlays/ws2812-pio-overlay.dts | 46 + + arch/arm/configs/bcm2709_defconfig | 1607 ++++ + arch/arm/configs/bcm2711_defconfig | 1642 ++++ + arch/arm/configs/bcmrpi_defconfig | 1599 ++++ arch/arm/include/asm/cacheflush.h | 21 + arch/arm/include/asm/glue-cache.h | 2 + arch/arm/include/asm/irqflags.h | 16 +- @@ -519,19 +530,19 @@ Subject: [PATCH] apply RPi patch of 6.6.59 (openEuler 6.6.0-75.0.0) .../boot/dts/broadcom/bcm2712-rpi-cm4io.dtsi | 28 + .../dts/broadcom/bcm2712-rpi-cm5-cm4io.dts | 5 + .../dts/broadcom/bcm2712-rpi-cm5-cm5io.dts | 5 + - .../boot/dts/broadcom/bcm2712-rpi-cm5.dtsi | 754 ++ + .../boot/dts/broadcom/bcm2712-rpi-cm5.dtsi | 769 ++ .../boot/dts/broadcom/bcm2712-rpi-cm5io.dtsi | 14 + .../dts/broadcom/bcm2712-rpi-cm5l-cm4io.dts | 5 + .../dts/broadcom/bcm2712-rpi-cm5l-cm5io.dts | 5 + - .../boot/dts/broadcom/bcm2712-rpi-cm5l.dtsi | 22 + - arch/arm64/boot/dts/broadcom/bcm2712-rpi.dtsi | 466 ++ - arch/arm64/boot/dts/broadcom/bcm2712.dtsi | 1308 +++ + .../boot/dts/broadcom/bcm2712-rpi-cm5l.dtsi | 21 + + arch/arm64/boot/dts/broadcom/bcm2712-rpi.dtsi | 475 ++ + arch/arm64/boot/dts/broadcom/bcm2712.dtsi | 1306 +++ .../boot/dts/broadcom/bcm2712d0-rpi-5-b.dts | 107 + - arch/arm64/boot/dts/broadcom/rp1.dtsi | 1289 +++ + arch/arm64/boot/dts/broadcom/rp1.dtsi | 1323 +++ arch/arm64/boot/dts/overlays | 1 + - arch/arm64/configs/bcm2711_defconfig | 1698 ++++ - arch/arm64/configs/bcm2712_defconfig | 1701 ++++ - arch/arm64/configs/bcmrpi3_defconfig | 1575 ++++ + arch/arm64/configs/bcm2711_defconfig | 1713 ++++ + arch/arm64/configs/bcm2712_defconfig | 1715 ++++ + arch/arm64/configs/bcmrpi3_defconfig | 1580 ++++ arch/arm64/crypto/aes-cipher-glue.c | 11 + arch/arm64/crypto/aes-glue.c | 4 +- arch/arm64/crypto/aes-neonbs-glue.c | 5 - @@ -566,19 +577,22 @@ Subject: [PATCH] apply RPi patch of 6.6.59 (openEuler 6.6.0-75.0.0) drivers/clk/clk-hifiberry-dachd.c | 331 + drivers/clk/clk-hifiberry-dacpro.c | 181 + drivers/clk/clk-rp1-sdio.c | 600 ++ - drivers/clk/clk-rp1.c | 2500 ++++++ - drivers/dma-buf/heaps/system_heap.c | 10 +- + drivers/clk/clk-rp1.c | 2492 ++++++ + drivers/dma-buf/heaps/system_heap.c | 13 +- drivers/dma/Kconfig | 4 + drivers/dma/Makefile | 1 + drivers/dma/bcm2708-dmaengine.c | 281 + drivers/dma/bcm2835-dma.c | 735 +- - .../dma/dw-axi-dmac/dw-axi-dmac-platform.c | 178 +- + .../dma/dw-axi-dmac/dw-axi-dmac-platform.c | 194 +- + drivers/firmware/Kconfig | 9 + + drivers/firmware/Makefile | 1 + drivers/firmware/psci/psci.c | 9 +- drivers/firmware/raspberrypi.c | 149 +- + drivers/firmware/rp1.c | 310 + drivers/gpio/Kconfig | 25 +- drivers/gpio/Makefile | 3 + drivers/gpio/gpio-bcm-virt.c | 214 + - drivers/gpio/gpio-brcmstb.c | 35 +- + drivers/gpio/gpio-brcmstb.c | 37 +- drivers/gpio/gpio-fsm.c | 1212 +++ drivers/gpio/gpio-mmio.c | 124 +- drivers/gpio/gpio-pca953x.c | 1 + @@ -587,18 +601,24 @@ Subject: [PATCH] apply RPi patch of 6.6.59 (openEuler 6.6.0-75.0.0) drivers/gpu/drm/Kconfig | 2 + drivers/gpu/drm/Makefile | 1 + drivers/gpu/drm/bridge/Kconfig | 1 + - drivers/gpu/drm/bridge/panel.c | 6 + + drivers/gpu/drm/bridge/panel.c | 8 + drivers/gpu/drm/bridge/tc358762.c | 26 +- + drivers/gpu/drm/drm_atomic.c | 2 +- drivers/gpu/drm/drm_atomic_helper.c | 18 +- drivers/gpu/drm/drm_atomic_state_helper.c | 14 + - drivers/gpu/drm/drm_atomic_uapi.c | 19 + + drivers/gpu/drm/drm_atomic_uapi.c | 29 + + drivers/gpu/drm/drm_blend.c | 51 +- drivers/gpu/drm/drm_bridge.c | 14 + drivers/gpu/drm/drm_color_mgmt.c | 40 +- - drivers/gpu/drm/drm_connector.c | 77 +- + drivers/gpu/drm/drm_connector.c | 80 +- drivers/gpu/drm/drm_fb_helper.c | 11 +- + drivers/gpu/drm/drm_framebuffer.c | 2 +- + drivers/gpu/drm/drm_mode_config.c | 2 +- drivers/gpu/drm/drm_modes.c | 5 +- + drivers/gpu/drm/drm_plane.c | 2 +- drivers/gpu/drm/drm_probe_helper.c | 5 +- drivers/gpu/drm/i915/display/intel_display.c | 13 + + drivers/gpu/drm/imx/ipuv3/ipuv3-crtc.c | 2 +- drivers/gpu/drm/msm/msm_atomic.c | 2 + drivers/gpu/drm/panel/Kconfig | 32 + drivers/gpu/drm/panel/Makefile | 3 + @@ -609,15 +629,16 @@ Subject: [PATCH] apply RPi patch of 6.6.59 (openEuler 6.6.0-75.0.0) drivers/gpu/drm/panel/panel-simple.c | 238 +- drivers/gpu/drm/panel/panel-sitronix-st7701.c | 407 +- drivers/gpu/drm/panel/panel-tdo-y17p.c | 277 + - drivers/gpu/drm/panel/panel-waveshare-dsi.c | 488 ++ + drivers/gpu/drm/panel/panel-waveshare-dsi.c | 609 ++ drivers/gpu/drm/rp1/Kconfig | 5 + drivers/gpu/drm/rp1/Makefile | 4 + - drivers/gpu/drm/rp1/rp1-dpi/Kconfig | 11 + + drivers/gpu/drm/rp1/rp1-dpi/Kconfig | 16 + drivers/gpu/drm/rp1/rp1-dpi/Makefile | 5 + - drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi.c | 417 + - drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi.h | 69 + + drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi.c | 480 ++ + drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi.h | 95 + drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi_cfg.c | 510 ++ - drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi_hw.c | 504 ++ + drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi_hw.c | 668 ++ + drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi_pio.c | 241 + drivers/gpu/drm/rp1/rp1-dsi/Kconfig | 14 + drivers/gpu/drm/rp1/rp1-dsi/Makefile | 5 + drivers/gpu/drm/rp1/rp1-dsi/rp1_dsi.c | 541 ++ @@ -653,22 +674,22 @@ Subject: [PATCH] apply RPi patch of 6.6.59 (openEuler 6.6.0-75.0.0) drivers/gpu/drm/vc4/vc4_debugfs.c | 3 +- drivers/gpu/drm/vc4/vc4_dpi.c | 22 + drivers/gpu/drm/vc4/vc4_drv.c | 90 +- - drivers/gpu/drm/vc4/vc4_drv.h | 172 +- - drivers/gpu/drm/vc4/vc4_dsi.c | 151 +- + drivers/gpu/drm/vc4/vc4_drv.h | 173 +- + drivers/gpu/drm/vc4/vc4_dsi.c | 213 +- drivers/gpu/drm/vc4/vc4_firmware_kms.c | 2077 +++++ drivers/gpu/drm/vc4/vc4_gem.c | 24 +- - drivers/gpu/drm/vc4/vc4_hdmi.c | 297 +- + drivers/gpu/drm/vc4/vc4_hdmi.c | 295 +- drivers/gpu/drm/vc4/vc4_hdmi.h | 43 + drivers/gpu/drm/vc4/vc4_hdmi_phy.c | 647 ++ drivers/gpu/drm/vc4/vc4_hdmi_regs.h | 222 +- - drivers/gpu/drm/vc4/vc4_hvs.c | 1867 ++++- + drivers/gpu/drm/vc4/vc4_hvs.c | 1669 +++- drivers/gpu/drm/vc4/vc4_irq.c | 10 +- drivers/gpu/drm/vc4/vc4_kms.c | 173 +- drivers/gpu/drm/vc4/vc4_perfmon.c | 20 +- - drivers/gpu/drm/vc4/vc4_plane.c | 1281 ++- + drivers/gpu/drm/vc4/vc4_plane.c | 1320 ++- drivers/gpu/drm/vc4/vc4_regs.h | 357 +- drivers/gpu/drm/vc4/vc4_render_cl.c | 2 +- - drivers/gpu/drm/vc4/vc4_txp.c | 91 +- + drivers/gpu/drm/vc4/vc4_txp.c | 119 +- drivers/gpu/drm/vc4/vc4_v3d.c | 12 +- drivers/gpu/drm/vc4/vc4_validate.c | 8 +- drivers/gpu/drm/vc4/vc4_validate_shaders.c | 2 +- @@ -696,6 +717,7 @@ Subject: [PATCH] apply RPi patch of 6.6.59 (openEuler 6.6.0-75.0.0) drivers/i2c/busses/i2c-gpio.c | 4 +- drivers/i2c/i2c-mux.c | 5 + drivers/iio/adc/mcp3422.c | 9 +- + drivers/iio/humidity/dht11.c | 6 +- drivers/iio/light/tsl4531.c | 7 + drivers/iio/light/veml6070.c | 7 + drivers/input/joystick/sensehat-joystick.c | 2 +- @@ -703,6 +725,7 @@ Subject: [PATCH] apply RPi patch of 6.6.59 (openEuler 6.6.0-75.0.0) drivers/input/touchscreen/edt-ft5x06.c | 124 +- drivers/input/touchscreen/goodix.c | 75 +- drivers/input/touchscreen/goodix.h | 5 + + drivers/input/touchscreen/ili210x.c | 63 +- drivers/iommu/Kconfig | 7 + drivers/iommu/Makefile | 2 +- drivers/iommu/bcm2712-iommu-cache.c | 77 + @@ -720,21 +743,26 @@ Subject: [PATCH] apply RPi patch of 6.6.59 (openEuler 6.6.0-75.0.0) drivers/leds/trigger/Makefile | 2 + drivers/leds/trigger/ledtrig-actpwr.c | 190 + drivers/leds/trigger/ledtrig-input.c | 55 + + drivers/mailbox/Kconfig | 9 + + drivers/mailbox/Makefile | 2 + drivers/mailbox/bcm2835-mailbox.c | 18 +- + drivers/mailbox/rp1-mailbox.c | 206 + .../media/common/videobuf2/videobuf2-core.c | 21 +- drivers/media/i2c/Kconfig | 127 + drivers/media/i2c/Makefile | 11 + drivers/media/i2c/ad5398_vcm.c | 340 + drivers/media/i2c/adv7180.c | 88 +- - drivers/media/i2c/arducam-pivariety.c | 1472 ++++ + drivers/media/i2c/arducam-pivariety.c | 1475 ++++ drivers/media/i2c/arducam-pivariety.h | 110 + drivers/media/i2c/arducam_64mp.c | 2618 ++++++ drivers/media/i2c/bu64754.c | 315 + drivers/media/i2c/dw9807-vcm.c | 227 +- - drivers/media/i2c/imx219.c | 330 +- + drivers/media/i2c/imx219.c | 413 +- drivers/media/i2c/imx258.c | 799 +- + drivers/media/i2c/imx290.c | 89 +- drivers/media/i2c/imx296.c | 166 +- - drivers/media/i2c/imx477.c | 2339 ++++++ + drivers/media/i2c/imx415.c | 442 +- + drivers/media/i2c/imx477.c | 2387 ++++++ drivers/media/i2c/imx500.c | 3227 ++++++++ drivers/media/i2c/imx519.c | 2146 +++++ drivers/media/i2c/imx708.c | 2116 +++++ @@ -751,42 +779,44 @@ Subject: [PATCH] apply RPi patch of 6.6.59 (openEuler 6.6.0-75.0.0) drivers/media/pci/Makefile | 3 +- drivers/media/pci/hailo/Kconfig | 6 + drivers/media/pci/hailo/Makefile | 34 + - drivers/media/pci/hailo/common/fw_operation.c | 103 + - drivers/media/pci/hailo/common/fw_operation.h | 25 + + drivers/media/pci/hailo/common/fw_operation.c | 147 + + drivers/media/pci/hailo/common/fw_operation.h | 29 + .../media/pci/hailo/common/fw_validation.c | 114 + - .../media/pci/hailo/common/fw_validation.h | 65 + - .../pci/hailo/common/hailo_ioctl_common.h | 669 ++ + .../media/pci/hailo/common/fw_validation.h | 58 + + .../pci/hailo/common/hailo_ioctl_common.h | 685 ++ .../pci/hailo/common/hailo_pcie_version.h | 13 + - .../media/pci/hailo/common/hailo_resource.c | 128 + + .../media/pci/hailo/common/hailo_resource.c | 141 + .../media/pci/hailo/common/hailo_resource.h | 39 + - drivers/media/pci/hailo/common/pcie_common.c | 872 ++ - drivers/media/pci/hailo/common/pcie_common.h | 168 + - drivers/media/pci/hailo/common/utils.h | 61 + - drivers/media/pci/hailo/common/vdma_common.c | 877 ++ - drivers/media/pci/hailo/common/vdma_common.h | 257 + + drivers/media/pci/hailo/common/pcie_common.c | 913 +++ + drivers/media/pci/hailo/common/pcie_common.h | 193 + + drivers/media/pci/hailo/common/soc_structs.h | 79 + + drivers/media/pci/hailo/common/utils.h | 82 + + drivers/media/pci/hailo/common/vdma_common.c | 876 ++ + drivers/media/pci/hailo/common/vdma_common.h | 284 + .../pci/hailo/include/hailo_pcie_version.h | 14 + - drivers/media/pci/hailo/src/fops.c | 762 ++ - drivers/media/pci/hailo/src/fops.h | 22 + - drivers/media/pci/hailo/src/pci_soc_ioctl.c | 155 + - drivers/media/pci/hailo/src/pci_soc_ioctl.h | 19 + - drivers/media/pci/hailo/src/pcie.c | 1095 +++ - drivers/media/pci/hailo/src/pcie.h | 84 + + drivers/media/pci/hailo/src/fops.c | 570 ++ + drivers/media/pci/hailo/src/fops.h | 23 + + drivers/media/pci/hailo/src/nnc.c | 299 + + drivers/media/pci/hailo/src/nnc.h | 22 + + drivers/media/pci/hailo/src/pcie.c | 1563 ++++ + drivers/media/pci/hailo/src/pcie.h | 131 + + drivers/media/pci/hailo/src/soc.c | 244 + + drivers/media/pci/hailo/src/soc.h | 26 + drivers/media/pci/hailo/src/sysfs.c | 45 + drivers/media/pci/hailo/src/sysfs.h | 13 + - drivers/media/pci/hailo/src/utils.c | 26 + drivers/media/pci/hailo/src/utils.h | 21 + - drivers/media/pci/hailo/utils/compact.h | 153 + + drivers/media/pci/hailo/utils/compact.h | 161 + drivers/media/pci/hailo/utils/fw_common.h | 19 + - .../pci/hailo/utils/integrated_nnc_utils.c | 101 + + .../pci/hailo/utils/integrated_nnc_utils.c | 109 + .../pci/hailo/utils/integrated_nnc_utils.h | 30 + drivers/media/pci/hailo/utils/logs.c | 8 + drivers/media/pci/hailo/utils/logs.h | 45 + - drivers/media/pci/hailo/vdma/ioctl.c | 715 ++ + drivers/media/pci/hailo/vdma/ioctl.c | 721 ++ drivers/media/pci/hailo/vdma/ioctl.h | 37 + - drivers/media/pci/hailo/vdma/memory.c | 661 ++ - drivers/media/pci/hailo/vdma/memory.h | 54 + - drivers/media/pci/hailo/vdma/vdma.c | 302 + - drivers/media/pci/hailo/vdma/vdma.h | 161 + + drivers/media/pci/hailo/vdma/memory.c | 729 ++ + drivers/media/pci/hailo/vdma/memory.h | 56 + + drivers/media/pci/hailo/vdma/vdma.c | 313 + + drivers/media/pci/hailo/vdma/vdma.h | 164 + drivers/media/platform/Kconfig | 2 + drivers/media/platform/Makefile | 2 + drivers/media/platform/bcm2835/Kconfig | 21 + @@ -798,7 +828,7 @@ Subject: [PATCH] apply RPi patch of 6.6.59 (openEuler 6.6.0-75.0.0) .../platform/raspberrypi/pisp_be/Kconfig | 12 + .../platform/raspberrypi/pisp_be/Makefile | 6 + .../platform/raspberrypi/pisp_be/pisp_be.c | 1866 +++++ - .../raspberrypi/pisp_be/pisp_be_formats.h | 519 ++ + .../raspberrypi/pisp_be/pisp_be_formats.h | 529 ++ .../platform/raspberrypi/rp1_cfe/Kconfig | 14 + .../platform/raspberrypi/rp1_cfe/Makefile | 6 + .../media/platform/raspberrypi/rp1_cfe/cfe.c | 2423 ++++++ @@ -828,18 +858,22 @@ Subject: [PATCH] apply RPi patch of 6.6.59 (openEuler 6.6.0-75.0.0) drivers/mfd/bcm2835-pm.c | 28 +- drivers/mfd/rp1.c | 376 + drivers/mfd/simple-mfd-i2c.c | 11 + - drivers/misc/Kconfig | 8 + - drivers/misc/Makefile | 1 + + drivers/misc/Kconfig | 26 + + drivers/misc/Makefile | 3 + drivers/misc/bcm2835_smi.c | 953 +++ + drivers/misc/rp1-fw-pio.h | 56 + + drivers/misc/rp1-pio.c | 1389 ++++ + drivers/misc/ws2812-pio-rp1.c | 507 ++ drivers/mmc/core/block.c | 92 +- drivers/mmc/core/bus.c | 2 + - drivers/mmc/core/card.h | 1 + + drivers/mmc/core/card.h | 7 + drivers/mmc/core/core.c | 20 +- + drivers/mmc/core/host.c | 11 +- drivers/mmc/core/mmc.c | 6 +- drivers/mmc/core/queue.c | 9 + drivers/mmc/core/queue.h | 1 + - drivers/mmc/core/quirks.h | 37 + - drivers/mmc/core/sd.c | 209 +- + drivers/mmc/core/quirks.h | 61 +- + drivers/mmc/core/sd.c | 213 +- drivers/mmc/core/sd_ops.c | 134 + drivers/mmc/core/sd_ops.h | 6 + drivers/mmc/host/Kconfig | 41 + @@ -848,7 +882,7 @@ Subject: [PATCH] apply RPi patch of 6.6.59 (openEuler 6.6.0-75.0.0) drivers/mmc/host/bcm2835-sdhost.c | 2220 +++++ drivers/mmc/host/bcm2835.c | 17 +- drivers/mmc/host/cqhci-core.c | 11 +- - drivers/mmc/host/sdhci-brcmstb.c | 311 +- + drivers/mmc/host/sdhci-brcmstb.c | 317 +- drivers/mmc/host/sdhci-iproc.c | 1 + drivers/mmc/host/sdhci-of-dwcmshc.c | 59 +- drivers/mmc/host/sdhci-pltfm.c | 8 + @@ -859,7 +893,7 @@ Subject: [PATCH] apply RPi patch of 6.6.59 (openEuler 6.6.0-75.0.0) .../net/ethernet/broadcom/genet/bcmgenet.h | 2 +- drivers/net/ethernet/broadcom/genet/bcmmii.c | 6 +- drivers/net/ethernet/cadence/macb.h | 25 + - drivers/net/ethernet/cadence/macb_main.c | 152 +- + drivers/net/ethernet/cadence/macb_main.c | 164 +- drivers/net/ethernet/realtek/Makefile | 3 + drivers/net/ethernet/realtek/r8169.h | 7 + drivers/net/ethernet/realtek/r8169_leds.c | 157 + @@ -880,13 +914,14 @@ Subject: [PATCH] apply RPi patch of 6.6.59 (openEuler 6.6.0-75.0.0) .../broadcom/brcm80211/brcmfmac/firmware.c | 21 +- .../broadcom/brcm80211/brcmfmac/fweh.c | 28 +- .../broadcom/brcm80211/brcmfmac/fweh.h | 31 +- - .../broadcom/brcm80211/brcmfmac/fwil_types.h | 45 + + .../broadcom/brcm80211/brcmfmac/fwil_types.h | 46 + .../broadcom/brcm80211/brcmfmac/p2p.c | 5 + .../broadcom/brcm80211/brcmfmac/pcie.c | 2 +- .../broadcom/brcm80211/brcmfmac/sdio.c | 263 +- .../broadcom/brcm80211/brcmfmac/sdio.h | 110 + .../broadcom/brcm80211/brcmfmac/usb.c | 4 +- .../broadcom/brcm80211/include/chipcommon.h | 2 + + drivers/nvme/host/pci.c | 4 + drivers/nvmem/Kconfig | 12 + drivers/nvmem/Makefile | 2 + drivers/nvmem/raspberrypi-otp.c | 133 + @@ -895,6 +930,7 @@ Subject: [PATCH] apply RPi patch of 6.6.59 (openEuler 6.6.0-75.0.0) drivers/of/configfs.c | 277 + drivers/of/overlay.c | 2 + drivers/pci/controller/pcie-brcmstb.c | 545 +- + drivers/pci/probe.c | 3 - drivers/perf/Kconfig | 8 + drivers/perf/Makefile | 1 + drivers/perf/raspberrypi_axi_monitor.c | 830 ++ @@ -916,11 +952,12 @@ Subject: [PATCH] apply RPi patch of 6.6.59 (openEuler 6.6.0-75.0.0) drivers/power/supply/rpi_poe_power.c | 243 + drivers/pps/clients/pps-gpio.c | 3 + drivers/pps/pps.c | 6 +- - drivers/pwm/Kconfig | 20 + - drivers/pwm/Makefile | 2 + + drivers/pwm/Kconfig | 31 + + drivers/pwm/Makefile | 3 + drivers/pwm/core.c | 62 +- drivers/pwm/pwm-bcm2835.c | 59 +- drivers/pwm/pwm-gpio.c | 240 + + drivers/pwm/pwm-pio-rp1.c | 251 + drivers/pwm/pwm-raspberrypi-poe.c | 81 +- drivers/pwm/pwm-renesas-tpu.c | 1 - drivers/pwm/pwm-rp1.c | 203 + @@ -942,11 +979,11 @@ Subject: [PATCH] apply RPi patch of 6.6.59 (openEuler 6.6.0-75.0.0) drivers/spi/Makefile | 1 + drivers/spi/spi-bcm2835.c | 37 +- drivers/spi/spi-dw-core.c | 132 +- - drivers/spi/spi-dw-dma.c | 43 +- + drivers/spi/spi-dw-dma.c | 51 +- drivers/spi/spi-dw-mmio.c | 8 +- drivers/spi/spi-dw.h | 3 + drivers/spi/spi-gpio.c | 105 +- - drivers/spi/spi-rp2040-gpio-bridge.c | 1244 +++ + drivers/spi/spi-rp2040-gpio-bridge.c | 1249 +++ drivers/spi/spi.c | 9 + drivers/spi/spidev.c | 8 +- drivers/staging/fbtft/fb_st7735r.c | 38 +- @@ -984,7 +1021,7 @@ Subject: [PATCH] apply RPi patch of 6.6.59 (openEuler 6.6.0-75.0.0) .../bcm2835-isp/bcm2835-isp-fmts.h | 558 ++ .../bcm2835-isp/bcm2835-v4l2-isp.c | 1833 +++++ .../include/linux/broadcom/vc_sm_cma_ioctl.h | 114 + - .../interface/vchiq_arm/vchiq_arm.c | 179 +- + .../interface/vchiq_arm/vchiq_arm.c | 184 +- .../staging/vc04_services/vc-sm-cma/Kconfig | 10 + .../staging/vc04_services/vc-sm-cma/Makefile | 12 + drivers/staging/vc04_services/vc-sm-cma/TODO | 1 + @@ -1008,16 +1045,19 @@ Subject: [PATCH] apply RPi patch of 6.6.59 (openEuler 6.6.0-75.0.0) drivers/tty/serial/8250/8250_bcm2835aux.c | 8 + drivers/tty/serial/8250/8250_core.c | 15 + drivers/tty/serial/8250/8250_port.c | 9 + - drivers/tty/serial/amba-pl011.c | 115 + - drivers/tty/serial/sc16is7xx.c | 5 + + drivers/tty/serial/Kconfig | 11 + + drivers/tty/serial/Makefile | 1 + + drivers/tty/serial/amba-pl011.c | 116 + + drivers/tty/serial/rpi-fw-uart.c | 563 ++ + drivers/tty/serial/sc16is7xx.c | 7 +- drivers/usb/Makefile | 1 + drivers/usb/core/generic.c | 1 + drivers/usb/core/hcd.c | 10 + drivers/usb/core/hub.c | 2 +- drivers/usb/core/message.c | 94 + drivers/usb/core/otg_productlist.h | 114 +- - drivers/usb/dwc3/core.c | 58 + - drivers/usb/dwc3/core.h | 17 +- + drivers/usb/dwc3/core.c | 68 + + drivers/usb/dwc3/core.h | 23 +- drivers/usb/dwc3/host.c | 9 +- drivers/usb/gadget/file_storage.c | 3676 +++++++++ drivers/usb/host/Kconfig | 10 + @@ -1082,11 +1122,11 @@ Subject: [PATCH] apply RPi patch of 6.6.59 (openEuler 6.6.0-75.0.0) drivers/usb/host/dwc_otg/test/dwc_otg_test.pm | 337 + .../usb/host/dwc_otg/test/test_mod_param.pl | 133 + drivers/usb/host/dwc_otg/test/test_sysfs.pl | 193 + - drivers/usb/host/xhci-mem.c | 40 +- + drivers/usb/host/xhci-mem.c | 48 +- drivers/usb/host/xhci-pci.c | 21 +- - drivers/usb/host/xhci-ring.c | 86 +- + drivers/usb/host/xhci-ring.c | 99 +- drivers/usb/host/xhci.c | 104 + - drivers/usb/host/xhci.h | 10 +- + drivers/usb/host/xhci.h | 12 +- drivers/usb/phy/phy-generic.c | 7 - drivers/video/backlight/Kconfig | 7 + drivers/video/backlight/Makefile | 1 + @@ -1105,10 +1145,12 @@ Subject: [PATCH] apply RPi patch of 6.6.59 (openEuler 6.6.0-75.0.0) drivers/w1/w1_io.c | 37 +- drivers/watchdog/bcm2835_wdt.c | 51 +- fs/ntfs3/fslog.c | 2 + + include/drm/drm_blend.h | 5 + include/drm/drm_color_mgmt.h | 3 + - include/drm/drm_connector.h | 7 + + include/drm/drm_connector.h | 18 + + include/drm/drm_crtc.h | 2 +- include/drm/drm_mipi_dsi.h | 38 +- - include/drm/drm_plane.h | 37 + + include/drm/drm_plane.h | 41 +- include/dt-bindings/clock/rp1.h | 60 + include/dt-bindings/gpio/gpio-fsm.h | 21 + include/dt-bindings/mfd/rp1.h | 235 + @@ -1124,11 +1166,15 @@ Subject: [PATCH] apply RPi patch of 6.6.59 (openEuler 6.6.0-75.0.0) include/linux/mfd/rpisense/framebuffer.h | 35 + include/linux/mfd/rpisense/joystick.h | 35 + include/linux/microchipphy.h | 8 + - include/linux/mmc/card.h | 3 + + include/linux/mmc/card.h | 4 + + include/linux/mmc/host.h | 1 + include/linux/mmc/sd.h | 12 + include/linux/module.h | 2 +- + include/linux/pio_instructions.h | 481 ++ + include/linux/pio_rp1.h | 1019 +++ include/linux/platform_data/dma-bcm2708.h | 143 + include/linux/pwm.h | 29 +- + include/linux/rp1-firmware.h | 53 + include/linux/rp1_platform.h | 20 + include/linux/usb.h | 2 + include/linux/usb/hcd.h | 7 + @@ -1137,16 +1183,19 @@ Subject: [PATCH] apply RPi patch of 6.6.59 (openEuler 6.6.0-75.0.0) include/media/raspberrypi/pisp_common.h | 65 + include/media/raspberrypi/pisp_types.h | 144 + include/media/videobuf2-core.h | 15 + - include/soc/bcm2835/raspberrypi-firmware.h | 29 +- + include/soc/bcm2835/raspberrypi-firmware.h | 31 +- + include/uapi/drm/drm_mode.h | 1 + include/uapi/drm/v3d_drm.h | 4 + include/uapi/linux/bcm2835-isp.h | 347 + include/uapi/linux/fb.h | 12 + include/uapi/linux/media-bus-format.h | 3 + - .../linux/media/raspberrypi/pisp_be_config.h | 968 +++ + .../linux/media/raspberrypi/pisp_be_config.h | 934 +++ .../linux/media/raspberrypi/pisp_common.h | 202 + + include/uapi/linux/serial_core.h | 3 + include/uapi/linux/v4l2-controls.h | 11 + include/uapi/linux/videodev2.h | 42 + - kernel/cgroup/cgroup.c | 25 + + include/uapi/misc/rp1_pio_if.h | 235 + + kernel/cgroup/cgroup.c | 33 + kernel/resource.c | 6 + lib/earlycpio.c | 1 + mm/cma.c | 36 + @@ -1158,12 +1207,15 @@ Subject: [PATCH] apply RPi patch of 6.6.59 (openEuler 6.6.0-75.0.0) net/wireless/certs/debian.hex | 1426 ++++ scripts/Makefile.dtbinst | 5 +- scripts/Makefile.lib | 19 + + sound/drivers/Kconfig | 10 + + sound/drivers/Makefile | 2 + + sound/drivers/pimidi.c | 1113 +++ sound/soc/bcm/Kconfig | 284 + sound/soc/bcm/Makefile | 73 +- sound/soc/bcm/allo-boss-dac.c | 471 ++ sound/soc/bcm/allo-boss2-dac.c | 1130 +++ sound/soc/bcm/allo-katana-codec.c | 386 + - sound/soc/bcm/allo-piano-dac-plus.c | 1064 +++ + sound/soc/bcm/allo-piano-dac-plus.c | 1027 +++ sound/soc/bcm/allo-piano-dac.c | 122 + .../bcm/audioinjector-isolated-soundcard.c | 184 + sound/soc/bcm/audioinjector-octo-soundcard.c | 347 + @@ -1193,7 +1245,7 @@ Subject: [PATCH] apply RPi patch of 6.6.59 (openEuler 6.6.0-75.0.0) sound/soc/bcm/pisound.c | 1255 +++ sound/soc/bcm/rpi-cirrus.c | 1027 +++ sound/soc/bcm/rpi-proto.c | 147 + - sound/soc/bcm/rpi-simple-soundcard.c | 560 ++ + sound/soc/bcm/rpi-simple-soundcard.c | 590 ++ sound/soc/bcm/rpi-wm8804-soundcard.c | 549 ++ sound/soc/codecs/Kconfig | 26 +- sound/soc/codecs/Makefile | 8 + @@ -1207,7 +1259,7 @@ Subject: [PATCH] apply RPi patch of 6.6.59 (openEuler 6.6.0-75.0.0) sound/soc/codecs/ma120x0p.c | 1380 ++++ sound/soc/codecs/pcm1794a.c | 69 + sound/soc/codecs/pcm512x-i2c.c | 4 + - sound/soc/codecs/pcm512x.c | 38 +- + sound/soc/codecs/pcm512x.c | 40 +- sound/soc/codecs/tas5713.c | 360 + sound/soc/codecs/tas5713.h | 210 + sound/soc/dwc/dwc-i2s.c | 190 +- @@ -1215,7 +1267,7 @@ Subject: [PATCH] apply RPi patch of 6.6.59 (openEuler 6.6.0-75.0.0) sound/soc/soc-core.c | 14 +- sound/usb/card.c | 8 +- sound/usb/quirks.c | 2 + - 1211 files changed, 246830 insertions(+), 4658 deletions(-) + 1263 files changed, 256193 insertions(+), 4758 deletions(-) create mode 100644 Documentation/admin-guide/media/bcm2835-isp.rst create mode 100644 Documentation/admin-guide/media/raspberrypi-pisp-be.dot create mode 100644 Documentation/admin-guide/media/raspberrypi-pisp-be.rst @@ -1395,6 +1447,7 @@ Subject: [PATCH] apply RPi patch of 6.6.59 (openEuler 6.6.0-75.0.0) create mode 100644 arch/arm/boot/dts/overlays/hy28b-overlay.dts create mode 100644 arch/arm/boot/dts/overlays/i-sabre-q2m-overlay.dts create mode 100644 arch/arm/boot/dts/overlays/i2c-bcm2708-overlay.dts + create mode 100644 arch/arm/boot/dts/overlays/i2c-buses.dtsi create mode 100644 arch/arm/boot/dts/overlays/i2c-fan-overlay.dts create mode 100644 arch/arm/boot/dts/overlays/i2c-gpio-overlay.dts create mode 100644 arch/arm/boot/dts/overlays/i2c-mux-overlay.dts @@ -1428,6 +1481,8 @@ Subject: [PATCH] apply RPi patch of 6.6.59 (openEuler 6.6.0-75.0.0) create mode 100644 arch/arm/boot/dts/overlays/imx296-overlay.dts create mode 100644 arch/arm/boot/dts/overlays/imx327-overlay.dts create mode 100644 arch/arm/boot/dts/overlays/imx378-overlay.dts + create mode 100644 arch/arm/boot/dts/overlays/imx415-overlay.dts + create mode 100644 arch/arm/boot/dts/overlays/imx415.dtsi create mode 100644 arch/arm/boot/dts/overlays/imx462-overlay.dts create mode 100644 arch/arm/boot/dts/overlays/imx477-overlay.dts create mode 100644 arch/arm/boot/dts/overlays/imx477_378-overlay.dtsi @@ -1507,6 +1562,7 @@ Subject: [PATCH] apply RPi patch of 6.6.59 (openEuler 6.6.0-75.0.0) create mode 100644 arch/arm/boot/dts/overlays/pifi-dac-zero-overlay.dts create mode 100644 arch/arm/boot/dts/overlays/pifi-mini-210-overlay.dts create mode 100644 arch/arm/boot/dts/overlays/piglow-overlay.dts + create mode 100644 arch/arm/boot/dts/overlays/pimidi-overlay.dts create mode 100644 arch/arm/boot/dts/overlays/pineboards-hat-ai-overlay.dts create mode 100644 arch/arm/boot/dts/overlays/pineboards-hatdrive-poe-plus-overlay.dts create mode 100644 arch/arm/boot/dts/overlays/piscreen-overlay.dts @@ -1520,14 +1576,17 @@ Subject: [PATCH] apply RPi patch of 6.6.59 (openEuler 6.6.0-75.0.0) create mode 100644 arch/arm/boot/dts/overlays/pps-gpio-overlay.dts create mode 100644 arch/arm/boot/dts/overlays/proto-codec-overlay.dts create mode 100644 arch/arm/boot/dts/overlays/pwm-2chan-overlay.dts + create mode 100644 arch/arm/boot/dts/overlays/pwm-gpio-fan-overlay.dts create mode 100644 arch/arm/boot/dts/overlays/pwm-gpio-overlay.dts create mode 100644 arch/arm/boot/dts/overlays/pwm-ir-tx-overlay.dts create mode 100644 arch/arm/boot/dts/overlays/pwm-overlay.dts + create mode 100644 arch/arm/boot/dts/overlays/pwm-pio-overlay.dts create mode 100644 arch/arm/boot/dts/overlays/pwm1-overlay.dts create mode 100644 arch/arm/boot/dts/overlays/qca7000-overlay.dts create mode 100644 arch/arm/boot/dts/overlays/qca7000-uart0-overlay.dts create mode 100644 arch/arm/boot/dts/overlays/ramoops-overlay.dts create mode 100644 arch/arm/boot/dts/overlays/ramoops-pi4-overlay.dts + create mode 100644 arch/arm/boot/dts/overlays/rootmaster-overlay.dts create mode 100644 arch/arm/boot/dts/overlays/rotary-encoder-overlay.dts create mode 100644 arch/arm/boot/dts/overlays/rpi-backlight-overlay.dts create mode 100644 arch/arm/boot/dts/overlays/rpi-codeczero-overlay.dts @@ -1535,6 +1594,7 @@ Subject: [PATCH] apply RPi patch of 6.6.59 (openEuler 6.6.0-75.0.0) create mode 100644 arch/arm/boot/dts/overlays/rpi-dacpro-overlay.dts create mode 100644 arch/arm/boot/dts/overlays/rpi-digiampplus-overlay.dts create mode 100644 arch/arm/boot/dts/overlays/rpi-ft5406-overlay.dts + create mode 100644 arch/arm/boot/dts/overlays/rpi-fw-uart-overlay.dts create mode 100644 arch/arm/boot/dts/overlays/rpi-poe-overlay.dts create mode 100644 arch/arm/boot/dts/overlays/rpi-poe-plus-overlay.dts create mode 100644 arch/arm/boot/dts/overlays/rpi-rp2040-gpio-bridge.dtsi @@ -1562,6 +1622,7 @@ Subject: [PATCH] apply RPi patch of 6.6.59 (openEuler 6.6.0-75.0.0) create mode 100644 arch/arm/boot/dts/overlays/spi-gpio40-45-overlay.dts create mode 100644 arch/arm/boot/dts/overlays/spi-rtc-overlay.dts create mode 100644 arch/arm/boot/dts/overlays/spi0-0cs-overlay.dts + create mode 100644 arch/arm/boot/dts/overlays/spi0-1cs-inverted-overlay.dts create mode 100644 arch/arm/boot/dts/overlays/spi0-1cs-overlay.dts create mode 100644 arch/arm/boot/dts/overlays/spi0-2cs-overlay.dts create mode 100644 arch/arm/boot/dts/overlays/spi1-1cs-overlay.dts @@ -1646,6 +1707,7 @@ Subject: [PATCH] apply RPi patch of 6.6.59 (openEuler 6.6.0-75.0.0) create mode 100644 arch/arm/boot/dts/overlays/waveshare-can-fd-hat-mode-b-overlay.dts create mode 100644 arch/arm/boot/dts/overlays/wittypi-overlay.dts create mode 100644 arch/arm/boot/dts/overlays/wm8960-soundcard-overlay.dts + create mode 100644 arch/arm/boot/dts/overlays/ws2812-pio-overlay.dts create mode 100644 arch/arm/configs/bcm2709_defconfig create mode 100644 arch/arm/configs/bcm2711_defconfig create mode 100644 arch/arm/configs/bcmrpi_defconfig @@ -1695,6 +1757,7 @@ Subject: [PATCH] apply RPi patch of 6.6.59 (openEuler 6.6.0-75.0.0) create mode 100644 drivers/clk/clk-rp1-sdio.c create mode 100644 drivers/clk/clk-rp1.c create mode 100644 drivers/dma/bcm2708-dmaengine.c + create mode 100644 drivers/firmware/rp1.c create mode 100644 drivers/gpio/gpio-bcm-virt.c create mode 100644 drivers/gpio/gpio-fsm.c create mode 100644 drivers/gpio/gpio-pwm.c @@ -1709,6 +1772,7 @@ Subject: [PATCH] apply RPi patch of 6.6.59 (openEuler 6.6.0-75.0.0) create mode 100644 drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi.h create mode 100644 drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi_cfg.c create mode 100644 drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi_hw.c + create mode 100644 drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi_pio.c create mode 100644 drivers/gpu/drm/rp1/rp1-dsi/Kconfig create mode 100644 drivers/gpu/drm/rp1/rp1-dsi/Makefile create mode 100644 drivers/gpu/drm/rp1/rp1-dsi/rp1_dsi.c @@ -1733,6 +1797,7 @@ Subject: [PATCH] apply RPi patch of 6.6.59 (openEuler 6.6.0-75.0.0) create mode 100644 drivers/irqchip/irq-bcm2712-mip.c create mode 100644 drivers/leds/trigger/ledtrig-actpwr.c create mode 100644 drivers/leds/trigger/ledtrig-input.c + create mode 100644 drivers/mailbox/rp1-mailbox.c create mode 100644 drivers/media/i2c/ad5398_vcm.c create mode 100644 drivers/media/i2c/arducam-pivariety.c create mode 100644 drivers/media/i2c/arducam-pivariety.h @@ -1758,19 +1823,21 @@ Subject: [PATCH] apply RPi patch of 6.6.59 (openEuler 6.6.0-75.0.0) create mode 100644 drivers/media/pci/hailo/common/hailo_resource.h create mode 100644 drivers/media/pci/hailo/common/pcie_common.c create mode 100644 drivers/media/pci/hailo/common/pcie_common.h + create mode 100644 drivers/media/pci/hailo/common/soc_structs.h create mode 100644 drivers/media/pci/hailo/common/utils.h create mode 100644 drivers/media/pci/hailo/common/vdma_common.c create mode 100644 drivers/media/pci/hailo/common/vdma_common.h create mode 100755 drivers/media/pci/hailo/include/hailo_pcie_version.h create mode 100644 drivers/media/pci/hailo/src/fops.c create mode 100644 drivers/media/pci/hailo/src/fops.h - create mode 100755 drivers/media/pci/hailo/src/pci_soc_ioctl.c - create mode 100755 drivers/media/pci/hailo/src/pci_soc_ioctl.h + create mode 100644 drivers/media/pci/hailo/src/nnc.c + create mode 100644 drivers/media/pci/hailo/src/nnc.h create mode 100644 drivers/media/pci/hailo/src/pcie.c create mode 100644 drivers/media/pci/hailo/src/pcie.h + create mode 100644 drivers/media/pci/hailo/src/soc.c + create mode 100644 drivers/media/pci/hailo/src/soc.h create mode 100644 drivers/media/pci/hailo/src/sysfs.c create mode 100644 drivers/media/pci/hailo/src/sysfs.h - create mode 100644 drivers/media/pci/hailo/src/utils.c create mode 100644 drivers/media/pci/hailo/src/utils.h create mode 100644 drivers/media/pci/hailo/utils/compact.h create mode 100644 drivers/media/pci/hailo/utils/fw_common.h @@ -1811,6 +1878,9 @@ Subject: [PATCH] apply RPi patch of 6.6.59 (openEuler 6.6.0-75.0.0) create mode 100644 drivers/media/platform/raspberrypi/rp1_cfe/pisp_types.h create mode 100644 drivers/mfd/rp1.c create mode 100644 drivers/misc/bcm2835_smi.c + create mode 100644 drivers/misc/rp1-fw-pio.h + create mode 100644 drivers/misc/rp1-pio.c + create mode 100644 drivers/misc/ws2812-pio-rp1.c create mode 100644 drivers/mmc/host/bcm2835-mmc.c create mode 100644 drivers/mmc/host/bcm2835-sdhost.c create mode 100644 drivers/net/ethernet/realtek/r8169_leds.c @@ -1821,6 +1891,7 @@ Subject: [PATCH] apply RPi patch of 6.6.59 (openEuler 6.6.0-75.0.0) create mode 100644 drivers/pinctrl/pinctrl-rp1.c create mode 100644 drivers/power/supply/rpi_poe_power.c create mode 100644 drivers/pwm/pwm-gpio.c + create mode 100644 drivers/pwm/pwm-pio-rp1.c create mode 100644 drivers/pwm/pwm-rp1.c create mode 100644 drivers/regulator/rpi-panel-v2-regulator.c create mode 100644 drivers/rtc/rtc-rpi.c @@ -1855,6 +1926,7 @@ Subject: [PATCH] apply RPi patch of 6.6.59 (openEuler 6.6.0-75.0.0) create mode 100644 drivers/staging/vc04_services/vc-sm-cma/vc_sm_cma_vchi.h create mode 100644 drivers/staging/vc04_services/vc-sm-cma/vc_sm_defs.h create mode 100644 drivers/staging/vc04_services/vc-sm-cma/vc_sm_knl.h + create mode 100644 drivers/tty/serial/rpi-fw-uart.c create mode 100644 drivers/usb/gadget/file_storage.c create mode 100644 drivers/usb/host/dwc_common_port/Makefile create mode 100644 drivers/usb/host/dwc_common_port/Makefile.fbsd @@ -1926,14 +1998,19 @@ Subject: [PATCH] apply RPi patch of 6.6.59 (openEuler 6.6.0-75.0.0) create mode 100644 include/linux/broadcom/vc_mem.h create mode 100644 include/linux/mfd/rpisense/framebuffer.h create mode 100644 include/linux/mfd/rpisense/joystick.h + create mode 100644 include/linux/pio_instructions.h + create mode 100644 include/linux/pio_rp1.h create mode 100644 include/linux/platform_data/dma-bcm2708.h + create mode 100644 include/linux/rp1-firmware.h create mode 100644 include/linux/rp1_platform.h create mode 100644 include/media/raspberrypi/pisp_common.h create mode 100644 include/media/raspberrypi/pisp_types.h create mode 100644 include/uapi/linux/bcm2835-isp.h create mode 100644 include/uapi/linux/media/raspberrypi/pisp_be_config.h create mode 100644 include/uapi/linux/media/raspberrypi/pisp_common.h + create mode 100644 include/uapi/misc/rp1_pio_if.h create mode 100644 net/wireless/certs/debian.hex + create mode 100644 sound/drivers/pimidi.c create mode 100644 sound/soc/bcm/allo-boss-dac.c create mode 100644 sound/soc/bcm/allo-boss2-dac.c create mode 100644 sound/soc/bcm/allo-katana-codec.c @@ -3580,6 +3657,19 @@ index 80d24220baa0..3415b26b5991 100644 assigned-clocks: true assigned-clock-parents: true +diff --git a/Documentation/devicetree/bindings/media/i2c/sony,imx290.yaml b/Documentation/devicetree/bindings/media/i2c/sony,imx290.yaml +index a531badc16c9..cf8f33041f84 100644 +--- a/Documentation/devicetree/bindings/media/i2c/sony,imx290.yaml ++++ b/Documentation/devicetree/bindings/media/i2c/sony,imx290.yaml +@@ -30,6 +30,8 @@ properties: + - sony,imx290lqr # Colour + - sony,imx290llr # Monochrome + - sony,imx327lqr # Colour ++ - sony,imx462lqr # Colour ++ - sony,imx462llr # Monochrome + - const: sony,imx290 + deprecated: true + diff --git a/Documentation/devicetree/bindings/media/i2c/sony,imx500.yaml b/Documentation/devicetree/bindings/media/i2c/sony,imx500.yaml new file mode 100644 index 000000000000..b8538ae6c80f @@ -4107,10 +4197,18 @@ index a43eb837f8da..fb0eb5bb53a5 100644 resets: maxItems: 5 diff --git a/Documentation/devicetree/bindings/net/cdns,macb.yaml b/Documentation/devicetree/bindings/net/cdns,macb.yaml -index bf8894a0257e..a9edfe6c1254 100644 +index bf8894a0257e..63a3be1581e1 100644 --- a/Documentation/devicetree/bindings/net/cdns,macb.yaml +++ b/Documentation/devicetree/bindings/net/cdns,macb.yaml -@@ -131,6 +131,22 @@ properties: +@@ -54,6 +54,7 @@ properties: + - cdns,np4-macb # NP4 SoC devices + - microchip,sama7g5-emac # Microchip SAMA7G5 ethernet interface + - microchip,sama7g5-gem # Microchip SAMA7G5 gigabit ethernet interface ++ - raspberrypi,rp1-gem # Raspberry Pi RP1 gigabit ethernet interface + - sifive,fu540-c000-gem # SiFive FU540-C000 SoC + - cdns,emac # Generic + - cdns,gem # Generic +@@ -131,6 +132,22 @@ properties: Node containing PHY children. If this node is not present, then PHYs will be direct children. @@ -4511,11 +4609,23 @@ index 9ce1df93d4c3..d911c203fa45 100644 gpio-sck: false gpio-miso: false diff --git a/Documentation/devicetree/bindings/usb/snps,dwc3.yaml b/Documentation/devicetree/bindings/usb/snps,dwc3.yaml -index a696f23730d3..d516979b0681 100644 +index a696f23730d3..25f2cf049ca9 100644 --- a/Documentation/devicetree/bindings/usb/snps,dwc3.yaml +++ b/Documentation/devicetree/bindings/usb/snps,dwc3.yaml -@@ -233,12 +233,17 @@ properties: +@@ -231,14 +231,29 @@ properties: + description: When set, disable u2mac linestate check during HS transmit + type: boolean ++ snps,enhanced-nak-fs-quirk: ++ description: ++ When set, the controller schedules many more handshakes to Async FS ++ endpoints, improving throughput when they frequently respond with NAKs. ++ ++ snps,enhanced-nak-hs-quirk: ++ description: ++ When set, the controller schedules many more handshakes to Async HS ++ endpoints, improving throughput when they frequently respond with NAKs. ++ snps,parkmode-disable-ss-quirk: description: - When set, all SuperSpeed bus instances in park mode are disabled. @@ -5004,7 +5114,7 @@ index 000000000000..f8d32547195b +zte ZTE Corp. +zyxel ZyXEL Communications Corp. diff --git a/Documentation/devicetree/bindings/vendor-prefixes.yaml b/Documentation/devicetree/bindings/vendor-prefixes.yaml -index 133cfb2bb05c..d5805b33bd0e 100644 +index 93258265c6b0..4bcc56af2446 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.yaml +++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml @@ -133,6 +133,8 @@ patternProperties: @@ -6188,7 +6298,7 @@ index b22778025fa0..5cb24fd81a51 100644 M: Alex Dubov diff --git a/README.md b/README.md new file mode 100644 -index 000000000000..e33ba1b6c950 +index 000000000000..e26e19b02627 --- /dev/null +++ b/README.md @@ -0,0 +1,30 @@ @@ -6211,10 +6321,6 @@ index 000000000000..e33ba1b6c950 +requirements for building and running the kernel, and information about +the problems which may result by upgrading your kernel. + -+Build status for rpi-5.15.y: -+[![Pi kernel build tests](https://github.com/raspberrypi/linux/actions/workflows/kernel-build.yml/badge.svg?branch=rpi-5.15.y)](https://github.com/raspberrypi/linux/actions/workflows/kernel-build.yml) -+[![dtoverlaycheck](https://github.com/raspberrypi/linux/actions/workflows/dtoverlaycheck.yml/badge.svg?branch=rpi-5.15.y)](https://github.com/raspberrypi/linux/actions/workflows/dtoverlaycheck.yml) -+ +Build status for rpi-6.1.y: +[![Pi kernel build tests](https://github.com/raspberrypi/linux/actions/workflows/kernel-build.yml/badge.svg?branch=rpi-6.1.y)](https://github.com/raspberrypi/linux/actions/workflows/kernel-build.yml) +[![dtoverlaycheck](https://github.com/raspberrypi/linux/actions/workflows/dtoverlaycheck.yml/badge.svg?branch=rpi-6.1.y)](https://github.com/raspberrypi/linux/actions/workflows/dtoverlaycheck.yml) @@ -6222,6 +6328,10 @@ index 000000000000..e33ba1b6c950 +Build status for rpi-6.6.y: +[![Pi kernel build tests](https://github.com/raspberrypi/linux/actions/workflows/kernel-build.yml/badge.svg?branch=rpi-6.6.y)](https://github.com/raspberrypi/linux/actions/workflows/kernel-build.yml) +[![dtoverlaycheck](https://github.com/raspberrypi/linux/actions/workflows/dtoverlaycheck.yml/badge.svg?branch=rpi-6.6.y)](https://github.com/raspberrypi/linux/actions/workflows/dtoverlaycheck.yml) ++ ++Build status for rpi-6.12.y: ++[![Pi kernel build tests](https://github.com/raspberrypi/linux/actions/workflows/kernel-build.yml/badge.svg?branch=rpi-6.12.y)](https://github.com/raspberrypi/linux/actions/workflows/kernel-build.yml) ++[![dtoverlaycheck](https://github.com/raspberrypi/linux/actions/workflows/dtoverlaycheck.yml/badge.svg?branch=rpi-6.12.y)](https://github.com/raspberrypi/linux/actions/workflows/dtoverlaycheck.yml) diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile index efe38eb25301..a2a407fb5b28 100644 --- a/arch/arm/boot/dts/Makefile @@ -10360,10 +10470,16 @@ index d5f8823230db..75ce412a85dd 100644 + }; +}; diff --git a/arch/arm/boot/dts/broadcom/bcm2711-rpi-400.dts b/arch/arm/boot/dts/broadcom/bcm2711-rpi-400.dts -index 5a2869a18bd5..d5b81b889018 100644 +index 5a2869a18bd5..1b8eae8d025f 100644 --- a/arch/arm/boot/dts/broadcom/bcm2711-rpi-400.dts +++ b/arch/arm/boot/dts/broadcom/bcm2711-rpi-400.dts -@@ -36,8 +36,53 @@ &led_pwr { +@@ -1,5 +1,4 @@ + // SPDX-License-Identifier: GPL-2.0 +-/dts-v1/; + #include "bcm2711-rpi-4-b.dts" + + / { +@@ -36,8 +35,53 @@ &led_pwr { gpios = <&gpio 42 GPIO_ACTIVE_HIGH>; }; @@ -10421,10 +10537,10 @@ index 5a2869a18bd5..d5b81b889018 100644 +}; diff --git a/arch/arm/boot/dts/broadcom/bcm2711-rpi-cm4.dts b/arch/arm/boot/dts/broadcom/bcm2711-rpi-cm4.dts new file mode 100644 -index 000000000000..678b37518fdc +index 000000000000..5f0e9c99a892 --- /dev/null +++ b/arch/arm/boot/dts/broadcom/bcm2711-rpi-cm4.dts -@@ -0,0 +1,499 @@ +@@ -0,0 +1,501 @@ +// SPDX-License-Identifier: GPL-2.0 +/dts-v1/; +#define BCM2711 @@ -10920,6 +11036,8 @@ index 000000000000..678b37518fdc + <&ant1>, "output-low?=on", + <&ant2>, "output-high?=off", + <&ant2>, "output-low?=on"; ++ noanthogs = <&ant1>,"status=disabled", ++ <&ant2>, "status=disabled"; + + pcie_tperst_clk_ms = <&pcie0>,"brcm,tperst-clk-ms:0"; + }; @@ -11828,9 +11946,45 @@ index 98817a6675b9..7b9e946db985 100644 &v3d { diff --git a/arch/arm/boot/dts/broadcom/bcm2711.dtsi b/arch/arm/boot/dts/broadcom/bcm2711.dtsi -index 4a379a14966d..09dbe7b3ca39 100644 +index 4a379a14966d..9e17fd00ce50 100644 --- a/arch/arm/boot/dts/broadcom/bcm2711.dtsi +++ b/arch/arm/boot/dts/broadcom/bcm2711.dtsi +@@ -134,7 +134,7 @@ uart2: serial@7e201400 { + clocks = <&clocks BCM2835_CLOCK_UART>, + <&clocks BCM2835_CLOCK_VPU>; + clock-names = "uartclk", "apb_pclk"; +- arm,primecell-periphid = <0x00241011>; ++ arm,primecell-periphid = <0x00341011>; + status = "disabled"; + }; + +@@ -145,7 +145,7 @@ uart3: serial@7e201600 { + clocks = <&clocks BCM2835_CLOCK_UART>, + <&clocks BCM2835_CLOCK_VPU>; + clock-names = "uartclk", "apb_pclk"; +- arm,primecell-periphid = <0x00241011>; ++ arm,primecell-periphid = <0x00341011>; + status = "disabled"; + }; + +@@ -156,7 +156,7 @@ uart4: serial@7e201800 { + clocks = <&clocks BCM2835_CLOCK_UART>, + <&clocks BCM2835_CLOCK_VPU>; + clock-names = "uartclk", "apb_pclk"; +- arm,primecell-periphid = <0x00241011>; ++ arm,primecell-periphid = <0x00341011>; + status = "disabled"; + }; + +@@ -167,7 +167,7 @@ uart5: serial@7e201a00 { + clocks = <&clocks BCM2835_CLOCK_UART>, + <&clocks BCM2835_CLOCK_VPU>; + clock-names = "uartclk", "apb_pclk"; +- arm,primecell-periphid = <0x00241011>; ++ arm,primecell-periphid = <0x00341011>; + status = "disabled"; + }; + @@ -277,7 +277,7 @@ pwm1: pwm@7e20c800 { reg = <0x7e20c800 0x28>; clocks = <&clocks BCM2835_CLOCK_PWM>; @@ -11840,6 +11994,23 @@ index 4a379a14966d..09dbe7b3ca39 100644 #pwm-cells = <3>; status = "disabled"; }; +@@ -451,8 +451,6 @@ IRQ_TYPE_LEVEL_LOW)>, + IRQ_TYPE_LEVEL_LOW)>, + ; +- /* This only applies to the ARMv7 stub */ +- arm,cpu-registers-not-fw-configured; + }; + + cpus: cpus { +@@ -1155,6 +1153,7 @@ &txp { + }; + + &uart0 { ++ arm,primecell-periphid = <0x00341011>; + interrupts = ; + }; + diff --git a/arch/arm/boot/dts/broadcom/bcm271x-rpi-bt.dtsi b/arch/arm/boot/dts/broadcom/bcm271x-rpi-bt.dtsi new file mode 100644 index 000000000000..c77e280ccd16 @@ -11973,10 +12144,10 @@ index 2ca8a2505a4d..6f09d96e1192 100644 compatible = "fixed-clock"; diff --git a/arch/arm/boot/dts/overlays/Makefile b/arch/arm/boot/dts/overlays/Makefile new file mode 100644 -index 000000000000..6b60068b125c +index 000000000000..272717f6d01d --- /dev/null +++ b/arch/arm/boot/dts/overlays/Makefile -@@ -0,0 +1,347 @@ +@@ -0,0 +1,355 @@ +# Overlays for the Raspberry Pi platform + +dtb-$(CONFIG_ARCH_BCM2835) += overlay_map.dtb hat_map.dtb @@ -12114,6 +12285,7 @@ index 000000000000..6b60068b125c + imx296.dtbo \ + imx327.dtbo \ + imx378.dtbo \ ++ imx415.dtbo \ + imx462.dtbo \ + imx477.dtbo \ + imx500.dtbo \ @@ -12182,6 +12354,7 @@ index 000000000000..6b60068b125c + pifi-dac-zero.dtbo \ + pifi-mini-210.dtbo \ + piglow.dtbo \ ++ pimidi.dtbo \ + pineboards-hat-ai.dtbo \ + pineboards-hatdrive-poe-plus.dtbo \ + piscreen.dtbo \ @@ -12197,12 +12370,15 @@ index 000000000000..6b60068b125c + pwm.dtbo \ + pwm-2chan.dtbo \ + pwm-gpio.dtbo \ ++ pwm-gpio-fan.dtbo \ + pwm-ir-tx.dtbo \ ++ pwm-pio.dtbo \ + pwm1.dtbo \ + qca7000.dtbo \ + qca7000-uart0.dtbo \ + ramoops.dtbo \ + ramoops-pi4.dtbo \ ++ rootmaster.dtbo \ + rotary-encoder.dtbo \ + rpi-backlight.dtbo \ + rpi-codeczero.dtbo \ @@ -12210,6 +12386,7 @@ index 000000000000..6b60068b125c + rpi-dacpro.dtbo \ + rpi-digiampplus.dtbo \ + rpi-ft5406.dtbo \ ++ rpi-fw-uart.dtbo \ + rpi-poe.dtbo \ + rpi-poe-plus.dtbo \ + rpi-sense.dtbo \ @@ -12237,6 +12414,7 @@ index 000000000000..6b60068b125c + spi-rtc.dtbo \ + spi0-0cs.dtbo \ + spi0-1cs.dtbo \ ++ spi0-1cs-inverted.dtbo \ + spi0-2cs.dtbo \ + spi1-1cs.dtbo \ + spi1-2cs.dtbo \ @@ -12317,7 +12495,8 @@ index 000000000000..6b60068b125c + waveshare-can-fd-hat-mode-a.dtbo \ + waveshare-can-fd-hat-mode-b.dtbo \ + wittypi.dtbo \ -+ wm8960-soundcard.dtbo ++ wm8960-soundcard.dtbo \ ++ ws2812-pio.dtbo + +targets += dtbs dtbs_install +targets += $(dtbo-y) @@ -12326,10 +12505,10 @@ index 000000000000..6b60068b125c +clean-files := *.dtbo diff --git a/arch/arm/boot/dts/overlays/README b/arch/arm/boot/dts/overlays/README new file mode 100644 -index 000000000000..5c6344eb6f98 +index 000000000000..820af14f58cf --- /dev/null +++ b/arch/arm/boot/dts/overlays/README -@@ -0,0 +1,5516 @@ +@@ -0,0 +1,5669 @@ +Introduction +============ + @@ -12485,6 +12664,12 @@ index 000000000000..5c6344eb6f98 + + noant Disable both antennas. CM4/5 only. + ++ noanthogs Disable the GPIO hogs on the antenna controls ++ so they can be controlled at runtime. Note that ++ using this parameter without suitable OS ++ support will result in attenuated WiFi and ++ Bluetooth signals. CM4/5 only. ++ + audio Set to "on" to enable the onboard ALSA audio + interface (default "off") + @@ -12633,10 +12818,31 @@ index 000000000000..5c6344eb6f98 + i2c_baudrate An alias for i2c_arm_baudrate + + i2c_csi_dsi Set to "on" to enable the i2c_csi_dsi interface ++ The I2C bus and GPIOs are platform specific: ++ B rev 1: ++ i2c-1 on 2 & 3 ++ B rev 2, B+, CM, Zero, Zero W, 2B, CM2, CM3, ++ CM4S: ++ i2c-0 on 28 & 29 ++ 3B, 3B+, Zero 2W, 4B, 400, CM4: ++ i2c-0 on 44 & 45 ++ 5, 500: ++ i2c-11/i2c-4 on 40 & 41 ++ CM5 on CM5IO: ++ i2c-0 on 0 & 1 ++ CM5 on CM4IO: ++ i2c-10/i2c-6 on 38 & 39 + + i2c_csi_dsi0 Set to "on" to enable the i2c_csi_dsi0 interface ++ The I2C bus and GPIOs are platform specific: ++ B rev 1 & 2, B+, CM, Zero, Zero W, 2B, CM2, ++ CM3, CM4S, 3B, 3B+, Zero 2W, 4B, 400, CM4, ++ CM5 on CM4IO: ++ i2c-0 on 0 & 1 ++ 5, 500, CM5 on CM5IO: ++ i2c-10/i2c-6 on 38 & 39 + -+ i2c_csi_dsi1 Set to "on" to enable the i2c_csi_dsi1 interface ++ i2c_csi_dsi1 A Pi 5 family-specific alias for i2c_csi_dsi. + + i2c_vc Set to "on" to enable the i2c interface + usually reserved for the VideoCore processor @@ -12710,9 +12916,14 @@ index 000000000000..5c6344eb6f98 + non-lite SKU of CM4). + (default "on") + -+ sd_cqe Set to "off" to disable Command Queueing if you -+ have an incompatible Class A2 SD card -+ (Pi 5 only, default "on") ++ sd_cqe Modify Command Queuing behaviour on the main SD ++ interface. Legal values are: ++ 0: disable CQ ++ 1: allow CQ for known-good SD A2 cards, and all ++ eMMC cards ++ 2: allow CQ for all SD A2 cards that aren't ++ known-bad, and all eMMC cards. ++ (2712 only, default "1") + + sd_overclock Clock (in MHz) to use when the MMC framework + requests 50MHz @@ -12868,6 +13079,8 @@ index 000000000000..5c6344eb6f98 +Params: addr I2C bus address of device. Set based on how the + addr pin is wired. (default=0x48 assumes addr + is pulled to GND) ++ i2c-bus Supports all the standard I2C bus selection ++ parameters - see "dtoverlay -h i2c-bus" + cha_enable Enable virtual channel a. + cha_cfg Set the configuration for virtual channel a. + (default=4 configures this channel for the @@ -12877,16 +13090,6 @@ index 000000000000..5c6344eb6f98 + cha_gain Set the gain of the Programmable Gain + Amplifier for this channel. (Default 1 sets the + full scale of the channel to 4.096 Volts) -+ i2c0 Choose the I2C0 bus on GPIOs 0&1 -+ i2c_csi_dsi Choose the I2C0 bus on GPIOs 44&45 -+ i2c3 Choose the I2C3 bus (configure with the i2c3 -+ overlay - BCM2711 only) -+ i2c4 Choose the I2C4 bus (configure with the i2c4 -+ overlay - BCM2711 only) -+ i2c5 Choose the I2C5 bus (configure with the i2c5 -+ overlay - BCM2711 only) -+ i2c6 Choose the I2C6 bus (configure with the i2c6 -+ overlay - BCM2711 only) + + Channel parameters can be set for each enabled channel. + A maximum of 4 channels can be enabled (letters a thru d). @@ -13557,19 +13760,11 @@ index 000000000000..5c6344eb6f98 + invx Touchscreen inverted x axis + invy Touchscreen inverted y axis + swapxy Touchscreen swapped x y axis -+ i2c0 Choose the I2C0 bus on GPIOs 0&1 -+ i2c1 Choose the I2C1 bus on GPIOs 2&3 -+ i2c3 Choose the I2C3 bus (configure with the i2c3 -+ overlay - BCM2711 only) -+ i2c4 Choose the I2C4 bus (configure with the i2c4 -+ overlay - BCM2711 only) -+ i2c5 Choose the I2C5 bus (configure with the i2c5 -+ overlay - BCM2711 only) -+ i2c6 Choose the I2C6 bus (configure with the i2c6 -+ overlay - BCM2711 only) + addr Sets the address for the touch controller. Note + that the device must be configured to use the + specified address. ++ i2c-bus Supports all the standard I2C bus selection ++ parameters - see "dtoverlay -h i2c-bus" + + +Name: enc28j60 @@ -13769,8 +13964,10 @@ index 000000000000..5c6344eb6f98 +Info: Enables I2C connected Goodix gt9271 multiple touch controller using + GPIOs 4 and 17 (pins 7 and 11 on GPIO header) for interrupt and reset. +Load: dtoverlay=goodix,= -+Params: interrupt GPIO used for interrupt (default 4) ++Params: addr I2C address (default 0x14) ++ interrupt GPIO used for interrupt (default 4) + reset GPIO used for reset (default 17) ++ i2c-path Override I2C path to allow for i2c-gpio buses + + +Name: googlevoicehat-soundcard @@ -14062,6 +14259,7 @@ index 000000000000..5c6344eb6f98 + display_height Height of the display in characters (default 2) + + display_width Width of the display in characters (default 16) ++ i2c-path Override I2C path to allow for i2c-gpio buses + + +Name: hd44780-lcd @@ -14192,6 +14390,9 @@ index 000000000000..5c6344eb6f98 + +Name: hifiberry-dac8x +Info: Configures the HifiBerry DAC8X audio cards (only on Pi5) ++ This driver also detects a stacked ADC8x and activates the ++ capture capabilities. ++ Note: for standalone use of the ADC8x activate the ADC8x module. +Load: dtoverlay=hifiberry-dac8x +Params: + @@ -14404,28 +14605,40 @@ index 000000000000..5c6344eb6f98 +Params: + + -+Name: i2c-fan -+Info: Adds support for a number of I2C fan controllers -+Load: dtoverlay=i2c-fan,= -+Params: addr Sets the address for the fan controller. Note -+ that the device must be configured to use the -+ specified address. -+ -+ i2c0 Choose the I2C0 bus on GPIOs 0&1 -+ -+ i2c_csi_dsi Choose the I2C0 bus on GPIOs 44&45 -+ ++Name: i2c-bus ++Info: This is not a real overlay. Many overlays support the use of a variety ++ of I2C buses, and this is where the relevant parameters are documented. ++Load: ++Params: i2c0 Choose the I2C0 bus on GPIOs 0&1 ++ i2c1 Choose the I2C1 bus on GPIOs 2&3 ++ i2c2 Choose the I2C2 bus (configure with the i2c2 ++ overlay - BCM2711 only) + i2c3 Choose the I2C3 bus (configure with the i2c3 + overlay - BCM2711 only) -+ + i2c4 Choose the I2C4 bus (configure with the i2c4 + overlay - BCM2711 only) -+ + i2c5 Choose the I2C5 bus (configure with the i2c5 + overlay - BCM2711 only) -+ + i2c6 Choose the I2C6 bus (configure with the i2c6 + overlay - BCM2711 only) ++ i2c_csi_dsi Choose the I2C bus connected to the main ++ camera/display connector. ++ See "dtparam -h i2c_csi_dsi" for details. ++ i2c_csi_dsi0 Choose the I2C bus connected to the second ++ camera/display connector, if present. ++ See "dtparam -h i2c_csi_dsi0" for details. ++ i2c-path Override I2C path to allow for i2c-gpio buses ++ ++ ++Name: i2c-fan ++Info: Adds support for a number of I2C fan controllers ++Load: dtoverlay=i2c-fan,= ++Params: addr Sets the address for the fan controller. Note ++ that the device must be configured to use the ++ specified address. ++ ++ i2c-bus Supports all the standard I2C bus selection ++ parameters - see "dtoverlay -h i2c-bus" + + minpwm PWM setting for the fan when the SoC is below + mintemp (range 0-255. default 0) @@ -14478,25 +14691,12 @@ index 000000000000..5c6344eb6f98 + + addr Change I2C address of the device (default 0x70) + ++ i2c-bus Supports all the standard I2C bus selection ++ parameters - see "dtoverlay -h i2c-bus" ++ + base Set an explicit base value for the channel bus + numbers + -+ i2c0 Choose the I2C0 bus on GPIOs 0&1 -+ -+ i2c_csi_dsi Choose the I2C0 bus on GPIOs 44&45 -+ -+ i2c3 Choose the I2C3 bus (configure with the i2c3 -+ overlay - BCM2711 only) -+ -+ i2c4 Choose the I2C3 bus (configure with the i2c3 -+ overlay - BCM2711 only) -+ -+ i2c5 Choose the I2C5 bus (configure with the i2c4 -+ overlay - BCM2711 only) -+ -+ i2c6 Choose the I2C6 bus (configure with the i2c6 -+ overlay - BCM2711 only) -+ + disconnect_on_idle Force the mux to disconnect all child buses + after every transaction. + @@ -14508,16 +14708,8 @@ index 000000000000..5c6344eb6f98 +Info: Adds support for an NXP PCA9685A I2C PWM controller on i2c_arm +Load: dtoverlay=i2c-pwm-pca9685a,= +Params: addr I2C address of PCA9685A (default 0x40) -+ i2c0 Choose the I2C0 bus on GPIOs 0&1 -+ i2c_csi_dsi Choose the I2C0 bus on GPIOs 44&45 -+ i2c3 Choose the I2C3 bus (configure with the i2c3 -+ overlay - BCM2711 only) -+ i2c4 Choose the I2C3 bus (configure with the i2c3 -+ overlay - BCM2711 only) -+ i2c5 Choose the I2C5 bus (configure with the i2c4 -+ overlay - BCM2711 only) -+ i2c6 Choose the I2C6 bus (configure with the i2c6 -+ overlay - BCM2711 only) ++ i2c-bus Supports all the standard I2C bus selection ++ parameters - see "dtoverlay -h i2c-bus" + + +Name: i2c-rtc @@ -14571,21 +14763,8 @@ index 000000000000..5c6344eb6f98 + + s35390a Select the ABLIC S35390A device + -+ i2c0 Choose the I2C0 bus on GPIOs 0&1 -+ -+ i2c_csi_dsi Choose the I2C0 bus on GPIOs 44&45 -+ -+ i2c3 Choose the I2C3 bus (configure with the i2c3 -+ overlay - BCM2711 only) -+ -+ i2c4 Choose the I2C3 bus (configure with the i2c3 -+ overlay - BCM2711 only) -+ -+ i2c5 Choose the I2C5 bus (configure with the i2c4 -+ overlay - BCM2711 only) -+ -+ i2c6 Choose the I2C6 bus (configure with the i2c6 -+ overlay - BCM2711 only) ++ i2c-bus Supports all the standard I2C bus selection ++ parameters - see "dtoverlay -h i2c-bus" + + addr Sets the address for the RTC. Note that the + device must be configured to use the specified @@ -14711,6 +14890,9 @@ index 000000000000..5c6344eb6f98 + JC42, LM75, MCP980x, MPU6050, MPU9250, MS5637, + MS5803, MS5805, MS5837, MS8607, SHT3x or TMP102 + ++ i2c-bus Supports all the standard I2C bus selection ++ parameters - see "dtoverlay -h i2c-bus" ++ + adt7410 Select the Analog Devices ADT7410 and ADT7420 + temperature sensors + Valid address 0x48-0x4b, default 0x48 @@ -14746,11 +14928,20 @@ index 000000000000..5c6344eb6f98 + ds1621 Select the Dallas Semiconductors DS1621 temp + sensor. Valid addresses 0x48-0x4f, default 0x48 + ++ gain Gain used for measuring shunt resistor current. ++ Valid values 1 or 4, default 1. (ina238 only, ++ disabled by default) ++ + hdc100x Select the Texas Instruments HDC100x temp sensor + Valid addresses 0x40-0x43, default 0x40 + + htu21 Select the HTU21 temperature and humidity sensor + ++ ina238 Select the TI INA238 power monitor. Valid ++ addresses 0x40-0x4F, default 0x40. ++ Uses parameters shunt-resistor and ++ ti,shunt-gain for configuration ++ + int_pin Set the GPIO to use for interrupts (max30102, + mpu6050 and mpu9250 only) + @@ -14809,6 +15000,10 @@ index 000000000000..5c6344eb6f98 + reset_pin GPIO to be used to reset the device (bno055 + only, disabled by default) + ++ shunt_resistor Value of shunt resistor used for current ++ measurement in uOhms. (ina238 only, disabled ++ by default) ++ + sht3x Select the Sensirion SHT3x temperature and + humidity sensors. Valid addresses 0x44-0x45, + default 0x44 @@ -14817,6 +15012,9 @@ index 000000000000..5c6344eb6f98 + humidity sensors. Valid addresses 0x44-0x45, + default 0x44 + ++ shtc3 Select the Sensirion SHTC3 temperature and ++ humidity sensors. ++ + si7020 Select the Silicon Labs Si7013/20/21 humidity/ + temperature sensor + @@ -14835,22 +15033,6 @@ index 000000000000..5c6344eb6f98 + veml6070 Select the Vishay VEML6070 ultraviolet light + sensor + -+ i2c0 Choose the I2C0 bus on GPIOs 0&1 -+ -+ i2c_csi_dsi Choose the I2C0 bus on GPIOs 44&45 -+ -+ i2c3 Choose the I2C3 bus (configure with the i2c3 -+ overlay - BCM2711 only) -+ -+ i2c4 Choose the I2C3 bus (configure with the i2c3 -+ overlay - BCM2711 only) -+ -+ i2c5 Choose the I2C5 bus (configure with the i2c4 -+ overlay - BCM2711 only) -+ -+ i2c6 Choose the I2C6 bus (configure with the i2c6 -+ overlay - BCM2711 only) -+ + +Name: i2c0 +Info: Change i2c0 pin usage. Not all pin combinations are usable on all @@ -14993,6 +15175,7 @@ index 000000000000..5c6344eb6f98 + touchscreen (in pixels) + sizey Touchscreen size y, vertical resolution of + touchscreen (in pixels) ++ i2c-path Override I2C path to allow for i2c-gpio buses + + +Name: imx219 @@ -15009,6 +15192,8 @@ index 000000000000..5c6344eb6f98 + cam0 Adopt the default configuration for CAM0 on a + Compute Module (CSI0, i2c_vc, and cam0_reg). + vcm Configure a VCM focus drive on the sensor. ++ 4lane Enable 4 CSI2 lanes. This requires a Compute ++ Module (1, 3, 4, or 5) or Pi 5. + + +Name: imx258 @@ -15112,6 +15297,39 @@ index 000000000000..5c6344eb6f98 + camera clamping I/Os such as XVS to 0V. + sync-source Configure as vsync source + sync-sink Configure as vsync sink ++ link-frequency Allowable link frequency values to use in Hz: ++ 450000000 (default), 453000000, 456000000. ++ ++ ++Name: imx415 ++Info: Sony IMX415 camera module. ++ Uses Unicam 1, which is the standard camera connector on most Pi ++ variants. By default this uses 4 CSI2 data lanes, so requires a ++ Compute Module or Pi5. ++Load: dtoverlay=imx415, ++Params: addr Set I2C address of sensor. Valid values are ++ 0x10, 0x1a, 0x36 and 0x37. Default is 0x37. ++ 4lane Enable 4 CSI2 data lanes. ++ clock-frequency Sets the clock frequency to match that used on ++ the board. ++ Valid values are 24, 27, 37.125, 72, or ++ 74.25MHz. ++ The default is 24MHz. ++ Note that the link frequencies permitted vary ++ based on the oscillator used. ++ link-frequency Confgures the link frequency to be used. Note ++ that the permitted values vary based on ++ clock-frequency and number of lanes. ++ The default is 360MHz for 720Mbit/s. ++ orientation Sensor orientation (0 = front, 1 = rear, ++ 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). ++ vcm Enable ad5398 VCM associated with the sensor. + + +Name: imx462 @@ -15154,6 +15372,8 @@ index 000000000000..5c6344eb6f98 + camera clamping I/Os such as XVS to 0V. + sync-source Configure as vsync source + sync-sink Configure as vsync sink ++ link-frequency Allowable link frequency values to use in Hz: ++ 450000000 (default), 453000000, 456000000. + + +Name: imx500 @@ -15454,18 +15674,11 @@ index 000000000000..5c6344eb6f98 + + addr I2C address of the MCP23017 (default: 0x20) + ++ i2c-bus Supports all the standard I2C bus selection ++ parameters - see "dtoverlay -h i2c-bus" ++ + mcp23008 Configure an MCP23008 instead. + noints Disable the interrupt GPIO line. -+ i2c0 Choose the I2C0 bus on GPIOs 0&1 -+ i2c_csi_dsi Choose the I2C0 bus on GPIOs 44&45 -+ i2c3 Choose the I2C3 bus (configure with the i2c3 -+ overlay - BCM2711 only) -+ i2c4 Choose the I2C4 bus (configure with the i2c4 -+ overlay - BCM2711 only) -+ i2c5 Choose the I2C5 bus (configure with the i2c5 -+ overlay - BCM2711 only) -+ i2c6 Choose the I2C6 bus (configure with the i2c6 -+ overlay - BCM2711 only) + + +Name: mcp23s17 @@ -15866,6 +16079,8 @@ index 000000000000..5c6344eb6f98 + configuring the sensor (default on) + cam0 Adopt the default configuration for CAM0 on a + Compute Module (CSI0, i2c_vc, and cam0_reg). ++ arducam Slow down the regulator for slow Arducam ++ modules. + + +Name: papirus @@ -15883,6 +16098,8 @@ index 000000000000..5c6344eb6f98 +Info: TI PCA953x family of I2C GPIO expanders. Default is for NXP PCA9534. +Load: dtoverlay=pca953x,= +Params: addr I2C address of expander. Default 0x20. ++ i2c-bus Supports all the standard I2C bus selection ++ parameters - see "dtoverlay -h i2c-bus" + pca6416 Select the NXP PCA6416 (16 bit) + pca9505 Select the NXP PCA9505 (40 bit) + pca9535 Select the NXP PCA9535 (16 bit) @@ -15920,6 +16137,8 @@ index 000000000000..5c6344eb6f98 +Load: dtoverlay=pcf857x,= +Params: addr I2C address of expander. Default + depends on model selected. ++ i2c-bus Supports all the standard I2C bus selection ++ parameters - see "dtoverlay -h i2c-bus" + pcf8574 Select the NXP PCF8574 (8 bit) + pcf8574a Select the NXP PCF8574A (8 bit) + pcf8575 Select the NXP PCF8575 (16 bit) @@ -16029,6 +16248,14 @@ index 000000000000..5c6344eb6f98 +Params: + + ++Name: pimidi ++Info: Configures the Blokas Labs Pimidi card ++Load: dtoverlay=pimidi,= ++Params: sel The position used for the sel rotary switch. ++ Each unit in the stack must be set on a unique ++ position. If param is omitted, sel=0 is assumed. ++ ++ +Name: pineboards-hat-ai +Info: Pineboards Hat Ai! overlay for the Google Coral Edge TPU +Load: dtoverlay=pineboards-hat-ai @@ -16241,6 +16468,46 @@ index 000000000000..5c6344eb6f98 +Params: gpio Output pin (default 4) + + ++Name: pwm-gpio-fan ++Info: Configure a GPIO connected PWM cooling fan controlled by the ++ software-based GPIO PWM kernel module ++Load: dtoverlay=pwm-gpio-fan,= ++Params: fan_gpio BCM number of the pin driving the fan, ++ default 18 (GPIO 18) ++ fan_temp0 CPU temperature at which fan is started with ++ low speed in millicelsius, ++ default 55000 (55 °C) ++ fan_temp1 CPU temperature at which fan is switched ++ to medium speed in millicelsius, ++ default 60000 (60 °C) ++ fan_temp2 CPU temperature at which fan is switched ++ to high speed in millicelsius, ++ default 67500 (67.5 °C) ++ fan_temp3 CPU temperature at which fan is switched ++ to max speed in millicelsius, ++ default 75000 (75 °C) ++ fan_temp0_hyst Temperature hysteris at which fan is stopped ++ in millicelsius,default 5000 (resulting ++ in 50 °C) ++ fan_temp1_hyst Temperature hysteris at which fan is switched ++ back to low speed in millicelsius, ++ default 5000 (resulting in 55 °C) ++ fan_temp2_hyst Temperature hysteris at which fan is switched ++ back to medium speed in millicelsius, ++ default 5000 (resulting in 62.5 °C) ++ fan_temp3_hyst Temperature hysteris at which fan is switched ++ back to high speed in millicelsius, ++ default 5000 (resulting in 70 °C) ++ fan_temp0_speed Fan speed for low cooling state in range ++ 0 to 255, default 114 (45% PWM duty cycle) ++ fan_temp1_speed Fan speed for medium cooling state in range ++ 0 to 255, default 152 (60% PWM duty cycle) ++ fan_temp2_speed Fan speed for high cooling state in range ++ 0 to 255, default 204 (80% PWM duty cycle) ++ fan_temp3_speed Fan speed for max cooling state in range ++ 0 to 255, default 255 (100% PWM duty cycle) ++ ++ +Name: pwm-ir-tx +Info: Use GPIO pin as pwm-assisted infrared transmitter output. + This is an alternative to "gpio-ir-tx". pwm-ir-tx makes use @@ -16254,6 +16521,14 @@ index 000000000000..5c6344eb6f98 + func Pin function (default 2 = Alt5) + + ++Name: pwm-pio ++Info: Configures a GPIO pin as PIO-assisted PWM output. Unlike hardware PWM, ++ this can be used on any RP1 GPIO in bank 0 (0-27). Up to 4 are ++ supported, assuming nothing else is using PIO. Pi 5 only. ++Load: dtoverlay=pwm-pio,= ++Params: gpio Output GPIO (0-27, default 4) ++ ++ +Name: pwm1 +Info: Configures one or two PWM channel on PWM1 (BCM2711 only) + N.B.: @@ -16320,6 +16595,13 @@ index 000000000000..5c6344eb6f98 + console-size Size of non-panic dmesg captures (default 0) + + ++Name: rootmaster ++Info: Overlay for OpenHydroponics RootMaster board. ++ https://openhydroponics.com/hw/rootmaster ++Load: dtoverlay=rootmaster ++Params: ++ ++ +Name: rotary-encoder +Info: Overlay for GPIO connected rotary encoder. +Load: dtoverlay=rotary-encoder,= @@ -16453,6 +16735,18 @@ index 000000000000..5c6344eb6f98 + touchscreen-swapped-x-y Swap X and Y cordinates (default 0); + + ++Name: rpi-fw-uart ++Info: Configures the firmware software UART driver. ++ This driver requires exclusive usage of the second VPU core. The ++ following config.txt entries should be set when this driver is used. ++ dtparam=audio=off ++ isp_use_vpu0=1 ++Load: dtoverlay=rpi-fw-uart,[=] ++Params: txd0_pin GPIO pin for TXD0 (any free - default 20) ++ ++ rxd0_pin GPIO pin for RXD0 (any free - default 21) ++ ++ +Name: rpi-poe +Info: Raspberry Pi PoE HAT fan +Load: dtoverlay=rpi-poe,[=] @@ -16554,6 +16848,8 @@ index 000000000000..5c6344eb6f98 +Load: dtoverlay=sc16is750-i2c,= +Params: int_pin GPIO used for IRQ (default 24) + addr Address (default 0x48) ++ i2c-bus Supports all the standard I2C bus selection ++ parameters - see "dtoverlay -h i2c-bus" + xtal On-board crystal frequency (default 14745600) + + @@ -16572,6 +16868,8 @@ index 000000000000..5c6344eb6f98 +Load: dtoverlay=sc16is752-i2c,= +Params: int_pin GPIO used for IRQ (default 24) + addr Address (default 0x48) ++ i2c-bus Supports all the standard I2C bus selection ++ parameters - see "dtoverlay -h i2c-bus" + xtal On-board crystal frequency (default 14745600) + + @@ -16758,6 +17056,14 @@ index 000000000000..5c6344eb6f98 + it for other uses. + + ++Name: spi0-1cs-inverted ++Info: Only use one CS pin for SPI0 and set to active-high ++Load: dtoverlay=spi0-1cs-inverted,= ++Params: cs0_pin GPIO pin for CS0 (default 8) ++ no_miso Don't claim and use the MISO pin (9), freeing ++ it for other uses. ++ ++ +Name: spi0-2cs +Info: Change the CS pins for SPI0 +Load: dtoverlay=spi0-2cs,= @@ -17389,6 +17695,7 @@ index 000000000000..5c6344eb6f98 + vsync-invert Vertical sync active low + de-invert Data Enable active low + pixclk-invert Negative edge pixel clock ++ interlaced Use an interlaced mode (where supported) + 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 @@ -17561,6 +17868,7 @@ index 000000000000..5c6344eb6f98 + invy Touchscreen inverted y axis + swapxy Touchscreen swapped x y axis + disable_touch Disables the touch screen overlay driver ++ rotation Display rotation {0,90,180,270} (default 0) + dsi0 Use DSI0 and i2c_csi_dsi0 (rather than + the default DSI1 and i2c_csi_dsi). + @@ -17627,6 +17935,8 @@ index 000000000000..5c6344eb6f98 + 8_0_inch 8.0" 1280x800 + 10_1_inch 10.1" 1280x800 + 11_9_inch 11.9" 320x1480 ++ 13_3_inch_4lane 13.3" 1920x1080 4lane ++ 13_3_inch_2lane 13.3" 1920x1080 2lane + i2c1 Use i2c-1 with jumper wires from GPIOs 2&3 + disable_touch Disable the touch controller + rotation Set the panel orientation property @@ -17825,6 +18135,28 @@ index 000000000000..5c6344eb6f98 + compatible Changes the codec compatibility + + ++Name: ws2812-pio ++Info: Configures a GPIO pin to drive a string of WS2812 LEDS using pio. It ++ can be enabled on any RP1 GPIO in bank 0 (0-27). Up to 4 are supported, ++ assuming nothing else is using PIO. Pi 5 only. ++Load: dtoverlay=ws2812-pio,= ++Params: brightness Set the initial brightness for the LEDs. The ++ brightness can be changed at runtime by writing ++ a single byte to offset 0 of the device. Note ++ that brightness is a multiplier for the pixel ++ values, and only white pixels can reach the ++ maximum visible brightness. (range 0-255, ++ default 255) ++ dev_name The name for the /dev/ device entry. Note that ++ if the name includes '%d' it will be replaced ++ by the instance number. (default 'leds%d') ++ gpio Output GPIO (0-27, default 4) ++ num_leds Number of LEDs (default 60) ++ rgbw 'rgbw=on' (or 'rgbw') indicates that each pixel ++ includes a white LED as well as the usual red, ++ green and blue. (default 'off') ++ ++ +Troubleshooting +=============== + @@ -18240,10 +18572,10 @@ index 000000000000..dc1764613a8b +}; diff --git a/arch/arm/boot/dts/overlays/ads1115-overlay.dts b/arch/arm/boot/dts/overlays/ads1115-overlay.dts new file mode 100644 -index 000000000000..64ada16de9c7 +index 000000000000..6a1d86929add --- /dev/null +++ b/arch/arm/boot/dts/overlays/ads1115-overlay.dts -@@ -0,0 +1,135 @@ +@@ -0,0 +1,105 @@ +/* + * TI ADS1115 multi-channel ADC overlay + */ @@ -18251,6 +18583,8 @@ index 000000000000..64ada16de9c7 +/dts-v1/; +/plugin/; + ++#include "i2c-buses.dtsi" ++ +/ { + compatible = "brcm,bcm2835"; + @@ -18327,27 +18661,6 @@ index 000000000000..64ada16de9c7 + }; + }; + -+ frag100: fragment@100 { -+ target = <&i2c1>; -+ i2cbus: __overlay__ { -+ status = "okay"; -+ }; -+ }; -+ -+ fragment@101 { -+ target = <&i2c0if>; -+ __dormant__ { -+ status = "okay"; -+ }; -+ }; -+ -+ fragment@102 { -+ target = <&i2c0mux>; -+ __dormant__ { -+ status = "okay"; -+ }; -+ }; -+ + __overrides__ { + addr = <&ads1115>,"reg:0"; + cha_enable = <0>,"=0"; @@ -18366,17 +18679,6 @@ index 000000000000..64ada16de9c7 + chd_cfg = <&channel_d>,"reg:0"; + chd_gain = <&channel_d>,"ti,gain:0"; + chd_datarate = <&channel_d>,"ti,datarate:0"; -+ i2c0 = <&frag100>, "target:0=",<&i2c0>; -+ i2c_csi_dsi = <&frag100>, "target:0=",<&i2c_csi_dsi>, -+ <0>,"+101+102"; -+ i2c3 = <&frag100>, "target?=0", -+ <&frag100>, "target-path=i2c3"; -+ i2c4 = <&frag100>, "target?=0", -+ <&frag100>, "target-path=i2c4"; -+ i2c5 = <&frag100>, "target?=0", -+ <&frag100>, "target-path=i2c5"; -+ i2c6 = <&frag100>, "target?=0", -+ <&frag100>, "target-path=i2c6"; + }; +}; diff --git a/arch/arm/boot/dts/overlays/ads7846-overlay.dts b/arch/arm/boot/dts/overlays/ads7846-overlay.dts @@ -23251,10 +23553,10 @@ index 000000000000..0d83e344ad97 +}; diff --git a/arch/arm/boot/dts/overlays/edt-ft5406-overlay.dts b/arch/arm/boot/dts/overlays/edt-ft5406-overlay.dts new file mode 100644 -index 000000000000..6e40c0ebb3bf +index 000000000000..7cbc3fa673a3 --- /dev/null +++ b/arch/arm/boot/dts/overlays/edt-ft5406-overlay.dts -@@ -0,0 +1,46 @@ +@@ -0,0 +1,22 @@ +/* + * Device Tree overlay for EDT 5406 touchscreen controller, as used on the + * Raspberry Pi 7" panel @@ -23264,49 +23566,25 @@ index 000000000000..6e40c0ebb3bf +/dts-v1/; +/plugin/; + ++#define ENABLE_I2C0_MUX ++#include "i2c-buses.dtsi" +#include "edt-ft5406.dtsi" + -+/ { -+ fragment@0 { -+ target = <&i2c0if>; -+ __overlay__ { -+ status = "okay"; -+ }; -+ }; -+ -+ fragment@1 { -+ target = <&i2c0mux>; -+ __overlay__ { -+ status = "okay"; -+ }; -+ }; ++&busfrag { ++ target = <&i2c_csi_dsi>; ++}; + ++/ { + __overrides__ { -+ i2c0 = <&ts_i2c_frag>,"target:0=",<&i2c0>; -+ i2c1 = <&ts_i2c_frag>, "target?=0", -+ <&ts_i2c_frag>, "target-path=i2c1", -+ <0>,"-0-1"; -+ i2c3 = <&ts_i2c_frag>, "target?=0", -+ <&ts_i2c_frag>, "target-path=i2c3", -+ <0>,"-0-1"; -+ i2c4 = <&ts_i2c_frag>, "target?=0", -+ <&ts_i2c_frag>, "target-path=i2c4", -+ <0>,"-0-1"; -+ i2c5 = <&ts_i2c_frag>, "target?=0", -+ <&ts_i2c_frag>, "target-path=i2c5", -+ <0>,"-0-1"; -+ i2c6 = <&ts_i2c_frag>, "target?=0", -+ <&ts_i2c_frag>, "target-path=i2c6", -+ <0>,"-0-1"; + addr = <&ft5406>,"reg:0"; + }; +}; diff --git a/arch/arm/boot/dts/overlays/edt-ft5406.dtsi b/arch/arm/boot/dts/overlays/edt-ft5406.dtsi new file mode 100644 -index 000000000000..16aa5cf91df5 +index 000000000000..3be2f18e898f --- /dev/null +++ b/arch/arm/boot/dts/overlays/edt-ft5406.dtsi -@@ -0,0 +1,49 @@ +@@ -0,0 +1,48 @@ +/* + * Device Tree overlay for an EDT FT5406 touchscreen + * @@ -23331,12 +23609,11 @@ index 000000000000..16aa5cf91df5 + }; + }; + -+ ts_i2c_frag: fragment@12 { -+ target = <&i2c_csi_dsi>; ++ fragment@12 { ++ target = <&i2cbus>; + __overlay__ { + #address-cells = <1>; + #size-cells = <0>; -+ status = "okay"; + + ft5406: ts@38 { + compatible = "edt,edt-ft5506"; @@ -24635,10 +24912,10 @@ index 000000000000..d2f1e9a888e0 +}; diff --git a/arch/arm/boot/dts/overlays/goodix-overlay.dts b/arch/arm/boot/dts/overlays/goodix-overlay.dts new file mode 100644 -index 000000000000..8571527de49a +index 000000000000..8a5035df9daa --- /dev/null +++ b/arch/arm/boot/dts/overlays/goodix-overlay.dts -@@ -0,0 +1,46 @@ +@@ -0,0 +1,49 @@ +// Device tree overlay for I2C connected Goodix gt9271 multiple touch controller +/dts-v1/; +/plugin/; @@ -24657,7 +24934,7 @@ index 000000000000..8571527de49a + }; + }; + -+ fragment@1 { ++ i2c_frag: fragment@1 { + target = <&i2c1>; + __overlay__ { + #address-cells = <1>; @@ -24678,11 +24955,14 @@ index 000000000000..8571527de49a + }; + + __overrides__ { ++ addr = <>9271>,"reg:0"; + interrupt = <&goodix_pins>,"brcm,pins:0", + <>9271>,"interrupts:0", + <>9271>,"irq-gpios:4"; + reset = <&goodix_pins>,"brcm,pins:4", + <>9271>,"reset-gpios:4"; ++ i2c-path = <&i2c_frag>, "target?=0", ++ <&i2c_frag>, "target-path"; + }; +}; diff --git a/arch/arm/boot/dts/overlays/googlevoicehat-soundcard-overlay.dts b/arch/arm/boot/dts/overlays/googlevoicehat-soundcard-overlay.dts @@ -25479,17 +25759,17 @@ index 000000000000..0b5d902e85b8 +}; diff --git a/arch/arm/boot/dts/overlays/hd44780-i2c-lcd-overlay.dts b/arch/arm/boot/dts/overlays/hd44780-i2c-lcd-overlay.dts new file mode 100644 -index 000000000000..36fcf049ffbf +index 000000000000..d5bfd762fe2e --- /dev/null +++ b/arch/arm/boot/dts/overlays/hd44780-i2c-lcd-overlay.dts -@@ -0,0 +1,57 @@ +@@ -0,0 +1,59 @@ +/dts-v1/; +/plugin/; + +/ { + compatible = "brcm,bcm2835"; + -+ fragment@0 { ++ i2c_frag: fragment@0 { + target = <&i2c_arm>; + __overlay__ { + status = "okay"; @@ -25537,6 +25817,8 @@ index 000000000000..36fcf049ffbf + display_height = <&lcd_screen>,"display-height-chars:0"; + display_width = <&lcd_screen>,"display-width-chars:0"; + addr = <&pcf857x>,"reg:0"; ++ i2c-path = <&i2c_frag>, "target?=0", ++ <&i2c_frag>, "target-path"; + }; + +}; @@ -26044,14 +26326,17 @@ index 000000000000..efb0e18dbdc4 +}; diff --git a/arch/arm/boot/dts/overlays/hifiberry-dac8x-overlay.dts b/arch/arm/boot/dts/overlays/hifiberry-dac8x-overlay.dts new file mode 100644 -index 000000000000..efeff792f396 +index 000000000000..1f4bcfc2328e --- /dev/null +++ b/arch/arm/boot/dts/overlays/hifiberry-dac8x-overlay.dts -@@ -0,0 +1,50 @@ +@@ -0,0 +1,56 @@ ++// SPDX-License-Identifier: GPL-2.0 +// Definitions for HiFiBerry DAC8x +/dts-v1/; +/plugin/; + ++#include ++ +/ { + compatible = "brcm,bcm2712"; + @@ -26060,8 +26345,10 @@ index 000000000000..efeff792f396 + __overlay__ { + rp1_i2s0_dac8x: rp1_i2s0_dac8x { + function = "i2s0"; -+ pins = "gpio18", "gpio19", "gpio21", -+ "gpio23", "gpio25", "gpio27"; ++ pins = "gpio18", "gpio19", "gpio20", ++ "gpio21", "gpio22", "gpio23", ++ "gpio24", "gpio25", "gpio26", ++ "gpio27"; + bias-disable; + status = "okay"; + }; @@ -26080,9 +26367,9 @@ index 000000000000..efeff792f396 + fragment@2 { + target-path = "/"; + __overlay__ { -+ pcm5102a-codec { ++ dummy-codec { + #sound-dai-cells = <0>; -+ compatible = "ti,pcm5102a"; ++ compatible = "snd-soc-dummy"; + status = "okay"; + }; + }; @@ -26093,6 +26380,7 @@ index 000000000000..efeff792f396 + __overlay__ { + compatible = "hifiberry,hifiberry-dac8x"; + i2s-controller = <&i2s_clk_producer>; ++ hasadc-gpio = <&gpio 5 GPIO_ACTIVE_LOW>; + status = "okay"; + }; + }; @@ -27249,18 +27537,93 @@ index 000000000000..8204b6b3aef8 + }; + }; +}; +diff --git a/arch/arm/boot/dts/overlays/i2c-buses.dtsi b/arch/arm/boot/dts/overlays/i2c-buses.dtsi +new file mode 100644 +index 000000000000..5c7699a8ac66 +--- /dev/null ++++ b/arch/arm/boot/dts/overlays/i2c-buses.dtsi +@@ -0,0 +1,67 @@ ++// Common i2c buses, and dtparams to select them ++ ++/ { ++ compatible = "brcm,bcm2835"; ++ ++ busfrag: fragment@100 { ++ target = <&i2c_arm>; ++ i2cbus: __overlay__ { ++ status = "okay"; ++ }; ++ }; ++ ++ fragment@101 { ++ target = <&i2c0if>; ++#ifdef ENABLE_I2C0_MUX ++ __overlay__ { ++#else ++ __dormant__ { ++#endif ++ status = "okay"; ++ }; ++ }; ++ ++ fragment@102 { ++ target = <&i2c0mux>; ++#ifdef ENABLE_I2C0_MUX ++ __overlay__ { ++#else ++ __dormant__ { ++#endif ++ status = "okay"; ++ }; ++ }; ++ ++ __overrides__ { ++ i2c0 = <&busfrag>,"target:0=",<&i2c0>, ++ <&busfrag>, "target-path?=0", ++ <0>,"+101+102"; ++ i2c_csi_dsi = <&busfrag>,"target:0=",<&i2c_csi_dsi>, ++ <&busfrag>, "target-path?=0", ++ <0>,"+101+102"; ++ i2c_csi_dsi0 = <&busfrag>, "target:0=",<&i2c_csi_dsi0>, ++ <&busfrag>, "target-path?=0", ++ <0>,"+101+102"; ++ i2c1 = <&busfrag>,"target:0=",<&i2c1>, ++ <&busfrag>, "target-path?=0", ++ <0>,"-101-102"; ++ i2c2 = <&busfrag>, "target?=0", ++ <&busfrag>, "target-path=i2c2", ++ <0>,"-101-102"; ++ i2c3 = <&busfrag>, "target?=0", ++ <&busfrag>, "target-path=i2c3", ++ <0>,"-101-102"; ++ i2c4 = <&busfrag>, "target?=0", ++ <&busfrag>, "target-path=i2c4", ++ <0>,"-101-102"; ++ i2c5 = <&busfrag>, "target?=0", ++ <&busfrag>, "target-path=i2c5", ++ <0>,"-101-102"; ++ i2c6 = <&busfrag>, "target?=0", ++ <&busfrag>, "target-path=i2c6", ++ <0>,"-101-102"; ++ i2c-path = <&busfrag>, "target?=0", ++ <&busfrag>, "target-path", ++ <0>,"-101-102"; ++ }; ++}; diff --git a/arch/arm/boot/dts/overlays/i2c-fan-overlay.dts b/arch/arm/boot/dts/overlays/i2c-fan-overlay.dts new file mode 100644 -index 000000000000..f2f4a2aa797a +index 000000000000..70e204cb81e6 --- /dev/null +++ b/arch/arm/boot/dts/overlays/i2c-fan-overlay.dts -@@ -0,0 +1,108 @@ +@@ -0,0 +1,77 @@ +// Definitions for I2C based sensors using the Industrial IO or HWMON interface. +/dts-v1/; +/plugin/; + +#include + ++#include "i2c-buses.dtsi" ++ +/ { + compatible = "brcm,bcm2835"; + @@ -27274,41 +27637,19 @@ index 000000000000..f2f4a2aa797a + emc2301: emc2301@2f { + compatible = "microchip,emc2301"; + reg = <0x2f>; -+ status = "okay"; + #cooling-cells = <0x02>; + }; + }; + }; + -+ frag100: fragment@100 { -+ target = <&i2c_arm>; -+ i2cbus: __overlay__ { -+ status = "okay"; -+ }; -+ }; -+ -+ fragment@101 { -+ target = <&i2c0if>; -+ __dormant__ { -+ status = "okay"; -+ }; -+ }; -+ -+ fragment@102 { -+ target = <&i2c0mux>; -+ __dormant__ { -+ status = "okay"; -+ }; -+ }; -+ -+ fragment@103 { ++ fragment@1 { + target = <&cpu_thermal>; + __overlay__ { + polling-delay = <2000>; /* milliseconds */ + }; + }; + -+ fragment@104 { ++ fragment@2 { + target = <&thermal_trips>; + __overlay__ { + fanmid0: fanmid0 { @@ -27324,7 +27665,7 @@ index 000000000000..f2f4a2aa797a + }; + }; + -+ fragment@105 { ++ fragment@3 { + target = <&cooling_maps>; + __overlay__ { + map0: map0 { @@ -27339,17 +27680,6 @@ index 000000000000..f2f4a2aa797a + }; + + __overrides__ { -+ i2c0 = <&frag100>,"target:0=",<&i2c0>; -+ i2c_csi_dsi = <&frag100>,"target:0=",<&i2c_csi_dsi>, -+ <0>,"+101+102"; -+ i2c3 = <&frag100>, "target?=0", -+ <&frag100>, "target-path=i2c3"; -+ i2c4 = <&frag100>, "target?=0", -+ <&frag100>, "target-path=i2c4"; -+ i2c5 = <&frag100>, "target?=0", -+ <&frag100>, "target-path=i2c5"; -+ i2c6 = <&frag100>, "target?=0", -+ <&frag100>, "target-path=i2c6"; + addr = <&emc2301>,"reg:0"; + minpwm = <&emc2301>,"emc2305,pwm-min.0"; + maxpwm = <&emc2301>,"emc2305,pwm-max.0"; @@ -27418,10 +27748,10 @@ index 000000000000..63231b5d7c0c +}; diff --git a/arch/arm/boot/dts/overlays/i2c-mux-overlay.dts b/arch/arm/boot/dts/overlays/i2c-mux-overlay.dts new file mode 100644 -index 000000000000..0575b276cd26 +index 000000000000..be79831ea8d2 --- /dev/null +++ b/arch/arm/boot/dts/overlays/i2c-mux-overlay.dts -@@ -0,0 +1,183 @@ +@@ -0,0 +1,149 @@ +// Umbrella I2C Mux overlay + +/dts-v1/; @@ -27429,6 +27759,8 @@ index 000000000000..0575b276cd26 + +#include + ++#include "i2c-buses.dtsi" ++ +/{ + compatible = "brcm,bcm2835"; + @@ -27437,7 +27769,6 @@ index 000000000000..0575b276cd26 + __dormant__ { + #address-cells = <1>; + #size-cells = <0>; -+ status = "okay"; + + pca9542: mux@70 { + compatible = "nxp,pca9542"; @@ -27464,7 +27795,6 @@ index 000000000000..0575b276cd26 + __dormant__ { + #address-cells = <1>; + #size-cells = <0>; -+ status = "okay"; + + pca9545: mux@70 { + compatible = "nxp,pca9545"; @@ -27501,7 +27831,6 @@ index 000000000000..0575b276cd26 + __dormant__ { + #address-cells = <1>; + #size-cells = <0>; -+ status = "okay"; + + pca9548: mux@70 { + compatible = "nxp,pca9548"; @@ -27553,27 +27882,6 @@ index 000000000000..0575b276cd26 + }; + }; + -+ frag100: fragment@100 { -+ target = <&i2c_arm>; -+ i2cbus: __overlay__ { -+ status = "okay"; -+ }; -+ }; -+ -+ fragment@101 { -+ target = <&i2c0if>; -+ __dormant__ { -+ status = "okay"; -+ }; -+ }; -+ -+ fragment@102 { -+ target = <&i2c0mux>; -+ __dormant__ { -+ status = "okay"; -+ }; -+ }; -+ + __overrides__ { + pca9542 = <0>, "+0"; + pca9545 = <0>, "+1"; @@ -27587,18 +27895,6 @@ index 000000000000..0575b276cd26 + <&pca9545>,"base-nr:0", + <&pca9548>,"base-nr:0"; + -+ i2c0 = <&frag100>, "target:0=",<&i2c0>, -+ <0>,"+101+102"; -+ i2c_csi_dsi = <&frag100>, "target:0=",<&i2c_csi_dsi>, -+ <0>,"+101+102"; -+ i2c3 = <&frag100>, "target?=0", -+ <&frag100>, "target-path=i2c3"; -+ i2c4 = <&frag100>, "target?=0", -+ <&frag100>, "target-path=i2c4"; -+ i2c5 = <&frag100>, "target?=0", -+ <&frag100>, "target-path=i2c5"; -+ i2c6 = <&frag100>, "target?=0", -+ <&frag100>, "target-path=i2c6"; + disconnect_on_idle = + <&pca9542>,"idle-state:0=", , + <&pca9545>,"idle-state:0=", , @@ -27607,14 +27903,16 @@ index 000000000000..0575b276cd26 +}; diff --git a/arch/arm/boot/dts/overlays/i2c-pwm-pca9685a-overlay.dts b/arch/arm/boot/dts/overlays/i2c-pwm-pca9685a-overlay.dts new file mode 100644 -index 000000000000..b8dfbd56d121 +index 000000000000..4941e78a3d93 --- /dev/null +++ b/arch/arm/boot/dts/overlays/i2c-pwm-pca9685a-overlay.dts -@@ -0,0 +1,61 @@ +@@ -0,0 +1,28 @@ +// Definitions for NXP PCA9685A I2C PWM controller on ARM I2C bus. +/dts-v1/; +/plugin/; + ++#include "i2c-buses.dtsi" ++ +/{ + compatible = "brcm,bcm2835"; + @@ -27623,7 +27921,6 @@ index 000000000000..b8dfbd56d121 + __overlay__ { + #address-cells = <1>; + #size-cells = <0>; -+ status = "okay"; + + pca: pca@40 { + compatible = "nxp,pca9685-pwm"; @@ -27634,42 +27931,8 @@ index 000000000000..b8dfbd56d121 + }; + }; + -+ -+ frag100: fragment@100 { -+ target = <&i2c_arm>; -+ i2cbus: __overlay__ { -+ status = "okay"; -+ }; -+ }; -+ -+ fragment@101 { -+ target = <&i2c0if>; -+ __dormant__ { -+ status = "okay"; -+ }; -+ }; -+ -+ fragment@102 { -+ target = <&i2c0mux>; -+ __dormant__ { -+ status = "okay"; -+ }; -+ }; -+ + __overrides__ { + addr = <&pca>,"reg:0"; -+ i2c0 = <&frag100>, "target:0=",<&i2c0>, -+ <0>,"+101+102"; -+ i2c_csi_dsi = <&frag100>, "target:0=",<&i2c_csi_dsi>, -+ <0>,"+101+102"; -+ i2c3 = <&frag100>, "target?=0", -+ <&frag100>, "target-path=i2c3"; -+ i2c4 = <&frag100>, "target?=0", -+ <&frag100>, "target-path=i2c4"; -+ i2c5 = <&frag100>, "target?=0", -+ <&frag100>, "target-path=i2c5"; -+ i2c6 = <&frag100>, "target?=0", -+ <&frag100>, "target-path=i2c6"; + }; +}; diff --git a/arch/arm/boot/dts/overlays/i2c-rtc-common.dtsi b/arch/arm/boot/dts/overlays/i2c-rtc-common.dtsi @@ -28084,62 +28347,23 @@ index 000000000000..c83480c1c327 +}; diff --git a/arch/arm/boot/dts/overlays/i2c-rtc-overlay.dts b/arch/arm/boot/dts/overlays/i2c-rtc-overlay.dts new file mode 100644 -index 000000000000..cd31eac7e333 +index 000000000000..e003244d894c --- /dev/null +++ b/arch/arm/boot/dts/overlays/i2c-rtc-overlay.dts -@@ -0,0 +1,42 @@ +@@ -0,0 +1,6 @@ +// Definitions for several I2C based Real Time Clocks +/dts-v1/; +/plugin/; + +#include "i2c-rtc-common.dtsi" -+ -+/ { -+ frag100: fragment@100 { -+ target = <&i2c_arm>; -+ i2cbus: __overlay__ { -+ status = "okay"; -+ }; -+ }; -+ -+ fragment@101 { -+ target = <&i2c0if>; -+ __dormant__ { -+ status = "okay"; -+ }; -+ }; -+ -+ fragment@102 { -+ target = <&i2c0mux>; -+ __dormant__ { -+ status = "okay"; -+ }; -+ }; -+ -+ __overrides__ { -+ i2c0 = <&frag100>, "target:0=",<&i2c0>; -+ i2c_csi_dsi = <&frag100>, "target:0=",<&i2c_csi_dsi>, -+ <0>,"+101+102"; -+ i2c3 = <&frag100>, "target?=0", -+ <&frag100>, "target-path=i2c3"; -+ i2c4 = <&frag100>, "target?=0", -+ <&frag100>, "target-path=i2c4"; -+ i2c5 = <&frag100>, "target?=0", -+ <&frag100>, "target-path=i2c5"; -+ i2c6 = <&frag100>, "target?=0", -+ <&frag100>, "target-path=i2c6"; -+ }; -+}; ++#include "i2c-buses.dtsi" diff --git a/arch/arm/boot/dts/overlays/i2c-sensor-common.dtsi b/arch/arm/boot/dts/overlays/i2c-sensor-common.dtsi new file mode 100755 -index 000000000000..83b2a1286426 +index 000000000000..f1662a0de40a --- /dev/null +++ b/arch/arm/boot/dts/overlays/i2c-sensor-common.dtsi -@@ -0,0 +1,597 @@ +@@ -0,0 +1,634 @@ +// Definitions for I2C based sensors using the Industrial IO or HWMON interface. -+/dts-v1/; -+/plugin/; -+ +#include + +/ { @@ -28667,6 +28891,42 @@ index 000000000000..83b2a1286426 + }; + }; + ++ fragment@35 { ++ target = <&i2cbus>; ++ __dormant__ { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ status = "okay"; ++ ++ ina238: ina238@48 { ++ compatible = "ti,ina238"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ reg = <0x40>; ++ /* uOhms, uint32_t */ ++ shunt-resistor = <1000>; ++ /* 1 or 4, (±40.96 mV or ±163.84 mV) */ ++ ti,shunt-gain = <1>; ++ status = "okay"; ++ }; ++ }; ++ }; ++ ++ fragment@36 { ++ target = <&i2cbus>; ++ __dormant__ { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ status = "okay"; ++ ++ shtc3: shtc3@70 { ++ compatible = "sensirion,shtc3"; ++ reg = <0x70>; ++ status = "okay"; ++ }; ++ }; ++ }; ++ + fragment@99 { + target = <&gpio>; + __dormant__ { @@ -28714,6 +28974,8 @@ index 000000000000..83b2a1286426 + bno055 = <0>,"+31"; + sht4x = <0>,"+32"; + adt7410 = <0>,"+34"; ++ ina238 = <0>,"+35"; ++ shtc3 = <0>,"+36"; + + addr = <&bme280>,"reg:0", <&bmp280>,"reg:0", <&tmp102>,"reg:0", + <&lm75>,"reg:0", <&hdc100x>,"reg:0", <&sht3x>,"reg:0", @@ -28723,7 +28985,7 @@ index 000000000000..83b2a1286426 + <&ms5837>,"reg:0", <&ms8607>,"reg:0", + <&mpu6050>,"reg:0", <&mpu9250>,"reg:0", + <&bno055>,"reg:0", <&sht4x>,"reg:0", -+ <&bmp380>,"reg:0", <&adt7410>,"reg:0"; ++ <&bmp380>,"reg:0", <&adt7410>,"reg:0", <&ina238>,"reg:0"; + int_pin = <&int_pins>, "brcm,pins:0", + <&int_pins>, "reg:0", + <&max30102>, "interrupts:0", @@ -28731,56 +28993,22 @@ index 000000000000..83b2a1286426 + <&mpu9250>, "interrupts:0"; + no_timeout = <&jc42>, "smbus-timeout-disable?"; + reset_pin = <&bno055>,"reset-gpios:4", <0>,"+30"; ++ shunt_resistor = <&ina238>,"shunt-resistor:0"; ++ gain = <&ina238>,"ti,shunt-gain:0"; + }; +}; diff --git a/arch/arm/boot/dts/overlays/i2c-sensor-overlay.dts b/arch/arm/boot/dts/overlays/i2c-sensor-overlay.dts new file mode 100755 -index 000000000000..f8a39659d83e +index 000000000000..29522e666908 --- /dev/null +++ b/arch/arm/boot/dts/overlays/i2c-sensor-overlay.dts -@@ -0,0 +1,42 @@ +@@ -0,0 +1,6 @@ +// Definitions for I2C based sensors using the Industrial IO or HWMON interface. +/dts-v1/; +/plugin/; + +#include "i2c-sensor-common.dtsi" -+ -+/ { -+ frag100: fragment@100 { -+ target = <&i2c_arm>; -+ i2cbus: __overlay__ { -+ status = "okay"; -+ }; -+ }; -+ -+ fragment@101 { -+ target = <&i2c0if>; -+ __dormant__ { -+ status = "okay"; -+ }; -+ }; -+ -+ fragment@102 { -+ target = <&i2c0mux>; -+ __dormant__ { -+ status = "okay"; -+ }; -+ }; -+ -+ __overrides__ { -+ i2c0 = <&frag100>, "target:0=",<&i2c0>; -+ i2c_csi_dsi = <&frag100>, "target:0=",<&i2c_csi_dsi>, -+ <0>,"+101+102"; -+ i2c3 = <&frag100>, "target?=0", -+ <&frag100>, "target-path=i2c3"; -+ i2c4 = <&frag100>, "target?=0", -+ <&frag100>, "target-path=i2c4"; -+ i2c5 = <&frag100>, "target?=0", -+ <&frag100>, "target-path=i2c5"; -+ i2c6 = <&frag100>, "target?=0", -+ <&frag100>, "target-path=i2c6"; -+ }; -+}; ++#include "i2c-buses.dtsi" diff --git a/arch/arm/boot/dts/overlays/i2c0-overlay.dts b/arch/arm/boot/dts/overlays/i2c0-overlay.dts new file mode 100644 index 000000000000..46bf1bf2dc5c @@ -29337,10 +29565,10 @@ index 000000000000..8b46067858d7 +}; diff --git a/arch/arm/boot/dts/overlays/ilitek251x-overlay.dts b/arch/arm/boot/dts/overlays/ilitek251x-overlay.dts new file mode 100644 -index 000000000000..551aba591d26 +index 000000000000..226d490ce092 --- /dev/null +++ b/arch/arm/boot/dts/overlays/ilitek251x-overlay.dts -@@ -0,0 +1,45 @@ +@@ -0,0 +1,47 @@ +// Device tree overlay for I2C connected Ilitek multiple touch controller +/dts-v1/; +/plugin/; @@ -29359,7 +29587,7 @@ index 000000000000..551aba591d26 + }; + }; + -+ fragment@1 { ++ frag1: fragment@1 { + target = <&i2c1>; + __overlay__ { + #address-cells = <1>; @@ -29384,14 +29612,16 @@ index 000000000000..551aba591d26 + <&ili251x>,"interrupts:0"; + sizex = <&ili251x>,"touchscreen-size-x:0"; + sizey = <&ili251x>,"touchscreen-size-y:0"; ++ i2c-path = <&frag1>, "target?=0", ++ <&frag1>, "target-path"; + }; +}; diff --git a/arch/arm/boot/dts/overlays/imx219-overlay.dts b/arch/arm/boot/dts/overlays/imx219-overlay.dts new file mode 100644 -index 000000000000..4c4bcd309a3d +index 000000000000..5c1a5d90ee3b --- /dev/null +++ b/arch/arm/boot/dts/overlays/imx219-overlay.dts -@@ -0,0 +1,89 @@ +@@ -0,0 +1,106 @@ +// SPDX-License-Identifier: GPL-2.0-only +// Definitions for IMX219 camera module on VC I2C bus +/dts-v1/; @@ -29459,6 +29689,22 @@ index 000000000000..4c4bcd309a3d + }; + }; + ++ fragment@201 { ++ target = <&csi_ep>; ++ __dormant__ { ++ data-lanes = <1 2 3 4>; ++ }; ++ }; ++ ++ fragment@202 { ++ target = <&cam_endpoint>; ++ __dormant__ { ++ data-lanes = <1 2 3 4>; ++ link-frequencies = ++ /bits/ 64 <363000000>; ++ }; ++ }; ++ + __overrides__ { + rotation = <&cam_node>,"rotation:0"; + orientation = <&cam_node>,"orientation:0"; @@ -29471,6 +29717,7 @@ index 000000000000..4c4bcd309a3d + <&vcm>, "VANA-supply:0=", <&cam0_reg>; + vcm = <&vcm>, "status=okay", + <&cam_node>,"lens-focus:0=", <&vcm>; ++ 4lane = <0>, "+201+202"; + }; +}; + @@ -29724,15 +29971,13 @@ index 000000000000..3de3c3910d90 +}; diff --git a/arch/arm/boot/dts/overlays/imx290_327-overlay.dtsi b/arch/arm/boot/dts/overlays/imx290_327-overlay.dtsi new file mode 100644 -index 000000000000..8fe48352e695 +index 000000000000..a69bc31d3663 --- /dev/null +++ b/arch/arm/boot/dts/overlays/imx290_327-overlay.dtsi -@@ -0,0 +1,112 @@ +@@ -0,0 +1,110 @@ +// SPDX-License-Identifier: GPL-2.0-only +// Partial definitions for IMX290 or IMX327 camera module on VC I2C bus +// The compatible string should be set in an overlay that then includes this one -+/dts-v1/; -+/plugin/; + +#include + @@ -30052,18 +30297,171 @@ index 000000000000..4a5072489a34 + sync-source = <&cam_node>,"trigger-mode:0=1"; + }; +}; +diff --git a/arch/arm/boot/dts/overlays/imx415-overlay.dts b/arch/arm/boot/dts/overlays/imx415-overlay.dts +new file mode 100644 +index 000000000000..d420b46abc8c +--- /dev/null ++++ b/arch/arm/boot/dts/overlays/imx415-overlay.dts +@@ -0,0 +1,116 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++// Definitions for IMX415 camera module on VC I2C bus ++/dts-v1/; ++/plugin/; ++ ++#include ++ ++/{ ++ compatible = "brcm,bcm2835"; ++ ++ fragment@0 { ++ target = <&i2c0if>; ++ __overlay__ { ++ status = "okay"; ++ }; ++ }; ++ ++ clk_frag: fragment@1 { ++ target = <&cam1_clk>; ++ cam_clk: __overlay__ { ++ status = "okay"; ++ clock-frequency = <24000000>; ++ }; ++ }; ++ ++ fragment@2 { ++ target = <&i2c0mux>; ++ __overlay__ { ++ status = "okay"; ++ }; ++ }; ++ ++ reg_frag: fragment@3 { ++ target = <&cam1_reg>; ++ cam_reg: __overlay__ { ++ startup-delay-us = <100000>; ++ }; ++ }; ++ ++ i2c_frag: fragment@100 { ++ target = <&i2c_csi_dsi>; ++ __overlay__ { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ status = "okay"; ++ ++ #include "imx415.dtsi" ++ ++ vcm: ad5398@c { ++ compatible = "adi,ad5398"; ++ reg = <0x0c>; ++ status = "disabled"; ++ VANA-supply = <&cam1_reg>; ++ }; ++ }; ++ }; ++ ++ csi_frag: fragment@101 { ++ target = <&csi1>; ++ csi: __overlay__ { ++ status = "okay"; ++ brcm,media-controller; ++ ++ port { ++ csi_ep: endpoint { ++ remote-endpoint = <&cam_endpoint>; ++ clock-lanes = <0>; ++ data-lanes = <1 2>; ++ clock-noncontinuous; ++ }; ++ }; ++ }; ++ }; ++ ++ fragment@201 { ++ target = <&cam_endpoint>; ++ __dormant__ { ++ data-lanes = <1 2 3 4>; ++ }; ++ }; ++ ++ fragment@202 { ++ target = <&csi_ep>; ++ __dormant__ { ++ data-lanes = <1 2 3 4>; ++ }; ++ }; ++ ++ ++ __overrides__ { ++ addr = <&cam_node>, "reg:0"; ++ rotation = <&cam_node>,"rotation:0"; ++ orientation = <&cam_node>,"orientation:0"; ++ media-controller = <&csi>,"brcm,media-controller?"; ++ cam0 = <&i2c_frag>, "target:0=",<&i2c_csi_dsi0>, ++ <&csi_frag>, "target:0=",<&csi0>, ++ <®_frag>, "target:0=",<&cam0_reg>, ++ <&clk_frag>, "target:0=",<&cam0_clk>, ++ <&cam_node>, "clocks:0=",<&cam0_clk>, ++ <&cam_node>, "avdd-supply:0=",<&cam0_reg>, ++ <&vcm>, "VANA-supply:0=", <&cam0_reg>; ++ vcm = <&vcm>, "status=okay", ++ <&cam_node>,"lens-focus:0=", <&vcm>; ++ clock-frequency = <&cam_clk>, "clock-frequency:0"; ++ link-frequency = <&cam_endpoint>,"link-frequencies#0"; ++ 4lane = <0>, "+201+202"; ++ }; ++}; ++ ++&cam_node { ++ status = "okay"; ++}; ++ ++&cam_endpoint { ++ remote-endpoint = <&csi_ep>; ++}; +diff --git a/arch/arm/boot/dts/overlays/imx415.dtsi b/arch/arm/boot/dts/overlays/imx415.dtsi +new file mode 100644 +index 000000000000..ba18550ddec7 +--- /dev/null ++++ b/arch/arm/boot/dts/overlays/imx415.dtsi +@@ -0,0 +1,27 @@ ++// Fragment that configures an imx415 ++ ++cam_node: imx415@37 { ++ compatible = "sony,imx415"; ++ reg = <0x37>; ++ status = "disabled"; ++ ++ clocks = <&cam1_clk>; ++ clock-names = "inck"; ++ ++ avdd-supply = <&cam1_reg>; /* 2.8v */ ++ dvdd-supply = <&cam_dummy_reg>; /* 1.8v */ ++ ovdd-supply = <&cam_dummy_reg>; /* 1.2v */ ++ ++ rotation = <180>; ++ orientation = <2>; ++ ++ port { ++ cam_endpoint: endpoint { ++ clock-lanes = <0>; ++ data-lanes = <1 2>; ++ //clock-noncontinuous; ++ link-frequencies = ++ /bits/ 64 <360000000>; ++ }; ++ }; ++}; diff --git a/arch/arm/boot/dts/overlays/imx462-overlay.dts b/arch/arm/boot/dts/overlays/imx462-overlay.dts new file mode 100644 -index 000000000000..c4d7aabe2efe +index 000000000000..6e4c5aaddff5 --- /dev/null +++ b/arch/arm/boot/dts/overlays/imx462-overlay.dts -@@ -0,0 +1,39 @@ +@@ -0,0 +1,35 @@ +// SPDX-License-Identifier: GPL-2.0-only +// Definitions for IMX462 camera module on VC I2C bus + -+// IMX462 is the successor to IMX290. The drivers currently don't support -+// any additional feature of IMX462, so use the IMX290 compatible strings -+// for now. ++// IMX462 is the successor to IMX290. + +/dts-v1/; +/plugin/; @@ -30077,19 +30475,17 @@ index 000000000000..c4d7aabe2efe + // Fragment numbers deliberately high to avoid conflicts with the + // included imx290_327 overlay file. + -+ //IMX462 is not defined in the bindings, so use IMX290 for now. -+ + fragment@101 { + target = <&cam_node>; + __overlay__ { -+ compatible = "sony,imx290lqr"; ++ compatible = "sony,imx462lqr"; + }; + }; + + fragment@102 { + target = <&cam_node>; + __dormant__ { -+ compatible = "sony,imx290llr"; ++ compatible = "sony,imx462llr"; + }; + }; + @@ -30122,10 +30518,10 @@ index 000000000000..8645162682f4 +}; diff --git a/arch/arm/boot/dts/overlays/imx477_378-overlay.dtsi b/arch/arm/boot/dts/overlays/imx477_378-overlay.dtsi new file mode 100644 -index 000000000000..1ce42c2c8946 +index 000000000000..17d5b4aa2042 --- /dev/null +++ b/arch/arm/boot/dts/overlays/imx477_378-overlay.dtsi -@@ -0,0 +1,92 @@ +@@ -0,0 +1,93 @@ +// SPDX-License-Identifier: GPL-2.0-only +// Definitions for IMX477 camera module on VC I2C bus + @@ -30208,6 +30604,7 @@ index 000000000000..1ce42c2c8946 + <&cam_node>, "clocks:0=",<&cam0_clk>, + <&cam_node>, "VANA-supply:0=",<&cam0_reg>; + always-on = <0>, "+99"; ++ link-frequency = <&cam_endpoint>,"link-frequencies#0"; + }; +}; + @@ -32095,25 +32492,20 @@ index 000000000000..e3f56608c643 +}; diff --git a/arch/arm/boot/dts/overlays/mcp23017-overlay.dts b/arch/arm/boot/dts/overlays/mcp23017-overlay.dts new file mode 100644 -index 000000000000..0f9e89484c55 +index 000000000000..e23780b985a3 --- /dev/null +++ b/arch/arm/boot/dts/overlays/mcp23017-overlay.dts -@@ -0,0 +1,103 @@ +@@ -0,0 +1,65 @@ +// Definitions for MCP23017 Gpio Extender from Microchip Semiconductor + +/dts-v1/; +/plugin/; + ++#include "i2c-buses.dtsi" ++ +/ { + compatible = "brcm,bcm2835"; + -+ fragment@0 { -+ target = <&i2cbus>; -+ __overlay__ { -+ status = "okay"; -+ }; -+ }; -+ + fragment@1 { + target = <&gpio>; + __overlay__ { @@ -32161,47 +32553,14 @@ index 000000000000..0f9e89484c55 + }; + }; + -+ frag100: fragment@100 { -+ target = <&i2c1>; -+ i2cbus: __overlay__ { -+ status = "okay"; -+ }; -+ }; -+ -+ fragment@101 { -+ target = <&i2c0if>; -+ __dormant__ { -+ status = "okay"; -+ }; -+ }; -+ -+ fragment@102 { -+ target = <&i2c0mux>; -+ __dormant__ { -+ status = "okay"; -+ }; -+ }; -+ + __overrides__ { + gpiopin = <&mcp23017_pins>,"brcm,pins:0", + <&mcp23017_irq>,"interrupts:0"; + addr = <&mcp23017>,"reg:0", <&mcp23017_pins>,"reg:0"; + mcp23008 = <0>,"=2"; + noints = <0>,"!1!3"; -+ i2c0 = <&frag100>, "target:0=",<&i2c0>; -+ i2c_csi_dsi = <&frag100>, "target:0=",<&i2c_csi_dsi>, -+ <0>,"+101+102"; -+ i2c3 = <&frag100>, "target?=0", -+ <&frag100>, "target-path=i2c3"; -+ i2c4 = <&frag100>, "target?=0", -+ <&frag100>, "target-path=i2c4"; -+ i2c5 = <&frag100>, "target?=0", -+ <&frag100>, "target-path=i2c5"; -+ i2c6 = <&frag100>, "target?=0", -+ <&frag100>, "target-path=i2c6"; + }; +}; -+ diff --git a/arch/arm/boot/dts/overlays/mcp23s17-overlay.dts b/arch/arm/boot/dts/overlays/mcp23s17-overlay.dts new file mode 100644 index 000000000000..484d64b225fb @@ -35763,10 +36122,10 @@ index 000000000000..561fed1db837 +}; diff --git a/arch/arm/boot/dts/overlays/ov9281-overlay.dts b/arch/arm/boot/dts/overlays/ov9281-overlay.dts new file mode 100644 -index 000000000000..ec95b7a8b2f1 +index 000000000000..b574aacd063c --- /dev/null +++ b/arch/arm/boot/dts/overlays/ov9281-overlay.dts -@@ -0,0 +1,78 @@ +@@ -0,0 +1,88 @@ +// SPDX-License-Identifier: GPL-2.0-only +// Definitions for OV9281 camera module on VC I2C bus +/dts-v1/; @@ -35798,7 +36157,6 @@ index 000000000000..ec95b7a8b2f1 + csi_ep: endpoint { + remote-endpoint = <&cam_endpoint>; + data-lanes = <1 2>; -+ clock-noncontinuous; + }; + }; + }; @@ -35826,6 +36184,14 @@ index 000000000000..ec95b7a8b2f1 + }; + }; + ++ reg_frag: fragment@5 { ++ target = <&cam1_reg>; ++ __dormant__ { ++ startup-delay-us = <20000>; ++ off-on-delay-us = <30000>; ++ }; ++ }; ++ + __overrides__ { + rotation = <&cam_node>,"rotation:0"; + orientation = <&cam_node>,"orientation:0"; @@ -35834,7 +36200,10 @@ index 000000000000..ec95b7a8b2f1 + <&csi_frag>, "target:0=",<&csi0>, + <&clk_frag>, "target:0=",<&cam0_clk>, + <&cam_node>, "clocks:0=",<&cam0_clk>, -+ <&cam_node>, "avdd-supply:0=",<&cam0_reg>; ++ <&cam_node>, "avdd-supply:0=",<&cam0_reg>, ++ <®_frag>, "target:0=",<&cam0_reg>; ++ arducam = <0>, "+5"; ++ + }; +}; + @@ -35847,10 +36216,10 @@ index 000000000000..ec95b7a8b2f1 +}; diff --git a/arch/arm/boot/dts/overlays/ov9281.dtsi b/arch/arm/boot/dts/overlays/ov9281.dtsi new file mode 100644 -index 000000000000..7df43bc6ef39 +index 000000000000..321a2eb8c8f2 --- /dev/null +++ b/arch/arm/boot/dts/overlays/ov9281.dtsi -@@ -0,0 +1,27 @@ +@@ -0,0 +1,26 @@ +// Fragment that configures an ov9281 + +cam_node: ov9281@60 { @@ -35872,7 +36241,6 @@ index 000000000000..7df43bc6ef39 + cam_endpoint: endpoint { + clock-lanes = <0>; + data-lanes = <1 2>; -+ clock-noncontinuous; + link-frequencies = + /bits/ 64 <400000000>; + }; @@ -35880,10 +36248,10 @@ index 000000000000..7df43bc6ef39 +}; diff --git a/arch/arm/boot/dts/overlays/overlay_map.dts b/arch/arm/boot/dts/overlays/overlay_map.dts new file mode 100644 -index 000000000000..2ddf4c7f4323 +index 000000000000..53256828de0a --- /dev/null +++ b/arch/arm/boot/dts/overlays/overlay_map.dts -@@ -0,0 +1,514 @@ +@@ -0,0 +1,518 @@ +/dts-v1/; + +/ { @@ -36126,6 +36494,10 @@ index 000000000000..2ddf4c7f4323 + bcm2712; + }; + ++ pwm-pio { ++ bcm2712; ++ }; ++ + pwm1 { + bcm2711; + }; @@ -36490,19 +36862,21 @@ index 000000000000..67052b53a59c +}; diff --git a/arch/arm/boot/dts/overlays/pca953x-overlay.dts b/arch/arm/boot/dts/overlays/pca953x-overlay.dts new file mode 100644 -index 000000000000..ab414e92e366 +index 000000000000..50ff90d7c072 --- /dev/null +++ b/arch/arm/boot/dts/overlays/pca953x-overlay.dts -@@ -0,0 +1,240 @@ +@@ -0,0 +1,59 @@ +// Definitions for NXP PCA953x family of I2C GPIO controllers on ARM I2C bus. +/dts-v1/; +/plugin/; + ++#include "i2c-buses.dtsi" ++ +/{ + compatible = "brcm,bcm2835"; + + fragment@0 { -+ target = <&i2c_arm>; ++ target = <&i2cbus>; + __overlay__ { + #address-cells = <1>; + #size-cells = <0>; @@ -36513,253 +36887,72 @@ index 000000000000..ab414e92e366 + reg = <0x20>; + gpio-controller; + #gpio-cells = <2>; -+ -+ status = "okay"; + }; + }; + }; + -+ fragment@1 { -+ target = <&pca>; -+ __dormant__ { -+ compatible = "nxp,pca6416"; -+ }; -+ }; -+ fragment@2 { -+ target = <&pca>; -+ __dormant__ { -+ compatible = "nxp,pca9505"; -+ }; -+ }; -+ fragment@3 { -+ target = <&pca>; -+ __dormant__ { -+ compatible = "nxp,pca9535"; -+ }; -+ }; -+ fragment@4 { -+ target = <&pca>; -+ __dormant__ { -+ compatible = "nxp,pca9536"; -+ }; -+ }; -+ fragment@5 { -+ target = <&pca>; -+ __dormant__ { -+ compatible = "nxp,pca9537"; -+ }; -+ }; -+ fragment@6 { -+ target = <&pca>; -+ __dormant__ { -+ compatible = "nxp,pca9538"; -+ }; -+ }; -+ fragment@7 { -+ target = <&pca>; -+ __dormant__ { -+ compatible = "nxp,pca9539"; -+ }; -+ }; -+ fragment@8 { -+ target = <&pca>; -+ __dormant__ { -+ compatible = "nxp,pca9554"; -+ }; -+ }; -+ fragment@9 { -+ target = <&pca>; -+ __dormant__ { -+ compatible = "nxp,pca9555"; -+ }; -+ }; -+ fragment@10 { -+ target = <&pca>; -+ __dormant__ { -+ compatible = "nxp,pca9556"; -+ }; -+ }; -+ fragment@11 { -+ target = <&pca>; -+ __dormant__ { -+ compatible = "nxp,pca9557"; -+ }; -+ }; -+ fragment@12 { -+ target = <&pca>; -+ __dormant__ { -+ compatible = "nxp,pca9574"; -+ }; -+ }; -+ fragment@13 { -+ target = <&pca>; -+ __dormant__ { -+ compatible = "nxp,pca9575"; -+ }; -+ }; -+ fragment@14 { -+ target = <&pca>; -+ __dormant__ { -+ compatible = "nxp,pca9698"; -+ }; -+ }; -+ fragment@15 { -+ target = <&pca>; -+ __dormant__ { -+ compatible = "nxp,pcal6416"; -+ }; -+ }; -+ fragment@16 { -+ target = <&pca>; -+ __dormant__ { -+ compatible = "nxp,pcal6524"; -+ }; -+ }; -+ fragment@17 { -+ target = <&pca>; -+ __dormant__ { -+ compatible = "nxp,pcal9555a"; -+ }; -+ }; -+ fragment@18 { -+ target = <&pca>; -+ __dormant__ { -+ compatible = "maxim,max7310"; -+ }; -+ }; -+ fragment@19 { -+ target = <&pca>; -+ __dormant__ { -+ compatible = "maxim,max7312"; -+ }; -+ }; -+ fragment@20 { -+ target = <&pca>; -+ __dormant__ { -+ compatible = "maxim,max7313"; -+ }; -+ }; -+ fragment@21 { -+ target = <&pca>; -+ __dormant__ { -+ compatible = "maxim,max7315"; -+ }; -+ }; -+ fragment@22 { -+ target = <&pca>; -+ __dormant__ { -+ compatible = "ti,pca6107"; -+ }; -+ }; -+ fragment@23 { -+ target = <&pca>; -+ __dormant__ { -+ compatible = "ti,tca6408"; -+ }; -+ }; -+ fragment@24 { -+ target = <&pca>; -+ __dormant__ { -+ compatible = "ti,tca6416"; -+ }; -+ }; -+ fragment@25 { -+ target = <&pca>; -+ __dormant__ { -+ compatible = "ti,tca6424"; -+ }; -+ }; -+ fragment@26 { -+ target = <&pca>; -+ __dormant__ { -+ compatible = "ti,tca9539"; -+ }; -+ }; -+ fragment@27 { -+ target = <&pca>; -+ __dormant__ { -+ compatible = "ti,tca9554"; -+ }; -+ }; -+ fragment@28 { -+ target = <&pca>; -+ __dormant__ { -+ compatible = "onnn,cat9554"; -+ }; -+ }; -+ fragment@29 { -+ target = <&pca>; -+ __dormant__ { -+ compatible = "onnn,pca9654"; -+ }; -+ }; -+ fragment@30 { -+ target = <&pca>; -+ __dormant__ { -+ compatible = "exar,xra1202"; -+ }; -+ }; -+ + __overrides__ { + addr = <&pca>,"reg:0"; -+ pca6416 = <0>, "+1"; -+ pca9505 = <0>, "+2"; -+ pca9535 = <0>, "+3"; -+ pca9536 = <0>, "+4"; -+ pca9537 = <0>, "+5"; -+ pca9538 = <0>, "+6"; -+ pca9539 = <0>, "+7"; -+ pca9554 = <0>, "+8"; -+ pca9555 = <0>, "+9"; -+ pca9556 = <0>, "+10"; -+ pca9557 = <0>, "+11"; -+ pca9574 = <0>, "+12"; -+ pca9575 = <0>, "+13"; -+ pca9698 = <0>, "+14"; -+ pcal6416 = <0>, "+15"; -+ pcal6524 = <0>, "+16"; -+ pcal9555a = <0>, "+17"; -+ max7310 = <0>, "+18"; -+ max7312 = <0>, "+19"; -+ max7313 = <0>, "+20"; -+ max7315 = <0>, "+21"; -+ pca6107 = <0>, "+22"; -+ tca6408 = <0>, "+23"; -+ tca6416 = <0>, "+24"; -+ tca6424 = <0>, "+25"; -+ tca9539 = <0>, "+26"; -+ tca9554 = <0>, "+27"; -+ cat9554 = <0>, "+28"; -+ pca9654 = <0>, "+29"; -+ xra1202 = <0>, "+30"; ++ pca6416 = <&pca>,"compatible=nxp,pca6416"; ++ pca9505 = <&pca>,"compatible=nxp,pca9505"; ++ pca9535 = <&pca>,"compatible=nxp,pca9535"; ++ pca9536 = <&pca>,"compatible=nxp,pca9536"; ++ pca9537 = <&pca>,"compatible=nxp,pca9537"; ++ pca9538 = <&pca>,"compatible=nxp,pca9538"; ++ pca9539 = <&pca>,"compatible=nxp,pca9539"; ++ pca9554 = <&pca>,"compatible=nxp,pca9554"; ++ pca9555 = <&pca>,"compatible=nxp,pca9555"; ++ pca9556 = <&pca>,"compatible=nxp,pca9556"; ++ pca9557 = <&pca>,"compatible=nxp,pca9557"; ++ pca9574 = <&pca>,"compatible=nxp,pca9574"; ++ pca9575 = <&pca>,"compatible=nxp,pca9575"; ++ pca9698 = <&pca>,"compatible=nxp,pca9698"; ++ pcal6416 = <&pca>,"compatible=nxp,pcal6416"; ++ pcal6524 = <&pca>,"compatible=nxp,pcal6524"; ++ pcal9555a = <&pca>,"compatible=nxp,pcal9555a"; ++ max7310 = <&pca>,"compatible=maxim,max7310"; ++ max7312 = <&pca>,"compatible=maxim,max7312"; ++ max7313 = <&pca>,"compatible=maxim,max7313"; ++ max7315 = <&pca>,"compatible=maxim,max7315"; ++ pca6107 = <&pca>,"compatible=ti,pca6107"; ++ tca6408 = <&pca>,"compatible=ti,tca6408"; ++ tca6416 = <&pca>,"compatible=ti,tca6416"; ++ tca6424 = <&pca>,"compatible=ti,tca6424"; ++ tca9539 = <&pca>,"compatible=ti,tca9539"; ++ tca9554 = <&pca>,"compatible=ti,tca9554"; ++ cat9554 = <&pca>,"compatible=onnn,cat9554"; ++ pca9654 = <&pca>,"compatible=onnn,pca9654"; ++ xra1202 = <&pca>,"compatible=exar,xra1202"; + }; +}; diff --git a/arch/arm/boot/dts/overlays/pcf857x-overlay.dts b/arch/arm/boot/dts/overlays/pcf857x-overlay.dts new file mode 100644 -index 000000000000..68943e1c3320 +index 000000000000..6b81d32c3818 --- /dev/null +++ b/arch/arm/boot/dts/overlays/pcf857x-overlay.dts -@@ -0,0 +1,32 @@ +@@ -0,0 +1,34 @@ +// Definitions for PCF857X GPIO Extender from NXP + +/dts-v1/; +/plugin/; + ++#include "i2c-buses.dtsi" ++ +/ { + compatible = "brcm,bcm2835"; + + fragment@0 { -+ target = <&i2c_arm>; ++ target = <&i2cbus>; + __overlay__ { + #address-cells = <1>; + #size-cells = <0>; ++ status = "okay"; + + pcf857x: pcf857x@0 { + compatible = ""; + reg = <0x00>; + gpio-controller; + #gpio-cells = <2>; -+ status = "okay"; + }; + }; + }; @@ -36768,7 +36961,7 @@ index 000000000000..68943e1c3320 + pcf8574 = <&pcf857x>,"compatible=nxp,pcf8574", <&pcf857x>,"reg:0=0x20"; + pcf8574a = <&pcf857x>,"compatible=nxp,pcf8574a", <&pcf857x>,"reg:0=0x38"; + pcf8575 = <&pcf857x>,"compatible=nxp,pcf8575", <&pcf857x>,"reg:0=0x20"; -+ pca8574 = <&pcf857x>,"compatible=nxp,pca8574", <&pcf857x>,"reg:0=0x20"; ++ pca8574 = <&pcf857x>,"compatible=nxp,pca8574", <&pcf857x>,"reg:0=0x20"; + addr = <&pcf857x>,"reg:0"; + }; +}; @@ -37448,6 +37641,65 @@ index 000000000000..075bceef158c + }; + }; +}; +diff --git a/arch/arm/boot/dts/overlays/pimidi-overlay.dts b/arch/arm/boot/dts/overlays/pimidi-overlay.dts +new file mode 100644 +index 000000000000..183738474afb +--- /dev/null ++++ b/arch/arm/boot/dts/overlays/pimidi-overlay.dts +@@ -0,0 +1,53 @@ ++/* ++ * Pimidi Linux kernel module. ++ * Copyright (C) 2017-2024 Vilniaus Blokas UAB, https://blokas.io/ ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; version 2 of the ++ * License. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++/dts-v1/; ++/plugin/; ++ ++#include ++#include ++ ++/ { ++ compatible = "brcm,bcm2835"; ++ ++ fragment@0 { ++ target = <&i2c_arm>; ++ __overlay__ { ++ status = "okay"; ++ ++ pimidi_ctrl: pimidi_ctrl@20 { ++ compatible = "blokaslabs,pimidi"; ++ ++ reg = <0x20>; ++ status = "okay"; ++ ++ interrupt-parent = <&gpio>; ++ interrupts = <23 IRQ_TYPE_LEVEL_LOW>; ++ interrupt-names = "data_ready"; ++ interrupt-controller; ++ #interrupt-cells = <2>; ++ ++ data-ready-gpios = <&gpio 23 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>; ++ reset-gpios = <&gpio 22 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>; ++ }; ++ }; ++ }; ++ ++ __overrides__ { ++ sel = <&pimidi_ctrl>,"reg:0{0=0x20,1=0x21,2=0x22,3=0x23}", ++ <&pimidi_ctrl>,"data-ready-gpios:4{0=23,1=5,2=6,3=27}", ++ <&pimidi_ctrl>,"interrupts:0{0=23,1=5,2=6,3=27}"; ++ }; ++}; diff --git a/arch/arm/boot/dts/overlays/pineboards-hat-ai-overlay.dts b/arch/arm/boot/dts/overlays/pineboards-hat-ai-overlay.dts new file mode 100644 index 000000000000..8160272f4705 @@ -37851,10 +38103,10 @@ index 000000000000..226bcbdf8a09 +}; diff --git a/arch/arm/boot/dts/overlays/pisound-pi5-overlay.dts b/arch/arm/boot/dts/overlays/pisound-pi5-overlay.dts new file mode 100644 -index 000000000000..a54974c446a0 +index 000000000000..0b07a6d03694 --- /dev/null +++ b/arch/arm/boot/dts/overlays/pisound-pi5-overlay.dts -@@ -0,0 +1,31 @@ +@@ -0,0 +1,28 @@ +/* + * Pisound Linux kernel module. + * Copyright (C) 2016-2024 Vilniaus Blokas UAB, https://blokas.io/pisound @@ -37874,9 +38126,6 @@ index 000000000000..a54974c446a0 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + -+/dts-v1/; -+/plugin/; -+ +#include "pisound-overlay.dts" + +&pisound_spi { @@ -38471,6 +38720,182 @@ index 000000000000..823c8b4126d1 + clock = <&frag1>,"assigned-clock-rates:0"; + }; +}; +diff --git a/arch/arm/boot/dts/overlays/pwm-gpio-fan-overlay.dts b/arch/arm/boot/dts/overlays/pwm-gpio-fan-overlay.dts +new file mode 100644 +index 000000000000..dc5e586a3251 +--- /dev/null ++++ b/arch/arm/boot/dts/overlays/pwm-gpio-fan-overlay.dts +@@ -0,0 +1,170 @@ ++/* ++ * Overlay for a GPIO connected PWM cooling fan controlled by software GPIO PWM ++ * ++ * Optional parameters: ++ * - "fan_gpio" BCM number of the pin driving the fan, default 18 (GPIO18) ++ * ++ * - "fan_temp0" CPU temperature at which fan is started with low speed in millicelsius, ++ * default 55000 (55 °C) ++ * - "fan_temp1" CPU temperature at which fan is switched to medium speed in millicelsius, ++ * default 60000 (60 °C) ++ * - "fan_temp2" CPU temperature at which fan is switched to high speed in millicelsius, ++ * default 67500 (67.5 °C) ++ * - "fan_temp3" CPU temperature at which fan is switched to max speed in millicelsius, ++ * default 75000 (75 °C) ++ * - "fan_temp0_hyst" Temperature hysteris at which fan is stopped in millicelsius, ++ * default 5000 (resulting in 50 °C) ++ * - "fan_temp1_hyst" Temperature hysteris at which fan is switched back to low speed ++ * in millicelsius, default 5000 (resulting in 55 °C) ++ * - "fan_temp2_hyst" Temperature hysteris at which fan is switched back to medium speed ++ * in millicelsius, default 5000 (resulting in 62.5 °C) ++ * - "fan_temp3_hyst" Temperature hysteris at which fan is switched back to high speed ++ * in millicelsius, default 5000 (resulting in 70 °C) ++ * - "fan_temp0_speed" Fan speed for low cooling state in range 0 to 255, ++ * default 114 (45% PWM duty cycle) ++ * - "fan_temp1_speed" Fan speed for medium cooling state in range 0 to 255, ++ * default 152 (60% PWM duty cycle) ++ * - "fan_temp2_speed" Fan speed for high cooling state in range 0 to 255, ++ * default 204 (80% PWM duty cycle) ++ * - "fan_temp3_speed" Fan speed for max cooling state in range 0 to 255, ++ * default 255 (100% PWM duty cycle) ++ * ++ * N.B. ++ * - Uses the software GPIO PWM kernel module instead of the Pis hardware PWMs (PWM0/PWM1). ++ * This will allow for an undisturbed concurrent usage of the Pis analogue audio output. ++ * ++ * Requires: ++ * - A PWM controlled cooling fan connected to the GPIO, such as an ++ * Argon mini-fan, HighPi Pro Fan or Waveshare FAN-4020-PWM-5V ++ * - Raspberry Pi OS Bookworm with kernel 6.6.62 or above ++ * ++ * Build: ++ * - sudo dtc -I dts -O dtb -o /boot/firmware/overlays/pwm-gpiofan.dtbo pwm-gpiofan-overlay.dts ++ * ++ * Activate: ++ * - sudo nano /boot/firmware/config.txt add "dtoverlay=pwm-gpiofan" ++ * ++ */ ++/dts-v1/; ++/plugin/; ++ ++/ { ++ compatible = "brcm,bcm2835"; ++ ++ fragment@0 { ++ target = <&gpio>; ++ __overlay__ { ++ pwm_gpio_pins: pwm_gpio_pins { ++ brcm,pins = <18>; /* gpio-pin = 18 */ ++ brcm,function = <1>; /* gpio function = output */ ++ brcm,pull = <0>; /* gpio pull up/down = off */ ++ }; ++ }; ++ }; ++ ++ fragment@1 { ++ target-path = "/"; ++ __overlay__ { ++ pwm_gpio: pwm_gpio { ++ compatible="pwm-gpio"; ++ #pwm-cells = <2>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pwm_gpio_pins>; ++ gpios = <&gpio 18 0>; /* gpio-pin = 18 */ ++ }; ++ }; ++ }; ++ ++ fragment@2 { ++ target-path = "/"; ++ __overlay__ { ++ fan0: pwm-fan { ++ compatible = "pwm-fan"; ++ #cooling-cells = <2>; ++ /* in ns = 20ms = 50 Hz */ ++ pwms = <&pwm_gpio 0 20000000 0>; ++ ++ cooling-min-state = <0>; ++ cooling-max-state = <4>; ++ /* PWM duty cycle values in a range from 0 to 255 */ ++ /* which correspond to thermal cooling states 0 to 4 */ ++ cooling-levels = <0 114 152 204 255>; ++ }; ++ }; ++ }; ++ ++ fragment@3 { ++ target = <&cpu_thermal>; ++ __overlay__ { ++ /* in ms = poll every 2s */ ++ polling-delay = <2000>; ++ }; ++ }; ++ ++ fragment@4 { ++ target = <&thermal_trips>; ++ __overlay__ { ++ /* below temperatures in millicelsius */ ++ trip0: trip0 { ++ temperature = <55000>; /* 55 °C */ ++ hysteresis = <5000>; /* 5 °C */ ++ type = "active"; ++ }; ++ trip1: trip1 { ++ temperature = <60000>; /* 60 °C */ ++ hysteresis = <5000>; /* 5 °C */ ++ type = "active"; ++ }; ++ trip2: trip2 { ++ temperature = <67500>; /* 67.5 °C */ ++ hysteresis = <5000>; /* 5 °C */ ++ type = "active"; ++ }; ++ trip3: trip3 { ++ temperature = <75000>; /* 75 °C */ ++ hysteresis = <5000>; /* 5 °C */ ++ type = "active"; ++ }; ++ }; ++ }; ++ ++ fragment@5 { ++ target = <&cooling_maps>; ++ __overlay__ { ++ map0 { ++ cooling-device = <&fan0 0 1>; ++ trip = <&trip0>; ++ }; ++ map1 { ++ cooling-device = <&fan0 1 2>; ++ trip = <&trip1>; ++ }; ++ map2 { ++ cooling-device = <&fan0 2 3>; ++ trip = <&trip2>; ++ }; ++ map3 { ++ cooling-device = <&fan0 3 4>; ++ trip = <&trip3>; ++ }; ++ }; ++ }; ++ ++ __overrides__ { ++ fan_gpio = <&pwm_gpio>,"gpios:4", ++ <&pwm_gpio_pins>,"brcm,pins:0"; ++ fan_temp0 = <&trip0>,"temperature:0"; ++ fan_temp0_hyst = <&trip0>,"hysteresis:0"; ++ fan_temp0_speed = <&fan0>,"cooling-levels:4"; ++ fan_temp1 = <&trip1>,"temperature:0"; ++ fan_temp1_hyst = <&trip1>,"hysteresis:0"; ++ fan_temp1_speed = <&fan0>,"cooling-levels:8"; ++ fan_temp2 = <&trip2>,"temperature:0"; ++ fan_temp2_hyst = <&trip2>,"hysteresis:0"; ++ fan_temp2_speed = <&fan0>,"cooling-levels:12"; ++ fan_temp3 = <&trip3>,"temperature:0"; ++ fan_temp3_hyst = <&trip3>,"hysteresis:0"; ++ fan_temp3_speed = <&fan0>,"cooling-levels:16"; ++ }; ++ ++}; diff --git a/arch/arm/boot/dts/overlays/pwm-gpio-overlay.dts b/arch/arm/boot/dts/overlays/pwm-gpio-overlay.dts new file mode 100644 index 000000000000..f5a1fb38e257 @@ -38611,6 +39036,51 @@ index 000000000000..32853492aaea + clock = <&frag1>,"assigned-clock-rates:0"; + }; +}; +diff --git a/arch/arm/boot/dts/overlays/pwm-pio-overlay.dts b/arch/arm/boot/dts/overlays/pwm-pio-overlay.dts +new file mode 100644 +index 000000000000..3f105a8c21ce +--- /dev/null ++++ b/arch/arm/boot/dts/overlays/pwm-pio-overlay.dts +@@ -0,0 +1,39 @@ ++// SPDX-License-Identifier: GPL-2.0 ++// Device tree overlay for RP1 PIO PWM. ++/dts-v1/; ++/plugin/; ++ ++/ { ++ compatible = "brcm,bcm2712"; ++ ++ fragment@0 { ++ target = <&gpio>; ++ __overlay__ { ++ pwm_pio_pins: pwm_pio_pins@4 { ++ brcm,pins = <4>; /* gpio 4 */ ++ function = "pio"; ++ bias-disable; ++ }; ++ }; ++ }; ++ ++ fragment@1 { ++ target-path = "/"; ++ __overlay__ { ++ pwm_pio: pwm_pio@4 { ++ compatible = "raspberrypi,pwm-pio-rp1"; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pwm_pio_pins>; ++ gpios = <&gpio 4 0>; ++ }; ++ }; ++ }; ++ ++ __overrides__ { ++ gpio = <&pwm_pio>,"gpios:4", ++ <&pwm_pio_pins>,"brcm,pins:0", ++ /* modify reg values to allow multiple instantiation */ ++ <&pwm_pio>,"reg:0", ++ <&pwm_pio_pins>,"reg:0"; ++ }; ++}; diff --git a/arch/arm/boot/dts/overlays/pwm1-overlay.dts b/arch/arm/boot/dts/overlays/pwm1-overlay.dts new file mode 100644 index 000000000000..3324d4160653 @@ -38678,12 +39148,12 @@ index 000000000000..3324d4160653 +}; diff --git a/arch/arm/boot/dts/overlays/qca7000-overlay.dts b/arch/arm/boot/dts/overlays/qca7000-overlay.dts new file mode 100644 -index 000000000000..cc5a4fda4e01 +index 000000000000..0184c53801db --- /dev/null +++ b/arch/arm/boot/dts/overlays/qca7000-overlay.dts @@ -0,0 +1,55 @@ +// Overlay for the Qualcomm Atheros QCA7000 on PLC Stamp micro EVK -+// Visit: https://chargebyte.com/products/evaluation-tools/plc-stamp-micro-2-evaluation-board for details ++// Visit: https://chargebyte.com -> Controllers & Modules -> Evaluation Tools -> PLC Stamp Micro 2 Evaluation Board for details + +/dts-v1/; +/plugin/; @@ -38739,12 +39209,12 @@ index 000000000000..cc5a4fda4e01 +}; diff --git a/arch/arm/boot/dts/overlays/qca7000-uart0-overlay.dts b/arch/arm/boot/dts/overlays/qca7000-uart0-overlay.dts new file mode 100644 -index 000000000000..f103916c9e1c +index 000000000000..3e4af02d91e2 --- /dev/null +++ b/arch/arm/boot/dts/overlays/qca7000-uart0-overlay.dts @@ -0,0 +1,46 @@ +// Overlay for the Qualcomm Atheros QCA7000 on PLC Stamp micro EVK -+// Visit: https://in-tech-smartcharging.com/products/evaluation-tools/plc-stamp-micro-2-evaluation-board for details ++// Visit: https://chargebyte.com -> Controllers & Modules -> Evaluation Tools -> PLC Stamp Micro 2 Evaluation Board for details + +/dts-v1/; +/plugin/; @@ -38851,6 +39321,89 @@ index 000000000000..1737e37f5724 + console-size = <&ramoops>,"console-size:0"; + }; +}; +diff --git a/arch/arm/boot/dts/overlays/rootmaster-overlay.dts b/arch/arm/boot/dts/overlays/rootmaster-overlay.dts +new file mode 100644 +index 000000000000..f5ebaea2abb5 +--- /dev/null ++++ b/arch/arm/boot/dts/overlays/rootmaster-overlay.dts +@@ -0,0 +1,77 @@ ++// redo: ovmerge -c mcp251xfd-overlay.dts,spi0-0,interrupt=25 w1-gpio-overlay.dts,gpiopin=4 ++ ++/dts-v1/; ++/plugin/; ++ ++#include ++#include ++#include ++ ++/ { ++ compatible = "brcm,bcm2835"; ++ fragment@0 { ++ target = <&spidev0>; ++ __overlay__ { ++ status = "disabled"; ++ }; ++ }; ++ fragment@1 { ++ target = <&gpio>; ++ __overlay__ { ++ mcp251xfd_pins: mcp251xfd_spi0_0_pins { ++ brcm,pins = <25>; ++ brcm,function = ; ++ }; ++ }; ++ }; ++ fragment@2 { ++ target-path = "/clocks"; ++ __overlay__ { ++ clk_mcp251xfd_osc: mcp251xfd-spi0-0-osc { ++ #clock-cells = <0>; ++ compatible = "fixed-clock"; ++ clock-frequency = <40000000>; ++ }; ++ }; ++ }; ++ fragment@3 { ++ target = <&spi0>; ++ __overlay__ { ++ status = "okay"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ mcp251xfd@0 { ++ compatible = "microchip,mcp251xfd"; ++ reg = <0>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&mcp251xfd_pins>; ++ spi-max-frequency = <20000000>; ++ interrupt-parent = <&gpio>; ++ interrupts = <25 IRQ_TYPE_LEVEL_LOW>; ++ clocks = <&clk_mcp251xfd_osc>; ++ }; ++ }; ++ }; ++ fragment@4 { ++ target-path = "/"; ++ __overlay__ { ++ onewire@4 { ++ compatible = "w1-gpio"; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&w1_pins>; ++ gpios = <&gpio 4 0>; ++ status = "okay"; ++ }; ++ }; ++ }; ++ fragment@5 { ++ target = <&gpio>; ++ __overlay__ { ++ w1_pins: w1_pins@4 { ++ brcm,pins = <4>; ++ brcm,function = <0>; ++ brcm,pull = <0>; ++ }; ++ }; ++ }; ++}; diff --git a/arch/arm/boot/dts/overlays/rotary-encoder-overlay.dts b/arch/arm/boot/dts/overlays/rotary-encoder-overlay.dts new file mode 100644 index 000000000000..ea1d952734e9 @@ -39058,6 +39611,53 @@ index 000000000000..8483c4f4b2eb + touchscreen-swapped-x-y = <&ts>,"touchscreen-swapped-x-y?"; + }; +}; +diff --git a/arch/arm/boot/dts/overlays/rpi-fw-uart-overlay.dts b/arch/arm/boot/dts/overlays/rpi-fw-uart-overlay.dts +new file mode 100644 +index 000000000000..c17556e91105 +--- /dev/null ++++ b/arch/arm/boot/dts/overlays/rpi-fw-uart-overlay.dts +@@ -0,0 +1,41 @@ ++// SPDX-License-Identifier: GPL-2.0 ++// Overlay for the Raspberry Pi Firmware UART driver ++/dts-v1/; ++/plugin/; ++ ++/{ ++ compatible = "brcm,bcm2835"; ++ ++ fragment@0 { ++ target = <&gpio>; ++ __overlay__ { ++ rpi_fw_uart_pins: rpi_fw_uart_pins@4 { ++ brcm,pins = <20 21>; ++ brcm,function = <1 0>; /* output input */ ++ brcm,pull = <0 2>; /* none pull-up */ ++ }; ++ }; ++ }; ++ ++ fragment@1 { ++ target = <&soc>; ++ __overlay__ { ++ rpi_fw_uart: rpi_fw_uart@7e000000 { ++ compatible = "raspberrypi,firmware-uart"; ++ reg = <0x7e000000 0x100>; /* VideoCore MS sync regs */ ++ firmware = <&firmware>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&rpi_fw_uart_pins>; ++ tx-gpios = <&gpio 20 0>; ++ rx-gpios = <&gpio 21 0>; ++ }; ++ }; ++ }; ++ ++ __overrides__ { ++ txd0_pin = <&rpi_fw_uart>,"tx-gpios:4", ++ <&rpi_fw_uart_pins>, "brcm,pins:0"; ++ rxd0_pin = <&rpi_fw_uart>,"rx-gpios:4", ++ <&rpi_fw_uart_pins>, "brcm,pins:4"; ++ }; ++}; diff --git a/arch/arm/boot/dts/overlays/rpi-poe-overlay.dts b/arch/arm/boot/dts/overlays/rpi-poe-overlay.dts new file mode 100644 index 000000000000..cfd9fe37e108 @@ -39605,22 +40205,23 @@ index 000000000000..c51f1c030a55 +}; diff --git a/arch/arm/boot/dts/overlays/sc16is750-i2c-overlay.dts b/arch/arm/boot/dts/overlays/sc16is750-i2c-overlay.dts new file mode 100644 -index 000000000000..09c08c1c5433 +index 000000000000..700e8a60f182 --- /dev/null +++ b/arch/arm/boot/dts/overlays/sc16is750-i2c-overlay.dts -@@ -0,0 +1,57 @@ +@@ -0,0 +1,58 @@ +/dts-v1/; +/plugin/; + ++#include "i2c-buses.dtsi" ++ +/ { + compatible = "brcm,bcm2835"; + + fragment@0 { -+ target = <&i2c_arm>; ++ target = <&i2cbus>; + __overlay__ { + #address-cells = <1>; + #size-cells = <0>; -+ status = "okay"; + + sc16is750: sc16is750@48 { + compatible = "nxp,sc16is750"; @@ -39638,7 +40239,7 @@ index 000000000000..09c08c1c5433 + }; + + fragment@1 { -+ target-path = "/"; ++ target = <&clocks>; + __overlay__ { + sc16is750_clk: sc16is750_i2c_clk@48 { + compatible = "fixed-clock"; @@ -39737,22 +40338,23 @@ index 000000000000..b289ee900edf +}; diff --git a/arch/arm/boot/dts/overlays/sc16is752-i2c-overlay.dts b/arch/arm/boot/dts/overlays/sc16is752-i2c-overlay.dts new file mode 100644 -index 000000000000..a7c538943859 +index 000000000000..d481d6aa8231 --- /dev/null +++ b/arch/arm/boot/dts/overlays/sc16is752-i2c-overlay.dts -@@ -0,0 +1,57 @@ +@@ -0,0 +1,58 @@ +/dts-v1/; +/plugin/; + ++#include "i2c-buses.dtsi" ++ +/ { + compatible = "brcm,bcm2835"; + + fragment@0 { -+ target = <&i2c_arm>; ++ target = <&i2cbus>; + __overlay__ { + #address-cells = <1>; + #size-cells = <0>; -+ status = "okay"; + + sc16is752: sc16is752@48 { + compatible = "nxp,sc16is752"; @@ -39770,7 +40372,7 @@ index 000000000000..a7c538943859 + }; + + fragment@1 { -+ target-path = "/"; ++ target = <&clocks>; + __overlay__ { + sc16is752_clk: sc16is752_i2c_clk@48 { + compatible = "fixed-clock"; @@ -40252,10 +40854,10 @@ index 000000000000..210d027a073e +}; diff --git a/arch/arm/boot/dts/overlays/seeed-can-fd-hat-v2-overlay.dts b/arch/arm/boot/dts/overlays/seeed-can-fd-hat-v2-overlay.dts new file mode 100644 -index 000000000000..e843d0b19745 +index 000000000000..fc3c7794c28a --- /dev/null +++ b/arch/arm/boot/dts/overlays/seeed-can-fd-hat-v2-overlay.dts -@@ -0,0 +1,117 @@ +@@ -0,0 +1,112 @@ +// redo: ovmerge -c mcp251xfd-overlay.dts,spi0-0,interrupt=25 mcp251xfd-overlay.dts,spi0-1,interrupt=24 i2c-rtc-overlay.dts,pcf85063 + +// Device tree overlay for https://www.seeedstudio.com/CAN-BUS-FD-HAT-for-Raspberry-Pi-p-4742.html @@ -40356,8 +40958,9 @@ index 000000000000..e843d0b19745 + }; + }; + fragment@8 { -+ target = <&i2cbus>; ++ target = <&i2c_arm>; + __overlay__ { ++ status = "okay"; + #address-cells = <1>; + #size-cells = <0>; + pcf85063@51 { @@ -40366,12 +40969,6 @@ index 000000000000..e843d0b19745 + }; + }; + }; -+ fragment@9 { -+ target = <&i2c_arm>; -+ i2cbus: __overlay__ { -+ status = "okay"; -+ }; -+ }; +}; diff --git a/arch/arm/boot/dts/overlays/sh1106-spi-overlay.dts b/arch/arm/boot/dts/overlays/sh1106-spi-overlay.dts new file mode 100644 @@ -40868,6 +41465,71 @@ index 000000000000..0d2acabf56a4 + no_miso = <0>,"=3"; + }; +}; +diff --git a/arch/arm/boot/dts/overlays/spi0-1cs-inverted-overlay.dts b/arch/arm/boot/dts/overlays/spi0-1cs-inverted-overlay.dts +new file mode 100644 +index 000000000000..dc1db2184df4 +--- /dev/null ++++ b/arch/arm/boot/dts/overlays/spi0-1cs-inverted-overlay.dts +@@ -0,0 +1,59 @@ ++/dts-v1/; ++/plugin/; ++ ++/* ++ * There are some devices that need an inverted Chip Select (CS) to select the ++ * device signal, as an example the AZDelivery 12864 display. That means that ++ * the CS polarity is active-high. To invert the CS signal the DT needs to set ++ * the cs-gpio to GPIO_ACTIVE_HIGH (0) in the controller and set the ++ * spi-cs-high in the peripheral property. On top of that, since this is a ++ * display the DT also needs to specify the write-only property. ++*/ ++ ++#include ++ ++/ { ++ compatible = "brcm,bcm2835"; ++ ++ fragment@0 { ++ target = <&spi0_cs_pins>; ++ frag0: __overlay__ { ++ brcm,pins = <8>; ++ }; ++ }; ++ ++ fragment@1 { ++ target = <&spi0>; ++ frag1: __overlay__ { ++ cs-gpios = <&gpio 8 GPIO_ACTIVE_HIGH>; ++ status = "okay"; ++ }; ++ }; ++ ++ fragment@2 { ++ target = <&spidev1>; ++ __overlay__ { ++ status = "disabled"; ++ }; ++ }; ++ ++ fragment@3 { ++ target = <&spi0_pins>; ++ __dormant__ { ++ brcm,pins = <10 11>; ++ }; ++ }; ++ ++ fragment@4 { ++ target = <&spidev0>; ++ __overlay__ { ++ spi-cs-high; ++ }; ++ }; ++ ++ __overrides__ { ++ cs0_pin = <&frag0>,"brcm,pins:0", ++ <&frag1>,"cs-gpios:4"; ++ no_miso = <0>,"=3"; ++ }; ++}; diff --git a/arch/arm/boot/dts/overlays/spi0-1cs-overlay.dts b/arch/arm/boot/dts/overlays/spi0-1cs-overlay.dts new file mode 100644 index 000000000000..e6eb66e2076a @@ -45595,17 +46257,14 @@ index 000000000000..1dc60ae6d967 +}; diff --git a/arch/arm/boot/dts/overlays/vc4-fkms-v3d-overlay.dts b/arch/arm/boot/dts/overlays/vc4-fkms-v3d-overlay.dts new file mode 100644 -index 000000000000..d201edbe7003 +index 000000000000..847a98f2a10c --- /dev/null +++ b/arch/arm/boot/dts/overlays/vc4-fkms-v3d-overlay.dts -@@ -0,0 +1,46 @@ +@@ -0,0 +1,43 @@ +/* + * vc4-fkms-v3d-overlay.dts + */ + -+/dts-v1/; -+/plugin/; -+ +#include "cma-overlay.dts" + +/ { @@ -45647,17 +46306,14 @@ index 000000000000..d201edbe7003 +}; diff --git a/arch/arm/boot/dts/overlays/vc4-fkms-v3d-pi4-overlay.dts b/arch/arm/boot/dts/overlays/vc4-fkms-v3d-pi4-overlay.dts new file mode 100644 -index 000000000000..1e10203dfd86 +index 000000000000..39e78280d475 --- /dev/null +++ b/arch/arm/boot/dts/overlays/vc4-fkms-v3d-pi4-overlay.dts -@@ -0,0 +1,50 @@ +@@ -0,0 +1,47 @@ +/* + * vc4-fkms-v3d-overlay.dts + */ + -+/dts-v1/; -+/plugin/; -+ +#include "cma-overlay.dts" + +&frag0 { @@ -45703,10 +46359,10 @@ index 000000000000..1e10203dfd86 +}; 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 000000000000..73c339ada161 +index 000000000000..8cf41167d4cc --- /dev/null +++ b/arch/arm/boot/dts/overlays/vc4-kms-dpi-generic-overlay.dts -@@ -0,0 +1,82 @@ +@@ -0,0 +1,83 @@ +/* + * vc4-kms-dpi-generic-overlay.dts + */ @@ -45768,6 +46424,7 @@ index 000000000000..73c339ada161 + vsync-invert = <&timing>, "vsync-active:0=0"; + de-invert = <&timing>, "de-active:0=0"; + pixclk-invert = <&timing>, "pixelclk-active:0=0"; ++ interlaced = <&timing>, "interlaced?"; + + width-mm = <&panel_generic>, "width-mm:0"; + height-mm = <&panel_generic>, "height-mm:0"; @@ -46308,11 +46965,11 @@ index 000000000000..67c884de2a8d +}; 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 new file mode 100644 -index 000000000000..302fa807d31d +index 000000000000..8d58d5e6067f --- /dev/null +++ b/arch/arm/boot/dts/overlays/vc4-kms-dsi-7inch-overlay.dts -@@ -0,0 +1,124 @@ -+/* +@@ -0,0 +1,121 @@ ++ /* + * Device Tree overlay for RaspberryPi 7" Touchscreen panel + * + */ @@ -46322,6 +46979,11 @@ index 000000000000..302fa807d31d + +#include "edt-ft5406.dtsi" + ++&ft5406 { ++ vcc-supply = <®_display>; ++ reset-gpio = <®_display 1 1>; ++}; ++ +/ { + /* No compatible as it will have come from edt-ft5406.dtsi */ + @@ -46391,7 +47053,7 @@ index 000000000000..302fa807d31d + + i2c_frag: fragment@2 { + target = <&i2c_csi_dsi>; -+ __overlay__ { ++ i2cbus: __overlay__ { + #address-cells = <1>; + #size-cells = <0>; + status = "okay"; @@ -46418,18 +47080,10 @@ index 000000000000..302fa807d31d + status = "okay"; + }; + }; -+ fragment@5 { -+ target = <&ft5406>; -+ __overlay__ { -+ vcc-supply = <®_display>; -+ reset-gpio = <®_display 1 1>; -+ }; -+ }; + + __overrides__ { + dsi0 = <&dsi_frag>, "target:0=",<&dsi0>, + <&i2c_frag>, "target:0=",<&i2c_csi_dsi0>, -+ <&ts_i2c_frag>, "target:0=",<&i2c_csi_dsi0>, + <&panel_disp>, "reg:0=0", + <®_bridge>, "reg:0=0", + <®_bridge>, "regulator-name=bridge_reg_0"; @@ -46678,10 +47332,10 @@ index 000000000000..1985766c0e67 +}; diff --git a/arch/arm/boot/dts/overlays/vc4-kms-dsi-ili9881-7inch-overlay.dts b/arch/arm/boot/dts/overlays/vc4-kms-dsi-ili9881-7inch-overlay.dts new file mode 100644 -index 000000000000..d1eb31df1d69 +index 000000000000..2c8eef6475f1 --- /dev/null +++ b/arch/arm/boot/dts/overlays/vc4-kms-dsi-ili9881-7inch-overlay.dts -@@ -0,0 +1,122 @@ +@@ -0,0 +1,123 @@ +/* + * vc4-kms-dsi-ili9881-5inch-overlay.dts + */ @@ -46802,6 +47456,7 @@ index 000000000000..d1eb31df1d69 + invy = <0>, "+11"; + swapxy = <>911>,"touchscreen-swapped-x-y?"; + disable_touch = <>911>, "status=disabled"; ++ rotation = <&dsi_panel>, "rotation:0"; + }; +}; diff --git a/arch/arm/boot/dts/overlays/vc4-kms-dsi-lt070me05000-overlay.dts b/arch/arm/boot/dts/overlays/vc4-kms-dsi-lt070me05000-overlay.dts @@ -46951,10 +47606,10 @@ index 000000000000..5dcd0f2243e2 +}; diff --git a/arch/arm/boot/dts/overlays/vc4-kms-dsi-waveshare-800x480-overlay.dts b/arch/arm/boot/dts/overlays/vc4-kms-dsi-waveshare-800x480-overlay.dts new file mode 100644 -index 000000000000..78e8f46c69cc +index 000000000000..7a7058d14d6c --- /dev/null +++ b/arch/arm/boot/dts/overlays/vc4-kms-dsi-waveshare-800x480-overlay.dts -@@ -0,0 +1,119 @@ +@@ -0,0 +1,116 @@ +/* + * Device Tree overlay for Waveshare 4.3" 800x480 panel. + * It tries to look like a Pi 7" panel, but fails with some of the timing @@ -46966,6 +47621,11 @@ index 000000000000..78e8f46c69cc + +#include "edt-ft5406.dtsi" + ++&ft5406 { ++ vcc-supply = <®_display>; ++ reset-gpio = <®_display 1 1>; ++}; ++ +/ { + /* No compatible as it will have come from edt-ft5406.dtsi */ + @@ -47030,7 +47690,7 @@ index 000000000000..78e8f46c69cc + + i2c_frag: fragment@2 { + target = <&i2c_csi_dsi>; -+ __overlay__ { ++ i2cbus: __overlay__ { + #address-cells = <1>; + #size-cells = <0>; + status = "okay"; @@ -47057,18 +47717,10 @@ index 000000000000..78e8f46c69cc + status = "okay"; + }; + }; -+ fragment@5 { -+ target = <&ft5406>; -+ __overlay__ { -+ vcc-supply = <®_display>; -+ reset-gpio = <®_display 1 1>; -+ }; -+ }; + + __overrides__ { + dsi0 = <&dsi_frag>, "target:0=",<&dsi0>, + <&i2c_frag>, "target:0=",<&i2c_csi_dsi0>, -+ <&ts_i2c_frag>, "target:0=",<&i2c_csi_dsi0>, + <®_bridge>, "reg:0=0", + <®_bridge>, "regulator-name=bridge_reg_0"; + disable_touch = <&ft5406>, "status=disabled"; @@ -47076,10 +47728,10 @@ index 000000000000..78e8f46c69cc +}; diff --git a/arch/arm/boot/dts/overlays/vc4-kms-dsi-waveshare-panel-overlay.dts b/arch/arm/boot/dts/overlays/vc4-kms-dsi-waveshare-panel-overlay.dts new file mode 100644 -index 000000000000..3b03ef09cdb9 +index 000000000000..9973ac0ce389 --- /dev/null +++ b/arch/arm/boot/dts/overlays/vc4-kms-dsi-waveshare-panel-overlay.dts -@@ -0,0 +1,133 @@ +@@ -0,0 +1,143 @@ +/* + * Device Tree overlay for Waveshare DSI Touchscreens + * @@ -47133,6 +47785,12 @@ index 000000000000..3b03ef09cdb9 + reg = <0x14>; + compatible = "goodix,gt911"; + }; ++ ++ touch2: ilitek@41 { ++ compatible = "ilitek,ili251x"; ++ reg = <0x41>; ++ status = "disabled"; ++ }; + }; + }; + @@ -47202,6 +47860,10 @@ index 000000000000..3b03ef09cdb9 + <&touch>, "touchscreen-inverted-x?", + <&touch>, "touchscreen-inverted-y?"; + 8_8_inch = <&panel>, "compatible=waveshare,8.8inch-panel"; ++ 13_3_inch_4lane = <&panel>, "compatible=waveshare,13.3inch-4lane-panel", ++ <&touch2>, "status=okay"; ++ 13_3_inch_2lane = <&panel>, "compatible=waveshare,13.3inch-2lane-panel", ++ <&touch2>, "status=okay"; + i2c1 = <&i2c_frag>, "target:0=",<&i2c1>, + <0>, "-3-4+5"; + disable_touch = <&touch>, "status=disabled"; @@ -47247,17 +47909,14 @@ index 000000000000..4c1aa1c70158 +}; diff --git a/arch/arm/boot/dts/overlays/vc4-kms-v3d-overlay.dts b/arch/arm/boot/dts/overlays/vc4-kms-v3d-overlay.dts new file mode 100644 -index 000000000000..26a5bd71945d +index 000000000000..f0b531b18dfc --- /dev/null +++ b/arch/arm/boot/dts/overlays/vc4-kms-v3d-overlay.dts -@@ -0,0 +1,124 @@ +@@ -0,0 +1,121 @@ +/* + * vc4-kms-v3d-overlay.dts + */ + -+/dts-v1/; -+/plugin/; -+ +#include + +#include "cma-overlay.dts" @@ -47377,17 +48036,14 @@ index 000000000000..26a5bd71945d +}; 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 new file mode 100644 -index 000000000000..c1e53e3ed575 +index 000000000000..02bbad743d77 --- /dev/null +++ b/arch/arm/boot/dts/overlays/vc4-kms-v3d-pi4-overlay.dts -@@ -0,0 +1,200 @@ +@@ -0,0 +1,197 @@ +/* + * vc4-kms-v3d-pi4-overlay.dts + */ + -+/dts-v1/; -+/plugin/; -+ +#include + +#include "cma-overlay.dts" @@ -47955,13 +48611,10 @@ index 000000000000..f44e325bc1f2 +}; diff --git a/arch/arm/boot/dts/overlays/w1-gpio-pi5-overlay.dts b/arch/arm/boot/dts/overlays/w1-gpio-pi5-overlay.dts new file mode 100644 -index 000000000000..a408d3d62d40 +index 000000000000..36f60548a7c8 --- /dev/null +++ b/arch/arm/boot/dts/overlays/w1-gpio-pi5-overlay.dts -@@ -0,0 +1,15 @@ -+/dts-v1/; -+/plugin/; -+ +@@ -0,0 +1,12 @@ +#include "w1-gpio-overlay.dts" + +/ { @@ -48024,13 +48677,10 @@ index 000000000000..953c6a1aeab9 +}; diff --git a/arch/arm/boot/dts/overlays/w1-gpio-pullup-pi5-overlay.dts b/arch/arm/boot/dts/overlays/w1-gpio-pullup-pi5-overlay.dts new file mode 100644 -index 000000000000..9d1fffb7e87c +index 000000000000..bca44070cbac --- /dev/null +++ b/arch/arm/boot/dts/overlays/w1-gpio-pullup-pi5-overlay.dts -@@ -0,0 +1,15 @@ -+/dts-v1/; -+/plugin/; -+ +@@ -0,0 +1,12 @@ +#include "w1-gpio-pullup-overlay.dts" + +/ { @@ -48661,12 +49311,64 @@ index 000000000000..d896c59f469b + compatible = <&wm8960>,"compatible"; + }; +}; +diff --git a/arch/arm/boot/dts/overlays/ws2812-pio-overlay.dts b/arch/arm/boot/dts/overlays/ws2812-pio-overlay.dts +new file mode 100644 +index 000000000000..4550e16d44e1 +--- /dev/null ++++ b/arch/arm/boot/dts/overlays/ws2812-pio-overlay.dts +@@ -0,0 +1,46 @@ ++// SPDX-License-Identifier: GPL-2.0 ++// Device tree overlay for RP1 PIO WS2812 driver. ++/dts-v1/; ++/plugin/; ++ ++/ { ++ compatible = "brcm,bcm2712"; ++ ++ fragment@0 { ++ target = <&gpio>; ++ __overlay__ { ++ ws2812_pio_pins: ws2812_pio_pins@4 { ++ brcm,pins = <4>; /* gpio 4 */ ++ function = "pio"; ++ bias-disable; ++ }; ++ }; ++ }; ++ ++ fragment@1 { ++ target-path = "/"; ++ __overlay__ { ++ ws2812_pio: ws2812_pio@4 { ++ compatible = "raspberrypi,ws2812-pio-rp1"; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&ws2812_pio_pins>; ++ dev-name = "leds%d"; ++ leds-gpios = <&gpio 4 0>; ++ rpi,num-leds = <60>; ++ rpi,brightness = <255>; ++ }; ++ }; ++ }; ++ ++ __overrides__ { ++ brightness = <&ws2812_pio>, "rpi,brightness:0"; ++ dev_name = <&ws2812_pio>, "dev-name"; ++ gpio = <&ws2812_pio>,"leds-gpios:4", ++ <&ws2812_pio_pins>,"brcm,pins:0", ++ /* modify reg values to allow multiple instantiation */ ++ <&ws2812_pio>,"reg:0", ++ <&ws2812_pio_pins>,"reg:0"; ++ num_leds = <&ws2812_pio>, "rpi,num-leds:0"; ++ rgbw = <&ws2812_pio>, "rpi,rgbw?"; ++ }; ++}; diff --git a/arch/arm/configs/bcm2709_defconfig b/arch/arm/configs/bcm2709_defconfig new file mode 100644 -index 000000000000..f3b3dcb04041 +index 000000000000..ae4732a2224f --- /dev/null +++ b/arch/arm/configs/bcm2709_defconfig -@@ -0,0 +1,1601 @@ +@@ -0,0 +1,1607 @@ +CONFIG_LOCALVERSION="-v7" +# CONFIG_LOCALVERSION_AUTO is not set +CONFIG_SYSVIPC=y @@ -48713,10 +49415,9 @@ index 000000000000..f3b3dcb04041 +CONFIG_CMDLINE="console=ttyAMA0,115200 kgdboc=ttyAMA0,115200 root=/dev/mmcblk0p2 rootfstype=ext4 rootwait" +CONFIG_CPU_FREQ=y +CONFIG_CPU_FREQ_STAT=y -+CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE=y -+CONFIG_CPU_FREQ_GOV_PERFORMANCE=y ++CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y ++CONFIG_CPU_FREQ_GOV_POWERSAVE=y +CONFIG_CPU_FREQ_GOV_USERSPACE=y -+CONFIG_CPU_FREQ_GOV_ONDEMAND=y +CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y +CONFIG_CPU_FREQ_GOV_SCHEDUTIL=y +CONFIG_CPUFREQ_DT=y @@ -49163,6 +49864,7 @@ index 000000000000..f3b3dcb04041 +CONFIG_ENC28J60=m +CONFIG_QCA7000_SPI=m +CONFIG_QCA7000_UART=m ++CONFIG_MSE102X=m +CONFIG_WIZNET_W5100=m +CONFIG_WIZNET_W5100_SPI=m +CONFIG_CAN_VCAN=m @@ -49340,6 +50042,7 @@ index 000000000000..f3b3dcb04041 +CONFIG_SERIAL_AMBA_PL011_CONSOLE=y +CONFIG_SERIAL_SC16IS7XX=m +CONFIG_SERIAL_SC16IS7XX_SPI=y ++CONFIG_SERIAL_RPI_FW=m +CONFIG_SERIAL_DEV_BUS=y +CONFIG_TTY_PRINTK=y +CONFIG_HW_RANDOM=y @@ -49419,6 +50122,7 @@ index 000000000000..f3b3dcb04041 +CONFIG_SENSORS_SHTC1=m +CONFIG_SENSORS_EMC2305=m +CONFIG_SENSORS_INA2XX=m ++CONFIG_SENSORS_INA238=m +CONFIG_SENSORS_TMP102=m +CONFIG_BCM2835_THERMAL=y +CONFIG_WATCHDOG=y @@ -49601,6 +50305,7 @@ index 000000000000..f3b3dcb04041 +CONFIG_VIDEO_IMX258=m +CONFIG_VIDEO_IMX290=m +CONFIG_VIDEO_IMX296=m ++CONFIG_VIDEO_IMX415=m +CONFIG_VIDEO_IMX477=m +CONFIG_VIDEO_IMX500=m +CONFIG_VIDEO_IMX519=m @@ -49681,6 +50386,7 @@ index 000000000000..f3b3dcb04041 +CONFIG_SND_MTPAV=m +CONFIG_SND_SERIAL_U16550=m +CONFIG_SND_MPU401=m ++CONFIG_SND_PIMIDI=m +CONFIG_SND_USB_AUDIO=m +CONFIG_SND_USB_UA101=m +CONFIG_SND_USB_CAIAQ=m @@ -49741,6 +50447,7 @@ index 000000000000..f3b3dcb04041 +CONFIG_SND_SOC_SPDIF=m +CONFIG_SND_SOC_TLV320AIC23_I2C=m +CONFIG_SND_SOC_WM8804_I2C=m ++CONFIG_SND_SOC_WM8904=m +CONFIG_SND_SOC_WM8960=m +CONFIG_SND_SIMPLE_CARD=m +CONFIG_HID_BATTERY_STRENGTH=y @@ -50244,6 +50951,7 @@ index 000000000000..f3b3dcb04041 +CONFIG_CRYPTO_USER_API_SKCIPHER=m +CONFIG_CRYPTO_USER_API_RNG=m +CONFIG_CRYPTO_USER_API_AEAD=m ++CONFIG_CRYPTO_NHPOLY1305_NEON=m +CONFIG_CRYPTO_SHA1_ARM_NEON=m +CONFIG_CRYPTO_AES_ARM=m +CONFIG_CRYPTO_AES_ARM_BS=m @@ -50270,10 +50978,10 @@ index 000000000000..f3b3dcb04041 +# CONFIG_UPROBE_EVENTS is not set diff --git a/arch/arm/configs/bcm2711_defconfig b/arch/arm/configs/bcm2711_defconfig new file mode 100644 -index 000000000000..e747b7b0991c +index 000000000000..ec4974041a5a --- /dev/null +++ b/arch/arm/configs/bcm2711_defconfig -@@ -0,0 +1,1633 @@ +@@ -0,0 +1,1642 @@ +CONFIG_LOCALVERSION="-v7l" +# CONFIG_LOCALVERSION_AUTO is not set +CONFIG_SYSVIPC=y @@ -50320,10 +51028,9 @@ index 000000000000..e747b7b0991c +CONFIG_CMDLINE="console=ttyAMA0,115200 kgdboc=ttyAMA0,115200 root=/dev/mmcblk0p2 rootfstype=ext4 rootwait" +CONFIG_CPU_FREQ=y +CONFIG_CPU_FREQ_STAT=y -+CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE=y -+CONFIG_CPU_FREQ_GOV_PERFORMANCE=y ++CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y ++CONFIG_CPU_FREQ_GOV_POWERSAVE=y +CONFIG_CPU_FREQ_GOV_USERSPACE=y -+CONFIG_CPU_FREQ_GOV_ONDEMAND=y +CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y +CONFIG_CPU_FREQ_GOV_SCHEDUTIL=y +CONFIG_CPUFREQ_DT=y @@ -50787,6 +51494,7 @@ index 000000000000..e747b7b0991c +CONFIG_QCA7000_SPI=m +CONFIG_QCA7000_UART=m +CONFIG_R8169=m ++CONFIG_MSE102X=m +CONFIG_WIZNET_W5100=m +CONFIG_WIZNET_W5100_SPI=m +CONFIG_MICREL_PHY=y @@ -50864,6 +51572,9 @@ index 000000000000..e747b7b0991c +CONFIG_BRCMFMAC=m +CONFIG_BRCMFMAC_USB=y +CONFIG_BRCMDBG=y ++CONFIG_IWLWIFI=m ++CONFIG_IWLDVM=m ++CONFIG_IWLMVM=m +CONFIG_HOSTAP=m +CONFIG_P54_COMMON=m +CONFIG_P54_USB=m @@ -50965,6 +51676,7 @@ index 000000000000..e747b7b0991c +CONFIG_SERIAL_AMBA_PL011_CONSOLE=y +CONFIG_SERIAL_SC16IS7XX=m +CONFIG_SERIAL_SC16IS7XX_SPI=y ++CONFIG_SERIAL_RPI_FW=m +CONFIG_SERIAL_DEV_BUS=y +CONFIG_TTY_PRINTK=y +CONFIG_HW_RANDOM=y @@ -51047,6 +51759,7 @@ index 000000000000..e747b7b0991c +CONFIG_SENSORS_SHTC1=m +CONFIG_SENSORS_EMC2305=m +CONFIG_SENSORS_INA2XX=m ++CONFIG_SENSORS_INA238=m +CONFIG_SENSORS_TMP102=m +CONFIG_BCM2711_THERMAL=y +CONFIG_WATCHDOG=y @@ -51232,6 +51945,7 @@ index 000000000000..e747b7b0991c +CONFIG_VIDEO_IMX258=m +CONFIG_VIDEO_IMX290=m +CONFIG_VIDEO_IMX296=m ++CONFIG_VIDEO_IMX415=m +CONFIG_VIDEO_IMX477=m +CONFIG_VIDEO_IMX500=m +CONFIG_VIDEO_IMX519=m @@ -51313,6 +52027,7 @@ index 000000000000..e747b7b0991c +CONFIG_SND_MTPAV=m +CONFIG_SND_SERIAL_U16550=m +CONFIG_SND_MPU401=m ++CONFIG_SND_PIMIDI=m +CONFIG_SND_USB_AUDIO=m +CONFIG_SND_USB_UA101=m +CONFIG_SND_USB_CAIAQ=m @@ -51373,6 +52088,7 @@ index 000000000000..e747b7b0991c +CONFIG_SND_SOC_SPDIF=m +CONFIG_SND_SOC_TLV320AIC23_I2C=m +CONFIG_SND_SOC_WM8804_I2C=m ++CONFIG_SND_SOC_WM8904=m +CONFIG_SND_SOC_WM8960=m +CONFIG_SND_SIMPLE_CARD=m +CONFIG_HID_BATTERY_STRENGTH=y @@ -51883,6 +52599,7 @@ index 000000000000..e747b7b0991c +CONFIG_CRYPTO_USER_API_SKCIPHER=m +CONFIG_CRYPTO_USER_API_RNG=m +CONFIG_CRYPTO_USER_API_AEAD=m ++CONFIG_CRYPTO_NHPOLY1305_NEON=m +CONFIG_CRYPTO_SHA1_ARM_NEON=m +CONFIG_CRYPTO_AES_ARM=m +CONFIG_CRYPTO_AES_ARM_BS=m @@ -51909,10 +52626,10 @@ index 000000000000..e747b7b0991c +# CONFIG_UPROBE_EVENTS is not set diff --git a/arch/arm/configs/bcmrpi_defconfig b/arch/arm/configs/bcmrpi_defconfig new file mode 100644 -index 000000000000..a11286624e01 +index 000000000000..911266f06ab2 --- /dev/null +++ b/arch/arm/configs/bcmrpi_defconfig -@@ -0,0 +1,1594 @@ +@@ -0,0 +1,1599 @@ +# CONFIG_LOCALVERSION_AUTO is not set +CONFIG_SYSVIPC=y +CONFIG_POSIX_MQUEUE=y @@ -51957,10 +52674,9 @@ index 000000000000..a11286624e01 +CONFIG_CMDLINE="console=ttyAMA0,115200 kgdboc=ttyAMA0,115200 root=/dev/mmcblk0p2 rootfstype=ext4 rootwait" +CONFIG_CPU_FREQ=y +CONFIG_CPU_FREQ_STAT=y -+CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE=y -+CONFIG_CPU_FREQ_GOV_PERFORMANCE=y ++CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y ++CONFIG_CPU_FREQ_GOV_POWERSAVE=y +CONFIG_CPU_FREQ_GOV_USERSPACE=y -+CONFIG_CPU_FREQ_GOV_ONDEMAND=y +CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y +CONFIG_CPUFREQ_DT=y +CONFIG_ARM_RASPBERRYPI_CPUFREQ=y @@ -52404,6 +53120,7 @@ index 000000000000..a11286624e01 +CONFIG_ENC28J60=m +CONFIG_QCA7000_SPI=m +CONFIG_QCA7000_UART=m ++CONFIG_MSE102X=m +CONFIG_WIZNET_W5100=m +CONFIG_WIZNET_W5100_SPI=m +CONFIG_CAN_VCAN=m @@ -52581,6 +53298,7 @@ index 000000000000..a11286624e01 +CONFIG_SERIAL_AMBA_PL011_CONSOLE=y +CONFIG_SERIAL_SC16IS7XX=m +CONFIG_SERIAL_SC16IS7XX_SPI=y ++CONFIG_SERIAL_RPI_FW=m +CONFIG_SERIAL_DEV_BUS=y +CONFIG_TTY_PRINTK=y +CONFIG_HW_RANDOM=y @@ -52659,6 +53377,7 @@ index 000000000000..a11286624e01 +CONFIG_SENSORS_SHTC1=m +CONFIG_SENSORS_EMC2305=m +CONFIG_SENSORS_INA2XX=m ++CONFIG_SENSORS_INA238=m +CONFIG_SENSORS_TMP102=m +CONFIG_BCM2835_THERMAL=y +CONFIG_WATCHDOG=y @@ -52841,6 +53560,7 @@ index 000000000000..a11286624e01 +CONFIG_VIDEO_IMX258=m +CONFIG_VIDEO_IMX290=m +CONFIG_VIDEO_IMX296=m ++CONFIG_VIDEO_IMX415=m +CONFIG_VIDEO_IMX477=m +CONFIG_VIDEO_IMX500=m +CONFIG_VIDEO_IMX519=m @@ -52921,6 +53641,7 @@ index 000000000000..a11286624e01 +CONFIG_SND_MTPAV=m +CONFIG_SND_SERIAL_U16550=m +CONFIG_SND_MPU401=m ++CONFIG_SND_PIMIDI=m +CONFIG_SND_USB_AUDIO=m +CONFIG_SND_USB_UA101=m +CONFIG_SND_USB_CAIAQ=m @@ -52980,6 +53701,7 @@ index 000000000000..a11286624e01 +CONFIG_SND_SOC_SPDIF=m +CONFIG_SND_SOC_TLV320AIC23_I2C=m +CONFIG_SND_SOC_WM8804_I2C=m ++CONFIG_SND_SOC_WM8904=m +CONFIG_SND_SOC_WM8960=m +CONFIG_SND_SIMPLE_CARD=m +CONFIG_HID_BATTERY_STRENGTH=y @@ -55678,7 +56400,7 @@ index 000000000000..c72d752e7400 +#include "arm/broadcom/bcm2711-rpi-cm4s.dts" diff --git a/arch/arm64/boot/dts/broadcom/bcm2712-rpi-5-b.dts b/arch/arm64/boot/dts/broadcom/bcm2712-rpi-5-b.dts new file mode 100644 -index 000000000000..a63ee4b678a1 +index 000000000000..74d420fef8a7 --- /dev/null +++ b/arch/arm64/boot/dts/broadcom/bcm2712-rpi-5-b.dts @@ -0,0 +1,750 @@ @@ -55879,7 +56601,7 @@ index 000000000000..a63ee4b678a1 + // This is the RP1 peripheral space + ranges = <0xc0 0x40000000 + 0x02000000 0x00 0x00000000 -+ 0x00 0x00400000>; ++ 0x00 0x00410000>; + + dma-ranges = + // inbound RP1 1x_xxxxxxxx -> PCIe 1x_xxxxxxxx @@ -55891,7 +56613,7 @@ index 000000000000..a63ee4b678a1 + // This allows the RP1 DMA controller to address RP1 hardware + <0xc0 0x40000000 + 0x02000000 0x0 0x00000000 -+ 0x0 0x00400000>, ++ 0x0 0x00410000>, + + // inbound RP1 0x_xxxxxxxx -> PCIe 1x_xxxxxxxx + <0x00 0x00000000 @@ -56049,7 +56771,7 @@ index 000000000000..a63ee4b678a1 + sd-uhs-sdr50; + sd-uhs-ddr50; + sd-uhs-sdr104; -+ supports-cqe; ++ supports-cqe = <1>; + cd-gpios = <&gio_aon 5 GPIO_ACTIVE_LOW>; + //no-1-8-v; + status = "okay"; @@ -56429,7 +57151,7 @@ index 000000000000..a63ee4b678a1 + +/ { + __overrides__ { -+ sd_cqe = <&sdio1>, "supports-cqe?"; ++ sd_cqe = <&sdio1>, "supports-cqe:0"; + }; +}; diff --git a/arch/arm64/boot/dts/broadcom/bcm2712-rpi-500.dts b/arch/arm64/boot/dts/broadcom/bcm2712-rpi-500.dts @@ -56638,10 +57360,10 @@ index 000000000000..6b5e147d569d +#include "bcm2712-rpi-cm5io.dtsi" diff --git a/arch/arm64/boot/dts/broadcom/bcm2712-rpi-cm5.dtsi b/arch/arm64/boot/dts/broadcom/bcm2712-rpi-cm5.dtsi new file mode 100644 -index 000000000000..1a293249ac43 +index 000000000000..0a44cffca71f --- /dev/null +++ b/arch/arm64/boot/dts/broadcom/bcm2712-rpi-cm5.dtsi -@@ -0,0 +1,754 @@ +@@ -0,0 +1,769 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include @@ -56820,7 +57542,7 @@ index 000000000000..1a293249ac43 + // This is the RP1 peripheral space + ranges = <0xc0 0x40000000 + 0x02000000 0x00 0x00000000 -+ 0x00 0x00400000>; ++ 0x00 0x00410000>; + + dma-ranges = + // inbound RP1 1x_xxxxxxxx -> PCIe 1x_xxxxxxxx @@ -56832,7 +57554,7 @@ index 000000000000..1a293249ac43 + // This allows the RP1 DMA controller to address RP1 hardware + <0xc0 0x40000000 + 0x02000000 0x0 0x00000000 -+ 0x0 0x00400000>, ++ 0x0 0x00410000>, + + // inbound RP1 0x_xxxxxxxx -> PCIe 1x_xxxxxxxx + <0x00 0x00000000 @@ -56983,7 +57705,7 @@ index 000000000000..1a293249ac43 + mmc-hs400-1_8v; + mmc-hs400-enhanced-strobe; + broken-cd; -+ supports-cqe; ++ supports-cqe = <1>; + status = "okay"; +}; + @@ -57362,7 +58084,7 @@ index 000000000000..1a293249ac43 + "-", // GPIO40 + "-", // GPIO41 + "USB_VBUS_EN", // GPIO42 -+ "USB_OC_N", // GPIO43 ++ "-", // GPIO43 + "RP1_STAT_LED", // GPIO44 + "FAN_PWM", // GPIO45 + "-", // GPIO46 @@ -57378,6 +58100,18 @@ index 000000000000..1a293249ac43 + function = "vbus1"; + pins = "gpio42", "gpio43"; + }; ++ ++ micclk1_hog { ++ gpio-hog; ++ gpios = <46 GPIO_ACTIVE_HIGH>; ++ output-high; ++ }; ++ ++ micdat1_hog { ++ gpio-hog; ++ gpios = <48 GPIO_ACTIVE_HIGH>; ++ output-high; ++ }; +}; + +/ { @@ -57394,11 +58128,14 @@ index 000000000000..1a293249ac43 + <&ant1>, "output-low?=on", + <&ant2>, "output-high?=off", + <&ant2>, "output-low?=on"; ++ noanthogs = <&ant1>,"status=disabled", ++ <&ant2>, "status=disabled"; ++ sd_cqe = <&sdio1>, "supports-cqe:0"; + }; +}; diff --git a/arch/arm64/boot/dts/broadcom/bcm2712-rpi-cm5io.dtsi b/arch/arm64/boot/dts/broadcom/bcm2712-rpi-cm5io.dtsi new file mode 100644 -index 000000000000..788fda2fcbfb +index 000000000000..4c616a21dc76 --- /dev/null +++ b/arch/arm64/boot/dts/broadcom/bcm2712-rpi-cm5io.dtsi @@ -0,0 +1,14 @@ @@ -57415,7 +58152,7 @@ index 000000000000..788fda2fcbfb + symlink = "i2c-6"; +}; + -+i2c_csi_dsi: &i2c_csi_dsi0 { }; // The connector that needs no jumper to enable ++i2c_csi_dsi: &i2c_csi_dsi1 { }; // The connector that needs no jumper to enable diff --git a/arch/arm64/boot/dts/broadcom/bcm2712-rpi-cm5l-cm4io.dts b/arch/arm64/boot/dts/broadcom/bcm2712-rpi-cm5l-cm4io.dts new file mode 100644 index 000000000000..71259a673d99 @@ -57440,12 +58177,11 @@ index 000000000000..11a56dfb7b48 +#include "bcm2712-rpi-cm5io.dtsi" diff --git a/arch/arm64/boot/dts/broadcom/bcm2712-rpi-cm5l.dtsi b/arch/arm64/boot/dts/broadcom/bcm2712-rpi-cm5l.dtsi new file mode 100644 -index 000000000000..98ac6c113e81 +index 000000000000..6ba1a3efdd87 --- /dev/null +++ b/arch/arm64/boot/dts/broadcom/bcm2712-rpi-cm5l.dtsi -@@ -0,0 +1,22 @@ +@@ -0,0 +1,21 @@ +// SPDX-License-Identifier: GPL-2.0 -+/dts-v1/; + +#include "bcm2712-rpi-cm5.dtsi" + @@ -57468,10 +58204,10 @@ index 000000000000..98ac6c113e81 +}; diff --git a/arch/arm64/boot/dts/broadcom/bcm2712-rpi.dtsi b/arch/arm64/boot/dts/broadcom/bcm2712-rpi.dtsi new file mode 100644 -index 000000000000..25e37b99b054 +index 000000000000..2317c2d86e88 --- /dev/null +++ b/arch/arm64/boot/dts/broadcom/bcm2712-rpi.dtsi -@@ -0,0 +1,466 @@ +@@ -0,0 +1,475 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include @@ -57571,9 +58307,13 @@ index 000000000000..25e37b99b054 + }; +}; + ++pio: &rp1_pio { ++ status = "okay"; ++}; ++ +/ { + chosen: chosen { -+ bootargs = "reboot=w coherent_pool=1M 8250.nr_uarts=1 pci=pcie_bus_safe cgroup_disable=memory numa_policy=interleave iommu_dma_numa_policy=interleave system_heap.max_order=0"; ++ bootargs = "reboot=w coherent_pool=1M 8250.nr_uarts=1 pci=pcie_bus_safe cgroup_disable=memory numa_policy=interleave"; + stdout-path = "serial10:115200n8"; + }; + @@ -57603,6 +58343,7 @@ index 000000000000..25e37b99b054 + i2c12 = &i2c_rp1boot; + mailbox = &mailbox; + mmc0 = &sdio1; ++ pio0 = &pio; + serial0 = &uart0; + serial1 = &uart1; + serial10 = &uart10; @@ -57768,6 +58509,10 @@ index 000000000000..25e37b99b054 + status = "okay"; +}; + ++&rp1_mbox { ++ status = "okay"; ++}; ++ +/* Add some gpiomem nodes to make the devices accessible to userspace. + * /dev/gpiomem should expose the registers for the interface with DT alias + * gpio. @@ -57940,10 +58685,10 @@ index 000000000000..25e37b99b054 +}; diff --git a/arch/arm64/boot/dts/broadcom/bcm2712.dtsi b/arch/arm64/boot/dts/broadcom/bcm2712.dtsi new file mode 100644 -index 000000000000..f4360daae488 +index 000000000000..b1edcf6d1465 --- /dev/null +++ b/arch/arm64/boot/dts/broadcom/bcm2712.dtsi -@@ -0,0 +1,1308 @@ +@@ -0,0 +1,1306 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include @@ -58136,7 +58881,7 @@ index 000000000000..f4360daae488 + clocks = <&clk_uart>, + <&clk_vpu>; + clock-names = "uartclk", "apb_pclk"; -+ arm,primecell-periphid = <0x00241011>; ++ arm,primecell-periphid = <0x00341011>; + status = "disabled"; + }; + @@ -58147,7 +58892,7 @@ index 000000000000..f4360daae488 + clocks = <&clk_uart>, + <&clk_vpu>; + clock-names = "uartclk", "apb_pclk"; -+ arm,primecell-periphid = <0x00241011>; ++ arm,primecell-periphid = <0x00341011>; + status = "disabled"; + }; + @@ -58158,7 +58903,7 @@ index 000000000000..f4360daae488 + clocks = <&clk_uart>, + <&clk_vpu>; + clock-names = "uartclk", "apb_pclk"; -+ arm,primecell-periphid = <0x00241011>; ++ arm,primecell-periphid = <0x00341011>; + status = "disabled"; + }; + @@ -58687,8 +59432,6 @@ index 000000000000..f4360daae488 + IRQ_TYPE_LEVEL_LOW)>, + ; -+ /* This only applies to the ARMv7 stub */ -+ arm,cpu-registers-not-fw-configured; + }; + + cpus: cpus { @@ -59156,7 +59899,7 @@ index 000000000000..f4360daae488 + clocks = <&clk_emmc2>; + sdhci-caps-mask = <0x0000C000 0x0>; + sdhci-caps = <0x0 0x0>; -+ supports-cqe; ++ supports-cqe = <1>; + mmc-ddr-3_3v; + status = "disabled"; + }; @@ -59367,10 +60110,10 @@ index 000000000000..d06536bc7592 +}; diff --git a/arch/arm64/boot/dts/broadcom/rp1.dtsi b/arch/arm64/boot/dts/broadcom/rp1.dtsi new file mode 100644 -index 000000000000..1f60f3f03a10 +index 000000000000..15e770a63c55 --- /dev/null +++ b/arch/arm64/boot/dts/broadcom/rp1.dtsi -@@ -0,0 +1,1289 @@ +@@ -0,0 +1,1323 @@ +#include +#include +#include @@ -59386,6 +60129,14 @@ index 000000000000..1f60f3f03a10 + + // ranges and dma-ranges must be provided by the includer + ++ rp1_mbox: mailbox@8000 { ++ compatible = "raspberrypi,rp1-mbox"; ++ status = "disabled"; ++ reg = <0xc0 0x40008000 0x0 0x4000>; // SYSCFG ++ interrupts = ; ++ #mbox-cells = <1>; ++ }; ++ + rp1_clocks: clocks@18000 { + compatible = "raspberrypi,rp1-clocks"; + #clock-cells = <1>; @@ -59397,6 +60148,7 @@ index 000000000000..1f60f3f03a10 + // RP1_PLL_VIDEO_CORE and dividers are now managed by VEC,DPI drivers + <&rp1_clocks RP1_PLL_SYS>, + <&rp1_clocks RP1_PLL_SYS_SEC>, ++ <&rp1_clocks RP1_CLK_ETH>, + <&rp1_clocks RP1_PLL_AUDIO>, + <&rp1_clocks RP1_PLL_AUDIO_SEC>, + <&rp1_clocks RP1_CLK_SYS>, @@ -59411,6 +60163,7 @@ index 000000000000..1f60f3f03a10 + <1536000000>, // RP1_PLL_AUDIO_CORE + <200000000>, // RP1_PLL_SYS + <125000000>, // RP1_PLL_SYS_SEC ++ <125000000>, // RP1_CLK_ETH + <61440000>, // RP1_PLL_AUDIO + <192000000>, // RP1_PLL_AUDIO_SEC + <200000000>, // RP1_CLK_SYS @@ -59428,11 +60181,11 @@ index 000000000000..1f60f3f03a10 + interrupts = ; + clocks = <&rp1_clocks RP1_CLK_UART &rp1_clocks RP1_PLL_SYS_PRI_PH>; + clock-names = "uartclk", "apb_pclk"; -+ // dmas = <&rp1_dma RP1_DMA_UART0_TX>, -+ // <&rp1_dma RP1_DMA_UART0_RX>; -+ // dma-names = "tx", "rx"; ++ dmas = <&rp1_dma RP1_DMA_UART0_TX>, ++ <&rp1_dma RP1_DMA_UART0_RX>; ++ dma-names = "tx", "rx"; + pinctrl-names = "default"; -+ arm,primecell-periphid = <0x00541011>; ++ arm,primecell-periphid = <0x00341011>; + uart-has-rtscts; + cts-event-workaround; + skip-init; @@ -59449,7 +60202,7 @@ index 000000000000..1f60f3f03a10 + // <&rp1_dma RP1_DMA_UART1_RX>; + // dma-names = "tx", "rx"; + pinctrl-names = "default"; -+ arm,primecell-periphid = <0x00541011>; ++ arm,primecell-periphid = <0x00341011>; + uart-has-rtscts; + cts-event-workaround; + skip-init; @@ -59466,7 +60219,7 @@ index 000000000000..1f60f3f03a10 + // <&rp1_dma RP1_DMA_UART2_RX>; + // dma-names = "tx", "rx"; + pinctrl-names = "default"; -+ arm,primecell-periphid = <0x00541011>; ++ arm,primecell-periphid = <0x00341011>; + uart-has-rtscts; + cts-event-workaround; + skip-init; @@ -59483,7 +60236,7 @@ index 000000000000..1f60f3f03a10 + // <&rp1_dma RP1_DMA_UART3_RX>; + // dma-names = "tx", "rx"; + pinctrl-names = "default"; -+ arm,primecell-periphid = <0x00541011>; ++ arm,primecell-periphid = <0x00341011>; + uart-has-rtscts; + cts-event-workaround; + skip-init; @@ -59500,7 +60253,7 @@ index 000000000000..1f60f3f03a10 + // <&rp1_dma RP1_DMA_UART4_RX>; + // dma-names = "tx", "rx"; + pinctrl-names = "default"; -+ arm,primecell-periphid = <0x00541011>; ++ arm,primecell-periphid = <0x00341011>; + uart-has-rtscts; + cts-event-workaround; + skip-init; @@ -59517,7 +60270,7 @@ index 000000000000..1f60f3f03a10 + // <&rp1_dma RP1_DMA_UART5_RX>; + // dma-names = "tx", "rx"; + pinctrl-names = "default"; -+ arm,primecell-periphid = <0x00541011>; ++ arm,primecell-periphid = <0x00341011>; + uart-has-rtscts; + cts-event-workaround; + skip-init; @@ -60341,12 +61094,15 @@ index 000000000000..1f60f3f03a10 + + rp1_eth: ethernet@100000 { + reg = <0xc0 0x40100000 0x0 0x4000>; -+ compatible = "cdns,macb"; ++ compatible = "raspberrypi,rp1-gem", "cdns,macb"; + #address-cells = <1>; + #size-cells = <0>; + interrupts = ; -+ clocks = <&macb_pclk &macb_hclk &rp1_clocks RP1_CLK_ETH_TSU>; -+ clock-names = "pclk", "hclk", "tsu_clk"; ++ clocks = <&rp1_clocks RP1_CLK_SYS ++ &rp1_clocks RP1_CLK_SYS ++ &rp1_clocks RP1_CLK_ETH_TSU ++ &rp1_clocks RP1_CLK_ETH>; ++ clock-names = "pclk", "hclk", "tsu_clk", "tx_clk"; + phy-mode = "rgmii-id"; + cdns,aw2w-max-pipe = /bits/ 8 <8>; + cdns,ar2r-max-pipe = /bits/ 8 <8>; @@ -60393,6 +61149,18 @@ index 000000000000..1f60f3f03a10 + status = "disabled"; + }; + ++ rp1_pio: pio@178000 { ++ reg = <0xc0 0x40178000 0x0 0x20>; ++ compatible = "raspberrypi,rp1-pio"; ++ firmware = <&rp1_firmware>; ++ dmas = <&rp1_dma RP1_DMA_PIO_CH0_TX>, <&rp1_dma RP1_DMA_PIO_CH0_RX>, ++ <&rp1_dma RP1_DMA_PIO_CH1_TX>, <&rp1_dma RP1_DMA_PIO_CH1_RX>, ++ <&rp1_dma RP1_DMA_PIO_CH2_TX>, <&rp1_dma RP1_DMA_PIO_CH2_RX>, ++ <&rp1_dma RP1_DMA_PIO_CH3_TX>, <&rp1_dma RP1_DMA_PIO_CH3_RX>; ++ dma-names = "tx0", "rx0", "tx1", "rx1", "tx2", "rx2", "tx3", "rx3"; ++ status = "disabled"; ++ }; ++ + rp1_mmc0: mmc@180000 { + reg = <0xc0 0x40180000 0x0 0x100>; + compatible = "raspberrypi,rp1-dwcmshc"; @@ -60429,7 +61197,7 @@ index 000000000000..1f60f3f03a10 + reg = <0xc0 0x40188000 0x0 0x1000>; + compatible = "snps,axi-dma-1.01a"; + interrupts = ; -+ clocks = <&sdhci_core &rp1_clocks RP1_CLK_SYS>; ++ clocks = <&rp1_clocks RP1_CLK_DMA &rp1_clocks RP1_CLK_SYS>; + clock-names = "core-clk", "cfgr-clk"; + + #dma-cells = <1>; @@ -60450,6 +61218,7 @@ index 000000000000..1f60f3f03a10 + usb3-lpm-capable; + snps,axi-pipe-limit = /bits/ 8 <8>; + snps,dis_rxdet_inp3_quirk; ++ snps,enhanced-nak-fs-quirk; + snps,parkmode-disable-ss-quirk; + snps,parkmode-disable-hs-quirk; + snps,parkmode-disable-fsls-quirk; @@ -60466,6 +61235,7 @@ index 000000000000..1f60f3f03a10 + usb3-lpm-capable; + snps,axi-pipe-limit = /bits/ 8 <8>; + snps,dis_rxdet_inp3_quirk; ++ snps,enhanced-nak-fs-quirk; + snps,parkmode-disable-ss-quirk; + snps,parkmode-disable-hs-quirk; + snps,parkmode-disable-fsls-quirk; @@ -60554,6 +61324,19 @@ index 000000000000..1f60f3f03a10 + assigned-clocks = <&rp1_clocks RP1_CLK_DPI>; + assigned-clock-parents = <&rp1_clocks RP1_PLL_VIDEO>; + }; ++ ++ sram: sram@400000 { ++ compatible = "mmio-sram"; ++ reg = <0xc0 0x40400000 0x0 0x10000>; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ranges = <0 0xc0 0x40400000 0x10000>; ++ ++ rp1_fw_shmem: shmem@ff00 { ++ compatible = "raspberrypi,rp1-shmem"; ++ reg = <0xff00 0x100>; // firmware mailbox buffer ++ }; ++ }; + }; +}; + @@ -60564,18 +61347,6 @@ index 000000000000..1f60f3f03a10 + clock-output-names = "xosc"; + clock-frequency = <50000000>; + }; -+ macb_pclk: macb_pclk { -+ compatible = "fixed-clock"; -+ #clock-cells = <0>; -+ clock-output-names = "pclk"; -+ clock-frequency = <200000000>; -+ }; -+ macb_hclk: macb_hclk { -+ compatible = "fixed-clock"; -+ #clock-cells = <0>; -+ clock-output-names = "hclk"; -+ clock-frequency = <200000000>; -+ }; + sdio_src: sdio_src { + // 400 MHz on FPGA. PLL sys VCO on asic + compatible = "fixed-clock"; @@ -60652,6 +61423,12 @@ index 000000000000..1f60f3f03a10 +}; + +/ { ++ rp1_firmware: rp1_firmware { ++ compatible = "raspberrypi,rp1-firmware", "simple-mfd"; ++ mboxes = <&rp1_mbox 0>; ++ shmem = <&rp1_fw_shmem>; ++ }; ++ + rp1_vdd_3v3: rp1_vdd_3v3 { + compatible = "regulator-fixed"; + regulator-name = "vdd-3v3"; @@ -60670,10 +61447,10 @@ index 000000000000..ded08646b6f6 \ No newline at end of file diff --git a/arch/arm64/configs/bcm2711_defconfig b/arch/arm64/configs/bcm2711_defconfig new file mode 100644 -index 000000000000..8e6081631a41 +index 000000000000..845dc739da5f --- /dev/null +++ b/arch/arm64/configs/bcm2711_defconfig -@@ -0,0 +1,1698 @@ +@@ -0,0 +1,1713 @@ +CONFIG_LOCALVERSION="-v8" +# CONFIG_LOCALVERSION_AUTO is not set +CONFIG_SYSVIPC=y @@ -60733,10 +61510,9 @@ index 000000000000..8e6081631a41 +CONFIG_CPU_IDLE=y +CONFIG_CPU_FREQ=y +CONFIG_CPU_FREQ_STAT=y -+CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE=y -+CONFIG_CPU_FREQ_GOV_PERFORMANCE=y ++CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y ++CONFIG_CPU_FREQ_GOV_POWERSAVE=y +CONFIG_CPU_FREQ_GOV_USERSPACE=y -+CONFIG_CPU_FREQ_GOV_ONDEMAND=y +CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y +CONFIG_CPU_FREQ_GOV_SCHEDUTIL=y +CONFIG_CPUFREQ_DT=y @@ -61132,6 +61908,7 @@ index 000000000000..8e6081631a41 +CONFIG_DEVTMPFS_MOUNT=y +# CONFIG_BRCMSTB_GISB_ARB is not set +CONFIG_RASPBERRYPI_FIRMWARE=y ++CONFIG_FIRMWARE_RP1=m +# CONFIG_EFI_VARS_PSTORE is not set +CONFIG_MTD=m +CONFIG_MTD_BLOCK=m @@ -61151,6 +61928,9 @@ index 000000000000..8e6081631a41 +CONFIG_BLK_DEV_RBD=m +CONFIG_BLK_DEV_NVME=y +CONFIG_NVME_HWMON=y ++CONFIG_RP1_PIO=m ++CONFIG_WS2812_PIO_RP1=m ++CONFIG_SRAM=y +CONFIG_EEPROM_AT24=m +CONFIG_EEPROM_AT25=m +CONFIG_TI_ST=m @@ -61208,6 +61988,7 @@ index 000000000000..8e6081631a41 +CONFIG_QCA7000_SPI=m +CONFIG_QCA7000_UART=m +CONFIG_R8169=m ++CONFIG_MSE102X=m +CONFIG_WIZNET_W5100=m +CONFIG_WIZNET_W5100_SPI=m +CONFIG_MICREL_PHY=y @@ -61285,6 +62066,9 @@ index 000000000000..8e6081631a41 +CONFIG_BRCMFMAC=m +CONFIG_BRCMFMAC_USB=y +CONFIG_BRCMDBG=y ++CONFIG_IWLWIFI=m ++CONFIG_IWLDVM=m ++CONFIG_IWLMVM=m +CONFIG_HOSTAP=m +CONFIG_P54_COMMON=m +CONFIG_P54_USB=m @@ -61386,6 +62170,7 @@ index 000000000000..8e6081631a41 +CONFIG_SERIAL_AMBA_PL011_CONSOLE=y +CONFIG_SERIAL_SC16IS7XX=m +CONFIG_SERIAL_SC16IS7XX_SPI=y ++CONFIG_SERIAL_RPI_FW=m +CONFIG_SERIAL_DEV_BUS=y +CONFIG_TTY_PRINTK=y +CONFIG_HW_RANDOM=y @@ -61474,6 +62259,7 @@ index 000000000000..8e6081631a41 +CONFIG_SENSORS_SHTC1=m +CONFIG_SENSORS_EMC2305=m +CONFIG_SENSORS_INA2XX=m ++CONFIG_SENSORS_INA238=m +CONFIG_SENSORS_TMP102=m +CONFIG_SENSORS_RP1_ADC=m +CONFIG_BCM2711_THERMAL=y @@ -61670,6 +62456,7 @@ index 000000000000..8e6081631a41 +CONFIG_VIDEO_IMX258=m +CONFIG_VIDEO_IMX290=m +CONFIG_VIDEO_IMX296=m ++CONFIG_VIDEO_IMX415=m +CONFIG_VIDEO_IMX477=m +CONFIG_VIDEO_IMX500=m +CONFIG_VIDEO_IMX519=m @@ -61754,6 +62541,7 @@ index 000000000000..8e6081631a41 +CONFIG_SND_MTPAV=m +CONFIG_SND_SERIAL_U16550=m +CONFIG_SND_MPU401=m ++CONFIG_SND_PIMIDI=m +CONFIG_SND_USB_AUDIO=m +CONFIG_SND_USB_UA101=m +CONFIG_SND_USB_CAIAQ=m @@ -61816,6 +62604,7 @@ index 000000000000..8e6081631a41 +CONFIG_SND_SOC_SPDIF=m +CONFIG_SND_SOC_TLV320AIC23_I2C=m +CONFIG_SND_SOC_WM8804_I2C=m ++CONFIG_SND_SOC_WM8904=m +CONFIG_SND_SOC_WM8960=m +CONFIG_SND_SIMPLE_CARD=m +CONFIG_HID_BATTERY_STRENGTH=y @@ -62153,6 +62942,7 @@ index 000000000000..8e6081631a41 +CONFIG_CLK_RASPBERRYPI=y +CONFIG_MAILBOX=y +CONFIG_BCM2835_MBOX=y ++CONFIG_MBOX_RP1=m +CONFIG_BCM2712_IOMMU=y +CONFIG_RASPBERRYPI_POWER=y +CONFIG_IIO=m @@ -62188,6 +62978,7 @@ index 000000000000..8e6081631a41 +CONFIG_PWM_BRCMSTB=y +CONFIG_PWM_GPIO=m +CONFIG_PWM_PCA9685=m ++CONFIG_PWM_PIO_RP1=m +CONFIG_PWM_RASPBERRYPI_POE=m +CONFIG_PWM_RP1=y +CONFIG_BCM2712_MIP=y @@ -62340,6 +63131,7 @@ index 000000000000..8e6081631a41 +CONFIG_CRYPTO_USER_API_SKCIPHER=m +CONFIG_CRYPTO_USER_API_RNG=m +CONFIG_CRYPTO_USER_API_AEAD=m ++CONFIG_CRYPTO_NHPOLY1305_NEON=m +CONFIG_CRYPTO_GHASH_ARM64_CE=m +CONFIG_CRYPTO_SHA1_ARM64_CE=m +CONFIG_CRYPTO_SHA2_ARM64_CE=m @@ -62374,10 +63166,10 @@ index 000000000000..8e6081631a41 +# CONFIG_STRICT_DEVMEM is not set diff --git a/arch/arm64/configs/bcm2712_defconfig b/arch/arm64/configs/bcm2712_defconfig new file mode 100644 -index 000000000000..7a4f1d4e151f +index 000000000000..3bdf65c72b40 --- /dev/null +++ b/arch/arm64/configs/bcm2712_defconfig -@@ -0,0 +1,1701 @@ +@@ -0,0 +1,1715 @@ +CONFIG_LOCALVERSION="-v8-16k" +# CONFIG_LOCALVERSION_AUTO is not set +CONFIG_SYSVIPC=y @@ -62438,10 +63230,9 @@ index 000000000000..7a4f1d4e151f +CONFIG_CPU_IDLE=y +CONFIG_CPU_FREQ=y +CONFIG_CPU_FREQ_STAT=y -+CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE=y -+CONFIG_CPU_FREQ_GOV_PERFORMANCE=y ++CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y ++CONFIG_CPU_FREQ_GOV_POWERSAVE=y +CONFIG_CPU_FREQ_GOV_USERSPACE=y -+CONFIG_CPU_FREQ_GOV_ONDEMAND=y +CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y +CONFIG_CPU_FREQ_GOV_SCHEDUTIL=y +CONFIG_CPUFREQ_DT=y @@ -62839,6 +63630,7 @@ index 000000000000..7a4f1d4e151f +CONFIG_DEVTMPFS_MOUNT=y +# CONFIG_BRCMSTB_GISB_ARB is not set +CONFIG_RASPBERRYPI_FIRMWARE=y ++CONFIG_FIRMWARE_RP1=m +# CONFIG_EFI_VARS_PSTORE is not set +CONFIG_MTD=m +CONFIG_MTD_BLOCK=m @@ -62858,6 +63650,9 @@ index 000000000000..7a4f1d4e151f +CONFIG_BLK_DEV_RBD=m +CONFIG_BLK_DEV_NVME=y +CONFIG_NVME_HWMON=y ++CONFIG_RP1_PIO=m ++CONFIG_WS2812_PIO_RP1=m ++CONFIG_SRAM=y +CONFIG_EEPROM_AT24=m +CONFIG_EEPROM_AT25=m +CONFIG_TI_ST=m @@ -62915,6 +63710,7 @@ index 000000000000..7a4f1d4e151f +CONFIG_QCA7000_SPI=m +CONFIG_QCA7000_UART=m +CONFIG_R8169=m ++CONFIG_MSE102X=m +CONFIG_WIZNET_W5100=m +CONFIG_WIZNET_W5100_SPI=m +CONFIG_MICREL_PHY=y @@ -62992,6 +63788,9 @@ index 000000000000..7a4f1d4e151f +CONFIG_BRCMFMAC=m +CONFIG_BRCMFMAC_USB=y +CONFIG_BRCMDBG=y ++CONFIG_IWLWIFI=m ++CONFIG_IWLDVM=m ++CONFIG_IWLMVM=m +CONFIG_HOSTAP=m +CONFIG_P54_COMMON=m +CONFIG_P54_USB=m @@ -63181,6 +63980,7 @@ index 000000000000..7a4f1d4e151f +CONFIG_SENSORS_SHTC1=m +CONFIG_SENSORS_EMC2305=m +CONFIG_SENSORS_INA2XX=m ++CONFIG_SENSORS_INA238=m +CONFIG_SENSORS_TMP102=m +CONFIG_SENSORS_RP1_ADC=m +CONFIG_BCM2711_THERMAL=y @@ -63377,6 +64177,7 @@ index 000000000000..7a4f1d4e151f +CONFIG_VIDEO_IMX258=m +CONFIG_VIDEO_IMX290=m +CONFIG_VIDEO_IMX296=m ++CONFIG_VIDEO_IMX415=m +CONFIG_VIDEO_IMX477=m +CONFIG_VIDEO_IMX500=m +CONFIG_VIDEO_IMX519=m @@ -63461,6 +64262,7 @@ index 000000000000..7a4f1d4e151f +CONFIG_SND_MTPAV=m +CONFIG_SND_SERIAL_U16550=m +CONFIG_SND_MPU401=m ++CONFIG_SND_PIMIDI=m +CONFIG_SND_USB_AUDIO=m +CONFIG_SND_USB_UA101=m +CONFIG_SND_USB_CAIAQ=m @@ -63523,6 +64325,7 @@ index 000000000000..7a4f1d4e151f +CONFIG_SND_SOC_SPDIF=m +CONFIG_SND_SOC_TLV320AIC23_I2C=m +CONFIG_SND_SOC_WM8804_I2C=m ++CONFIG_SND_SOC_WM8904=m +CONFIG_SND_SOC_WM8960=m +CONFIG_SND_SIMPLE_CARD=m +CONFIG_HID_BATTERY_STRENGTH=y @@ -63860,6 +64663,7 @@ index 000000000000..7a4f1d4e151f +CONFIG_CLK_RASPBERRYPI=y +CONFIG_MAILBOX=y +CONFIG_BCM2835_MBOX=y ++CONFIG_MBOX_RP1=m +CONFIG_BCM2712_IOMMU=y +CONFIG_RASPBERRYPI_POWER=y +CONFIG_IIO=m @@ -63895,6 +64699,7 @@ index 000000000000..7a4f1d4e151f +CONFIG_PWM_BRCMSTB=y +CONFIG_PWM_GPIO=m +CONFIG_PWM_PCA9685=m ++CONFIG_PWM_PIO_RP1=m +CONFIG_PWM_RASPBERRYPI_POE=m +CONFIG_PWM_RP1=y +CONFIG_BCM2712_MIP=y @@ -64047,6 +64852,7 @@ index 000000000000..7a4f1d4e151f +CONFIG_CRYPTO_USER_API_SKCIPHER=m +CONFIG_CRYPTO_USER_API_RNG=m +CONFIG_CRYPTO_USER_API_AEAD=m ++CONFIG_CRYPTO_NHPOLY1305_NEON=m +CONFIG_CRYPTO_GHASH_ARM64_CE=m +CONFIG_CRYPTO_SHA1_ARM64_CE=m +CONFIG_CRYPTO_SHA2_ARM64_CE=m @@ -64081,10 +64887,10 @@ index 000000000000..7a4f1d4e151f +# CONFIG_STRICT_DEVMEM is not set diff --git a/arch/arm64/configs/bcmrpi3_defconfig b/arch/arm64/configs/bcmrpi3_defconfig new file mode 100644 -index 000000000000..86ad9e77c251 +index 000000000000..7ab46c0502f3 --- /dev/null +++ b/arch/arm64/configs/bcmrpi3_defconfig -@@ -0,0 +1,1575 @@ +@@ -0,0 +1,1580 @@ +CONFIG_LOCALVERSION="-v8" +# CONFIG_LOCALVERSION_AUTO is not set +CONFIG_SYSVIPC=y @@ -64139,10 +64945,9 @@ index 000000000000..86ad9e77c251 +CONFIG_CPU_IDLE=y +CONFIG_CPU_FREQ=y +CONFIG_CPU_FREQ_STAT=y -+CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE=y -+CONFIG_CPU_FREQ_GOV_PERFORMANCE=y ++CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y ++CONFIG_CPU_FREQ_GOV_POWERSAVE=y +CONFIG_CPU_FREQ_GOV_USERSPACE=y -+CONFIG_CPU_FREQ_GOV_ONDEMAND=y +CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y +CONFIG_CPU_FREQ_GOV_SCHEDUTIL=y +CONFIG_CPUFREQ_DT=y @@ -64583,6 +65388,7 @@ index 000000000000..86ad9e77c251 +CONFIG_ENC28J60=m +CONFIG_QCA7000_SPI=m +CONFIG_QCA7000_UART=m ++CONFIG_MSE102X=m +CONFIG_WIZNET_W5100=m +CONFIG_WIZNET_W5100_SPI=m +CONFIG_CAN_VCAN=m @@ -64756,6 +65562,7 @@ index 000000000000..86ad9e77c251 +CONFIG_SERIAL_AMBA_PL011_CONSOLE=y +CONFIG_SERIAL_SC16IS7XX=m +CONFIG_SERIAL_SC16IS7XX_SPI=y ++CONFIG_SERIAL_RPI_FW=m +CONFIG_SERIAL_DEV_BUS=y +CONFIG_TTY_PRINTK=y +CONFIG_HW_RANDOM=y @@ -64832,6 +65639,7 @@ index 000000000000..86ad9e77c251 +CONFIG_SENSORS_SHTC1=m +CONFIG_SENSORS_EMC2305=m +CONFIG_SENSORS_INA2XX=m ++CONFIG_SENSORS_INA238=m +CONFIG_SENSORS_TMP102=m +CONFIG_BCM2835_THERMAL=y +CONFIG_WATCHDOG=y @@ -65066,6 +65874,7 @@ index 000000000000..86ad9e77c251 +CONFIG_SND_MTPAV=m +CONFIG_SND_SERIAL_U16550=m +CONFIG_SND_MPU401=m ++CONFIG_SND_PIMIDI=m +CONFIG_SND_USB_AUDIO=m +CONFIG_SND_USB_UA101=m +CONFIG_SND_USB_CAIAQ=m @@ -65126,6 +65935,7 @@ index 000000000000..86ad9e77c251 +CONFIG_SND_SOC_SPDIF=m +CONFIG_SND_SOC_TLV320AIC23_I2C=m +CONFIG_SND_SOC_WM8804_I2C=m ++CONFIG_SND_SOC_WM8904=m +CONFIG_SND_SOC_WM8960=m +CONFIG_SND_SIMPLE_CARD=m +CONFIG_HID_BATTERY_STRENGTH=y @@ -65638,6 +66448,7 @@ index 000000000000..86ad9e77c251 +CONFIG_CRYPTO_USER_API_SKCIPHER=m +CONFIG_CRYPTO_USER_API_RNG=m +CONFIG_CRYPTO_USER_API_AEAD=m ++CONFIG_CRYPTO_NHPOLY1305_NEON=m +CONFIG_CRYPTO_AES_ARM64=m +CONFIG_CRYPTO_AES_ARM64_BS=m +# CONFIG_CRYPTO_HW is not set @@ -65791,7 +66602,7 @@ index 7466b6066d87..2c5f88bb8764 100644 } diff --git a/arch/arm64/kernel/process.c b/arch/arm64/kernel/process.c -index 068e5bb2661b..0465e6645537 100644 +index fe3f89445fcb..1b1c7fe73013 100644 --- a/arch/arm64/kernel/process.c +++ b/arch/arm64/kernel/process.c @@ -96,9 +96,7 @@ void machine_shutdown(void) @@ -65806,10 +66617,10 @@ index 068e5bb2661b..0465e6645537 100644 /* diff --git a/arch/arm64/kernel/setup.c b/arch/arm64/kernel/setup.c -index d82fd6902ea8..15cbdeb56f61 100644 +index 7099c69a2c7f..e0d85a983b0d 100644 --- a/arch/arm64/kernel/setup.c +++ b/arch/arm64/kernel/setup.c -@@ -270,9 +270,9 @@ static void __init request_standard_resources(void) +@@ -274,9 +274,9 @@ static void __init request_standard_resources(void) size_t res_size; kernel_code.start = __pa_symbol(_stext); @@ -69587,10 +70398,10 @@ index 000000000000..7412e24d34cf +MODULE_LICENSE("GPL"); diff --git a/drivers/clk/clk-rp1.c b/drivers/clk/clk-rp1.c new file mode 100644 -index 000000000000..ae383588063a +index 000000000000..3ae2f51d4771 --- /dev/null +++ b/drivers/clk/clk-rp1.c -@@ -0,0 +1,2500 @@ +@@ -0,0 +1,2492 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2023 Raspberry Pi Ltd. @@ -70520,7 +71331,8 @@ index 000000000000..ae383588063a + const struct rp1_pll_data *data = divider->data; + + spin_lock(&clockman->regs_lock); -+ clockman_write(clockman, data->ctrl_reg, PLL_SEC_RST); ++ clockman_write(clockman, data->ctrl_reg, ++ clockman_read(clockman, data->ctrl_reg) | PLL_SEC_RST); + spin_unlock(&clockman->regs_lock); +} + @@ -71096,8 +71908,6 @@ index 000000000000..ae383588063a + .round_rate = rp1_varsrc_round_rate, +}; + -+static bool rp1_clk_is_claimed(const char *name); -+ +static struct clk_hw *rp1_register_pll_core(struct rp1_clockman *clockman, + const void *data) +{ @@ -71113,7 +71923,7 @@ index 000000000000..ae383588063a + init.num_parents = 1; + init.name = pll_core_data->name; + init.ops = &rp1_pll_core_ops; -+ init.flags = pll_core_data->flags | CLK_IGNORE_UNUSED | CLK_IS_CRITICAL; ++ init.flags = pll_core_data->flags | CLK_IS_CRITICAL; + + pll_core = kzalloc(sizeof(*pll_core), GFP_KERNEL); + if (!pll_core) @@ -71146,7 +71956,7 @@ index 000000000000..ae383588063a + init.num_parents = 1; + init.name = pll_data->name; + init.ops = &rp1_pll_ops; -+ init.flags = pll_data->flags | CLK_IGNORE_UNUSED | CLK_IS_CRITICAL; ++ init.flags = pll_data->flags; + + pll = kzalloc(sizeof(*pll), GFP_KERNEL); + if (!pll) @@ -71180,7 +71990,7 @@ index 000000000000..ae383588063a + init.num_parents = 1; + init.name = ph_data->name; + init.ops = &rp1_pll_ph_ops; -+ init.flags = ph_data->flags | CLK_IGNORE_UNUSED; ++ init.flags = ph_data->flags; + + ph = kzalloc(sizeof(*ph), GFP_KERNEL); + if (!ph) @@ -71213,7 +72023,7 @@ index 000000000000..ae383588063a + init.num_parents = 1; + init.name = divider_data->name; + init.ops = &rp1_pll_divider_ops; -+ init.flags = divider_data->flags | CLK_IGNORE_UNUSED; ++ init.flags = divider_data->flags; + + divider = devm_kzalloc(clockman->dev, sizeof(*divider), GFP_KERNEL); + if (!divider) @@ -71227,11 +72037,6 @@ index 000000000000..ae383588063a + divider->div.hw.init = &init; + divider->div.table = pll_sec_div_table; + -+ if (!rp1_clk_is_claimed(divider_data->source_pll)) -+ init.flags |= CLK_IS_CRITICAL; -+ if (!rp1_clk_is_claimed(divider_data->name)) -+ divider->div.flags |= CLK_IS_CRITICAL; -+ + divider->clockman = clockman; + divider->data = divider_data; + @@ -71261,7 +72066,7 @@ index 000000000000..ae383588063a + init.num_parents = + clock_data->num_std_parents + clock_data->num_aux_parents; + init.name = clock_data->name; -+ init.flags = clock_data->flags | CLK_IGNORE_UNUSED; ++ init.flags = clock_data->flags; + init.ops = &rp1_clk_ops; + + clock = devm_kzalloc(clockman->dev, sizeof(*clock), GFP_KERNEL); @@ -71291,7 +72096,6 @@ index 000000000000..ae383588063a + init.parent_names = &ref_clock; + init.num_parents = 1; + init.name = name; -+ init.flags = CLK_IGNORE_UNUSED; + init.ops = &rp1_varsrc_ops; + + clock = devm_kzalloc(clockman->dev, sizeof(*clock), GFP_KERNEL); @@ -71453,6 +72257,8 @@ index 000000000000..ae383588063a + .max_freq = 200 * MHz, + .fc0_src = FC_NUM(0, 4), + .clk_src_mask = 0x3, ++ /* Always enabled in hardware */ ++ .flags = CLK_IS_CRITICAL, + ), + + [RP1_CLK_SLOW_SYS] = REGISTER_CLK( @@ -71467,6 +72273,29 @@ index 000000000000..ae383588063a + .max_freq = 50 * MHz, + .fc0_src = FC_NUM(1, 4), + .clk_src_mask = 0x1, ++ /* Always enabled in hardware */ ++ .flags = CLK_IS_CRITICAL, ++ ), ++ ++ [RP1_CLK_DMA] = REGISTER_CLK( ++ .name = "clk_dma", ++ .parents = {"pll_sys_pri_ph", ++ "pll_video", ++ "xosc", ++ "clksrc_gp0", ++ "clksrc_gp1", ++ "clksrc_gp2", ++ "clksrc_gp3", ++ "clksrc_gp4", ++ "clksrc_gp5"}, ++ .num_std_parents = 0, ++ .num_aux_parents = 9, ++ .ctrl_reg = CLK_DMA_CTRL, ++ .div_int_reg = CLK_DMA_DIV_INT, ++ .sel_reg = CLK_DMA_SEL, ++ .div_int_max = DIV_INT_8BIT_MAX, ++ .max_freq = 100 * MHz, ++ .fc0_src = FC_NUM(2, 2), + ), + + [RP1_CLK_UART] = REGISTER_CLK( @@ -71986,24 +72815,6 @@ index 000000000000..ae383588063a + [RP1_CLK_MIPI1_DSI_BYTECLOCK] = REGISTER_VARSRC("clksrc_mipi1_dsi_byteclk"), +}; + -+static bool rp1_clk_claimed[ARRAY_SIZE(clk_desc_array)]; -+ -+static bool rp1_clk_is_claimed(const char *name) -+{ -+ unsigned int i; -+ -+ for (i = 0; i < ARRAY_SIZE(clk_desc_array); i++) { -+ if (clk_desc_array[i].data) { -+ const char *clk_name = *(const char **)(clk_desc_array[i].data); -+ -+ if (!strcmp(name, clk_name)) -+ return rp1_clk_claimed[i]; -+ } -+ } -+ -+ return false; -+} -+ +static int rp1_clk_probe(struct platform_device *pdev) +{ + const struct rp1_clk_desc *desc; @@ -72014,7 +72825,6 @@ index 000000000000..ae383588063a + const size_t asize = ARRAY_SIZE(clk_desc_array); + u32 chip_id, platform; + unsigned int i; -+ u32 clk_id; + int ret; + + clockman = devm_kzalloc(dev, struct_size(clockman, onecell.hws, asize), @@ -72031,13 +72841,6 @@ index 000000000000..ae383588063a + if (IS_ERR(clockman->regs)) + return PTR_ERR(clockman->regs); + -+ memset(rp1_clk_claimed, 0, sizeof(rp1_clk_claimed)); -+ for (i = 0; -+ !of_property_read_u32_index(pdev->dev.of_node, "claim-clocks", -+ i, &clk_id); -+ i++) -+ rp1_clk_claimed[clk_id] = true; -+ + platform_set_drvdata(pdev, clockman); + + clockman->onecell.num = asize; @@ -72092,14 +72895,19 @@ index 000000000000..ae383588063a +MODULE_DESCRIPTION("RP1 clock driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/dma-buf/heaps/system_heap.c b/drivers/dma-buf/heaps/system_heap.c -index 9076d47ed2ef..9dc5dfeaca2a 100644 +index 9076d47ed2ef..02c4604caf12 100644 --- a/drivers/dma-buf/heaps/system_heap.c +++ b/drivers/dma-buf/heaps/system_heap.c -@@ -54,6 +54,11 @@ static gfp_t order_flags[] = {HIGH_ORDER_GFP, HIGH_ORDER_GFP, LOW_ORDER_GFP}; - static const unsigned int orders[] = {8, 4, 0}; +@@ -51,9 +51,15 @@ static gfp_t order_flags[] = {HIGH_ORDER_GFP, HIGH_ORDER_GFP, LOW_ORDER_GFP}; + * of order 0 pages can significantly improve the performance of many IOMMUs + * by reducing TLB pressure and time spent updating page tables. + */ +-static const unsigned int orders[] = {8, 4, 0}; ++#define MAX_ORDERS_VALUE 8 ++static const unsigned int orders[] = {MAX_ORDERS_VALUE, 4, 0}; #define NUM_ORDERS ARRAY_SIZE(orders) -+static unsigned int module_max_order = orders[0]; ++static unsigned int module_max_order = MAX_ORDERS_VALUE; + +module_param_named(max_order, module_max_order, uint, 0400); +MODULE_PARM_DESC(max_order, "Maximum allocation order override."); @@ -72107,7 +72915,7 @@ index 9076d47ed2ef..9dc5dfeaca2a 100644 static struct sg_table *dup_sg_table(struct sg_table *table) { struct sg_table *new_table; -@@ -339,7 +344,7 @@ static struct dma_buf *system_heap_allocate(struct dma_heap *heap, +@@ -339,7 +345,7 @@ static struct dma_buf *system_heap_allocate(struct dma_heap *heap, struct system_heap_buffer *buffer; DEFINE_DMA_BUF_EXPORT_INFO(exp_info); unsigned long size_remaining = len; @@ -72116,7 +72924,7 @@ index 9076d47ed2ef..9dc5dfeaca2a 100644 struct dma_buf *dmabuf; struct sg_table *table; struct scatterlist *sg; -@@ -433,6 +438,9 @@ static int system_heap_create(void) +@@ -433,6 +439,9 @@ static int system_heap_create(void) if (IS_ERR(sys_heap)) return PTR_ERR(sys_heap); @@ -73556,7 +74364,7 @@ index 0807fb9eb262..e159f976a6b1 100644 MODULE_ALIAS("platform:bcm2835-dma"); MODULE_DESCRIPTION("BCM2835 DMA engine driver"); diff --git a/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c b/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c -index 72fb40de58b3..cc842d8ed2ad 100644 +index 72fb40de58b3..acd0833b7bd4 100644 --- a/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c +++ b/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c @@ -12,6 +12,7 @@ @@ -73737,7 +74545,20 @@ index 72fb40de58b3..cc842d8ed2ad 100644 config.dst_multblk_type = DWAXIDMAC_MBLK_TYPE_LL; config.src_multblk_type = DWAXIDMAC_MBLK_TYPE_LL; config.tt_fc = DWAXIDMAC_TT_FC_MEM_TO_MEM_DMAC; -@@ -523,7 +606,7 @@ static void dw_axi_dma_set_hw_channel(struct axi_dma_chan *chan, bool set) +@@ -453,9 +536,11 @@ static void dma_chan_issue_pending(struct dma_chan *dchan) + { + struct axi_dma_chan *chan = dchan_to_axi_dma_chan(dchan); + unsigned long flags; ++ bool was_empty; + + spin_lock_irqsave(&chan->vc.lock, flags); +- if (vchan_issue_pending(&chan->vc)) ++ was_empty = list_empty(&chan->vc.desc_issued); ++ if (vchan_issue_pending(&chan->vc) && was_empty) + axi_chan_start_first_queued(chan); + spin_unlock_irqrestore(&chan->vc.lock, flags); + } +@@ -523,7 +608,7 @@ static void dw_axi_dma_set_hw_channel(struct axi_dma_chan *chan, bool set) unsigned long reg_value, val; if (!chip->apb_regs) { @@ -73746,7 +74567,7 @@ index 72fb40de58b3..cc842d8ed2ad 100644 return; } -@@ -611,34 +694,41 @@ static int dw_axi_dma_set_hw_desc(struct axi_dma_chan *chan, +@@ -611,34 +696,53 @@ static int dw_axi_dma_set_hw_desc(struct axi_dma_chan *chan, size_t axi_block_ts; size_t block_ts; u32 ctllo, ctlhi; @@ -73785,6 +74606,18 @@ index 72fb40de58b3..cc842d8ed2ad 100644 + reg_burst_msize = axi_dma_encode_msize(chan->config.src_maxburst); reg_width = __ffs(chan->config.src_addr_width); - device_addr = chan->config.src_addr; ++ /* ++ * For devices where transfer lengths are not known upfront, ++ * there is a danger when the destination is wider than the ++ * source that partial words can be lost at the end of a transfer. ++ * Ideally the controller would be able to flush the residue, but ++ * it can't - it's not even possible to tell that there is any. ++ * Instead, allow the client driver to avoid the problem by setting ++ * a smaller width. ++ */ ++ if (chan->config.dst_addr_width && ++ (chan->config.dst_addr_width < mem_width)) ++ mem_width = chan->config.dst_addr_width; + device_addr = phys_to_dma(chan->chip->dev, chan->config.src_addr); ctllo = reg_width << CH_CTL_L_SRC_WIDTH_POS | mem_width << CH_CTL_L_DST_WIDTH_POS | @@ -73793,7 +74626,7 @@ index 72fb40de58b3..cc842d8ed2ad 100644 DWAXIDMAC_CH_CTL_L_INC << CH_CTL_L_DST_INC_POS | DWAXIDMAC_CH_CTL_L_NOINC << CH_CTL_L_SRC_INC_POS; block_ts = len >> reg_width; -@@ -674,14 +764,17 @@ static int dw_axi_dma_set_hw_desc(struct axi_dma_chan *chan, +@@ -674,14 +778,17 @@ static int dw_axi_dma_set_hw_desc(struct axi_dma_chan *chan, } hw_desc->lli->block_ts_lo = cpu_to_le32(block_ts - 1); @@ -73814,7 +74647,7 @@ index 72fb40de58b3..cc842d8ed2ad 100644 return 0; } -@@ -698,9 +791,6 @@ static size_t calculate_block_len(struct axi_dma_chan *chan, +@@ -698,9 +805,6 @@ static size_t calculate_block_len(struct axi_dma_chan *chan, case DMA_MEM_TO_DEV: data_width = BIT(chan->chip->dw->hdata->m_data_width); mem_width = __ffs(data_width | dma_addr | buf_len); @@ -73824,7 +74657,7 @@ index 72fb40de58b3..cc842d8ed2ad 100644 block_len = axi_block_ts << mem_width; break; case DMA_DEV_TO_MEM: -@@ -771,6 +861,8 @@ dw_axi_dma_chan_prep_cyclic(struct dma_chan *dchan, dma_addr_t dma_addr, +@@ -771,6 +875,8 @@ dw_axi_dma_chan_prep_cyclic(struct dma_chan *dchan, dma_addr_t dma_addr, src_addr += segment_len; } @@ -73833,7 +74666,7 @@ index 72fb40de58b3..cc842d8ed2ad 100644 llp = desc->hw_desc[0].llp; /* Managed transfer list */ -@@ -836,6 +928,9 @@ dw_axi_dma_chan_prep_slave_sg(struct dma_chan *dchan, struct scatterlist *sgl, +@@ -836,6 +942,9 @@ dw_axi_dma_chan_prep_slave_sg(struct dma_chan *dchan, struct scatterlist *sgl, mem = sg_dma_address(sg); len = sg_dma_len(sg); num_segments = DIV_ROUND_UP(sg_dma_len(sg), axi_block_len); @@ -73843,7 +74676,7 @@ index 72fb40de58b3..cc842d8ed2ad 100644 segment_len = DIV_ROUND_UP(sg_dma_len(sg), num_segments); do { -@@ -850,6 +945,8 @@ dw_axi_dma_chan_prep_slave_sg(struct dma_chan *dchan, struct scatterlist *sgl, +@@ -850,6 +959,8 @@ dw_axi_dma_chan_prep_slave_sg(struct dma_chan *dchan, struct scatterlist *sgl, } while (len >= segment_len); } @@ -73852,7 +74685,7 @@ index 72fb40de58b3..cc842d8ed2ad 100644 /* Set end-of-link to the last link descriptor of list */ set_desc_last(&desc->hw_desc[num_sgs - 1]); -@@ -957,6 +1054,8 @@ dma_chan_prep_dma_memcpy(struct dma_chan *dchan, dma_addr_t dst_adr, +@@ -957,6 +1068,8 @@ dma_chan_prep_dma_memcpy(struct dma_chan *dchan, dma_addr_t dst_adr, num++; } @@ -73861,7 +74694,7 @@ index 72fb40de58b3..cc842d8ed2ad 100644 /* Set end-of-link to the last link descriptor of list */ set_desc_last(&desc->hw_desc[num - 1]); /* Managed transfer list */ -@@ -1005,7 +1104,7 @@ static void axi_chan_dump_lli(struct axi_dma_chan *chan, +@@ -1005,7 +1118,7 @@ static void axi_chan_dump_lli(struct axi_dma_chan *chan, static void axi_chan_list_dump_lli(struct axi_dma_chan *chan, struct axi_dma_desc *desc_head) { @@ -73870,7 +74703,7 @@ index 72fb40de58b3..cc842d8ed2ad 100644 int i; for (i = 0; i < count; i++) -@@ -1048,11 +1147,11 @@ static noinline void axi_chan_handle_err(struct axi_dma_chan *chan, u32 status) +@@ -1048,11 +1161,11 @@ static noinline void axi_chan_handle_err(struct axi_dma_chan *chan, u32 status) static void axi_chan_block_xfer_complete(struct axi_dma_chan *chan) { @@ -73883,7 +74716,7 @@ index 72fb40de58b3..cc842d8ed2ad 100644 u64 llp; int i; -@@ -1074,6 +1173,7 @@ static void axi_chan_block_xfer_complete(struct axi_dma_chan *chan) +@@ -1074,6 +1187,7 @@ static void axi_chan_block_xfer_complete(struct axi_dma_chan *chan) if (chan->cyclic) { desc = vd_to_axi_desc(vd); if (desc) { @@ -73891,7 +74724,7 @@ index 72fb40de58b3..cc842d8ed2ad 100644 llp = lo_hi_readq(chan->chan_regs + CH_LLP); for (i = 0; i < count; i++) { hw_desc = &desc->hw_desc[i]; -@@ -1094,6 +1194,9 @@ static void axi_chan_block_xfer_complete(struct axi_dma_chan *chan) +@@ -1094,6 +1208,9 @@ static void axi_chan_block_xfer_complete(struct axi_dma_chan *chan) /* Remove the completed descriptor from issued list before completing */ list_del(&vd->node); vchan_cookie_complete(vd); @@ -73901,7 +74734,7 @@ index 72fb40de58b3..cc842d8ed2ad 100644 } out: -@@ -1323,6 +1426,10 @@ static int parse_device_properties(struct axi_dma_chip *chip) +@@ -1323,6 +1440,10 @@ static int parse_device_properties(struct axi_dma_chip *chip) chip->dw->hdata->nr_masters = tmp; @@ -73912,7 +74745,7 @@ index 72fb40de58b3..cc842d8ed2ad 100644 ret = device_property_read_u32(dev, "snps,data-width", &tmp); if (ret) return ret; -@@ -1375,6 +1482,7 @@ static int dw_probe(struct platform_device *pdev) +@@ -1375,6 +1496,7 @@ static int dw_probe(struct platform_device *pdev) struct dw_axi_dma *dw; struct dw_axi_dma_hcfg *hdata; struct reset_control *resets; @@ -73920,7 +74753,7 @@ index 72fb40de58b3..cc842d8ed2ad 100644 unsigned int flags; u32 i; int ret; -@@ -1490,9 +1598,21 @@ static int dw_probe(struct platform_device *pdev) +@@ -1490,9 +1612,21 @@ static int dw_probe(struct platform_device *pdev) * Synopsis DesignWare AxiDMA datasheet mentioned Maximum * supported blocks is 1024. Device register width is 4 bytes. * Therefore, set constraint to 1024 * 4. @@ -73943,6 +74776,38 @@ index 72fb40de58b3..cc842d8ed2ad 100644 platform_set_drvdata(pdev, chip); pm_runtime_enable(chip->dev); +diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig +index cc4716c037a6..6293ecb2d484 100644 +--- a/drivers/firmware/Kconfig ++++ b/drivers/firmware/Kconfig +@@ -153,6 +153,15 @@ config RASPBERRYPI_FIRMWARE + This option enables support for communicating with the firmware on the + Raspberry Pi. + ++config FIRMWARE_RP1 ++ tristate "RP1 Firmware Driver" ++ depends on MBOX_RP1 ++ help ++ The Raspberry Pi RP1 processor presents a firmware ++ interface using shared memory and a mailbox. To enable ++ the driver that communicates with it, say Y. Otherwise, ++ say N. ++ + config FW_CFG_SYSFS + tristate "QEMU fw_cfg device support in sysfs" + depends on SYSFS && (ARM || ARM64 || PARISC || PPC_PMAC || SPARC || X86 || SW64) +diff --git a/drivers/firmware/Makefile b/drivers/firmware/Makefile +index 28fcddcd688f..12185bc9e495 100644 +--- a/drivers/firmware/Makefile ++++ b/drivers/firmware/Makefile +@@ -17,6 +17,7 @@ obj-$(CONFIG_ISCSI_IBFT) += iscsi_ibft.o + obj-$(CONFIG_FIRMWARE_MEMMAP) += memmap.o + obj-$(CONFIG_MTK_ADSP_IPC) += mtk-adsp-ipc.o + obj-$(CONFIG_RASPBERRYPI_FIRMWARE) += raspberrypi.o ++obj-$(CONFIG_FIRMWARE_RP1) += rp1.o + obj-$(CONFIG_FW_CFG_SYSFS) += qemu_fw_cfg.o + obj-$(CONFIG_QCOM_SCM) += qcom-scm.o + qcom-scm-objs += qcom_scm.o qcom_scm-smc.o qcom_scm-legacy.o diff --git a/drivers/firmware/psci/psci.c b/drivers/firmware/psci/psci.c index 2328ca58bba6..7a1e42237cf2 100644 --- a/drivers/firmware/psci/psci.c @@ -74185,6 +75050,322 @@ index 428ae54d3196..f1a2505ebc9e 100644 MODULE_AUTHOR("Eric Anholt "); MODULE_DESCRIPTION("Raspberry Pi firmware driver"); +diff --git a/drivers/firmware/rp1.c b/drivers/firmware/rp1.c +new file mode 100644 +index 000000000000..a258452c8b33 +--- /dev/null ++++ b/drivers/firmware/rp1.c +@@ -0,0 +1,310 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (C) 2023-24 Raspberry Pi Ltd. ++ * ++ * Parts of this driver are based on: ++ * - raspberrypi.c, by Eric Anholt ++ * Copyright (C) 2015 Broadcom ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define RP1_MAILBOX_FIRMWARE 0 ++ ++enum rp1_firmware_ops { ++ MBOX_SUCCESS = 0x0000, ++ GET_FIRMWARE_VERSION = 0x0001, // na -> 160-bit version ++ GET_FEATURE = 0x0002, // FOURCC -> op base (0 == unsupported), op count ++ ++ COMMON_COUNT ++}; ++ ++struct rp1_firmware { ++ struct mbox_client cl; ++ struct mbox_chan *chan; /* The doorbell channel */ ++ uint32_t __iomem *buf; /* The shared buffer */ ++ u32 buf_size; /* The size of the shared buffer */ ++ struct completion c; ++ ++ struct kref consumers; ++}; ++ ++struct rp1_get_feature_resp { ++ uint32_t op_base; ++ uint32_t op_count; ++}; ++ ++static DEFINE_MUTEX(transaction_lock); ++ ++static const struct of_device_id rp1_firmware_of_match[] = { ++ { .compatible = "raspberrypi,rp1-firmware", }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, rp1_firmware_of_match); ++ ++static void response_callback(struct mbox_client *cl, void *msg) ++{ ++ struct rp1_firmware *fw = container_of(cl, struct rp1_firmware, cl); ++ ++ complete(&fw->c); ++} ++ ++/* ++ * Sends a request to the RP1 firmware and synchronously waits for the reply. ++ * Returns zero or a positive count of response bytes on success, negative on ++ * error. ++ */ ++ ++int rp1_firmware_message(struct rp1_firmware *fw, uint16_t op, ++ const void *data, unsigned int data_len, ++ void *resp, unsigned int resp_space) ++{ ++ int ret; ++ u32 rc; ++ ++ if (data_len + 4 > fw->buf_size) ++ return -EINVAL; ++ ++ mutex_lock(&transaction_lock); ++ ++ memcpy_toio(&fw->buf[1], data, data_len); ++ writel((op << 16) | data_len, fw->buf); ++ ++ reinit_completion(&fw->c); ++ ret = mbox_send_message(fw->chan, NULL); ++ if (ret >= 0) { ++ if (wait_for_completion_timeout(&fw->c, HZ)) ++ ret = 0; ++ else ++ ret = -ETIMEDOUT; ++ } else { ++ dev_err(fw->cl.dev, "mbox_send_message returned %d\n", ret); ++ } ++ ++ if (ret == 0) { ++ rc = readl(fw->buf); ++ if (rc & 0x80000000) { ++ ret = (int32_t)rc; ++ } else { ++ ret = min(rc, resp_space); ++ memcpy_fromio(resp, &fw->buf[1], ret); ++ } ++ } ++ ++ mutex_unlock(&transaction_lock); ++ ++ return ret; ++} ++EXPORT_SYMBOL_GPL(rp1_firmware_message); ++ ++static void rp1_firmware_delete(struct kref *kref) ++{ ++ struct rp1_firmware *fw = container_of(kref, struct rp1_firmware, consumers); ++ ++ mbox_free_channel(fw->chan); ++ kfree(fw); ++} ++ ++void rp1_firmware_put(struct rp1_firmware *fw) ++{ ++ if (!IS_ERR_OR_NULL(fw)) ++ kref_put(&fw->consumers, rp1_firmware_delete); ++} ++EXPORT_SYMBOL_GPL(rp1_firmware_put); ++ ++int rp1_firmware_get_feature(struct rp1_firmware *fw, uint32_t fourcc, ++ uint32_t *op_base, uint32_t *op_count) ++{ ++ struct rp1_get_feature_resp resp; ++ int ret; ++ ++ memset(&resp, 0, sizeof(resp)); ++ ret = rp1_firmware_message(fw, GET_FEATURE, ++ &fourcc, sizeof(fourcc), ++ &resp, sizeof(resp)); ++ *op_base = resp.op_base; ++ *op_count = resp.op_count; ++ if (ret < 0) ++ return ret; ++ if (ret < sizeof(resp) || !resp.op_base) ++ return -EOPNOTSUPP; ++ return 0; ++} ++EXPORT_SYMBOL_GPL(rp1_firmware_get_feature); ++ ++static void devm_rp1_firmware_put(void *data) ++{ ++ struct rp1_firmware *fw = data; ++ ++ rp1_firmware_put(fw); ++} ++ ++/** ++ * rp1_firmware_get - Get pointer to rp1_firmware structure. ++ * ++ * The reference to rp1_firmware has to be released with rp1_firmware_put(). ++ * ++ * Returns an error pointer on failure. ++ */ ++struct rp1_firmware *rp1_firmware_get(struct device_node *client) ++{ ++ const char *match = rp1_firmware_of_match[0].compatible; ++ struct platform_device *pdev; ++ struct device_node *fwnode; ++ struct rp1_firmware *fw = NULL; ++ ++ if (!client) ++ return NULL; ++ fwnode = of_parse_phandle(client, "firmware", 0); ++ if (!fwnode) ++ return NULL; ++ if (!of_device_is_compatible(fwnode, match)) { ++ of_node_put(fwnode); ++ return ERR_PTR(-ENXIO); ++ } ++ ++ pdev = of_find_device_by_node(fwnode); ++ of_node_put(fwnode); ++ ++ if (!pdev) ++ return ERR_PTR(-ENXIO); ++ ++ fw = platform_get_drvdata(pdev); ++ if (IS_ERR_OR_NULL(fw)) ++ goto err_exit; ++ ++ if (!kref_get_unless_zero(&fw->consumers)) ++ goto err_exit; ++ ++ put_device(&pdev->dev); ++ ++ return fw; ++ ++err_exit: ++ put_device(&pdev->dev); ++ return fw; ++} ++EXPORT_SYMBOL_GPL(rp1_firmware_get); ++ ++/** ++ * devm_rp1_firmware_get - Get pointer to rp1_firmware structure. ++ * @firmware_node: Pointer to the firmware Device Tree node. ++ * ++ * Returns NULL is the firmware device is not ready. ++ */ ++struct rp1_firmware *devm_rp1_firmware_get(struct device *dev, struct device_node *client) ++{ ++ struct rp1_firmware *fw; ++ int ret; ++ ++ fw = rp1_firmware_get(client); ++ if (IS_ERR_OR_NULL(fw)) ++ return fw; ++ ++ ret = devm_add_action_or_reset(dev, devm_rp1_firmware_put, fw); ++ if (ret) ++ return ERR_PTR(ret); ++ ++ return fw; ++} ++EXPORT_SYMBOL_GPL(devm_rp1_firmware_get); ++ ++static int rp1_firmware_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct device_node *shmem; ++ struct rp1_firmware *fw; ++ struct resource res; ++ uint32_t version[5]; ++ int ret; ++ ++ shmem = of_parse_phandle(dev->of_node, "shmem", 0); ++ if (!of_device_is_compatible(shmem, "raspberrypi,rp1-shmem")) { ++ of_node_put(shmem); ++ return -ENXIO; ++ } ++ ++ ret = of_address_to_resource(shmem, 0, &res); ++ of_node_put(shmem); ++ if (ret) { ++ dev_err(dev, "failed to get shared memory (%pOF) - %d\n", shmem, ret); ++ return ret; ++ } ++ ++ /* ++ * Memory will be freed by rp1_firmware_delete() once all users have ++ * released their firmware handles. Don't use devm_kzalloc() here. ++ */ ++ fw = kzalloc(sizeof(*fw), GFP_KERNEL); ++ if (!fw) ++ return -ENOMEM; ++ ++ fw->buf_size = resource_size(&res); ++ fw->buf = devm_ioremap(dev, res.start, fw->buf_size); ++ if (!fw->buf) { ++ dev_err(dev, "failed to ioremap shared memory\n"); ++ kfree(fw); ++ return -EADDRNOTAVAIL; ++ } ++ ++ fw->cl.dev = dev; ++ fw->cl.rx_callback = response_callback; ++ fw->cl.tx_block = false; ++ ++ fw->chan = mbox_request_channel(&fw->cl, RP1_MAILBOX_FIRMWARE); ++ if (IS_ERR(fw->chan)) { ++ int ret = PTR_ERR(fw->chan); ++ ++ if (ret != -EPROBE_DEFER) ++ dev_err(dev, "Failed to get mbox channel: %d\n", ret); ++ kfree(fw); ++ return ret; ++ } ++ ++ init_completion(&fw->c); ++ kref_init(&fw->consumers); ++ ++ ret = rp1_firmware_message(fw, GET_FIRMWARE_VERSION, ++ NULL, 0, &version, sizeof(version)); ++ if (ret == sizeof(version)) { ++ dev_info(dev, "RP1 Firmware version %08x%08x%08x%08x%08x\n", ++ version[0], version[1], version[2], version[3], version[4]); ++ platform_set_drvdata(pdev, fw); ++ } else { ++ rp1_firmware_put(fw); ++ platform_set_drvdata(pdev, ERR_PTR(-ENOENT)); ++ } ++ ++ return 0; ++} ++ ++static int rp1_firmware_remove(struct platform_device *pdev) ++{ ++ struct rp1_firmware *fw = platform_get_drvdata(pdev); ++ ++ rp1_firmware_put(fw); ++ ++ return 0; ++} ++ ++static struct platform_driver rp1_firmware_driver = { ++ .driver = { ++ .name = "rp1-firmware", ++ .of_match_table = rp1_firmware_of_match, ++ }, ++ .probe = rp1_firmware_probe, ++ .remove = rp1_firmware_remove, ++}; ++ ++module_platform_driver(rp1_firmware_driver); ++ ++MODULE_AUTHOR("Phil Elwell "); ++MODULE_DESCRIPTION("RP1 firmware driver"); ++MODULE_LICENSE("GPL v2"); diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 904b71c06eba..66499ab25646 100644 --- a/drivers/gpio/Kconfig @@ -74487,7 +75668,7 @@ index 000000000000..3b1ae097cd7b +MODULE_DESCRIPTION("brcmvirt GPIO driver"); +MODULE_ALIAS("platform:brcmvirt-gpio"); diff --git a/drivers/gpio/gpio-brcmstb.c b/drivers/gpio/gpio-brcmstb.c -index bccdbfd5ec80..5f7e93335dcb 100644 +index 4588666a52d5..1ad94409cfe4 100644 --- a/drivers/gpio/gpio-brcmstb.c +++ b/drivers/gpio/gpio-brcmstb.c @@ -50,7 +50,6 @@ struct brcmstb_gpio_priv { @@ -74536,8 +75717,8 @@ index bccdbfd5ec80..5f7e93335dcb 100644 if (offset >= gc->ngpio || offset < 0) return -EINVAL; -@@ -598,8 +598,8 @@ static int brcmstb_gpio_probe(struct platform_device *pdev) - const __be32 *p; +@@ -596,8 +596,8 @@ static int brcmstb_gpio_probe(struct platform_device *pdev) + struct resource *res; u32 bank_width; int num_banks = 0; + int num_gpios = 0; @@ -74546,7 +75727,7 @@ index bccdbfd5ec80..5f7e93335dcb 100644 unsigned long flags = 0; bool need_wakeup_event = false; -@@ -613,7 +613,6 @@ static int brcmstb_gpio_probe(struct platform_device *pdev) +@@ -611,7 +611,6 @@ static int brcmstb_gpio_probe(struct platform_device *pdev) if (IS_ERR(reg_base)) return PTR_ERR(reg_base); @@ -74554,16 +75735,18 @@ index bccdbfd5ec80..5f7e93335dcb 100644 priv->reg_base = reg_base; priv->pdev = pdev; -@@ -639,6 +638,8 @@ static int brcmstb_gpio_probe(struct platform_device *pdev) +@@ -637,7 +636,9 @@ static int brcmstb_gpio_probe(struct platform_device *pdev) #if defined(CONFIG_MIPS) && defined(__BIG_ENDIAN) flags = BGPIOF_BIG_ENDIAN_BYTE_ORDER; #endif +- + if (of_property_read_bool(np, "brcm,gpio-direct")) + flags |= BGPIOF_REG_DIRECT; - - of_property_for_each_u32(np, "brcm,gpio-bank-widths", prop, p, - bank_width) { -@@ -653,7 +654,7 @@ static int brcmstb_gpio_probe(struct platform_device *pdev) ++ + of_property_for_each_u32(np, "brcm,gpio-bank-widths", bank_width) { + struct brcmstb_gpio_bank *bank; + struct gpio_chip *gc; +@@ -650,7 +651,7 @@ static int brcmstb_gpio_probe(struct platform_device *pdev) dev_dbg(dev, "Width 0 found: Empty bank @ %d\n", num_banks); num_banks++; @@ -74572,7 +75755,7 @@ index bccdbfd5ec80..5f7e93335dcb 100644 continue; } -@@ -688,16 +689,18 @@ static int brcmstb_gpio_probe(struct platform_device *pdev) +@@ -685,16 +686,18 @@ static int brcmstb_gpio_probe(struct platform_device *pdev) } gc->owner = THIS_MODULE; @@ -74594,7 +75777,7 @@ index bccdbfd5ec80..5f7e93335dcb 100644 gc->offset = bank->id * MAX_GPIO_PER_BANK; if (priv->parent_irq > 0) gc->to_irq = brcmstb_gpio_to_irq; -@@ -706,8 +709,10 @@ static int brcmstb_gpio_probe(struct platform_device *pdev) +@@ -703,8 +706,10 @@ static int brcmstb_gpio_probe(struct platform_device *pdev) * Mask all interrupts by default, since wakeup interrupts may * be retained from S5 cold boot */ @@ -74607,7 +75790,7 @@ index bccdbfd5ec80..5f7e93335dcb 100644 err = gpiochip_add_data(gc, bank); if (err) { -@@ -715,7 +720,7 @@ static int brcmstb_gpio_probe(struct platform_device *pdev) +@@ -712,7 +717,7 @@ static int brcmstb_gpio_probe(struct platform_device *pdev) bank->id); goto fail; } @@ -74616,7 +75799,7 @@ index bccdbfd5ec80..5f7e93335dcb 100644 dev_dbg(dev, "bank=%d, base=%d, ngpio=%d, width=%d\n", bank->id, gc->base, gc->ngpio, bank->width); -@@ -726,7 +731,7 @@ static int brcmstb_gpio_probe(struct platform_device *pdev) +@@ -723,7 +728,7 @@ static int brcmstb_gpio_probe(struct platform_device *pdev) num_banks++; } @@ -76021,10 +77204,10 @@ index c9f9f4e36c89..25c08cc5cd20 100644 } else { if (flags & BGPIOF_NO_OUTPUT) diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c -index ce9a94e33280..11bdd678272d 100644 +index 9c33f9da724c..faeb5a6cfb15 100644 --- a/drivers/gpio/gpio-pca953x.c +++ b/drivers/gpio/gpio-pca953x.c -@@ -1347,6 +1347,7 @@ static const struct of_device_id pca953x_dt_ids[] = { +@@ -1330,6 +1330,7 @@ static const struct of_device_id pca953x_dt_ids[] = { { .compatible = "ti,tca6424", .data = OF_953X(24, PCA_INT), }, { .compatible = "ti,tca9538", .data = OF_953X( 8, PCA_INT), }, { .compatible = "ti,tca9539", .data = OF_953X(16, PCA_INT), }, @@ -76183,7 +77366,7 @@ index 000000000000..1dbdf8717eb3 +MODULE_AUTHOR("Dave Stevenson "); +MODULE_DESCRIPTION("PWM GPIO driver"); diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c -index 5c0016c77d2a..3710585e7da4 100644 +index efb592b6f6aa..d1881980a123 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -58,6 +58,8 @@ @@ -76288,7 +77471,7 @@ index 3e6a4e2044c0..b1c5ef817598 100644 Driver for display connectors with support for DDC and hot-plug detection. Most display controllers handle display connectors diff --git a/drivers/gpu/drm/bridge/panel.c b/drivers/gpu/drm/bridge/panel.c -index a1dd2ead8dcc..307b6bd6a6d8 100644 +index a1dd2ead8dcc..b079ffa8129e 100644 --- a/drivers/gpu/drm/bridge/panel.c +++ b/drivers/gpu/drm/bridge/panel.c @@ -14,6 +14,7 @@ @@ -76299,7 +77482,14 @@ index a1dd2ead8dcc..307b6bd6a6d8 100644 struct panel_bridge { struct drm_bridge bridge; -@@ -86,6 +87,11 @@ static int panel_bridge_attach(struct drm_bridge *bridge, +@@ -81,11 +82,18 @@ static int panel_bridge_attach(struct drm_bridge *bridge, + return ret; + } + ++ connector->interlace_allowed = true; ++ + drm_panel_bridge_set_orientation(connector, bridge); + drm_connector_attach_encoder(&panel_bridge->connector, bridge->encoder); @@ -76376,6 +77566,19 @@ index 46198af9eebb..3f6db0f66812 100644 } return ret; +diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c +index c277b198fa3f..c472c663bc13 100644 +--- a/drivers/gpu/drm/drm_atomic.c ++++ b/drivers/gpu/drm/drm_atomic.c +@@ -451,7 +451,7 @@ static void drm_atomic_crtc_print_state(struct drm_printer *p, + drm_printf(p, "\tactive_changed=%d\n", state->active_changed); + drm_printf(p, "\tconnectors_changed=%d\n", state->connectors_changed); + drm_printf(p, "\tcolor_mgmt_changed=%d\n", state->color_mgmt_changed); +- drm_printf(p, "\tplane_mask=%x\n", state->plane_mask); ++ drm_printf(p, "\tplane_mask=%llx\n", state->plane_mask); + drm_printf(p, "\tconnector_mask=%x\n", state->connector_mask); + drm_printf(p, "\tencoder_mask=%x\n", state->encoder_mask); + drm_printf(p, "\tmode: " DRM_MODE_FMT "\n", DRM_MODE_ARG(&state->mode)); diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index 554d4468aa7c..d759c5ebd410 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c @@ -76445,7 +77648,7 @@ index 784e63d70a42..d7c761d9fb72 100644 if (!drm_object_property_get_default_value(&plane->base, plane->zpos_property, diff --git a/drivers/gpu/drm/drm_atomic_uapi.c b/drivers/gpu/drm/drm_atomic_uapi.c -index ab03b08433f8..3c5375016d73 100644 +index ab03b08433f8..f60a6bcb46ac 100644 --- a/drivers/gpu/drm/drm_atomic_uapi.c +++ b/drivers/gpu/drm/drm_atomic_uapi.c @@ -580,6 +580,10 @@ static int drm_atomic_plane_set_property(struct drm_plane *plane, @@ -76495,7 +77698,22 @@ index ab03b08433f8..3c5375016d73 100644 } else if (property == config->legacy_tv_mode_property) { state->tv.legacy_mode = val; } else if (property == config->tv_mode_property) { -@@ -809,6 +822,12 @@ static int drm_atomic_connector_set_property(struct drm_connector *connector, +@@ -798,6 +811,14 @@ static int drm_atomic_connector_set_property(struct drm_connector *connector, + state->max_requested_bpc = val; + } else if (property == connector->privacy_screen_sw_state_property) { + state->privacy_screen_sw_state = val; ++ } else if (property == connector->rotation_property) { ++ if (!is_power_of_2(val & DRM_MODE_ROTATE_MASK)) { ++ drm_dbg_atomic(connector->dev, ++ "[CONNECTOR:%d:%s] bad rotation bitmask: 0x%llx\n", ++ connector->base.id, connector->name, val); ++ return -EINVAL; ++ } ++ state->rotation = val; + } else if (connector->funcs->atomic_set_property) { + return connector->funcs->atomic_set_property(connector, + state, property, val); +@@ -809,6 +830,12 @@ static int drm_atomic_connector_set_property(struct drm_connector *connector, return -EINVAL; } @@ -76508,6 +77726,105 @@ index ab03b08433f8..3c5375016d73 100644 return 0; } +@@ -881,6 +908,8 @@ drm_atomic_connector_get_property(struct drm_connector *connector, + *val = state->max_requested_bpc; + } else if (property == connector->privacy_screen_sw_state_property) { + *val = state->privacy_screen_sw_state; ++ } else if (property == connector->rotation_property) { ++ *val = state->rotation; + } else if (connector->funcs->atomic_get_property) { + return connector->funcs->atomic_get_property(connector, + state, property, val); +diff --git a/drivers/gpu/drm/drm_blend.c b/drivers/gpu/drm/drm_blend.c +index 6e74de833466..4e63000713db 100644 +--- a/drivers/gpu/drm/drm_blend.c ++++ b/drivers/gpu/drm/drm_blend.c +@@ -235,6 +235,16 @@ int drm_plane_create_alpha_property(struct drm_plane *plane) + } + EXPORT_SYMBOL(drm_plane_create_alpha_property); + ++static const struct drm_prop_enum_list drm_rotate_props[] = { ++ { __builtin_ffs(DRM_MODE_ROTATE_0) - 1, "rotate-0" }, ++ { __builtin_ffs(DRM_MODE_ROTATE_90) - 1, "rotate-90" }, ++ { __builtin_ffs(DRM_MODE_ROTATE_180) - 1, "rotate-180" }, ++ { __builtin_ffs(DRM_MODE_ROTATE_270) - 1, "rotate-270" }, ++ { __builtin_ffs(DRM_MODE_REFLECT_X) - 1, "reflect-x" }, ++ { __builtin_ffs(DRM_MODE_REFLECT_Y) - 1, "reflect-y" }, ++ { __builtin_ffs(DRM_MODE_TRANSPOSE) - 1, "transpose" }, ++}; ++ + /** + * drm_plane_create_rotation_property - create a new rotation property + * @plane: drm plane +@@ -263,6 +273,8 @@ EXPORT_SYMBOL(drm_plane_create_alpha_property); + * "reflect-x" + * DRM_MODE_REFLECT_Y: + * "reflect-y" ++ * DRM_MODE_TRANSPOSE: ++ * "transpose" + * + * Rotation is the specified amount in degrees in counter clockwise direction, + * the X and Y axis are within the source rectangle, i.e. the X/Y axis before +@@ -273,14 +285,6 @@ int drm_plane_create_rotation_property(struct drm_plane *plane, + unsigned int rotation, + unsigned int supported_rotations) + { +- static const struct drm_prop_enum_list props[] = { +- { __builtin_ffs(DRM_MODE_ROTATE_0) - 1, "rotate-0" }, +- { __builtin_ffs(DRM_MODE_ROTATE_90) - 1, "rotate-90" }, +- { __builtin_ffs(DRM_MODE_ROTATE_180) - 1, "rotate-180" }, +- { __builtin_ffs(DRM_MODE_ROTATE_270) - 1, "rotate-270" }, +- { __builtin_ffs(DRM_MODE_REFLECT_X) - 1, "reflect-x" }, +- { __builtin_ffs(DRM_MODE_REFLECT_Y) - 1, "reflect-y" }, +- }; + struct drm_property *prop; + + WARN_ON((supported_rotations & DRM_MODE_ROTATE_MASK) == 0); +@@ -288,7 +292,8 @@ int drm_plane_create_rotation_property(struct drm_plane *plane, + WARN_ON(rotation & ~supported_rotations); + + prop = drm_property_create_bitmask(plane->dev, 0, "rotation", +- props, ARRAY_SIZE(props), ++ drm_rotate_props, ++ ARRAY_SIZE(drm_rotate_props), + supported_rotations); + if (!prop) + return -ENOMEM; +@@ -304,6 +309,34 @@ int drm_plane_create_rotation_property(struct drm_plane *plane, + } + EXPORT_SYMBOL(drm_plane_create_rotation_property); + ++int drm_connector_create_rotation_property(struct drm_connector *conn, ++ unsigned int rotation, ++ unsigned int supported_rotations) ++{ ++ struct drm_property *prop; ++ ++ WARN_ON((supported_rotations & DRM_MODE_ROTATE_MASK) == 0); ++ WARN_ON(!is_power_of_2(rotation & DRM_MODE_ROTATE_MASK)); ++ WARN_ON(rotation & ~supported_rotations); ++ ++ prop = drm_property_create_bitmask(conn->dev, 0, "rotation", ++ drm_rotate_props, ++ ARRAY_SIZE(drm_rotate_props), ++ supported_rotations); ++ if (!prop) ++ return -ENOMEM; ++ ++ drm_object_attach_property(&conn->base, prop, rotation); ++ ++ if (conn->state) ++ conn->state->rotation = rotation; ++ ++ conn->rotation_property = prop; ++ ++ return 0; ++} ++EXPORT_SYMBOL(drm_connector_create_rotation_property); ++ + /** + * drm_rotation_simplify() - Try to simplify the rotation + * @rotation: Rotation to be simplified diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c index 62d8a291c49c..2ae6ac3a4b7a 100644 --- a/drivers/gpu/drm/drm_bridge.c @@ -76599,7 +77916,7 @@ index d021497841b8..973c6aeff8a1 100644 * drm_color_lut_check - check validity of lookup table * @lut: property blob containing LUT to check diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c -index 309aad5f0c80..476ea750acf4 100644 +index 309aad5f0c80..09cd1df2d6c4 100644 --- a/drivers/gpu/drm/drm_connector.c +++ b/drivers/gpu/drm/drm_connector.c @@ -33,6 +33,7 @@ @@ -76703,7 +78020,17 @@ index 309aad5f0c80..476ea750acf4 100644 if (connector->connector_type_id < 0) { ret = connector->connector_type_id; goto out_put_id; -@@ -996,6 +1049,7 @@ static const struct drm_prop_enum_list drm_tv_mode_enum_list[] = { +@@ -308,7 +361,8 @@ static int __drm_connector_init(struct drm_device *dev, + + drm_object_attach_property(&connector->base, + config->non_desktop_property, +- 0); ++ (connector_type != DRM_MODE_CONNECTOR_VIRTUAL && ++ connector_type != DRM_MODE_CONNECTOR_WRITEBACK) ? 0 : 1); + drm_object_attach_property(&connector->base, + config->tile_property, + 0); +@@ -996,6 +1050,7 @@ static const struct drm_prop_enum_list drm_tv_mode_enum_list[] = { { DRM_MODE_TV_MODE_PAL_M, "PAL-M" }, { DRM_MODE_TV_MODE_PAL_N, "PAL-N" }, { DRM_MODE_TV_MODE_SECAM, "SECAM" }, @@ -76711,7 +78038,7 @@ index 309aad5f0c80..476ea750acf4 100644 }; DRM_ENUM_NAME_FN(drm_get_tv_mode_name, drm_tv_mode_enum_list) -@@ -1682,6 +1736,12 @@ EXPORT_SYMBOL(drm_connector_attach_dp_subconnector_property); +@@ -1682,6 +1737,12 @@ EXPORT_SYMBOL(drm_connector_attach_dp_subconnector_property); * TV Mode is CCIR System B (aka 625-lines) together with * the SECAM Color Encoding. * @@ -76724,7 +78051,7 @@ index 309aad5f0c80..476ea750acf4 100644 * Drivers can set up this property by calling * drm_mode_create_tv_properties(). */ -@@ -2637,10 +2697,15 @@ int drm_connector_set_orientation_from_panel( +@@ -2637,10 +2698,15 @@ int drm_connector_set_orientation_from_panel( { enum drm_panel_orientation orientation; @@ -76771,8 +78098,34 @@ index 618b04523033..e6698f3a5b73 100644 ret = register_framebuffer(info); if (ret < 0) return ret; +diff --git a/drivers/gpu/drm/drm_framebuffer.c b/drivers/gpu/drm/drm_framebuffer.c +index 1955eaeba0ab..8e53a46b5708 100644 +--- a/drivers/gpu/drm/drm_framebuffer.c ++++ b/drivers/gpu/drm/drm_framebuffer.c +@@ -959,7 +959,7 @@ static int atomic_remove_fb(struct drm_framebuffer *fb) + struct drm_connector *conn __maybe_unused; + struct drm_connector_state *conn_state; + int i, ret; +- unsigned plane_mask; ++ u64 plane_mask; + bool disable_crtcs = false; + + retry_disable: +diff --git a/drivers/gpu/drm/drm_mode_config.c b/drivers/gpu/drm/drm_mode_config.c +index 87eb591fe9b5..02d1f7afbbe2 100644 +--- a/drivers/gpu/drm/drm_mode_config.c ++++ b/drivers/gpu/drm/drm_mode_config.c +@@ -636,7 +636,7 @@ void drm_mode_config_validate(struct drm_device *dev) + struct drm_encoder *encoder; + struct drm_crtc *crtc; + struct drm_plane *plane; +- u32 primary_with_crtc = 0, cursor_with_crtc = 0; ++ u64 primary_with_crtc = 0, cursor_with_crtc = 0; + unsigned int num_primary = 0; + + if (!drm_core_check_feature(dev, DRIVER_MODESET)) diff --git a/drivers/gpu/drm/drm_modes.c b/drivers/gpu/drm/drm_modes.c -index ac9a406250c5..5a6d966f7ada 100644 +index 9325b015c6c0..808a599787c4 100644 --- a/drivers/gpu/drm/drm_modes.c +++ b/drivers/gpu/drm/drm_modes.c @@ -530,7 +530,8 @@ static int fill_analog_mode(struct drm_device *dev, @@ -76794,6 +78147,19 @@ index ac9a406250c5..5a6d966f7ada 100644 analog = DRM_MODE_ANALOG_PAL; break; +diff --git a/drivers/gpu/drm/drm_plane.c b/drivers/gpu/drm/drm_plane.c +index 311e179904a2..38901eb3a5ad 100644 +--- a/drivers/gpu/drm/drm_plane.c ++++ b/drivers/gpu/drm/drm_plane.c +@@ -249,7 +249,7 @@ static int __drm_universal_plane_init(struct drm_device *dev, + int ret; + + /* plane index is used with 32bit bitmasks */ +- if (WARN_ON(config->num_total_plane >= 32)) ++ if (WARN_ON(config->num_total_plane >= 64)) + return -EINVAL; + + /* diff --git a/drivers/gpu/drm/drm_probe_helper.c b/drivers/gpu/drm/drm_probe_helper.c index c90afb5d0898..49816a7353bc 100644 --- a/drivers/gpu/drm/drm_probe_helper.c @@ -76834,6 +78200,19 @@ index 1a59fca40252..0e941ce3d5a4 100644 ret = intel_atomic_prepare_commit(state); if (ret) { drm_dbg_atomic(&dev_priv->drm, +diff --git a/drivers/gpu/drm/imx/ipuv3/ipuv3-crtc.c b/drivers/gpu/drm/imx/ipuv3/ipuv3-crtc.c +index 5f423a2e0ede..4365db021cee 100644 +--- a/drivers/gpu/drm/imx/ipuv3/ipuv3-crtc.c ++++ b/drivers/gpu/drm/imx/ipuv3/ipuv3-crtc.c +@@ -230,7 +230,7 @@ static int ipu_crtc_atomic_check(struct drm_crtc *crtc, + { + struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, + crtc); +- u32 primary_plane_mask = drm_plane_mask(crtc->primary); ++ u64 primary_plane_mask = drm_plane_mask(crtc->primary); + + if (crtc_state->active && (primary_plane_mask & crtc_state->plane_mask) == 0) + return -EINVAL; diff --git a/drivers/gpu/drm/msm/msm_atomic.c b/drivers/gpu/drm/msm/msm_atomic.c index 9c45d641b521..5c8e5611304f 100644 --- a/drivers/gpu/drm/msm/msm_atomic.c @@ -78663,7 +80042,7 @@ index 4618c892cdd6..4c418962aa9b 100644 /* Look up the DSI host. It needs to probe before we do. */ endpoint = of_graph_get_next_endpoint(dev->of_node, NULL); diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c -index 11ade6bac592..36cda6335369 100644 +index 37fe54c34b14..ff2d168e4b15 100644 --- a/drivers/gpu/drm/panel/panel-simple.c +++ b/drivers/gpu/drm/panel/panel-simple.c @@ -40,6 +40,7 @@ @@ -78851,7 +80230,7 @@ index 11ade6bac592..36cda6335369 100644 static const struct display_timing rocktech_rk070er9427_timing = { .pixelclock = { 26400000, 33300000, 46800000 }, .hactive = { 800, 800, 800 }, -@@ -4273,6 +4338,9 @@ static const struct of_device_id platform_of_match[] = { +@@ -4298,6 +4363,9 @@ static const struct of_device_id platform_of_match[] = { }, { .compatible = "friendlyarm,hd702e", .data = &friendlyarm_hd702e, @@ -78861,7 +80240,7 @@ index 11ade6bac592..36cda6335369 100644 }, { .compatible = "giantplus,gpg482739qs5", .data = &giantplus_gpg482739qs5 -@@ -4294,6 +4362,9 @@ static const struct of_device_id platform_of_match[] = { +@@ -4319,6 +4387,9 @@ static const struct of_device_id platform_of_match[] = { }, { .compatible = "innolux,at043tn24", .data = &innolux_at043tn24, @@ -78871,7 +80250,7 @@ index 11ade6bac592..36cda6335369 100644 }, { .compatible = "innolux,at070tn92", .data = &innolux_at070tn92, -@@ -4426,6 +4497,9 @@ static const struct of_device_id platform_of_match[] = { +@@ -4451,6 +4522,9 @@ static const struct of_device_id platform_of_match[] = { }, { .compatible = "rocktech,rk043fn48h", .data = &rocktech_rk043fn48h, @@ -78881,7 +80260,7 @@ index 11ade6bac592..36cda6335369 100644 }, { .compatible = "rocktech,rk070er9427", .data = &rocktech_rk070er9427, -@@ -4780,6 +4854,9 @@ static const struct panel_desc_dsi osd101t2045_53ts = { +@@ -4808,6 +4882,9 @@ static const struct panel_desc_dsi osd101t2045_53ts = { .lanes = 4, }; @@ -78891,7 +80270,7 @@ index 11ade6bac592..36cda6335369 100644 static const struct of_device_id dsi_of_match[] = { { .compatible = "auo,b080uan01", -@@ -4802,21 +4879,138 @@ static const struct of_device_id dsi_of_match[] = { +@@ -4830,21 +4907,138 @@ static const struct of_device_id dsi_of_match[] = { }, { .compatible = "osddisplays,osd101t2045-53ts", .data = &osd101t2045_53ts @@ -79912,10 +81291,10 @@ index 000000000000..28c2a33691fd +MODULE_LICENSE("GPL v2"); diff --git a/drivers/gpu/drm/panel/panel-waveshare-dsi.c b/drivers/gpu/drm/panel/panel-waveshare-dsi.c new file mode 100644 -index 000000000000..2cca27e4ecc8 +index 000000000000..4041d31c71a2 --- /dev/null +++ b/drivers/gpu/drm/panel/panel-waveshare-dsi.c -@@ -0,0 +1,488 @@ +@@ -0,0 +1,609 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright © 2023 Raspberry Pi Ltd @@ -79950,6 +81329,12 @@ index 000000000000..2cca27e4ecc8 + enum drm_panel_orientation orientation; +}; + ++struct ws_panel_data { ++ const struct drm_display_mode *mode; ++ int lanes; ++ unsigned long mode_flags; ++}; ++ +/* 2.8inch 480x640 + * https://www.waveshare.com/product/raspberry-pi/displays/2.8inch-dsi-lcd.htm + */ @@ -79965,6 +81350,12 @@ index 000000000000..2cca27e4ecc8 + .vtotal = 640 + 150 + 50 + 150, +}; + ++static const struct ws_panel_data ws_panel_2_8_data = { ++ .mode = &ws_panel_2_8_mode, ++ .lanes = 2, ++ .mode_flags = MIPI_DSI_MODE_VIDEO_HSE | MIPI_DSI_MODE_VIDEO | MIPI_DSI_CLOCK_NON_CONTINUOUS, ++}; ++ +/* 3.4inch 800x800 Round + * https://www.waveshare.com/product/displays/lcd-oled/3.4inch-dsi-lcd-c.htm + */ @@ -79980,6 +81371,12 @@ index 000000000000..2cca27e4ecc8 + .vtotal = 800 + 8 + 4 + 16, +}; + ++static const struct ws_panel_data ws_panel_3_4_data = { ++ .mode = &ws_panel_3_4_mode, ++ .lanes = 2, ++ .mode_flags = MIPI_DSI_MODE_VIDEO_HSE | MIPI_DSI_MODE_VIDEO | MIPI_DSI_CLOCK_NON_CONTINUOUS, ++}; ++ +/* 4.0inch 480x800 + * https://www.waveshare.com/product/raspberry-pi/displays/4inch-dsi-lcd.htm + */ @@ -79995,6 +81392,12 @@ index 000000000000..2cca27e4ecc8 + .vtotal = 800 + 20 + 100 + 20, +}; + ++static const struct ws_panel_data ws_panel_4_0_data = { ++ .mode = &ws_panel_4_0_mode, ++ .lanes = 2, ++ .mode_flags = MIPI_DSI_MODE_VIDEO_HSE | MIPI_DSI_MODE_VIDEO | MIPI_DSI_CLOCK_NON_CONTINUOUS, ++}; ++ +/* 7.0inch C 1024x600 + * https://www.waveshare.com/product/raspberry-pi/displays/lcd-oled/7inch-dsi-lcd-c-with-case-a.htm + */ @@ -80010,6 +81413,12 @@ index 000000000000..2cca27e4ecc8 + .vtotal = 600 + 10 + 10 + 10, +}; + ++static const struct ws_panel_data ws_panel_7_0_c_data = { ++ .mode = &ws_panel_7_0_c_mode, ++ .lanes = 2, ++ .mode_flags = MIPI_DSI_MODE_VIDEO_HSE | MIPI_DSI_MODE_VIDEO | MIPI_DSI_CLOCK_NON_CONTINUOUS, ++}; ++ +/* 7.9inch 400x1280 + * https://www.waveshare.com/product/raspberry-pi/displays/7.9inch-dsi-lcd.htm + */ @@ -80025,6 +81434,12 @@ index 000000000000..2cca27e4ecc8 + .vtotal = 1280 + 20 + 10 + 20, +}; + ++static const struct ws_panel_data ws_panel_7_9_data = { ++ .mode = &ws_panel_7_9_mode, ++ .lanes = 2, ++ .mode_flags = MIPI_DSI_MODE_VIDEO_HSE | MIPI_DSI_MODE_VIDEO | MIPI_DSI_CLOCK_NON_CONTINUOUS, ++}; ++ +/* 8.0inch or 10.1inch 1280x800 + * https://www.waveshare.com/product/raspberry-pi/displays/8inch-dsi-lcd-c.htm + * https://www.waveshare.com/product/raspberry-pi/displays/10.1inch-dsi-lcd-c.htm @@ -80041,6 +81456,12 @@ index 000000000000..2cca27e4ecc8 + .vtotal = 800 + 40 + 48 + 40, +}; + ++static const struct ws_panel_data ws_panel_10_1_data = { ++ .mode = &ws_panel_10_1_mode, ++ .lanes = 2, ++ .mode_flags = MIPI_DSI_MODE_VIDEO_HSE | MIPI_DSI_MODE_VIDEO | MIPI_DSI_CLOCK_NON_CONTINUOUS, ++}; ++ +/* 11.9inch 320x1480 + * https://www.waveshare.com/product/raspberry-pi/displays/11.9inch-dsi-lcd.htm + */ @@ -80056,6 +81477,12 @@ index 000000000000..2cca27e4ecc8 + .vtotal = 1480 + 60 + 60 + 60, +}; + ++static const struct ws_panel_data ws_panel_11_9_data = { ++ .mode = &ws_panel_11_9_mode, ++ .lanes = 2, ++ .mode_flags = MIPI_DSI_MODE_VIDEO_HSE | MIPI_DSI_MODE_VIDEO | MIPI_DSI_CLOCK_NON_CONTINUOUS, ++}; ++ +static const struct drm_display_mode ws_panel_4_mode = { + .clock = 50000, + .hdisplay = 720, @@ -80068,6 +81495,12 @@ index 000000000000..2cca27e4ecc8 + .vtotal = 720 + 8 + 4 + 16, +}; + ++static const struct ws_panel_data ws_panel_4_data = { ++ .mode = &ws_panel_4_mode, ++ .lanes = 2, ++ .mode_flags = MIPI_DSI_MODE_VIDEO_HSE | MIPI_DSI_MODE_VIDEO | MIPI_DSI_CLOCK_NON_CONTINUOUS, ++}; ++ +/* 5.0inch 720x1280 + * https://www.waveshare.com/5inch-dsi-lcd-d.htm + */ @@ -80083,6 +81516,12 @@ index 000000000000..2cca27e4ecc8 + .vtotal = 1280 + 20 + 20 + 20, +}; + ++static const struct ws_panel_data ws_panel_5_0_data = { ++ .mode = &ws_panel_5_0_mode, ++ .lanes = 2, ++ .mode_flags = MIPI_DSI_MODE_VIDEO_HSE | MIPI_DSI_MODE_VIDEO | MIPI_DSI_CLOCK_NON_CONTINUOUS, ++}; ++ +/* 6.25inch 720x1560 + * https://www.waveshare.com/6.25inch-dsi-lcd.htm + */ @@ -80098,6 +81537,12 @@ index 000000000000..2cca27e4ecc8 + .vtotal = 1560 + 20 + 20 + 20, +}; + ++static const struct ws_panel_data ws_panel_6_25_data = { ++ .mode = &ws_panel_6_25_mode, ++ .lanes = 2, ++ .mode_flags = MIPI_DSI_MODE_VIDEO_HSE | MIPI_DSI_MODE_VIDEO | MIPI_DSI_CLOCK_NON_CONTINUOUS, ++}; ++ +/* 8.8inch 480x1920 + * https://www.waveshare.com/8.8inch-dsi-lcd.htm + */ @@ -80113,6 +81558,48 @@ index 000000000000..2cca27e4ecc8 + .vtotal = 1920 + 20 + 20 + 20, +}; + ++static const struct ws_panel_data ws_panel_8_8_data = { ++ .mode = &ws_panel_8_8_mode, ++ .lanes = 2, ++ .mode_flags = MIPI_DSI_MODE_VIDEO_HSE | MIPI_DSI_MODE_VIDEO | MIPI_DSI_CLOCK_NON_CONTINUOUS, ++}; ++ ++static const struct drm_display_mode ws_panel_13_3_4lane_mode = { ++ .clock = 148500, ++ .hdisplay = 1920, ++ .hsync_start = 1920 + 88, ++ .hsync_end = 1920 + 88 + 44, ++ .htotal = 1920 + 88 + 44 + 148, ++ .vdisplay = 1080, ++ .vsync_start = 1080 + 4, ++ .vsync_end = 1080 + 4 + 5, ++ .vtotal = 1080 + 4 + 5 + 36, ++}; ++ ++static const struct ws_panel_data ws_panel_13_3_4lane_data = { ++ .mode = &ws_panel_13_3_4lane_mode, ++ .lanes = 4, ++ .mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_LPM, ++}; ++ ++static const struct drm_display_mode ws_panel_13_3_2lane_mode = { ++ .clock = 83333, ++ .hdisplay = 1920, ++ .hsync_start = 1920 + 88, ++ .hsync_end = 1920 + 88 + 44, ++ .htotal = 1920 + 88 + 44 + 148, ++ .vdisplay = 1080, ++ .vsync_start = 1080 + 4, ++ .vsync_end = 1080 + 4 + 5, ++ .vtotal = 1080 + 4 + 5 + 36, ++}; ++ ++static const struct ws_panel_data ws_panel_13_3_2lane_data = { ++ .mode = &ws_panel_13_3_2lane_mode, ++ .lanes = 2, ++ .mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_LPM, ++}; ++ +static struct ws_panel *panel_to_ts(struct drm_panel *panel) +{ + return container_of(panel, struct ws_panel, base); @@ -80150,7 +81637,10 @@ index 000000000000..2cca27e4ecc8 +{ + struct ws_panel *ts = panel_to_ts(panel); + -+ ws_panel_i2c_write(ts, 0xad, 0x01); ++ if (ts->mode == &ws_panel_13_3_2lane_mode) ++ ws_panel_i2c_write(ts, 0xad, 0x02); ++ else ++ ws_panel_i2c_write(ts, 0xad, 0x01); + + return 0; +} @@ -80246,13 +81736,18 @@ index 000000000000..2cca27e4ecc8 + .channel = 0, + .node = NULL, + }; ++ const struct ws_panel_data *_ws_panel_data; + int ret; + + ts = devm_kzalloc(dev, sizeof(*ts), GFP_KERNEL); + if (!ts) + return -ENOMEM; + -+ ts->mode = of_device_get_match_data(dev); ++ _ws_panel_data = of_device_get_match_data(dev); ++ if (!_ws_panel_data) ++ return -EINVAL; ++ ++ ts->mode = _ws_panel_data->mode; + if (!ts->mode) + return -EINVAL; + @@ -80314,10 +81809,9 @@ index 000000000000..2cca27e4ecc8 + */ + drm_panel_add(&ts->base); + -+ ts->dsi->mode_flags = MIPI_DSI_MODE_VIDEO_HSE | MIPI_DSI_MODE_VIDEO | -+ MIPI_DSI_CLOCK_NON_CONTINUOUS; ++ ts->dsi->mode_flags = _ws_panel_data->mode_flags; + ts->dsi->format = MIPI_DSI_FMT_RGB888; -+ ts->dsi->lanes = 2; ++ ts->dsi->lanes = _ws_panel_data->lanes; + + ret = devm_mipi_dsi_attach(dev, ts->dsi); + @@ -80350,40 +81844,46 @@ index 000000000000..2cca27e4ecc8 +static const struct of_device_id ws_panel_of_ids[] = { + { + .compatible = "waveshare,2.8inch-panel", -+ .data = &ws_panel_2_8_mode, ++ .data = &ws_panel_2_8_data, + }, { + .compatible = "waveshare,3.4inch-panel", -+ .data = &ws_panel_3_4_mode, ++ .data = &ws_panel_3_4_data, + }, { + .compatible = "waveshare,4.0inch-panel", -+ .data = &ws_panel_4_0_mode, ++ .data = &ws_panel_4_0_data, + }, { + .compatible = "waveshare,7.0inch-c-panel", -+ .data = &ws_panel_7_0_c_mode, ++ .data = &ws_panel_7_0_c_data, + }, { + .compatible = "waveshare,7.9inch-panel", -+ .data = &ws_panel_7_9_mode, ++ .data = &ws_panel_7_9_data, + }, { + .compatible = "waveshare,8.0inch-panel", -+ .data = &ws_panel_10_1_mode, ++ .data = &ws_panel_10_1_data, + }, { + .compatible = "waveshare,10.1inch-panel", -+ .data = &ws_panel_10_1_mode, ++ .data = &ws_panel_10_1_data, + }, { + .compatible = "waveshare,11.9inch-panel", -+ .data = &ws_panel_11_9_mode, ++ .data = &ws_panel_11_9_data, + }, { + .compatible = "waveshare,4inch-panel", -+ .data = &ws_panel_4_mode, ++ .data = &ws_panel_4_data, + }, { + .compatible = "waveshare,5.0inch-panel", -+ .data = &ws_panel_5_0_mode, ++ .data = &ws_panel_5_0_data, + }, { + .compatible = "waveshare,6.25inch-panel", -+ .data = &ws_panel_6_25_mode, ++ .data = &ws_panel_6_25_data, + }, { + .compatible = "waveshare,8.8inch-panel", -+ .data = &ws_panel_8_8_mode, ++ .data = &ws_panel_8_8_data, ++ }, { ++ .compatible = "waveshare,13.3inch-4lane-panel", ++ .data = &ws_panel_13_3_4lane_data, ++ }, { ++ .compatible = "waveshare,13.3inch-2lane-panel", ++ .data = &ws_panel_13_3_2lane_data, + }, { + /* sentinel */ + } @@ -80427,10 +81927,10 @@ index 000000000000..0f915b158e96 + diff --git a/drivers/gpu/drm/rp1/rp1-dpi/Kconfig b/drivers/gpu/drm/rp1/rp1-dpi/Kconfig new file mode 100644 -index 000000000000..72c72709a5ce +index 000000000000..95d17902094d --- /dev/null +++ b/drivers/gpu/drm/rp1/rp1-dpi/Kconfig -@@ -0,0 +1,11 @@ +@@ -0,0 +1,16 @@ +# SPDX-License-Identifier: GPL-2.0-only +config DRM_RP1_DPI + tristate "DRM Support for RP1 DPI" @@ -80440,25 +81940,30 @@ index 000000000000..72c72709a5ce + select DRM_VRAM_HELPER + select DRM_TTM + select DRM_TTM_HELPER ++ depends on RP1_PIO || !RP1_PIO + help -+ Choose this option to enable Video Out on RP1 ++ Choose this option to enable DPI output on Raspberry Pi RP1 ++ ++ There is an optional dependency on RP1_PIO, as the PIO block ++ must be used to fix up interlaced sync. Interlaced DPI modes ++ will be unavailable when RP1_PIO is not selected. diff --git a/drivers/gpu/drm/rp1/rp1-dpi/Makefile b/drivers/gpu/drm/rp1/rp1-dpi/Makefile new file mode 100644 -index 000000000000..79fdc7903167 +index 000000000000..30d499c2959e --- /dev/null +++ b/drivers/gpu/drm/rp1/rp1-dpi/Makefile @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0-only + -+drm-rp1-dpi-y := rp1_dpi.o rp1_dpi_hw.o rp1_dpi_cfg.o ++drm-rp1-dpi-y := rp1_dpi.o rp1_dpi_hw.o rp1_dpi_cfg.o rp1_dpi_pio.o + +obj-$(CONFIG_DRM_RP1_DPI) += drm-rp1-dpi.o diff --git a/drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi.c b/drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi.c new file mode 100644 -index 000000000000..c97114a89165 +index 000000000000..5a4ae4abae42 --- /dev/null +++ b/drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi.c -@@ -0,0 +1,417 @@ +@@ -0,0 +1,480 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * DRM Driver for DPI output on Raspberry Pi RP1 @@ -80541,6 +82046,7 @@ index 000000000000..c97114a89165 + if (dpi->dpi_running && + fb->format->format != dpi->cur_fmt) { + rp1dpi_hw_stop(dpi); ++ rp1dpi_pio_stop(dpi); + dpi->dpi_running = false; + } + if (!dpi->dpi_running) { @@ -80549,6 +82055,7 @@ index 000000000000..c97114a89165 + dpi->bus_fmt, + dpi->de_inv, + &pipe->crtc.state->mode); ++ rp1dpi_pio_start(dpi, &pipe->crtc.state->mode); + dpi->dpi_running = true; + } + dpi->cur_fmt = fb->format->format; @@ -80648,6 +82155,7 @@ index 000000000000..c97114a89165 + drm_crtc_vblank_off(&pipe->crtc); + if (dpi->dpi_running) { + rp1dpi_hw_stop(dpi); ++ rp1dpi_pio_stop(dpi); + dpi->dpi_running = false; + } + clk_disable_unprepare(dpi->clocks[RP1DPI_CLK_DPI]); @@ -80675,12 +82183,28 @@ index 000000000000..c97114a89165 + rp1dpi_hw_vblank_ctrl(dpi, 0); +} + ++static enum drm_mode_status rp1dpi_pipe_mode_valid(struct drm_simple_display_pipe *pipe, ++ const struct drm_display_mode *mode) ++{ ++#if !IS_REACHABLE(CONFIG_RP1_PIO) ++ if (mode->flags & DRM_MODE_FLAG_INTERLACE) ++ return MODE_NO_INTERLACE; ++#endif ++ if (mode->clock < 1000) /* 1 MHz */ ++ return MODE_CLOCK_LOW; ++ if (mode->clock > 200000) /* 200 MHz */ ++ return MODE_CLOCK_HIGH; ++ ++ return MODE_OK; ++} ++ +static const struct drm_simple_display_pipe_funcs rp1dpi_pipe_funcs = { + .enable = rp1dpi_pipe_enable, + .update = rp1dpi_pipe_update, + .disable = rp1dpi_pipe_disable, + .enable_vblank = rp1dpi_pipe_enable_vblank, + .disable_vblank = rp1dpi_pipe_disable_vblank, ++ .mode_valid = rp1dpi_pipe_mode_valid, +}; + +static const struct drm_mode_config_funcs rp1dpi_mode_funcs = { @@ -80697,6 +82221,7 @@ index 000000000000..c97114a89165 + if (dpi->dpi_running || rp1dpi_hw_busy(dpi)) { + rp1dpi_hw_stop(dpi); + clk_disable_unprepare(dpi->clocks[RP1DPI_CLK_DPI]); ++ rp1dpi_pio_stop(dpi); + dpi->dpi_running = false; + } + rp1dpi_vidout_poweroff(dpi); @@ -80733,8 +82258,9 @@ index 000000000000..c97114a89165 + struct device *dev = &pdev->dev; + struct rp1_dpi *dpi; + struct drm_bridge *bridge = NULL; ++ const char *rgb_order = NULL; + struct drm_panel *panel; -+ int i, ret; ++ int i, j, ret; + + dev_info(dev, __func__); + ret = drm_of_find_panel_or_bridge(pdev->dev.of_node, 0, 0, @@ -80756,6 +82282,7 @@ index 000000000000..c97114a89165 + return ret; + } + dpi->pdev = pdev; ++ spin_lock_init(&dpi->hw_lock); + + dpi->bus_fmt = default_bus_fmt; + ret = of_property_read_u32(dev->of_node, "default_bus_fmt", &dpi->bus_fmt); @@ -80793,6 +82320,47 @@ index 000000000000..c97114a89165 + if (ret) + goto done_err; + ++ dpi->rgb_order_override = RP1DPI_ORDER_UNCHANGED; ++ if (!of_property_read_string(dev->of_node, "rgb_order", &rgb_order)) { ++ if (!strcmp(rgb_order, "rgb")) ++ dpi->rgb_order_override = RP1DPI_ORDER_RGB; ++ else if (!strcmp(rgb_order, "bgr")) ++ dpi->rgb_order_override = RP1DPI_ORDER_BGR; ++ else if (!strcmp(rgb_order, "grb")) ++ dpi->rgb_order_override = RP1DPI_ORDER_GRB; ++ else if (!strcmp(rgb_order, "brg")) ++ dpi->rgb_order_override = RP1DPI_ORDER_BRG; ++ else ++ DRM_ERROR("Invalid dpi order %s - ignored\n", rgb_order); ++ } ++ ++ /* Check if PIO can snoop on or override DPI's GPIO1 */ ++ dpi->gpio1_used = false; ++ for (i = 0; !dpi->gpio1_used; i++) { ++ u32 p = 0; ++ const char *str = NULL; ++ struct device_node *np1 = of_parse_phandle(dev->of_node, "pinctrl-0", i); ++ ++ if (!np1) ++ break; ++ ++ if (!of_property_read_string(np1, "function", &str) && !strcmp(str, "dpi")) { ++ for (j = 0; !dpi->gpio1_used; j++) { ++ if (of_property_read_string_index(np1, "pins", j, &str)) ++ break; ++ if (!strcmp(str, "gpio1")) ++ dpi->gpio1_used = true; ++ } ++ for (j = 0; !dpi->gpio1_used; j++) { ++ if (of_property_read_u32_index(np1, "brcm,pins", j, &p)) ++ break; ++ if (p == 1) ++ dpi->gpio1_used = true; ++ } ++ } ++ of_node_put(np1); ++ } ++ + /* Now we have all our resources, finish driver initialization */ + dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64)); + init_completion(&dpi->finished); @@ -80878,10 +82446,10 @@ index 000000000000..c97114a89165 +MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi.h b/drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi.h new file mode 100644 -index 000000000000..1d32216bcca6 +index 000000000000..848a043e1e24 --- /dev/null +++ b/drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi.h -@@ -0,0 +1,69 @@ +@@ -0,0 +1,95 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * DRM Driver for DSI output on Raspberry Pi RP1 @@ -80909,6 +82477,13 @@ index 000000000000..1d32216bcca6 +#define RP1DPI_CLK_PLLCORE 2 +#define RP1DPI_NUM_CLOCKS 3 + ++/* Codes (in LE byte order) used for S/W permutation */ ++#define RP1DPI_ORDER_UNCHANGED 0 ++#define RP1DPI_ORDER_RGB 0x020100 ++#define RP1DPI_ORDER_BGR 0x000102 ++#define RP1DPI_ORDER_GRB 0x020001 ++#define RP1DPI_ORDER_BRG 0x010002 ++ +/* ---------------------------------------------------------------------- */ + +struct rp1_dpi { @@ -80929,7 +82504,20 @@ index 000000000000..1d32216bcca6 + u32 bus_fmt; + bool de_inv, clk_inv; + bool dpi_running, pipe_enabled; ++ unsigned int rgb_order_override; + struct completion finished; ++ ++ /* Experimental stuff for interlace follows */ ++ struct rp1_pio_client *pio; ++ bool gpio1_used; ++ bool pio_stole_gpio2; ++ ++ spinlock_t hw_lock; /* the following are used in line-match ISR */ ++ dma_addr_t last_dma_addr; ++ u32 last_stride; ++ u32 shorter_front_porch; ++ bool interlaced; ++ bool lower_field_flag; +}; + +/* ---------------------------------------------------------------------- */ @@ -80951,6 +82539,12 @@ index 000000000000..1d32216bcca6 + +void rp1dpi_vidout_setup(struct rp1_dpi *dpi, bool drive_negedge); +void rp1dpi_vidout_poweroff(struct rp1_dpi *dpi); ++ ++/* ---------------------------------------------------------------------- */ ++/* PIO control -- we need PIO to generate VSync (from DE) when interlaced */ ++ ++int rp1dpi_pio_start(struct rp1_dpi *dpi, const struct drm_display_mode *mode); ++void rp1dpi_pio_stop(struct rp1_dpi *dpi); diff --git a/drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi_cfg.c b/drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi_cfg.c new file mode 100644 index 000000000000..cd328b98d4da @@ -81469,10 +83063,10 @@ index 000000000000..cd328b98d4da +} diff --git a/drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi_hw.c b/drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi_hw.c new file mode 100644 -index 000000000000..e4c2592d8498 +index 000000000000..b6b844afb2e3 --- /dev/null +++ b/drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi_hw.c -@@ -0,0 +1,504 @@ +@@ -0,0 +1,668 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * DRM Driver for DPI output on Raspberry Pi RP1 @@ -81677,7 +83271,7 @@ index 000000000000..e4c2592d8498 +// Status +#define DPI_DMA_STATUS 0x3c + -+#define BITS(field, val) (((val) << (field ## _SHIFT)) & (field ## _MASK)) ++#define BITS(field, val) FIELD_PREP((field ## _MASK), val) + +static unsigned int rp1dpi_hw_read(struct rp1_dpi *dpi, unsigned int reg) +{ @@ -81698,80 +83292,112 @@ index 000000000000..e4c2592d8498 + return (rp1dpi_hw_read(dpi, DPI_DMA_STATUS) & 0xF8F) ? 1 : 0; +} + -+/* Table of supported input (in-memory/DMA) pixel formats. */ ++/* ++ * Table of supported input (in-memory/DMA) pixel formats. ++ * ++ * RP1 DPI describes RGB components in terms of their MS bit position, a 10-bit ++ * left-aligned bit-mask, and an optional right-shift-and-OR used for scaling. ++ * To make it easier to permute R, G and B components, we re-pack these fields ++ * into 32-bit code-words, which don't themselves correspond to any register. ++ */ ++ ++#define RGB_CODE(scale, shift, mask) (((scale) << 24) | ((shift) << 16) | (mask)) ++#define RGB_SCALE(c) ((c) >> 24) ++#define RGB_SHIFT(c) (((c) >> 16) & 31) ++#define RGB_MASK(c) ((c) & 0x3ff) ++ +struct rp1dpi_ipixfmt { -+ u32 format; /* DRM format code */ -+ u32 mask; /* RGB masks (10 bits each, left justified) */ -+ u32 shift; /* RGB MSB positions in the memory word */ -+ u32 rgbsz; /* Shifts used for scaling; also (BPP/8-1) */ ++ u32 format; /* DRM format code */ ++ u32 rgb_code[3]; /* (width&7), MS bit position, 10-bit mask */ ++ u32 bpp; /* Bytes per pixel minus one */ +}; + -+#define IMASK_RGB(r, g, b) (BITS(DPI_DMA_IMASK_R, r) | \ -+ BITS(DPI_DMA_IMASK_G, g) | \ -+ BITS(DPI_DMA_IMASK_B, b)) -+#define OMASK_RGB(r, g, b) (BITS(DPI_DMA_OMASK_R, r) | \ -+ BITS(DPI_DMA_OMASK_G, g) | \ -+ BITS(DPI_DMA_OMASK_B, b)) -+#define ISHIFT_RGB(r, g, b) (BITS(DPI_DMA_SHIFT_IR, r) | \ -+ BITS(DPI_DMA_SHIFT_IG, g) | \ -+ BITS(DPI_DMA_SHIFT_IB, b)) -+#define OSHIFT_RGB(r, g, b) (BITS(DPI_DMA_SHIFT_OR, r) | \ -+ BITS(DPI_DMA_SHIFT_OG, g) | \ -+ BITS(DPI_DMA_SHIFT_OB, b)) -+ +static const struct rp1dpi_ipixfmt my_formats[] = { + { -+ .format = DRM_FORMAT_XRGB8888, -+ .mask = IMASK_RGB(0x3fc, 0x3fc, 0x3fc), -+ .shift = ISHIFT_RGB(23, 15, 7), -+ .rgbsz = BITS(DPI_DMA_RGBSZ_BPP, 3), ++ .format = DRM_FORMAT_XRGB8888, ++ .rgb_code = { ++ RGB_CODE(0, 23, 0x3fc), ++ RGB_CODE(0, 15, 0x3fc), ++ RGB_CODE(0, 7, 0x3fc), ++ }, ++ .bpp = 3, + }, + { -+ .format = DRM_FORMAT_XBGR8888, -+ .mask = IMASK_RGB(0x3fc, 0x3fc, 0x3fc), -+ .shift = ISHIFT_RGB(7, 15, 23), -+ .rgbsz = BITS(DPI_DMA_RGBSZ_BPP, 3), ++ .format = DRM_FORMAT_XBGR8888, ++ .rgb_code = { ++ RGB_CODE(0, 7, 0x3fc), ++ RGB_CODE(0, 15, 0x3fc), ++ RGB_CODE(0, 23, 0x3fc), ++ }, ++ .bpp = 3, + }, + { -+ .format = DRM_FORMAT_ARGB8888, -+ .mask = IMASK_RGB(0x3fc, 0x3fc, 0x3fc), -+ .shift = ISHIFT_RGB(23, 15, 7), -+ .rgbsz = BITS(DPI_DMA_RGBSZ_BPP, 3), ++ .format = DRM_FORMAT_ARGB8888, ++ .rgb_code = { ++ RGB_CODE(0, 23, 0x3fc), ++ RGB_CODE(0, 15, 0x3fc), ++ RGB_CODE(0, 7, 0x3fc), ++ }, ++ .bpp = 3, + }, + { -+ .format = DRM_FORMAT_ABGR8888, -+ .mask = IMASK_RGB(0x3fc, 0x3fc, 0x3fc), -+ .shift = ISHIFT_RGB(7, 15, 23), -+ .rgbsz = BITS(DPI_DMA_RGBSZ_BPP, 3), ++ .format = DRM_FORMAT_ABGR8888, ++ .rgb_code = { ++ RGB_CODE(0, 7, 0x3fc), ++ RGB_CODE(0, 15, 0x3fc), ++ RGB_CODE(0, 23, 0x3fc), ++ }, ++ .bpp = 3, + }, + { -+ .format = DRM_FORMAT_RGB888, -+ .mask = IMASK_RGB(0x3fc, 0x3fc, 0x3fc), -+ .shift = ISHIFT_RGB(23, 15, 7), -+ .rgbsz = BITS(DPI_DMA_RGBSZ_BPP, 2), ++ .format = DRM_FORMAT_RGB888, ++ .rgb_code = { ++ RGB_CODE(0, 23, 0x3fc), ++ RGB_CODE(0, 15, 0x3fc), ++ RGB_CODE(0, 7, 0x3fc), ++ }, ++ .bpp = 2, + }, + { -+ .format = DRM_FORMAT_BGR888, -+ .mask = IMASK_RGB(0x3fc, 0x3fc, 0x3fc), -+ .shift = ISHIFT_RGB(7, 15, 23), -+ .rgbsz = BITS(DPI_DMA_RGBSZ_BPP, 2), ++ .format = DRM_FORMAT_BGR888, ++ .rgb_code = { ++ RGB_CODE(0, 7, 0x3fc), ++ RGB_CODE(0, 15, 0x3fc), ++ RGB_CODE(0, 23, 0x3fc), ++ }, ++ .bpp = 2, + }, + { -+ .format = DRM_FORMAT_RGB565, -+ .mask = IMASK_RGB(0x3e0, 0x3f0, 0x3e0), -+ .shift = ISHIFT_RGB(15, 10, 4), -+ .rgbsz = BITS(DPI_DMA_RGBSZ_R, 5) | BITS(DPI_DMA_RGBSZ_G, 6) | -+ BITS(DPI_DMA_RGBSZ_B, 5) | BITS(DPI_DMA_RGBSZ_BPP, 1), ++ .format = DRM_FORMAT_RGB565, ++ .rgb_code = { ++ RGB_CODE(5, 15, 0x3e0), ++ RGB_CODE(6, 10, 0x3f0), ++ RGB_CODE(5, 4, 0x3e0), ++ }, ++ .bpp = 1, + }, -+ { -+ .format = DRM_FORMAT_BGR565, -+ .mask = IMASK_RGB(0x3e0, 0x3f0, 0x3e0), -+ .shift = ISHIFT_RGB(4, 10, 15), -+ .rgbsz = BITS(DPI_DMA_RGBSZ_R, 5) | BITS(DPI_DMA_RGBSZ_G, 6) | -+ BITS(DPI_DMA_RGBSZ_B, 5) | BITS(DPI_DMA_RGBSZ_BPP, 1), -+ } +}; + ++#define IMASK_RGB(r, g, b) (FIELD_PREP_CONST(DPI_DMA_IMASK_R_MASK, r) | \ ++ FIELD_PREP_CONST(DPI_DMA_IMASK_G_MASK, g) | \ ++ FIELD_PREP_CONST(DPI_DMA_IMASK_B_MASK, b)) ++#define OMASK_RGB(r, g, b) (FIELD_PREP_CONST(DPI_DMA_OMASK_R_MASK, r) | \ ++ FIELD_PREP_CONST(DPI_DMA_OMASK_G_MASK, g) | \ ++ FIELD_PREP_CONST(DPI_DMA_OMASK_B_MASK, b)) ++#define ISHIFT_RGB(r, g, b) (FIELD_PREP_CONST(DPI_DMA_SHIFT_IR_MASK, r) | \ ++ FIELD_PREP_CONST(DPI_DMA_SHIFT_IG_MASK, g) | \ ++ FIELD_PREP_CONST(DPI_DMA_SHIFT_IB_MASK, b)) ++#define OSHIFT_RGB(r, g, b) (FIELD_PREP_CONST(DPI_DMA_SHIFT_OR_MASK, r) | \ ++ FIELD_PREP_CONST(DPI_DMA_SHIFT_OG_MASK, g) | \ ++ FIELD_PREP_CONST(DPI_DMA_SHIFT_OB_MASK, b)) ++ ++/* ++ * Function to update *shift with output positions, and return output RGB masks. ++ * By the time we get here, RGB order has been normalized to RGB (R most significant). ++ * Note that an internal bus is 30 bits wide: bits [21:20], [11:10], [1:0] are dropped. ++ * This makes the packed RGB5656 and RGB666 formats problematic, as colour components ++ * need to straddle the gaps; we mitigate this by hijacking input masks and scaling. ++ */ +static u32 set_output_format(u32 bus_format, u32 *shift, u32 *imask, u32 *rgbsz) +{ + switch (bus_format) { @@ -81779,6 +83405,7 @@ index 000000000000..e4c2592d8498 + if (*shift == ISHIFT_RGB(15, 10, 4)) { + /* When framebuffer is RGB565, we can output RGB565 */ + *shift = ISHIFT_RGB(15, 7, 0) | OSHIFT_RGB(19, 9, 0); ++ *imask = IMASK_RGB(0x3fc, 0x3fc, 0); + *rgbsz &= DPI_DMA_RGBSZ_BPP_MASK; + return OMASK_RGB(0x3fc, 0x3fc, 0); + } @@ -81793,7 +83420,7 @@ index 000000000000..e4c2592d8498 + case MEDIA_BUS_FMT_BGR666_1X18: + /* due to a HW limitation, bit-depth is effectively RGB444 */ + *shift |= OSHIFT_RGB(23, 15, 7); -+ *imask &= IMASK_RGB(0x3c0, 0x3c0, 0x3c0); ++ *imask = IMASK_RGB(0x3c0, 0x3c0, 0x3c0); + *rgbsz = BITS(DPI_DMA_RGBSZ_R, 2) | (*rgbsz & DPI_DMA_RGBSZ_BPP_MASK); + return OMASK_RGB(0x330, 0x3c0, 0x3c0); + @@ -81829,57 +83456,61 @@ index 000000000000..e4c2592d8498 + u32 in_format, u32 bus_format, bool de_inv, + struct drm_display_mode const *mode) +{ -+ u32 shift, imask, omask, rgbsz; -+ int i; -+ -+ pr_info("%s: in_fmt=\'%c%c%c%c\' bus_fmt=0x%x mode=%dx%d total=%dx%d %dkHz %cH%cV%cD%cC", -+ __func__, in_format, in_format >> 8, in_format >> 16, in_format >> 24, bus_format, -+ mode->hdisplay, mode->vdisplay, -+ mode->htotal, mode->vtotal, -+ mode->clock, -+ (mode->flags & DRM_MODE_FLAG_NHSYNC) ? '-' : '+', -+ (mode->flags & DRM_MODE_FLAG_NVSYNC) ? '-' : '+', -+ de_inv ? '-' : '+', -+ dpi->clk_inv ? '-' : '+'); -+ -+ /* -+ * Configure all DPI/DMA block registers, except base address. -+ * DMA will not actually start until a FB base address is specified -+ * using rp1dpi_hw_update(). -+ */ -+ rp1dpi_hw_write(dpi, DPI_DMA_VISIBLE_AREA, -+ BITS(DPI_DMA_VISIBLE_AREA_ROWSM1, mode->vdisplay - 1) | -+ BITS(DPI_DMA_VISIBLE_AREA_COLSM1, mode->hdisplay - 1)); -+ -+ rp1dpi_hw_write(dpi, DPI_DMA_SYNC_WIDTH, -+ BITS(DPI_DMA_SYNC_WIDTH_ROWSM1, mode->vsync_end - mode->vsync_start - 1) | -+ BITS(DPI_DMA_SYNC_WIDTH_COLSM1, mode->hsync_end - mode->hsync_start - 1)); -+ -+ /* In these registers, "back porch" time includes sync width */ -+ rp1dpi_hw_write(dpi, DPI_DMA_BACK_PORCH, -+ BITS(DPI_DMA_BACK_PORCH_ROWSM1, mode->vtotal - mode->vsync_start - 1) | -+ BITS(DPI_DMA_BACK_PORCH_COLSM1, mode->htotal - mode->hsync_start - 1)); -+ -+ rp1dpi_hw_write(dpi, DPI_DMA_FRONT_PORCH, -+ BITS(DPI_DMA_FRONT_PORCH_ROWSM1, mode->vsync_start - mode->vdisplay - 1) | -+ BITS(DPI_DMA_FRONT_PORCH_COLSM1, mode->hsync_start - mode->hdisplay - 1)); -+ -+ /* Input to output pixel format conversion */ ++ u32 shift, imask, omask, rgbsz, vctrl; ++ u32 rgb_code[3]; ++ int order, i; ++ ++ drm_info(&dpi->drm, ++ "in_fmt=\'%c%c%c%c\' bus_fmt=0x%x mode=%dx%d total=%dx%d%s %dkHz %cH%cV%cD%cC", ++ in_format, in_format >> 8, in_format >> 16, in_format >> 24, bus_format, ++ mode->hdisplay, mode->vdisplay, ++ mode->htotal, mode->vtotal, ++ (mode->flags & DRM_MODE_FLAG_INTERLACE) ? "i" : "", ++ mode->clock, ++ (mode->flags & DRM_MODE_FLAG_NHSYNC) ? '-' : '+', ++ (mode->flags & DRM_MODE_FLAG_NVSYNC) ? '-' : '+', ++ de_inv ? '-' : '+', ++ dpi->clk_inv ? '-' : '+'); ++ ++ /* Look up the input (in-memory) pixel format */ + for (i = 0; i < ARRAY_SIZE(my_formats); ++i) { + if (my_formats[i].format == in_format) + break; + } + if (i >= ARRAY_SIZE(my_formats)) { + pr_err("%s: bad input format\n", __func__); -+ i = 4; ++ i = ARRAY_SIZE(my_formats) - 1; + } -+ if (BUS_FMT_IS_BGR(bus_format)) -+ i ^= 1; -+ shift = my_formats[i].shift; -+ imask = my_formats[i].mask; -+ rgbsz = my_formats[i].rgbsz; ++ ++ /* ++ * Although these RGB orderings refer to the output (DPI bus) format, ++ * here we permute the *input* components. After this point, "Red" ++ * will be most significant (highest numbered GPIOs), regardless ++ * of rgb_order or bus_format. This simplifies later workarounds. ++ */ ++ order = dpi->rgb_order_override; ++ if (order == RP1DPI_ORDER_UNCHANGED) ++ order = BUS_FMT_IS_BGR(bus_format) ? RP1DPI_ORDER_BGR : RP1DPI_ORDER_RGB; ++ rgb_code[0] = my_formats[i].rgb_code[order & 3]; ++ rgb_code[1] = my_formats[i].rgb_code[(order >> 8) & 3]; ++ rgb_code[2] = my_formats[i].rgb_code[(order >> 16) & 3]; ++ rgbsz = FIELD_PREP(DPI_DMA_RGBSZ_BPP_MASK, my_formats[i].bpp) | ++ FIELD_PREP(DPI_DMA_RGBSZ_R_MASK, RGB_SCALE(rgb_code[0])) | ++ FIELD_PREP(DPI_DMA_RGBSZ_G_MASK, RGB_SCALE(rgb_code[1])) | ++ FIELD_PREP(DPI_DMA_RGBSZ_B_MASK, RGB_SCALE(rgb_code[2])); ++ shift = FIELD_PREP(DPI_DMA_SHIFT_IR_MASK, RGB_SHIFT(rgb_code[0])) | ++ FIELD_PREP(DPI_DMA_SHIFT_IG_MASK, RGB_SHIFT(rgb_code[1])) | ++ FIELD_PREP(DPI_DMA_SHIFT_IB_MASK, RGB_SHIFT(rgb_code[2])); ++ imask = FIELD_PREP(DPI_DMA_IMASK_R_MASK, RGB_MASK(rgb_code[0])) | ++ FIELD_PREP(DPI_DMA_IMASK_G_MASK, RGB_MASK(rgb_code[1])) | ++ FIELD_PREP(DPI_DMA_IMASK_B_MASK, RGB_MASK(rgb_code[2])); + omask = set_output_format(bus_format, &shift, &imask, &rgbsz); + ++ /* ++ * Configure all DPI/DMA block registers, except base address. ++ * DMA will not actually start until a FB base address is specified ++ * using rp1dpi_hw_update(). ++ */ + rp1dpi_hw_write(dpi, DPI_DMA_IMASK, imask); + rp1dpi_hw_write(dpi, DPI_DMA_OMASK, omask); + rp1dpi_hw_write(dpi, DPI_DMA_SHIFT, shift); @@ -81892,6 +83523,89 @@ index 000000000000..e4c2592d8498 + BITS(DPI_DMA_QOS_LLEV, 0x8) | + BITS(DPI_DMA_QOS_LQOS, 0x7)); + ++ if (!(mode->flags & DRM_MODE_FLAG_INTERLACE)) { ++ rp1dpi_hw_write(dpi, DPI_DMA_VISIBLE_AREA, ++ BITS(DPI_DMA_VISIBLE_AREA_ROWSM1, mode->vdisplay - 1) | ++ BITS(DPI_DMA_VISIBLE_AREA_COLSM1, mode->hdisplay - 1)); ++ ++ rp1dpi_hw_write(dpi, DPI_DMA_SYNC_WIDTH, ++ BITS(DPI_DMA_SYNC_WIDTH_ROWSM1, ++ mode->vsync_end - mode->vsync_start - 1) | ++ BITS(DPI_DMA_SYNC_WIDTH_COLSM1, ++ mode->hsync_end - mode->hsync_start - 1)); ++ ++ /* In these registers, "back porch" time includes sync width */ ++ rp1dpi_hw_write(dpi, DPI_DMA_BACK_PORCH, ++ BITS(DPI_DMA_BACK_PORCH_ROWSM1, ++ mode->vtotal - mode->vsync_start - 1) | ++ BITS(DPI_DMA_BACK_PORCH_COLSM1, ++ mode->htotal - mode->hsync_start - 1)); ++ ++ rp1dpi_hw_write(dpi, DPI_DMA_FRONT_PORCH, ++ BITS(DPI_DMA_FRONT_PORCH_ROWSM1, ++ mode->vsync_start - mode->vdisplay - 1) | ++ BITS(DPI_DMA_FRONT_PORCH_COLSM1, ++ mode->hsync_start - mode->hdisplay - 1)); ++ ++ vctrl = BITS(DPI_DMA_CONTROL_VSYNC_POL, !!(mode->flags & DRM_MODE_FLAG_NVSYNC)) | ++ BITS(DPI_DMA_CONTROL_VBP_EN, (mode->vtotal != mode->vsync_start)) | ++ BITS(DPI_DMA_CONTROL_VFP_EN, (mode->vsync_start != mode->vdisplay)) | ++ BITS(DPI_DMA_CONTROL_VSYNC_EN, (mode->vsync_end != mode->vsync_start)); ++ ++ dpi->interlaced = false; ++ } else { ++ /* ++ * Experimental interlace support ++ * ++ * RP1 DPI hardware wasn't designed to support interlace, but lets us change ++ * both the VFP line count and the next DMA address while running. That allows ++ * pixel data to be correctly timed for interlace, but VSYNC remains wrong. ++ * ++ * It is necessary to use external hardware (such as PIO) to regenerate VSYNC ++ * based on HSYNC, DE (which *must* both be mapped to GPIOs 1, 3 respectively). ++ * This driver includes a PIO program to do that, when DE is enabled. ++ * ++ * An alternative fixup is to synthesize CSYNC from HSYNC and modified-VSYNC. ++ * We don't implement that here, but to facilitate it, DPI's VSYNC is replaced ++ * by a "helper signal" that pulses low for 1 or 2 scan-lines, starting 2.0 or ++ * 2.5 scan-lines respectively before nominal VSYNC start. ++ */ ++ int vact = mode->vdisplay >> 1; /* visible lines per field. Can't do half-lines */ ++ int vtot0 = mode->vtotal >> 1; /* vtotal should always be odd when interlaced. */ ++ int vfp0 = (mode->vsync_start >= mode->vdisplay + 4) ? ++ ((mode->vsync_start - mode->vdisplay - 2) >> 1) : 1; ++ int vbp = max(0, vtot0 - vact - vfp0); ++ ++ rp1dpi_hw_write(dpi, DPI_DMA_VISIBLE_AREA, ++ BITS(DPI_DMA_VISIBLE_AREA_ROWSM1, vact - 1) | ++ BITS(DPI_DMA_VISIBLE_AREA_COLSM1, mode->hdisplay - 1)); ++ ++ rp1dpi_hw_write(dpi, DPI_DMA_SYNC_WIDTH, ++ BITS(DPI_DMA_SYNC_WIDTH_ROWSM1, vtot0 - 2) | ++ BITS(DPI_DMA_SYNC_WIDTH_COLSM1, ++ mode->hsync_end - mode->hsync_start - 1)); ++ ++ rp1dpi_hw_write(dpi, DPI_DMA_BACK_PORCH, ++ BITS(DPI_DMA_BACK_PORCH_ROWSM1, vbp - 1) | ++ BITS(DPI_DMA_BACK_PORCH_COLSM1, ++ mode->htotal - mode->hsync_start - 1)); ++ ++ dpi->shorter_front_porch = ++ BITS(DPI_DMA_FRONT_PORCH_ROWSM1, vfp0 - 1) | ++ BITS(DPI_DMA_FRONT_PORCH_COLSM1, ++ mode->hsync_start - mode->hdisplay - 1); ++ rp1dpi_hw_write(dpi, DPI_DMA_FRONT_PORCH, dpi->shorter_front_porch); ++ ++ vctrl = BITS(DPI_DMA_CONTROL_VSYNC_POL, 0) | ++ BITS(DPI_DMA_CONTROL_VBP_EN, (vbp > 0)) | ++ BITS(DPI_DMA_CONTROL_VFP_EN, 1) | ++ BITS(DPI_DMA_CONTROL_VSYNC_EN, 1); ++ ++ dpi->interlaced = true; ++ } ++ dpi->lower_field_flag = false; ++ dpi->last_dma_addr = 0; ++ + rp1dpi_hw_write(dpi, DPI_DMA_IRQ_FLAGS, -1); + rp1dpi_hw_vblank_ctrl(dpi, 1); + @@ -81900,49 +83614,64 @@ index 000000000000..e4c2592d8498 + pr_warn("%s: Unexpectedly busy at start!", __func__); + + rp1dpi_hw_write(dpi, DPI_DMA_CONTROL, ++ vctrl | + BITS(DPI_DMA_CONTROL_ARM, !i) | + BITS(DPI_DMA_CONTROL_AUTO_REPEAT, 1) | + BITS(DPI_DMA_CONTROL_HIGH_WATER, 448) | + BITS(DPI_DMA_CONTROL_DEN_POL, de_inv) | + BITS(DPI_DMA_CONTROL_HSYNC_POL, !!(mode->flags & DRM_MODE_FLAG_NHSYNC)) | -+ BITS(DPI_DMA_CONTROL_VSYNC_POL, !!(mode->flags & DRM_MODE_FLAG_NVSYNC)) | -+ BITS(DPI_DMA_CONTROL_COLORM, 0) | -+ BITS(DPI_DMA_CONTROL_SHUTDN, 0) | + BITS(DPI_DMA_CONTROL_HBP_EN, (mode->htotal != mode->hsync_end)) | + BITS(DPI_DMA_CONTROL_HFP_EN, (mode->hsync_start != mode->hdisplay)) | -+ BITS(DPI_DMA_CONTROL_VBP_EN, (mode->vtotal != mode->vsync_end)) | -+ BITS(DPI_DMA_CONTROL_VFP_EN, (mode->vsync_start != mode->vdisplay)) | -+ BITS(DPI_DMA_CONTROL_HSYNC_EN, (mode->hsync_end != mode->hsync_start)) | -+ BITS(DPI_DMA_CONTROL_VSYNC_EN, (mode->vsync_end != mode->vsync_start))); ++ BITS(DPI_DMA_CONTROL_HSYNC_EN, (mode->hsync_end != mode->hsync_start))); +} + +void rp1dpi_hw_update(struct rp1_dpi *dpi, dma_addr_t addr, u32 offset, u32 stride) +{ -+ u64 a = addr + offset; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&dpi->hw_lock, flags); + + /* + * Update STRIDE, DMAH and DMAL only. When called after rp1dpi_hw_setup(), + * DMA starts immediately; if already running, the buffer will flip at -+ * the next vertical sync event. ++ * the next vertical sync event. In interlaced mode, we need to adjust ++ * the address and stride to display only the current field, saving ++ * the original address (so it can be flipped for subsequent fields). + */ ++ addr += offset; ++ dpi->last_dma_addr = addr; ++ dpi->last_stride = stride; ++ if (dpi->interlaced) { ++ if (dpi->lower_field_flag) ++ addr += stride; ++ stride *= 2; ++ } + rp1dpi_hw_write(dpi, DPI_DMA_DMA_STRIDE, stride); -+ rp1dpi_hw_write(dpi, DPI_DMA_DMA_ADDR_H, a >> 32); -+ rp1dpi_hw_write(dpi, DPI_DMA_DMA_ADDR_L, a & 0xFFFFFFFFu); ++ rp1dpi_hw_write(dpi, DPI_DMA_DMA_ADDR_H, addr >> 32); ++ rp1dpi_hw_write(dpi, DPI_DMA_DMA_ADDR_L, addr & 0xFFFFFFFFu); ++ ++ spin_unlock_irqrestore(&dpi->hw_lock, flags); +} + +void rp1dpi_hw_stop(struct rp1_dpi *dpi) +{ + u32 ctrl; ++ unsigned long flags; + + /* -+ * Stop DMA by turning off the Auto-Repeat flag, and wait up to 100ms for -+ * the current and any queued frame to end. "Force drain" flags are not used, -+ * as they seem to prevent DMA from re-starting properly; it's safer to wait. ++ * Stop DMA by turning off Auto-Repeat (and disable S/W field-flip), ++ * then wait up to 100ms for the current and any queued frame to end. ++ * (There is a "force drain" flag, but it can leave DPI in a broken ++ * state which prevents it from restarting; it's safer to wait.) + */ ++ spin_lock_irqsave(&dpi->hw_lock, flags); ++ dpi->last_dma_addr = 0; + reinit_completion(&dpi->finished); + ctrl = rp1dpi_hw_read(dpi, DPI_DMA_CONTROL); + ctrl &= ~(DPI_DMA_CONTROL_ARM_MASK | DPI_DMA_CONTROL_AUTO_REPEAT_MASK); + rp1dpi_hw_write(dpi, DPI_DMA_CONTROL, ctrl); ++ spin_unlock_irqrestore(&dpi->hw_lock, flags); ++ + if (!wait_for_completion_timeout(&dpi->finished, HZ / 10)) + drm_err(&dpi->drm, "%s: timed out waiting for idle\n", __func__); + rp1dpi_hw_write(dpi, DPI_DMA_IRQ_EN, 0); @@ -81951,10 +83680,11 @@ index 000000000000..e4c2592d8498 +void rp1dpi_hw_vblank_ctrl(struct rp1_dpi *dpi, int enable) +{ + rp1dpi_hw_write(dpi, DPI_DMA_IRQ_EN, -+ BITS(DPI_DMA_IRQ_EN_AFIFO_EMPTY, 1) | -+ BITS(DPI_DMA_IRQ_EN_UNDERFLOW, 1) | -+ BITS(DPI_DMA_IRQ_EN_DMA_READY, !!enable) | -+ BITS(DPI_DMA_IRQ_EN_MATCH_LINE, 4095)); ++ BITS(DPI_DMA_IRQ_EN_AFIFO_EMPTY, 1) | ++ BITS(DPI_DMA_IRQ_EN_UNDERFLOW, 1) | ++ BITS(DPI_DMA_IRQ_EN_DMA_READY, !!enable) | ++ BITS(DPI_DMA_IRQ_EN_MATCH, dpi->interlaced) | ++ BITS(DPI_DMA_IRQ_EN_MATCH_LINE, 32)); +} + +irqreturn_t rp1dpi_hw_isr(int irq, void *dev) @@ -81973,10 +83703,285 @@ index 000000000000..e4c2592d8498 + drm_crtc_handle_vblank(&dpi->pipe.crtc); + if (u & DPI_DMA_IRQ_FLAGS_AFIFO_EMPTY_MASK) + complete(&dpi->finished); ++ ++ /* ++ * Added for interlace support: We use this mid-frame interrupt to ++ * wobble the VFP between fields, re-submitting the next-buffer address ++ * with an offset to display the opposite field. NB: rp1dpi_hw_update() ++ * may be called at any time, before or after, so locking is needed. ++ * H/W Auto-update is no longer needed (unless this IRQ is lost). ++ */ ++ if ((u & DPI_DMA_IRQ_FLAGS_MATCH_MASK) && dpi->interlaced) { ++ unsigned long flags; ++ dma_addr_t a; ++ ++ spin_lock_irqsave(&dpi->hw_lock, flags); ++ dpi->lower_field_flag = !dpi->lower_field_flag; ++ rp1dpi_hw_write(dpi, DPI_DMA_FRONT_PORCH, ++ dpi->shorter_front_porch + ++ BITS(DPI_DMA_FRONT_PORCH_ROWSM1, ++ dpi->lower_field_flag)); ++ a = dpi->last_dma_addr; ++ if (a) { ++ if (dpi->lower_field_flag) ++ a += dpi->last_stride; ++ rp1dpi_hw_write(dpi, DPI_DMA_DMA_ADDR_H, a >> 32); ++ rp1dpi_hw_write(dpi, DPI_DMA_DMA_ADDR_L, a & 0xFFFFFFFFu); ++ } ++ spin_unlock_irqrestore(&dpi->hw_lock, flags); ++ } + } + } ++ + return u ? IRQ_HANDLED : IRQ_NONE; +} +diff --git a/drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi_pio.c b/drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi_pio.c +new file mode 100644 +index 000000000000..036f5ec62e76 +--- /dev/null ++++ b/drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi_pio.c +@@ -0,0 +1,241 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++/* ++ * PIO code for Raspberry Pi RP1 DPI driver ++ * ++ * Copyright (c) 2024 Raspberry Pi Limited. ++ */ ++ ++/* ++ * Use PIO to fix up VSYNC for interlaced modes. ++ * ++ * For this to work we *require* DPI's pinctrl to enable DE on GPIO1. ++ * PIO can then snoop on HSYNC and DE pins to generate corrected VSYNC. ++ * ++ * Note that corrected VSYNC outputs will not be synchronous to DPICLK, ++ * will lag HSYNC by about 30ns and may suffer up to 5ns of jitter. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "rp1_dpi.h" ++ ++#if IS_REACHABLE(CONFIG_RP1_PIO) ++ ++#include ++ ++/* ++ * Start a PIO SM to generate an interrupt just after HSYNC onset, then another ++ * after a fixed delay (during which we assume HSYNC will have been deasserted). ++ */ ++ ++static int rp1dpi_pio_start_timer_both(struct rp1_dpi *dpi, u32 flags, u32 tc) ++{ ++ static const u16 instructions[2][5] = { ++ { 0xa022, 0x2083, 0xc001, 0x0043, 0xc001 }, /* posedge */ ++ { 0xa022, 0x2003, 0xc001, 0x0043, 0xc001 }, /* negedge */ ++ }; ++ const struct pio_program prog = { ++ .instructions = instructions[(flags & DRM_MODE_FLAG_NHSYNC) ? 1 : 0], ++ .length = ARRAY_SIZE(instructions[0]), ++ .origin = -1 ++ }; ++ int offset, sm; ++ ++ sm = pio_claim_unused_sm(dpi->pio, true); ++ if (sm < 0) ++ return -EBUSY; ++ ++ offset = pio_add_program(dpi->pio, &prog); ++ if (offset == PIO_ORIGIN_ANY) ++ return -EBUSY; ++ ++ pio_sm_config cfg = pio_get_default_sm_config(); ++ ++ pio_sm_set_enabled(dpi->pio, sm, false); ++ sm_config_set_wrap(&cfg, offset, offset + 4); ++ pio_sm_init(dpi->pio, sm, offset, &cfg); ++ ++ pio_sm_put(dpi->pio, sm, tc - 4); ++ pio_sm_exec(dpi->pio, sm, pio_encode_pull(false, false)); ++ pio_sm_exec(dpi->pio, sm, pio_encode_out(pio_y, 32)); ++ pio_sm_set_enabled(dpi->pio, sm, true); ++ ++ return 0; ++} ++ ++/* ++ * Snoop on DE, HSYNC to count half-lines in the vertical blanking interval ++ * to determine when the VSYNC pulse should start and finish. Then, at a ++ * suitable moment (which should be an odd number of half-lines since the ++ * last active line), sample DE again to detect field phase. ++ * ++ * This version assumes VFP length is within 2..129 half-lines for any field ++ * (one half-line delay is needed to sample DE; we always wait for the next ++ * half-line boundary to improve VSync start accuracy). ++ */ ++ ++static int rp1dpi_pio_vsync_ilace(struct rp1_dpi *dpi, ++ struct drm_display_mode const *mode) ++{ ++ static const int wrap_target = 14; ++ static const int wrap = 26; ++ u16 instructions[] = { /* This is mutable */ ++ 0xa0e6, // 0: mov osr, isr side 0 ; top: rewind parameters ++ 0x2081, // 1: wait 1 gpio, 1 side 0 ; main: while (!DE) wait; ++ 0x2783, // 2: wait 1 gpio, 3 side 0 [7] ; do { @HSync ++ 0xc041, // 3: irq clear 1 side 0 ; flush stale IRQs ++ 0x20c1, // 4: wait 1 irq, 1 side 0 ; @midline ++ 0x00c1, // 5: jmp pin, 1 side 0 ; } while (DE) ++ 0x0007, // 6: jmp 7 side 0 ; ++ 0x6027, // 7: out x, 7 side 0 ; x = VFPlen - 2 ++ 0x000a, // 8: jmp 10 side 0 ; while (x--) { ++ 0x20c1, // 9: wait 1 irq, 1 side 0 ; @halfline ++ 0x0049, // 10: jmp x--, 9 side 0 ; } ++ 0x6021, // 11: out x, 1 side 0 ; test for aligned case ++ 0x003a, // 12: jmp !x, 26 side 0 ; if (!x) goto precise; ++ 0x20c1, // 13: wait 1 irq, 1 side 0 ; @halfline ++ // .wrap_target ; vsjoin: ++ 0xb722, // 14: mov x, y side 1 [7] ; VSYNC=1; x = VSyncLen ++ 0xd041, // 15: irq clear 1 side 1 ; VSYNC=1; flush stale IRQs ++ 0x30c1, // 16: wait 1 irq, 1 side 1 ; VSYNC=1; do { @halfline ++ 0x1050, // 17: jmp x--, 16 side 1 ; VSYNC=1; } while (x--) ++ 0x6028, // 18: out x, 8 side 0 ; VSYNC=0; x = VBPLen ++ 0x0015, // 19: jmp 21 side 0 ; while (x--) { ++ 0x20c1, // 20: wait 1 irq, 1 side 0 ; @halfline ++ 0x0054, // 21: jmp x--, 20 side 0 ; } ++ 0x00c0, // 22: jmp pin, 0 side 0 ; if (DE) reset phase ++ 0x0018, // 23: jmp 24 side 0 ; ++ 0x00e1, // 24: jmp !osre, 1 side 0 ; if (!phase) goto main ++ 0x0000, // 25: jmp 0 side 0 ; goto top ++ 0x2083, // 26: wait 1 gpio, 3 side 0 ; precise: @HSync ++ // .wrap ; goto vsjoin ++ }; ++ struct pio_program prog = { ++ .instructions = instructions, ++ .length = ARRAY_SIZE(instructions), ++ .origin = -1 ++ }; ++ pio_sm_config cfg = pio_get_default_sm_config(); ++ unsigned int i, offset; ++ u32 tc, vfp, vbp; ++ u32 sysclk = clock_get_hz(clk_sys); ++ int sm = pio_claim_unused_sm(dpi->pio, true); ++ ++ if (sm < 0) ++ return -EBUSY; ++ ++ /* Compute mid-line time constant and start the timer SM */ ++ tc = (mode->htotal * (u64)sysclk) / (u64)(2000u * mode->clock); ++ if (rp1dpi_pio_start_timer_both(dpi, mode->flags, tc) < 0) { ++ pio_sm_unclaim(dpi->pio, sm); ++ return -EBUSY; ++ } ++ ++ /* Adapt program code according to DE and Sync polarity; configure program */ ++ pio_sm_set_enabled(dpi->pio, sm, false); ++ if (dpi->de_inv) { ++ instructions[1] ^= 0x0080; ++ instructions[5] = 0x00c7; ++ instructions[6] = 0x0001; ++ instructions[22] = 0x00d8; ++ instructions[23] = 0x0000; ++ } ++ for (i = 0; i < ARRAY_SIZE(instructions); i++) { ++ if (mode->flags & DRM_MODE_FLAG_NVSYNC) ++ instructions[i] ^= 0x1000; ++ if ((mode->flags & DRM_MODE_FLAG_NHSYNC) && (instructions[i] & 0xe07f) == 0x2003) ++ instructions[i] ^= 0x0080; ++ } ++ offset = pio_add_program(dpi->pio, &prog); ++ if (offset == PIO_ORIGIN_ANY) ++ return -EBUSY; ++ ++ /* Configure pins and SM */ ++ dpi->pio_stole_gpio2 = true; ++ sm_config_set_wrap(&cfg, offset + wrap_target, offset + wrap); ++ sm_config_set_sideset(&cfg, 1, false, false); ++ sm_config_set_sideset_pins(&cfg, 2); ++ pio_gpio_init(dpi->pio, 2); ++ sm_config_set_jmp_pin(&cfg, 1); /* "DE" is always GPIO1 */ ++ pio_sm_init(dpi->pio, sm, offset, &cfg); ++ pio_sm_set_consecutive_pindirs(dpi->pio, sm, 2, 1, true); ++ ++ /* Compute vertical times, remembering how we rounded vdisplay, vtotal */ ++ vfp = mode->vsync_start - (mode->vdisplay & ~1); ++ vbp = (mode->vtotal | 1) - mode->vsync_end; ++ if (vfp > 128) { ++ vbp += vfp - 128; ++ vfp = 128; ++ } else if (vfp < 3) { ++ vbp = (vbp > 3 - vfp) ? (vbp - 3 + vfp) : 0; ++ vfp = 3; ++ } ++ ++ pio_sm_put(dpi->pio, sm, ++ (vfp - 2) + ((vfp & 1) << 7) + (vbp << 8) + ++ ((vfp - 3) << 16) + (((~vfp) & 1) << 23) + ((vbp + 1) << 24)); ++ pio_sm_put(dpi->pio, sm, mode->vsync_end - mode->vsync_start - 1); ++ pio_sm_exec(dpi->pio, sm, pio_encode_pull(false, false)); ++ pio_sm_exec(dpi->pio, sm, pio_encode_out(pio_y, 32)); ++ pio_sm_exec(dpi->pio, sm, pio_encode_in(pio_y, 32)); ++ pio_sm_exec(dpi->pio, sm, pio_encode_pull(false, false)); ++ pio_sm_exec(dpi->pio, sm, pio_encode_out(pio_y, 32)); ++ pio_sm_set_enabled(dpi->pio, sm, true); ++ ++ return 0; ++} ++ ++int rp1dpi_pio_start(struct rp1_dpi *dpi, const struct drm_display_mode *mode) ++{ ++ int r; ++ ++ if (!(mode->flags & DRM_MODE_FLAG_INTERLACE) || !dpi->gpio1_used) ++ return 0; ++ ++ if (dpi->pio) ++ pio_close(dpi->pio); ++ ++ dpi->pio = pio_open(); ++ if (IS_ERR(dpi->pio)) { ++ drm_err(&dpi->drm, "Could not open PIO\n"); ++ dpi->pio = NULL; ++ return -ENODEV; ++ } ++ ++ r = rp1dpi_pio_vsync_ilace(dpi, mode); ++ if (r) { ++ drm_err(&dpi->drm, "Failed to initialize PIO\n"); ++ rp1dpi_pio_stop(dpi); ++ } ++ ++ return r; ++} ++ ++void rp1dpi_pio_stop(struct rp1_dpi *dpi) ++{ ++ if (dpi->pio) { ++ if (dpi->pio_stole_gpio2) ++ pio_gpio_set_function(dpi->pio, 2, GPIO_FUNC_FSEL1); ++ pio_close(dpi->pio); ++ dpi->pio_stole_gpio2 = false; ++ dpi->pio = NULL; ++ } ++} ++ ++#else /* !IS_REACHABLE(CONFIG_RP1_PIO) */ ++ ++int rp1dpi_pio_start(struct rp1_dpi *dpi, const struct drm_display_mode *mode) ++{ ++ return -ENODEV; ++} ++ ++void rp1dpi_pio_stop(struct rp1_dpi *dpi) ++{ ++} ++ ++#endif diff --git a/drivers/gpu/drm/rp1/rp1-dsi/Kconfig b/drivers/gpu/drm/rp1/rp1-dsi/Kconfig new file mode 100644 index 000000000000..80c57bc48792 @@ -88686,7 +90691,7 @@ index 2e94ce788c71..cf9016a0f3ea 100644 * treat 0 as special, such as the occlusion query counters * where 0 means "disabled". diff --git a/drivers/gpu/drm/v3d/v3d_irq.c b/drivers/gpu/drm/v3d/v3d_irq.c -index e714d5318f30..ecdd79b31e72 100644 +index b2d59a168697..3c6fb8062ac5 100644 --- a/drivers/gpu/drm/v3d/v3d_irq.c +++ b/drivers/gpu/drm/v3d/v3d_irq.c @@ -14,21 +14,23 @@ @@ -88738,15 +90743,16 @@ index e714d5318f30..ecdd79b31e72 100644 + v3d->gpu_queue_stats[V3D_BIN].last_exec_end = local_clock(); trace_v3d_bcl_irq(&v3d->drm, fence->seqno); - dma_fence_signal(&fence->base); -@@ -109,15 +112,18 @@ v3d_irq(int irq, void *arg) + +@@ -112,6 +115,7 @@ v3d_irq(int irq, void *arg) if (intsts & V3D_INT_FRDONE) { struct v3d_fence *fence = to_v3d_fence(v3d->render_job->base.irq_fence); + v3d->gpu_queue_stats[V3D_RENDER].last_exec_end = local_clock(); trace_v3d_rcl_irq(&v3d->drm, fence->seqno); - dma_fence_signal(&fence->base); + +@@ -121,9 +125,11 @@ v3d_irq(int irq, void *arg) status = IRQ_HANDLED; } @@ -88758,8 +90764,8 @@ index e714d5318f30..ecdd79b31e72 100644 + v3d->gpu_queue_stats[V3D_CSD].last_exec_end = local_clock(); trace_v3d_csd_irq(&v3d->drm, fence->seqno); - dma_fence_signal(&fence->base); -@@ -127,7 +133,7 @@ v3d_irq(int irq, void *arg) + +@@ -136,7 +142,7 @@ v3d_irq(int irq, void *arg) /* We shouldn't be triggering these if we have GMP in * always-allowed mode. */ @@ -88768,15 +90774,15 @@ index e714d5318f30..ecdd79b31e72 100644 dev_err(v3d->drm.dev, "GMP violation\n"); /* V3D 4.2 wires the hub and core IRQs together, so if we & -@@ -154,6 +160,7 @@ v3d_hub_irq(int irq, void *arg) +@@ -163,6 +169,7 @@ v3d_hub_irq(int irq, void *arg) if (intsts & V3D_HUB_INT_TFUC) { struct v3d_fence *fence = to_v3d_fence(v3d->tfu_job->base.irq_fence); + v3d->gpu_queue_stats[V3D_TFU].last_exec_end = local_clock(); trace_v3d_tfu_irq(&v3d->drm, fence->seqno); - dma_fence_signal(&fence->base); -@@ -177,6 +184,7 @@ v3d_hub_irq(int irq, void *arg) + +@@ -189,6 +196,7 @@ v3d_hub_irq(int irq, void *arg) "GMP", }; const char *client = "?"; @@ -88784,7 +90790,7 @@ index e714d5318f30..ecdd79b31e72 100644 V3D_WRITE(V3D_MMU_CTL, V3D_READ(V3D_MMU_CTL)); -@@ -186,6 +194,7 @@ v3d_hub_irq(int irq, void *arg) +@@ -198,6 +206,7 @@ v3d_hub_irq(int irq, void *arg) client = v3d41_axi_ids[axi_id]; } @@ -88792,7 +90798,7 @@ index e714d5318f30..ecdd79b31e72 100644 dev_err(v3d->drm.dev, "MMU error from client %s (%d) at 0x%llx%s%s%s\n", client, axi_id, (long long)vio_addr, ((intsts & V3D_HUB_INT_MMU_WRV) ? -@@ -194,6 +203,12 @@ v3d_hub_irq(int irq, void *arg) +@@ -206,6 +215,12 @@ v3d_hub_irq(int irq, void *arg) ", pte invalid" : ""), ((intsts & V3D_HUB_INT_MMU_CAP) ? ", cap exceeded" : "")); @@ -88805,7 +90811,7 @@ index e714d5318f30..ecdd79b31e72 100644 status = IRQ_HANDLED; } -@@ -211,8 +226,8 @@ v3d_irq_init(struct v3d_dev *v3d) +@@ -223,8 +238,8 @@ v3d_irq_init(struct v3d_dev *v3d) * for us. */ for (core = 0; core < v3d->cores; core++) @@ -88816,7 +90822,7 @@ index e714d5318f30..ecdd79b31e72 100644 irq1 = platform_get_irq_optional(v3d_to_pdev(v3d), 1); if (irq1 == -EPROBE_DEFER) -@@ -256,12 +271,12 @@ v3d_irq_enable(struct v3d_dev *v3d) +@@ -268,12 +283,12 @@ v3d_irq_enable(struct v3d_dev *v3d) /* Enable our set of interrupts, masking out any others. */ for (core = 0; core < v3d->cores; core++) { @@ -88833,7 +90839,7 @@ index e714d5318f30..ecdd79b31e72 100644 } void -@@ -276,8 +291,8 @@ v3d_irq_disable(struct v3d_dev *v3d) +@@ -288,8 +303,8 @@ v3d_irq_disable(struct v3d_dev *v3d) /* Clear any pending interrupts we might have left. */ for (core = 0; core < v3d->cores; core++) @@ -88845,7 +90851,7 @@ index e714d5318f30..ecdd79b31e72 100644 cancel_work_sync(&v3d->overflow_mem_work); } diff --git a/drivers/gpu/drm/v3d/v3d_mmu.c b/drivers/gpu/drm/v3d/v3d_mmu.c -index 5a453532901f..14f3af40d6f6 100644 +index 166d4a88daee..e36ec3343b06 100644 --- a/drivers/gpu/drm/v3d/v3d_mmu.c +++ b/drivers/gpu/drm/v3d/v3d_mmu.c @@ -21,8 +21,6 @@ @@ -91557,7 +93563,7 @@ index 1b3531374967..220e8625402b 100644 { .compatible = "brcm,cygnus-vc4", }, {}, diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h -index bf66499765fb..0868cdbf1fce 100644 +index ac4ad95b3643..90c4bc7d31b1 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.h +++ b/drivers/gpu/drm/vc4/vc4_drv.h @@ -14,6 +14,7 @@ @@ -91605,7 +93611,7 @@ index bf66499765fb..0868cdbf1fce 100644 struct vc4_hang_state *hang_state; -@@ -309,13 +322,48 @@ struct vc4_v3d { +@@ -309,6 +322,34 @@ struct vc4_v3d { struct debugfs_regset32 regset; }; @@ -91640,9 +93646,8 @@ index bf66499765fb..0868cdbf1fce 100644 struct vc4_hvs { struct vc4_dev *vc4; struct platform_device *pdev; - void __iomem *regs; - u32 __iomem *dlist; -+ unsigned int dlist_mem_size; +@@ -317,6 +358,14 @@ struct vc4_hvs { + unsigned int dlist_mem_size; struct clk *core_clk; + struct clk *disp_clk; @@ -91651,10 +93656,12 @@ index bf66499765fb..0868cdbf1fce 100644 + unsigned int desc; + unsigned int enabled: 1; + } eof_irq[HVS_NUM_CHANNELS]; ++ ++ bool bg_fill[HVS_NUM_CHANNELS]; unsigned long max_core_rate; -@@ -323,11 +371,24 @@ struct vc4_hvs { +@@ -324,11 +373,24 @@ struct vc4_hvs { * list. Units are dwords. */ struct drm_mm dlist_mm; @@ -91679,7 +93686,7 @@ index bf66499765fb..0868cdbf1fce 100644 struct debugfs_regset32 regset; -@@ -346,7 +407,7 @@ struct vc4_hvs { +@@ -347,7 +409,7 @@ struct vc4_hvs { bool vc5_hdmi_enable_4096by2160; }; @@ -91688,7 +93695,7 @@ index bf66499765fb..0868cdbf1fce 100644 struct vc4_hvs_state { struct drm_private_state base; -@@ -393,7 +454,7 @@ struct vc4_plane_state { +@@ -394,7 +456,7 @@ struct vc4_plane_state { */ u32 pos0_offset; u32 pos2_offset; @@ -91697,7 +93704,7 @@ index bf66499765fb..0868cdbf1fce 100644 u32 lbm_offset; /* Offset where the plane's dlist was last stored in the -@@ -403,7 +464,7 @@ struct vc4_plane_state { +@@ -404,7 +466,7 @@ struct vc4_plane_state { /* Clipped coordinates of the plane on the display. */ int crtc_x, crtc_y, crtc_w, crtc_h; @@ -91706,7 +93713,7 @@ index bf66499765fb..0868cdbf1fce 100644 u32 src_x, src_y; u32 src_w[2], src_h[2]; -@@ -413,13 +474,14 @@ struct vc4_plane_state { +@@ -414,13 +476,14 @@ struct vc4_plane_state { bool is_unity; bool is_yuv; @@ -91727,7 +93734,7 @@ index bf66499765fb..0868cdbf1fce 100644 /* Set when the plane has per-pixel alpha content or does not cover * the entire screen. This is a hint to the CRTC that it might need -@@ -455,7 +517,8 @@ enum vc4_encoder_type { +@@ -456,7 +519,8 @@ enum vc4_encoder_type { VC4_ENCODER_TYPE_DSI1, VC4_ENCODER_TYPE_SMI, VC4_ENCODER_TYPE_DPI, @@ -91737,7 +93744,7 @@ index bf66499765fb..0868cdbf1fce 100644 }; struct vc4_encoder { -@@ -469,6 +532,7 @@ struct vc4_encoder { +@@ -470,6 +534,7 @@ struct vc4_encoder { void (*post_crtc_disable)(struct drm_encoder *encoder, struct drm_atomic_state *state); void (*post_crtc_powerdown)(struct drm_encoder *encoder, struct drm_atomic_state *state); @@ -91745,7 +93752,7 @@ index bf66499765fb..0868cdbf1fce 100644 }; #define to_vc4_encoder(_encoder) \ -@@ -490,6 +554,17 @@ struct drm_encoder *vc4_find_encoder_by_type(struct drm_device *drm, +@@ -491,6 +556,17 @@ struct drm_encoder *vc4_find_encoder_by_type(struct drm_device *drm, return NULL; } @@ -91763,7 +93770,7 @@ index bf66499765fb..0868cdbf1fce 100644 struct vc4_crtc_data { const char *name; -@@ -502,7 +577,18 @@ struct vc4_crtc_data { +@@ -503,7 +579,18 @@ struct vc4_crtc_data { int hvs_output; }; @@ -91783,7 +93790,7 @@ index bf66499765fb..0868cdbf1fce 100644 struct vc4_pv_data { struct vc4_crtc_data base; -@@ -512,6 +598,8 @@ struct vc4_pv_data { +@@ -513,6 +600,8 @@ struct vc4_pv_data { /* Number of pixels output per clock period */ u8 pixels_per_clock; @@ -91792,7 +93799,7 @@ index bf66499765fb..0868cdbf1fce 100644 enum vc4_encoder_type encoder_types[4]; }; -@@ -524,6 +612,8 @@ extern const struct vc4_pv_data bcm2711_pv1_data; +@@ -525,6 +614,8 @@ extern const struct vc4_pv_data bcm2711_pv1_data; extern const struct vc4_pv_data bcm2711_pv2_data; extern const struct vc4_pv_data bcm2711_pv3_data; extern const struct vc4_pv_data bcm2711_pv4_data; @@ -91801,7 +93808,7 @@ index bf66499765fb..0868cdbf1fce 100644 struct vc4_crtc { struct drm_crtc base; -@@ -534,9 +624,19 @@ struct vc4_crtc { +@@ -535,9 +626,19 @@ struct vc4_crtc { /* Timestamp at start of vblank irq - unaffected by lock delays. */ ktime_t t_vblank; @@ -91824,7 +93831,7 @@ index bf66499765fb..0868cdbf1fce 100644 struct drm_pending_vblank_event *event; -@@ -587,22 +687,27 @@ vc4_crtc_to_vc4_pv_data(const struct vc4_crtc *crtc) +@@ -588,22 +689,27 @@ vc4_crtc_to_vc4_pv_data(const struct vc4_crtc *crtc) return container_of_const(data, struct vc4_pv_data, base); } @@ -91860,7 +93867,7 @@ index bf66499765fb..0868cdbf1fce 100644 unsigned long hvs_load; -@@ -639,6 +744,12 @@ struct vc4_crtc_state { +@@ -640,6 +746,12 @@ struct vc4_crtc_state { writel(val, hvs->regs + (offset)); \ } while (0) @@ -91873,7 +93880,7 @@ index bf66499765fb..0868cdbf1fce 100644 #define VC4_REG32(reg) { .name = #reg, .offset = reg } struct vc4_exec_info { -@@ -963,6 +1074,9 @@ extern struct platform_driver vc4_dsi_driver; +@@ -964,6 +1076,9 @@ extern struct platform_driver vc4_dsi_driver; /* vc4_fence.c */ extern const struct dma_fence_ops vc4_fence_ops; @@ -91883,7 +93890,7 @@ index bf66499765fb..0868cdbf1fce 100644 /* vc4_gem.c */ int vc4_gem_init(struct drm_device *dev); int vc4_submit_cl_ioctl(struct drm_device *dev, void *data, -@@ -1001,10 +1115,14 @@ void vc4_irq_reset(struct drm_device *dev); +@@ -1002,10 +1117,14 @@ void vc4_irq_reset(struct drm_device *dev); /* vc4_hvs.c */ extern struct platform_driver vc4_hvs_driver; @@ -91899,7 +93906,7 @@ index bf66499765fb..0868cdbf1fce 100644 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); -@@ -1022,6 +1140,12 @@ int vc4_kms_load(struct drm_device *dev); +@@ -1023,6 +1142,12 @@ int vc4_kms_load(struct drm_device *dev); struct drm_plane *vc4_plane_init(struct drm_device *dev, enum drm_plane_type type, uint32_t possible_crtcs); @@ -91913,10 +93920,39 @@ index bf66499765fb..0868cdbf1fce 100644 u32 vc4_plane_write_dlist(struct drm_plane *plane, u32 __iomem *dlist); u32 vc4_plane_dlist_size(const struct drm_plane_state *state); diff --git a/drivers/gpu/drm/vc4/vc4_dsi.c b/drivers/gpu/drm/vc4/vc4_dsi.c -index 46f6c4ce61c5..78f232656172 100644 +index 46f6c4ce61c5..a50c6e6bc7f9 100644 --- a/drivers/gpu/drm/vc4/vc4_dsi.c +++ b/drivers/gpu/drm/vc4/vc4_dsi.c -@@ -286,6 +286,8 @@ +@@ -44,7 +44,6 @@ + + #define DSI_CMD_FIFO_DEPTH 16 + #define DSI_PIX_FIFO_DEPTH 256 +-#define DSI_PIX_FIFO_WIDTH 4 + + #define DSI0_CTRL 0x00 + +@@ -170,11 +169,15 @@ + #define DSI1_DISP1_CTRL 0x2c + /* Format of the data written to TXPKT_PIX_FIFO. */ + # define DSI_DISP1_PFORMAT_MASK VC4_MASK(2, 1) +-# define DSI_DISP1_PFORMAT_SHIFT 1 +-# define DSI_DISP1_PFORMAT_16BIT 0 +-# define DSI_DISP1_PFORMAT_24BIT 1 +-# define DSI_DISP1_PFORMAT_32BIT_LE 2 +-# define DSI_DISP1_PFORMAT_32BIT_BE 3 ++# define DSI1_DISP1_PFORMAT_SHIFT 1 ++# define DSI0_DISP1_PFORMAT_16BIT 0 ++# define DSI0_DISP1_PFORMAT_16BIT_ADJ 1 ++# define DSI0_DISP1_PFORMAT_24BIT 2 ++# define DSI0_DISP1_PFORMAT_32BIT_LE 3 /* NB Invalid, but required for macros to work */ ++# define DSI1_DISP1_PFORMAT_16BIT 0 ++# define DSI1_DISP1_PFORMAT_24BIT 1 ++# define DSI1_DISP1_PFORMAT_32BIT_LE 2 ++# define DSI1_DISP1_PFORMAT_32BIT_BE 3 + + /* DISP1 is always command mode. */ + # define DSI_DISP1_ENABLE BIT(0) +@@ -286,6 +289,8 @@ DSI1_INT_PR_TO) #define DSI0_STAT 0x2c @@ -91925,7 +93961,7 @@ index 46f6c4ce61c5..78f232656172 100644 #define DSI0_HSTX_TO_CNT 0x30 #define DSI0_LPRX_TO_CNT 0x34 #define DSI0_TA_TO_CNT 0x38 -@@ -358,6 +360,16 @@ +@@ -358,6 +363,16 @@ # define DSI_PHY_AFEC0_CTATADJ_MASK VC4_MASK(3, 0) # define DSI_PHY_AFEC0_CTATADJ_SHIFT 0 @@ -91942,7 +93978,7 @@ index 46f6c4ce61c5..78f232656172 100644 #define DSI0_PHY_AFEC1 0x68 # define DSI0_PHY_AFEC1_IDR_DLANE1_MASK VC4_MASK(10, 8) # define DSI0_PHY_AFEC1_IDR_DLANE1_SHIFT 8 -@@ -398,7 +410,8 @@ +@@ -398,7 +413,8 @@ # define DSI1_CTRL_DISABLE_DISP_ECCC BIT(1) # define DSI0_CTRL_CTRL0 BIT(0) # define DSI1_CTRL_EN BIT(0) @@ -91952,7 +93988,15 @@ index 46f6c4ce61c5..78f232656172 100644 DSI0_CTRL_CLR_PBCF | \ DSI0_CTRL_CLR_CPBCF | \ DSI0_CTRL_CLR_PDF | \ -@@ -815,6 +828,15 @@ static void vc4_dsi_bridge_post_disable(struct drm_bridge *bridge, +@@ -540,6 +556,7 @@ struct vc4_dsi_variant { + unsigned int port; + + bool broken_axi_workaround; ++ unsigned int cmd_fifo_width; + + const char *debugfs_name; + const struct debugfs_reg32 *regs; +@@ -815,6 +832,15 @@ static void vc4_dsi_bridge_post_disable(struct drm_bridge *bridge, struct vc4_dsi *dsi = bridge_to_vc4_dsi(bridge); struct device *dev = &dsi->pdev->dev; @@ -91968,7 +94012,7 @@ index 46f6c4ce61c5..78f232656172 100644 clk_disable_unprepare(dsi->pll_phy_clock); clk_disable_unprepare(dsi->escape_clock); clk_disable_unprepare(dsi->pixel_clock); -@@ -845,6 +867,7 @@ static bool vc4_dsi_bridge_mode_fixup(struct drm_bridge *bridge, +@@ -845,6 +871,7 @@ static bool vc4_dsi_bridge_mode_fixup(struct drm_bridge *bridge, unsigned long pixel_clock_hz = mode->clock * 1000; unsigned long pll_clock = pixel_clock_hz * dsi->divider; int divider; @@ -91976,7 +94020,7 @@ index 46f6c4ce61c5..78f232656172 100644 /* Find what divider gets us a faster clock than the requested * pixel clock. -@@ -861,12 +884,27 @@ static bool vc4_dsi_bridge_mode_fixup(struct drm_bridge *bridge, +@@ -861,12 +888,27 @@ static bool vc4_dsi_bridge_mode_fixup(struct drm_bridge *bridge, pixel_clock_hz = pll_clock / dsi->divider; adjusted_mode->clock = pixel_clock_hz / 1000; @@ -92007,7 +94051,7 @@ index 46f6c4ce61c5..78f232656172 100644 return true; } -@@ -926,12 +964,31 @@ static void vc4_dsi_bridge_pre_enable(struct drm_bridge *bridge, +@@ -926,12 +968,31 @@ static void vc4_dsi_bridge_pre_enable(struct drm_bridge *bridge, "Failed to set phy clock to %ld: %d\n", phy_clock, ret); } @@ -92040,7 +94084,7 @@ index 46f6c4ce61c5..78f232656172 100644 DSI_CTRL_HSDT_EOT_DISABLE | DSI_CTRL_RX_LPDT_EOT_DISABLE); -@@ -984,20 +1041,6 @@ static void vc4_dsi_bridge_pre_enable(struct drm_bridge *bridge, +@@ -984,20 +1045,6 @@ static void vc4_dsi_bridge_pre_enable(struct drm_bridge *bridge, mdelay(1); } @@ -92061,20 +94105,33 @@ index 46f6c4ce61c5..78f232656172 100644 /* Yes, we set the DSI0P/DSI1P pixel clock to the byte rate, * not the pixel clock rate. DSIxP take from the APHY's byte, * DDR2, or DDR4 clock (we use byte) and feed into the PV at -@@ -1113,12 +1156,6 @@ static void vc4_dsi_bridge_pre_enable(struct drm_bridge *bridge, - DSI_DISP1_PFORMAT) | - DSI_DISP1_ENABLE); - +@@ -1108,16 +1155,16 @@ static void vc4_dsi_bridge_pre_enable(struct drm_bridge *bridge, + /* Set up DISP1 for transferring long command payloads through + * the pixfifo. + */ +- DSI_PORT_WRITE(DISP1_CTRL, +- VC4_SET_FIELD(DSI_DISP1_PFORMAT_32BIT_LE, +- DSI_DISP1_PFORMAT) | +- DSI_DISP1_ENABLE); +- - /* Ungate the block. */ - if (dsi->variant->port == 0) - DSI_PORT_WRITE(CTRL, DSI_PORT_READ(CTRL) | DSI0_CTRL_CTRL0); -- else ++ if (dsi->variant->cmd_fifo_width == 4) ++ DSI_PORT_WRITE(DISP1_CTRL, ++ VC4_SET_FIELD(DSI_PORT_BIT(DISP1_PFORMAT_32BIT_LE), ++ DSI_DISP1_PFORMAT) | ++ DSI_DISP1_ENABLE); + else - DSI_PORT_WRITE(CTRL, DSI_PORT_READ(CTRL) | DSI1_CTRL_EN); -- ++ DSI_PORT_WRITE(DISP1_CTRL, ++ VC4_SET_FIELD(DSI_PORT_BIT(DISP1_PFORMAT_24BIT), ++ DSI_DISP1_PFORMAT) | ++ DSI_DISP1_ENABLE); + /* Bring AFE out of reset. */ DSI_PORT_WRITE(PHY_AFEC0, - DSI_PORT_READ(PHY_AFEC0) & -@@ -1168,10 +1205,9 @@ static int vc4_dsi_bridge_attach(struct drm_bridge *bridge, +@@ -1168,10 +1215,9 @@ static int vc4_dsi_bridge_attach(struct drm_bridge *bridge, &dsi->bridge, flags); } @@ -92087,7 +94144,53 @@ index 46f6c4ce61c5..78f232656172 100644 struct mipi_dsi_packet packet; u32 pkth = 0, pktc = 0; int i, ret; -@@ -1280,10 +1316,12 @@ static ssize_t vc4_dsi_host_transfer(struct mipi_dsi_host *host, +@@ -1199,9 +1245,9 @@ static ssize_t vc4_dsi_host_transfer(struct mipi_dsi_host *host, + pix_fifo_len = 0; + } else { + cmd_fifo_len = (packet.payload_length % +- DSI_PIX_FIFO_WIDTH); ++ dsi->variant->cmd_fifo_width); + pix_fifo_len = ((packet.payload_length - cmd_fifo_len) / +- DSI_PIX_FIFO_WIDTH); ++ dsi->variant->cmd_fifo_width); + } + + WARN_ON_ONCE(pix_fifo_len >= DSI_PIX_FIFO_DEPTH); +@@ -1219,14 +1265,25 @@ static ssize_t vc4_dsi_host_transfer(struct mipi_dsi_host *host, + + for (i = 0; i < cmd_fifo_len; i++) + DSI_PORT_WRITE(TXPKT_CMD_FIFO, packet.payload[i]); +- for (i = 0; i < pix_fifo_len; i++) { +- const u8 *pix = packet.payload + cmd_fifo_len + i * 4; +- +- DSI_PORT_WRITE(TXPKT_PIX_FIFO, +- pix[0] | +- pix[1] << 8 | +- pix[2] << 16 | +- pix[3] << 24); ++ if (dsi->variant->cmd_fifo_width == 4) { ++ for (i = 0; i < pix_fifo_len; i++) { ++ const u8 *pix = packet.payload + cmd_fifo_len + i * 4; ++ ++ DSI_PORT_WRITE(TXPKT_PIX_FIFO, ++ pix[0] | ++ pix[1] << 8 | ++ pix[2] << 16 | ++ pix[3] << 24); ++ } ++ } else { ++ for (i = 0; i < pix_fifo_len; i++) { ++ const u8 *pix = packet.payload + cmd_fifo_len + i * 3; ++ ++ DSI_PORT_WRITE(TXPKT_PIX_FIFO, ++ pix[2] | ++ pix[1] << 8 | ++ pix[0] << 16); ++ } + } + + if (msg->flags & MIPI_DSI_MSG_USE_LPM) +@@ -1280,10 +1337,12 @@ static ssize_t vc4_dsi_host_transfer(struct mipi_dsi_host *host, DSI_PORT_WRITE(TXPKT1C, pktc); if (!wait_for_completion_timeout(&dsi->xfer_completion, @@ -92104,7 +94207,7 @@ index 46f6c4ce61c5..78f232656172 100644 ret = -ETIMEDOUT; } else { ret = dsi->xfer_result; -@@ -1326,7 +1364,8 @@ static ssize_t vc4_dsi_host_transfer(struct mipi_dsi_host *host, +@@ -1326,7 +1385,8 @@ static ssize_t vc4_dsi_host_transfer(struct mipi_dsi_host *host, return ret; reset_fifo_and_return: @@ -92114,7 +94217,7 @@ index 46f6c4ce61c5..78f232656172 100644 DSI_PORT_WRITE(TXPKT1C, DSI_PORT_READ(TXPKT1C) & ~DSI_TXPKT1C_CMD_EN); udelay(1); -@@ -1339,6 +1378,40 @@ static ssize_t vc4_dsi_host_transfer(struct mipi_dsi_host *host, +@@ -1339,6 +1399,40 @@ static ssize_t vc4_dsi_host_transfer(struct mipi_dsi_host *host, return ret; } @@ -92155,7 +94258,7 @@ index 46f6c4ce61c5..78f232656172 100644 static const struct component_ops vc4_dsi_ops; static int vc4_dsi_host_attach(struct mipi_dsi_host *host, struct mipi_dsi_device *device) -@@ -1418,6 +1491,15 @@ static const struct drm_bridge_funcs vc4_dsi_bridge_funcs = { +@@ -1418,6 +1512,15 @@ static const struct drm_bridge_funcs vc4_dsi_bridge_funcs = { .mode_fixup = vc4_dsi_bridge_mode_fixup, }; @@ -92171,7 +94274,31 @@ index 46f6c4ce61c5..78f232656172 100644 static int vc4_dsi_late_register(struct drm_encoder *encoder) { struct drm_device *drm = encoder->dev; -@@ -1662,6 +1744,9 @@ static int vc4_dsi_bind(struct device *dev, struct device *master, void *data) +@@ -1434,6 +1537,7 @@ static const struct drm_encoder_funcs vc4_dsi_encoder_funcs = { + + static const struct vc4_dsi_variant bcm2711_dsi1_variant = { + .port = 1, ++ .cmd_fifo_width = 4, + .debugfs_name = "dsi1_regs", + .regs = dsi1_regs, + .nregs = ARRAY_SIZE(dsi1_regs), +@@ -1441,6 +1545,7 @@ static const struct vc4_dsi_variant bcm2711_dsi1_variant = { + + static const struct vc4_dsi_variant bcm2835_dsi0_variant = { + .port = 0, ++ .cmd_fifo_width = 3, + .debugfs_name = "dsi0_regs", + .regs = dsi0_regs, + .nregs = ARRAY_SIZE(dsi0_regs), +@@ -1448,6 +1553,7 @@ static const struct vc4_dsi_variant bcm2835_dsi0_variant = { + + static const struct vc4_dsi_variant bcm2835_dsi1_variant = { + .port = 1, ++ .cmd_fifo_width = 4, + .broken_axi_workaround = true, + .debugfs_name = "dsi1_regs", + .regs = dsi1_regs, +@@ -1662,6 +1768,9 @@ static int vc4_dsi_bind(struct device *dev, struct device *master, void *data) dsi->encoder.type = dsi->variant->port ? VC4_ENCODER_TYPE_DSI1 : VC4_ENCODER_TYPE_DSI0; @@ -94377,7 +96504,7 @@ index 03648f954985..0d94165d4b6b 100644 switch (args->madv) { diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c -index d4487f4cb303..e1f2ce494241 100644 +index 1727d447786f..e1f2ce494241 100644 --- a/drivers/gpu/drm/vc4/vc4_hdmi.c +++ b/drivers/gpu/drm/vc4/vc4_hdmi.c @@ -41,6 +41,8 @@ @@ -94694,15 +96821,6 @@ index d4487f4cb303..e1f2ce494241 100644 if (mode->flags & DRM_MODE_FLAG_DBLCLK) { /* Only try to fixup DBLCLK modes to get 480i and 576i * working. -@@ -2396,7 +2495,7 @@ static int vc4_hdmi_audio_startup(struct device *dev, void *data) - } - - if (!vc4_hdmi_audio_can_stream(vc4_hdmi)) { -- ret = -ENODEV; -+ ret = -ENOTSUPP; - goto out_dev_exit; - } - @@ -2523,6 +2622,7 @@ static int vc4_hdmi_audio_prepare(struct device *dev, void *data, { struct vc4_hdmi *vc4_hdmi = dev_get_drvdata(dev); @@ -95999,7 +98117,7 @@ index b04b2fc8d831..59bfd69f54d9 100644 writel(value, base + field->offset); } diff --git a/drivers/gpu/drm/vc4/vc4_hvs.c b/drivers/gpu/drm/vc4/vc4_hvs.c -index 04af672caacb..02a9bb43035d 100644 +index bea1b3d35cdd..2eefa4a4d299 100644 --- a/drivers/gpu/drm/vc4/vc4_hvs.c +++ b/drivers/gpu/drm/vc4/vc4_hvs.c @@ -33,7 +33,7 @@ @@ -96151,41 +98269,10 @@ index 04af672caacb..02a9bb43035d 100644 void vc4_hvs_dump_state(struct vc4_hvs *hvs) { struct drm_device *drm = &hvs->vc4->base; -@@ -110,7 +243,8 @@ static int vc4_hvs_debugfs_dlist(struct seq_file *m, void *data) - struct vc4_dev *vc4 = to_vc4_dev(dev); - struct vc4_hvs *hvs = vc4->hvs; - struct drm_printer p = drm_seq_file_printer(m); -- unsigned int next_entry_start = 0; -+ unsigned int dlist_mem_size = hvs->dlist_mem_size; -+ unsigned int next_entry_start; - unsigned int i, j; - u32 dlist_word, dispstat; - -@@ -123,9 +257,59 @@ static int vc4_hvs_debugfs_dlist(struct seq_file *m, void *data) - continue; - } +@@ -145,6 +278,206 @@ static int vc4_hvs_debugfs_dlist(struct seq_file *m, void *data) + return 0; + } -+ drm_printf(&p, "HVS chan %u:\n", i); -+ next_entry_start = 0; -+ -+ for (j = HVS_READ(SCALER_DISPLISTX(i)); j < dlist_mem_size; j++) { -+ dlist_word = readl((u32 __iomem *)vc4->hvs->dlist + j); -+ drm_printf(&p, "dlist: %02d: 0x%08x\n", j, -+ dlist_word); -+ if (!next_entry_start || -+ next_entry_start == j) { -+ if (dlist_word & SCALER_CTL0_END) -+ break; -+ next_entry_start = j + -+ VC4_GET_FIELD(dlist_word, -+ SCALER_CTL0_SIZE); -+ } -+ } -+ } -+ -+ return 0; -+} -+ +static int vc6_hvs_debugfs_dlist(struct seq_file *m, void *data) +{ + struct drm_debugfs_entry *entry = m->private; @@ -96209,9 +98296,8 @@ index 04af672caacb..02a9bb43035d 100644 + continue; + } + - drm_printf(&p, "HVS chan %u:\n", i); - -- for (j = HVS_READ(SCALER_DISPLISTX(i)); j < 256; j++) { ++ drm_printf(&p, "HVS chan %u:\n", i); ++ + active_dlist = VC4_GET_FIELD6(HVS_READ(SCALER6_DISPX_DL(i)), + DISPX_DL_LACT); + next_entry_start = 0; @@ -96219,13 +98305,23 @@ index 04af672caacb..02a9bb43035d 100644 + for (j = active_dlist; j < dlist_mem_size; j++) { + u32 dlist_word; + - dlist_word = readl((u32 __iomem *)vc4->hvs->dlist + j); - drm_printf(&p, "dlist: %02d: 0x%08x\n", j, - dlist_word); -@@ -143,6 +327,157 @@ static int vc4_hvs_debugfs_dlist(struct seq_file *m, void *data) - return 0; - } - ++ dlist_word = readl((u32 __iomem *)vc4->hvs->dlist + j); ++ drm_printf(&p, "dlist: %02d: 0x%08x\n", j, ++ dlist_word); ++ if (!next_entry_start || ++ next_entry_start == j) { ++ if (dlist_word & SCALER_CTL0_END) ++ break; ++ next_entry_start = j + ++ VC4_GET_FIELD(dlist_word, ++ SCALER_CTL0_SIZE); ++ } ++ } ++ } ++ ++ return 0; ++} ++ +static int vc5_hvs_debugfs_gamma(struct seq_file *m, void *data) +{ + struct drm_debugfs_entry *entry = m->private; @@ -96380,7 +98476,7 @@ index 04af672caacb..02a9bb43035d 100644 /* The filter kernel is composed of dwords each containing 3 9-bit * signed integers packed next to each other. */ -@@ -176,6 +511,9 @@ static int vc4_hvs_debugfs_dlist(struct seq_file *m, void *data) +@@ -178,6 +511,9 @@ static int vc4_hvs_debugfs_dlist(struct seq_file *m, void *data) static const u32 mitchell_netravali_1_3_1_3_kernel[] = VC4_LINEAR_PHASE_KERNEL(0, -2, -6, -8, -10, -8, -3, 2, 18, 50, 82, 119, 155, 187, 213, 227); @@ -96390,7 +98486,7 @@ index 04af672caacb..02a9bb43035d 100644 static int vc4_hvs_upload_linear_kernel(struct vc4_hvs *hvs, struct drm_mm_node *space, -@@ -213,12 +551,15 @@ static int vc4_hvs_upload_linear_kernel(struct vc4_hvs *hvs, +@@ -215,16 +551,16 @@ static int vc4_hvs_upload_linear_kernel(struct vc4_hvs *hvs, static void vc4_hvs_lut_load(struct vc4_hvs *hvs, struct vc4_crtc *vc4_crtc) { @@ -96402,12 +98498,16 @@ index 04af672caacb..02a9bb43035d 100644 int idx; u32 i; +- if (!drm_dev_enter(drm, &idx)) +- return; + WARN_ON_ONCE(vc4->gen > VC4_GEN_5); -+ - if (!drm_dev_enter(drm, &idx)) + +- if (hvs->vc4->is_vc5) ++ if (!drm_dev_enter(drm, &idx)) return; -@@ -243,7 +584,8 @@ static void vc4_hvs_lut_load(struct vc4_hvs *hvs, + /* The LUT memory is laid out with each HVS channel in order, +@@ -248,7 +584,8 @@ static void vc4_hvs_lut_load(struct vc4_hvs *hvs, static void vc4_hvs_update_gamma_lut(struct vc4_hvs *hvs, struct vc4_crtc *vc4_crtc) { @@ -96417,24 +98517,18 @@ index 04af672caacb..02a9bb43035d 100644 struct drm_color_lut *lut = crtc_state->gamma_lut->data; u32 length = drm_color_lut_size(crtc_state->gamma_lut); u32 i; -@@ -257,218 +599,740 @@ static void vc4_hvs_update_gamma_lut(struct vc4_hvs *hvs, +@@ -262,27 +599,396 @@ static void vc4_hvs_update_gamma_lut(struct vc4_hvs *hvs, vc4_hvs_lut_load(hvs, vc4_crtc); } --u8 vc4_hvs_get_fifo_frame_count(struct vc4_hvs *hvs, unsigned int fifo) +static void vc5_hvs_write_gamma_entry(struct vc4_hvs *hvs, + u32 offset, + struct vc5_gamma_entry *gamma) - { -- struct drm_device *drm = &hvs->vc4->base; -- u8 field = 0; -- int idx; ++{ + HVS_WRITE(offset, gamma->x_c_terms); + HVS_WRITE(offset + 4, gamma->grad_term); +} - -- if (!drm_dev_enter(drm, &idx)) -- return 0; ++ +static void vc5_hvs_lut_load(struct vc4_hvs *hvs, + struct vc4_crtc *vc4_crtc) +{ @@ -96458,20 +98552,7 @@ index 04af672caacb..02a9bb43035d 100644 + vc5_hvs_write_gamma_entry(hvs, offset, &vc4_crtc->pwl_a[i]); + } +} - -- switch (fifo) { -- case 0: -- field = VC4_GET_FIELD(HVS_READ(SCALER_DISPSTAT1), -- SCALER_DISPSTAT1_FRCNT0); -- break; -- case 1: -- field = VC4_GET_FIELD(HVS_READ(SCALER_DISPSTAT1), -- SCALER_DISPSTAT1_FRCNT1); -- break; -- case 2: -- field = VC4_GET_FIELD(HVS_READ(SCALER_DISPSTAT2), -- SCALER_DISPSTAT2_FRCNT2); -- break; ++ +static void vc5_hvs_update_gamma_lut(struct vc4_hvs *hvs, + struct vc4_crtc *vc4_crtc) +{ @@ -96510,120 +98591,75 @@ index 04af672caacb..02a9bb43035d 100644 + 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); - } - -- drm_dev_exit(idx); -- return field; ++ } ++ + vc5_hvs_lut_load(hvs, vc4_crtc); - } - --int vc4_hvs_get_fifo_from_output(struct vc4_hvs *hvs, unsigned int output) ++} ++ +static void vc4_hvs_irq_enable_eof(struct vc4_hvs *hvs, + unsigned int channel) - { - struct vc4_dev *vc4 = hvs->vc4; -- u32 reg; -- int ret; -- -- if (!vc4->is_vc5) -- return output; - -- /* -- * NOTE: We should probably use drm_dev_enter()/drm_dev_exit() -- * here, but this function is only used during the DRM device -- * initialization, so we should be fine. -- */ ++{ ++ struct vc4_dev *vc4 = hvs->vc4; ++ + if (hvs->eof_irq[channel].enabled) + return; - -- switch (output) { -- case 0: -- return 0; ++ + switch (vc4->gen) { + case VC4_GEN_4: + HVS_WRITE(SCALER_DISPCTRL, + HVS_READ(SCALER_DISPCTRL) | + SCALER_DISPCTRL_DSPEIEOF(channel)); + break; - -- case 1: -- return 1; ++ + case VC4_GEN_5: + HVS_WRITE(SCALER_DISPCTRL, + HVS_READ(SCALER_DISPCTRL) | + SCALER5_DISPCTRL_DSPEIEOF(channel)); + break; - -- case 2: -- reg = HVS_READ(SCALER_DISPECTRL); -- ret = FIELD_GET(SCALER_DISPECTRL_DSP2_MUX_MASK, reg); -- if (ret == 0) -- return 2; ++ + case VC4_GEN_6: + enable_irq(hvs->eof_irq[channel].desc); + break; - -- return 0; ++ + default: + break; + } - -- case 3: -- reg = HVS_READ(SCALER_DISPCTRL); -- ret = FIELD_GET(SCALER_DISPCTRL_DSP3_MUX_MASK, reg); -- if (ret == 3) -- return -EPIPE; ++ + hvs->eof_irq[channel].enabled = true; +} - -- return ret; ++ +static void vc4_hvs_irq_clear_eof(struct vc4_hvs *hvs, + unsigned int channel) +{ + struct vc4_dev *vc4 = hvs->vc4; - -- case 4: -- reg = HVS_READ(SCALER_DISPEOLN); -- ret = FIELD_GET(SCALER_DISPEOLN_DSP4_MUX_MASK, reg); -- if (ret == 3) -- return -EPIPE; ++ + if (!hvs->eof_irq[channel].enabled) + return; - -- return ret; ++ + switch (vc4->gen) { + case VC4_GEN_4: + HVS_WRITE(SCALER_DISPCTRL, + HVS_READ(SCALER_DISPCTRL) & + ~SCALER_DISPCTRL_DSPEIEOF(channel)); + break; - -- case 5: -- reg = HVS_READ(SCALER_DISPDITHER); -- ret = FIELD_GET(SCALER_DISPDITHER_DSP5_MUX_MASK, reg); -- if (ret == 3) -- return -EPIPE; ++ + case VC4_GEN_5: + HVS_WRITE(SCALER_DISPCTRL, + HVS_READ(SCALER_DISPCTRL) & + ~SCALER5_DISPCTRL_DSPEIEOF(channel)); + break; - -- return ret; ++ + case VC4_GEN_6: + disable_irq_nosync(hvs->eof_irq[channel].desc); + break; - - default: -- return -EPIPE; ++ ++ default: + break; - } ++ } + + hvs->eof_irq[channel].enabled = false; - } - --static int vc4_hvs_init_channel(struct vc4_hvs *hvs, struct drm_crtc *crtc, -- struct drm_display_mode *mode, bool oneshot) ++} ++ +static void vc4_hvs_free_dlist_entry_locked(struct vc4_hvs *hvs, + struct vc4_hvs_dlist_allocation *alloc); + @@ -96631,58 +98667,23 @@ index 04af672caacb..02a9bb43035d 100644 +vc4_hvs_alloc_dlist_entry(struct vc4_hvs *hvs, + unsigned int channel, + size_t dlist_count) - { - struct vc4_dev *vc4 = hvs->vc4; -- struct drm_device *drm = &vc4->base; -- struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc); -- struct vc4_crtc_state *vc4_crtc_state = to_vc4_crtc_state(crtc->state); -- unsigned int chan = vc4_crtc_state->assigned_channel; -- bool interlace = mode->flags & DRM_MODE_FLAG_INTERLACE; -- u32 dispbkgndx; -- u32 dispctrl; -- int idx; ++{ ++ struct vc4_dev *vc4 = hvs->vc4; + struct drm_device *dev = &vc4->base; + struct vc4_hvs_dlist_allocation *alloc; + struct vc4_hvs_dlist_allocation *cur, *next; + unsigned long flags; + int ret; - -- if (!drm_dev_enter(drm, &idx)) -- return -ENODEV; ++ + if (channel == VC4_HVS_CHANNEL_DISABLED) + return NULL; - -- HVS_WRITE(SCALER_DISPCTRLX(chan), 0); -- HVS_WRITE(SCALER_DISPCTRLX(chan), SCALER_DISPCTRLX_RESET); -- HVS_WRITE(SCALER_DISPCTRLX(chan), 0); ++ + alloc = kzalloc(sizeof(*alloc), GFP_KERNEL); + if (!alloc) + return ERR_PTR(-ENOMEM); - -- /* Turn on the scaler, which will wait for vstart to start -- * compositing. -- * When feeding the transposer, we should operate in oneshot -- * mode. -- */ -- dispctrl = SCALER_DISPCTRLX_ENABLE; -- dispbkgndx = HVS_READ(SCALER_DISPBKGNDX(chan)); ++ + INIT_LIST_HEAD(&alloc->node); - -- if (!vc4->is_vc5) { -- dispctrl |= VC4_SET_FIELD(mode->hdisplay, -- SCALER_DISPCTRLX_WIDTH) | -- VC4_SET_FIELD(mode->vdisplay, -- SCALER_DISPCTRLX_HEIGHT) | -- (oneshot ? SCALER_DISPCTRLX_ONESHOT : 0); -- dispbkgndx |= SCALER_DISPBKGND_AUTOHS; -- } else { -- dispctrl |= VC4_SET_FIELD(mode->hdisplay, -- SCALER5_DISPCTRLX_WIDTH) | -- VC4_SET_FIELD(mode->vdisplay, -- SCALER5_DISPCTRLX_HEIGHT) | -- (oneshot ? SCALER5_DISPCTRLX_ONESHOT : 0); -- dispbkgndx &= ~SCALER5_DISPBKGND_BCK2BCK; -- } ++ + spin_lock_irqsave(&hvs->mm_lock, flags); + ret = drm_mm_insert_node(&hvs->dlist_mm, &alloc->mm_node, + dlist_count); @@ -96701,66 +98702,44 @@ index 04af672caacb..02a9bb43035d 100644 + list_for_each_entry_safe(cur, next, &hvs->stale_dlist_entries, node) { + vc4_hvs_free_dlist_entry_locked(hvs, cur); + } - -- HVS_WRITE(SCALER_DISPCTRLX(chan), dispctrl); ++ + ret = drm_mm_insert_node(&hvs->dlist_mm, &alloc->mm_node, + dlist_count); + spin_unlock_irqrestore(&hvs->mm_lock, flags); - -- dispbkgndx &= ~SCALER_DISPBKGND_GAMMA; -- dispbkgndx &= ~SCALER_DISPBKGND_INTERLACE; ++ + if (ret) + return ERR_PTR(ret); + } - -- HVS_WRITE(SCALER_DISPBKGNDX(chan), dispbkgndx | -- ((!vc4->is_vc5) ? SCALER_DISPBKGND_GAMMA : 0) | -- (interlace ? SCALER_DISPBKGND_INTERLACE : 0)); ++ + alloc->channel = channel; - -- /* Reload the LUT, since the SRAMs would have been disabled if -- * all CRTCs had SCALER_DISPBKGND_GAMMA unset at once. -- */ -- vc4_hvs_lut_load(hvs, vc4_crtc); ++ + return alloc; +} - -- drm_dev_exit(idx); ++ +static void vc4_hvs_free_dlist_entry_locked(struct vc4_hvs *hvs, + struct vc4_hvs_dlist_allocation *alloc) +{ + lockdep_assert_held(&hvs->mm_lock); - -- return 0; ++ + if (!list_empty(&alloc->node)) + list_del(&alloc->node); + + drm_mm_remove_node(&alloc->mm_node); + kfree(alloc); - } - --void vc4_hvs_stop_channel(struct vc4_hvs *hvs, unsigned int chan) ++} ++ +void vc4_hvs_mark_dlist_entry_stale(struct vc4_hvs *hvs, + struct vc4_hvs_dlist_allocation *alloc) - { -- struct drm_device *drm = &hvs->vc4->base; -- int idx; ++{ + unsigned long flags; + u8 frcnt; - -- if (!drm_dev_enter(drm, &idx)) ++ + if (!alloc) - return; - -- if (HVS_READ(SCALER_DISPCTRLX(chan)) & SCALER_DISPCTRLX_ENABLE) -- goto out; ++ return; ++ + if (!drm_mm_node_allocated(&alloc->mm_node)) + return; - -- HVS_WRITE(SCALER_DISPCTRLX(chan), -- HVS_READ(SCALER_DISPCTRLX(chan)) | SCALER_DISPCTRLX_RESET); -- HVS_WRITE(SCALER_DISPCTRLX(chan), -- HVS_READ(SCALER_DISPCTRLX(chan)) & ~SCALER_DISPCTRLX_ENABLE); ++ + /* + * Kunit tests run with a mock device and we consider any hardware + * access a test failure. Let's free the dlist allocation right away if @@ -96775,46 +98754,25 @@ index 04af672caacb..02a9bb43035d 100644 + spin_unlock_irqrestore(&hvs->mm_lock, flags); + return; + } - -- /* Once we leave, the scaler should be disabled and its fifo empty. */ -- WARN_ON_ONCE(HVS_READ(SCALER_DISPCTRLX(chan)) & SCALER_DISPCTRLX_RESET); ++ + frcnt = vc4_hvs_get_fifo_frame_count(hvs, alloc->channel); + alloc->target_frame_count = (frcnt + 1) & ((1 << 6) - 1); - -- WARN_ON_ONCE(VC4_GET_FIELD(HVS_READ(SCALER_DISPSTATX(chan)), -- SCALER_DISPSTATX_MODE) != -- SCALER_DISPSTATX_MODE_DISABLED); ++ + spin_lock_irqsave(&hvs->mm_lock, flags); - -- WARN_ON_ONCE((HVS_READ(SCALER_DISPSTATX(chan)) & -- (SCALER_DISPSTATX_FULL | SCALER_DISPSTATX_EMPTY)) != -- SCALER_DISPSTATX_EMPTY); ++ + list_add_tail(&alloc->node, &hvs->stale_dlist_entries); - --out: -- drm_dev_exit(idx); ++ + HVS_WRITE(SCALER_DISPSTAT, SCALER_DISPSTAT_EOF(alloc->channel)); + vc4_hvs_irq_enable_eof(hvs, alloc->channel); + + spin_unlock_irqrestore(&hvs->mm_lock, flags); - } - --int vc4_hvs_atomic_check(struct drm_crtc *crtc, struct drm_atomic_state *state) ++} ++ +static void vc4_hvs_schedule_dlist_sweep(struct vc4_hvs *hvs, + unsigned int channel) - { -- struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc); -- struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(crtc_state); -- struct drm_device *dev = crtc->dev; -- struct vc4_dev *vc4 = to_vc4_dev(dev); -- struct drm_plane *plane; - unsigned long flags; -- const struct drm_plane_state *plane_state; -- u32 dlist_count = 0; -- int ret; - -- /* The pixelvalve can only feed one encoder (and encoders are -- * 1:1 with connectors.) ++{ ++ unsigned long flags; ++ + spin_lock_irqsave(&hvs->mm_lock, flags); + + if (!list_empty(&hvs->stale_dlist_entries)) @@ -96905,23 +98863,31 @@ index 04af672caacb..02a9bb43035d 100644 + spin_unlock_irqrestore(&hvs->mm_lock, flags); +} + -+u8 vc4_hvs_get_fifo_frame_count(struct vc4_hvs *hvs, unsigned int fifo) -+{ + u8 vc4_hvs_get_fifo_frame_count(struct vc4_hvs *hvs, unsigned int fifo) + { +- struct drm_device *drm = &hvs->vc4->base; + struct vc4_dev *vc4 = hvs->vc4; + struct drm_device *drm = &vc4->base; -+ u8 field = 0; -+ int idx; -+ + u8 field = 0; + int idx; + + WARN_ON_ONCE(vc4->gen > VC4_GEN_6); + -+ if (!drm_dev_enter(drm, &idx)) -+ return 0; -+ + if (!drm_dev_enter(drm, &idx)) + return 0; + +- switch (fifo) { +- case 0: +- field = VC4_GET_FIELD(HVS_READ(SCALER_DISPSTAT1), +- SCALER_DISPSTAT1_FRCNT0); + switch (vc4->gen) { + case VC4_GEN_6: + field = VC4_GET_FIELD6(HVS_READ(SCALER6_DISPX_STATUS(fifo)), + DISPX_STATUS_FRCNT); -+ break; + break; +- case 1: +- field = VC4_GET_FIELD(HVS_READ(SCALER_DISPSTAT1), +- SCALER_DISPSTAT1_FRCNT1); + case VC4_GEN_5: + switch (fifo) { + case 0: @@ -96937,7 +98903,10 @@ index 04af672caacb..02a9bb43035d 100644 + SCALER5_DISPSTAT2_FRCNT2); + break; + } -+ break; + break; +- case 2: +- field = VC4_GET_FIELD(HVS_READ(SCALER_DISPSTAT2), +- SCALER_DISPSTAT2_FRCNT2); + case VC4_GEN_4: + switch (fifo) { + case 0: @@ -96953,25 +98922,25 @@ index 04af672caacb..02a9bb43035d 100644 + SCALER_DISPSTAT2_FRCNT2); + break; + } -+ break; -+ } -+ -+ drm_dev_exit(idx); -+ return field; -+} -+ -+int vc4_hvs_get_fifo_from_output(struct vc4_hvs *hvs, unsigned int output) -+{ -+ struct vc4_dev *vc4 = hvs->vc4; -+ u32 reg; -+ int ret; -+ + break; + } + +@@ -296,57 +1002,82 @@ int vc4_hvs_get_fifo_from_output(struct vc4_hvs *hvs, unsigned int output) + u32 reg; + int ret; + +- if (!vc4->is_vc5) + WARN_ON_ONCE(vc4->gen > VC4_GEN_6); + + switch (vc4->gen) { + case VC4_GEN_4: -+ return output; -+ + return output; + +- /* +- * NOTE: We should probably use drm_dev_enter()/drm_dev_exit() +- * here, but this function is only used during the DRM device +- * initialization, so we should be fine. +- */ + case VC4_GEN_5: + /* + * NOTE: We should probably use @@ -96979,36 +98948,59 @@ index 04af672caacb..02a9bb43035d 100644 + * function is only used during the DRM device + * initialization, so we should be fine. + */ -+ + +- switch (output) { +- case 0: +- return 0; + switch (output) { + case 0: + return 0; -+ + +- case 1: +- return 1; + case 1: + return 1; -+ + +- case 2: +- reg = HVS_READ(SCALER_DISPECTRL); +- ret = FIELD_GET(SCALER_DISPECTRL_DSP2_MUX_MASK, reg); +- if (ret == 0) +- return 2; + case 2: + reg = HVS_READ(SCALER_DISPECTRL); + ret = FIELD_GET(SCALER_DISPECTRL_DSP2_MUX_MASK, reg); + if (ret == 0) + return 2; -+ + +- return 0; + return 0; -+ + +- case 3: +- reg = HVS_READ(SCALER_DISPCTRL); +- ret = FIELD_GET(SCALER_DISPCTRL_DSP3_MUX_MASK, reg); +- if (ret == 3) +- return -EPIPE; + case 3: + reg = HVS_READ(SCALER_DISPCTRL); + ret = FIELD_GET(SCALER_DISPCTRL_DSP3_MUX_MASK, reg); + if (ret == 3) + return -EPIPE; -+ + +- return ret; + return ret; -+ + +- case 4: +- reg = HVS_READ(SCALER_DISPEOLN); +- ret = FIELD_GET(SCALER_DISPEOLN_DSP4_MUX_MASK, reg); +- if (ret == 3) +- return -EPIPE; + case 4: + reg = HVS_READ(SCALER_DISPEOLN); + ret = FIELD_GET(SCALER_DISPEOLN_DSP4_MUX_MASK, reg); + if (ret == 3) + return -EPIPE; -+ + +- return ret; + return ret; + + case 5: @@ -97018,16 +99010,23 @@ index 04af672caacb..02a9bb43035d 100644 + return -EPIPE; + + return ret; -+ + +- case 5: +- reg = HVS_READ(SCALER_DISPDITHER); +- ret = FIELD_GET(SCALER_DISPDITHER_DSP5_MUX_MASK, reg); +- if (ret == 3) + default: -+ return -EPIPE; + return -EPIPE; + } -+ + +- return ret; + case VC4_GEN_6: + switch (output) { + case 0: + return 0; -+ + +- default: +- return -EPIPE; + case 2: + return 2; + @@ -97039,93 +99038,69 @@ index 04af672caacb..02a9bb43035d 100644 + default: + return -EPIPE; + } -+ } + } + + return -EPIPE; -+} -+ -+static int vc4_hvs_init_channel(struct vc4_hvs *hvs, struct drm_crtc *crtc, -+ struct drm_display_mode *mode, bool oneshot) -+{ -+ struct vc4_dev *vc4 = hvs->vc4; -+ struct drm_device *drm = &vc4->base; -+ struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc); -+ struct vc4_crtc_state *vc4_crtc_state = to_vc4_crtc_state(crtc->state); -+ unsigned int chan = vc4_crtc_state->assigned_channel; -+ bool interlace = mode->flags & DRM_MODE_FLAG_INTERLACE; -+ u32 dispbkgndx; -+ u32 dispctrl; -+ int idx; -+ + } + + static int vc4_hvs_init_channel(struct vc4_hvs *hvs, struct drm_crtc *crtc, +@@ -362,6 +1093,8 @@ static int vc4_hvs_init_channel(struct vc4_hvs *hvs, struct drm_crtc *crtc, + u32 dispctrl; + int idx; + + WARN_ON_ONCE(vc4->gen > VC4_GEN_5); + -+ if (!drm_dev_enter(drm, &idx)) -+ return -ENODEV; -+ -+ HVS_WRITE(SCALER_DISPCTRLX(chan), 0); -+ HVS_WRITE(SCALER_DISPCTRLX(chan), SCALER_DISPCTRLX_RESET); -+ HVS_WRITE(SCALER_DISPCTRLX(chan), 0); -+ -+ /* Turn on the scaler, which will wait for vstart to start -+ * compositing. -+ * When feeding the transposer, we should operate in oneshot -+ * mode. -+ */ -+ dispctrl = SCALER_DISPCTRLX_ENABLE; -+ dispbkgndx = HVS_READ(SCALER_DISPBKGNDX(chan)); -+ + if (!drm_dev_enter(drm, &idx)) + return -ENODEV; + +@@ -377,7 +1110,7 @@ static int vc4_hvs_init_channel(struct vc4_hvs *hvs, struct drm_crtc *crtc, + dispctrl = SCALER_DISPCTRLX_ENABLE; + dispbkgndx = HVS_READ(SCALER_DISPBKGNDX(chan)); + +- if (!vc4->is_vc5) { + if (vc4->gen == VC4_GEN_4) { -+ dispctrl |= VC4_SET_FIELD(mode->hdisplay, -+ SCALER_DISPCTRLX_WIDTH) | -+ VC4_SET_FIELD(mode->vdisplay, -+ SCALER_DISPCTRLX_HEIGHT) | -+ (oneshot ? SCALER_DISPCTRLX_ONESHOT : 0); -+ dispbkgndx |= SCALER_DISPBKGND_AUTOHS; -+ } else { -+ dispctrl |= VC4_SET_FIELD(mode->hdisplay, -+ SCALER5_DISPCTRLX_WIDTH) | -+ VC4_SET_FIELD(mode->vdisplay, -+ SCALER5_DISPCTRLX_HEIGHT) | -+ (oneshot ? SCALER5_DISPCTRLX_ONESHOT : 0); -+ dispbkgndx &= ~SCALER5_DISPBKGND_BCK2BCK; -+ } -+ -+ HVS_WRITE(SCALER_DISPCTRLX(chan), dispctrl); -+ -+ dispbkgndx &= ~SCALER_DISPBKGND_GAMMA; -+ dispbkgndx &= ~SCALER_DISPBKGND_INTERLACE; -+ + dispctrl |= VC4_SET_FIELD(mode->hdisplay, + SCALER_DISPCTRLX_WIDTH) | + VC4_SET_FIELD(mode->vdisplay, +@@ -398,25 +1131,70 @@ static int vc4_hvs_init_channel(struct vc4_hvs *hvs, 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 | -+ (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. -+ */ + HVS_WRITE(SCALER_DISPBKGNDX(chan), dispbkgndx | +- ((!vc4->is_vc5) ? 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(hvs, vc4_crtc); + if (vc4->gen == VC4_GEN_4) + vc4_hvs_lut_load(hvs, vc4_crtc); + else + vc5_hvs_lut_load(hvs, vc4_crtc); -+ -+ drm_dev_exit(idx); -+ -+ return 0; -+} -+ + + drm_dev_exit(idx); + + return 0; + } + +-void vc4_hvs_stop_channel(struct vc4_hvs *hvs, unsigned int chan) +static int vc6_hvs_init_channel(struct vc4_hvs *hvs, struct drm_crtc *crtc, + struct drm_display_mode *mode, bool oneshot) -+{ + { +- struct drm_device *drm = &hvs->vc4->base; + struct vc4_dev *vc4 = hvs->vc4; + struct drm_device *drm = &vc4->base; + struct vc4_crtc_state *vc4_crtc_state = to_vc4_crtc_state(crtc->state); + unsigned int chan = vc4_crtc_state->assigned_channel; + bool interlace = mode->flags & DRM_MODE_FLAG_INTERLACE; + u32 disp_ctrl1; -+ int idx; -+ + int idx; + + WARN_ON_ONCE(vc4->gen != VC4_GEN_6); + + if (!drm_dev_enter(drm, &idx)) @@ -97159,22 +99134,16 @@ index 04af672caacb..02a9bb43035d 100644 + + WARN_ON_ONCE(vc4->gen > VC4_GEN_5); + -+ if (!drm_dev_enter(drm, &idx)) -+ return; -+ -+ if (!(HVS_READ(SCALER_DISPCTRLX(chan)) & SCALER_DISPCTRLX_ENABLE)) -+ goto out; -+ -+ HVS_WRITE(SCALER_DISPCTRLX(chan), SCALER_DISPCTRLX_RESET); -+ HVS_WRITE(SCALER_DISPCTRLX(chan), 0); -+ -+ /* Once we leave, the scaler should be disabled and its fifo empty. */ -+ WARN_ON_ONCE(HVS_READ(SCALER_DISPCTRLX(chan)) & SCALER_DISPCTRLX_RESET); -+ -+ WARN_ON_ONCE(VC4_GET_FIELD(HVS_READ(SCALER_DISPSTATX(chan)), -+ SCALER_DISPSTATX_MODE) != -+ SCALER_DISPSTATX_MODE_DISABLED); -+ + if (!drm_dev_enter(drm, &idx)) + return; + +@@ -433,25 +1211,98 @@ void vc4_hvs_stop_channel(struct vc4_hvs *hvs, unsigned int chan) + SCALER_DISPSTATX_MODE) != + SCALER_DISPSTATX_MODE_DISABLED); + +- WARN_ON_ONCE((HVS_READ(SCALER_DISPSTATX(chan)) & +- (SCALER_DISPSTATX_FULL | SCALER_DISPSTATX_EMPTY)) != +- SCALER_DISPSTATX_EMPTY); +out: + drm_dev_exit(idx); +} @@ -97202,11 +99171,11 @@ index 04af672caacb..02a9bb43035d 100644 + WARN_ON_ONCE(VC4_GET_FIELD6(HVS_READ(SCALER6_DISPX_STATUS(chan)), + DISPX_STATUS_MODE) != + SCALER6(DISPX_STATUS_MODE_DISABLED)); -+ -+out: -+ drm_dev_exit(idx); -+} -+ + + out: + drm_dev_exit(idx); + } + +void vc4_hvs_stop_channel(struct vc4_hvs *hvs, unsigned int chan) +{ + struct vc4_dev *vc4 = hvs->vc4; @@ -97257,20 +99226,22 @@ index 04af672caacb..02a9bb43035d 100644 + 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); -+ struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(crtc_state); + 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); + struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(crtc_state); + struct vc4_hvs_dlist_allocation *alloc; -+ struct drm_device *dev = crtc->dev; -+ struct vc4_dev *vc4 = to_vc4_dev(dev); -+ struct drm_plane *plane; -+ const struct drm_plane_state *plane_state; -+ u32 dlist_count = 0; -+ -+ /* The pixelvalve can only feed one encoder (and encoders are -+ * 1:1 with connectors.) - */ + struct drm_device *dev = crtc->dev; + struct vc4_dev *vc4 = to_vc4_dev(dev); + struct drm_plane *plane; +- unsigned long flags; + const struct drm_plane_state *plane_state; + u32 dlist_count = 0; +- int ret; + + /* The pixelvalve can only feed one encoder (and encoders are + * 1:1 with connectors.) +@@ -459,19 +1310,29 @@ int vc4_hvs_atomic_check(struct drm_crtc *crtc, struct drm_atomic_state *state) if (hweight32(crtc_state->connector_mask) > 1) return -EINVAL; @@ -97309,7 +99280,7 @@ index 04af672caacb..02a9bb43035d 100644 } static void vc4_hvs_install_dlist(struct drm_crtc *crtc) -@@ -482,8 +1346,16 @@ static void vc4_hvs_install_dlist(struct drm_crtc *crtc) +@@ -485,8 +1346,16 @@ static void vc4_hvs_install_dlist(struct drm_crtc *crtc) if (!drm_dev_enter(dev, &idx)) return; @@ -97328,7 +99299,7 @@ index 04af672caacb..02a9bb43035d 100644 drm_dev_exit(idx); } -@@ -510,8 +1382,10 @@ static void vc4_hvs_update_dlist(struct drm_crtc *crtc) +@@ -513,8 +1382,10 @@ static void vc4_hvs_update_dlist(struct drm_crtc *crtc) spin_unlock_irqrestore(&dev->event_lock, flags); } @@ -97340,7 +99311,7 @@ index 04af672caacb..02a9bb43035d 100644 spin_unlock_irqrestore(&vc4_crtc->irq_lock, flags); } -@@ -538,7 +1412,11 @@ void vc4_hvs_atomic_enable(struct drm_crtc *crtc, +@@ -541,7 +1412,11 @@ void vc4_hvs_atomic_enable(struct drm_crtc *crtc, vc4_hvs_install_dlist(crtc); vc4_hvs_update_dlist(crtc); @@ -97353,7 +99324,7 @@ index 04af672caacb..02a9bb43035d 100644 } void vc4_hvs_atomic_disable(struct drm_crtc *crtc, -@@ -567,13 +1445,14 @@ void vc4_hvs_atomic_flush(struct drm_crtc *crtc, +@@ -570,13 +1445,14 @@ void vc4_hvs_atomic_flush(struct drm_crtc *crtc, struct drm_plane *plane; struct vc4_plane_state *vc4_plane_state; bool debug_dump_regs = false; @@ -97371,7 +99342,7 @@ index 04af672caacb..02a9bb43035d 100644 if (!drm_dev_enter(dev, &idx)) { vc4_crtc_send_vblank(crtc); return; -@@ -587,6 +1466,9 @@ void vc4_hvs_atomic_flush(struct drm_crtc *crtc, +@@ -590,6 +1466,9 @@ void vc4_hvs_atomic_flush(struct drm_crtc *crtc, vc4_hvs_dump_state(hvs); } @@ -97381,7 +99352,7 @@ index 04af672caacb..02a9bb43035d 100644 /* Copy all the active planes' dlist contents to the hardware dlist. */ do { found = false; -@@ -620,15 +1502,29 @@ void vc4_hvs_atomic_flush(struct drm_crtc *crtc, +@@ -623,15 +1502,22 @@ void vc4_hvs_atomic_flush(struct drm_crtc *crtc, writel(SCALER_CTL0_END, dlist_next); dlist_next++; @@ -97394,14 +99365,7 @@ index 04af672caacb..02a9bb43035d 100644 /* This sets a black background color fill, as is the case * with other DRM drivers. */ -+ if (enable_bg_fill) -+ HVS_WRITE(SCALER6_DISPX_CTRL1(channel), -+ HVS_READ(SCALER6_DISPX_CTRL1(channel)) | -+ SCALER6(DISPX_CTRL1_BGENB)); -+ else -+ HVS_WRITE(SCALER6_DISPX_CTRL1(channel), -+ HVS_READ(SCALER6_DISPX_CTRL1(channel)) & -+ ~SCALER6(DISPX_CTRL1_BGENB)); ++ hvs->bg_fill[channel] = enable_bg_fill; + } else { + /* we can actually run with a lower core clock when background + * fill is enabled on VC4_GEN_5 so leave it enabled always. @@ -97413,7 +99377,7 @@ index 04af672caacb..02a9bb43035d 100644 /* Only update DISPLIST if the CRTC was already running and is not * being disabled. -@@ -645,15 +1541,28 @@ void vc4_hvs_atomic_flush(struct drm_crtc *crtc, +@@ -648,15 +1534,28 @@ void vc4_hvs_atomic_flush(struct drm_crtc *crtc, if (crtc->state->color_mgmt_changed) { u32 dispbkgndx = HVS_READ(SCALER_DISPBKGNDX(channel)); @@ -97445,7 +99409,7 @@ index 04af672caacb..02a9bb43035d 100644 } HVS_WRITE(SCALER_DISPBKGNDX(channel), dispbkgndx); } -@@ -668,16 +1577,20 @@ void vc4_hvs_atomic_flush(struct drm_crtc *crtc, +@@ -672,16 +1571,20 @@ void vc4_hvs_atomic_flush(struct drm_crtc *crtc, void vc4_hvs_mask_underrun(struct vc4_hvs *hvs, int channel) { @@ -97469,7 +99433,7 @@ index 04af672caacb..02a9bb43035d 100644 HVS_WRITE(SCALER_DISPCTRL, dispctrl); -@@ -686,16 +1599,20 @@ void vc4_hvs_mask_underrun(struct vc4_hvs *hvs, int channel) +@@ -690,16 +1593,20 @@ void vc4_hvs_mask_underrun(struct vc4_hvs *hvs, int channel) void vc4_hvs_unmask_underrun(struct vc4_hvs *hvs, int channel) { @@ -97493,7 +99457,7 @@ index 04af672caacb..02a9bb43035d 100644 HVS_WRITE(SCALER_DISPSTAT, SCALER_DISPSTAT_EUFLOW(channel)); -@@ -723,6 +1640,8 @@ static irqreturn_t vc4_hvs_irq_handler(int irq, void *data) +@@ -727,6 +1634,8 @@ static irqreturn_t vc4_hvs_irq_handler(int irq, void *data) u32 status; u32 dspeislur; @@ -97502,7 +99466,7 @@ index 04af672caacb..02a9bb43035d 100644 /* * NOTE: We don't need to protect the register access using * drm_dev_enter() there because the interrupt handler lifetime -@@ -738,8 +1657,10 @@ static irqreturn_t vc4_hvs_irq_handler(int irq, void *data) +@@ -742,8 +1651,10 @@ static irqreturn_t vc4_hvs_irq_handler(int irq, void *data) control = HVS_READ(SCALER_DISPCTRL); for (channel = 0; channel < SCALER_CHANNELS_COUNT; channel++) { @@ -97515,7 +99479,7 @@ index 04af672caacb..02a9bb43035d 100644 /* Interrupt masking is not always honored, so check it here. */ if (status & SCALER_DISPSTAT_EUFLOW(channel) && control & dspeislur) { -@@ -748,14 +1669,42 @@ static irqreturn_t vc4_hvs_irq_handler(int irq, void *data) +@@ -752,6 +1663,11 @@ static irqreturn_t vc4_hvs_irq_handler(int irq, void *data) irqret = IRQ_HANDLED; } @@ -97526,18 +99490,11 @@ index 04af672caacb..02a9bb43035d 100644 + } } -- /* Clear every per-channel interrupt flag. */ -- HVS_WRITE(SCALER_DISPSTAT, SCALER_DISPSTAT_IRQMASK(0) | -- SCALER_DISPSTAT_IRQMASK(1) | -- SCALER_DISPSTAT_IRQMASK(2)); -+ /* Clear every per-channel interrupt flag. */ -+ HVS_WRITE(SCALER_DISPSTAT, SCALER_DISPSTAT_IRQMASK(0) | -+ SCALER_DISPSTAT_IRQMASK(1) | -+ SCALER_DISPSTAT_IRQMASK(2)); -+ -+ return irqret; -+} -+ + /* Clear every per-channel interrupt flag. */ +@@ -762,143 +1678,207 @@ static irqreturn_t vc4_hvs_irq_handler(int irq, void *data) + return irqret; + } + +static irqreturn_t vc6_hvs_eof_irq_handler(int irq, void *data) +{ + struct drm_device *dev = data; @@ -97554,16 +99511,25 @@ index 04af672caacb..02a9bb43035d 100644 + if (hvs->eof_irq[i].desc != irq) + continue; + ++ if (hvs->bg_fill[i]) ++ HVS_WRITE(SCALER6_DISPX_CTRL1(i), ++ HVS_READ(SCALER6_DISPX_CTRL1(i)) | ++ SCALER6(DISPX_CTRL1_BGENB)); ++ else ++ HVS_WRITE(SCALER6_DISPX_CTRL1(i), ++ HVS_READ(SCALER6_DISPX_CTRL1(i)) & ++ ~SCALER6(DISPX_CTRL1_BGENB)); ++ + vc4_hvs_schedule_dlist_sweep(hvs, i); + return IRQ_HANDLED; + } - -- return irqret; ++ + return IRQ_NONE; - } - ++} ++ int vc4_hvs_debugfs_init(struct drm_minor *minor) -@@ -764,136 +1713,164 @@ int vc4_hvs_debugfs_init(struct drm_minor *minor) + { + struct drm_device *drm = minor->dev; struct vc4_dev *vc4 = to_vc4_dev(drm); struct vc4_hvs *hvs = vc4->hvs; @@ -97624,14 +99590,6 @@ index 04af672caacb..02a9bb43035d 100644 spin_lock_init(&hvs->mm_lock); -- /* Set up the HVS display list memory manager. We never -- * overwrite the setup from the bootloader (just 128b out of -- * our 16K), since we don't want to scramble the screen when -- * transitioning from the firmware's boot setup to runtime. -- */ -- drm_mm_init(&hvs->dlist_mm, -- HVS_BOOTLOADER_DLIST_END, -- (SCALER_DLIST_SIZE >> 2) - HVS_BOOTLOADER_DLIST_END); + INIT_LIST_HEAD(&hvs->stale_dlist_entries); + INIT_WORK(&hvs->free_dlist_work, vc4_hvs_dlist_free_work); + @@ -97672,9 +99630,16 @@ index 04af672caacb..02a9bb43035d 100644 + return ERR_PTR(-ENODEV); + } + -+ drm_mm_init(&hvs->dlist_mm, dlist_start, dlist_size); -+ -+ hvs->dlist_mem_size = dlist_size; + /* Set up the HVS display list memory manager. We never + * overwrite the setup from the bootloader (just 128b out of + * our 16K), since we don't want to scramble the screen when + * transitioning from the firmware's boot setup to runtime. + */ +- hvs->dlist_mem_size = (SCALER_DLIST_SIZE >> 2) - HVS_BOOTLOADER_DLIST_END; + drm_mm_init(&hvs->dlist_mm, + HVS_BOOTLOADER_DLIST_END, +- hvs->dlist_mem_size); ++ (SCALER_DLIST_SIZE >> 2) - HVS_BOOTLOADER_DLIST_END); /* Set up the HVS LBM memory manager. We could have some more * complicated data structure that allowed reuse of LBM areas @@ -97687,12 +99652,12 @@ index 04af672caacb..02a9bb43035d 100644 - else - /* 60k words of 4x12-bit pixels */ - drm_mm_init(&hvs->lbm_mm, 0, 60 * 1024); - +- - vc4->hvs = hvs; - - return hvs; -} -- + -static int vc4_hvs_bind(struct device *dev, struct device *master, void *data) -{ - struct platform_device *pdev = to_platform_device(dev); @@ -97812,7 +99777,7 @@ index 04af672caacb..02a9bb43035d 100644 reg = HVS_READ(SCALER_DISPECTRL); reg &= ~SCALER_DISPECTRL_DSP2_MUX_MASK; -@@ -916,13 +1893,11 @@ static int vc4_hvs_bind(struct device *dev, struct device *master, void *data) +@@ -921,13 +1901,11 @@ static int vc4_hvs_bind(struct device *dev, struct device *master, void *data) reg | VC4_SET_FIELD(3, SCALER_DISPDITHER_DSP5_MUX)); dispctrl = HVS_READ(SCALER_DISPCTRL); @@ -97827,25 +99792,7 @@ index 04af672caacb..02a9bb43035d 100644 dispctrl &= ~(SCALER_DISPCTRL_DMAEIRQ | SCALER_DISPCTRL_SLVWREIRQ | SCALER_DISPCTRL_SLVRDEIRQ | -@@ -951,6 +1926,17 @@ static int vc4_hvs_bind(struct device *dev, struct device *master, void *data) - SCALER_DISPCTRL_SCLEIRQ); - - -+ /* Set AXI panic mode. -+ * VC4 panics when < 2 lines in FIFO. -+ * VC5 panics when less than 1 line in the FIFO. -+ */ -+ dispctrl &= ~(SCALER_DISPCTRL_PANIC0_MASK | -+ SCALER_DISPCTRL_PANIC1_MASK | -+ SCALER_DISPCTRL_PANIC2_MASK); -+ dispctrl |= VC4_SET_FIELD(2, SCALER_DISPCTRL_PANIC0); -+ dispctrl |= VC4_SET_FIELD(2, SCALER_DISPCTRL_PANIC1); -+ dispctrl |= VC4_SET_FIELD(2, SCALER_DISPCTRL_PANIC2); -+ - /* Set AXI panic mode. - * VC4 panics when < 2 lines in FIFO. - * VC5 panics when less than 1 line in the FIFO. -@@ -964,9 +1950,160 @@ static int vc4_hvs_bind(struct device *dev, struct device *master, void *data) +@@ -980,9 +1958,160 @@ static int vc4_hvs_bind(struct device *dev, struct device *master, void *data) HVS_WRITE(SCALER_DISPCTRL, dispctrl); @@ -98008,7 +99955,7 @@ index 04af672caacb..02a9bb43035d 100644 /* The COB is 20736 pixels, or just over 10 lines at 2048 wide. * The bottom 2048 pixels are full 32bpp RGBA (intended for the * TXP composing RGBA to memory), whilst the remainder are only -@@ -990,7 +2127,9 @@ static int vc4_hvs_bind(struct device *dev, struct device *master, void *data) +@@ -1006,7 +2135,9 @@ static int vc4_hvs_bind(struct device *dev, struct device *master, void *data) top = VC4_COB_SIZE; reg |= (top - 1) << 16; HVS_WRITE(SCALER_DISPBASE0, reg); @@ -98019,7 +99966,7 @@ index 04af672caacb..02a9bb43035d 100644 /* The COB is 44416 pixels, or 10.8 lines at 4096 wide. * The bottom 4096 pixels are full RGBA (intended for the TXP * composing RGBA to memory), whilst the remainder are only -@@ -1016,13 +2155,187 @@ static int vc4_hvs_bind(struct device *dev, struct device *master, void *data) +@@ -1032,13 +2163,187 @@ static int vc4_hvs_bind(struct device *dev, struct device *master, void *data) top = VC5_COB_SIZE; reg |= top << 16; HVS_WRITE(SCALER_DISPBASE0, reg); @@ -98209,7 +100156,7 @@ index 04af672caacb..02a9bb43035d 100644 return 0; } -@@ -1036,6 +2349,8 @@ static void vc4_hvs_unbind(struct device *dev, struct device *master, +@@ -1052,6 +2357,8 @@ static void vc4_hvs_unbind(struct device *dev, struct device *master, if (drm_mm_node_allocated(&vc4->hvs->mitchell_netravali_filter)) drm_mm_remove_node(&vc4->hvs->mitchell_netravali_filter); @@ -98218,7 +100165,7 @@ index 04af672caacb..02a9bb43035d 100644 drm_mm_for_each_node_safe(node, next, &vc4->hvs->dlist_mm) drm_mm_remove_node(node); -@@ -1046,6 +2361,10 @@ static void vc4_hvs_unbind(struct device *dev, struct device *master, +@@ -1062,6 +2369,10 @@ static void vc4_hvs_unbind(struct device *dev, struct device *master, drm_mm_remove_node(node); drm_mm_takedown(&vc4->hvs->lbm_mm); @@ -98229,7 +100176,7 @@ index 04af672caacb..02a9bb43035d 100644 clk_disable_unprepare(hvs->core_clk); vc4->hvs = NULL; -@@ -1068,6 +2387,8 @@ static void vc4_hvs_dev_remove(struct platform_device *pdev) +@@ -1084,6 +2395,8 @@ static void vc4_hvs_dev_remove(struct platform_device *pdev) static const struct of_device_id vc4_hvs_dt_match[] = { { .compatible = "brcm,bcm2711-hvs" }, @@ -98719,7 +100666,7 @@ index c00a5cc2316d..75ff2f748235 100644 if (!vc4->v3d) { diff --git a/drivers/gpu/drm/vc4/vc4_plane.c b/drivers/gpu/drm/vc4/vc4_plane.c -index 5948e34f7f81..acedc1b89f2e 100644 +index 5948e34f7f81..fdd6ba310324 100644 --- a/drivers/gpu/drm/vc4/vc4_plane.c +++ b/drivers/gpu/drm/vc4/vc4_plane.c @@ -109,6 +109,18 @@ static const struct hvs_format { @@ -98741,7 +100688,7 @@ index 5948e34f7f81..acedc1b89f2e 100644 { .drm = DRM_FORMAT_YUV420, .hvs = HVS_PIXEL_FORMAT_YCBCR_YUV420_3PLANE, -@@ -251,9 +263,9 @@ static const struct hvs_format *vc4_get_hvs_format(u32 drm_format) +@@ -251,9 +263,13 @@ static const struct hvs_format *vc4_get_hvs_format(u32 drm_format) static enum vc4_scaling_mode vc4_get_scaling_mode(u32 src, u32 dst) { @@ -98749,11 +100696,15 @@ index 5948e34f7f81..acedc1b89f2e 100644 + if (dst == src >> 16) return VC4_SCALING_NONE; - if (3 * dst >= 2 * src) -+ if (3 * dst >= 2 * (src >> 16)) ++ ++ if (src <= (1 << 16)) ++ /* Source rectangle <= 1 pixel can use TPZ for resize/upscale */ ++ return VC4_SCALING_TPZ; ++ else if (3 * dst >= 2 * (src >> 16)) return VC4_SCALING_PPF; else return VC4_SCALING_TPZ; -@@ -264,9 +276,12 @@ static bool plane_enabled(struct drm_plane_state *state) +@@ -264,9 +280,12 @@ static bool plane_enabled(struct drm_plane_state *state) return state->fb && !WARN_ON(!state->crtc); } @@ -98767,7 +100718,7 @@ index 5948e34f7f81..acedc1b89f2e 100644 if (WARN_ON(!plane->state)) return NULL; -@@ -275,7 +290,13 @@ static struct drm_plane_state *vc4_plane_duplicate_state(struct drm_plane *plane +@@ -275,7 +294,13 @@ static struct drm_plane_state *vc4_plane_duplicate_state(struct drm_plane *plane if (!vc4_state) return NULL; @@ -98782,7 +100733,7 @@ index 5948e34f7f81..acedc1b89f2e 100644 vc4_state->dlist_initialized = 0; __drm_atomic_helper_plane_duplicate_state(plane, &vc4_state->base); -@@ -294,18 +315,63 @@ static struct drm_plane_state *vc4_plane_duplicate_state(struct drm_plane *plane +@@ -294,18 +319,63 @@ static struct drm_plane_state *vc4_plane_duplicate_state(struct drm_plane *plane return &vc4_state->base; } @@ -98839,13 +100790,13 @@ index 5948e34f7f81..acedc1b89f2e 100644 + + for (i = 0; i < DRM_FORMAT_MAX_PLANES; i++) { + struct vc4_upm_refcounts *refcount; ++ ++ if (!vc4_state->upm_handle[i]) ++ continue; - spin_lock_irqsave(&vc4->hvs->mm_lock, irqflags); - drm_mm_remove_node(&vc4_state->lbm); - spin_unlock_irqrestore(&vc4->hvs->mm_lock, irqflags); -+ if (!vc4_state->upm_handle[i]) -+ continue; -+ + refcount = &hvs->upm_refcounts[vc4_state->upm_handle[i]]; + + if (refcount_dec_and_test(&refcount->refcount)) @@ -98853,7 +100804,7 @@ index 5948e34f7f81..acedc1b89f2e 100644 } kfree(vc4_state->dlist); -@@ -314,7 +380,7 @@ static void vc4_plane_destroy_state(struct drm_plane *plane, +@@ -314,7 +384,7 @@ static void vc4_plane_destroy_state(struct drm_plane *plane, } /* Called during init to allocate the plane's atomic state. */ @@ -98862,7 +100813,7 @@ index 5948e34f7f81..acedc1b89f2e 100644 { struct vc4_plane_state *vc4_state; -@@ -438,12 +504,11 @@ static int vc4_plane_setup_clipping_and_scaling(struct drm_plane_state *state) +@@ -438,12 +508,11 @@ static int vc4_plane_setup_clipping_and_scaling(struct drm_plane_state *state) { struct vc4_plane_state *vc4_state = to_vc4_plane_state(state); struct drm_framebuffer *fb = state->fb; @@ -98876,7 +100827,7 @@ index 5948e34f7f81..acedc1b89f2e 100644 crtc_state = drm_atomic_get_existing_crtc_state(state->state, state->crtc); -@@ -457,26 +522,21 @@ static int vc4_plane_setup_clipping_and_scaling(struct drm_plane_state *state) +@@ -457,26 +526,21 @@ static int vc4_plane_setup_clipping_and_scaling(struct drm_plane_state *state) if (ret) return ret; @@ -98912,7 +100863,7 @@ index 5948e34f7f81..acedc1b89f2e 100644 ret = vc4_plane_margins_adj(state); if (ret) return ret; -@@ -510,6 +570,12 @@ static int vc4_plane_setup_clipping_and_scaling(struct drm_plane_state *state) +@@ -510,6 +574,12 @@ static int vc4_plane_setup_clipping_and_scaling(struct drm_plane_state *state) */ if (vc4_state->x_scaling[1] == VC4_SCALING_NONE) vc4_state->x_scaling[1] = VC4_SCALING_PPF; @@ -98925,7 +100876,7 @@ index 5948e34f7f81..acedc1b89f2e 100644 } else { vc4_state->is_yuv = false; vc4_state->x_scaling[1] = VC4_SCALING_NONE; -@@ -521,9 +587,12 @@ static int vc4_plane_setup_clipping_and_scaling(struct drm_plane_state *state) +@@ -521,33 +591,91 @@ static int vc4_plane_setup_clipping_and_scaling(struct drm_plane_state *state) static void vc4_write_tpz(struct vc4_plane_state *vc4_state, u32 src, u32 dst) { @@ -98934,13 +100885,22 @@ index 5948e34f7f81..acedc1b89f2e 100644 - scale = (1 << 16) * src / dst; + WARN_ON_ONCE(vc4->gen > VC4_GEN_6); -+ -+ scale = src / dst; - /* The specs note that while the reciprocal would be defined - * as (1<<32)/scale, ~0 is close enough. -@@ -531,23 +600,73 @@ static void vc4_write_tpz(struct vc4_plane_state *vc4_state, u32 src, u32 dst) - recip = ~0 / scale; +- /* The specs note that while the reciprocal would be defined +- * as (1<<32)/scale, ~0 is close enough. +- */ +- recip = ~0 / scale; ++ if ((dst << 16) < src) { ++ scale = src / dst; ++ ++ /* The specs note that while the reciprocal would be defined ++ * as (1<<32)/scale, ~0 is close enough. ++ */ ++ recip = ~0 / scale; ++ } else { ++ scale = (1 << 16) + 1; ++ recip = (1 << 16) - 1; ++ } vc4_dlist_write(vc4_state, + /* @@ -99017,7 +100977,7 @@ index 5948e34f7f81..acedc1b89f2e 100644 { struct vc4_plane_state *vc4_state = to_vc4_plane_state(state); struct vc4_dev *vc4 = to_vc4_dev(state->plane->dev); -@@ -569,7 +688,7 @@ static u32 vc4_lbm_size(struct drm_plane_state *state) +@@ -569,7 +697,7 @@ static u32 vc4_lbm_size(struct drm_plane_state *state) if (vc4_state->x_scaling[0] == VC4_SCALING_TPZ) pix_per_line = vc4_state->crtc_w; else @@ -99026,7 +100986,7 @@ index 5948e34f7f81..acedc1b89f2e 100644 if (!vc4_state->is_yuv) { if (vc4_state->y_scaling[0] == VC4_SCALING_TPZ) -@@ -587,29 +706,161 @@ static u32 vc4_lbm_size(struct drm_plane_state *state) +@@ -587,29 +715,161 @@ static u32 vc4_lbm_size(struct drm_plane_state *state) } /* Align it to 64 or 128 (hvs5) bytes */ @@ -99192,7 +101152,7 @@ index 5948e34f7f81..acedc1b89f2e 100644 vc4_dlist_write(vc4_state, 0xc0c0c0c0); } -@@ -660,7 +911,8 @@ static void vc4_plane_calc_load(struct drm_plane_state *state) +@@ -660,7 +920,8 @@ static void vc4_plane_calc_load(struct drm_plane_state *state) for (i = 0; i < fb->format->num_planes; i++) { /* Even if the bandwidth/plane required for a single frame is * @@ -99202,7 +101162,7 @@ index 5948e34f7f81..acedc1b89f2e 100644 * * when downscaling, we have to read more pixels per line in * the time frame reserved for a single line, so the bandwidth -@@ -669,11 +921,11 @@ static void vc4_plane_calc_load(struct drm_plane_state *state) +@@ -669,11 +930,11 @@ static void vc4_plane_calc_load(struct drm_plane_state *state) * load by this number. We're likely over-estimating the read * demand, but that's better than under-estimating it. */ @@ -99218,7 +101178,7 @@ index 5948e34f7f81..acedc1b89f2e 100644 vc4_state->hvs_load += vc4_state->crtc_h * vc4_state->crtc_w; } -@@ -684,43 +936,208 @@ static void vc4_plane_calc_load(struct drm_plane_state *state) +@@ -684,43 +945,208 @@ static void vc4_plane_calc_load(struct drm_plane_state *state) static int vc4_plane_allocate_lbm(struct drm_plane_state *state) { @@ -99440,7 +101400,7 @@ index 5948e34f7f81..acedc1b89f2e 100644 /* * The colorspace conversion matrices are held in 3 entries in the dlist. * Create an array of them, with entries for each full and limited mode, and -@@ -768,6 +1185,11 @@ static const u32 colorspace_coeffs[2][DRM_COLOR_ENCODING_MAX][3] = { +@@ -768,6 +1194,11 @@ static const u32 colorspace_coeffs[2][DRM_COLOR_ENCODING_MAX][3] = { static u32 vc4_hvs4_get_alpha_blend_mode(struct drm_plane_state *state) { @@ -99452,7 +101412,7 @@ index 5948e34f7f81..acedc1b89f2e 100644 if (!state->fb->format->has_alpha) return VC4_SET_FIELD(SCALER_POS2_ALPHA_MODE_FIXED, SCALER_POS2_ALPHA_MODE); -@@ -789,6 +1211,17 @@ static u32 vc4_hvs4_get_alpha_blend_mode(struct drm_plane_state *state) +@@ -789,6 +1220,17 @@ static u32 vc4_hvs4_get_alpha_blend_mode(struct drm_plane_state *state) static u32 vc4_hvs5_get_alpha_blend_mode(struct drm_plane_state *state) { @@ -99470,7 +101430,7 @@ index 5948e34f7f81..acedc1b89f2e 100644 if (!state->fb->format->has_alpha) return VC4_SET_FIELD(SCALER5_CTL2_ALPHA_MODE_FIXED, SCALER5_CTL2_ALPHA_MODE); -@@ -808,6 +1241,21 @@ static u32 vc4_hvs5_get_alpha_blend_mode(struct drm_plane_state *state) +@@ -808,6 +1250,21 @@ static u32 vc4_hvs5_get_alpha_blend_mode(struct drm_plane_state *state) } } @@ -99492,7 +101452,7 @@ index 5948e34f7f81..acedc1b89f2e 100644 /* Writes out a full display list for an active plane to the plane's * private dlist state. */ -@@ -826,9 +1274,11 @@ static int vc4_plane_mode_set(struct drm_plane *plane, +@@ -826,9 +1283,11 @@ static int vc4_plane_mode_set(struct drm_plane *plane, bool mix_plane_alpha; bool covers_screen; u32 scl0, scl1, pitch0; @@ -99505,14 +101465,15 @@ index 5948e34f7f81..acedc1b89f2e 100644 int ret, i; if (vc4_state->dlist_initialized) -@@ -838,6 +1288,15 @@ static int vc4_plane_mode_set(struct drm_plane *plane, +@@ -838,6 +1297,16 @@ static int vc4_plane_mode_set(struct drm_plane *plane, if (ret) return ret; + width = vc4_state->src_w[0] >> 16; + height = vc4_state->src_h[0] >> 16; + -+ if (!width || !height || !vc4_state->crtc_w || !vc4_state->crtc_h) { ++ if (!vc4_state->src_w[0] || !vc4_state->src_h[0] || ++ !vc4_state->crtc_w || !vc4_state->crtc_h) { + /* 0 source size probably means the plane is offscreen */ + vc4_state->dlist_initialized = 1; + return 0; @@ -99521,7 +101482,7 @@ index 5948e34f7f81..acedc1b89f2e 100644 /* SCL1 is used for Cb/Cr scaling of planar formats. For RGB * and 4:4:4, scl1 should be set to scl0 so both channels of * the scaler do the same thing. For YUV, the Y plane needs -@@ -858,9 +1317,11 @@ static int vc4_plane_mode_set(struct drm_plane *plane, +@@ -858,9 +1327,11 @@ static int vc4_plane_mode_set(struct drm_plane *plane, DRM_MODE_REFLECT_Y); /* We must point to the last line when Y reflection is enabled. */ @@ -99535,7 +101496,7 @@ index 5948e34f7f81..acedc1b89f2e 100644 switch (base_format_mod) { case DRM_FORMAT_MOD_LINEAR: -@@ -871,13 +1332,8 @@ static int vc4_plane_mode_set(struct drm_plane *plane, +@@ -871,13 +1342,8 @@ static int vc4_plane_mode_set(struct drm_plane *plane, * out. */ for (i = 0; i < num_planes; i++) { @@ -99551,7 +101512,7 @@ index 5948e34f7f81..acedc1b89f2e 100644 } break; -@@ -898,7 +1354,7 @@ static int vc4_plane_mode_set(struct drm_plane *plane, +@@ -898,7 +1364,7 @@ static int vc4_plane_mode_set(struct drm_plane *plane, * pitch * tile_h == tile_size * tiles_per_row */ u32 tiles_w = fb->pitches[0] >> (tile_size_shift - tile_h_shift); @@ -99560,7 +101521,7 @@ index 5948e34f7f81..acedc1b89f2e 100644 u32 tiles_r = tiles_w - tiles_l; u32 tiles_t = src_y >> tile_h_shift; /* Intra-tile offsets, which modify the base address (the -@@ -908,7 +1364,7 @@ static int vc4_plane_mode_set(struct drm_plane *plane, +@@ -908,7 +1374,7 @@ static int vc4_plane_mode_set(struct drm_plane *plane, u32 tile_y = (src_y >> 4) & 1; u32 subtile_y = (src_y >> 2) & 3; u32 utile_y = src_y & 3; @@ -99569,7 +101530,7 @@ index 5948e34f7f81..acedc1b89f2e 100644 u32 y_off = src_y & tile_h_mask; /* When Y reflection is requested we must set the -@@ -932,19 +1388,18 @@ static int vc4_plane_mode_set(struct drm_plane *plane, +@@ -932,19 +1398,18 @@ static int vc4_plane_mode_set(struct drm_plane *plane, VC4_SET_FIELD(y_off, SCALER_PITCH0_TILE_Y_OFFSET) | VC4_SET_FIELD(tiles_l, SCALER_PITCH0_TILE_WIDTH_L) | VC4_SET_FIELD(tiles_r, SCALER_PITCH0_TILE_WIDTH_R)); @@ -99596,7 +101557,7 @@ index 5948e34f7f81..acedc1b89f2e 100644 } break; -@@ -1004,7 +1459,7 @@ static int vc4_plane_mode_set(struct drm_plane *plane, +@@ -1004,7 +1469,7 @@ static int vc4_plane_mode_set(struct drm_plane *plane, * of the 12-pixels in that 128-bit word is the * first pixel to be used */ @@ -99605,7 +101566,7 @@ index 5948e34f7f81..acedc1b89f2e 100644 u32 aligned = remaining_pixels / 12; u32 last_bits = remaining_pixels % 12; -@@ -1026,18 +1481,16 @@ static int vc4_plane_mode_set(struct drm_plane *plane, +@@ -1026,18 +1491,16 @@ static int vc4_plane_mode_set(struct drm_plane *plane, return -EINVAL; } pix_per_tile = tile_w / fb->format->cpp[0]; @@ -99629,7 +101590,7 @@ index 5948e34f7f81..acedc1b89f2e 100644 } pitch0 = VC4_SET_FIELD(param, SCALER_TILE_HEIGHT); -@@ -1050,6 +1503,28 @@ static int vc4_plane_mode_set(struct drm_plane *plane, +@@ -1050,6 +1513,28 @@ static int vc4_plane_mode_set(struct drm_plane *plane, return -EINVAL; } @@ -99658,7 +101619,7 @@ index 5948e34f7f81..acedc1b89f2e 100644 /* Don't waste cycles mixing with plane alpha if the set alpha * is opaque or there is no per-pixel alpha information. * In any case we use the alpha property value as the fixed alpha. -@@ -1057,7 +1532,7 @@ static int vc4_plane_mode_set(struct drm_plane *plane, +@@ -1057,7 +1542,7 @@ static int vc4_plane_mode_set(struct drm_plane *plane, mix_plane_alpha = state->alpha != DRM_BLEND_ALPHA_OPAQUE && fb->format->has_alpha; @@ -99667,7 +101628,7 @@ index 5948e34f7f81..acedc1b89f2e 100644 /* Control word */ vc4_dlist_write(vc4_state, SCALER_CTL0_VALID | -@@ -1092,10 +1567,8 @@ static int vc4_plane_mode_set(struct drm_plane *plane, +@@ -1092,10 +1577,8 @@ static int vc4_plane_mode_set(struct drm_plane *plane, vc4_dlist_write(vc4_state, (mix_plane_alpha ? SCALER_POS2_ALPHA_MIX : 0) | vc4_hvs4_get_alpha_blend_mode(state) | @@ -99680,7 +101641,7 @@ index 5948e34f7f81..acedc1b89f2e 100644 /* Position Word 3: Context. Written by the HVS. */ vc4_dlist_write(vc4_state, 0xc0c0c0c0); -@@ -1148,10 +1621,8 @@ static int vc4_plane_mode_set(struct drm_plane *plane, +@@ -1148,10 +1631,8 @@ static int vc4_plane_mode_set(struct drm_plane *plane, /* Position Word 2: Source Image Size */ vc4_state->pos2_offset = vc4_state->dlist_count; vc4_dlist_write(vc4_state, @@ -99693,7 +101654,7 @@ index 5948e34f7f81..acedc1b89f2e 100644 /* Position Word 3: Context. Written by the HVS. */ vc4_dlist_write(vc4_state, 0xc0c0c0c0); -@@ -1162,9 +1633,13 @@ static int vc4_plane_mode_set(struct drm_plane *plane, +@@ -1162,9 +1643,13 @@ static int vc4_plane_mode_set(struct drm_plane *plane, * * The pointers may be any byte address. */ @@ -99710,7 +101671,7 @@ index 5948e34f7f81..acedc1b89f2e 100644 /* Pointer Context Word 0/1/2: Written by the HVS */ for (i = 0; i < num_planes; i++) -@@ -1234,7 +1709,18 @@ static int vc4_plane_mode_set(struct drm_plane *plane, +@@ -1234,7 +1719,18 @@ static int vc4_plane_mode_set(struct drm_plane *plane, vc4_state->y_scaling[0] == VC4_SCALING_PPF || vc4_state->x_scaling[1] == VC4_SCALING_PPF || vc4_state->y_scaling[1] == VC4_SCALING_PPF) { @@ -99730,7 +101691,7 @@ index 5948e34f7f81..acedc1b89f2e 100644 SCALER_PPF_KERNEL_OFFSET); /* HPPF plane 0 */ -@@ -1274,49 +1760,506 @@ static int vc4_plane_mode_set(struct drm_plane *plane, +@@ -1274,43 +1770,501 @@ static int vc4_plane_mode_set(struct drm_plane *plane, return 0; } @@ -99749,21 +101710,17 @@ index 5948e34f7f81..acedc1b89f2e 100644 - plane); - struct vc4_plane_state *vc4_state = to_vc4_plane_state(new_plane_state); - int ret; -- -- vc4_state->dlist_count = 0; + struct drm_plane_state *state = &vc4_state->base; + struct vc4_dev *vc4 = to_vc4_dev(state->plane->dev); + u32 ret = 0; -- if (!plane_enabled(new_plane_state)) -- return 0; +- vc4_state->dlist_count = 0; + if (vc4_state->is_yuv) { + enum drm_color_encoding color_encoding = state->color_encoding; + enum drm_color_range color_range = state->color_range; -- ret = vc4_plane_mode_set(plane, new_plane_state); -- if (ret) -- return ret; +- if (!plane_enabled(new_plane_state)) +- return 0; + /* CSC pre-loaded with: + * 0 = BT601 limited range + * 1 = BT709 limited range @@ -99777,8 +101734,9 @@ index 5948e34f7f81..acedc1b89f2e 100644 + if (color_range > DRM_COLOR_YCBCR_FULL_RANGE) + color_range = DRM_COLOR_YCBCR_LIMITED_RANGE; -- return vc4_plane_allocate_lbm(new_plane_state); --} +- ret = vc4_plane_mode_set(plane, new_plane_state); +- if (ret) +- return ret; + if (vc4->step_d0) { + ret |= SCALER6D0_CTL2_CSC_ENABLE; + ret |= VC4_SET_FIELD(color_encoding + (color_range * 3), @@ -99790,24 +101748,21 @@ index 5948e34f7f81..acedc1b89f2e 100644 + } + } +- return vc4_plane_allocate_lbm(new_plane_state); ++ return ret; + } + -static void vc4_plane_atomic_update(struct drm_plane *plane, - struct drm_atomic_state *state) --{ ++static int vc6_plane_mode_set(struct drm_plane *plane, ++ struct drm_plane_state *state) + { - /* No contents here. Since we don't know where in the CRTC's - * dlist we should be stored, our dlist is uploaded to the - * hardware with vc4_plane_write_dlist() at CRTC atomic_flush - * time. - */ -+ return ret; - } - --u32 vc4_plane_write_dlist(struct drm_plane *plane, u32 __iomem *dlist) -+static int vc6_plane_mode_set(struct drm_plane *plane, -+ struct drm_plane_state *state) - { -- struct vc4_plane_state *vc4_state = to_vc4_plane_state(plane->state); -- int i; -- int idx; +-} - + struct drm_device *drm = plane->dev; + struct vc4_dev *vc4 = to_vc4_dev(drm); @@ -99838,7 +101793,8 @@ index 5948e34f7f81..acedc1b89f2e 100644 + width = vc4_state->src_w[0] >> 16; + height = vc4_state->src_h[0] >> 16; + -+ if (!width || !height || !vc4_state->crtc_w || !vc4_state->crtc_h) { ++ if (!vc4_state->src_w[0] || !vc4_state->src_h[0] || ++ !vc4_state->crtc_w || !vc4_state->crtc_h) { + /* 0 source size probably means the plane is offscreen. + * 0 destination size is a redundant plane. + */ @@ -100263,16 +102219,10 @@ index 5948e34f7f81..acedc1b89f2e 100644 + */ +} + -+u32 vc4_plane_write_dlist(struct drm_plane *plane, u32 __iomem *dlist) -+{ -+ struct vc4_plane_state *vc4_state = to_vc4_plane_state(plane->state); -+ int i; -+ int idx; -+ - if (!drm_dev_enter(plane->dev, &idx)) - goto out; - -@@ -1346,7 +2289,8 @@ void vc4_plane_async_set_fb(struct drm_plane *plane, struct drm_framebuffer *fb) + u32 vc4_plane_write_dlist(struct drm_plane *plane, u32 __iomem *dlist) + { + struct vc4_plane_state *vc4_state = to_vc4_plane_state(plane->state); +@@ -1346,7 +2300,8 @@ void vc4_plane_async_set_fb(struct drm_plane *plane, struct drm_framebuffer *fb) { struct vc4_plane_state *vc4_state = to_vc4_plane_state(plane->state); struct drm_gem_dma_object *bo = drm_fb_dma_get_gem_obj(fb, 0); @@ -100282,7 +102232,7 @@ index 5948e34f7f81..acedc1b89f2e 100644 int idx; if (!drm_dev_enter(plane->dev, &idx)) -@@ -1356,19 +2300,38 @@ void vc4_plane_async_set_fb(struct drm_plane *plane, struct drm_framebuffer *fb) +@@ -1356,19 +2311,38 @@ void vc4_plane_async_set_fb(struct drm_plane *plane, struct drm_framebuffer *fb) * because this is only called on the primary plane. */ WARN_ON_ONCE(plane->state->crtc_x < 0 || plane->state->crtc_y < 0); @@ -100332,7 +102282,7 @@ index 5948e34f7f81..acedc1b89f2e 100644 drm_dev_exit(idx); } -@@ -1423,8 +2386,6 @@ static void vc4_plane_atomic_async_update(struct drm_plane *plane, +@@ -1423,8 +2397,6 @@ static void vc4_plane_atomic_async_update(struct drm_plane *plane, sizeof(vc4_state->y_scaling)); vc4_state->is_unity = new_vc4_state->is_unity; vc4_state->is_yuv = new_vc4_state->is_yuv; @@ -100341,7 +102291,7 @@ index 5948e34f7f81..acedc1b89f2e 100644 vc4_state->needs_bg_fill = new_vc4_state->needs_bg_fill; /* Update the current vc4_state pos0, pos2 and ptr0 dlist entries. */ -@@ -1432,8 +2393,8 @@ static void vc4_plane_atomic_async_update(struct drm_plane *plane, +@@ -1432,8 +2404,8 @@ static void vc4_plane_atomic_async_update(struct drm_plane *plane, new_vc4_state->dlist[vc4_state->pos0_offset]; vc4_state->dlist[vc4_state->pos2_offset] = new_vc4_state->dlist[vc4_state->pos2_offset]; @@ -100352,7 +102302,7 @@ index 5948e34f7f81..acedc1b89f2e 100644 /* Note that we can't just call vc4_plane_write_dlist() * because that would smash the context data that the HVS is -@@ -1443,8 +2404,8 @@ static void vc4_plane_atomic_async_update(struct drm_plane *plane, +@@ -1443,8 +2415,8 @@ static void vc4_plane_atomic_async_update(struct drm_plane *plane, &vc4_state->hw_dlist[vc4_state->pos0_offset]); writel(vc4_state->dlist[vc4_state->pos2_offset], &vc4_state->hw_dlist[vc4_state->pos2_offset]); @@ -100363,7 +102313,7 @@ index 5948e34f7f81..acedc1b89f2e 100644 drm_dev_exit(idx); } -@@ -1454,11 +2415,15 @@ static int vc4_plane_atomic_async_check(struct drm_plane *plane, +@@ -1454,11 +2426,15 @@ static int vc4_plane_atomic_async_check(struct drm_plane *plane, { struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, plane); @@ -100380,7 +102330,7 @@ index 5948e34f7f81..acedc1b89f2e 100644 if (ret) return ret; -@@ -1471,7 +2436,7 @@ static int vc4_plane_atomic_async_check(struct drm_plane *plane, +@@ -1471,7 +2447,7 @@ static int vc4_plane_atomic_async_check(struct drm_plane *plane, if (old_vc4_state->dlist_count != new_vc4_state->dlist_count || old_vc4_state->pos0_offset != new_vc4_state->pos0_offset || old_vc4_state->pos2_offset != new_vc4_state->pos2_offset || @@ -100389,7 +102339,7 @@ index 5948e34f7f81..acedc1b89f2e 100644 vc4_lbm_size(plane->state) != vc4_lbm_size(new_plane_state)) return -EINVAL; -@@ -1481,7 +2446,7 @@ static int vc4_plane_atomic_async_check(struct drm_plane *plane, +@@ -1481,7 +2457,7 @@ static int vc4_plane_atomic_async_check(struct drm_plane *plane, for (i = 0; i < new_vc4_state->dlist_count; i++) { if (i == new_vc4_state->pos0_offset || i == new_vc4_state->pos2_offset || @@ -100398,7 +102348,7 @@ index 5948e34f7f81..acedc1b89f2e 100644 (new_vc4_state->lbm_offset && i == new_vc4_state->lbm_offset)) continue; -@@ -1629,7 +2594,7 @@ struct drm_plane *vc4_plane_init(struct drm_device *dev, +@@ -1629,7 +2605,7 @@ struct drm_plane *vc4_plane_init(struct drm_device *dev, }; for (i = 0; i < ARRAY_SIZE(hvs_formats); i++) { @@ -100407,7 +102357,7 @@ index 5948e34f7f81..acedc1b89f2e 100644 formats[num_formats] = hvs_formats[i].drm; num_formats++; } -@@ -1644,7 +2609,7 @@ struct drm_plane *vc4_plane_init(struct drm_device *dev, +@@ -1644,7 +2620,7 @@ struct drm_plane *vc4_plane_init(struct drm_device *dev, return ERR_CAST(vc4_plane); plane = &vc4_plane->base; @@ -100416,7 +102366,7 @@ index 5948e34f7f81..acedc1b89f2e 100644 drm_plane_helper_add(plane, &vc5_plane_helper_funcs); else drm_plane_helper_add(plane, &vc4_plane_helper_funcs); -@@ -1669,6 +2634,12 @@ struct drm_plane *vc4_plane_init(struct drm_device *dev, +@@ -1669,6 +2645,12 @@ struct drm_plane *vc4_plane_init(struct drm_device *dev, DRM_COLOR_YCBCR_BT709, DRM_COLOR_YCBCR_LIMITED_RANGE); @@ -100429,6 +102379,58 @@ index 5948e34f7f81..acedc1b89f2e 100644 if (type == DRM_PLANE_TYPE_PRIMARY) drm_plane_create_zpos_immutable_property(plane, 0); +@@ -1676,12 +2658,27 @@ struct drm_plane *vc4_plane_init(struct drm_device *dev, + } + + #define VC4_NUM_OVERLAY_PLANES 16 ++#define VC4_NUM_TXP_OVERLAY_PLANES 32 + + int vc4_plane_create_additional_planes(struct drm_device *drm) + { + struct drm_plane *cursor_plane; + struct drm_crtc *crtc; + unsigned int i; ++ struct drm_crtc *txp_crtc; ++ uint32_t non_txp_crtc_mask; ++ ++ drm_for_each_crtc(crtc, drm) { ++ struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc); ++ ++ if (vc4_crtc->feeds_txp) { ++ txp_crtc = crtc; ++ break; ++ } ++ } ++ ++ non_txp_crtc_mask = GENMASK(drm->mode_config.num_crtc - 1, 0) - ++ drm_crtc_mask(txp_crtc); + + /* Set up some arbitrary number of planes. We're not limited + * by a set number of physical registers, just the space in +@@ -1695,7 +2692,22 @@ int vc4_plane_create_additional_planes(struct drm_device *drm) + for (i = 0; i < VC4_NUM_OVERLAY_PLANES; i++) { + struct drm_plane *plane = + vc4_plane_init(drm, DRM_PLANE_TYPE_OVERLAY, +- GENMASK(drm->mode_config.num_crtc - 1, 0)); ++ non_txp_crtc_mask); ++ ++ if (IS_ERR(plane)) ++ continue; ++ ++ /* Create zpos property. Max of all the overlays + 1 primary + ++ * 1 cursor plane on a crtc. ++ */ ++ drm_plane_create_zpos_property(plane, i + 1, 1, ++ VC4_NUM_OVERLAY_PLANES + 1); ++ } ++ ++ for (i = 0; i < VC4_NUM_TXP_OVERLAY_PLANES; i++) { ++ struct drm_plane *plane = ++ vc4_plane_init(drm, DRM_PLANE_TYPE_OVERLAY, ++ drm_crtc_mask(txp_crtc)); + + if (IS_ERR(plane)) + continue; diff --git a/drivers/gpu/drm/vc4/vc4_regs.h b/drivers/gpu/drm/vc4/vc4_regs.h index f3763bd600f6..3608cdec29ad 100644 --- a/drivers/gpu/drm/vc4/vc4_regs.h @@ -100869,10 +102871,18 @@ index 1bda5010f15a..14079853338e 100644 if (args->min_x_tile > args->max_x_tile || diff --git a/drivers/gpu/drm/vc4/vc4_txp.c b/drivers/gpu/drm/vc4/vc4_txp.c -index ffe1f7d1b911..b87d5f8ad4b8 100644 +index ffe1f7d1b911..08d51840598a 100644 --- a/drivers/gpu/drm/vc4/vc4_txp.c +++ b/drivers/gpu/drm/vc4/vc4_txp.c -@@ -145,6 +145,9 @@ +@@ -15,6 +15,7 @@ + + #include + #include ++#include + #include + #include + #include +@@ -145,6 +146,9 @@ /* Number of lines received and committed to memory. */ #define TXP_PROGRESS 0x10 @@ -100882,7 +102892,7 @@ index ffe1f7d1b911..b87d5f8ad4b8 100644 #define TXP_READ(offset) \ ({ \ kunit_fail_current_test("Accessing a register in a unit test!\n"); \ -@@ -159,6 +162,7 @@ +@@ -159,6 +163,7 @@ struct vc4_txp { struct vc4_crtc base; @@ -100890,7 +102900,34 @@ index ffe1f7d1b911..b87d5f8ad4b8 100644 struct platform_device *pdev; -@@ -286,9 +290,13 @@ static void vc4_txp_connector_atomic_commit(struct drm_connector *conn, +@@ -255,10 +260,22 @@ static int vc4_txp_connector_atomic_check(struct drm_connector *conn, + crtc_state = drm_atomic_get_new_crtc_state(state, conn_state->crtc); + + fb = conn_state->writeback_job->fb; +- if (fb->width != crtc_state->mode.hdisplay || +- fb->height != crtc_state->mode.vdisplay) { +- DRM_DEBUG_KMS("Invalid framebuffer size %ux%u\n", +- fb->width, fb->height); ++ if ((conn_state->rotation == DRM_MODE_ROTATE_0 && ++ fb->width != crtc_state->mode.hdisplay && ++ fb->height != crtc_state->mode.vdisplay) || ++ (conn_state->rotation == (DRM_MODE_ROTATE_0 | DRM_MODE_TRANSPOSE) && ++ fb->width != crtc_state->mode.vdisplay && ++ fb->height != crtc_state->mode.hdisplay)) { ++ DRM_DEBUG_KMS("Invalid framebuffer size %ux%u vs mode %ux%u\n", ++ fb->width, fb->height, ++ crtc_state->mode.hdisplay, crtc_state->mode.vdisplay); ++ return -EINVAL; ++ } ++ ++ if (conn_state->rotation & DRM_MODE_TRANSPOSE && ++ (fb->format->format == DRM_FORMAT_RGB888 || ++ fb->format->format == DRM_FORMAT_BGR888)) { ++ DRM_DEBUG_KMS("24bpp formats not supported when transposing\n"); + return -EINVAL; + } + +@@ -286,9 +303,13 @@ static void vc4_txp_connector_atomic_commit(struct drm_connector *conn, struct drm_connector_state *conn_state = drm_atomic_get_new_connector_state(state, conn); struct vc4_txp *txp = connector_to_vc4_txp(conn); @@ -100904,7 +102941,7 @@ index ffe1f7d1b911..b87d5f8ad4b8 100644 u32 ctrl; int idx; int i; -@@ -308,9 +316,11 @@ static void vc4_txp_connector_atomic_commit(struct drm_connector *conn, +@@ -308,9 +329,11 @@ static void vc4_txp_connector_atomic_commit(struct drm_connector *conn, return; ctrl = TXP_GO | TXP_EI | @@ -100917,7 +102954,14 @@ index ffe1f7d1b911..b87d5f8ad4b8 100644 if (fb->format->has_alpha) ctrl |= TXP_ALPHA_ENABLE; else -@@ -324,11 +334,25 @@ static void vc4_txp_connector_atomic_commit(struct drm_connector *conn, +@@ -320,15 +343,32 @@ static void vc4_txp_connector_atomic_commit(struct drm_connector *conn, + */ + ctrl |= TXP_ALPHA_INVERT; + ++ if (conn_state->rotation & DRM_MODE_TRANSPOSE) ++ ctrl |= TXP_TRANSPOSE; ++ + if (!drm_dev_enter(drm, &idx)) return; gem = drm_fb_dma_get_gem_obj(fb, 0); @@ -100946,7 +102990,7 @@ index ffe1f7d1b911..b87d5f8ad4b8 100644 TXP_WRITE(TXP_DST_CTRL, ctrl); -@@ -362,6 +386,7 @@ static const struct drm_connector_funcs vc4_txp_connector_funcs = { +@@ -362,6 +402,7 @@ static const struct drm_connector_funcs vc4_txp_connector_funcs = { static void vc4_txp_encoder_disable(struct drm_encoder *encoder) { struct drm_device *drm = encoder->dev; @@ -100954,7 +102998,7 @@ index ffe1f7d1b911..b87d5f8ad4b8 100644 struct vc4_txp *txp = encoder_to_vc4_txp(encoder); int idx; -@@ -380,7 +405,8 @@ static void vc4_txp_encoder_disable(struct drm_encoder *encoder) +@@ -380,7 +421,8 @@ static void vc4_txp_encoder_disable(struct drm_encoder *encoder) WARN_ON(TXP_READ(TXP_DST_CTRL) & TXP_BUSY); } @@ -100964,7 +103008,7 @@ index ffe1f7d1b911..b87d5f8ad4b8 100644 drm_dev_exit(idx); } -@@ -484,17 +510,49 @@ static irqreturn_t vc4_txp_interrupt(int irq, void *data) +@@ -484,17 +526,49 @@ static irqreturn_t vc4_txp_interrupt(int irq, void *data) return IRQ_HANDLED; } @@ -101019,7 +103063,7 @@ index ffe1f7d1b911..b87d5f8ad4b8 100644 struct vc4_encoder *vc4_encoder; struct drm_encoder *encoder; struct vc4_crtc *vc4_crtc; -@@ -509,6 +567,11 @@ static int vc4_txp_bind(struct device *dev, struct device *master, void *data) +@@ -509,6 +583,11 @@ static int vc4_txp_bind(struct device *dev, struct device *master, void *data) if (!txp) return -ENOMEM; @@ -101031,7 +103075,7 @@ index ffe1f7d1b911..b87d5f8ad4b8 100644 txp->pdev = pdev; txp->regs = vc4_ioremap_regs(pdev, 0); if (IS_ERR(txp->regs)) -@@ -519,13 +582,13 @@ static int vc4_txp_bind(struct device *dev, struct device *master, void *data) +@@ -519,13 +598,13 @@ static int vc4_txp_bind(struct device *dev, struct device *master, void *data) vc4_crtc->regset.regs = txp_regs; vc4_crtc->regset.nregs = ARRAY_SIZE(txp_regs); @@ -101047,7 +103091,18 @@ index ffe1f7d1b911..b87d5f8ad4b8 100644 encoder = &vc4_encoder->base; encoder->possible_crtcs = drm_crtc_mask(&vc4_crtc->base); -@@ -579,7 +642,9 @@ static void vc4_txp_remove(struct platform_device *pdev) +@@ -545,6 +624,10 @@ static int vc4_txp_bind(struct device *dev, struct device *master, void *data) + if (ret) + return ret; + ++ drm_connector_create_rotation_property(&txp->connector.base, DRM_MODE_ROTATE_0, ++ DRM_MODE_ROTATE_0 | ++ DRM_MODE_TRANSPOSE); ++ + ret = devm_request_irq(dev, irq, vc4_txp_interrupt, 0, + dev_name(dev), txp); + if (ret) +@@ -579,7 +662,9 @@ static void vc4_txp_remove(struct platform_device *pdev) } static const struct of_device_id vc4_txp_dt_match[] = { @@ -101644,10 +103699,10 @@ index 000000000000..e8d2b4b162f7 + VC_IMAGE_YUVINFO_CSC_REC_2020 = 9, +}; diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h -index dd696cb559ae..eec8a133ff73 100644 +index 1174626904cb..04d6c2a507fd 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h -@@ -242,6 +242,9 @@ +@@ -243,6 +243,9 @@ #define USB_VENDOR_ID_BAANTO 0x2453 #define USB_DEVICE_ID_BAANTO_MT_190W2 0x0100 @@ -101657,7 +103712,7 @@ index dd696cb559ae..eec8a133ff73 100644 #define USB_VENDOR_ID_BELKIN 0x050d #define USB_DEVICE_ID_FLIP_KVM 0x3201 -@@ -1420,6 +1423,9 @@ +@@ -1405,6 +1408,9 @@ #define USB_VENDOR_ID_XIAOMI 0x2717 #define USB_DEVICE_ID_MI_SILENT_MOUSE 0x5014 @@ -102491,7 +104546,7 @@ index 79657910b79e..122a1031b4d8 100644 .id_table = sht3x_ids, }; diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig -index 9766c2ade6d8..5044b032adb9 100644 +index 585e7e4a1875..1a895c842122 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -16,6 +16,25 @@ config I2C_CCGX_UCSI @@ -103661,6 +105716,23 @@ index 0778a8fb6866..b80d2b53627b 100644 { } }; MODULE_DEVICE_TABLE(of, mcp3422_of_match); +diff --git a/drivers/iio/humidity/dht11.c b/drivers/iio/humidity/dht11.c +index c97e25448772..81c0bb9dd851 100644 +--- a/drivers/iio/humidity/dht11.c ++++ b/drivers/iio/humidity/dht11.c +@@ -152,9 +152,9 @@ static int dht11_decode(struct dht11 *dht11, int offset) + dht11->temperature = (((temp_int & 0x7f) << 8) + temp_dec) * + ((temp_int & 0x80) ? -100 : 100); + dht11->humidity = ((hum_int << 8) + hum_dec) * 100; +- } else if (temp_dec == 0 && hum_dec == 0) { /* DHT11 */ +- dht11->temperature = temp_int * 1000; +- dht11->humidity = hum_int * 1000; ++ } else if (temp_dec < 10 && hum_dec < 10) { /* DHT11 */ ++ dht11->temperature = temp_int * 1000 + temp_dec * 100; ++ dht11->humidity = hum_int * 1000 + hum_dec * 100; + } else { + dev_err(dht11->dev, + "Don't know how to decode data: %d %d %d %d\n", diff --git a/drivers/iio/light/tsl4531.c b/drivers/iio/light/tsl4531.c index 4da7d78906d4..39a6b1f8fe99 100644 --- a/drivers/iio/light/tsl4531.c @@ -104251,6 +106323,115 @@ index 87797cc88b32..235dd5f264c5 100644 }; int goodix_i2c_read(struct i2c_client *client, u16 reg, u8 *buf, int len); +diff --git a/drivers/input/touchscreen/ili210x.c b/drivers/input/touchscreen/ili210x.c +index 6a77babcf722..654c1bafaf4b 100644 +--- a/drivers/input/touchscreen/ili210x.c ++++ b/drivers/input/touchscreen/ili210x.c +@@ -67,6 +67,8 @@ struct ili210x { + u8 version_proto[2]; + u8 ic_mode[2]; + bool stop; ++ struct timer_list poll_timer; ++ struct work_struct poll_work; + }; + + static int ili210x_read_reg(struct i2c_client *client, +@@ -360,6 +362,34 @@ static irqreturn_t ili210x_irq(int irq, void *irq_data) + return IRQ_HANDLED; + } + ++static void ili210x_poll_work(struct work_struct *work) ++{ ++ struct ili210x *priv = container_of(work, struct ili210x, poll_work); ++ struct i2c_client *client = priv->client; ++ const struct ili2xxx_chip *chip = priv->chip; ++ u8 touchdata[ILI210X_DATA_SIZE] = { 0 }; ++ bool touch; ++ int error; ++ ++ error = chip->get_touch_data(client, touchdata); ++ if (error) { ++ dev_err(&client->dev, "Unable to get touch data: %d\n", error); ++ return; ++ } ++ ++ touch = ili210x_report_events(priv, touchdata); ++} ++ ++static void ili210x_poll_timer_callback(struct timer_list *t) ++{ ++ struct ili210x *priv = from_timer(priv, t, poll_timer); ++ ++ schedule_work(&priv->poll_work); ++ ++ if (!priv->stop) ++ mod_timer(&priv->poll_timer, jiffies + msecs_to_jiffies(ILI2XXX_POLL_PERIOD)); ++} ++ + static int ili251x_firmware_update_resolution(struct device *dev) + { + struct i2c_client *client = to_i2c_client(dev); +@@ -945,11 +975,6 @@ static int ili210x_i2c_probe(struct i2c_client *client) + return -ENODEV; + } + +- if (client->irq <= 0) { +- dev_err(dev, "No IRQ!\n"); +- return -EINVAL; +- } +- + reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(reset_gpio)) + return PTR_ERR(reset_gpio); +@@ -1001,12 +1026,17 @@ static int ili210x_i2c_probe(struct i2c_client *client) + return error; + } + +- error = devm_request_threaded_irq(dev, client->irq, NULL, ili210x_irq, +- IRQF_ONESHOT, client->name, priv); +- if (error) { +- dev_err(dev, "Unable to request touchscreen IRQ, err: %d\n", +- error); +- return error; ++ if (client->irq) { ++ error = devm_request_threaded_irq(dev, client->irq, NULL, ili210x_irq, ++ IRQF_ONESHOT, client->name, priv); ++ if (error) { ++ dev_err(dev, "Unable to request touchscreen IRQ, err: %d\n", error); ++ return error; ++ } ++ } else { ++ timer_setup(&priv->poll_timer, ili210x_poll_timer_callback, 0); ++ mod_timer(&priv->poll_timer, jiffies + msecs_to_jiffies(ILI2XXX_POLL_PERIOD)); ++ INIT_WORK(&priv->poll_work, ili210x_poll_work); + } + + error = devm_add_action_or_reset(dev, ili210x_stop, priv); +@@ -1029,6 +1059,16 @@ static int ili210x_i2c_probe(struct i2c_client *client) + return 0; + } + ++static void ili210x_i2c_remove(struct i2c_client *client) ++{ ++ struct ili210x *tsdata = i2c_get_clientdata(client); ++ ++ if (!client->irq) { ++ del_timer(&tsdata->poll_timer); ++ cancel_work_sync(&tsdata->poll_work); ++ } ++} ++ + static const struct i2c_device_id ili210x_i2c_id[] = { + { "ili210x", (long)&ili210x_chip }, + { "ili2117", (long)&ili211x_chip }, +@@ -1054,6 +1094,7 @@ static struct i2c_driver ili210x_ts_driver = { + }, + .id_table = ili210x_i2c_id, + .probe = ili210x_i2c_probe, ++ .remove = ili210x_i2c_remove, + }; + + module_i2c_driver(ili210x_ts_driver); diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig index 56eafa478c34..c5726a5794f4 100644 --- a/drivers/iommu/Kconfig @@ -105086,10 +107267,10 @@ index 000000000000..31b811e426dd + +#endif diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c -index 9cdc28dc2cf8..709d38620ba9 100644 +index 8e02783eabcb..4ef997dc5c22 100644 --- a/drivers/iommu/iommu.c +++ b/drivers/iommu/iommu.c -@@ -2429,6 +2429,30 @@ size_t iommu_pgsize(struct iommu_domain *domain, unsigned long iova, +@@ -2430,6 +2430,30 @@ size_t iommu_pgsize(struct iommu_domain *domain, unsigned long iova, } EXPORT_SYMBOL_GPL(iommu_pgsize); @@ -105120,7 +107301,7 @@ index 9cdc28dc2cf8..709d38620ba9 100644 static int __iommu_map(struct iommu_domain *domain, unsigned long iova, phys_addr_t paddr, size_t size, int prot, gfp_t gfp) { -@@ -2439,12 +2463,13 @@ static int __iommu_map(struct iommu_domain *domain, unsigned long iova, +@@ -2440,12 +2464,13 @@ static int __iommu_map(struct iommu_domain *domain, unsigned long iova, phys_addr_t orig_paddr = paddr; int ret = 0; @@ -105137,7 +107318,7 @@ index 9cdc28dc2cf8..709d38620ba9 100644 /* find out the minimum page size supported */ min_pagesz = 1 << __ffs(domain->pgsize_bitmap); -@@ -2462,14 +2487,10 @@ static int __iommu_map(struct iommu_domain *domain, unsigned long iova, +@@ -2463,14 +2488,10 @@ static int __iommu_map(struct iommu_domain *domain, unsigned long iova, pr_debug("map: iova 0x%lx pa %pa size 0x%zx\n", iova, &paddr, size); while (size) { @@ -105155,7 +107336,7 @@ index 9cdc28dc2cf8..709d38620ba9 100644 /* * Some pages may have been mapped, even if an error occurred, * so we should account for those so they can be unmapped. -@@ -2506,11 +2527,8 @@ int iommu_map(struct iommu_domain *domain, unsigned long iova, +@@ -2507,11 +2528,8 @@ int iommu_map(struct iommu_domain *domain, unsigned long iova, return -EINVAL; ret = __iommu_map(domain, iova, paddr, size, prot, gfp); @@ -105169,7 +107350,7 @@ index 9cdc28dc2cf8..709d38620ba9 100644 return ret; -@@ -2522,6 +2540,19 @@ int iommu_map(struct iommu_domain *domain, unsigned long iova, +@@ -2523,6 +2541,19 @@ int iommu_map(struct iommu_domain *domain, unsigned long iova, } EXPORT_SYMBOL_GPL(iommu_map); @@ -105189,7 +107370,7 @@ index 9cdc28dc2cf8..709d38620ba9 100644 static size_t __iommu_unmap(struct iommu_domain *domain, unsigned long iova, size_t size, struct iommu_iotlb_gather *iotlb_gather) -@@ -2531,10 +2562,11 @@ static size_t __iommu_unmap(struct iommu_domain *domain, +@@ -2532,10 +2563,11 @@ static size_t __iommu_unmap(struct iommu_domain *domain, unsigned long orig_iova = iova; unsigned int min_pagesz; @@ -105203,7 +107384,7 @@ index 9cdc28dc2cf8..709d38620ba9 100644 return 0; /* find out the minimum page size supported */ -@@ -2558,10 +2590,9 @@ static size_t __iommu_unmap(struct iommu_domain *domain, +@@ -2559,10 +2591,9 @@ static size_t __iommu_unmap(struct iommu_domain *domain, * or we hit an area that isn't mapped. */ while (unmapped < size) { @@ -105217,7 +107398,7 @@ index 9cdc28dc2cf8..709d38620ba9 100644 if (!unmapped_page) break; -@@ -2644,11 +2675,8 @@ ssize_t iommu_map_sg(struct iommu_domain *domain, unsigned long iova, +@@ -2645,11 +2676,8 @@ ssize_t iommu_map_sg(struct iommu_domain *domain, unsigned long iova, sg = sg_next(sg); } @@ -106231,6 +108412,34 @@ index 000000000000..8a974a355656 +MODULE_AUTHOR("Phil Elwell "); +MODULE_DESCRIPTION("Set LED GPIO to Input \"trigger\""); +MODULE_LICENSE("GPL"); +diff --git a/drivers/mailbox/Kconfig b/drivers/mailbox/Kconfig +index bc2e265cb02d..19aa026e55eb 100644 +--- a/drivers/mailbox/Kconfig ++++ b/drivers/mailbox/Kconfig +@@ -295,4 +295,13 @@ config QCOM_IPCC + acts as an interrupt controller for receiving interrupts from clients. + Say Y here if you want to build this driver. + ++config MBOX_RP1 ++ tristate "RP1 Mailbox" ++ depends on MFD_RP1 ++ help ++ An implementation of a mailbox interface to the Raspberry Pi RP1 I/O ++ interface. Although written as a mailbox driver, the hardware only ++ provides an array of 32 doorbells. ++ Say Y here if you want to use the RP1 Mailbox. ++ + endif +diff --git a/drivers/mailbox/Makefile b/drivers/mailbox/Makefile +index fc9376117111..71b415c9f7b5 100644 +--- a/drivers/mailbox/Makefile ++++ b/drivers/mailbox/Makefile +@@ -62,3 +62,5 @@ obj-$(CONFIG_SPRD_MBOX) += sprd-mailbox.o + obj-$(CONFIG_QCOM_IPCC) += qcom-ipcc.o + + obj-$(CONFIG_APPLE_MAILBOX) += apple-mailbox.o ++ ++obj-$(CONFIG_MBOX_RP1) += rp1-mailbox.o diff --git a/drivers/mailbox/bcm2835-mailbox.c b/drivers/mailbox/bcm2835-mailbox.c index ea12fb8d2401..8c54980b4b7e 100644 --- a/drivers/mailbox/bcm2835-mailbox.c @@ -106280,6 +108489,218 @@ index ea12fb8d2401..8c54980b4b7e 100644 MODULE_AUTHOR("Lubomir Rintel "); MODULE_DESCRIPTION("BCM2835 mailbox IPC driver"); +diff --git a/drivers/mailbox/rp1-mailbox.c b/drivers/mailbox/rp1-mailbox.c +new file mode 100644 +index 000000000000..0e8af098b62b +--- /dev/null ++++ b/drivers/mailbox/rp1-mailbox.c +@@ -0,0 +1,206 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (C) 2023 Raspberry Pi Ltd. ++ * ++ * Parts of this driver are based on: ++ * - bcm2835-mailbox.c ++ * Copyright (C) 2010,2015 Broadcom ++ * Copyright (C) 2013-2014 Lubomir Rintel ++ * Copyright (C) 2013 Craig McGeachie ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* ++ * RP1's PROC_EVENTS register can generate interrupts on the M3 cores (when ++ * enabled). The 32-bit register is treated as 32 events, all of which share a ++ * common interrupt. HOST_EVENTS is the same in the reverse direction. ++ */ ++#define SYSCFG_PROC_EVENTS 0x00000008 ++#define SYSCFG_HOST_EVENTS 0x0000000c ++#define SYSCFG_HOST_EVENT_IRQ_EN 0x00000010 ++#define SYSCFG_HOST_EVENT_IRQ 0x00000014 ++ ++#define HW_SET_BITS 0x00002000 ++#define HW_CLR_BITS 0x00003000 ++ ++#define MAX_CHANS 4 /* 32 is the hardware limit */ ++ ++struct rp1_mbox { ++ void __iomem *regs; ++ unsigned int irq; ++ struct mbox_controller controller; ++}; ++ ++static struct rp1_mbox *rp1_chan_mbox(struct mbox_chan *chan) ++{ ++ return container_of(chan->mbox, struct rp1_mbox, controller); ++} ++ ++static unsigned int rp1_chan_event(struct mbox_chan *chan) ++{ ++ return (unsigned int)(uintptr_t)chan->con_priv; ++} ++ ++static irqreturn_t rp1_mbox_irq(int irq, void *dev_id) ++{ ++ struct rp1_mbox *mbox = dev_id; ++ struct mbox_chan *chan; ++ unsigned int doorbell; ++ unsigned int evs; ++ ++ evs = readl(mbox->regs + SYSCFG_HOST_EVENT_IRQ); ++ writel(evs, mbox->regs + SYSCFG_HOST_EVENTS + HW_CLR_BITS); ++ ++ while (evs) { ++ doorbell = __ffs(evs); ++ chan = &mbox->controller.chans[doorbell]; ++ mbox_chan_received_data(chan, NULL); ++ evs &= ~(1 << doorbell); ++ } ++ return IRQ_HANDLED; ++} ++ ++static int rp1_send_data(struct mbox_chan *chan, void *data) ++{ ++ struct rp1_mbox *mbox = rp1_chan_mbox(chan); ++ unsigned int event = rp1_chan_event(chan); ++ ++ writel(event, mbox->regs + SYSCFG_PROC_EVENTS + HW_SET_BITS); ++ ++ return 0; ++} ++ ++static int rp1_startup(struct mbox_chan *chan) ++{ ++ struct rp1_mbox *mbox = rp1_chan_mbox(chan); ++ unsigned int event = rp1_chan_event(chan); ++ ++ writel(event, mbox->regs + SYSCFG_HOST_EVENT_IRQ_EN + HW_SET_BITS); ++ ++ return 0; ++} ++ ++static void rp1_shutdown(struct mbox_chan *chan) ++{ ++ struct rp1_mbox *mbox = rp1_chan_mbox(chan); ++ unsigned int event = rp1_chan_event(chan); ++ ++ writel(event, mbox->regs + SYSCFG_HOST_EVENT_IRQ_EN + HW_CLR_BITS); ++} ++ ++static bool rp1_last_tx_done(struct mbox_chan *chan) ++{ ++ struct rp1_mbox *mbox = rp1_chan_mbox(chan); ++ unsigned int event = rp1_chan_event(chan); ++ unsigned int evs; ++ ++ evs = readl(mbox->regs + SYSCFG_HOST_EVENT_IRQ); ++ ++ return !(evs & event); ++} ++ ++static const struct mbox_chan_ops rp1_mbox_chan_ops = { ++ .send_data = rp1_send_data, ++ .startup = rp1_startup, ++ .shutdown = rp1_shutdown, ++ .last_tx_done = rp1_last_tx_done ++}; ++ ++static struct mbox_chan *rp1_mbox_xlate(struct mbox_controller *mbox, ++ const struct of_phandle_args *spec) ++{ ++ struct mbox_chan *chan; ++ unsigned int doorbell; ++ ++ if (spec->args_count != 1) ++ return ERR_PTR(-EINVAL); ++ ++ doorbell = spec->args[0]; ++ if (doorbell >= MAX_CHANS) ++ return ERR_PTR(-EINVAL); ++ ++ chan = &mbox->chans[doorbell]; ++ ++ chan->con_priv = (void *)(uintptr_t)(1 << doorbell); ++ ++ return chan; ++} ++ ++static int rp1_mbox_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct mbox_chan *chans; ++ struct rp1_mbox *mbox; ++ int ret = 0; ++ ++ mbox = devm_kzalloc(dev, sizeof(*mbox), GFP_KERNEL); ++ if (mbox == NULL) ++ return -ENOMEM; ++ ++ ret = devm_request_irq(dev, platform_get_irq(pdev, 0), ++ rp1_mbox_irq, 0, dev_name(dev), mbox); ++ if (ret) { ++ dev_err(dev, "Failed to register a mailbox IRQ handler: %d\n", ++ ret); ++ return -ENODEV; ++ } ++ ++ mbox->regs = devm_platform_ioremap_resource(pdev, 0); ++ if (IS_ERR(mbox->regs)) { ++ ret = PTR_ERR(mbox->regs); ++ return ret; ++ } ++ ++ chans = devm_kcalloc(dev, MAX_CHANS, sizeof(*chans), GFP_KERNEL); ++ if (!chans) ++ return -ENOMEM; ++ ++ mbox->controller.txdone_poll = true; ++ mbox->controller.txpoll_period = 5; ++ mbox->controller.ops = &rp1_mbox_chan_ops; ++ mbox->controller.of_xlate = &rp1_mbox_xlate; ++ mbox->controller.dev = dev; ++ mbox->controller.num_chans = MAX_CHANS; ++ mbox->controller.chans = chans; ++ ++ ret = devm_mbox_controller_register(dev, &mbox->controller); ++ if (ret) ++ return ret; ++ ++ platform_set_drvdata(pdev, mbox); ++ ++ return 0; ++} ++ ++static const struct of_device_id rp1_mbox_of_match[] = { ++ { .compatible = "raspberrypi,rp1-mbox", }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, rp1_mbox_of_match); ++ ++static struct platform_driver rp1_mbox_driver = { ++ .driver = { ++ .name = "rp1-mbox", ++ .of_match_table = rp1_mbox_of_match, ++ }, ++ .probe = rp1_mbox_probe, ++}; ++ ++module_platform_driver(rp1_mbox_driver); ++ ++MODULE_AUTHOR("Phil Elwell "); ++MODULE_DESCRIPTION("RP1 mailbox IPC driver"); ++MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/common/videobuf2/videobuf2-core.c b/drivers/media/common/videobuf2/videobuf2-core.c index 29bfc2bf796b..0a34ad9d9ce4 100644 --- a/drivers/media/common/videobuf2/videobuf2-core.c @@ -107114,10 +109535,10 @@ index 99ba925e8ec8..413021887f96 100644 diff --git a/drivers/media/i2c/arducam-pivariety.c b/drivers/media/i2c/arducam-pivariety.c new file mode 100644 -index 000000000000..6bb9e9c48e5c +index 000000000000..5bac086e9957 --- /dev/null +++ b/drivers/media/i2c/arducam-pivariety.c -@@ -0,0 +1,1472 @@ +@@ -0,0 +1,1475 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * A V4L2 driver for Arducam Pivariety Cameras @@ -108328,6 +110749,8 @@ index 000000000000..6bb9e9c48e5c + if (ret) + return ret; + ++ mutex_init(&pivariety->mutex); ++ + index = 0; + while (1) { + ret = pivariety_write(pivariety, CTRL_INDEX_REG, index); @@ -108415,6 +110838,7 @@ index 000000000000..6bb9e9c48e5c + v4l2_ctrl_handler_setup(ctrl_hdlr); + return 0; +err: ++ mutex_destroy(&pivariety->mutex); + return -ENODEV; +} + @@ -111981,7 +114405,7 @@ index 4148009e0e01..2362c4813f5e 100644 static const struct dev_pm_ops dw9807_pm_ops = { diff --git a/drivers/media/i2c/imx219.c b/drivers/media/i2c/imx219.c -index a9a8cd148f4f..91854f16c5b9 100644 +index a9a8cd148f4f..4b39391131df 100644 --- a/drivers/media/i2c/imx219.c +++ b/drivers/media/i2c/imx219.c @@ -77,7 +77,7 @@ @@ -112017,7 +114441,21 @@ index a9a8cd148f4f..91854f16c5b9 100644 #define IMX219_REG_CSI_DATA_FORMAT_A CCI_REG16(0x018c) -@@ -159,6 +161,33 @@ +@@ -146,10 +148,11 @@ + + /* Pixel rate is fixed for all the modes */ + #define IMX219_PIXEL_RATE 182400000 +-#define IMX219_PIXEL_RATE_4LANE 280800000 ++#define IMX219_PIXEL_RATE_4LANE 281600000 + + #define IMX219_DEFAULT_LINK_FREQ 456000000 +-#define IMX219_DEFAULT_LINK_FREQ_4LANE 363000000 ++#define IMX219_DEFAULT_LINK_FREQ_4LANE_UNSUPPORTED 363000000 ++#define IMX219_DEFAULT_LINK_FREQ_4LANE 364000000 + + /* IMX219 native and active pixel array size. */ + #define IMX219_NATIVE_WIDTH 3296U +@@ -159,6 +162,33 @@ #define IMX219_PIXEL_ARRAY_WIDTH 3280U #define IMX219_PIXEL_ARRAY_HEIGHT 2464U @@ -112051,7 +114489,7 @@ index a9a8cd148f4f..91854f16c5b9 100644 struct imx219_reg_list { unsigned int num_of_regs; const struct cci_reg_sequence *regs; -@@ -180,8 +209,8 @@ struct imx219_mode { +@@ -180,8 +210,8 @@ struct imx219_mode { /* Default register values */ struct imx219_reg_list reg_list; @@ -112062,7 +114500,58 @@ index a9a8cd148f4f..91854f16c5b9 100644 }; static const struct cci_reg_sequence imx219_common_regs[] = { -@@ -386,7 +415,10 @@ static const struct imx219_mode supported_modes[] = { +@@ -195,15 +225,6 @@ static const struct cci_reg_sequence imx219_common_regs[] = { + { CCI_REG8(0x30eb), 0x05 }, + { CCI_REG8(0x30eb), 0x09 }, + +- /* PLL Clock Table */ +- { IMX219_REG_VTPXCK_DIV, 5 }, +- { IMX219_REG_VTSYCK_DIV, 1 }, +- { IMX219_REG_PREPLLCK_VT_DIV, 3 }, /* 0x03 = AUTO set */ +- { IMX219_REG_PREPLLCK_OP_DIV, 3 }, /* 0x03 = AUTO set */ +- { IMX219_REG_PLL_VT_MPY, 57 }, +- { IMX219_REG_OPSYCK_DIV, 1 }, +- { IMX219_REG_PLL_OP_MPY, 114 }, +- + /* Undocumented registers */ + { CCI_REG8(0x455e), 0x00 }, + { CCI_REG8(0x471e), 0x4b }, +@@ -287,6 +308,34 @@ static const struct cci_reg_sequence raw10_framefmt_regs[] = { + { IMX219_REG_OPPXCK_DIV, 10 }, + }; + ++static const struct cci_reg_sequence imx219_2lane_regs[] = { ++ /* PLL Clock Table */ ++ { IMX219_REG_VTPXCK_DIV, 5 }, ++ { IMX219_REG_VTSYCK_DIV, 1 }, ++ { IMX219_REG_PREPLLCK_VT_DIV, 3 }, /* 0x03 = AUTO set */ ++ { IMX219_REG_PREPLLCK_OP_DIV, 3 }, /* 0x03 = AUTO set */ ++ { IMX219_REG_PLL_VT_MPY, 57 }, ++ { IMX219_REG_OPSYCK_DIV, 1 }, ++ { IMX219_REG_PLL_OP_MPY, 114 }, ++ ++ /* 2-Lane CSI Mode */ ++ { IMX219_REG_CSI_LANE_MODE, IMX219_CSI_2_LANE_MODE }, ++}; ++ ++static const struct cci_reg_sequence imx219_4lane_regs[] = { ++ /* PLL Clock Table */ ++ { IMX219_REG_VTPXCK_DIV, 5 }, ++ { IMX219_REG_VTSYCK_DIV, 1 }, ++ { IMX219_REG_PREPLLCK_VT_DIV, 3 }, /* 0x03 = AUTO set */ ++ { IMX219_REG_PREPLLCK_OP_DIV, 3 }, /* 0x03 = AUTO set */ ++ { IMX219_REG_PLL_VT_MPY, 88 }, ++ { IMX219_REG_OPSYCK_DIV, 1 }, ++ { IMX219_REG_PLL_OP_MPY, 91 }, ++ ++ /* 4-Lane CSI Mode */ ++ { IMX219_REG_CSI_LANE_MODE, IMX219_CSI_4_LANE_MODE }, ++}; ++ + static const s64 imx219_link_freq_menu[] = { + IMX219_DEFAULT_LINK_FREQ, + }; +@@ -386,7 +435,10 @@ static const struct imx219_mode supported_modes[] = { .num_of_regs = ARRAY_SIZE(mode_3280x2464_regs), .regs = mode_3280x2464_regs, }, @@ -112074,7 +114563,7 @@ index a9a8cd148f4f..91854f16c5b9 100644 }, { /* 1080P 30fps cropped */ -@@ -403,7 +435,10 @@ static const struct imx219_mode supported_modes[] = { +@@ -403,7 +455,10 @@ static const struct imx219_mode supported_modes[] = { .num_of_regs = ARRAY_SIZE(mode_1920_1080_regs), .regs = mode_1920_1080_regs, }, @@ -112086,7 +114575,7 @@ index a9a8cd148f4f..91854f16c5b9 100644 }, { /* 2x2 binned 30fps mode */ -@@ -420,7 +455,10 @@ static const struct imx219_mode supported_modes[] = { +@@ -420,7 +475,10 @@ static const struct imx219_mode supported_modes[] = { .num_of_regs = ARRAY_SIZE(mode_1640_1232_regs), .regs = mode_1640_1232_regs, }, @@ -112098,7 +114587,7 @@ index a9a8cd148f4f..91854f16c5b9 100644 }, { /* 640x480 30fps mode */ -@@ -437,13 +475,16 @@ static const struct imx219_mode supported_modes[] = { +@@ -437,13 +495,16 @@ static const struct imx219_mode supported_modes[] = { .num_of_regs = ARRAY_SIZE(mode_640_480_regs), .regs = mode_640_480_regs, }, @@ -112117,7 +114606,7 @@ index a9a8cd148f4f..91854f16c5b9 100644 struct regmap *regmap; struct clk *xclk; /* system clock to IMX219 */ -@@ -495,12 +536,64 @@ static u32 imx219_get_format_code(struct imx219 *imx219, u32 code) +@@ -495,12 +556,64 @@ static u32 imx219_get_format_code(struct imx219 *imx219, u32 code) return imx219_mbus_formats[i]; } @@ -112182,7 +114671,7 @@ index a9a8cd148f4f..91854f16c5b9 100644 if (ctrl->id == V4L2_CID_VBLANK) { int exposure_max, exposure_def; -@@ -522,6 +615,10 @@ static int imx219_set_ctrl(struct v4l2_ctrl *ctrl) +@@ -522,6 +635,10 @@ static int imx219_set_ctrl(struct v4l2_ctrl *ctrl) if (pm_runtime_get_if_in_use(&client->dev) == 0) return 0; @@ -112193,7 +114682,7 @@ index a9a8cd148f4f..91854f16c5b9 100644 switch (ctrl->id) { case V4L2_CID_ANALOGUE_GAIN: cci_write(imx219->regmap, IMX219_REG_ANALOG_GAIN, -@@ -529,7 +626,7 @@ static int imx219_set_ctrl(struct v4l2_ctrl *ctrl) +@@ -529,7 +646,7 @@ static int imx219_set_ctrl(struct v4l2_ctrl *ctrl) break; case V4L2_CID_EXPOSURE: cci_write(imx219->regmap, IMX219_REG_EXPOSURE, @@ -112202,7 +114691,7 @@ index a9a8cd148f4f..91854f16c5b9 100644 break; case V4L2_CID_DIGITAL_GAIN: cci_write(imx219->regmap, IMX219_REG_DIGITAL_GAIN, -@@ -546,7 +643,11 @@ static int imx219_set_ctrl(struct v4l2_ctrl *ctrl) +@@ -546,7 +663,11 @@ static int imx219_set_ctrl(struct v4l2_ctrl *ctrl) break; case V4L2_CID_VBLANK: cci_write(imx219->regmap, IMX219_REG_VTS, @@ -112215,7 +114704,7 @@ index a9a8cd148f4f..91854f16c5b9 100644 break; case V4L2_CID_TEST_PATTERN_RED: cci_write(imx219->regmap, IMX219_REG_TESTP_RED, -@@ -614,6 +715,13 @@ static int imx219_init_cfg(struct v4l2_subdev *sd, +@@ -614,6 +735,13 @@ static int imx219_init_cfg(struct v4l2_subdev *sd, crop->width = IMX219_PIXEL_ARRAY_WIDTH; crop->height = IMX219_PIXEL_ARRAY_HEIGHT; @@ -112229,7 +114718,7 @@ index a9a8cd148f4f..91854f16c5b9 100644 return 0; } -@@ -623,10 +731,20 @@ static int imx219_enum_mbus_code(struct v4l2_subdev *sd, +@@ -623,10 +751,20 @@ static int imx219_enum_mbus_code(struct v4l2_subdev *sd, { struct imx219 *imx219 = to_imx219(sd); @@ -112252,7 +114741,7 @@ index a9a8cd148f4f..91854f16c5b9 100644 return 0; } -@@ -638,68 +756,109 @@ static int imx219_enum_frame_size(struct v4l2_subdev *sd, +@@ -638,68 +776,109 @@ static int imx219_enum_frame_size(struct v4l2_subdev *sd, struct imx219 *imx219 = to_imx219(sd); u32 code; @@ -112266,15 +114755,15 @@ index a9a8cd148f4f..91854f16c5b9 100644 + if (fse->pad == IMAGE_PAD) { + if (fse->index >= ARRAY_SIZE(supported_modes)) + return -EINVAL; -+ -+ code = imx219_get_format_code(imx219, fse->code); -+ if (fse->code != code) -+ return -EINVAL; - fse->min_width = supported_modes[fse->index].width; - fse->max_width = fse->min_width; - fse->min_height = supported_modes[fse->index].height; - fse->max_height = fse->min_height; ++ code = imx219_get_format_code(imx219, fse->code); ++ if (fse->code != code) ++ return -EINVAL; ++ + fse->min_width = supported_modes[fse->index].width; + fse->max_width = fse->min_width; + fse->min_height = supported_modes[fse->index].height; @@ -112407,7 +114896,7 @@ index a9a8cd148f4f..91854f16c5b9 100644 } return 0; -@@ -730,24 +889,20 @@ static int imx219_set_framefmt(struct imx219 *imx219, +@@ -730,24 +909,20 @@ static int imx219_set_framefmt(struct imx219 *imx219, static int imx219_set_binning(struct imx219 *imx219, const struct v4l2_mbus_framefmt *format) { @@ -112442,7 +114931,22 @@ index a9a8cd148f4f..91854f16c5b9 100644 } return -EINVAL; -@@ -1053,11 +1208,6 @@ static const struct v4l2_subdev_ops imx219_subdev_ops = { +@@ -786,9 +961,11 @@ static int imx219_get_selection(struct v4l2_subdev *sd, + + static int imx219_configure_lanes(struct imx219 *imx219) + { +- return cci_write(imx219->regmap, IMX219_REG_CSI_LANE_MODE, +- imx219->lanes == 2 ? IMX219_CSI_2_LANE_MODE : +- IMX219_CSI_4_LANE_MODE, NULL); ++ /* Write the appropriate PLL settings for the number of MIPI lanes */ ++ return cci_multi_reg_write(imx219->regmap, ++ imx219->lanes == 2 ? imx219_2lane_regs : imx219_4lane_regs, ++ imx219->lanes == 2 ? ARRAY_SIZE(imx219_2lane_regs) : ++ ARRAY_SIZE(imx219_4lane_regs), NULL); + }; + + static int imx219_start_streaming(struct imx219 *imx219, +@@ -1053,11 +1230,6 @@ static const struct v4l2_subdev_ops imx219_subdev_ops = { }; @@ -112454,7 +114958,7 @@ index a9a8cd148f4f..91854f16c5b9 100644 /* Initialize control handlers */ static int imx219_init_controls(struct imx219 *imx219) { -@@ -1065,7 +1215,7 @@ static int imx219_init_controls(struct imx219 *imx219) +@@ -1065,7 +1237,7 @@ static int imx219_init_controls(struct imx219 *imx219) struct v4l2_ctrl_handler *ctrl_hdlr; unsigned int height = imx219->mode->height; struct v4l2_fwnode_device_properties props; @@ -112463,7 +114967,7 @@ index a9a8cd148f4f..91854f16c5b9 100644 int i, ret; ctrl_hdlr = &imx219->ctrl_handler; -@@ -1074,11 +1224,11 @@ static int imx219_init_controls(struct imx219 *imx219) +@@ -1074,11 +1246,11 @@ static int imx219_init_controls(struct imx219 *imx219) return ret; /* By default, PIXEL_RATE is read only */ @@ -112478,7 +114982,7 @@ index a9a8cd148f4f..91854f16c5b9 100644 imx219->link_freq = v4l2_ctrl_new_int_menu(ctrl_hdlr, &imx219_ctrl_ops, -@@ -1094,12 +1244,11 @@ static int imx219_init_controls(struct imx219 *imx219) +@@ -1094,12 +1266,11 @@ static int imx219_init_controls(struct imx219 *imx219) V4L2_CID_VBLANK, IMX219_VBLANK_MIN, IMX219_VTS_MAX - height, 1, imx219->mode->vts_def - height); @@ -112494,7 +114998,54 @@ index a9a8cd148f4f..91854f16c5b9 100644 exposure_max = imx219->mode->vts_def - 4; exposure_def = (exposure_max < IMX219_EXPOSURE_DEFAULT) ? exposure_max : IMX219_EXPOSURE_DEFAULT; -@@ -1319,9 +1468,10 @@ static int imx219_probe(struct i2c_client *client) +@@ -1185,6 +1356,7 @@ static int imx219_check_hwcfg(struct device *dev, struct imx219 *imx219) + .bus_type = V4L2_MBUS_CSI2_DPHY + }; + int ret = -EINVAL; ++ bool link_frequency_valid = false; + + endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(dev), NULL); + if (!endpoint) { +@@ -1211,11 +1383,33 @@ static int imx219_check_hwcfg(struct device *dev, struct imx219 *imx219) + goto error_out; + } + +- if (ep_cfg.nr_of_link_frequencies != 1 || +- (ep_cfg.link_frequencies[0] != ((imx219->lanes == 2) ? +- IMX219_DEFAULT_LINK_FREQ : IMX219_DEFAULT_LINK_FREQ_4LANE))) { +- dev_err(dev, "Link frequency not supported: %lld\n", +- ep_cfg.link_frequencies[0]); ++ if (ep_cfg.nr_of_link_frequencies == 1) { ++ switch (imx219->lanes) { ++ case 2: ++ if (ep_cfg.link_frequencies[0] == ++ IMX219_DEFAULT_LINK_FREQ) ++ link_frequency_valid = true; ++ break; ++ case 4: ++ if (ep_cfg.link_frequencies[0] == ++ IMX219_DEFAULT_LINK_FREQ_4LANE) ++ link_frequency_valid = true; ++ else if (ep_cfg.link_frequencies[0] == ++ IMX219_DEFAULT_LINK_FREQ_4LANE_UNSUPPORTED) { ++ dev_warn(dev, "Link frequency of %d not supported, but has been incorrectly advertised previously\n", ++ IMX219_DEFAULT_LINK_FREQ_4LANE_UNSUPPORTED); ++ dev_warn(dev, "Using link frequency of %d\n", ++ IMX219_DEFAULT_LINK_FREQ_4LANE); ++ link_frequency_valid = true; ++ } ++ break; ++ } ++ } ++ ++ if (!link_frequency_valid) { ++ dev_err_probe(dev, -EINVAL, ++ "Link frequency not supported: %lld\n", ++ ep_cfg.link_frequencies[0]); + goto error_out; + } + +@@ -1319,9 +1513,10 @@ static int imx219_probe(struct i2c_client *client) imx219->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; /* Initialize source pad */ @@ -113878,6 +116429,184 @@ index e196565e846e..e30e14d63823 100644 MODULE_DEVICE_TABLE(of, imx258_dt_ids); static struct i2c_driver imx258_i2c_driver = { +diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c +index f8ada6c1ef65..509ccc247383 100644 +--- a/drivers/media/i2c/imx290.c ++++ b/drivers/media/i2c/imx290.c +@@ -13,6 +13,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -41,6 +42,9 @@ + #define IMX290_WINMODE_720P (1 << 4) + #define IMX290_WINMODE_CROP (4 << 4) + #define IMX290_FR_FDG_SEL CCI_REG8(0x3009) ++#define IMX290_FDG_HCG BIT(4) ++#define IMX290_FRSEL_60FPS BIT(0) ++#define IMX290_FDG_LCG 0 + #define IMX290_BLKLEVEL CCI_REG16_LE(0x300a) + #define IMX290_GAIN CCI_REG8(0x3014) + #define IMX290_VMAX CCI_REG24_LE(0x3018) +@@ -162,6 +166,10 @@ + + #define IMX290_NUM_SUPPLIES 3 + ++static bool hcg_mode; ++module_param(hcg_mode, bool, 0664); ++MODULE_PARM_DESC(hcg_mode, "Enable HCG mode"); ++ + enum imx290_colour_variant { + IMX290_VARIANT_COLOUR, + IMX290_VARIANT_MONO, +@@ -172,12 +180,15 @@ enum imx290_model { + IMX290_MODEL_IMX290LQR, + IMX290_MODEL_IMX290LLR, + IMX290_MODEL_IMX327LQR, ++ IMX290_MODEL_IMX462LQR, ++ IMX290_MODEL_IMX462LLR, + }; + + struct imx290_model_info { + enum imx290_colour_variant colour_variant; + const struct cci_reg_sequence *init_regs; + size_t init_regs_num; ++ unsigned int max_analog_gain; + const char *name; + }; + +@@ -317,6 +328,50 @@ static const struct cci_reg_sequence imx290_global_init_settings_290[] = { + { CCI_REG8(0x33b3), 0x04 }, + }; + ++static const struct cci_reg_sequence imx290_global_init_settings_462[] = { ++ { CCI_REG8(0x300f), 0x00 }, ++ { CCI_REG8(0x3010), 0x21 }, ++ { CCI_REG8(0x3011), 0x02 }, ++ { CCI_REG8(0x3016), 0x09 }, ++ { CCI_REG8(0x3070), 0x02 }, ++ { CCI_REG8(0x3071), 0x11 }, ++ { CCI_REG8(0x309b), 0x10 }, ++ { CCI_REG8(0x309c), 0x22 }, ++ { CCI_REG8(0x30a2), 0x02 }, ++ { CCI_REG8(0x30a6), 0x20 }, ++ { CCI_REG8(0x30a8), 0x20 }, ++ { CCI_REG8(0x30aa), 0x20 }, ++ { CCI_REG8(0x30ac), 0x20 }, ++ { CCI_REG8(0x30b0), 0x43 }, ++ { CCI_REG8(0x3119), 0x9e }, ++ { CCI_REG8(0x311c), 0x1e }, ++ { CCI_REG8(0x311e), 0x08 }, ++ { CCI_REG8(0x3128), 0x05 }, ++ { CCI_REG8(0x313d), 0x83 }, ++ { CCI_REG8(0x3150), 0x03 }, ++ { CCI_REG8(0x317e), 0x00 }, ++ { CCI_REG8(0x32b8), 0x50 }, ++ { CCI_REG8(0x32b9), 0x10 }, ++ { CCI_REG8(0x32ba), 0x00 }, ++ { CCI_REG8(0x32bb), 0x04 }, ++ { CCI_REG8(0x32c8), 0x50 }, ++ { CCI_REG8(0x32c9), 0x10 }, ++ { CCI_REG8(0x32ca), 0x00 }, ++ { CCI_REG8(0x32cb), 0x04 }, ++ { CCI_REG8(0x332c), 0xd3 }, ++ { CCI_REG8(0x332d), 0x10 }, ++ { CCI_REG8(0x332e), 0x0d }, ++ { CCI_REG8(0x3358), 0x06 }, ++ { CCI_REG8(0x3359), 0xe1 }, ++ { CCI_REG8(0x335a), 0x11 }, ++ { CCI_REG8(0x3360), 0x1e }, ++ { CCI_REG8(0x3361), 0x61 }, ++ { CCI_REG8(0x3362), 0x10 }, ++ { CCI_REG8(0x33b0), 0x50 }, ++ { CCI_REG8(0x33b2), 0x1a }, ++ { CCI_REG8(0x33b3), 0x04 }, ++}; ++ + #define IMX290_NUM_CLK_REGS 2 + static const struct cci_reg_sequence xclk_regs[][IMX290_NUM_CLK_REGS] = { + [IMX290_CLK_37_125] = { +@@ -650,7 +705,8 @@ static int imx290_set_data_lanes(struct imx290 *imx290) + &ret); + cci_write(imx290->regmap, IMX290_CSI_LANE_MODE, imx290->nlanes - 1, + &ret); +- cci_write(imx290->regmap, IMX290_FR_FDG_SEL, 0x01, &ret); ++ cci_write(imx290->regmap, IMX290_FR_FDG_SEL, IMX290_FRSEL_60FPS | ++ (hcg_mode ? IMX290_FDG_HCG : IMX290_FDG_LCG), &ret); + + return ret; + } +@@ -879,14 +935,10 @@ static int imx290_ctrl_init(struct imx290 *imx290) + * up to 72.0dB (240) add further digital gain. Limit the range to + * analog gain only, support for digital gain can be added separately + * if needed. +- * +- * The IMX327 and IMX462 are largely compatible with the IMX290, but +- * have an analog gain range of 0.0dB to 29.4dB and 42dB of digital +- * gain. When support for those sensors gets added to the driver, the +- * gain control should be adjusted accordingly. + */ + v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops, +- V4L2_CID_ANALOGUE_GAIN, 0, 100, 1, 0); ++ V4L2_CID_ANALOGUE_GAIN, 0, ++ imx290->model->max_analog_gain, 1, 0); + + /* + * Correct range will be determined through imx290_ctrl_update setting +@@ -1437,20 +1489,37 @@ static const struct imx290_model_info imx290_models[] = { + .colour_variant = IMX290_VARIANT_COLOUR, + .init_regs = imx290_global_init_settings_290, + .init_regs_num = ARRAY_SIZE(imx290_global_init_settings_290), ++ .max_analog_gain = 100, + .name = "imx290", + }, + [IMX290_MODEL_IMX290LLR] = { + .colour_variant = IMX290_VARIANT_MONO, + .init_regs = imx290_global_init_settings_290, + .init_regs_num = ARRAY_SIZE(imx290_global_init_settings_290), ++ .max_analog_gain = 100, + .name = "imx290", + }, + [IMX290_MODEL_IMX327LQR] = { + .colour_variant = IMX290_VARIANT_COLOUR, + .init_regs = imx290_global_init_settings_327, + .init_regs_num = ARRAY_SIZE(imx290_global_init_settings_327), ++ .max_analog_gain = 98, + .name = "imx327", + }, ++ [IMX290_MODEL_IMX462LQR] = { ++ .colour_variant = IMX290_VARIANT_COLOUR, ++ .init_regs = imx290_global_init_settings_462, ++ .init_regs_num = ARRAY_SIZE(imx290_global_init_settings_462), ++ .max_analog_gain = 98, ++ .name = "imx462", ++ }, ++ [IMX290_MODEL_IMX462LLR] = { ++ .colour_variant = IMX290_VARIANT_MONO, ++ .init_regs = imx290_global_init_settings_462, ++ .init_regs_num = ARRAY_SIZE(imx290_global_init_settings_462), ++ .max_analog_gain = 98, ++ .name = "imx462", ++ }, + }; + + static int imx290_parse_dt(struct imx290 *imx290) +@@ -1646,6 +1715,12 @@ static const struct of_device_id imx290_of_match[] = { + }, { + .compatible = "sony,imx327lqr", + .data = &imx290_models[IMX290_MODEL_IMX327LQR], ++ }, { ++ .compatible = "sony,imx462lqr", ++ .data = &imx290_models[IMX290_MODEL_IMX462LQR], ++ }, { ++ .compatible = "sony,imx462llr", ++ .data = &imx290_models[IMX290_MODEL_IMX462LLR], + }, + { /* sentinel */ }, + }; diff --git a/drivers/media/i2c/imx296.c b/drivers/media/i2c/imx296.c index 3b4539b622b4..e87cc1dc038e 100644 --- a/drivers/media/i2c/imx296.c @@ -114179,12 +116908,644 @@ index 3b4539b622b4..e87cc1dc038e 100644 ret = imx296_read(sensor, IMX296_SENSOR_INFO); if (ret < 0) { dev_err(sensor->dev, "failed to read sensor information (%d)\n", +diff --git a/drivers/media/i2c/imx415.c b/drivers/media/i2c/imx415.c +index 3f00172df3cc..7affead11d22 100644 +--- a/drivers/media/i2c/imx415.c ++++ b/drivers/media/i2c/imx415.c +@@ -25,6 +25,10 @@ + #define IMX415_PIXEL_ARRAY_WIDTH 3864 + #define IMX415_PIXEL_ARRAY_HEIGHT 2192 + #define IMX415_PIXEL_ARRAY_VBLANK 58 ++#define IMX415_EXPOSURE_OFFSET 8 ++ ++#define IMX415_PIXEL_RATE_74_25MHZ 891000000 ++#define IMX415_PIXEL_RATE_72MHZ 864000000 + + #define IMX415_NUM_CLK_PARAM_REGS 11 + +@@ -56,7 +60,10 @@ + #define IMX415_OUTSEL IMX415_REG_8BIT(0x30C0) + #define IMX415_DRV IMX415_REG_8BIT(0x30C1) + #define IMX415_VMAX IMX415_REG_24BIT(0x3024) ++#define IMX415_VMAX_MAX 0xfffff + #define IMX415_HMAX IMX415_REG_16BIT(0x3028) ++#define IMX415_HMAX_MAX 0xffff ++#define IMX415_HMAX_MULTIPLIER 12 + #define IMX415_SHR0 IMX415_REG_24BIT(0x3050) + #define IMX415_GAIN_PCG_0 IMX415_REG_16BIT(0x3090) + #define IMX415_AGAIN_MIN 0 +@@ -124,7 +131,7 @@ struct imx415_clk_params { + /* INCK Settings - includes all lane rate and INCK dependent registers */ + static const struct imx415_clk_params imx415_clk_params[] = { + { +- .lane_rate = 594000000, ++ .lane_rate = 594000000UL, + .inck = 27000000, + .regs[0] = { IMX415_BCWAIT_TIME, 0x05D }, + .regs[1] = { IMX415_CPWAIT_TIME, 0x042 }, +@@ -139,7 +146,37 @@ static const struct imx415_clk_params imx415_clk_params[] = { + .regs[10] = { IMX415_TXCLKESC_FREQ, 0x06C0 }, + }, + { +- .lane_rate = 720000000, ++ .lane_rate = 594000000UL, ++ .inck = 37125000, ++ .regs[0] = { IMX415_BCWAIT_TIME, 0x07F }, ++ .regs[1] = { IMX415_CPWAIT_TIME, 0x05B }, ++ .regs[2] = { IMX415_SYS_MODE, 0x7 }, ++ .regs[3] = { IMX415_INCKSEL1, 0x00 }, ++ .regs[4] = { IMX415_INCKSEL2, 0x24 }, ++ .regs[5] = { IMX415_INCKSEL3, 0x080 }, ++ .regs[6] = { IMX415_INCKSEL4, 0x0E0 }, ++ .regs[7] = { IMX415_INCKSEL5, 0x24 }, ++ .regs[8] = { IMX415_INCKSEL6, 0x0 }, ++ .regs[9] = { IMX415_INCKSEL7, 0x1 }, ++ .regs[10] = { IMX415_TXCLKESC_FREQ, 0x0984 }, ++ }, ++ { ++ .lane_rate = 594000000UL, ++ .inck = 74250000, ++ .regs[0] = { IMX415_BCWAIT_TIME, 0x0FF }, ++ .regs[1] = { IMX415_CPWAIT_TIME, 0x0B6 }, ++ .regs[2] = { IMX415_SYS_MODE, 0x7 }, ++ .regs[3] = { IMX415_INCKSEL1, 0x00 }, ++ .regs[4] = { IMX415_INCKSEL2, 0x28 }, ++ .regs[5] = { IMX415_INCKSEL3, 0x080 }, ++ .regs[6] = { IMX415_INCKSEL4, 0x0E0 }, ++ .regs[7] = { IMX415_INCKSEL5, 0x28 }, ++ .regs[8] = { IMX415_INCKSEL6, 0x0 }, ++ .regs[9] = { IMX415_INCKSEL7, 0x1 }, ++ .regs[10] = { IMX415_TXCLKESC_FREQ, 0x1290 }, ++ }, ++ { ++ .lane_rate = 720000000UL, + .inck = 24000000, + .regs[0] = { IMX415_BCWAIT_TIME, 0x054 }, + .regs[1] = { IMX415_CPWAIT_TIME, 0x03B }, +@@ -154,7 +191,22 @@ static const struct imx415_clk_params imx415_clk_params[] = { + .regs[10] = { IMX415_TXCLKESC_FREQ, 0x0600 }, + }, + { +- .lane_rate = 891000000, ++ .lane_rate = 720000000UL, ++ .inck = 72000000, ++ .regs[0] = { IMX415_BCWAIT_TIME, 0x0F8 }, ++ .regs[1] = { IMX415_CPWAIT_TIME, 0x0B0 }, ++ .regs[2] = { IMX415_SYS_MODE, 0x9 }, ++ .regs[3] = { IMX415_INCKSEL1, 0x00 }, ++ .regs[4] = { IMX415_INCKSEL2, 0x28 }, ++ .regs[5] = { IMX415_INCKSEL3, 0x0A0 }, ++ .regs[6] = { IMX415_INCKSEL4, 0x0E0 }, ++ .regs[7] = { IMX415_INCKSEL5, 0x28 }, ++ .regs[8] = { IMX415_INCKSEL6, 0x0 }, ++ .regs[9] = { IMX415_INCKSEL7, 0x1 }, ++ .regs[10] = { IMX415_TXCLKESC_FREQ, 0x1200 }, ++ }, ++ { ++ .lane_rate = 891000000UL, + .inck = 27000000, + .regs[0] = { IMX415_BCWAIT_TIME, 0x05D }, + .regs[1] = { IMX415_CPWAIT_TIME, 0x042 }, +@@ -169,7 +221,37 @@ static const struct imx415_clk_params imx415_clk_params[] = { + .regs[10] = { IMX415_TXCLKESC_FREQ, 0x06C0 }, + }, + { +- .lane_rate = 1440000000, ++ .lane_rate = 891000000UL, ++ .inck = 37125000, ++ .regs[0] = { IMX415_BCWAIT_TIME, 0x07F }, ++ .regs[1] = { IMX415_CPWAIT_TIME, 0x05B }, ++ .regs[2] = { IMX415_SYS_MODE, 0x5 }, ++ .regs[3] = { IMX415_INCKSEL1, 0x00 }, ++ .regs[4] = { IMX415_INCKSEL2, 0x24 }, ++ .regs[5] = { IMX415_INCKSEL3, 0x0C0 }, ++ .regs[6] = { IMX415_INCKSEL4, 0x0E0 }, ++ .regs[7] = { IMX415_INCKSEL5, 0x24 }, ++ .regs[8] = { IMX415_INCKSEL6, 0x0 }, ++ .regs[9] = { IMX415_INCKSEL7, 0x1 }, ++ .regs[10] = { IMX415_TXCLKESC_FREQ, 0x0948 }, ++ }, ++ { ++ .lane_rate = 891000000UL, ++ .inck = 74250000, ++ .regs[0] = { IMX415_BCWAIT_TIME, 0x0FF }, ++ .regs[1] = { IMX415_CPWAIT_TIME, 0x0B6 }, ++ .regs[2] = { IMX415_SYS_MODE, 0x5 }, ++ .regs[3] = { IMX415_INCKSEL1, 0x00 }, ++ .regs[4] = { IMX415_INCKSEL2, 0x28 }, ++ .regs[5] = { IMX415_INCKSEL3, 0x0C0 }, ++ .regs[6] = { IMX415_INCKSEL4, 0x0E0 }, ++ .regs[7] = { IMX415_INCKSEL5, 0x28 }, ++ .regs[8] = { IMX415_INCKSEL6, 0x0 }, ++ .regs[9] = { IMX415_INCKSEL7, 0x1 }, ++ .regs[10] = { IMX415_TXCLKESC_FREQ, 0x1290 }, ++ }, ++ { ++ .lane_rate = 1440000000UL, + .inck = 24000000, + .regs[0] = { IMX415_BCWAIT_TIME, 0x054 }, + .regs[1] = { IMX415_CPWAIT_TIME, 0x03B }, +@@ -184,7 +266,22 @@ static const struct imx415_clk_params imx415_clk_params[] = { + .regs[10] = { IMX415_TXCLKESC_FREQ, 0x0600 }, + }, + { +- .lane_rate = 1485000000, ++ .lane_rate = 1440000000UL, ++ .inck = 72000000, ++ .regs[0] = { IMX415_BCWAIT_TIME, 0x0F8 }, ++ .regs[1] = { IMX415_CPWAIT_TIME, 0x0B0 }, ++ .regs[2] = { IMX415_SYS_MODE, 0x8 }, ++ .regs[3] = { IMX415_INCKSEL1, 0x00 }, ++ .regs[4] = { IMX415_INCKSEL2, 0x28 }, ++ .regs[5] = { IMX415_INCKSEL3, 0x0A0 }, ++ .regs[6] = { IMX415_INCKSEL4, 0x0E0 }, ++ .regs[7] = { IMX415_INCKSEL5, 0x28 }, ++ .regs[8] = { IMX415_INCKSEL6, 0x1 }, ++ .regs[9] = { IMX415_INCKSEL7, 0x0 }, ++ .regs[10] = { IMX415_TXCLKESC_FREQ, 0x1200 }, ++ }, ++ { ++ .lane_rate = 1485000000UL, + .inck = 27000000, + .regs[0] = { IMX415_BCWAIT_TIME, 0x05D }, + .regs[1] = { IMX415_CPWAIT_TIME, 0x042 }, +@@ -198,13 +295,175 @@ static const struct imx415_clk_params imx415_clk_params[] = { + .regs[9] = { IMX415_INCKSEL7, 0x0 }, + .regs[10] = { IMX415_TXCLKESC_FREQ, 0x06C0 }, + }, ++ { ++ .lane_rate = 1485000000UL, ++ .inck = 37125000, ++ .regs[0] = { IMX415_BCWAIT_TIME, 0x07F }, ++ .regs[1] = { IMX415_CPWAIT_TIME, 0x05B }, ++ .regs[2] = { IMX415_SYS_MODE, 0x8 }, ++ .regs[3] = { IMX415_INCKSEL1, 0x00 }, ++ .regs[4] = { IMX415_INCKSEL2, 0x24 }, ++ .regs[5] = { IMX415_INCKSEL3, 0x0A0 }, ++ .regs[6] = { IMX415_INCKSEL4, 0x0E0 }, ++ .regs[7] = { IMX415_INCKSEL5, 0x24 }, ++ .regs[8] = { IMX415_INCKSEL6, 0x1 }, ++ .regs[9] = { IMX415_INCKSEL7, 0x0 }, ++ .regs[10] = { IMX415_TXCLKESC_FREQ, 0x0948 }, ++ }, ++ { ++ .lane_rate = 1485000000UL, ++ .inck = 74250000, ++ .regs[0] = { IMX415_BCWAIT_TIME, 0x0FF }, ++ .regs[1] = { IMX415_CPWAIT_TIME, 0x0B6 }, ++ .regs[2] = { IMX415_SYS_MODE, 0x8 }, ++ .regs[3] = { IMX415_INCKSEL1, 0x00 }, ++ .regs[4] = { IMX415_INCKSEL2, 0x28 }, ++ .regs[5] = { IMX415_INCKSEL3, 0x0A0 }, ++ .regs[6] = { IMX415_INCKSEL4, 0x0E0 }, ++ .regs[7] = { IMX415_INCKSEL5, 0x28 }, ++ .regs[8] = { IMX415_INCKSEL6, 0x1 }, ++ .regs[9] = { IMX415_INCKSEL7, 0x0 }, ++ .regs[10] = { IMX415_TXCLKESC_FREQ, 0x1290 }, ++ }, ++ { ++ .lane_rate = 1782000000UL, ++ .inck = 27000000, ++ .regs[0] = { IMX415_BCWAIT_TIME, 0x05D }, ++ .regs[1] = { IMX415_CPWAIT_TIME, 0x042 }, ++ .regs[2] = { IMX415_SYS_MODE, 0x4 }, ++ .regs[3] = { IMX415_INCKSEL1, 0x00 }, ++ .regs[4] = { IMX415_INCKSEL2, 0x23 }, ++ .regs[5] = { IMX415_INCKSEL3, 0x0C6 }, ++ .regs[6] = { IMX415_INCKSEL4, 0x0E7 }, ++ .regs[7] = { IMX415_INCKSEL5, 0x23 }, ++ .regs[8] = { IMX415_INCKSEL6, 0x1 }, ++ .regs[9] = { IMX415_INCKSEL7, 0x0 }, ++ .regs[10] = { IMX415_TXCLKESC_FREQ, 0x06C0 }, ++ }, ++ { ++ .lane_rate = 1782000000UL, ++ .inck = 37125000, ++ .regs[0] = { IMX415_BCWAIT_TIME, 0x07F }, ++ .regs[1] = { IMX415_CPWAIT_TIME, 0x05B }, ++ .regs[2] = { IMX415_SYS_MODE, 0x4 }, ++ .regs[3] = { IMX415_INCKSEL1, 0x00 }, ++ .regs[4] = { IMX415_INCKSEL2, 0x24 }, ++ .regs[5] = { IMX415_INCKSEL3, 0x0C0 }, ++ .regs[6] = { IMX415_INCKSEL4, 0x0E0 }, ++ .regs[7] = { IMX415_INCKSEL5, 0x24 }, ++ .regs[8] = { IMX415_INCKSEL6, 0x1 }, ++ .regs[9] = { IMX415_INCKSEL7, 0x0 }, ++ .regs[10] = { IMX415_TXCLKESC_FREQ, 0x0948 }, ++ }, ++ { ++ .lane_rate = 1782000000UL, ++ .inck = 74250000, ++ .regs[0] = { IMX415_BCWAIT_TIME, 0x0FF }, ++ .regs[1] = { IMX415_CPWAIT_TIME, 0x0B6 }, ++ .regs[2] = { IMX415_SYS_MODE, 0x4 }, ++ .regs[3] = { IMX415_INCKSEL1, 0x00 }, ++ .regs[4] = { IMX415_INCKSEL2, 0x28 }, ++ .regs[5] = { IMX415_INCKSEL3, 0x0C0 }, ++ .regs[6] = { IMX415_INCKSEL4, 0x0E0 }, ++ .regs[7] = { IMX415_INCKSEL5, 0x28 }, ++ .regs[8] = { IMX415_INCKSEL6, 0x1 }, ++ .regs[9] = { IMX415_INCKSEL7, 0x0 }, ++ .regs[10] = { IMX415_TXCLKESC_FREQ, 0x1290 }, ++ }, ++ { ++ .lane_rate = 2079000000UL, ++ .inck = 27000000, ++ .regs[0] = { IMX415_BCWAIT_TIME, 0x05D }, ++ .regs[1] = { IMX415_CPWAIT_TIME, 0x042 }, ++ .regs[2] = { IMX415_SYS_MODE, 0x2 }, ++ .regs[3] = { IMX415_INCKSEL1, 0x00 }, ++ .regs[4] = { IMX415_INCKSEL2, 0x23 }, ++ .regs[5] = { IMX415_INCKSEL3, 0x0E7 }, ++ .regs[6] = { IMX415_INCKSEL4, 0x0E7 }, ++ .regs[7] = { IMX415_INCKSEL5, 0x23 }, ++ .regs[8] = { IMX415_INCKSEL6, 0x1 }, ++ .regs[9] = { IMX415_INCKSEL7, 0x0 }, ++ .regs[10] = { IMX415_TXCLKESC_FREQ, 0x06C0 }, ++ }, ++ { ++ .lane_rate = 2079000000UL, ++ .inck = 37125000, ++ .regs[0] = { IMX415_BCWAIT_TIME, 0x07F }, ++ .regs[1] = { IMX415_CPWAIT_TIME, 0x05B }, ++ .regs[2] = { IMX415_SYS_MODE, 0x2 }, ++ .regs[3] = { IMX415_INCKSEL1, 0x00 }, ++ .regs[4] = { IMX415_INCKSEL2, 0x24 }, ++ .regs[5] = { IMX415_INCKSEL3, 0x0E0 }, ++ .regs[6] = { IMX415_INCKSEL4, 0x0E0 }, ++ .regs[7] = { IMX415_INCKSEL5, 0x24 }, ++ .regs[8] = { IMX415_INCKSEL6, 0x1 }, ++ .regs[9] = { IMX415_INCKSEL7, 0x0 }, ++ .regs[10] = { IMX415_TXCLKESC_FREQ, 0x0948 }, ++ }, ++ { ++ .lane_rate = 2079000000UL, ++ .inck = 74250000, ++ .regs[0] = { IMX415_BCWAIT_TIME, 0x0FF }, ++ .regs[1] = { IMX415_CPWAIT_TIME, 0x0B6 }, ++ .regs[2] = { IMX415_SYS_MODE, 0x2 }, ++ .regs[3] = { IMX415_INCKSEL1, 0x00 }, ++ .regs[4] = { IMX415_INCKSEL2, 0x28 }, ++ .regs[5] = { IMX415_INCKSEL3, 0x0E0 }, ++ .regs[6] = { IMX415_INCKSEL4, 0x0E0 }, ++ .regs[7] = { IMX415_INCKSEL5, 0x28 }, ++ .regs[8] = { IMX415_INCKSEL6, 0x1 }, ++ .regs[9] = { IMX415_INCKSEL7, 0x0 }, ++ .regs[10] = { IMX415_TXCLKESC_FREQ, 0x1290 }, ++ }, ++ { ++ .lane_rate = 2376000000UL, ++ .inck = 27000000, ++ .regs[0] = { IMX415_BCWAIT_TIME, 0x05D }, ++ .regs[1] = { IMX415_CPWAIT_TIME, 0x042 }, ++ .regs[2] = { IMX415_SYS_MODE, 0x0 }, ++ .regs[3] = { IMX415_INCKSEL1, 0x00 }, ++ .regs[4] = { IMX415_INCKSEL2, 0x23 }, ++ .regs[5] = { IMX415_INCKSEL3, 0x108 }, ++ .regs[6] = { IMX415_INCKSEL4, 0x0E7 }, ++ .regs[7] = { IMX415_INCKSEL5, 0x23 }, ++ .regs[8] = { IMX415_INCKSEL6, 0x1 }, ++ .regs[9] = { IMX415_INCKSEL7, 0x0 }, ++ .regs[10] = { IMX415_TXCLKESC_FREQ, 0x06C0 }, ++ }, ++ { ++ .lane_rate = 2376000000UL, ++ .inck = 37125000, ++ .regs[0] = { IMX415_BCWAIT_TIME, 0x07F }, ++ .regs[1] = { IMX415_CPWAIT_TIME, 0x05B }, ++ .regs[2] = { IMX415_SYS_MODE, 0x0 }, ++ .regs[3] = { IMX415_INCKSEL1, 0x00 }, ++ .regs[4] = { IMX415_INCKSEL2, 0x24 }, ++ .regs[5] = { IMX415_INCKSEL3, 0x100 }, ++ .regs[6] = { IMX415_INCKSEL4, 0x0E0 }, ++ .regs[7] = { IMX415_INCKSEL5, 0x24 }, ++ .regs[8] = { IMX415_INCKSEL6, 0x1 }, ++ .regs[9] = { IMX415_INCKSEL7, 0x0 }, ++ .regs[10] = { IMX415_TXCLKESC_FREQ, 0x0948 }, ++ }, ++ { ++ .lane_rate = 2376000000UL, ++ .inck = 74250000, ++ .regs[0] = { IMX415_BCWAIT_TIME, 0x0FF }, ++ .regs[1] = { IMX415_CPWAIT_TIME, 0x0B6 }, ++ .regs[2] = { IMX415_SYS_MODE, 0x0 }, ++ .regs[3] = { IMX415_INCKSEL1, 0x00 }, ++ .regs[4] = { IMX415_INCKSEL2, 0x28 }, ++ .regs[5] = { IMX415_INCKSEL3, 0x100 }, ++ .regs[6] = { IMX415_INCKSEL4, 0x0E0 }, ++ .regs[7] = { IMX415_INCKSEL5, 0x28 }, ++ .regs[8] = { IMX415_INCKSEL6, 0x1 }, ++ .regs[9] = { IMX415_INCKSEL7, 0x0 }, ++ .regs[10] = { IMX415_TXCLKESC_FREQ, 0x1290 }, ++ }, + }; + +-/* all-pixel 2-lane 720 Mbps 15.74 Hz mode */ +-static const struct imx415_reg imx415_mode_2_720[] = { +- { IMX415_VMAX, 0x08CA }, +- { IMX415_HMAX, 0x07F0 }, +- { IMX415_LANEMODE, IMX415_LANEMODE_2 }, ++/* 720 Mbps CSI configuration */ ++static const struct imx415_reg imx415_linkrate_720mbps[] = { + { IMX415_TCLKPOST, 0x006F }, + { IMX415_TCLKPREPARE, 0x002F }, + { IMX415_TCLKTRAIL, 0x002F }, +@@ -216,11 +475,8 @@ static const struct imx415_reg imx415_mode_2_720[] = { + { IMX415_TLPX, 0x0027 }, + }; + +-/* all-pixel 2-lane 1440 Mbps 30.01 Hz mode */ +-static const struct imx415_reg imx415_mode_2_1440[] = { +- { IMX415_VMAX, 0x08CA }, +- { IMX415_HMAX, 0x042A }, +- { IMX415_LANEMODE, IMX415_LANEMODE_2 }, ++/* 1440 Mbps CSI configuration */ ++static const struct imx415_reg imx415_linkrate_1440mbps[] = { + { IMX415_TCLKPOST, 0x009F }, + { IMX415_TCLKPREPARE, 0x0057 }, + { IMX415_TCLKTRAIL, 0x0057 }, +@@ -232,11 +488,8 @@ static const struct imx415_reg imx415_mode_2_1440[] = { + { IMX415_TLPX, 0x004F }, + }; + +-/* all-pixel 4-lane 891 Mbps 30 Hz mode */ +-static const struct imx415_reg imx415_mode_4_891[] = { +- { IMX415_VMAX, 0x08CA }, +- { IMX415_HMAX, 0x044C }, +- { IMX415_LANEMODE, IMX415_LANEMODE_4 }, ++/* 891 Mbps */ ++static const struct imx415_reg imx415_linkrate_891mbps[] = { + { IMX415_TCLKPOST, 0x007F }, + { IMX415_TCLKPREPARE, 0x0037 }, + { IMX415_TCLKTRAIL, 0x0037 }, +@@ -253,39 +506,9 @@ struct imx415_mode_reg_list { + const struct imx415_reg *regs; + }; + +-/* +- * Mode : number of lanes, lane rate and frame rate dependent settings +- * +- * pixel_rate and hmax_pix are needed to calculate hblank for the v4l2 ctrl +- * interface. These values can not be found in the data sheet and should be +- * treated as virtual values. Use following table when adding new modes. +- * +- * lane_rate lanes fps hmax_pix pixel_rate +- * +- * 594 2 10.000 4400 99000000 +- * 891 2 15.000 4400 148500000 +- * 720 2 15.748 4064 144000000 +- * 1782 2 30.000 4400 297000000 +- * 2079 2 30.000 4400 297000000 +- * 1440 2 30.019 4510 304615385 +- * +- * 594 4 20.000 5500 247500000 +- * 594 4 25.000 4400 247500000 +- * 720 4 25.000 4400 247500000 +- * 720 4 30.019 4510 304615385 +- * 891 4 30.000 4400 297000000 +- * 1440 4 30.019 4510 304615385 +- * 1440 4 60.038 4510 609230769 +- * 1485 4 60.000 4400 594000000 +- * 1782 4 60.000 4400 594000000 +- * 2079 4 60.000 4400 594000000 +- * 2376 4 90.164 4392 891000000 +- */ + struct imx415_mode { + u64 lane_rate; +- u32 lanes; +- u32 hmax_pix; +- u64 pixel_rate; ++ u32 hmax_min[2]; + struct imx415_mode_reg_list reg_list; + }; + +@@ -293,32 +516,26 @@ struct imx415_mode { + static const struct imx415_mode supported_modes[] = { + { + .lane_rate = 720000000, +- .lanes = 2, +- .hmax_pix = 4064, +- .pixel_rate = 144000000, ++ .hmax_min = { 2032, 1066 }, + .reg_list = { +- .num_of_regs = ARRAY_SIZE(imx415_mode_2_720), +- .regs = imx415_mode_2_720, ++ .num_of_regs = ARRAY_SIZE(imx415_linkrate_720mbps), ++ .regs = imx415_linkrate_720mbps, + }, + }, + { + .lane_rate = 1440000000, +- .lanes = 2, +- .hmax_pix = 4510, +- .pixel_rate = 304615385, ++ .hmax_min = { 1066, 533 }, + .reg_list = { +- .num_of_regs = ARRAY_SIZE(imx415_mode_2_1440), +- .regs = imx415_mode_2_1440, ++ .num_of_regs = ARRAY_SIZE(imx415_linkrate_1440mbps), ++ .regs = imx415_linkrate_1440mbps, + }, + }, + { + .lane_rate = 891000000, +- .lanes = 4, +- .hmax_pix = 4400, +- .pixel_rate = 297000000, ++ .hmax_min = { 1100, 550 }, + .reg_list = { +- .num_of_regs = ARRAY_SIZE(imx415_mode_4_891), +- .regs = imx415_mode_4_891, ++ .num_of_regs = ARRAY_SIZE(imx415_linkrate_891mbps), ++ .regs = imx415_linkrate_891mbps, + }, + }, + }; +@@ -347,6 +564,7 @@ static const char *const imx415_test_pattern_menu[] = { + struct imx415 { + struct device *dev; + struct clk *clk; ++ unsigned long pixel_rate; + struct regulator_bulk_data supplies[ARRAY_SIZE(imx415_supply_names)]; + struct gpio_desc *reset; + struct regmap *regmap; +@@ -360,8 +578,10 @@ struct imx415 { + + struct v4l2_ctrl_handler ctrls; + struct v4l2_ctrl *vblank; ++ struct v4l2_ctrl *hblank; + struct v4l2_ctrl *hflip; + struct v4l2_ctrl *vflip; ++ struct v4l2_ctrl *exposure; + + unsigned int cur_mode; + unsigned int num_data_lanes; +@@ -540,16 +760,37 @@ static int imx415_s_ctrl(struct v4l2_ctrl *ctrl) + ctrls); + const struct v4l2_mbus_framefmt *format; + struct v4l2_subdev_state *state; ++ u32 exposure_max; + unsigned int vmax; + unsigned int flip; +- +- if (!sensor->streaming) +- return 0; ++ int ret; + + state = v4l2_subdev_get_locked_active_state(&sensor->subdev); + format = v4l2_subdev_get_pad_format(&sensor->subdev, state, 0); + ++ if (ctrl->id == V4L2_CID_VBLANK) { ++ exposure_max = format->height + ctrl->val - ++ IMX415_EXPOSURE_OFFSET; ++ __v4l2_ctrl_modify_range(sensor->exposure, ++ sensor->exposure->minimum, ++ exposure_max, sensor->exposure->step, ++ sensor->exposure->default_value); ++ } ++ ++ if (!sensor->streaming) ++ return 0; ++ + switch (ctrl->id) { ++ case V4L2_CID_VBLANK: ++ ret = imx415_write(sensor, IMX415_VMAX, ++ format->height + ctrl->val); ++ if (ret) ++ return ret; ++ /* ++ * Deliberately fall through as exposure is set based on VMAX ++ * which has just changed. ++ */ ++ fallthrough; + case V4L2_CID_EXPOSURE: + /* clamp the exposure value to VMAX. */ + vmax = format->height + sensor->vblank->cur.val; +@@ -569,6 +810,11 @@ static int imx415_s_ctrl(struct v4l2_ctrl *ctrl) + case V4L2_CID_TEST_PATTERN: + return imx415_set_testpattern(sensor, ctrl->val); + ++ case V4L2_CID_HBLANK: ++ return imx415_write(sensor, IMX415_HMAX, ++ (format->width + ctrl->val) / ++ IMX415_HMAX_MULTIPLIER); ++ + default: + return -EINVAL; + } +@@ -582,11 +828,11 @@ static int imx415_ctrls_init(struct imx415 *sensor) + { + struct v4l2_fwnode_device_properties props; + struct v4l2_ctrl *ctrl; +- u64 pixel_rate = supported_modes[sensor->cur_mode].pixel_rate; + u64 lane_rate = supported_modes[sensor->cur_mode].lane_rate; + u32 exposure_max = IMX415_PIXEL_ARRAY_HEIGHT + +- IMX415_PIXEL_ARRAY_VBLANK - 8; +- u32 hblank; ++ IMX415_PIXEL_ARRAY_VBLANK - ++ IMX415_EXPOSURE_OFFSET; ++ u32 hblank_min, hblank_max; + unsigned int i; + int ret; + +@@ -614,36 +860,33 @@ static int imx415_ctrls_init(struct imx415 *sensor) + if (ctrl) + ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; + +- v4l2_ctrl_new_std(&sensor->ctrls, &imx415_ctrl_ops, V4L2_CID_EXPOSURE, +- 4, exposure_max, 1, exposure_max); ++ sensor->exposure = v4l2_ctrl_new_std(&sensor->ctrls, &imx415_ctrl_ops, ++ V4L2_CID_EXPOSURE, 4, ++ exposure_max, 1, exposure_max); + + v4l2_ctrl_new_std(&sensor->ctrls, &imx415_ctrl_ops, + V4L2_CID_ANALOGUE_GAIN, IMX415_AGAIN_MIN, + IMX415_AGAIN_MAX, IMX415_AGAIN_STEP, + IMX415_AGAIN_MIN); + +- hblank = supported_modes[sensor->cur_mode].hmax_pix - +- IMX415_PIXEL_ARRAY_WIDTH; ++ hblank_min = (supported_modes[sensor->cur_mode].hmax_min[sensor->num_data_lanes == 2 ? 0 : 1] * ++ IMX415_HMAX_MULTIPLIER) - IMX415_PIXEL_ARRAY_WIDTH; ++ hblank_max = (IMX415_HMAX_MAX * IMX415_HMAX_MULTIPLIER) - ++ IMX415_PIXEL_ARRAY_WIDTH; + ctrl = v4l2_ctrl_new_std(&sensor->ctrls, &imx415_ctrl_ops, +- V4L2_CID_HBLANK, hblank, hblank, 1, hblank); +- if (ctrl) +- ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; ++ V4L2_CID_HBLANK, hblank_min, ++ hblank_max, IMX415_HMAX_MULTIPLIER, ++ hblank_min); + + sensor->vblank = v4l2_ctrl_new_std(&sensor->ctrls, &imx415_ctrl_ops, + V4L2_CID_VBLANK, + IMX415_PIXEL_ARRAY_VBLANK, +- IMX415_PIXEL_ARRAY_VBLANK, 1, +- IMX415_PIXEL_ARRAY_VBLANK); +- if (sensor->vblank) +- sensor->vblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; ++ IMX415_VMAX_MAX - IMX415_PIXEL_ARRAY_HEIGHT, ++ 1, IMX415_PIXEL_ARRAY_VBLANK); + +- /* +- * The pixel rate used here is a virtual value and can be used for +- * calculating the frame rate together with hblank. It may not +- * necessarily be the physically correct pixel clock. +- */ +- v4l2_ctrl_new_std(&sensor->ctrls, NULL, V4L2_CID_PIXEL_RATE, pixel_rate, +- pixel_rate, 1, pixel_rate); ++ v4l2_ctrl_new_std(&sensor->ctrls, NULL, V4L2_CID_PIXEL_RATE, ++ sensor->pixel_rate, sensor->pixel_rate, 1, ++ sensor->pixel_rate); + + sensor->hflip = v4l2_ctrl_new_std(&sensor->ctrls, &imx415_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); +@@ -694,7 +937,11 @@ static int imx415_set_mode(struct imx415 *sensor, int mode) + return ret; + } + +- return 0; ++ ret = imx415_write(sensor, IMX415_LANEMODE, ++ sensor->num_data_lanes == 2 ? IMX415_LANEMODE_2 : ++ IMX415_LANEMODE_4); ++ ++ return ret; + } + + static int imx415_setup(struct imx415 *sensor, struct v4l2_subdev_state *state) +@@ -1123,8 +1370,6 @@ static int imx415_parse_hw_config(struct imx415 *sensor) + } + + for (j = 0; j < ARRAY_SIZE(supported_modes); ++j) { +- if (sensor->num_data_lanes != supported_modes[j].lanes) +- continue; + if (bus_cfg.link_frequencies[i] * 2 != + supported_modes[j].lane_rate) + continue; +@@ -1139,6 +1384,17 @@ static int imx415_parse_hw_config(struct imx415 *sensor) + "no valid sensor mode defined\n"); + goto done_endpoint_free; + } ++ switch (inck) { ++ case 27000000: ++ case 37125000: ++ case 74250000: ++ sensor->pixel_rate = IMX415_PIXEL_RATE_74_25MHZ; ++ break; ++ case 24000000: ++ case 72000000: ++ sensor->pixel_rate = IMX415_PIXEL_RATE_72MHZ; ++ break; ++ } + + lane_rate = supported_modes[sensor->cur_mode].lane_rate; + for (i = 0; i < ARRAY_SIZE(imx415_clk_params); ++i) { diff --git a/drivers/media/i2c/imx477.c b/drivers/media/i2c/imx477.c new file mode 100644 -index 000000000000..dce7f2b4fa57 +index 000000000000..317f9adf9f6b --- /dev/null +++ b/drivers/media/i2c/imx477.c -@@ -0,0 +1,2339 @@ +@@ -0,0 +1,2387 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * A V4L2 driver for Sony IMX477 cameras. @@ -114351,8 +117712,48 @@ index 000000000000..dce7f2b4fa57 + struct imx477_reg_list reg_list; +}; + -+static const s64 imx477_link_freq_menu[] = { -+ IMX477_DEFAULT_LINK_FREQ, ++/* Link frequency setup */ ++enum { ++ IMX477_LINK_FREQ_450MHZ, ++ IMX477_LINK_FREQ_453MHZ, ++ IMX477_LINK_FREQ_456MHZ, ++}; ++ ++static const s64 link_freqs[] = { ++ [IMX477_LINK_FREQ_450MHZ] = 450000000, ++ [IMX477_LINK_FREQ_453MHZ] = 453000000, ++ [IMX477_LINK_FREQ_456MHZ] = 456000000, ++}; ++ ++/* 450MHz is the nominal "default" link frequency */ ++static const struct imx477_reg link_450Mhz_regs[] = { ++ {0x030E, 0x00}, ++ {0x030F, 0x96}, ++}; ++ ++static const struct imx477_reg link_453Mhz_regs[] = { ++ {0x030E, 0x00}, ++ {0x030F, 0x97}, ++}; ++ ++static const struct imx477_reg link_456Mhz_regs[] = { ++ {0x030E, 0x00}, ++ {0x030F, 0x98}, ++}; ++ ++static const struct imx477_reg_list link_freq_regs[] = { ++ [IMX477_LINK_FREQ_450MHZ] = { ++ .regs = link_450Mhz_regs, ++ .num_of_regs = ARRAY_SIZE(link_450Mhz_regs) ++ }, ++ [IMX477_LINK_FREQ_453MHZ] = { ++ .regs = link_453Mhz_regs, ++ .num_of_regs = ARRAY_SIZE(link_453Mhz_regs) ++ }, ++ [IMX477_LINK_FREQ_456MHZ] = { ++ .regs = link_456Mhz_regs, ++ .num_of_regs = ARRAY_SIZE(link_456Mhz_regs) ++ }, +}; + +static const struct imx477_reg mode_common_regs[] = { @@ -114745,8 +118146,6 @@ index 000000000000..dce7f2b4fa57 + {0x0309, 0x0c}, + {0x030b, 0x02}, + {0x030d, 0x02}, -+ {0x030e, 0x00}, -+ {0x030f, 0x96}, + {0x0310, 0x01}, + {0x0820, 0x07}, + {0x0821, 0x08}, @@ -114846,8 +118245,6 @@ index 000000000000..dce7f2b4fa57 + {0x0309, 0x0c}, + {0x030b, 0x02}, + {0x030d, 0x02}, -+ {0x030e, 0x00}, -+ {0x030f, 0x96}, + {0x0310, 0x01}, + {0x0820, 0x07}, + {0x0821, 0x08}, @@ -114947,8 +118344,6 @@ index 000000000000..dce7f2b4fa57 + {0x0309, 0x0c}, + {0x030b, 0x02}, + {0x030d, 0x02}, -+ {0x030e, 0x00}, -+ {0x030f, 0x96}, + {0x0310, 0x01}, + {0x0820, 0x07}, + {0x0821, 0x08}, @@ -115077,8 +118472,6 @@ index 000000000000..dce7f2b4fa57 + {0x0309, 0x0a}, + {0x030b, 0x02}, + {0x030d, 0x02}, -+ {0x030e, 0x00}, -+ {0x030f, 0x96}, + {0x0310, 0x01}, + {0x0820, 0x07}, + {0x0821, 0x08}, @@ -115308,6 +118701,8 @@ index 000000000000..dce7f2b4fa57 + struct v4l2_ctrl *vblank; + struct v4l2_ctrl *hblank; + ++ unsigned int link_freq_idx; ++ + /* Current mode */ + const struct imx477_mode *mode; + @@ -115899,7 +119294,7 @@ index 000000000000..dce7f2b4fa57 +static int imx477_start_streaming(struct imx477 *imx477) +{ + struct i2c_client *client = v4l2_get_subdevdata(&imx477->sd); -+ const struct imx477_reg_list *reg_list; ++ const struct imx477_reg_list *reg_list, *freq_regs; + const struct imx477_reg_list *extra_regs; + int ret, tm; + @@ -115912,6 +119307,13 @@ index 000000000000..dce7f2b4fa57 + extra_regs->num_of_regs); + } + ++ if (!ret) { ++ /* Update the link frequency registers */ ++ freq_regs = &link_freq_regs[imx477->link_freq_idx]; ++ ret = imx477_write_regs(imx477, freq_regs->regs, ++ freq_regs->num_of_regs); ++ } ++ + if (ret) { + dev_err(&client->dev, "%s failed to set common settings\n", + __func__); @@ -116197,9 +119599,8 @@ index 000000000000..dce7f2b4fa57 + /* LINK_FREQ is also read only */ + imx477->link_freq = + v4l2_ctrl_new_int_menu(ctrl_hdlr, &imx477_ctrl_ops, -+ V4L2_CID_LINK_FREQ, -+ ARRAY_SIZE(imx477_link_freq_menu) - 1, 0, -+ imx477_link_freq_menu); ++ V4L2_CID_LINK_FREQ, 0, 0, ++ &link_freqs[imx477->link_freq_idx]); + if (imx477->link_freq) + imx477->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; + @@ -116297,13 +119698,14 @@ index 000000000000..dce7f2b4fa57 + mutex_destroy(&imx477->mutex); +} + -+static int imx477_check_hwcfg(struct device *dev) ++static int imx477_check_hwcfg(struct device *dev, struct imx477 *imx477) +{ + struct fwnode_handle *endpoint; + struct v4l2_fwnode_endpoint ep_cfg = { + .bus_type = V4L2_MBUS_CSI2_DPHY + }; + int ret = -EINVAL; ++ int i; + + endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(dev), NULL); + if (!endpoint) { @@ -116328,11 +119730,18 @@ index 000000000000..dce7f2b4fa57 + goto error_out; + } + -+ if (ep_cfg.nr_of_link_frequencies != 1 || -+ ep_cfg.link_frequencies[0] != IMX477_DEFAULT_LINK_FREQ) { ++ for (i = 0; i < ARRAY_SIZE(link_freqs); i++) { ++ if (link_freqs[i] == ep_cfg.link_frequencies[0]) { ++ imx477->link_freq_idx = i; ++ break; ++ } ++ } ++ ++ if (i == ARRAY_SIZE(link_freqs)) { + dev_err(dev, "Link frequency not supported: %lld\n", + ep_cfg.link_frequencies[0]); -+ goto error_out; ++ ret = -EINVAL; ++ goto error_out; + } + + ret = 0; @@ -116393,7 +119802,7 @@ index 000000000000..dce7f2b4fa57 + (const struct imx477_compatible_data *)match->data; + + /* Check the hardware configuration in device tree */ -+ if (imx477_check_hwcfg(dev)) ++ if (imx477_check_hwcfg(dev, imx477)) + return -EINVAL; + + /* Default the trigger mode from OF to -1, which means invalid */ @@ -131234,7 +134643,7 @@ index 675fb37a6fea..fc26f2b6a604 100644 ov7251->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; ov7251->pad.flags = MEDIA_PAD_FL_SOURCE; diff --git a/drivers/media/i2c/ov9282.c b/drivers/media/i2c/ov9282.c -index 068c7449f50e..0b092a425cfc 100644 +index 7498f55b3ef2..5b8583d9a8c2 100644 --- a/drivers/media/i2c/ov9282.c +++ b/drivers/media/i2c/ov9282.c @@ -1078,12 +1078,16 @@ static int ov9282_set_stream(struct v4l2_subdev *sd, int enable) @@ -131257,7 +134666,7 @@ index 068c7449f50e..0b092a425cfc 100644 dev_err(ov9282->dev, "chip id mismatch: %x!=%x", OV9282_ID, val); diff --git a/drivers/media/i2c/tc358743.c b/drivers/media/i2c/tc358743.c -index 558152575d10..d4fb57d5b123 100644 +index c81dd4183404..e5a9c84dd832 100644 --- a/drivers/media/i2c/tc358743.c +++ b/drivers/media/i2c/tc358743.c @@ -110,7 +110,7 @@ static inline struct tc358743_state *to_state(struct v4l2_subdev *sd) @@ -131567,7 +134976,7 @@ index 000000000000..bd011b6b8f0e + Enable build of Hailo AI accelerator PCIe driver. diff --git a/drivers/media/pci/hailo/Makefile b/drivers/media/pci/hailo/Makefile new file mode 100644 -index 000000000000..cea09ca1ee37 +index 000000000000..c1079ae28771 --- /dev/null +++ b/drivers/media/pci/hailo/Makefile @@ -0,0 +1,34 @@ @@ -131581,9 +134990,9 @@ index 000000000000..cea09ca1ee37 + +hailo_pci-objs += src/pcie.o +hailo_pci-objs += src/fops.o -+hailo_pci-objs += src/utils.o +hailo_pci-objs += src/sysfs.o -+hailo_pci-objs += src/pci_soc_ioctl.o ++hailo_pci-objs += src/nnc.o ++hailo_pci-objs += src/soc.o + +hailo_pci-objs += $(COMMON_SRC_DIRECTORY)/fw_validation.o +hailo_pci-objs += $(COMMON_SRC_DIRECTORY)/fw_operation.o @@ -131607,14 +135016,14 @@ index 000000000000..cea09ca1ee37 +clean-files := $(hailo_pci-objs) diff --git a/drivers/media/pci/hailo/common/fw_operation.c b/drivers/media/pci/hailo/common/fw_operation.c new file mode 100644 -index 000000000000..ef9926441981 +index 000000000000..fb3b7c167340 --- /dev/null +++ b/drivers/media/pci/hailo/common/fw_operation.c -@@ -0,0 +1,103 @@ +@@ -0,0 +1,147 @@ +// SPDX-License-Identifier: MIT +/** -+ * Copyright (c) 2022 Hailo Technologies Ltd. All rights reserved. -+**/ ++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. ++ **/ + +#include "fw_operation.h" + @@ -131628,7 +135037,10 @@ index 000000000000..ef9926441981 + u32 chip_offset; +} FW_DEBUG_BUFFER_HEADER_t; + -+#define DEBUG_BUFFER_DATA_SIZE (DEBUG_BUFFER_TOTAL_SIZE - sizeof(FW_DEBUG_BUFFER_HEADER_t)) ++#define DEBUG_BUFFER_DATA_SIZE (DEBUG_BUFFER_TOTAL_SIZE - sizeof(FW_DEBUG_BUFFER_HEADER_t)) ++#define PCIE_D2H_NOTIFICATION_SRAM_OFFSET (0x640 + 0x640) ++#define PCIE_APP_CPU_DEBUG_OFFSET (8*1024) ++#define PCIE_CORE_CPU_DEBUG_OFFSET (PCIE_APP_CPU_DEBUG_OFFSET + DEBUG_BUFFER_TOTAL_SIZE) + +int hailo_read_firmware_notification(struct hailo_resource *resource, struct hailo_d2h_notification *notification) +{ @@ -131648,6 +135060,21 @@ index 000000000000..ef9926441981 + return 0; +} + ++int hailo_pcie_read_firmware_notification(struct hailo_resource *resource, ++ struct hailo_d2h_notification *notification) ++{ ++ struct hailo_resource notification_resource; ++ ++ if (PCIE_D2H_NOTIFICATION_SRAM_OFFSET > resource->size) { ++ return -EINVAL; ++ } ++ ++ notification_resource.address = resource->address + PCIE_D2H_NOTIFICATION_SRAM_OFFSET, ++ notification_resource.size = sizeof(struct hailo_d2h_notification); ++ ++ return hailo_read_firmware_notification(¬ification_resource, notification); ++} ++ +static inline size_t calculate_log_ready_to_read(FW_DEBUG_BUFFER_HEADER_t *header) +{ + size_t ready_to_read = 0; @@ -131714,17 +135141,43 @@ index 000000000000..ef9926441981 + params->read_bytes = ready_to_read; + return 0; +} ++ ++long hailo_pcie_read_firmware_log(struct hailo_resource *resource, struct hailo_read_log_params *params) ++{ ++ long err = 0; ++ struct hailo_resource log_resource = {resource->address, DEBUG_BUFFER_TOTAL_SIZE}; ++ ++ if (HAILO_CPU_ID_CPU0 == params->cpu_id) { ++ log_resource.address += PCIE_APP_CPU_DEBUG_OFFSET; ++ } else if (HAILO_CPU_ID_CPU1 == params->cpu_id) { ++ log_resource.address += PCIE_CORE_CPU_DEBUG_OFFSET; ++ } else { ++ return -EINVAL; ++ } ++ ++ if (0 == params->buffer_size) { ++ params->read_bytes = 0; ++ return 0; ++ } ++ ++ err = hailo_read_firmware_log(&log_resource, params); ++ if (0 != err) { ++ return err; ++ } ++ ++ return 0; ++} \ No newline at end of file diff --git a/drivers/media/pci/hailo/common/fw_operation.h b/drivers/media/pci/hailo/common/fw_operation.h new file mode 100644 -index 000000000000..c7cf068bdb3f +index 000000000000..8c8185ce6ba8 --- /dev/null +++ b/drivers/media/pci/hailo/common/fw_operation.h -@@ -0,0 +1,25 @@ +@@ -0,0 +1,29 @@ +// SPDX-License-Identifier: MIT +/** -+ * Copyright (c) 2022 Hailo Technologies Ltd. All rights reserved. -+**/ ++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. ++ **/ + +#ifndef _HAILO_COMMON_FIRMWARE_OPERATION_H_ +#define _HAILO_COMMON_FIRMWARE_OPERATION_H_ @@ -131739,8 +135192,12 @@ index 000000000000..c7cf068bdb3f + +int hailo_read_firmware_notification(struct hailo_resource *resource, struct hailo_d2h_notification *notification); + ++int hailo_pcie_read_firmware_notification(struct hailo_resource *resource, struct hailo_d2h_notification *notification); ++ +long hailo_read_firmware_log(struct hailo_resource *fw_logger_resource, struct hailo_read_log_params *params); + ++long hailo_pcie_read_firmware_log(struct hailo_resource *resource, struct hailo_read_log_params *params); ++ +#ifdef __cplusplus +} +#endif @@ -131748,13 +135205,13 @@ index 000000000000..c7cf068bdb3f +#endif /* _HAILO_COMMON_FIRMWARE_OPERATION_H_ */ diff --git a/drivers/media/pci/hailo/common/fw_validation.c b/drivers/media/pci/hailo/common/fw_validation.c new file mode 100644 -index 000000000000..a197435c102d +index 000000000000..2eb59dfd7466 --- /dev/null +++ b/drivers/media/pci/hailo/common/fw_validation.c @@ -0,0 +1,114 @@ +// SPDX-License-Identifier: MIT +/** -+ * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved. ++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. + **/ + +#include "fw_validation.h" @@ -131795,8 +135252,8 @@ index 000000000000..a197435c102d + case HAILO_BOARD_TYPE_HAILO10H: + expected_firmware_magic = FIRMWARE_HEADER_MAGIC_HAILO15; + break; -+ case HAILO_BOARD_TYPE_PLUTO: -+ expected_firmware_magic = FIRMWARE_HEADER_MAGIC_PLUTO; ++ case HAILO_BOARD_TYPE_HAILO15L: ++ expected_firmware_magic = FIRMWARE_HEADER_MAGIC_HAILO15L; + break; + default: + err = -EINVAL; @@ -131839,15 +135296,15 @@ index 000000000000..a197435c102d +} + +int FW_VALIDATION__validate_cert_header(uintptr_t firmware_base_address, -+ size_t firmware_size, u32 *outer_consumed_firmware_offset, secure_boot_certificate_t **out_firmware_cert) ++ size_t firmware_size, u32 *outer_consumed_firmware_offset, secure_boot_certificate_header_t **out_firmware_cert) +{ + -+ secure_boot_certificate_t *firmware_cert = NULL; ++ secure_boot_certificate_header_t *firmware_cert = NULL; + int err = -EINVAL; + u32 consumed_firmware_offset = *outer_consumed_firmware_offset; + -+ firmware_cert = (secure_boot_certificate_t *) (firmware_base_address + consumed_firmware_offset); -+ CONSUME_FIRMWARE(sizeof(secure_boot_certificate_t), -EINVAL); ++ firmware_cert = (secure_boot_certificate_header_t *) (firmware_base_address + consumed_firmware_offset); ++ CONSUME_FIRMWARE(sizeof(secure_boot_certificate_header_t), -EINVAL); + + if ((MAXIMUM_FIRMWARE_CERT_KEY_SIZE < firmware_cert->key_size) || + (MAXIMUM_FIRMWARE_CERT_CONTENT_SIZE < firmware_cert->content_size)) { @@ -131868,13 +135325,13 @@ index 000000000000..a197435c102d + diff --git a/drivers/media/pci/hailo/common/fw_validation.h b/drivers/media/pci/hailo/common/fw_validation.h new file mode 100644 -index 000000000000..106ee5007297 +index 000000000000..b1f7f32bc708 --- /dev/null +++ b/drivers/media/pci/hailo/common/fw_validation.h -@@ -0,0 +1,65 @@ +@@ -0,0 +1,58 @@ +// SPDX-License-Identifier: MIT +/** -+ * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved. ++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. + **/ + +#ifndef PCIE_COMMON_FIRMWARE_HEADER_UTILS_H_ @@ -131883,15 +135340,9 @@ index 000000000000..106ee5007297 +#include "hailo_ioctl_common.h" +#include + -+#define FIRMWARE_HEADER_MAGIC_HAILO8 (0x1DD89DE0) -+#define FIRMWARE_HEADER_MAGIC_HAILO15 (0xE905DAAB) -+#define FIRMWARE_HEADER_MAGIC_PLUTO (0xF94739AB) -+ -+#ifndef HAILO_EMULATOR -+#define FIRMWARE_WAIT_TIMEOUT_MS (5000) -+#else /* ifndef HAILO_EMULATOR */ -+#define FIRMWARE_WAIT_TIMEOUT_MS (500000) -+#endif /* ifndef HAILO_EMULATOR */ ++#define FIRMWARE_HEADER_MAGIC_HAILO8 (0x1DD89DE0) ++#define FIRMWARE_HEADER_MAGIC_HAILO15 (0xE905DAAB) ++#define FIRMWARE_HEADER_MAGIC_HAILO15L (0xF94739AB) + +typedef enum { + FIRMWARE_HEADER_VERSION_INITIAL = 0, @@ -131918,8 +135369,7 @@ index 000000000000..106ee5007297 +typedef struct { + u32 key_size; + u32 content_size; -+ u8 certificates_data[0]; -+} secure_boot_certificate_t; ++} secure_boot_certificate_header_t; + +#ifdef _MSC_VER +#pragma warning(pop) @@ -131934,26 +135384,25 @@ index 000000000000..106ee5007297 + firmware_header_t **out_firmware_header, enum hailo_board_type board_type); + +int FW_VALIDATION__validate_cert_header(uintptr_t firmware_base_address, -+ size_t firmware_size, u32 *outer_consumed_firmware_offset, secure_boot_certificate_t **out_firmware_cert); ++ size_t firmware_size, u32 *outer_consumed_firmware_offset, secure_boot_certificate_header_t **out_firmware_cert); + +#endif -\ No newline at end of file diff --git a/drivers/media/pci/hailo/common/hailo_ioctl_common.h b/drivers/media/pci/hailo/common/hailo_ioctl_common.h new file mode 100644 -index 000000000000..5f6cddf5aa2e +index 000000000000..3333513ce246 --- /dev/null +++ b/drivers/media/pci/hailo/common/hailo_ioctl_common.h -@@ -0,0 +1,669 @@ +@@ -0,0 +1,685 @@ +// SPDX-License-Identifier: (GPL-2.0 WITH Linux-syscall-note) AND MIT +/** -+ * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved. ++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. + **/ + +#ifndef _HAILO_IOCTL_COMMON_H_ +#define _HAILO_IOCTL_COMMON_H_ + +#define HAILO_DRV_VER_MAJOR 4 -+#define HAILO_DRV_VER_MINOR 18 ++#define HAILO_DRV_VER_MINOR 20 +#define HAILO_DRV_VER_REVISION 0 + +#define _STRINGIFY_EXPANDED( x ) #x @@ -131963,10 +135412,12 @@ index 000000000000..5f6cddf5aa2e + +// This value is not easily changeable. +// For example: the channel interrupts ioctls assume we have up to 32 channels -+#define MAX_VDMA_CHANNELS_PER_ENGINE (32) -+#define MAX_VDMA_ENGINES (3) -+#define SIZE_OF_VDMA_DESCRIPTOR (16) -+#define VDMA_DEST_CHANNELS_START (16) ++#define MAX_VDMA_CHANNELS_PER_ENGINE (32) ++#define VDMA_CHANNELS_PER_ENGINE_PER_DIRECTION (16) ++#define MAX_VDMA_ENGINES (3) ++#define SIZE_OF_VDMA_DESCRIPTOR (16) ++#define VDMA_DEST_CHANNELS_START (16) ++#define MAX_SG_DESCS_COUNT (64 * 1024u) + +#define HAILO_VDMA_MAX_ONGOING_TRANSFERS (128) +#define HAILO_VDMA_MAX_ONGOING_TRANSFERS_MASK (HAILO_VDMA_MAX_ONGOING_TRANSFERS - 1) @@ -131983,8 +135434,12 @@ index 000000000000..5f6cddf5aa2e +#define FW_ACCESS_APP_CPU_CONTROL_MASK (1 << FW_ACCESS_CONTROL_INTERRUPT_SHIFT) +#define FW_ACCESS_DRIVER_SHUTDOWN_SHIFT (2) +#define FW_ACCESS_DRIVER_SHUTDOWN_MASK (1 << FW_ACCESS_DRIVER_SHUTDOWN_SHIFT) -+#define FW_ACCESS_SOC_CONNECT_SHIFT (3) -+#define FW_ACCESS_SOC_CONNECT_MASK (1 << FW_ACCESS_SOC_CONNECT_SHIFT) ++// HRT-15790 TODO: separate nnc interrupts and soc interrupts ++#define FW_ACCESS_SOFT_RESET_SHIFT (3) ++#define FW_ACCESS_SOFT_RESET_MASK (1 << FW_ACCESS_SOFT_RESET_SHIFT) ++ ++#define FW_ACCESS_SOC_CONTROL_SHIFT (3) ++#define FW_ACCESS_SOC_CONTROL_MASK (1 << FW_ACCESS_SOC_CONTROL_SHIFT) + +#define INVALID_VDMA_CHANNEL (0xff) + @@ -132129,7 +135584,6 @@ index 000000000000..5f6cddf5aa2e +}; + +// Enum that states what type of buffer we are working with in the driver -+// TODO: HRT-13580 - Add specific type for user allocated and for driver allocated +enum hailo_dma_buffer_type { + HAILO_DMA_USER_PTR_BUFFER = 0, + HAILO_DMA_DMABUF_BUFFER = 1, @@ -132191,6 +135645,12 @@ index 000000000000..5f6cddf5aa2e + uintptr_t desc_handle; // in +}; + ++struct hailo_write_action_list_params { ++ uint8_t *data; // in ++ size_t size; // in ++ uint64_t dma_address; // out ++}; ++ +/* structure used in ioctl HAILO_DESC_LIST_BIND_VDMA_BUFFER */ +struct hailo_desc_list_program_params { + size_t buffer_handle; // in @@ -132338,7 +135798,7 @@ index 000000000000..5f6cddf5aa2e +enum hailo_board_type { + HAILO_BOARD_TYPE_HAILO8 = 0, + HAILO_BOARD_TYPE_HAILO15, -+ HAILO_BOARD_TYPE_PLUTO, ++ HAILO_BOARD_TYPE_HAILO15L, + HAILO_BOARD_TYPE_HAILO10H, + HAILO_BOARD_TYPE_HAILO10H_LEGACY, + HAILO_BOARD_TYPE_COUNT, @@ -132454,6 +135914,7 @@ index 000000000000..5f6cddf5aa2e + +/* structure used in ioctl HAILO_SOC_CONNECT */ +struct hailo_soc_connect_params { ++ uint16_t port_number; // in + uint8_t input_channel_index; // out + uint8_t output_channel_index; // out + uintptr_t input_desc_handle; // in @@ -132468,6 +135929,7 @@ index 000000000000..5f6cddf5aa2e + +/* structure used in ioctl HAILO_PCI_EP_ACCEPT */ +struct hailo_pci_ep_accept_params { ++ uint16_t port_number; // in + uint8_t input_channel_index; // out + uint8_t output_channel_index; // out + uintptr_t input_desc_handle; // in @@ -132508,6 +135970,7 @@ index 000000000000..5f6cddf5aa2e + struct hailo_soc_close_params SocCloseParams; + struct hailo_pci_ep_accept_params AcceptParams; + struct hailo_pci_ep_close_params PciEpCloseParams; ++ struct hailo_write_action_list_params WriteActionListParams; + } Buffer; +}; +#endif // _MSC_VER @@ -132578,6 +136041,7 @@ index 000000000000..5f6cddf5aa2e + HAILO_DISABLE_NOTIFICATION_CODE, + HAILO_READ_LOG_CODE, + HAILO_RESET_NN_CORE_CODE, ++ HAILO_WRITE_ACTION_LIST_CODE, + + // Must be last + HAILO_NNC_IOCTL_MAX_NR @@ -132588,6 +136052,7 @@ index 000000000000..5f6cddf5aa2e +#define HAILO_DISABLE_NOTIFICATION _IO_(HAILO_NNC_IOCTL_MAGIC, HAILO_DISABLE_NOTIFICATION_CODE) +#define HAILO_READ_LOG _IOWR_(HAILO_NNC_IOCTL_MAGIC, HAILO_READ_LOG_CODE, struct hailo_read_log_params) +#define HAILO_RESET_NN_CORE _IO_(HAILO_NNC_IOCTL_MAGIC, HAILO_RESET_NN_CORE_CODE) ++#define HAILO_WRITE_ACTION_LIST _IOW_(HAILO_NNC_IOCTL_MAGIC, HAILO_WRITE_ACTION_LIST_CODE, struct hailo_write_action_list_params) + +enum hailo_soc_ioctl_code { + HAILO_SOC_IOCTL_CONNECT_CODE, @@ -132635,31 +136100,38 @@ index 000000000000..059e5d8a5c87 \ No newline at end of file diff --git a/drivers/media/pci/hailo/common/hailo_resource.c b/drivers/media/pci/hailo/common/hailo_resource.c new file mode 100644 -index 000000000000..05413e705d20 +index 000000000000..548deb2da262 --- /dev/null +++ b/drivers/media/pci/hailo/common/hailo_resource.c -@@ -0,0 +1,128 @@ +@@ -0,0 +1,141 @@ +// SPDX-License-Identifier: MIT +/** -+ * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved. ++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. + **/ + +#include "hailo_resource.h" + ++#include "utils.h" ++ +#include +#include +#include +#include + ++#define ALIGN_TO_32_BIT(addr) ((addr) & (~((uintptr_t)0x3))) + +u8 hailo_resource_read8(struct hailo_resource *resource, size_t offset) +{ -+ return ioread8((u8*)resource->address + offset); ++ u32 val = ioread32((u8*)ALIGN_TO_32_BIT(resource->address + offset)); ++ u64 offset_in_bits = BITS_IN_BYTE * ((resource->address + offset) - ALIGN_TO_32_BIT(resource->address + offset)); ++ return (u8)READ_BITS_AT_OFFSET(BYTE_SIZE * BITS_IN_BYTE, offset_in_bits, val); +} + +u16 hailo_resource_read16(struct hailo_resource *resource, size_t offset) +{ -+ return ioread16((u8*)resource->address + offset); ++ u32 val = ioread32((u8*)ALIGN_TO_32_BIT(resource->address + offset)); ++ u64 offset_in_bits = BITS_IN_BYTE * ((resource->address + offset) - ALIGN_TO_32_BIT(resource->address + offset)); ++ return (u16)READ_BITS_AT_OFFSET(WORD_SIZE * BITS_IN_BYTE, offset_in_bits, val); +} + +u32 hailo_resource_read32(struct hailo_resource *resource, size_t offset) @@ -132669,12 +136141,18 @@ index 000000000000..05413e705d20 + +void hailo_resource_write8(struct hailo_resource *resource, size_t offset, u8 value) +{ -+ iowrite8(value, (u8*)resource->address + offset); ++ u32 initial_val = ioread32((u8*)ALIGN_TO_32_BIT(resource->address + offset)); ++ u64 offset_in_bits = BITS_IN_BYTE * ((resource->address + offset) - ALIGN_TO_32_BIT(resource->address + offset)); ++ iowrite32(WRITE_BITS_AT_OFFSET(BYTE_SIZE * BITS_IN_BYTE, offset_in_bits, initial_val, value), ++ (u8*)ALIGN_TO_32_BIT(resource->address + offset)); +} + +void hailo_resource_write16(struct hailo_resource *resource, size_t offset, u16 value) +{ -+ iowrite16(value, (u8*)resource->address + offset); ++ u32 initial_val = ioread32((u8*)ALIGN_TO_32_BIT(resource->address + offset)); ++ u64 offset_in_bits = BITS_IN_BYTE * ((resource->address + offset) - ALIGN_TO_32_BIT(resource->address + offset)); ++ iowrite32(WRITE_BITS_AT_OFFSET(WORD_SIZE * BITS_IN_BYTE, offset_in_bits, initial_val, value), ++ (u8*)ALIGN_TO_32_BIT(resource->address + offset)); +} + +void hailo_resource_write32(struct hailo_resource *resource, size_t offset, u32 value) @@ -132769,13 +136247,13 @@ index 000000000000..05413e705d20 +} diff --git a/drivers/media/pci/hailo/common/hailo_resource.h b/drivers/media/pci/hailo/common/hailo_resource.h new file mode 100644 -index 000000000000..891a211fde33 +index 000000000000..c27a09756876 --- /dev/null +++ b/drivers/media/pci/hailo/common/hailo_resource.h @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: MIT +/** -+ * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved. ++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. + **/ + +#ifndef _HAILO_COMMON_HAILO_RESOURCE_H_ @@ -132815,17 +136293,18 @@ index 000000000000..891a211fde33 \ No newline at end of file diff --git a/drivers/media/pci/hailo/common/pcie_common.c b/drivers/media/pci/hailo/common/pcie_common.c new file mode 100644 -index 000000000000..a85787c03c9d +index 000000000000..a119d637cf4d --- /dev/null +++ b/drivers/media/pci/hailo/common/pcie_common.c -@@ -0,0 +1,872 @@ +@@ -0,0 +1,913 @@ +// SPDX-License-Identifier: MIT +/** -+ * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved. ++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. + **/ + +#include "pcie_common.h" +#include "fw_operation.h" ++#include "soc_structs.h" + +#include +#include @@ -132850,54 +136329,48 @@ index 000000000000..a85787c03c9d + +#define ATR0_PCIE_BRIDGE_OFFSET (0x700) + ++#define ATR_PCIE_BRIDGE_OFFSET(atr_index) (ATR0_PCIE_BRIDGE_OFFSET + (atr_index * 0x20)) ++ +#define MAXIMUM_APP_FIRMWARE_CODE_SIZE (0x40000) +#define MAXIMUM_CORE_FIRMWARE_CODE_SIZE (0x20000) + +#define FIRMWARE_LOAD_WAIT_MAX_RETRIES (100) +#define FIRMWARE_LOAD_SLEEP_MS (50) + -+#define PCIE_APP_CPU_DEBUG_OFFSET (8*1024) -+#define PCIE_CORE_CPU_DEBUG_OFFSET (PCIE_APP_CPU_DEBUG_OFFSET + DEBUG_BUFFER_TOTAL_SIZE) -+ -+#define PCIE_D2H_NOTIFICATION_SRAM_OFFSET (0x640 + 0x640) +#define PCIE_REQUEST_SIZE_OFFSET (0x640) + +#define PCIE_CONFIG_VENDOR_OFFSET (0x0098) + -+#define HAILO_PCIE_HOST_DMA_DATA_ID (0) +#define HAILO_PCIE_DMA_DEVICE_INTERRUPTS_BITMASK (1 << 4) +#define HAILO_PCIE_DMA_HOST_INTERRUPTS_BITMASK (1 << 5) +#define HAILO_PCIE_DMA_SRC_CHANNELS_BITMASK (0x0000FFFF) + +#define HAILO_PCIE_MAX_ATR_TABLE_INDEX (3) + -+#define MAX_FILES_PER_STAGE (4) -+ +#define BOOT_STATUS_UNINITIALIZED (0x1) + ++#define PCIE_CONTROL_SECTION_ADDRESS_H8 (0x60000000) ++#define PCIE_BLOCK_ADDRESS_ATR1 (0x200000) ++ ++#define PCIE_CONFIG_PCIE_CFG_QM_ROUTING_MODE_SET(reg_offset) \ ++ (reg_offset) = (((reg_offset) & ~0x00000004L) | ((uint32_t)(1) << 2)) ++ ++ +struct hailo_fw_addresses { + u32 boot_fw_header; + u32 app_fw_code_ram_base; + u32 boot_key_cert; + u32 boot_cont_cert; -+ u32 boot_fw_trigger; + u32 core_code_ram_base; + u32 core_fw_header; -+ u32 atr0_trsl_addr1; + u32 raise_ready_offset; + u32 boot_status; -+}; -+ -+struct loading_stage { -+ const struct hailo_file_batch *batch; ++ u32 pcie_cfg_regs; +}; + +struct hailo_board_compatibility { + struct hailo_fw_addresses fw_addresses; -+ const char *fw_filename; -+ const struct hailo_config_constants board_cfg; -+ const struct hailo_config_constants fw_cfg; -+ const struct loading_stage stages[MAX_LOADING_STAGES]; ++ const struct hailo_pcie_loading_stage stages[MAX_LOADING_STAGES]; +}; + +static const struct hailo_file_batch hailo10h_files_stg1[] = { @@ -132906,28 +136379,32 @@ index 000000000000..a85787c03c9d + .address = 0xA0000, + .max_size = 0x8004, + .is_mandatory = true, -+ .has_header = false ++ .has_header = false, ++ .has_core = false + }, + { + .filename = "hailo/hailo10h/u-boot.dtb.signed", + .address = 0xA8004, + .max_size = 0x20000, + .is_mandatory = true, -+ .has_header = false ++ .has_header = false, ++ .has_core = false + }, + { + .filename = "hailo/hailo10h/scu_fw.bin", + .address = 0x20000, + .max_size = 0x40000, + .is_mandatory = true, -+ .has_header = true ++ .has_header = true, ++ .has_core = false + }, + { + .filename = NULL, + .address = 0x00, + .max_size = 0x00, + .is_mandatory = false, -+ .has_header = false ++ .has_header = false, ++ .has_core = false + } +}; + @@ -132937,169 +136414,274 @@ index 000000000000..a85787c03c9d + .address = 0x85000000, + .max_size = 0x1000000, + .is_mandatory = true, -+ .has_header = false ++ .has_header = false, ++ .has_core = false + }, + { + .filename = "hailo/hailo10h/u-boot-tfa.itb", + .address = 0x86000000, + .max_size = 0x1000000, + .is_mandatory = true, -+ .has_header = false ++ .has_header = false, ++ .has_core = false + }, + { + .filename = "hailo/hailo10h/fitImage", + .address = 0x87000000, + .max_size = 0x1000000, + .is_mandatory = true, -+ .has_header = false ++ .has_header = false, ++ .has_core = false + }, + { -+ .filename = "hailo/hailo10h/core-image-minimal-hailo10-m2.ext4.gz", ++ .filename = "hailo/hailo10h/image-fs", ++#ifndef HAILO_EMULATOR + .address = 0x88000000, ++#else ++ // TODO : HRT-15692 - merge two cases ++ .address = 0x89000000, ++#endif /* ifndef HAILO_EMULATOR */ + .max_size = 0x20000000, // Max size 512MB + .is_mandatory = true, -+ .has_header = false ++ .has_header = false, ++ .has_core = false ++ } ++}; ++ ++// If loading linux from EMMC - only need few files from second batch (u-boot-spl.bin and u-boot-tfa.itb) ++static const struct hailo_file_batch hailo10h_files_stg2_linux_in_emmc[] = { ++ { ++ .filename = "hailo/hailo10h/u-boot-spl.bin", ++ .address = 0x85000000, ++ .max_size = 0x1000000, ++ .is_mandatory = true, ++ .has_header = false, ++ .has_core = false ++ }, ++ { ++ .filename = "hailo/hailo10h/u-boot-tfa.itb", ++ .address = 0x86000000, ++ .max_size = 0x1000000, ++ .is_mandatory = true, ++ .has_header = false, ++ .has_core = false ++ }, ++ { ++ .filename = NULL, ++ .address = 0x00, ++ .max_size = 0x00, ++ .is_mandatory = false, ++ .has_header = false, ++ .has_core = false ++ }, ++}; ++ ++static const struct hailo_file_batch hailo8_files_stg1[] = { ++ { ++ .filename = "hailo/hailo8_fw.bin", ++ .address = 0x20000, ++ .max_size = 0x50000, ++ .is_mandatory = true, ++ .has_header = true, ++ .has_core = true ++ }, ++ { ++ .filename = "hailo/hailo8_board_cfg.bin", ++ .address = 0x60001000, ++ .max_size = PCIE_HAILO8_BOARD_CFG_MAX_SIZE, ++ .is_mandatory = false, ++ .has_header = false, ++ .has_core = false ++ }, ++ { ++ .filename = "hailo/hailo8_fw_cfg.bin", ++ .address = 0x60001500, ++ .max_size = PCIE_HAILO8_FW_CFG_MAX_SIZE, ++ .is_mandatory = false, ++ .has_header = false, ++ .has_core = false ++ }, ++ { ++ .filename = NULL, ++ .address = 0x00, ++ .max_size = 0x00, ++ .is_mandatory = false, ++ .has_header = false, ++ .has_core = false ++ } ++}; ++ ++static const struct hailo_file_batch hailo10h_legacy_files_stg1[] = { ++ { ++ .filename = "hailo/hailo15_fw.bin", ++ .address = 0x20000, ++ .max_size = 0x100000, ++ .is_mandatory = true, ++ .has_header = true, ++ .has_core = true + }, ++ { ++ .filename = NULL, ++ .address = 0x00, ++ .max_size = 0x00, ++ .is_mandatory = false, ++ .has_header = false, ++ .has_core = false ++ } ++}; ++ ++// TODO HRT-15014 - Fix names for hailo15l legacy accelerator ++static const struct hailo_file_batch hailo15l_files_stg1[] = { ++ { ++ .filename = "hailo/hailo15l_fw.bin", ++ .address = 0x20000, ++ .max_size = 0x100000, ++ .is_mandatory = true, ++ .has_header = true, ++ .has_core = true ++ }, ++ { ++ .filename = NULL, ++ .address = 0x00, ++ .max_size = 0x00, ++ .is_mandatory = false, ++ .has_header = false, ++ .has_core = false ++ } +}; + +static const struct hailo_board_compatibility compat[HAILO_BOARD_TYPE_COUNT] = { + [HAILO_BOARD_TYPE_HAILO8] = { + .fw_addresses = { + .boot_fw_header = 0xE0030, -+ .boot_fw_trigger = 0xE0980, + .boot_key_cert = 0xE0048, + .boot_cont_cert = 0xE0390, + .app_fw_code_ram_base = 0x60000, + .core_code_ram_base = 0xC0000, + .core_fw_header = 0xA0000, -+ .atr0_trsl_addr1 = 0x60000000, + .raise_ready_offset = 0x1684, + .boot_status = 0xe0000, + }, -+ .fw_filename = "hailo/hailo8_fw.bin", -+ .board_cfg = { -+ .filename = "hailo/hailo8_board_cfg.bin", -+ .address = 0x60001000, -+ .max_size = PCIE_HAILO8_BOARD_CFG_MAX_SIZE, -+ }, -+ .fw_cfg = { -+ .filename = "hailo/hailo8_fw_cfg.bin", -+ .address = 0x60001500, -+ .max_size = PCIE_HAILO8_FW_CFG_MAX_SIZE, ++ .stages = { ++ { ++ .batch = hailo8_files_stg1, ++ .trigger_address = 0xE0980, ++ .timeout = FIRMWARE_WAIT_TIMEOUT_MS, ++ .amount_of_files_in_stage = 3 ++ }, + }, + }, + [HAILO_BOARD_TYPE_HAILO10H_LEGACY] = { + .fw_addresses = { + .boot_fw_header = 0x88000, -+ .boot_fw_trigger = 0x88c98, + .boot_key_cert = 0x88018, + .boot_cont_cert = 0x886a8, + .app_fw_code_ram_base = 0x20000, + .core_code_ram_base = 0x60000, + .core_fw_header = 0xC0000, -+ .atr0_trsl_addr1 = 0x000BE000, + .raise_ready_offset = 0x1754, + .boot_status = 0x80000, + }, -+ .fw_filename = "hailo/hailo15_fw.bin", -+ .board_cfg = { -+ .filename = NULL, -+ .address = 0, -+ .max_size = 0, -+ }, -+ .fw_cfg = { -+ .filename = NULL, -+ .address = 0, -+ .max_size = 0, ++ .stages = { ++ { ++ .batch = hailo10h_legacy_files_stg1, ++ .trigger_address = 0x88c98, ++ .timeout = FIRMWARE_WAIT_TIMEOUT_MS, ++ .amount_of_files_in_stage = 1 ++ }, + }, + }, + [HAILO_BOARD_TYPE_HAILO10H] = { + .fw_addresses = { + .boot_fw_header = 0x88000, -+ .boot_fw_trigger = 0x88c98, + .boot_key_cert = 0x88018, + .boot_cont_cert = 0x886a8, + .app_fw_code_ram_base = 0x20000, + .core_code_ram_base = 0, + .core_fw_header = 0, -+ .atr0_trsl_addr1 = 0x000BE000, + .raise_ready_offset = 0x1754, + .boot_status = 0x80000, -+ }, -+ .fw_filename = NULL, -+ .board_cfg = { -+ .filename = NULL, -+ .address = 0, -+ .max_size = 0, -+ }, -+ .fw_cfg = { -+ .filename = NULL, -+ .address = 0, -+ .max_size = 0, ++ .pcie_cfg_regs = 0x002009dc, + }, + .stages = { + { + .batch = hailo10h_files_stg1, ++ .trigger_address = 0x88c98, ++ .timeout = FIRMWARE_WAIT_TIMEOUT_MS, ++ .amount_of_files_in_stage = 3 + }, + { + .batch = hailo10h_files_stg2, ++ .trigger_address = 0x84000000, ++ .timeout = PCI_EP_WAIT_TIMEOUT_MS, ++ .amount_of_files_in_stage = 4 ++ }, ++ { ++ .batch = hailo10h_files_stg2_linux_in_emmc, ++ .trigger_address = 0x84000000, ++ .timeout = FIRMWARE_WAIT_TIMEOUT_MS, ++ .amount_of_files_in_stage = 2 + }, + }, + }, + // HRT-11344 : none of these matter except raise_ready_offset seeing as we load fw seperately - not through driver + // After implementing bootloader put correct values here -+ [HAILO_BOARD_TYPE_PLUTO] = { ++ [HAILO_BOARD_TYPE_HAILO15L] = { + .fw_addresses = { + .boot_fw_header = 0x88000, -+ .boot_fw_trigger = 0x88c98, + .boot_key_cert = 0x88018, + .boot_cont_cert = 0x886a8, + .app_fw_code_ram_base = 0x20000, + .core_code_ram_base = 0x60000, + .core_fw_header = 0xC0000, -+ .atr0_trsl_addr1 = 0x000BE000, + // NOTE: After they update hw consts - check register fw_access_interrupt_w1s of pcie_config + .raise_ready_offset = 0x174c, + .boot_status = 0x80000, + }, -+ .fw_filename = "hailo/pluto_fw.bin", -+ .board_cfg = { -+ .filename = NULL, -+ .address = 0, -+ .max_size = 0, -+ }, -+ .fw_cfg = { -+ .filename = NULL, -+ .address = 0, -+ .max_size = 0, ++ .stages = { ++ { ++ .batch = hailo15l_files_stg1, ++ .trigger_address = 0x88c98, ++ .timeout = FIRMWARE_WAIT_TIMEOUT_MS, ++ .amount_of_files_in_stage = 1 ++ }, + }, + } +}; + ++const struct hailo_pcie_loading_stage *hailo_pcie_get_loading_stage_info(enum hailo_board_type board_type, ++ enum loading_stages stage) ++{ ++ return &compat[board_type].stages[stage]; ++} ++ ++static u32 read_and_clear_reg(struct hailo_resource *resource, u32 offset) ++{ ++ u32 value = hailo_resource_read32(resource, offset); ++ if (value != 0) { ++ hailo_resource_write32(resource, offset, value); ++ } ++ return value; ++} + +bool hailo_pcie_read_interrupt(struct hailo_pcie_resources *resources, struct hailo_pcie_interrupt_source *source) +{ -+ u32 channel_data_source = 0; -+ u32 channel_data_dest = 0; ++ u32 istatus_host = 0; + memset(source, 0, sizeof(*source)); + -+ source->interrupt_bitmask = hailo_resource_read32(&resources->config, BCS_ISTATUS_HOST); -+ if (0 == source->interrupt_bitmask) { ++ istatus_host = read_and_clear_reg(&resources->config, BCS_ISTATUS_HOST); ++ if (0 == istatus_host) { + return false; + } + -+ // clear signal -+ hailo_resource_write32(&resources->config, BCS_ISTATUS_HOST, source->interrupt_bitmask); ++ source->sw_interrupts = (istatus_host >> BCS_ISTATUS_HOST_SW_IRQ_SHIFT); + -+ if (source->interrupt_bitmask & BCS_ISTATUS_HOST_VDMA_SRC_IRQ_MASK) { -+ channel_data_source = hailo_resource_read32(&resources->config, BCS_SOURCE_INTERRUPT_PER_CHANNEL); -+ hailo_resource_write32(&resources->config, BCS_SOURCE_INTERRUPT_PER_CHANNEL, channel_data_source); ++ if (istatus_host & BCS_ISTATUS_HOST_VDMA_SRC_IRQ_MASK) { ++ source->vdma_channels_bitmap |= read_and_clear_reg(&resources->config, BCS_SOURCE_INTERRUPT_PER_CHANNEL); + } -+ if (source->interrupt_bitmask & BCS_ISTATUS_HOST_VDMA_DEST_IRQ_MASK) { -+ channel_data_dest = hailo_resource_read32(&resources->config, BCS_DESTINATION_INTERRUPT_PER_CHANNEL); -+ hailo_resource_write32(&resources->config, BCS_DESTINATION_INTERRUPT_PER_CHANNEL, channel_data_dest); ++ if (istatus_host & BCS_ISTATUS_HOST_VDMA_DEST_IRQ_MASK) { ++ source->vdma_channels_bitmap |= read_and_clear_reg(&resources->config, BCS_DESTINATION_INTERRUPT_PER_CHANNEL); + } -+ source->vdma_channels_bitmap = channel_data_source | channel_data_dest; + + return true; +} @@ -133161,19 +136743,13 @@ index 000000000000..a85787c03c9d + hailo_resource_write32(&resources->fw_access, fw_addresses->raise_ready_offset, fw_access_value); +} + -+int hailo_pcie_read_firmware_notification(struct hailo_pcie_resources *resources, -+ struct hailo_d2h_notification *notification) ++void hailo_pcie_write_firmware_soft_reset(struct hailo_pcie_resources *resources) +{ -+ struct hailo_resource notification_resource; -+ -+ if (PCIE_D2H_NOTIFICATION_SRAM_OFFSET > resources->fw_access.size) { -+ return -EINVAL; -+ } -+ -+ notification_resource.address = resources->fw_access.address + PCIE_D2H_NOTIFICATION_SRAM_OFFSET, -+ notification_resource.size = sizeof(struct hailo_d2h_notification); ++ const struct hailo_fw_addresses *fw_addresses = &(compat[resources->board_type].fw_addresses); ++ const u32 fw_access_value = FW_ACCESS_SOFT_RESET_MASK; + -+ return hailo_read_firmware_notification(¬ification_resource, notification); ++ // Write shutdown flag to FW ++ hailo_resource_write32(&resources->fw_access, fw_addresses->raise_ready_offset, fw_access_value); +} + +int hailo_pcie_configure_atr_table(struct hailo_resource *bridge_config, u64 trsl_addr, u32 atr_index) @@ -133188,7 +136764,7 @@ index 000000000000..a85787c03c9d + }; + + BUG_ON(HAILO_PCIE_MAX_ATR_TABLE_INDEX < atr_index); -+ offset = ATR0_PCIE_BRIDGE_OFFSET + (atr_index * 0x20); ++ offset = ATR_PCIE_BRIDGE_OFFSET(atr_index); + + return hailo_resource_write_buffer(bridge_config, offset, sizeof(atr), (void*)&atr); +} @@ -133198,7 +136774,7 @@ index 000000000000..a85787c03c9d + size_t offset = 0; + + BUG_ON(HAILO_PCIE_MAX_ATR_TABLE_INDEX < atr_index); -+ offset = ATR0_PCIE_BRIDGE_OFFSET + (atr_index * 0x20); ++ offset = ATR_PCIE_BRIDGE_OFFSET(atr_index); + + hailo_resource_read_buffer(bridge_config, offset, sizeof(*atr), (void*)atr); +} @@ -133209,7 +136785,7 @@ index 000000000000..a85787c03c9d + u32 ATR_INDEX = 0; + BUG_ON(dest_offset + len > (u32)resources->fw_access.size); + -+ (void)hailo_pcie_configure_atr_table(&resources->config, (u64)dest, ATR_INDEX); ++ (void)hailo_pcie_configure_atr_table(&resources->config, dest, ATR_INDEX); + (void)hailo_resource_write_buffer(&resources->fw_access, dest_offset, len, src); +} + @@ -133219,13 +136795,13 @@ index 000000000000..a85787c03c9d + u32 ATR_INDEX = 0; + BUG_ON(src_offset + len > (u32)resources->fw_access.size); + -+ (void)hailo_pcie_configure_atr_table(&resources->config, (u64)src, ATR_INDEX); ++ (void)hailo_pcie_configure_atr_table(&resources->config, src, ATR_INDEX); + (void)hailo_resource_read_buffer(&resources->fw_access, src_offset, len, dest); +} + +// Note: this function modify the device ATR table (that is also used by the firmware for control and vdma). +// Use with caution, and restore the original atr if needed. -+void write_memory(struct hailo_pcie_resources *resources, hailo_ptr_t dest, const void *src, u32 len) ++static void write_memory(struct hailo_pcie_resources *resources, hailo_ptr_t dest, const void *src, u32 len) +{ + struct hailo_atr_config previous_atr = {0}; + hailo_ptr_t base_address = (dest & ~ATR_TABLE_SIZE_MASK); @@ -133238,8 +136814,8 @@ index 000000000000..a85787c03c9d + + if (base_address != dest) { + // Data is not aligned, write the first chunk -+ chunk_len = min(base_address + ATR_TABLE_SIZE - dest, len); -+ write_memory_chunk(resources, base_address, dest - base_address, src, chunk_len); ++ chunk_len = min((u32)(base_address + ATR_TABLE_SIZE - dest), len); ++ write_memory_chunk(resources, base_address, (u32)(dest - base_address), src, chunk_len); + offset += chunk_len; + } + @@ -133267,9 +136843,9 @@ index 000000000000..a85787c03c9d + hailo_pcie_read_atr_table(&resources->config, &previous_atr, ATR_INDEX); + + if (base_address != src) { -+ // Data is not aligned, write the first chunk -+ chunk_len = min(base_address + ATR_TABLE_SIZE - src, len); -+ read_memory_chunk(resources, base_address, src - base_address, dest, chunk_len); ++ // Data is not aligned, read the first chunk ++ chunk_len = min((u32)(base_address + ATR_TABLE_SIZE - src), len); ++ read_memory_chunk(resources, base_address, (u32)(src - base_address), dest, chunk_len); + offset += chunk_len; + } + @@ -133283,13 +136859,25 @@ index 000000000000..a85787c03c9d + (((u64)(previous_atr.atr_trsl_addr_2) << 32) | previous_atr.atr_trsl_addr_1), ATR_INDEX); +} + ++// Note: This function use for enabling the vDMA transaction host<->device by read modify write of the EP registers in the SOC - for fast boot over vDMA. ++void hailo_pcie_configure_ep_registers_for_dma_transaction(struct hailo_pcie_resources *resources) ++{ ++ u32 reg_routing_mercury = 0; ++ ++ BUG_ON(compat[resources->board_type].fw_addresses.pcie_cfg_regs == 0); ++ ++ read_memory(resources, compat[resources->board_type].fw_addresses.pcie_cfg_regs, ®_routing_mercury, sizeof(reg_routing_mercury)); ++ PCIE_CONFIG_PCIE_CFG_QM_ROUTING_MODE_SET(reg_routing_mercury); ++ write_memory(resources, compat[resources->board_type].fw_addresses.pcie_cfg_regs, ®_routing_mercury, sizeof(reg_routing_mercury)); ++} ++ +static void hailo_write_app_firmware(struct hailo_pcie_resources *resources, firmware_header_t *fw_header, -+ secure_boot_certificate_t *fw_cert) ++ secure_boot_certificate_header_t *fw_cert) +{ + const struct hailo_fw_addresses *fw_addresses = &(compat[resources->board_type].fw_addresses); -+ void *fw_code = (void*)((u8*)fw_header + sizeof(firmware_header_t)); -+ void *key_data = &fw_cert->certificates_data[0]; -+ void *content_data = &fw_cert->certificates_data[fw_cert->key_size]; ++ u8 *fw_code = ((u8*)fw_header + sizeof(firmware_header_t)); ++ u8 *key_data = ((u8*)fw_cert + sizeof(secure_boot_certificate_header_t)); ++ u8 *content_data = key_data + fw_cert->key_size; + + write_memory(resources, fw_addresses->boot_fw_header, fw_header, sizeof(firmware_header_t)); + @@ -133308,13 +136896,11 @@ index 000000000000..a85787c03c9d + write_memory(resources, fw_addresses->core_fw_header, fw_header, sizeof(firmware_header_t)); +} + -+void hailo_trigger_firmware_boot(struct hailo_pcie_resources *resources) ++void hailo_trigger_firmware_boot(struct hailo_pcie_resources *resources, u32 stage) +{ -+ const struct hailo_fw_addresses *fw_addresses = &(compat[resources->board_type].fw_addresses); + u32 pcie_finished = 1; + -+ write_memory(resources, fw_addresses->boot_fw_trigger, -+ (void*)&pcie_finished, sizeof(pcie_finished)); ++ write_memory(resources, compat[resources->board_type].stages[stage].trigger_address, (void*)&pcie_finished, sizeof(pcie_finished)); +} + +u32 hailo_get_boot_status(struct hailo_pcie_resources *resources) @@ -133322,8 +136908,7 @@ index 000000000000..a85787c03c9d + u32 boot_status = 0; + const struct hailo_fw_addresses *fw_addresses = &(compat[resources->board_type].fw_addresses); + -+ read_memory(resources, fw_addresses->boot_status, -+ &boot_status, sizeof(boot_status)); ++ read_memory(resources, fw_addresses->boot_status, &boot_status, sizeof(boot_status)); + + return boot_status; +} @@ -133338,11 +136923,11 @@ index 000000000000..a85787c03c9d +*/ +static int FW_VALIDATION__validate_fw_headers(uintptr_t firmware_base_address, size_t firmware_size, + firmware_header_t **out_app_firmware_header, firmware_header_t **out_core_firmware_header, -+ secure_boot_certificate_t **out_firmware_cert, enum hailo_board_type board_type) ++ secure_boot_certificate_header_t **out_firmware_cert, enum hailo_board_type board_type) +{ + firmware_header_t *app_firmware_header = NULL; + firmware_header_t *core_firmware_header = NULL; -+ secure_boot_certificate_t *firmware_cert = NULL; ++ secure_boot_certificate_header_t *firmware_cert = NULL; + int err = -EINVAL; + u32 consumed_firmware_offset = 0; + @@ -133392,25 +136977,25 @@ index 000000000000..a85787c03c9d + return err; +} + -+static int write_single_file(struct hailo_pcie_resources *resources, const struct hailo_file_batch *files_batch, struct device *dev) ++static int write_single_file(struct hailo_pcie_resources *resources, const struct hailo_file_batch *file_info, struct device *dev) +{ + const struct firmware *firmware = NULL; + firmware_header_t *app_firmware_header = NULL; -+ secure_boot_certificate_t *firmware_cert = NULL; ++ secure_boot_certificate_header_t *firmware_cert = NULL; + firmware_header_t *core_firmware_header = NULL; + int err = 0; + -+ err = request_firmware_direct(&firmware, files_batch->filename, dev); ++ err = request_firmware_direct(&firmware, file_info->filename, dev); + if (err < 0) { + return err; + } + -+ if (firmware->size > files_batch->max_size) { ++ if (firmware->size > file_info->max_size) { + release_firmware(firmware); + return -EFBIG; + } + -+ if (files_batch->has_header) { ++ if (file_info->has_header) { + err = FW_VALIDATION__validate_fw_headers((uintptr_t)firmware->data, firmware->size, + &app_firmware_header, &core_firmware_header, &firmware_cert, resources->board_type); + if (err < 0) { @@ -133419,8 +137004,11 @@ index 000000000000..a85787c03c9d + } + + hailo_write_app_firmware(resources, app_firmware_header, firmware_cert); ++ if (file_info->has_core) { ++ hailo_write_core_firmware(resources, core_firmware_header); ++ } + } else { -+ write_memory(resources, files_batch->address, (void*)firmware->data, firmware->size); ++ write_memory(resources, file_info->address, (void*)firmware->data, firmware->size); + } + + release_firmware(firmware); @@ -133430,16 +137018,14 @@ index 000000000000..a85787c03c9d + +int hailo_pcie_write_firmware_batch(struct device *dev, struct hailo_pcie_resources *resources, u32 stage) +{ -+ const struct hailo_file_batch *files_batch = compat[resources->board_type].stages[stage].batch; ++ const struct hailo_pcie_loading_stage *stage_info = hailo_pcie_get_loading_stage_info(resources->board_type, stage); ++ const struct hailo_file_batch *files_batch = stage_info->batch; ++ const u8 amount_of_files = stage_info->amount_of_files_in_stage; + int file_index = 0; + int err = 0; + -+ for (file_index = 0; file_index < MAX_FILES_PER_STAGE; file_index++) ++ for (file_index = 0; file_index < amount_of_files; file_index++) + { -+ if (NULL == files_batch[file_index].filename) { -+ break; -+ } -+ + dev_notice(dev, "Writing file %s\n", files_batch[file_index].filename); + + err = write_single_file(resources, &files_batch[file_index], dev); @@ -133453,49 +137039,29 @@ index 000000000000..a85787c03c9d + dev_notice(dev, "File %s written successfully\n", files_batch[file_index].filename); + } + -+ return 0; -+} -+ -+int hailo_pcie_write_firmware(struct hailo_pcie_resources *resources, const void *fw_data, size_t fw_size) -+{ -+ firmware_header_t *app_firmware_header = NULL; -+ secure_boot_certificate_t *firmware_cert = NULL; -+ firmware_header_t *core_firmware_header = NULL; -+ -+ int err = FW_VALIDATION__validate_fw_headers((uintptr_t)fw_data, fw_size, -+ &app_firmware_header, &core_firmware_header, &firmware_cert, resources->board_type); -+ if (err < 0) { -+ return err; -+ } -+ -+ hailo_write_app_firmware(resources, app_firmware_header, firmware_cert); -+ hailo_write_core_firmware(resources, core_firmware_header); -+ -+ hailo_trigger_firmware_boot(resources); ++ hailo_trigger_firmware_boot(resources, stage); + + return 0; +} + -+// TODO: HRT-14147 - remove this function -+bool hailo_pcie_is_device_ready_for_boot(struct hailo_pcie_resources *resources) -+{ -+ return hailo_get_boot_status(resources) == BOOT_STATUS_UNINITIALIZED; -+} -+ +bool hailo_pcie_is_firmware_loaded(struct hailo_pcie_resources *resources) +{ + u32 offset; + u32 atr_value; + -+ // TODO: HRT-14147 -+ if (HAILO_BOARD_TYPE_HAILO10H == resources->board_type) { -+ return !hailo_pcie_is_device_ready_for_boot(resources); ++ if (HAILO_BOARD_TYPE_HAILO8 == resources->board_type) { ++ offset = ATR_PCIE_BRIDGE_OFFSET(0) + offsetof(struct hailo_atr_config, atr_trsl_addr_1); ++ atr_value = hailo_resource_read32(&resources->config, offset); ++ ++ return (PCIE_CONTROL_SECTION_ADDRESS_H8 == atr_value); + } ++ else { ++ offset = ATR_PCIE_BRIDGE_OFFSET(1) + offsetof(struct hailo_atr_config, atr_trsl_addr_1); ++ atr_value = hailo_resource_read32(&resources->config, offset); + -+ offset = ATR0_PCIE_BRIDGE_OFFSET + offsetof(struct hailo_atr_config, atr_trsl_addr_1); -+ atr_value = hailo_resource_read32(&resources->config, offset); ++ return (PCIE_BLOCK_ADDRESS_ATR1 == atr_value); ++ } + -+ return atr_value == compat[resources->board_type].fw_addresses.atr0_trsl_addr1; +} + +bool hailo_pcie_wait_for_firmware(struct hailo_pcie_resources *resources) @@ -133512,32 +137078,6 @@ index 000000000000..a85787c03c9d + return false; +} + -+int hailo_pcie_write_config_common(struct hailo_pcie_resources *resources, const void* config_data, -+ const size_t config_size, const struct hailo_config_constants *config_consts) -+{ -+ if (config_size > config_consts->max_size) { -+ return -EINVAL; -+ } -+ -+ write_memory(resources, config_consts->address, config_data, (u32)config_size); -+ return 0; -+} -+ -+const struct hailo_config_constants* hailo_pcie_get_board_config_constants(const enum hailo_board_type board_type) { -+ BUG_ON(board_type >= HAILO_BOARD_TYPE_COUNT || board_type < 0); -+ return &compat[board_type].board_cfg; -+} -+ -+const struct hailo_config_constants* hailo_pcie_get_user_config_constants(const enum hailo_board_type board_type) { -+ BUG_ON(board_type >= HAILO_BOARD_TYPE_COUNT || board_type < 0); -+ return &compat[board_type].fw_cfg; -+} -+ -+const char* hailo_pcie_get_fw_filename(const enum hailo_board_type board_type) { -+ BUG_ON(board_type >= HAILO_BOARD_TYPE_COUNT || board_type < 0); -+ return compat[board_type].fw_filename; -+} -+ +void hailo_pcie_update_channel_interrupts_mask(struct hailo_pcie_resources* resources, u32 channels_bitmap) +{ + size_t i = 0; @@ -133565,8 +137105,7 @@ index 000000000000..a85787c03c9d + hailo_resource_write32(&resources->config, BCS_DESTINATION_INTERRUPT_PER_CHANNEL, 0xFFFFFFFF); + hailo_resource_write32(&resources->config, BCS_SOURCE_INTERRUPT_PER_CHANNEL, 0xFFFFFFFF); + -+ mask |= (BCS_ISTATUS_HOST_FW_IRQ_CONTROL_MASK | BCS_ISTATUS_HOST_FW_IRQ_NOTIFICATION | -+ BCS_ISTATUS_HOST_DRIVER_DOWN | BCS_ISTATUS_SOC_CONNECT_ACCEPTED); ++ mask |= BCS_ISTATUS_HOST_SW_IRQ_MASK; + hailo_resource_write32(&resources->config, BSC_IMASK_HOST, mask); +} + @@ -133575,45 +137114,15 @@ index 000000000000..a85787c03c9d + hailo_resource_write32(&resources->config, BSC_IMASK_HOST, 0); +} + -+long hailo_pcie_read_firmware_log(struct hailo_pcie_resources *resources, struct hailo_read_log_params *params) -+{ -+ long err = 0; -+ struct hailo_resource log_resource = {resources->fw_access.address, DEBUG_BUFFER_TOTAL_SIZE}; -+ -+ if (HAILO_CPU_ID_CPU0 == params->cpu_id) { -+ log_resource.address += PCIE_APP_CPU_DEBUG_OFFSET; -+ } else if (HAILO_CPU_ID_CPU1 == params->cpu_id) { -+ log_resource.address += PCIE_CORE_CPU_DEBUG_OFFSET; -+ } else { -+ return -EINVAL; -+ } -+ -+ if (0 == params->buffer_size) { -+ params->read_bytes = 0; -+ return 0; -+ } -+ -+ err = hailo_read_firmware_log(&log_resource, params); -+ if (0 != err) { -+ return err; -+ } -+ -+ return 0; -+} -+ +static int direct_memory_transfer(struct hailo_pcie_resources *resources, + struct hailo_memory_transfer_params *params) +{ -+ if (params->address > U32_MAX) { -+ return -EFAULT; -+ } -+ + switch (params->transfer_direction) { + case TRANSFER_READ: -+ read_memory(resources, (u32)params->address, params->buffer, (u32)params->count); ++ read_memory(resources, params->address, params->buffer, (u32)params->count); + break; + case TRANSFER_WRITE: -+ write_memory(resources, (u32)params->address, params->buffer, (u32)params->count); ++ write_memory(resources, params->address, params->buffer, (u32)params->count); + break; + default: + return -EINVAL; @@ -133653,7 +137162,7 @@ index 000000000000..a85787c03c9d + switch(resources->board_type) { + case HAILO_BOARD_TYPE_HAILO8: + case HAILO_BOARD_TYPE_HAILO10H_LEGACY: -+ case HAILO_BOARD_TYPE_PLUTO: ++ case HAILO_BOARD_TYPE_HAILO15L: + resources->accelerator_type = HAILO_ACCELERATOR_TYPE_NNC; + break; + case HAILO_BOARD_TYPE_HAILO10H: @@ -133666,16 +137175,18 @@ index 000000000000..a85787c03c9d + return 0; +} + -+// On PCIe, just return the address -+static u64 encode_dma_address(dma_addr_t dma_address, u8 channel_id) ++// On PCIe, just return the start address ++u64 hailo_pcie_encode_desc_dma_address_range(dma_addr_t dma_address_start, dma_addr_t dma_address_end, u32 step, u8 channel_id) +{ + (void)channel_id; -+ return (u64)dma_address; ++ (void)dma_address_end; ++ (void)step; ++ return (u64)dma_address_start; +} + +struct hailo_vdma_hw hailo_pcie_vdma_hw = { + .hw_ops = { -+ .encode_desc_dma_address = encode_dma_address ++ .encode_desc_dma_address_range = hailo_pcie_encode_desc_dma_address_range, + }, + .ddr_data_id = HAILO_PCIE_HOST_DMA_DATA_ID, + .device_interrupts_bitmask = HAILO_PCIE_DMA_DEVICE_INTERRUPTS_BITMASK, @@ -133683,24 +137194,31 @@ index 000000000000..a85787c03c9d + .src_channels_bitmask = HAILO_PCIE_DMA_SRC_CHANNELS_BITMASK, +}; + -+void hailo_soc_write_soc_connect(struct hailo_pcie_resources *resources) ++void hailo_pcie_soc_write_request(struct hailo_pcie_resources *resources, ++ const struct hailo_pcie_soc_request *request) +{ + const struct hailo_fw_addresses *fw_addresses = &(compat[resources->board_type].fw_addresses); -+ const u32 soc_connect_value = FW_ACCESS_SOC_CONNECT_MASK; ++ BUILD_BUG_ON_MSG((sizeof(*request) % sizeof(u32)) != 0, "Request must be a multiple of 4 bytes"); + -+ // Write shutdown flag to FW -+ hailo_resource_write32(&resources->fw_access, fw_addresses->raise_ready_offset, soc_connect_value); ++ hailo_resource_write_buffer(&resources->fw_access, 0, sizeof(*request), (void*)request); ++ hailo_resource_write32(&resources->fw_access, fw_addresses->raise_ready_offset, FW_ACCESS_SOC_CONTROL_MASK); ++} ++ ++void hailo_pcie_soc_read_response(struct hailo_pcie_resources *resources, ++ struct hailo_pcie_soc_response *response) ++{ ++ BUILD_BUG_ON_MSG((sizeof(*response) % sizeof(u32)) != 0, "Request must be a multiple of 4 bytes"); ++ hailo_resource_read_buffer(&resources->fw_access, 0, sizeof(*response), response); +} -\ No newline at end of file diff --git a/drivers/media/pci/hailo/common/pcie_common.h b/drivers/media/pci/hailo/common/pcie_common.h new file mode 100644 -index 000000000000..642391c58999 +index 000000000000..9248a3bbdd3a --- /dev/null +++ b/drivers/media/pci/hailo/common/pcie_common.h -@@ -0,0 +1,168 @@ +@@ -0,0 +1,193 @@ +// SPDX-License-Identifier: MIT +/** -+ * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved. ++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. + **/ + +#ifndef _HAILO_COMMON_PCIE_COMMON_H_ @@ -133712,15 +137230,14 @@ index 000000000000..642391c58999 +#include "fw_operation.h" +#include "utils.h" +#include "vdma_common.h" ++#include "soc_structs.h" + +#include +#include + + -+#define BCS_ISTATUS_HOST_FW_IRQ_CONTROL_MASK (0x04000000) -+#define BCS_ISTATUS_HOST_FW_IRQ_NOTIFICATION (0x02000000) -+#define BCS_ISTATUS_HOST_DRIVER_DOWN (0x08000000) -+#define BCS_ISTATUS_SOC_CONNECT_ACCEPTED (0x10000000) ++#define BCS_ISTATUS_HOST_SW_IRQ_MASK (0xFF000000) ++#define BCS_ISTATUS_HOST_SW_IRQ_SHIFT (24) +#define BCS_ISTATUS_HOST_VDMA_SRC_IRQ_MASK (0x000000FF) +#define BCS_ISTATUS_HOST_VDMA_DEST_IRQ_MASK (0x0000FF00) + @@ -133734,15 +137251,21 @@ index 000000000000..642391c58999 +#define HAILO_PCIE_FW_ACCESS_BAR (4) + +#define HAILO_PCIE_DMA_ENGINES_COUNT (1) ++#define PCI_VDMA_ENGINE_INDEX (0) ++ ++#define MAX_FILES_PER_STAGE (4) ++ ++#define HAILO_PCIE_HOST_DMA_DATA_ID (0) ++#define HAILO_PCI_EP_HOST_DMA_DATA_ID (6) + +#define DRIVER_NAME "hailo" + +#define PCI_VENDOR_ID_HAILO 0x1e60 +#define PCI_DEVICE_ID_HAILO_HAILO8 0x2864 -+#define PCI_DEVICE_ID_HAILO_HAILO15 0x45C4 -+#define PCI_DEVICE_ID_HAILO_PLUTO 0x43a2 ++#define PCI_DEVICE_ID_HAILO_HAILO10H 0x45C4 ++#define PCI_DEVICE_ID_HAILO_HAILO15L 0x43a2 + -+typedef u32 hailo_ptr_t; ++typedef u64 hailo_ptr_t; + +struct hailo_pcie_resources { + struct hailo_resource config; // BAR0 @@ -133763,27 +137286,29 @@ index 000000000000..642391c58999 +enum loading_stages { + FIRST_STAGE = 0, + SECOND_STAGE = 1, -+ MAX_LOADING_STAGES = 2 ++ SECOND_STAGE_LINUX_IN_EMMC = 2, ++ MAX_LOADING_STAGES = 3 +}; + -+enum hailo_pcie_interrupt_masks { -+ FW_CONTROL = BCS_ISTATUS_HOST_FW_IRQ_CONTROL_MASK, -+ FW_NOTIFICATION = BCS_ISTATUS_HOST_FW_IRQ_NOTIFICATION, -+ DRIVER_DOWN = BCS_ISTATUS_HOST_DRIVER_DOWN, -+ SOC_CONNECT_ACCEPTED = BCS_ISTATUS_SOC_CONNECT_ACCEPTED, -+ VDMA_SRC_IRQ_MASK = BCS_ISTATUS_HOST_VDMA_SRC_IRQ_MASK, -+ VDMA_DEST_IRQ_MASK = BCS_ISTATUS_HOST_VDMA_DEST_IRQ_MASK ++enum hailo_pcie_nnc_sw_interrupt_masks { ++ HAILO_PCIE_NNC_FW_NOTIFICATION_IRQ = 0x2, ++ HAILO_PCIE_NNC_FW_CONTROL_IRQ = 0x4, ++ HAILO_PCIE_NNC_DRIVER_DOWN_IRQ = 0x8, +}; + -+struct hailo_pcie_interrupt_source { -+ u32 interrupt_bitmask; -+ u32 vdma_channels_bitmap; ++enum hailo_pcie_soc_sw_interrupt_masks { ++ HAILO_PCIE_SOC_CONTROL_IRQ = 0x10, ++ HAILO_PCIE_SOC_CLOSE_IRQ = 0x20, +}; + -+struct hailo_config_constants { -+ const char *filename; -+ u32 address; -+ size_t max_size; ++enum hailo_pcie_boot_interrupt_masks { ++ HAILO_PCIE_BOOT_SOFT_RESET_IRQ = 0x1, ++ HAILO_PCIE_BOOT_IRQ = 0x2, ++}; ++ ++struct hailo_pcie_interrupt_source { ++ u32 sw_interrupts; ++ u32 vdma_channels_bitmap; +}; + +struct hailo_file_batch { @@ -133792,6 +137317,14 @@ index 000000000000..642391c58999 + size_t max_size; + bool is_mandatory; + bool has_header; ++ bool has_core; ++}; ++ ++struct hailo_pcie_loading_stage { ++ const struct hailo_file_batch *batch; ++ u32 trigger_address; ++ u32 timeout; ++ u8 amount_of_files_in_stage; +}; + +// TODO: HRT-6144 - Align Windows/Linux to QNX @@ -133818,8 +137351,23 @@ index 000000000000..642391c58999 +extern "C" { +#endif + ++ ++#ifndef HAILO_EMULATOR ++#define TIME_UNTIL_REACH_BOOTLOADER (10) ++#define PCI_EP_WAIT_TIMEOUT_MS (40000) ++#define FIRMWARE_WAIT_TIMEOUT_MS (5000) ++#else /* ifndef HAILO_EMULATOR */ ++// PCI EP timeout is defined to 50000000 because on Emulator the boot time + linux init time can be very long (4+ hours) ++#define TIME_UNTIL_REACH_BOOTLOADER (10000) ++#define PCI_EP_WAIT_TIMEOUT_MS (50000000) ++#define FIRMWARE_WAIT_TIMEOUT_MS (5000000) ++#endif /* ifndef HAILO_EMULATOR */ ++ +extern struct hailo_vdma_hw hailo_pcie_vdma_hw; + ++const struct hailo_pcie_loading_stage* hailo_pcie_get_loading_stage_info(enum hailo_board_type board_type, ++ enum loading_stages stage); ++ +// Reads the interrupt source from BARs, return false if there is no interrupt. +// note - this function clears the interrupt signals. +bool hailo_pcie_read_interrupt(struct hailo_pcie_resources *resources, struct hailo_pcie_interrupt_source *source); @@ -133830,27 +137378,17 @@ index 000000000000..642391c58999 +int hailo_pcie_write_firmware_control(struct hailo_pcie_resources *resources, const struct hailo_fw_control *command); +int hailo_pcie_read_firmware_control(struct hailo_pcie_resources *resources, struct hailo_fw_control *command); + -+int hailo_pcie_write_firmware(struct hailo_pcie_resources *resources, const void *fw_data, size_t fw_size); +int hailo_pcie_write_firmware_batch(struct device *dev, struct hailo_pcie_resources *resources, u32 stage); +bool hailo_pcie_is_firmware_loaded(struct hailo_pcie_resources *resources); +bool hailo_pcie_wait_for_firmware(struct hailo_pcie_resources *resources); + -+int hailo_pcie_read_firmware_notification(struct hailo_pcie_resources *resources, -+ struct hailo_d2h_notification *notification); -+ -+int hailo_pcie_write_config_common(struct hailo_pcie_resources *resources, const void* config_data, -+ const size_t config_size, const struct hailo_config_constants *config_consts); -+const struct hailo_config_constants* hailo_pcie_get_board_config_constants(const enum hailo_board_type board_type); -+const struct hailo_config_constants* hailo_pcie_get_user_config_constants(const enum hailo_board_type board_type); -+const char* hailo_pcie_get_fw_filename(const enum hailo_board_type board_type); -+ -+long hailo_pcie_read_firmware_log(struct hailo_pcie_resources *resources, struct hailo_read_log_params *params); +int hailo_pcie_memory_transfer(struct hailo_pcie_resources *resources, struct hailo_memory_transfer_params *params); + +bool hailo_pcie_is_device_connected(struct hailo_pcie_resources *resources); +void hailo_pcie_write_firmware_driver_shutdown(struct hailo_pcie_resources *resources); -+void write_memory(struct hailo_pcie_resources *resources, hailo_ptr_t dest, const void *src, u32 len); -+void hailo_trigger_firmware_boot(struct hailo_pcie_resources *resources); ++void hailo_pcie_write_firmware_soft_reset(struct hailo_pcie_resources *resources); ++void hailo_pcie_configure_ep_registers_for_dma_transaction(struct hailo_pcie_resources *resources); ++void hailo_trigger_firmware_boot(struct hailo_pcie_resources *resources, u32 stage); + +int hailo_set_device_type(struct hailo_pcie_resources *resources); + @@ -133859,23 +137397,113 @@ index 000000000000..642391c58999 +int hailo_pcie_configure_atr_table(struct hailo_resource *bridge_config, u64 trsl_addr, u32 atr_index); +void hailo_pcie_read_atr_table(struct hailo_resource *bridge_config, struct hailo_atr_config *atr, u32 atr_index); + -+void hailo_soc_write_soc_connect(struct hailo_pcie_resources *resources); ++u64 hailo_pcie_encode_desc_dma_address_range(dma_addr_t dma_address_start, dma_addr_t dma_address_end, u32 step, u8 channel_id); ++ ++void hailo_pcie_soc_write_request(struct hailo_pcie_resources *resources, ++ const struct hailo_pcie_soc_request *request); ++void hailo_pcie_soc_read_response(struct hailo_pcie_resources *resources, ++ struct hailo_pcie_soc_response *response); + +#ifdef __cplusplus +} +#endif + +#endif /* _HAILO_COMMON_PCIE_COMMON_H_ */ +diff --git a/drivers/media/pci/hailo/common/soc_structs.h b/drivers/media/pci/hailo/common/soc_structs.h +new file mode 100644 +index 000000000000..5a00c028c87f +--- /dev/null ++++ b/drivers/media/pci/hailo/common/soc_structs.h +@@ -0,0 +1,79 @@ ++// SPDX-License-Identifier: MIT ++/** ++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. ++ **/ ++/** ++ * Contains definitions for pcie soc to pcie ep communication ++ */ ++ ++#ifndef __HAILO_COMMON_SOC_STRUCTS__ ++#define __HAILO_COMMON_SOC_STRUCTS__ ++ ++#include ++ ++#pragma pack(push, 1) ++ ++struct hailo_pcie_soc_connect_request { ++ u16 port; ++}; ++ ++struct hailo_pcie_soc_connect_response { ++ u8 input_channel_index; ++ u8 output_channel_index; ++}; ++ ++ ++struct hailo_pcie_soc_close_request { ++ u32 channels_bitmap; ++}; ++ ++struct hailo_pcie_soc_close_response { ++ u8 reserved; ++}; ++ ++enum hailo_pcie_soc_control_code { ++ // Start from big initial value to ensure the right code was used (using 0 ++ // as initiale may cause confusion if the code was not set correctly). ++ HAILO_PCIE_SOC_CONTROL_CODE_CONNECT = 0x100, ++ HAILO_PCIE_SOC_CONTROL_CODE_CLOSE, ++ HAILO_PCIE_SOC_CONTROL_CODE_INVALID, ++}; ++ ++#define HAILO_PCIE_SOC_MAX_REQUEST_SIZE_BYTES (16) ++#define HAILO_PCIE_SOC_MAX_RESPONSE_SIZE_BYTES (16) ++ ++// IRQ to signal the PCIe that the EP was closed/released ++#define PCI_EP_SOC_CLOSED_IRQ (0x00000020) ++#define PCI_EP_SOC_CONNECT_RESPONSE (0x00000010) ++ ++struct hailo_pcie_soc_request { ++ u32 control_code; ++ union { ++ struct hailo_pcie_soc_connect_request connect; ++ struct hailo_pcie_soc_close_request close; ++ u8 pad[HAILO_PCIE_SOC_MAX_REQUEST_SIZE_BYTES]; ++ }; ++}; ++ ++struct hailo_pcie_soc_response { ++ u32 control_code; ++ s32 status; ++ union { ++ struct hailo_pcie_soc_connect_response connect; ++ struct hailo_pcie_soc_close_response close; ++ u8 pad[HAILO_PCIE_SOC_MAX_RESPONSE_SIZE_BYTES]; ++ }; ++}; ++ ++#pragma pack(pop) ++ ++// Compile time validate function. Don't need to call it. ++static inline void __validate_soc_struct_sizes(void) ++{ ++ BUILD_BUG_ON_MSG(sizeof(struct hailo_pcie_soc_request) != ++ sizeof(u32) + HAILO_PCIE_SOC_MAX_REQUEST_SIZE_BYTES, "Invalid request size"); ++ BUILD_BUG_ON_MSG(sizeof(struct hailo_pcie_soc_response) != ++ sizeof(u32) + sizeof(s32) + HAILO_PCIE_SOC_MAX_RESPONSE_SIZE_BYTES, "Invalid response size"); ++} ++ ++#endif /* __HAILO_COMMON_SOC_STRUCTS__ */ \ No newline at end of file diff --git a/drivers/media/pci/hailo/common/utils.h b/drivers/media/pci/hailo/common/utils.h new file mode 100644 -index 000000000000..fcf34978c807 +index 000000000000..0d53b65799b8 --- /dev/null +++ b/drivers/media/pci/hailo/common/utils.h -@@ -0,0 +1,61 @@ +@@ -0,0 +1,82 @@ +// SPDX-License-Identifier: MIT +/** -+ * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved. ++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. + **/ + +#ifndef _HAILO_DRIVER_UTILS_H_ @@ -133883,6 +137511,11 @@ index 000000000000..fcf34978c807 + +#include + ++#define DWORD_SIZE (4) ++#define WORD_SIZE (2) ++#define BYTE_SIZE (1) ++#define BITS_IN_BYTE (8) ++ +#define hailo_clear_bit(bit, pval) { *(pval) &= ~(1 << bit); } +#define hailo_test_bit(pos,var_addr) ((*var_addr) & (1<<(pos))) + @@ -133925,6 +137558,22 @@ index 000000000000..fcf34978c807 + return result; +} + ++// Gets the nearest power of 2 >= value, for any value <= MAX_POWER_OF_2_VALUE. Otherwise POWER_OF_2_ERROR is returned. ++#define MAX_POWER_OF_2_VALUE (0x80000000) ++#define POWER_OF_2_ERROR ((uint32_t)-1) ++static inline uint32_t get_nearest_powerof_2(uint32_t value) ++{ ++ uint32_t power_of_2 = 1; ++ if (value > MAX_POWER_OF_2_VALUE) { ++ return POWER_OF_2_ERROR; ++ } ++ ++ while (value > power_of_2) { ++ power_of_2 <<= 1; ++ } ++ return power_of_2; ++} ++ +#ifndef DIV_ROUND_UP +#define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d)) +#endif @@ -133937,13 +137586,13 @@ index 000000000000..fcf34978c807 \ No newline at end of file diff --git a/drivers/media/pci/hailo/common/vdma_common.c b/drivers/media/pci/hailo/common/vdma_common.c new file mode 100644 -index 000000000000..c8b60c848416 +index 000000000000..56d879eed864 --- /dev/null +++ b/drivers/media/pci/hailo/common/vdma_common.c -@@ -0,0 +1,877 @@ +@@ -0,0 +1,876 @@ +// SPDX-License-Identifier: MIT +/** -+ * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved. ++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. + **/ + +#include "vdma_common.h" @@ -133958,16 +137607,6 @@ index 000000000000..c8b60c848416 +#include +#include + -+ -+#define CHANNEL_BASE_OFFSET(channel_index) ((channel_index) << 5) -+ -+#define CHANNEL_CONTROL_OFFSET (0x0) -+#define CHANNEL_DEPTH_ID_OFFSET (0x1) -+#define CHANNEL_NUM_AVAIL_OFFSET (0x2) -+#define CHANNEL_NUM_PROC_OFFSET (0x4) -+#define CHANNEL_ERROR_OFFSET (0x8) -+#define CHANNEL_DEST_REGS_OFFSET (0x10) -+ +#define VDMA_CHANNEL_CONTROL_START (0x1) +#define VDMA_CHANNEL_CONTROL_ABORT (0b00) +#define VDMA_CHANNEL_CONTROL_ABORT_PAUSE (0b10) @@ -134005,11 +137644,6 @@ index 000000000000..c8b60c848416 +#define VDMA_CHANNEL_NUM_PROCESSED_MASK ((1 << VDMA_CHANNEL_NUM_PROCESSED_WIDTH) - 1) +#define VDMA_CHANNEL_NUM_ONGOING_MASK VDMA_CHANNEL_NUM_PROCESSED_MASK + -+#define DWORD_SIZE (4) -+#define WORD_SIZE (2) -+#define BYTE_SIZE (1) -+#define BITS_IN_BYTE (8) -+ +#define TIMESTAMPS_CIRC_SPACE(timestamp_list) \ + CIRC_SPACE((timestamp_list).head, (timestamp_list).tail, CHANNEL_IRQ_TIMESTAMPS_SIZE) +#define TIMESTAMPS_CIRC_CNT(timestamp_list) \ @@ -134093,7 +137727,7 @@ index 000000000000..c8b60c848416 + return true; +} + -+void hailo_vdma_program_descriptor(struct hailo_vdma_descriptor *descriptor, u64 dma_address, size_t page_size, ++static void hailo_vdma_program_descriptor(struct hailo_vdma_descriptor *descriptor, u64 dma_address, size_t page_size, + u8 data_id) +{ + descriptor->PageSize_DescControl = (u32)((page_size << DESCRIPTOR_PAGE_SIZE_SHIFT) + @@ -134108,42 +137742,54 @@ index 000000000000..c8b60c848416 + return (channel_index < MAX_VDMA_CHANNELS_PER_ENGINE) ? (channel_index & 0xF) : INVALID_VDMA_CHANNEL; +} + -+static int program_descriptors_in_chunk( ++int hailo_vdma_program_descriptors_in_chunk( + struct hailo_vdma_hw *vdma_hw, + dma_addr_t chunk_addr, + unsigned int chunk_size, + struct hailo_vdma_descriptors_list *desc_list, + u32 desc_index, + u32 max_desc_index, -+ u8 channel_id) ++ u8 channel_index, ++ u8 data_id) +{ -+ const u32 desc_per_chunk = DIV_ROUND_UP(chunk_size, desc_list->desc_page_size); ++ const u16 page_size = desc_list->desc_page_size; ++ const u32 descs_to_program = DIV_ROUND_UP(chunk_size, page_size); ++ const u32 starting_desc_index = desc_index; ++ const u32 residue_size = chunk_size % page_size; + struct hailo_vdma_descriptor *dma_desc = NULL; -+ u16 size_to_program = 0; -+ u32 index = 0; + u64 encoded_addr = 0; + -+ for (index = 0; index < desc_per_chunk; index++) { -+ if (desc_index > max_desc_index) { -+ return -ERANGE; -+ } ++ if (descs_to_program == 0) { ++ // Nothing to program ++ return 0; ++ } + -+ encoded_addr = vdma_hw->hw_ops.encode_desc_dma_address(chunk_addr, channel_id); -+ if (INVALID_VDMA_ADDRESS == encoded_addr) { -+ return -EFAULT; -+ } ++ // We iterate through descriptors [desc_index, desc_index + descs_to_program) ++ if (desc_index + descs_to_program > max_desc_index + 1) { ++ return -ERANGE; ++ } + -+ dma_desc = &desc_list->desc_list[desc_index % desc_list->desc_count]; -+ size_to_program = chunk_size > desc_list->desc_page_size ? -+ desc_list->desc_page_size : (u16)chunk_size; -+ hailo_vdma_program_descriptor(dma_desc, encoded_addr, size_to_program, vdma_hw->ddr_data_id); ++ encoded_addr = vdma_hw->hw_ops.encode_desc_dma_address_range(chunk_addr, chunk_addr + chunk_size, page_size, get_channel_id(channel_index)); ++ if (INVALID_VDMA_ADDRESS == encoded_addr) { ++ return -EFAULT; ++ } + -+ chunk_addr += size_to_program; -+ chunk_size -= size_to_program; -+ desc_index++; ++ // Program all descriptors except the last one ++ for (desc_index = starting_desc_index; desc_index < starting_desc_index + descs_to_program - 1; desc_index++) { ++ // 'desc_index & desc_list_len_mask' is used instead of modulo; see hailo_vdma_descriptors_list documentation. ++ hailo_vdma_program_descriptor( ++ &desc_list->desc_list[desc_index & desc_list->desc_count_mask], ++ encoded_addr, page_size, data_id); ++ encoded_addr += page_size; + } + -+ return (int)desc_per_chunk; ++ // Handle the last descriptor outside of the loop ++ // 'desc_index & desc_list_len_mask' is used instead of modulo; see hailo_vdma_descriptors_list documentation. ++ dma_desc = &desc_list->desc_list[desc_index & desc_list->desc_count_mask]; ++ hailo_vdma_program_descriptor(dma_desc, encoded_addr, ++ (residue_size == 0) ? page_size : (u16)residue_size, data_id); ++ ++ return (int)descs_to_program; +} + +static unsigned long get_interrupts_bitmask(struct hailo_vdma_hw *vdma_hw, @@ -134177,13 +137823,12 @@ index 000000000000..c8b60c848416 + enum hailo_vdma_interrupts_domain last_desc_interrupts, + bool is_debug) +{ -+ const u8 channel_id = get_channel_id(channel_index); + int desc_programmed = 0; ++ int descs_programmed_in_chunk = 0; + u32 max_desc_index = 0; + u32 chunk_size = 0; + struct scatterlist *sg_entry = NULL; + unsigned int i = 0; -+ int ret = 0; + size_t buffer_current_offset = 0; + dma_addr_t chunk_start_addr = 0; + u32 program_size = buffer->size; @@ -134215,14 +137860,14 @@ index 000000000000..c8b60c848416 + (u32)(sg_dma_len(sg_entry)); + chunk_size = min((u32)program_size, chunk_size); + -+ ret = program_descriptors_in_chunk(vdma_hw, chunk_start_addr, chunk_size, desc_list, -+ starting_desc, max_desc_index, channel_id); -+ if (ret < 0) { -+ return ret; ++ descs_programmed_in_chunk = hailo_vdma_program_descriptors_in_chunk(vdma_hw, chunk_start_addr, chunk_size, desc_list, ++ starting_desc, max_desc_index, channel_index, vdma_hw->ddr_data_id); ++ if (descs_programmed_in_chunk < 0) { ++ return descs_programmed_in_chunk; + } + -+ desc_programmed += ret; -+ starting_desc = starting_desc + ret; ++ desc_programmed += descs_programmed_in_chunk; ++ starting_desc = starting_desc + descs_programmed_in_chunk; + program_size -= chunk_size; + buffer_current_offset += sg_dma_len(sg_entry); + } @@ -134299,16 +137944,16 @@ index 000000000000..c8b60c848416 + return 0; +} + -+static void set_num_avail(u8 __iomem *host_regs, u16 num_avail) ++void hailo_vdma_set_num_avail(u8 __iomem *regs, u16 num_avail) +{ -+ u32 host_regs_val = ioread32(host_regs); -+ iowrite32(WRITE_BITS_AT_OFFSET(WORD_SIZE * BITS_IN_BYTE, CHANNEL_NUM_AVAIL_OFFSET * BITS_IN_BYTE, host_regs_val, num_avail), -+ host_regs); ++ u32 regs_val = ioread32(regs); ++ iowrite32(WRITE_BITS_AT_OFFSET(WORD_SIZE * BITS_IN_BYTE, CHANNEL_NUM_AVAIL_OFFSET * BITS_IN_BYTE, regs_val, num_avail), ++ regs); +} + -+static u16 get_num_proc(u8 __iomem *host_regs) ++u16 hailo_vdma_get_num_proc(u8 __iomem *regs) +{ -+ return READ_BITS_AT_OFFSET(WORD_SIZE * BITS_IN_BYTE, 0, ioread32(host_regs + CHANNEL_NUM_PROC_OFFSET)); ++ return READ_BITS_AT_OFFSET(WORD_SIZE * BITS_IN_BYTE, 0, ioread32(regs + CHANNEL_NUM_PROC_OFFSET)); +} + +int hailo_vdma_launch_transfer( @@ -134391,7 +138036,7 @@ index 000000000000..c8b60c848416 + + new_num_avail = (u16)((last_desc + 1) % desc_list->desc_count); + channel->state.num_avail = new_num_avail; -+ set_num_avail(channel->host_regs, new_num_avail); ++ hailo_vdma_set_num_avail(channel->host_regs, new_num_avail); + + return (int)total_descs; +} @@ -134399,7 +138044,7 @@ index 000000000000..c8b60c848416 +static void hailo_vdma_push_timestamp(struct hailo_vdma_channel *channel) +{ + struct hailo_channel_interrupt_timestamp_list *timestamp_list = &channel->timestamp_list; -+ const u16 num_proc = get_num_proc(channel->host_regs); ++ const u16 num_proc = hailo_vdma_get_num_proc(channel->host_regs); + if (TIMESTAMPS_CIRC_SPACE(*timestamp_list) != 0) { + timestamp_list->timestamps[timestamp_list->head].timestamp_ns = ktime_get_ns(); + timestamp_list->timestamps[timestamp_list->head].desc_num_processed = num_proc; @@ -134526,21 +138171,23 @@ index 000000000000..c8b60c848416 + engine->enabled_channels &= ~bitmap; + + for_each_vdma_channel(engine, channel, channel_index) { -+ channel_state_init(&channel->state); ++ if (hailo_test_bit(channel_index, &bitmap)) { ++ channel_state_init(&channel->state); + -+ while (ONGOING_TRANSFERS_CIRC_CNT(channel->ongoing_transfers) > 0) { -+ struct hailo_ongoing_transfer transfer; -+ ongoing_transfer_pop(channel, &transfer); ++ while (ONGOING_TRANSFERS_CIRC_CNT(channel->ongoing_transfers) > 0) { ++ struct hailo_ongoing_transfer transfer; ++ ongoing_transfer_pop(channel, &transfer); ++ ++ if (channel->last_desc_list == NULL) { ++ pr_err("Channel %d has ongoing transfers but no desc list\n", channel->index); ++ continue; ++ } + -+ if (channel->last_desc_list == NULL) { -+ pr_err("Channel %d has ongoing transfers but no desc list\n", channel->index); -+ continue; ++ clear_dirty_descs(channel, &transfer); + } + -+ clear_dirty_descs(channel, &transfer); ++ channel->last_desc_list = NULL; + } -+ -+ channel->last_desc_list = NULL; + } +} + @@ -134659,7 +138306,7 @@ index 000000000000..c8b60c848416 + // the actual hw_num_processed is a number between 1 and desc_count. + // Therefore the value can be desc_count, in this case we change it to + // zero. -+ hw_num_proc = get_num_proc(channel->host_regs) & channel->state.desc_count_mask; ++ hw_num_proc = hailo_vdma_get_num_proc(channel->host_regs) & channel->state.desc_count_mask; + + while (ONGOING_TRANSFERS_CIRC_CNT(channel->ongoing_transfers) > 0) { + struct hailo_ongoing_transfer *cur_transfer = @@ -134714,12 +138361,13 @@ index 000000000000..c8b60c848416 + VDMA_CHANNEL_CONTROL_ABORT), host_regs); +} + -+int hailo_vdma_start_channel(u8 __iomem *host_regs, uint64_t desc_dma_address, uint8_t desc_depth, ++int hailo_vdma_start_channel(u8 __iomem *regs, uint64_t desc_dma_address, uint32_t desc_count, + uint8_t data_id) +{ + u16 dma_address_l = 0; + u32 dma_address_h = 0; + u32 desc_depth_data_id = 0; ++ u8 desc_depth = ceil_log2(desc_count); + + if (((desc_dma_address & 0xFFFF) != 0) || + (desc_depth > DESCRIPTOR_LIST_MAX_DEPTH)) { @@ -134732,22 +138380,22 @@ index 000000000000..c8b60c848416 + } + + // Stop old channel state -+ hailo_vdma_stop_channel(host_regs); ++ hailo_vdma_stop_channel(regs); + + // Configure address, depth and id + dma_address_l = (uint16_t)((desc_dma_address >> 16) & 0xFFFF); + iowrite32(WRITE_BITS_AT_OFFSET(WORD_SIZE * BITS_IN_BYTE, (VDMA_CHANNEL__ADDRESS_L_OFFSET - -+ VDMA_CHANNEL__ALIGNED_ADDRESS_L_OFFSET) * BITS_IN_BYTE, ioread32(host_regs + -+ VDMA_CHANNEL__ALIGNED_ADDRESS_L_OFFSET), dma_address_l), host_regs + VDMA_CHANNEL__ALIGNED_ADDRESS_L_OFFSET); ++ VDMA_CHANNEL__ALIGNED_ADDRESS_L_OFFSET) * BITS_IN_BYTE, ioread32(regs + ++ VDMA_CHANNEL__ALIGNED_ADDRESS_L_OFFSET), dma_address_l), regs + VDMA_CHANNEL__ALIGNED_ADDRESS_L_OFFSET); + + dma_address_h = (uint32_t)(desc_dma_address >> 32); -+ iowrite32(dma_address_h, host_regs + VDMA_CHANNEL__ADDRESS_H_OFFSET); ++ iowrite32(dma_address_h, regs + VDMA_CHANNEL__ADDRESS_H_OFFSET); + + desc_depth_data_id = (uint32_t)(desc_depth << VDMA_CHANNEL_DESC_DEPTH_SHIFT) | + (data_id << VDMA_CHANNEL_DATA_ID_SHIFT); -+ iowrite32(desc_depth_data_id, host_regs); ++ iowrite32(desc_depth_data_id, regs); + -+ start_vdma_control_register(host_regs); ++ start_vdma_control_register(regs); + + return 0; +} @@ -134787,10 +138435,10 @@ index 000000000000..c8b60c848416 + return -ETIMEDOUT; +} + -+void hailo_vdma_stop_channel(u8 __iomem *host_regs) ++void hailo_vdma_stop_channel(u8 __iomem *regs) +{ + int err = 0; -+ u8 host_side_channel_regs = READ_BITS_AT_OFFSET(BYTE_SIZE * BITS_IN_BYTE, CHANNEL_CONTROL_OFFSET * BITS_IN_BYTE, ioread32(host_regs)); ++ u8 host_side_channel_regs = READ_BITS_AT_OFFSET(BYTE_SIZE * BITS_IN_BYTE, CHANNEL_CONTROL_OFFSET * BITS_IN_BYTE, ioread32(regs)); + + if ((host_side_channel_regs & VDMA_CHANNEL_CONTROL_START_ABORT_PAUSE_RESUME_BITMASK) == VDMA_CHANNEL_CONTROL_ABORT_PAUSE) { + // The channel is aborted (we set the channel to VDMA_CHANNEL_CONTROL_ABORT_PAUSE at the end of this function) @@ -134800,17 +138448,17 @@ index 000000000000..c8b60c848416 + // Pause the channel + // The channel is paused to allow for "all transfers from fetched descriptors..." to be "...completed" + // (from PLDA PCIe refernce manual, "9.2.5 Starting a Channel and Transferring Data") -+ hailo_vdma_channel_pause(host_regs); ++ hailo_vdma_channel_pause(regs); + + // Even if channel is stuck and not idle, force abort and return error in the end -+ err = hailo_vdma_wait_until_channel_idle(host_regs); ++ err = hailo_vdma_wait_until_channel_idle(regs); + // Success oriented - if error occured print error but still abort channel + if (err < 0) { + pr_err("Timeout occured while waiting for channel to become idle\n"); + } + + // Abort the channel (even of hailo_vdma_wait_until_channel_idle function fails) -+ hailo_vdma_channel_abort(host_regs); ++ hailo_vdma_channel_abort(regs); +} + +bool hailo_check_channel_index(u8 channel_index, u32 src_channels_bitmask, bool is_input_channel) @@ -134821,13 +138469,13 @@ index 000000000000..c8b60c848416 \ No newline at end of file diff --git a/drivers/media/pci/hailo/common/vdma_common.h b/drivers/media/pci/hailo/common/vdma_common.h new file mode 100644 -index 000000000000..0d84ac6d31bd +index 000000000000..9176543b085c --- /dev/null +++ b/drivers/media/pci/hailo/common/vdma_common.h -@@ -0,0 +1,257 @@ +@@ -0,0 +1,284 @@ +// SPDX-License-Identifier: MIT +/** -+ * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved. ++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. + **/ + +#ifndef _HAILO_COMMON_VDMA_COMMON_H_ @@ -134843,6 +138491,15 @@ index 000000000000..0d84ac6d31bd +#define VDMA_DESCRIPTOR_LIST_ALIGN (1 << 16) +#define INVALID_VDMA_ADDRESS (0) + ++#define CHANNEL_BASE_OFFSET(channel_index) ((channel_index) << 5) ++ ++#define CHANNEL_CONTROL_OFFSET (0x0) ++#define CHANNEL_DEPTH_ID_OFFSET (0x1) ++#define CHANNEL_NUM_AVAIL_OFFSET (0x2) ++#define CHANNEL_NUM_PROC_OFFSET (0x4) ++#define CHANNEL_ERROR_OFFSET (0x8) ++#define CHANNEL_DEST_REGS_OFFSET (0x10) ++ +#ifdef __cplusplus +extern "C" +{ @@ -134857,7 +138514,13 @@ index 000000000000..0d84ac6d31bd + +struct hailo_vdma_descriptors_list { + struct hailo_vdma_descriptor *desc_list; -+ u32 desc_count; // Must be power of 2 if is_circular is set. ++ // Must be power of 2 if is_circular is set. ++ u32 desc_count; ++ // The nearest power of 2 to desc_count (including desc_count), minus 1. ++ // * If the list is circular, then 'index & desc_count_mask' can be used instead of modulo. ++ // * Otherwise, we can't wrap around the list anyway. However, for any index < desc_count, 'index & desc_count_mask' ++ // will return the same value. ++ u32 desc_count_mask; + u16 desc_page_size; + bool is_circular; +}; @@ -134940,9 +138603,10 @@ index 000000000000..0d84ac6d31bd +}; + +struct hailo_vdma_hw_ops { -+ // Accepts some dma_addr_t mapped to the device and encodes it using -+ // hw specific encode. returns INVALID_VDMA_ADDRESS on failure. -+ u64 (*encode_desc_dma_address)(dma_addr_t dma_address, u8 channel_id); ++ // Accepts start, end and step of an address range (of type dma_addr_t). ++ // Returns the encoded base address or INVALID_VDMA_ADDRESS if the range/step is invalid. ++ // All addresses in the range of [returned_addr, returned_addr + step, returned_addr + 2*step, ..., dma_address_end) are valid. ++ u64 (*encode_desc_dma_address_range)(dma_addr_t dma_address_start, dma_addr_t dma_address_end, u32 step, u8 channel_id); +}; + +struct hailo_vdma_hw { @@ -134963,12 +138627,9 @@ index 000000000000..0d84ac6d31bd + for (index = 0, element = &array[index]; index < size; index++, element = &array[index]) + +#define for_each_vdma_channel(engine, channel, channel_index) \ -+ _for_each_element_array(engine->channels, MAX_VDMA_CHANNELS_PER_ENGINE, \ ++ _for_each_element_array((engine)->channels, MAX_VDMA_CHANNELS_PER_ENGINE, \ + channel, channel_index) + -+void hailo_vdma_program_descriptor(struct hailo_vdma_descriptor *descriptor, u64 dma_address, size_t page_size, -+ u8 data_id); -+ +/** + * Program the given descriptors list to map the given buffer. + * @@ -134995,6 +138656,20 @@ index 000000000000..0d84ac6d31bd + enum hailo_vdma_interrupts_domain last_desc_interrupts, + bool is_debug); + ++int hailo_vdma_program_descriptors_in_chunk( ++ struct hailo_vdma_hw *vdma_hw, ++ dma_addr_t chunk_addr, ++ unsigned int chunk_size, ++ struct hailo_vdma_descriptors_list *desc_list, ++ u32 desc_index, ++ u32 max_desc_index, ++ u8 channel_index, ++ u8 data_id); ++ ++void hailo_vdma_set_num_avail(u8 __iomem *regs, u16 num_avail); ++ ++u16 hailo_vdma_get_num_proc(u8 __iomem *regs); ++ +/** + * Launch a transfer on some vdma channel. Includes: + * 1. Binding the transfer buffers to the descriptors list. @@ -135072,9 +138747,9 @@ index 000000000000..0d84ac6d31bd + struct hailo_vdma_engine *engine, u32 irq_channels_bitmap, + transfer_done_cb_t transfer_done, void *transfer_done_opaque); + -+int hailo_vdma_start_channel(u8 __iomem *host_regs, uint64_t desc_dma_address, uint8_t desc_depth, uint8_t data_id); ++int hailo_vdma_start_channel(u8 __iomem *regs, uint64_t desc_dma_address, uint32_t desc_count, uint8_t data_id); + -+void hailo_vdma_stop_channel(u8 __iomem *host_regs); ++void hailo_vdma_stop_channel(u8 __iomem *regs); + +bool hailo_check_channel_index(u8 channel_index, u32 src_channels_bitmask, bool is_input_channel); + @@ -135104,13 +138779,13 @@ index 000000000000..936bd7d4a477 +#endif /* _HAILO_PCIE_VERSION_H_ */ diff --git a/drivers/media/pci/hailo/src/fops.c b/drivers/media/pci/hailo/src/fops.c new file mode 100644 -index 000000000000..6b455774d973 +index 000000000000..3b1cba647be7 --- /dev/null +++ b/drivers/media/pci/hailo/src/fops.c -@@ -0,0 +1,762 @@ +@@ -0,0 +1,570 @@ +// SPDX-License-Identifier: GPL-2.0 +/** -+ * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved. ++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. + **/ + +#include @@ -135129,14 +138804,14 @@ index 000000000000..6b455774d973 +#include +#endif + -+#include "utils.h" +#include "fops.h" +#include "vdma_common.h" +#include "utils/logs.h" +#include "vdma/memory.h" +#include "vdma/ioctl.h" +#include "utils/compact.h" -+#include "pci_soc_ioctl.h" ++#include "nnc.h" ++#include "soc.h" + + +#if LINUX_VERSION_CODE >= KERNEL_VERSION( 4, 13, 0 ) @@ -135158,13 +138833,6 @@ index 000000000000..6b455774d973 +// On pcie driver there is only one dma engine +#define DEFAULT_VDMA_ENGINE_INDEX (0) + -+#if !defined(HAILO_EMULATOR) -+#define DEFAULT_SHUTDOWN_TIMEOUT_MS (5) -+#else /* !defined(HAILO_EMULATOR) */ -+#define DEFAULT_SHUTDOWN_TIMEOUT_MS (1000) -+#endif /* !defined(HAILO_EMULATOR) */ -+ -+static long hailo_add_notification_wait(struct hailo_pcie_board *board, struct file *filp); + +static struct hailo_file_context *create_file_context(struct hailo_pcie_board *board, struct file *filp) +{ @@ -135234,7 +138902,7 @@ index 000000000000..6b455774d973 + + previous_power_state = pBoard->pDev->current_state; + if (PCI_D0 != previous_power_state) { -+ hailo_info(pBoard, "Waking up board"); ++ hailo_info(pBoard, "Waking up board change state from %d to PCI_D0\n", previous_power_state); + err = pci_set_power_state(pBoard->pDev, PCI_D0); + if (err < 0) { + hailo_err(pBoard, "Failed waking up board %d", err); @@ -135258,7 +138926,11 @@ index 000000000000..6b455774d973 + interrupts_enabled_by_filp = true; + } + -+ err = hailo_add_notification_wait(pBoard, filp); ++ if (pBoard->pcie_resources.accelerator_type == HAILO_ACCELERATOR_TYPE_NNC) { ++ err = hailo_nnc_file_context_init(pBoard, context); ++ } else { ++ err = hailo_soc_file_context_init(pBoard, context); ++ } + if (err < 0) { + goto l_release_irq; + } @@ -135276,6 +138948,7 @@ index 000000000000..6b455774d973 + +l_revert_power_state: + if (pBoard->pDev->current_state != previous_power_state) { ++ hailo_info(pBoard, "Power changing state from %d to %d\n", previous_power_state, pBoard->pDev->current_state); + if (pci_set_power_state(pBoard->pDev, previous_power_state) < 0) { + hailo_err(pBoard, "Failed setting power state back to %d\n", (int)previous_power_state); + } @@ -135290,34 +138963,6 @@ index 000000000000..6b455774d973 + return err; +} + -+int hailo_pcie_driver_down(struct hailo_pcie_board *board) -+{ -+ long completion_result = 0; -+ int err = 0; -+ -+ reinit_completion(&board->driver_down.reset_completed); -+ -+ hailo_pcie_write_firmware_driver_shutdown(&board->pcie_resources); -+ -+ // Wait for response -+ completion_result = -+ wait_for_completion_timeout(&board->driver_down.reset_completed, msecs_to_jiffies(DEFAULT_SHUTDOWN_TIMEOUT_MS)); -+ if (completion_result <= 0) { -+ if (0 == completion_result) { -+ hailo_err(board, "hailo_pcie_driver_down, timeout waiting for shutdown response (timeout_ms=%d)\n", DEFAULT_SHUTDOWN_TIMEOUT_MS); -+ err = -ETIMEDOUT; -+ } else { -+ hailo_info(board, "hailo_pcie_driver_down, wait for completion failed with err=%ld (process was interrupted or killed)\n", -+ completion_result); -+ err = completion_result; -+ } -+ goto l_exit; -+ } -+ -+l_exit: -+ return err; -+} -+ +int hailo_pcie_fops_release(struct inode *inode, struct file *filp) +{ + struct hailo_pcie_board *board = (struct hailo_pcie_board *)filp->private_data; @@ -135344,12 +138989,10 @@ index 000000000000..6b455774d973 + hailo_err(board, "Invalid file context\n"); + } + -+ hailo_pcie_clear_notification_wait_list(board, filp); -+ -+ if (filp == board->vdma.used_by_filp) { -+ if (hailo_pcie_driver_down(board)) { -+ hailo_err(board, "Failed sending FW shutdown event"); -+ } ++ if (board->pcie_resources.accelerator_type == HAILO_ACCELERATOR_TYPE_NNC) { ++ hailo_nnc_file_context_finalize(board, context); ++ } else { ++ hailo_soc_file_context_finalize(board, context); + } + + hailo_vdma_file_context_finalize(&context->vdma_context, &board->vdma, filp); @@ -135360,6 +139003,7 @@ index 000000000000..6b455774d973 + hailo_disable_interrupts(board); + + if (power_mode_enabled()) { ++ hailo_info(board, "Power change state to PCI_D3hot\n"); + if (board->pDev && pci_set_power_state(board->pDev, PCI_D3hot) < 0) { + hailo_err(board, "Failed setting power state to D3hot"); + } @@ -135411,44 +139055,23 @@ index 000000000000..6b455774d973 + return err; +} + -+static long hailo_read_log_ioctl(struct hailo_pcie_board *pBoard, unsigned long arg) -+{ -+ long err = 0; -+ struct hailo_read_log_params params; -+ -+ if (copy_from_user(¶ms, (void __user*)arg, sizeof(params))) { -+ hailo_err(pBoard, "HAILO_READ_LOG, copy_from_user fail\n"); -+ return -ENOMEM; -+ } -+ -+ if (0 > (err = hailo_pcie_read_firmware_log(&pBoard->pcie_resources, ¶ms))) { -+ hailo_err(pBoard, "HAILO_READ_LOG, reading from log failed with error: %ld \n", err); -+ return err; -+ } -+ -+ if (copy_to_user((void*)arg, ¶ms, sizeof(params))) { -+ return -ENOMEM; -+ } -+ -+ return 0; -+} -+ +static void firmware_notification_irq_handler(struct hailo_pcie_board *board) +{ + struct hailo_notification_wait *notif_wait_cursor = NULL; + int err = 0; + unsigned long irq_saved_flags = 0; + -+ spin_lock_irqsave(&board->notification_read_spinlock, irq_saved_flags); -+ err = hailo_pcie_read_firmware_notification(&board->pcie_resources, &board->notification_cache); -+ spin_unlock_irqrestore(&board->notification_read_spinlock, irq_saved_flags); ++ spin_lock_irqsave(&board->nnc.notification_read_spinlock, irq_saved_flags); ++ err = hailo_pcie_read_firmware_notification(&board->pcie_resources.fw_access, &board->nnc.notification_cache); ++ spin_unlock_irqrestore(&board->nnc.notification_read_spinlock, irq_saved_flags); + + if (err < 0) { + hailo_err(board, "Failed reading firmware notification"); + } + else { ++ // TODO: HRT-14502 move interrupt handling to nnc + rcu_read_lock(); -+ list_for_each_entry_rcu(notif_wait_cursor, &board->notification_wait_list, notification_wait_list) ++ list_for_each_entry_rcu(notif_wait_cursor, &board->nnc.notification_wait_list, notification_wait_list) + { + complete(¬if_wait_cursor->notification_completion); + } @@ -135456,6 +139079,54 @@ index 000000000000..6b455774d973 + } +} + ++static void boot_irq_handler(struct hailo_pcie_board *board, struct hailo_pcie_interrupt_source *irq_source) ++{ ++ if (irq_source->sw_interrupts & HAILO_PCIE_BOOT_SOFT_RESET_IRQ) { ++ hailo_dbg(board, "soft reset trigger IRQ\n"); ++ complete(&board->soft_reset.reset_completed); ++ } ++ if (irq_source->sw_interrupts & HAILO_PCIE_BOOT_IRQ) { ++ hailo_dbg(board, "boot trigger IRQ\n"); ++ complete_all(&board->fw_boot.fw_loaded_completion); ++ } else { ++ board->fw_boot.boot_used_channel_bitmap &= ~irq_source->vdma_channels_bitmap; ++ hailo_dbg(board, "boot vDMA data IRQ - channel_bitmap = 0x%x\n", irq_source->vdma_channels_bitmap); ++ if (0 == board->fw_boot.boot_used_channel_bitmap) { ++ complete_all(&board->fw_boot.vdma_boot_completion); ++ hailo_dbg(board, "boot vDMA data trigger IRQ\n"); ++ } ++ } ++} ++ ++static void nnc_irq_handler(struct hailo_pcie_board *board, struct hailo_pcie_interrupt_source *irq_source) ++{ ++ if (irq_source->sw_interrupts & HAILO_PCIE_NNC_FW_CONTROL_IRQ) { ++ complete(&board->nnc.fw_control.completion); ++ } ++ ++ if (irq_source->sw_interrupts & HAILO_PCIE_NNC_DRIVER_DOWN_IRQ) { ++ complete(&board->driver_down.reset_completed); ++ } ++ ++ if (irq_source->sw_interrupts & HAILO_PCIE_NNC_FW_NOTIFICATION_IRQ) { ++ firmware_notification_irq_handler(board); ++ } ++} ++ ++static void soc_irq_handler(struct hailo_pcie_board *board, struct hailo_pcie_interrupt_source *irq_source) ++{ ++ if (irq_source->sw_interrupts & HAILO_PCIE_SOC_CONTROL_IRQ) { ++ complete_all(&board->soc.control_resp_ready); ++ } ++ ++ if (irq_source->sw_interrupts & HAILO_PCIE_SOC_CLOSE_IRQ) { ++ hailo_info(board, "soc_irq_handler - HAILO_PCIE_SOC_CLOSE_IRQ\n"); ++ // always use bitmap=0xFFFFFFFF - it is ok to wake all interrupts since each handler will check if the stream was aborted or not. ++ hailo_vdma_wakeup_interrupts(&board->vdma, &board->vdma.vdma_engines[DEFAULT_VDMA_ENGINE_INDEX], ++ 0xFFFFFFFF); ++ } ++} ++ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,22) +irqreturn_t hailo_irqhandler(int irq, void *dev_id, struct pt_regs *regs) +#else @@ -135482,202 +139153,32 @@ index 000000000000..6b455774d973 + + return_value = IRQ_HANDLED; + -+ // wake fw_control if needed -+ if (irq_source.interrupt_bitmask & FW_CONTROL) { -+ complete(&board->fw_control.completion); -+ } -+ -+ // wake driver_down if needed -+ if (irq_source.interrupt_bitmask & DRIVER_DOWN) { -+ complete(&board->driver_down.reset_completed); -+ } -+ -+ if (irq_source.interrupt_bitmask & FW_NOTIFICATION) { -+ if (!completion_done(&board->fw_loaded_completion)) { -+ // Complete firmware loaded completion -+ complete_all(&board->fw_loaded_completion); ++ if (board->fw_boot.is_in_boot) { ++ boot_irq_handler(board, &irq_source); ++ } else { ++ if (HAILO_ACCELERATOR_TYPE_NNC == board->pcie_resources.accelerator_type) { ++ nnc_irq_handler(board, &irq_source); ++ } else if (HAILO_ACCELERATOR_TYPE_SOC == board->pcie_resources.accelerator_type) { ++ soc_irq_handler(board, &irq_source); + } else { -+ firmware_notification_irq_handler(board); ++ hailo_err(board, "Invalid accelerator type %d\n", board->pcie_resources.accelerator_type); + } -+ } -+ -+ if (irq_source.interrupt_bitmask & SOC_CONNECT_ACCEPTED) { -+ complete_all(&board->soc_connect_accepted); -+ } + -+ if (0 != irq_source.vdma_channels_bitmap) { -+ hailo_vdma_irq_handler(&board->vdma, DEFAULT_VDMA_ENGINE_INDEX, -+ irq_source.vdma_channels_bitmap); ++ if (0 != irq_source.vdma_channels_bitmap) { ++ hailo_vdma_irq_handler(&board->vdma, DEFAULT_VDMA_ENGINE_INDEX, ++ irq_source.vdma_channels_bitmap); ++ } + } + } + + return return_value; +} + -+static long hailo_get_notification_wait_thread(struct hailo_pcie_board *pBoard, struct file *filp, -+ struct hailo_notification_wait **current_waiting_thread) -+{ -+ struct hailo_notification_wait *cursor = NULL; -+ // note: safe to access without rcu because the notification_wait_list is closed only on file release -+ list_for_each_entry(cursor, &pBoard->notification_wait_list, notification_wait_list) -+ { -+ if ((current->tgid == cursor->tgid) && (filp == cursor->filp)) { -+ *current_waiting_thread = cursor; -+ return 0; -+ } -+ } -+ -+ return -EFAULT; -+} -+ -+static long hailo_add_notification_wait(struct hailo_pcie_board *board, struct file *filp) -+{ -+ struct hailo_notification_wait *new_notification_wait = NULL; -+ if (!(new_notification_wait = kmalloc(sizeof(*new_notification_wait), GFP_KERNEL))) { -+ hailo_err(board, "Failed to allocate notification wait structure.\n"); -+ return -ENOMEM; -+ } -+ new_notification_wait->tgid = current->tgid; -+ new_notification_wait->filp = filp; -+ new_notification_wait->is_disabled = false; -+ init_completion(&new_notification_wait->notification_completion); -+ list_add_rcu(&new_notification_wait->notification_wait_list, &board->notification_wait_list); -+ return 0; -+} -+ -+static long hailo_read_notification_ioctl(struct hailo_pcie_board *pBoard, unsigned long arg, struct file *filp, -+ bool* should_up_board_mutex) -+{ -+ long err = 0; -+ struct hailo_notification_wait *current_waiting_thread = NULL; -+ struct hailo_d2h_notification *notification = &pBoard->notification_to_user; -+ unsigned long irq_saved_flags; -+ -+ err = hailo_get_notification_wait_thread(pBoard, filp, ¤t_waiting_thread); -+ if (0 != err) { -+ goto l_exit; -+ } -+ up(&pBoard->mutex); -+ -+ if (0 > (err = wait_for_completion_interruptible(¤t_waiting_thread->notification_completion))) { -+ hailo_info(pBoard, -+ "HAILO_READ_NOTIFICATION - wait_for_completion_interruptible error. err=%ld. tgid=%d (process was interrupted or killed)\n", -+ err, current_waiting_thread->tgid); -+ *should_up_board_mutex = false; -+ goto l_exit; -+ } -+ -+ if (down_interruptible(&pBoard->mutex)) { -+ hailo_info(pBoard, "HAILO_READ_NOTIFICATION - down_interruptible error (process was interrupted or killed)\n"); -+ *should_up_board_mutex = false; -+ err = -ERESTARTSYS; -+ goto l_exit; -+ } -+ -+ // Check if was disabled -+ if (current_waiting_thread->is_disabled) { -+ hailo_info(pBoard, "HAILO_READ_NOTIFICATION, can't find notification wait for tgid=%d\n", current->tgid); -+ err = -EINVAL; -+ goto l_exit; -+ } -+ -+ reinit_completion(¤t_waiting_thread->notification_completion); -+ -+ spin_lock_irqsave(&pBoard->notification_read_spinlock, irq_saved_flags); -+ notification->buffer_len = pBoard->notification_cache.buffer_len; -+ memcpy(notification->buffer, pBoard->notification_cache.buffer, notification->buffer_len); -+ spin_unlock_irqrestore(&pBoard->notification_read_spinlock, irq_saved_flags); -+ -+ if (copy_to_user((void __user*)arg, notification, sizeof(*notification))) { -+ hailo_err(pBoard, "HAILO_READ_NOTIFICATION copy_to_user fail\n"); -+ err = -ENOMEM; -+ goto l_exit; -+ } -+ -+l_exit: -+ return err; -+} -+ -+static long hailo_disable_notification(struct hailo_pcie_board *pBoard, struct file *filp) -+{ -+ struct hailo_notification_wait *cursor = NULL; -+ -+ hailo_info(pBoard, "HAILO_DISABLE_NOTIFICATION: disable notification"); -+ rcu_read_lock(); -+ list_for_each_entry_rcu(cursor, &pBoard->notification_wait_list, notification_wait_list) { -+ if ((current->tgid == cursor->tgid) && (filp == cursor->filp)) { -+ cursor->is_disabled = true; -+ complete(&cursor->notification_completion); -+ break; -+ } -+ } -+ rcu_read_unlock(); -+ -+ return 0; -+} -+ -+static int hailo_fw_control(struct hailo_pcie_board *pBoard, unsigned long arg, bool* should_up_board_mutex) -+{ -+ struct hailo_fw_control *command = &pBoard->fw_control.command; -+ long completion_result = 0; -+ int err = 0; -+ -+ up(&pBoard->mutex); -+ *should_up_board_mutex = false; -+ -+ if (down_interruptible(&pBoard->fw_control.mutex)) { -+ hailo_info(pBoard, "hailo_fw_control down_interruptible fail tgid:%d (process was interrupted or killed)\n", current->tgid); -+ return -ERESTARTSYS; -+ } -+ -+ if (copy_from_user(command, (void __user*)arg, sizeof(*command))) { -+ hailo_err(pBoard, "hailo_fw_control, copy_from_user fail\n"); -+ err = -ENOMEM; -+ goto l_exit; -+ } -+ -+ reinit_completion(&pBoard->fw_control.completion); -+ -+ err = hailo_pcie_write_firmware_control(&pBoard->pcie_resources, command); -+ if (err < 0) { -+ hailo_err(pBoard, "Failed writing fw control to pcie\n"); -+ goto l_exit; -+ } -+ -+ // Wait for response -+ completion_result = wait_for_completion_interruptible_timeout(&pBoard->fw_control.completion, msecs_to_jiffies(command->timeout_ms)); -+ if (completion_result <= 0) { -+ if (0 == completion_result) { -+ hailo_err(pBoard, "hailo_fw_control, timeout waiting for control (timeout_ms=%d)\n", command->timeout_ms); -+ err = -ETIMEDOUT; -+ } else { -+ hailo_info(pBoard, "hailo_fw_control, wait for completion failed with err=%ld (process was interrupted or killed)\n", completion_result); -+ err = -EINTR; -+ } -+ goto l_exit; -+ } -+ -+ err = hailo_pcie_read_firmware_control(&pBoard->pcie_resources, command); -+ if (err < 0) { -+ hailo_err(pBoard, "Failed reading fw control from pcie\n"); -+ goto l_exit; -+ } -+ -+ if (copy_to_user((void __user*)arg, command, sizeof(*command))) { -+ hailo_err(pBoard, "hailo_fw_control, copy_to_user fail\n"); -+ err = -ENOMEM; -+ goto l_exit; -+ } -+ -+l_exit: -+ up(&pBoard->fw_control.mutex); -+ return err; -+} -+ +static long hailo_query_device_properties(struct hailo_pcie_board *board, unsigned long arg) +{ + struct hailo_device_properties props = { + .desc_max_page_size = board->desc_max_page_size, ++ .board_type = board->pcie_resources.board_type, + .allocation_mode = board->allocation_mode, + .dma_type = HAILO_DMA_TYPE_PCIE, + .dma_engines_count = board->vdma.vdma_engines_count, @@ -135728,24 +139229,6 @@ index 000000000000..6b455774d973 + } +} + -+static long hailo_nnc_ioctl(struct hailo_pcie_board *board, unsigned int cmd, unsigned long arg, -+ struct file *filp, bool *should_up_board_mutex) -+{ -+ switch (cmd) { -+ case HAILO_FW_CONTROL: -+ return hailo_fw_control(board, arg, should_up_board_mutex); -+ case HAILO_READ_NOTIFICATION: -+ return hailo_read_notification_ioctl(board, arg, filp, should_up_board_mutex); -+ case HAILO_DISABLE_NOTIFICATION: -+ return hailo_disable_notification(board, filp); -+ case HAILO_READ_LOG: -+ return hailo_read_log_ioctl(board, arg); -+ default: -+ hailo_err(board, "Invalid nnc ioctl code 0x%x (nr: %d)\n", cmd, _IOC_NR(cmd)); -+ return -ENOTTY; -+ } -+} -+ +long hailo_pcie_fops_unlockedioctl(struct file* filp, unsigned int cmd, unsigned long arg) +{ + long err = 0; @@ -135804,7 +139287,7 @@ index 000000000000..6b455774d973 + hailo_err(board, "Ioctl %d is not supported on this accelerator type\n", _IOC_TYPE(cmd)); + err = -EINVAL; + } else { -+ err = hailo_soc_ioctl(board, &context->vdma_context, &board->vdma, cmd, arg); ++ err = hailo_soc_ioctl(board, context, &board->vdma, cmd, arg); + } + break; + case HAILO_NNC_IOCTL_MAGIC: @@ -135872,23 +139355,24 @@ index 000000000000..6b455774d973 +} diff --git a/drivers/media/pci/hailo/src/fops.h b/drivers/media/pci/hailo/src/fops.h new file mode 100644 -index 000000000000..a9ca67f4e3e3 +index 000000000000..9b940249b1a7 --- /dev/null +++ b/drivers/media/pci/hailo/src/fops.h -@@ -0,0 +1,22 @@ +@@ -0,0 +1,23 @@ +// SPDX-License-Identifier: GPL-2.0 +/** -+ * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved. ++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. + **/ + +#ifndef _HAILO_PCI_FOPS_H_ +#define _HAILO_PCI_FOPS_H_ + ++#include "pcie.h" ++ +int hailo_pcie_fops_open(struct inode* inode, struct file* filp); +int hailo_pcie_fops_release(struct inode* inode, struct file* filp); +long hailo_pcie_fops_unlockedioctl(struct file* filp, unsigned int cmd, unsigned long arg); +int hailo_pcie_fops_mmap(struct file* filp, struct vm_area_struct *vma); -+int hailo_pcie_driver_down(struct hailo_pcie_board *board); +void hailo_pcie_ep_init(struct hailo_pcie_board *board); + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,22) @@ -135898,203 +139382,350 @@ index 000000000000..a9ca67f4e3e3 +#endif + +#endif /* _HAILO_PCI_FOPS_H_ */ -diff --git a/drivers/media/pci/hailo/src/pci_soc_ioctl.c b/drivers/media/pci/hailo/src/pci_soc_ioctl.c -new file mode 100755 -index 000000000000..74db9b4403f6 +diff --git a/drivers/media/pci/hailo/src/nnc.c b/drivers/media/pci/hailo/src/nnc.c +new file mode 100644 +index 000000000000..10faafcb415f --- /dev/null -+++ b/drivers/media/pci/hailo/src/pci_soc_ioctl.c -@@ -0,0 +1,155 @@ ++++ b/drivers/media/pci/hailo/src/nnc.c +@@ -0,0 +1,299 @@ +// SPDX-License-Identifier: GPL-2.0 +/** + * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. + **/ -+#include "pci_soc_ioctl.h" ++/** ++ * A Hailo PCIe NNC device is a device contains a NNC (neural network core) and some basic FW. ++ * The device supports sending controls, receiving notification and reading the FW log. ++ */ ++ ++#include "nnc.h" ++#include "hailo_ioctl_common.h" + -+#include "utils.h" -+#include "vdma_common.h" +#include "utils/logs.h" -+#include "vdma/memory.h" ++#include "utils/compact.h" + -+#define PCI_SOC_VDMA_ENGINE_INDEX (0) -+#define PCI_SOC_WAIT_FOR_CONNECT_TIMEOUT_MS (10000) ++#include + -+long hailo_soc_ioctl(struct hailo_pcie_board *board, struct hailo_vdma_file_context *context, -+ struct hailo_vdma_controller *controller, unsigned int cmd, unsigned long arg) ++#if !defined(HAILO_EMULATOR) ++#define DEFAULT_SHUTDOWN_TIMEOUT_MS (5) ++#else /* !defined(HAILO_EMULATOR) */ ++#define DEFAULT_SHUTDOWN_TIMEOUT_MS (1000) ++#endif /* !defined(HAILO_EMULATOR) */ ++ ++void hailo_nnc_init(struct hailo_pcie_nnc *nnc) +{ -+ switch (cmd) { -+ case HAILO_SOC_CONNECT: -+ return hailo_soc_connect_ioctl(board, context, controller, arg); -+ case HAILO_SOC_CLOSE: -+ return hailo_soc_close_ioctl(board, controller, arg); -+ default: -+ hailo_err(board, "Invalid pcie EP ioctl code 0x%x (nr: %d)\n", cmd, _IOC_NR(cmd)); -+ return -ENOTTY; ++ sema_init(&nnc->fw_control.mutex, 1); ++ spin_lock_init(&nnc->notification_read_spinlock); ++ init_completion(&nnc->fw_control.completion); ++ INIT_LIST_HEAD(&nnc->notification_wait_list); ++ memset(&nnc->notification_cache, 0, sizeof(nnc->notification_cache)); ++} ++ ++void hailo_nnc_finalize(struct hailo_pcie_nnc *nnc) ++{ ++ struct hailo_notification_wait *cursor = NULL; ++ ++ // Lock rcu_read_lock and send notification_completion to wake anyone waiting on the notification_wait_list when removed ++ rcu_read_lock(); ++ list_for_each_entry_rcu(cursor, &nnc->notification_wait_list, notification_wait_list) { ++ cursor->is_disabled = true; ++ complete(&cursor->notification_completion); + } ++ rcu_read_unlock(); +} + -+long hailo_soc_connect_ioctl(struct hailo_pcie_board *board, struct hailo_vdma_file_context *context, -+ struct hailo_vdma_controller *controller, unsigned long arg) ++static int hailo_fw_control(struct hailo_pcie_board *board, unsigned long arg, bool* should_up_board_mutex) +{ -+ struct hailo_soc_connect_params params; -+ struct hailo_vdma_channel *input_channel = NULL; -+ struct hailo_vdma_channel *output_channel = NULL; -+ struct hailo_vdma_engine *vdma_engine = NULL; -+ struct hailo_descriptors_list_buffer *input_descriptors_buffer = NULL; -+ struct hailo_descriptors_list_buffer *output_descriptors_buffer = NULL; -+ uint8_t depth = 0; -+ int err = 0; ++ struct hailo_fw_control *command = &board->nnc.fw_control.command; + long completion_result = 0; ++ int err = 0; + -+ if (copy_from_user(¶ms, (void *)arg, sizeof(params))) { -+ hailo_err(board, "copy_from_user fail\n"); -+ return -ENOMEM; ++ up(&board->mutex); ++ *should_up_board_mutex = false; ++ ++ if (down_interruptible(&board->nnc.fw_control.mutex)) { ++ hailo_info(board, "hailo_fw_control down_interruptible fail tgid:%d (process was interrupted or killed)\n", current->tgid); ++ return -ERESTARTSYS; ++ } ++ ++ if (copy_from_user(command, (void __user*)arg, sizeof(*command))) { ++ hailo_err(board, "hailo_fw_control, copy_from_user fail\n"); ++ err = -ENOMEM; ++ goto l_exit; + } + -+ // TODO: have pci_ep choose the channel indexes the soc will use - for now use 0 and 16 -+ params.input_channel_index = 0; -+ params.output_channel_index = 16; ++ reinit_completion(&board->nnc.fw_control.completion); + -+ reinit_completion(&board->soc_connect_accepted); -+ hailo_soc_write_soc_connect(&board->pcie_resources); ++ err = hailo_pcie_write_firmware_control(&board->pcie_resources, command); ++ if (err < 0) { ++ hailo_err(board, "Failed writing fw control to pcie\n"); ++ goto l_exit; ++ } + -+ // Wait for completion -+ completion_result = wait_for_completion_interruptible_timeout(&board->soc_connect_accepted, -+ msecs_to_jiffies(PCI_SOC_WAIT_FOR_CONNECT_TIMEOUT_MS)); -+ if (0 > completion_result) { ++ // Wait for response ++ completion_result = wait_for_completion_interruptible_timeout(&board->nnc.fw_control.completion, msecs_to_jiffies(command->timeout_ms)); ++ if (completion_result <= 0) { + if (0 == completion_result) { -+ hailo_err(board, "Timeout waiting for connect to be accepted (timeout_ms=%d)\n", PCI_SOC_WAIT_FOR_CONNECT_TIMEOUT_MS); -+ return -ETIMEDOUT; ++ hailo_err(board, "hailo_fw_control, timeout waiting for control (timeout_ms=%d)\n", command->timeout_ms); ++ err = -ETIMEDOUT; + } else { -+ hailo_info(board, "soc connect failed with err=%ld (process was interrupted or killed)\n", -+ completion_result); -+ return -EINTR; ++ hailo_info(board, "hailo_fw_control, wait for completion failed with err=%ld (process was interrupted or killed)\n", completion_result); ++ err = -EINTR; + } ++ goto l_exit; + } + -+ vdma_engine = &controller->vdma_engines[PCI_SOC_VDMA_ENGINE_INDEX]; -+ input_channel = &vdma_engine->channels[params.input_channel_index]; -+ output_channel = &vdma_engine->channels[params.output_channel_index]; ++ err = hailo_pcie_read_firmware_control(&board->pcie_resources, command); ++ if (err < 0) { ++ hailo_err(board, "Failed reading fw control from pcie\n"); ++ goto l_exit; ++ } + -+ input_descriptors_buffer = hailo_vdma_find_descriptors_buffer(context, params.input_desc_handle); -+ output_descriptors_buffer = hailo_vdma_find_descriptors_buffer(context, params.output_desc_handle); -+ if (NULL == input_descriptors_buffer || NULL == output_descriptors_buffer) { -+ hailo_dev_err(&board->pDev->dev, "input / output descriptors buffer not found \n"); -+ return -EINVAL; ++ if (copy_to_user((void __user*)arg, command, sizeof(*command))) { ++ hailo_err(board, "hailo_fw_control, copy_to_user fail\n"); ++ err = -ENOMEM; ++ goto l_exit; + } + -+ // Make sure channels that we are accepting are not already enabled -+ if (0 != (vdma_engine->enabled_channels & params.input_channel_index) || -+ 0 != (vdma_engine->enabled_channels & params.output_channel_index)) { -+ hailo_dev_err(&board->pDev->dev, "Trying to accept already enabled channels\n"); -+ return -EINVAL; ++l_exit: ++ up(&board->nnc.fw_control.mutex); ++ return err; ++} ++ ++static long hailo_get_notification_wait_thread(struct hailo_pcie_board *board, struct file *filp, ++ struct hailo_notification_wait **current_waiting_thread) ++{ ++ struct hailo_notification_wait *cursor = NULL; ++ // note: safe to access without rcu because the notification_wait_list is closed only on file release ++ list_for_each_entry(cursor, &board->nnc.notification_wait_list, notification_wait_list) ++ { ++ if ((current->tgid == cursor->tgid) && (filp == cursor->filp)) { ++ *current_waiting_thread = cursor; ++ return 0; ++ } + } + -+ if (!is_powerof2((size_t)input_descriptors_buffer->desc_list.desc_count) || -+ !is_powerof2((size_t)output_descriptors_buffer->desc_list.desc_count)) { -+ hailo_dev_err(&board->pDev->dev, "Invalid desc list size\n"); -+ return -EINVAL; ++ return -EFAULT; ++} ++ ++static long hailo_read_notification_ioctl(struct hailo_pcie_board *board, unsigned long arg, struct file *filp, ++ bool* should_up_board_mutex) ++{ ++ long err = 0; ++ struct hailo_notification_wait *current_waiting_thread = NULL; ++ struct hailo_d2h_notification *notification = &board->nnc.notification_to_user; ++ unsigned long irq_saved_flags; ++ ++ err = hailo_get_notification_wait_thread(board, filp, ¤t_waiting_thread); ++ if (0 != err) { ++ goto l_exit; + } ++ up(&board->mutex); + -+ // configure and start input channel -+ depth = ceil_log2(input_descriptors_buffer->desc_list.desc_count); -+ // DMA Direction is only to get channel index - so -+ err = hailo_vdma_start_channel(input_channel->host_regs, input_descriptors_buffer->dma_address, depth, -+ board->vdma.hw->ddr_data_id); -+ if (err < 0) { -+ hailo_dev_err(&board->pDev->dev, "Error starting vdma input channel index %u\n", params.input_channel_index); -+ return -EINVAL; ++ if (0 > (err = wait_for_completion_interruptible(¤t_waiting_thread->notification_completion))) { ++ hailo_info(board, ++ "HAILO_READ_NOTIFICATION - wait_for_completion_interruptible error. err=%ld. tgid=%d (process was interrupted or killed)\n", ++ err, current_waiting_thread->tgid); ++ *should_up_board_mutex = false; ++ goto l_exit; + } -+ -+ // configure and start output channel -+ depth = ceil_log2(output_descriptors_buffer->desc_list.desc_count); -+ // DMA Direction is only to get channel index - so -+ err = hailo_vdma_start_channel(output_channel->host_regs, output_descriptors_buffer->dma_address, depth, -+ board->vdma.hw->ddr_data_id); -+ if (err < 0) { -+ hailo_dev_err(&board->pDev->dev, "Error starting vdma output channel index %u\n", params.output_channel_index); -+ // Close input channel -+ hailo_vdma_stop_channel(input_channel->host_regs); -+ return -EINVAL; ++ ++ if (down_interruptible(&board->mutex)) { ++ hailo_info(board, "HAILO_READ_NOTIFICATION - down_interruptible error (process was interrupted or killed)\n"); ++ *should_up_board_mutex = false; ++ err = -ERESTARTSYS; ++ goto l_exit; + } + -+ if (copy_to_user((void *)arg, ¶ms, sizeof(params))) { -+ hailo_dev_err(&board->pDev->dev, "copy_to_user fail\n"); -+ return -ENOMEM; ++ // Check if was disabled ++ if (current_waiting_thread->is_disabled) { ++ hailo_info(board, "HAILO_READ_NOTIFICATION - notification disabled for tgid=%d\n", current->tgid); ++ err = -ECANCELED; ++ goto l_exit; ++ } ++ ++ reinit_completion(¤t_waiting_thread->notification_completion); ++ ++ spin_lock_irqsave(&board->nnc.notification_read_spinlock, irq_saved_flags); ++ notification->buffer_len = board->nnc.notification_cache.buffer_len; ++ memcpy(notification->buffer, board->nnc.notification_cache.buffer, notification->buffer_len); ++ spin_unlock_irqrestore(&board->nnc.notification_read_spinlock, irq_saved_flags); ++ ++ if (copy_to_user((void __user*)arg, notification, sizeof(*notification))) { ++ hailo_err(board, "HAILO_READ_NOTIFICATION copy_to_user fail\n"); ++ err = -ENOMEM; ++ goto l_exit; + } + ++l_exit: ++ return err; ++} ++ ++static long hailo_disable_notification(struct hailo_pcie_board *board, struct file *filp) ++{ ++ struct hailo_notification_wait *cursor = NULL; ++ ++ hailo_info(board, "HAILO_DISABLE_NOTIFICATION: disable notification"); ++ rcu_read_lock(); ++ list_for_each_entry_rcu(cursor, &board->nnc.notification_wait_list, notification_wait_list) { ++ if ((current->tgid == cursor->tgid) && (filp == cursor->filp)) { ++ cursor->is_disabled = true; ++ complete(&cursor->notification_completion); ++ break; ++ } ++ } ++ rcu_read_unlock(); ++ + return 0; +} + -+long hailo_soc_close_ioctl(struct hailo_pcie_board *board, struct hailo_vdma_controller *controller, unsigned long arg) ++static long hailo_read_log_ioctl(struct hailo_pcie_board *board, unsigned long arg) +{ -+ struct hailo_soc_close_params params; -+ struct hailo_vdma_channel *input_channel = NULL; -+ struct hailo_vdma_channel *output_channel = NULL; -+ struct hailo_vdma_engine *vdma_engine = NULL; ++ long err = 0; ++ struct hailo_read_log_params params; + -+ if (copy_from_user(¶ms, (void *)arg, sizeof(params))) { -+ hailo_dev_err(&board->pDev->dev, "copy_from_user fail\n"); ++ if (copy_from_user(¶ms, (void __user*)arg, sizeof(params))) { ++ hailo_err(board, "HAILO_READ_LOG, copy_from_user fail\n"); + return -ENOMEM; + } + -+ vdma_engine = &controller->vdma_engines[PCI_SOC_VDMA_ENGINE_INDEX]; ++ if (0 > (err = hailo_pcie_read_firmware_log(&board->pcie_resources.fw_access, ¶ms))) { ++ hailo_err(board, "HAILO_READ_LOG, reading from log failed with error: %ld \n", err); ++ return err; ++ } + -+ if (!hailo_check_channel_index(params.input_channel_index, controller->hw->src_channels_bitmask, true)) { -+ hailo_dev_err(&board->pDev->dev, "Invalid input channel index %u\n", params.input_channel_index); -+ return -EINVAL; ++ if (copy_to_user((void*)arg, ¶ms, sizeof(params))) { ++ return -ENOMEM; + } + -+ if (!hailo_check_channel_index(params.output_channel_index, controller->hw->src_channels_bitmask, false)) { -+ hailo_dev_err(&board->pDev->dev, "Invalid output channel index %u\n", params.output_channel_index); -+ return -EINVAL; ++ return 0; ++} ++ ++long hailo_nnc_ioctl(struct hailo_pcie_board *board, unsigned int cmd, unsigned long arg, ++ struct file *filp, bool *should_up_board_mutex) ++{ ++ switch (cmd) { ++ case HAILO_FW_CONTROL: ++ return hailo_fw_control(board, arg, should_up_board_mutex); ++ case HAILO_READ_NOTIFICATION: ++ return hailo_read_notification_ioctl(board, arg, filp, should_up_board_mutex); ++ case HAILO_DISABLE_NOTIFICATION: ++ return hailo_disable_notification(board, filp); ++ case HAILO_READ_LOG: ++ return hailo_read_log_ioctl(board, arg); ++ default: ++ hailo_err(board, "Invalid nnc ioctl code 0x%x (nr: %d)\n", cmd, _IOC_NR(cmd)); ++ return -ENOTTY; + } ++} + -+ input_channel = &vdma_engine->channels[params.input_channel_index]; -+ output_channel = &vdma_engine->channels[params.output_channel_index]; + -+ // Close channels -+ hailo_vdma_stop_channel(input_channel->host_regs); -+ hailo_vdma_stop_channel(output_channel->host_regs); ++static int add_notification_wait(struct hailo_pcie_board *board, struct file *filp) ++{ ++ struct hailo_notification_wait *wait = kmalloc(sizeof(*wait), GFP_KERNEL); ++ if (!wait) { ++ hailo_err(board, "Failed to allocate notification wait structure.\n"); ++ return -ENOMEM; ++ } ++ wait->tgid = current->tgid; ++ wait->filp = filp; ++ wait->is_disabled = false; ++ init_completion(&wait->notification_completion); ++ list_add_rcu(&wait->notification_wait_list, &board->nnc.notification_wait_list); ++ return 0; ++} ++ ++int hailo_nnc_file_context_init(struct hailo_pcie_board *board, struct hailo_file_context *context) ++{ ++ return add_notification_wait(board, context->filp); ++} ++ ++static void clear_notification_wait_list(struct hailo_pcie_board *board, struct file *filp) ++{ ++ struct hailo_notification_wait *cur = NULL, *next = NULL; ++ list_for_each_entry_safe(cur, next, &board->nnc.notification_wait_list, notification_wait_list) { ++ if (cur->filp == filp) { ++ list_del_rcu(&cur->notification_wait_list); ++ synchronize_rcu(); ++ kfree(cur); ++ } ++ } ++} ++ ++int hailo_nnc_driver_down(struct hailo_pcie_board *board) ++{ ++ long completion_result = 0; ++ int err = 0; ++ ++ reinit_completion(&board->driver_down.reset_completed); + + hailo_pcie_write_firmware_driver_shutdown(&board->pcie_resources); -+ return 0; ++ ++ // Wait for response ++ completion_result = ++ wait_for_completion_timeout(&board->driver_down.reset_completed, msecs_to_jiffies(DEFAULT_SHUTDOWN_TIMEOUT_MS)); ++ if (completion_result <= 0) { ++ if (0 == completion_result) { ++ hailo_err(board, "hailo_nnc_driver_down, timeout waiting for shutdown response (timeout_ms=%d)\n", DEFAULT_SHUTDOWN_TIMEOUT_MS); ++ err = -ETIMEDOUT; ++ } else { ++ hailo_info(board, "hailo_nnc_driver_down, wait for completion failed with err=%ld (process was interrupted or killed)\n", ++ completion_result); ++ err = completion_result; ++ } ++ goto l_exit; ++ } ++ ++l_exit: ++ return err; ++} ++ ++void hailo_nnc_file_context_finalize(struct hailo_pcie_board *board, struct hailo_file_context *context) ++{ ++ clear_notification_wait_list(board, context->filp); ++ ++ if (context->filp == board->vdma.used_by_filp) { ++ hailo_nnc_driver_down(board); ++ } +} \ No newline at end of file -diff --git a/drivers/media/pci/hailo/src/pci_soc_ioctl.h b/drivers/media/pci/hailo/src/pci_soc_ioctl.h -new file mode 100755 -index 000000000000..474fda9fd332 +diff --git a/drivers/media/pci/hailo/src/nnc.h b/drivers/media/pci/hailo/src/nnc.h +new file mode 100644 +index 000000000000..1bfac99202f1 --- /dev/null -+++ b/drivers/media/pci/hailo/src/pci_soc_ioctl.h -@@ -0,0 +1,19 @@ ++++ b/drivers/media/pci/hailo/src/nnc.h +@@ -0,0 +1,22 @@ +// SPDX-License-Identifier: GPL-2.0 +/** + * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. + **/ + -+#ifndef _HAILO_PCI_SOC_IOCTL_H_ -+#define _HAILO_PCI_SOC_IOCTL_H_ ++#ifndef _HAILO_PCI_NNC_H_ ++#define _HAILO_PCI_NNC_H_ + -+#include "vdma/ioctl.h" +#include "pcie.h" + ++void hailo_nnc_init(struct hailo_pcie_nnc *nnc); ++void hailo_nnc_finalize(struct hailo_pcie_nnc *nnc); + -+long hailo_soc_ioctl(struct hailo_pcie_board *board, struct hailo_vdma_file_context *context, -+ struct hailo_vdma_controller *controller, unsigned int cmd, unsigned long arg); -+long hailo_soc_connect_ioctl(struct hailo_pcie_board *board, struct hailo_vdma_file_context *context, -+ struct hailo_vdma_controller *controller, unsigned long arg); -+long hailo_soc_close_ioctl(struct hailo_pcie_board *board, struct hailo_vdma_controller *controller, unsigned long arg); ++long hailo_nnc_ioctl(struct hailo_pcie_board *board, unsigned int cmd, unsigned long arg, ++ struct file *filp, bool *should_up_board_mutex); + -+#endif // _HAILO_PCI_SOC_IOCTL_H_ ++int hailo_nnc_file_context_init(struct hailo_pcie_board *board, struct hailo_file_context *context); ++void hailo_nnc_file_context_finalize(struct hailo_pcie_board *board, struct hailo_file_context *context); ++ ++int hailo_nnc_driver_down(struct hailo_pcie_board *board); ++ ++#endif /* _HAILO_PCI_NNC_H_ */ \ No newline at end of file diff --git a/drivers/media/pci/hailo/src/pcie.c b/drivers/media/pci/hailo/src/pcie.c new file mode 100644 -index 000000000000..170ae819bb0f +index 000000000000..cbac1e5787a8 --- /dev/null +++ b/drivers/media/pci/hailo/src/pcie.c -@@ -0,0 +1,1095 @@ +@@ -0,0 +1,1563 @@ +// SPDX-License-Identifier: GPL-2.0 +/** -+ * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved. ++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. + **/ + +#include @@ -136107,6 +139738,7 @@ index 000000000000..170ae819bb0f +#include +#include +#include ++#include + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 16, 0) +#include @@ -136116,11 +139748,14 @@ index 000000000000..170ae819bb0f + +#include "hailo_ioctl_common.h" +#include "pcie.h" ++#include "nnc.h" ++#include "soc.h" +#include "fops.h" +#include "sysfs.h" +#include "utils/logs.h" +#include "utils/compact.h" +#include "vdma/vdma.h" ++#include "vdma/memory.h" + +#if LINUX_VERSION_CODE < KERNEL_VERSION( 5, 4, 0 ) +#include @@ -136134,11 +139769,13 @@ index 000000000000..170ae819bb0f + HAILO_FORCE_BUFFER_FROM_DRIVER = 2, +}; + -+//Debug flag ++// Debug flag +static int force_desc_page_size = 0; +static bool g_is_power_mode_enabled = true; +static int force_allocation_from_driver = HAILO_NO_FORCE_BUFFER; -+static bool force_hailo15_legacy_mode = false; ++static bool force_hailo10h_legacy_mode = false; ++static bool force_boot_linux_from_eemc = false; ++static bool support_soft_reset = true; + +#define DEVICE_NODE_NAME "hailo" +static int char_major = 0; @@ -136300,7 +139937,7 @@ index 000000000000..170ae819bb0f + /* Double-check ASPM control. If not disabled by the above, the + * BIOS is preventing that from happening (or CONFIG_PCIEASPM is + * not enabled); override by writing PCI config space directly. -+ */ ++ */ + err = pcie_capability_read_word(pdev, PCI_EXP_LNKCTL, &pdev_aspmc); + if (err < 0) { + hailo_err(board, "Couldn't read LNKCTL capability\n"); @@ -136382,182 +140019,638 @@ index 000000000000..170ae819bb0f + up(&g_hailo_add_board_mutex); +} + -+static int hailo_write_config(struct hailo_pcie_resources *resources, struct device *dev, -+ const struct hailo_config_constants *config_consts) ++/** ++ * Wait until the relevant completion is done. ++ * ++ * @param completion - pointer to the completion struct to wait for. ++ * @param msecs - the amount of time to wait in milliseconds. ++ * @return false if timed out, true if completed. ++ */ ++static bool wait_for_firmware_completion(struct completion *completion, unsigned int msecs) +{ -+ const struct firmware *config = NULL; -+ int err = 0; ++ return (0 != wait_for_completion_timeout(completion, msecs_to_jiffies(msecs))); ++} + -+ if (NULL == config_consts->filename) { -+ // Config not supported for platform -+ return 0; ++/** ++ * Program one FW file descriptors to the vDMA engine. ++ * ++ * @param dev - pointer to the device struct we are working on. ++ * @param boot_dma_state - pointer to the boot dma state struct which includes all of the boot resources. ++ * @param file_address - the address of the file in the device memory. ++ * @param transfer_buffer - the buffer to program to the vDMA engine. ++ * @param channel_index - the index of the channel to program. ++ * @param filename - the name of the file to program. ++ * @param raise_int_on_completion - true if this is the last descriptors chunk in the specific channel in the boot flow, false otherwise. If true - will enable ++ * an IRQ for the relevant channel when the transfer is finished. ++ * @return the amount of descriptors programmed on success, negative error code on failure. ++ */ ++static int pcie_vdma_program_one_file_descriptors(struct device *dev, struct hailo_pcie_boot_dma_channel_state *boot_channel_state, ++ u32 file_address, struct hailo_vdma_mapped_transfer_buffer transfer_buffer, u8 channel_index, const char *filename, bool raise_int_on_completion) ++{ ++ int device_desc = 0, host_desc = 0; ++ enum hailo_vdma_interrupts_domain interrupts_domain = raise_int_on_completion ? HAILO_VDMA_INTERRUPTS_DOMAIN_HOST : ++ HAILO_VDMA_INTERRUPTS_DOMAIN_NONE; ++ ++ hailo_dev_dbg(dev, "channel_index = %d, file_name = %s, file_address = 0x%x, transfer_buffer.offset = 0x%x,\ ++ size_to_program = 0x%x, starting_desc/desc_index = 0x%x\n", channel_index, filename, file_address, ++ transfer_buffer.offset, transfer_buffer.size, boot_channel_state->desc_program_num); ++ ++ // program descriptors ++ device_desc = hailo_vdma_program_descriptors_in_chunk(&hailo_pcie_vdma_hw, file_address, transfer_buffer.size, ++ &boot_channel_state->device_descriptors_buffer.desc_list, boot_channel_state->desc_program_num, ++ (boot_channel_state->device_descriptors_buffer.desc_list.desc_count - 1), channel_index, HAILO_PCI_EP_HOST_DMA_DATA_ID); ++ if (device_desc < 0) { ++ hailo_dev_err(dev, "Failed to program device descriptors, error = %u\n", device_desc); ++ return device_desc; + } + -+ err = request_firmware_direct(&config, config_consts->filename, dev); -+ if (err < 0) { -+ hailo_dev_info(dev, "Config %s not found\n", config_consts->filename); -+ return 0; ++ host_desc = hailo_vdma_program_descriptors_list(&hailo_pcie_vdma_hw, &boot_channel_state->host_descriptors_buffer.desc_list, ++ boot_channel_state->desc_program_num, &transfer_buffer, true, channel_index, interrupts_domain, false); ++ if (host_desc < 0) { ++ hailo_dev_err(dev, "Failed to program host descriptors, error = %u\n", host_desc); ++ return host_desc; ++ } ++ ++ // checks that same amount of decsriptors were programmed on device side and host side ++ if (host_desc != device_desc) { ++ hailo_dev_err(dev, "Host and device descriptors should be the same\n"); ++ return -EINVAL; + } + -+ hailo_dev_notice(dev, "Writing config %s\n", config_consts->filename); ++ return host_desc; ++} + -+ err = hailo_pcie_write_config_common(resources, config->data, config->size, config_consts); ++/** ++ * Program one FW file to the vDMA engine. ++ * ++ * @param board - pointer to the board struct we are working on. ++ * @param boot_dma_state - pointer to the boot dma state struct which includes all of the boot resources. ++ * @param file_address - the address of the file in the device memory. ++ * @param filename - the name of the file to program. ++ * @param raise_int_on_completion - true if this is the last file in the boot flow, false otherwise. uses to enable an IRQ for the ++ * relevant channel when the transfer is finished. ++ * @return 0 on success, negative error code on failure. at the end of the function the firmware is released. ++ */ ++static int pcie_vdma_program_one_file(struct hailo_pcie_board *board, struct hailo_pcie_boot_dma_state *boot_dma_state, u32 file_address, ++ const char *filename, bool raise_int_on_completion) ++{ ++ const struct firmware *firmware = NULL; ++ struct hailo_vdma_mapped_transfer_buffer transfer_buffer = {0}; ++ int desc_programmed = 0; ++ int err = 0; ++ size_t bytes_copied = 0, remaining_size = 0, data_offset = 0, desc_num_left = 0, current_desc_to_program = 0; ++ ++ hailo_notice(board, "Programing file %s for dma transfer\n", filename); ++ ++ // load firmware directly without usermode helper for the relevant file ++ err = request_firmware_direct(&firmware, filename, board->vdma.dev); + if (err < 0) { -+ if (-EINVAL == err) { -+ hailo_dev_warn(dev, "Config size %zu is bigger than max %zu\n", config->size, config_consts->max_size); -+ } -+ release_firmware(config); ++ hailo_err(board, "Failed to allocate memory for file %s\n", filename); + return err; + } + -+ release_firmware(config); ++ // set the remaining size as the whole file size to begin with ++ remaining_size = firmware->size; ++ ++ while (remaining_size > 0) { ++ struct hailo_pcie_boot_dma_channel_state *channel = &boot_dma_state->channels[boot_dma_state->curr_channel_index]; ++ bool is_last_desc_chunk_of_curr_channel = false; ++ bool rais_interrupt_on_last_chunk = false; ++ ++ hailo_dbg(board, "desc_program_num = 0x%x, desc_page_size = 0x%x, on channel = %d\n", ++ channel->desc_program_num, HAILO_PCI_OVER_VDMA_PAGE_SIZE, boot_dma_state->curr_channel_index); ++ ++ // increment the channel index if the current channel is full ++ if ((MAX_SG_DESCS_COUNT - 1) == channel->desc_program_num) { ++ boot_dma_state->curr_channel_index++; ++ channel = &boot_dma_state->channels[boot_dma_state->curr_channel_index]; ++ board->fw_boot.boot_used_channel_bitmap |= (1 << boot_dma_state->curr_channel_index); ++ } ++ ++ // calculate the number of descriptors left to program and the number of bytes left to program ++ desc_num_left = (MAX_SG_DESCS_COUNT - 1) - channel->desc_program_num; ++ ++ // prepare the transfer buffer to make sure all the fields are initialized ++ transfer_buffer.sg_table = &channel->sg_table; ++ transfer_buffer.size = min(remaining_size, (desc_num_left * HAILO_PCI_OVER_VDMA_PAGE_SIZE)); ++ // no need to check for overflow since the variables are constant and always desc_program_num <= max u16 (65536) ++ // & the buffer max size is 256 Mb << 4G (max u32) ++ transfer_buffer.offset = (channel->desc_program_num * HAILO_PCI_OVER_VDMA_PAGE_SIZE); ++ ++ // check if this is the last descriptor chunk to program in the whole boot flow ++ current_desc_to_program = (transfer_buffer.size / HAILO_PCI_OVER_VDMA_PAGE_SIZE); ++ is_last_desc_chunk_of_curr_channel = ((MAX_SG_DESCS_COUNT - 1) == ++ (current_desc_to_program + channel->desc_program_num)); ++ rais_interrupt_on_last_chunk = (is_last_desc_chunk_of_curr_channel || (raise_int_on_completion && ++ (remaining_size == transfer_buffer.size))); ++ ++ // try to copy the file to the buffer, if failed, release the firmware and return ++ bytes_copied = sg_pcopy_from_buffer(transfer_buffer.sg_table->sgl, transfer_buffer.sg_table->orig_nents, ++ &firmware->data[data_offset], transfer_buffer.size, transfer_buffer.offset); ++ if (transfer_buffer.size != bytes_copied) { ++ hailo_err(board, "There is not enough memory allocated to copy file %s\n", filename); ++ release_firmware(firmware); ++ return -EFBIG; ++ } ++ ++ // program the descriptors ++ desc_programmed = pcie_vdma_program_one_file_descriptors(&board->pDev->dev, channel, (file_address + data_offset), ++ transfer_buffer, boot_dma_state->curr_channel_index, filename, rais_interrupt_on_last_chunk); ++ if (desc_programmed < 0) { ++ hailo_err(board, "Failed to program descriptors for file %s, on cahnnel = %d\n", filename, ++ boot_dma_state->curr_channel_index); ++ release_firmware(firmware); ++ return desc_programmed; ++ } ++ ++ // Update remaining size, data_offset and desc_program_num for the next iteration ++ remaining_size -= transfer_buffer.size; ++ data_offset += transfer_buffer.size; ++ channel->desc_program_num += desc_programmed; ++ } ++ ++ hailo_notice(board, "File %s programed successfully\n", filename); ++ ++ release_firmware(firmware); ++ ++ return desc_programmed; ++} ++ ++/** ++ * Program the entire batch of firmware files to the vDMA engine. ++ * ++ * @param board - pointer to the board struct we are working on. ++ * @param boot_dma_state - pointer to the boot dma state struct which includes all of the boot resources. ++ * @param resources - pointer to the hailo_pcie_resources struct. ++ * @param stage - the stage to program. ++ * @return 0 on success, negative error code on failure. ++ */ ++static long pcie_vdma_program_entire_batch(struct hailo_pcie_board *board, struct hailo_pcie_boot_dma_state *boot_dma_state, ++ struct hailo_pcie_resources *resources, u32 stage) ++{ ++ long err = 0; ++ int file_index = 0; ++ const struct hailo_pcie_loading_stage *stage_info = hailo_pcie_get_loading_stage_info(resources->board_type, stage); ++ const struct hailo_file_batch *files_batch = stage_info->batch; ++ const u8 amount_of_files = stage_info->amount_of_files_in_stage; ++ const char *filename = NULL; ++ u32 file_address = 0; ++ ++ for (file_index = 0; file_index < amount_of_files; file_index++) ++ { ++ filename = files_batch[file_index].filename; ++ file_address = files_batch[file_index].address; ++ ++ if (NULL == filename) { ++ hailo_err(board, "The amount of files wasn't specified for stage %d\n", stage); ++ break; ++ } ++ ++ err = pcie_vdma_program_one_file(board, boot_dma_state, file_address, filename, ++ (file_index == (amount_of_files - 1))); ++ if (err < 0) { ++ hailo_err(board, "Failed to program file %s\n", filename); ++ return err; ++ } ++ } ++ + return 0; +} + -+static bool wait_for_firmware_completion(struct completion *fw_load_completion) ++/** ++ * Release noncontinuous memory (virtual continuous memory). (sg table and kernel_addrs) ++ * ++ * @param dev - pointer to the device struct we are working on. ++ * @param sg_table - the sg table to release. ++ * @param kernel_addrs - the kernel address to release. ++ */ ++static void pcie_vdma_release_noncontinuous_memory(struct device *dev, struct sg_table *sg_table, void *kernel_addrs) +{ -+ return (0 != wait_for_completion_timeout(fw_load_completion, msecs_to_jiffies(FIRMWARE_WAIT_TIMEOUT_MS))); ++ dma_unmap_sg(dev, sg_table->sgl, sg_table->orig_nents, DMA_TO_DEVICE); ++ sg_free_table(sg_table); ++ vfree(kernel_addrs); +} + -+static int hailo_load_firmware(struct hailo_pcie_resources *resources, -+ struct device *dev, struct completion *fw_load_completion) ++/** ++ * Allocate noncontinuous memory (virtual continuous memory). ++ * ++ * @param dev - pointer to the device struct we are working on. ++ * @param buffer_size - the size of the buffer to allocate. ++ * @param kernel_addrs - pointer to the allocated buffer. ++ * @param sg_table - pointer to the sg table struct. ++ * @return 0 on success, negative error code on failure. on failure all resurces are released. (pages array, sg table, kernel_addrs) ++ */ ++static long pcie_vdma_allocate_noncontinuous_memory(struct device *dev, u64 buffer_size, void **kernel_addrs, struct sg_table *sg_table) +{ -+ const struct firmware *firmware = NULL; -+ int err = 0; -+ u32 boot_status = 0; ++ struct page **pages = NULL; ++ size_t npages = 0; ++ struct scatterlist *sgl = NULL; ++ long err = 0; ++ size_t i = 0; + -+ if (hailo_pcie_is_firmware_loaded(resources)) { -+ hailo_dev_warn(dev, "Firmware was already loaded\n"); -+ return 0; ++ // allocate noncontinuous memory for the kernel address (virtual continuous memory) ++ *kernel_addrs = vmalloc(buffer_size); ++ if (NULL == *kernel_addrs) { ++ hailo_dev_err(dev, "Failed to allocate memory for kernel_addrs\n"); ++ err = -ENOMEM; ++ goto exit; + } + -+ reinit_completion(fw_load_completion); ++ // map the memory to pages ++ npages = DIV_ROUND_UP(buffer_size, PAGE_SIZE); + -+ err = hailo_write_config(resources, dev, hailo_pcie_get_board_config_constants(resources->board_type)); -+ if (err < 0) { -+ hailo_dev_err(dev, "Failed writing board config"); -+ return err; ++ // allocate memory for a virtually contiguous array for the pages ++ pages = kvmalloc_array(npages, sizeof(*pages), GFP_KERNEL); ++ if (!pages) { ++ err = -ENOMEM; ++ hailo_dev_err(dev, "Failed to allocate memory for pages\n"); ++ goto release_user_addrs; ++ } ++ ++ // walk a vmap address to the struct page it maps ++ for (i = 0; i < npages; i++) { ++ pages[i] = vmalloc_to_page(*kernel_addrs + (i * PAGE_SIZE)); ++ if (!pages[i]) { ++ err = -ENOMEM; ++ hailo_dev_err(dev, "Failed to get page from vmap address\n"); ++ goto release_array; ++ } + } + -+ err = hailo_write_config(resources, dev, hailo_pcie_get_user_config_constants(resources->board_type)); ++ // allocate and initialize the sg table from a list of pages ++ sgl = sg_alloc_table_from_pages_segment_compat(sg_table, pages, npages, 0, buffer_size, SGL_MAX_SEGMENT_SIZE, NULL, ++ 0, GFP_KERNEL); ++ if (IS_ERR(sgl)) { ++ err = PTR_ERR(sgl); ++ hailo_dev_err(dev, "sg table alloc failed (err %ld)..\n", err); ++ goto release_array; ++ } ++ ++ // map the sg list ++ sg_table->nents = dma_map_sg(dev, sg_table->sgl, sg_table->orig_nents, DMA_TO_DEVICE); ++ if (0 == sg_table->nents) { ++ hailo_dev_err(dev, "failed to map sg list for user buffer\n"); ++ err = -ENXIO; ++ goto release_sg_table; ++ } ++ ++ // clean exit - just release the pages array & return err = 0 ++ err = 0; ++ kfree(pages); ++ goto exit; ++ ++release_sg_table: ++ dma_unmap_sg(dev, sg_table->sgl, sg_table->orig_nents, DMA_TO_DEVICE); ++release_array: ++ kfree(pages); ++release_user_addrs: ++ vfree(*kernel_addrs); ++exit: ++ return err; ++} ++ ++/** ++ * Release all boot resources. ++ * ++ * @param board - pointer to the board struct we are working on. ++ * @param engine - pointer to the vdma engine struct. ++ * @param boot_dma_state - pointer to the boot dma state struct which includes all of the boot resources. ++ */ ++static void pcie_vdme_release_boot_resources(struct hailo_pcie_board *board, struct hailo_vdma_engine *engine, ++ struct hailo_pcie_boot_dma_state *boot_dma_state) ++{ ++ u8 channel_index = 0; ++ ++ // release all the resources ++ for (channel_index = 0; channel_index < HAILO_PCI_OVER_VDMA_NUM_CHANNELS; channel_index++) { ++ struct hailo_pcie_boot_dma_channel_state *channel = &boot_dma_state->channels[channel_index]; ++ // release descriptor lists ++ if (channel->host_descriptors_buffer.kernel_address != NULL) { ++ hailo_desc_list_release(&board->pDev->dev, &channel->host_descriptors_buffer); ++ } ++ if (channel->device_descriptors_buffer.kernel_address != NULL) { ++ hailo_desc_list_release(&board->pDev->dev, &channel->device_descriptors_buffer); ++ } ++ ++ // stops all boot vDMA channels ++ hailo_vdma_stop_channel(engine->channels[channel_index].host_regs); ++ hailo_vdma_stop_channel(engine->channels[channel_index].device_regs); ++ ++ // release noncontinuous memory (virtual continuous memory) ++ if (channel->kernel_addrs != NULL) { ++ pcie_vdma_release_noncontinuous_memory(&board->pDev->dev, &channel->sg_table, channel->kernel_addrs); ++ } ++ } ++} ++ ++/** ++ * Allocate boot resources for vDMA transfer. ++ * ++ * @param desc_page_size - the size of the descriptor page. ++ * @param board - pointer to the board struct we are working on. ++ * @param boot_dma_state - pointer to the boot dma state struct which includes all of the boot resources. ++ * @param engine - pointer to the vDMA engine struct. ++ * @return 0 on success, negative error code on failure. in case of failure descriptor lists are released, ++ * boot vDMA channels are stopped and memory is released. ++ */ ++static long pcie_vdme_allocate_boot_resources(u32 desc_page_size, struct hailo_pcie_board *board, ++ struct hailo_pcie_boot_dma_state *boot_dma_state, struct hailo_vdma_engine *engine) ++{ ++ long err = 0; ++ uintptr_t device_handle = 0, host_handle = 0; ++ u8 channel_index = 0; ++ ++ for (channel_index = 0; channel_index < HAILO_PCI_OVER_VDMA_NUM_CHANNELS; channel_index++) { ++ struct hailo_pcie_boot_dma_channel_state *channel = &boot_dma_state->channels[channel_index]; ++ ++ // create 2 descriptors list - 1 for the host & 1 for the device for each channel ++ err = hailo_desc_list_create(&board->pDev->dev, MAX_SG_DESCS_COUNT, desc_page_size, host_handle, false, ++ &channel->host_descriptors_buffer); ++ if (err < 0) { ++ hailo_err(board, "failed to allocate host descriptors list buffer\n"); ++ goto release_all_resources; ++ } ++ ++ err = hailo_desc_list_create(&board->pDev->dev, MAX_SG_DESCS_COUNT, desc_page_size, device_handle, false, ++ &channel->device_descriptors_buffer); ++ if (err < 0) { ++ hailo_err(board, "failed to allocate device descriptors list buffer\n"); ++ goto release_all_resources; ++ } ++ ++ // start vDMA channels - both sides with DDR at the host side (AKA ID 0) ++ err = hailo_vdma_start_channel(engine->channels[channel_index].host_regs, ++ channel->host_descriptors_buffer.dma_address, ++ channel->host_descriptors_buffer.desc_list.desc_count, board->vdma.hw->ddr_data_id); ++ if (err < 0) { ++ hailo_err(board, "Error starting host vdma channel\n"); ++ goto release_all_resources; ++ } ++ ++ err = hailo_vdma_start_channel(engine->channels[channel_index].device_regs, ++ channel->device_descriptors_buffer.dma_address, ++ channel->device_descriptors_buffer.desc_list.desc_count, board->vdma.hw->ddr_data_id); ++ if (err < 0) { ++ hailo_err(board, "Error starting device vdma channel\n"); ++ goto release_all_resources; ++ } ++ ++ // initialize the buffer size per channel ++ channel->buffer_size = (MAX_SG_DESCS_COUNT * desc_page_size); ++ ++ // allocate noncontinuous memory (virtual continuous memory) ++ err = pcie_vdma_allocate_noncontinuous_memory(&board->pDev->dev, channel->buffer_size, &channel->kernel_addrs, ++ &channel->sg_table); ++ if (err < 0) { ++ hailo_err(board, "Failed to allocate noncontinuous memory\n"); ++ goto release_all_resources; ++ } ++ } ++ ++ return 0; ++ ++release_all_resources: ++ pcie_vdme_release_boot_resources(board, engine, boot_dma_state); ++ return err; ++} ++ ++/** ++ * Write FW boot files over vDMA using multiple channels for timing optimizations. ++ * ++ * The function is divided into the following steps: ++ * 1) Allocate resources for the boot process. ++ * 2) Programs descriptors to point to the memory and start the vDMA. ++ * 3) Waits until the vDMA is done and triggers the device to start the boot process. ++ * 4) Releases all the resources. ++ * ++ * @param board - pointer to the board struct. ++ * @param stage - the stage of the boot process. ++ * @param desc_page_size - the size of the descriptor page. ++ * @return 0 on success, negative error code on failure. in any case all resurces are released. ++ */ ++static long pcie_write_firmware_batch_over_dma(struct hailo_pcie_board *board, u32 stage, u32 desc_page_size) ++{ ++ long err = 0; ++ struct hailo_vdma_engine *engine = &board->vdma.vdma_engines[PCI_VDMA_ENGINE_INDEX]; ++ u8 channel_index = 0; ++ ++ err = pcie_vdme_allocate_boot_resources(desc_page_size, board, &board->fw_boot.boot_dma_state, engine); + if (err < 0) { -+ hailo_dev_err(dev, "Failed writing fw config"); ++ hailo_err(board, "Failed to create descriptors and start channels\n"); + return err; + } + -+ // read firmware file -+ err = request_firmware_direct(&firmware, hailo_pcie_get_fw_filename(resources->board_type), dev); ++ // initialize the completion for the vDMA boot data completion ++ reinit_completion(&board->fw_boot.vdma_boot_completion); ++ ++ err = pcie_vdma_program_entire_batch(board, &board->fw_boot.boot_dma_state, &board->pcie_resources, stage); + if (err < 0) { -+ hailo_dev_warn(dev, "Firmware file not found (/lib/firmware/%s), please upload the firmware manually \n", -+ hailo_pcie_get_fw_filename(resources->board_type)); -+ return 0; ++ hailo_err(board, "Failed to program entire batch\n"); ++ goto release_all; + } + -+ err = hailo_pcie_write_firmware(resources, firmware->data, firmware->size); -+ if (err < 0) { -+ hailo_dev_err(dev, "Failed writing firmware. err %d\n", err); -+ release_firmware(firmware); -+ return err; ++ // sync the sg tables for the device before statirng the vDMA ++ for (channel_index = 0; channel_index < HAILO_PCI_OVER_VDMA_NUM_CHANNELS; channel_index++) { ++ dma_sync_sgtable_for_device(&board->pDev->dev, &board->fw_boot.boot_dma_state.channels[channel_index].sg_table, ++ DMA_TO_DEVICE); + } + -+ release_firmware(firmware); ++ // start the vDMA transfer on all channels ++ for (channel_index = 0; channel_index < HAILO_PCI_OVER_VDMA_NUM_CHANNELS; channel_index++) { ++ struct hailo_pcie_boot_dma_channel_state *channel = &board->fw_boot.boot_dma_state.channels[channel_index]; ++ if (channel->desc_program_num != 0) { ++ hailo_vdma_set_num_avail(engine->channels[channel_index].host_regs, channel->desc_program_num); ++ hailo_vdma_set_num_avail(engine->channels[channel_index].device_regs, channel->desc_program_num); ++ hailo_dbg(board, "Set num avail to %u, on channel %u\n", channel->desc_program_num, channel_index); ++ } ++ } + -+ if (!wait_for_firmware_completion(fw_load_completion)) { -+ boot_status = hailo_get_boot_status(resources); -+ hailo_dev_err(dev, "Timeout waiting for firmware file, boot status %u\n", boot_status); -+ return -ETIMEDOUT; ++ if (!wait_for_firmware_completion(&board->fw_boot.vdma_boot_completion, hailo_pcie_get_loading_stage_info(board->pcie_resources.board_type, SECOND_STAGE)->timeout)) { ++ hailo_err(board, "Timeout waiting for vDMA boot data completion\n"); ++ err = -ETIMEDOUT; ++ goto release_all; + } + -+ hailo_dev_notice(dev, "Firmware was loaded successfully\n"); -+ return 0; ++ hailo_notice(board, "vDMA transfer completed, triggering boot\n"); ++ reinit_completion(&board->fw_boot.fw_loaded_completion); ++ hailo_trigger_firmware_boot(&board->pcie_resources, stage); ++ ++release_all: ++ pcie_vdme_release_boot_resources(board, engine, &board->fw_boot.boot_dma_state); ++ return err; +} + -+static int hailo_load_firmware_batch(struct hailo_pcie_resources *resources, ++static int load_soc_firmware(struct hailo_pcie_board *board, struct hailo_pcie_resources *resources, + struct device *dev, struct completion *fw_load_completion) +{ + u32 boot_status = 0; -+ u32 pcie_finished = 1; + int err = 0; ++ u32 second_stage = force_boot_linux_from_eemc ? SECOND_STAGE_LINUX_IN_EMMC : SECOND_STAGE; + + if (hailo_pcie_is_firmware_loaded(resources)) { -+ hailo_dev_warn(dev, "Firmware batch was already loaded\n"); ++ hailo_dev_warn(dev, "SOC Firmware batch was already loaded\n"); + return 0; + } + ++ // configure the EP registers for the DMA transaction ++ hailo_pcie_configure_ep_registers_for_dma_transaction(resources); ++ + init_completion(fw_load_completion); ++ init_completion(&board->fw_boot.vdma_boot_completion); + + err = hailo_pcie_write_firmware_batch(dev, resources, FIRST_STAGE); + if (err < 0) { -+ hailo_dev_err(dev, "Failed writing firmware files. err %d\n", err); ++ hailo_dev_err(dev, "Failed writing SOC FIRST_STAGE firmware files. err %d\n", err); + return err; + } + -+ hailo_trigger_firmware_boot(resources); ++ if (!wait_for_firmware_completion(fw_load_completion, hailo_pcie_get_loading_stage_info(resources->board_type, FIRST_STAGE)->timeout)) { ++ boot_status = hailo_get_boot_status(resources); ++ hailo_dev_err(dev, "Timeout waiting for SOC FIRST_STAGE firmware file, boot status %u\n", boot_status); ++ return -ETIMEDOUT; ++ } + -+ if (!wait_for_firmware_completion(fw_load_completion)) { ++ reinit_completion(fw_load_completion); ++ ++ err = (int)pcie_write_firmware_batch_over_dma(board, second_stage, HAILO_PCI_OVER_VDMA_PAGE_SIZE); ++ if (err < 0) { ++ hailo_dev_err(dev, "Failed writing SOC SECOND_STAGE firmware files over vDMA. err %d\n", err); ++ return err; ++ } ++ ++ if (!wait_for_firmware_completion(fw_load_completion, hailo_pcie_get_loading_stage_info(resources->board_type, SECOND_STAGE)->timeout)) { + boot_status = hailo_get_boot_status(resources); -+ hailo_dev_err(dev, "Timeout waiting for firmware file, boot status %u\n", boot_status); ++ hailo_dev_err(dev, "Timeout waiting for SOC SECOND_STAGE firmware file, boot status %u\n", boot_status); + return -ETIMEDOUT; + } ++ + reinit_completion(fw_load_completion); ++ reinit_completion(&board->fw_boot.vdma_boot_completion); ++ ++ hailo_dev_notice(dev, "SOC Firmware Batch loaded successfully\n"); ++ ++ return 0; ++} ++static int load_nnc_firmware(struct hailo_pcie_board *board) ++{ ++ u32 boot_status = 0; ++ int err = 0; ++ struct device *dev = &board->pDev->dev; ++ ++ if (hailo_pcie_is_firmware_loaded(&board->pcie_resources)) { ++ if (support_soft_reset) { ++ err = hailo_pcie_soft_reset(&board->pcie_resources, &board->soft_reset.reset_completed); // send control, wait for done ++ if (err < 0) { ++ hailo_dev_err(dev, "Failed hailo pcie soft reset. err %d\n", err); ++ return 0; ++ } ++ hailo_dev_notice(dev, "Soft reset done\n"); ++ } else { ++ hailo_dev_warn(dev, "NNC Firmware batch was already loaded\n"); ++ return 0; ++ } ++ } ++ ++ init_completion(&board->fw_boot.fw_loaded_completion); + -+ err = hailo_pcie_write_firmware_batch(dev, resources, SECOND_STAGE); ++ err = hailo_pcie_write_firmware_batch(dev, &board->pcie_resources, FIRST_STAGE); + if (err < 0) { -+ hailo_dev_err(dev, "Failed writing firmware files. err %d\n", err); ++ hailo_dev_err(dev, "Failed writing NNC firmware files. err %d\n", err); + return err; + } + -+ // TODO: HRT-13838 - Remove, move address to compat, make write_memory static -+ write_memory(resources, 0x84000000, (void*)&pcie_finished, sizeof(pcie_finished)); -+ -+ if (!wait_for_firmware_completion(fw_load_completion)) { -+ boot_status = hailo_get_boot_status(resources); -+ hailo_dev_err(dev, "Timeout waiting for firmware file, boot status %u\n", boot_status); ++ if (!wait_for_firmware_completion(&board->fw_boot.fw_loaded_completion, hailo_pcie_get_loading_stage_info(board->pcie_resources.board_type, FIRST_STAGE)->timeout)) { ++ boot_status = hailo_get_boot_status(&board->pcie_resources); ++ hailo_dev_err(dev, "Timeout waiting for NNC firmware file, boot status %u\n", boot_status); + return -ETIMEDOUT; + } + -+ hailo_dev_notice(dev, "Firmware Batch loaded successfully\n"); ++ hailo_dev_notice(dev, "NNC Firmware loaded successfully\n"); + + return 0; +} + -+static int hailo_activate_board(struct hailo_pcie_board *board) ++int hailo_pcie_soft_reset(struct hailo_pcie_resources *resources, struct completion *reset_completed) +{ ++ bool completion_result = false; + int err = 0; + -+ (void)hailo_pcie_disable_aspm(board, PCIE_LINK_STATE_L0S, false); ++ hailo_pcie_write_firmware_soft_reset(resources); + -+ err = hailo_enable_interrupts(board); -+ if (err < 0) { -+ hailo_err(board, "Failed Enabling interrupts %d\n", err); ++ reinit_completion(reset_completed); ++ ++ // Wait for response ++ completion_result = ++ wait_for_firmware_completion(reset_completed, msecs_to_jiffies(FIRMWARE_WAIT_TIMEOUT_MS)); ++ if (completion_result == false) { ++ pr_warn("hailo reset firmware, timeout waiting for shutdown response (timeout_ms=%d)\n", FIRMWARE_WAIT_TIMEOUT_MS); ++ err = -ETIMEDOUT; + return err; + } + -+ switch (board->pcie_resources.board_type) { -+ case HAILO_BOARD_TYPE_HAILO10H: -+ err = hailo_load_firmware_batch(&board->pcie_resources, &board->pDev->dev, -+ &board->fw_loaded_completion); -+ break; -+ case HAILO_BOARD_TYPE_HAILO10H_LEGACY: -+ case HAILO_BOARD_TYPE_PLUTO: -+ case HAILO_BOARD_TYPE_HAILO8: -+ err = hailo_load_firmware(&board->pcie_resources, &board->pDev->dev, -+ &board->fw_loaded_completion); -+ break; ++ msleep(TIME_UNTIL_REACH_BOOTLOADER); ++ pr_notice("hailo_driver_down finished\n"); ++ ++ return err; ++} ++ ++static int load_firmware(struct hailo_pcie_board *board) ++{ ++ switch (board->pcie_resources.accelerator_type) { ++ case HAILO_ACCELERATOR_TYPE_SOC: ++ return load_soc_firmware(board, &board->pcie_resources, &board->pDev->dev, &board->fw_boot.fw_loaded_completion); ++ case HAILO_ACCELERATOR_TYPE_NNC: ++ return load_nnc_firmware(board); + default: -+ hailo_err(board, "Invalid board type"); -+ err = -EINVAL; ++ hailo_err(board, "Invalid board type %d\n", board->pcie_resources.accelerator_type); ++ return -EINVAL; + } ++} ++ ++static int enable_boot_interrupts(struct hailo_pcie_board *board) ++{ ++ int err = hailo_enable_interrupts(board); + if (err < 0) { -+ hailo_err(board, "Firmware load failed\n"); -+ hailo_disable_interrupts(board); ++ hailo_err(board, "Failed enabling interrupts %d\n", err); + return err; + } + ++ board->fw_boot.is_in_boot = true; ++ return 0; ++} ++ ++static void disable_boot_interrupts(struct hailo_pcie_board *board) ++{ ++ board->fw_boot.is_in_boot = false; + hailo_disable_interrupts(board); ++} ++ ++static int hailo_activate_board(struct hailo_pcie_board *board) ++{ ++ int err = 0; ++ ktime_t start_time = 0, end_time = 0; ++ ++ (void)hailo_pcie_disable_aspm(board, PCIE_LINK_STATE_L0S, false); ++ ++ err = enable_boot_interrupts(board); ++ if (err < 0) { ++ return err; ++ } ++ ++ start_time = ktime_get(); ++ err = load_firmware(board); ++ end_time = ktime_get(); ++ hailo_notice(board, "FW loaded, took %lld ms\n", ktime_to_ms(ktime_sub(end_time, start_time))); ++ disable_boot_interrupts(board); ++ ++ if (err < 0) { ++ hailo_err(board, "Firmware load failed\n"); ++ return err; ++ } + + if (power_mode_enabled()) { + // Setting the device to low power state, until the user opens the device ++ hailo_info(board, "Power change state to PCI_D3hot\n"); + err = pci_set_power_state(board->pDev, PCI_D3hot); + if (err < 0) { + hailo_err(board, "Set power state failed %d\n", err); @@ -136672,12 +140765,9 @@ index 000000000000..170ae819bb0f + } + + -+ // There is no HAILO15 as mercury through pcie unless it's legacy mode (H15 as accelerator) or HAILO-10H -+ if (HAILO_BOARD_TYPE_HAILO15 == board_type){ -+ if (true == force_hailo15_legacy_mode) { ++ if (HAILO_BOARD_TYPE_HAILO10H == board_type){ ++ if (true == force_hailo10h_legacy_mode) { + board_type = HAILO_BOARD_TYPE_HAILO10H_LEGACY; -+ } else { -+ board_type = HAILO_BOARD_TYPE_HAILO10H; + } + } + @@ -136848,22 +140938,20 @@ index 000000000000..170ae819bb0f + } + + pBoard->interrupts_enabled = false; -+ init_completion(&pBoard->fw_loaded_completion); -+ init_completion(&pBoard->soc_connect_accepted); ++ pBoard->fw_boot.is_in_boot = false; ++ init_completion(&pBoard->fw_boot.fw_loaded_completion); + + sema_init(&pBoard->mutex, 1); + atomic_set(&pBoard->ref_count, 0); + INIT_LIST_HEAD(&pBoard->open_files_list); + -+ sema_init(&pBoard->fw_control.mutex, 1); -+ spin_lock_init(&pBoard->notification_read_spinlock); -+ init_completion(&pBoard->fw_control.completion); ++ // Init both soc and nnc, since the interrupts are shared. ++ hailo_nnc_init(&pBoard->nnc); ++ hailo_soc_init(&pBoard->soc); + + init_completion(&pBoard->driver_down.reset_completed); ++ init_completion(&pBoard->soft_reset.reset_completed); + -+ INIT_LIST_HEAD(&pBoard->notification_wait_list); -+ -+ memset(&pBoard->notification_cache, 0, sizeof(pBoard->notification_cache)); + memset(&pBoard->memory_transfer_params, 0, sizeof(pBoard->memory_transfer_params)); + + err = hailo_pcie_vdma_controller_init(&pBoard->vdma, &pBoard->pDev->dev, @@ -136880,6 +140968,10 @@ index 000000000000..170ae819bb0f + goto probe_release_pcie_resources; + } + ++ // Initialize the boot channel bitmap to 1 since channel 0 is always used for boot ++ // (we will always use at least 1 channel which is LSB in the bitmap) ++ pBoard->fw_boot.boot_used_channel_bitmap = (1 << 0); ++ memset(&pBoard->fw_boot.boot_dma_state, 0, sizeof(pBoard->fw_boot.boot_dma_state)); + err = hailo_activate_board(pBoard); + if (err < 0) { + hailo_err(pBoard, "Failed activating board %d\n", err); @@ -136926,7 +141018,6 @@ index 000000000000..170ae819bb0f +static void hailo_pcie_remove(struct pci_dev* pDev) +{ + struct hailo_pcie_board* pBoard = (struct hailo_pcie_board*) pci_get_drvdata(pDev); -+ struct hailo_notification_wait *cursor = NULL; + + pci_notice(pDev, "Remove: Releasing board\n"); + @@ -136958,13 +141049,7 @@ index 000000000000..170ae819bb0f + + pci_set_drvdata(pDev, NULL); + -+ // Lock rcu_read_lock and send notification_completion to wake anyone waiting on the notification_wait_list when removed -+ rcu_read_lock(); -+ list_for_each_entry_rcu(cursor, &pBoard->notification_wait_list, notification_wait_list) { -+ cursor->is_disabled = true; -+ complete(&cursor->notification_completion); -+ } -+ rcu_read_unlock(); ++ hailo_nnc_finalize(&pBoard->nnc); + + up(&pBoard->mutex); + @@ -136983,6 +141068,15 @@ index 000000000000..170ae819bb0f + +} + ++inline int driver_down(struct hailo_pcie_board *board) ++{ ++ if (board->pcie_resources.accelerator_type == HAILO_ACCELERATOR_TYPE_NNC) { ++ return hailo_nnc_driver_down(board); ++ } else { ++ return hailo_soc_driver_down(board); ++ } ++} ++ +#ifdef CONFIG_PM_SLEEP +static int hailo_pcie_suspend(struct device *dev) +{ @@ -136993,17 +141087,16 @@ index 000000000000..170ae819bb0f + // lock board to wait for any pending operations + down(&board->mutex); + -+ // Disable all interrupts. All interrupts from Hailo chip would be masked. -+ hailo_disable_interrupts(board); -+ -+ // Close all vDMA channels + if (board->vdma.used_by_filp != NULL) { -+ err = hailo_pcie_driver_down(board); ++ err = driver_down(board); + if (err < 0) { + dev_notice(dev, "Error while trying to call FW to close vdma channels\n"); + } + } + ++ // Disable all interrupts. All interrupts from Hailo chip would be masked. ++ hailo_disable_interrupts(board); ++ + // Un validate all activae file contexts so every new action would return error to the user. + list_for_each_entry(cur, &board->open_files_list, open_files_list) { + cur->is_valid = false; @@ -137013,8 +141106,8 @@ index 000000000000..170ae819bb0f + up(&board->mutex); + + dev_notice(dev, "PM's suspend\n"); -+ // Continue system suspend -+ return err; ++ // Success Oriented - Continue system suspend even in case of error (otherwise system will not suspend correctly) ++ return 0; +} + +static int hailo_pcie_resume(struct device *dev) @@ -137024,10 +141117,10 @@ index 000000000000..170ae819bb0f + + if ((err = hailo_activate_board(board)) < 0) { + dev_err(dev, "Failed activating board %d\n", err); -+ return err; + } + + dev_notice(dev, "PM's resume\n"); ++ // Success Oriented - Continue system resume even in case of error (otherwise system will not suspend correctly) + return 0; +} +#endif /* CONFIG_PM_SLEEP */ @@ -137048,7 +141141,7 @@ index 000000000000..170ae819bb0f + down(&board->mutex); + if (board->vdma.used_by_filp != NULL) { + // Try to close all vDMA channels before reset -+ err = hailo_pcie_driver_down(board); ++ err = driver_down(board); + if (err < 0) { + pci_err(pdev, "Error while trying to call FW to close vdma channels (errno %d)\n", err); + } @@ -137082,8 +141175,8 @@ index 000000000000..170ae819bb0f +static struct pci_device_id hailo_pcie_id_table[] = +{ + {PCI_DEVICE_DATA(HAILO, HAILO8, HAILO_BOARD_TYPE_HAILO8)}, -+ {PCI_DEVICE_DATA(HAILO, HAILO15, HAILO_BOARD_TYPE_HAILO15)}, -+ {PCI_DEVICE_DATA(HAILO, PLUTO, HAILO_BOARD_TYPE_PLUTO)}, ++ {PCI_DEVICE_DATA(HAILO, HAILO10H, HAILO_BOARD_TYPE_HAILO10H)}, ++ {PCI_DEVICE_DATA(HAILO, HAILO15L, HAILO_BOARD_TYPE_HAILO15L)}, + {0,0,0,0,0,0,0 }, +}; + @@ -137179,8 +141272,14 @@ index 000000000000..170ae819bb0f +module_param(force_desc_page_size, int, S_IRUGO); +MODULE_PARM_DESC(force_desc_page_size, "Determines the maximum DMA descriptor page size (must be a power of 2)"); + -+module_param(force_hailo15_legacy_mode, bool, S_IRUGO); -+MODULE_PARM_DESC(force_hailo15_legacy_mode, "Forces work with Hailo15 in legacy mode(relevant for emulators)"); ++module_param(force_hailo10h_legacy_mode, bool, S_IRUGO); ++MODULE_PARM_DESC(force_hailo10h_legacy_mode, "Forces work with Hailo10h in legacy mode(relevant for emulators)"); ++ ++module_param(force_boot_linux_from_eemc, bool, S_IRUGO); ++MODULE_PARM_DESC(force_boot_linux_from_eemc, "Boot the linux image from eemc (Requires special Image)"); ++ ++module_param(support_soft_reset, bool, S_IRUGO); ++MODULE_PARM_DESC(support_soft_reset, "enables driver reload to reload a new firmware as well"); + +MODULE_AUTHOR("Hailo Technologies Ltd."); +MODULE_DESCRIPTION("Hailo PCIe driver"); @@ -137189,13 +141288,13 @@ index 000000000000..170ae819bb0f + diff --git a/drivers/media/pci/hailo/src/pcie.h b/drivers/media/pci/hailo/src/pcie.h new file mode 100644 -index 000000000000..cfee8be8b820 +index 000000000000..b90cbb6b2268 --- /dev/null +++ b/drivers/media/pci/hailo/src/pcie.h -@@ -0,0 +1,84 @@ +@@ -0,0 +1,131 @@ +// SPDX-License-Identifier: GPL-2.0 +/** -+ * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved. ++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. + **/ + +#ifndef _HAILO_PCI_PCIE_H_ @@ -137214,6 +141313,9 @@ index 000000000000..cfee8be8b820 + +#include + ++#define HAILO_PCI_OVER_VDMA_NUM_CHANNELS (8) ++#define HAILO_PCI_OVER_VDMA_PAGE_SIZE (512) ++ +struct hailo_fw_control_info { + // protects that only one fw control will be send at a time + struct semaphore mutex; @@ -137228,6 +141330,11 @@ index 000000000000..cfee8be8b820 + struct completion reset_completed; +}; + ++struct hailo_pcie_soft_reset { ++ // called from the interrupt handler to notify that FW completed reset ++ struct completion reset_completed; ++}; ++ +struct hailo_fw_boot { + // the filp that enabled interrupts for fw boot. the interrupt is enabled if this is not null + struct file *filp; @@ -137236,6 +141343,19 @@ index 000000000000..cfee8be8b820 +}; + + ++struct hailo_pcie_nnc { ++ struct hailo_fw_control_info fw_control; ++ ++ spinlock_t notification_read_spinlock; ++ struct list_head notification_wait_list; ++ struct hailo_d2h_notification notification_cache; ++ struct hailo_d2h_notification notification_to_user; ++}; ++ ++struct hailo_pcie_soc { ++ struct completion control_resp_ready; ++}; ++ +// Context for each open file handle +// TODO: store board and use as actual context +struct hailo_file_context { @@ -137243,6 +141363,33 @@ index 000000000000..cfee8be8b820 + struct file *filp; + struct hailo_vdma_file_context vdma_context; + bool is_valid; ++ u32 soc_used_channels_bitmap; ++}; ++ ++struct hailo_pcie_boot_dma_channel_state { ++ struct hailo_descriptors_list_buffer host_descriptors_buffer; ++ struct hailo_descriptors_list_buffer device_descriptors_buffer; ++ struct sg_table sg_table; ++ u64 buffer_size; ++ void *kernel_addrs; ++ u32 desc_program_num; ++}; ++ ++struct hailo_pcie_boot_dma_state { ++ struct hailo_pcie_boot_dma_channel_state channels[HAILO_PCI_OVER_VDMA_NUM_CHANNELS]; ++ u8 curr_channel_index; ++}; ++ ++struct hailo_pcie_fw_boot { ++ struct hailo_pcie_boot_dma_state boot_dma_state; ++ // is_in_boot is set to true when the board is in boot mode ++ bool is_in_boot; ++ // boot_used_channel_bitmap is a bitmap of the channels that are used for boot ++ u16 boot_used_channel_bitmap; ++ // fw_loaded_completion is used to notify that the FW was loaded - SOC & NNC ++ struct completion fw_loaded_completion; ++ // vdma_boot_completion is used to notify that the vDMA boot data was transferred completely on all used channels for boot ++ struct completion vdma_boot_completion; +}; + +struct hailo_pcie_board { @@ -137252,21 +141399,19 @@ index 000000000000..cfee8be8b820 + atomic_t ref_count; + struct list_head open_files_list; + struct hailo_pcie_resources pcie_resources; -+ struct hailo_fw_control_info fw_control; ++ struct hailo_pcie_nnc nnc; ++ struct hailo_pcie_soc soc; + struct hailo_pcie_driver_down_info driver_down; ++ struct hailo_pcie_soft_reset soft_reset; + struct semaphore mutex; + struct hailo_vdma_controller vdma; -+ spinlock_t notification_read_spinlock; -+ struct list_head notification_wait_list; -+ struct hailo_d2h_notification notification_cache; -+ struct hailo_d2h_notification notification_to_user; ++ ++ struct hailo_pcie_fw_boot fw_boot; ++ + struct hailo_memory_transfer_params memory_transfer_params; + u32 desc_max_page_size; + enum hailo_allocation_mode allocation_mode; -+ struct completion fw_loaded_completion; + bool interrupts_enabled; -+ // Only needed in accelerator type soc -+ struct completion soc_connect_accepted; +}; + +bool power_mode_enabled(void); @@ -137274,18 +141419,303 @@ index 000000000000..cfee8be8b820 +struct hailo_pcie_board* hailo_pcie_get_board_index(u32 index); +void hailo_disable_interrupts(struct hailo_pcie_board *board); +int hailo_enable_interrupts(struct hailo_pcie_board *board); ++int hailo_pcie_soft_reset(struct hailo_pcie_resources *resources, struct completion *reset_completed); + +#endif /* _HAILO_PCI_PCIE_H_ */ + +diff --git a/drivers/media/pci/hailo/src/soc.c b/drivers/media/pci/hailo/src/soc.c +new file mode 100644 +index 000000000000..064567ce406a +--- /dev/null ++++ b/drivers/media/pci/hailo/src/soc.c +@@ -0,0 +1,244 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/** ++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. ++ **/ ++/** ++ * A Hailo PCIe NNC device is a device contains a full SoC over PCIe. The SoC contains NNC (neural network core) and ++ * some application processor (pci_ep). ++ */ ++ ++#include "soc.h" ++ ++#include "vdma_common.h" ++#include "utils/logs.h" ++#include "vdma/memory.h" ++#include "pcie_common.h" ++ ++#include ++ ++#ifndef HAILO_EMULATOR ++#define PCI_SOC_CONTROL_CONNECT_TIMEOUT_MS (1000) ++#else ++#define PCI_SOC_CONTROL_CONNECT_TIMEOUT_MS (1000000) ++#endif /* ifndef HAILO_EMULATOR */ ++ ++void hailo_soc_init(struct hailo_pcie_soc *soc) ++{ ++ init_completion(&soc->control_resp_ready); ++} ++ ++long hailo_soc_ioctl(struct hailo_pcie_board *board, struct hailo_file_context *context, ++ struct hailo_vdma_controller *controller, unsigned int cmd, unsigned long arg) ++{ ++ switch (cmd) { ++ case HAILO_SOC_CONNECT: ++ return hailo_soc_connect_ioctl(board, context, controller, arg); ++ case HAILO_SOC_CLOSE: ++ return hailo_soc_close_ioctl(board, controller, context, arg); ++ default: ++ hailo_err(board, "Invalid pcie EP ioctl code 0x%x (nr: %d)\n", cmd, _IOC_NR(cmd)); ++ return -ENOTTY; ++ } ++} ++ ++static int soc_control(struct hailo_pcie_board *board, ++ const struct hailo_pcie_soc_request *request, ++ struct hailo_pcie_soc_response *response) ++{ ++ int ret = 0; ++ reinit_completion(&board->soc.control_resp_ready); ++ ++ hailo_pcie_soc_write_request(&board->pcie_resources, request); ++ ++ ret = wait_for_completion_interruptible_timeout(&board->soc.control_resp_ready, ++ msecs_to_jiffies(PCI_SOC_CONTROL_CONNECT_TIMEOUT_MS)); ++ if (ret <= 0) { ++ if (0 == ret) { ++ hailo_err(board, "Timeout waiting for soc control (timeout_ms=%d)\n", PCI_SOC_CONTROL_CONNECT_TIMEOUT_MS); ++ return -ETIMEDOUT; ++ } else { ++ hailo_info(board, "soc control failed with err=%d (process was interrupted or killed)\n", ++ ret); ++ return ret; ++ } ++ } ++ ++ hailo_pcie_soc_read_response(&board->pcie_resources, response); ++ ++ if (response->status < 0) { ++ hailo_err(board, "soc control failed with status=%d\n", response->status); ++ return response->status; ++ } ++ ++ if (response->control_code != request->control_code) { ++ hailo_err(board, "Invalid response control code %d (expected %d)\n", ++ response->control_code, request->control_code); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++long hailo_soc_connect_ioctl(struct hailo_pcie_board *board, struct hailo_file_context *context, ++ struct hailo_vdma_controller *controller, unsigned long arg) ++{ ++ struct hailo_pcie_soc_request request = {0}; ++ struct hailo_pcie_soc_response response = {0}; ++ struct hailo_soc_connect_params params; ++ struct hailo_vdma_channel *input_channel = NULL; ++ struct hailo_vdma_channel *output_channel = NULL; ++ struct hailo_vdma_engine *vdma_engine = &controller->vdma_engines[PCI_VDMA_ENGINE_INDEX]; ++ struct hailo_descriptors_list_buffer *input_descriptors_buffer = NULL; ++ struct hailo_descriptors_list_buffer *output_descriptors_buffer = NULL; ++ int err = 0; ++ ++ if (copy_from_user(¶ms, (void *)arg, sizeof(params))) { ++ hailo_err(board, "copy_from_user fail\n"); ++ return -ENOMEM; ++ } ++ ++ request = (struct hailo_pcie_soc_request) { ++ .control_code = HAILO_PCIE_SOC_CONTROL_CODE_CONNECT, ++ .connect = { ++ .port = params.port_number ++ } ++ }; ++ err = soc_control(board, &request, &response); ++ if (err < 0) { ++ return err; ++ } ++ ++ params.input_channel_index = response.connect.input_channel_index; ++ params.output_channel_index = response.connect.output_channel_index; ++ ++ if (!hailo_check_channel_index(params.input_channel_index, controller->hw->src_channels_bitmask, true)) { ++ hailo_dev_err(&board->pDev->dev, "Invalid input channel index %u\n", params.input_channel_index); ++ return -EINVAL; ++ } ++ ++ if (!hailo_check_channel_index(params.output_channel_index, controller->hw->src_channels_bitmask, false)) { ++ hailo_dev_err(&board->pDev->dev, "Invalid output channel index %u\n", params.output_channel_index); ++ return -EINVAL; ++ } ++ ++ input_channel = &vdma_engine->channels[params.input_channel_index]; ++ output_channel = &vdma_engine->channels[params.output_channel_index]; ++ ++ input_descriptors_buffer = hailo_vdma_find_descriptors_buffer(&context->vdma_context, params.input_desc_handle); ++ output_descriptors_buffer = hailo_vdma_find_descriptors_buffer(&context->vdma_context, params.output_desc_handle); ++ if (NULL == input_descriptors_buffer || NULL == output_descriptors_buffer) { ++ hailo_dev_err(&board->pDev->dev, "input / output descriptors buffer not found \n"); ++ return -EINVAL; ++ } ++ ++ if (!is_powerof2((size_t)input_descriptors_buffer->desc_list.desc_count) || ++ !is_powerof2((size_t)output_descriptors_buffer->desc_list.desc_count)) { ++ hailo_dev_err(&board->pDev->dev, "Invalid desc list size\n"); ++ return -EINVAL; ++ } ++ ++ // configure and start input channel ++ // DMA Direction is only to get channel index - so ++ err = hailo_vdma_start_channel(input_channel->host_regs, input_descriptors_buffer->dma_address, input_descriptors_buffer->desc_list.desc_count, ++ board->vdma.hw->ddr_data_id); ++ if (err < 0) { ++ hailo_dev_err(&board->pDev->dev, "Error starting vdma input channel index %u\n", params.input_channel_index); ++ return -EINVAL; ++ } ++ ++ // Store the input channels state in bitmap (open) ++ hailo_set_bit(params.input_channel_index, &context->soc_used_channels_bitmap); ++ ++ // configure and start output channel ++ // DMA Direction is only to get channel index - so ++ err = hailo_vdma_start_channel(output_channel->host_regs, output_descriptors_buffer->dma_address, output_descriptors_buffer->desc_list.desc_count, ++ board->vdma.hw->ddr_data_id); ++ if (err < 0) { ++ hailo_dev_err(&board->pDev->dev, "Error starting vdma output channel index %u\n", params.output_channel_index); ++ // Close input channel ++ hailo_vdma_stop_channel(input_channel->host_regs); ++ return -EINVAL; ++ } ++ ++ // Store the output channels state in bitmap (open) ++ hailo_set_bit(params.output_channel_index, &context->soc_used_channels_bitmap); ++ ++ if (copy_to_user((void *)arg, ¶ms, sizeof(params))) { ++ hailo_dev_err(&board->pDev->dev, "copy_to_user fail\n"); ++ return -ENOMEM; ++ } ++ ++ return 0; ++} ++ ++static int close_channels(struct hailo_pcie_board *board, u32 channels_bitmap) ++{ ++ struct hailo_pcie_soc_request request = {0}; ++ struct hailo_pcie_soc_response response = {0}; ++ struct hailo_vdma_engine *engine = &board->vdma.vdma_engines[PCI_VDMA_ENGINE_INDEX]; ++ struct hailo_vdma_channel *channel = NULL; ++ u8 channel_index = 0; ++ ++ hailo_info(board, "Closing channels bitmap 0x%x\n", channels_bitmap); ++ for_each_vdma_channel(engine, channel, channel_index) { ++ if (hailo_test_bit(channel_index, &channels_bitmap)) { ++ hailo_vdma_stop_channel(channel->host_regs); ++ } ++ } ++ ++ request = (struct hailo_pcie_soc_request) { ++ .control_code = HAILO_PCIE_SOC_CONTROL_CODE_CLOSE, ++ .close = { ++ .channels_bitmap = channels_bitmap ++ } ++ }; ++ return soc_control(board, &request, &response); ++} ++ ++long hailo_soc_close_ioctl(struct hailo_pcie_board *board, struct hailo_vdma_controller *controller, ++ struct hailo_file_context *context, unsigned long arg) ++{ ++ struct hailo_soc_close_params params; ++ u32 channels_bitmap = 0; ++ int err = 0; ++ ++ if (copy_from_user(¶ms, (void *)arg, sizeof(params))) { ++ hailo_dev_err(&board->pDev->dev, "copy_from_user fail\n"); ++ return -ENOMEM; ++ } ++ ++ // TOOD: check channels are connected ++ ++ channels_bitmap = (1 << params.input_channel_index) | (1 << params.output_channel_index); ++ ++ err = close_channels(board, channels_bitmap); ++ if (0 != err) { ++ hailo_dev_err(&board->pDev->dev, "Error closing channels\n"); ++ return err; ++ } ++ ++ // Store the channel state in bitmap (closed) ++ hailo_clear_bit(params.input_channel_index, &context->soc_used_channels_bitmap); ++ hailo_clear_bit(params.output_channel_index, &context->soc_used_channels_bitmap); ++ ++ return err; ++} ++ ++int hailo_soc_file_context_init(struct hailo_pcie_board *board, struct hailo_file_context *context) ++{ ++ // Nothing to init yet ++ return 0; ++} ++ ++void hailo_soc_file_context_finalize(struct hailo_pcie_board *board, struct hailo_file_context *context) ++{ ++ // close only channels connected by this (by bitmap) ++ if (context->soc_used_channels_bitmap != 0) { ++ close_channels(board, context->soc_used_channels_bitmap); ++ } ++} ++ ++int hailo_soc_driver_down(struct hailo_pcie_board *board) ++{ ++ return close_channels(board, 0xFFFFFFFF); ++} +\ No newline at end of file +diff --git a/drivers/media/pci/hailo/src/soc.h b/drivers/media/pci/hailo/src/soc.h +new file mode 100644 +index 000000000000..6b2e64deb300 +--- /dev/null ++++ b/drivers/media/pci/hailo/src/soc.h +@@ -0,0 +1,26 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/** ++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. ++ **/ ++ ++#ifndef _HAILO_PCI_SOC_IOCTL_H_ ++#define _HAILO_PCI_SOC_IOCTL_H_ ++ ++#include "vdma/ioctl.h" ++#include "pcie.h" ++ ++ ++void hailo_soc_init(struct hailo_pcie_soc *soc); ++ ++long hailo_soc_ioctl(struct hailo_pcie_board *board, struct hailo_file_context *context, ++ struct hailo_vdma_controller *controller, unsigned int cmd, unsigned long arg); ++long hailo_soc_connect_ioctl(struct hailo_pcie_board *board, struct hailo_file_context *context, ++ struct hailo_vdma_controller *controller, unsigned long arg); ++long hailo_soc_close_ioctl(struct hailo_pcie_board *board, struct hailo_vdma_controller *controller, struct hailo_file_context *context, unsigned long arg); ++ ++int hailo_soc_file_context_init(struct hailo_pcie_board *board, struct hailo_file_context *context); ++void hailo_soc_file_context_finalize(struct hailo_pcie_board *board, struct hailo_file_context *context); ++ ++int hailo_soc_driver_down(struct hailo_pcie_board *board); ++ ++#endif // _HAILO_PCI_SOC_IOCTL_H_ +\ No newline at end of file diff --git a/drivers/media/pci/hailo/src/sysfs.c b/drivers/media/pci/hailo/src/sysfs.c new file mode 100644 -index 000000000000..6b914fb58aea +index 000000000000..67bccac5e097 --- /dev/null +++ b/drivers/media/pci/hailo/src/sysfs.c @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: GPL-2.0 +/** -+ * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved. ++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. + **/ + +#include "sysfs.h" @@ -137330,13 +141760,13 @@ index 000000000000..6b914fb58aea +const struct attribute_group **g_hailo_dev_groups = hailo_dev_groups; diff --git a/drivers/media/pci/hailo/src/sysfs.h b/drivers/media/pci/hailo/src/sysfs.h new file mode 100644 -index 000000000000..eaf9e7231fc4 +index 000000000000..135fb37f7942 --- /dev/null +++ b/drivers/media/pci/hailo/src/sysfs.h @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: GPL-2.0 +/** -+ * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved. ++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. + **/ + +#ifndef _HAILO_PCI_SYSFS_H_ @@ -137347,38 +141777,6 @@ index 000000000000..eaf9e7231fc4 +extern const struct attribute_group **g_hailo_dev_groups; + +#endif /* _HAILO_PCI_SYSFS_H_ */ -diff --git a/drivers/media/pci/hailo/src/utils.c b/drivers/media/pci/hailo/src/utils.c -new file mode 100644 -index 000000000000..5f87e2c00e89 ---- /dev/null -+++ b/drivers/media/pci/hailo/src/utils.c -@@ -0,0 +1,26 @@ -+// SPDX-License-Identifier: GPL-2.0 -+/** -+ * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved. -+ **/ -+ -+#include -+#include -+#include -+#include -+ -+#include "pcie.h" -+#include "utils.h" -+#include "utils/logs.h" -+ -+ -+void hailo_pcie_clear_notification_wait_list(struct hailo_pcie_board *pBoard, struct file *filp) -+{ -+ struct hailo_notification_wait *cur = NULL, *next = NULL; -+ list_for_each_entry_safe(cur, next, &pBoard->notification_wait_list, notification_wait_list) { -+ if (cur->filp == filp) { -+ list_del_rcu(&cur->notification_wait_list); -+ synchronize_rcu(); -+ kfree(cur); -+ } -+ } -+} diff --git a/drivers/media/pci/hailo/src/utils.h b/drivers/media/pci/hailo/src/utils.h new file mode 100644 index 000000000000..b357150086c7 @@ -137408,13 +141806,13 @@ index 000000000000..b357150086c7 +#endif /* _HAILO_PCI_UTILS_H_ */ diff --git a/drivers/media/pci/hailo/utils/compact.h b/drivers/media/pci/hailo/utils/compact.h new file mode 100644 -index 000000000000..c33967d0c2f2 +index 000000000000..81d0dd5c0531 --- /dev/null +++ b/drivers/media/pci/hailo/utils/compact.h -@@ -0,0 +1,153 @@ +@@ -0,0 +1,161 @@ +// SPDX-License-Identifier: GPL-2.0 +/** -+ * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved. ++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. + **/ + +#ifndef _HAILO_PCI_COMPACT_H_ @@ -137462,6 +141860,14 @@ index 000000000000..c33967d0c2f2 +} +#endif + ++#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 8, 0) ++static inline void dma_sync_sgtable_for_device(struct device *dev, ++ struct sg_table *sgt, enum dma_data_direction dir) ++{ ++ dma_sync_sg_for_device(dev, sgt->sgl, sgt->orig_nents, dir); ++} ++#endif ++ +#ifndef _LINUX_MMAP_LOCK_H +static inline void mmap_read_lock(struct mm_struct *mm) +{ @@ -137568,13 +141974,13 @@ index 000000000000..c33967d0c2f2 \ No newline at end of file diff --git a/drivers/media/pci/hailo/utils/fw_common.h b/drivers/media/pci/hailo/utils/fw_common.h new file mode 100644 -index 000000000000..713b180acc66 +index 000000000000..5f61cf3f0739 --- /dev/null +++ b/drivers/media/pci/hailo/utils/fw_common.h @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: GPL-2.0 +/** -+ * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved. ++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. + **/ + +#ifndef _HAILO_LINUX_COMMON_H_ @@ -137594,13 +142000,13 @@ index 000000000000..713b180acc66 \ No newline at end of file diff --git a/drivers/media/pci/hailo/utils/integrated_nnc_utils.c b/drivers/media/pci/hailo/utils/integrated_nnc_utils.c new file mode 100755 -index 000000000000..599e740b3b1c +index 000000000000..4b717e42b4e9 --- /dev/null +++ b/drivers/media/pci/hailo/utils/integrated_nnc_utils.c -@@ -0,0 +1,101 @@ +@@ -0,0 +1,109 @@ +// SPDX-License-Identifier: GPL-2.0 +/** -+ * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved. ++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. + **/ + +#include "integrated_nnc_utils.h" @@ -137643,11 +142049,19 @@ index 000000000000..599e740b3b1c + void __iomem * remap_ptr; + + shmem = of_parse_phandle(pdev->dev.of_node, "shmem", index); ++ if (!shmem) { ++ hailo_dev_err(&pdev->dev, "Failed to find shmem node index: %d in device tree\n", index); ++ return -ENODEV; ++ } ++ + ret = of_address_to_resource(shmem, 0, &res); + if (ret) { + hailo_dev_err(&pdev->dev, "hailo_ioremap_shmem, failed to get memory (index: %d)\n", index); ++ of_node_put(shmem); + return ret; + } ++ ++ // Decrement the refcount of the node + of_node_put(shmem); + + remap_ptr = devm_ioremap(&pdev->dev, res.start, resource_size(&res)); @@ -137702,13 +142116,13 @@ index 000000000000..599e740b3b1c \ No newline at end of file diff --git a/drivers/media/pci/hailo/utils/integrated_nnc_utils.h b/drivers/media/pci/hailo/utils/integrated_nnc_utils.h new file mode 100755 -index 000000000000..8995f44fef95 +index 000000000000..4ec23d8ce3f9 --- /dev/null +++ b/drivers/media/pci/hailo/utils/integrated_nnc_utils.h @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: GPL-2.0 +/** -+ * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved. ++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. + **/ + +#ifndef _INTEGRATED_NNC_UTILS_H_ @@ -137738,13 +142152,13 @@ index 000000000000..8995f44fef95 +#endif /* _INTEGRATED_NNC_UTILS_H_ */ diff --git a/drivers/media/pci/hailo/utils/logs.c b/drivers/media/pci/hailo/utils/logs.c new file mode 100644 -index 000000000000..a215d3ddda41 +index 000000000000..3787fe1ffd06 --- /dev/null +++ b/drivers/media/pci/hailo/utils/logs.c @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0 +/** -+ * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved. ++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. + **/ + +#include "logs.h" @@ -137752,13 +142166,13 @@ index 000000000000..a215d3ddda41 +int o_dbg = LOGLEVEL_NOTICE; diff --git a/drivers/media/pci/hailo/utils/logs.h b/drivers/media/pci/hailo/utils/logs.h new file mode 100644 -index 000000000000..4179ef439003 +index 000000000000..95b80a405249 --- /dev/null +++ b/drivers/media/pci/hailo/utils/logs.h @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: GPL-2.0 +/** -+ * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved. ++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. + **/ + +#ifndef _COMMON_LOGS_H_ @@ -137804,13 +142218,13 @@ index 000000000000..4179ef439003 \ No newline at end of file diff --git a/drivers/media/pci/hailo/vdma/ioctl.c b/drivers/media/pci/hailo/vdma/ioctl.c new file mode 100644 -index 000000000000..2b29e2461509 +index 000000000000..d5be26a6d188 --- /dev/null +++ b/drivers/media/pci/hailo/vdma/ioctl.c -@@ -0,0 +1,715 @@ +@@ -0,0 +1,721 @@ +// SPDX-License-Identifier: GPL-2.0 +/** -+ * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved. ++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. + **/ + +#include "ioctl.h" @@ -137822,7 +142236,7 @@ index 000000000000..2b29e2461509 +#include + + -+long hailo_vdma_enable_channels_ioctl(struct hailo_vdma_controller *controller, unsigned long arg) ++long hailo_vdma_enable_channels_ioctl(struct hailo_vdma_controller *controller, unsigned long arg, struct hailo_vdma_file_context *context) +{ + struct hailo_vdma_enable_channels_params input; + struct hailo_vdma_engine *engine = NULL; @@ -137850,12 +142264,15 @@ index 000000000000..2b29e2461509 + hailo_vdma_update_interrupts_mask(controller, engine_index); + hailo_dev_info(controller->dev, "Enabled interrupts for engine %u, channels bitmap 0x%x\n", + engine_index, channels_bitmap); ++ ++ // Update the context with the enabled channels bitmap ++ context->enabled_channels_bitmap[engine_index] |= channels_bitmap; + } + + return 0; +} + -+long hailo_vdma_disable_channels_ioctl(struct hailo_vdma_controller *controller, unsigned long arg) ++long hailo_vdma_disable_channels_ioctl(struct hailo_vdma_controller *controller, unsigned long arg, struct hailo_vdma_file_context *context) +{ + struct hailo_vdma_disable_channels_params input; + struct hailo_vdma_engine *engine = NULL; @@ -137887,6 +142304,9 @@ index 000000000000..2b29e2461509 + + hailo_dev_info(controller->dev, "Disabled channels for engine %u, bitmap 0x%x\n", + engine_index, channels_bitmap); ++ ++ // Update the context with the disabled channels bitmap ++ context->enabled_channels_bitmap[engine_index] &= ~channels_bitmap; + } + + // Wake up threads waiting @@ -138014,7 +142434,7 @@ index 000000000000..2b29e2461509 + return -EFAULT; + } + -+ hailo_dev_info(controller->dev, "address %lx tgid %d size: %zu\n", ++ hailo_dev_dbg(controller->dev, "address %lx tgid %d size: %zu\n", + buf_info.user_address, current->tgid, buf_info.size); + + direction = get_dma_direction(buf_info.data_direction); @@ -138041,7 +142461,7 @@ index 000000000000..2b29e2461509 + } + + list_add(&mapped_buffer->mapped_user_buffer_list, &context->mapped_user_buffer_list); -+ hailo_dev_info(controller->dev, "buffer %lx (handle %zu) is mapped\n", ++ hailo_dev_dbg(controller->dev, "buffer %lx (handle %zu) is mapped\n", + buf_info.user_address, buf_info.mapped_handle); + return 0; +} @@ -138057,7 +142477,7 @@ index 000000000000..2b29e2461509 + return -EFAULT; + } + -+ hailo_dev_info(controller->dev, "unmap user buffer handle %zu\n", buffer_unmap_params.mapped_handle); ++ hailo_dev_dbg(controller->dev, "unmap user buffer handle %zu\n", buffer_unmap_params.mapped_handle); + + mapped_buffer = hailo_vdma_find_mapped_user_buffer(context, buffer_unmap_params.mapped_handle); + if (mapped_buffer == NULL) { @@ -138526,13 +142946,13 @@ index 000000000000..2b29e2461509 \ No newline at end of file diff --git a/drivers/media/pci/hailo/vdma/ioctl.h b/drivers/media/pci/hailo/vdma/ioctl.h new file mode 100644 -index 000000000000..d2b480d2d422 +index 000000000000..a9016c3162a3 --- /dev/null +++ b/drivers/media/pci/hailo/vdma/ioctl.h @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: GPL-2.0 +/** -+ * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved. ++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. + **/ + +#ifndef _HAILO_VDMA_IOCTL_H_ @@ -138540,8 +142960,8 @@ index 000000000000..d2b480d2d422 + +#include "vdma/vdma.h" + -+long hailo_vdma_enable_channels_ioctl(struct hailo_vdma_controller *controller, unsigned long arg); -+long hailo_vdma_disable_channels_ioctl(struct hailo_vdma_controller *controller, unsigned long arg); ++long hailo_vdma_enable_channels_ioctl(struct hailo_vdma_controller *controller, unsigned long arg, struct hailo_vdma_file_context *context); ++long hailo_vdma_disable_channels_ioctl(struct hailo_vdma_controller *controller, unsigned long arg, struct hailo_vdma_file_context *context); +long hailo_vdma_interrupts_wait_ioctl(struct hailo_vdma_controller *controller, unsigned long arg, + struct semaphore *mutex, bool *should_up_board_mutex); + @@ -138570,18 +142990,19 @@ index 000000000000..d2b480d2d422 \ No newline at end of file diff --git a/drivers/media/pci/hailo/vdma/memory.c b/drivers/media/pci/hailo/vdma/memory.c new file mode 100644 -index 000000000000..b06de38579fb +index 000000000000..8c210c420a95 --- /dev/null +++ b/drivers/media/pci/hailo/vdma/memory.c -@@ -0,0 +1,661 @@ +@@ -0,0 +1,729 @@ +// SPDX-License-Identifier: GPL-2.0 +/** -+ * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved. ++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. + **/ + +#define pr_fmt(fmt) "hailo: " fmt + +#include "memory.h" ++#include "utils.h" +#include "utils/compact.h" + +#include @@ -138589,10 +143010,10 @@ index 000000000000..b06de38579fb +#include +#include + -+ -+#define SGL_MAX_SEGMENT_SIZE (0x10000) +// See linux/mm.h +#define MMIO_AND_NO_PAGES_VMA_MASK (VM_IO | VM_PFNMAP) ++// The linux kernel names the dmabuf's vma vm_file field "dmabuf" ++#define VMA_VM_FILE_DMABUF_NAME ("dmabuf") + +static int map_mmio_address(uintptr_t user_address, u32 size, struct vm_area_struct *vma, + struct sg_table *sgt); @@ -138602,10 +143023,16 @@ index 000000000000..b06de38579fb + +#if LINUX_VERSION_CODE >= KERNEL_VERSION( 3, 3, 0 ) + -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 16, 0) ++#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 13, 0) ++#define DMA_NS_NAME DMA_BUF ++#else ++#define DMA_NS_NAME "DMA_BUF" ++#endif // LINUX_VERSION_CODE < KERNEL_VERSION(6, 13, 0) ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 15, 0) +// Import DMA_BUF namespace for needed kernels -+MODULE_IMPORT_NS(DMA_BUF); -+#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(5, 16, 0) */ ++MODULE_IMPORT_NS(DMA_NS_NAME); ++#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(5, 15, 0) */ + +static int hailo_map_dmabuf(struct device *dev, int dmabuf_fd, enum dma_data_direction direction, struct sg_table *sgt, + struct hailo_dmabuf_info *dmabuf_info) @@ -138678,6 +143105,39 @@ index 000000000000..b06de38579fb + +#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION( 3, 3, 0 ) */ + ++// Function that checks if the vma is backed by a mapped dmabuf ++static bool is_dmabuf_vma(struct vm_area_struct *vma) ++{ ++ return (vma && vma->vm_file && (0 == strcmp(vma->vm_file->f_path.dentry->d_name.name, VMA_VM_FILE_DMABUF_NAME))); ++} ++ ++static int create_fd_from_vma(struct device *dev, struct vm_area_struct *vma) { ++ struct file *file = NULL; ++ int fd = 0; ++ ++ if (!vma || !vma->vm_file) { ++ dev_err(dev, "Invalid VMA or no associated file.\n"); ++ return -EINVAL; ++ } ++ ++ file = vma->vm_file; ++ ++ // This functions increments the ref count of the file ++ get_file(file); ++ ++ // 0 for default flags ++ fd = get_unused_fd_flags(0); ++ if (fd < 0) { ++ dev_err(dev, "Failed to get unused file descriptor.\n"); ++ fput(file); ++ return fd; ++ } ++ ++ // Install the file into the file descriptor table ++ fd_install(fd, file); ++ return fd; ++} ++ +struct hailo_vdma_buffer *hailo_vdma_buffer_map(struct device *dev, + uintptr_t user_address, size_t size, enum dma_data_direction direction, + enum hailo_dma_buffer_type buffer_type, struct hailo_vdma_low_memory_buffer *low_mem_driver_allocated_buffer) @@ -138687,7 +143147,8 @@ index 000000000000..b06de38579fb + struct sg_table sgt = {0}; + struct vm_area_struct *vma = NULL; + bool is_mmio = false; -+ struct hailo_dmabuf_info dmabuf_info = {0}; ++ struct hailo_dmabuf_info dmabuf_info = {0}; ++ bool created_dmabuf_fd_from_vma = false; + + mapped_buffer = kzalloc(sizeof(*mapped_buffer), GFP_KERNEL); + if (NULL == mapped_buffer) { @@ -138696,12 +143157,27 @@ index 000000000000..b06de38579fb + goto cleanup; + } + -+ if (IS_ENABLED(HAILO_SUPPORT_MMIO_DMA_MAPPING) && (HAILO_DMA_DMABUF_BUFFER != buffer_type)) { ++ if (HAILO_DMA_DMABUF_BUFFER != buffer_type) { + vma = find_vma(current->mm, user_address); -+ if (NULL == vma) { -+ dev_err(dev, "no vma for virt_addr/size = 0x%08lx/0x%08zx\n", user_address, size); -+ ret = -EFAULT; -+ goto cleanup; ++ if (IS_ENABLED(HAILO_SUPPORT_MMIO_DMA_MAPPING)) { ++ if (NULL == vma) { ++ dev_err(dev, "no vma for virt_addr/size = 0x%08lx/0x%08zx\n", user_address, size); ++ ret = -EFAULT; ++ goto cleanup; ++ } ++ } ++ ++ if (is_dmabuf_vma(vma)) { ++ dev_dbg(dev, "Given vma is backed by dmabuf - creating fd and mapping as dmabuf\n"); ++ buffer_type = HAILO_DMA_DMABUF_BUFFER; ++ ret = create_fd_from_vma(dev, vma); ++ if (ret < 0) { ++ dev_err(dev, "Failed creating fd from vma in given dmabuf\n"); ++ goto cleanup; ++ } ++ // Override user address with fd to the dmabuf - like normal dmabuf flow ++ user_address = ret; ++ created_dmabuf_fd_from_vma = true; + } + } + @@ -138732,6 +143208,11 @@ index 000000000000..b06de38579fb + dev_err(dev, "Failed mapping dmabuf\n"); + goto cleanup; + } ++ // If created dmabuf fd from vma need to decrement refcount and release fd ++ if (created_dmabuf_fd_from_vma) { ++ fput(vma->vm_file); ++ put_unused_fd(user_address); ++ } + } else { + // user_address is a standard 'struct page' backed memory address + ret = prepare_sg_table(&sgt, user_address, size, low_mem_driver_allocated_buffer); @@ -138892,6 +143373,11 @@ index 000000000000..b06de38579fb + size_t buffer_size = 0; + const u64 align = VDMA_DESCRIPTOR_LIST_ALIGN; //First addr must be aligned on 64 KB (from the VDMA registers documentation) + ++ if (MAX_POWER_OF_2_VALUE < descriptors_count) { ++ dev_err(dev, "Invalid descriptors count %u\n", descriptors_count); ++ return -EINVAL; ++ } ++ + buffer_size = descriptors_count * sizeof(struct hailo_vdma_descriptor); + buffer_size = ALIGN(buffer_size, align); + @@ -138899,9 +143385,9 @@ index 000000000000..b06de38579fb + &descriptors->dma_address, GFP_KERNEL | __GFP_ZERO); + if (descriptors->kernel_address == NULL) { + dev_err(dev, "Failed to allocate descriptors list, desc_count 0x%x, buffer_size 0x%zx, This failure means there is not a sufficient amount of CMA memory " -+ "(contiguous physical memory), This usually is caused by lack of general system memory. Please check you have sufficent memory.\n", ++ "(contiguous physical memory), This usually is caused by lack of general system memory. Please check you have sufficient memory.\n", + descriptors_count, buffer_size); -+ return -ENOMEM; ++ return -ENOBUFS; + } + + descriptors->buffer_size = buffer_size; @@ -138909,6 +143395,8 @@ index 000000000000..b06de38579fb + + descriptors->desc_list.desc_list = descriptors->kernel_address; + descriptors->desc_list.desc_count = descriptors_count; ++ // No need to check the return value of get_nearest_powerof_2 because we already checked the input ++ descriptors->desc_list.desc_count_mask = is_circular ? (descriptors_count - 1) : (get_nearest_powerof_2(descriptors_count) - 1); + descriptors->desc_list.desc_page_size = desc_page_size; + descriptors->desc_list.is_circular = is_circular; + @@ -139035,7 +143523,7 @@ index 000000000000..b06de38579fb + if (NULL == kernel_address) { + dev_warn(dev, "Failed to allocate continuous buffer, size 0x%zx. This failure means there is not a sufficient amount of CMA memory " + "(contiguous physical memory), This usually is caused by lack of general system memory. Please check you have sufficent memory.\n", size); -+ return -ENOMEM; ++ return -ENOBUFS; + } + + continuous_buffer->kernel_address = kernel_address; @@ -139237,13 +143725,13 @@ index 000000000000..b06de38579fb +} diff --git a/drivers/media/pci/hailo/vdma/memory.h b/drivers/media/pci/hailo/vdma/memory.h new file mode 100644 -index 000000000000..5819639fc899 +index 000000000000..f8bffcf91432 --- /dev/null +++ b/drivers/media/pci/hailo/vdma/memory.h -@@ -0,0 +1,54 @@ +@@ -0,0 +1,56 @@ +// SPDX-License-Identifier: GPL-2.0 +/** -+ * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved. ++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. + **/ +/** + * vDMA memory utility (including allocation and mappings) @@ -139254,6 +143742,8 @@ index 000000000000..5819639fc899 + +#include "vdma/vdma.h" + ++#define SGL_MAX_SEGMENT_SIZE (0x10000) ++ +struct hailo_vdma_buffer *hailo_vdma_buffer_map(struct device *dev, uintptr_t user_address, size_t size, + enum dma_data_direction direction, enum hailo_dma_buffer_type buffer_type, + struct hailo_vdma_low_memory_buffer *low_mem_driver_allocated_buffer); @@ -139298,13 +143788,13 @@ index 000000000000..5819639fc899 \ No newline at end of file diff --git a/drivers/media/pci/hailo/vdma/vdma.c b/drivers/media/pci/hailo/vdma/vdma.c new file mode 100644 -index 000000000000..413a67f84d40 +index 000000000000..0ad2c5016a8f --- /dev/null +++ b/drivers/media/pci/hailo/vdma/vdma.c -@@ -0,0 +1,302 @@ +@@ -0,0 +1,313 @@ +// SPDX-License-Identifier: GPL-2.0 +/** -+ * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved. ++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. + **/ + +#define pr_fmt(fmt) "hailo: " fmt @@ -139409,6 +143899,9 @@ index 000000000000..413a67f84d40 + INIT_LIST_HEAD(&context->descriptors_buffer_list); + INIT_LIST_HEAD(&context->vdma_low_memory_buffer_list); + INIT_LIST_HEAD(&context->continuous_buffer_list); ++ ++ BUILD_BUG_ON_MSG(MAX_VDMA_CHANNELS_PER_ENGINE > sizeof(context->enabled_channels_bitmap[0]) * BITS_IN_BYTE, ++ "Unexpected amount of VDMA channels per engine"); +} + +void hailo_vdma_update_interrupts_mask(struct hailo_vdma_controller *controller, @@ -139423,21 +143916,22 @@ index 000000000000..413a67f84d40 +{ + size_t engine_index = 0; + struct hailo_vdma_engine *engine = NULL; -+ const u32 channels_bitmap = 0xFFFFFFFF; // disable all channel interrupts + unsigned long irq_saved_flags = 0; + // In case of FLR, the vdma registers will be NULL + const bool is_device_up = (NULL != controller->dev); + -+ if (filp == controller->used_by_filp) { -+ for_each_vdma_engine(controller, engine, engine_index) { -+ hailo_vdma_engine_disable_channels(engine, channels_bitmap); ++ for_each_vdma_engine(controller, engine, engine_index) { ++ if (context->enabled_channels_bitmap[engine_index]) { ++ hailo_dev_info(controller->dev, "Disabling channels for engine %zu, channels bitmap 0x%x\n", engine_index, ++ context->enabled_channels_bitmap[engine_index]); ++ hailo_vdma_engine_disable_channels(engine, context->enabled_channels_bitmap[engine_index]); + + if (is_device_up) { + hailo_vdma_update_interrupts_mask(controller, engine_index); + } + + spin_lock_irqsave(&controller->interrupts_lock, irq_saved_flags); -+ hailo_vdma_engine_clear_channel_interrupts(engine, channels_bitmap); ++ hailo_vdma_engine_clear_channel_interrupts(engine, context->enabled_channels_bitmap[engine_index]); + spin_unlock_irqrestore(&controller->interrupts_lock, irq_saved_flags); + } + } @@ -139452,10 +143946,21 @@ index 000000000000..413a67f84d40 + } +} + ++void hailo_vdma_wakeup_interrupts(struct hailo_vdma_controller *controller, struct hailo_vdma_engine *engine, ++ u32 channels_bitmap) ++{ ++ unsigned long irq_saved_flags = 0; ++ ++ spin_lock_irqsave(&controller->interrupts_lock, irq_saved_flags); ++ hailo_vdma_engine_set_channel_interrupts(engine, channels_bitmap); ++ spin_unlock_irqrestore(&controller->interrupts_lock, irq_saved_flags); ++ ++ wake_up_interruptible_all(&controller->interrupts_wq); ++} ++ +void hailo_vdma_irq_handler(struct hailo_vdma_controller *controller, + size_t engine_index, u32 channels_bitmap) +{ -+ unsigned long irq_saved_flags = 0; + struct hailo_vdma_engine *engine = NULL; + + BUG_ON(engine_index >= controller->vdma_engines_count); @@ -139463,11 +143968,7 @@ index 000000000000..413a67f84d40 + + hailo_vdma_engine_push_timestamps(engine, channels_bitmap); + -+ spin_lock_irqsave(&controller->interrupts_lock, irq_saved_flags); -+ hailo_vdma_engine_set_channel_interrupts(engine, channels_bitmap); -+ spin_unlock_irqrestore(&controller->interrupts_lock, irq_saved_flags); -+ -+ wake_up_interruptible_all(&controller->interrupts_wq); ++ hailo_vdma_wakeup_interrupts(controller, engine, channels_bitmap); +} + +long hailo_vdma_ioctl(struct hailo_vdma_file_context *context, struct hailo_vdma_controller *controller, @@ -139475,9 +143976,9 @@ index 000000000000..413a67f84d40 +{ + switch (cmd) { + case HAILO_VDMA_ENABLE_CHANNELS: -+ return hailo_vdma_enable_channels_ioctl(controller, arg); ++ return hailo_vdma_enable_channels_ioctl(controller, arg, context); + case HAILO_VDMA_DISABLE_CHANNELS: -+ return hailo_vdma_disable_channels_ioctl(controller, arg); ++ return hailo_vdma_disable_channels_ioctl(controller, arg, context); + case HAILO_VDMA_INTERRUPTS_WAIT: + return hailo_vdma_interrupts_wait_ioctl(controller, arg, mutex, should_up_board_mutex); + case HAILO_VDMA_INTERRUPTS_READ_TIMESTAMPS: @@ -139606,13 +144107,13 @@ index 000000000000..413a67f84d40 +} diff --git a/drivers/media/pci/hailo/vdma/vdma.h b/drivers/media/pci/hailo/vdma/vdma.h new file mode 100644 -index 000000000000..c518aefc9a0c +index 000000000000..d0e693e879c3 --- /dev/null +++ b/drivers/media/pci/hailo/vdma/vdma.h -@@ -0,0 +1,161 @@ +@@ -0,0 +1,164 @@ +// SPDX-License-Identifier: GPL-2.0 +/** -+ * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved. ++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. + **/ +/** + * Hailo vdma engine definitions @@ -139742,6 +144243,7 @@ index 000000000000..c518aefc9a0c + struct list_head descriptors_buffer_list; + struct list_head vdma_low_memory_buffer_list; + struct list_head continuous_buffer_list; ++ u32 enabled_channels_bitmap[MAX_VDMA_ENGINES]; +}; + + @@ -139757,6 +144259,8 @@ index 000000000000..c518aefc9a0c +void hailo_vdma_file_context_finalize(struct hailo_vdma_file_context *context, + struct hailo_vdma_controller *controller, struct file *filp); + ++void hailo_vdma_wakeup_interrupts(struct hailo_vdma_controller *controller, struct hailo_vdma_engine *engine, ++ u32 channels_bitmap); +void hailo_vdma_irq_handler(struct hailo_vdma_controller *controller, size_t engine_index, + u32 channels_bitmap); + @@ -145567,10 +150071,10 @@ index 000000000000..f1628dcda340 +MODULE_LICENSE("GPL"); diff --git a/drivers/media/platform/raspberrypi/pisp_be/pisp_be_formats.h b/drivers/media/platform/raspberrypi/pisp_be/pisp_be_formats.h new file mode 100644 -index 000000000000..b5cb7b8c7531 +index 000000000000..09edc2774668 --- /dev/null +++ b/drivers/media/platform/raspberrypi/pisp_be/pisp_be_formats.h -@@ -0,0 +1,519 @@ +@@ -0,0 +1,529 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * PiSP Back End driver image format definitions. @@ -145702,6 +150206,16 @@ index 000000000000..b5cb7b8c7531 + .colorspace_mask = V4L2_COLORSPACE_MASK_ALL_SRGB, + .colorspace_default = V4L2_COLORSPACE_SMPTE170M, + }, ++ { ++ .fourcc = V4L2_PIX_FMT_YUV422P, ++ /* 128 alignment to ensure U/V planes are 64 byte aligned. */ ++ .align = 128, ++ .bit_depth = 8, ++ .plane_factor = { P3(1), P3(0.5), P3(0.5) }, ++ .num_planes = 1, ++ .colorspace_mask = V4L2_COLORSPACE_MASK_ALL_SRGB, ++ .colorspace_default = V4L2_COLORSPACE_SMPTE170M, ++ }, + /* Multiplane YUV formats */ + { + .fourcc = V4L2_PIX_FMT_YUV420M, @@ -152059,10 +156573,10 @@ index 6eda79533208..67f74304f2bf 100644 }; MODULE_DEVICE_TABLE(of, simple_mfd_i2c_of_match); diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig -index 452e8ea8bfd7..c3e2cbde2f22 100644 +index 452e8ea8bfd7..6d29cb9f9cb2 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig -@@ -9,6 +9,14 @@ config SENSORS_LIS3LV02D +@@ -9,6 +9,32 @@ config SENSORS_LIS3LV02D tristate depends on INPUT @@ -152073,12 +156587,30 @@ index 452e8ea8bfd7..c3e2cbde2f22 100644 + help + Driver for enabling and using Broadcom's Secondary/Slow Memory Interface. + Appears as /dev/bcm2835_smi. For ioctl interface see drivers/misc/bcm2835_smi.h ++ ++config RP1_PIO ++ tristate "Raspberry Pi RP1 PIO driver" ++ depends on FIRMWARE_RP1 || COMPILE_TEST ++ default n ++ help ++ Driver providing control of the Raspberry Pi PIO block, as found in ++ RP1. ++ ++config WS2812_PIO_RP1 ++ tristate "Raspberry Pi PIO-base WS2812 driver" ++ depends on RP1_PIO || COMPILE_TEST ++ default n ++ help ++ Driver for the WS2812 (NeoPixel) LEDs using the RP1 PIO hardware. ++ The driver creates a character device to which rgbw pixels may be ++ written. Single-byte writes to offset 0 set the brightness at ++ runtime. + config AD525X_DPOT tristate "Analog Devices Digital Potentiometers" depends on (I2C || SPI) && SYSFS diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile -index 16fe31fbf7d4..8e59afc0b1a3 100644 +index 16fe31fbf7d4..4f8161ec1d4f 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -9,6 +9,7 @@ obj-$(CONFIG_AD525X_DPOT) += ad525x_dpot.o @@ -152089,6 +156621,15 @@ index 16fe31fbf7d4..8e59afc0b1a3 100644 obj-$(CONFIG_DUMMY_IRQ) += dummy-irq.o obj-$(CONFIG_ICS932S401) += ics932s401.o obj-$(CONFIG_LKDTM) += lkdtm/ +@@ -17,6 +18,8 @@ obj-$(CONFIG_TIFM_7XX1) += tifm_7xx1.o + obj-$(CONFIG_PHANTOM) += phantom.o + obj-$(CONFIG_QCOM_COINCELL) += qcom-coincell.o + obj-$(CONFIG_QCOM_FASTRPC) += fastrpc.o ++obj-$(CONFIG_RP1_PIO) += rp1-pio.o ++obj-$(CONFIG_WS2812_PIO_RP1) += ws2812-pio-rp1.o + obj-$(CONFIG_SENSORS_BH1770) += bh1770glc.o + obj-$(CONFIG_SENSORS_APDS990X) += apds990x.o + obj-$(CONFIG_ENCLOSURE_SERVICES) += enclosure.o diff --git a/drivers/misc/bcm2835_smi.c b/drivers/misc/bcm2835_smi.c new file mode 100644 index 000000000000..acb54c2224cd @@ -153048,6 +157589,1976 @@ index 000000000000..acb54c2224cd +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Device driver for BCM2835's secondary memory interface"); +MODULE_AUTHOR("Luke Wren "); +diff --git a/drivers/misc/rp1-fw-pio.h b/drivers/misc/rp1-fw-pio.h +new file mode 100644 +index 000000000000..ba28cba38f84 +--- /dev/null ++++ b/drivers/misc/rp1-fw-pio.h +@@ -0,0 +1,56 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++/* ++ * Copyright (C) 2023 2023-2024 Raspberry Pi Ltd. ++ */ ++ ++#ifndef __SOC_RP1_FIRMWARE_OPS_H__ ++#define __SOC_RP1_FIRMWARE_OPS_H__ ++ ++#include ++ ++#define FOURCC_PIO RP1_FOURCC("PIO ") ++ ++enum rp1_pio_ops { ++ PIO_CAN_ADD_PROGRAM, // u16 num_instrs, u16 origin -> origin ++ PIO_ADD_PROGRAM, // u16 num_instrs, u16 origin, u16 prog[] -> rc ++ PIO_REMOVE_PROGRAM, // u16 num_instrs, u16 origin ++ PIO_CLEAR_INSTR_MEM, // - ++ ++ PIO_SM_CLAIM, // u16 mask -> sm ++ PIO_SM_UNCLAIM, // u16 mask ++ PIO_SM_IS_CLAIMED, // u16 mask -> claimed ++ ++ PIO_SM_INIT, // u16 sm, u16 initial_pc, u32 sm_config[4] ++ PIO_SM_SET_CONFIG, // u16 sm, u16 rsvd, u32 sm_config[4] ++ PIO_SM_EXEC, // u16 sm, u16 instr, u8 blocking, u8 rsvd ++ PIO_SM_CLEAR_FIFOS, // u16 sm ++ PIO_SM_SET_CLKDIV, // u16 sm, u16 div_int, u8 div_frac, u8 rsvd ++ PIO_SM_SET_PINS, // u16 sm, u16 rsvd, u32 values, u32 mask ++ PIO_SM_SET_PINDIRS, // u16 sm, u16 rsvd, u32 dirs, u32 mask ++ PIO_SM_SET_ENABLED, // u16 mask, u8 enable, u8 rsvd ++ PIO_SM_RESTART, // u16 mask ++ PIO_SM_CLKDIV_RESTART, // u16 mask ++ PIO_SM_ENABLE_SYNC, // u16 mask ++ PIO_SM_PUT, // u16 sm, u8 blocking, u8 rsvd, u32 data ++ PIO_SM_GET, // u16 sm, u8 blocking, u8 rsvd -> u32 data ++ PIO_SM_SET_DMACTRL, // u16 sm, u16 is_tx, u32 ctrl ++ ++ GPIO_INIT, // u16 gpio ++ GPIO_SET_FUNCTION, // u16 gpio, u16 fn ++ GPIO_SET_PULLS, // u16 gpio, u8 up, u8 down ++ GPIO_SET_OUTOVER, // u16 gpio, u16 value ++ GPIO_SET_INOVER, // u16 gpio, u16 value ++ GPIO_SET_OEOVER, // u16 gpio, u16 value ++ GPIO_SET_INPUT_ENABLED, // u16 gpio, u16 value ++ GPIO_SET_DRIVE_STRENGTH, // u16 gpio, u16 value ++ ++ READ_HW, // src address, len -> data bytes ++ WRITE_HW, // dst address, data ++ ++ PIO_SM_FIFO_STATE, // u16 sm, u8 tx -> u16 level, u8 empty, u8 full ++ PIO_SM_DRAIN_TX, // u16 sm ++ ++ PIO_COUNT ++}; ++ ++#endif +diff --git a/drivers/misc/rp1-pio.c b/drivers/misc/rp1-pio.c +new file mode 100644 +index 000000000000..41f0a6d9c49a +--- /dev/null ++++ b/drivers/misc/rp1-pio.c +@@ -0,0 +1,1389 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * PIO driver for RP1 ++ * ++ * Copyright (C) 2023-2024 Raspberry Pi Ltd. ++ * ++ * Parts of this driver are based on: ++ * - vcio.c, by Noralf Trønnes ++ * Copyright (C) 2010 Broadcom ++ * Copyright (C) 2015 Noralf Trønnes ++ * Copyright (C) 2021 Raspberry Pi (Trading) Ltd. ++ * - bcm2835_smi.c & bcm2835_smi_dev.c by Luke Wren ++ * Copyright (c) 2015 Raspberry Pi (Trading) Ltd. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "rp1-fw-pio.h" ++ ++#define DRIVER_NAME "rp1-pio" ++ ++#define RP1_PIO_SMS_COUNT 4 ++#define RP1_PIO_INSTR_COUNT 32 ++ ++#define MAX_ARG_SIZE 256 ++ ++#define RP1_PIO_FIFO_TX0 0x00 ++#define RP1_PIO_FIFO_TX1 0x04 ++#define RP1_PIO_FIFO_TX2 0x08 ++#define RP1_PIO_FIFO_TX3 0x0c ++#define RP1_PIO_FIFO_RX0 0x10 ++#define RP1_PIO_FIFO_RX1 0x14 ++#define RP1_PIO_FIFO_RX2 0x18 ++#define RP1_PIO_FIFO_RX3 0x1c ++ ++#define RP1_PIO_DMACTRL_DEFAULT 0x80000104 ++ ++#define HANDLER(_n, _f) \ ++ [_IOC_NR(PIO_IOC_ ## _n)] = { #_n, rp1_pio_ ## _f, _IOC_SIZE(PIO_IOC_ ## _n) } ++ ++ ++#define ROUND_UP(x, y) (((x) + (y) - 1) - (((x) + (y) - 1) % (y))) ++ ++#define DMA_BOUNCE_BUFFER_SIZE 0x1000 ++#define DMA_BOUNCE_BUFFER_COUNT 4 ++ ++struct dma_xfer_state { ++ struct dma_info *dma; ++ void (*callback)(void *param); ++ void *callback_param; ++}; ++ ++struct dma_buf_info { ++ void *buf; ++ dma_addr_t dma_addr; ++ struct scatterlist sgl; ++}; ++ ++struct dma_info { ++ struct semaphore buf_sem; ++ struct dma_chan *chan; ++ size_t buf_size; ++ size_t buf_count; ++ unsigned int head_idx; ++ unsigned int tail_idx; ++ struct dma_buf_info bufs[DMA_BOUNCE_BUFFER_COUNT]; ++}; ++ ++struct rp1_pio_device { ++ struct platform_device *pdev; ++ struct rp1_firmware *fw; ++ uint16_t fw_pio_base; ++ uint16_t fw_pio_count; ++ dev_t dev_num; ++ struct class *dev_class; ++ struct cdev cdev; ++ phys_addr_t phys_addr; ++ uint32_t claimed_sms; ++ uint32_t claimed_dmas; ++ spinlock_t lock; ++ struct mutex instr_mutex; ++ struct dma_info dma_configs[RP1_PIO_SMS_COUNT][RP1_PIO_DIR_COUNT]; ++ uint32_t used_instrs; ++ uint8_t instr_refcounts[RP1_PIO_INSTR_COUNT]; ++ uint16_t instrs[RP1_PIO_INSTR_COUNT]; ++ uint client_count; ++}; ++ ++struct rp1_pio_client { ++ struct rp1_pio_device *pio; ++ uint32_t claimed_sms; ++ uint32_t claimed_instrs; ++ uint32_t claimed_dmas; ++ int error; ++}; ++ ++static struct rp1_pio_device *g_pio; ++ ++static int rp1_pio_message(struct rp1_pio_device *pio, ++ uint16_t op, const void *data, unsigned int data_len) ++{ ++ uint32_t rc; ++ int ret; ++ ++ if (op >= pio->fw_pio_count) ++ return -EOPNOTSUPP; ++ ret = rp1_firmware_message(pio->fw, pio->fw_pio_base + op, ++ data, data_len, ++ &rc, sizeof(rc)); ++ if (ret == 4) ++ ret = rc; ++ return ret; ++} ++ ++static int rp1_pio_message_resp(struct rp1_pio_device *pio, ++ uint16_t op, const void *data, unsigned int data_len, ++ void *resp, void __user *userbuf, unsigned int resp_len) ++{ ++ uint32_t resp_buf[1 + 32]; ++ int ret; ++ ++ if (op >= pio->fw_pio_count) ++ return -EOPNOTSUPP; ++ if (resp_len + 4 >= sizeof(resp_buf)) ++ return -EINVAL; ++ if (!resp && !userbuf) ++ return -EINVAL; ++ ret = rp1_firmware_message(pio->fw, pio->fw_pio_base + op, ++ data, data_len, ++ resp_buf, resp_len + 4); ++ if (ret >= 4 && !resp_buf[0]) { ++ ret -= 4; ++ if (resp) ++ memcpy(resp, &resp_buf[1], ret); ++ else if (copy_to_user(userbuf, &resp_buf[1], ret)) ++ ret = -EFAULT; ++ } else if (ret >= 0) { ++ ret = -EIO; ++ } ++ return ret; ++} ++ ++static int rp1_pio_read_hw(struct rp1_pio_client *client, void *param) ++{ ++ struct rp1_pio_device *pio = client->pio; ++ struct rp1_access_hw_args *args = param; ++ ++ return rp1_pio_message_resp(pio, READ_HW, ++ args, 8, NULL, args->data, args->len); ++} ++ ++static int rp1_pio_write_hw(struct rp1_pio_client *client, void *param) ++{ ++ struct rp1_pio_device *pio = client->pio; ++ struct rp1_access_hw_args *args = param; ++ uint32_t write_buf[32 + 1]; ++ int len; ++ ++ len = min(args->len, sizeof(write_buf) - 4); ++ write_buf[0] = args->addr; ++ if (copy_from_user(&write_buf[1], args->data, len)) ++ return -EFAULT; ++ return rp1_firmware_message(pio->fw, pio->fw_pio_base + WRITE_HW, ++ write_buf, 4 + len, NULL, 0); ++} ++ ++static int rp1_pio_find_program(struct rp1_pio_device *pio, ++ struct rp1_pio_add_program_args *prog) ++{ ++ uint start, end, prog_size; ++ uint32_t used_mask; ++ uint i; ++ ++ start = (prog->origin != RP1_PIO_ORIGIN_ANY) ? prog->origin : 0; ++ end = (prog->origin != RP1_PIO_ORIGIN_ANY) ? prog->origin : ++ (RP1_PIO_INSTRUCTION_COUNT - prog->num_instrs); ++ prog_size = sizeof(prog->instrs[0]) * prog->num_instrs; ++ used_mask = (uint32_t)(~0) >> (32 - prog->num_instrs); ++ ++ /* Find the best match */ ++ for (i = start; i <= end; i++) { ++ uint32_t mask = used_mask << i; ++ ++ if ((pio->used_instrs & mask) != mask) ++ continue; ++ if (!memcmp(pio->instrs + i, prog->instrs, prog_size)) ++ return i; ++ } ++ ++ return -1; ++} ++ ++int rp1_pio_can_add_program(struct rp1_pio_client *client, void *param) ++{ ++ struct rp1_pio_add_program_args *args = param; ++ struct rp1_pio_device *pio = client->pio; ++ int offset; ++ ++ if (args->num_instrs > RP1_PIO_INSTR_COUNT || ++ ((args->origin != RP1_PIO_ORIGIN_ANY) && ++ (args->origin >= RP1_PIO_INSTR_COUNT || ++ ((args->origin + args->num_instrs) > RP1_PIO_INSTR_COUNT)))) ++ return -EINVAL; ++ ++ mutex_lock(&pio->instr_mutex); ++ offset = rp1_pio_find_program(pio, args); ++ mutex_unlock(&pio->instr_mutex); ++ if (offset >= 0) ++ return offset; ++ ++ /* Don't send the instructions, just the header */ ++ return rp1_pio_message(pio, PIO_CAN_ADD_PROGRAM, args, ++ offsetof(struct rp1_pio_add_program_args, instrs)); ++} ++EXPORT_SYMBOL_GPL(rp1_pio_can_add_program); ++ ++int rp1_pio_add_program(struct rp1_pio_client *client, void *param) ++{ ++ struct rp1_pio_add_program_args *args = param; ++ struct rp1_pio_device *pio = client->pio; ++ int offset; ++ uint i; ++ ++ if (args->num_instrs > RP1_PIO_INSTR_COUNT || ++ ((args->origin != RP1_PIO_ORIGIN_ANY) && ++ (args->origin >= RP1_PIO_INSTR_COUNT || ++ ((args->origin + args->num_instrs) > RP1_PIO_INSTR_COUNT)))) ++ return -EINVAL; ++ ++ mutex_lock(&pio->instr_mutex); ++ offset = rp1_pio_find_program(pio, args); ++ if (offset < 0) ++ offset = rp1_pio_message(client->pio, PIO_ADD_PROGRAM, args, sizeof(*args)); ++ ++ if (offset >= 0) { ++ uint32_t used_mask; ++ uint prog_size; ++ ++ used_mask = ((uint32_t)(~0) >> (-args->num_instrs & 0x1f)) << offset; ++ prog_size = sizeof(args->instrs[0]) * args->num_instrs; ++ ++ if ((pio->used_instrs & used_mask) != used_mask) { ++ pio->used_instrs |= used_mask; ++ memcpy(pio->instrs + offset, args->instrs, prog_size); ++ } ++ client->claimed_instrs |= used_mask; ++ for (i = 0; i < args->num_instrs; i++) ++ pio->instr_refcounts[offset + i]++; ++ } ++ mutex_unlock(&pio->instr_mutex); ++ return offset; ++} ++EXPORT_SYMBOL_GPL(rp1_pio_add_program); ++ ++static void rp1_pio_remove_instrs(struct rp1_pio_device *pio, uint32_t mask) ++{ ++ struct rp1_pio_remove_program_args args; ++ uint i; ++ ++ mutex_lock(&pio->instr_mutex); ++ args.num_instrs = 0; ++ for (i = 0; ; i++, mask >>= 1) { ++ if ((mask & 1) && pio->instr_refcounts[i] && !--pio->instr_refcounts[i]) { ++ pio->used_instrs &= ~(1 << i); ++ args.num_instrs++; ++ } else if (args.num_instrs) { ++ args.origin = i - args.num_instrs; ++ rp1_pio_message(pio, PIO_REMOVE_PROGRAM, &args, sizeof(args)); ++ args.num_instrs = 0; ++ } ++ if (!mask) ++ break; ++ } ++ mutex_unlock(&pio->instr_mutex); ++} ++ ++int rp1_pio_remove_program(struct rp1_pio_client *client, void *param) ++{ ++ struct rp1_pio_remove_program_args *args = param; ++ uint32_t used_mask; ++ int ret = -ENOENT; ++ ++ if (args->num_instrs > RP1_PIO_INSTR_COUNT || ++ args->origin >= RP1_PIO_INSTR_COUNT || ++ (args->origin + args->num_instrs) > RP1_PIO_INSTR_COUNT) ++ return -EINVAL; ++ ++ used_mask = ((uint32_t)(~0) >> (32 - args->num_instrs)) << args->origin; ++ if ((client->claimed_instrs & used_mask) == used_mask) { ++ client->claimed_instrs &= ~used_mask; ++ rp1_pio_remove_instrs(client->pio, used_mask); ++ ret = 0; ++ } ++ return ret; ++} ++EXPORT_SYMBOL_GPL(rp1_pio_remove_program); ++ ++int rp1_pio_clear_instr_mem(struct rp1_pio_client *client, void *param) ++{ ++ struct rp1_pio_device *pio = client->pio; ++ ++ mutex_lock(&pio->instr_mutex); ++ (void)rp1_pio_message(client->pio, PIO_CLEAR_INSTR_MEM, NULL, 0); ++ memset(pio->instr_refcounts, 0, sizeof(pio->instr_refcounts)); ++ pio->used_instrs = 0; ++ client->claimed_instrs = 0; ++ mutex_unlock(&pio->instr_mutex); ++ return 0; ++} ++EXPORT_SYMBOL_GPL(rp1_pio_clear_instr_mem); ++ ++int rp1_pio_sm_claim(struct rp1_pio_client *client, void *param) ++{ ++ struct rp1_pio_sm_claim_args *args = param; ++ struct rp1_pio_device *pio = client->pio; ++ int ret; ++ ++ mutex_lock(&pio->instr_mutex); ++ ret = rp1_pio_message(client->pio, PIO_SM_CLAIM, args, sizeof(*args)); ++ if (ret >= 0) { ++ if (args->mask) ++ client->claimed_sms |= args->mask; ++ else ++ client->claimed_sms |= (1 << ret); ++ pio->claimed_sms |= client->claimed_sms; ++ } ++ mutex_unlock(&pio->instr_mutex); ++ return ret; ++} ++EXPORT_SYMBOL_GPL(rp1_pio_sm_claim); ++ ++int rp1_pio_sm_unclaim(struct rp1_pio_client *client, void *param) ++{ ++ struct rp1_pio_sm_claim_args *args = param; ++ struct rp1_pio_device *pio = client->pio; ++ ++ mutex_lock(&pio->instr_mutex); ++ (void)rp1_pio_message(client->pio, PIO_SM_UNCLAIM, args, sizeof(*args)); ++ client->claimed_sms &= ~args->mask; ++ pio->claimed_sms &= ~args->mask; ++ mutex_unlock(&pio->instr_mutex); ++ return 0; ++} ++EXPORT_SYMBOL_GPL(rp1_pio_sm_unclaim); ++ ++int rp1_pio_sm_is_claimed(struct rp1_pio_client *client, void *param) ++{ ++ struct rp1_pio_sm_claim_args *args = param; ++ ++ return rp1_pio_message(client->pio, PIO_SM_IS_CLAIMED, args, sizeof(*args)); ++} ++EXPORT_SYMBOL_GPL(rp1_pio_sm_is_claimed); ++ ++int rp1_pio_sm_init(struct rp1_pio_client *client, void *param) ++{ ++ struct rp1_pio_sm_init_args *args = param; ++ ++ return rp1_pio_message(client->pio, PIO_SM_INIT, args, sizeof(*args)); ++} ++EXPORT_SYMBOL_GPL(rp1_pio_sm_init); ++ ++int rp1_pio_sm_set_config(struct rp1_pio_client *client, void *param) ++{ ++ struct rp1_pio_sm_set_config_args *args = param; ++ ++ return rp1_pio_message(client->pio, PIO_SM_SET_CONFIG, args, sizeof(*args)); ++} ++EXPORT_SYMBOL_GPL(rp1_pio_sm_set_config); ++ ++int rp1_pio_sm_exec(struct rp1_pio_client *client, void *param) ++{ ++ struct rp1_pio_sm_exec_args *args = param; ++ ++ return rp1_pio_message(client->pio, PIO_SM_EXEC, args, sizeof(*args)); ++} ++EXPORT_SYMBOL_GPL(rp1_pio_sm_exec); ++ ++int rp1_pio_sm_clear_fifos(struct rp1_pio_client *client, void *param) ++{ ++ struct rp1_pio_sm_clear_fifos_args *args = param; ++ ++ return rp1_pio_message(client->pio, PIO_SM_CLEAR_FIFOS, args, sizeof(*args)); ++} ++EXPORT_SYMBOL_GPL(rp1_pio_sm_clear_fifos); ++ ++int rp1_pio_sm_set_clkdiv(struct rp1_pio_client *client, void *param) ++{ ++ struct rp1_pio_sm_set_clkdiv_args *args = param; ++ ++ return rp1_pio_message(client->pio, PIO_SM_SET_CLKDIV, args, sizeof(*args)); ++} ++EXPORT_SYMBOL_GPL(rp1_pio_sm_set_clkdiv); ++ ++int rp1_pio_sm_set_pins(struct rp1_pio_client *client, void *param) ++{ ++ struct rp1_pio_sm_set_pins_args *args = param; ++ ++ return rp1_pio_message(client->pio, PIO_SM_SET_PINS, args, sizeof(*args)); ++} ++EXPORT_SYMBOL_GPL(rp1_pio_sm_set_pins); ++ ++int rp1_pio_sm_set_pindirs(struct rp1_pio_client *client, void *param) ++{ ++ struct rp1_pio_sm_set_pindirs_args *args = param; ++ ++ return rp1_pio_message(client->pio, PIO_SM_SET_PINDIRS, args, sizeof(*args)); ++} ++EXPORT_SYMBOL_GPL(rp1_pio_sm_set_pindirs); ++ ++int rp1_pio_sm_set_enabled(struct rp1_pio_client *client, void *param) ++{ ++ struct rp1_pio_sm_set_enabled_args *args = param; ++ ++ return rp1_pio_message(client->pio, PIO_SM_SET_ENABLED, args, sizeof(*args)); ++} ++EXPORT_SYMBOL_GPL(rp1_pio_sm_set_enabled); ++ ++int rp1_pio_sm_restart(struct rp1_pio_client *client, void *param) ++{ ++ struct rp1_pio_sm_restart_args *args = param; ++ ++ return rp1_pio_message(client->pio, PIO_SM_RESTART, args, sizeof(*args)); ++} ++EXPORT_SYMBOL_GPL(rp1_pio_sm_restart); ++ ++int rp1_pio_sm_clkdiv_restart(struct rp1_pio_client *client, void *param) ++{ ++ struct rp1_pio_sm_restart_args *args = param; ++ ++ return rp1_pio_message(client->pio, PIO_SM_CLKDIV_RESTART, args, sizeof(*args)); ++} ++EXPORT_SYMBOL_GPL(rp1_pio_sm_clkdiv_restart); ++ ++int rp1_pio_sm_enable_sync(struct rp1_pio_client *client, void *param) ++{ ++ struct rp1_pio_sm_enable_sync_args *args = param; ++ ++ return rp1_pio_message(client->pio, PIO_SM_ENABLE_SYNC, args, sizeof(*args)); ++} ++EXPORT_SYMBOL_GPL(rp1_pio_sm_enable_sync); ++ ++int rp1_pio_sm_put(struct rp1_pio_client *client, void *param) ++{ ++ struct rp1_pio_sm_put_args *args = param; ++ ++ return rp1_pio_message(client->pio, PIO_SM_PUT, args, sizeof(*args)); ++} ++EXPORT_SYMBOL_GPL(rp1_pio_sm_put); ++ ++int rp1_pio_sm_get(struct rp1_pio_client *client, void *param) ++{ ++ struct rp1_pio_sm_get_args *args = param; ++ int ret; ++ ++ ret = rp1_pio_message_resp(client->pio, PIO_SM_GET, args, sizeof(*args), ++ &args->data, NULL, sizeof(args->data)); ++ if (ret >= 0) ++ return offsetof(struct rp1_pio_sm_get_args, data) + ret; ++ return ret; ++} ++EXPORT_SYMBOL_GPL(rp1_pio_sm_get); ++ ++int rp1_pio_sm_set_dmactrl(struct rp1_pio_client *client, void *param) ++{ ++ struct rp1_pio_sm_set_dmactrl_args *args = param; ++ ++ return rp1_pio_message(client->pio, PIO_SM_SET_DMACTRL, args, sizeof(*args)); ++} ++EXPORT_SYMBOL_GPL(rp1_pio_sm_set_dmactrl); ++ ++int rp1_pio_sm_fifo_state(struct rp1_pio_client *client, void *param) ++{ ++ struct rp1_pio_sm_fifo_state_args *args = param; ++ const int level_offset = offsetof(struct rp1_pio_sm_fifo_state_args, level); ++ int ret; ++ ++ ret = rp1_pio_message_resp(client->pio, PIO_SM_FIFO_STATE, args, sizeof(*args), ++ &args->level, NULL, sizeof(*args) - level_offset); ++ if (ret >= 0) ++ return level_offset + ret; ++ return ret; ++} ++EXPORT_SYMBOL_GPL(rp1_pio_sm_fifo_state); ++ ++int rp1_pio_sm_drain_tx(struct rp1_pio_client *client, void *param) ++{ ++ struct rp1_pio_sm_clear_fifos_args *args = param; ++ ++ return rp1_pio_message(client->pio, PIO_SM_DRAIN_TX, args, sizeof(*args)); ++} ++EXPORT_SYMBOL_GPL(rp1_pio_sm_drain_tx); ++ ++int rp1_pio_gpio_init(struct rp1_pio_client *client, void *param) ++{ ++ struct rp1_gpio_init_args *args = param; ++ ++ return rp1_pio_message(client->pio, GPIO_INIT, args, sizeof(*args)); ++} ++EXPORT_SYMBOL_GPL(rp1_pio_gpio_init); ++ ++int rp1_pio_gpio_set_function(struct rp1_pio_client *client, void *param) ++{ ++ struct rp1_gpio_set_function_args *args = param; ++ ++ return rp1_pio_message(client->pio, GPIO_SET_FUNCTION, args, sizeof(*args)); ++} ++EXPORT_SYMBOL_GPL(rp1_pio_gpio_set_function); ++ ++int rp1_pio_gpio_set_pulls(struct rp1_pio_client *client, void *param) ++{ ++ struct rp1_gpio_set_pulls_args *args = param; ++ ++ return rp1_pio_message(client->pio, GPIO_SET_PULLS, args, sizeof(*args)); ++} ++EXPORT_SYMBOL_GPL(rp1_pio_gpio_set_pulls); ++ ++int rp1_pio_gpio_set_outover(struct rp1_pio_client *client, void *param) ++{ ++ struct rp1_gpio_set_args *args = param; ++ ++ return rp1_pio_message(client->pio, GPIO_SET_OUTOVER, args, sizeof(*args)); ++} ++EXPORT_SYMBOL_GPL(rp1_pio_gpio_set_outover); ++ ++int rp1_pio_gpio_set_inover(struct rp1_pio_client *client, void *param) ++{ ++ struct rp1_gpio_set_args *args = param; ++ ++ return rp1_pio_message(client->pio, GPIO_SET_INOVER, args, sizeof(*args)); ++} ++EXPORT_SYMBOL_GPL(rp1_pio_gpio_set_inover); ++ ++int rp1_pio_gpio_set_oeover(struct rp1_pio_client *client, void *param) ++{ ++ struct rp1_gpio_set_args *args = param; ++ ++ return rp1_pio_message(client->pio, GPIO_SET_OEOVER, args, sizeof(*args)); ++} ++EXPORT_SYMBOL_GPL(rp1_pio_gpio_set_oeover); ++ ++int rp1_pio_gpio_set_input_enabled(struct rp1_pio_client *client, void *param) ++{ ++ struct rp1_gpio_set_args *args = param; ++ ++ return rp1_pio_message(client->pio, GPIO_SET_INPUT_ENABLED, args, sizeof(*args)); ++} ++EXPORT_SYMBOL_GPL(rp1_pio_gpio_set_input_enabled); ++ ++int rp1_pio_gpio_set_drive_strength(struct rp1_pio_client *client, void *param) ++{ ++ struct rp1_gpio_set_args *args = param; ++ ++ return rp1_pio_message(client->pio, GPIO_SET_DRIVE_STRENGTH, args, sizeof(*args)); ++} ++EXPORT_SYMBOL_GPL(rp1_pio_gpio_set_drive_strength); ++ ++static void rp1_pio_sm_dma_callback(void *param) ++{ ++ struct dma_info *dma = param; ++ ++ up(&dma->buf_sem); ++} ++ ++static void rp1_pio_sm_kernel_dma_callback(void *param) ++{ ++ struct dma_xfer_state *dxs = param; ++ ++ dxs->dma->tail_idx++; ++ up(&dxs->dma->buf_sem); ++ ++ dxs->callback(dxs->callback_param); ++ ++ kfree(dxs); ++} ++ ++static void rp1_pio_sm_dma_free(struct device *dev, struct dma_info *dma) ++{ ++ dmaengine_terminate_all(dma->chan); ++ while (dma->buf_count > 0) { ++ dma->buf_count--; ++ dma_free_coherent(dev, ROUND_UP(dma->buf_size, PAGE_SIZE), ++ dma->bufs[dma->buf_count].buf, ++ dma->bufs[dma->buf_count].dma_addr); ++ } ++ ++ dma_release_channel(dma->chan); ++} ++ ++static int rp1_pio_sm_config_xfer_internal(struct rp1_pio_client *client, uint sm, uint dir, ++ uint buf_size, uint buf_count) ++{ ++ struct rp1_pio_sm_set_dmactrl_args set_dmactrl_args; ++ struct rp1_pio_device *pio = client->pio; ++ struct platform_device *pdev = pio->pdev; ++ struct device *dev = &pdev->dev; ++ struct dma_slave_config config = {}; ++ phys_addr_t fifo_addr; ++ struct dma_info *dma; ++ uint32_t dma_mask; ++ char chan_name[4]; ++ int ret = 0; ++ ++ if (sm >= RP1_PIO_SMS_COUNT || dir >= RP1_PIO_DIR_COUNT) ++ return -EINVAL; ++ if ((buf_count || buf_size) && ++ (!buf_size || (buf_size & 3) || ++ !buf_count || buf_count > DMA_BOUNCE_BUFFER_COUNT)) ++ return -EINVAL; ++ ++ dma_mask = 1 << (sm * 2 + dir); ++ ++ dma = &pio->dma_configs[sm][dir]; ++ ++ spin_lock(&pio->lock); ++ if (pio->claimed_dmas & dma_mask) ++ rp1_pio_sm_dma_free(dev, dma); ++ pio->claimed_dmas |= dma_mask; ++ client->claimed_dmas |= dma_mask; ++ spin_unlock(&pio->lock); ++ ++ dma->buf_size = buf_size; ++ /* Round up the allocations */ ++ buf_size = ROUND_UP(buf_size, PAGE_SIZE); ++ sema_init(&dma->buf_sem, 0); ++ ++ /* Allocate and configure a DMA channel */ ++ /* Careful - each SM FIFO has its own DREQ value */ ++ chan_name[0] = (dir == RP1_PIO_DIR_TO_SM) ? 't' : 'r'; ++ chan_name[1] = 'x'; ++ chan_name[2] = '0' + sm; ++ chan_name[3] = '\0'; ++ ++ dma->chan = dma_request_chan(dev, chan_name); ++ if (IS_ERR(dma->chan)) ++ return PTR_ERR(dma->chan); ++ ++ /* Alloc and map bounce buffers */ ++ for (dma->buf_count = 0; dma->buf_count < buf_count; dma->buf_count++) { ++ struct dma_buf_info *dbi = &dma->bufs[dma->buf_count]; ++ ++ dbi->buf = dma_alloc_coherent(dma->chan->device->dev, buf_size, ++ &dbi->dma_addr, GFP_KERNEL); ++ if (!dbi->buf) { ++ ret = -ENOMEM; ++ goto err_dma_free; ++ } ++ sg_init_table(&dbi->sgl, 1); ++ sg_dma_address(&dbi->sgl) = dbi->dma_addr; ++ } ++ ++ fifo_addr = pio->phys_addr; ++ fifo_addr += sm * (RP1_PIO_FIFO_TX1 - RP1_PIO_FIFO_TX0); ++ fifo_addr += (dir == RP1_PIO_DIR_TO_SM) ? RP1_PIO_FIFO_TX0 : RP1_PIO_FIFO_RX0; ++ ++ config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; ++ config.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; ++ config.src_addr = fifo_addr; ++ config.dst_addr = fifo_addr; ++ config.direction = (dir == RP1_PIO_DIR_TO_SM) ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM; ++ ++ ret = dmaengine_slave_config(dma->chan, &config); ++ if (ret) ++ goto err_dma_free; ++ ++ set_dmactrl_args.sm = sm; ++ set_dmactrl_args.is_tx = (dir == RP1_PIO_DIR_TO_SM); ++ set_dmactrl_args.ctrl = RP1_PIO_DMACTRL_DEFAULT; ++ if (dir == RP1_PIO_DIR_FROM_SM) ++ set_dmactrl_args.ctrl = (RP1_PIO_DMACTRL_DEFAULT & ~0x1f) | 1; ++ ++ ret = rp1_pio_sm_set_dmactrl(client, &set_dmactrl_args); ++ if (ret) ++ goto err_dma_free; ++ ++ return 0; ++ ++err_dma_free: ++ rp1_pio_sm_dma_free(dev, dma); ++ ++ spin_lock(&pio->lock); ++ client->claimed_dmas &= ~dma_mask; ++ pio->claimed_dmas &= ~dma_mask; ++ spin_unlock(&pio->lock); ++ ++ return ret; ++} ++ ++static int rp1_pio_sm_config_xfer_user(struct rp1_pio_client *client, void *param) ++{ ++ struct rp1_pio_sm_config_xfer_args *args = param; ++ ++ return rp1_pio_sm_config_xfer_internal(client, args->sm, args->dir, ++ args->buf_size, args->buf_count); ++} ++ ++static int rp1_pio_sm_config_xfer32_user(struct rp1_pio_client *client, void *param) ++{ ++ struct rp1_pio_sm_config_xfer32_args *args = param; ++ ++ return rp1_pio_sm_config_xfer_internal(client, args->sm, args->dir, ++ args->buf_size, args->buf_count); ++} ++ ++static int rp1_pio_sm_tx_user(struct rp1_pio_device *pio, struct dma_info *dma, ++ const void __user *userbuf, size_t bytes) ++{ ++ struct platform_device *pdev = pio->pdev; ++ struct dma_async_tx_descriptor *desc; ++ struct device *dev = &pdev->dev; ++ int ret = 0; ++ ++ /* Clean the slate - we're running synchronously */ ++ dma->head_idx = 0; ++ dma->tail_idx = 0; ++ ++ while (bytes > 0) { ++ size_t copy_bytes = min(bytes, dma->buf_size); ++ struct dma_buf_info *dbi; ++ ++ /* grab the next free buffer, waiting if they're all full */ ++ if (dma->head_idx - dma->tail_idx == dma->buf_count) { ++ if (down_timeout(&dma->buf_sem, ++ msecs_to_jiffies(1000))) { ++ dev_err(dev, "DMA bounce timed out\n"); ++ break; ++ } ++ dma->tail_idx++; ++ } ++ ++ dbi = &dma->bufs[dma->head_idx % dma->buf_count]; ++ ++ sg_dma_len(&dbi->sgl) = copy_bytes; ++ ++ ret = copy_from_user(dbi->buf, userbuf, copy_bytes); ++ if (ret < 0) ++ break; ++ ++ userbuf += copy_bytes; ++ ++ desc = dmaengine_prep_slave_sg(dma->chan, &dbi->sgl, 1, ++ DMA_MEM_TO_DEV, ++ DMA_PREP_INTERRUPT | DMA_CTRL_ACK | ++ DMA_PREP_FENCE); ++ if (!desc) { ++ dev_err(dev, "DMA preparation failed\n"); ++ ret = -EIO; ++ break; ++ } ++ ++ desc->callback = rp1_pio_sm_dma_callback; ++ desc->callback_param = dma; ++ ++ /* Submit the buffer - the callback will kick the semaphore */ ++ ret = dmaengine_submit(desc); ++ if (ret < 0) ++ break; ++ ret = 0; ++ ++ dma_async_issue_pending(dma->chan); ++ ++ dma->head_idx++; ++ bytes -= copy_bytes; ++ } ++ ++ /* Block for completion */ ++ while (dma->tail_idx != dma->head_idx) { ++ if (down_timeout(&dma->buf_sem, msecs_to_jiffies(1000))) { ++ dev_err(dev, "DMA wait timed out\n"); ++ ret = -ETIMEDOUT; ++ break; ++ } ++ dma->tail_idx++; ++ } ++ ++ return ret; ++} ++ ++static int rp1_pio_sm_rx_user(struct rp1_pio_device *pio, struct dma_info *dma, ++ void __user *userbuf, size_t bytes) ++{ ++ struct platform_device *pdev = pio->pdev; ++ struct dma_async_tx_descriptor *desc; ++ struct device *dev = &pdev->dev; ++ int ret = 0; ++ ++ /* Clean the slate - we're running synchronously */ ++ dma->head_idx = 0; ++ dma->tail_idx = 0; ++ ++ while (bytes || dma->tail_idx != dma->head_idx) { ++ size_t copy_bytes = min(bytes, dma->buf_size); ++ struct dma_buf_info *dbi; ++ ++ /* ++ * wait for the next RX to complete if all the buffers are ++ * outstanding or we're finishing up. ++ */ ++ if (!bytes || dma->head_idx - dma->tail_idx == dma->buf_count) { ++ if (down_timeout(&dma->buf_sem, ++ msecs_to_jiffies(1000))) { ++ dev_err(dev, "DMA wait timed out\n"); ++ ret = -ETIMEDOUT; ++ break; ++ } ++ ++ dbi = &dma->bufs[dma->tail_idx++ % dma->buf_count]; ++ ret = copy_to_user(userbuf, dbi->buf, sg_dma_len(&dbi->sgl)); ++ if (ret < 0) ++ break; ++ userbuf += sg_dma_len(&dbi->sgl); ++ ++ if (!bytes) ++ continue; ++ } ++ ++ dbi = &dma->bufs[dma->head_idx % dma->buf_count]; ++ sg_dma_len(&dbi->sgl) = copy_bytes; ++ desc = dmaengine_prep_slave_sg(dma->chan, &dbi->sgl, 1, ++ DMA_DEV_TO_MEM, ++ DMA_PREP_INTERRUPT | DMA_CTRL_ACK | ++ DMA_PREP_FENCE); ++ if (!desc) { ++ dev_err(dev, "DMA preparation failed\n"); ++ ret = -EIO; ++ break; ++ } ++ ++ desc->callback = rp1_pio_sm_dma_callback; ++ desc->callback_param = dma; ++ ++ /* Submit the buffer - the callback will kick the semaphore */ ++ ret = dmaengine_submit(desc); ++ if (ret < 0) ++ break; ++ ++ dma_async_issue_pending(dma->chan); ++ ++ dma->head_idx++; ++ bytes -= copy_bytes; ++ } ++ ++ return ret; ++} ++ ++static int rp1_pio_sm_xfer_data32_user(struct rp1_pio_client *client, void *param) ++{ ++ struct rp1_pio_sm_xfer_data32_args *args = param; ++ struct rp1_pio_device *pio = client->pio; ++ struct dma_info *dma; ++ ++ if (args->sm >= RP1_PIO_SMS_COUNT || args->dir >= RP1_PIO_DIR_COUNT || ++ !args->data_bytes || !args->data) ++ return -EINVAL; ++ ++ dma = &pio->dma_configs[args->sm][args->dir]; ++ ++ if (args->dir == RP1_PIO_DIR_TO_SM) ++ return rp1_pio_sm_tx_user(pio, dma, args->data, args->data_bytes); ++ else ++ return rp1_pio_sm_rx_user(pio, dma, args->data, args->data_bytes); ++} ++ ++static int rp1_pio_sm_xfer_data_user(struct rp1_pio_client *client, void *param) ++{ ++ struct rp1_pio_sm_xfer_data_args *args = param; ++ struct rp1_pio_sm_xfer_data32_args args32; ++ ++ args32.sm = args->sm; ++ args32.dir = args->dir; ++ args32.data_bytes = args->data_bytes; ++ args32.data = args->data; ++ ++ return rp1_pio_sm_xfer_data32_user(client, &args32); ++} ++ ++int rp1_pio_sm_config_xfer(struct rp1_pio_client *client, uint sm, uint dir, ++ uint buf_size, uint buf_count) ++{ ++ return rp1_pio_sm_config_xfer_internal(client, sm, dir, buf_size, buf_count); ++} ++EXPORT_SYMBOL_GPL(rp1_pio_sm_config_xfer); ++ ++int rp1_pio_sm_xfer_data(struct rp1_pio_client *client, uint sm, uint dir, ++ uint data_bytes, void *data, dma_addr_t dma_addr, ++ void (*callback)(void *param), void *param) ++{ ++ struct rp1_pio_device *pio = client->pio; ++ struct platform_device *pdev = pio->pdev; ++ struct dma_async_tx_descriptor *desc; ++ struct dma_xfer_state *dxs = NULL; ++ struct device *dev = &pdev->dev; ++ struct dma_buf_info *dbi = NULL; ++ struct scatterlist sg; ++ struct dma_info *dma; ++ int ret = 0; ++ ++ if (sm >= RP1_PIO_SMS_COUNT || dir >= RP1_PIO_DIR_COUNT) ++ return -EINVAL; ++ ++ dma = &pio->dma_configs[sm][dir]; ++ ++ if (!dma_addr) { ++ dxs = kmalloc(sizeof(*dxs), GFP_KERNEL); ++ dxs->dma = dma; ++ dxs->callback = callback; ++ dxs->callback_param = param; ++ callback = rp1_pio_sm_kernel_dma_callback; ++ param = dxs; ++ ++ if (!dma->buf_count || data_bytes > dma->buf_size) ++ return -EINVAL; ++ ++ /* Grab a dma buffer */ ++ if (dma->head_idx - dma->tail_idx == dma->buf_count) { ++ if (down_timeout(&dma->buf_sem, msecs_to_jiffies(1000))) { ++ dev_err(dev, "DMA wait timed out\n"); ++ return -ETIMEDOUT; ++ } ++ } ++ ++ dbi = &dma->bufs[dma->head_idx % dma->buf_count]; ++ dma_addr = dbi->dma_addr; ++ ++ if (dir == PIO_DIR_TO_SM) ++ memcpy(dbi->buf, data, data_bytes); ++ } ++ ++ sg_init_table(&sg, 1); ++ sg_dma_address(&sg) = dma_addr; ++ sg_dma_len(&sg) = data_bytes; ++ ++ desc = dmaengine_prep_slave_sg(dma->chan, &sg, 1, ++ (dir == PIO_DIR_TO_SM) ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM, ++ DMA_PREP_INTERRUPT | DMA_CTRL_ACK | ++ DMA_PREP_FENCE); ++ if (!desc) { ++ dev_err(dev, "DMA preparation failed\n"); ++ return -EIO; ++ } ++ ++ desc->callback = callback; ++ desc->callback_param = param; ++ ++ ret = dmaengine_submit(desc); ++ if (ret < 0) { ++ dev_err(dev, "dmaengine_submit failed (%d)\n", ret); ++ return ret; ++ } ++ ++ dma_async_issue_pending(dma->chan); ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(rp1_pio_sm_xfer_data); ++ ++struct handler_info { ++ const char *name; ++ int (*func)(struct rp1_pio_client *client, void *param); ++ int argsize; ++} ioctl_handlers[] = { ++ HANDLER(SM_CONFIG_XFER, sm_config_xfer_user), ++ HANDLER(SM_XFER_DATA, sm_xfer_data_user), ++ HANDLER(SM_XFER_DATA32, sm_xfer_data32_user), ++ HANDLER(SM_CONFIG_XFER32, sm_config_xfer32_user), ++ ++ HANDLER(CAN_ADD_PROGRAM, can_add_program), ++ HANDLER(ADD_PROGRAM, add_program), ++ HANDLER(REMOVE_PROGRAM, remove_program), ++ HANDLER(CLEAR_INSTR_MEM, clear_instr_mem), ++ ++ HANDLER(SM_CLAIM, sm_claim), ++ HANDLER(SM_UNCLAIM, sm_unclaim), ++ HANDLER(SM_IS_CLAIMED, sm_is_claimed), ++ ++ HANDLER(SM_INIT, sm_init), ++ HANDLER(SM_SET_CONFIG, sm_set_config), ++ HANDLER(SM_EXEC, sm_exec), ++ HANDLER(SM_CLEAR_FIFOS, sm_clear_fifos), ++ HANDLER(SM_SET_CLKDIV, sm_set_clkdiv), ++ HANDLER(SM_SET_PINS, sm_set_pins), ++ HANDLER(SM_SET_PINDIRS, sm_set_pindirs), ++ HANDLER(SM_SET_ENABLED, sm_set_enabled), ++ HANDLER(SM_RESTART, sm_restart), ++ HANDLER(SM_CLKDIV_RESTART, sm_clkdiv_restart), ++ HANDLER(SM_ENABLE_SYNC, sm_enable_sync), ++ HANDLER(SM_PUT, sm_put), ++ HANDLER(SM_GET, sm_get), ++ HANDLER(SM_SET_DMACTRL, sm_set_dmactrl), ++ HANDLER(SM_FIFO_STATE, sm_fifo_state), ++ HANDLER(SM_DRAIN_TX, sm_drain_tx), ++ ++ HANDLER(GPIO_INIT, gpio_init), ++ HANDLER(GPIO_SET_FUNCTION, gpio_set_function), ++ HANDLER(GPIO_SET_PULLS, gpio_set_pulls), ++ HANDLER(GPIO_SET_OUTOVER, gpio_set_outover), ++ HANDLER(GPIO_SET_INOVER, gpio_set_inover), ++ HANDLER(GPIO_SET_OEOVER, gpio_set_oeover), ++ HANDLER(GPIO_SET_INPUT_ENABLED, gpio_set_input_enabled), ++ HANDLER(GPIO_SET_DRIVE_STRENGTH, gpio_set_drive_strength), ++ ++ HANDLER(READ_HW, read_hw), ++ HANDLER(WRITE_HW, write_hw), ++}; ++ ++struct rp1_pio_client *rp1_pio_open(void) ++{ ++ struct rp1_pio_client *client; ++ ++ if (!g_pio) ++ return ERR_PTR(-ENOENT); ++ ++ client = kzalloc(sizeof(*client), GFP_KERNEL); ++ if (!client) ++ return ERR_PTR(-ENOMEM); ++ ++ client->pio = g_pio; ++ ++ return client; ++} ++EXPORT_SYMBOL_GPL(rp1_pio_open); ++ ++void rp1_pio_close(struct rp1_pio_client *client) ++{ ++ struct rp1_pio_device *pio = client->pio; ++ uint claimed_dmas = client->claimed_dmas; ++ int i; ++ ++ /* Free any allocated resources */ ++ ++ for (i = 0; claimed_dmas; i++) { ++ uint mask = (1 << i); ++ ++ if (claimed_dmas & mask) { ++ struct dma_info *dma = &pio->dma_configs[i >> 1][i & 1]; ++ ++ claimed_dmas &= ~mask; ++ rp1_pio_sm_dma_free(&pio->pdev->dev, dma); ++ } ++ } ++ ++ spin_lock(&pio->lock); ++ pio->claimed_dmas &= ~client->claimed_dmas; ++ spin_unlock(&pio->lock); ++ ++ if (client->claimed_sms) { ++ struct rp1_pio_sm_set_enabled_args se_args = { ++ .mask = client->claimed_sms, .enable = 0 ++ }; ++ struct rp1_pio_sm_claim_args uc_args = { ++ .mask = client->claimed_sms ++ }; ++ ++ rp1_pio_sm_set_enabled(client, &se_args); ++ rp1_pio_sm_unclaim(client, &uc_args); ++ } ++ ++ if (client->claimed_instrs) ++ rp1_pio_remove_instrs(pio, client->claimed_instrs); ++ ++ /* Reinitialise the SM? */ ++ ++ kfree(client); ++} ++EXPORT_SYMBOL_GPL(rp1_pio_close); ++ ++void rp1_pio_set_error(struct rp1_pio_client *client, int err) ++{ ++ client->error = err; ++} ++EXPORT_SYMBOL_GPL(rp1_pio_set_error); ++ ++int rp1_pio_get_error(const struct rp1_pio_client *client) ++{ ++ return client->error; ++} ++EXPORT_SYMBOL_GPL(rp1_pio_get_error); ++ ++void rp1_pio_clear_error(struct rp1_pio_client *client) ++{ ++ client->error = 0; ++} ++EXPORT_SYMBOL_GPL(rp1_pio_clear_error); ++ ++static int rp1_pio_file_open(struct inode *inode, struct file *filp) ++{ ++ struct rp1_pio_client *client; ++ ++ client = rp1_pio_open(); ++ if (IS_ERR(client)) ++ return PTR_ERR(client); ++ ++ filp->private_data = client; ++ ++ return 0; ++} ++ ++static int rp1_pio_file_release(struct inode *inode, struct file *filp) ++{ ++ struct rp1_pio_client *client = filp->private_data; ++ ++ rp1_pio_close(client); ++ ++ return 0; ++} ++ ++static long rp1_pio_ioctl(struct file *filp, unsigned int ioctl_num, ++ unsigned long ioctl_param) ++{ ++ struct rp1_pio_client *client = filp->private_data; ++ struct device *dev = &client->pio->pdev->dev; ++ void __user *argp = (void __user *)ioctl_param; ++ int nr = _IOC_NR(ioctl_num); ++ int sz = _IOC_SIZE(ioctl_num); ++ struct handler_info *hdlr = &ioctl_handlers[nr]; ++ uint32_t argbuf[MAX_ARG_SIZE/sizeof(uint32_t)]; ++ int ret; ++ ++ if (nr >= ARRAY_SIZE(ioctl_handlers) || !hdlr->func) { ++ dev_err(dev, "unknown ioctl: %x\n", ioctl_num); ++ return -EOPNOTSUPP; ++ } ++ ++ if (sz != hdlr->argsize) { ++ dev_err(dev, "wrong %s argsize (expected %d, got %d)\n", ++ hdlr->name, hdlr->argsize, sz); ++ return -EINVAL; ++ } ++ ++ if (copy_from_user(argbuf, argp, sz)) ++ return -EFAULT; ++ ++ ret = (hdlr->func)(client, argbuf); ++ dev_dbg(dev, "%s: %s -> %d\n", __func__, hdlr->name, ret); ++ if (ret > 0) { ++ if (copy_to_user(argp, argbuf, ret)) ++ ret = -EFAULT; ++ } ++ ++ return ret; ++} ++ ++#ifdef CONFIG_COMPAT ++ ++struct rp1_pio_sm_xfer_data_args_compat { ++ uint16_t sm; ++ uint16_t dir; ++ uint16_t data_bytes; ++ compat_uptr_t data; ++}; ++ ++struct rp1_pio_sm_xfer_data32_args_compat { ++ uint16_t sm; ++ uint16_t dir; ++ uint32_t data_bytes; ++ compat_uptr_t data; ++}; ++ ++struct rp1_access_hw_args_compat { ++ uint32_t addr; ++ uint32_t len; ++ compat_uptr_t data; ++}; ++ ++#define PIO_IOC_SM_XFER_DATA_COMPAT \ ++ _IOW(PIO_IOC_MAGIC, 1, struct rp1_pio_sm_xfer_data_args_compat) ++#define PIO_IOC_SM_XFER_DATA32_COMPAT \ ++ _IOW(PIO_IOC_MAGIC, 2, struct rp1_pio_sm_xfer_data32_args_compat) ++#define PIO_IOC_READ_HW_COMPAT _IOW(PIO_IOC_MAGIC, 8, struct rp1_access_hw_args_compat) ++#define PIO_IOC_WRITE_HW_COMPAT _IOW(PIO_IOC_MAGIC, 9, struct rp1_access_hw_args_compat) ++ ++static long rp1_pio_compat_ioctl(struct file *filp, unsigned int ioctl_num, ++ unsigned long ioctl_param) ++{ ++ struct rp1_pio_client *client = filp->private_data; ++ ++ switch (ioctl_num) { ++ case PIO_IOC_SM_XFER_DATA_COMPAT: ++ { ++ struct rp1_pio_sm_xfer_data_args_compat compat_param; ++ struct rp1_pio_sm_xfer_data_args param; ++ ++ if (copy_from_user(&compat_param, compat_ptr(ioctl_param), sizeof(compat_param))) ++ return -EFAULT; ++ param.sm = compat_param.sm; ++ param.dir = compat_param.dir; ++ param.data_bytes = compat_param.data_bytes; ++ param.data = compat_ptr(compat_param.data); ++ return rp1_pio_sm_xfer_data_user(client, ¶m); ++ } ++ case PIO_IOC_SM_XFER_DATA32_COMPAT: ++ { ++ struct rp1_pio_sm_xfer_data32_args_compat compat_param; ++ struct rp1_pio_sm_xfer_data32_args param; ++ ++ if (copy_from_user(&compat_param, compat_ptr(ioctl_param), sizeof(compat_param))) ++ return -EFAULT; ++ param.sm = compat_param.sm; ++ param.dir = compat_param.dir; ++ param.data_bytes = compat_param.data_bytes; ++ param.data = compat_ptr(compat_param.data); ++ return rp1_pio_sm_xfer_data32_user(client, ¶m); ++ } ++ ++ case PIO_IOC_READ_HW_COMPAT: ++ case PIO_IOC_WRITE_HW_COMPAT: ++ { ++ struct rp1_access_hw_args_compat compat_param; ++ struct rp1_access_hw_args param; ++ ++ if (copy_from_user(&compat_param, compat_ptr(ioctl_param), sizeof(compat_param))) ++ return -EFAULT; ++ param.addr = compat_param.addr; ++ param.len = compat_param.len; ++ param.data = compat_ptr(compat_param.data); ++ if (ioctl_num == PIO_IOC_READ_HW_COMPAT) ++ return rp1_pio_read_hw(client, ¶m); ++ else ++ return rp1_pio_write_hw(client, ¶m); ++ } ++ default: ++ return rp1_pio_ioctl(filp, ioctl_num, ioctl_param); ++ } ++} ++#else ++#define rp1_pio_compat_ioctl NULL ++#endif ++ ++const struct file_operations rp1_pio_fops = { ++ .owner = THIS_MODULE, ++ .open = rp1_pio_file_open, ++ .release = rp1_pio_file_release, ++ .unlocked_ioctl = rp1_pio_ioctl, ++ .compat_ioctl = rp1_pio_compat_ioctl, ++}; ++ ++static int rp1_pio_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct resource *ioresource; ++ struct rp1_pio_device *pio; ++ struct rp1_firmware *fw; ++ uint32_t op_count = 0; ++ uint32_t op_base = 0; ++ struct device *cdev; ++ char dev_name[16]; ++ void *p; ++ int ret; ++ int i; ++ ++ /* Run-time check for a build-time misconfiguration */ ++ for (i = 0; i < ARRAY_SIZE(ioctl_handlers); i++) { ++ struct handler_info *hdlr = &ioctl_handlers[i]; ++ ++ if (WARN_ON(hdlr->argsize > MAX_ARG_SIZE)) ++ return -EINVAL; ++ } ++ ++ pdev->id = of_alias_get_id(pdev->dev.of_node, "pio"); ++ if (pdev->id < 0) ++ return dev_err_probe(dev, pdev->id, "alias is missing\n"); ++ ++ fw = devm_rp1_firmware_get(dev, dev->of_node); ++ if (!fw) ++ return dev_err_probe(dev, -EPROBE_DEFER, "failed to find RP1 firmware driver\n"); ++ if (IS_ERR(fw)) { ++ dev_warn(dev, "failed to contact RP1 firmware\n"); ++ return PTR_ERR(fw); ++ } ++ ret = rp1_firmware_get_feature(fw, FOURCC_PIO, &op_base, &op_count); ++ if (ret < 0) ++ return ret; ++ ++ pio = devm_kzalloc(&pdev->dev, sizeof(*pio), GFP_KERNEL); ++ if (!pio) ++ return -ENOMEM; ++ ++ platform_set_drvdata(pdev, pio); ++ pio->fw_pio_base = op_base; ++ pio->fw_pio_count = op_count; ++ pio->pdev = pdev; ++ pio->fw = fw; ++ spin_lock_init(&pio->lock); ++ mutex_init(&pio->instr_mutex); ++ ++ p = devm_platform_get_and_ioremap_resource(pdev, 0, &ioresource); ++ if (IS_ERR(p)) ++ return PTR_ERR(p); ++ ++ pio->phys_addr = ioresource->start; ++ ++ ret = alloc_chrdev_region(&pio->dev_num, 0, 1, DRIVER_NAME); ++ if (ret < 0) { ++ dev_err(dev, "alloc_chrdev_region failed (rc=%d)\n", ret); ++ goto out_err; ++ } ++ ++ pio->dev_class = class_create(DRIVER_NAME); ++ if (IS_ERR(pio->dev_class)) { ++ ret = PTR_ERR(pio->dev_class); ++ dev_err(dev, "class_create failed (err %d)\n", ret); ++ goto out_unregister; ++ } ++ ++ cdev_init(&pio->cdev, &rp1_pio_fops); ++ ret = cdev_add(&pio->cdev, pio->dev_num, 1); ++ if (ret) { ++ dev_err(dev, "cdev_add failed (err %d)\n", ret); ++ goto out_class_destroy; ++ } ++ ++ sprintf(dev_name, "pio%d", pdev->id); ++ cdev = device_create(pio->dev_class, NULL, pio->dev_num, NULL, dev_name); ++ if (IS_ERR(cdev)) { ++ ret = PTR_ERR(cdev); ++ dev_err(dev, "%s: device_create failed (err %d)\n", __func__, ret); ++ goto out_cdev_del; ++ } ++ ++ g_pio = pio; ++ ++ dev_info(dev, "Created instance as %s\n", dev_name); ++ return 0; ++ ++out_cdev_del: ++ cdev_del(&pio->cdev); ++ ++out_class_destroy: ++ class_destroy(pio->dev_class); ++ ++out_unregister: ++ unregister_chrdev_region(pio->dev_num, 1); ++ ++out_err: ++ return ret; ++} ++ ++static void rp1_pio_remove(struct platform_device *pdev) ++{ ++ struct rp1_pio_device *pio = platform_get_drvdata(pdev); ++ ++ /* There should be no clients */ ++ ++ if (g_pio == pio) ++ g_pio = NULL; ++ ++ device_destroy(pio->dev_class, pio->dev_num); ++ cdev_del(&pio->cdev); ++ class_destroy(pio->dev_class); ++ unregister_chrdev_region(pio->dev_num, 1); ++} ++ ++static const struct of_device_id rp1_pio_ids[] = { ++ { .compatible = "raspberrypi,rp1-pio" }, ++ { } ++}; ++MODULE_DEVICE_TABLE(of, rp1_pio_ids); ++ ++static struct platform_driver rp1_pio_driver = { ++ .driver = { ++ .name = "rp1-pio", ++ .of_match_table = of_match_ptr(rp1_pio_ids), ++ }, ++ .probe = rp1_pio_probe, ++ .remove_new = rp1_pio_remove, ++ .shutdown = rp1_pio_remove, ++}; ++ ++module_platform_driver(rp1_pio_driver); ++ ++MODULE_DESCRIPTION("PIO controller driver for Raspberry Pi RP1"); ++MODULE_AUTHOR("Phil Elwell"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/misc/ws2812-pio-rp1.c b/drivers/misc/ws2812-pio-rp1.c +new file mode 100644 +index 000000000000..ee840c6b3312 +--- /dev/null ++++ b/drivers/misc/ws2812-pio-rp1.c +@@ -0,0 +1,507 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Raspberry Pi PIO-based WS2812 driver ++ * ++ * Copyright (C) 2014-2024 Raspberry Pi Ltd. ++ * ++ * Author: Phil Elwell (phil@raspberrypi.com) ++ * ++ * Based on the ws2812 driver by Gordon Hollingworth ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define DRIVER_NAME "ws2812-pio-rp1" ++#define MAX_INSTANCES 4 ++ ++#define RESET_US 50 ++#define PIXEL_BYTES 4 ++ ++struct ws2812_pio_rp1_state { ++ struct device *dev; ++ struct gpio_desc *gpiod; ++ struct gpio_desc *power_gpiod; ++ uint gpio; ++ PIO pio; ++ uint sm; ++ uint offset; ++ ++ u8 *buffer; ++ u8 *pixbuf; ++ u32 pixbuf_size; ++ u32 write_end; ++ ++ u8 brightness; ++ u32 invert; ++ u32 num_leds; ++ u32 xfer_end_us; ++ bool is_rgbw; ++ struct delayed_work deferred_work; ++ ++ struct completion dma_completion; ++ struct cdev cdev; ++ dev_t dev_num; ++ const char *dev_name; ++}; ++ ++static DEFINE_MUTEX(ws2812_pio_mutex); ++static DEFINE_IDA(ws2812_pio_ida); ++static long ws2812_pio_ref_count; ++static struct class *ws2812_pio_class; ++static dev_t ws2812_pio_dev_num; ++/* ++ * WS2812B gamma correction ++ * GammaE=255*(res/255).^(1/.45) ++ * From: http://rgb-123.com/ws2812-color-output/ ++ */ ++ ++static const u8 ws2812_gamma[] = { ++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ++ 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, ++ 2, 2, 2, 3, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, ++ 6, 6, 6, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 11, 11, ++ 11, 12, 12, 13, 13, 13, 14, 14, 15, 15, 16, 16, 17, 17, 18, 18, ++ 19, 19, 20, 21, 21, 22, 22, 23, 23, 24, 25, 25, 26, 27, 27, 28, ++ 29, 29, 30, 31, 31, 32, 33, 34, 34, 35, 36, 37, 37, 38, 39, 40, ++ 40, 41, 42, 43, 44, 45, 46, 46, 47, 48, 49, 50, 51, 52, 53, 54, ++ 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, ++ 71, 72, 73, 74, 76, 77, 78, 79, 80, 81, 83, 84, 85, 86, 88, 89, ++ 90, 91, 93, 94, 95, 96, 98, 99, 100, 102, 103, 104, 106, 107, 109, 110, ++ 111, 113, 114, 116, 117, 119, 120, 121, 123, 124, 126, 128, 129, 131, 132, 134, ++ 135, 137, 138, 140, 142, 143, 145, 146, 148, 150, 151, 153, 155, 157, 158, 160, ++ 162, 163, 165, 167, 169, 170, 172, 174, 176, 178, 179, 181, 183, 185, 187, 189, ++ 191, 193, 194, 196, 198, 200, 202, 204, 206, 208, 210, 212, 214, 216, 218, 220, ++ 222, 224, 227, 229, 231, 233, 235, 237, 239, 241, 244, 246, 248, 250, 252, 255 ++}; ++ ++// ------ // ++// ws2812 // ++// ------ // ++ ++#define ws2812_wrap_target 0 ++#define ws2812_wrap 3 ++ ++#define ws2812_T1 3 ++#define ws2812_T2 4 ++#define ws2812_T3 3 ++ ++static const uint16_t ws2812_program_instructions[] = { ++ // .wrap_target ++ 0x6221, // 0: out x, 1 side 0 [2] ++ 0x1223, // 1: jmp !x, 3 side 1 [2] ++ 0x1300, // 2: jmp 0 side 1 [3] ++ 0xa342, // 3: nop side 0 [3] ++ // .wrap ++}; ++ ++static const struct pio_program ws2812_program = { ++ .instructions = ws2812_program_instructions, ++ .length = 4, ++ .origin = -1, ++}; ++ ++static inline pio_sm_config ws2812_program_get_default_config(uint offset) ++{ ++ pio_sm_config c = pio_get_default_sm_config(); ++ ++ sm_config_set_wrap(&c, offset + ws2812_wrap_target, offset + ws2812_wrap); ++ sm_config_set_sideset(&c, 1, false, false); ++ return c; ++} ++ ++static inline void ws2812_program_init(PIO pio, uint sm, uint offset, uint pin, uint freq, ++ bool rgbw) ++{ ++ int cycles_per_bit = ws2812_T1 + ws2812_T2 + ws2812_T3; ++ struct fp24_8 div; ++ pio_sm_config c; ++ ++ pio_gpio_init(pio, pin); ++ pio_sm_set_consecutive_pindirs(pio, sm, pin, 1, true); ++ c = ws2812_program_get_default_config(offset); ++ sm_config_set_sideset_pins(&c, pin); ++ sm_config_set_out_shift(&c, false, true, rgbw ? 32 : 24); ++ sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX); ++ div = make_fp24_8(clock_get_hz(clk_sys), freq * cycles_per_bit); ++ sm_config_set_clkdiv(&c, div); ++ pio_sm_init(pio, sm, offset, &c); ++ pio_sm_set_enabled(pio, sm, true); ++} ++ ++static uint8_t ws2812_apply_gamma(uint8_t brightness, uint8_t val) ++{ ++ int bright; ++ ++ if (!val) ++ return 0; ++ bright = (val * brightness) / 255; ++ return ws2812_gamma[bright]; ++} ++ ++static inline uint8_t *rgbw_u32(const struct ws2812_pio_rp1_state *state, ++ uint8_t r, uint8_t g, uint8_t b, uint8_t w, uint8_t *p) ++{ ++ p[0] = ws2812_apply_gamma(state->brightness, w); ++ p[1] = ws2812_apply_gamma(state->brightness, b); ++ p[2] = ws2812_apply_gamma(state->brightness, r); ++ p[3] = ws2812_apply_gamma(state->brightness, g); ++ return p + 4; ++} ++ ++static void ws2812_dma_complete(void *param) ++{ ++ struct ws2812_pio_rp1_state *state = param; ++ ++ complete(&state->dma_completion); ++} ++ ++static void ws2812_update_leds(struct ws2812_pio_rp1_state *state, uint length) ++{ ++ init_completion(&state->dma_completion); ++ if (!pio_sm_xfer_data(state->pio, state->sm, PIO_DIR_TO_SM, length, state->buffer, 0, ++ (void (*)(void *))ws2812_dma_complete, state)) { ++ wait_for_completion(&state->dma_completion); ++ usleep_range(RESET_US, RESET_US + 100); ++ } ++} ++ ++static void ws2812_clear_leds(struct ws2812_pio_rp1_state *state) ++{ ++ uint8_t *p_buffer; ++ uint length; ++ int i; ++ ++ p_buffer = state->buffer; ++ for (i = 0; i < state->num_leds; i++) ++ p_buffer = rgbw_u32(state, 0, 0, 0, 0, p_buffer); ++ ++ length = (void *)p_buffer - (void *)state->buffer; ++ ++ ws2812_update_leds(state, length); ++} ++ ++/* ++ * Function to write the RGB buffer to the WS2812 leds, the input buffer ++ * contains a sequence of up to num_leds RGB32 integers, these are then ++ * gamma-corrected before being sent to the PIO state machine. ++ */ ++ ++static ssize_t ws2812_pio_rp1_write(struct file *filp, const char __user *buf, size_t count, ++ loff_t *ppos) ++{ ++ struct ws2812_pio_rp1_state *state; ++ uint32_t pixbuf_size; ++ unsigned long delay; ++ loff_t pos = *ppos; ++ int err = 0; ++ ++ state = (struct ws2812_pio_rp1_state *)filp->private_data; ++ pixbuf_size = state->pixbuf_size; ++ ++ if (pos > pixbuf_size) ++ return -EFBIG; ++ ++ if (count > pixbuf_size) { ++ err = -EFBIG; ++ count = pixbuf_size; ++ } ++ ++ if (pos + count > pixbuf_size) { ++ if (!err) ++ err = -ENOSPC; ++ ++ count = pixbuf_size - pos; ++ } ++ ++ if (!pos && count == 1) { ++ if (copy_from_user(&state->brightness, buf, 1)) ++ return -EFAULT; ++ } else { ++ if (copy_from_user(state->pixbuf + pos, buf, count)) ++ return -EFAULT; ++ pos += count; ++ state->write_end = (u32)pos; ++ } ++ ++ *ppos = pos; ++ ++ delay = (state->write_end == pixbuf_size) ? 0 : HZ / 20; ++ schedule_delayed_work(&state->deferred_work, delay); ++ ++ return err ? err : count; ++} ++ ++static void ws2812_pio_rp1_deferred_work(struct work_struct *work) ++{ ++ struct ws2812_pio_rp1_state *state = ++ container_of(work, struct ws2812_pio_rp1_state, deferred_work.work); ++ uint8_t *p_buffer; ++ uint32_t *p_rgb; ++ int blank_bytes; ++ uint length; ++ int i; ++ ++ blank_bytes = state->pixbuf_size - state->write_end; ++ if (blank_bytes > 0) ++ memset(state->pixbuf + state->write_end, 0, blank_bytes); ++ ++ p_rgb = (uint32_t *)state->pixbuf; ++ p_buffer = state->buffer; ++ ++ for (i = 0; i < state->num_leds; i++) { ++ uint32_t rgbw_pix = *(p_rgb++); ++ ++ p_buffer = rgbw_u32(state, ++ (uint8_t)(rgbw_pix >> 0), ++ (uint8_t)(rgbw_pix >> 8), ++ (uint8_t)(rgbw_pix >> 16), ++ (uint8_t)(rgbw_pix >> 24), ++ p_buffer); ++ } ++ ++ length = (void *)p_buffer - (void *)state->buffer; ++ ++ ws2812_update_leds(state, length); ++} ++ ++static int ws2812_pio_rp1_open(struct inode *inode, struct file *file) ++{ ++ struct ws2812_pio_rp1_state *state; ++ ++ state = container_of(inode->i_cdev, struct ws2812_pio_rp1_state, cdev); ++ file->private_data = state; ++ ++ return 0; ++} ++ ++const struct file_operations ws2812_pio_rp1_fops = { ++ .owner = THIS_MODULE, ++ .write = ws2812_pio_rp1_write, ++ .open = ws2812_pio_rp1_open, ++}; ++ ++/* ++ * Probe function ++ */ ++static int ws2812_pio_rp1_probe(struct platform_device *pdev) ++{ ++ struct device_node *np = pdev->dev.of_node; ++ struct of_phandle_args of_args = { 0 }; ++ struct ws2812_pio_rp1_state *state; ++ struct device *dev = &pdev->dev; ++ struct device *char_dev; ++ const char *dev_name; ++ uint32_t brightness; ++ bool is_rp1; ++ int minor; ++ int ret; ++ ++ state = devm_kzalloc(dev, sizeof(*state), GFP_KERNEL); ++ if (IS_ERR(state)) ++ return PTR_ERR(state); ++ ++ state->dev = dev; ++ ++ platform_set_drvdata(pdev, state); ++ ++ ret = of_property_read_u32(np, "rpi,num-leds", &state->num_leds); ++ if (ret) ++ return dev_err_probe(dev, ret, "Could not get num-leds\n"); ++ ++ brightness = 255; ++ of_property_read_u32(np, "rpi,brightness", &brightness); ++ state->brightness = min(brightness, 255); ++ ++ state->pixbuf_size = state->num_leds * PIXEL_BYTES; ++ ++ state->is_rgbw = of_property_read_bool(np, "rpi,rgbw"); ++ state->gpiod = devm_gpiod_get(dev, "leds", GPIOD_ASIS); ++ if (IS_ERR(state->gpiod)) ++ return dev_err_probe(dev, PTR_ERR(state->gpiod), ++ "Could not get a gpio\n"); ++ ++ /* This must be an RP1 GPIO in the first bank, and retrieve the offset. */ ++ /* Unfortunately I think this has to be done by parsing the gpios property */ ++ ++ /* This really shouldn't fail, given that we have a gpiod */ ++ if (of_parse_phandle_with_args(np, "leds-gpios", "#gpio-cells", 0, &of_args)) ++ return dev_err_probe(dev, -EINVAL, ++ "Can't find gpio declaration\n"); ++ ++ is_rp1 = of_device_is_compatible(of_args.np, "raspberrypi,rp1-gpio"); ++ of_node_put(of_args.np); ++ if (!is_rp1 || of_args.args_count != 2) ++ return dev_err_probe(dev, -EINVAL, ++ "Not an RP1 gpio\n"); ++ ++ state->gpio = of_args.args[0]; ++ ++ state->pixbuf = devm_kmalloc(dev, state->pixbuf_size, GFP_KERNEL); ++ if (state->pixbuf == NULL) ++ return -ENOMEM; ++ ++ state->buffer = devm_kmalloc(dev, state->num_leds * PIXEL_BYTES, GFP_KERNEL); ++ if (state->buffer == NULL) ++ return -ENOMEM; ++ ++ ret = of_property_read_string(np, "dev-name", &dev_name); ++ if (ret) { ++ pr_err("Failed to read 'dev-name' property\n"); ++ return ret; ++ } ++ ++ state->pio = pio_open(); ++ if (IS_ERR(state->pio)) ++ return dev_err_probe(dev, PTR_ERR(state->pio), ++ "Could not open PIO\n"); ++ ++ state->sm = pio_claim_unused_sm(state->pio, false); ++ if ((int)state->sm < 0) { ++ dev_err(dev, "No free PIO SM\n"); ++ ret = -EBUSY; ++ goto fail_pio; ++ } ++ ++ state->offset = pio_add_program(state->pio, &ws2812_program); ++ if (state->offset == PIO_ORIGIN_ANY) { ++ dev_err(dev, "Not enough PIO program space\n"); ++ ret = -EBUSY; ++ goto fail_pio; ++ } ++ ++ pio_sm_config_xfer(state->pio, state->sm, PIO_DIR_TO_SM, state->num_leds * sizeof(int), 1); ++ ++ pio_sm_clear_fifos(state->pio, state->sm); ++ pio_sm_set_clkdiv(state->pio, state->sm, make_fp24_8(1, 1)); ++ ws2812_program_init(state->pio, state->sm, state->offset, state->gpio, 800000, ++ state->is_rgbw); ++ ++ mutex_lock(&ws2812_pio_mutex); ++ ++ if (!ws2812_pio_ref_count) { ++ ret = alloc_chrdev_region(&ws2812_pio_dev_num, 0, MAX_INSTANCES, DRIVER_NAME); ++ if (ret < 0) { ++ dev_err(dev, "alloc_chrdev_region failed (rc=%d)\n", ret); ++ goto fail_mutex; ++ } ++ ++ ws2812_pio_class = class_create(DRIVER_NAME); ++ if (IS_ERR(ws2812_pio_class)) { ++ pr_err("Unable to create class " DRIVER_NAME "\n"); ++ ret = PTR_ERR(ws2812_pio_class); ++ goto fail_chrdev; ++ } ++ } ++ ++ ws2812_pio_ref_count++; ++ ++ minor = ida_alloc_range(&ws2812_pio_ida, 0, MAX_INSTANCES - 1, GFP_KERNEL); ++ if (minor < 0) { ++ pr_err("No free instances\n"); ++ ret = minor; ++ goto fail_class; ++ ++ } ++ ++ mutex_unlock(&ws2812_pio_mutex); ++ ++ state->dev_num = MKDEV(MAJOR(ws2812_pio_dev_num), minor); ++ state->dev_name = devm_kasprintf(dev, GFP_KERNEL, dev_name, minor); ++ ++ char_dev = device_create(ws2812_pio_class, NULL, state->dev_num, NULL, state->dev_name); ++ ++ if (IS_ERR(char_dev)) { ++ pr_err("Unable to create device %s\n", state->dev_name); ++ ret = PTR_ERR(char_dev); ++ goto fail_ida; ++ } ++ ++ state->cdev.owner = THIS_MODULE; ++ cdev_init(&state->cdev, &ws2812_pio_rp1_fops); ++ ++ ret = cdev_add(&state->cdev, state->dev_num, 1); ++ if (ret) { ++ pr_err("cdev_add failed\n"); ++ goto fail_device; ++ } ++ ++ INIT_DELAYED_WORK(&state->deferred_work, ws2812_pio_rp1_deferred_work); ++ ++ ws2812_clear_leds(state); ++ ++ dev_info(&pdev->dev, "Instantiated %d LEDs on GPIO %d as /dev/%s\n", ++ state->num_leds, state->gpio, state->dev_name); ++ ++ return 0; ++ ++fail_device: ++ device_destroy(ws2812_pio_class, state->dev_num); ++fail_ida: ++ mutex_lock(&ws2812_pio_mutex); ++ ida_free(&ws2812_pio_ida, minor); ++fail_class: ++ ws2812_pio_ref_count--; ++ if (ws2812_pio_ref_count) ++ goto fail_mutex; ++ class_destroy(ws2812_pio_class); ++fail_chrdev: ++ unregister_chrdev_region(ws2812_pio_dev_num, MAX_INSTANCES); ++fail_mutex: ++ mutex_unlock(&ws2812_pio_mutex); ++fail_pio: ++ pio_close(state->pio); ++ ++ return ret; ++} ++ ++static void ws2812_pio_rp1_remove(struct platform_device *pdev) ++{ ++ struct ws2812_pio_rp1_state *state = platform_get_drvdata(pdev); ++ ++ cancel_delayed_work(&state->deferred_work); ++ platform_set_drvdata(pdev, NULL); ++ ++ cdev_del(&state->cdev); ++ device_destroy(ws2812_pio_class, state->dev_num); ++ ++ mutex_lock(&ws2812_pio_mutex); ++ ida_free(&ws2812_pio_ida, MINOR(state->dev_num)); ++ ws2812_pio_ref_count--; ++ if (!ws2812_pio_ref_count) { ++ class_destroy(ws2812_pio_class); ++ unregister_chrdev_region(ws2812_pio_dev_num, MAX_INSTANCES); ++ } ++ mutex_unlock(&ws2812_pio_mutex); ++ ++ pio_close(state->pio); ++} ++ ++static const struct of_device_id ws2812_pio_rp1_match[] = { ++ { .compatible = "raspberrypi,ws2812-pio-rp1" }, ++ { } ++}; ++MODULE_DEVICE_TABLE(of, ws2812_pio_rp1_match); ++ ++static struct platform_driver ws2812_pio_rp1_driver = { ++ .driver = { ++ .name = "ws2812-pio-rp1", ++ .of_match_table = ws2812_pio_rp1_match, ++ }, ++ .probe = ws2812_pio_rp1_probe, ++ .remove_new = ws2812_pio_rp1_remove, ++}; ++module_platform_driver(ws2812_pio_rp1_driver); ++ ++MODULE_DESCRIPTION("WS2812 PIO RP1 driver"); ++MODULE_AUTHOR("Phil Elwell"); ++MODULE_LICENSE("GPL"); diff --git a/drivers/mmc/core/block.c b/drivers/mmc/core/block.c index 3564a0f63c9c..d5aca7b6dac7 100644 --- a/drivers/mmc/core/block.c @@ -153276,10 +159787,10 @@ index 3564a0f63c9c..d5aca7b6dac7 100644 if (ret) goto out; diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c -index 0af96548e7da..ee6c0fdbb3e9 100644 +index 74bc422059ee..96dac16bfb5e 100644 --- a/drivers/mmc/core/bus.c +++ b/drivers/mmc/core/bus.c -@@ -264,6 +264,8 @@ static void mmc_release_card(struct device *dev) +@@ -266,6 +266,8 @@ static void mmc_release_card(struct device *dev) sdio_free_common_cis(card); @@ -153289,19 +159800,34 @@ index 0af96548e7da..ee6c0fdbb3e9 100644 kfree(card); diff --git a/drivers/mmc/core/card.h b/drivers/mmc/core/card.h -index b7754a1b8d97..c282b71668bd 100644 +index 8476754b1b17..8b90c17b0a50 100644 --- a/drivers/mmc/core/card.h +++ b/drivers/mmc/core/card.h -@@ -84,6 +84,7 @@ struct mmc_fixup { - #define CID_MANFID_TOSHIBA 0x11 +@@ -85,10 +85,12 @@ struct mmc_fixup { + #define CID_MANFID_GIGASTONE 0x12 #define CID_MANFID_MICRON 0x13 #define CID_MANFID_SAMSUNG 0x15 +#define CID_MANFID_SAMSUNG_SD 0x1b #define CID_MANFID_APACER 0x27 #define CID_MANFID_KINGSTON 0x70 #define CID_MANFID_HYNIX 0x90 + #define CID_MANFID_KINGSTON_SD 0x9F ++#define CID_MANFID_LONGSYS_SD 0xAD + #define CID_MANFID_NUMONYX 0xFE + + #define END_FIXUP { NULL } +@@ -291,4 +293,9 @@ static inline int mmc_card_broken_sd_poweroff_notify(const struct mmc_card *c) + return c->quirks & MMC_QUIRK_BROKEN_SD_POWEROFF_NOTIFY; + } + ++static inline int mmc_card_working_sd_cq(const struct mmc_card *c) ++{ ++ return c->quirks & MMC_QUIRK_WORKING_SD_CQ; ++} ++ + #endif diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c -index a8c17b4cd737..d5e9c55a9e6c 100644 +index 39b5846db389..40374e2cd61f 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -455,6 +455,7 @@ int mmc_cqe_start_req(struct mmc_host *host, struct mmc_request *mrq) @@ -153355,6 +159881,35 @@ index a8c17b4cd737..d5e9c55a9e6c 100644 return 1; return 0; } +diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c +index cf396e8f34e9..11ad635a35b9 100644 +--- a/drivers/mmc/core/host.c ++++ b/drivers/mmc/core/host.c +@@ -275,7 +275,7 @@ EXPORT_SYMBOL(mmc_of_parse_clk_phase); + int mmc_of_parse(struct mmc_host *host) + { + struct device *dev = host->parent; +- u32 bus_width, drv_type, cd_debounce_delay_ms; ++ u32 bus_width, drv_type, cd_debounce_delay_ms, cq_allow; + int ret; + + if (!dev || !dev_fwnode(dev)) +@@ -410,6 +410,15 @@ int mmc_of_parse(struct mmc_host *host) + host->caps2 &= ~(MMC_CAP2_HS400_1_8V | MMC_CAP2_HS400_1_2V | + MMC_CAP2_HS400_ES); + ++ cq_allow = 0; ++ /* ++ * Downstream property - if a u32 and 2 instead of a bool, ++ * trust most A2 SD cards claiming CQ support. ++ */ ++ device_property_read_u32(dev, "supports-cqe", &cq_allow); ++ if (cq_allow == 2) ++ host->caps2 |= MMC_CAP2_SD_CQE_PERMISSIVE; ++ + /* Must be after "non-removable" check */ + if (device_property_read_u32(dev, "fixed-emmc-driver-type", &drv_type) == 0) { + if (host->caps & MMC_CAP_NONREMOVABLE) diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 7e39017e440f..8f1baf4a5430 100644 --- a/drivers/mmc/core/mmc.c @@ -153431,13 +159986,35 @@ index 9ade3bcbb714..7610cc759910 100644 #define MMC_CQE_DCMD_BUSY BIT(0) bool busy; diff --git a/drivers/mmc/core/quirks.h b/drivers/mmc/core/quirks.h -index 92905fc46436..2c6921e6f6dd 100644 +index 89b512905be1..efb0deb29aff 100644 --- a/drivers/mmc/core/quirks.h +++ b/drivers/mmc/core/quirks.h -@@ -25,6 +25,26 @@ static const struct mmc_fixup __maybe_unused mmc_sd_fixups[] = { - 0, -1ull, SDIO_ANY_ID, SDIO_ANY_ID, add_quirk_sd, - MMC_QUIRK_BROKEN_SD_CACHE, EXT_CSD_REV_ANY), - +@@ -18,10 +18,42 @@ + static const struct mmc_fixup __maybe_unused mmc_sd_fixups[] = { + /* + * Kingston Canvas Go! Plus microSD cards never finish SD cache flush. +- * This has so far only been observed on cards from 11/2019, while new +- * cards from 2023/05 do not exhibit this behavior. ++ * This has been observed on cards from 2019/11 and 2021/11, while new ++ * cards from 2023/05 and 2024/08 do not exhibit this behavior. + */ +- _FIXUP_EXT("SD64G", CID_MANFID_KINGSTON_SD, 0x5449, 2019, 11, ++ _FIXUP_EXT(CID_NAME_ANY, CID_MANFID_KINGSTON_SD, 0x5449, 2019, CID_MONTH_ANY, ++ 0, -1ull, SDIO_ANY_ID, SDIO_ANY_ID, add_quirk_sd, ++ MMC_QUIRK_BROKEN_SD_CACHE, EXT_CSD_REV_ANY), ++ ++ _FIXUP_EXT(CID_NAME_ANY, CID_MANFID_KINGSTON_SD, 0x5449, 2020, CID_MONTH_ANY, ++ 0, -1ull, SDIO_ANY_ID, SDIO_ANY_ID, add_quirk_sd, ++ MMC_QUIRK_BROKEN_SD_CACHE, EXT_CSD_REV_ANY), ++ ++ _FIXUP_EXT(CID_NAME_ANY, CID_MANFID_KINGSTON_SD, 0x5449, 2021, CID_MONTH_ANY, ++ 0, -1ull, SDIO_ANY_ID, SDIO_ANY_ID, add_quirk_sd, ++ MMC_QUIRK_BROKEN_SD_CACHE, EXT_CSD_REV_ANY), ++ ++ _FIXUP_EXT(CID_NAME_ANY, CID_MANFID_KINGSTON_SD, 0x5449, 2022, CID_MONTH_ANY, ++ 0, -1ull, SDIO_ANY_ID, SDIO_ANY_ID, add_quirk_sd, ++ MMC_QUIRK_BROKEN_SD_CACHE, EXT_CSD_REV_ANY), ++ + /* + * Samsung Pro Plus/EVO Plus/Pro Ultimate SD cards (2023) claim to cache + * flush OK, but become unresponsive afterwards. @@ -153455,13 +160032,23 @@ index 92905fc46436..2c6921e6f6dd 100644 + MMC_QUIRK_BROKEN_SD_CACHE, EXT_CSD_REV_ANY), + + _FIXUP_EXT(CID_NAME_ANY, CID_MANFID_SANDISK_SD, 0x5344, 2020, CID_MONTH_ANY, -+ 0, -1ull, SDIO_ANY_ID, SDIO_ANY_ID, add_quirk_sd, -+ MMC_QUIRK_BROKEN_SD_CACHE, EXT_CSD_REV_ANY), + 0, -1ull, SDIO_ANY_ID, SDIO_ANY_ID, add_quirk_sd, + MMC_QUIRK_BROKEN_SD_CACHE, EXT_CSD_REV_ANY), + +@@ -34,6 +66,12 @@ static const struct mmc_fixup __maybe_unused mmc_sd_fixups[] = { + MMC_QUIRK_BROKEN_SD_CACHE | MMC_QUIRK_BROKEN_SD_POWEROFF_NOTIFY, + EXT_CSD_REV_ANY), + ++ /* SD A2 allow-list - only trust CQ on these cards */ ++ /* Raspberry Pi A2 cards */ ++ _FIXUP_EXT(CID_NAME_ANY, CID_MANFID_LONGSYS_SD, 0x4c53, CID_YEAR_ANY, CID_MONTH_ANY, ++ cid_rev(1, 0, 0, 0), -1ull, SDIO_ANY_ID, SDIO_ANY_ID, add_quirk_sd, ++ MMC_QUIRK_WORKING_SD_CQ, EXT_CSD_REV_ANY), + END_FIXUP }; -@@ -134,6 +154,23 @@ static const struct mmc_fixup __maybe_unused mmc_blk_fixups[] = { +@@ -143,6 +181,23 @@ static const struct mmc_fixup __maybe_unused mmc_blk_fixups[] = { MMC_FIXUP(CID_NAME_ANY, CID_MANFID_SANDISK_SD, 0x5344, add_quirk_sd, MMC_QUIRK_BROKEN_SD_DISCARD), @@ -153486,7 +160073,7 @@ index 92905fc46436..2c6921e6f6dd 100644 }; diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c -index 240469a881a2..b1ea31d98442 100644 +index f02c3e5eb5c8..6b523ef2a435 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -715,7 +715,8 @@ MMC_DEV_ATTR(oemid, "0x%04x\n", card->cid.oemid); @@ -153812,10 +160399,14 @@ index 240469a881a2..b1ea31d98442 100644 if (err) goto free_card; } -@@ -1563,13 +1506,41 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr, +@@ -1563,13 +1506,45 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr, goto free_card; } ++ /* Disallow command queueing on unvetted cards unless overridden */ ++ if (!(host->caps2 & MMC_CAP2_SD_CQE_PERMISSIVE) && !mmc_card_working_sd_cq(card)) ++ card->ext_csd.cmdq_support = false; ++ + /* Enable command queueing if supported */ + if (card->ext_csd.cmdq_support && host->caps2 & MMC_CAP2_CQE) { + /* @@ -153857,7 +160448,7 @@ index 240469a881a2..b1ea31d98442 100644 } } -@@ -1650,7 +1621,7 @@ static int sd_busy_poweroff_notify_cb(void *cb_data, bool *busy) +@@ -1650,7 +1625,7 @@ static int sd_busy_poweroff_notify_cb(void *cb_data, bool *busy) * one byte offset and is one byte long. The Power Off Notification * Ready is bit 0. */ @@ -153866,7 +160457,7 @@ index 240469a881a2..b1ea31d98442 100644 card->ext_power.offset + 1, 1, data->reg_buf); if (err) { pr_warn("%s: error %d reading status reg of PM func\n", -@@ -1676,7 +1647,7 @@ static int sd_poweroff_notify(struct mmc_card *card) +@@ -1676,7 +1651,7 @@ static int sd_poweroff_notify(struct mmc_card *card) * Set the Power Off Notification bit in the power management settings * register at 2 bytes offset. */ @@ -155683,7 +162274,7 @@ index 000000000000..fc9f7886eb21 +MODULE_AUTHOR("Gellert Weisz"); diff --git a/drivers/mmc/host/bcm2835-sdhost.c b/drivers/mmc/host/bcm2835-sdhost.c new file mode 100644 -index 000000000000..8e95a9df2f88 +index 000000000000..b8fb5d5625a2 --- /dev/null +++ b/drivers/mmc/host/bcm2835-sdhost.c @@ -0,0 +1,2220 @@ @@ -157660,7 +164251,7 @@ index 000000000000..8e95a9df2f88 + } + + mmc->max_segs = 128; -+ mmc->max_req_size = 524288; ++ mmc->max_req_size = min_t(size_t, 524288, dma_max_mapping_size(&pdev->dev)); + mmc->max_seg_size = mmc->max_req_size; + mmc->max_blk_size = 512; + mmc->max_blk_count = 65535; @@ -157987,7 +164578,7 @@ index fe7a4eac9595..5da5bf8ad038 100644 return ret; } diff --git a/drivers/mmc/host/sdhci-brcmstb.c b/drivers/mmc/host/sdhci-brcmstb.c -index 25664cd5e90f..143226c2afc2 100644 +index 25664cd5e90f..81c62e2bbedd 100644 --- a/drivers/mmc/host/sdhci-brcmstb.c +++ b/drivers/mmc/host/sdhci-brcmstb.c @@ -11,6 +11,8 @@ @@ -158326,23 +164917,29 @@ index 25664cd5e90f..143226c2afc2 100644 {}, }; -@@ -256,6 +513,8 @@ static int sdhci_brcmstb_probe(struct platform_device *pdev) +@@ -254,8 +511,10 @@ static int sdhci_brcmstb_probe(struct platform_device *pdev) + struct sdhci_pltfm_host *pltfm_host; + const struct of_device_id *match; struct sdhci_brcmstb_priv *priv; - u32 actual_clock_mhz; +- u32 actual_clock_mhz; ++ u32 actual_clock_mhz, cqe; struct sdhci_host *host; + struct resource *iomem; + bool no_pinctrl = false; struct clk *clk; struct clk *base_clk = NULL; int res; -@@ -278,12 +537,19 @@ static int sdhci_brcmstb_probe(struct platform_device *pdev) +@@ -278,12 +537,21 @@ static int sdhci_brcmstb_probe(struct platform_device *pdev) return PTR_ERR(host); pltfm_host = sdhci_priv(host); + pltfm_host->clk = clk; + priv = sdhci_pltfm_priv(pltfm_host); - if (device_property_read_bool(&pdev->dev, "supports-cqe")) { +- if (device_property_read_bool(&pdev->dev, "supports-cqe")) { ++ cqe = 0; ++ device_property_read_u32(&pdev->dev, "supports-cqe", &cqe); ++ if (cqe > 0) { priv->flags |= BRCMSTB_PRIV_FLAGS_HAS_CQE; match_priv->ops->irq = sdhci_brcmstb_cqhci_irq; } @@ -158355,7 +164952,7 @@ index 25664cd5e90f..143226c2afc2 100644 /* Map in the non-standard CFG registers */ priv->cfg_regs = devm_platform_get_and_ioremap_resource(pdev, 1, NULL); if (IS_ERR(priv->cfg_regs)) { -@@ -296,6 +562,43 @@ static int sdhci_brcmstb_probe(struct platform_device *pdev) +@@ -296,6 +564,43 @@ static int sdhci_brcmstb_probe(struct platform_device *pdev) if (res) goto err; @@ -158399,7 +164996,7 @@ index 25664cd5e90f..143226c2afc2 100644 /* * Automatic clock gating does not work for SD cards that may * voltage switch so only enable it for non-removable devices. -@@ -312,6 +615,13 @@ static int sdhci_brcmstb_probe(struct platform_device *pdev) +@@ -312,6 +617,13 @@ static int sdhci_brcmstb_probe(struct platform_device *pdev) (host->mmc->caps2 & MMC_CAP2_HS400_ES)) host->mmc_host_ops.hs400_enhanced_strobe = match_priv->hs400es; @@ -158413,7 +165010,7 @@ index 25664cd5e90f..143226c2afc2 100644 /* * Supply the existing CAPS, but clear the UHS modes. This * will allow these modes to be specified by device tree -@@ -362,7 +672,6 @@ static int sdhci_brcmstb_probe(struct platform_device *pdev) +@@ -362,7 +674,6 @@ static int sdhci_brcmstb_probe(struct platform_device *pdev) if (res) goto err; @@ -158933,7 +165530,7 @@ index 78c972bb1d96..2e259d91b936 100644 struct macb_tx_skb rm9200_txq[2]; unsigned int max_tx_length; diff --git a/drivers/net/ethernet/cadence/macb_main.c b/drivers/net/ethernet/cadence/macb_main.c -index 8f61731e4554..0f9080371c4e 100644 +index 8f61731e4554..acff4a4e7933 100644 --- a/drivers/net/ethernet/cadence/macb_main.c +++ b/drivers/net/ethernet/cadence/macb_main.c @@ -40,6 +40,9 @@ @@ -159139,7 +165736,33 @@ index 8f61731e4554..0f9080371c4e 100644 .get_link_ksettings = macb_get_link_ksettings, .set_link_ksettings = macb_set_link_ksettings, .get_ringparam = macb_get_ringparam, -@@ -5062,6 +5176,11 @@ static int macb_probe(struct platform_device *pdev) +@@ -4909,6 +5023,17 @@ static const struct macb_config versal_config = { + .usrio = &macb_default_usrio, + }; + ++static const struct macb_config raspberrypi_rp1_config = { ++ .caps = MACB_CAPS_GIGABIT_MODE_AVAILABLE | MACB_CAPS_CLK_HW_CHG | ++ MACB_CAPS_JUMBO | ++ MACB_CAPS_GEM_HAS_PTP, ++ .dma_burst_length = 16, ++ .clk_init = macb_clk_init, ++ .init = macb_init, ++ .usrio = &macb_default_usrio, ++ .jumbo_max_len = 10240, ++}; ++ + static const struct of_device_id macb_dt_ids[] = { + { .compatible = "cdns,at91sam9260-macb", .data = &at91sam9260_config }, + { .compatible = "cdns,macb" }, +@@ -4929,6 +5054,7 @@ static const struct of_device_id macb_dt_ids[] = { + { .compatible = "microchip,mpfs-macb", .data = &mpfs_config }, + { .compatible = "microchip,sama7g5-gem", .data = &sama7g5_gem_config }, + { .compatible = "microchip,sama7g5-emac", .data = &sama7g5_emac_config }, ++ { .compatible = "raspberrypi,rp1-gem", .data = &raspberrypi_rp1_config }, + { .compatible = "xlnx,zynqmp-gem", .data = &zynqmp_config}, + { .compatible = "xlnx,zynq-gem", .data = &zynq_config }, + { .compatible = "xlnx,versal-gem", .data = &versal_config}, +@@ -5062,6 +5188,11 @@ static int macb_probe(struct platform_device *pdev) } } } @@ -159151,7 +165774,7 @@ index 8f61731e4554..0f9080371c4e 100644 spin_lock_init(&bp->lock); /* setup capabilities */ -@@ -5117,6 +5236,21 @@ static int macb_probe(struct platform_device *pdev) +@@ -5117,6 +5248,21 @@ static int macb_probe(struct platform_device *pdev) else bp->phy_interface = interface; @@ -159173,7 +165796,7 @@ index 8f61731e4554..0f9080371c4e 100644 /* IP specific init */ err = init(pdev); if (err) -@@ -5193,6 +5327,19 @@ static int macb_remove(struct platform_device *pdev) +@@ -5193,6 +5339,19 @@ static int macb_remove(struct platform_device *pdev) return 0; } @@ -159193,7 +165816,7 @@ index 8f61731e4554..0f9080371c4e 100644 static int __maybe_unused macb_suspend(struct device *dev) { struct net_device *netdev = dev_get_drvdata(dev); -@@ -5407,6 +5554,7 @@ static const struct dev_pm_ops macb_pm_ops = { +@@ -5407,6 +5566,7 @@ static const struct dev_pm_ops macb_pm_ops = { static struct platform_driver macb_driver = { .probe = macb_probe, .remove = macb_remove, @@ -159399,7 +166022,7 @@ index 000000000000..007d077edcad + rtl8168_setup_ldev(leds + i, ndev, i); +} diff --git a/drivers/net/ethernet/realtek/r8169_main.c b/drivers/net/ethernet/realtek/r8169_main.c -index 115adbce6c56..010f09d39679 100644 +index 6b9961ad0412..15ae2de905d9 100644 --- a/drivers/net/ethernet/realtek/r8169_main.c +++ b/drivers/net/ethernet/realtek/r8169_main.c @@ -289,6 +289,7 @@ enum rtl8168_8101_registers { @@ -159481,7 +166104,7 @@ index 115adbce6c56..010f09d39679 100644 static void r8168fp_adjust_ocp_cmd(struct rtl8169_private *tp, u32 *cmd, int type) { /* based on RTL8168FP_OOBMAC_BASE in vendor driver */ -@@ -5283,6 +5341,7 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) +@@ -5289,6 +5347,7 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) raw_spin_lock_init(&tp->cfg9346_usage_lock); raw_spin_lock_init(&tp->config25_lock); raw_spin_lock_init(&tp->mac_ocp_lock); @@ -159489,7 +166112,7 @@ index 115adbce6c56..010f09d39679 100644 dev->tstats = devm_netdev_alloc_pcpu_stats(&pdev->dev, struct pcpu_sw_netstats); -@@ -5439,6 +5498,12 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) +@@ -5445,6 +5504,12 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) if (rc) return rc; @@ -159697,7 +166320,7 @@ index 0b88635f4fbc..ae266a69822a 100644 priv->chip_id = phy_read_mmd(phydev, 3, LAN88XX_MMD3_CHIP_ID); priv->chip_rev = phy_read_mmd(phydev, 3, LAN88XX_MMD3_CHIP_REV); diff --git a/drivers/net/usb/lan78xx.c b/drivers/net/usb/lan78xx.c -index 2ae33ecb6749..8581f30f7bd6 100644 +index 09173d7b87ed..844911b06c5a 100644 --- a/drivers/net/usb/lan78xx.c +++ b/drivers/net/usb/lan78xx.c @@ -609,6 +609,20 @@ static int lan78xx_alloc_tx_resources(struct lan78xx_net *dev) @@ -159739,7 +166362,7 @@ index 2ae33ecb6749..8581f30f7bd6 100644 }; static void lan78xx_init_mac_address(struct lan78xx_net *dev) -@@ -2410,6 +2428,22 @@ static int lan78xx_phy_init(struct lan78xx_net *dev) +@@ -2411,6 +2429,22 @@ static int lan78xx_phy_init(struct lan78xx_net *dev) mii_adv_to_linkmode_adv_t(fc, mii_adv); linkmode_or(phydev->advertising, fc, phydev->advertising); @@ -159762,7 +166385,7 @@ index 2ae33ecb6749..8581f30f7bd6 100644 if (phydev->mdio.dev.of_node) { u32 reg; int len; -@@ -2883,6 +2917,11 @@ static int lan78xx_reset(struct lan78xx_net *dev) +@@ -2884,6 +2918,11 @@ static int lan78xx_reset(struct lan78xx_net *dev) int ret; u32 buf; u8 sig; @@ -159774,7 +166397,7 @@ index 2ae33ecb6749..8581f30f7bd6 100644 ret = lan78xx_read_reg(dev, HW_CFG, &buf); if (ret < 0) -@@ -2947,6 +2986,10 @@ static int lan78xx_reset(struct lan78xx_net *dev) +@@ -2948,6 +2987,10 @@ static int lan78xx_reset(struct lan78xx_net *dev) buf |= HW_CFG_MEF_; @@ -159785,7 +166408,7 @@ index 2ae33ecb6749..8581f30f7bd6 100644 ret = lan78xx_write_reg(dev, HW_CFG, buf); if (ret < 0) return ret; -@@ -3046,6 +3089,9 @@ static int lan78xx_reset(struct lan78xx_net *dev) +@@ -3047,6 +3090,9 @@ static int lan78xx_reset(struct lan78xx_net *dev) buf |= MAC_CR_AUTO_DUPLEX_ | MAC_CR_AUTO_SPEED_; } } @@ -159795,7 +166418,7 @@ index 2ae33ecb6749..8581f30f7bd6 100644 ret = lan78xx_write_reg(dev, MAC_CR, buf); if (ret < 0) return ret; -@@ -3443,8 +3489,14 @@ static int lan78xx_bind(struct lan78xx_net *dev, struct usb_interface *intf) +@@ -3444,8 +3490,14 @@ static int lan78xx_bind(struct lan78xx_net *dev, struct usb_interface *intf) if (DEFAULT_RX_CSUM_ENABLE) dev->net->features |= NETIF_F_RXCSUM; @@ -159812,7 +166435,7 @@ index 2ae33ecb6749..8581f30f7bd6 100644 if (DEFAULT_VLAN_RX_OFFLOAD) dev->net->features |= NETIF_F_HW_VLAN_CTAG_RX; -@@ -4412,7 +4464,13 @@ static int lan78xx_probe(struct usb_interface *intf, +@@ -4415,7 +4467,13 @@ static int lan78xx_probe(struct usb_interface *intf, if (ret < 0) goto out4; @@ -160806,7 +167429,7 @@ index 48414e8b9389..65e33df996dd 100644 const struct brcmf_event_msg *evtmsg, void *data); diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h -index b68c46caabe8..a5f96efdbac0 100644 +index b68c46caabe8..044fe0b62398 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h @@ -178,6 +178,11 @@ @@ -160821,7 +167444,7 @@ index b68c46caabe8..a5f96efdbac0 100644 /* MAX_CHUNK_LEN is the maximum length for data passing to firmware in each * ioctl. It is relatively small because firmware has small maximum size input * playload restriction for ioctls. -@@ -598,6 +603,46 @@ struct brcmf_wsec_sae_pwd_le { +@@ -598,6 +603,47 @@ struct brcmf_wsec_sae_pwd_le { u8 key[BRCMF_WSEC_MAX_SAE_PASSWORD_LEN]; }; @@ -160838,6 +167461,7 @@ index b68c46caabe8..a5f96efdbac0 100644 + u8 peer_mac[ETH_ALEN]; + __le32 ssid_len; + u8 ssid[IEEE80211_MAX_SSID_LEN]; ++ u8 pmkid[WLAN_PMKID_LEN]; +}; + +/** @@ -161434,6 +168058,38 @@ index 0340bba96868..090a75bcd728 100644 +#define DEFAULT_43012_MIN_RES_MASK 0x0f8bfe77 + #endif /* _SBCHIPC_H */ +diff --git a/drivers/nvme/host/pci.c b/drivers/nvme/host/pci.c +index 52c8fd3d5c47..e4ee8e61d7fa 100644 +--- a/drivers/nvme/host/pci.c ++++ b/drivers/nvme/host/pci.c +@@ -1932,6 +1932,7 @@ static void nvme_free_host_mem(struct nvme_dev *dev) + dev->nr_host_mem_descs = 0; + } + ++#if 0 + static int __nvme_alloc_host_mem(struct nvme_dev *dev, u64 preferred, + u32 chunk_size) + { +@@ -2000,9 +2001,11 @@ static int __nvme_alloc_host_mem(struct nvme_dev *dev, u64 preferred, + dev->host_mem_descs = NULL; + return -ENOMEM; + } ++#endif + + static int nvme_alloc_host_mem(struct nvme_dev *dev, u64 min, u64 preferred) + { ++#if 0 + u64 min_chunk = min_t(u64, preferred, PAGE_SIZE * MAX_ORDER_NR_PAGES); + u64 hmminds = max_t(u32, dev->ctrl.hmminds * 4096, PAGE_SIZE * 2); + u64 chunk_size; +@@ -2015,6 +2018,7 @@ static int nvme_alloc_host_mem(struct nvme_dev *dev, u64 min, u64 preferred) + nvme_free_host_mem(dev); + } + } ++#endif + + return -ENOMEM; + } diff --git a/drivers/nvmem/Kconfig b/drivers/nvmem/Kconfig index 5bc9c4874fe3..37137f23eee1 100644 --- a/drivers/nvmem/Kconfig @@ -162935,6 +169591,20 @@ index e47a77f943b1..457a8d29fad8 100644 return 0; fail: +diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c +index c879d88807e7..87aef41eacf8 100644 +--- a/drivers/pci/probe.c ++++ b/drivers/pci/probe.c +@@ -970,9 +970,6 @@ static int pci_register_host_bridge(struct pci_host_bridge *bridge) + else + pr_info("PCI host bridge to bus %s\n", name); + +- if (nr_node_ids > 1 && pcibus_to_node(bus) == NUMA_NO_NODE) +- dev_warn(&bus->dev, "Unknown NUMA node; performance will be reduced\n"); +- + /* Coalesce contiguous windows */ + resource_list_for_each_entry_safe(window, n, &resources) { + if (list_is_last(&window->node, &resources)) diff --git a/drivers/perf/Kconfig b/drivers/perf/Kconfig index 7b7b15f9bb6f..8de6595f1001 100644 --- a/drivers/perf/Kconfig @@ -163813,10 +170483,10 @@ index 1d89a2fd9b79..a67ac49a0d59 100644 select GENERIC_PHY select SOC_BRCMSTB if ARCH_BRCMSTB diff --git a/drivers/phy/broadcom/phy-brcm-usb-init-synopsys.c b/drivers/phy/broadcom/phy-brcm-usb-init-synopsys.c -index 4c10cafded4e..8d9e91c99bb4 100644 +index 530b571607c0..829d92804e1e 100644 --- a/drivers/phy/broadcom/phy-brcm-usb-init-synopsys.c +++ b/drivers/phy/broadcom/phy-brcm-usb-init-synopsys.c -@@ -335,6 +335,36 @@ static void usb_init_common_7216(struct brcm_usb_init_params *params) +@@ -341,6 +341,36 @@ static void usb_init_common_7216(struct brcm_usb_init_params *params) usb_init_common(params); } @@ -163853,7 +170523,7 @@ index 4c10cafded4e..8d9e91c99bb4 100644 static void usb_init_xhci(struct brcm_usb_init_params *params) { pr_debug("%s\n", __func__); -@@ -380,6 +410,18 @@ static void usb_uninit_common_7211b0(struct brcm_usb_init_params *params) +@@ -386,6 +416,18 @@ static void usb_uninit_common_7211b0(struct brcm_usb_init_params *params) } @@ -163872,7 +170542,7 @@ index 4c10cafded4e..8d9e91c99bb4 100644 static void usb_uninit_xhci(struct brcm_usb_init_params *params) { -@@ -434,6 +476,16 @@ static const struct brcm_usb_init_ops bcm7211b0_ops = { +@@ -440,6 +482,16 @@ static const struct brcm_usb_init_ops bcm7211b0_ops = { .set_dual_select = usb_set_dual_select, }; @@ -163889,7 +170559,7 @@ index 4c10cafded4e..8d9e91c99bb4 100644 void brcm_usb_dvr_init_7216(struct brcm_usb_init_params *params) { -@@ -451,3 +503,10 @@ void brcm_usb_dvr_init_7211b0(struct brcm_usb_init_params *params) +@@ -457,3 +509,10 @@ void brcm_usb_dvr_init_7211b0(struct brcm_usb_init_params *params) params->family_name = "7211"; params->ops = &bcm7211b0_ops; } @@ -167537,7 +174207,7 @@ index 5d19baae6a38..0675c8a2e560 100644 return pps_cdev_ioctl(file, cmd, arg); } diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig -index 8ebcddf91f7b..5adaa948c28b 100644 +index 8ebcddf91f7b..4e7147c23a6a 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig @@ -217,6 +217,17 @@ config PWM_FSL_FTM @@ -167558,7 +174228,25 @@ index 8ebcddf91f7b..5adaa948c28b 100644 config PWM_HIBVT tristate "HiSilicon BVT PWM support" depends on ARCH_HISI || COMPILE_TEST -@@ -473,6 +484,15 @@ config PWM_RASPBERRYPI_POE +@@ -454,6 +465,17 @@ config PWM_PCA9685 + To compile this driver as a module, choose M here: the module + will be called pwm-pca9685. + ++config PWM_PIO_RP1 ++ tristate "RP1 PIO PWM support" ++ depends on FIRMWARE_RP1 || COMPILE_TEST ++ help ++ This is a PWM framework driver for Raspberry Pi 5, using the PIO ++ hardware of RP1 to provide PWM functionality. Supports up to 4 ++ instances on GPIOs in bank 0. ++ ++ To compile this driver as a module, choose M here: the module ++ will be called pwm-pio-rp1. ++ + config PWM_PXA + tristate "PXA PWM support" + depends on ARCH_PXA || ARCH_MMP || COMPILE_TEST +@@ -473,6 +495,15 @@ config PWM_RASPBERRYPI_POE Enable Raspberry Pi firmware controller PWM bus used to control the official RPI PoE hat @@ -167575,7 +174263,7 @@ index 8ebcddf91f7b..5adaa948c28b 100644 tristate "Renesas R-Car PWM support" depends on ARCH_RENESAS || COMPILE_TEST diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile -index c822389c2a24..58591a0a7d92 100644 +index c822389c2a24..ba58ed4f754d 100644 --- a/drivers/pwm/Makefile +++ b/drivers/pwm/Makefile @@ -18,6 +18,7 @@ obj-$(CONFIG_PWM_CROS_EC) += pwm-cros-ec.o @@ -167586,8 +174274,11 @@ index c822389c2a24..58591a0a7d92 100644 obj-$(CONFIG_PWM_HIBVT) += pwm-hibvt.o obj-$(CONFIG_PWM_IMG) += pwm-img.o obj-$(CONFIG_PWM_IMX1) += pwm-imx1.o -@@ -43,6 +44,7 @@ obj-$(CONFIG_PWM_OMAP_DMTIMER) += pwm-omap-dmtimer.o +@@ -41,8 +42,10 @@ obj-$(CONFIG_PWM_MXS) += pwm-mxs.o + obj-$(CONFIG_PWM_NTXEC) += pwm-ntxec.o + obj-$(CONFIG_PWM_OMAP_DMTIMER) += pwm-omap-dmtimer.o obj-$(CONFIG_PWM_PCA9685) += pwm-pca9685.o ++obj-$(CONFIG_PWM_PIO_RP1) += pwm-pio-rp1.o obj-$(CONFIG_PWM_PXA) += pwm-pxa.o obj-$(CONFIG_PWM_RASPBERRYPI_POE) += pwm-raspberrypi-poe.o +obj-$(CONFIG_PWM_RP1) += pwm-rp1.o @@ -168065,6 +174756,263 @@ index 000000000000..638d46f7a9fb +MODULE_DESCRIPTION("PWM GPIO driver"); +MODULE_AUTHOR("Vincent Whitchurch"); +MODULE_LICENSE("GPL"); +diff --git a/drivers/pwm/pwm-pio-rp1.c b/drivers/pwm/pwm-pio-rp1.c +new file mode 100644 +index 000000000000..9bed7ba87197 +--- /dev/null ++++ b/drivers/pwm/pwm-pio-rp1.c +@@ -0,0 +1,251 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++/* ++ * Raspberry Pi PIO PWM. ++ * ++ * Copyright (C) 2024 Raspberry Pi Ltd. ++ * ++ * Author: Phil Elwell (phil@raspberrypi.com) ++ * ++ * Based on the pwm-rp1 driver by: ++ * Naushir Patuck ++ * and on the pwm-gpio driver by: ++ * Vincent Whitchurch ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++struct pwm_pio_rp1 { ++ struct pwm_chip chip; ++ struct device *dev; ++ struct gpio_desc *gpiod; ++ struct mutex mutex; ++ PIO pio; ++ uint sm; ++ uint offset; ++ uint gpio; ++ uint32_t period; /* In SM cycles */ ++ uint32_t duty_cycle; /* In SM cycles */ ++ enum pwm_polarity polarity; ++ bool enabled; ++}; ++ ++/* Generated from pwm.pio by pioasm */ ++#define pwm_wrap_target 0 ++#define pwm_wrap 6 ++#define pwm_loop_ticks 3 ++ ++static const uint16_t pwm_program_instructions[] = { ++ // .wrap_target ++ 0x9080, // 0: pull noblock side 0 ++ 0xa027, // 1: mov x, osr ++ 0xa046, // 2: mov y, isr ++ 0x00a5, // 3: jmp x != y, 5 ++ 0x1806, // 4: jmp 6 side 1 ++ 0xa042, // 5: nop ++ 0x0083, // 6: jmp y--, 3 ++ // .wrap ++}; ++ ++static const struct pio_program pwm_program = { ++ .instructions = pwm_program_instructions, ++ .length = 7, ++ .origin = -1, ++}; ++ ++static unsigned int pwm_pio_resolution __read_mostly; ++ ++static inline pio_sm_config pwm_program_get_default_config(uint offset) ++{ ++ pio_sm_config c = pio_get_default_sm_config(); ++ ++ sm_config_set_wrap(&c, offset + pwm_wrap_target, offset + pwm_wrap); ++ sm_config_set_sideset(&c, 2, true, false); ++ return c; ++} ++ ++static inline void pwm_program_init(PIO pio, uint sm, uint offset, uint pin) ++{ ++ pio_gpio_init(pio, pin); ++ ++ pio_sm_set_consecutive_pindirs(pio, sm, pin, 1, true); ++ pio_sm_config c = pwm_program_get_default_config(offset); ++ ++ sm_config_set_sideset_pins(&c, pin); ++ pio_sm_init(pio, sm, offset, &c); ++} ++ ++/* Write `period` to the input shift register - must be disabled */ ++static void pio_pwm_set_period(PIO pio, uint sm, uint32_t period) ++{ ++ pio_sm_put_blocking(pio, sm, period); ++ pio_sm_exec(pio, sm, pio_encode_pull(false, false)); ++ pio_sm_exec(pio, sm, pio_encode_out(pio_isr, 32)); ++} ++ ++/* Write `level` to TX FIFO. State machine will copy this into X. */ ++static void pio_pwm_set_level(PIO pio, uint sm, uint32_t level) ++{ ++ pio_sm_put_blocking(pio, sm, level); ++} ++ ++static int pwm_pio_rp1_apply(struct pwm_chip *chip, struct pwm_device *pwm, ++ const struct pwm_state *state) ++{ ++ struct pwm_pio_rp1 *ppwm = container_of(chip, struct pwm_pio_rp1, chip); ++ uint32_t new_duty_cycle; ++ uint32_t new_period; ++ ++ if (state->duty_cycle && state->duty_cycle < pwm_pio_resolution) ++ return -EINVAL; ++ ++ if (state->duty_cycle != state->period && ++ (state->period - state->duty_cycle < pwm_pio_resolution)) ++ return -EINVAL; ++ ++ new_period = state->period / pwm_pio_resolution; ++ new_duty_cycle = state->duty_cycle / pwm_pio_resolution; ++ ++ mutex_lock(&ppwm->mutex); ++ ++ if ((ppwm->enabled && !state->enabled) || new_period != ppwm->period) { ++ pio_sm_set_enabled(ppwm->pio, ppwm->sm, false); ++ ppwm->enabled = false; ++ } ++ ++ if (new_period != ppwm->period) { ++ pio_pwm_set_period(ppwm->pio, ppwm->sm, new_period); ++ ppwm->period = new_period; ++ } ++ ++ if (state->enabled && new_duty_cycle != ppwm->duty_cycle) { ++ pio_pwm_set_level(ppwm->pio, ppwm->sm, new_duty_cycle); ++ ppwm->duty_cycle = new_duty_cycle; ++ } ++ ++ if (state->polarity != ppwm->polarity) { ++ pio_gpio_set_outover(ppwm->pio, ppwm->gpio, ++ (state->polarity == PWM_POLARITY_INVERSED) ? ++ GPIO_OVERRIDE_INVERT : GPIO_OVERRIDE_NORMAL); ++ ppwm->polarity = state->polarity; ++ } ++ ++ if (!ppwm->enabled && state->enabled) { ++ pio_sm_set_enabled(ppwm->pio, ppwm->sm, true); ++ ppwm->enabled = true; ++ } ++ ++ mutex_unlock(&ppwm->mutex); ++ ++ return 0; ++} ++ ++static const struct pwm_ops pwm_pio_rp1_ops = { ++ .apply = pwm_pio_rp1_apply, ++}; ++ ++static int pwm_pio_rp1_probe(struct platform_device *pdev) ++{ ++ struct device_node *np = pdev->dev.of_node; ++ struct of_phandle_args of_args = { 0 }; ++ struct device *dev = &pdev->dev; ++ struct pwm_pio_rp1 *ppwm; ++ struct pwm_chip *chip; ++ bool is_rp1; ++ ++ ppwm = devm_kzalloc(dev, sizeof(*ppwm), GFP_KERNEL); ++ if (IS_ERR(ppwm)) ++ return PTR_ERR(ppwm); ++ ++ chip = &ppwm->chip; ++ ++ mutex_init(&ppwm->mutex); ++ ++ ppwm->gpiod = devm_gpiod_get(dev, NULL, GPIOD_ASIS); ++ /* Need to check that this is an RP1 GPIO in the first bank, and retrieve the offset */ ++ /* Unfortunately I think this has to be done by parsing the gpios property */ ++ if (IS_ERR(ppwm->gpiod)) ++ return dev_err_probe(dev, PTR_ERR(ppwm->gpiod), ++ "could not get a gpio\n"); ++ ++ /* This really shouldn't fail, given that we have a gpiod */ ++ if (of_parse_phandle_with_args(np, "gpios", "#gpio-cells", 0, &of_args)) ++ return dev_err_probe(dev, -EINVAL, ++ "can't find gpio declaration\n"); ++ ++ is_rp1 = of_device_is_compatible(of_args.np, "raspberrypi,rp1-gpio"); ++ of_node_put(of_args.np); ++ if (!is_rp1 || of_args.args_count != 2) ++ return dev_err_probe(dev, -EINVAL, ++ "not an RP1 gpio\n"); ++ ++ ppwm->gpio = of_args.args[0]; ++ ++ ppwm->pio = pio_open(); ++ if (IS_ERR(ppwm->pio)) ++ return dev_err_probe(dev, PTR_ERR(ppwm->pio), ++ "%pfw: could not open PIO\n", ++ dev_fwnode(dev)); ++ ++ ppwm->sm = pio_claim_unused_sm(ppwm->pio, false); ++ if ((int)ppwm->sm < 0) { ++ pio_close(ppwm->pio); ++ return dev_err_probe(dev, -EBUSY, ++ "%pfw: no free PIO SM\n", ++ dev_fwnode(dev)); ++ } ++ ++ ppwm->offset = pio_add_program(ppwm->pio, &pwm_program); ++ if (ppwm->offset == PIO_ORIGIN_ANY) { ++ pio_close(ppwm->pio); ++ return dev_err_probe(dev, -EBUSY, ++ "%pfw: not enough PIO program space\n", ++ dev_fwnode(dev)); ++ } ++ ++ pwm_program_init(ppwm->pio, ppwm->sm, ppwm->offset, ppwm->gpio); ++ ++ pwm_pio_resolution = (1000u * 1000 * 1000 * pwm_loop_ticks) / clock_get_hz(clk_sys); ++ ++ chip->dev = dev; ++ chip->ops = &pwm_pio_rp1_ops; ++ chip->atomic = true; ++ chip->npwm = 1; ++ ++ platform_set_drvdata(pdev, ppwm); ++ ++ return devm_pwmchip_add(dev, chip); ++} ++ ++static void pwm_pio_rp1_remove(struct platform_device *pdev) ++{ ++ struct pwm_pio_rp1 *ppwm = platform_get_drvdata(pdev); ++ ++ pio_close(ppwm->pio); ++} ++ ++static const struct of_device_id pwm_pio_rp1_dt_ids[] = { ++ { .compatible = "raspberrypi,pwm-pio-rp1" }, ++ { /* sentinel */ } ++}; ++MODULE_DEVICE_TABLE(of, pwm_pio_rp1_dt_ids); ++ ++static struct platform_driver pwm_pio_rp1_driver = { ++ .driver = { ++ .name = "pwm-pio-rp1", ++ .of_match_table = pwm_pio_rp1_dt_ids, ++ }, ++ .probe = pwm_pio_rp1_probe, ++ .remove_new = pwm_pio_rp1_remove, ++}; ++module_platform_driver(pwm_pio_rp1_driver); ++ ++MODULE_DESCRIPTION("PWM PIO RP1 driver"); ++MODULE_AUTHOR("Phil Elwell"); ++MODULE_LICENSE("GPL"); diff --git a/drivers/pwm/pwm-raspberrypi-poe.c b/drivers/pwm/pwm-raspberrypi-poe.c index 2939b71a7ba7..4cf3d6fd5011 100644 --- a/drivers/pwm/pwm-raspberrypi-poe.c @@ -169740,7 +176688,7 @@ index 0274c9295514..c3f5ee811fb1 100644 if (dws->dma_ops && dws->dma_ops->dma_exit) diff --git a/drivers/spi/spi-dw-dma.c b/drivers/spi/spi-dw-dma.c -index 0ecbb6c36e23..93729275c63c 100644 +index 0ecbb6c36e23..16ca94149651 100644 --- a/drivers/spi/spi-dw-dma.c +++ b/drivers/spi/spi-dw-dma.c @@ -6,6 +6,7 @@ @@ -169751,7 +176699,36 @@ index 0ecbb6c36e23..93729275c63c 100644 #include #include #include -@@ -470,13 +471,12 @@ static int dw_spi_dma_setup(struct dw_spi *dws, struct spi_transfer *xfer) +@@ -303,6 +304,12 @@ static int dw_spi_dma_wait_tx_done(struct dw_spi *dws, + return -EIO; + } + ++ if (!xfer->rx_buf) { ++ delay.value = dws->n_bytes * BITS_PER_BYTE; ++ while (dw_readl(dws, DW_SPI_SR) & DW_SPI_SR_BUSY) ++ spi_delay_exec(&delay, xfer); ++ } ++ + return 0; + } + +@@ -329,7 +336,6 @@ static int dw_spi_dma_config_tx(struct dw_spi *dws) + txconf.direction = DMA_MEM_TO_DEV; + txconf.dst_addr = dws->dma_addr; + txconf.dst_maxburst = dws->txburst; +- txconf.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + txconf.dst_addr_width = dw_spi_dma_convert_width(dws->n_bytes); + txconf.device_fc = false; + +@@ -430,7 +436,6 @@ static int dw_spi_dma_config_rx(struct dw_spi *dws) + rxconf.direction = DMA_DEV_TO_MEM; + rxconf.src_addr = dws->dma_addr; + rxconf.src_maxburst = dws->rxburst; +- rxconf.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + rxconf.src_addr_width = dw_spi_dma_convert_width(dws->n_bytes); + rxconf.device_fc = false; + +@@ -470,13 +475,12 @@ static int dw_spi_dma_setup(struct dw_spi *dws, struct spi_transfer *xfer) u16 imr, dma_ctrl; int ret; @@ -169770,7 +176747,7 @@ index 0ecbb6c36e23..93729275c63c 100644 if (xfer->rx_buf) { ret = dw_spi_dma_config_rx(dws); -@@ -485,13 +485,17 @@ static int dw_spi_dma_setup(struct dw_spi *dws, struct spi_transfer *xfer) +@@ -485,13 +489,17 @@ static int dw_spi_dma_setup(struct dw_spi *dws, struct spi_transfer *xfer) } /* Set the DMA handshaking interface */ @@ -169790,7 +176767,7 @@ index 0ecbb6c36e23..93729275c63c 100644 if (xfer->rx_buf) imr |= DW_SPI_INT_RXUI | DW_SPI_INT_RXOI; dw_spi_umask_intr(dws, imr); -@@ -508,15 +512,16 @@ static int dw_spi_dma_transfer_all(struct dw_spi *dws, +@@ -508,15 +516,16 @@ static int dw_spi_dma_transfer_all(struct dw_spi *dws, { int ret; @@ -169813,7 +176790,7 @@ index 0ecbb6c36e23..93729275c63c 100644 if (ret) goto err_clear_dmac; -@@ -524,7 +529,15 @@ static int dw_spi_dma_transfer_all(struct dw_spi *dws, +@@ -524,7 +533,15 @@ static int dw_spi_dma_transfer_all(struct dw_spi *dws, dma_async_issue_pending(dws->rxchan); } @@ -170065,10 +177042,10 @@ index d8db4564b406..c95e4e9c14e3 100644 static int spi_gpio_probe(struct platform_device *pdev) diff --git a/drivers/spi/spi-rp2040-gpio-bridge.c b/drivers/spi/spi-rp2040-gpio-bridge.c new file mode 100644 -index 000000000000..418bd0ebb0c0 +index 000000000000..304e34f1dbf2 --- /dev/null +++ b/drivers/spi/spi-rp2040-gpio-bridge.c -@@ -0,0 +1,1244 @@ +@@ -0,0 +1,1249 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * RP2040 GPIO Bridge @@ -170688,7 +177665,6 @@ index 000000000000..418bd0ebb0c0 + &clock_mux); + + gpiod_direction_output(priv_data->fast_xfer_gpios->desc[0], 1); -+ gpiod_direction_output(priv_data->fast_xfer_gpios->desc[1], 0); + + rp2040_gbdg_rp1_calc_offsets(priv_data->fast_xfer_data_index, + &data_bank, &data_offset); @@ -171027,7 +178003,8 @@ index 000000000000..418bd0ebb0c0 + + rp2040_gbdg->fast_xfer_gpios = + devm_gpiod_get_array_optional(dev, "fast_xfer", GPIOD_ASIS); -+ if (!rp2040_gbdg->fast_xfer_gpios) { ++ if (IS_ERR_OR_NULL(rp2040_gbdg->fast_xfer_gpios)) { ++ rp2040_gbdg->fast_xfer_gpios = NULL; + dev_info(dev, "Could not acquire fast_xfer-gpios\n"); + goto node_put; + } @@ -171059,6 +178036,11 @@ index 000000000000..418bd0ebb0c0 + goto node_put; + } + ++ /* ++ * fast_xfer mode requires first data bit to be clocked on a rising ++ * edge. Configure as output-low here before fast_xfer mode is entered. ++ */ ++ gpiod_direction_output(rp2040_gbdg->fast_xfer_gpios->desc[1], 0); +node_put: + if (of_args[0].np) + of_node_put(of_args[0].np); @@ -171314,10 +178296,10 @@ index 000000000000..418bd0ebb0c0 +MODULE_LICENSE("GPL"); +MODULE_SOFTDEP("pre: md5"); diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c -index 91e067418bbb..5d8081e09ed7 100644 +index 46a4a6b25b3b..245fb14d1291 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c -@@ -3736,6 +3736,7 @@ static int spi_set_cs_timing(struct spi_device *spi) +@@ -3743,6 +3743,7 @@ static int spi_set_cs_timing(struct spi_device *spi) */ int spi_setup(struct spi_device *spi) { @@ -171325,7 +178307,7 @@ index 91e067418bbb..5d8081e09ed7 100644 unsigned bad_bits, ugly_bits; int status = 0; -@@ -3756,6 +3757,14 @@ int spi_setup(struct spi_device *spi) +@@ -3763,6 +3764,14 @@ int spi_setup(struct spi_device *spi) (SPI_TX_DUAL | SPI_TX_QUAD | SPI_TX_OCTAL | SPI_RX_DUAL | SPI_RX_QUAD | SPI_RX_OCTAL))) return -EINVAL; @@ -183884,7 +190866,7 @@ index 000000000000..107460ad1be3 + +#endif /* __VC_SM_CMA_IOCTL_H */ diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c -index 92aa98bbdc66..83988b4f3cc5 100644 +index 92aa98bbdc66..366eaf6f1015 100644 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c @@ -22,6 +22,7 @@ @@ -183929,7 +190911,7 @@ index 92aa98bbdc66..83988b4f3cc5 100644 struct rpi_firmware *fw; }; -@@ -115,10 +127,16 @@ struct vchiq_arm_state { +@@ -115,10 +127,21 @@ struct vchiq_arm_state { int first_connect; }; @@ -183937,6 +190919,11 @@ index 92aa98bbdc66..83988b4f3cc5 100644 + .cache_line_size = 64, + .use_36bit_addrs = true, +}; ++ ++struct vchiq_2835_state { ++ int inited; ++ struct vchiq_arm_state arm_state; ++}; + struct vchiq_pagelist_info { struct pagelist *pagelist; @@ -183946,7 +190933,7 @@ index 92aa98bbdc66..83988b4f3cc5 100644 enum dma_data_direction dma_dir; unsigned int num_pages; unsigned int pages_need_release; -@@ -139,10 +157,13 @@ static void __iomem *g_regs; +@@ -139,10 +162,13 @@ static void __iomem *g_regs; * of 32. */ static unsigned int g_cache_line_size = 32; @@ -183960,7 +190947,7 @@ index 92aa98bbdc66..83988b4f3cc5 100644 static DEFINE_SEMAPHORE(g_free_fragments_mutex, 1); -@@ -172,15 +193,20 @@ static void +@@ -172,15 +198,20 @@ static void cleanup_pagelistinfo(struct vchiq_instance *instance, struct vchiq_pagelist_info *pagelistinfo) { if (pagelistinfo->scatterlist_mapped) { @@ -183984,7 +190971,7 @@ index 92aa98bbdc66..83988b4f3cc5 100644 } static inline bool -@@ -215,6 +241,7 @@ create_pagelist(struct vchiq_instance *instance, char *buf, char __user *ubuf, +@@ -215,6 +246,7 @@ create_pagelist(struct vchiq_instance *instance, char *buf, char __user *ubuf, u32 *addrs; unsigned int num_pages, offset, i, k; int actual_pages; @@ -183992,7 +190979,7 @@ index 92aa98bbdc66..83988b4f3cc5 100644 size_t pagelist_size; struct scatterlist *scatterlist, *sg; int dma_buffers; -@@ -244,8 +271,14 @@ create_pagelist(struct vchiq_instance *instance, char *buf, char __user *ubuf, +@@ -244,8 +276,14 @@ create_pagelist(struct vchiq_instance *instance, char *buf, char __user *ubuf, /* Allocate enough storage to hold the page pointers and the page * list */ @@ -184009,7 +190996,7 @@ index 92aa98bbdc66..83988b4f3cc5 100644 vchiq_log_trace(vchiq_arm_log_level, "%s - %pK", __func__, pagelist); -@@ -266,6 +299,7 @@ create_pagelist(struct vchiq_instance *instance, char *buf, char __user *ubuf, +@@ -266,6 +304,7 @@ create_pagelist(struct vchiq_instance *instance, char *buf, char __user *ubuf, pagelistinfo->pagelist = pagelist; pagelistinfo->pagelist_buffer_size = pagelist_size; pagelistinfo->dma_addr = dma_addr; @@ -184017,7 +191004,7 @@ index 92aa98bbdc66..83988b4f3cc5 100644 pagelistinfo->dma_dir = (type == PAGELIST_WRITE) ? DMA_TO_DEVICE : DMA_FROM_DEVICE; pagelistinfo->num_pages = num_pages; -@@ -332,7 +366,7 @@ create_pagelist(struct vchiq_instance *instance, char *buf, char __user *ubuf, +@@ -332,7 +371,7 @@ create_pagelist(struct vchiq_instance *instance, char *buf, char __user *ubuf, count -= len; } @@ -184026,7 +191013,7 @@ index 92aa98bbdc66..83988b4f3cc5 100644 scatterlist, num_pages, pagelistinfo->dma_dir); -@@ -346,22 +380,61 @@ create_pagelist(struct vchiq_instance *instance, char *buf, char __user *ubuf, +@@ -346,22 +385,61 @@ create_pagelist(struct vchiq_instance *instance, char *buf, char __user *ubuf, /* Combine adjacent blocks for performance */ k = 0; @@ -184104,7 +191091,7 @@ index 92aa98bbdc66..83988b4f3cc5 100644 } /* Partial cache lines (fragments) require special measures */ -@@ -405,7 +478,7 @@ free_pagelist(struct vchiq_instance *instance, struct vchiq_pagelist_info *pagel +@@ -405,7 +483,7 @@ free_pagelist(struct vchiq_instance *instance, struct vchiq_pagelist_info *pagel * NOTE: dma_unmap_sg must be called before the * cpu can touch any of the data/pages. */ @@ -184113,7 +191100,7 @@ index 92aa98bbdc66..83988b4f3cc5 100644 pagelistinfo->num_pages, pagelistinfo->dma_dir); pagelistinfo->scatterlist_mapped = 0; -@@ -460,6 +533,7 @@ free_pagelist(struct vchiq_instance *instance, struct vchiq_pagelist_info *pagel +@@ -460,6 +538,7 @@ free_pagelist(struct vchiq_instance *instance, struct vchiq_pagelist_info *pagel static int vchiq_platform_init(struct platform_device *pdev, struct vchiq_state *state) { struct device *dev = &pdev->dev; @@ -184121,7 +191108,7 @@ index 92aa98bbdc66..83988b4f3cc5 100644 struct vchiq_drvdata *drvdata = platform_get_drvdata(pdev); struct rpi_firmware *fw = drvdata->fw; struct vchiq_slot_zero *vchiq_slot_zero; -@@ -481,6 +555,24 @@ static int vchiq_platform_init(struct platform_device *pdev, struct vchiq_state +@@ -481,6 +560,24 @@ static int vchiq_platform_init(struct platform_device *pdev, struct vchiq_state g_cache_line_size = drvdata->cache_line_size; g_fragments_size = 2 * g_cache_line_size; @@ -184146,7 +191133,7 @@ index 92aa98bbdc66..83988b4f3cc5 100644 /* Allocate space for the channels in coherent memory */ slot_mem_size = PAGE_ALIGN(TOTAL_SLOTS * VCHIQ_SLOT_SIZE); frag_mem_size = PAGE_ALIGN(g_fragments_size * MAX_FRAGMENTS); -@@ -493,13 +585,14 @@ static int vchiq_platform_init(struct platform_device *pdev, struct vchiq_state +@@ -493,13 +590,14 @@ static int vchiq_platform_init(struct platform_device *pdev, struct vchiq_state } WARN_ON(((unsigned long)slot_mem & (PAGE_SIZE - 1)) != 0); @@ -184162,7 +191149,7 @@ index 92aa98bbdc66..83988b4f3cc5 100644 vchiq_slot_zero->platform_data[VCHIQ_PLATFORM_FRAGMENTS_COUNT_IDX] = MAX_FRAGMENTS; -@@ -533,7 +626,6 @@ static int vchiq_platform_init(struct platform_device *pdev, struct vchiq_state +@@ -533,7 +631,6 @@ static int vchiq_platform_init(struct platform_device *pdev, struct vchiq_state } /* Send the base address of the slots to VideoCore */ @@ -184170,7 +191157,7 @@ index 92aa98bbdc66..83988b4f3cc5 100644 err = rpi_firmware_property(fw, RPI_FIRMWARE_VCHIQ_INIT, &channelbase, sizeof(channelbase)); if (err) { -@@ -547,6 +639,15 @@ static int vchiq_platform_init(struct platform_device *pdev, struct vchiq_state +@@ -547,6 +644,15 @@ static int vchiq_platform_init(struct platform_device *pdev, struct vchiq_state return -ENXIO; } @@ -184186,7 +191173,7 @@ index 92aa98bbdc66..83988b4f3cc5 100644 vchiq_log_info(vchiq_arm_log_level, "vchiq_init - done (slots %pK, phys %pad)", vchiq_slot_zero, &slot_phys); -@@ -1752,6 +1853,7 @@ void vchiq_platform_conn_state_changed(struct vchiq_state *state, +@@ -1752,6 +1858,7 @@ void vchiq_platform_conn_state_changed(struct vchiq_state *state, static const struct of_device_id vchiq_of_match[] = { { .compatible = "brcm,bcm2835-vchiq", .data = &bcm2835_drvdata }, { .compatible = "brcm,bcm2836-vchiq", .data = &bcm2836_drvdata }, @@ -184194,7 +191181,7 @@ index 92aa98bbdc66..83988b4f3cc5 100644 {}, }; MODULE_DEVICE_TABLE(of, vchiq_of_match); -@@ -1761,6 +1863,7 @@ vchiq_register_child(struct platform_device *pdev, const char *name) +@@ -1761,6 +1868,7 @@ vchiq_register_child(struct platform_device *pdev, const char *name) { struct platform_device_info pdevinfo; struct platform_device *child; @@ -184202,7 +191189,7 @@ index 92aa98bbdc66..83988b4f3cc5 100644 memset(&pdevinfo, 0, sizeof(pdevinfo)); -@@ -1769,12 +1872,32 @@ vchiq_register_child(struct platform_device *pdev, const char *name) +@@ -1769,12 +1877,32 @@ vchiq_register_child(struct platform_device *pdev, const char *name) pdevinfo.id = PLATFORM_DEVID_NONE; pdevinfo.dma_mask = DMA_BIT_MASK(32); @@ -184235,7 +191222,7 @@ index 92aa98bbdc66..83988b4f3cc5 100644 return child; } -@@ -1825,8 +1948,11 @@ static int vchiq_probe(struct platform_device *pdev) +@@ -1825,8 +1953,11 @@ static int vchiq_probe(struct platform_device *pdev) goto error_exit; } @@ -184247,7 +191234,7 @@ index 92aa98bbdc66..83988b4f3cc5 100644 return 0; -@@ -1838,8 +1964,11 @@ static int vchiq_probe(struct platform_device *pdev) +@@ -1838,8 +1969,11 @@ static int vchiq_probe(struct platform_device *pdev) static void vchiq_remove(struct platform_device *pdev) { @@ -188314,7 +195301,7 @@ index 4f4502fb5454..f31e3c9b7d37 100644 ret = serial8250_register_8250_port(&up); if (ret < 0) { diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c -index 3449f8790e46..e561f21cd6c3 100644 +index c536028e92dc..679a74d9ff29 100644 --- a/drivers/tty/serial/8250/8250_core.c +++ b/drivers/tty/serial/8250/8250_core.c @@ -253,6 +253,18 @@ static void serial8250_timeout(struct timer_list *t) @@ -188347,7 +195334,7 @@ index 3449f8790e46..e561f21cd6c3 100644 * If the "interrupt" for this port doesn't correspond with any * hardware interrupt, we use a timer-based system. The original diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c -index a17803da83f8..8099e6a261c2 100644 +index 2b1b2928ef7b..e854c61065f1 100644 --- a/drivers/tty/serial/8250/8250_port.c +++ b/drivers/tty/serial/8250/8250_port.c @@ -1529,6 +1529,9 @@ static void serial8250_stop_tx(struct uart_port *port) @@ -188380,8 +195367,42 @@ index a17803da83f8..8099e6a261c2 100644 } return status; +diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig +index bdc568a4ab66..c91d260cb1eb 100644 +--- a/drivers/tty/serial/Kconfig ++++ b/drivers/tty/serial/Kconfig +@@ -1578,6 +1578,17 @@ config SERIAL_NUVOTON_MA35D1_CONSOLE + but you can alter that using a kernel command line option such as + "console=ttyNVTx". + ++config SERIAL_RPI_FW ++ tristate "Raspberry Pi Firmware software UART support" ++ depends on RASPBERRYPI_FIRMWARE || COMPILE_TEST ++ select SERIAL_CORE ++ help ++ This selects the Raspberry Pi firmware UART. This is a bit-bashed ++ implementation running on the Raspbery Pi VPU core. ++ This is not supported on Raspberry Pi 5 or newer platforms. ++ ++ If unsure, say N. ++ + endmenu + + config SERIAL_MCTRL_GPIO +diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile +index 138abbc89738..ef0cc5d6fc4c 100644 +--- a/drivers/tty/serial/Makefile ++++ b/drivers/tty/serial/Makefile +@@ -88,6 +88,7 @@ obj-$(CONFIG_SERIAL_MILBEAUT_USIO) += milbeaut_usio.o + obj-$(CONFIG_SERIAL_SIFIVE) += sifive.o + obj-$(CONFIG_SERIAL_LITEUART) += liteuart.o + obj-$(CONFIG_SERIAL_SUNPLUS) += sunplus-uart.o ++obj-$(CONFIG_SERIAL_RPI_FW) += rpi-fw-uart.o + + # GPIOLIB helpers for modem control lines + obj-$(CONFIG_SERIAL_MCTRL_GPIO) += serial_mctrl_gpio.o diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c -index 362bbcdece0d..39fd89e8e663 100644 +index bb31ac9cae8c..778bac1981a4 100644 --- a/drivers/tty/serial/amba-pl011.c +++ b/drivers/tty/serial/amba-pl011.c @@ -152,6 +152,20 @@ static const struct vendor_data vendor_sbsa = { @@ -188405,7 +195426,15 @@ index 362bbcdece0d..39fd89e8e663 100644 #ifdef CONFIG_ACPI_SPCR_TABLE static const struct vendor_data vendor_qdt_qdf2400_e44 = { .reg_offset = pl011_std_offsets, -@@ -473,6 +487,12 @@ static void pl011_dma_probe(struct uart_amba_port *uap) +@@ -454,6 +468,7 @@ static void pl011_dma_probe(struct uart_amba_port *uap) + .src_addr = uap->port.mapbase + + pl011_reg_to_offset(uap, REG_DR), + .src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE, ++ .dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE, + .direction = DMA_DEV_TO_MEM, + .src_maxburst = uap->fifosize >> 2, + .device_fc = false, +@@ -473,6 +488,12 @@ static void pl011_dma_probe(struct uart_amba_port *uap) "RX DMA disabled - no residue processing\n"); return; } @@ -188418,7 +195447,7 @@ index 362bbcdece0d..39fd89e8e663 100644 } dmaengine_slave_config(chan, &rx_conf); uap->dmarx.chan = chan; -@@ -1456,6 +1476,7 @@ static bool pl011_tx_char(struct uart_amba_port *uap, unsigned char c, +@@ -1456,6 +1477,7 @@ static bool pl011_tx_char(struct uart_amba_port *uap, unsigned char c, return false; /* unable to transmit character */ pl011_write(c, uap, REG_DR); @@ -188426,7 +195455,7 @@ index 362bbcdece0d..39fd89e8e663 100644 uap->port.icount.tx++; return true; -@@ -1486,6 +1507,10 @@ static bool pl011_tx_chars(struct uart_amba_port *uap, bool from_irq) +@@ -1486,6 +1508,10 @@ static bool pl011_tx_chars(struct uart_amba_port *uap, bool from_irq) if (likely(from_irq) && count-- == 0) break; @@ -188437,7 +195466,7 @@ index 362bbcdece0d..39fd89e8e663 100644 if (!pl011_tx_char(uap, xmit->buf[xmit->tail], from_irq)) break; -@@ -2807,6 +2832,11 @@ static int pl011_probe(struct amba_device *dev, const struct amba_id *id) +@@ -2814,6 +2840,11 @@ static int pl011_probe(struct amba_device *dev, const struct amba_id *id) if (IS_ERR(uap->clk)) return PTR_ERR(uap->clk); @@ -188449,7 +195478,7 @@ index 362bbcdece0d..39fd89e8e663 100644 uap->reg_offset = vendor->reg_offset; uap->vendor = vendor; uap->fifosize = vendor->get_fifosize(dev); -@@ -2969,6 +2999,88 @@ static struct platform_driver arm_sbsa_uart_platform_driver = { +@@ -2976,6 +3007,88 @@ static struct platform_driver arm_sbsa_uart_platform_driver = { }, }; @@ -188538,7 +195567,7 @@ index 362bbcdece0d..39fd89e8e663 100644 static const struct amba_id pl011_ids[] = { { .id = 0x00041011, -@@ -3002,12 +3114,15 @@ static int __init pl011_init(void) +@@ -3009,12 +3122,15 @@ static int __init pl011_init(void) if (platform_driver_register(&arm_sbsa_uart_platform_driver)) pr_warn("could not register SBSA UART platform driver\n"); @@ -188554,8 +195583,577 @@ index 362bbcdece0d..39fd89e8e663 100644 amba_driver_unregister(&pl011_driver); } +diff --git a/drivers/tty/serial/rpi-fw-uart.c b/drivers/tty/serial/rpi-fw-uart.c +new file mode 100644 +index 000000000000..14d8cfc004e1 +--- /dev/null ++++ b/drivers/tty/serial/rpi-fw-uart.c +@@ -0,0 +1,563 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2024, Raspberry Pi Ltd. All rights reserved. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define RPI_FW_UART_RX_FIFO_RD 0xb0 ++#define RPI_FW_UART_RX_FIFO_WR 0xb4 ++#define RPI_FW_UART_TX_FIFO_RD 0xb8 ++#define RPI_FW_UART_TX_FIFO_WR 0xbc ++ ++#define RPI_FW_UART_FIFO_SIZE 32 ++#define RPI_FW_UART_FIFO_SIZE_MASK (RPI_FW_UART_FIFO_SIZE - 1) ++ ++#define RPI_FW_UART_MIN_VERSION 3 ++ ++struct rpi_fw_uart_params { ++ u32 start; ++ u32 baud; ++ u32 data_bits; ++ u32 stop_bits; ++ u32 gpio_rx; ++ u32 gpio_tx; ++ u32 flags; ++ u32 fifosize; ++ u32 rx_buffer; ++ u32 tx_buffer; ++ u32 version; ++ u32 fifo_reg_base; ++}; ++ ++struct rpi_fw_uart { ++ struct uart_driver driver; ++ struct uart_port port; ++ struct rpi_firmware *firmware; ++ struct gpio_desc *rx_gpiod; ++ struct gpio_desc *tx_gpiod; ++ unsigned int rx_gpio; ++ unsigned int tx_gpio; ++ unsigned int baud; ++ unsigned int data_bits; ++ unsigned int stop_bits; ++ unsigned char __iomem *base; ++ size_t dma_buffer_size; ++ ++ struct hrtimer trigger_start_rx; ++ ktime_t rx_poll_delay; ++ void *rx_buffer; ++ dma_addr_t rx_buffer_dma_addr; ++ int rx_stop; ++ ++ void *tx_buffer; ++ dma_addr_t tx_buffer_dma_addr; ++}; ++ ++static unsigned int rpi_fw_uart_tx_is_full(struct uart_port *port) ++{ ++ struct rpi_fw_uart *rfu = container_of(port, struct rpi_fw_uart, port); ++ u32 rd, wr; ++ ++ rd = readl(rfu->base + RPI_FW_UART_TX_FIFO_RD); ++ wr = readl(rfu->base + RPI_FW_UART_TX_FIFO_WR); ++ return ((wr + 1) & RPI_FW_UART_FIFO_SIZE_MASK) == rd; ++} ++ ++static unsigned int rpi_fw_uart_tx_is_empty(struct uart_port *port) ++{ ++ struct rpi_fw_uart *rfu = container_of(port, struct rpi_fw_uart, port); ++ u32 rd, wr; ++ ++ if (!rfu->tx_buffer) ++ return 1; ++ ++ rd = readl(rfu->base + RPI_FW_UART_TX_FIFO_RD); ++ wr = readl(rfu->base + RPI_FW_UART_TX_FIFO_WR); ++ ++ return rd == wr; ++} ++ ++unsigned int rpi_fw_uart_rx_is_empty(struct uart_port *port) ++{ ++ struct rpi_fw_uart *rfu = container_of(port, struct rpi_fw_uart, port); ++ u32 rd, wr; ++ ++ if (!rfu->rx_buffer) ++ return 1; ++ ++ rd = readl(rfu->base + RPI_FW_UART_RX_FIFO_RD); ++ wr = readl(rfu->base + RPI_FW_UART_RX_FIFO_WR); ++ ++ return rd == wr; ++} ++ ++static unsigned int rpi_fw_uart_tx_empty(struct uart_port *port) ++{ ++ return rpi_fw_uart_tx_is_empty(port) ? TIOCSER_TEMT : 0; ++} ++ ++static void rpi_fw_uart_set_mctrl(struct uart_port *port, unsigned int mctrl) ++{ ++ /* ++ * No hardware flow control, firmware automatically configures ++ * TX to output high and RX to input low. ++ */ ++ dev_dbg(port->dev, "%s mctrl %u\n", __func__, mctrl); ++} ++ ++static unsigned int rpi_fw_uart_get_mctrl(struct uart_port *port) ++{ ++ /* No hardware flow control */ ++ return TIOCM_CTS; ++} ++ ++static void rpi_fw_uart_stop(struct uart_port *port) ++{ ++ struct rpi_fw_uart_params msg = {.start = 0}; ++ struct rpi_fw_uart *rfu = container_of(port, struct rpi_fw_uart, port); ++ ++ hrtimer_cancel(&rfu->trigger_start_rx); ++ ++ if (rpi_firmware_property(rfu->firmware, ++ RPI_FIRMWARE_SET_SW_UART, ++ &msg, sizeof(msg))) ++ dev_warn(port->dev, ++ "Failed to shutdown rpi-fw uart. Firmware not configured?"); ++} ++ ++static void rpi_fw_uart_stop_tx(struct uart_port *port) ++{ ++ /* No supported by the current firmware APIs. */ ++} ++ ++static void rpi_fw_uart_stop_rx(struct uart_port *port) ++{ ++ struct rpi_fw_uart *rfu = container_of(port, struct rpi_fw_uart, port); ++ ++ rfu->rx_stop = 1; ++} ++ ++static unsigned int rpi_fw_write(struct uart_port *port, const char *s, ++ unsigned int count) ++{ ++ struct rpi_fw_uart *rfu = container_of(port, struct rpi_fw_uart, port); ++ u8 *out = rfu->tx_buffer; ++ unsigned int consumed = 0; ++ ++ while (consumed < count && !rpi_fw_uart_tx_is_full(port)) { ++ u32 wp = readl(rfu->base + RPI_FW_UART_TX_FIFO_WR) ++ & RPI_FW_UART_FIFO_SIZE_MASK; ++ out[wp] = s[consumed++]; ++ wp = (wp + 1) & RPI_FW_UART_FIFO_SIZE_MASK; ++ writel(wp, rfu->base + RPI_FW_UART_TX_FIFO_WR); ++ } ++ return consumed; ++} ++ ++/* Called with port.lock taken */ ++static void rpi_fw_uart_start_tx(struct uart_port *port) ++{ ++ struct circ_buf *xmit; ++ ++ xmit = &port->state->xmit; ++ for (;;) { ++ unsigned int consumed; ++ unsigned long count = CIRC_CNT_TO_END(xmit->head, xmit->tail, ++ UART_XMIT_SIZE); ++ if (!count) ++ break; ++ ++ consumed = rpi_fw_write(port, &xmit->buf[xmit->tail], count); ++ uart_xmit_advance(port, consumed); ++ } ++ uart_write_wakeup(port); ++} ++ ++/* Called with port.lock taken */ ++static void rpi_fw_uart_start_rx(struct uart_port *port) ++{ ++ struct tty_port *tty_port = &port->state->port; ++ struct rpi_fw_uart *rfu = container_of(port, struct rpi_fw_uart, port); ++ int count = 0; ++ ++ /* ++ * RX is polled, read up to a full buffer of data before trying again ++ * so that this can be interrupted if the firmware is filling the ++ * buffer too fast ++ */ ++ while (!rpi_fw_uart_rx_is_empty(port) && count < port->fifosize) { ++ const u8 *in = rfu->rx_buffer; ++ u32 rp = readl(rfu->base + RPI_FW_UART_RX_FIFO_RD) ++ & RPI_FW_UART_FIFO_SIZE_MASK; ++ ++ tty_insert_flip_char(tty_port, in[rp], TTY_NORMAL); ++ rp = (rp + 1) & RPI_FW_UART_FIFO_SIZE_MASK; ++ writel(rp, rfu->base + RPI_FW_UART_RX_FIFO_RD); ++ count++; ++ } ++ if (count) ++ tty_flip_buffer_push(tty_port); ++} ++ ++static enum hrtimer_restart rpi_fw_uart_trigger_rx(struct hrtimer *t) ++{ ++ unsigned long flags; ++ struct rpi_fw_uart *rfu = container_of(t, struct rpi_fw_uart, ++ trigger_start_rx); ++ ++ spin_lock_irqsave(&rfu->port.lock, flags); ++ if (rfu->rx_stop) { ++ spin_unlock_irqrestore(&rfu->port.lock, flags); ++ return HRTIMER_NORESTART; ++ } ++ ++ rpi_fw_uart_start_rx(&rfu->port); ++ spin_unlock_irqrestore(&rfu->port.lock, flags); ++ hrtimer_forward_now(t, rfu->rx_poll_delay); ++ return HRTIMER_RESTART; ++} ++ ++static void rpi_fw_uart_break_ctl(struct uart_port *port, int ctl) ++{ ++ dev_dbg(port->dev, "%s ctl %d\n", __func__, ctl); ++} ++ ++static int rpi_fw_uart_configure(struct uart_port *port) ++{ ++ struct rpi_fw_uart *rfu = container_of(port, struct rpi_fw_uart, port); ++ struct rpi_fw_uart_params msg; ++ unsigned long flags; ++ int rc; ++ ++ rpi_fw_uart_stop(port); ++ ++ memset(&msg, 0, sizeof(msg)); ++ msg.start = 1; ++ msg.gpio_rx = rfu->rx_gpio; ++ msg.gpio_tx = rfu->tx_gpio; ++ msg.data_bits = rfu->data_bits; ++ msg.stop_bits = rfu->stop_bits; ++ msg.baud = rfu->baud; ++ msg.fifosize = RPI_FW_UART_FIFO_SIZE; ++ msg.rx_buffer = (u32) rfu->rx_buffer_dma_addr; ++ msg.tx_buffer = (u32) rfu->tx_buffer_dma_addr; ++ ++ rfu->rx_poll_delay = ms_to_ktime(50); ++ ++ /* ++ * Reconfigures the firmware UART with the new settings. On the first ++ * call retrieve the addresses of the FIFO buffers. The buffers are ++ * allocated at startup and are not de-allocated. ++ * NB rpi_firmware_property can block ++ */ ++ rc = rpi_firmware_property(rfu->firmware, ++ RPI_FIRMWARE_SET_SW_UART, ++ &msg, sizeof(msg)); ++ if (rc) ++ goto fail; ++ ++ rc = rpi_firmware_property(rfu->firmware, ++ RPI_FIRMWARE_GET_SW_UART, ++ &msg, sizeof(msg)); ++ if (rc) ++ goto fail; ++ ++ dev_dbg(port->dev, "version %08x, reg addr %x\n", msg.version, ++ msg.fifo_reg_base); ++ ++ dev_dbg(port->dev, "started %d baud %u data %u stop %u rx %u tx %u flags %u fifosize %u\n", ++ msg.start, msg.baud, msg.data_bits, msg.stop_bits, ++ msg.gpio_rx, msg.gpio_tx, msg.flags, msg.fifosize); ++ ++ if (msg.fifosize != port->fifosize) { ++ dev_err(port->dev, "Expected fifo size %u actual %u", ++ port->fifosize, msg.fifosize); ++ rc = -EINVAL; ++ goto fail; ++ } ++ ++ if (!msg.start) { ++ dev_err(port->dev, "Firmware service not running\n"); ++ rc = -EINVAL; ++ } ++ ++ spin_lock_irqsave(&rfu->port.lock, flags); ++ rfu->rx_stop = 0; ++ hrtimer_start(&rfu->trigger_start_rx, ++ rfu->rx_poll_delay, HRTIMER_MODE_REL); ++ spin_unlock_irqrestore(&rfu->port.lock, flags); ++ return 0; ++fail: ++ dev_err(port->dev, "Failed to configure rpi-fw uart. Firmware not configured?"); ++ return rc; ++} ++ ++static void rpi_fw_uart_free_buffers(struct uart_port *port) ++{ ++ struct rpi_fw_uart *rfu = container_of(port, struct rpi_fw_uart, port); ++ ++ if (rfu->rx_buffer) ++ dma_free_coherent(port->dev, rfu->dma_buffer_size, ++ rfu->rx_buffer, GFP_ATOMIC); ++ ++ if (rfu->tx_buffer) ++ dma_free_coherent(port->dev, rfu->dma_buffer_size, ++ rfu->tx_buffer, GFP_ATOMIC); ++ ++ rfu->rx_buffer = NULL; ++ rfu->tx_buffer = NULL; ++ rfu->rx_buffer_dma_addr = 0; ++ rfu->tx_buffer_dma_addr = 0; ++} ++ ++static int rpi_fw_uart_alloc_buffers(struct uart_port *port) ++{ ++ struct rpi_fw_uart *rfu = container_of(port, struct rpi_fw_uart, port); ++ ++ if (rfu->tx_buffer) ++ return 0; ++ ++ rfu->dma_buffer_size = PAGE_ALIGN(RPI_FW_UART_FIFO_SIZE); ++ ++ rfu->rx_buffer = dma_alloc_coherent(port->dev, rfu->dma_buffer_size, ++ &rfu->rx_buffer_dma_addr, GFP_ATOMIC); ++ ++ if (!rfu->rx_buffer) ++ goto alloc_fail; ++ ++ rfu->tx_buffer = dma_alloc_coherent(port->dev, rfu->dma_buffer_size, ++ &rfu->tx_buffer_dma_addr, GFP_ATOMIC); ++ ++ if (!rfu->tx_buffer) ++ goto alloc_fail; ++ ++ dev_dbg(port->dev, "alloc-buffers %p %x %p %x\n", ++ rfu->rx_buffer, (u32) rfu->rx_buffer_dma_addr, ++ rfu->tx_buffer, (u32) rfu->tx_buffer_dma_addr); ++ return 0; ++ ++alloc_fail: ++ dev_err(port->dev, "%s uart buffer allocation failed\n", __func__); ++ rpi_fw_uart_free_buffers(port); ++ return -ENOMEM; ++} ++ ++static int rpi_fw_uart_startup(struct uart_port *port) ++{ ++ int rc; ++ ++ rc = rpi_fw_uart_alloc_buffers(port); ++ if (rc) ++ dev_err(port->dev, "Failed to start\n"); ++ return rc; ++} ++ ++static void rpi_fw_uart_shutdown(struct uart_port *port) ++{ ++ rpi_fw_uart_stop(port); ++ rpi_fw_uart_free_buffers(port); ++} ++ ++static void rpi_fw_uart_set_termios(struct uart_port *port, ++ struct ktermios *new, ++ const struct ktermios *old) ++{ ++ struct rpi_fw_uart *rfu = ++ container_of(port, struct rpi_fw_uart, port); ++ rfu->baud = uart_get_baud_rate(port, new, old, 50, 115200); ++ rfu->stop_bits = (new->c_cflag & CSTOPB) ? 2 : 1; ++ ++ rpi_fw_uart_configure(port); ++} ++ ++static const struct uart_ops rpi_fw_uart_ops = { ++ .tx_empty = rpi_fw_uart_tx_empty, ++ .set_mctrl = rpi_fw_uart_set_mctrl, ++ .get_mctrl = rpi_fw_uart_get_mctrl, ++ .stop_rx = rpi_fw_uart_stop_rx, ++ .stop_tx = rpi_fw_uart_stop_tx, ++ .start_tx = rpi_fw_uart_start_tx, ++ .break_ctl = rpi_fw_uart_break_ctl, ++ .startup = rpi_fw_uart_startup, ++ .shutdown = rpi_fw_uart_shutdown, ++ .set_termios = rpi_fw_uart_set_termios, ++}; ++ ++static int rpi_fw_uart_get_gpio_offset(struct device *dev, const char *name) ++{ ++ struct of_phandle_args of_args = { 0 }; ++ bool is_bcm28xx; ++ ++ /* This really shouldn't fail, given that we have a gpiod */ ++ if (of_parse_phandle_with_args(dev->of_node, name, "#gpio-cells", 0, &of_args)) ++ return dev_err_probe(dev, -EINVAL, "can't find gpio declaration\n"); ++ ++ is_bcm28xx = of_device_is_compatible(of_args.np, "brcm,bcm2835-gpio") || ++ of_device_is_compatible(of_args.np, "brcm,bcm2711-gpio"); ++ of_node_put(of_args.np); ++ if (!is_bcm28xx || of_args.args_count != 2) ++ return dev_err_probe(dev, -EINVAL, "not a BCM28xx gpio\n"); ++ ++ return of_args.args[0]; ++} ++ ++static int rpi_fw_uart_probe(struct platform_device *pdev) ++{ ++ struct device_node *firmware_node; ++ struct device *dev = &pdev->dev; ++ struct rpi_firmware *firmware; ++ struct uart_port *port; ++ struct rpi_fw_uart *rfu; ++ struct rpi_fw_uart_params msg; ++ int version_major; ++ int err; ++ ++ dev_dbg(dev, "%s of_node %p\n", __func__, dev->of_node); ++ ++ /* ++ * We can be probed either through the an old-fashioned ++ * platform device registration or through a DT node that is a ++ * child of the firmware node. Handle both cases. ++ */ ++ if (dev->of_node) ++ firmware_node = of_parse_phandle(dev->of_node, "firmware", 0); ++ else ++ firmware_node = of_find_compatible_node(NULL, NULL, ++ "raspberrypi,bcm2835-firmware"); ++ if (!firmware_node) { ++ dev_err(dev, "Missing firmware node\n"); ++ return -ENOENT; ++ } ++ ++ firmware = devm_rpi_firmware_get(dev, firmware_node); ++ of_node_put(firmware_node); ++ if (!firmware) ++ return -EPROBE_DEFER; ++ ++ rfu = devm_kzalloc(dev, sizeof(*rfu), GFP_KERNEL); ++ if (!rfu) ++ return -ENOMEM; ++ ++ rfu->firmware = firmware; ++ ++ err = rpi_firmware_property(rfu->firmware, RPI_FIRMWARE_GET_SW_UART, ++ &msg, sizeof(msg)); ++ if (err) { ++ dev_err(dev, "VC firmware does not support rpi-fw-uart\n"); ++ return err; ++ } ++ ++ version_major = msg.version >> 16; ++ if (msg.version < RPI_FW_UART_MIN_VERSION) { ++ dev_err(dev, "rpi-fw-uart fw version %d is too old min version %d\n", ++ version_major, RPI_FW_UART_MIN_VERSION); ++ return -EINVAL; ++ } ++ ++ rfu->rx_gpiod = devm_gpiod_get(dev, "rx", GPIOD_IN); ++ if (IS_ERR(rfu->rx_gpiod)) ++ return PTR_ERR(rfu->rx_gpiod); ++ ++ rfu->tx_gpiod = devm_gpiod_get(dev, "tx", GPIOD_OUT_HIGH); ++ if (IS_ERR(rfu->tx_gpiod)) ++ return PTR_ERR(rfu->tx_gpiod); ++ ++ rfu->rx_gpio = rpi_fw_uart_get_gpio_offset(dev, "rx-gpios"); ++ if (rfu->rx_gpio < 0) ++ return rfu->rx_gpio; ++ rfu->tx_gpio = rpi_fw_uart_get_gpio_offset(dev, "tx-gpios"); ++ if (rfu->tx_gpio < 0) ++ return rfu->tx_gpio; ++ ++ rfu->base = devm_platform_ioremap_resource(pdev, 0); ++ if (IS_ERR(rfu->base)) ++ return PTR_ERR(rfu->base); ++ ++ /* setup the driver */ ++ rfu->driver.owner = THIS_MODULE; ++ rfu->driver.driver_name = "ttyRFU"; ++ rfu->driver.dev_name = "ttyRFU"; ++ rfu->driver.nr = 1; ++ rfu->data_bits = 8; ++ ++ /* RX is polled */ ++ hrtimer_init(&rfu->trigger_start_rx, CLOCK_MONOTONIC, HRTIMER_MODE_REL); ++ rfu->trigger_start_rx.function = rpi_fw_uart_trigger_rx; ++ ++ err = uart_register_driver(&rfu->driver); ++ if (err) { ++ dev_err(dev, "failed to register UART driver: %d\n", ++ err); ++ return err; ++ } ++ ++ /* setup the port */ ++ port = &rfu->port; ++ spin_lock_init(&port->lock); ++ port->dev = &pdev->dev; ++ port->type = PORT_RPI_FW; ++ port->ops = &rpi_fw_uart_ops; ++ port->fifosize = RPI_FW_UART_FIFO_SIZE; ++ port->iotype = UPIO_MEM; ++ port->flags = UPF_BOOT_AUTOCONF; ++ port->private_data = rfu; ++ ++ err = uart_add_one_port(&rfu->driver, port); ++ if (err) { ++ dev_err(dev, "failed to add UART port: %d\n", err); ++ goto unregister_uart; ++ } ++ platform_set_drvdata(pdev, rfu); ++ ++ dev_info(dev, "version %d.%d gpios tx %u rx %u\n", ++ msg.version >> 16, msg.version & 0xffff, ++ rfu->tx_gpio, rfu->rx_gpio); ++ return 0; ++ ++unregister_uart: ++ uart_unregister_driver(&rfu->driver); ++ ++ return err; ++} ++ ++static int rpi_fw_uart_remove(struct platform_device *pdev) ++{ ++ struct rpi_fw_uart *rfu = platform_get_drvdata(pdev); ++ ++ uart_remove_one_port(&rfu->driver, &rfu->port); ++ uart_unregister_driver(&rfu->driver); ++ ++ return 0; ++} ++ ++static const struct of_device_id rpi_fw_match[] = { ++ { .compatible = "raspberrypi,firmware-uart" }, ++ { } ++}; ++MODULE_DEVICE_TABLE(of, rpi_fw_match); ++ ++static struct platform_driver rpi_fw_driver = { ++ .driver = { ++ .name = "rpi_fw-uart", ++ .of_match_table = rpi_fw_match, ++ }, ++ .probe = rpi_fw_uart_probe, ++ .remove = rpi_fw_uart_remove, ++}; ++module_platform_driver(rpi_fw_driver); ++ ++MODULE_AUTHOR("Tim Gover "); ++MODULE_LICENSE("GPL"); ++MODULE_DESCRIPTION("Raspberry Pi Firmware Software UART driver"); diff --git a/drivers/tty/serial/sc16is7xx.c b/drivers/tty/serial/sc16is7xx.c -index 673aeda71388..df6023ddaee0 100644 +index 2962e2351f2e..2879b764e493 100644 --- a/drivers/tty/serial/sc16is7xx.c +++ b/drivers/tty/serial/sc16is7xx.c @@ -766,6 +766,8 @@ static bool sc16is7xx_port_irq(struct sc16is7xx_port *s, int portno) @@ -188577,6 +196175,15 @@ index 673aeda71388..df6023ddaee0 100644 /* Enable modem status polling */ uart_port_lock_irqsave(port, &flags); sc16is7xx_enable_ms(port); +@@ -1464,7 +1469,7 @@ static int sc16is7xx_setup_mctrl_ports(struct sc16is7xx_port *s, + } + + static const struct serial_rs485 sc16is7xx_rs485_supported = { +- .flags = SER_RS485_ENABLED | SER_RS485_RTS_AFTER_SEND, ++ .flags = SER_RS485_ENABLED | SER_RS485_RTS_ON_SEND | SER_RS485_RTS_AFTER_SEND, + .delay_rts_before_send = 1, + .delay_rts_after_send = 1, /* Not supported but keep returning -EINVAL */ + }; diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile index 949eca0adebe..80af388baede 100644 --- a/drivers/usb/Makefile @@ -188623,10 +196230,10 @@ index 12b6dfeaf658..0de6ac768188 100644 * endpoint state is gone from hardware. usb_hcd_flush_endpoint() must * have been called previously. Use for set_configuration, set_interface, diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c -index 1ba3feb5e190..88e32e32929c 100644 +index d54fbf6555c5..3d5093f88eaf 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c -@@ -5710,7 +5710,7 @@ static void port_event(struct usb_hub *hub, int port1) +@@ -5723,7 +5723,7 @@ static void port_event(struct usb_hub *hub, int port1) port_dev->over_current_count++; port_over_current_notify(port_dev); @@ -188905,7 +196512,7 @@ index db67df29fb2b..b16e528859a8 100644 } diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c -index fcb509059d7c..220d09c555d9 100644 +index 318ae24a41f4..0778adc52f37 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -1208,6 +1208,24 @@ static void dwc3_config_threshold(struct dwc3 *dwc) @@ -188942,7 +196549,19 @@ index fcb509059d7c..220d09c555d9 100644 ret = dwc3_phy_power_on(dwc); if (ret) goto err_exit_phy; -@@ -1352,6 +1372,9 @@ static int dwc3_core_init(struct dwc3 *dwc) +@@ -1346,12 +1366,21 @@ static int dwc3_core_init(struct dwc3 *dwc) + if (dwc->dis_tx_ipgap_linecheck_quirk) + reg |= DWC3_GUCTL1_TX_IPGAP_LINECHECK_DIS; + ++ if (dwc->enh_nak_fs_quirk) ++ reg |= DWC3_GUCTL1_NAK_PER_ENH_FS; ++ ++ if (dwc->enh_nak_hs_quirk) ++ reg |= DWC3_GUCTL1_NAK_PER_ENH_HS; ++ + if (dwc->parkmode_disable_ss_quirk) + reg |= DWC3_GUCTL1_PARKMODE_DISABLE_SS; + if (dwc->parkmode_disable_hs_quirk) reg |= DWC3_GUCTL1_PARKMODE_DISABLE_HS; @@ -188952,7 +196571,7 @@ index fcb509059d7c..220d09c555d9 100644 if (DWC3_VER_IS_WITHIN(DWC3, 290A, ANY) && (dwc->maximum_speed == USB_SPEED_HIGH || dwc->maximum_speed == USB_SPEED_FULL)) -@@ -1362,6 +1385,24 @@ static int dwc3_core_init(struct dwc3 *dwc) +@@ -1362,6 +1391,24 @@ static int dwc3_core_init(struct dwc3 *dwc) dwc3_config_threshold(dwc); @@ -188977,15 +196596,15 @@ index fcb509059d7c..220d09c555d9 100644 return 0; err_power_off_phy: -@@ -1505,6 +1546,7 @@ static void dwc3_get_properties(struct dwc3 *dwc) +@@ -1505,6 +1552,7 @@ static void dwc3_get_properties(struct dwc3 *dwc) u8 tx_thr_num_pkt_prd = 0; u8 tx_max_burst_prd = 0; u8 tx_fifo_resize_max_num; + u8 axi_pipe_limit; - const char *usb_psy_name; - int ret; -@@ -1527,6 +1569,9 @@ static void dwc3_get_properties(struct dwc3 *dwc) + /* default to highest possible threshold */ + lpm_nyet_threshold = 0xf; +@@ -1525,6 +1573,9 @@ static void dwc3_get_properties(struct dwc3 *dwc) */ tx_fifo_resize_max_num = 6; @@ -188995,7 +196614,15 @@ index fcb509059d7c..220d09c555d9 100644 dwc->maximum_speed = usb_get_maximum_speed(dev); dwc->max_ssp_rate = usb_get_maximum_ssp_rate(dev); dwc->dr_mode = usb_get_dr_mode(dev); -@@ -1628,6 +1673,8 @@ static void dwc3_get_properties(struct dwc3 *dwc) +@@ -1615,10 +1666,16 @@ static void dwc3_get_properties(struct dwc3 *dwc) + "snps,resume-hs-terminations"); + dwc->ulpi_ext_vbus_drv = device_property_read_bool(dev, + "snps,ulpi-ext-vbus-drv"); ++ dwc->enh_nak_fs_quirk = device_property_read_bool(dev, ++ "snps,enhanced-nak-fs-quirk"); ++ dwc->enh_nak_hs_quirk = device_property_read_bool(dev, ++ "snps,enhanced-nak-hs-quirk"); + dwc->parkmode_disable_ss_quirk = device_property_read_bool(dev, "snps,parkmode-disable-ss-quirk"); dwc->parkmode_disable_hs_quirk = device_property_read_bool(dev, "snps,parkmode-disable-hs-quirk"); @@ -189004,7 +196631,7 @@ index fcb509059d7c..220d09c555d9 100644 dwc->gfladj_refclk_lpm_sel = device_property_read_bool(dev, "snps,gfladj-refclk-lpm-sel-quirk"); -@@ -1648,6 +1695,9 @@ static void dwc3_get_properties(struct dwc3 *dwc) +@@ -1639,6 +1696,9 @@ static void dwc3_get_properties(struct dwc3 *dwc) dwc->dis_split_quirk = device_property_read_bool(dev, "snps,dis-split-quirk"); @@ -189014,7 +196641,7 @@ index fcb509059d7c..220d09c555d9 100644 dwc->lpm_nyet_threshold = lpm_nyet_threshold; dwc->tx_de_emphasis = tx_de_emphasis; -@@ -1665,6 +1715,8 @@ static void dwc3_get_properties(struct dwc3 *dwc) +@@ -1656,6 +1716,8 @@ static void dwc3_get_properties(struct dwc3 *dwc) dwc->tx_thr_num_pkt_prd = tx_thr_num_pkt_prd; dwc->tx_max_burst_prd = tx_max_burst_prd; @@ -189023,9 +196650,9 @@ index fcb509059d7c..220d09c555d9 100644 dwc->imod_interval = 0; dwc->tx_fifo_resize_max_num = tx_fifo_resize_max_num; -@@ -1940,6 +1992,12 @@ static int dwc3_probe(struct platform_device *pdev) - - dwc3_get_properties(dwc); +@@ -1952,6 +2014,12 @@ static int dwc3_probe(struct platform_device *pdev) + if (IS_ERR(dwc->usb_psy)) + return dev_err_probe(dev, PTR_ERR(dwc->usb_psy), "couldn't get usb power supply\n"); + if (!dwc->sysdev_is_parent) { + ret = dma_set_mask_and_coherent(dwc->sysdev, DMA_BIT_MASK(64)); @@ -189037,7 +196664,7 @@ index fcb509059d7c..220d09c555d9 100644 if (IS_ERR(dwc->reset)) { ret = PTR_ERR(dwc->reset); diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h -index 3325796f3cb4..1338b30466af 100644 +index 516bace7e1dc..4d985479ff75 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -185,6 +185,9 @@ @@ -189050,15 +196677,19 @@ index 3325796f3cb4..1338b30466af 100644 /* Global Debug LSP MUX Select */ #define DWC3_GDBGLSPMUX_ENDBC BIT(15) /* Host only */ #define DWC3_GDBGLSPMUX_HOSTSELECT(n) ((n) & 0x3fff) -@@ -268,6 +271,7 @@ +@@ -266,8 +269,11 @@ + #define DWC3_GUCTL1_TX_IPGAP_LINECHECK_DIS BIT(28) + #define DWC3_GUCTL1_DEV_FORCE_20_CLK_FOR_30_CLK BIT(26) #define DWC3_GUCTL1_DEV_L1_EXIT_BY_HW BIT(24) ++#define DWC3_GUCTL1_NAK_PER_ENH_FS BIT(19) ++#define DWC3_GUCTL1_NAK_PER_ENH_HS BIT(18) #define DWC3_GUCTL1_PARKMODE_DISABLE_SS BIT(17) #define DWC3_GUCTL1_PARKMODE_DISABLE_HS BIT(16) +#define DWC3_GUCTL1_PARKMODE_DISABLE_FSLS BIT(15) #define DWC3_GUCTL1_RESUME_OPMODE_HS_HOST BIT(10) /* Global Status Register */ -@@ -1061,6 +1065,7 @@ struct dwc3_scratchpad_array { +@@ -1067,6 +1073,7 @@ struct dwc3_scratchpad_array { * @tx_max_burst_prd: max periodic ESS transmit burst size * @tx_fifo_resize_max_num: max number of fifos allocated during txfifo resize * @clear_stall_protocol: endpoint number that requires a delayed status phase @@ -189066,7 +196697,7 @@ index 3325796f3cb4..1338b30466af 100644 * @hsphy_interface: "utmi" or "ulpi" * @connected: true when we're connected to a host, false otherwise * @softconnect: true when gadget connect is called, false when disconnect runs -@@ -1112,10 +1117,12 @@ struct dwc3_scratchpad_array { +@@ -1118,10 +1125,14 @@ struct dwc3_scratchpad_array { * generation after resume from suspend. * @ulpi_ext_vbus_drv: Set to confiure the upli chip to drives CPEN pin * VBUS with an external supply. @@ -189074,6 +196705,8 @@ index 3325796f3cb4..1338b30466af 100644 - * instances in park mode. - * @parkmode_disable_hs_quirk: set if we need to disable all HishSpeed - * instances in park mode. ++ * @enh_nak_fs_quirk: Set to schedule more handshakes to Async FS endpoints. ++ * @enh_nak_hs_quirk: Set to schedule more handshakes to Async HS endpoints. + * @parkmode_disable_ss_quirk: If set, disable park mode feature for all + * Superspeed instances. + * @parkmode_disable_hs_quirk: If set, disable park mode feature for all @@ -189083,7 +196716,7 @@ index 3325796f3cb4..1338b30466af 100644 * @tx_de_emphasis_quirk: set if we enable Tx de-emphasis quirk * @tx_de_emphasis: Tx de-emphasis value * 0 - -6dB de-emphasis -@@ -1298,6 +1305,7 @@ struct dwc3 { +@@ -1304,6 +1315,7 @@ struct dwc3 { u8 tx_max_burst_prd; u8 tx_fifo_resize_max_num; u8 clear_stall_protocol; @@ -189091,8 +196724,12 @@ index 3325796f3cb4..1338b30466af 100644 const char *hsphy_interface; -@@ -1341,6 +1349,7 @@ struct dwc3 { +@@ -1345,8 +1357,11 @@ struct dwc3 { + unsigned dis_tx_ipgap_linecheck_quirk:1; + unsigned resume_hs_terminations:1; unsigned ulpi_ext_vbus_drv:1; ++ unsigned enh_nak_fs_quirk:1; ++ unsigned enh_nak_hs_quirk:1; unsigned parkmode_disable_ss_quirk:1; unsigned parkmode_disable_hs_quirk:1; + unsigned parkmode_disable_fsls_quirk:1; @@ -248895,10 +256532,25 @@ index 000000000000..cdc9963176e5 +test_main(); +0; diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c -index 54c47463c215..17f4a9fabee6 100644 +index 54c47463c215..24e1854e18b1 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c -@@ -1400,6 +1400,7 @@ int xhci_endpoint_init(struct xhci_hcd *xhci, +@@ -716,6 +716,14 @@ void xhci_setup_streams_ep_input_ctx(struct xhci_hcd *xhci, + ep_ctx->ep_info &= cpu_to_le32(~EP_MAXPSTREAMS_MASK); + ep_ctx->ep_info |= cpu_to_le32(EP_MAXPSTREAMS(max_primary_streams) + | EP_HAS_LSA); ++ ++ /* ++ * Set Host Initiated Data Move Disable to always defer stream ++ * selection to the device. xHC implementations may treat this ++ * field as "don't care, forced to 1" anyway - xHCI 1.2 s4.12.1. ++ */ ++ ep_ctx->ep_info2 |= EP_HID; ++ + ep_ctx->deq = cpu_to_le64(stream_info->ctx_array_dma); + } + +@@ -1400,6 +1408,7 @@ int xhci_endpoint_init(struct xhci_hcd *xhci, unsigned int ep_index; struct xhci_ep_ctx *ep_ctx; struct xhci_ring *ep_ring; @@ -248906,7 +256558,7 @@ index 54c47463c215..17f4a9fabee6 100644 unsigned int max_packet; enum xhci_ring_type ring_type; u32 max_esit_payload; -@@ -1409,6 +1410,8 @@ int xhci_endpoint_init(struct xhci_hcd *xhci, +@@ -1409,6 +1418,8 @@ int xhci_endpoint_init(struct xhci_hcd *xhci, unsigned int mult; unsigned int avg_trb_len; unsigned int err_count = 0; @@ -248915,7 +256567,7 @@ index 54c47463c215..17f4a9fabee6 100644 ep_index = xhci_get_endpoint_index(&ep->desc); ep_ctx = xhci_get_ep_ctx(xhci, virt_dev->in_ctx, ep_index); -@@ -1440,9 +1443,35 @@ int xhci_endpoint_init(struct xhci_hcd *xhci, +@@ -1440,9 +1451,35 @@ int xhci_endpoint_init(struct xhci_hcd *xhci, mult = xhci_get_endpoint_mult(udev, ep); max_packet = usb_endpoint_maxp(&ep->desc); @@ -248952,7 +256604,7 @@ index 54c47463c215..17f4a9fabee6 100644 /* FIXME dig Mult and streams info out of ep companion desc */ /* Allow 3 retries for everything but isoc, set CErr = 3 */ -@@ -2241,12 +2270,17 @@ xhci_alloc_interrupter(struct xhci_hcd *xhci, gfp_t flags) +@@ -2241,12 +2278,17 @@ xhci_alloc_interrupter(struct xhci_hcd *xhci, gfp_t flags) struct device *dev = xhci_to_hcd(xhci)->self.sysdev; struct xhci_interrupter *ir; int ret; @@ -248971,7 +256623,7 @@ index 54c47463c215..17f4a9fabee6 100644 0, flags); if (!ir->event_ring) { xhci_warn(xhci, "Failed to allocate interrupter event ring\n"); -@@ -2283,7 +2317,7 @@ xhci_add_interrupter(struct xhci_hcd *xhci, struct xhci_interrupter *ir, +@@ -2283,7 +2325,7 @@ xhci_add_interrupter(struct xhci_hcd *xhci, struct xhci_interrupter *ir, /* set ERST count with the number of entries in the segment table */ erst_size = readl(&ir->ir_set->erst_size); erst_size &= ERST_SIZE_MASK; @@ -248981,7 +256633,7 @@ index 54c47463c215..17f4a9fabee6 100644 erst_base = xhci_read_64(xhci, &ir->ir_set->erst_base); diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c -index b23ee4e9bde0..755c9faef791 100644 +index ff461c3ed6ff..31e7625e325b 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c @@ -27,6 +27,8 @@ @@ -249010,7 +256662,7 @@ index b23ee4e9bde0..755c9faef791 100644 static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci) { struct pci_dev *pdev = to_pci_dev(dev); -@@ -497,8 +509,15 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci) +@@ -487,8 +499,15 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci) pdev->device == 0x3432) xhci->quirks |= XHCI_BROKEN_STREAMS; @@ -249028,10 +256680,30 @@ index b23ee4e9bde0..755c9faef791 100644 if (pdev->vendor == PCI_VENDOR_ID_ASMEDIA && pdev->device == PCI_DEVICE_ID_ASMEDIA_1042_XHCI) { diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c -index 84cc0bf7066d..1c319e713b7f 100644 +index 9d3f1cfe388d..ba7686d3e101 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c -@@ -633,8 +633,11 @@ static int xhci_move_dequeue_past_td(struct xhci_hcd *xhci, +@@ -507,6 +507,19 @@ void xhci_ring_ep_doorbell(struct xhci_hcd *xhci, + + trace_xhci_ring_ep_doorbell(slot_id, DB_VALUE(ep_index, stream_id)); + ++ /* ++ * For non-coherent systems with PCIe DMA (such as Pi 4, Pi 5) there ++ * is a theoretical race between the TRB write and barrier, which ++ * is reported complete as soon as the write leaves the CPU domain, ++ * the doorbell write, which may be reported as complete by the RC ++ * at some arbitrary point, and the visibility of new TRBs in system ++ * RAM by the endpoint DMA engine. ++ * ++ * This read before the write positively serialises the CPU state ++ * by incurring a round-trip across the link. ++ */ ++ readl(db_addr); ++ + writel(DB_VALUE(ep_index, stream_id), db_addr); + /* flush the write */ + readl(db_addr); +@@ -635,8 +648,11 @@ static int xhci_move_dequeue_past_td(struct xhci_hcd *xhci, struct xhci_ring *ep_ring; struct xhci_command *cmd; struct xhci_segment *new_seg; @@ -249043,7 +256715,7 @@ index 84cc0bf7066d..1c319e713b7f 100644 dma_addr_t addr; u64 hw_dequeue; bool cycle_found = false; -@@ -672,7 +675,27 @@ static int xhci_move_dequeue_past_td(struct xhci_hcd *xhci, +@@ -674,7 +690,27 @@ static int xhci_move_dequeue_past_td(struct xhci_hcd *xhci, hw_dequeue = xhci_get_hw_deq(xhci, dev, ep_index, stream_id); new_seg = ep_ring->deq_seg; new_deq = ep_ring->dequeue; @@ -249072,7 +256744,7 @@ index 84cc0bf7066d..1c319e713b7f 100644 /* * We want to find the pointer, segment and cycle state of the new trb -@@ -705,6 +728,15 @@ static int xhci_move_dequeue_past_td(struct xhci_hcd *xhci, +@@ -707,6 +743,15 @@ static int xhci_move_dequeue_past_td(struct xhci_hcd *xhci, } while (!cycle_found || !td_last_trb_found); deq_found: @@ -249088,7 +256760,7 @@ index 84cc0bf7066d..1c319e713b7f 100644 /* Don't update the ring cycle state for the producer (us). */ addr = xhci_trb_virt_to_dma(new_seg, new_deq); -@@ -715,9 +747,9 @@ static int xhci_move_dequeue_past_td(struct xhci_hcd *xhci, +@@ -717,9 +762,9 @@ static int xhci_move_dequeue_past_td(struct xhci_hcd *xhci, } if ((ep->ep_state & SET_DEQ_PENDING)) { @@ -249101,7 +256773,7 @@ index 84cc0bf7066d..1c319e713b7f 100644 } /* This function gets called from contexts where it cannot sleep */ -@@ -3641,6 +3673,48 @@ static int xhci_align_td(struct xhci_hcd *xhci, struct urb *urb, u32 enqd_len, +@@ -3685,6 +3730,48 @@ static int xhci_align_td(struct xhci_hcd *xhci, struct urb *urb, u32 enqd_len, return 1; } @@ -249150,7 +256822,7 @@ index 84cc0bf7066d..1c319e713b7f 100644 /* This is very similar to what ehci-q.c qtd_fill() does */ int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags, struct urb *urb, int slot_id, unsigned int ep_index) -@@ -3797,6 +3871,8 @@ int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags, +@@ -3841,6 +3928,8 @@ int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags, } check_trb_math(urb, enqd_len); @@ -249159,7 +256831,7 @@ index 84cc0bf7066d..1c319e713b7f 100644 giveback_first_trb(xhci, slot_id, ep_index, urb->stream_id, start_cycle, start_trb); return 0; -@@ -3932,6 +4008,8 @@ int xhci_queue_ctrl_tx(struct xhci_hcd *xhci, gfp_t mem_flags, +@@ -3990,6 +4079,8 @@ int xhci_queue_ctrl_tx(struct xhci_hcd *xhci, gfp_t mem_flags, /* Event on completion */ field | TRB_IOC | TRB_TYPE(TRB_STATUS) | ep_ring->cycle_state); @@ -249169,10 +256841,10 @@ index 84cc0bf7066d..1c319e713b7f 100644 start_cycle, start_trb); return 0; diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c -index f005ce1f91ca..f2a81c18a28c 100644 +index 70e6c240a540..3ef2c3de9141 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c -@@ -1496,6 +1496,109 @@ static int xhci_check_maxpacket(struct xhci_hcd *xhci, unsigned int slot_id, +@@ -1497,6 +1497,109 @@ static int xhci_check_maxpacket(struct xhci_hcd *xhci, unsigned int slot_id, return ret; } @@ -249282,7 +256954,7 @@ index f005ce1f91ca..f2a81c18a28c 100644 /* * non-error returns are a promise to giveback() the urb later * we drop ownership so next owner (or urb unlink) can get it -@@ -5328,6 +5431,7 @@ static const struct hc_driver xhci_hc_driver = { +@@ -5360,6 +5463,7 @@ static const struct hc_driver xhci_hc_driver = { .endpoint_reset = xhci_endpoint_reset, .check_bandwidth = xhci_check_bandwidth, .reset_bandwidth = xhci_reset_bandwidth, @@ -249291,10 +256963,19 @@ index f005ce1f91ca..f2a81c18a28c 100644 .enable_device = xhci_enable_device, .update_hub_device = xhci_update_hub_device, diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h -index e8530f0ee27a..e79177e7bf43 100644 +index 02a3e93e1a63..7ee95d456ab4 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h -@@ -1425,8 +1425,9 @@ struct urb_priv { +@@ -492,6 +492,8 @@ struct xhci_ep_ctx { + #define CTX_TO_EP_MAXPSTREAMS(p) (((p) & EP_MAXPSTREAMS_MASK) >> 10) + /* Endpoint is set up with a Linear Stream Array (vs. Secondary Stream Array) */ + #define EP_HAS_LSA (1 << 15) ++/* Host initiated data move disable in info2 */ ++#define EP_HID (1 << 7) + /* hosts with LEC=1 use bits 31:24 as ESIT high bits. */ + #define CTX_TO_MAX_ESIT_PAYLOAD_HI(p) (((p) >> 24) & 0xff) + +@@ -1426,8 +1428,9 @@ struct urb_priv { * Each segment table entry is 4*32bits long. 1K seems like an ok size: * (1K bytes * 8bytes/bit) / (4*32 bits) = 64 segment entries in the table, * meaning 64 ring segments. @@ -249306,9 +256987,9 @@ index e8530f0ee27a..e79177e7bf43 100644 /* Poll every 60 seconds */ #define POLL_TIMEOUT 60 /* Stop endpoint command timeout (secs) for URB cancellation watchdog timer */ -@@ -1662,6 +1663,11 @@ struct xhci_hcd { - #define XHCI_WRITE_64_HI_LO BIT_ULL(47) +@@ -1664,6 +1667,11 @@ struct xhci_hcd { #define XHCI_CDNS_SCTX_QUIRK BIT_ULL(48) + #define XHCI_ETRON_HOST BIT_ULL(49) +/* Downstream VLI fixes */ +#define XHCI_AVOID_DQ_ON_LINK BIT_ULL(56) @@ -254060,6 +261741,27 @@ index 2a1aeab53ea4..1d4255d1999d 100644 } if (log->page_size != t32 || +diff --git a/include/drm/drm_blend.h b/include/drm/drm_blend.h +index 88bdfec3bd88..a84c58f3f13c 100644 +--- a/include/drm/drm_blend.h ++++ b/include/drm/drm_blend.h +@@ -34,6 +34,7 @@ + struct drm_device; + struct drm_atomic_state; + struct drm_plane; ++struct drm_connector; + + static inline bool drm_rotation_90_or_270(unsigned int rotation) + { +@@ -58,4 +59,8 @@ int drm_atomic_normalize_zpos(struct drm_device *dev, + struct drm_atomic_state *state); + int drm_plane_create_blend_mode_property(struct drm_plane *plane, + unsigned int supported_modes); ++ ++int drm_connector_create_rotation_property(struct drm_connector *conn, ++ unsigned int rotation, ++ unsigned int supported_rotations); + #endif diff --git a/include/drm/drm_color_mgmt.h b/include/drm/drm_color_mgmt.h index 6b5eec10c3db..5810aa8a9d87 100644 --- a/include/drm/drm_color_mgmt.h @@ -254075,7 +261777,7 @@ index 6b5eec10c3db..5810aa8a9d87 100644 * enum drm_color_lut_tests - hw-specific LUT tests to perform * diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h -index d304ec8dd06b..5ba62fa5ef54 100644 +index d304ec8dd06b..617a49f93b7d 100644 --- a/include/drm/drm_connector.h +++ b/include/drm/drm_connector.h @@ -201,6 +201,13 @@ enum drm_connector_tv_mode { @@ -254092,6 +261794,44 @@ index d304ec8dd06b..5ba62fa5ef54 100644 /** * @DRM_MODE_TV_MODE_MAX: Number of analog TV output modes. * +@@ -1031,6 +1038,11 @@ struct drm_connector_state { + * DRM blob property for HDR output metadata + */ + struct drm_property_blob *hdr_output_metadata; ++ ++ /** ++ * @rotation: Connector property to rotate the maximum output image. ++ */ ++ u32 rotation; + }; + + /** +@@ -1704,6 +1716,12 @@ struct drm_connector { + */ + struct drm_property *privacy_screen_hw_state_property; + ++ /** ++ * @rotation_property: Optional DRM property controlling rotation of the ++ * output. ++ */ ++ struct drm_property *rotation_property; ++ + #define DRM_CONNECTOR_POLL_HPD (1 << 0) + #define DRM_CONNECTOR_POLL_CONNECT (1 << 1) + #define DRM_CONNECTOR_POLL_DISCONNECT (1 << 2) +diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h +index 4476b01f063d..44cbb8fe9f0f 100644 +--- a/include/drm/drm_crtc.h ++++ b/include/drm/drm_crtc.h +@@ -193,7 +193,7 @@ struct drm_crtc_state { + * @plane_mask: Bitmask of drm_plane_mask(plane) of planes attached to + * this CRTC. + */ +- u32 plane_mask; ++ u64 plane_mask; + + /** + * @connector_mask: Bitmask of drm_connector_mask(connector) of diff --git a/include/drm/drm_mipi_dsi.h b/include/drm/drm_mipi_dsi.h index 900262f4c234..48bfdd398fb0 100644 --- a/include/drm/drm_mipi_dsi.h @@ -254153,7 +261893,7 @@ index 900262f4c234..48bfdd398fb0 100644 /* transmit data ending at the same time for all lanes within one hsync */ #define MIPI_DSI_HS_PKT_END_ALIGNED BIT(12) diff --git a/include/drm/drm_plane.h b/include/drm/drm_plane.h -index 882a8a2aceba..744947bbeeb7 100644 +index 882a8a2aceba..ac0343300de8 100644 --- a/include/drm/drm_plane.h +++ b/include/drm/drm_plane.h @@ -178,6 +178,24 @@ struct drm_plane_state { @@ -254207,6 +261947,18 @@ index 882a8a2aceba..744947bbeeb7 100644 KABI_RESERVE(1) KABI_RESERVE(2) KABI_RESERVE(3) +@@ -888,9 +925,9 @@ static inline unsigned int drm_plane_index(const struct drm_plane *plane) + * drm_plane_mask - find the mask of a registered plane + * @plane: plane to find mask for + */ +-static inline u32 drm_plane_mask(const struct drm_plane *plane) ++static inline u64 drm_plane_mask(const struct drm_plane *plane) + { +- return 1 << drm_plane_index(plane); ++ return 1ULL << drm_plane_index(plane); + } + + struct drm_plane * drm_plane_from_index(struct drm_device *dev, int idx); diff --git a/include/dt-bindings/clock/rp1.h b/include/dt-bindings/clock/rp1.h new file mode 100644 index 000000000000..1ebb25f16923 @@ -255112,7 +262864,7 @@ index 83ec4bf9809e..ce1e7a9a5990 100644 void (*iotlb_sync)(struct iommu_domain *domain, struct iommu_iotlb_gather *iotlb_gather); diff --git a/include/linux/leds.h b/include/linux/leds.h -index d3056bc6f0a1..eafe6034550f 100644 +index e91802cdc416..225d336fd7d2 100644 --- a/include/linux/leds.h +++ b/include/linux/leds.h @@ -115,6 +115,9 @@ struct led_classdev { @@ -255227,18 +262979,19 @@ index 517288da19fd..626c450d71f4 100644 #define PHY_ARDENNES_MMD_DEV_3_PHY_CFG (0x806A) #define PHY_ARDENNES_MMD_DEV_3_PHY_CFG_ZD_DLY_EN_ (0x2000) diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h -index 0fe6576eb81f..52e46062d19a 100644 +index acdf6511d226..0240f2d009f6 100644 --- a/include/linux/mmc/card.h +++ b/include/linux/mmc/card.h -@@ -299,6 +299,7 @@ struct mmc_card { - #define MMC_QUIRK_BROKEN_SD_DISCARD (1<<14) /* Disable broken SD discard support */ +@@ -300,6 +300,8 @@ struct mmc_card { #define MMC_QUIRK_BROKEN_SD_CACHE (1<<15) /* Disable broken SD cache support */ #define MMC_QUIRK_BROKEN_CACHE_FLUSH (1<<16) /* Don't flush cache until the write has occurred */ + #define MMC_QUIRK_BROKEN_SD_POWEROFF_NOTIFY (1<<17) /* Disable broken SD poweroff notify support */ ++#define MMC_QUIRK_WORKING_SD_CQ (1<<30) /* SD card has known-good CQ implementation */ +#define MMC_QUIRK_ERASE_BROKEN (1<<31) /* Skip erase */ bool written_flag; /* Indicates eMMC has been written since power on */ bool reenable_cmdq; /* Re-enable Command Queue */ -@@ -322,6 +323,7 @@ struct mmc_card { +@@ -323,6 +325,7 @@ struct mmc_card { struct sd_switch_caps sw_caps; /* switch (CMD6) caps */ struct sd_ext_reg ext_power; /* SD extension reg for PM */ struct sd_ext_reg ext_perf; /* SD extension reg for PERF */ @@ -255246,7 +262999,7 @@ index 0fe6576eb81f..52e46062d19a 100644 unsigned int sdio_funcs; /* number of SDIO functions */ atomic_t sdio_funcs_probed; /* number of probed SDIO funcs */ -@@ -344,6 +346,7 @@ struct mmc_card { +@@ -345,6 +348,7 @@ struct mmc_card { unsigned int nr_parts; struct workqueue_struct *complete_wq; /* Private workqueue */ @@ -255254,6 +263007,18 @@ index 0fe6576eb81f..52e46062d19a 100644 KABI_RESERVE(0) KABI_RESERVE(1) KABI_RESERVE(2) +diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h +index 6e54e83cd918..2d29abdab096 100644 +--- a/include/linux/mmc/host.h ++++ b/include/linux/mmc/host.h +@@ -432,6 +432,7 @@ struct mmc_host { + #define MMC_CAP2_CRYPTO 0 + #endif + #define MMC_CAP2_ALT_GPT_TEGRA (1 << 28) /* Host with eMMC that has GPT entry at a non-standard location */ ++#define MMC_CAP2_SD_CQE_PERMISSIVE (1 << 31) /* Ignore allow-list for CQ capable SD card detection */ + + int fixed_drv_type; /* fixed driver type for non-removable media */ + diff --git a/include/linux/mmc/sd.h b/include/linux/mmc/sd.h index 6727576a8755..52f2c7a90a13 100644 --- a/include/linux/mmc/sd.h @@ -255297,6 +263062,1518 @@ index 990f9d66d2f1..74a8b16b3bcf 100644 unsigned int btf_data_size; void *btf_data; #else +diff --git a/include/linux/pio_instructions.h b/include/linux/pio_instructions.h +new file mode 100644 +index 000000000000..a72934b1ed60 +--- /dev/null ++++ b/include/linux/pio_instructions.h +@@ -0,0 +1,481 @@ ++/* SPDX-License-Identifier: BSD-3-Clause */ ++/* ++ * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. ++ */ ++ ++#ifndef _HARDWARE_PIO_INSTRUCTIONS_H ++#define _HARDWARE_PIO_INSTRUCTIONS_H ++ ++/** \brief PIO instruction encoding ++ * \defgroup pio_instructions pio_instructions ++ * \ingroup hardware_pio ++ * ++ * Functions for generating PIO instruction encodings programmatically. In debug builds ++ *`PARAM_ASSERTIONS_ENABLED_PIO_INSTRUCTIONS` can be set to 1 to enable validation of encoding function ++ * parameters. ++ * ++ * For fuller descriptions of the instructions in question see the "RP2040 Datasheet" ++ */ ++ ++// PICO_CONFIG: PARAM_ASSERTIONS_ENABLED_PIO_INSTRUCTIONS, Enable/disable assertions in the PIO instructions, type=bool, default=0, group=pio_instructions ++#ifndef PARAM_ASSERTIONS_ENABLED_PIO_INSTRUCTIONS ++#define PARAM_ASSERTIONS_ENABLED_PIO_INSTRUCTIONS 0 ++#endif ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++enum pio_instr_bits { ++ pio_instr_bits_jmp = 0x0000, ++ pio_instr_bits_wait = 0x2000, ++ pio_instr_bits_in = 0x4000, ++ pio_instr_bits_out = 0x6000, ++ pio_instr_bits_push = 0x8000, ++ pio_instr_bits_pull = 0x8080, ++ pio_instr_bits_mov = 0xa000, ++ pio_instr_bits_irq = 0xc000, ++ pio_instr_bits_set = 0xe000, ++}; ++ ++#ifndef NDEBUG ++#define _PIO_INVALID_IN_SRC 0x08u ++#define _PIO_INVALID_OUT_DEST 0x10u ++#define _PIO_INVALID_SET_DEST 0x20u ++#define _PIO_INVALID_MOV_SRC 0x40u ++#define _PIO_INVALID_MOV_DEST 0x80u ++#else ++#define _PIO_INVALID_IN_SRC 0u ++#define _PIO_INVALID_OUT_DEST 0u ++#define _PIO_INVALID_SET_DEST 0u ++#define _PIO_INVALID_MOV_SRC 0u ++#define _PIO_INVALID_MOV_DEST 0u ++#endif ++ ++/*! \brief Enumeration of values to pass for source/destination args for instruction encoding functions ++ * \ingroup pio_instructions ++ * ++ * \note Not all values are suitable for all functions. Validity is only checked in debug mode when ++ * `PARAM_ASSERTIONS_ENABLED_PIO_INSTRUCTIONS` is 1 ++ */ ++enum pio_src_dest { ++ pio_pins = 0u, ++ pio_x = 1u, ++ pio_y = 2u, ++ pio_null = 3u | _PIO_INVALID_SET_DEST | _PIO_INVALID_MOV_DEST, ++ pio_pindirs = 4u | _PIO_INVALID_IN_SRC | _PIO_INVALID_MOV_SRC | _PIO_INVALID_MOV_DEST, ++ pio_exec_mov = 4u | _PIO_INVALID_IN_SRC | _PIO_INVALID_OUT_DEST | _PIO_INVALID_SET_DEST | _PIO_INVALID_MOV_SRC, ++ pio_status = 5u | _PIO_INVALID_IN_SRC | _PIO_INVALID_OUT_DEST | _PIO_INVALID_SET_DEST | _PIO_INVALID_MOV_DEST, ++ pio_pc = 5u | _PIO_INVALID_IN_SRC | _PIO_INVALID_SET_DEST | _PIO_INVALID_MOV_SRC, ++ pio_isr = 6u | _PIO_INVALID_SET_DEST, ++ pio_osr = 7u | _PIO_INVALID_OUT_DEST | _PIO_INVALID_SET_DEST, ++ pio_exec_out = 7u | _PIO_INVALID_IN_SRC | _PIO_INVALID_SET_DEST | _PIO_INVALID_MOV_SRC | _PIO_INVALID_MOV_DEST, ++}; ++ ++static inline uint _pio_major_instr_bits(uint instr) { ++ return instr & 0xe000u; ++} ++ ++static inline uint _pio_encode_instr_and_args(enum pio_instr_bits instr_bits, uint arg1, uint arg2) { ++ valid_params_if(PIO_INSTRUCTIONS, arg1 <= 0x7); ++#if PARAM_ASSERTIONS_ENABLED(PIO_INSTRUCTIONS) ++ uint32_t major = _pio_major_instr_bits(instr_bits); ++ if (major == pio_instr_bits_in || major == pio_instr_bits_out) { ++ assert(arg2 && arg2 <= 32); ++ } else { ++ assert(arg2 <= 31); ++ } ++#endif ++ return instr_bits | (arg1 << 5u) | (arg2 & 0x1fu); ++} ++ ++static inline uint _pio_encode_instr_and_src_dest(enum pio_instr_bits instr_bits, enum pio_src_dest dest, uint value) { ++ return _pio_encode_instr_and_args(instr_bits, dest & 7u, value); ++} ++ ++/*! \brief Encode just the delay slot bits of an instruction ++ * \ingroup pio_instructions ++ * ++ * \note This function does not return a valid instruction encoding; instead it returns an encoding of the delay ++ * slot suitable for `OR`ing with the result of an encoding function for an actual instruction. Care should be taken when ++ * combining the results of this function with the results of \ref pio_encode_sideset and \ref pio_encode_sideset_opt ++ * as they share the same bits within the instruction encoding. ++ * ++ * \param cycles the number of cycles 0-31 (or less if side set is being used) ++ * \return the delay slot bits to be ORed with an instruction encoding ++ */ ++static inline uint pio_encode_delay(uint cycles) { ++ // note that the maximum cycles will be smaller if sideset_bit_count > 0 ++ valid_params_if(PIO_INSTRUCTIONS, cycles <= 0x1f); ++ return cycles << 8u; ++} ++ ++/*! \brief Encode just the side set bits of an instruction (in non optional side set mode) ++ * \ingroup pio_instructions ++ * ++ * \note This function does not return a valid instruction encoding; instead it returns an encoding of the side set bits ++ * suitable for `OR`ing with the result of an encoding function for an actual instruction. Care should be taken when ++ * combining the results of this function with the results of \ref pio_encode_delay as they share the same bits ++ * within the instruction encoding. ++ * ++ * \param sideset_bit_count number of side set bits as would be specified via `.sideset` in pioasm ++ * \param value the value to sideset on the pins ++ * \return the side set bits to be ORed with an instruction encoding ++ */ ++static inline uint pio_encode_sideset(uint sideset_bit_count, uint value) { ++ valid_params_if(PIO_INSTRUCTIONS, sideset_bit_count >= 1 && sideset_bit_count <= 5); ++ valid_params_if(PIO_INSTRUCTIONS, value <= ((1u << sideset_bit_count) - 1)); ++ return value << (13u - sideset_bit_count); ++} ++ ++/*! \brief Encode just the side set bits of an instruction (in optional -`opt` side set mode) ++ * \ingroup pio_instructions ++ * ++ * \note This function does not return a valid instruction encoding; instead it returns an encoding of the side set bits ++ * suitable for `OR`ing with the result of an encoding function for an actual instruction. Care should be taken when ++ * combining the results of this function with the results of \ref pio_encode_delay as they share the same bits ++ * within the instruction encoding. ++ * ++ * \param sideset_bit_count number of side set bits as would be specified via `.sideset opt` in pioasm ++ * \param value the value to sideset on the pins ++ * \return the side set bits to be ORed with an instruction encoding ++ */ ++static inline uint pio_encode_sideset_opt(uint sideset_bit_count, uint value) { ++ valid_params_if(PIO_INSTRUCTIONS, sideset_bit_count >= 1 && sideset_bit_count <= 4); ++ valid_params_if(PIO_INSTRUCTIONS, value <= ((1u << sideset_bit_count) - 1)); ++ return 0x1000u | value << (12u - sideset_bit_count); ++} ++ ++/*! \brief Encode an unconditional JMP instruction ++ * \ingroup pio_instructions ++ * ++ * This is the equivalent of `JMP ` ++ * ++ * \param addr The target address 0-31 (an absolute address within the PIO instruction memory) ++ * \return The instruction encoding with 0 delay and no side set value ++ * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt ++ */ ++static inline uint pio_encode_jmp(uint addr) { ++ return _pio_encode_instr_and_args(pio_instr_bits_jmp, 0, addr); ++} ++ ++/*! \brief Encode a conditional JMP if scratch X zero instruction ++ * \ingroup pio_instructions ++ * ++ * This is the equivalent of `JMP !X ` ++ * ++ * \param addr The target address 0-31 (an absolute address within the PIO instruction memory) ++ * \return The instruction encoding with 0 delay and no side set value ++ * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt ++ */ ++static inline uint pio_encode_jmp_not_x(uint addr) { ++ return _pio_encode_instr_and_args(pio_instr_bits_jmp, 1, addr); ++} ++ ++/*! \brief Encode a conditional JMP if scratch X non-zero (and post-decrement X) instruction ++ * \ingroup pio_instructions ++ * ++ * This is the equivalent of `JMP X-- ` ++ * ++ * \param addr The target address 0-31 (an absolute address within the PIO instruction memory) ++ * \return The instruction encoding with 0 delay and no side set value ++ * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt ++ */ ++static inline uint pio_encode_jmp_x_dec(uint addr) { ++ return _pio_encode_instr_and_args(pio_instr_bits_jmp, 2, addr); ++} ++ ++/*! \brief Encode a conditional JMP if scratch Y zero instruction ++ * \ingroup pio_instructions ++ * ++ * This is the equivalent of `JMP !Y ` ++ * ++ * \param addr The target address 0-31 (an absolute address within the PIO instruction memory) ++ * \return The instruction encoding with 0 delay and no side set value ++ * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt ++ */ ++static inline uint pio_encode_jmp_not_y(uint addr) { ++ return _pio_encode_instr_and_args(pio_instr_bits_jmp, 3, addr); ++} ++ ++/*! \brief Encode a conditional JMP if scratch Y non-zero (and post-decrement Y) instruction ++ * \ingroup pio_instructions ++ * ++ * This is the equivalent of `JMP Y-- ` ++ * ++ * \param addr The target address 0-31 (an absolute address within the PIO instruction memory) ++ * \return The instruction encoding with 0 delay and no side set value ++ * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt ++ */ ++static inline uint pio_encode_jmp_y_dec(uint addr) { ++ return _pio_encode_instr_and_args(pio_instr_bits_jmp, 4, addr); ++} ++ ++/*! \brief Encode a conditional JMP if scratch X not equal scratch Y instruction ++ * \ingroup pio_instructions ++ * ++ * This is the equivalent of `JMP X!=Y ` ++ * ++ * \param addr The target address 0-31 (an absolute address within the PIO instruction memory) ++ * \return The instruction encoding with 0 delay and no side set value ++ * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt ++ */ ++static inline uint pio_encode_jmp_x_ne_y(uint addr) { ++ return _pio_encode_instr_and_args(pio_instr_bits_jmp, 5, addr); ++} ++ ++/*! \brief Encode a conditional JMP if input pin high instruction ++ * \ingroup pio_instructions ++ * ++ * This is the equivalent of `JMP PIN ` ++ * ++ * \param addr The target address 0-31 (an absolute address within the PIO instruction memory) ++ * \return The instruction encoding with 0 delay and no side set value ++ * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt ++ */ ++static inline uint pio_encode_jmp_pin(uint addr) { ++ return _pio_encode_instr_and_args(pio_instr_bits_jmp, 6, addr); ++} ++ ++/*! \brief Encode a conditional JMP if output shift register not empty instruction ++ * \ingroup pio_instructions ++ * ++ * This is the equivalent of `JMP !OSRE ` ++ * ++ * \param addr The target address 0-31 (an absolute address within the PIO instruction memory) ++ * \return The instruction encoding with 0 delay and no side set value ++ * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt ++ */ ++static inline uint pio_encode_jmp_not_osre(uint addr) { ++ return _pio_encode_instr_and_args(pio_instr_bits_jmp, 7, addr); ++} ++ ++static inline uint _pio_encode_irq(bool relative, uint irq) { ++ valid_params_if(PIO_INSTRUCTIONS, irq <= 7); ++ return (relative ? 0x10u : 0x0u) | irq; ++} ++ ++/*! \brief Encode a WAIT for GPIO pin instruction ++ * \ingroup pio_instructions ++ * ++ * This is the equivalent of `WAIT GPIO ` ++ * ++ * \param polarity true for `WAIT 1`, false for `WAIT 0` ++ * \param gpio The real GPIO number 0-31 ++ * \return The instruction encoding with 0 delay and no side set value ++ * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt ++ */ ++static inline uint pio_encode_wait_gpio(bool polarity, uint gpio) { ++ return _pio_encode_instr_and_args(pio_instr_bits_wait, 0u | (polarity ? 4u : 0u), gpio); ++} ++ ++/*! \brief Encode a WAIT for pin instruction ++ * \ingroup pio_instructions ++ * ++ * This is the equivalent of `WAIT PIN ` ++ * ++ * \param polarity true for `WAIT 1`, false for `WAIT 0` ++ * \param pin The pin number 0-31 relative to the executing SM's input pin mapping ++ * \return The instruction encoding with 0 delay and no side set value ++ * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt ++ */ ++static inline uint pio_encode_wait_pin(bool polarity, uint pin) { ++ return _pio_encode_instr_and_args(pio_instr_bits_wait, 1u | (polarity ? 4u : 0u), pin); ++} ++ ++/*! \brief Encode a WAIT for IRQ instruction ++ * \ingroup pio_instructions ++ * ++ * This is the equivalent of `WAIT IRQ ` ++ * ++ * \param polarity true for `WAIT 1`, false for `WAIT 0` ++ * \param relative true for a `WAIT IRQ REL`, false for regular `WAIT IRQ ` ++ * \param irq the irq number 0-7 ++ * \return The instruction encoding with 0 delay and no side set value ++ * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt ++ */ ++static inline uint pio_encode_wait_irq(bool polarity, bool relative, uint irq) { ++ valid_params_if(PIO_INSTRUCTIONS, irq <= 7); ++ return _pio_encode_instr_and_args(pio_instr_bits_wait, 2u | (polarity ? 4u : 0u), _pio_encode_irq(relative, irq)); ++} ++ ++/*! \brief Encode an IN instruction ++ * \ingroup pio_instructions ++ * ++ * This is the equivalent of `IN , ` ++ * ++ * \param src The source to take data from ++ * \param count The number of bits 1-32 ++ * \return The instruction encoding with 0 delay and no side set value ++ * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt ++ */ ++static inline uint pio_encode_in(enum pio_src_dest src, uint count) { ++ valid_params_if(PIO_INSTRUCTIONS, !(src & _PIO_INVALID_IN_SRC)); ++ return _pio_encode_instr_and_src_dest(pio_instr_bits_in, src, count); ++} ++ ++/*! \brief Encode an OUT instruction ++ * \ingroup pio_instructions ++ * ++ * This is the equivalent of `OUT , ` ++ * ++ * \param dest The destination to write data to ++ * \param count The number of bits 1-32 ++ * \return The instruction encoding with 0 delay and no side set value ++ * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt ++ */ ++static inline uint pio_encode_out(enum pio_src_dest dest, uint count) { ++ valid_params_if(PIO_INSTRUCTIONS, !(dest & _PIO_INVALID_OUT_DEST)); ++ return _pio_encode_instr_and_src_dest(pio_instr_bits_out, dest, count); ++} ++ ++/*! \brief Encode a PUSH instruction ++ * \ingroup pio_instructions ++ * ++ * This is the equivalent of `PUSH , ` ++ * ++ * \param if_full true for `PUSH IF_FULL ...`, false for `PUSH ...` ++ * \param block true for `PUSH ... BLOCK`, false for `PUSH ...` ++ * \return The instruction encoding with 0 delay and no side set value ++ * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt ++ */ ++static inline uint pio_encode_push(bool if_full, bool block) { ++ return _pio_encode_instr_and_args(pio_instr_bits_push, (if_full ? 2u : 0u) | (block ? 1u : 0u), 0); ++} ++ ++/*! \brief Encode a PULL instruction ++ * \ingroup pio_instructions ++ * ++ * This is the equivalent of `PULL , ` ++ * ++ * \param if_empty true for `PULL IF_EMPTY ...`, false for `PULL ...` ++ * \param block true for `PULL ... BLOCK`, false for `PULL ...` ++ * \return The instruction encoding with 0 delay and no side set value ++ * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt ++ */ ++static inline uint pio_encode_pull(bool if_empty, bool block) { ++ return _pio_encode_instr_and_args(pio_instr_bits_pull, (if_empty ? 2u : 0u) | (block ? 1u : 0u), 0); ++} ++ ++/*! \brief Encode a MOV instruction ++ * \ingroup pio_instructions ++ * ++ * This is the equivalent of `MOV , ` ++ * ++ * \param dest The destination to write data to ++ * \param src The source to take data from ++ * \return The instruction encoding with 0 delay and no side set value ++ * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt ++ */ ++static inline uint pio_encode_mov(enum pio_src_dest dest, enum pio_src_dest src) { ++ valid_params_if(PIO_INSTRUCTIONS, !(dest & _PIO_INVALID_MOV_DEST)); ++ valid_params_if(PIO_INSTRUCTIONS, !(src & _PIO_INVALID_MOV_SRC)); ++ return _pio_encode_instr_and_src_dest(pio_instr_bits_mov, dest, src & 7u); ++} ++ ++/*! \brief Encode a MOV instruction with bit invert ++ * \ingroup pio_instructions ++ * ++ * This is the equivalent of `MOV , ~` ++ * ++ * \param dest The destination to write inverted data to ++ * \param src The source to take data from ++ * \return The instruction encoding with 0 delay and no side set value ++ * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt ++ */ ++static inline uint pio_encode_mov_not(enum pio_src_dest dest, enum pio_src_dest src) { ++ valid_params_if(PIO_INSTRUCTIONS, !(dest & _PIO_INVALID_MOV_DEST)); ++ valid_params_if(PIO_INSTRUCTIONS, !(src & _PIO_INVALID_MOV_SRC)); ++ return _pio_encode_instr_and_src_dest(pio_instr_bits_mov, dest, (1u << 3u) | (src & 7u)); ++} ++ ++/*! \brief Encode a MOV instruction with bit reverse ++ * \ingroup pio_instructions ++ * ++ * This is the equivalent of `MOV , ::` ++ * ++ * \param dest The destination to write bit reversed data to ++ * \param src The source to take data from ++ * \return The instruction encoding with 0 delay and no side set value ++ * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt ++ */ ++static inline uint pio_encode_mov_reverse(enum pio_src_dest dest, enum pio_src_dest src) { ++ valid_params_if(PIO_INSTRUCTIONS, !(dest & _PIO_INVALID_MOV_DEST)); ++ valid_params_if(PIO_INSTRUCTIONS, !(src & _PIO_INVALID_MOV_SRC)); ++ return _pio_encode_instr_and_src_dest(pio_instr_bits_mov, dest, (2u << 3u) | (src & 7u)); ++} ++ ++/*! \brief Encode a IRQ SET instruction ++ * \ingroup pio_instructions ++ * ++ * This is the equivalent of `IRQ SET ` ++ * ++ * \param relative true for a `IRQ SET REL`, false for regular `IRQ SET ` ++ * \param irq the irq number 0-7 ++ * \return The instruction encoding with 0 delay and no side set value ++ * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt ++ */ ++static inline uint pio_encode_irq_set(bool relative, uint irq) { ++ return _pio_encode_instr_and_args(pio_instr_bits_irq, 0, _pio_encode_irq(relative, irq)); ++} ++ ++/*! \brief Encode a IRQ WAIT instruction ++ * \ingroup pio_instructions ++ * ++ * This is the equivalent of `IRQ WAIT ` ++ * ++ * \param relative true for a `IRQ WAIT REL`, false for regular `IRQ WAIT ` ++ * \param irq the irq number 0-7 ++ * \return The instruction encoding with 0 delay and no side set value ++ * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt ++ */ ++static inline uint pio_encode_irq_wait(bool relative, uint irq) { ++ return _pio_encode_instr_and_args(pio_instr_bits_irq, 1, _pio_encode_irq(relative, irq)); ++} ++ ++/*! \brief Encode a IRQ CLEAR instruction ++ * \ingroup pio_instructions ++ * ++ * This is the equivalent of `IRQ CLEAR ` ++ * ++ * \param relative true for a `IRQ CLEAR REL`, false for regular `IRQ CLEAR ` ++ * \param irq the irq number 0-7 ++ * \return The instruction encoding with 0 delay and no side set value ++ * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt ++ */ ++static inline uint pio_encode_irq_clear(bool relative, uint irq) { ++ return _pio_encode_instr_and_args(pio_instr_bits_irq, 2, _pio_encode_irq(relative, irq)); ++} ++ ++/*! \brief Encode a SET instruction ++ * \ingroup pio_instructions ++ * ++ * This is the equivalent of `SET , ` ++ * ++ * \param dest The destination to apply the value to ++ * \param value The value 0-31 ++ * \return The instruction encoding with 0 delay and no side set value ++ * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt ++ */ ++static inline uint pio_encode_set(enum pio_src_dest dest, uint value) { ++ valid_params_if(PIO_INSTRUCTIONS, !(dest & _PIO_INVALID_SET_DEST)); ++ return _pio_encode_instr_and_src_dest(pio_instr_bits_set, dest, value); ++} ++ ++/*! \brief Encode a NOP instruction ++ * \ingroup pio_instructions ++ * ++ * This is the equivalent of `NOP` which is itself encoded as `MOV y, y` ++ * ++ * \return The instruction encoding with 0 delay and no side set value ++ * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt ++ */ ++static inline uint pio_encode_nop(void) { ++ return pio_encode_mov(pio_y, pio_y); ++} ++ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif +diff --git a/include/linux/pio_rp1.h b/include/linux/pio_rp1.h +new file mode 100644 +index 000000000000..f262fdd9c8f1 +--- /dev/null ++++ b/include/linux/pio_rp1.h +@@ -0,0 +1,1019 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++/* ++ * Copyright (c) 2024 Raspberry Pi Ltd. ++ * All rights reserved. ++ */ ++ ++#ifndef _PIO_RP1_H ++#define _PIO_RP1_H ++ ++#include ++ ++#define PARAM_WARNINGS_ENABLED 1 ++ ++#ifdef DEBUG ++#define PARAM_WARNINGS_ENABLED 1 ++#endif ++ ++#ifndef PARAM_WARNINGS_ENABLED ++#define PARAM_WARNINGS_ENABLED 0 ++#endif ++ ++#define bad_params_if(client, test) \ ++ ({ bool f = (test); if (f && client) pio_set_error(client, -EINVAL); \ ++ if (f && PARAM_WARNINGS_ENABLED) WARN_ON((test)); \ ++ f; }) ++ ++#ifndef PARAM_ASSERTIONS_ENABLE_ALL ++#define PARAM_ASSERTIONS_ENABLE_ALL 0 ++#endif ++ ++#ifndef PARAM_ASSERTIONS_DISABLE_ALL ++#define PARAM_ASSERTIONS_DISABLE_ALL 0 ++#endif ++ ++#define PARAM_ASSERTIONS_ENABLED(x) \ ++ ((PARAM_ASSERTIONS_ENABLED_ ## x || PARAM_ASSERTIONS_ENABLE_ALL) && \ ++ !PARAM_ASSERTIONS_DISABLE_ALL) ++#define valid_params_if(x, test) ({if (PARAM_ASSERTIONS_ENABLED(x)) WARN_ON(test); }) ++ ++#include ++ ++#define NUM_PIO_STATE_MACHINES 4 ++#define PIO_INSTRUCTION_COUNT 32 ++#define PIO_ORIGIN_ANY ((uint)(~0)) ++#define GPIOS_MASK ((1 << RP1_PIO_GPIO_COUNT) - 1) ++ ++#define PICO_NO_HARDWARE 0 ++ ++#define pio0 pio_open_helper(0) ++ ++#define PROC_PIO_SM0_PINCTRL_OUT_BASE_BITS 0x0000001f ++#define PROC_PIO_SM0_PINCTRL_OUT_BASE_LSB 0 ++#define PROC_PIO_SM0_PINCTRL_OUT_COUNT_BITS 0x03f00000 ++#define PROC_PIO_SM0_PINCTRL_OUT_COUNT_LSB 20 ++#define PROC_PIO_SM0_PINCTRL_SET_BASE_BITS 0x000003e0 ++#define PROC_PIO_SM0_PINCTRL_SET_BASE_LSB 5 ++#define PROC_PIO_SM0_PINCTRL_SET_COUNT_BITS 0x1c000000 ++#define PROC_PIO_SM0_PINCTRL_SET_COUNT_LSB 26 ++#define PROC_PIO_SM0_PINCTRL_IN_BASE_BITS 0x000f8000 ++#define PROC_PIO_SM0_PINCTRL_IN_BASE_LSB 15 ++#define PROC_PIO_SM0_PINCTRL_SIDESET_BASE_BITS 0x00007c00 ++#define PROC_PIO_SM0_PINCTRL_SIDESET_BASE_LSB 10 ++#define PROC_PIO_SM0_PINCTRL_SIDESET_COUNT_BITS 0xe0000000 ++#define PROC_PIO_SM0_PINCTRL_SIDESET_COUNT_LSB 29 ++#define PROC_PIO_SM0_EXECCTRL_SIDE_EN_BITS 0x40000000 ++#define PROC_PIO_SM0_EXECCTRL_SIDE_EN_LSB 30 ++#define PROC_PIO_SM0_EXECCTRL_SIDE_PINDIR_BITS 0x20000000 ++#define PROC_PIO_SM0_EXECCTRL_SIDE_PINDIR_LSB 29 ++#define PROC_PIO_SM0_CLKDIV_INT_LSB 16 ++#define PROC_PIO_SM0_CLKDIV_FRAC_LSB 8 ++#define PROC_PIO_SM0_EXECCTRL_WRAP_TOP_BITS 0x0001f000 ++#define PROC_PIO_SM0_EXECCTRL_WRAP_TOP_LSB 12 ++#define PROC_PIO_SM0_EXECCTRL_WRAP_BOTTOM_BITS 0x00000f80 ++#define PROC_PIO_SM0_EXECCTRL_WRAP_BOTTOM_LSB 7 ++#define PROC_PIO_SM0_EXECCTRL_JMP_PIN_BITS 0x1f000000 ++#define PROC_PIO_SM0_EXECCTRL_JMP_PIN_LSB 24 ++#define PROC_PIO_SM0_SHIFTCTRL_IN_SHIFTDIR_BITS 0x00040000 ++#define PROC_PIO_SM0_SHIFTCTRL_IN_SHIFTDIR_LSB 18 ++#define PROC_PIO_SM0_SHIFTCTRL_AUTOPULL_BITS 0x00020000 ++#define PROC_PIO_SM0_SHIFTCTRL_AUTOPULL_LSB 17 ++#define PROC_PIO_SM0_SHIFTCTRL_AUTOPUSH_BITS 0x00010000 ++#define PROC_PIO_SM0_SHIFTCTRL_AUTOPUSH_LSB 16 ++#define PROC_PIO_SM0_SHIFTCTRL_PUSH_THRESH_BITS 0x01f00000 ++#define PROC_PIO_SM0_SHIFTCTRL_PUSH_THRESH_LSB 20 ++#define PROC_PIO_SM0_SHIFTCTRL_OUT_SHIFTDIR_BITS 0x00080000 ++#define PROC_PIO_SM0_SHIFTCTRL_OUT_SHIFTDIR_LSB 19 ++#define PROC_PIO_SM0_SHIFTCTRL_PULL_THRESH_BITS 0x3e000000 ++#define PROC_PIO_SM0_SHIFTCTRL_PULL_THRESH_LSB 25 ++#define PROC_PIO_SM0_SHIFTCTRL_FJOIN_TX_BITS 0x40000000 ++#define PROC_PIO_SM0_SHIFTCTRL_FJOIN_TX_LSB 30 ++#define PROC_PIO_SM0_SHIFTCTRL_FJOIN_RX_BITS 0x80000000 ++#define PROC_PIO_SM0_SHIFTCTRL_FJOIN_RX_LSB 31 ++#define PROC_PIO_SM0_EXECCTRL_OUT_STICKY_BITS 0x00020000 ++#define PROC_PIO_SM0_EXECCTRL_OUT_STICKY_LSB 17 ++#define PROC_PIO_SM0_EXECCTRL_INLINE_OUT_EN_BITS 0x00040000 ++#define PROC_PIO_SM0_EXECCTRL_INLINE_OUT_EN_LSB 18 ++#define PROC_PIO_SM0_EXECCTRL_OUT_EN_SEL_BITS 0x00f80000 ++#define PROC_PIO_SM0_EXECCTRL_OUT_EN_SEL_LSB 19 ++#define PROC_PIO_SM0_EXECCTRL_STATUS_SEL_BITS 0x00000020 ++#define PROC_PIO_SM0_EXECCTRL_STATUS_SEL_LSB 5 ++#define PROC_PIO_SM0_EXECCTRL_STATUS_N_BITS 0x0000001f ++#define PROC_PIO_SM0_EXECCTRL_STATUS_N_LSB 0 ++ ++enum pio_fifo_join { ++ PIO_FIFO_JOIN_NONE = 0, ++ PIO_FIFO_JOIN_TX = 1, ++ PIO_FIFO_JOIN_RX = 2, ++}; ++ ++enum pio_mov_status_type { ++ STATUS_TX_LESSTHAN = 0, ++ STATUS_RX_LESSTHAN = 1 ++}; ++ ++enum pio_xfer_dir { ++ PIO_DIR_TO_SM, ++ PIO_DIR_FROM_SM, ++ PIO_DIR_COUNT ++}; ++ ++enum clock_index { ++ clk_sys = 5 ++}; ++ ++typedef struct pio_program { ++ const uint16_t *instructions; ++ uint8_t length; ++ int8_t origin; // required instruction memory origin or -1 ++} pio_program_t; ++ ++enum gpio_function { ++ GPIO_FUNC_FSEL0 = 0, ++ GPIO_FUNC_FSEL1 = 1, ++ GPIO_FUNC_FSEL2 = 2, ++ GPIO_FUNC_FSEL3 = 3, ++ GPIO_FUNC_FSEL4 = 4, ++ GPIO_FUNC_FSEL5 = 5, ++ GPIO_FUNC_FSEL6 = 6, ++ GPIO_FUNC_FSEL7 = 7, ++ GPIO_FUNC_FSEL8 = 8, ++ GPIO_FUNC_NULL = 0x1f, ++ ++ // Name a few ++ GPIO_FUNC_SYS_RIO = 5, ++ GPIO_FUNC_PROC_RIO = 6, ++ GPIO_FUNC_PIO = 7, ++}; ++ ++enum gpio_irq_level { ++ GPIO_IRQ_LEVEL_LOW = 0x1u, ++ GPIO_IRQ_LEVEL_HIGH = 0x2u, ++ GPIO_IRQ_EDGE_FALL = 0x4u, ++ GPIO_IRQ_EDGE_RISE = 0x8u, ++}; ++ ++enum gpio_override { ++ GPIO_OVERRIDE_NORMAL = 0, ++ GPIO_OVERRIDE_INVERT = 1, ++ GPIO_OVERRIDE_LOW = 2, ++ GPIO_OVERRIDE_HIGH = 3, ++}; ++enum gpio_slew_rate { ++ GPIO_SLEW_RATE_SLOW = 0, ++ GPIO_SLEW_RATE_FAST = 1 ++}; ++ ++enum gpio_drive_strength { ++ GPIO_DRIVE_STRENGTH_2MA = 0, ++ GPIO_DRIVE_STRENGTH_4MA = 1, ++ GPIO_DRIVE_STRENGTH_8MA = 2, ++ GPIO_DRIVE_STRENGTH_12MA = 3 ++}; ++ ++struct fp24_8 { ++ uint32_t val; ++}; ++ ++typedef rp1_pio_sm_config pio_sm_config; ++ ++typedef struct rp1_pio_client *PIO; ++ ++int rp1_pio_init(void); ++PIO rp1_pio_open(void); ++void rp1_pio_close(struct rp1_pio_client *client); ++void rp1_pio_set_error(struct rp1_pio_client *client, int err); ++int rp1_pio_get_error(const struct rp1_pio_client *client); ++void rp1_pio_clear_error(struct rp1_pio_client *client); ++int rp1_pio_sm_config_xfer(struct rp1_pio_client *client, uint sm, uint dir, ++ uint buf_size, uint buf_count); ++int rp1_pio_sm_xfer_data(struct rp1_pio_client *client, uint sm, uint dir, ++ uint data_bytes, void *data, dma_addr_t dma_addr, ++ void (*callback)(void *param), void *param); ++ ++int rp1_pio_can_add_program(struct rp1_pio_client *client, void *param); ++int rp1_pio_add_program(struct rp1_pio_client *client, void *param); ++int rp1_pio_remove_program(struct rp1_pio_client *client, void *param); ++int rp1_pio_clear_instr_mem(struct rp1_pio_client *client, void *param); ++int rp1_pio_sm_claim(struct rp1_pio_client *client, void *param); ++int rp1_pio_sm_unclaim(struct rp1_pio_client *client, void *param); ++int rp1_pio_sm_is_claimed(struct rp1_pio_client *client, void *param); ++int rp1_pio_sm_init(struct rp1_pio_client *client, void *param); ++int rp1_pio_sm_set_config(struct rp1_pio_client *client, void *param); ++int rp1_pio_sm_exec(struct rp1_pio_client *client, void *param); ++int rp1_pio_sm_clear_fifos(struct rp1_pio_client *client, void *param); ++int rp1_pio_sm_set_clkdiv(struct rp1_pio_client *client, void *param); ++int rp1_pio_sm_set_pins(struct rp1_pio_client *client, void *param); ++int rp1_pio_sm_set_pindirs(struct rp1_pio_client *client, void *param); ++int rp1_pio_sm_set_enabled(struct rp1_pio_client *client, void *param); ++int rp1_pio_sm_restart(struct rp1_pio_client *client, void *param); ++int rp1_pio_sm_clkdiv_restart(struct rp1_pio_client *client, void *param); ++int rp1_pio_sm_enable_sync(struct rp1_pio_client *client, void *param); ++int rp1_pio_sm_put(struct rp1_pio_client *client, void *param); ++int rp1_pio_sm_get(struct rp1_pio_client *client, void *param); ++int rp1_pio_sm_set_dmactrl(struct rp1_pio_client *client, void *param); ++int rp1_pio_sm_fifo_state(struct rp1_pio_client *client, void *param); ++int rp1_pio_sm_drain_tx(struct rp1_pio_client *client, void *param); ++int rp1_pio_gpio_init(struct rp1_pio_client *client, void *param); ++int rp1_pio_gpio_set_function(struct rp1_pio_client *client, void *param); ++int rp1_pio_gpio_set_pulls(struct rp1_pio_client *client, void *param); ++int rp1_pio_gpio_set_outover(struct rp1_pio_client *client, void *param); ++int rp1_pio_gpio_set_inover(struct rp1_pio_client *client, void *param); ++int rp1_pio_gpio_set_oeover(struct rp1_pio_client *client, void *param); ++int rp1_pio_gpio_set_input_enabled(struct rp1_pio_client *client, void *param); ++int rp1_pio_gpio_set_drive_strength(struct rp1_pio_client *client, void *param); ++ ++static inline int pio_init(void) ++{ ++ return rp1_pio_init(); ++} ++ ++static inline struct rp1_pio_client *pio_open(void) ++{ ++ return rp1_pio_open(); ++} ++ ++static inline void pio_close(struct rp1_pio_client *client) ++{ ++ rp1_pio_close(client); ++} ++ ++static inline void pio_set_error(struct rp1_pio_client *client, int err) ++{ ++ rp1_pio_set_error(client, err); ++} ++ ++static inline int pio_get_error(const struct rp1_pio_client *client) ++{ ++ return rp1_pio_get_error(client); ++} ++ ++static inline void pio_clear_error(struct rp1_pio_client *client) ++{ ++ rp1_pio_clear_error(client); ++} ++ ++static inline int pio_sm_config_xfer(struct rp1_pio_client *client, uint sm, uint dir, ++ uint buf_size, uint buf_count) ++{ ++ return rp1_pio_sm_config_xfer(client, sm, dir, buf_size, buf_count); ++} ++ ++static inline int pio_sm_xfer_data(struct rp1_pio_client *client, uint sm, uint dir, ++ uint data_bytes, void *data, dma_addr_t dma_addr, ++ void (*callback)(void *param), void *param) ++{ ++ return rp1_pio_sm_xfer_data(client, sm, dir, data_bytes, data, dma_addr, callback, param); ++} ++ ++static inline struct fp24_8 make_fp24_8(uint mul, uint div) ++{ ++ struct fp24_8 res = { .val = ((unsigned long long)mul << 8) / div }; ++ ++ return res; ++} ++ ++static inline bool pio_can_add_program(struct rp1_pio_client *client, ++ const pio_program_t *program) ++{ ++ struct rp1_pio_add_program_args args; ++ ++ if (bad_params_if(client, program->length > PIO_INSTRUCTION_COUNT)) ++ return false; ++ args.origin = (program->origin == -1) ? PIO_ORIGIN_ANY : program->origin; ++ args.num_instrs = program->length; ++ ++ memcpy(args.instrs, program->instructions, args.num_instrs * sizeof(args.instrs[0])); ++ return rp1_pio_can_add_program(client, &args); ++} ++ ++static inline bool pio_can_add_program_at_offset(struct rp1_pio_client *client, ++ const pio_program_t *program, uint offset) ++{ ++ struct rp1_pio_add_program_args args; ++ ++ if (bad_params_if(client, program->length > PIO_INSTRUCTION_COUNT || ++ offset >= PIO_INSTRUCTION_COUNT)) ++ return false; ++ args.origin = offset; ++ args.num_instrs = program->length; ++ ++ memcpy(args.instrs, program->instructions, args.num_instrs * sizeof(args.instrs[0])); ++ return !rp1_pio_can_add_program(client, &args); ++} ++ ++static inline uint pio_add_program(struct rp1_pio_client *client, const pio_program_t *program) ++{ ++ struct rp1_pio_add_program_args args; ++ int offset; ++ ++ if (bad_params_if(client, program->length > PIO_INSTRUCTION_COUNT)) ++ return PIO_ORIGIN_ANY; ++ args.origin = (program->origin == -1) ? PIO_ORIGIN_ANY : program->origin; ++ args.num_instrs = program->length; ++ ++ memcpy(args.instrs, program->instructions, args.num_instrs * sizeof(args.instrs[0])); ++ offset = rp1_pio_add_program(client, &args); ++ return (offset >= 0) ? offset : PIO_ORIGIN_ANY; ++} ++ ++static inline int pio_add_program_at_offset(struct rp1_pio_client *client, ++ const pio_program_t *program, uint offset) ++{ ++ struct rp1_pio_add_program_args args; ++ ++ if (bad_params_if(client, program->length > PIO_INSTRUCTION_COUNT || ++ offset >= PIO_INSTRUCTION_COUNT)) ++ return -EINVAL; ++ args.origin = offset; ++ args.num_instrs = program->length; ++ ++ memcpy(args.instrs, program->instructions, args.num_instrs * sizeof(args.instrs[0])); ++ return rp1_pio_add_program(client, &args); ++} ++ ++static inline int pio_remove_program(struct rp1_pio_client *client, const pio_program_t *program, ++ uint loaded_offset) ++{ ++ struct rp1_pio_remove_program_args args; ++ ++ args.origin = loaded_offset; ++ args.num_instrs = program->length; ++ ++ return rp1_pio_remove_program(client, &args); ++} ++ ++static inline int pio_clear_instruction_memory(struct rp1_pio_client *client) ++{ ++ return rp1_pio_clear_instr_mem(client, NULL); ++} ++ ++static inline int pio_sm_claim(struct rp1_pio_client *client, uint sm) ++{ ++ struct rp1_pio_sm_claim_args args = { .mask = 1 << sm }; ++ ++ if (bad_params_if(client, sm >= NUM_PIO_STATE_MACHINES)) ++ return -EINVAL; ++ ++ return rp1_pio_sm_claim(client, &args); ++} ++ ++static inline int pio_claim_sm_mask(struct rp1_pio_client *client, uint mask) ++{ ++ struct rp1_pio_sm_claim_args args = { .mask = mask }; ++ ++ if (bad_params_if(client, mask >= (1 << NUM_PIO_STATE_MACHINES))) ++ return -EINVAL; ++ ++ return rp1_pio_sm_claim(client, &args); ++} ++ ++static inline int pio_sm_unclaim(struct rp1_pio_client *client, uint sm) ++{ ++ struct rp1_pio_sm_claim_args args = { .mask = 1 << sm }; ++ ++ if (bad_params_if(client, sm >= NUM_PIO_STATE_MACHINES)) ++ return -EINVAL; ++ ++ return rp1_pio_sm_unclaim(client, &args); ++} ++ ++static inline int pio_claim_unused_sm(struct rp1_pio_client *client, bool required) ++{ ++ struct rp1_pio_sm_claim_args args = { .mask = 0 }; ++ int sm; ++ ++ sm = rp1_pio_sm_claim(client, &args); ++ if (sm < 0 && required) ++ WARN_ON("No PIO state machines are available"); ++ return sm; ++} ++ ++static inline bool pio_sm_is_claimed(struct rp1_pio_client *client, uint sm) ++{ ++ struct rp1_pio_sm_claim_args args = { .mask = (1 << sm) }; ++ ++ if (bad_params_if(client, sm >= NUM_PIO_STATE_MACHINES)) ++ return true; ++ return rp1_pio_sm_is_claimed(client, &args); ++} ++ ++static inline int pio_sm_init(struct rp1_pio_client *client, uint sm, uint initial_pc, ++ const pio_sm_config *config) ++{ ++ struct rp1_pio_sm_init_args args = { .sm = sm, .initial_pc = initial_pc, ++ .config = *config }; ++ ++ if (bad_params_if(client, sm >= NUM_PIO_STATE_MACHINES || ++ initial_pc >= PIO_INSTRUCTION_COUNT)) ++ return -EINVAL; ++ ++ return rp1_pio_sm_init(client, &args); ++} ++ ++static inline int pio_sm_set_config(struct rp1_pio_client *client, uint sm, ++ const pio_sm_config *config) ++{ ++ struct rp1_pio_sm_init_args args = { .sm = sm, .config = *config }; ++ ++ if (bad_params_if(client, sm >= NUM_PIO_STATE_MACHINES)) ++ return -EINVAL; ++ ++ return rp1_pio_sm_set_config(client, &args); ++} ++ ++static inline int pio_sm_exec(struct rp1_pio_client *client, uint sm, uint instr) ++{ ++ struct rp1_pio_sm_exec_args args = { .sm = sm, .instr = instr, .blocking = false }; ++ ++ if (bad_params_if(client, sm >= NUM_PIO_STATE_MACHINES || instr > (uint16_t)~0)) ++ return -EINVAL; ++ ++ return rp1_pio_sm_exec(client, &args); ++} ++ ++static inline int pio_sm_exec_wait_blocking(struct rp1_pio_client *client, uint sm, uint instr) ++{ ++ struct rp1_pio_sm_exec_args args = { .sm = sm, .instr = instr, .blocking = true }; ++ ++ if (bad_params_if(client, sm >= NUM_PIO_STATE_MACHINES || instr > (uint16_t)~0)) ++ return -EINVAL; ++ ++ return rp1_pio_sm_exec(client, &args); ++} ++ ++static inline int pio_sm_clear_fifos(struct rp1_pio_client *client, uint sm) ++{ ++ struct rp1_pio_sm_clear_fifos_args args = { .sm = sm }; ++ ++ if (bad_params_if(client, sm >= NUM_PIO_STATE_MACHINES)) ++ return -EINVAL; ++ return rp1_pio_sm_clear_fifos(client, &args); ++} ++ ++static inline bool pio_calculate_clkdiv_from_fp24_8(struct fp24_8 div, uint16_t *div_int, ++ uint8_t *div_frac) ++{ ++ uint inum = (div.val >> 8); ++ ++ if (bad_params_if(NULL, inum < 1 || inum > 65536)) ++ return false; ++ *div_int = (uint16_t)inum; ++ if (*div_int == 0) ++ *div_frac = 0; ++ else ++ *div_frac = div.val & 0xff; ++ return true; ++} ++ ++static inline int pio_sm_set_clkdiv_int_frac(struct rp1_pio_client *client, uint sm, ++ uint16_t div_int, uint8_t div_frac) ++{ ++ struct rp1_pio_sm_set_clkdiv_args args = { .sm = sm, .div_int = div_int, ++ .div_frac = div_frac }; ++ ++ if (bad_params_if(client, sm >= NUM_PIO_STATE_MACHINES || ++ (div_int == 0 && div_frac != 0))) ++ return -EINVAL; ++ return rp1_pio_sm_set_clkdiv(client, &args); ++} ++ ++static inline int pio_sm_set_clkdiv(struct rp1_pio_client *client, uint sm, struct fp24_8 div) ++{ ++ struct rp1_pio_sm_set_clkdiv_args args = { .sm = sm }; ++ ++ if (!pio_calculate_clkdiv_from_fp24_8(div, &args.div_int, &args.div_frac)) ++ return -EINVAL; ++ return rp1_pio_sm_set_clkdiv(client, &args); ++} ++ ++static inline int pio_sm_set_pins(struct rp1_pio_client *client, uint sm, uint32_t pin_values) ++{ ++ struct rp1_pio_sm_set_pins_args args = { .sm = sm, .values = pin_values, ++ .mask = GPIOS_MASK }; ++ ++ if (bad_params_if(client, sm >= NUM_PIO_STATE_MACHINES)) ++ return -EINVAL; ++ return rp1_pio_sm_set_pins(client, &args); ++} ++ ++static inline int pio_sm_set_pins_with_mask(struct rp1_pio_client *client, uint sm, ++ uint32_t pin_values, uint32_t pin_mask) ++{ ++ struct rp1_pio_sm_set_pins_args args = { .sm = sm, .values = pin_values, ++ .mask = pin_mask }; ++ ++ if (bad_params_if(client, sm >= NUM_PIO_STATE_MACHINES)) ++ return -EINVAL; ++ return rp1_pio_sm_set_pins(client, &args); ++} ++ ++static inline int pio_sm_set_pindirs_with_mask(struct rp1_pio_client *client, uint sm, ++ uint32_t pin_dirs, uint32_t pin_mask) ++{ ++ struct rp1_pio_sm_set_pindirs_args args = { .sm = sm, .dirs = pin_dirs, ++ .mask = pin_mask }; ++ ++ if (bad_params_if(client, sm >= NUM_PIO_STATE_MACHINES || ++ (pin_dirs & GPIOS_MASK) != pin_dirs || ++ (pin_mask & pin_mask) != pin_mask)) ++ return -EINVAL; ++ return rp1_pio_sm_set_pindirs(client, &args); ++} ++ ++static inline int pio_sm_set_consecutive_pindirs(struct rp1_pio_client *client, uint sm, ++ uint pin_base, uint pin_count, bool is_out) ++{ ++ uint32_t mask = ((1 << pin_count) - 1) << pin_base; ++ struct rp1_pio_sm_set_pindirs_args args = { .sm = sm, .dirs = is_out ? mask : 0, ++ .mask = mask }; ++ ++ if (bad_params_if(client, sm >= NUM_PIO_STATE_MACHINES || ++ pin_base >= RP1_PIO_GPIO_COUNT || ++ pin_count > RP1_PIO_GPIO_COUNT || ++ (pin_base + pin_count) > RP1_PIO_GPIO_COUNT)) ++ return -EINVAL; ++ return rp1_pio_sm_set_pindirs(client, &args); ++} ++ ++static inline int pio_sm_set_enabled(struct rp1_pio_client *client, uint sm, bool enabled) ++{ ++ struct rp1_pio_sm_set_enabled_args args = { .mask = (1 << sm), .enable = enabled }; ++ ++ if (bad_params_if(client, sm >= NUM_PIO_STATE_MACHINES)) ++ return -EINVAL; ++ return rp1_pio_sm_set_enabled(client, &args); ++} ++ ++static inline int pio_set_sm_mask_enabled(struct rp1_pio_client *client, uint32_t mask, ++ bool enabled) ++{ ++ struct rp1_pio_sm_set_enabled_args args = { .mask = mask, .enable = enabled }; ++ ++ if (bad_params_if(client, mask >= (1 << NUM_PIO_STATE_MACHINES))) ++ return -EINVAL; ++ return rp1_pio_sm_set_enabled(client, &args); ++} ++ ++static inline int pio_sm_restart(struct rp1_pio_client *client, uint sm) ++{ ++ struct rp1_pio_sm_restart_args args = { .mask = (1 << sm) }; ++ ++ if (bad_params_if(client, sm >= NUM_PIO_STATE_MACHINES)) ++ return -EINVAL; ++ return rp1_pio_sm_restart(client, &args); ++} ++ ++static inline int pio_restart_sm_mask(struct rp1_pio_client *client, uint32_t mask) ++{ ++ struct rp1_pio_sm_restart_args args = { .mask = (uint16_t)mask }; ++ ++ if (bad_params_if(client, mask >= (1 << NUM_PIO_STATE_MACHINES))) ++ return -EINVAL; ++ return rp1_pio_sm_restart(client, &args); ++} ++ ++static inline int pio_sm_clkdiv_restart(struct rp1_pio_client *client, uint sm) ++{ ++ struct rp1_pio_sm_restart_args args = { .mask = (1 << sm) }; ++ ++ if (bad_params_if(client, sm >= NUM_PIO_STATE_MACHINES)) ++ return -EINVAL; ++ return rp1_pio_sm_clkdiv_restart(client, &args); ++} ++ ++static inline int pio_clkdiv_restart_sm_mask(struct rp1_pio_client *client, uint32_t mask) ++{ ++ struct rp1_pio_sm_restart_args args = { .mask = (uint16_t)mask }; ++ ++ if (bad_params_if(client, mask >= (1 << NUM_PIO_STATE_MACHINES))) ++ return -EINVAL; ++ return rp1_pio_sm_clkdiv_restart(client, &args); ++} ++ ++static inline int pio_enable_sm_in_sync_mask(struct rp1_pio_client *client, uint32_t mask) ++{ ++ struct rp1_pio_sm_enable_sync_args args = { .mask = (uint16_t)mask }; ++ ++ if (bad_params_if(client, mask >= (1 << NUM_PIO_STATE_MACHINES))) ++ return -EINVAL; ++ return rp1_pio_sm_enable_sync(client, &args); ++} ++ ++static inline int pio_sm_set_dmactrl(struct rp1_pio_client *client, uint sm, bool is_tx, ++ uint32_t ctrl) ++{ ++ struct rp1_pio_sm_set_dmactrl_args args = { .sm = sm, .is_tx = is_tx, .ctrl = ctrl }; ++ ++ if (bad_params_if(client, sm >= NUM_PIO_STATE_MACHINES)) ++ return -EINVAL; ++ return rp1_pio_sm_set_dmactrl(client, &args); ++}; ++ ++static inline int pio_sm_drain_tx_fifo(struct rp1_pio_client *client, uint sm) ++{ ++ struct rp1_pio_sm_clear_fifos_args args = { .sm = sm }; ++ ++ if (bad_params_if(client, sm >= NUM_PIO_STATE_MACHINES)) ++ return -EINVAL; ++ return rp1_pio_sm_drain_tx(client, &args); ++}; ++ ++static inline int pio_sm_put(struct rp1_pio_client *client, uint sm, uint32_t data) ++{ ++ struct rp1_pio_sm_put_args args = { .sm = (uint16_t)sm, .blocking = false, .data = data }; ++ ++ if (bad_params_if(client, sm >= NUM_PIO_STATE_MACHINES)) ++ return -EINVAL; ++ return rp1_pio_sm_put(client, &args); ++} ++ ++static inline int pio_sm_put_blocking(struct rp1_pio_client *client, uint sm, uint32_t data) ++{ ++ struct rp1_pio_sm_put_args args = { .sm = (uint16_t)sm, .blocking = true, .data = data }; ++ ++ if (bad_params_if(client, sm >= NUM_PIO_STATE_MACHINES)) ++ return -EINVAL; ++ return rp1_pio_sm_put(client, &args); ++} ++ ++static inline uint32_t pio_sm_get(struct rp1_pio_client *client, uint sm) ++{ ++ struct rp1_pio_sm_get_args args = { .sm = (uint16_t)sm, .blocking = false }; ++ ++ if (!bad_params_if(client, sm >= NUM_PIO_STATE_MACHINES)) ++ rp1_pio_sm_get(client, &args); ++ return args.data; ++} ++ ++static inline uint32_t pio_sm_get_blocking(struct rp1_pio_client *client, uint sm) ++{ ++ struct rp1_pio_sm_get_args args = { .sm = (uint16_t)sm, .blocking = true }; ++ ++ if (!bad_params_if(client, sm >= NUM_PIO_STATE_MACHINES)) ++ rp1_pio_sm_get(client, &args); ++ return args.data; ++} ++ ++static inline int pio_sm_is_rx_fifo_empty(struct rp1_pio_client *client, uint sm) ++{ ++ struct rp1_pio_sm_fifo_state_args args = { .sm = sm, .tx = false }; ++ int ret; ++ ++ if (bad_params_if(client, sm >= NUM_PIO_STATE_MACHINES)) ++ return -EINVAL; ++ ret = rp1_pio_sm_fifo_state(client, &args); ++ if (ret == sizeof(args)) ++ ret = args.empty; ++ return ret; ++}; ++ ++static inline int pio_sm_is_rx_fifo_full(struct rp1_pio_client *client, uint sm) ++{ ++ struct rp1_pio_sm_fifo_state_args args = { .sm = sm, .tx = false }; ++ int ret; ++ ++ if (bad_params_if(client, sm >= NUM_PIO_STATE_MACHINES)) ++ return -EINVAL; ++ ret = rp1_pio_sm_fifo_state(client, &args); ++ if (ret == sizeof(args)) ++ ret = args.full; ++ return ret; ++}; ++ ++static inline int pio_sm_rx_fifo_level(struct rp1_pio_client *client, uint sm) ++{ ++ struct rp1_pio_sm_fifo_state_args args = { .sm = sm, .tx = false }; ++ int ret; ++ ++ if (bad_params_if(client, sm >= NUM_PIO_STATE_MACHINES)) ++ return -EINVAL; ++ ret = rp1_pio_sm_fifo_state(client, &args); ++ if (ret == sizeof(args)) ++ ret = args.level; ++ return ret; ++}; ++ ++static inline int pio_sm_is_tx_fifo_empty(struct rp1_pio_client *client, uint sm) ++{ ++ struct rp1_pio_sm_fifo_state_args args = { .sm = sm, .tx = true }; ++ int ret; ++ ++ if (bad_params_if(client, sm >= NUM_PIO_STATE_MACHINES)) ++ return -EINVAL; ++ ret = rp1_pio_sm_fifo_state(client, &args); ++ if (ret == sizeof(args)) ++ ret = args.empty; ++ return ret; ++}; ++ ++static inline int pio_sm_is_tx_fifo_full(struct rp1_pio_client *client, uint sm) ++{ ++ struct rp1_pio_sm_fifo_state_args args = { .sm = sm, .tx = true }; ++ int ret; ++ ++ if (bad_params_if(client, sm >= NUM_PIO_STATE_MACHINES)) ++ return -EINVAL; ++ ret = rp1_pio_sm_fifo_state(client, &args); ++ if (ret == sizeof(args)) ++ ret = args.full; ++ return ret; ++}; ++ ++static inline int pio_sm_tx_fifo_level(struct rp1_pio_client *client, uint sm) ++{ ++ struct rp1_pio_sm_fifo_state_args args = { .sm = sm, .tx = true }; ++ int ret; ++ ++ if (bad_params_if(client, sm >= NUM_PIO_STATE_MACHINES)) ++ return -EINVAL; ++ ret = rp1_pio_sm_fifo_state(client, &args); ++ if (ret == sizeof(args)) ++ ret = args.level; ++ return ret; ++}; ++ ++static inline void sm_config_set_out_pins(pio_sm_config *c, uint out_base, uint out_count) ++{ ++ if (bad_params_if(NULL, out_base >= RP1_PIO_GPIO_COUNT || ++ out_count > RP1_PIO_GPIO_COUNT)) ++ return; ++ ++ c->pinctrl = (c->pinctrl & ~(PROC_PIO_SM0_PINCTRL_OUT_BASE_BITS | ++ PROC_PIO_SM0_PINCTRL_OUT_COUNT_BITS)) | ++ (out_base << PROC_PIO_SM0_PINCTRL_OUT_BASE_LSB) | ++ (out_count << PROC_PIO_SM0_PINCTRL_OUT_COUNT_LSB); ++} ++ ++static inline void sm_config_set_set_pins(pio_sm_config *c, uint set_base, uint set_count) ++{ ++ if (bad_params_if(NULL, set_base >= RP1_PIO_GPIO_COUNT || ++ set_count > 5)) ++ return; ++ ++ c->pinctrl = (c->pinctrl & ~(PROC_PIO_SM0_PINCTRL_SET_BASE_BITS | ++ PROC_PIO_SM0_PINCTRL_SET_COUNT_BITS)) | ++ (set_base << PROC_PIO_SM0_PINCTRL_SET_BASE_LSB) | ++ (set_count << PROC_PIO_SM0_PINCTRL_SET_COUNT_LSB); ++} ++ ++ ++static inline void sm_config_set_in_pins(pio_sm_config *c, uint in_base) ++{ ++ if (bad_params_if(NULL, in_base >= RP1_PIO_GPIO_COUNT)) ++ return; ++ ++ c->pinctrl = (c->pinctrl & ~PROC_PIO_SM0_PINCTRL_IN_BASE_BITS) | ++ (in_base << PROC_PIO_SM0_PINCTRL_IN_BASE_LSB); ++} ++ ++static inline void sm_config_set_sideset_pins(pio_sm_config *c, uint sideset_base) ++{ ++ if (bad_params_if(NULL, sideset_base >= RP1_PIO_GPIO_COUNT)) ++ return; ++ ++ c->pinctrl = (c->pinctrl & ~PROC_PIO_SM0_PINCTRL_SIDESET_BASE_BITS) | ++ (sideset_base << PROC_PIO_SM0_PINCTRL_SIDESET_BASE_LSB); ++} ++ ++static inline void sm_config_set_sideset(pio_sm_config *c, uint bit_count, bool optional, ++ bool pindirs) ++{ ++ if (bad_params_if(NULL, bit_count > 5 || ++ (optional && (bit_count == 0)))) ++ return; ++ c->pinctrl = (c->pinctrl & ~PROC_PIO_SM0_PINCTRL_SIDESET_COUNT_BITS) | ++ (bit_count << PROC_PIO_SM0_PINCTRL_SIDESET_COUNT_LSB); ++ ++ c->execctrl = (c->execctrl & ~(PROC_PIO_SM0_EXECCTRL_SIDE_EN_BITS | ++ PROC_PIO_SM0_EXECCTRL_SIDE_PINDIR_BITS)) | ++ (optional << PROC_PIO_SM0_EXECCTRL_SIDE_EN_LSB) | ++ (pindirs << PROC_PIO_SM0_EXECCTRL_SIDE_PINDIR_LSB); ++} ++ ++static inline void sm_config_set_clkdiv_int_frac(pio_sm_config *c, uint16_t div_int, ++ uint8_t div_frac) ++{ ++ if (bad_params_if(NULL, div_int == 0 && div_frac != 0)) ++ return; ++ ++ c->clkdiv = ++ (((uint)div_frac) << PROC_PIO_SM0_CLKDIV_FRAC_LSB) | ++ (((uint)div_int) << PROC_PIO_SM0_CLKDIV_INT_LSB); ++} ++ ++static inline void sm_config_set_clkdiv(pio_sm_config *c, struct fp24_8 div) ++{ ++ uint16_t div_int; ++ uint8_t div_frac; ++ ++ pio_calculate_clkdiv_from_fp24_8(div, &div_int, &div_frac); ++ sm_config_set_clkdiv_int_frac(c, div_int, div_frac); ++} ++ ++static inline void sm_config_set_wrap(pio_sm_config *c, uint wrap_target, uint wrap) ++{ ++ if (bad_params_if(NULL, wrap >= PIO_INSTRUCTION_COUNT || ++ wrap_target >= PIO_INSTRUCTION_COUNT)) ++ return; ++ ++ c->execctrl = (c->execctrl & ~(PROC_PIO_SM0_EXECCTRL_WRAP_TOP_BITS | ++ PROC_PIO_SM0_EXECCTRL_WRAP_BOTTOM_BITS)) | ++ (wrap_target << PROC_PIO_SM0_EXECCTRL_WRAP_BOTTOM_LSB) | ++ (wrap << PROC_PIO_SM0_EXECCTRL_WRAP_TOP_LSB); ++} ++ ++static inline void sm_config_set_jmp_pin(pio_sm_config *c, uint pin) ++{ ++ if (bad_params_if(NULL, pin >= RP1_PIO_GPIO_COUNT)) ++ return; ++ ++ c->execctrl = (c->execctrl & ~PROC_PIO_SM0_EXECCTRL_JMP_PIN_BITS) | ++ (pin << PROC_PIO_SM0_EXECCTRL_JMP_PIN_LSB); ++} ++ ++static inline void sm_config_set_in_shift(pio_sm_config *c, bool shift_right, bool autopush, ++ uint push_threshold) ++{ ++ if (bad_params_if(NULL, push_threshold > 32)) ++ return; ++ ++ c->shiftctrl = (c->shiftctrl & ++ ~(PROC_PIO_SM0_SHIFTCTRL_IN_SHIFTDIR_BITS | ++ PROC_PIO_SM0_SHIFTCTRL_AUTOPUSH_BITS | ++ PROC_PIO_SM0_SHIFTCTRL_PUSH_THRESH_BITS)) | ++ (shift_right << PROC_PIO_SM0_SHIFTCTRL_IN_SHIFTDIR_LSB) | ++ (autopush << PROC_PIO_SM0_SHIFTCTRL_AUTOPUSH_LSB) | ++ ((push_threshold & 0x1fu) << PROC_PIO_SM0_SHIFTCTRL_PUSH_THRESH_LSB); ++} ++ ++static inline void sm_config_set_out_shift(pio_sm_config *c, bool shift_right, bool autopull, ++ uint pull_threshold) ++{ ++ if (bad_params_if(NULL, pull_threshold > 32)) ++ return; ++ ++ c->shiftctrl = (c->shiftctrl & ++ ~(PROC_PIO_SM0_SHIFTCTRL_OUT_SHIFTDIR_BITS | ++ PROC_PIO_SM0_SHIFTCTRL_AUTOPULL_BITS | ++ PROC_PIO_SM0_SHIFTCTRL_PULL_THRESH_BITS)) | ++ (shift_right << PROC_PIO_SM0_SHIFTCTRL_OUT_SHIFTDIR_LSB) | ++ (autopull << PROC_PIO_SM0_SHIFTCTRL_AUTOPULL_LSB) | ++ ((pull_threshold & 0x1fu) << PROC_PIO_SM0_SHIFTCTRL_PULL_THRESH_LSB); ++} ++ ++static inline void sm_config_set_fifo_join(pio_sm_config *c, enum pio_fifo_join join) ++{ ++ if (bad_params_if(NULL, join != PIO_FIFO_JOIN_NONE && ++ join != PIO_FIFO_JOIN_TX && ++ join != PIO_FIFO_JOIN_RX)) ++ return; ++ ++ c->shiftctrl = (c->shiftctrl & (uint)~(PROC_PIO_SM0_SHIFTCTRL_FJOIN_TX_BITS | ++ PROC_PIO_SM0_SHIFTCTRL_FJOIN_RX_BITS)) | ++ (((uint)join) << PROC_PIO_SM0_SHIFTCTRL_FJOIN_TX_LSB); ++} ++ ++static inline void sm_config_set_out_special(pio_sm_config *c, bool sticky, bool has_enable_pin, ++ uint enable_pin_index) ++{ ++ c->execctrl = (c->execctrl & ++ (uint)~(PROC_PIO_SM0_EXECCTRL_OUT_STICKY_BITS | ++ PROC_PIO_SM0_EXECCTRL_INLINE_OUT_EN_BITS | ++ PROC_PIO_SM0_EXECCTRL_OUT_EN_SEL_BITS)) | ++ (sticky << PROC_PIO_SM0_EXECCTRL_OUT_STICKY_LSB) | ++ (has_enable_pin << PROC_PIO_SM0_EXECCTRL_INLINE_OUT_EN_LSB) | ++ ((enable_pin_index << PROC_PIO_SM0_EXECCTRL_OUT_EN_SEL_LSB) & ++ PROC_PIO_SM0_EXECCTRL_OUT_EN_SEL_BITS); ++} ++ ++static inline void sm_config_set_mov_status(pio_sm_config *c, enum pio_mov_status_type status_sel, ++ uint status_n) ++{ ++ if (bad_params_if(NULL, status_sel != STATUS_TX_LESSTHAN && ++ status_sel != STATUS_RX_LESSTHAN)) ++ return; ++ ++ c->execctrl = (c->execctrl ++ & ~(PROC_PIO_SM0_EXECCTRL_STATUS_SEL_BITS | PROC_PIO_SM0_EXECCTRL_STATUS_N_BITS)) ++ | ((((uint)status_sel) << PROC_PIO_SM0_EXECCTRL_STATUS_SEL_LSB) & ++ PROC_PIO_SM0_EXECCTRL_STATUS_SEL_BITS) ++ | ((status_n << PROC_PIO_SM0_EXECCTRL_STATUS_N_LSB) & ++ PROC_PIO_SM0_EXECCTRL_STATUS_N_BITS); ++} ++ ++static inline pio_sm_config pio_get_default_sm_config(void) ++{ ++ pio_sm_config c = { 0 }; ++ ++ sm_config_set_clkdiv_int_frac(&c, 1, 0); ++ sm_config_set_wrap(&c, 0, 31); ++ sm_config_set_in_shift(&c, true, false, 32); ++ sm_config_set_out_shift(&c, true, false, 32); ++ return c; ++} ++ ++static inline uint32_t clock_get_hz(enum clock_index clk_index) ++{ ++ const uint32_t MHZ = 1000000; ++ ++ if (bad_params_if(NULL, clk_index != clk_sys)) ++ return 0; ++ return 200 * MHZ; ++} ++ ++static inline int pio_gpio_set_function(struct rp1_pio_client *client, uint gpio, ++ enum gpio_function fn) ++{ ++ struct rp1_gpio_set_function_args args = { .gpio = gpio, .fn = fn }; ++ ++ if (bad_params_if(client, gpio >= RP1_PIO_GPIO_COUNT)) ++ return -EINVAL; ++ return rp1_pio_gpio_set_function(client, &args); ++} ++ ++static inline int pio_gpio_init(struct rp1_pio_client *client, uint gpio) ++{ ++ struct rp1_gpio_init_args args = { .gpio = gpio }; ++ int ret; ++ ++ if (bad_params_if(client, gpio >= RP1_PIO_GPIO_COUNT)) ++ return -EINVAL; ++ ret = rp1_pio_gpio_init(client, &args); ++ if (ret) ++ return ret; ++ return pio_gpio_set_function(client, gpio, RP1_GPIO_FUNC_PIO); ++} ++ ++static inline int pio_gpio_set_pulls(struct rp1_pio_client *client, uint gpio, bool up, bool down) ++{ ++ struct rp1_gpio_set_pulls_args args = { .gpio = gpio, .up = up, .down = down }; ++ ++ if (bad_params_if(client, gpio >= RP1_PIO_GPIO_COUNT)) ++ return -EINVAL; ++ return rp1_pio_gpio_set_pulls(client, &args); ++} ++ ++static inline int pio_gpio_set_outover(struct rp1_pio_client *client, uint gpio, uint value) ++{ ++ struct rp1_gpio_set_args args = { .gpio = gpio, .value = value }; ++ ++ if (bad_params_if(client, gpio >= RP1_PIO_GPIO_COUNT)) ++ return -EINVAL; ++ return rp1_pio_gpio_set_outover(client, &args); ++} ++ ++static inline int pio_gpio_set_inover(struct rp1_pio_client *client, uint gpio, uint value) ++{ ++ struct rp1_gpio_set_args args = { .gpio = gpio, .value = value }; ++ ++ if (bad_params_if(client, gpio >= RP1_PIO_GPIO_COUNT)) ++ return -EINVAL; ++ return rp1_pio_gpio_set_inover(client, &args); ++} ++ ++static inline int pio_gpio_set_oeover(struct rp1_pio_client *client, uint gpio, uint value) ++{ ++ struct rp1_gpio_set_args args = { .gpio = gpio, .value = value }; ++ ++ if (bad_params_if(client, gpio >= RP1_PIO_GPIO_COUNT)) ++ return -EINVAL; ++ return rp1_pio_gpio_set_oeover(client, &args); ++} ++ ++static inline int pio_gpio_set_input_enabled(struct rp1_pio_client *client, uint gpio, ++ bool enabled) ++{ ++ struct rp1_gpio_set_args args = { .gpio = gpio, .value = enabled }; ++ ++ if (bad_params_if(client, gpio >= RP1_PIO_GPIO_COUNT)) ++ return -EINVAL; ++ return rp1_pio_gpio_set_input_enabled(client, &args); ++} ++ ++static inline int pio_gpio_set_drive_strength(struct rp1_pio_client *client, uint gpio, ++ enum gpio_drive_strength drive) ++{ ++ struct rp1_gpio_set_args args = { .gpio = gpio, .value = drive }; ++ ++ if (bad_params_if(client, gpio >= RP1_PIO_GPIO_COUNT)) ++ return -EINVAL; ++ return rp1_pio_gpio_set_drive_strength(client, &args); ++} ++ ++static inline int pio_gpio_pull_up(struct rp1_pio_client *client, uint gpio) ++{ ++ return pio_gpio_set_pulls(client, gpio, true, false); ++} ++ ++static inline int pio_gpio_pull_down(struct rp1_pio_client *client, uint gpio) ++{ ++ return pio_gpio_set_pulls(client, gpio, false, true); ++} ++ ++static inline int pio_gpio_disable_pulls(struct rp1_pio_client *client, uint gpio) ++{ ++ return pio_gpio_set_pulls(client, gpio, false, false); ++} ++ ++#endif diff --git a/include/linux/platform_data/dma-bcm2708.h b/include/linux/platform_data/dma-bcm2708.h new file mode 100644 index 000000000000..6ca874d332a8 @@ -255522,6 +264799,65 @@ index 63426d8255e4..4bfbd25ade06 100644 } static inline int pwm_config(struct pwm_device *pwm, int duty_ns, +diff --git a/include/linux/rp1-firmware.h b/include/linux/rp1-firmware.h +new file mode 100644 +index 000000000000..19f11d6d79b1 +--- /dev/null ++++ b/include/linux/rp1-firmware.h +@@ -0,0 +1,53 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++/* ++ * Copyright (C) 2023 2023-2024 Raspberry Pi Ltd. ++ */ ++ ++#ifndef __SOC_RP1_FIRMWARE_H__ ++#define __SOC_RP1_FIRMWARE_H__ ++ ++#include ++#include ++ ++#define RP1_FOURCC(s) ((uint32_t)((s[0] << 24) | (s[1] << 16) | (s[2] << 8) | (s[3] << 0))) ++ ++struct rp1_firmware; ++ ++#if IS_ENABLED(CONFIG_FIRMWARE_RP1) ++int rp1_firmware_message(struct rp1_firmware *fw, uint16_t op, ++ const void *data, unsigned int data_len, ++ void *resp, unsigned int resp_space); ++void rp1_firmware_put(struct rp1_firmware *fw); ++struct rp1_firmware *rp1_firmware_get(struct device_node *fwnode); ++struct rp1_firmware *devm_rp1_firmware_get(struct device *dev, struct device_node *fwnode); ++int rp1_firmware_get_feature(struct rp1_firmware *fw, uint32_t fourcc, ++ uint32_t *op_base, uint32_t *op_count); ++#else ++static inline int rp1_firmware_message(struct rp1_firmware *fw, uint16_t op, ++ const void *data, unsigned int data_len, ++ void *resp, unsigned int resp_space) ++{ ++ return -EOPNOTSUPP; ++} ++ ++static inline void rp1_firmware_put(struct rp1_firmware *fw) { } ++ ++static inline struct rp1_firmware *rp1_firmware_get(struct device_node *fwnode) ++{ ++ return NULL; ++} ++ ++static inline struct rp1_firmware *devm_rp1_firmware_get(struct device *dev, ++ struct device_node *fwnode) ++{ ++ return NULL; ++} ++ ++static inline int rp1_firmware_get_feature(struct rp1_firmware *fw, uint32_t fourcc, ++ uint32_t *op_base, uint32_t *op_count) ++{ ++ return -EOPNOTSUPP; ++} ++#endif ++ ++#endif /* __SOC_RP1_FIRMWARE_H__ */ diff --git a/include/linux/rp1_platform.h b/include/linux/rp1_platform.h new file mode 100644 index 000000000000..f805dbe1ed9b @@ -255887,7 +265223,7 @@ index 4b6a9d2ea372..a40c1b3e24b8 100644 * vb2_core_expbuf() - Export a buffer as a file descriptor. * @q: pointer to &struct vb2_queue with videobuf2 queue. diff --git a/include/soc/bcm2835/raspberrypi-firmware.h b/include/soc/bcm2835/raspberrypi-firmware.h -index 73cac8d0287e..b4bc8b675607 100644 +index 73cac8d0287e..3e71d45477a4 100644 --- a/include/soc/bcm2835/raspberrypi-firmware.h +++ b/include/soc/bcm2835/raspberrypi-firmware.h @@ -36,6 +36,8 @@ struct rpi_firmware_property_tag_header { @@ -255907,7 +265243,7 @@ index 73cac8d0287e..b4bc8b675607 100644 RPI_FIRMWARE_GET_DOMAIN_STATE = 0x00030030, RPI_FIRMWARE_GET_THROTTLED = 0x00030046, RPI_FIRMWARE_GET_CLOCK_MEASURED = 0x00030047, -@@ -89,8 +92,11 @@ enum rpi_firmware_property_tag { +@@ -89,9 +92,14 @@ enum rpi_firmware_property_tag { RPI_FIRMWARE_GET_PERIPH_REG = 0x00030045, RPI_FIRMWARE_SET_PERIPH_REG = 0x00038045, RPI_FIRMWARE_GET_POE_HAT_VAL = 0x00030049, @@ -255918,9 +265254,12 @@ index 73cac8d0287e..b4bc8b675607 100644 + RPI_FIRMWARE_GET_REBOOT_FLAGS = 0x00030064, + RPI_FIRMWARE_SET_REBOOT_FLAGS = 0x00038064, RPI_FIRMWARE_NOTIFY_DISPLAY_DONE = 0x00030066, ++ RPI_FIRMWARE_GET_SW_UART = 0x0003008a, ++ RPI_FIRMWARE_SET_SW_UART = 0x0003808a, /* Dispmanx TAGS */ -@@ -105,9 +111,16 @@ enum rpi_firmware_property_tag { + RPI_FIRMWARE_FRAMEBUFFER_ALLOCATE = 0x00040001, +@@ -105,9 +113,16 @@ enum rpi_firmware_property_tag { RPI_FIRMWARE_FRAMEBUFFER_GET_VIRTUAL_OFFSET = 0x00040009, RPI_FIRMWARE_FRAMEBUFFER_GET_OVERSCAN = 0x0004000a, RPI_FIRMWARE_FRAMEBUFFER_GET_PALETTE = 0x0004000b, @@ -255937,7 +265276,7 @@ index 73cac8d0287e..b4bc8b675607 100644 RPI_FIRMWARE_FRAMEBUFFER_TEST_PHYSICAL_WIDTH_HEIGHT = 0x00044003, RPI_FIRMWARE_FRAMEBUFFER_TEST_VIRTUAL_WIDTH_HEIGHT = 0x00044004, RPI_FIRMWARE_FRAMEBUFFER_TEST_DEPTH = 0x00044005, -@@ -116,22 +129,33 @@ enum rpi_firmware_property_tag { +@@ -116,22 +131,33 @@ enum rpi_firmware_property_tag { RPI_FIRMWARE_FRAMEBUFFER_TEST_VIRTUAL_OFFSET = 0x00044009, RPI_FIRMWARE_FRAMEBUFFER_TEST_OVERSCAN = 0x0004400a, RPI_FIRMWARE_FRAMEBUFFER_TEST_PALETTE = 0x0004400b, @@ -255971,7 +265310,7 @@ index 73cac8d0287e..b4bc8b675607 100644 RPI_FIRMWARE_GET_COMMAND_LINE = 0x00050001, RPI_FIRMWARE_GET_DMA_CHANNELS = 0x00060001, }; -@@ -152,9 +176,12 @@ enum rpi_firmware_clk_id { +@@ -152,9 +178,12 @@ enum rpi_firmware_clk_id { RPI_FIRMWARE_M2MC_CLK_ID, RPI_FIRMWARE_PIXEL_BVB_CLK_ID, RPI_FIRMWARE_VEC_CLK_ID, @@ -255984,6 +265323,18 @@ index 73cac8d0287e..b4bc8b675607 100644 /** * struct rpi_firmware_clk_rate_request - Firmware Request for a rate * @id: ID of the clock being queried +diff --git a/include/uapi/drm/drm_mode.h b/include/uapi/drm/drm_mode.h +index ea1b639bcb28..25f563cb853d 100644 +--- a/include/uapi/drm/drm_mode.h ++++ b/include/uapi/drm/drm_mode.h +@@ -203,6 +203,7 @@ extern "C" { + */ + #define DRM_MODE_REFLECT_X (1<<4) + #define DRM_MODE_REFLECT_Y (1<<5) ++#define DRM_MODE_TRANSPOSE (1<<6) + + /* + * DRM_MODE_REFLECT_MASK diff --git a/include/uapi/drm/v3d_drm.h b/include/uapi/drm/v3d_drm.h index 3dfc0af8756a..8d2ee7df3b50 100644 --- a/include/uapi/drm/v3d_drm.h @@ -256396,10 +265747,10 @@ index a03c543cb072..7fbeb5a95f45 100644 #endif /* __LINUX_MEDIA_BUS_FORMAT_H */ diff --git a/include/uapi/linux/media/raspberrypi/pisp_be_config.h b/include/uapi/linux/media/raspberrypi/pisp_be_config.h new file mode 100644 -index 000000000000..cbeb714f4d61 +index 000000000000..82560db4da61 --- /dev/null +++ b/include/uapi/linux/media/raspberrypi/pisp_be_config.h -@@ -0,0 +1,968 @@ +@@ -0,0 +1,934 @@ +/* SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note */ +/* + * PiSP Back End configuration definitions. @@ -257118,13 +266469,6 @@ index 000000000000..cbeb714f4d61 +/** + * struct pisp_be_config - RaspberryPi PiSP Back End Processing configuration + * -+ * @input_buffer: Input buffer addresses -+ * @tdn_input_buffer: TDN input buffer addresses -+ * @stitch_input_buffer: Stitch input buffer addresses -+ * @tdn_output_buffer: TDN output buffer addresses -+ * @stitch_output_buffer: Stitch output buffer addresses -+ * @output_buffer: Output buffers addresses -+ * @hog_buffer: HOG buffer addresses + * @global: Global PiSP configuration + * @input_format: Input image format + * @decompress: Decompress configuration @@ -257163,28 +266507,10 @@ index 000000000000..cbeb714f4d61 + * @output_format: Output format configuration + * @hog: HOG configuration + * @axi: AXI bus configuration -+ * @lsc_extra: LSC extra info -+ * @cac_extra: CAC extra info -+ * @downscale_extra: Downscaler extra info -+ * @resample_extra: Resample extra info -+ * @crop: Crop configuration -+ * @hog_format: HOG format info -+ * @dirty_flags_bayer: Bayer enable dirty flags -+ * (:c:type:`pisp_be_bayer_enable`) -+ * @dirty_flags_rgb: RGB enable dirty flags -+ * (:c:type:`pisp_be_rgb_enable`) -+ * @dirty_flags_extra: Extra dirty flags + */ +struct pisp_be_config { -+ /* I/O configuration: */ -+ struct pisp_be_input_buffer_config input_buffer; -+ struct pisp_be_tdn_input_buffer_config tdn_input_buffer; -+ struct pisp_be_stitch_input_buffer_config stitch_input_buffer; -+ struct pisp_be_tdn_output_buffer_config tdn_output_buffer; -+ struct pisp_be_stitch_output_buffer_config stitch_output_buffer; -+ struct pisp_be_output_buffer_config -+ output_buffer[PISP_BACK_END_NUM_OUTPUTS]; -+ struct pisp_be_hog_buffer_config hog_buffer; ++ /* For backward compatibility */ ++ uint8_t pad0[112]; + /* Processing configuration: */ + struct pisp_be_global_config global; + struct pisp_image_format_config input_format; @@ -257225,17 +266551,8 @@ index 000000000000..cbeb714f4d61 + output_format[PISP_BACK_END_NUM_OUTPUTS]; + struct pisp_be_hog_config hog; + struct pisp_be_axi_config axi; -+ /* Non-register fields: */ -+ struct pisp_be_lsc_extra lsc_extra; -+ struct pisp_be_cac_extra cac_extra; -+ struct pisp_be_downscale_extra -+ downscale_extra[PISP_BACK_END_NUM_OUTPUTS]; -+ struct pisp_be_resample_extra resample_extra[PISP_BACK_END_NUM_OUTPUTS]; -+ struct pisp_be_crop_config crop; -+ struct pisp_image_format_config hog_format; -+ __u32 dirty_flags_bayer; /* these use pisp_be_bayer_enable */ -+ __u32 dirty_flags_rgb; /* use pisp_be_rgb_enable */ -+ __u32 dirty_flags_extra; /* these use pisp_be_dirty_t */ ++ /* For backward compatibility */ ++ uint8_t pad1[84]; +} __attribute__((packed)); + +/** @@ -257576,6 +266893,18 @@ index 000000000000..cbdccfed1261 +} __attribute__((packed)); + +#endif /* _UAPI_PISP_COMMON_H_ */ +diff --git a/include/uapi/linux/serial_core.h b/include/uapi/linux/serial_core.h +index add349889d0a..6e0a49c65b11 100644 +--- a/include/uapi/linux/serial_core.h ++++ b/include/uapi/linux/serial_core.h +@@ -245,4 +245,7 @@ + /* Sunplus UART */ + #define PORT_SUNPLUS 123 + ++/* RPi firmware UART */ ++#define PORT_RPI_FW 124 ++ + #endif /* _UAPILINUX_SERIAL_CORE_H */ diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h index c3604a0a3e30..22bfc38787cc 100644 --- a/include/uapi/linux/v4l2-controls.h @@ -257700,11 +267029,252 @@ index 78260e5d9985..f8157c9c9da6 100644 /* priv field value to indicates that subsequent fields are valid. */ #define V4L2_PIX_FMT_PRIV_MAGIC 0xfeedcafe +diff --git a/include/uapi/misc/rp1_pio_if.h b/include/uapi/misc/rp1_pio_if.h +new file mode 100644 +index 000000000000..a9a54d3322ec +--- /dev/null ++++ b/include/uapi/misc/rp1_pio_if.h +@@ -0,0 +1,235 @@ ++/* SPDX-License-Identifier: GPL-2.0 + WITH Linux-syscall-note */ ++/* ++ * Copyright (c) 2023-24 Raspberry Pi Ltd. ++ * All rights reserved. ++ */ ++#ifndef _PIO_RP1_IF_H ++#define _PIO_RP1_IF_H ++ ++#include ++ ++#define RP1_PIO_INSTRUCTION_COUNT 32 ++#define RP1_PIO_SM_COUNT 4 ++#define RP1_PIO_GPIO_COUNT 28 ++#define RP1_GPIO_FUNC_PIO 7 ++ ++#define RP1_PIO_ORIGIN_ANY ((uint16_t)(~0)) ++ ++#define RP1_PIO_DIR_TO_SM 0 ++#define RP1_PIO_DIR_FROM_SM 1 ++#define RP1_PIO_DIR_COUNT 2 ++ ++typedef struct { ++ uint32_t clkdiv; ++ uint32_t execctrl; ++ uint32_t shiftctrl; ++ uint32_t pinctrl; ++} rp1_pio_sm_config; ++ ++struct rp1_pio_add_program_args { ++ uint16_t num_instrs; ++ uint16_t origin; ++ uint16_t instrs[RP1_PIO_INSTRUCTION_COUNT]; ++}; ++ ++struct rp1_pio_remove_program_args { ++ uint16_t num_instrs; ++ uint16_t origin; ++}; ++ ++struct rp1_pio_sm_claim_args { ++ uint16_t mask; ++}; ++ ++struct rp1_pio_sm_init_args { ++ uint16_t sm; ++ uint16_t initial_pc; ++ rp1_pio_sm_config config; ++}; ++ ++struct rp1_pio_sm_set_config_args { ++ uint16_t sm; ++ uint16_t rsvd; ++ rp1_pio_sm_config config; ++}; ++ ++struct rp1_pio_sm_exec_args { ++ uint16_t sm; ++ uint16_t instr; ++ uint8_t blocking; ++ uint8_t rsvd; ++}; ++ ++struct rp1_pio_sm_clear_fifos_args { ++ uint16_t sm; ++}; ++ ++struct rp1_pio_sm_set_clkdiv_args { ++ uint16_t sm; ++ uint16_t div_int; ++ uint8_t div_frac; ++ uint8_t rsvd; ++}; ++ ++struct rp1_pio_sm_set_pins_args { ++ uint16_t sm; ++ uint16_t rsvd; ++ uint32_t values; ++ uint32_t mask; ++}; ++ ++struct rp1_pio_sm_set_pindirs_args { ++ uint16_t sm; ++ uint16_t rsvd; ++ uint32_t dirs; ++ uint32_t mask; ++}; ++ ++struct rp1_pio_sm_set_enabled_args { ++ uint16_t mask; ++ uint8_t enable; ++ uint8_t rsvd; ++}; ++ ++struct rp1_pio_sm_restart_args { ++ uint16_t mask; ++}; ++ ++struct rp1_pio_sm_clkdiv_restart_args { ++ uint16_t mask; ++}; ++ ++struct rp1_pio_sm_enable_sync_args { ++ uint16_t mask; ++}; ++ ++struct rp1_pio_sm_put_args { ++ uint16_t sm; ++ uint8_t blocking; ++ uint8_t rsvd; ++ uint32_t data; ++}; ++ ++struct rp1_pio_sm_get_args { ++ uint16_t sm; ++ uint8_t blocking; ++ uint8_t rsvd; ++ uint32_t data; /* OUT */ ++}; ++ ++struct rp1_pio_sm_set_dmactrl_args { ++ uint16_t sm; ++ uint8_t is_tx; ++ uint8_t rsvd; ++ uint32_t ctrl; ++}; ++ ++struct rp1_pio_sm_fifo_state_args { ++ uint16_t sm; ++ uint8_t tx; ++ uint8_t rsvd; ++ uint16_t level; /* OUT */ ++ uint8_t empty; /* OUT */ ++ uint8_t full; /* OUT */ ++}; ++ ++struct rp1_gpio_init_args { ++ uint16_t gpio; ++}; ++ ++struct rp1_gpio_set_function_args { ++ uint16_t gpio; ++ uint16_t fn; ++}; ++ ++struct rp1_gpio_set_pulls_args { ++ uint16_t gpio; ++ uint8_t up; ++ uint8_t down; ++}; ++ ++struct rp1_gpio_set_args { ++ uint16_t gpio; ++ uint16_t value; ++}; ++ ++struct rp1_pio_sm_config_xfer_args { ++ uint16_t sm; ++ uint16_t dir; ++ uint16_t buf_size; ++ uint16_t buf_count; ++}; ++ ++struct rp1_pio_sm_config_xfer32_args { ++ uint16_t sm; ++ uint16_t dir; ++ uint32_t buf_size; ++ uint32_t buf_count; ++}; ++ ++struct rp1_pio_sm_xfer_data_args { ++ uint16_t sm; ++ uint16_t dir; ++ uint16_t data_bytes; ++ void *data; ++}; ++ ++struct rp1_pio_sm_xfer_data32_args { ++ uint16_t sm; ++ uint16_t dir; ++ uint32_t data_bytes; ++ void *data; ++}; ++ ++struct rp1_access_hw_args { ++ uint32_t addr; ++ uint32_t len; ++ void *data; ++}; ++ ++#define PIO_IOC_MAGIC 102 ++ ++#define PIO_IOC_SM_CONFIG_XFER _IOW(PIO_IOC_MAGIC, 0, struct rp1_pio_sm_config_xfer_args) ++#define PIO_IOC_SM_XFER_DATA _IOW(PIO_IOC_MAGIC, 1, struct rp1_pio_sm_xfer_data_args) ++#define PIO_IOC_SM_XFER_DATA32 _IOW(PIO_IOC_MAGIC, 2, struct rp1_pio_sm_xfer_data32_args) ++#define PIO_IOC_SM_CONFIG_XFER32 _IOW(PIO_IOC_MAGIC, 3, struct rp1_pio_sm_config_xfer32_args) ++ ++#define PIO_IOC_READ_HW _IOW(PIO_IOC_MAGIC, 8, struct rp1_access_hw_args) ++#define PIO_IOC_WRITE_HW _IOW(PIO_IOC_MAGIC, 9, struct rp1_access_hw_args) ++ ++#define PIO_IOC_CAN_ADD_PROGRAM _IOW(PIO_IOC_MAGIC, 10, struct rp1_pio_add_program_args) ++#define PIO_IOC_ADD_PROGRAM _IOW(PIO_IOC_MAGIC, 11, struct rp1_pio_add_program_args) ++#define PIO_IOC_REMOVE_PROGRAM _IOW(PIO_IOC_MAGIC, 12, struct rp1_pio_remove_program_args) ++#define PIO_IOC_CLEAR_INSTR_MEM _IO(PIO_IOC_MAGIC, 13) ++ ++#define PIO_IOC_SM_CLAIM _IOW(PIO_IOC_MAGIC, 20, struct rp1_pio_sm_claim_args) ++#define PIO_IOC_SM_UNCLAIM _IOW(PIO_IOC_MAGIC, 21, struct rp1_pio_sm_claim_args) ++#define PIO_IOC_SM_IS_CLAIMED _IOW(PIO_IOC_MAGIC, 22, struct rp1_pio_sm_claim_args) ++ ++#define PIO_IOC_SM_INIT _IOW(PIO_IOC_MAGIC, 30, struct rp1_pio_sm_init_args) ++#define PIO_IOC_SM_SET_CONFIG _IOW(PIO_IOC_MAGIC, 31, struct rp1_pio_sm_set_config_args) ++#define PIO_IOC_SM_EXEC _IOW(PIO_IOC_MAGIC, 32, struct rp1_pio_sm_exec_args) ++#define PIO_IOC_SM_CLEAR_FIFOS _IOW(PIO_IOC_MAGIC, 33, struct rp1_pio_sm_clear_fifos_args) ++#define PIO_IOC_SM_SET_CLKDIV _IOW(PIO_IOC_MAGIC, 34, struct rp1_pio_sm_set_clkdiv_args) ++#define PIO_IOC_SM_SET_PINS _IOW(PIO_IOC_MAGIC, 35, struct rp1_pio_sm_set_pins_args) ++#define PIO_IOC_SM_SET_PINDIRS _IOW(PIO_IOC_MAGIC, 36, struct rp1_pio_sm_set_pindirs_args) ++#define PIO_IOC_SM_SET_ENABLED _IOW(PIO_IOC_MAGIC, 37, struct rp1_pio_sm_set_enabled_args) ++#define PIO_IOC_SM_RESTART _IOW(PIO_IOC_MAGIC, 38, struct rp1_pio_sm_restart_args) ++#define PIO_IOC_SM_CLKDIV_RESTART _IOW(PIO_IOC_MAGIC, 39, struct rp1_pio_sm_restart_args) ++#define PIO_IOC_SM_ENABLE_SYNC _IOW(PIO_IOC_MAGIC, 40, struct rp1_pio_sm_enable_sync_args) ++#define PIO_IOC_SM_PUT _IOW(PIO_IOC_MAGIC, 41, struct rp1_pio_sm_put_args) ++#define PIO_IOC_SM_GET _IOWR(PIO_IOC_MAGIC, 42, struct rp1_pio_sm_get_args) ++#define PIO_IOC_SM_SET_DMACTRL _IOW(PIO_IOC_MAGIC, 43, struct rp1_pio_sm_set_dmactrl_args) ++#define PIO_IOC_SM_FIFO_STATE _IOW(PIO_IOC_MAGIC, 44, struct rp1_pio_sm_fifo_state_args) ++#define PIO_IOC_SM_DRAIN_TX _IOW(PIO_IOC_MAGIC, 45, struct rp1_pio_sm_clear_fifos_args) ++ ++#define PIO_IOC_GPIO_INIT _IOW(PIO_IOC_MAGIC, 50, struct rp1_gpio_init_args) ++#define PIO_IOC_GPIO_SET_FUNCTION _IOW(PIO_IOC_MAGIC, 51, struct rp1_gpio_set_function_args) ++#define PIO_IOC_GPIO_SET_PULLS _IOW(PIO_IOC_MAGIC, 52, struct rp1_gpio_set_pulls_args) ++#define PIO_IOC_GPIO_SET_OUTOVER _IOW(PIO_IOC_MAGIC, 53, struct rp1_gpio_set_args) ++#define PIO_IOC_GPIO_SET_INOVER _IOW(PIO_IOC_MAGIC, 54, struct rp1_gpio_set_args) ++#define PIO_IOC_GPIO_SET_OEOVER _IOW(PIO_IOC_MAGIC, 55, struct rp1_gpio_set_args) ++#define PIO_IOC_GPIO_SET_INPUT_ENABLED _IOW(PIO_IOC_MAGIC, 56, struct rp1_gpio_set_args) ++#define PIO_IOC_GPIO_SET_DRIVE_STRENGTH _IOW(PIO_IOC_MAGIC, 57, struct rp1_gpio_set_args) ++ ++#endif diff --git a/kernel/cgroup/cgroup.c b/kernel/cgroup/cgroup.c -index c26a9b3a3576..af05b0e86153 100644 +index b4b760fd7790..463a3b7e18d5 100644 --- a/kernel/cgroup/cgroup.c +++ b/kernel/cgroup/cgroup.c -@@ -6888,6 +6888,31 @@ static int __init cgroup_disable(char *str) +@@ -6891,6 +6891,39 @@ static int __init cgroup_disable(char *str) } __setup("cgroup_disable=", cgroup_disable); @@ -257723,11 +267293,19 @@ index c26a9b3a3576..af05b0e86153 100644 + strcmp(token, ss->legacy_name)) + continue; + -+ cgroup_feature_disable_mask &= ~(1 << i); + static_branch_enable(cgroup_subsys_enabled_key[i]); + pr_info("Enabling %s control group subsystem\n", + ss->name); + } ++ ++ for (i = 0; i < OPT_FEATURE_COUNT; i++) { ++ if (strcmp(token, cgroup_opt_feature_names[i])) ++ continue; ++ cgroup_feature_disable_mask &= ~(1 << i); ++ pr_info("Enabling %s control group feature\n", ++ cgroup_opt_feature_names[i]); ++ break; ++ } + } + return 1; +} @@ -257807,10 +267385,10 @@ index 61c92b9b7664..5715b4f84bd7 100644 + return cma_for_each_area(check_range, &range); +} diff --git a/mm/mempolicy.c b/mm/mempolicy.c -index f4dfeb5f052f..3ebb1b343284 100644 +index 219c098b3ffa..5f06bbb19129 100644 --- a/mm/mempolicy.c +++ b/mm/mempolicy.c -@@ -2977,7 +2977,9 @@ void __init numa_policy_init(void) +@@ -2979,7 +2979,9 @@ void __init numa_policy_init(void) /* Reset policy of current process to default */ void numa_default_policy(void) { @@ -257821,7 +267399,7 @@ index f4dfeb5f052f..3ebb1b343284 100644 } /* -@@ -2996,7 +2998,6 @@ static const char * const policy_modes[] = +@@ -2998,7 +3000,6 @@ static const char * const policy_modes[] = }; @@ -257829,7 +267407,7 @@ index f4dfeb5f052f..3ebb1b343284 100644 /** * mpol_parse_str - parse string to mempolicy, for tmpfs mpol mount option. * @str: string containing mempolicy to parse -@@ -3009,13 +3010,18 @@ static const char * const policy_modes[] = +@@ -3011,13 +3012,18 @@ static const char * const policy_modes[] = */ int mpol_parse_str(char *str, struct mempolicy **mpol) { @@ -257849,7 +267427,7 @@ index f4dfeb5f052f..3ebb1b343284 100644 if (flags) *flags++ = '\0'; /* terminate mode string */ -@@ -3094,9 +3100,16 @@ int mpol_parse_str(char *str, struct mempolicy **mpol) +@@ -3096,9 +3102,16 @@ int mpol_parse_str(char *str, struct mempolicy **mpol) goto out; } @@ -257869,7 +267447,7 @@ index f4dfeb5f052f..3ebb1b343284 100644 /* * Save nodes for mpol_to_str() to show the tmpfs mount options -@@ -3129,7 +3142,29 @@ int mpol_parse_str(char *str, struct mempolicy **mpol) +@@ -3131,7 +3144,29 @@ int mpol_parse_str(char *str, struct mempolicy **mpol) *mpol = new; return err; } @@ -257901,7 +267479,7 @@ index f4dfeb5f052f..3ebb1b343284 100644 /** * mpol_to_str - format a mempolicy structure for printing diff --git a/mm/page_alloc.c b/mm/page_alloc.c -index 86922efd7c3e..d6784fbd451e 100644 +index 09aea6306ec3..bbad3b168bb3 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -207,6 +207,27 @@ EXPORT_SYMBOL(node_states); @@ -257932,7 +267510,7 @@ index 86922efd7c3e..d6784fbd451e 100644 /* * A cached value of the page's pageblock's migratetype, used when the page is * put on a pcplist. Used to avoid the pageblock migratetype lookup when -@@ -2115,12 +2136,13 @@ __rmqueue(struct zone *zone, unsigned int order, int migratetype, +@@ -2130,12 +2151,13 @@ __rmqueue(struct zone *zone, unsigned int order, int migratetype, if (IS_ENABLED(CONFIG_CMA)) { /* * Balance movable allocations between regular and CMA areas by @@ -257950,10 +267528,10 @@ index 86922efd7c3e..d6784fbd451e 100644 if (page) return page; diff --git a/mm/vmscan.c b/mm/vmscan.c -index 8e605361b714..ff800f3a1b59 100644 +index 5a55788c5b95..7cd50a5f3753 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c -@@ -4764,7 +4764,7 @@ void lru_gen_look_around(struct page_vma_mapped_walk *pvmw) +@@ -4775,7 +4775,7 @@ void lru_gen_look_around(struct page_vma_mapped_walk *pvmw) if (!folio) continue; @@ -257963,10 +267541,10 @@ index 8e605361b714..ff800f3a1b59 100644 young++; diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c -index c553b637cda7..1500bf5fdb4f 100644 +index d6f40806ee51..220c5cff4fba 100644 --- a/net/bluetooth/hci_sync.c +++ b/net/bluetooth/hci_sync.c -@@ -4865,6 +4865,8 @@ static const struct { +@@ -4873,6 +4873,8 @@ static const struct { */ static int hci_dev_setup_sync(struct hci_dev *hdev) { @@ -257975,7 +267553,7 @@ index c553b637cda7..1500bf5fdb4f 100644 int ret = 0; bool invalid_bdaddr; size_t i; -@@ -4893,7 +4895,8 @@ static int hci_dev_setup_sync(struct hci_dev *hdev) +@@ -4901,7 +4903,8 @@ static int hci_dev_setup_sync(struct hci_dev *hdev) test_bit(HCI_QUIRK_USE_BDADDR_PROPERTY, &hdev->quirks); if (!ret) { if (test_bit(HCI_QUIRK_USE_BDADDR_PROPERTY, &hdev->quirks) && @@ -259484,7 +269062,7 @@ index fa3ad33a19df..38af1db2a98e 100644 .PHONY: $(PHONY) diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib -index 9443c7ca1183..0ad40aa42efe 100644 +index 81c5cd729692..b70f9a0ce128 100644 --- a/scripts/Makefile.lib +++ b/scripts/Makefile.lib @@ -356,6 +356,7 @@ DTC_FLAGS += -Wno-interrupt_provider \ @@ -259520,6 +269098,1164 @@ index 9443c7ca1183..0ad40aa42efe 100644 dtc-tmp = $(subst $(comma),_,$(dot-target).dts.tmp) # Bzip2 +diff --git a/sound/drivers/Kconfig b/sound/drivers/Kconfig +index 6debd8e95cb7..e2c4b6fd756f 100644 +--- a/sound/drivers/Kconfig ++++ b/sound/drivers/Kconfig +@@ -263,4 +263,14 @@ config SND_AC97_POWER_SAVE_DEFAULT + + See SND_AC97_POWER_SAVE for more details. + ++config SND_PIMIDI ++ tristate "Pimidi driver" ++ depends on SND_SEQUENCER && CRC8 ++ select SND_RAWMIDI ++ help ++ Say Y here to include support for Blokas Pimidi. ++ ++ To compile this driver as a module, choose M here: the module ++ will be called snd-pimidi. ++ + endif # SND_DRIVERS +diff --git a/sound/drivers/Makefile b/sound/drivers/Makefile +index 2c0c7092d396..ad2fa50a6758 100644 +--- a/sound/drivers/Makefile ++++ b/sound/drivers/Makefile +@@ -9,6 +9,7 @@ snd-aloop-objs := aloop.o + snd-mtpav-objs := mtpav.o + snd-mts64-objs := mts64.o + snd-pcmtest-objs := pcmtest.o ++snd-pimidi-objs := pimidi.o + snd-portman2x4-objs := portman2x4.o + snd-serial-u16550-objs := serial-u16550.o + snd-serial-generic-objs := serial-generic.o +@@ -23,6 +24,7 @@ obj-$(CONFIG_SND_SERIAL_U16550) += snd-serial-u16550.o + obj-$(CONFIG_SND_SERIAL_GENERIC) += snd-serial-generic.o + obj-$(CONFIG_SND_MTPAV) += snd-mtpav.o + obj-$(CONFIG_SND_MTS64) += snd-mts64.o ++obj-$(CONFIG_SND_PIMIDI) += snd-pimidi.o + obj-$(CONFIG_SND_PORTMAN2X4) += snd-portman2x4.o + + obj-$(CONFIG_SND) += opl3/ opl4/ mpu401/ vx/ pcsp/ +diff --git a/sound/drivers/pimidi.c b/sound/drivers/pimidi.c +new file mode 100644 +index 000000000000..1750778cf2e2 +--- /dev/null ++++ b/sound/drivers/pimidi.c +@@ -0,0 +1,1113 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++/* ++ * Pimidi Linux kernel module. ++ * Copyright (C) 2017-2024 Vilniaus Blokas UAB, https://blokas.io/ ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; version 2 of the ++ * License. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++ ++#define PIMIDI_LOG_IMPL(instance, log_func, msg, ...) log_func("pimidi(%s)[%c]: " msg "\n", \ ++ __func__, (instance) ? (instance)->d + '0' : 'G', ## __VA_ARGS__) ++ ++#ifdef PIMIDI_DEBUG ++# define printd(instance, ...) PIMIDI_LOG_IMPL(instance, pr_alert, __VA_ARGS__) ++# define printd_rl(instance, ...) PIMIDI_LOG_IMPL(instance, pr_alert_ratelimited, __VA_ARGS__) ++# define printd_g(...) printd((struct pimidi_instance *)NULL, __VA_ARGS__) ++#else ++# define printd(instance, ...) do {} while (0) ++# define printd_rl(instance, ...) do {} while (0) ++# define printd_g(...) do {} while (0) ++#endif ++ ++#define printe(instance, ...) PIMIDI_LOG_IMPL(instance, pr_err, __VA_ARGS__) ++#define printe_rl(instance, ...) PIMIDI_LOG_IMPL(instance, pr_err_ratelimited, __VA_ARGS__) ++#define printi(instance, ...) PIMIDI_LOG_IMPL(instance, pr_info, __VA_ARGS__) ++#define printw(instance, ...) PIMIDI_LOG_IMPL(instance, pr_warn, __VA_ARGS__) ++#define printw_rl(instance, ...) PIMIDI_LOG_IMPL(instance, pr_warn_ratelimited, __VA_ARGS__) ++ ++#define printe_g(...) printe((struct pimidi_instance *)NULL, __VA_ARGS__) ++#define printi_g(...) printi((struct pimidi_instance *)NULL, __VA_ARGS__) ++ ++DECLARE_CRC8_TABLE(pimidi_crc8_table); ++enum { PIMIDI_CRC8_POLYNOMIAL = 0x83 }; ++enum { PIMIDI_MAX_DEVICES = 4 }; ++enum { PIMIDI_MAX_PACKET_SIZE = 17 }; ++enum { PIMIDI_PORTS = 2 }; ++ ++struct pimidi_shared { ++ // lock protects the shared reset_gpio and devices list. ++ struct mutex lock; ++ struct gpio_desc *reset_gpio; ++ struct workqueue_struct *work_queue; ++ struct list_head devices; ++}; ++ ++static struct pimidi_shared pimidi_global = { ++ .devices = LIST_HEAD_INIT(pimidi_global.devices), ++}; ++ ++struct pimidi_version_t { ++ u8 hwrev; ++ u8 major; ++ u8 minor; ++ u8 build; ++}; ++ ++enum { PIMIDI_IN_FIFO_SIZE = 4096 }; ++ ++struct pimidi_midi_port { ++ // in_lock protects the input substream. ++ struct mutex in_lock; ++ // out_lock protects the output substream. ++ struct mutex out_lock; ++ DECLARE_KFIFO(in_fifo, uint8_t, PIMIDI_IN_FIFO_SIZE); ++ unsigned int last_output_at; ++ unsigned int output_buffer_used_in_millibytes; ++ struct work_struct in_handler; ++ struct delayed_work out_handler; ++ unsigned long enabled_streams; ++ unsigned int tx_cnt; ++ unsigned int rx_cnt; ++}; ++ ++struct pimidi_instance { ++ struct list_head list; ++ struct i2c_client *i2c_client; ++ struct pimidi_version_t version; ++ char serial[11]; ++ char d; ++ struct gpio_desc *data_ready_gpio; ++ ++ struct work_struct drdy_handler; ++ ++ // comm_lock serializes I2C communication. ++ struct mutex comm_lock; ++ char *rx_buf; ++ size_t rx_len; ++ int rx_status; ++ struct completion *rx_completion; ++ ++ struct snd_rawmidi *rawmidi; ++ struct pimidi_midi_port midi_port[PIMIDI_PORTS]; ++ bool stopping; ++}; ++ ++static struct snd_rawmidi_substream *pimidi_find_substream(struct snd_rawmidi *rawmidi, ++ int stream, ++ int number ++ ) ++{ ++ struct snd_rawmidi_substream *substream; ++ ++ list_for_each_entry(substream, &rawmidi->streams[stream].substreams, list) { ++ if (substream->number == number) ++ return substream; ++ } ++ return NULL; ++} ++ ++static void pimidi_midi_in_handler(struct pimidi_instance *instance, int port) ++{ ++ int i, n, err; ++ ++ printd(instance, "(%d)", port); ++ ++ struct pimidi_midi_port *midi_port = &instance->midi_port[port]; ++ ++ if (!test_bit(SNDRV_RAWMIDI_STREAM_INPUT, &midi_port->enabled_streams)) { ++ printd(instance, "Input not enabled for %d", port); ++ return; ++ } ++ ++ u8 data[512]; ++ ++ n = kfifo_out_peek(&midi_port->in_fifo, data, sizeof(data)); ++ printd(instance, "Peeked %d MIDI bytes", n); ++ ++ mutex_lock(&midi_port->in_lock); ++ struct snd_rawmidi_substream *substream = ++ pimidi_find_substream(instance->rawmidi, ++ SNDRV_RAWMIDI_STREAM_INPUT, ++ port); ++ ++ err = snd_rawmidi_receive(substream, data, n); ++ if (err > 0) ++ midi_port->rx_cnt += err; ++ mutex_unlock(&midi_port->in_lock); ++ ++ for (i = 0; i < err; ++i) ++ kfifo_skip(&midi_port->in_fifo); ++ ++ if (n != err) ++ printw_rl(instance, ++ "Not all MIDI data consumed for port %d: %d / %d", port, err, n); ++ ++ if (!kfifo_is_empty(&midi_port->in_fifo) && !instance->stopping) ++ queue_work(pimidi_global.work_queue, &midi_port->in_handler); ++ ++ printd(instance, "Done"); ++} ++ ++static void pimidi_midi_in_handler_0(struct work_struct *work) ++{ ++ pimidi_midi_in_handler(container_of(work, struct pimidi_instance, midi_port[0].in_handler), ++ 0); ++} ++ ++static void pimidi_midi_in_handler_1(struct work_struct *work) ++{ ++ pimidi_midi_in_handler(container_of(work, struct pimidi_instance, midi_port[1].in_handler), ++ 1); ++} ++ ++static void pimidi_midi_out_handler(struct pimidi_instance *instance, int port) ++{ ++ printd(instance, "(%d)", port); ++ if (!test_bit(SNDRV_RAWMIDI_STREAM_OUTPUT, &instance->midi_port[port].enabled_streams)) { ++ printd(instance, "Output not enabled for %d", port); ++ return; ++ } ++ ++ struct pimidi_midi_port *midi_port = &instance->midi_port[port]; ++ ++ struct snd_rawmidi_substream *substream = ++ pimidi_find_substream(instance->rawmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, port); ++ ++ mutex_lock(&midi_port->out_lock); ++ ++ enum { MIDI_MILLI_BYTES_PER_JIFFY = 3125000 / HZ }; ++ enum { MIDI_MAX_OUTPUT_BUFFER_SIZE_IN_MILLIBYTES = ++ (512 - PIMIDI_MAX_PACKET_SIZE - 1) * 1000 }; ++ ++ unsigned int now = jiffies; ++ unsigned int millibytes_became_available = ++ (MIDI_MILLI_BYTES_PER_JIFFY) * (now - midi_port->last_output_at); ++ ++ midi_port->output_buffer_used_in_millibytes = ++ midi_port->output_buffer_used_in_millibytes <= ++ millibytes_became_available ? 0 : midi_port->output_buffer_used_in_millibytes - ++ millibytes_became_available; ++ ++ unsigned int output_buffer_available = ++ (MIDI_MAX_OUTPUT_BUFFER_SIZE_IN_MILLIBYTES ++ - midi_port->output_buffer_used_in_millibytes) ++ / 1000; ++ ++ u8 buffer[PIMIDI_MAX_PACKET_SIZE]; ++ int n, batch, err; ++ ++ for (batch = 0; batch < 3; ++batch) { ++ if (output_buffer_available == 0) ++ printd(instance, "Buffer full"); ++ ++ printd(instance, "Buffer available: %u (%u +%u, %u -> %u, dt %u) (%u) @ %u", ++ output_buffer_available, midi_port->output_buffer_used_in_millibytes, ++ millibytes_became_available, midi_port->last_output_at, now, ++ now - midi_port->last_output_at, midi_port->tx_cnt, HZ); ++ midi_port->last_output_at = now; ++ ++ n = output_buffer_available ++ ? snd_rawmidi_transmit_peek(substream, buffer + 1, ++ min(output_buffer_available, ++ sizeof(buffer) - 2)) ++ : 0; ++ if (n > 0) { ++ printd(instance, "Peeked: %d", n); ++ snd_rawmidi_transmit_ack(substream, n); ++ ++ buffer[0] = (port << 4) | n; ++ buffer[n + 1] = ~crc8(pimidi_crc8_table, buffer, n + 1, CRC8_INIT_VALUE); ++ ++#ifdef PIMIDI_DEBUG ++ pr_debug("%s[%d]: Sending %d bytes:", __func__, instance->d, n + 2); ++ int i; ++ ++ for (i = 0; i < n + 2; ++i) ++ pr_cont(" %02x", buffer[i]); ++ ++ pr_cont("\n"); ++#endif ++ mutex_lock(&instance->comm_lock); ++ err = i2c_master_send(instance->i2c_client, buffer, n + 2); ++ mutex_unlock(&instance->comm_lock); ++ ++ if (err < 0) { ++ printe(instance, ++ "Error occurred when sending MIDI data over I2C! (%d)", ++ err); ++ goto cleanup; ++ } ++ ++ midi_port->tx_cnt += n; ++ midi_port->output_buffer_used_in_millibytes += n * 1000; ++ output_buffer_available -= n; ++ } else if (n < 0) { ++ err = n; ++ printe(instance, "snd_rawmidi_transmit_peek returned error %d!", err); ++ goto cleanup; ++ } else { ++ break; ++ } ++ } ++ ++ printd(instance, "Checking if empty %p", substream); ++ if (!snd_rawmidi_transmit_empty(substream) && !instance->stopping) { ++ unsigned int delay = 1; ++ ++ if (output_buffer_available == 0) ++ delay = 125000 / MIDI_MILLI_BYTES_PER_JIFFY; ++ printd(instance, "Queue more work after %u jiffies", delay); ++ mod_delayed_work(pimidi_global.work_queue, &midi_port->out_handler, delay); ++ } ++ ++cleanup: ++ mutex_unlock(&midi_port->out_lock); ++ printd(instance, "Done"); ++} ++ ++static void pimidi_midi_out_handler_0(struct work_struct *work) ++{ ++ pimidi_midi_out_handler(container_of(work, struct pimidi_instance, ++ midi_port[0].out_handler.work), 0); ++} ++ ++static void pimidi_midi_out_handler_1(struct work_struct *work) ++{ ++ pimidi_midi_out_handler(container_of(work, struct pimidi_instance, ++ midi_port[1].out_handler.work), 1); ++} ++ ++static void pimidi_midi_output_trigger(struct snd_rawmidi_substream *substream, int up) ++{ ++ struct pimidi_instance *instance = substream->rmidi->private_data; ++ ++ printd(instance, "(%d, %d, %d)", substream->stream, substream->number, up); ++ ++ if (up == 0) { ++ clear_bit(substream->stream, ++ &instance->midi_port[substream->number].enabled_streams); ++ } else { ++ set_bit(substream->stream, ++ &instance->midi_port[substream->number].enabled_streams); ++ if (!delayed_work_pending(&instance->midi_port[substream->number].out_handler)) { ++ printd(instance, "Queueing work"); ++ queue_delayed_work(pimidi_global.work_queue, ++ &instance->midi_port[substream->number].out_handler, 0); ++ } ++ } ++} ++ ++static void pimidi_midi_output_drain(struct snd_rawmidi_substream *substream) ++{ ++ struct pimidi_instance *instance = substream->rmidi->private_data; ++ ++ printd(instance, "(%d, %d)", substream->stream, substream->number); ++ ++ printd(instance, "Begin draining!"); ++ ++ queue_delayed_work(pimidi_global.work_queue, ++ &instance->midi_port[substream->number].out_handler, 0); ++ ++ unsigned long deadline = jiffies + 5 * HZ; ++ ++ do { ++ printd(instance, "Before flush"); ++ while (delayed_work_pending(&instance->midi_port[substream->number].out_handler)) ++ flush_delayed_work(&instance->midi_port[substream->number].out_handler); ++ printd(instance, "Flushed"); ++ } while (!snd_rawmidi_transmit_empty(substream) && time_before(jiffies, deadline)); ++ ++ printd(instance, "Done!"); ++} ++ ++static int pimidi_midi_output_close(struct snd_rawmidi_substream *substream) ++{ ++ struct pimidi_instance *instance = substream->rmidi->private_data; ++ struct pimidi_midi_port *midi_port = &instance->midi_port[substream->number]; ++ ++ mutex_lock(&midi_port->out_lock); ++ clear_bit(substream->stream, &midi_port->enabled_streams); ++ mutex_unlock(&midi_port->out_lock); ++ return 0; ++} ++ ++static int pimidi_midi_input_close(struct snd_rawmidi_substream *substream) ++{ ++ struct pimidi_instance *instance = substream->rmidi->private_data; ++ struct pimidi_midi_port *midi_port = &instance->midi_port[substream->number]; ++ ++ mutex_lock(&midi_port->in_lock); ++ clear_bit(substream->stream, &midi_port->enabled_streams); ++ mutex_unlock(&midi_port->in_lock); ++ return 0; ++} ++ ++static void pimidi_midi_input_trigger(struct snd_rawmidi_substream *substream, int up) ++{ ++ struct pimidi_instance *instance = substream->rmidi->private_data; ++ ++ printd(instance, "(%d, %d, %d)", substream->stream, substream->number, up); ++ ++ if (up == 0) { ++ clear_bit(substream->stream, ++ &instance->midi_port[substream->number].enabled_streams); ++ cancel_work_sync(&instance->midi_port[substream->number].in_handler); ++ } else { ++ set_bit(substream->stream, ++ &instance->midi_port[substream->number].enabled_streams); ++ if (!instance->stopping) ++ queue_work(pimidi_global.work_queue, ++ &instance->midi_port[substream->number].in_handler); ++ } ++} ++ ++static void pimidi_get_port_info(struct snd_rawmidi *rmidi, int number, ++ struct snd_seq_port_info *seq_port_info) ++{ ++ printd_g("%p, %d, %p", rmidi, number, seq_port_info); ++ seq_port_info->type = ++ SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC | ++ SNDRV_SEQ_PORT_TYPE_HARDWARE | ++ SNDRV_SEQ_PORT_TYPE_PORT; ++ strscpy(seq_port_info->name, number == 0 ? "a" : "b", ++ sizeof(seq_port_info->name)); ++ seq_port_info->midi_voices = 0; ++} ++ ++static const struct snd_rawmidi_global_ops pimidi_midi_ops = { ++ .get_port_info = pimidi_get_port_info, ++}; ++ ++static int pimidi_midi_open(struct snd_rawmidi_substream *substream) ++{ ++ printd_g("(%p) stream=%d number=%d", substream, substream->stream, substream->number); ++ return 0; ++} ++ ++static const struct snd_rawmidi_ops pimidi_midi_output_ops = { ++ .open = pimidi_midi_open, ++ .close = pimidi_midi_output_close, ++ .trigger = pimidi_midi_output_trigger, ++ .drain = pimidi_midi_output_drain, ++}; ++ ++static const struct snd_rawmidi_ops pimidi_midi_input_ops = { ++ .open = pimidi_midi_open, ++ .close = pimidi_midi_input_close, ++ .trigger = pimidi_midi_input_trigger, ++}; ++ ++static int pimidi_register(struct pimidi_instance *instance) ++{ ++ int err = 0; ++ ++ mutex_lock(&pimidi_global.lock); ++ printd(instance, "Registering..."); ++ if (!pimidi_global.reset_gpio) { ++ printd_g("Getting reset pin."); ++ pimidi_global.reset_gpio = gpiod_get(&instance->i2c_client->dev, "reset", ++ GPIOD_OUT_LOW); ++ if (IS_ERR(pimidi_global.reset_gpio)) { ++ err = PTR_ERR(pimidi_global.reset_gpio); ++ printe_g("gpiod_get failed: %d", err); ++ pimidi_global.reset_gpio = NULL; ++ mutex_unlock(&pimidi_global.lock); ++ return err; ++ } ++ } ++ list_add_tail(&instance->list, &pimidi_global.devices); ++ mutex_unlock(&pimidi_global.lock); ++ return err; ++} ++ ++static void pimidi_unregister(struct pimidi_instance *instance) ++{ ++ mutex_lock(&pimidi_global.lock); ++ printd(instance, "Unregistering..."); ++ list_del(&instance->list); ++ if (list_empty(&pimidi_global.devices)) { ++ printd_g("Releasing reset pin"); ++ gpiod_put(pimidi_global.reset_gpio); ++ pimidi_global.reset_gpio = NULL; ++ } ++ mutex_unlock(&pimidi_global.lock); ++} ++ ++static void pimidi_perform_reset(void) ++{ ++ mutex_lock(&pimidi_global.lock); ++ ++ printd_g("Performing reset."); ++ ++ struct list_head *p; ++ ++ list_for_each(p, &pimidi_global.devices) { ++ struct pimidi_instance *instance = list_entry(p, struct pimidi_instance, list); ++ ++ printd(instance, "Pausing..."); ++ instance->stopping = true; ++ disable_irq(instance->i2c_client->irq); ++ cancel_work(&instance->drdy_handler); ++ ++ int i; ++ ++ for (i = 0; i < PIMIDI_PORTS; ++i) { ++ cancel_work(&instance->midi_port[i].in_handler); ++ cancel_delayed_work(&instance->midi_port[i].out_handler); ++ } ++ ++ drain_workqueue(pimidi_global.work_queue); ++ } ++ ++ printd_g("Reset = low"); ++ gpiod_set_value(pimidi_global.reset_gpio, 1); ++ ++ list_for_each(p, &pimidi_global.devices) { ++ struct pimidi_instance *instance = list_entry(p, struct pimidi_instance, list); ++ ++ if (gpiod_is_active_low(instance->data_ready_gpio)) ++ gpiod_toggle_active_low(instance->data_ready_gpio); ++ gpiod_direction_output(instance->data_ready_gpio, 1); ++ printd(instance, "DRDY high"); ++ } ++ ++ usleep_range(1000, 5000); ++ printd_g("Reset = high"); ++ gpiod_set_value(pimidi_global.reset_gpio, 0); ++ msleep(30); ++ ++ int i; ++ ++ for (i = 0; i < PIMIDI_MAX_DEVICES; ++i) { ++ usleep_range(1000, 3000); ++ list_for_each(p, &pimidi_global.devices) { ++ struct pimidi_instance *instance = list_entry(p, struct pimidi_instance, ++ list); ++ ++ if (instance->d < i) ++ continue; ++ printd(instance, "DRDY -> %d", !gpiod_get_value(instance->data_ready_gpio)); ++ gpiod_set_value(instance->data_ready_gpio, ++ !gpiod_get_value(instance->data_ready_gpio)); ++ } ++ } ++ usleep_range(16000, 20000); ++ ++ list_for_each(p, &pimidi_global.devices) { ++ struct pimidi_instance *instance = list_entry(p, struct pimidi_instance, list); ++ ++ if (!gpiod_is_active_low(instance->data_ready_gpio)) ++ gpiod_toggle_active_low(instance->data_ready_gpio); ++ ++ printd(instance, "DRDY input"); ++ gpiod_direction_input(instance->data_ready_gpio); ++ ++ printd(instance, "Resume..."); ++ instance->stopping = false; ++ enable_irq(instance->i2c_client->irq); ++ } ++ ++ printd_g("Reset done."); ++ usleep_range(16000, 20000); ++ ++ mutex_unlock(&pimidi_global.lock); ++} ++ ++static int pimidi_read_version(struct pimidi_version_t *version, struct pimidi_instance *instance) ++{ ++ memset(version, 0, sizeof(*version)); ++ ++ const char cmd[4] = { 0xb2, 0x01, 0x01, 0x95 }; ++ ++ char result[9]; ++ ++ memset(result, 0, sizeof(result)); ++ ++ DECLARE_COMPLETION_ONSTACK(done); ++ ++ mutex_lock(&instance->comm_lock); ++ int err = i2c_master_send(instance->i2c_client, cmd, sizeof(cmd)); ++ ++ if (err < 0) { ++ mutex_unlock(&instance->comm_lock); ++ return err; ++ } ++ instance->rx_buf = result; ++ instance->rx_len = sizeof(result); ++ instance->rx_completion = &done; ++ mutex_unlock(&instance->comm_lock); ++ ++ printd(instance, "Waiting for drdy"); ++ wait_for_completion_io_timeout(&done, msecs_to_jiffies(1000u)); ++ printd(instance, "Done waiting"); ++ ++ if (!completion_done(&done)) { ++ mutex_lock(&instance->comm_lock); ++ instance->rx_buf = NULL; ++ instance->rx_len = 0; ++ instance->rx_status = -ETIMEDOUT; ++ instance->rx_completion = NULL; ++ mutex_unlock(&instance->comm_lock); ++ return -ETIMEDOUT; ++ } ++ ++ if (CRC8_GOOD_VALUE(pimidi_crc8_table) != crc8(pimidi_crc8_table, result, sizeof(result), ++ CRC8_INIT_VALUE)) ++ return -EIO; ++ ++ const char expected[4] = { 0xb7, 0x81, 0x01, 0x00 }; ++ ++ if (memcmp(result, expected, sizeof(expected)) != 0) ++ return -EPROTO; ++ ++ u32 v = ntohl(*(uint32_t *)(result + 4)); ++ ++ version->hwrev = v >> 24; ++ version->major = (v & 0x00ff0000) >> 16; ++ version->minor = (v & 0x0000ff00) >> 8; ++ version->build = v & 0x000000ff; ++ ++ return 0; ++} ++ ++static int pimidi_read_serial(char serial[11], struct pimidi_instance *instance) ++{ ++ memset(serial, 0, sizeof(char[11])); ++ ++ const char cmd[4] = { 0xb2, 0x03, 0x04, 0x97 }; ++ ++ char result[PIMIDI_MAX_PACKET_SIZE]; ++ ++ memset(result, 0, sizeof(result)); ++ ++ DECLARE_COMPLETION_ONSTACK(done); ++ ++ mutex_lock(&instance->comm_lock); ++ int err = i2c_master_send(instance->i2c_client, cmd, sizeof(cmd)); ++ ++ if (err < 0) { ++ mutex_unlock(&instance->comm_lock); ++ return err; ++ } ++ instance->rx_buf = result; ++ instance->rx_len = sizeof(result); ++ instance->rx_completion = &done; ++ mutex_unlock(&instance->comm_lock); ++ ++ printd(instance, "Waiting for drdy"); ++ wait_for_completion_io_timeout(&done, msecs_to_jiffies(1000u)); ++ printd(instance, "Done waiting"); ++ ++ if (!completion_done(&done)) { ++ mutex_lock(&instance->comm_lock); ++ instance->rx_buf = NULL; ++ instance->rx_len = 0; ++ instance->rx_status = -ETIMEDOUT; ++ instance->rx_completion = NULL; ++ mutex_unlock(&instance->comm_lock); ++ printe(instance, "Timed out"); ++ return -ETIMEDOUT; ++ } ++ ++ if (CRC8_GOOD_VALUE(pimidi_crc8_table) != crc8(pimidi_crc8_table, result, ++ (result[0] & 0x0f) + 2, CRC8_INIT_VALUE)) ++ return -EIO; ++ ++ const char expected[4] = { 0xbd, 0x83, 0x04, 0x0a }; ++ ++ if (memcmp(result, expected, sizeof(expected)) != 0) { ++ printe(instance, "Unexpected response: %02x %02x %02x %02x", result[0], result[1], ++ result[2], result[3]); ++ return -EPROTO; ++ } ++ ++ memcpy(serial, result + 4, 10); ++ ++ if (strspn(serial, "\xff") == 10) ++ strscpy(serial, "(unset)", 8); ++ ++ return 0; ++} ++ ++static void pimidi_handle_midi_data(struct pimidi_instance *instance, int port, const uint8_t *data, ++ unsigned int n) ++{ ++ printd(instance, "Handling MIDI data for port %d (%u bytes)", port, n); ++ if (n == 0) ++ return; ++ ++ struct pimidi_midi_port *midi_port = &instance->midi_port[port]; ++ ++ kfifo_in(&midi_port->in_fifo, data, n); ++ ++ if (!instance->stopping) ++ queue_work(pimidi_global.work_queue, &midi_port->in_handler); ++ ++ printd(instance, "Done"); ++} ++ ++static void pimidi_drdy_continue(struct pimidi_instance *instance) ++{ ++ if (instance->stopping) { ++ printd(instance, "Refusing to queue work / enable IRQ due to stopping."); ++ return; ++ } ++ ++ if (gpiod_get_value(instance->data_ready_gpio)) { ++ printd_rl(instance, "Queue work due to DRDY line still low"); ++ queue_work(pimidi_global.work_queue, &instance->drdy_handler); ++ } else { ++ printd_rl(instance, "Enabling irq for more data"); ++ enable_irq(gpiod_to_irq(instance->data_ready_gpio)); ++ } ++} ++ ++static void pimidi_drdy_handler(struct work_struct *work) ++{ ++ struct pimidi_instance *instance = container_of(work, struct pimidi_instance, drdy_handler); ++ ++ printd(instance, "(%p)", work); ++ ++ mutex_lock(&instance->comm_lock); ++ if (!instance->rx_completion) { ++ u8 data[PIMIDI_MAX_PACKET_SIZE]; ++ int n = i2c_master_recv(instance->i2c_client, data, 3); ++ ++ if (n < 0) { ++ printe(instance, "Error reading from device: %d", n); ++ mutex_unlock(&instance->comm_lock); ++ pimidi_drdy_continue(instance); ++ return; ++ } ++ ++ if (data[0] == 0xfe) { ++ printe_rl(instance, "Invalid packet 0x%02x 0x%02x 0x%02x", data[0], data[1], ++ data[2]); ++ mutex_unlock(&instance->comm_lock); ++ pimidi_drdy_continue(instance); ++ return; ++ } ++ ++ int len = (data[0] & 0x0f) + 2; ++ ++ if (len > n) { ++ printd(instance, "Need %d more bytes", len - n); ++ int err = i2c_master_recv(instance->i2c_client, data + n, len - n); ++ ++ if (err < 0) { ++ printe(instance, "Error reading remainder from device: %d", err); ++ mutex_unlock(&instance->comm_lock); ++ pimidi_drdy_continue(instance); ++ return; ++#ifdef PIMIDI_DEBUG ++ } else { ++ pr_debug("Recv_2:"); ++ int i; ++ ++ for (i = n; i < len; ++i) ++ pr_cont(" %02x", data[i]); ++ pr_cont("\n"); ++#endif ++ } ++ } ++ ++ if (CRC8_GOOD_VALUE(pimidi_crc8_table) == crc8(pimidi_crc8_table, data, len, ++ CRC8_INIT_VALUE)) { ++ switch (data[0] & 0xf0) { ++ case 0x00: ++ pimidi_handle_midi_data(instance, 0, data + 1, len - 2); ++ break; ++ case 0x10: ++ pimidi_handle_midi_data(instance, 1, data + 1, len - 2); ++ break; ++ default: ++ printd(instance, "Unhandled command %02x", data[0]); ++ break; ++ } ++ } else { ++ printe(instance, "I2C rx corruption detected."); ++ pr_info("Packet [%d]:", len); ++ int i; ++ ++ for (i = 0; i < len; ++i) ++ pr_cont(" %02x", data[i]); ++ pr_cont("\n"); ++ } ++ ++ mutex_unlock(&instance->comm_lock); ++ } else { ++ printd(instance, "Completing drdy"); ++ instance->rx_status = i2c_master_recv(instance->i2c_client, instance->rx_buf, 3); ++ printd(instance, "Recv_1 %02x %02x %02x", instance->rx_buf[0], instance->rx_buf[1], ++ instance->rx_buf[2]); ++ if (instance->rx_len > 3 && instance->rx_status == 3) { ++ instance->rx_status = i2c_master_recv(instance->i2c_client, ++ instance->rx_buf + 3, ++ instance->rx_len - 3); ++ if (instance->rx_status >= 0) ++ instance->rx_status += 3; ++#ifdef PIMIDI_DEBUG ++ pr_debug("Recv_2:"); ++ int i; ++ ++ for (i = 3; i < instance->rx_len; ++i) ++ pr_cont(" %02x", instance->rx_buf[i]); ++ pr_cont("\n"); ++#endif ++ } ++ struct completion *done = instance->rx_completion; ++ ++ instance->rx_buf = NULL; ++ instance->rx_len = 0; ++ instance->rx_completion = NULL; ++ complete_all(done); ++ mutex_unlock(&instance->comm_lock); ++ } ++ ++ pimidi_drdy_continue(instance); ++} ++ ++static irqreturn_t pimidi_drdy_interrupt_handler(int irq, void *dev_id) ++{ ++ struct pimidi_instance *instance = (struct pimidi_instance *)dev_id; ++ ++ if (instance->stopping) { ++ printd(instance, "DRDY interrupt, but stopping, ignoring..."); ++ return IRQ_HANDLED; ++ } ++ ++ printd(instance, "DRDY interrupt, masking"); ++ disable_irq_nosync(irq); ++ ++ printd(instance, "Queue work due to DRDY interrupt"); ++ queue_work(pimidi_global.work_queue, &instance->drdy_handler); ++ ++ return IRQ_HANDLED; ++} ++ ++static void pimidi_proc_stat_show(struct snd_info_entry *entry, struct snd_info_buffer *buffer) ++{ ++ const unsigned int *d = entry->private_data; ++ ++ snd_iprintf(buffer, "%u\n", *d); ++} ++ ++static void pimidi_proc_serial_show(struct snd_info_entry *entry, struct snd_info_buffer *buffer) ++{ ++ struct pimidi_instance *instance = entry->private_data; ++ ++ snd_iprintf(buffer, "%s\n", instance->serial); ++} ++ ++static void pimidi_proc_version_show(struct snd_info_entry *entry, struct snd_info_buffer *buffer) ++{ ++ struct pimidi_instance *instance = entry->private_data; ++ ++ snd_iprintf(buffer, "%u.%u.%u\n", instance->version.major, instance->version.minor, ++ instance->version.build); ++} ++ ++static void pimidi_proc_hwrev_show(struct snd_info_entry *entry, struct snd_info_buffer *buffer) ++{ ++ struct pimidi_instance *instance = entry->private_data; ++ ++ snd_iprintf(buffer, "%u\n", instance->version.hwrev); ++} ++ ++static int pimidi_i2c_probe(struct i2c_client *client) ++{ ++ struct snd_card *card = NULL; ++ int err, d, i; ++ ++ d = client->addr - 0x20; ++ ++ if (d < 0 || d >= 8) { ++ printe_g("Unexpected device address: %d", client->addr); ++ err = -EINVAL; ++ goto finalize; ++ } ++ ++ err = snd_card_new(&client->dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1, THIS_MODULE, ++ sizeof(struct pimidi_instance), &card); ++ ++ if (err) { ++ printe_g("snd_card_new failed: %d", err); ++ return err; ++ } ++ ++ struct pimidi_instance *instance = (struct pimidi_instance *)card->private_data; ++ ++ instance->i2c_client = client; ++ instance->d = d; ++ ++ struct snd_rawmidi *rawmidi; ++ ++ err = snd_rawmidi_new(card, card->shortname, 0, 2, 2, &rawmidi); ++ if (err < 0) { ++ printe(instance, "snd_rawmidi_new failed: %d", err); ++ goto finalize; ++ } ++ ++ instance->rawmidi = rawmidi; ++ strscpy(rawmidi->name, "pimidi", sizeof(rawmidi->name)); ++ ++ rawmidi->info_flags = ++ SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_DUPLEX; ++ rawmidi->private_data = instance; ++ rawmidi->ops = &pimidi_midi_ops; ++ ++ snd_rawmidi_set_ops(rawmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &pimidi_midi_output_ops); ++ snd_rawmidi_set_ops(rawmidi, SNDRV_RAWMIDI_STREAM_INPUT, &pimidi_midi_input_ops); ++ ++ instance->data_ready_gpio = devm_gpiod_get(&client->dev, "data-ready", GPIOD_OUT_HIGH); ++ if (IS_ERR(instance->data_ready_gpio)) { ++ err = PTR_ERR(instance->data_ready_gpio); ++ printe(instance, "devm_gpiod_get failed: %d", err); ++ goto finalize; ++ } ++ ++ err = pimidi_register(instance); ++ if (err < 0) { ++ printe(instance, "pimidi_register failed: %d", err); ++ goto finalize; ++ } ++ ++ pimidi_perform_reset(); ++ ++ INIT_WORK(&instance->drdy_handler, pimidi_drdy_handler); ++ mutex_init(&instance->comm_lock); ++ ++ err = devm_request_irq(&client->dev, client->irq, pimidi_drdy_interrupt_handler, ++ IRQF_SHARED | IRQF_TRIGGER_LOW, "data_ready_int", instance); ++ ++ if (err != 0) { ++ printe(instance, "data_available IRQ request failed! %d", err); ++ goto finalize; ++ } ++ ++ err = pimidi_read_version(&instance->version, instance); ++ if (err < 0) { ++ printe(instance, "pimidi_read_version failed: %d", err); ++ goto finalize; ++ } ++ ++ err = pimidi_read_serial(instance->serial, instance); ++ if (err < 0) { ++ printe(instance, "pimidi_read_serial failed: %d", err); ++ goto finalize; ++ } else if (instance->serial[0] != 'P' || instance->serial[1] != 'M' || ++ strlen(instance->serial) != 10) { ++ printe(instance, "Unexpected serial number: %s", instance->serial); ++ err = -EIO; ++ goto finalize; ++ } ++ ++ printi(instance, "pimidi%d hw:%d version %u.%u.%u-%u, serial %s", ++ d, ++ card->number, ++ instance->version.major, ++ instance->version.minor, ++ instance->version.build, ++ instance->version.hwrev, ++ instance->serial ++ ); ++ ++ strscpy(card->driver, "snd-pimidi", sizeof(card->driver)); ++ snprintf(card->shortname, sizeof(card->shortname), "pimidi%d", d); ++ snprintf(card->longname, sizeof(card->longname), "pimidi%d %s", d, instance->serial); ++ snprintf(card->id, sizeof(card->id), "pimidi%d", d); ++ ++ snprintf(pimidi_find_substream(rawmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, 0)->name, ++ 10u, "pimidi%d-a", d); ++ snprintf(pimidi_find_substream(rawmidi, SNDRV_RAWMIDI_STREAM_INPUT, 0)->name, ++ 10u, "pimidi%d-a", d); ++ snprintf(pimidi_find_substream(rawmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, 1)->name, ++ 10u, "pimidi%d-b", d); ++ snprintf(pimidi_find_substream(rawmidi, SNDRV_RAWMIDI_STREAM_INPUT, 1)->name, ++ 10u, "pimidi%d-b", d); ++ ++ err = snd_card_ro_proc_new(card, "a-tx", &instance->midi_port[0].tx_cnt, ++ pimidi_proc_stat_show); ++ err = snd_card_ro_proc_new(card, "a-rx", &instance->midi_port[0].rx_cnt, ++ pimidi_proc_stat_show); ++ err = snd_card_ro_proc_new(card, "b-tx", &instance->midi_port[1].tx_cnt, ++ pimidi_proc_stat_show); ++ err = snd_card_ro_proc_new(card, "b-rx", &instance->midi_port[1].rx_cnt, ++ pimidi_proc_stat_show); ++ err = snd_card_ro_proc_new(card, "serial", instance, pimidi_proc_serial_show); ++ err = snd_card_ro_proc_new(card, "version", instance, pimidi_proc_version_show); ++ err = snd_card_ro_proc_new(card, "hwrev", instance, pimidi_proc_hwrev_show); ++ if (err < 0) { ++ printe(instance, "snd_card_ro_proc_new failed: %d", err); ++ goto finalize; ++ } ++ ++ err = snd_card_register(card); ++ if (err < 0) { ++ printe(instance, "snd_card_register failed: %d", err); ++ goto finalize; ++ } ++ ++finalize: ++ if (err) { ++ instance->stopping = true; ++ cancel_work_sync(&instance->drdy_handler); ++ mutex_destroy(&instance->comm_lock); ++ pimidi_unregister(instance); ++ snd_card_free(card); ++ return err; ++ } ++ ++ for (i = 0; i < PIMIDI_PORTS; ++i) { ++ struct pimidi_midi_port *port = &instance->midi_port[i]; ++ ++ mutex_init(&port->in_lock); ++ mutex_init(&port->out_lock); ++ INIT_WORK(&port->in_handler, ++ i == 0 ? pimidi_midi_in_handler_0 : pimidi_midi_in_handler_1); ++ INIT_DELAYED_WORK(&port->out_handler, ++ i == 0 ? pimidi_midi_out_handler_0 : pimidi_midi_out_handler_1); ++ INIT_KFIFO(port->in_fifo); ++ port->last_output_at = jiffies; ++ } ++ ++ i2c_set_clientdata(client, card); ++ return 0; ++} ++ ++static void pimidi_i2c_remove(struct i2c_client *client) ++{ ++ printd_g("(%p)", client); ++ ++ int i; ++ struct snd_card *card = i2c_get_clientdata(client); ++ ++ if (card) { ++ printi_g("Unloading hw:%d %s", card->number, card->longname); ++ struct pimidi_instance *instance = (struct pimidi_instance *)card->private_data; ++ ++ instance->stopping = true; ++ i2c_set_clientdata(client, NULL); ++ devm_free_irq(&client->dev, client->irq, instance); ++ cancel_work_sync(&instance->drdy_handler); ++ ++ for (i = 0; i < PIMIDI_PORTS; ++i) { ++ cancel_work_sync(&instance->midi_port[i].in_handler); ++ cancel_delayed_work_sync(&instance->midi_port[i].out_handler); ++ mutex_destroy(&instance->midi_port[i].out_lock); ++ mutex_destroy(&instance->midi_port[i].in_lock); ++ kfifo_free(&instance->midi_port[i].in_fifo); ++ } ++ ++ mutex_destroy(&instance->comm_lock); ++ pimidi_unregister(instance); ++ snd_card_free(card); ++ } ++} ++ ++static const struct i2c_device_id pimidi_i2c_ids[] = { ++ { "pimidi", 0 }, ++ {} ++}; ++MODULE_DEVICE_TABLE(i2c, pimidi_i2c_ids); ++ ++static const struct of_device_id pimidi_i2c_dt_ids[] = { ++ { .compatible = "blokaslabs,pimidi", }, ++ {} ++}; ++MODULE_DEVICE_TABLE(of, pimidi_i2c_dt_ids); ++ ++static struct i2c_driver pimidi_i2c_driver = { ++ .driver = { ++ .name = "pimidi", ++ .owner = THIS_MODULE, ++ .of_match_table = of_match_ptr(pimidi_i2c_dt_ids), ++ }, ++ .probe = pimidi_i2c_probe, ++ .remove = pimidi_i2c_remove, ++ .id_table = pimidi_i2c_ids, ++}; ++ ++int pimidi_module_init(void) ++{ ++ int err = 0; ++ ++ mutex_init(&pimidi_global.lock); ++ ++ INIT_LIST_HEAD(&pimidi_global.devices); ++ ++ pimidi_global.work_queue = create_singlethread_workqueue("pimidi"); ++ if (!pimidi_global.work_queue) { ++ err = -ENOMEM; ++ goto cleanup; ++ } ++ ++ err = i2c_add_driver(&pimidi_i2c_driver); ++ if (err < 0) ++ goto cleanup; ++ ++ crc8_populate_msb(pimidi_crc8_table, PIMIDI_CRC8_POLYNOMIAL); ++ ++ return 0; ++ ++cleanup: ++ mutex_destroy(&pimidi_global.lock); ++ return err; ++} ++ ++void pimidi_module_exit(void) ++{ ++ i2c_del_driver(&pimidi_i2c_driver); ++ mutex_lock(&pimidi_global.lock); ++ if (pimidi_global.reset_gpio) { ++ gpiod_put(pimidi_global.reset_gpio); ++ pimidi_global.reset_gpio = NULL; ++ } ++ mutex_unlock(&pimidi_global.lock); ++ ++ destroy_workqueue(pimidi_global.work_queue); ++ pimidi_global.work_queue = NULL; ++ ++ mutex_destroy(&pimidi_global.lock); ++} ++ ++module_init(pimidi_module_init); ++module_exit(pimidi_module_exit); ++ ++MODULE_AUTHOR("Giedrius Trainavi\xc4\x8dius "); ++MODULE_DESCRIPTION("MIDI driver for Blokas Pimidi, https://blokas.io/"); ++MODULE_LICENSE("GPL"); ++ ++/* vim: set ts=8 sw=8 noexpandtab: */ diff --git a/sound/soc/bcm/Kconfig b/sound/soc/bcm/Kconfig index 4218057b0874..fa50cab51478 100644 --- a/sound/soc/bcm/Kconfig @@ -261901,10 +272637,10 @@ index 000000000000..88d312adfbe4 +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/bcm/allo-piano-dac-plus.c b/sound/soc/bcm/allo-piano-dac-plus.c new file mode 100644 -index 000000000000..df167325a85e +index 000000000000..c47bfa5c8f31 --- /dev/null +++ b/sound/soc/bcm/allo-piano-dac-plus.c -@@ -0,0 +1,1064 @@ +@@ -0,0 +1,1027 @@ +/* + * ALSA ASoC Machine Driver for Allo Piano DAC Plus Subwoofer + * @@ -262359,14 +273095,6 @@ index 000000000000..df167325a85e + + rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[0]); + -+ if (digital_gain_0db_limit) { -+ ret = snd_soc_limit_volume(card, "Subwoofer Playback Volume", -+ 207); -+ if (ret < 0) -+ dev_warn(card->dev, "Failed to set volume limit: %d\n", -+ ret); -+ } -+ + // When in Dual Mono, Sub vol control should not set anything. + if (glb_ptr->dual_mode != 1) { //Not in Dual Mono mode + @@ -262469,14 +273197,6 @@ index 000000000000..df167325a85e + + rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[0]); + -+ if (digital_gain_0db_limit) { -+ ret = snd_soc_limit_volume(card, "Master Playback Volume", -+ 207); -+ if (ret < 0) -+ dev_warn(card->dev, "Failed to set volume limit: %d\n", -+ ret); -+ } -+ + if (glb_ptr->dual_mode == 1) { //in Dual Mono Mode + + ret = snd_soc_component_write(asoc_rtd_to_codec(rtd, 0)->component, @@ -262654,21 +273374,6 @@ index 000000000000..df167325a85e + + mutex_init(&glb_ptr->lock); + -+ if (digital_gain_0db_limit) { -+ int ret; -+ -+ //Set volume limit on both dacs -+ for (i = 0; i < ARRAY_SIZE(codec_ctl_pfx); i++) { -+ char cname[256]; -+ -+ sprintf(cname, "%s %s", codec_ctl_pfx[i], codec_ctl_name[0]); -+ ret = snd_soc_limit_volume(card, cname, 207); -+ if (ret < 0) -+ dev_warn(card->dev, "Failed to set volume limit: %d\n", -+ ret); -+ } -+ } -+ + // Remove codec controls + for (i = 0; i < ARRAY_SIZE(codec_ctl_pfx); i++) { + for (j = 0; j < ARRAY_SIZE(codec_ctl_name); j++) { @@ -262676,10 +273381,7 @@ index 000000000000..df167325a85e + + sprintf(cname, "%s %s", codec_ctl_pfx[i], codec_ctl_name[j]); + kctl = snd_soc_card_get_kcontrol(card, cname); -+ if (!kctl) { -+ dev_err(rtd->card->dev, "Control %s not found\n", -+ cname); -+ } else { ++ if (kctl) { + kctl->vd[0].access = + SNDRV_CTL_ELEM_ACCESS_READWRITE; + snd_ctl_remove(card->snd_card, kctl); @@ -262885,47 +273587,44 @@ index 000000000000..df167325a85e + + allo_piano_2_1_codecs[0].of_node = + of_parse_phandle(pdev->dev.of_node, "audio-codec", 0); -+ if (!allo_piano_2_1_codecs[0].of_node) { -+ dev_err(&pdev->dev, -+ "Property 'audio-codec' missing or invalid\n"); -+ return -EINVAL; -+ } -+ + allo_piano_2_1_codecs[1].of_node = + of_parse_phandle(pdev->dev.of_node, "audio-codec", 1); -+ if (!allo_piano_2_1_codecs[1].of_node) { -+ dev_err(&pdev->dev, ++ if (!allo_piano_2_1_codecs[0].of_node || !allo_piano_2_1_codecs[1].of_node) ++ return dev_err_probe(&pdev->dev, -EINVAL, + "Property 'audio-codec' missing or invalid\n"); -+ return -EINVAL; -+ } + + mute_gpio[0] = devm_gpiod_get_optional(&pdev->dev, "mute1", + GPIOD_OUT_LOW); -+ if (IS_ERR(mute_gpio[0])) { -+ ret = PTR_ERR(mute_gpio[0]); -+ dev_err(&pdev->dev, -+ "failed to get mute1 gpio6: %d\n", ret); -+ return ret; -+ } ++ if (IS_ERR(mute_gpio[0])) ++ return dev_err_probe(&pdev->dev, PTR_ERR(mute_gpio[0]), ++ "failed to get mute1 gpio\n"); + + mute_gpio[1] = devm_gpiod_get_optional(&pdev->dev, "mute2", + GPIOD_OUT_LOW); -+ if (IS_ERR(mute_gpio[1])) { -+ ret = PTR_ERR(mute_gpio[1]); -+ dev_err(&pdev->dev, -+ "failed to get mute2 gpio25: %d\n", ret); -+ return ret; -+ } ++ if (IS_ERR(mute_gpio[1])) ++ return dev_err_probe(&pdev->dev, PTR_ERR(mute_gpio[1]), ++ "failed to get mute2 gpio\n"); + + if (mute_gpio[0] && mute_gpio[1]) + snd_allo_piano_dac.set_bias_level = + snd_allo_piano_set_bias_level; + + ret = snd_soc_register_card(&snd_allo_piano_dac); -+ if (ret < 0) { -+ dev_err(&pdev->dev, -+ "snd_soc_register_card() failed: %d\n", ret); -+ return ret; ++ if (ret < 0) ++ return dev_err_probe(&pdev->dev, ret, "snd_soc_register_card() failed\n"); ++ ++ if (digital_gain_0db_limit) { ++ ret = snd_soc_limit_volume(card, "Master Playback Volume", ++ 207); ++ if (ret < 0) ++ dev_warn(card->dev, "Failed to set master volume limit: %d\n", ++ ret); ++ ++ ret = snd_soc_limit_volume(card, "Subwoofer Playback Volume", ++ 207); ++ if (ret < 0) ++ dev_warn(card->dev, "Failed to set subwoofer volume limit: %d\n", ++ ret); + } + + if ((mute_gpio[0]) && (mute_gpio[1])) @@ -271749,10 +282448,10 @@ index 000000000000..9a5cf91719fb +MODULE_LICENSE("GPL"); diff --git a/sound/soc/bcm/rpi-simple-soundcard.c b/sound/soc/bcm/rpi-simple-soundcard.c new file mode 100644 -index 000000000000..c8f681cb07ca +index 000000000000..cc37df468c78 --- /dev/null +++ b/sound/soc/bcm/rpi-simple-soundcard.c -@@ -0,0 +1,560 @@ +@@ -0,0 +1,590 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * rpi-simple-soundcard.c -- ALSA SoC Raspberry Pi soundcard. @@ -272109,16 +282808,46 @@ index 000000000000..c8f681cb07ca + .dai = snd_hifiberry_dac_dai, +}; + ++SND_SOC_DAILINK_DEFS(hifiberry_dac8x, ++ DAILINK_COMP_ARRAY(COMP_EMPTY()), ++ DAILINK_COMP_ARRAY(COMP_CODEC("snd-soc-dummy", "snd-soc-dummy-dai")), ++ DAILINK_COMP_ARRAY(COMP_EMPTY())); ++ +static int hifiberry_dac8x_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); ++ struct snd_soc_card *card = rtd->card; ++ struct gpio_desc *gpio_desc; ++ bool has_adc; + -+ /* override the defaults to reflect 4 x PCM5102A on the card -+ * and limit the sample rate to 192ksps -+ */ ++ /* Configure the codec for 8 channel playback */ + codec_dai->driver->playback.channels_max = 8; + codec_dai->driver->playback.rates = SNDRV_PCM_RATE_8000_192000; + ++ /* Activate capture based on ADC8x detection */ ++ gpio_desc = devm_gpiod_get(card->dev, "hasadc", GPIOD_IN); ++ if (IS_ERR(gpio_desc)) { ++ dev_err(card->dev, "Failed to get GPIO: %ld\n", PTR_ERR(gpio_desc)); ++ return PTR_ERR(gpio_desc); ++ } ++ ++ has_adc = gpiod_get_value(gpio_desc); ++ ++ if (has_adc) { ++ struct snd_soc_dai_link *dai = rtd->dai_link; ++ ++ dev_info(card->dev, "ADC8x detected: capture enabled\n"); ++ codec_dai->driver->symmetric_rate = 1; ++ codec_dai->driver->symmetric_channels = 1; ++ codec_dai->driver->symmetric_sample_bits = 1; ++ codec_dai->driver->capture.rates = SNDRV_PCM_RATE_8000_192000; ++ dai->name = "HiFiBerry DAC8xADC8x"; ++ dai->stream_name = "HiFiBerry DAC8xADC8x HiFi"; ++ } else { ++ dev_info(card->dev, "no ADC8x detected\n"); ++ rtd->dai_link->playback_only = 1; // Disable capture ++ } ++ + return 0; +} + @@ -272130,7 +282859,7 @@ index 000000000000..c8f681cb07ca + SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, + .init = hifiberry_dac8x_init, -+ SND_SOC_DAILINK_REG(hifiberry_dac), ++ SND_SOC_DAILINK_REG(hifiberry_dac8x), + }, +}; + @@ -272869,7 +283598,7 @@ index 000000000000..1a8f8e67e8a0 +MODULE_DESCRIPTION("ASoC Raspberry Pi Hat generic digi driver for WM8804 based cards"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig -index f1e1dbc509f6..e2777a369e2d 100644 +index 6d105a23c828..f50fa1da116c 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -119,6 +119,7 @@ config SND_SOC_ALL_CODECS @@ -272954,7 +283683,7 @@ index f1e1dbc509f6..e2777a369e2d 100644 config SND_SOC_TFA989X tristate "NXP/Goodix TFA989X (TFA1) amplifiers" depends on I2C -@@ -2404,4 +2422,8 @@ config SND_SOC_LPASS_TX_MACRO +@@ -2405,4 +2423,8 @@ config SND_SOC_LPASS_TX_MACRO select SND_SOC_LPASS_MACRO_COMMON tristate "Qualcomm TX Macro in LPASS(Low Power Audio SubSystem)" @@ -275092,7 +285821,7 @@ index 5cd2b64b9337..4be476a280e1 100644 }; MODULE_DEVICE_TABLE(of, pcm512x_of_match); diff --git a/sound/soc/codecs/pcm512x.c b/sound/soc/codecs/pcm512x.c -index 89059a673cf0..6fb35e656455 100644 +index 89059a673cf0..c653998497f3 100644 --- a/sound/soc/codecs/pcm512x.c +++ b/sound/soc/codecs/pcm512x.c @@ -48,6 +48,7 @@ struct pcm512x_priv { @@ -275112,6 +285841,15 @@ index 89059a673cf0..6fb35e656455 100644 }; static const struct snd_pcm_hw_constraint_list constraints_slave = { +@@ -629,7 +630,7 @@ static int pcm512x_dai_startup_slave(struct snd_pcm_substream *substream, + struct regmap *regmap = pcm512x->regmap; + + if (IS_ERR(pcm512x->sclk)) { +- dev_info(dev, "No SCLK, using BCLK: %ld\n", ++ dev_dbg(dev, "No SCLK, using BCLK: %ld\n", + PTR_ERR(pcm512x->sclk)); + + /* Disable reporting of missing SCLK as an error */ @@ -1258,10 +1259,34 @@ static int pcm512x_hw_params(struct snd_pcm_substream *substream, return ret; } @@ -276221,10 +286959,10 @@ index 7743ea983b1a..cee90e193d22 100644 /* * For devices with more than one control interface, we assume the diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c -index 54dff2173b8c..481fb2070e7b 100644 +index 93d9ed8983df..871550ccd8d2 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c -@@ -2224,6 +2224,8 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = { +@@ -2253,6 +2253,8 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = { QUIRK_FLAG_ALIGN_TRANSFER), DEVICE_FLG(0x534d, 0x2109, /* MacroSilicon MS2109 */ QUIRK_FLAG_ALIGN_TRANSFER), diff --git a/raspberrypi-kernel.spec b/raspberrypi-kernel.spec index 061640de3572f847bbeaec272a9f0a160a1d4f48..fec5ef4d532ddbde6eb0943c3eb539ad6c5b97e3 100644 --- a/raspberrypi-kernel.spec +++ b/raspberrypi-kernel.spec @@ -2,13 +2,13 @@ %global KernelVer %{version}-%{release}.raspi.%{_target_cpu} -%global hulkrelease 75.0.0 +%global hulkrelease 84.0.0 %global debug_package %{nil} Name: raspberrypi-kernel Version: 6.6.0 -Release: %{hulkrelease}.11 +Release: %{hulkrelease}.12 Summary: Linux Kernel License: GPLv2 URL: http://www.kernel.org/ @@ -281,6 +281,10 @@ fi /usr/src/kernels/%{KernelVer}-* %changelog +* Tue Apr 8 2025 Yafen Fang - 6.6.0-84.0.0.12 +- update kernel version to openEuler 6.6.0-84.0.0 +- update Raspberry Pi patch, last commit (f1076a9d7a269d72b6707283560d0d38203cb07a): delete the comment + * Fri Jan 24 2025 Yafen Fang - 6.6.0-75.0.0.11 - update kernel version to openEuler 6.6.0-75.0.0 - iommu: Hook up '->unmap_pages' driver callback